Re: u_serial ring buffer bug

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Sunday 23 August 2009, George Spelvin wrote:
> Subject: usb/gadget/u_serial: Simplify output buffer implementation.
> 
> There's no need to store buf_size in a variable, given that it's a
> compile-time cosntant.  And since it's a pwoer of 2, use a different
> indexing scheme that lets us fill the circular buffer rather than
> needing to reserve one byte to disambiguate empty and full.
> ---
> > Patches accepted.  :)
> 
> Something like this?

This makes more than one patch.  I count:

 - using buf size as a compile time constant

 - changing the index scheme

 - a few gratuitous changes like
	unsigned len;
	...
	len = ...;

   becoming
	unsigned len = ...;

 - maybe more

One for just the first is a no-brainer.  The second needs
review; by itself please.  The third I'd rather not see.


> One question I have is whether it's worth removing 
> the locking from gs_write_room and gs_chars_in_buffer.  I avoided it for
> now, on the assumption that those aren't hot code paths, but it could
> be done.

No worth it for now.


> Does anyone have opinions on the matter?
> 
> (Indeed, you could have independent read and write locking, so readers
> and writers could overlap, but that's a bigger design question that
> I don't feel like researching right now.)
> 
> diff --git a/drivers/usb/gadget/u_serial.c b/drivers/usb/gadget/u_serial.c
> index fc6e709..8f0f1f8 100644
> --- a/drivers/usb/gadget/u_serial.c
> +++ b/drivers/usb/gadget/u_serial.c
> @@ -80,10 +80,8 @@
>  
>  /* circular buffer */
>  struct gs_buf {
> -	unsigned		buf_size;
> -	char			*buf_buf;
> -	char			*buf_get;
> -	char			*buf_put;
> +	char	*buf_buf;	/* WRITE_BUF_SIZE */
> +	unsigned buf_get, buf_put;	/* Offsets */
>  };
>  
>  /*
> @@ -144,15 +142,13 @@ static unsigned	n_ports;
>   *
>   * Allocate a circular buffer and all associated memory.
>   */
> -static int gs_buf_alloc(struct gs_buf *gb, unsigned size)
> +static int gs_buf_alloc(struct gs_buf *gb)
>  {
> -	gb->buf_buf = kmalloc(size, GFP_KERNEL);
> +	gb->buf_buf = kmalloc(WRITE_BUF_SIZE, GFP_KERNEL);
>  	if (gb->buf_buf == NULL)
>  		return -ENOMEM;
>  
> -	gb->buf_size = size;
> -	gb->buf_put = gb->buf_buf;
> -	gb->buf_get = gb->buf_buf;
> +	gb->buf_put = gb->buf_get = 0;
>  
>  	return 0;
>  }
> @@ -161,6 +157,8 @@ static int gs_buf_alloc(struct gs_buf *gb, unsigned size)
>   * gs_buf_free
>   *
>   * Free the buffer and all associated memory.
> + * Must be idempotent; we call this early if possible, and again on
> + * final disconnect.
>   */
>  static void gs_buf_free(struct gs_buf *gb)
>  {
> @@ -185,9 +183,9 @@ static void gs_buf_clear(struct gs_buf *gb)
>   * Return the number of bytes of data written into the circular
>   * buffer.
>   */
> -static unsigned gs_buf_data_avail(struct gs_buf *gb)
> +static unsigned gs_buf_data_avail(struct gs_buf const *gb)
>  {
> -	return (gb->buf_size + gb->buf_put - gb->buf_get) % gb->buf_size;
> +	return gb->buf_put - gb->buf_get;
>  }
>  
>  /*
> @@ -196,9 +194,9 @@ static unsigned gs_buf_data_avail(struct gs_buf *gb)
>   * Return the number of bytes of space available in the circular
>   * buffer.
>   */
> -static unsigned gs_buf_space_avail(struct gs_buf *gb)
> +static unsigned gs_buf_space_avail(struct gs_buf const *gb)
>  {
> -	return (gb->buf_size + gb->buf_get - gb->buf_put - 1) % gb->buf_size;
> +	return WRITE_BUF_SIZE - gs_buf_data_avail(gb);
>  }
>  
>  /*
> @@ -212,27 +210,26 @@ static unsigned gs_buf_space_avail(struct gs_buf *gb)
>  static unsigned
>  gs_buf_put(struct gs_buf *gb, const char *buf, unsigned count)
>  {
> -	unsigned len;
> +	unsigned len = gs_buf_space_avail(gb);
> +	unsigned put_pos = gb->buf_put % WRITE_BUF_SIZE;
>  
> -	len  = gs_buf_space_avail(gb);
>  	if (count > len)
>  		count = len;
>  
> -	if (count == 0)
> +	if (count == 0)		/* This short-cut isn't actually necessary */
>  		return 0;
>  
> -	len = gb->buf_buf + gb->buf_size - gb->buf_put;
> +	len = WRITE_BUF_SIZE - put_pos;
> +
>  	if (count > len) {
> -		memcpy(gb->buf_put, buf, len);
> -		memcpy(gb->buf_buf, buf+len, count - len);
> -		gb->buf_put = gb->buf_buf + count - len;
> +		memcpy(gb->buf_buf + put_pos, buf, len);
> +		len = count - len;
> +		put_pos = 0;
>  	} else {
> -		memcpy(gb->buf_put, buf, count);
> -		if (count < len)
> -			gb->buf_put += count;
> -		else /* count == len */
> -			gb->buf_put = gb->buf_buf;
> +		len = count;
>  	}
> +	memcpy(gb->buf_buf + put_pos, buf, len);
> +	gb->buf_put += count;
>  
>  	return count;
>  }
> @@ -248,27 +245,25 @@ gs_buf_put(struct gs_buf *gb, const char *buf, unsigned count)
>  static unsigned
>  gs_buf_get(struct gs_buf *gb, char *buf, unsigned count)
>  {
> -	unsigned len;
> +	unsigned len = gs_buf_data_avail(gb);
> +	unsigned get_pos = gb->buf_get % WRITE_BUF_SIZE;
>  
> -	len = gs_buf_data_avail(gb);
>  	if (count > len)
>  		count = len;
>  
> -	if (count == 0)
> +	if (count == 0)		/* This short-cut isn't actually necessary */
>  		return 0;
>  
> -	len = gb->buf_buf + gb->buf_size - gb->buf_get;
> +	len = WRITE_BUF_SIZE - get_pos;
>  	if (count > len) {
> -		memcpy(buf, gb->buf_get, len);
> -		memcpy(buf+len, gb->buf_buf, count - len);
> -		gb->buf_get = gb->buf_buf + count - len;
> +		memcpy(buf, gb->buf_buf + get_pos, len);
> +		len = count - len;
> +		get_pos = 0;
>  	} else {
> -		memcpy(buf, gb->buf_get, count);
> -		if (count < len)
> -			gb->buf_get += count;
> -		else /* count == len */
> -			gb->buf_get = gb->buf_buf;
> +		len = count;
>  	}
> +	memcpy(buf, gb->buf_buf + get_pos, len);
> +	gb->buf_get += count;
>  
>  	return count;
>  }
> @@ -325,14 +320,7 @@ void gs_free_req(struct usb_ep *ep, struct usb_request *req)
>  static unsigned
>  gs_send_packet(struct gs_port *port, char *packet, unsigned size)
>  {
> -	unsigned len;
> -
> -	len = gs_buf_data_avail(&port->port_write_buf);
> -	if (len < size)
> -		size = len;
> -	if (size != 0)
> -		size = gs_buf_get(&port->port_write_buf, packet, size);
> -	return size;
> +	return gs_buf_get(&port->port_write_buf, packet, size);
>  }
>  
>  /*
> @@ -760,7 +748,7 @@ static int gs_open(struct tty_struct *tty, struct file *file)
>  	if (port->port_write_buf.buf_buf == NULL) {
>  
>  		spin_unlock_irq(&port->port_lock);
> -		status = gs_buf_alloc(&port->port_write_buf, WRITE_BUF_SIZE);
> +		status = gs_buf_alloc(&port->port_write_buf);
>  		spin_lock_irq(&port->port_lock);
>  
>  		if (status) {
> @@ -891,8 +879,7 @@ static int gs_write(struct tty_struct *tty, const unsigned char *buf, int count)
>  			port->port_num, tty, count);
>  
>  	spin_lock_irqsave(&port->port_lock, flags);
> -	if (count)
> -		count = gs_buf_put(&port->port_write_buf, buf, count);
> +	count = gs_buf_put(&port->port_write_buf, buf, count);
>  	/* treat count == 0 as flush_chars() */
>  	if (port->port_usb)
>  		status = gs_start_tx(port);
> @@ -930,6 +917,10 @@ static void gs_flush_chars(struct tty_struct *tty)
>  	spin_unlock_irqrestore(&port->port_lock, flags);
>  }
>  
> +/*
> + * This could be made lock-free, if gs_buf's buf_get and buf_put were
> + * made atomic.  Is that worth doing?
> + */
>  static int gs_write_room(struct tty_struct *tty)
>  {
>  	struct gs_port	*port = tty->driver_data;
> @@ -947,6 +938,7 @@ static int gs_write_room(struct tty_struct *tty)
>  	return room;
>  }
>  
> +/* Likewise */
>  static int gs_chars_in_buffer(struct tty_struct *tty)
>  {
>  	struct gs_port	*port = tty->driver_data;
> 
> 
> 
> 
> 
> 


--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux