Blame view

drivers/tty/tty_buffer.c 16.2 KB
e3b3d0f54   Greg Kroah-Hartman   tty: add SPDX ide...
1
  // SPDX-License-Identifier: GPL-2.0
e04957365   Alan Cox   tty: split the bu...
2
3
4
5
6
7
8
9
10
11
12
13
14
  /*
   * Tty buffer allocation management
   */
  
  #include <linux/types.h>
  #include <linux/errno.h>
  #include <linux/tty.h>
  #include <linux/tty_driver.h>
  #include <linux/tty_flip.h>
  #include <linux/timer.h>
  #include <linux/string.h>
  #include <linux/slab.h>
  #include <linux/sched.h>
e04957365   Alan Cox   tty: split the bu...
15
16
17
18
  #include <linux/wait.h>
  #include <linux/bitops.h>
  #include <linux/delay.h>
  #include <linux/module.h>
593fb1ae4   George Spelvin   pps: Move timesta...
19
  #include <linux/ratelimit.h>
e04957365   Alan Cox   tty: split the bu...
20

1cef50e31   Peter Hurley   tty: Fix flip buf...
21
22
23
  
  #define MIN_TTYB_SIZE	256
  #define TTYB_ALIGN_MASK	255
7bfe0b711   Peter Hurley   tty: Track flip b...
24
25
26
27
  /*
   * Byte threshold to limit memory consumption for flip buffers.
   * The actual memory limit is > 2x this amount.
   */
7ab57b76e   Manfred Schlaegl   tty: increase the...
28
  #define TTYB_DEFAULT_MEM_LIMIT	(640 * 1024UL)
7bfe0b711   Peter Hurley   tty: Track flip b...
29

9114fe8cc   Peter Hurley   tty: Remove priva...
30
31
32
33
34
35
36
37
38
  /*
   * We default to dicing tty buffer allocations to this many characters
   * in order to avoid multiple page allocations. We know the size of
   * tty_buffer itself but it must also be taken into account that the
   * the buffer is 256 byte aligned. See tty_buffer_find for the allocation
   * logic this must match
   */
  
  #define TTY_BUFFER_PAGE	(((PAGE_SIZE - sizeof(struct tty_buffer)) / 2) & ~0xFF)
7bfe0b711   Peter Hurley   tty: Track flip b...
39
  /**
a7c8d58c7   Peter Hurley   tty: Fix unsafe v...
40
41
42
   *	tty_buffer_lock_exclusive	-	gain exclusive access to buffer
   *	tty_buffer_unlock_exclusive	-	release exclusive access
   *
fa4419545   Jiri Slaby   tty: fix kernel-doc
43
   *	@port: tty port owning the flip buffer
a7c8d58c7   Peter Hurley   tty: Fix unsafe v...
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
   *
   *	Guarantees safe use of the line discipline's receive_buf() method by
   *	excluding the buffer work and any pending flush from using the flip
   *	buffer. Data can continue to be added concurrently to the flip buffer
   *	from the driver side.
   *
   *	On release, the buffer work is restarted if there is data in the
   *	flip buffer
   */
  
  void tty_buffer_lock_exclusive(struct tty_port *port)
  {
  	struct tty_bufhead *buf = &port->buf;
  
  	atomic_inc(&buf->priority);
  	mutex_lock(&buf->lock);
  }
28a821c30   Ben Hutchings   Staging: speakup:...
61
  EXPORT_SYMBOL_GPL(tty_buffer_lock_exclusive);
a7c8d58c7   Peter Hurley   tty: Fix unsafe v...
62
63
64
65
66
67
68
69
70
71
72
73
74
  
  void tty_buffer_unlock_exclusive(struct tty_port *port)
  {
  	struct tty_bufhead *buf = &port->buf;
  	int restart;
  
  	restart = buf->head->commit != buf->head->read;
  
  	atomic_dec(&buf->priority);
  	mutex_unlock(&buf->lock);
  	if (restart)
  		queue_work(system_unbound_wq, &buf->work);
  }
28a821c30   Ben Hutchings   Staging: speakup:...
75
  EXPORT_SYMBOL_GPL(tty_buffer_unlock_exclusive);
a7c8d58c7   Peter Hurley   tty: Fix unsafe v...
76
77
  
  /**
7bfe0b711   Peter Hurley   tty: Track flip b...
78
   *	tty_buffer_space_avail	-	return unused buffer space
fa4419545   Jiri Slaby   tty: fix kernel-doc
79
   *	@port: tty port owning the flip buffer
7bfe0b711   Peter Hurley   tty: Track flip b...
80
81
82
83
84
85
86
87
88
89
90
   *
   *	Returns the # of bytes which can be written by the driver without
   *	reaching the buffer limit.
   *
   *	Note: this does not guarantee that memory is available to write
   *	the returned # of bytes (use tty_prepare_flip_string_xxx() to
   *	pre-allocate if memory guarantee is required).
   */
  
  int tty_buffer_space_avail(struct tty_port *port)
  {
5dda4ca55   Peter Hurley   tty: Rename tty b...
91
  	int space = port->buf.mem_limit - atomic_read(&port->buf.mem_used);
7bfe0b711   Peter Hurley   tty: Track flip b...
92
93
  	return max(space, 0);
  }
