diff options
Diffstat (limited to 'drivers/char/tty_io.c')
-rw-r--r-- | drivers/char/tty_io.c | 266 |
1 files changed, 234 insertions, 32 deletions
diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 4b1eef51ec59..1eda82b31a61 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c | |||
@@ -166,9 +166,12 @@ static struct tty_struct *alloc_tty_struct(void) | |||
166 | return tty; | 166 | return tty; |
167 | } | 167 | } |
168 | 168 | ||
169 | static void tty_buffer_free_all(struct tty_struct *); | ||
170 | |||
169 | static inline void free_tty_struct(struct tty_struct *tty) | 171 | static inline void free_tty_struct(struct tty_struct *tty) |
170 | { | 172 | { |
171 | kfree(tty->write_buf); | 173 | kfree(tty->write_buf); |
174 | tty_buffer_free_all(tty); | ||
172 | kfree(tty); | 175 | kfree(tty); |
173 | } | 176 | } |
174 | 177 | ||
@@ -231,6 +234,201 @@ static int check_tty_count(struct tty_struct *tty, const char *routine) | |||
231 | } | 234 | } |
232 | 235 | ||
233 | /* | 236 | /* |
237 | * Tty buffer allocation management | ||
238 | */ | ||
239 | |||
240 | static void tty_buffer_free_all(struct tty_struct *tty) | ||
241 | { | ||
242 | struct tty_buffer *thead; | ||
243 | while((thead = tty->buf.head) != NULL) { | ||
244 | tty->buf.head = thead->next; | ||
245 | kfree(thead); | ||
246 | } | ||
247 | while((thead = tty->buf.free) != NULL) { | ||
248 | tty->buf.free = thead->next; | ||
249 | kfree(thead); | ||
250 | } | ||
251 | tty->buf.tail = NULL; | ||
252 | } | ||
253 | |||
254 | static void tty_buffer_init(struct tty_struct *tty) | ||
255 | { | ||
256 | tty->buf.head = NULL; | ||
257 | tty->buf.tail = NULL; | ||
258 | tty->buf.free = NULL; | ||
259 | } | ||
260 | |||
261 | static struct tty_buffer *tty_buffer_alloc(size_t size) | ||
262 | { | ||
263 | struct tty_buffer *p = kmalloc(sizeof(struct tty_buffer) + 2 * size, GFP_ATOMIC); | ||
264 | if(p == NULL) | ||
265 | return NULL; | ||
266 | p->used = 0; | ||
267 | p->size = size; | ||
268 | p->next = NULL; | ||
269 | p->char_buf_ptr = (char *)(p->data); | ||
270 | p->flag_buf_ptr = (unsigned char *)p->char_buf_ptr + size; | ||
271 | /* printk("Flip create %p\n", p); */ | ||
272 | return p; | ||
273 | } | ||
274 | |||
275 | /* Must be called with the tty_read lock held. This needs to acquire strategy | ||
276 | code to decide if we should kfree or relink a given expired buffer */ | ||
277 | |||
278 | static void tty_buffer_free(struct tty_struct *tty, struct tty_buffer *b) | ||
279 | { | ||
280 | /* Dumb strategy for now - should keep some stats */ | ||
281 | /* printk("Flip dispose %p\n", b); */ | ||
282 | if(b->size >= 512) | ||
283 | kfree(b); | ||
284 | else { | ||
285 | b->next = tty->buf.free; | ||
286 | tty->buf.free = b; | ||
287 | } | ||
288 | } | ||
289 | |||
290 | static struct tty_buffer *tty_buffer_find(struct tty_struct *tty, size_t size) | ||
291 | { | ||
292 | struct tty_buffer **tbh = &tty->buf.free; | ||
293 | while((*tbh) != NULL) { | ||
294 | struct tty_buffer *t = *tbh; | ||
295 | if(t->size >= size) { | ||
296 | *tbh = t->next; | ||
297 | t->next = NULL; | ||
298 | t->used = 0; | ||
299 | /* DEBUG ONLY */ | ||
300 | memset(t->data, '*', size); | ||
301 | /* printk("Flip recycle %p\n", t); */ | ||
302 | return t; | ||
303 | } | ||
304 | tbh = &((*tbh)->next); | ||
305 | } | ||
306 | /* Round the buffer size out */ | ||
307 | size = (size + 0xFF) & ~ 0xFF; | ||
308 | return tty_buffer_alloc(size); | ||
309 | /* Should possibly check if this fails for the largest buffer we | ||
310 | have queued and recycle that ? */ | ||
311 | } | ||
312 | |||
313 | int tty_buffer_request_room(struct tty_struct *tty, size_t size) | ||
314 | { | ||
315 | struct tty_buffer *b = tty->buf.head, *n; | ||
316 | int left = 0; | ||
317 | |||
318 | /* OPTIMISATION: We could keep a per tty "zero" sized buffer to | ||
319 | remove this conditional if its worth it. This would be invisible | ||
320 | to the callers */ | ||
321 | if(b != NULL) | ||
322 | left = b->size - b->used; | ||
323 | if(left >= size) | ||
324 | return size; | ||
325 | /* This is the slow path - looking for new buffers to use */ | ||
326 | n = tty_buffer_find(tty, size); | ||
327 | if(n == NULL) | ||
328 | return left; | ||
329 | n->next = b; | ||
330 | if(b != NULL) | ||
331 | b->next = n; | ||
332 | else | ||
333 | tty->buf.head = n; | ||
334 | tty->buf.tail = n; | ||
335 | return size; | ||
336 | } | ||
337 | |||
338 | EXPORT_SYMBOL_GPL(tty_buffer_request_room); | ||
339 | |||
340 | int tty_insert_flip_string(struct tty_struct *tty, unsigned char *chars, size_t size) | ||
341 | { | ||
342 | int copied = 0; | ||
343 | do { | ||
344 | int space = tty_buffer_request_room(tty, size - copied); | ||
345 | struct tty_buffer *tb = tty->buf.tail; | ||
346 | /* If there is no space then tb may be NULL */ | ||
347 | if(unlikely(space == 0)) | ||
348 | break; | ||
349 | memcpy(tb->char_buf_ptr + tb->used, chars, space); | ||
350 | memset(tb->flag_buf_ptr + tb->used, TTY_NORMAL, space); | ||
351 | tb->used += space; | ||
352 | copied += space; | ||
353 | chars += space; | ||
354 | /* printk("Flip insert %d.\n", space); */ | ||
355 | } | ||
356 | /* There is a small chance that we need to split the data over | ||
357 | several buffers. If this is the case we must loop */ | ||
358 | while (unlikely(size > copied)); | ||
359 | return copied; | ||
360 | } | ||
361 | |||
362 | EXPORT_SYMBOL_GPL(tty_insert_flip_string); | ||
363 | |||
364 | int tty_insert_flip_string_flags(struct tty_struct *tty, unsigned char *chars, char *flags, size_t size) | ||
365 | { | ||
366 | int copied = 0; | ||
367 | do { | ||
368 | int space = tty_buffer_request_room(tty, size - copied); | ||
369 | struct tty_buffer *tb = tty->buf.tail; | ||
370 | /* If there is no space then tb may be NULL */ | ||
371 | if(unlikely(space == 0)) | ||
372 | break; | ||
373 | memcpy(tb->char_buf_ptr + tb->used, chars, space); | ||
374 | memcpy(tb->flag_buf_ptr + tb->used, flags, space); | ||
375 | tb->used += space; | ||
376 | copied += space; | ||
377 | chars += space; | ||
378 | flags += space; | ||
379 | } | ||
380 | /* There is a small chance that we need to split the data over | ||
381 | several buffers. If this is the case we must loop */ | ||
382 | while (unlikely(size > copied)); | ||
383 | return copied; | ||
384 | } | ||
385 | |||
386 | EXPORT_SYMBOL_GPL(tty_insert_flip_string_flags); | ||
387 | |||
388 | |||
389 | /* | ||
390 | * Prepare a block of space in the buffer for data. Returns the length | ||
391 | * available and buffer pointer to the space which is now allocated and | ||
392 | * accounted for as ready for normal characters. This is used for drivers | ||
393 | * that need their own block copy routines into the buffer. There is no | ||
394 | * guarantee the buffer is a DMA target! | ||
395 | */ | ||
396 | |||
397 | int tty_prepare_flip_string(struct tty_struct *tty, unsigned char **chars, size_t size) | ||
398 | { | ||
399 | int space = tty_buffer_request_room(tty, size); | ||
400 | struct tty_buffer *tb = tty->buf.tail; | ||
401 | *chars = tb->char_buf_ptr + tb->used; | ||
402 | memset(tb->flag_buf_ptr + tb->used, TTY_NORMAL, space); | ||
403 | tb->used += space; | ||
404 | return space; | ||
405 | } | ||
406 | |||
407 | EXPORT_SYMBOL_GPL(tty_prepare_flip_string); | ||
408 | |||
409 | /* | ||
410 | * Prepare a block of space in the buffer for data. Returns the length | ||
411 | * available and buffer pointer to the space which is now allocated and | ||
412 | * accounted for as ready for characters. This is used for drivers | ||
413 | * that need their own block copy routines into the buffer. There is no | ||
414 | * guarantee the buffer is a DMA target! | ||
415 | */ | ||
416 | |||
417 | int tty_prepare_flip_string_flags(struct tty_struct *tty, unsigned char **chars, char **flags, size_t size) | ||
418 | { | ||
419 | int space = tty_buffer_request_room(tty, size); | ||
420 | struct tty_buffer *tb = tty->buf.tail; | ||
421 | *chars = tb->char_buf_ptr + tb->used; | ||
422 | *flags = tb->flag_buf_ptr + tb->used; | ||
423 | tb->used += space; | ||
424 | return space; | ||
425 | } | ||
426 | |||
427 | EXPORT_SYMBOL_GPL(tty_prepare_flip_string_flags); | ||
428 | |||
429 | |||
430 | |||
431 | /* | ||
234 | * This is probably overkill for real world processors but | 432 | * This is probably overkill for real world processors but |
235 | * they are not on hot paths so a little discipline won't do | 433 | * they are not on hot paths so a little discipline won't do |
236 | * any harm. | 434 | * any harm. |
@@ -492,6 +690,17 @@ restart: | |||
492 | if (ld == NULL) | 690 | if (ld == NULL) |
493 | return -EINVAL; | 691 | return -EINVAL; |
494 | 692 | ||
693 | /* | ||
694 | * No more input please, we are switching. The new ldisc | ||
695 | * will update this value in the ldisc open function | ||
696 | */ | ||
697 | |||
698 | tty->receive_room = 0; | ||
699 | |||
700 | /* | ||
701 | * Problem: What do we do if this blocks ? | ||
702 | */ | ||
703 | |||
495 | tty_wait_until_sent(tty, 0); | 704 | tty_wait_until_sent(tty, 0); |
496 | 705 | ||
497 | if (tty->ldisc.num == ldisc) { | 706 | if (tty->ldisc.num == ldisc) { |
@@ -560,9 +769,9 @@ restart: | |||
560 | * we say so later on. | 769 | * we say so later on. |
561 | */ | 770 | */ |
562 | 771 | ||
563 | work = cancel_delayed_work(&tty->flip.work); | 772 | work = cancel_delayed_work(&tty->buf.work); |
564 | /* | 773 | /* |
565 | * Wait for ->hangup_work and ->flip.work handlers to terminate | 774 | * Wait for ->hangup_work and ->buf.work handlers to terminate |
566 | */ | 775 | */ |
567 | 776 | ||
568 | flush_scheduled_work(); | 777 | flush_scheduled_work(); |
@@ -616,7 +825,7 @@ restart: | |||
616 | /* Restart it in case no characters kick it off. Safe if | 825 | /* Restart it in case no characters kick it off. Safe if |
617 | already running */ | 826 | already running */ |
618 | if (work) | 827 | if (work) |
619 | schedule_delayed_work(&tty->flip.work, 1); | 828 | schedule_delayed_work(&tty->buf.work, 1); |
620 | return retval; | 829 | return retval; |
621 | } | 830 | } |
622 | 831 | ||
@@ -1721,10 +1930,10 @@ static void release_dev(struct file * filp) | |||
1721 | */ | 1930 | */ |
1722 | clear_bit(TTY_LDISC, &tty->flags); | 1931 | clear_bit(TTY_LDISC, &tty->flags); |
1723 | clear_bit(TTY_DONT_FLIP, &tty->flags); | 1932 | clear_bit(TTY_DONT_FLIP, &tty->flags); |
1724 | cancel_delayed_work(&tty->flip.work); | 1933 | cancel_delayed_work(&tty->buf.work); |
1725 | 1934 | ||
1726 | /* | 1935 | /* |
1727 | * Wait for ->hangup_work and ->flip.work handlers to terminate | 1936 | * Wait for ->hangup_work and ->buf.work handlers to terminate |
1728 | */ | 1937 | */ |
1729 | 1938 | ||
1730 | flush_scheduled_work(); | 1939 | flush_scheduled_work(); |
@@ -2518,17 +2727,15 @@ EXPORT_SYMBOL(do_SAK); | |||
2518 | 2727 | ||
2519 | /* | 2728 | /* |
2520 | * This routine is called out of the software interrupt to flush data | 2729 | * This routine is called out of the software interrupt to flush data |
2521 | * from the flip buffer to the line discipline. | 2730 | * from the buffer chain to the line discipline. |
2522 | */ | 2731 | */ |
2523 | 2732 | ||
2524 | static void flush_to_ldisc(void *private_) | 2733 | static void flush_to_ldisc(void *private_) |
2525 | { | 2734 | { |
2526 | struct tty_struct *tty = (struct tty_struct *) private_; | 2735 | struct tty_struct *tty = (struct tty_struct *) private_; |
2527 | unsigned char *cp; | ||
2528 | char *fp; | ||
2529 | int count; | ||
2530 | unsigned long flags; | 2736 | unsigned long flags; |
2531 | struct tty_ldisc *disc; | 2737 | struct tty_ldisc *disc; |
2738 | struct tty_buffer *tbuf; | ||
2532 | 2739 | ||
2533 | disc = tty_ldisc_ref(tty); | 2740 | disc = tty_ldisc_ref(tty); |
2534 | if (disc == NULL) /* !TTY_LDISC */ | 2741 | if (disc == NULL) /* !TTY_LDISC */ |
@@ -2538,28 +2745,22 @@ static void flush_to_ldisc(void *private_) | |||
2538 | /* | 2745 | /* |
2539 | * Do it after the next timer tick: | 2746 | * Do it after the next timer tick: |
2540 | */ | 2747 | */ |
2541 | schedule_delayed_work(&tty->flip.work, 1); | 2748 | schedule_delayed_work(&tty->buf.work, 1); |
2542 | goto out; | 2749 | goto out; |
2543 | } | 2750 | } |
2544 | spin_lock_irqsave(&tty->read_lock, flags); | 2751 | spin_lock_irqsave(&tty->read_lock, flags); |
2545 | if (tty->flip.buf_num) { | 2752 | while((tbuf = tty->buf.head) != NULL) { |
2546 | cp = tty->flip.char_buf + TTY_FLIPBUF_SIZE; | 2753 | tty->buf.head = tbuf->next; |
2547 | fp = tty->flip.flag_buf + TTY_FLIPBUF_SIZE; | 2754 | spin_unlock_irqrestore(&tty->read_lock, flags); |
2548 | tty->flip.buf_num = 0; | 2755 | /* printk("Process buffer %p for %d\n", tbuf, tbuf->used); */ |
2549 | tty->flip.char_buf_ptr = tty->flip.char_buf; | 2756 | disc->receive_buf(tty, tbuf->char_buf_ptr, |
2550 | tty->flip.flag_buf_ptr = tty->flip.flag_buf; | 2757 | tbuf->flag_buf_ptr, |
2551 | } else { | 2758 | tbuf->used); |
2552 | cp = tty->flip.char_buf; | 2759 | spin_lock_irqsave(&tty->read_lock, flags); |
2553 | fp = tty->flip.flag_buf; | 2760 | tty_buffer_free(tty, tbuf); |
2554 | tty->flip.buf_num = 1; | 2761 | } |
2555 | tty->flip.char_buf_ptr = tty->flip.char_buf + TTY_FLIPBUF_SIZE; | 2762 | tty->buf.tail = NULL; |
2556 | tty->flip.flag_buf_ptr = tty->flip.flag_buf + TTY_FLIPBUF_SIZE; | ||
2557 | } | ||
2558 | count = tty->flip.count; | ||
2559 | tty->flip.count = 0; | ||
2560 | spin_unlock_irqrestore(&tty->read_lock, flags); | 2763 | spin_unlock_irqrestore(&tty->read_lock, flags); |
2561 | |||
2562 | disc->receive_buf(tty, cp, fp, count); | ||
2563 | out: | 2764 | out: |
2564 | tty_ldisc_deref(disc); | 2765 | tty_ldisc_deref(disc); |
2565 | } | 2766 | } |
@@ -2654,11 +2855,12 @@ void tty_flip_buffer_push(struct tty_struct *tty) | |||
2654 | if (tty->low_latency) | 2855 | if (tty->low_latency) |
2655 | flush_to_ldisc((void *) tty); | 2856 | flush_to_ldisc((void *) tty); |
2656 | else | 2857 | else |
2657 | schedule_delayed_work(&tty->flip.work, 1); | 2858 | schedule_delayed_work(&tty->buf.work, 1); |
2658 | } | 2859 | } |
2659 | 2860 | ||
2660 | EXPORT_SYMBOL(tty_flip_buffer_push); | 2861 | EXPORT_SYMBOL(tty_flip_buffer_push); |
2661 | 2862 | ||
2863 | |||
2662 | /* | 2864 | /* |
2663 | * This subroutine initializes a tty structure. | 2865 | * This subroutine initializes a tty structure. |
2664 | */ | 2866 | */ |
@@ -2669,10 +2871,10 @@ static void initialize_tty_struct(struct tty_struct *tty) | |||
2669 | tty_ldisc_assign(tty, tty_ldisc_get(N_TTY)); | 2871 | tty_ldisc_assign(tty, tty_ldisc_get(N_TTY)); |
2670 | tty->pgrp = -1; | 2872 | tty->pgrp = -1; |
2671 | tty->overrun_time = jiffies; | 2873 | tty->overrun_time = jiffies; |
2672 | tty->flip.char_buf_ptr = tty->flip.char_buf; | 2874 | tty->buf.head = tty->buf.tail = NULL; |
2673 | tty->flip.flag_buf_ptr = tty->flip.flag_buf; | 2875 | tty_buffer_init(tty); |
2674 | INIT_WORK(&tty->flip.work, flush_to_ldisc, tty); | 2876 | INIT_WORK(&tty->buf.work, flush_to_ldisc, tty); |
2675 | init_MUTEX(&tty->flip.pty_sem); | 2877 | init_MUTEX(&tty->buf.pty_sem); |
2676 | init_MUTEX(&tty->termios_sem); | 2878 | init_MUTEX(&tty->termios_sem); |
2677 | init_waitqueue_head(&tty->write_wait); | 2879 | init_waitqueue_head(&tty->write_wait); |
2678 | init_waitqueue_head(&tty->read_wait); | 2880 | init_waitqueue_head(&tty->read_wait); |