From: Ming Lei <tom.leiming@xxxxxxxxx> This patch releases the usb_serial_port object after releasing usb_serial object, because .shutdown of usb_serial_driver often references port object and it is called in destroy_serial(), when it is touched, port object has been released. This bug is introduced by Alan's patch: usb-serial-fix-lifetime-and-locking-problems.patch. The bug is reproduced very easily by the following steps: 1, plug a usb serial port and unplug it; or 2,plug a use serial port, open it ,disconnect it, and close it. In each case, the oops will happen: [ 157.595138] BUG: unable to handle kernel NULL pointer dereference at (null) [ 157.595146] IP: [<ffffffffa03c1608>] generic_cleanup+0x11/0x6d [usbserial] [ 157.595157] PGD 0 [ 157.595160] Oops: 0000 [#1] PREEMPT SMP DEBUG_PAGEALLOC [ 157.595167] last sysfs file: /sys/devices/pci0000:00/0000:00:1d.7/usb2/2-2/2-2.1/2-2.1:1.0/bInterfaceClass [ 157.595170] CPU 0 [ 157.595173] Modules linked in: ch341 usbserial vfat fat fuse sunrpc ipv6 acpi_cpufreq snd_hda_codec_idt snd_hda_intel snd_hda_codec snd_hwdep snd_seq_device snd_pcm_oss snd_mixer_oss iwl3945 snd_pcm iwlcore mac80211 usbhid ohci1394 snd_timer snd i2c_i801 ieee1394 cfg80211 i2c_core tg3 sg libphy processor snd_page_alloc battery ac button thermal sr_mod cdrom evdev dcdbas ata_generic ata_piix libata sd_mod scsi_mod ext3 jbd mbcache uhci_hcd ohci_hcd ehci_hcd usbcore [last unloaded: microcode] [ 157.595237] Pid: 6326, comm: lockdev Not tainted 2.6.30-rc3-gkh-00001-g7168b05 #96 Latitude D630 [ 157.595240] RIP: 0010:[<ffffffffa03c1608>] [<ffffffffa03c1608>] generic_cleanup+0x11/0x6d [usbserial] [ 157.595248] RSP: 0018:ffff880010d43be8 EFLAGS: 00010246 [ 157.595251] RAX: 0000000000000001 RBX: 0000000000000000 RCX: 0000000000000000 [ 157.595260] RDX: 0000000000000000 RSI: ffffffffa03c0faf RDI: 0000000000000000 [ 157.595263] RBP: ffff880010d43bf8 R08: 0000000000000001 R09: ffff880010d43bd8 [ 157.595266] R10: 0000000000000002 R11: 0000000087654321 R12: ffff88003ddaddf8 [ 157.595269] R13: ffff88003ddaddf0 R14: ffff88003ddaddf0 R15: ffff88003350b9e0 [ 157.595272] FS: 0000000000000000(0000) GS:ffff880002400000(0000) knlGS:0000000000000000 [ 157.595276] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 157.595279] CR2: 0000000000000000 CR3: 0000000000201000 CR4: 00000000000006e0 [ 157.595282] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 [ 157.595285] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 [ 157.595289] Process lockdev (pid: 6326, threadinfo ffff880010d42000, task ffff880010c09640) [ 157.595291] Stack: [ 157.595293] 0000000000000001 ffff88003ddaddf8 ffff880010d43c28 ffffffffa03c16ac [ 157.595300] 0000000000000246 ffff88003ddade50 ffff88003ddade50 ffff88003ddaddf0 [ 157.595307] ffff880010d43c58 ffffffffa03c0ffb ffff88003ddade50 ffffffffa03c0faf [ 157.595315] Call Trace: [ 157.595317] [<ffffffffa03c16ac>] usb_serial_generic_shutdown+0x48/0x5d [usbserial] [ 157.595325] [<ffffffffa03c0ffb>] destroy_serial+0x4c/0xef [usbserial] [ 157.595332] [<ffffffffa03c0faf>] ? destroy_serial+0x0/0xef [usbserial] [ 157.595340] [<ffffffff80355855>] kref_put+0x4b/0x57 [ 157.595347] [<ffffffffa03c0d2c>] usb_serial_put+0x2d/0x3d [usbserial] [ 157.595354] [<ffffffffa03c0e81>] serial_close+0x145/0x15e [usbserial] [ 157.595361] [<ffffffff803aae0a>] tty_release_dev+0x1bf/0x4e8 [ 157.595368] [<ffffffff803ab144>] ? tty_release+0x11/0x24 [ 157.595372] [<ffffffff8048b20e>] ? lock_kernel+0x2b/0xbe [ 157.595378] [<ffffffff8035e09d>] ? _raw_spin_trylock+0x11/0x37 [ 157.595384] [<ffffffff803ab14c>] tty_release+0x19/0x24 [ 157.595388] [<ffffffff802c8049>] __fput+0xeb/0x1a7 [ 157.595394] [<ffffffff802c811d>] fput+0x18/0x1a [ 157.595399] [<ffffffff802c53c8>] filp_close+0x67/0x72 [ 157.595404] [<ffffffff80241391>] put_files_struct+0x6b/0xc2 [ 157.595409] [<ffffffff8024142f>] exit_files+0x47/0x50 [ 157.595414] [<ffffffff80242bb1>] do_exit+0x1ff/0x6a6 [ 157.595418] [<ffffffff8048a31c>] ? trace_hardirqs_off_thunk+0x3a/0x3c [ 157.595424] [<ffffffff8020bbf9>] ? retint_swapgs+0x13/0x1b [ 157.595431] [<ffffffff802430d7>] do_group_exit+0x7f/0xaf [ 157.595435] [<ffffffff80243119>] sys_exit_group+0x12/0x16 [ 157.595440] [<ffffffff8020b142>] system_call_fastpath+0x16/0x1b [ 157.595445] Code: c6 de 33 3c a0 48 c7 c7 10 34 3c a0 31 c0 e8 80 58 0c e0 89 d8 5b 41 5c c9 c3 55 83 3d b1 5c 00 00 00 48 89 e5 41 54 53 48 89 fb <4c> 8b 27 74 23 0f b6 8f 88 02 00 00 48 c7 c2 b0 27 3c a0 48 c7 [ 157.595517] RIP [<ffffffffa03c1608>] generic_cleanup+0x11/0x6d [usbserial] [ 157.595524] RSP <ffff880010d43be8> [ 157.595526] CR2: 0000000000000000 [ 157.595530] ---[ end trace c18d5f6118ef6990 ]--- Signed-off-by: Ming Lei <tom.leiming@xxxxxxxxx> --- drivers/usb/serial/bus.c | 9 ++++++--- drivers/usb/serial/usb-serial.c | 25 +++++++++++++++++++++---- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/drivers/usb/serial/bus.c b/drivers/usb/serial/bus.c index 83bbb5b..45b837b 100644 --- a/drivers/usb/serial/bus.c +++ b/drivers/usb/serial/bus.c @@ -87,9 +87,11 @@ exit: return retval; } +#define to_usb_serial_driver(d) container_of(d, struct usb_serial_driver, \ + driver) static int usb_serial_device_remove(struct device *dev) { - struct usb_serial_driver *driver; + struct usb_serial_driver *driver = NULL; struct usb_serial_port *port; int retval = 0; int minor; @@ -100,8 +102,9 @@ static int usb_serial_device_remove(struct device *dev) device_remove_file(&port->dev, &dev_attr_port_number); - driver = port->serial->type; - if (driver->port_remove) { + if (dev->driver) + driver = to_usb_serial_driver(dev->driver); + if (driver && driver->port_remove) { if (!try_module_get(driver->driver.owner)) { dev_err(dev, "module get failed, exiting\n"); retval = -EIO; diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 2c09006..892ac97 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -305,7 +305,6 @@ static void serial_close(struct tty_struct *tty, struct file *filp) --port->port.count; count = port->port.count; mutex_unlock(&port->mutex); - put_device(&port->dev); /* Mustn't dereference port any more */ if (count == 0) { @@ -316,6 +315,10 @@ static void serial_close(struct tty_struct *tty, struct file *filp) } usb_serial_put(serial); + /*Must release port after serial, for .shutdown + * need to reference port.*/ + put_device(&port->dev); + /* Mustn't dereference serial any more */ if (count == 0) module_put(owner); @@ -1053,6 +1056,8 @@ void usb_serial_disconnect(struct usb_interface *interface) struct usb_serial *serial = usb_get_intfdata(interface); struct device *dev = &interface->dev; struct usb_serial_port *port; + struct usb_serial_port **port_save; + unsigned char ports_num; usb_serial_console_disconnect(serial); dbg("%s", __func__); @@ -1063,7 +1068,15 @@ void usb_serial_disconnect(struct usb_interface *interface) serial->disconnected = 1; mutex_unlock(&serial->disc_mutex); - for (i = 0; i < serial->num_ports; ++i) { + ports_num = serial->num_ports; + port_save = kmalloc(ports_num*sizeof(struct usb_serial_port *), \ + GFP_KERNEL); + if (!port_save) { + dev_err(&interface->dev, "%s- failed\n", __func__); + return; + } + + for (i = 0; i < ports_num; ++i) { port = serial->port[i]; if (port) { struct tty_struct *tty = tty_port_tty_get(&port->port); @@ -1073,13 +1086,17 @@ void usb_serial_disconnect(struct usb_interface *interface) } kill_traffic(port); cancel_work_sync(&port->work); - device_unregister(&port->dev); - serial->port[i] = NULL; + port_save[i] = port; } } /* let the last holder of this object * cause it to be cleaned up */ usb_serial_put(serial); + + for (i = 0; i < ports_num; ++i) + device_unregister(&port_save[i]->dev); + kfree(port_save); + dev_info(dev, "device disconnected\n"); } EXPORT_SYMBOL_GPL(usb_serial_disconnect); -- 1.6.0.GIT -- 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