c4a8dab58   Peter Hurley   staging/fwserial:...
94
  EXPORT_SYMBOL_GPL(tty_buffer_space_avail);
7bfe0b711   Peter Hurley   tty: Track flip b...
95

9dd5139f9   Peter Hurley   tty: Factor flip ...
96
97
98
99
100
101
102
  static void tty_buffer_reset(struct tty_buffer *p, size_t size)
  {
  	p->used = 0;
  	p->size = size;
  	p->next = NULL;
  	p->commit = 0;
  	p->read = 0;
acc0f67f3   Peter Hurley   tty: Halve flip b...
103
  	p->flags = 0;
9dd5139f9   Peter Hurley   tty: Factor flip ...
104
  }
e04957365   Alan Cox   tty: split the bu...
105
106
  /**
   *	tty_buffer_free_all		-	free buffers used by a tty
fa4419545   Jiri Slaby   tty: fix kernel-doc
107
   *	@port: tty port to free from
e04957365   Alan Cox   tty: split the bu...
108
109
110
   *
   *	Remove all the buffers pending on a tty whether queued with data
   *	or in the free ring. Must be called when the tty is no longer in use
e04957365   Alan Cox   tty: split the bu...
111
   */
ecbbfd44a   Jiri Slaby   TTY: move tty buf...
112
  void tty_buffer_free_all(struct tty_port *port)
e04957365   Alan Cox   tty: split the bu...
113
  {
ecbbfd44a   Jiri Slaby   TTY: move tty buf...
114
  	struct tty_bufhead *buf = &port->buf;
809850b7a   Peter Hurley   tty: Use lockless...
115
116
  	struct tty_buffer *p, *next;
  	struct llist_node *llist;
feacbecb3   Jiri Slaby   TTY: tty_buffer, ...
117
118
  	unsigned int freed = 0;
  	int still_used;
5cff39c69   Jiri Slaby   TTY: tty_buffer, ...
119

2cf7b67e8   Peter Hurley   tty: Use generic ...
120
121
  	while ((p = buf->head) != NULL) {
  		buf->head = p->next;
feacbecb3   Jiri Slaby   TTY: tty_buffer, ...
122
  		freed += p->size;
7391ee169   Peter Hurley   tty: Simplify fli...
123
124
  		if (p->size > 0)
  			kfree(p);
e04957365   Alan Cox   tty: split the bu...
125
  	}
809850b7a   Peter Hurley   tty: Use lockless...
126
127
  	llist = llist_del_all(&buf->free);
  	llist_for_each_entry_safe(p, next, llist, free)
2cf7b67e8   Peter Hurley   tty: Use generic ...
128
  		kfree(p);
809850b7a   Peter Hurley   tty: Use lockless...
129

7391ee169   Peter Hurley   tty: Simplify fli...
130
131
132
  	tty_buffer_reset(&buf->sentinel, 0);
  	buf->head = &buf->sentinel;
  	buf->tail = &buf->sentinel;
7bfe0b711   Peter Hurley   tty: Track flip b...
133

feacbecb3   Jiri Slaby   TTY: tty_buffer, ...
134
135
136
  	still_used = atomic_xchg(&buf->mem_used, 0);
  	WARN(still_used != freed, "we still have not freed %d bytes!",
  			still_used - freed);
e04957365   Alan Cox   tty: split the bu...
137
138
139
140
  }
  
  /**
   *	tty_buffer_alloc	-	allocate a tty buffer
fa4419545   Jiri Slaby   tty: fix kernel-doc
141
   *	@port: tty port
e04957365   Alan Cox   tty: split the bu...
142
143
144
   *	@size: desired size (characters)
   *
   *	Allocate a new tty buffer to hold the desired number of characters.
11b9faa44   Peter Hurley   tty: Merge tty_bu...
145
146
   *	We round our buffers off in 256 character chunks to get better
   *	allocation behaviour.
e04957365   Alan Cox   tty: split the bu...
147
148
   *	Return NULL if out of memory or the allocation would exceed the
   *	per device queue
e04957365   Alan Cox   tty: split the bu...
149
   */
ecbbfd44a   Jiri Slaby   TTY: move tty buf...
150
  static struct tty_buffer *tty_buffer_alloc(struct tty_port *port, size_t size)
