aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/serial/sh-sci.c
diff options
context:
space:
mode:
authorAlan Cox <alan@lxorguk.ukuu.org.uk>2006-01-09 23:54:13 -0500
committerLinus Torvalds <torvalds@g5.osdl.org>2006-01-10 11:01:59 -0500
commit33f0f88f1c51ae5c2d593d26960c760ea154c2e2 (patch)
treef53a38cf49406863f079d74d0e8f91b276f7c1a9 /drivers/serial/sh-sci.c
parent6ed80991a2dce4afc113be35089c564d62fa1f11 (diff)
[PATCH] TTY layer buffering revamp
The API and code have been through various bits of initial review by serial driver people but they definitely need to live somewhere for a while so the unconverted drivers can get knocked into shape, existing drivers that have been updated can be better tuned and bugs whacked out. This replaces the tty flip buffers with kmalloc objects in rings. In the normal situation for an IRQ driven serial port at typical speeds the behaviour is pretty much the same, two buffers end up allocated and the kernel cycles between them as before. When there are delays or at high speed we now behave far better as the buffer pool can grow a bit rather than lose characters. This also means that we can operate at higher speeds reliably. For drivers that receive characters in blocks (DMA based, USB and especially virtualisation) the layer allows a lot of driver specific code that works around the tty layer with private secondary queues to be removed. The IBM folks need this sort of layer, the smart serial port people do, the virtualisers do (because a virtualised tty typically operates at infinite speed rather than emulating 9600 baud). Finally many drivers had invalid and unsafe attempts to avoid buffer overflows by directly invoking tty methods extracted out of the innards of work queue structs. These are no longer needed and all go away. That fixes various random hangs with serial ports on overflow. The other change in here is to optimise the receive_room path that is used by some callers. It turns out that only one ldisc uses receive room except asa constant and it updates it far far less than the value is read. We thus make it a variable not a function call. I expect the code to contain bugs due to the size alone but I'll be watching and squashing them and feeding out new patches as it goes. Because the buffers now dynamically expand you should only run out of buffering when the kernel runs out of memory for real. That means a lot of the horrible hacks high performance drivers used to do just aren't needed any more. Description: tty_insert_flip_char is an old API and continues to work as before, as does tty_flip_buffer_push() [this is why many drivers dont need modification]. It does now also return the number of chars inserted There are also tty_buffer_request_room(tty, len) which asks for a buffer block of the length requested and returns the space found. This improves efficiency with hardware that knows how much to transfer. and tty_insert_flip_string_flags(tty, str, flags, len) to insert a string of characters and flags For a smart interface the usual code is len = tty_request_buffer_room(tty, amount_hardware_says); tty_insert_flip_string(tty, buffer_from_card, len); More description! At the moment tty buffers are attached directly to the tty. This is causing a lot of the problems related to tty layer locking, also problems at high speed and also with bursty data (such as occurs in virtualised environments) I'm working on ripping out the flip buffers and replacing them with a pool of dynamically allocated buffers. This allows both for old style "byte I/O" devices and also helps virtualisation and smart devices where large blocks of data suddenely materialise and need storing. So far so good. Lots of drivers reference tty->flip.*. Several of them also call directly and unsafely into function pointers it provides. This will all break. Most drivers can use tty_insert_flip_char which can be kept as an API but others need more. At the moment I've added the following interfaces, if people think more will be needed now is a good time to say int tty_buffer_request_room(tty, size) Try and ensure at least size bytes are available, returns actual room (may be zero). At the moment it just uses the flipbuf space but that will change. Repeated calls without characters being added are not cumulative. (ie if you call it with 1, 1, 1, and then 4 you'll have four characters of space. The other functions will also try and grow buffers in future but this will be a more efficient way when you know block sizes. int tty_insert_flip_char(tty, ch, flag) As before insert a character if there is room. Now returns 1 for success, 0 for failure. int tty_insert_flip_string(tty, str, len) Insert a block of non error characters. Returns the number inserted. int tty_prepare_flip_string(tty, strptr, len) Adjust the buffer to allow len characters to be added. Returns a buffer pointer in strptr and the length available. This allows for hardware that needs to use functions like insl or mencpy_fromio. Signed-off-by: Alan Cox <alan@redhat.com> Cc: Paul Fulghum <paulkf@microgate.com> Signed-off-by: Hirokazu Takata <takata@linux-m32r.org> Signed-off-by: Serge Hallyn <serue@us.ibm.com> Signed-off-by: Jeff Dike <jdike@addtoit.com> Signed-off-by: John Hawkes <hawkes@sgi.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com> Signed-off-by: Adrian Bunk <bunk@stusta.de> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'drivers/serial/sh-sci.c')
-rw-r--r--drivers/serial/sh-sci.c81
1 files changed, 32 insertions, 49 deletions
diff --git a/drivers/serial/sh-sci.c b/drivers/serial/sh-sci.c
index 430754ebac8a..a9e070759628 100644
--- a/drivers/serial/sh-sci.c
+++ b/drivers/serial/sh-sci.c
@@ -482,6 +482,7 @@ static inline void sci_receive_chars(struct uart_port *port,
482 struct tty_struct *tty = port->info->tty; 482 struct tty_struct *tty = port->info->tty;
483 int i, count, copied = 0; 483 int i, count, copied = 0;
484 unsigned short status; 484 unsigned short status;
485 unsigned char flag;
485 486
486 status = sci_in(port, SCxSR); 487 status = sci_in(port, SCxSR);
487 if (!(status & SCxSR_RDxF(port))) 488 if (!(status & SCxSR_RDxF(port)))
@@ -499,8 +500,7 @@ static inline void sci_receive_chars(struct uart_port *port,
499#endif 500#endif
500 501
501 /* Don't copy more bytes than there is room for in the buffer */ 502 /* Don't copy more bytes than there is room for in the buffer */
502 if (tty->flip.count + count > TTY_FLIPBUF_SIZE) 503 count = tty_buffer_request_room(tty, count);
503 count = TTY_FLIPBUF_SIZE - tty->flip.count;
504 504
505 /* If for any reason we can't copy more data, we're done! */ 505 /* If for any reason we can't copy more data, we're done! */
506 if (count == 0) 506 if (count == 0)
@@ -512,8 +512,7 @@ static inline void sci_receive_chars(struct uart_port *port,
512 || uart_handle_sysrq_char(port, c, regs)) { 512 || uart_handle_sysrq_char(port, c, regs)) {
513 count = 0; 513 count = 0;
514 } else { 514 } else {
515 tty->flip.char_buf_ptr[0] = c; 515 tty_insert_flip_char(tty, c, TTY_NORMAL);
516 tty->flip.flag_buf_ptr[0] = TTY_NORMAL;
517 } 516 }
518 } else { 517 } else {
519 for (i=0; i<count; i++) { 518 for (i=0; i<count; i++) {
@@ -542,26 +541,21 @@ static inline void sci_receive_chars(struct uart_port *port,
542 } 541 }
543 542
544 /* Store data and status */ 543 /* Store data and status */
545 tty->flip.char_buf_ptr[i] = c;
546 if (status&SCxSR_FER(port)) { 544 if (status&SCxSR_FER(port)) {
547 tty->flip.flag_buf_ptr[i] = TTY_FRAME; 545 flag = TTY_FRAME;
548 pr_debug("sci: frame error\n"); 546 pr_debug("sci: frame error\n");
549 } else if (status&SCxSR_PER(port)) { 547 } else if (status&SCxSR_PER(port)) {
550 tty->flip.flag_buf_ptr[i] = TTY_PARITY; 548 flag = TTY_PARITY;
551 pr_debug("sci: parity error\n"); 549 pr_debug("sci: parity error\n");
552 } else { 550 } else
553 tty->flip.flag_buf_ptr[i] = TTY_NORMAL; 551 flag = TTY_NORMAL;
554 } 552 tty_insert_flip_char(tty, c, flag);
555 } 553 }
556 } 554 }
557 555
558 sci_in(port, SCxSR); /* dummy read */ 556 sci_in(port, SCxSR); /* dummy read */
559 sci_out(port, SCxSR, SCxSR_RDxF_CLEAR(port)); 557 sci_out(port, SCxSR, SCxSR_RDxF_CLEAR(port));
560 558
561 /* Update the kernel buffer end */
562 tty->flip.count += count;
563 tty->flip.char_buf_ptr += count;
564 tty->flip.flag_buf_ptr += count;
565 copied += count; 559 copied += count;
566 port->icount.rx += count; 560 port->icount.rx += count;
567 } 561 }
@@ -608,48 +602,45 @@ static inline int sci_handle_errors(struct uart_port *port)
608 unsigned short status = sci_in(port, SCxSR); 602 unsigned short status = sci_in(port, SCxSR);
609 struct tty_struct *tty = port->info->tty; 603 struct tty_struct *tty = port->info->tty;
610 604
611 if (status&SCxSR_ORER(port) && tty->flip.count<TTY_FLIPBUF_SIZE) { 605 if (status&SCxSR_ORER(port)) {
612 /* overrun error */ 606 /* overrun error */
613 copied++; 607 if(tty_insert_flip_char(tty, 0, TTY_OVERRUN))
614 *tty->flip.flag_buf_ptr++ = TTY_OVERRUN; 608 copied++;
615 pr_debug("sci: overrun error\n"); 609 pr_debug("sci: overrun error\n");
616 } 610 }
617 611
618 if (status&SCxSR_FER(port) && tty->flip.count<TTY_FLIPBUF_SIZE) { 612 if (status&SCxSR_FER(port)) {
619 if (sci_rxd_in(port) == 0) { 613 if (sci_rxd_in(port) == 0) {
620 /* Notify of BREAK */ 614 /* Notify of BREAK */
621 struct sci_port * sci_port = (struct sci_port *)port; 615 struct sci_port * sci_port = (struct sci_port *)port;
622 if(!sci_port->break_flag) { 616 if(!sci_port->break_flag) {
623 sci_port->break_flag = 1; 617 sci_port->break_flag = 1;
624 sci_schedule_break_timer((struct sci_port *)port); 618 sci_schedule_break_timer((struct sci_port *)port);
625 /* Do sysrq handling. */ 619 /* Do sysrq handling. */
626 if(uart_handle_break(port)) { 620 if(uart_handle_break(port))
627 return 0; 621 return 0;
628 }
629 pr_debug("sci: BREAK detected\n"); 622 pr_debug("sci: BREAK detected\n");
630 copied++; 623 if(tty_insert_flip_char(tty, 0, TTY_BREAK))
631 *tty->flip.flag_buf_ptr++ = TTY_BREAK; 624 copied++;
632 } 625 }
633 } 626 }
634 else { 627 else {
635 /* frame error */ 628 /* frame error */
636 copied++; 629 if(tty_insert_flip_char(tty, 0, TTY_FRAME))
637 *tty->flip.flag_buf_ptr++ = TTY_FRAME; 630 copied++;
638 pr_debug("sci: frame error\n"); 631 pr_debug("sci: frame error\n");
639 } 632 }
640 } 633 }
641 634
642 if (status&SCxSR_PER(port) && tty->flip.count<TTY_FLIPBUF_SIZE) { 635 if (status&SCxSR_PER(port)) {
636 if(tty_insert_flip_char(tty, 0, TTY_PARITY))
637 copied++;
643 /* parity error */ 638 /* parity error */
644 copied++;
645 *tty->flip.flag_buf_ptr++ = TTY_PARITY;
646 pr_debug("sci: parity error\n"); 639 pr_debug("sci: parity error\n");
647 } 640 }
648 641
649 if (copied) { 642 if (copied)
650 tty->flip.count += copied;
651 tty_flip_buffer_push(tty); 643 tty_flip_buffer_push(tty);
652 }
653 644
654 return copied; 645 return copied;
655} 646}
@@ -661,15 +652,14 @@ static inline int sci_handle_breaks(struct uart_port *port)
661 struct tty_struct *tty = port->info->tty; 652 struct tty_struct *tty = port->info->tty;
662 struct sci_port *s = &sci_ports[port->line]; 653 struct sci_port *s = &sci_ports[port->line];
663 654
664 if (!s->break_flag && status & SCxSR_BRK(port) && 655 if (!s->break_flag && status & SCxSR_BRK(port))
665 tty->flip.count < TTY_FLIPBUF_SIZE) {
666#if defined(CONFIG_CPU_SH3) 656#if defined(CONFIG_CPU_SH3)
667 /* Debounce break */ 657 /* Debounce break */
668 s->break_flag = 1; 658 s->break_flag = 1;
669#endif 659#endif
670 /* Notify of BREAK */ 660 /* Notify of BREAK */
671 copied++; 661 if(tty_insert_flip_char(tty, 0, TTY_BREAK))
672 *tty->flip.flag_buf_ptr++ = TTY_BREAK; 662 copied++;
673 pr_debug("sci: BREAK detected\n"); 663 pr_debug("sci: BREAK detected\n");
674 } 664 }
675 665
@@ -677,19 +667,15 @@ static inline int sci_handle_breaks(struct uart_port *port)
677 /* XXX: Handle SCIF overrun error */ 667 /* XXX: Handle SCIF overrun error */
678 if (port->type == PORT_SCIF && (sci_in(port, SCLSR) & SCIF_ORER) != 0) { 668 if (port->type == PORT_SCIF && (sci_in(port, SCLSR) & SCIF_ORER) != 0) {
679 sci_out(port, SCLSR, 0); 669 sci_out(port, SCLSR, 0);
680 if(tty->flip.count<TTY_FLIPBUF_SIZE) { 670 if(tty_insert_flip_char(tty, 0, TTY_OVERRUN)) {
681 copied++; 671 copied++;
682 *tty->flip.flag_buf_ptr++ = TTY_OVERRUN;
683 pr_debug("sci: overrun error\n"); 672 pr_debug("sci: overrun error\n");
684 } 673 }
685 } 674 }
686#endif 675#endif
687 676
688 if (copied) { 677 if (copied)
689 tty->flip.count += copied;
690 tty_flip_buffer_push(tty); 678 tty_flip_buffer_push(tty);
691 }
692
693 return copied; 679 return copied;
694} 680}
695 681
@@ -732,12 +718,9 @@ static irqreturn_t sci_er_interrupt(int irq, void *ptr, struct pt_regs *regs)
732 struct tty_struct *tty = port->info->tty; 718 struct tty_struct *tty = port->info->tty;
733 719
734 sci_out(port, SCLSR, 0); 720 sci_out(port, SCLSR, 0);
735 if(tty->flip.count<TTY_FLIPBUF_SIZE) { 721 tty_insert_flip_char(tty, 0, TTY_OVERRUN);
736 *tty->flip.flag_buf_ptr++ = TTY_OVERRUN; 722 tty_flip_buffer_push(tty);
737 tty->flip.count++; 723 pr_debug("scif: overrun error\n");
738 tty_flip_buffer_push(tty);
739 pr_debug("scif: overrun error\n");
740 }
741 } 724 }
742#endif 725#endif
743 sci_rx_interrupt(irq, ptr, regs); 726 sci_rx_interrupt(irq, ptr, regs);