linux-rt-users.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: "Remy Bohmer" <linux@bohmer.net>
To: "Andrew Victor" <avictor.za@gmail.com>
Cc: "Steven Rostedt" <rostedt@goodmis.org>,
	"Andrew Victor" <linux@maxim.org.za>,
	RT <linux-rt-users@vger.kernel.org>,
	"Ingo Molnar" <mingo@elte.hu>,
	"Thomas Gleixner" <tglx@linutronix.de>
Subject: Re: [PATCH]: Atmel Serial Console interrupt handler splitup
Date: Thu, 13 Dec 2007 21:35:32 +0100	[thread overview]
Message-ID: <3efb10970712131235t47a9ceb5y7d9a7d5023d07bf6@mail.gmail.com> (raw)
In-Reply-To: <3efb10970712131232j5f747420g4aa8b5bd956e3dd3@mail.gmail.com>

[-- Attachment #1: Type: text/plain, Size: 174 bytes --]

> Because I know it is common practice in the kernel, I attached 3 new
> patches to inline these :-))

Grmbl, 1 wrong file attached. Here is the correct one.

Regards,

Remy

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: atmel_serial_irq_splitup_no_at91.patch --]
[-- Type: text/x-patch; name=atmel_serial_irq_splitup_no_at91.patch, Size: 7580 bytes --]

This patch splits up the interrupt handler of the serial port
into a interrupt top-half and some tasklets.

The goal is to get the interrupt top-half as short as possible to
minimize latencies on interrupts. But the old code also does some
calls in the interrupt handler that are not allowed on preempt-RT
in IRQF_NODELAY context. This handler is executed in this context
because of the interrupt sharing with the timer interrupt.
The timer interrupt on Preempt-RT runs in IRQF_NODELAY context.

2 tasklets are used:
* one for handling the error statuses
* one for pushing the incoming characters into the tty-layer.

The Transmit path was IRQF_NODELAY safe by itself, and is not adapted.
The read path for DMA(PDC) is also not adapted, because that code
does not run in IRQF_NODELAY context due to irq-sharing. The DBGU 
which is shared with the timer-irq does not work with DMA, so 
therefor this is no problem.

Reading the complete receive queue is still done in the top-half
because we never want to miss any incoming character.

This patch demands the following patches to be installed first:
* atmel_serial_cleanup.patch

Signed-off-by: Remy Bohmer <linux@bohmer.net>
---
 drivers/serial/atmel_serial.c |  150 +++++++++++++++++++++++++++++++++++++-----
 1 file changed, 133 insertions(+), 17 deletions(-)

Index: linux-2.6.23.1-rt5/drivers/serial/atmel_serial.c
===================================================================
--- linux-2.6.23.1-rt5.orig/drivers/serial/atmel_serial.c	2007-12-13 21:33:08.000000000 +0100
+++ linux-2.6.23.1-rt5/drivers/serial/atmel_serial.c	2007-12-13 21:33:10.000000000 +0100
@@ -111,6 +111,22 @@
 static int (*atmel_open_hook) (struct uart_port *);
 static void (*atmel_close_hook) (struct uart_port *);
 
+struct atmel_uart_char {
+	unsigned int status;
+	unsigned int overrun;
+	unsigned int ch;
+	unsigned int flg;
+};
+
+#define ATMEL_SERIAL_RINGSIZE 1024
+
+struct atmel_uart_ring {
+	unsigned int          head;
+	unsigned int          tail;
+	unsigned int          count;
+	struct atmel_uart_char data[ATMEL_SERIAL_RINGSIZE];
+};
+
 /*
  * We wrap our port structure around the generic uart_port.
  */
@@ -119,6 +135,13 @@ struct atmel_uart_port {
 	struct clk 	 	*clk;		/* uart clock */
 	unsigned short 		suspended;	/* is port suspended? */
 	int 			break_active;	/* break being received */
+
+	struct tasklet_struct   rx_task;
+	struct tasklet_struct   status_task;
+	unsigned int 		irq_pending;
+	unsigned int 		irq_status;
+
+	struct atmel_uart_ring  rx_ring;
 };
 
 static struct atmel_uart_port atmel_ports[ATMEL_MAX_UART];