e04957365   Alan Cox   tty: split the bu...
151
  {
809850b7a   Peter Hurley   tty: Use lockless...
152
  	struct llist_node *free;
e04957365   Alan Cox   tty: split the bu...
153
  	struct tty_buffer *p;
11b9faa44   Peter Hurley   tty: Merge tty_bu...
154
155
156
157
  	/* Round the buffer size out */
  	size = __ALIGN_MASK(size, TTYB_ALIGN_MASK);
  
  	if (size <= MIN_TTYB_SIZE) {
809850b7a   Peter Hurley   tty: Use lockless...
158
159
160
  		free = llist_del_first(&port->buf.free);
  		if (free) {
  			p = llist_entry(free, struct tty_buffer, free);
11b9faa44   Peter Hurley   tty: Merge tty_bu...
161
162
163
164
165
166
  			goto found;
  		}
  	}
  
  	/* Should possibly check if this fails for the largest buffer we
  	   have queued and recycle that ? */
5dda4ca55   Peter Hurley   tty: Rename tty b...
167
  	if (atomic_read(&port->buf.mem_used) > port->buf.mem_limit)
e04957365   Alan Cox   tty: split the bu...
168
169
170
171
  		return NULL;
  	p = kmalloc(sizeof(struct tty_buffer) + 2 * size, GFP_ATOMIC);
  	if (p == NULL)
  		return NULL;
9dd5139f9   Peter Hurley   tty: Factor flip ...
172

11b9faa44   Peter Hurley   tty: Merge tty_bu...
173
  found:
9dd5139f9   Peter Hurley   tty: Factor flip ...
174
  	tty_buffer_reset(p, size);
5dda4ca55   Peter Hurley   tty: Rename tty b...
175
  	atomic_add(size, &port->buf.mem_used);
e04957365   Alan Cox   tty: split the bu...
176
177
178
179
180
  	return p;
  }
  
  /**
   *	tty_buffer_free		-	free a tty buffer
fa4419545   Jiri Slaby   tty: fix kernel-doc
181
   *	@port: tty port owning the buffer
e04957365   Alan Cox   tty: split the bu...
182
183
184
185
   *	@b: the buffer to free
   *
   *	Free a tty buffer, or add it to the free list according to our
   *	internal strategy
e04957365   Alan Cox   tty: split the bu...
186
   */
ecbbfd44a   Jiri Slaby   TTY: move tty buf...
187
  static void tty_buffer_free(struct tty_port *port, struct tty_buffer *b)
e04957365   Alan Cox   tty: split the bu...
188
  {
ecbbfd44a   Jiri Slaby   TTY: move tty buf...
189
  	struct tty_bufhead *buf = &port->buf;
5cff39c69   Jiri Slaby   TTY: tty_buffer, ...
190

e04957365   Alan Cox   tty: split the bu...
191
  	/* Dumb strategy for now - should keep some stats */
5dda4ca55   Peter Hurley   tty: Rename tty b...
192
  	WARN_ON(atomic_sub_return(b->size, &buf->mem_used) < 0);
e04957365   Alan Cox   tty: split the bu...
193

1cef50e31   Peter Hurley   tty: Fix flip buf...
194
  	if (b->size > MIN_TTYB_SIZE)
e04957365   Alan Cox   tty: split the bu...
195
  		kfree(b);
7391ee169   Peter Hurley   tty: Simplify fli...
196
  	else if (b->size > 0)
809850b7a   Peter Hurley   tty: Use lockless...
197
  		llist_add(&b->free, &buf->free);
e04957365   Alan Cox   tty: split the bu...
198
199
200
  }
  
  /**
e04957365   Alan Cox   tty: split the bu...
201
202
   *	tty_buffer_flush		-	flush full tty buffers
   *	@tty: tty to flush
86c80a8e2   Peter Hurley   tty: Flush ldisc ...
203
   *	@ld:  optional ldisc ptr (must be referenced)
e04957365   Alan Cox   tty: split the bu...
204
   *
86c80a8e2   Peter Hurley   tty: Flush ldisc ...
205
206
   *	flush all the buffers containing receive data. If ld != NULL,
   *	flush the ldisc input buffer.
e04957365   Alan Cox   tty: split the bu...
207
   *
a7c8d58c7   Peter Hurley   tty: Fix unsafe v...
208
   *	Locking: takes buffer lock to ensure single-threaded flip buffer
e9975fdec   Peter Hurley   tty: Ensure singl...
209
   *		 'consumer'
e04957365   Alan Cox   tty: split the bu...
210
   */
86c80a8e2   Peter Hurley   tty: Flush ldisc ...
211
  void tty_buffer_flush(struct tty_struct *tty, struct tty_ldisc *ld)
