ia64/xen-unstable

changeset 9437:6aa5179f2416

Reduce spin-waiting in Xen serial driver:
1. Split the serial port lock into receiver and transmitter locks.
2. In the ns16550 interrupt, only call the generic serial service
routines if there is receive (or transmit) work to do.
3. In the generic transmit ISR, avoid long spin-waits by *trying*
to take the transmitter lock and, if that fails, check again
whether the transmitter is empty. This will allow us to bail
bail quickly if there is a long-term lock holder stuffing lots
of bytes.

Also, gdbstub should be setting its serial handle in synchronous mode,
just for sanity.

Signed-off-by: Keir Fraser <keir@xensource.com>
author kaf24@firebug.cl.cam.ac.uk
date Thu Mar 23 17:17:08 2006 +0100 (2006-03-23)
parents c947b278a349
children bddcfe70fbef
files xen/common/gdbstub.c xen/drivers/char/ns16550.c xen/drivers/char/serial.c xen/include/xen/serial.h
line diff
     1.1 --- a/xen/common/gdbstub.c	Thu Mar 23 15:53:52 2006 +0100
     1.2 +++ b/xen/common/gdbstub.c	Thu Mar 23 17:17:08 2006 +0100
     1.3 @@ -562,6 +562,7 @@ initialise_gdb(void)
     1.4      gdb_ctx->serhnd = serial_parse_handle(opt_gdb);
     1.5      if ( gdb_ctx->serhnd != -1 )
     1.6          printk("GDB stub initialised.\n");
     1.7 +    serial_start_sync(gdb_ctx->serhnd);
     1.8  }
     1.9  
    1.10  /*
     2.1 --- a/xen/drivers/char/ns16550.c	Thu Mar 23 15:53:52 2006 +0100
     2.2 +++ b/xen/drivers/char/ns16550.c	Thu Mar 23 17:17:08 2006 +0100
     2.3 @@ -121,8 +121,11 @@ static void ns16550_interrupt(
     2.4  
     2.5      while ( !(ns_read_reg(uart, IIR) & IIR_NOINT) )
     2.6      {
     2.7 -        serial_tx_interrupt(port, regs);
     2.8 -        serial_rx_interrupt(port, regs);
     2.9 +        char lsr = ns_read_reg(uart, LSR);
    2.10 +        if ( lsr & LSR_THRE )
    2.11 +            serial_tx_interrupt(port, regs);
    2.12 +        if ( lsr & LSR_DR )
    2.13 +            serial_rx_interrupt(port, regs);
    2.14      }
    2.15  }
    2.16  
     3.1 --- a/xen/drivers/char/serial.c	Thu Mar 23 15:53:52 2006 +0100
     3.2 +++ b/xen/drivers/char/serial.c	Thu Mar 23 17:17:08 2006 +0100
     3.3 @@ -7,6 +7,7 @@
     3.4   */
     3.5  
     3.6  #include <xen/config.h>
     3.7 +#include <xen/delay.h>
     3.8  #include <xen/init.h>
     3.9  #include <xen/irq.h>
    3.10  #include <xen/keyhandler.h> 
    3.11 @@ -15,8 +16,8 @@
    3.12  #include <xen/serial.h>
    3.13  
    3.14  static struct serial_port com[2] = {
    3.15 -    { .lock = SPIN_LOCK_UNLOCKED }, 
    3.16 -    { .lock = SPIN_LOCK_UNLOCKED }
    3.17 +    { .rx_lock = SPIN_LOCK_UNLOCKED, .tx_lock = SPIN_LOCK_UNLOCKED }, 
    3.18 +    { .rx_lock = SPIN_LOCK_UNLOCKED, .tx_lock = SPIN_LOCK_UNLOCKED }
    3.19  };
    3.20  
    3.21  void serial_rx_interrupt(struct serial_port *port, struct cpu_user_regs *regs)
    3.22 @@ -25,7 +26,7 @@ void serial_rx_interrupt(struct serial_p
    3.23      serial_rx_fn fn = NULL;
    3.24      unsigned long flags;
    3.25  
    3.26 -    spin_lock_irqsave(&port->lock, flags);
    3.27 +    spin_lock_irqsave(&port->rx_lock, flags);
    3.28  
    3.29      if ( port->driver->getc(port, &c) )
    3.30      {
    3.31 @@ -39,7 +40,7 @@ void serial_rx_interrupt(struct serial_p
    3.32              port->rxbuf[MASK_SERIAL_RXBUF_IDX(port->rxbufp++)] = c;            
    3.33      }
    3.34  
    3.35 -    spin_unlock_irqrestore(&port->lock, flags);
    3.36 +    spin_unlock_irqrestore(&port->rx_lock, flags);
    3.37  
    3.38      if ( fn != NULL )
    3.39          (*fn)(c & 0x7f, regs);
    3.40 @@ -50,7 +51,19 @@ void serial_tx_interrupt(struct serial_p
    3.41      int i;
    3.42      unsigned long flags;
    3.43  
    3.44 -    spin_lock_irqsave(&port->lock, flags);
    3.45 +    local_irq_save(flags);
    3.46 +
    3.47 +    /*
    3.48 +     * Avoid spinning for a long time: if there is a long-term lock holder
    3.49 +     * then we know that they'll be stuffing bytes into the transmitter which
    3.50 +     * will therefore not be empty for long.
    3.51 +     */
    3.52 +    while ( !spin_trylock(&port->tx_lock) )
    3.53 +    {
    3.54 +        if ( !port->driver->tx_empty(port) )
    3.55 +            return;
    3.56 +        cpu_relax();
    3.57 +    }
    3.58  
    3.59      if ( port->driver->tx_empty(port) )
    3.60      {
    3.61 @@ -63,7 +76,7 @@ void serial_tx_interrupt(struct serial_p
    3.62          }
    3.63      }
    3.64  
    3.65 -    spin_unlock_irqrestore(&port->lock, flags);
    3.66 +    spin_unlock_irqrestore(&port->tx_lock, flags);
    3.67  }
    3.68  
    3.69  static void __serial_putc(struct serial_port *port, char c)
    3.70 @@ -117,7 +130,7 @@ void serial_putc(int handle, char c)
    3.71      if ( (handle == -1) || !port->driver || !port->driver->putc )
    3.72          return;
    3.73  
    3.74 -    spin_lock_irqsave(&port->lock, flags);
    3.75 +    spin_lock_irqsave(&port->tx_lock, flags);
    3.76  
    3.77      if ( (c == '\n') && (handle & SERHND_COOKED) )
    3.78          __serial_putc(port, '\r');
    3.79 @@ -129,7 +142,7 @@ void serial_putc(int handle, char c)
    3.80  
    3.81      __serial_putc(port, c);
    3.82  
    3.83 -    spin_unlock_irqrestore(&port->lock, flags);
    3.84 +    spin_unlock_irqrestore(&port->tx_lock, flags);
    3.85  }
    3.86  
    3.87  void serial_puts(int handle, const char *s)
    3.88 @@ -141,7 +154,7 @@ void serial_puts(int handle, const char 
    3.89      if ( (handle == -1) || !port->driver || !port->driver->putc )
    3.90          return;
    3.91  
    3.92 -    spin_lock_irqsave(&port->lock, flags);
    3.93 +    spin_lock_irqsave(&port->tx_lock, flags);
    3.94  
    3.95      while ( (c = *s++) != '\0' )
    3.96      {
    3.97 @@ -156,7 +169,7 @@ void serial_puts(int handle, const char 
    3.98          __serial_putc(port, c);
    3.99      }
   3.100  
   3.101 -    spin_unlock_irqrestore(&port->lock, flags);
   3.102 +    spin_unlock_irqrestore(&port->tx_lock, flags);
   3.103  }
   3.104  
   3.105  char serial_getc(int handle)
   3.106 @@ -168,27 +181,28 @@ char serial_getc(int handle)
   3.107      if ( (handle == -1) || !port->driver || !port->driver->getc )
   3.108          return '\0';
   3.109  
   3.110 -    do {        
   3.111 +    do {
   3.112          for ( ; ; )
   3.113          {
   3.114 -            spin_lock_irqsave(&port->lock, flags);
   3.115 +            spin_lock_irqsave(&port->rx_lock, flags);
   3.116              
   3.117              if ( port->rxbufp != port->rxbufc )
   3.118              {
   3.119                  c = port->rxbuf[MASK_SERIAL_RXBUF_IDX(port->rxbufc++)];
   3.120 -                spin_unlock_irqrestore(&port->lock, flags);
   3.121 +                spin_unlock_irqrestore(&port->rx_lock, flags);
   3.122                  break;
   3.123              }
   3.124              
   3.125              if ( port->driver->getc(port, &c) )
   3.126              {
   3.127 -                spin_unlock_irqrestore(&port->lock, flags);
   3.128 +                spin_unlock_irqrestore(&port->rx_lock, flags);
   3.129                  break;
   3.130              }
   3.131  
   3.132 -            spin_unlock_irqrestore(&port->lock, flags);
   3.133 +            spin_unlock_irqrestore(&port->rx_lock, flags);
   3.134  
   3.135              cpu_relax();
   3.136 +            udelay(100);
   3.137          }
   3.138      } while ( ((handle & SERHND_LO) &&  (c & 0x80)) ||
   3.139                ((handle & SERHND_HI) && !(c & 0x80)) );
   3.140 @@ -241,7 +255,7 @@ void serial_set_rx_handler(int handle, s
   3.141      if ( handle == -1 )
   3.142          return;
   3.143  
   3.144 -    spin_lock_irqsave(&port->lock, flags);
   3.145 +    spin_lock_irqsave(&port->rx_lock, flags);
   3.146  
   3.147      if ( port->rx != NULL )
   3.148          goto fail;
   3.149 @@ -265,11 +279,11 @@ void serial_set_rx_handler(int handle, s
   3.150          port->rx = fn;
   3.151      }
   3.152  
   3.153 -    spin_unlock_irqrestore(&port->lock, flags);
   3.154 +    spin_unlock_irqrestore(&port->rx_lock, flags);
   3.155      return;
   3.156  
   3.157   fail:
   3.158 -    spin_unlock_irqrestore(&port->lock, flags);
   3.159 +    spin_unlock_irqrestore(&port->rx_lock, flags);
   3.160      printk("ERROR: Conflicting receive handlers for COM%d\n", 
   3.161             handle & SERHND_IDX);
   3.162  }
   3.163 @@ -277,8 +291,13 @@ void serial_set_rx_handler(int handle, s
   3.164  void serial_force_unlock(int handle)
   3.165  {
   3.166      struct serial_port *port = &com[handle & SERHND_IDX];
   3.167 -    if ( handle != -1 )
   3.168 -        port->lock = SPIN_LOCK_UNLOCKED;
   3.169 +
   3.170 +    if ( handle == -1 )
   3.171 +        return;
   3.172 +
   3.173 +    port->rx_lock = SPIN_LOCK_UNLOCKED;
   3.174 +    port->tx_lock = SPIN_LOCK_UNLOCKED;
   3.175 +
   3.176      serial_start_sync(handle);
   3.177  }
   3.178  
   3.179 @@ -290,7 +309,7 @@ void serial_start_sync(int handle)
   3.180      if ( handle == -1 )
   3.181          return;
   3.182      
   3.183 -    spin_lock_irqsave(&port->lock, flags);
   3.184 +    spin_lock_irqsave(&port->tx_lock, flags);
   3.185  
   3.186      if ( port->sync++ == 0 )
   3.187      {
   3.188 @@ -303,7 +322,7 @@ void serial_start_sync(int handle)
   3.189          }
   3.190      }
   3.191  
   3.192 -    spin_unlock_irqrestore(&port->lock, flags);
   3.193 +    spin_unlock_irqrestore(&port->tx_lock, flags);
   3.194  }
   3.195  
   3.196  void serial_end_sync(int handle)
   3.197 @@ -314,11 +333,11 @@ void serial_end_sync(int handle)
   3.198      if ( handle == -1 )
   3.199          return;
   3.200      
   3.201 -    spin_lock_irqsave(&port->lock, flags);
   3.202 +    spin_lock_irqsave(&port->tx_lock, flags);
   3.203  
   3.204      port->sync--;
   3.205  
   3.206 -    spin_unlock_irqrestore(&port->lock, flags);
   3.207 +    spin_unlock_irqrestore(&port->tx_lock, flags);
   3.208  }
   3.209  
   3.210  int serial_tx_space(int handle)
     4.1 --- a/xen/include/xen/serial.h	Thu Mar 23 15:53:52 2006 +0100
     4.2 +++ b/xen/include/xen/serial.h	Thu Mar 23 17:17:08 2006 +0100
     4.3 @@ -42,7 +42,7 @@ struct serial_port {
     4.4      char                rxbuf[SERIAL_RXBUFSZ];
     4.5      unsigned int        rxbufp, rxbufc;
     4.6      /* Serial I/O is concurrency-safe. */
     4.7 -    spinlock_t          lock;
     4.8 +    spinlock_t          rx_lock, tx_lock;
     4.9  };
    4.10  
    4.11  struct uart_driver {