aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
authorBjørn Mork <bjorn@mork.no>2012-07-26 19:11:41 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-08-10 14:51:43 -0400
commita1028f0abfb321e0f87c10ac0cce8508097c2b42 (patch)
treee14f23e94b198a8cb90d2f94f6687cd314b70b18 /drivers/usb
parent5c263b92f828af6a8cf54041db45ceae5af8f2ab (diff)
usb: usb_wwan: replace release and disconnect with a port_remove hook
Doing port specific cleanup in the .port_remove hook is a lot simpler and safer than doing it in the USB driver .release or .disconnect methods. The removal of the port from the usb-serial bus will happen before the USB driver cleanup, so we must be careful about accessing port specific driver data from any USB driver functions. This problem surfaced after the commit 0998d0631 device-core: Ensure drvdata = NULL when no driver is bound which turned the previous unsafe access into a reliable NULL pointer dereference. Fixes the following Oops: [ 243.148471] BUG: unable to handle kernel NULL pointer dereference at (null) [ 243.148508] IP: [<ffffffffa0468527>] stop_read_write_urbs+0x37/0x80 [usb_wwan] [ 243.148556] PGD 79d60067 PUD 79d61067 PMD 0 [ 243.148590] Oops: 0000 [#1] SMP [ 243.148617] Modules linked in: sr_mod cdrom qmi_wwan usbnet option cdc_wdm usb_wwan usbserial usb_storage uas fuse af_packet ip6table_filter ip6_tables iptable_filter ip_tables x_tables tun edd cpufreq_conservative cpufreq_userspace cpufreq_powersave snd_pcm_oss snd_mixer_oss acpi_cpufreq snd_seq mperf snd_seq_device coretemp arc4 sg hp_wmi sparse_keymap uvcvideo videobuf2_core videodev videobuf2_vmalloc videobuf2_memops rtl8192ce rtl8192c_common rtlwifi joydev pcspkr microcode mac80211 i2c_i801 lpc_ich r8169 snd_hda_codec_idt cfg80211 snd_hda_intel snd_hda_codec rfkill snd_hwdep snd_pcm wmi snd_timer ac snd soundcore snd_page_alloc battery uhci_hcd i915 drm_kms_helper drm i2c_algo_bit ehci_hcd thermal usbcore video usb_common button processor thermal_sys [ 243.149007] CPU 1 [ 243.149027] Pid: 135, comm: khubd Not tainted 3.5.0-rc7-next-20120720-1-vanilla #1 Hewlett-Packard HP Mini 110-3700 /1584 [ 243.149072] RIP: 0010:[<ffffffffa0468527>] [<ffffffffa0468527>] stop_read_write_urbs+0x37/0x80 [usb_wwan] [ 243.149118] RSP: 0018:ffff880037e75b30 EFLAGS: 00010286 [ 243.149133] RAX: 0000000000000000 RBX: 0000000000000000 RCX: ffff88005912aa28 [ 243.149150] RDX: ffff88005e95f028 RSI: 0000000000000000 RDI: ffff88005f7c1a10 [ 243.149166] RBP: ffff880037e75b60 R08: 0000000000000000 R09: ffffffff812cea90 [ 243.149182] R10: 0000000000000000 R11: 0000000000000001 R12: ffff88006539b440 [ 243.149198] R13: ffff88006539b440 R14: 0000000000000000 R15: 0000000000000000 [ 243.149216] FS: 0000000000000000(0000) GS:ffff88007ee80000(0000) knlGS:0000000000000000 [ 243.149233] CS: 0010 DS: 0000 ES: 0000 CR0: 000000008005003b [ 243.149248] CR2: 0000000000000000 CR3: 0000000079fe0000 CR4: 00000000000007e0 [ 243.149264] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 [ 243.149280] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 [ 243.149298] Process khubd (pid: 135, threadinfo ffff880037e74000, task ffff880037d40600) [ 243.149313] Stack: [ 243.149323] ffff880037e75b40 ffff88006539b440 ffff8800799bc830 ffff88005f7c1800 [ 243.149348] 0000000000000001 ffff88006539b448 ffff880037e75b70 ffffffffa04685e9 [ 243.149371] ffff880037e75bc0 ffffffffa0473765 ffff880037354988 ffff88007b594800 [ 243.149395] Call Trace: [ 243.149419] [<ffffffffa04685e9>] usb_wwan_disconnect+0x9/0x10 [usb_wwan] [ 243.149447] [<ffffffffa0473765>] usb_serial_disconnect+0xd5/0x120 [usbserial] [ 243.149511] [<ffffffffa0046b48>] usb_unbind_interface+0x58/0x1a0 [usbcore] [ 243.149545] [<ffffffff8139ebd7>] __device_release_driver+0x77/0xe0 [ 243.149567] [<ffffffff8139ec67>] device_release_driver+0x27/0x40 [ 243.149587] [<ffffffff8139e5cf>] bus_remove_device+0xdf/0x150 [ 243.149608] [<ffffffff8139bc78>] device_del+0x118/0x1a0 [ 243.149661] [<ffffffffa0044590>] usb_disable_device+0xb0/0x280 [usbcore] [ 243.149718] [<ffffffffa003c6fd>] usb_disconnect+0x9d/0x140 [usbcore] [ 243.149770] [<ffffffffa003da7d>] hub_port_connect_change+0xad/0x8a0 [usbcore] [ 243.149825] [<ffffffffa0043bf5>] ? usb_control_msg+0xe5/0x110 [usbcore] [ 243.149878] [<ffffffffa003e6e3>] hub_events+0x473/0x760 [usbcore] [ 243.149931] [<ffffffffa003ea05>] hub_thread+0x35/0x1d0 [usbcore] [ 243.149955] [<ffffffff81061960>] ? add_wait_queue+0x60/0x60 [ 243.150004] [<ffffffffa003e9d0>] ? hub_events+0x760/0x760 [usbcore] [ 243.150026] [<ffffffff8106133e>] kthread+0x8e/0xa0 [ 243.150047] [<ffffffff8157ec04>] kernel_thread_helper+0x4/0x10 [ 243.150068] [<ffffffff810612b0>] ? flush_kthread_work+0x120/0x120 [ 243.150088] [<ffffffff8157ec00>] ? gs_change+0xb/0xb [ 243.150101] Code: fd 41 54 53 48 83 ec 08 80 7f 1a 00 74 57 49 89 fc 31 db 90 49 8b 7c 24 20 45 31 f6 48 81 c7 10 02 00 00 e8 bc 64 f3 e0 49 89 c7 <4b> 8b 3c 37 49 83 c6 08 e8 4c a5 bd ff 49 83 fe 20 75 ed 45 30 [ 243.150257] RIP [<ffffffffa0468527>] stop_read_write_urbs+0x37/0x80 [usb_wwan] [ 243.150282] RSP <ffff880037e75b30> [ 243.150294] CR2: 0000000000000000 [ 243.177170] ---[ end trace fba433d9015ffb8c ]--- Reported-by: Dan Carpenter <dan.carpenter@oracle.com> Reported-by: Thomas Schäfer <tschaefer@t-online.de> Suggested-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Bjørn Mork <bjorn@mork.no> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/serial/ipw.c3
-rw-r--r--drivers/usb/serial/option.c4
-rw-r--r--drivers/usb/serial/qcserial.c5
-rw-r--r--drivers/usb/serial/usb-wwan.h3
-rw-r--r--drivers/usb/serial/usb_wwan.c64
5 files changed, 31 insertions, 48 deletions
diff --git a/drivers/usb/serial/ipw.c b/drivers/usb/serial/ipw.c
index 5811d34b6c6b..2cb30c535839 100644
--- a/drivers/usb/serial/ipw.c
+++ b/drivers/usb/serial/ipw.c
@@ -227,7 +227,6 @@ static void ipw_release(struct usb_serial *serial)
227{ 227{
228 struct usb_wwan_intf_private *data = usb_get_serial_data(serial); 228 struct usb_wwan_intf_private *data = usb_get_serial_data(serial);
229 229
230 usb_wwan_release(serial);
231 usb_set_serial_data(serial, NULL); 230 usb_set_serial_data(serial, NULL);
232 kfree(data); 231 kfree(data);
233} 232}
@@ -309,12 +308,12 @@ static struct usb_serial_driver ipw_device = {
309 .description = "IPWireless converter", 308 .description = "IPWireless converter",
310 .id_table = id_table, 309 .id_table = id_table,
311 .num_ports = 1, 310 .num_ports = 1,
312 .disconnect = usb_wwan_disconnect,
313 .open = ipw_open, 311 .open = ipw_open,
314 .close = ipw_close, 312 .close = ipw_close,
315 .probe = ipw_probe, 313 .probe = ipw_probe,
316 .attach = usb_wwan_startup, 314 .attach = usb_wwan_startup,
317 .release = ipw_release, 315 .release = ipw_release,
316 .port_remove = usb_wwan_port_remove,
318 .dtr_rts = ipw_dtr_rts, 317 .dtr_rts = ipw_dtr_rts,
319 .write = usb_wwan_write, 318 .write = usb_wwan_write,
320}; 319};
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index b09388606dc0..99306281dcba 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -1240,8 +1240,8 @@ static struct usb_serial_driver option_1port_device = {
1240 .tiocmset = usb_wwan_tiocmset, 1240 .tiocmset = usb_wwan_tiocmset,
1241 .ioctl = usb_wwan_ioctl, 1241 .ioctl = usb_wwan_ioctl,
1242 .attach = usb_wwan_startup, 1242 .attach = usb_wwan_startup,
1243 .disconnect = usb_wwan_disconnect,
1244 .release = option_release, 1243 .release = option_release,
1244 .port_remove = usb_wwan_port_remove,
1245 .read_int_callback = option_instat_callback, 1245 .read_int_callback = option_instat_callback,
1246#ifdef CONFIG_PM 1246#ifdef CONFIG_PM
1247 .suspend = usb_wwan_suspend, 1247 .suspend = usb_wwan_suspend,
@@ -1357,8 +1357,6 @@ static void option_release(struct usb_serial *serial)
1357 struct usb_wwan_intf_private *intfdata = usb_get_serial_data(serial); 1357 struct usb_wwan_intf_private *intfdata = usb_get_serial_data(serial);
1358 struct option_private *priv = intfdata->private; 1358 struct option_private *priv = intfdata->private;
1359 1359
1360 usb_wwan_release(serial);
1361
1362 kfree(priv); 1360 kfree(priv);
1363 kfree(intfdata); 1361 kfree(intfdata);
1364} 1362}
diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c
index 8d103019d6aa..314ae8ceba41 100644
--- a/drivers/usb/serial/qcserial.c
+++ b/drivers/usb/serial/qcserial.c
@@ -262,8 +262,7 @@ static void qc_release(struct usb_serial *serial)
262{ 262{
263 struct usb_wwan_intf_private *priv = usb_get_serial_data(serial); 263 struct usb_wwan_intf_private *priv = usb_get_serial_data(serial);
264 264
265 /* Call usb_wwan release & free the private data allocated in qcprobe */ 265 /* Free the private data allocated in qcprobe */
266 usb_wwan_release(serial);
267 usb_set_serial_data(serial, NULL); 266 usb_set_serial_data(serial, NULL);
268 kfree(priv); 267 kfree(priv);
269} 268}
@@ -283,8 +282,8 @@ static struct usb_serial_driver qcdevice = {
283 .write_room = usb_wwan_write_room, 282 .write_room = usb_wwan_write_room,
284 .chars_in_buffer = usb_wwan_chars_in_buffer, 283 .chars_in_buffer = usb_wwan_chars_in_buffer,
285 .attach = usb_wwan_startup, 284 .attach = usb_wwan_startup,
286 .disconnect = usb_wwan_disconnect,
287 .release = qc_release, 285 .release = qc_release,
286 .port_remove = usb_wwan_port_remove,
288#ifdef CONFIG_PM 287#ifdef CONFIG_PM
289 .suspend = usb_wwan_suspend, 288 .suspend = usb_wwan_suspend,
290 .resume = usb_wwan_resume, 289 .resume = usb_wwan_resume,
diff --git a/drivers/usb/serial/usb-wwan.h b/drivers/usb/serial/usb-wwan.h
index c47b6ec03063..1f034d2397c6 100644
--- a/drivers/usb/serial/usb-wwan.h
+++ b/drivers/usb/serial/usb-wwan.h
@@ -9,8 +9,7 @@ extern void usb_wwan_dtr_rts(struct usb_serial_port *port, int on);
9extern int usb_wwan_open(struct tty_struct *tty, struct usb_serial_port *port); 9extern int usb_wwan_open(struct tty_struct *tty, struct usb_serial_port *port);
10extern void usb_wwan_close(struct usb_serial_port *port); 10extern void usb_wwan_close(struct usb_serial_port *port);
11extern int usb_wwan_startup(struct usb_serial *serial); 11extern int usb_wwan_startup(struct usb_serial *serial);
12extern void usb_wwan_disconnect(struct usb_serial *serial); 12extern int usb_wwan_port_remove(struct usb_serial_port *port);
13extern void usb_wwan_release(struct usb_serial *serial);
14extern int usb_wwan_write_room(struct tty_struct *tty); 13extern int usb_wwan_write_room(struct tty_struct *tty);
15extern void usb_wwan_set_termios(struct tty_struct *tty, 14extern void usb_wwan_set_termios(struct tty_struct *tty,
16 struct usb_serial_port *port, 15 struct usb_serial_port *port,
diff --git a/drivers/usb/serial/usb_wwan.c b/drivers/usb/serial/usb_wwan.c
index f35971dff4a5..7d0811335b9a 100644
--- a/drivers/usb/serial/usb_wwan.c
+++ b/drivers/usb/serial/usb_wwan.c
@@ -565,62 +565,50 @@ bail_out_error:
565} 565}
566EXPORT_SYMBOL(usb_wwan_startup); 566EXPORT_SYMBOL(usb_wwan_startup);
567 567
568static void stop_read_write_urbs(struct usb_serial *serial) 568int usb_wwan_port_remove(struct usb_serial_port *port)
569{ 569{
570 int i, j; 570 int i;
571 struct usb_serial_port *port;
572 struct usb_wwan_port_private *portdata; 571 struct usb_wwan_port_private *portdata;
573 572
574 /* Stop reading/writing urbs */ 573 portdata = usb_get_serial_port_data(port);
575 for (i = 0; i < serial->num_ports; ++i) { 574 usb_set_serial_port_data(port, NULL);
576 port = serial->port[i]; 575
577 portdata = usb_get_serial_port_data(port); 576 /* Stop reading/writing urbs and free them */
578 for (j = 0; j < N_IN_URB; j++) 577 for (i = 0; i < N_IN_URB; i++) {
579 usb_kill_urb(portdata->in_urbs[j]); 578 usb_kill_urb(portdata->in_urbs[i]);
580 for (j = 0; j < N_OUT_URB; j++) 579 usb_free_urb(portdata->in_urbs[i]);
581 usb_kill_urb(portdata->out_urbs[j]); 580 free_page((unsigned long)portdata->in_buffer[i]);
581 }
582 for (i = 0; i < N_OUT_URB; i++) {
583 usb_kill_urb(portdata->out_urbs[i]);
584 usb_free_urb(portdata->out_urbs[i]);
585 kfree(portdata->out_buffer[i]);
582 } 586 }
583}
584 587
585void usb_wwan_disconnect(struct usb_serial *serial) 588 /* Now free port private data */
586{ 589 kfree(portdata);
587 stop_read_write_urbs(serial); 590 return 0;
588} 591}
589EXPORT_SYMBOL(usb_wwan_disconnect); 592EXPORT_SYMBOL(usb_wwan_port_remove);
590 593
591void usb_wwan_release(struct usb_serial *serial) 594#ifdef CONFIG_PM
595static void stop_read_write_urbs(struct usb_serial *serial)
592{ 596{
593 int i, j; 597 int i, j;
594 struct usb_serial_port *port; 598 struct usb_serial_port *port;
595 struct usb_wwan_port_private *portdata; 599 struct usb_wwan_port_private *portdata;
596 600
597 /* Now free them */ 601 /* Stop reading/writing urbs */
598 for (i = 0; i < serial->num_ports; ++i) { 602 for (i = 0; i < serial->num_ports; ++i) {
599 port = serial->port[i]; 603 port = serial->port[i];
600 portdata = usb_get_serial_port_data(port); 604 portdata = usb_get_serial_port_data(port);
601 605 for (j = 0; j < N_IN_URB; j++)
602 for (j = 0; j < N_IN_URB; j++) { 606 usb_kill_urb(portdata->in_urbs[j]);
603 usb_free_urb(portdata->in_urbs[j]); 607 for (j = 0; j < N_OUT_URB; j++)
604 free_page((unsigned long) 608 usb_kill_urb(portdata->out_urbs[j]);
605 portdata->in_buffer[j]);
606 portdata->in_urbs[j] = NULL;
607 }
608 for (j = 0; j < N_OUT_URB; j++) {
609 usb_free_urb(portdata->out_urbs[j]);
610 kfree(portdata->out_buffer[j]);
611 portdata->out_urbs[j] = NULL;
612 }
613 }
614
615 /* Now free per port private data */
616 for (i = 0; i < serial->num_ports; i++) {
617 port = serial->port[i];
618 kfree(usb_get_serial_port_data(port));
619 } 609 }
620} 610}
621EXPORT_SYMBOL(usb_wwan_release);
622 611
623#ifdef CONFIG_PM
624int usb_wwan_suspend(struct usb_serial *serial, pm_message_t message) 612int usb_wwan_suspend(struct usb_serial *serial, pm_message_t message)
625{ 613{
626 struct usb_wwan_intf_private *intfdata = serial->private; 614 struct usb_wwan_intf_private *intfdata = serial->private;