e04957365   Alan Cox   tty: split the bu...
212
  {
2fc20661e   Jiri Slaby   TTY: move TTY_FLU...
213
  	struct tty_port *port = tty->port;
ecbbfd44a   Jiri Slaby   TTY: move tty buf...
214
  	struct tty_bufhead *buf = &port->buf;
47aa658a0   Peter Hurley   tty: Merge __tty_...
215
  	struct tty_buffer *next;
e04957365   Alan Cox   tty: split the bu...
216

a7c8d58c7   Peter Hurley   tty: Fix unsafe v...
217
  	atomic_inc(&buf->priority);
e9975fdec   Peter Hurley   tty: Ensure singl...
218

a7c8d58c7   Peter Hurley   tty: Fix unsafe v...
219
  	mutex_lock(&buf->lock);
9e6b7cd7e   Dmitry Vyukov   tty: fix data rac...
220
221
222
223
  	/* paired w/ release in __tty_buffer_request_room; ensures there are
  	 * no pending memory accesses to the freed buffer
  	 */
  	while ((next = smp_load_acquire(&buf->head->next)) != NULL) {
47aa658a0   Peter Hurley   tty: Merge __tty_...
224
225
226
227
  		tty_buffer_free(port, buf->head);
  		buf->head = next;
  	}
  	buf->head->read = buf->head->commit;
86c80a8e2   Peter Hurley   tty: Flush ldisc ...
228
229
230
  
  	if (ld && ld->ops->flush_buffer)
  		ld->ops->flush_buffer(tty);
a7c8d58c7   Peter Hurley   tty: Fix unsafe v...
231
232
  	atomic_dec(&buf->priority);
  	mutex_unlock(&buf->lock);
e04957365   Alan Cox   tty: split the bu...
233
234
235
  }
  
  /**
64325a3be   Ilya Zykov   tty: Correct tty ...
236
   *	tty_buffer_request_room		-	grow tty buffer if needed
fa4419545   Jiri Slaby   tty: fix kernel-doc
237
   *	@port: tty port
e04957365   Alan Cox   tty: split the bu...
238
   *	@size: size desired
acc0f67f3   Peter Hurley   tty: Halve flip b...
239
   *	@flags: buffer flags if new buffer allocated (default = 0)
e04957365   Alan Cox   tty: split the bu...
240
241
242
   *
   *	Make at least size bytes of linear space available for the tty
   *	buffer. If we fail return the size we managed to find.
acc0f67f3   Peter Hurley   tty: Halve flip b...
243
244
245
246
   *
   *	Will change over to a new buffer if the current buffer is encoded as
   *	TTY_NORMAL (so has no flags buffer) and the new buffer requires
   *	a flags buffer.
e04957365   Alan Cox   tty: split the bu...
247
   */
acc0f67f3   Peter Hurley   tty: Halve flip b...
248
249
  static int __tty_buffer_request_room(struct tty_port *port, size_t size,
  				     int flags)
e04957365   Alan Cox   tty: split the bu...
250
  {
ecbbfd44a   Jiri Slaby   TTY: move tty buf...
251
  	struct tty_bufhead *buf = &port->buf;
e04957365   Alan Cox   tty: split the bu...
252
  	struct tty_buffer *b, *n;
acc0f67f3   Peter Hurley   tty: Halve flip b...
253
  	int left, change;
e8437d7ec   Peter Hurley   tty: Make driver-...
254

5cff39c69   Jiri Slaby   TTY: tty_buffer, ...
255
  	b = buf->tail;
acc0f67f3   Peter Hurley   tty: Halve flip b...
256
257
258
259
  	if (b->flags & TTYB_NORMAL)
  		left = 2 * b->size - b->used;
  	else
  		left = b->size - b->used;
e04957365   Alan Cox   tty: split the bu...
260

acc0f67f3   Peter Hurley   tty: Halve flip b...
261
262
  	change = (b->flags & TTYB_NORMAL) && (~flags & TTYB_NORMAL);
  	if (change || left < size) {
e04957365   Alan Cox   tty: split the bu...
263
  		/* This is the slow path - looking for new buffers to use */
e16cb0a72   Greg Kroah-Hartman   tty: tty_buffer.c...
264
265
  		n = tty_buffer_alloc(port, size);
  		if (n != NULL) {
acc0f67f3   Peter Hurley   tty: Halve flip b...
266
  			n->flags = flags;
5cff39c69   Jiri Slaby   TTY: tty_buffer, ...
267
  			buf->tail = n;
facd885c7   Dmitry Vyukov   tty: fix data rac...
268
269
270
271
  			/* paired w/ acquire in flush_to_ldisc(); ensures
  			 * flush_to_ldisc() sees buffer data.
  			 */
  			smp_store_release(&b->commit, b->used);
069f38b49   Peter Hurley   tty: Replace smp_...
272
  			/* paired w/ acquire in flush_to_ldisc(); ensures the
62a0d8d7c   Peter Hurley   tty: Fix lockless...
273
274
275
  			 * latest commit value can be read before the head is
  			 * advanced to the next buffer
  			 */
069f38b49   Peter Hurley   tty: Replace smp_...
276
  			smp_store_release(&b->next, n);
acc0f67f3   Peter Hurley   tty: Halve flip b...
277
278
279
  		} else if (change)
  			size = 0;
  		else
e04957365   Alan Cox   tty: split the bu...
280
281
  			size = left;
  	}
e04957365   Alan Cox   tty: split the bu...
282
283
  	return size;
  }
acc0f67f3   Peter Hurley   tty: Halve flip b...
284
285
286
287
288
  
  int tty_buffer_request_room(struct tty_port *port, size_t size)
  {
  	return __tty_buffer_request_room(port, size, 0);
  }
e04957365   Alan Cox   tty: split the bu...
289
290
291
  EXPORT_SYMBOL_GPL(tty_buffer_request_room);
  
  /**
2832fc11f   Alan Cox   USB: tty: Add a f...
292
   *	tty_insert_flip_string_fixed_flag - Add characters to the tty buffer
2f6933571   Jiri Slaby   TTY: convert more...
293
   *	@port: tty port
e04957365   Alan Cox   tty: split the bu...
294
   *	@chars: characters
2832fc11f   Alan Cox   USB: tty: Add a f...
295
   *	@flag: flag value for each character
e04957365   Alan Cox   tty: split the bu...
296
297
298
   *	@size: size
   *
   *	Queue a series of bytes to the tty buffering. All the characters
ccc5ca8d4   Johan Hovold   tty: fix obsolete...
299
   *	passed are marked with the supplied flag. Returns the number added.
e04957365   Alan Cox   tty: split the bu...
300
   */