@@ -249,12 +272,41 @@ static void atmel_break_ctl(struct uart_
 }
 
 /*
+ * Stores the incoming character in the ring buffer
+ */
+static void
+atmel_buffer_rx_char(struct uart_port *port, unsigned int status,
+		     unsigned int overrun,   unsigned int ch,
+		     unsigned int flg)
+{
+	struct atmel_uart_port *atmel_port = (struct atmel_uart_port *)port;
+	struct atmel_uart_ring *ring = &atmel_port->rx_ring;
+	struct atmel_uart_char *c;
+
+	spin_lock(&port->lock);
+
+	if (ring->count == ATMEL_SERIAL_RINGSIZE)
+		goto out; /* Buffer overflow, ignore char */
+
+	c = &ring->data[ring->head];
+	c->status  = status;
+	c->overrun = overrun;
+	c->ch      = ch;
+	c->flg     = flg;
+
+	ring->head++;
+	ring->head %= ATMEL_SERIAL_RINGSIZE;
+	ring->count++;
+out:
+	spin_unlock(&port->lock);
+}
+
+/*
  * Characters received (called from interrupt handler)
  */
 static void atmel_rx_chars(struct uart_port *port)
 {
 	struct atmel_uart_port *atmel_port = (struct atmel_uart_port *)port;
-	struct tty_struct *tty = port->info->tty;
 	unsigned int status, ch, flg;
 
 	status = UART_GET_CSR(port);
@@ -315,13 +367,13 @@ static void atmel_rx_chars(struct uart_p
 		if (uart_handle_sysrq_char(port, ch))
 			goto ignore_char;
 
-		uart_insert_char(port, status, ATMEL_US_OVRE, ch, flg);
+		atmel_buffer_rx_char(port, status, ATMEL_US_OVRE, ch, flg);
 
 ignore_char:
 		status = UART_GET_CSR(port);
 	}
 
-	tty_flip_buffer_push(tty);
+	tasklet_schedule(&atmel_port->rx_task);
 }
 
 /*
@@ -381,7 +433,7 @@ atmel_handle_receive(struct uart_port *p
 }
 
 /*
- * transmit interrupt handler.
+ * transmit interrupt handler. (Transmit is IRQF_NODELAY safe)
  */
 static inline void
 atmel_handle_transmit(struct uart_port *port, unsigned int pending)
@@ -398,19 +450,16 @@ static inline void
 atmel_handle_status(struct uart_port *port, unsigned int pending,
 		    unsigned int status)
 {
-	/* TODO: All reads to CSR will clear these interrupts! */
-	if (pending & ATMEL_US_RIIC)
-		port->icount.rng++;
-	if (pending & ATMEL_US_DSRIC)
-		port->icount.dsr++;
-	if (pending & ATMEL_US_DCDIC)
-		uart_handle_dcd_change(port, !(status & ATMEL_US_DCD));
-	if (pending & ATMEL_US_CTSIC)
-		uart_handle_cts_change(port, !(status & ATMEL_US_CTS));
-	if (pending &
-		(ATMEL_US_RIIC | ATMEL_US_DSRIC | ATMEL_US_DCDIC |
-		ATMEL_US_CTSIC))
-		wake_up_interruptible(&port->info->delta_msr_wait);
+	struct atmel_uart_port *atmel_port = (struct atmel_uart_port *)port;
+
+	spin_lock(&port->lock);
+
+	atmel_port->irq_pending = pending;
+	atmel_port->irq_status  = status;
+
+	spin_unlock(&port->lock);
+
+	tasklet_schedule(&atmel_port->status_task);
 }
 
 /*
@@ -438,6 +487,66 @@ static irqreturn_t atmel_interrupt(int i
 }
 
 /*
+ * tasklet handling tty stuff outside the interrupt handler.
+ */
+static void atmel_rx_handler_task(unsigned long data)
+{
+	struct uart_port *port = (struct uart_port *)data;
+	struct atmel_uart_port *atmel_port = (struct atmel_uart_port *)port;
+	struct atmel_uart_ring *ring = &atmel_port->rx_ring;
+	unsigned long flags;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	while (ring->count) {
+		struct atmel_uart_char c = ring->data[ring->tail];
+
+		ring->tail++;
+		ring->tail %= ATMEL_SERIAL_RINGSIZE;
+		ring->count--;
+
+		spin_unlock_irqrestore(&port->lock, flags);
+
+		uart_insert_char(port, c.status, c.overrun, c.ch, c.flg);
+
+		spin_lock_irqsave(&port->lock, flags);
+	}
+
+	spin_unlock_irqrestore(&port->lock, flags);
+
+	tty_flip_buffer_push(port->info->tty);
+}
+
+static void atmel_status_handler_task(unsigned long data)
+{
+	struct uart_port *port = (struct uart_port *)data;
+	struct atmel_uart_port *atmel_port = (struct atmel_uart_port *)port;
+	unsigned long flags;
+	unsigned int pending, status;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	pending = atmel_port->irq_pending;
+	status = atmel_port->irq_status;
+
+	spin_unlock_irqrestore(&port->lock, flags);
+
+	/* TODO: All reads to CSR will clear these interrupts! */
+	if (pending & ATMEL_US_RIIC)
+		port->icount.rng++;
+	if (pending & ATMEL_US_DSRIC)
+		port->icount.dsr++;
+	if (pending & ATMEL_US_DCDIC)
+		uart_handle_dcd_change(port, !(status & ATMEL_US_DCD));
+	if (pending & ATMEL_US_CTSIC)
+		uart_handle_cts_change(port, !(status & ATMEL_US_CTS));
+	if (pending &
+		(ATMEL_US_RIIC | ATMEL_US_DSRIC | ATMEL_US_DCDIC |
+		ATMEL_US_CTSIC))
+		wake_up_interruptible(&port->info->delta_msr_wait);
+}
+
+/*
  * Perform initialization and enable port for reception
  */
 static int atmel_startup(struct uart_port *port)
