diff options
author | Oliver Neukum <oneukum@suse.de> | 2007-01-13 01:29:26 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2007-02-07 18:44:38 -0500 |
commit | 34ef50e5b1f96c2d8c0f3d28b7d407743806256c (patch) | |
tree | bdf5fe45a270ebd77c895c7563c3fbf4c230019a /drivers | |
parent | fdcba53e2d58272bcdb5f1fad694602ccf02ad46 (diff) |
USB: race fixes for usb-serial step 1
- introduce a spinlock for serial_table to eliminate the window between
looking up a device and getting a reference
- delay inscription of a new device into serial_table until it is fully
initialised
- make sure disconnect() kills all URBs to avoid leckage across a soft unbind
Signed-off-by: Oliver Neukum <oneukum@suse.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/usb/serial/usb-serial.c | 44 |
1 files changed, 31 insertions, 13 deletions
diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 90beb5c50e59..3780362eb9f0 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c | |||
@@ -59,14 +59,19 @@ static struct usb_driver usb_serial_driver = { | |||
59 | 59 | ||
60 | static int debug; | 60 | static int debug; |
61 | static struct usb_serial *serial_table[SERIAL_TTY_MINORS]; /* initially all NULL */ | 61 | static struct usb_serial *serial_table[SERIAL_TTY_MINORS]; /* initially all NULL */ |
62 | static spinlock_t table_lock; | ||
62 | static LIST_HEAD(usb_serial_driver_list); | 63 | static LIST_HEAD(usb_serial_driver_list); |
63 | 64 | ||
64 | struct usb_serial *usb_serial_get_by_index(unsigned index) | 65 | struct usb_serial *usb_serial_get_by_index(unsigned index) |
65 | { | 66 | { |
66 | struct usb_serial *serial = serial_table[index]; | 67 | struct usb_serial *serial; |
68 | |||
69 | spin_lock(&table_lock); | ||
70 | serial = serial_table[index]; | ||
67 | 71 | ||
68 | if (serial) | 72 | if (serial) |
69 | kref_get(&serial->kref); | 73 | kref_get(&serial->kref); |
74 | spin_unlock(&table_lock); | ||
70 | return serial; | 75 | return serial; |
71 | } | 76 | } |
72 | 77 | ||
@@ -78,6 +83,7 @@ static struct usb_serial *get_free_serial (struct usb_serial *serial, int num_po | |||
78 | dbg("%s %d", __FUNCTION__, num_ports); | 83 | dbg("%s %d", __FUNCTION__, num_ports); |
79 | 84 | ||
80 | *minor = 0; | 85 | *minor = 0; |
86 | spin_lock(&table_lock); | ||
81 | for (i = 0; i < SERIAL_TTY_MINORS; ++i) { | 87 | for (i = 0; i < SERIAL_TTY_MINORS; ++i) { |
82 | if (serial_table[i]) | 88 | if (serial_table[i]) |
83 | continue; | 89 | continue; |
@@ -96,8 +102,10 @@ static struct usb_serial *get_free_serial (struct usb_serial *serial, int num_po | |||
96 | dbg("%s - minor base = %d", __FUNCTION__, *minor); | 102 | dbg("%s - minor base = %d", __FUNCTION__, *minor); |
97 | for (i = *minor; (i < (*minor + num_ports)) && (i < SERIAL_TTY_MINORS); ++i) | 103 | for (i = *minor; (i < (*minor + num_ports)) && (i < SERIAL_TTY_MINORS); ++i) |
98 | serial_table[i] = serial; | 104 | serial_table[i] = serial; |
105 | spin_unlock(&table_lock); | ||
99 | return serial; | 106 | return serial; |
100 | } | 107 | } |
108 | spin_unlock(&table_lock); | ||
101 | return NULL; | 109 | return NULL; |
102 | } | 110 | } |
103 | 111 | ||
@@ -110,9 +118,11 @@ static void return_serial(struct usb_serial *serial) | |||
110 | if (serial == NULL) | 118 | if (serial == NULL) |
111 | return; | 119 | return; |
112 | 120 | ||
121 | spin_lock(&table_lock); | ||
113 | for (i = 0; i < serial->num_ports; ++i) { | 122 | for (i = 0; i < serial->num_ports; ++i) { |
114 | serial_table[serial->minor + i] = NULL; | 123 | serial_table[serial->minor + i] = NULL; |
115 | } | 124 | } |
125 | spin_unlock(&table_lock); | ||
116 | } | 126 | } |
117 | 127 | ||
118 | static void destroy_serial(struct kref *kref) | 128 | static void destroy_serial(struct kref *kref) |
@@ -559,15 +569,20 @@ static void port_release(struct device *dev) | |||
559 | port_free(port); | 569 | port_free(port); |
560 | } | 570 | } |
561 | 571 | ||
562 | static void port_free(struct usb_serial_port *port) | 572 | static void kill_traffic(struct usb_serial_port *port) |
563 | { | 573 | { |
564 | usb_kill_urb(port->read_urb); | 574 | usb_kill_urb(port->read_urb); |
565 | usb_free_urb(port->read_urb); | ||
566 | usb_kill_urb(port->write_urb); | 575 | usb_kill_urb(port->write_urb); |
567 | usb_free_urb(port->write_urb); | ||
568 | usb_kill_urb(port->interrupt_in_urb); | 576 | usb_kill_urb(port->interrupt_in_urb); |
569 | usb_free_urb(port->interrupt_in_urb); | ||
570 | usb_kill_urb(port->interrupt_out_urb); | 577 | usb_kill_urb(port->interrupt_out_urb); |
578 | } | ||
579 | |||
580 | static void port_free(struct usb_serial_port *port) | ||
581 | { | ||
582 | kill_traffic(port); | ||
583 | usb_free_urb(port->read_urb); | ||
584 | usb_free_urb(port->write_urb); | ||
585 | usb_free_urb(port->interrupt_in_urb); | ||
571 | usb_free_urb(port->interrupt_out_urb); | 586 | usb_free_urb(port->interrupt_out_urb); |
572 | kfree(port->bulk_in_buffer); | 587 | kfree(port->bulk_in_buffer); |
573 | kfree(port->bulk_out_buffer); | 588 | kfree(port->bulk_out_buffer); |
@@ -802,12 +817,6 @@ int usb_serial_probe(struct usb_interface *interface, | |||
802 | num_ports = type->num_ports; | 817 | num_ports = type->num_ports; |
803 | } | 818 | } |
804 | 819 | ||
805 | if (get_free_serial (serial, num_ports, &minor) == NULL) { | ||
806 | dev_err(&interface->dev, "No more free serial devices\n"); | ||
807 | kfree (serial); | ||
808 | return -ENOMEM; | ||
809 | } | ||
810 | |||
811 | serial->minor = minor; | 820 | serial->minor = minor; |
812 | serial->num_ports = num_ports; | 821 | serial->num_ports = num_ports; |
813 | serial->num_bulk_in = num_bulk_in; | 822 | serial->num_bulk_in = num_bulk_in; |
@@ -956,6 +965,11 @@ int usb_serial_probe(struct usb_interface *interface, | |||
956 | } | 965 | } |
957 | } | 966 | } |
958 | 967 | ||
968 | if (get_free_serial (serial, num_ports, &minor) == NULL) { | ||
969 | dev_err(&interface->dev, "No more free serial devices\n"); | ||
970 | goto probe_error; | ||
971 | } | ||
972 | |||
959 | /* register all of the individual ports with the driver core */ | 973 | /* register all of the individual ports with the driver core */ |
960 | for (i = 0; i < num_ports; ++i) { | 974 | for (i = 0; i < num_ports; ++i) { |
961 | port = serial->port[i]; | 975 | port = serial->port[i]; |
@@ -1033,8 +1047,11 @@ void usb_serial_disconnect(struct usb_interface *interface) | |||
1033 | if (serial) { | 1047 | if (serial) { |
1034 | for (i = 0; i < serial->num_ports; ++i) { | 1048 | for (i = 0; i < serial->num_ports; ++i) { |
1035 | port = serial->port[i]; | 1049 | port = serial->port[i]; |
1036 | if (port && port->tty) | 1050 | if (port) { |
1037 | tty_hangup(port->tty); | 1051 | if (port->tty) |
1052 | tty_hangup(port->tty); | ||
1053 | kill_traffic(port); | ||
1054 | } | ||
1038 | } | 1055 | } |
1039 | /* let the last holder of this object | 1056 | /* let the last holder of this object |
1040 | * cause it to be cleaned up */ | 1057 | * cause it to be cleaned up */ |
@@ -1071,6 +1088,7 @@ static int __init usb_serial_init(void) | |||
1071 | return -ENOMEM; | 1088 | return -ENOMEM; |
1072 | 1089 | ||
1073 | /* Initialize our global data */ | 1090 | /* Initialize our global data */ |
1091 | spin_lock_init(&table_lock); | ||
1074 | for (i = 0; i < SERIAL_TTY_MINORS; ++i) { | 1092 | for (i = 0; i < SERIAL_TTY_MINORS; ++i) { |
1075 | serial_table[i] = NULL; | 1093 | serial_table[i] = NULL; |
1076 | } | 1094 | } |