2f6933571   Jiri Slaby   TTY: convert more...
301
  int tty_insert_flip_string_fixed_flag(struct tty_port *port,
2832fc11f   Alan Cox   USB: tty: Add a f...
302
  		const unsigned char *chars, char flag, size_t size)
e04957365   Alan Cox   tty: split the bu...
303
304
305
  {
  	int copied = 0;
  	do {
d4bee0a67   Fang Wenqi   tty_buffer: Fix d...
306
  		int goal = min_t(size_t, size - copied, TTY_BUFFER_PAGE);
acc0f67f3   Peter Hurley   tty: Halve flip b...
307
308
  		int flags = (flag == TTY_NORMAL) ? TTYB_NORMAL : 0;
  		int space = __tty_buffer_request_room(port, goal, flags);
64325a3be   Ilya Zykov   tty: Correct tty ...
309
  		struct tty_buffer *tb = port->buf.tail;
7391ee169   Peter Hurley   tty: Simplify fli...
310
  		if (unlikely(space == 0))
e04957365   Alan Cox   tty: split the bu...
311
  			break;
1fc359fc3   Peter Hurley   tty: Compute flip...
312
  		memcpy(char_buf_ptr(tb, tb->used), chars, space);
acc0f67f3   Peter Hurley   tty: Halve flip b...
313
314
  		if (~tb->flags & TTYB_NORMAL)
  			memset(flag_buf_ptr(tb, tb->used), flag, space);
e04957365   Alan Cox   tty: split the bu...
315
316
317
318
319
320
321
322
  		tb->used += space;
  		copied += space;
  		chars += space;
  		/* There is a small chance that we need to split the data over
  		   several buffers. If this is the case we must loop */
  	} while (unlikely(size > copied));
  	return copied;
  }
2832fc11f   Alan Cox   USB: tty: Add a f...
323
  EXPORT_SYMBOL(tty_insert_flip_string_fixed_flag);
e04957365   Alan Cox   tty: split the bu...
324
325
326
  
  /**
   *	tty_insert_flip_string_flags	-	Add characters to the tty buffer
2f6933571   Jiri Slaby   TTY: convert more...
327
   *	@port: tty port
e04957365   Alan Cox   tty: split the bu...
328
329
330
331
332
333
334
   *	@chars: characters
   *	@flags: flag bytes
   *	@size: size
   *
   *	Queue a series of bytes to the tty buffering. For each character
   *	the flags array indicates the status of the character. Returns the
   *	number added.
e04957365   Alan Cox   tty: split the bu...
335
   */
2f6933571   Jiri Slaby   TTY: convert more...
336
  int tty_insert_flip_string_flags(struct tty_port *port,
e04957365   Alan Cox   tty: split the bu...
337
338
339
340
  		const unsigned char *chars, const char *flags, size_t size)
  {
  	int copied = 0;
  	do {
d4bee0a67   Fang Wenqi   tty_buffer: Fix d...
341
  		int goal = min_t(size_t, size - copied, TTY_BUFFER_PAGE);
64325a3be   Ilya Zykov   tty: Correct tty ...
342
343
  		int space = tty_buffer_request_room(port, goal);
  		struct tty_buffer *tb = port->buf.tail;
7391ee169   Peter Hurley   tty: Simplify fli...
344
  		if (unlikely(space == 0))
e04957365   Alan Cox   tty: split the bu...
345
  			break;
1fc359fc3   Peter Hurley   tty: Compute flip...
346
347
  		memcpy(char_buf_ptr(tb, tb->used), chars, space);
  		memcpy(flag_buf_ptr(tb, tb->used), flags, space);
e04957365   Alan Cox   tty: split the bu...
348
349
350
351
352
353
354
355
356
357
358
359
  		tb->used += space;
  		copied += space;
  		chars += space;
  		flags += space;
  		/* There is a small chance that we need to split the data over
  		   several buffers. If this is the case we must loop */
  	} while (unlikely(size > copied));
  	return copied;
  }
  EXPORT_SYMBOL(tty_insert_flip_string_flags);
  
  /**
979990c62   Arnd Bergmann   tty: improve tty_...
360
361
362
363
364
365
366
367
368
369
   *	__tty_insert_flip_char   -	Add one character to the tty buffer
   *	@port: tty port
   *	@ch: character
   *	@flag: flag byte
   *
   *	Queue a single byte to the tty buffering, with an optional flag.
   *	This is the slow path of tty_insert_flip_char.
   */
  int __tty_insert_flip_char(struct tty_port *port, unsigned char ch, char flag)
  {
8a5a90a2a   Arnd Bergmann   tty: fix __tty_in...
370
  	struct tty_buffer *tb;
979990c62   Arnd Bergmann   tty: improve tty_...
371
  	int flags = (flag == TTY_NORMAL) ? TTYB_NORMAL : 0;
065ea0a7a   Arnd Bergmann   tty: improve tty_...
372
  	if (!__tty_buffer_request_room(port, 1, flags))
979990c62   Arnd Bergmann   tty: improve tty_...
373
  		return 0;
8a5a90a2a   Arnd Bergmann   tty: fix __tty_in...
374
  	tb = port->buf.tail;
065ea0a7a   Arnd Bergmann   tty: improve tty_...
375
376
  	if (~tb->flags & TTYB_NORMAL)
  		*flag_buf_ptr(tb, tb->used) = flag;
979990c62   Arnd Bergmann   tty: improve tty_...
377
378
379
380
381
382
383
  	*char_buf_ptr(tb, tb->used++) = ch;
  
  	return 1;
  }
  EXPORT_SYMBOL(__tty_insert_flip_char);
  
  /**
e04957365   Alan Cox   tty: split the bu...
384
   *	tty_schedule_flip	-	push characters to ldisc
6732c8bb8   Jiri Slaby   TTY: switch tty_s...
385
   *	@port: tty port to push from
e04957365   Alan Cox   tty: split the bu...
386
387
388
389
   *
   *	Takes any pending buffers and transfers their ownership to the
   *	ldisc side of the queue. It then schedules those characters for
   *	processing by the line discipline.
e04957365   Alan Cox   tty: split the bu...
390
   */