@@ -770,6 +879,13 @@ static void __devinit atmel_init_port(st
 	port->mapbase	= pdev->resource[0].start;
 	port->irq	= pdev->resource[1].start;
 
+	tasklet_init(&atmel_port->rx_task, atmel_rx_handler_task,
+			(unsigned long)port);
+	tasklet_init(&atmel_port->status_task, atmel_status_handler_task,
+			(unsigned long)port);
+
+	memset(&atmel_port->rx_ring, 0, sizeof(atmel_port->rx_ring));
+
 	if (data->regs)
 		/* Already mapped by setup code */
 		port->membase = data->regs;

  reply	other threads:[~2007-12-13 20:35 UTC|newest]

Thread overview: 20+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2007-12-07 15:24 [PATCH]: Atmel Serial Console interrupt handler splitup Remy Bohmer
2007-12-07 18:31 ` David Brownell
2007-12-07 19:57 ` Andrew Victor
2007-12-07 20:38   ` Remy Bohmer
2007-12-07 21:16     ` Remy Bohmer
2007-12-12 21:10 ` Steven Rostedt
2007-12-12 22:29   ` Remy Bohmer
2007-12-13 16:40     ` Remy Bohmer
2007-12-13 17:33       ` Andrew Victor
2007-12-13 20:32         ` Remy Bohmer
2007-12-13 20:35           ` Remy Bohmer [this message]
2007-12-14 11:46             ` Remy Bohmer
2007-12-17 12:17               ` Haavard Skinnemoen
2007-12-17 18:13                 ` Haavard Skinnemoen
2007-12-17 20:56                 ` Remy Bohmer
2007-12-17 23:12                   ` Haavard Skinnemoen
2007-12-18  7:32                     ` Remy Bohmer
2007-12-17 23:49                   ` Russell King - ARM Linux
2007-12-18  9:07                     ` Haavard Skinnemoen
     [not found] <AANLkTi=TyvXEtoZGatP5pxymLSna9g6ULV46i72bYgc4@mail.gmail.com>
2010-10-29 13:52 ` Remy Bohmer

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=3efb10970712131235t47a9ceb5y7d9a7d5023d07bf6@mail.gmail.com \
    --to=linux@bohmer.net \
    --cc=avictor.za@gmail.com \
    --cc=linux-rt-users@vger.kernel.org \
    --cc=linux@maxim.org.za \
    --cc=mingo@elte.hu \
    --cc=rostedt@goodmis.org \
    --cc=tglx@linutronix.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).