Re: [PATCH 2/3] USB: serial: make minor allocation dynamic

2013-06-06 Thread Johan Hovold
On Wed, Jun 05, 2013 at 10:54:55AM -0700, Greg KH wrote:
 From: Greg Kroah-Hartman gre...@linuxfoundation.org
 
 This moves the allocation of minor device numbers from a static array to
 be dynamic, using the idr interface.  This means that you could
 potentially get gaps in a minor number range for a single USB serial
 device with multiple ports, but all should still work properly.
 
 Note, we still have the limitation of 255 USB to serial devices in the
 system, as that is all we are registering with the TTY layer at this
 point in time.
 
 Signed-off-by: Greg Kroah-Hartman gre...@linuxfoundation.org

[...]

 -static struct usb_serial *get_free_serial(struct usb_serial *serial,
 - int num_ports, unsigned int *minor)
 +static int get_free_port(struct usb_serial_port *port)
  {
 - unsigned int i, j;
 - int good_spot;
 -
 - dev_dbg(serial-interface-dev, %s %d\n, __func__, num_ports);
 + int i;
  
 - *minor = 0;
   mutex_lock(table_lock);
 - for (i = 0; i  SERIAL_TTY_MINORS; ++i) {
 - if (serial_table[i])
 - continue;
 + i = idr_alloc(serial_minors, port, 0, 0, GFP_KERNEL);
 + if (i  0)
 + goto exit;
 + port-minor = i;
 +exit:
 + mutex_unlock(table_lock);
 + return i;
 +}
  
 - good_spot = 1;
 - for (j = 1; j = num_ports-1; ++j)
 - if ((i+j = SERIAL_TTY_MINORS) || (serial_table[i+j])) {
 - good_spot = 0;
 - i += j;
 - break;
 - }
 - if (good_spot == 0)
 - continue;
 +static int get_free_serial(struct usb_serial *serial, int num_ports,
 +unsigned int *minor)
 +{
 + unsigned int i;
 + unsigned int j;
 + int x;
  
 - *minor = i;
 - j = 0;
 - dev_dbg(serial-interface-dev, %s - minor base = %d\n, 
 __func__, *minor);
 - for (i = *minor; (i  (*minor + num_ports))  (i  
 SERIAL_TTY_MINORS); ++i, ++j) {
 - serial_table[i] = serial;
 - serial-port[j]-minor = i;
 - serial-port[j]-port_number = i - *minor;
 - }
 - mutex_unlock(table_lock);
 - return serial;
 + dev_dbg(serial-interface-dev, %s %d\n, __func__, num_ports);
 +
 + *minor = 0x;

You could use SERIAL_TTY_NO_MINOR here -- if needed at all, as it has
already been set in create_serial.

 + for (i = 0; i  num_ports; ++i) {
 + x = get_free_port(serial-port[i]);
 + if (x  0)
 + goto error;
 + if (*minor == 0x)
 + *minor = x;

We must not update *minor until all port minors have been allocated, or
idr_remove might get called for unallocated minors or even minor numbers
of other ports in return_serial when the serial struct is destroyed.

 + serial-port[i]-port_number = i;
   }
 - mutex_unlock(table_lock);
 - return NULL;
 + return 0;
 +error:
 + /* unwind the already allocated minors */
 + for (j = 0; j  i; ++j)
 + idr_remove(serial_minors, serial-port[j]-minor);
 + return x;

table_lock?

  }
  
  static void return_serial(struct usb_serial *serial)
 @@ -123,7 +128,7 @@ static void return_serial(struct usb_ser
  
   mutex_lock(table_lock);
   for (i = 0; i  serial-num_ports; ++i)
 - serial_table[serial-minor + i] = NULL;
 + idr_remove(serial_minors, serial-port[i]-minor);
   mutex_unlock(table_lock);
  }
  

[...]

Looks good otherwise.

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


Re: [PATCH 2/3] USB: serial: make minor allocation dynamic

2013-06-06 Thread Greg KH
On Thu, Jun 06, 2013 at 02:17:18PM +0200, Johan Hovold wrote:
 On Wed, Jun 05, 2013 at 10:54:55AM -0700, Greg KH wrote:
  From: Greg Kroah-Hartman gre...@linuxfoundation.org
  
  This moves the allocation of minor device numbers from a static array to
  be dynamic, using the idr interface.  This means that you could
  potentially get gaps in a minor number range for a single USB serial
  device with multiple ports, but all should still work properly.
  
  Note, we still have the limitation of 255 USB to serial devices in the
  system, as that is all we are registering with the TTY layer at this
  point in time.
  
  Signed-off-by: Greg Kroah-Hartman gre...@linuxfoundation.org
 
 [...]
 
  -static struct usb_serial *get_free_serial(struct usb_serial *serial,
  -   int num_ports, unsigned int *minor)
  +static int get_free_port(struct usb_serial_port *port)
   {
  -   unsigned int i, j;
  -   int good_spot;
  -
  -   dev_dbg(serial-interface-dev, %s %d\n, __func__, num_ports);
  +   int i;
   
  -   *minor = 0;
  mutex_lock(table_lock);
  -   for (i = 0; i  SERIAL_TTY_MINORS; ++i) {
  -   if (serial_table[i])
  -   continue;
  +   i = idr_alloc(serial_minors, port, 0, 0, GFP_KERNEL);
  +   if (i  0)
  +   goto exit;
  +   port-minor = i;
  +exit:
  +   mutex_unlock(table_lock);
  +   return i;
  +}
   
  -   good_spot = 1;
  -   for (j = 1; j = num_ports-1; ++j)
  -   if ((i+j = SERIAL_TTY_MINORS) || (serial_table[i+j])) {
  -   good_spot = 0;
  -   i += j;
  -   break;
  -   }
  -   if (good_spot == 0)
  -   continue;
  +static int get_free_serial(struct usb_serial *serial, int num_ports,
  +  unsigned int *minor)
  +{
  +   unsigned int i;
  +   unsigned int j;
  +   int x;
   
  -   *minor = i;
  -   j = 0;
  -   dev_dbg(serial-interface-dev, %s - minor base = %d\n, 
  __func__, *minor);
  -   for (i = *minor; (i  (*minor + num_ports))  (i  
  SERIAL_TTY_MINORS); ++i, ++j) {
  -   serial_table[i] = serial;
  -   serial-port[j]-minor = i;
  -   serial-port[j]-port_number = i - *minor;
  -   }
  -   mutex_unlock(table_lock);
  -   return serial;
  +   dev_dbg(serial-interface-dev, %s %d\n, __func__, num_ports);
  +
  +   *minor = 0x;
 
 You could use SERIAL_TTY_NO_MINOR here -- if needed at all, as it has
 already been set in create_serial.
 
  +   for (i = 0; i  num_ports; ++i) {
  +   x = get_free_port(serial-port[i]);
  +   if (x  0)
  +   goto error;
  +   if (*minor == 0x)
  +   *minor = x;
 
 We must not update *minor until all port minors have been allocated, or
 idr_remove might get called for unallocated minors or even minor numbers
 of other ports in return_serial when the serial struct is destroyed.

Good point.  In looking at this further, I really need to drop the
usb_serial structure's minor field completly, as it doesn't make sense
anymore.

I'll go rework all of that and post a v2 of this series, thanks.

  +   serial-port[i]-port_number = i;
  }
  -   mutex_unlock(table_lock);
  -   return NULL;
  +   return 0;
  +error:
  +   /* unwind the already allocated minors */
  +   for (j = 0; j  i; ++j)
  +   idr_remove(serial_minors, serial-port[j]-minor);
  +   return x;
 
 table_lock?

Good catch, now fixed.

   }
   
   static void return_serial(struct usb_serial *serial)
  @@ -123,7 +128,7 @@ static void return_serial(struct usb_ser
   
  mutex_lock(table_lock);
  for (i = 0; i  serial-num_ports; ++i)
  -   serial_table[serial-minor + i] = NULL;
  +   idr_remove(serial_minors, serial-port[i]-minor);
  mutex_unlock(table_lock);
   }
   
 
 [...]
 
 Looks good otherwise.

Thanks for the review, much appreciated.

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


[PATCH 2/3] USB: serial: make minor allocation dynamic

2013-06-05 Thread Greg KH
From: Greg Kroah-Hartman gre...@linuxfoundation.org

This moves the allocation of minor device numbers from a static array to
be dynamic, using the idr interface.  This means that you could
potentially get gaps in a minor number range for a single USB serial
device with multiple ports, but all should still work properly.

Note, we still have the limitation of 255 USB to serial devices in the
system, as that is all we are registering with the TTY layer at this
point in time.

Signed-off-by: Greg Kroah-Hartman gre...@linuxfoundation.org

---
 drivers/usb/serial/usb-serial.c |   97 
 include/linux/usb/serial.h  |4 -
 2 files changed, 51 insertions(+), 50 deletions(-)

--- a/drivers/usb/serial/usb-serial.c
+++ b/drivers/usb/serial/usb-serial.c
@@ -37,6 +37,7 @@
 #include linux/usb.h
 #include linux/usb/serial.h
 #include linux/kfifo.h
+#include linux/idr.h
 #include pl2303.h
 
 #define DRIVER_AUTHOR Greg Kroah-Hartman gre...@linuxfoundation.org
@@ -49,7 +50,7 @@
drivers depend on it.
 */
 
-static struct usb_serial *serial_table[SERIAL_TTY_MINORS];
+static DEFINE_IDR(serial_minors);
 static DEFINE_MUTEX(table_lock);
 static LIST_HEAD(usb_serial_driver_list);
 
@@ -60,61 +61,65 @@ static LIST_HEAD(usb_serial_driver_list)
  */
 struct usb_serial *usb_serial_get_by_index(unsigned index)
 {
-   struct usb_serial *serial;
+   struct usb_serial *serial = NULL;
+   struct usb_serial_port *port;
 
mutex_lock(table_lock);
-   serial = serial_table[index];
+   port = idr_find(serial_minors, index);
+   if (!port)
+   goto exit;
 
-   if (serial) {
-   mutex_lock(serial-disc_mutex);
-   if (serial-disconnected) {
-   mutex_unlock(serial-disc_mutex);
-   serial = NULL;
-   } else {
-   kref_get(serial-kref);
-   }
+   serial = port-serial;
+   mutex_lock(serial-disc_mutex);
+   if (serial-disconnected) {
+   mutex_unlock(serial-disc_mutex);
+   serial = NULL;
+   } else {
+   kref_get(serial-kref);
}
+exit:
mutex_unlock(table_lock);
return serial;
 }
 
-static struct usb_serial *get_free_serial(struct usb_serial *serial,
-   int num_ports, unsigned int *minor)
+static int get_free_port(struct usb_serial_port *port)
 {
-   unsigned int i, j;
-   int good_spot;
-
-   dev_dbg(serial-interface-dev, %s %d\n, __func__, num_ports);
+   int i;
 
-   *minor = 0;
mutex_lock(table_lock);
-   for (i = 0; i  SERIAL_TTY_MINORS; ++i) {
-   if (serial_table[i])
-   continue;
+   i = idr_alloc(serial_minors, port, 0, 0, GFP_KERNEL);
+   if (i  0)
+   goto exit;
+   port-minor = i;
+exit:
+   mutex_unlock(table_lock);
+   return i;
+}
 
-   good_spot = 1;
-   for (j = 1; j = num_ports-1; ++j)
-   if ((i+j = SERIAL_TTY_MINORS) || (serial_table[i+j])) {
-   good_spot = 0;
-   i += j;
-   break;
-   }
-   if (good_spot == 0)
-   continue;
+static int get_free_serial(struct usb_serial *serial, int num_ports,
+  unsigned int *minor)
+{
+   unsigned int i;
+   unsigned int j;
+   int x;
 
-   *minor = i;
-   j = 0;
-   dev_dbg(serial-interface-dev, %s - minor base = %d\n, 
__func__, *minor);
-   for (i = *minor; (i  (*minor + num_ports))  (i  
SERIAL_TTY_MINORS); ++i, ++j) {
-   serial_table[i] = serial;
-   serial-port[j]-minor = i;
-   serial-port[j]-port_number = i - *minor;
-   }
-   mutex_unlock(table_lock);
-   return serial;
+   dev_dbg(serial-interface-dev, %s %d\n, __func__, num_ports);
+
+   *minor = 0x;
+   for (i = 0; i  num_ports; ++i) {
+   x = get_free_port(serial-port[i]);
+   if (x  0)
+   goto error;
+   if (*minor == 0x)
+   *minor = x;
+   serial-port[i]-port_number = i;
}
-   mutex_unlock(table_lock);
-   return NULL;
+   return 0;
+error:
+   /* unwind the already allocated minors */
+   for (j = 0; j  i; ++j)
+   idr_remove(serial_minors, serial-port[j]-minor);
+   return x;
 }
 
 static void return_serial(struct usb_serial *serial)
@@ -123,7 +128,7 @@ static void return_serial(struct usb_ser
 
mutex_lock(table_lock);
for (i = 0; i  serial-num_ports; ++i)
-   serial_table[serial-minor + i] = NULL;
+   idr_remove(serial_minors, serial-port[i]-minor);