6732c8bb8   Jiri Slaby   TTY: switch tty_s...
391
  void tty_schedule_flip(struct tty_port *port)
e04957365   Alan Cox   tty: split the bu...
392
  {
6732c8bb8   Jiri Slaby   TTY: switch tty_s...
393
  	struct tty_bufhead *buf = &port->buf;
5cff39c69   Jiri Slaby   TTY: tty_buffer, ...
394

facd885c7   Dmitry Vyukov   tty: fix data rac...
395
396
397
398
  	/* paired w/ acquire in flush_to_ldisc(); ensures
  	 * flush_to_ldisc() sees buffer data.
  	 */
  	smp_store_release(&buf->tail->commit, buf->tail->used);
e052c6d15   Peter Hurley   tty: Use unbound ...
399
  	queue_work(system_unbound_wq, &buf->work);
e04957365   Alan Cox   tty: split the bu...
400
401
402
403
404
  }
  EXPORT_SYMBOL(tty_schedule_flip);
  
  /**
   *	tty_prepare_flip_string		-	make room for characters
2f6933571   Jiri Slaby   TTY: convert more...
405
   *	@port: tty port
e04957365   Alan Cox   tty: split the bu...
406
407
408
409
410
411
412
413
   *	@chars: return pointer for character write area
   *	@size: desired size
   *
   *	Prepare a block of space in the buffer for data. Returns the length
   *	available and buffer pointer to the space which is now allocated and
   *	accounted for as ready for normal characters. This is used for drivers
   *	that need their own block copy routines into the buffer. There is no
   *	guarantee the buffer is a DMA target!
e04957365   Alan Cox   tty: split the bu...
414
   */
2f6933571   Jiri Slaby   TTY: convert more...
415
  int tty_prepare_flip_string(struct tty_port *port, unsigned char **chars,
ecbbfd44a   Jiri Slaby   TTY: move tty buf...
416
  		size_t size)
e04957365   Alan Cox   tty: split the bu...
417
  {
acc0f67f3   Peter Hurley   tty: Halve flip b...
418
  	int space = __tty_buffer_request_room(port, size, TTYB_NORMAL);
e04957365   Alan Cox   tty: split the bu...
419
  	if (likely(space)) {
64325a3be   Ilya Zykov   tty: Correct tty ...
420
  		struct tty_buffer *tb = port->buf.tail;
1fc359fc3   Peter Hurley   tty: Compute flip...
421
  		*chars = char_buf_ptr(tb, tb->used);
acc0f67f3   Peter Hurley   tty: Halve flip b...
422
423
  		if (~tb->flags & TTYB_NORMAL)
  			memset(flag_buf_ptr(tb, tb->used), TTY_NORMAL, space);
e04957365   Alan Cox   tty: split the bu...
424
425
426
427
428
  		tb->used += space;
  	}
  	return space;
  }
  EXPORT_SYMBOL_GPL(tty_prepare_flip_string);
8d082cd30   Peter Hurley   tty: Unify receiv...
429
430
431
432
433
434
435
436
437
438
  /**
   *	tty_ldisc_receive_buf		-	forward data to line discipline
   *	@ld:	line discipline to process input
   *	@p:	char buffer
   *	@f:	TTY_* flags buffer
   *	@count:	number of bytes to process
   *
   *	Callers other than flush_to_ldisc() need to exclude the kworker
   *	from concurrent use of the line discipline, see paste_selection().
   *
e7e51dcf3   Johan Hovold   tty: fix tty_ldis...
439
   *	Returns the number of bytes processed
8d082cd30   Peter Hurley   tty: Unify receiv...
440
   */
c92d781f1   Rob Herring   tty: constify tty...
441
  int tty_ldisc_receive_buf(struct tty_ldisc *ld, const unsigned char *p,
8d082cd30   Peter Hurley   tty: Unify receiv...
442
443
444
445
446
447
448
449
450
451
452
453
  			  char *f, int count)
  {
  	if (ld->ops->receive_buf2)
  		count = ld->ops->receive_buf2(ld->tty, p, f, count);
  	else {
  		count = min_t(int, count, ld->tty->receive_room);
  		if (count && ld->ops->receive_buf)
  			ld->ops->receive_buf(ld->tty, p, f, count);
  	}
  	return count;
  }
  EXPORT_SYMBOL_GPL(tty_ldisc_receive_buf);
e04957365   Alan Cox   tty: split the bu...
454

da261e7fe   Peter Hurley   tty: Simplify tty...
455
  static int
c3485ee0d   Rob Herring   tty_port: Add por...
456
  receive_buf(struct tty_port *port, struct tty_buffer *head, int count)
da261e7fe   Peter Hurley   tty: Simplify tty...
457
  {
1fc359fc3   Peter Hurley   tty: Compute flip...
458
  	unsigned char *p = char_buf_ptr(head, head->read);
acc0f67f3   Peter Hurley   tty: Halve flip b...
459
  	char	      *f = NULL;
c9a8e5fce   Linus Torvalds   tty: wipe buffer.
460
  	int n;
acc0f67f3   Peter Hurley   tty: Halve flip b...
461
462
463
  
  	if (~head->flags & TTYB_NORMAL)
  		f = flag_buf_ptr(head, head->read);
da261e7fe   Peter Hurley   tty: Simplify tty...
464

c9a8e5fce   Linus Torvalds   tty: wipe buffer.
465
466
467
468
  	n = port->client_ops->receive_buf(port, p, f, count);
  	if (n > 0)
  		memset(p, 0, n);
  	return n;
da261e7fe   Peter Hurley   tty: Simplify tty...
469
  }
e04957365   Alan Cox   tty: split the bu...
470
471
472
473
474
475
476
477
  
  /**
   *	flush_to_ldisc
   *	@work: tty structure passed from work queue.
   *
   *	This routine is called out of the software interrupt to flush data
   *	from the buffer chain to the line discipline.
   *
e9975fdec   Peter Hurley   tty: Ensure singl...
478
479
   *	The receive_buf method is single threaded for each tty instance.
   *
a7c8d58c7   Peter Hurley   tty: Fix unsafe v...
480
   *	Locking: takes buffer lock to ensure single-threaded flip buffer
e9975fdec   Peter Hurley   tty: Ensure singl...
481
   *		 'consumer'
e04957365   Alan Cox   tty: split the bu...
482
483
484
485
   */
  
  static void flush_to_ldisc(struct work_struct *work)
  {
ecbbfd44a   Jiri Slaby   TTY: move tty buf...
486
487
  	struct tty_port *port = container_of(work, struct tty_port, buf.work);
  	struct tty_bufhead *buf = &port->buf;
e04957365   Alan Cox   tty: split the bu...
488

a7c8d58c7   Peter Hurley   tty: Fix unsafe v...
489
  	mutex_lock(&buf->lock);
45242006e   Linus Torvalds   Make flush_to_ldi...
490

d7a68be4f   Peter Hurley   tty: Only perform...
491
492
  	while (1) {
  		struct tty_buffer *head = buf->head;
62a0d8d7c   Peter Hurley   tty: Fix lockless...
493
  		struct tty_buffer *next;
d7a68be4f   Peter Hurley   tty: Only perform...
494
  		int count;
a7c8d58c7   Peter Hurley   tty: Fix unsafe v...
495
496
  		/* Ldisc or user is trying to gain exclusive access */
  		if (atomic_read(&buf->priority))
d7a68be4f   Peter Hurley   tty: Only perform...
497
  			break;
069f38b49   Peter Hurley   tty: Replace smp_...
498
  		/* paired w/ release in __tty_buffer_request_room();
62a0d8d7c   Peter Hurley   tty: Fix lockless...
499
500
501
  		 * ensures commit value read is not stale if the head
  		 * is advancing to the next buffer
  		 */
069f38b49   Peter Hurley   tty: Replace smp_...
502
  		next = smp_load_acquire(&head->next);
facd885c7   Dmitry Vyukov   tty: fix data rac...
503
504
505
506
  		/* paired w/ release in __tty_buffer_request_room() or in
  		 * tty_buffer_flush(); ensures we see the committed buffer data
  		 */
  		count = smp_load_acquire(&head->commit) - head->read;
d7a68be4f   Peter Hurley   tty: Only perform...
507
  		if (!count) {
0f40fbbcc   Brian Bloniarz   Fix OpenSSH pty r...
508
  			if (next == NULL)
da261e7fe   Peter Hurley   tty: Simplify tty...
509
  				break;
62a0d8d7c   Peter Hurley   tty: Fix lockless...
510
  			buf->head = next;
d7a68be4f   Peter Hurley   tty: Only perform...
511
512
  			tty_buffer_free(port, head);
  			continue;
e04957365   Alan Cox   tty: split the bu...
513
  		}
d7a68be4f   Peter Hurley   tty: Only perform...
514

c3485ee0d   Rob Herring   tty_port: Add por...
515
  		count = receive_buf(port, head, count);
d7a68be4f   Peter Hurley   tty: Only perform...
516
517
  		if (!count)
  			break;
af5554f95   Peter Hurley   tty: buffers: Mov...
518
  		head->read += count;
e04957365   Alan Cox   tty: split the bu...
519
  	}
45242006e   Linus Torvalds   Make flush_to_ldi...
520

a7c8d58c7   Peter Hurley   tty: Fix unsafe v...
521
  	mutex_unlock(&buf->lock);
e04957365   Alan Cox   tty: split the bu...
522

e04957365   Alan Cox   tty: split the bu...
523
524
525
526
  }
  
  /**
   *	tty_flip_buffer_push	-	terminal
2e124b4a3   Jiri Slaby   TTY: switch tty_f...
527
   *	@port: tty port to push
e04957365   Alan Cox   tty: split the bu...
528
   *
a9c3f68f3   Peter Hurley   tty: Fix low_late...
529
530
   *	Queue a push of the terminal flip buffers to the line discipline.
   *	Can be called from IRQ/atomic context.
e04957365   Alan Cox   tty: split the bu...
531
532
533
   *
   *	In the event of the queue being busy for flipping the work will be
   *	held off and retried later.
e04957365   Alan Cox   tty: split the bu...
534
   */
2e124b4a3   Jiri Slaby   TTY: switch tty_f...
535
  void tty_flip_buffer_push(struct tty_port *port)
e04957365   Alan Cox   tty: split the bu...
536
  {
a9c3f68f3   Peter Hurley   tty: Fix low_late...
537
  	tty_schedule_flip(port);
e04957365   Alan Cox   tty: split the bu...
538
539
540
541
542
  }
  EXPORT_SYMBOL(tty_flip_buffer_push);
  
  /**
   *	tty_buffer_init		-	prepare a tty buffer structure
fa4419545   Jiri Slaby   tty: fix kernel-doc
543
   *	@port: tty port to initialise
e04957365   Alan Cox   tty: split the bu...
544
545
546
   *
   *	Set up the initial state of the buffer management for a tty device.
   *	Must be called before the other tty buffer functions are used.
e04957365   Alan Cox   tty: split the bu...
547
   */
ecbbfd44a   Jiri Slaby   TTY: move tty buf...
548
  void tty_buffer_init(struct tty_port *port)
e04957365   Alan Cox   tty: split the bu...
549
  {
ecbbfd44a   Jiri Slaby   TTY: move tty buf...
550
  	struct tty_bufhead *buf = &port->buf;
5cff39c69   Jiri Slaby   TTY: tty_buffer, ...
551

a7c8d58c7   Peter Hurley   tty: Fix unsafe v...
552
  	mutex_init(&buf->lock);
7391ee169   Peter Hurley   tty: Simplify fli...
553
554
555
  	tty_buffer_reset(&buf->sentinel, 0);
  	buf->head = &buf->sentinel;
  	buf->tail = &buf->sentinel;
809850b7a   Peter Hurley   tty: Use lockless...
556
  	init_llist_head(&buf->free);
5dda4ca55   Peter Hurley   tty: Rename tty b...
557
  	atomic_set(&buf->mem_used, 0);
a7c8d58c7   Peter Hurley   tty: Fix unsafe v...
558
  	atomic_set(&buf->priority, 0);
5cff39c69   Jiri Slaby   TTY: tty_buffer, ...
559
  	INIT_WORK(&buf->work, flush_to_ldisc);
4d18e6eff   Peter Hurley   tty: Enable confi...
560
  	buf->mem_limit = TTYB_DEFAULT_MEM_LIMIT;
e04957365   Alan Cox   tty: split the bu...
561
  }
4d18e6eff   Peter Hurley   tty: Enable confi...
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
  
  /**
   *	tty_buffer_set_limit	-	change the tty buffer memory limit
   *	@port: tty port to change
   *
   *	Change the tty buffer memory limit.
   *	Must be called before the other tty buffer functions are used.
   */
  
  int tty_buffer_set_limit(struct tty_port *port, int limit)
  {
  	if (limit < MIN_TTYB_SIZE)
  		return -EINVAL;
  	port->buf.mem_limit = limit;
  	return 0;
  }
  EXPORT_SYMBOL_GPL(tty_buffer_set_limit);
1d1d14da1   Peter Hurley   pty: Fix buffer f...
579
580
581
582
583
584
  
  /* slave ptys can claim nested buffer lock when handling BRK and INTR */
  void tty_buffer_set_lock_subclass(struct tty_port *port)
  {
  	lockdep_set_subclass(&port->buf.lock, TTY_LOCK_SLAVE);
  }
e176058f0   Peter Hurley   tty: Abstract tty...
585
586
587
588
589
590
591
592
593
594
  
  bool tty_buffer_restart_work(struct tty_port *port)
  {
  	return queue_work(system_unbound_wq, &port->buf.work);
  }
  
  bool tty_buffer_cancel_work(struct tty_port *port)
  {
  	return cancel_work_sync(&port->buf.work);
  }
0f40fbbcc   Brian Bloniarz   Fix OpenSSH pty r...
595
596
597
598
599
  
  void tty_buffer_flush_work(struct tty_port *port)
  {
  	flush_work(&port->buf.work);
  }