From: Greg Kroah-Hartman Date: Tue, 30 Jun 2009 17:50:44 +0000 (-0700) Subject: .30 patches X-Git-Tag: v2.6.27.26~18 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=3995b5a2cce55f406eb1e8272cd5a162eee8cdab;p=thirdparty%2Fkernel%2Fstable-queue.git .30 patches --- diff --git a/queue-2.6.30/ath5k-avoid-pci-fatal-interrupts-by-restoring-retry_timeout-disabling.patch b/queue-2.6.30/ath5k-avoid-pci-fatal-interrupts-by-restoring-retry_timeout-disabling.patch new file mode 100644 index 00000000000..ab9b4bf14d2 --- /dev/null +++ b/queue-2.6.30/ath5k-avoid-pci-fatal-interrupts-by-restoring-retry_timeout-disabling.patch @@ -0,0 +1,37 @@ +From 8451d22dad40a66416b8d9c0952efa09ec5398c5 Mon Sep 17 00:00:00 2001 +From: Jouni Malinen +Date: Tue, 16 Jun 2009 11:59:23 +0300 +Subject: ath5k: avoid PCI FATAL interrupts by restoring RETRY_TIMEOUT disabling + +From: Jouni Malinen + +commit 8451d22dad40a66416b8d9c0952efa09ec5398c5 upstream. + +This reverts 'ath5k: remove dummy PCI "retry timeout" fix' on the +same theory as in 'ath9k: Fix PCI FATAL interrupts by restoring +RETRY_TIMEOUT disabling'. + +Reported-by: Bob Copeland +Signed-off-by: John W. Linville +Signed-off-by: Greg Kroah-Hartman + +--- + drivers/net/wireless/ath5k/base.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +--- a/drivers/net/wireless/ath5k/base.c ++++ b/drivers/net/wireless/ath5k/base.c +@@ -685,6 +685,13 @@ ath5k_pci_resume(struct pci_dev *pdev) + if (err) + return err; + ++ /* ++ * Suspend/Resume resets the PCI configuration space, so we have to ++ * re-disable the RETRY_TIMEOUT register (0x41) to keep ++ * PCI Tx retries from interfering with C3 CPU state ++ */ ++ pci_write_config_byte(pdev, 0x41, 0); ++ + err = request_irq(pdev->irq, ath5k_intr, IRQF_SHARED, "ath", sc); + if (err) { + ATH5K_ERR(sc, "request_irq failed\n"); diff --git a/queue-2.6.30/mm-fix-handling-of-pagesets-for-downed-cpus.patch b/queue-2.6.30/mm-fix-handling-of-pagesets-for-downed-cpus.patch new file mode 100644 index 00000000000..47e55f59c3d --- /dev/null +++ b/queue-2.6.30/mm-fix-handling-of-pagesets-for-downed-cpus.patch @@ -0,0 +1,62 @@ +From 364df0ebfbbb1330bfc6ca159f4d6020efc15a12 Mon Sep 17 00:00:00 2001 +From: Dimitri Sivanich +Date: Tue, 23 Jun 2009 12:37:04 -0700 +Subject: mm: fix handling of pagesets for downed cpus + +From: Dimitri Sivanich + +commit 364df0ebfbbb1330bfc6ca159f4d6020efc15a12 upstream. + +After downing/upping a cpu, an attempt to set +/proc/sys/vm/percpu_pagelist_fraction results in an oops in +percpu_pagelist_fraction_sysctl_handler(). + +If a processor is downed then we need to set the pageset pointer back to +the boot pageset. + +Updates of the high water marks should not access pagesets of unpopulated +zones (those pointer go to the boot pagesets which would be no longer +functional if their size would be increased beyond zero). + +Signed-off-by: Dimitri Sivanich +Signed-off-by: Christoph Lameter +Reviewed-by: KOSAKI Motohiro +Cc: Nick Piggin +Cc: Mel Gorman +Signed-off-by: Andrew Morton +Signed-off-by: Linus Torvalds +Signed-off-by: Greg Kroah-Hartman + +--- + mm/page_alloc.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +--- a/mm/page_alloc.c ++++ b/mm/page_alloc.c +@@ -2812,7 +2812,7 @@ bad: + if (dzone == zone) + break; + kfree(zone_pcp(dzone, cpu)); +- zone_pcp(dzone, cpu) = NULL; ++ zone_pcp(dzone, cpu) = &boot_pageset[cpu]; + } + return -ENOMEM; + } +@@ -2827,7 +2827,7 @@ static inline void free_zone_pagesets(in + /* Free per_cpu_pageset if it is slab allocated */ + if (pset != &boot_pageset[cpu]) + kfree(pset); +- zone_pcp(zone, cpu) = NULL; ++ zone_pcp(zone, cpu) = &boot_pageset[cpu]; + } + } + +@@ -4501,7 +4501,7 @@ int percpu_pagelist_fraction_sysctl_hand + ret = proc_dointvec_minmax(table, write, file, buffer, length, ppos); + if (!write || (ret == -EINVAL)) + return ret; +- for_each_zone(zone) { ++ for_each_populated_zone(zone) { + for_each_online_cpu(cpu) { + unsigned long high; + high = zone->present_pages / percpu_pagelist_fraction; diff --git a/queue-2.6.30/mv643xx_eth-fix-unicast-filter-programming-in-promiscuous-mode.patch b/queue-2.6.30/mv643xx_eth-fix-unicast-filter-programming-in-promiscuous-mode.patch new file mode 100644 index 00000000000..a6a687b1642 --- /dev/null +++ b/queue-2.6.30/mv643xx_eth-fix-unicast-filter-programming-in-promiscuous-mode.patch @@ -0,0 +1,60 @@ +From 6877f54e6a3326c99aaf84b7bff6a3019da0b847 Mon Sep 17 00:00:00 2001 +From: Prabhanjan Sarnaik +Date: Thu, 18 Jun 2009 11:35:02 +0000 +Subject: mv643xx_eth: fix unicast filter programming in promiscuous mode + +From: Prabhanjan Sarnaik + +commit 6877f54e6a3326c99aaf84b7bff6a3019da0b847 upstream. + +The Unicast Promiscious Mode (UPM) bit in the mv643xx_eth port +configuration register doesn't do exactly what its name would suggest: +setting this bit merely enables reception of all unicast frames with a +destination address that differs from our local MAC address in bits +[47:4]. In particular, it doesn't have any effect on unicast frames +with a destination address that matches our MAC address in bits [47:4] +-- these will still be tested against the 16-entry unicast address +filter table. + +Therefore, if the interface is set to promiscuous mode, just setting +the unicast promiscuous bit isn't enough -- we need to set all filter +bits in the unicast filter table to 1 as well. + +Reported-by: Sachin Sanap +Signed-off-by: Prabhanjan Sarnaik +Tested-by: Siddarth Gore +Tested-by: Mahavir Jain +Signed-off-by: Lennert Buytenhek +Signed-off-by: David S. Miller +Signed-off-by: Greg Kroah-Hartman + +--- + drivers/net/mv643xx_eth.c | 7 +++---- + 1 file changed, 3 insertions(+), 4 deletions(-) + +--- a/drivers/net/mv643xx_eth.c ++++ b/drivers/net/mv643xx_eth.c +@@ -1751,12 +1751,12 @@ static void mv643xx_eth_program_unicast_ + + uc_addr_set(mp, dev->dev_addr); + +- port_config = rdlp(mp, PORT_CONFIG); ++ port_config = rdlp(mp, PORT_CONFIG) & ~UNICAST_PROMISCUOUS_MODE; ++ + nibbles = uc_addr_filter_mask(dev); + if (!nibbles) { + port_config |= UNICAST_PROMISCUOUS_MODE; +- wrlp(mp, PORT_CONFIG, port_config); +- return; ++ nibbles = 0xffff; + } + + for (i = 0; i < 16; i += 4) { +@@ -1777,7 +1777,6 @@ static void mv643xx_eth_program_unicast_ + wrl(mp, off, v); + } + +- port_config &= ~UNICAST_PROMISCUOUS_MODE; + wrlp(mp, PORT_CONFIG, port_config); + } + diff --git a/queue-2.6.30/n_r3964-fix-lock-imbalance.patch b/queue-2.6.30/n_r3964-fix-lock-imbalance.patch new file mode 100644 index 00000000000..75e833f072e --- /dev/null +++ b/queue-2.6.30/n_r3964-fix-lock-imbalance.patch @@ -0,0 +1,85 @@ +From eca41044268887838fa122aa24475df8f23d614c Mon Sep 17 00:00:00 2001 +From: Jiri Slaby +Date: Mon, 22 Jun 2009 18:42:03 +0100 +Subject: n_r3964: fix lock imbalance + +From: Jiri Slaby + +commit eca41044268887838fa122aa24475df8f23d614c upstream. + +There is omitted BKunL in r3964_read. + +Centralize the paths to one point with one unlock. + +Signed-off-by: Jiri Slaby +Signed-off-by: Alan Cox +Signed-off-by: Linus Torvalds +Signed-off-by: Greg Kroah-Hartman + +--- + drivers/char/n_r3964.c | 26 ++++++++++++++------------ + 1 file changed, 14 insertions(+), 12 deletions(-) + +--- a/drivers/char/n_r3964.c ++++ b/drivers/char/n_r3964.c +@@ -1062,7 +1062,7 @@ static ssize_t r3964_read(struct tty_str + struct r3964_client_info *pClient; + struct r3964_message *pMsg; + struct r3964_client_message theMsg; +- int count; ++ int ret; + + TRACE_L("read()"); + +@@ -1074,8 +1074,8 @@ static ssize_t r3964_read(struct tty_str + if (pMsg == NULL) { + /* no messages available. */ + if (file->f_flags & O_NONBLOCK) { +- unlock_kernel(); +- return -EAGAIN; ++ ret = -EAGAIN; ++ goto unlock; + } + /* block until there is a message: */ + wait_event_interruptible(pInfo->read_wait, +@@ -1085,29 +1085,31 @@ static ssize_t r3964_read(struct tty_str + /* If we still haven't got a message, we must have been signalled */ + + if (!pMsg) { +- unlock_kernel(); +- return -EINTR; ++ ret = -EINTR; ++ goto unlock; + } + + /* deliver msg to client process: */ + theMsg.msg_id = pMsg->msg_id; + theMsg.arg = pMsg->arg; + theMsg.error_code = pMsg->error_code; +- count = sizeof(struct r3964_client_message); ++ ret = sizeof(struct r3964_client_message); + + kfree(pMsg); + TRACE_M("r3964_read - msg kfree %p", pMsg); + +- if (copy_to_user(buf, &theMsg, count)) { +- unlock_kernel(); +- return -EFAULT; ++ if (copy_to_user(buf, &theMsg, ret)) { ++ ret = -EFAULT; ++ goto unlock; + } + +- TRACE_PS("read - return %d", count); +- return count; ++ TRACE_PS("read - return %d", ret); ++ goto unlock; + } ++ ret = -EPERM; ++unlock: + unlock_kernel(); +- return -EPERM; ++ return ret; + } + + static ssize_t r3964_write(struct tty_struct *tty, struct file *file, diff --git a/queue-2.6.30/parport_pc-after-superio-probing-restore-original-register-values.patch b/queue-2.6.30/parport_pc-after-superio-probing-restore-original-register-values.patch new file mode 100644 index 00000000000..1be032a59a9 --- /dev/null +++ b/queue-2.6.30/parport_pc-after-superio-probing-restore-original-register-values.patch @@ -0,0 +1,134 @@ +From e2434dc1c19412639dd047a4d4eff8ed0e5d0d50 Mon Sep 17 00:00:00 2001 +From: Jens Rottmann +Date: Mon, 22 Jun 2009 16:51:49 +0100 +Subject: parport_pc: after superio probing restore original register values + +From: Jens Rottmann + +commit e2434dc1c19412639dd047a4d4eff8ed0e5d0d50 upstream. + +CONFIG_PARPORT_PC_SUPERIO probes for various superio chips by writing +byte sequences to a set of different potential I/O ranges. But the +probed ranges are not exclusive to parallel ports. Some of our boards +just happen to have a watchdog in one of them. Took us almost a week +to figure out why some distros reboot without warning after running +flawlessly for 3 hours. For exactly 170 = 0xAA minutes, that is ... + +Fixed by restoring original values after probing. Also fixed too small +request_region() in detect_and_report_it87(). + +Signed-off-by: Jens Rottmann +Signed-off-by: Alan Cox +Acked-by: Jeff Garzik +Signed-off-by: Linus Torvalds +Signed-off-by: Greg Kroah-Hartman + +--- + drivers/parport/parport_pc.c | 31 +++++++++++++++++++++++++------ + 1 file changed, 25 insertions(+), 6 deletions(-) + +--- a/drivers/parport/parport_pc.c ++++ b/drivers/parport/parport_pc.c +@@ -1413,11 +1413,13 @@ static void __devinit decode_smsc(int ef + + static void __devinit winbond_check(int io, int key) + { +- int devid,devrev,oldid,x_devid,x_devrev,x_oldid; ++ int origval, devid, devrev, oldid, x_devid, x_devrev, x_oldid; + + if (!request_region(io, 3, __func__)) + return; + ++ origval = inb(io); /* Save original value */ ++ + /* First probe without key */ + outb(0x20,io); + x_devid=inb(io+1); +@@ -1437,6 +1439,8 @@ static void __devinit winbond_check(int + oldid=inb(io+1); + outb(0xaa,io); /* Magic Seal */ + ++ outb(origval, io); /* in case we poked some entirely different hardware */ ++ + if ((x_devid == devid) && (x_devrev == devrev) && (x_oldid == oldid)) + goto out; /* protection against false positives */ + +@@ -1447,11 +1451,15 @@ out: + + static void __devinit winbond_check2(int io,int key) + { +- int devid,devrev,oldid,x_devid,x_devrev,x_oldid; ++ int origval[3], devid, devrev, oldid, x_devid, x_devrev, x_oldid; + + if (!request_region(io, 3, __func__)) + return; + ++ origval[0] = inb(io); /* Save original values */ ++ origval[1] = inb(io + 1); ++ origval[2] = inb(io + 2); ++ + /* First probe without the key */ + outb(0x20,io+2); + x_devid=inb(io+2); +@@ -1470,6 +1478,10 @@ static void __devinit winbond_check2(int + oldid=inb(io+2); + outb(0xaa,io); /* Magic Seal */ + ++ outb(origval[0], io); /* in case we poked some entirely different hardware */ ++ outb(origval[1], io + 1); ++ outb(origval[2], io + 2); ++ + if ((x_devid == devid) && (x_devrev == devrev) && (x_oldid == oldid)) + goto out; /* protection against false positives */ + +@@ -1480,11 +1492,13 @@ out: + + static void __devinit smsc_check(int io, int key) + { +- int id,rev,oldid,oldrev,x_id,x_rev,x_oldid,x_oldrev; ++ int origval, id, rev, oldid, oldrev, x_id, x_rev, x_oldid, x_oldrev; + + if (!request_region(io, 3, __func__)) + return; + ++ origval = inb(io); /* Save original value */ ++ + /* First probe without the key */ + outb(0x0d,io); + x_oldid=inb(io+1); +@@ -1508,6 +1522,8 @@ static void __devinit smsc_check(int io, + rev=inb(io+1); + outb(0xaa,io); /* Magic Seal */ + ++ outb(origval, io); /* in case we poked some entirely different hardware */ ++ + if ((x_id == id) && (x_oldrev == oldrev) && + (x_oldid == oldid) && (x_rev == rev)) + goto out; /* protection against false positives */ +@@ -1544,11 +1560,12 @@ static void __devinit detect_and_report_ + static void __devinit detect_and_report_it87(void) + { + u16 dev; +- u8 r; ++ u8 origval, r; + if (verbose_probing) + printk(KERN_DEBUG "IT8705 Super-IO detection, now testing port 2E ...\n"); +- if (!request_region(0x2e, 1, __func__)) ++ if (!request_region(0x2e, 2, __func__)) + return; ++ origval = inb(0x2e); /* Save original value */ + outb(0x87, 0x2e); + outb(0x01, 0x2e); + outb(0x55, 0x2e); +@@ -1568,8 +1585,10 @@ static void __devinit detect_and_report_ + outb(r | 8, 0x2F); + outb(0x02, 0x2E); /* Lock */ + outb(0x02, 0x2F); ++ } else { ++ outb(origval, 0x2e); /* Oops, sorry to disturb */ + } +- release_region(0x2e, 1); ++ release_region(0x2e, 2); + } + #endif /* CONFIG_PARPORT_PC_SUPERIO */ + diff --git a/queue-2.6.30/parport_pc-set-properly-the-dma_mask-for-parport_pc-device.patch b/queue-2.6.30/parport_pc-set-properly-the-dma_mask-for-parport_pc-device.patch new file mode 100644 index 00000000000..4a02455e244 --- /dev/null +++ b/queue-2.6.30/parport_pc-set-properly-the-dma_mask-for-parport_pc-device.patch @@ -0,0 +1,48 @@ +From dfa7c4d869b7d3d37b70f1de856f2901b6ebfcf0 Mon Sep 17 00:00:00 2001 +From: FUJITA Tomonori +Date: Mon, 22 Jun 2009 16:54:27 +0100 +Subject: parport_pc: set properly the dma_mask for parport_pc device + +From: FUJITA Tomonori + +commit dfa7c4d869b7d3d37b70f1de856f2901b6ebfcf0 upstream. + +parport_pc_probe_port() creates the own 'parport_pc' device if the +device argument is NULL. Then parport_pc_probe_port() doesn't +initialize the dma_mask and coherent_dma_mask of the device and calls +dma_alloc_coherent with it. dma_alloc_coherent fails because +dma_alloc_coherent() doesn't accept the uninitialized dma_mask: + +http://lkml.org/lkml/2009/6/16/150 + +Long ago, X86_32 and X86_64 had the own dma_alloc_coherent +implementations; X86_32 accepted a device having dma_mask that is not +initialized however X86_64 didn't. When we merged them, we chose to +prohibit a device having dma_mask that is not initialized. I think +that it's good to require drivers to set up dma_mask (and +coherent_dma_mask) properly if the drivers want DMA. + +Signed-off-by: FUJITA Tomonori +Reported-by: Malcom Blaney +Tested-by: Malcom Blaney +Signed-off-by: Alan Cox +Acked-by: Jeff Garzik +Signed-off-by: Linus Torvalds +Signed-off-by: Greg Kroah-Hartman + +--- + drivers/parport/parport_pc.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/drivers/parport/parport_pc.c ++++ b/drivers/parport/parport_pc.c +@@ -2193,6 +2193,9 @@ struct parport *parport_pc_probe_port(un + if (IS_ERR(pdev)) + return NULL; + dev = &pdev->dev; ++ ++ dev->coherent_dma_mask = DMA_BIT_MASK(24); ++ dev->dma_mask = &dev->coherent_dma_mask; + } + + ops = kmalloc(sizeof(struct parport_operations), GFP_KERNEL); diff --git a/queue-2.6.30/pci-pm-fix-handling-of-devices-without-pm-support-by-pci_target_state.patch b/queue-2.6.30/pci-pm-fix-handling-of-devices-without-pm-support-by-pci_target_state.patch new file mode 100644 index 00000000000..dcc94f29f01 --- /dev/null +++ b/queue-2.6.30/pci-pm-fix-handling-of-devices-without-pm-support-by-pci_target_state.patch @@ -0,0 +1,48 @@ +From d2abdf62882d982c58e7a6b09ecdcfcc28075e2e Mon Sep 17 00:00:00 2001 +From: Rafael J. Wysocki +Date: Sun, 14 Jun 2009 21:25:02 +0200 +Subject: PCI PM: Fix handling of devices without PM support by pci_target_state() + +From: Rafael J. Wysocki + +commit d2abdf62882d982c58e7a6b09ecdcfcc28075e2e upstream. + +If a PCI device is not power-manageable either by the platform, or +with the help of the native PCI PM interface, pci_target_state() will +return either PCI_D3hot, or PCI_POWER_ERROR for it, depending on +whether or not the device is configured to wake up the system. Alas, +none of these return values is correct, because each of them causes +pci_prepare_to_sleep() to return error code, although it should +complete successfully in such a case. + +Fix this problem by making pci_target_state() always return PCI_D0 +for devices that cannot be power managed. + +Signed-off-by: Rafael J. Wysocki +Signed-off-by: Jesse Barnes +Signed-off-by: Greg Kroah-Hartman + +--- + drivers/pci/pci.c | 5 ++--- + 1 file changed, 2 insertions(+), 3 deletions(-) + +--- a/drivers/pci/pci.c ++++ b/drivers/pci/pci.c +@@ -1282,15 +1282,14 @@ pci_power_t pci_target_state(struct pci_ + default: + target_state = state; + } ++ } else if (!dev->pm_cap) { ++ target_state = PCI_D0; + } else if (device_may_wakeup(&dev->dev)) { + /* + * Find the deepest state from which the device can generate + * wake-up events, make it the target state and enable device + * to generate PME#. + */ +- if (!dev->pm_cap) +- return PCI_POWER_ERROR; +- + if (dev->pme_support) { + while (target_state + && !(dev->pme_support & (1 << target_state))) diff --git a/queue-2.6.30/pci-pm-follow-pci_pm_ctrl_no_soft_reset-during-transitions-from-d3.patch b/queue-2.6.30/pci-pm-follow-pci_pm_ctrl_no_soft_reset-during-transitions-from-d3.patch new file mode 100644 index 00000000000..d15519c6b0f --- /dev/null +++ b/queue-2.6.30/pci-pm-follow-pci_pm_ctrl_no_soft_reset-during-transitions-from-d3.patch @@ -0,0 +1,42 @@ +From f62795f1e892ca9269849fa83de97621da7e02c0 Mon Sep 17 00:00:00 2001 +From: Rafael J. Wysocki +Date: Mon, 18 May 2009 22:51:12 +0200 +Subject: PCI PM: Follow PCI_PM_CTRL_NO_SOFT_RESET during transitions from D3 + +From: Rafael J. Wysocki + +commit f62795f1e892ca9269849fa83de97621da7e02c0 upstream. + +According to the PCI PM specification (PCI Bus Power Management +Interface Specification, Rev. 1.2, Section 5.4.1) we are supposed to +reinitialize devices that have PCI_PM_CTRL_NO_SOFT_RESET clear during +all transitions from PCI_D3hot to PCI_D0, but we only do it if the +device's current_state field is equal to PCI_UNKNOWN. + +This may lead to problems if a device with PCI_PM_CTRL_NO_SOFT_RESET +unset is put into PCI_D3hot at run time by its driver and +pci_set_power_state() is used to put it back into PCI_D0, because in +that case the device will remain uninitialized after +pci_set_power_state() has returned. Prevent that from happening by +modifying pci_raw_set_power_state() to reinitialize devices with +PCI_PM_CTRL_NO_SOFT_RESET unset during all transitions from D3 to D0. + +Signed-off-by: Rafael J. Wysocki +Signed-off-by: Jesse Barnes +Signed-off-by: Greg Kroah-Hartman + +--- + drivers/pci/pci.c | 2 ++ + 1 file changed, 2 insertions(+) + +--- a/drivers/pci/pci.c ++++ b/drivers/pci/pci.c +@@ -480,6 +480,8 @@ static int pci_raw_set_power_state(struc + pmcsr &= ~PCI_PM_CTRL_STATE_MASK; + pmcsr |= state; + break; ++ case PCI_D3hot: ++ case PCI_D3cold: + case PCI_UNKNOWN: /* Boot-up */ + if ((pmcsr & PCI_PM_CTRL_STATE_MASK) == PCI_D3hot + && !(pmcsr & PCI_PM_CTRL_NO_SOFT_RESET)) diff --git a/queue-2.6.30/pcmcia-cm4000-fix-lock-imbalance.patch b/queue-2.6.30/pcmcia-cm4000-fix-lock-imbalance.patch new file mode 100644 index 00000000000..138209c3bd3 --- /dev/null +++ b/queue-2.6.30/pcmcia-cm4000-fix-lock-imbalance.patch @@ -0,0 +1,32 @@ +From 69ae59d7d8df14413cf0a97b3e372d7dc8352563 Mon Sep 17 00:00:00 2001 +From: Jiri Slaby +Date: Mon, 22 Jun 2009 18:42:10 +0100 +Subject: pcmcia/cm4000: fix lock imbalance + +From: Jiri Slaby + +commit 69ae59d7d8df14413cf0a97b3e372d7dc8352563 upstream. + +Don't return from switch/case, break instead, so that we unlock BKL. + +Signed-off-by: Jiri Slaby +Signed-off-by: Alan Cox +Signed-off-by: Linus Torvalds +Signed-off-by: Greg Kroah-Hartman + +--- + drivers/char/pcmcia/cm4000_cs.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +--- a/drivers/char/pcmcia/cm4000_cs.c ++++ b/drivers/char/pcmcia/cm4000_cs.c +@@ -1575,7 +1575,8 @@ static long cmm_ioctl(struct file *filp, + clear_bit(LOCK_IO, &dev->flags); + wake_up_interruptible(&dev->ioq); + +- return 0; ++ rc = 0; ++ break; + case CM_IOCSPTS: + { + struct ptsreq krnptsreq; diff --git a/queue-2.6.30/qla2xxx-correct-overflow-during-dump-processing-on-large-memory-isp23xx-parts.patch b/queue-2.6.30/qla2xxx-correct-overflow-during-dump-processing-on-large-memory-isp23xx-parts.patch new file mode 100644 index 00000000000..49fbf3967a6 --- /dev/null +++ b/queue-2.6.30/qla2xxx-correct-overflow-during-dump-processing-on-large-memory-isp23xx-parts.patch @@ -0,0 +1,33 @@ +From e18e963b7e533149676b5d387d0a56160df9f111 Mon Sep 17 00:00:00 2001 +From: Andrew Vasquez +Date: Wed, 17 Jun 2009 10:30:31 -0700 +Subject: qla2xxx: Correct (again) overflow during dump-processing on large-memory ISP23xx parts. + +From: Andrew Vasquez + +commit e18e963b7e533149676b5d387d0a56160df9f111 upstream. + +Commit 7b867cf76fbcc8d77867cbec6f509f71dce8a98f ([SCSI] qla2xxx: +Refactor qla data structures) inadvertently reverted +e792121ec85672c1fa48f79d13986a3f4f56c590 ([SCSI] qla2xxx: Correct +overflow during dump-processing on large-memory ISP23xx parts.). + +Signed-off-by: Andrew Vasquez +Signed-off-by: James Bottomley +Signed-off-by: Greg Kroah-Hartman + +--- + drivers/scsi/qla2xxx/qla_dbg.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/scsi/qla2xxx/qla_dbg.c ++++ b/drivers/scsi/qla2xxx/qla_dbg.c +@@ -218,7 +218,7 @@ qla24xx_soft_reset(struct qla_hw_data *h + + static int + qla2xxx_dump_ram(struct qla_hw_data *ha, uint32_t addr, uint16_t *ram, +- uint16_t ram_words, void **nxt) ++ uint32_t ram_words, void **nxt) + { + int rval; + uint32_t cnt, stat, timer, words, idx; diff --git a/queue-2.6.30/series b/queue-2.6.30/series index 308760c1bc6..a6f9034527e 100644 --- a/queue-2.6.30/series +++ b/queue-2.6.30/series @@ -79,3 +79,15 @@ cifs-fix-fh_mutex-locking-in-cifs_reopen_file.patch vt_ioctl-fix-lock-imbalance.patch x86-fix-non-lazy-gs-handling-in-sys_vm86.patch x86-set-cpu_llc_id-on-amd-cpus.patch +usb-serial-replace-shutdown-with-disconnect-release.patch +pcmcia-cm4000-fix-lock-imbalance.patch +n_r3964-fix-lock-imbalance.patch +parport_pc-set-properly-the-dma_mask-for-parport_pc-device.patch +parport_pc-after-superio-probing-restore-original-register-values.patch +mv643xx_eth-fix-unicast-filter-programming-in-promiscuous-mode.patch +ath5k-avoid-pci-fatal-interrupts-by-restoring-retry_timeout-disabling.patch +sound-seq_midi_event-fix-decoding-of-rpn-events.patch +pci-pm-fix-handling-of-devices-without-pm-support-by-pci_target_state.patch +pci-pm-follow-pci_pm_ctrl_no_soft_reset-during-transitions-from-d3.patch +qla2xxx-correct-overflow-during-dump-processing-on-large-memory-isp23xx-parts.patch +mm-fix-handling-of-pagesets-for-downed-cpus.patch diff --git a/queue-2.6.30/sound-seq_midi_event-fix-decoding-of-rpn-events.patch b/queue-2.6.30/sound-seq_midi_event-fix-decoding-of-rpn-events.patch new file mode 100644 index 00000000000..ea8caec8142 --- /dev/null +++ b/queue-2.6.30/sound-seq_midi_event-fix-decoding-of-rpn-events.patch @@ -0,0 +1,38 @@ +From 6423f9ea8035138d70bae1a278d3b57b743f8b3e Mon Sep 17 00:00:00 2001 +From: Clemens Ladisch +Date: Mon, 22 Jun 2009 10:01:59 +0200 +Subject: sound: seq_midi_event: fix decoding of (N)RPN events + +From: Clemens Ladisch + +commit 6423f9ea8035138d70bae1a278d3b57b743f8b3e upstream. + +When decoding (N)RPN sequencer events into raw MIDI commands, the +extra_decode_xrpn() function had accidentally swapped the MSB and LSB +controller values of both the parameter number and the data value. + +Signed-off-by: Clemens Ladisch +Signed-off-by: Takashi Iwai +Signed-off-by: Greg Kroah-Hartman + +--- + sound/core/seq/seq_midi_event.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +--- a/sound/core/seq/seq_midi_event.c ++++ b/sound/core/seq/seq_midi_event.c +@@ -504,10 +504,10 @@ static int extra_decode_xrpn(struct snd_ + if (dev->nostat && count < 12) + return -ENOMEM; + cmd = MIDI_CMD_CONTROL|(ev->data.control.channel & 0x0f); +- bytes[0] = ev->data.control.param & 0x007f; +- bytes[1] = (ev->data.control.param & 0x3f80) >> 7; +- bytes[2] = ev->data.control.value & 0x007f; +- bytes[3] = (ev->data.control.value & 0x3f80) >> 7; ++ bytes[0] = (ev->data.control.param & 0x3f80) >> 7; ++ bytes[1] = ev->data.control.param & 0x007f; ++ bytes[2] = (ev->data.control.value & 0x3f80) >> 7; ++ bytes[3] = ev->data.control.value & 0x007f; + if (cmd != dev->lastcmd && !dev->nostat) { + if (count < 9) + return -ENOMEM; diff --git a/queue-2.6.30/usb-serial-replace-shutdown-with-disconnect-release.patch b/queue-2.6.30/usb-serial-replace-shutdown-with-disconnect-release.patch new file mode 100644 index 00000000000..59b3a5cbaec --- /dev/null +++ b/queue-2.6.30/usb-serial-replace-shutdown-with-disconnect-release.patch @@ -0,0 +1,1604 @@ +From stern@rowland.harvard.edu Tue Jun 30 09:54:21 2009 +From: Alan Stern +Date: Tue, 30 Jun 2009 11:26:47 -0400 (EDT) +Subject: usb-serial: replace shutdown with disconnect, release +To: stable@kernel.org +Message-ID: + +From: Alan Stern + +commit f9c99bb8b3a1ec81af68d484a551307326c2e933 upstream + +This patch splits up the shutdown method of usb_serial_driver into a +disconnect and a release method. + +The problem is that the usb-serial core was calling shutdown during +disconnect handling, but drivers didn't expect it to be called until +after all the open file references had been closed. The result was an +oops when the close method tried to use memory that had been +deallocated by shutdown. + +Signed-off-by: Alan Stern +Signed-off-by: Greg Kroah-Hartman + +--- + drivers/staging/uc2322/aten2011.c | 4 +-- + drivers/usb/serial/aircable.c | 5 +--- + drivers/usb/serial/belkin_sa.c | 7 ++--- + drivers/usb/serial/cp210x.c | 6 ++-- + drivers/usb/serial/cyberjack.c | 20 ++++++++++++---- + drivers/usb/serial/cypress_m8.c | 11 ++++---- + drivers/usb/serial/digi_acceleport.c | 20 ++++++++++++---- + drivers/usb/serial/empeg.c | 8 ------ + drivers/usb/serial/ftdi_sio.c | 14 ----------- + drivers/usb/serial/garmin_gps.c | 16 ++++++++++-- + drivers/usb/serial/generic.c | 9 +++++-- + drivers/usb/serial/io_edgeport.c | 29 ++++++++++++++++------- + drivers/usb/serial/io_tables.h | 12 ++++++--- + drivers/usb/serial/io_ti.c | 22 +++++++++++++---- + drivers/usb/serial/ipaq.c | 7 ----- + drivers/usb/serial/iuu_phoenix.c | 6 ++-- + drivers/usb/serial/keyspan.c | 13 +++++++++- + drivers/usb/serial/keyspan.h | 12 ++++++--- + drivers/usb/serial/keyspan_pda.c | 4 +-- + drivers/usb/serial/kl5kusb105.c | 39 +++++++++++++++++-------------- + drivers/usb/serial/kobil_sct.c | 12 +++------ + drivers/usb/serial/mct_u232.c | 13 ++++------ + drivers/usb/serial/mos7720.c | 9 ++----- + drivers/usb/serial/mos7840.c | 42 +++++++++++++++++++++++++++++----- + drivers/usb/serial/omninet.c | 19 ++++++++++++--- + drivers/usb/serial/opticon.c | 14 ++++++++--- + drivers/usb/serial/option.c | 17 +++++++++---- + drivers/usb/serial/oti6858.c | 7 ++--- + drivers/usb/serial/pl2303.c | 5 +--- + drivers/usb/serial/sierra.c | 28 +++++++++++++++++++--- + drivers/usb/serial/spcp8x5.c | 5 +--- + drivers/usb/serial/symbolserial.c | 14 ++++++++--- + drivers/usb/serial/ti_usb_3410_5052.c | 10 +++----- + drivers/usb/serial/usb-serial.c | 29 ++++++++++------------- + drivers/usb/serial/visor.c | 13 ++++------ + drivers/usb/serial/whiteheat.c | 6 ++-- + include/linux/usb/serial.h | 12 ++++++--- + 37 files changed, 319 insertions(+), 200 deletions(-) + +--- a/drivers/staging/uc2322/aten2011.c ++++ b/drivers/staging/uc2322/aten2011.c +@@ -2336,7 +2336,7 @@ static int ATEN2011_startup(struct usb_s + return 0; + } + +-static void ATEN2011_shutdown(struct usb_serial *serial) ++static void ATEN2011_release(struct usb_serial *serial) + { + int i; + struct ATENINTL_port *ATEN2011_port; +@@ -2382,7 +2382,7 @@ static struct usb_serial_driver aten_ser + .tiocmget = ATEN2011_tiocmget, + .tiocmset = ATEN2011_tiocmset, + .attach = ATEN2011_startup, +- .shutdown = ATEN2011_shutdown, ++ .release = ATEN2011_release, + .read_bulk_callback = ATEN2011_bulk_in_callback, + .read_int_callback = ATEN2011_interrupt_callback, + }; +--- a/drivers/usb/serial/aircable.c ++++ b/drivers/usb/serial/aircable.c +@@ -364,7 +364,7 @@ static int aircable_attach(struct usb_se + return 0; + } + +-static void aircable_shutdown(struct usb_serial *serial) ++static void aircable_release(struct usb_serial *serial) + { + + struct usb_serial_port *port = serial->port[0]; +@@ -375,7 +375,6 @@ static void aircable_shutdown(struct usb + if (priv) { + serial_buf_free(priv->tx_buf); + serial_buf_free(priv->rx_buf); +- usb_set_serial_port_data(port, NULL); + kfree(priv); + } + } +@@ -601,7 +600,7 @@ static struct usb_serial_driver aircable + .num_ports = 1, + .attach = aircable_attach, + .probe = aircable_probe, +- .shutdown = aircable_shutdown, ++ .release = aircable_release, + .write = aircable_write, + .write_room = aircable_write_room, + .write_bulk_callback = aircable_write_bulk_callback, +--- a/drivers/usb/serial/belkin_sa.c ++++ b/drivers/usb/serial/belkin_sa.c +@@ -90,7 +90,7 @@ static int debug; + + /* function prototypes for a Belkin USB Serial Adapter F5U103 */ + static int belkin_sa_startup(struct usb_serial *serial); +-static void belkin_sa_shutdown(struct usb_serial *serial); ++static void belkin_sa_release(struct usb_serial *serial); + static int belkin_sa_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp); + static void belkin_sa_close(struct tty_struct *tty, +@@ -143,7 +143,7 @@ static struct usb_serial_driver belkin_d + .tiocmget = belkin_sa_tiocmget, + .tiocmset = belkin_sa_tiocmset, + .attach = belkin_sa_startup, +- .shutdown = belkin_sa_shutdown, ++ .release = belkin_sa_release, + }; + + +@@ -198,14 +198,13 @@ static int belkin_sa_startup(struct usb_ + } + + +-static void belkin_sa_shutdown(struct usb_serial *serial) ++static void belkin_sa_release(struct usb_serial *serial) + { + struct belkin_sa_private *priv; + int i; + + dbg("%s", __func__); + +- /* stop reads and writes on all ports */ + for (i = 0; i < serial->num_ports; ++i) { + /* My special items, the standard routines free my urbs */ + priv = usb_get_serial_port_data(serial->port[i]); +--- a/drivers/usb/serial/cp210x.c ++++ b/drivers/usb/serial/cp210x.c +@@ -51,7 +51,7 @@ static int cp2101_tiocmset_port(struct u + unsigned int, unsigned int); + static void cp2101_break_ctl(struct tty_struct *, int); + static int cp2101_startup(struct usb_serial *); +-static void cp2101_shutdown(struct usb_serial *); ++static void cp2101_disconnect(struct usb_serial *); + + static int debug; + +@@ -131,7 +131,7 @@ static struct usb_serial_driver cp2101_d + .tiocmget = cp2101_tiocmget, + .tiocmset = cp2101_tiocmset, + .attach = cp2101_startup, +- .shutdown = cp2101_shutdown, ++ .disconnect = cp2101_disconnect, + }; + + /* Config request types */ +@@ -773,7 +773,7 @@ static int cp2101_startup(struct usb_ser + return 0; + } + +-static void cp2101_shutdown(struct usb_serial *serial) ++static void cp2101_disconnect(struct usb_serial *serial) + { + int i; + +--- a/drivers/usb/serial/cyberjack.c ++++ b/drivers/usb/serial/cyberjack.c +@@ -58,7 +58,8 @@ static int debug; + + /* Function prototypes */ + static int cyberjack_startup(struct usb_serial *serial); +-static void cyberjack_shutdown(struct usb_serial *serial); ++static void cyberjack_disconnect(struct usb_serial *serial); ++static void cyberjack_release(struct usb_serial *serial); + static int cyberjack_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp); + static void cyberjack_close(struct tty_struct *tty, +@@ -95,7 +96,8 @@ static struct usb_serial_driver cyberjac + .id_table = id_table, + .num_ports = 1, + .attach = cyberjack_startup, +- .shutdown = cyberjack_shutdown, ++ .disconnect = cyberjack_disconnect, ++ .release = cyberjack_release, + .open = cyberjack_open, + .close = cyberjack_close, + .write = cyberjack_write, +@@ -149,17 +151,25 @@ static int cyberjack_startup(struct usb_ + return 0; + } + +-static void cyberjack_shutdown(struct usb_serial *serial) ++static void cyberjack_disconnect(struct usb_serial *serial) + { + int i; + + dbg("%s", __func__); + +- for (i = 0; i < serial->num_ports; ++i) { ++ for (i = 0; i < serial->num_ports; ++i) + usb_kill_urb(serial->port[i]->interrupt_in_urb); ++} ++ ++static void cyberjack_release(struct usb_serial *serial) ++{ ++ int i; ++ ++ dbg("%s", __func__); ++ ++ for (i = 0; i < serial->num_ports; ++i) { + /* My special items, the standard routines free my urbs */ + kfree(usb_get_serial_port_data(serial->port[i])); +- usb_set_serial_port_data(serial->port[i], NULL); + } + } + +--- a/drivers/usb/serial/cypress_m8.c ++++ b/drivers/usb/serial/cypress_m8.c +@@ -171,7 +171,7 @@ struct cypress_buf { + static int cypress_earthmate_startup(struct usb_serial *serial); + static int cypress_hidcom_startup(struct usb_serial *serial); + static int cypress_ca42v2_startup(struct usb_serial *serial); +-static void cypress_shutdown(struct usb_serial *serial); ++static void cypress_release(struct usb_serial *serial); + static int cypress_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp); + static void cypress_close(struct tty_struct *tty, +@@ -215,7 +215,7 @@ static struct usb_serial_driver cypress_ + .id_table = id_table_earthmate, + .num_ports = 1, + .attach = cypress_earthmate_startup, +- .shutdown = cypress_shutdown, ++ .release = cypress_release, + .open = cypress_open, + .close = cypress_close, + .write = cypress_write, +@@ -241,7 +241,7 @@ static struct usb_serial_driver cypress_ + .id_table = id_table_cyphidcomrs232, + .num_ports = 1, + .attach = cypress_hidcom_startup, +- .shutdown = cypress_shutdown, ++ .release = cypress_release, + .open = cypress_open, + .close = cypress_close, + .write = cypress_write, +@@ -267,7 +267,7 @@ static struct usb_serial_driver cypress_ + .id_table = id_table_nokiaca42v2, + .num_ports = 1, + .attach = cypress_ca42v2_startup, +- .shutdown = cypress_shutdown, ++ .release = cypress_release, + .open = cypress_open, + .close = cypress_close, + .write = cypress_write, +@@ -613,7 +613,7 @@ static int cypress_ca42v2_startup(struct + } /* cypress_ca42v2_startup */ + + +-static void cypress_shutdown(struct usb_serial *serial) ++static void cypress_release(struct usb_serial *serial) + { + struct cypress_private *priv; + +@@ -626,7 +626,6 @@ static void cypress_shutdown(struct usb_ + if (priv) { + cypress_buf_free(priv->buf); + kfree(priv); +- usb_set_serial_port_data(serial->port[0], NULL); + } + } + +--- a/drivers/usb/serial/digi_acceleport.c ++++ b/drivers/usb/serial/digi_acceleport.c +@@ -460,7 +460,8 @@ static void digi_close(struct tty_struct + struct file *filp); + static int digi_startup_device(struct usb_serial *serial); + static int digi_startup(struct usb_serial *serial); +-static void digi_shutdown(struct usb_serial *serial); ++static void digi_disconnect(struct usb_serial *serial); ++static void digi_release(struct usb_serial *serial); + static void digi_read_bulk_callback(struct urb *urb); + static int digi_read_inb_callback(struct urb *urb); + static int digi_read_oob_callback(struct urb *urb); +@@ -522,7 +523,8 @@ static struct usb_serial_driver digi_acc + .tiocmget = digi_tiocmget, + .tiocmset = digi_tiocmset, + .attach = digi_startup, +- .shutdown = digi_shutdown, ++ .disconnect = digi_disconnect, ++ .release = digi_release, + }; + + static struct usb_serial_driver digi_acceleport_4_device = { +@@ -548,7 +550,8 @@ static struct usb_serial_driver digi_acc + .tiocmget = digi_tiocmget, + .tiocmset = digi_tiocmset, + .attach = digi_startup, +- .shutdown = digi_shutdown, ++ .disconnect = digi_disconnect, ++ .release = digi_release, + }; + + +@@ -1589,16 +1592,23 @@ static int digi_startup(struct usb_seria + } + + +-static void digi_shutdown(struct usb_serial *serial) ++static void digi_disconnect(struct usb_serial *serial) + { + int i; +- dbg("digi_shutdown: TOP, in_interrupt()=%ld", in_interrupt()); ++ dbg("digi_disconnect: TOP, in_interrupt()=%ld", in_interrupt()); + + /* stop reads and writes on all ports */ + for (i = 0; i < serial->type->num_ports + 1; i++) { + usb_kill_urb(serial->port[i]->read_urb); + usb_kill_urb(serial->port[i]->write_urb); + } ++} ++ ++ ++static void digi_release(struct usb_serial *serial) ++{ ++ int i; ++ dbg("digi_release: TOP, in_interrupt()=%ld", in_interrupt()); + + /* free the private data structures for all ports */ + /* number of regular ports + 1 for the out-of-band port */ +--- a/drivers/usb/serial/empeg.c ++++ b/drivers/usb/serial/empeg.c +@@ -91,7 +91,6 @@ static int empeg_chars_in_buffer(struct + static void empeg_throttle(struct tty_struct *tty); + static void empeg_unthrottle(struct tty_struct *tty); + static int empeg_startup(struct usb_serial *serial); +-static void empeg_shutdown(struct usb_serial *serial); + static void empeg_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios); + static void empeg_write_bulk_callback(struct urb *urb); +@@ -125,7 +124,6 @@ static struct usb_serial_driver empeg_de + .throttle = empeg_throttle, + .unthrottle = empeg_unthrottle, + .attach = empeg_startup, +- .shutdown = empeg_shutdown, + .set_termios = empeg_set_termios, + .write = empeg_write, + .write_room = empeg_write_room, +@@ -429,12 +427,6 @@ static int empeg_startup(struct usb_ser + } + + +-static void empeg_shutdown(struct usb_serial *serial) +-{ +- dbg("%s", __func__); +-} +- +- + static void empeg_set_termios(struct tty_struct *tty, + struct usb_serial_port *port, struct ktermios *old_termios) + { +--- a/drivers/usb/serial/ftdi_sio.c ++++ b/drivers/usb/serial/ftdi_sio.c +@@ -714,7 +714,6 @@ static const char *ftdi_chip_name[] = { + /* function prototypes for a FTDI serial converter */ + static int ftdi_sio_probe(struct usb_serial *serial, + const struct usb_device_id *id); +-static void ftdi_shutdown(struct usb_serial *serial); + static int ftdi_sio_port_probe(struct usb_serial_port *port); + static int ftdi_sio_port_remove(struct usb_serial_port *port); + static int ftdi_open(struct tty_struct *tty, +@@ -770,7 +769,6 @@ static struct usb_serial_driver ftdi_sio + .ioctl = ftdi_ioctl, + .set_termios = ftdi_set_termios, + .break_ctl = ftdi_break_ctl, +- .shutdown = ftdi_shutdown, + }; + + +@@ -1460,18 +1458,6 @@ static int ftdi_mtxorb_hack_setup(struct + return 0; + } + +-/* ftdi_shutdown is called from usbserial:usb_serial_disconnect +- * it is called when the usb device is disconnected +- * +- * usbserial:usb_serial_disconnect +- * calls __serial_close for each open of the port +- * shutdown is called then (ie ftdi_shutdown) +- */ +-static void ftdi_shutdown(struct usb_serial *serial) +-{ +- dbg("%s", __func__); +-} +- + static void ftdi_sio_priv_release(struct kref *k) + { + struct ftdi_private *priv = container_of(k, struct ftdi_private, kref); +--- a/drivers/usb/serial/garmin_gps.c ++++ b/drivers/usb/serial/garmin_gps.c +@@ -1528,7 +1528,7 @@ static int garmin_attach(struct usb_seri + } + + +-static void garmin_shutdown(struct usb_serial *serial) ++static void garmin_disconnect(struct usb_serial *serial) + { + struct usb_serial_port *port = serial->port[0]; + struct garmin_data *garmin_data_p = usb_get_serial_port_data(port); +@@ -1537,8 +1537,17 @@ static void garmin_shutdown(struct usb_s + + usb_kill_urb(port->interrupt_in_urb); + del_timer_sync(&garmin_data_p->timer); ++} ++ ++ ++static void garmin_release(struct usb_serial *serial) ++{ ++ struct usb_serial_port *port = serial->port[0]; ++ struct garmin_data *garmin_data_p = usb_get_serial_port_data(port); ++ ++ dbg("%s", __func__); ++ + kfree(garmin_data_p); +- usb_set_serial_port_data(port, NULL); + } + + +@@ -1557,7 +1566,8 @@ static struct usb_serial_driver garmin_d + .throttle = garmin_throttle, + .unthrottle = garmin_unthrottle, + .attach = garmin_attach, +- .shutdown = garmin_shutdown, ++ .disconnect = garmin_disconnect, ++ .release = garmin_release, + .write = garmin_write, + .write_room = garmin_write_room, + .write_bulk_callback = garmin_write_bulk_callback, +--- a/drivers/usb/serial/generic.c ++++ b/drivers/usb/serial/generic.c +@@ -63,7 +63,8 @@ struct usb_serial_driver usb_serial_gene + .id_table = generic_device_ids, + .usb_driver = &generic_driver, + .num_ports = 1, +- .shutdown = usb_serial_generic_shutdown, ++ .disconnect = usb_serial_generic_disconnect, ++ .release = usb_serial_generic_release, + .throttle = usb_serial_generic_throttle, + .unthrottle = usb_serial_generic_unthrottle, + .resume = usb_serial_generic_resume, +@@ -413,7 +414,7 @@ void usb_serial_generic_unthrottle(struc + } + } + +-void usb_serial_generic_shutdown(struct usb_serial *serial) ++void usb_serial_generic_disconnect(struct usb_serial *serial) + { + int i; + +@@ -424,3 +425,7 @@ void usb_serial_generic_shutdown(struct + generic_cleanup(serial->port[i]); + } + ++void usb_serial_generic_release(struct usb_serial *serial) ++{ ++ dbg("%s", __func__); ++} +--- a/drivers/usb/serial/io_edgeport.c ++++ b/drivers/usb/serial/io_edgeport.c +@@ -225,7 +225,8 @@ static int edge_tiocmget(struct tty_str + static int edge_tiocmset(struct tty_struct *tty, struct file *file, + unsigned int set, unsigned int clear); + static int edge_startup(struct usb_serial *serial); +-static void edge_shutdown(struct usb_serial *serial); ++static void edge_disconnect(struct usb_serial *serial); ++static void edge_release(struct usb_serial *serial); + + #include "io_tables.h" /* all of the devices that this driver supports */ + +@@ -3195,21 +3196,16 @@ static int edge_startup(struct usb_seria + + + /**************************************************************************** +- * edge_shutdown ++ * edge_disconnect + * This function is called whenever the device is removed from the usb bus. + ****************************************************************************/ +-static void edge_shutdown(struct usb_serial *serial) ++static void edge_disconnect(struct usb_serial *serial) + { + struct edgeport_serial *edge_serial = usb_get_serial_data(serial); +- int i; + + dbg("%s", __func__); + + /* stop reads and writes on all ports */ +- for (i = 0; i < serial->num_ports; ++i) { +- kfree(usb_get_serial_port_data(serial->port[i])); +- usb_set_serial_port_data(serial->port[i], NULL); +- } + /* free up our endpoint stuff */ + if (edge_serial->is_epic) { + usb_kill_urb(edge_serial->interrupt_read_urb); +@@ -3220,9 +3216,24 @@ static void edge_shutdown(struct usb_ser + usb_free_urb(edge_serial->read_urb); + kfree(edge_serial->bulk_in_buffer); + } ++} ++ ++ ++/**************************************************************************** ++ * edge_release ++ * This function is called when the device structure is deallocated. ++ ****************************************************************************/ ++static void edge_release(struct usb_serial *serial) ++{ ++ struct edgeport_serial *edge_serial = usb_get_serial_data(serial); ++ int i; ++ ++ dbg("%s", __func__); ++ ++ for (i = 0; i < serial->num_ports; ++i) ++ kfree(usb_get_serial_port_data(serial->port[i])); + + kfree(edge_serial); +- usb_set_serial_data(serial, NULL); + } + + +--- a/drivers/usb/serial/io_tables.h ++++ b/drivers/usb/serial/io_tables.h +@@ -117,7 +117,8 @@ static struct usb_serial_driver edgeport + .throttle = edge_throttle, + .unthrottle = edge_unthrottle, + .attach = edge_startup, +- .shutdown = edge_shutdown, ++ .disconnect = edge_disconnect, ++ .release = edge_release, + .ioctl = edge_ioctl, + .set_termios = edge_set_termios, + .tiocmget = edge_tiocmget, +@@ -145,7 +146,8 @@ static struct usb_serial_driver edgeport + .throttle = edge_throttle, + .unthrottle = edge_unthrottle, + .attach = edge_startup, +- .shutdown = edge_shutdown, ++ .disconnect = edge_disconnect, ++ .release = edge_release, + .ioctl = edge_ioctl, + .set_termios = edge_set_termios, + .tiocmget = edge_tiocmget, +@@ -173,7 +175,8 @@ static struct usb_serial_driver edgeport + .throttle = edge_throttle, + .unthrottle = edge_unthrottle, + .attach = edge_startup, +- .shutdown = edge_shutdown, ++ .disconnect = edge_disconnect, ++ .release = edge_release, + .ioctl = edge_ioctl, + .set_termios = edge_set_termios, + .tiocmget = edge_tiocmget, +@@ -200,7 +203,8 @@ static struct usb_serial_driver epic_dev + .throttle = edge_throttle, + .unthrottle = edge_unthrottle, + .attach = edge_startup, +- .shutdown = edge_shutdown, ++ .disconnect = edge_disconnect, ++ .release = edge_release, + .ioctl = edge_ioctl, + .set_termios = edge_set_termios, + .tiocmget = edge_tiocmget, +--- a/drivers/usb/serial/io_ti.c ++++ b/drivers/usb/serial/io_ti.c +@@ -2664,7 +2664,7 @@ cleanup: + return -ENOMEM; + } + +-static void edge_shutdown(struct usb_serial *serial) ++static void edge_disconnect(struct usb_serial *serial) + { + int i; + struct edgeport_port *edge_port; +@@ -2674,12 +2674,22 @@ static void edge_shutdown(struct usb_ser + for (i = 0; i < serial->num_ports; ++i) { + edge_port = usb_get_serial_port_data(serial->port[i]); + edge_remove_sysfs_attrs(edge_port->port); ++ } ++} ++ ++static void edge_release(struct usb_serial *serial) ++{ ++ int i; ++ struct edgeport_port *edge_port; ++ ++ dbg("%s", __func__); ++ ++ for (i = 0; i < serial->num_ports; ++i) { ++ edge_port = usb_get_serial_port_data(serial->port[i]); + edge_buf_free(edge_port->ep_out_buf); + kfree(edge_port); +- usb_set_serial_port_data(serial->port[i], NULL); + } + kfree(usb_get_serial_data(serial)); +- usb_set_serial_data(serial, NULL); + } + + +@@ -2916,7 +2926,8 @@ static struct usb_serial_driver edgeport + .throttle = edge_throttle, + .unthrottle = edge_unthrottle, + .attach = edge_startup, +- .shutdown = edge_shutdown, ++ .disconnect = edge_disconnect, ++ .release = edge_release, + .port_probe = edge_create_sysfs_attrs, + .ioctl = edge_ioctl, + .set_termios = edge_set_termios, +@@ -2945,7 +2956,8 @@ static struct usb_serial_driver edgeport + .throttle = edge_throttle, + .unthrottle = edge_unthrottle, + .attach = edge_startup, +- .shutdown = edge_shutdown, ++ .disconnect = edge_disconnect, ++ .release = edge_release, + .port_probe = edge_create_sysfs_attrs, + .ioctl = edge_ioctl, + .set_termios = edge_set_termios, +--- a/drivers/usb/serial/ipaq.c ++++ b/drivers/usb/serial/ipaq.c +@@ -80,7 +80,6 @@ static void ipaq_close(struct tty_struct + struct usb_serial_port *port, struct file *filp); + static int ipaq_calc_num_ports(struct usb_serial *serial); + static int ipaq_startup(struct usb_serial *serial); +-static void ipaq_shutdown(struct usb_serial *serial); + static int ipaq_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count); + static int ipaq_write_bulk(struct usb_serial_port *port, +@@ -577,7 +576,6 @@ static struct usb_serial_driver ipaq_dev + .close = ipaq_close, + .attach = ipaq_startup, + .calc_num_ports = ipaq_calc_num_ports, +- .shutdown = ipaq_shutdown, + .write = ipaq_write, + .write_room = ipaq_write_room, + .chars_in_buffer = ipaq_chars_in_buffer, +@@ -992,11 +990,6 @@ static int ipaq_startup(struct usb_seria + return usb_reset_configuration(serial->dev); + } + +-static void ipaq_shutdown(struct usb_serial *serial) +-{ +- dbg("%s", __func__); +-} +- + static int __init ipaq_init(void) + { + int retval; +--- a/drivers/usb/serial/iuu_phoenix.c ++++ b/drivers/usb/serial/iuu_phoenix.c +@@ -122,8 +122,8 @@ static int iuu_startup(struct usb_serial + return 0; + } + +-/* Shutdown function */ +-static void iuu_shutdown(struct usb_serial *serial) ++/* Release function */ ++static void iuu_release(struct usb_serial *serial) + { + struct usb_serial_port *port = serial->port[0]; + struct iuu_private *priv = usb_get_serial_port_data(port); +@@ -1176,7 +1176,7 @@ static struct usb_serial_driver iuu_devi + .tiocmget = iuu_tiocmget, + .tiocmset = iuu_tiocmset, + .attach = iuu_startup, +- .shutdown = iuu_shutdown, ++ .release = iuu_release, + }; + + static int __init iuu_init(void) +--- a/drivers/usb/serial/keyspan.c ++++ b/drivers/usb/serial/keyspan.c +@@ -2682,7 +2682,7 @@ static int keyspan_startup(struct usb_se + return 0; + } + +-static void keyspan_shutdown(struct usb_serial *serial) ++static void keyspan_disconnect(struct usb_serial *serial) + { + int i, j; + struct usb_serial_port *port; +@@ -2722,6 +2722,17 @@ static void keyspan_shutdown(struct usb_ + usb_free_urb(p_priv->out_urbs[j]); + } + } ++} ++ ++static void keyspan_release(struct usb_serial *serial) ++{ ++ int i; ++ struct usb_serial_port *port; ++ struct keyspan_serial_private *s_priv; ++ ++ dbg("%s", __func__); ++ ++ s_priv = usb_get_serial_data(serial); + + /* dbg("Freeing serial->private."); */ + kfree(s_priv); +--- a/drivers/usb/serial/keyspan.h ++++ b/drivers/usb/serial/keyspan.h +@@ -42,7 +42,8 @@ static void keyspan_close (struct tty_s + struct usb_serial_port *port, + struct file *filp); + static int keyspan_startup (struct usb_serial *serial); +-static void keyspan_shutdown (struct usb_serial *serial); ++static void keyspan_disconnect (struct usb_serial *serial); ++static void keyspan_release (struct usb_serial *serial); + static int keyspan_write_room (struct tty_struct *tty); + + static int keyspan_write (struct tty_struct *tty, +@@ -569,7 +570,8 @@ static struct usb_serial_driver keyspan_ + .tiocmget = keyspan_tiocmget, + .tiocmset = keyspan_tiocmset, + .attach = keyspan_startup, +- .shutdown = keyspan_shutdown, ++ .disconnect = keyspan_disconnect, ++ .release = keyspan_release, + }; + + static struct usb_serial_driver keyspan_2port_device = { +@@ -589,7 +591,8 @@ static struct usb_serial_driver keyspan_ + .tiocmget = keyspan_tiocmget, + .tiocmset = keyspan_tiocmset, + .attach = keyspan_startup, +- .shutdown = keyspan_shutdown, ++ .disconnect = keyspan_disconnect, ++ .release = keyspan_release, + }; + + static struct usb_serial_driver keyspan_4port_device = { +@@ -609,7 +612,8 @@ static struct usb_serial_driver keyspan_ + .tiocmget = keyspan_tiocmget, + .tiocmset = keyspan_tiocmset, + .attach = keyspan_startup, +- .shutdown = keyspan_shutdown, ++ .disconnect = keyspan_disconnect, ++ .release = keyspan_release, + }; + + #endif +--- a/drivers/usb/serial/keyspan_pda.c ++++ b/drivers/usb/serial/keyspan_pda.c +@@ -795,7 +795,7 @@ static int keyspan_pda_startup(struct us + return 0; + } + +-static void keyspan_pda_shutdown(struct usb_serial *serial) ++static void keyspan_pda_release(struct usb_serial *serial) + { + dbg("%s", __func__); + +@@ -853,7 +853,7 @@ static struct usb_serial_driver keyspan_ + .tiocmget = keyspan_pda_tiocmget, + .tiocmset = keyspan_pda_tiocmset, + .attach = keyspan_pda_startup, +- .shutdown = keyspan_pda_shutdown, ++ .release = keyspan_pda_release, + }; + + +--- a/drivers/usb/serial/kl5kusb105.c ++++ b/drivers/usb/serial/kl5kusb105.c +@@ -73,7 +73,8 @@ static int debug; + * Function prototypes + */ + static int klsi_105_startup(struct usb_serial *serial); +-static void klsi_105_shutdown(struct usb_serial *serial); ++static void klsi_105_disconnect(struct usb_serial *serial); ++static void klsi_105_release(struct usb_serial *serial); + static int klsi_105_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp); + static void klsi_105_close(struct tty_struct *tty, +@@ -132,7 +133,8 @@ static struct usb_serial_driver kl5kusb1 + .tiocmget = klsi_105_tiocmget, + .tiocmset = klsi_105_tiocmset, + .attach = klsi_105_startup, +- .shutdown = klsi_105_shutdown, ++ .disconnect = klsi_105_disconnect, ++ .release = klsi_105_release, + .throttle = klsi_105_throttle, + .unthrottle = klsi_105_unthrottle, + }; +@@ -316,7 +318,7 @@ err_cleanup: + } /* klsi_105_startup */ + + +-static void klsi_105_shutdown(struct usb_serial *serial) ++static void klsi_105_disconnect(struct usb_serial *serial) + { + int i; + +@@ -326,33 +328,36 @@ static void klsi_105_shutdown(struct usb + for (i = 0; i < serial->num_ports; ++i) { + struct klsi_105_private *priv = + usb_get_serial_port_data(serial->port[i]); +- unsigned long flags; + + if (priv) { + /* kill our write urb pool */ + int j; + struct urb **write_urbs = priv->write_urb_pool; +- spin_lock_irqsave(&priv->lock, flags); + + for (j = 0; j < NUM_URBS; j++) { + if (write_urbs[j]) { +- /* FIXME - uncomment the following +- * usb_kill_urb call when the host +- * controllers get fixed to set +- * urb->dev = NULL after the urb is +- * finished. Otherwise this call +- * oopses. */ +- /* usb_kill_urb(write_urbs[j]); */ +- kfree(write_urbs[j]->transfer_buffer); ++ usb_kill_urb(write_urbs[j]); + usb_free_urb(write_urbs[j]); + } + } +- spin_unlock_irqrestore(&priv->lock, flags); +- kfree(priv); +- usb_set_serial_port_data(serial->port[i], NULL); + } + } +-} /* klsi_105_shutdown */ ++} /* klsi_105_disconnect */ ++ ++ ++static void klsi_105_release(struct usb_serial *serial) ++{ ++ int i; ++ ++ dbg("%s", __func__); ++ ++ for (i = 0; i < serial->num_ports; ++i) { ++ struct klsi_105_private *priv = ++ usb_get_serial_port_data(serial->port[i]); ++ ++ kfree(priv); ++ } ++} /* klsi_105_release */ + + static int klsi_105_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) +--- a/drivers/usb/serial/kobil_sct.c ++++ b/drivers/usb/serial/kobil_sct.c +@@ -69,7 +69,7 @@ static int debug; + + /* Function prototypes */ + static int kobil_startup(struct usb_serial *serial); +-static void kobil_shutdown(struct usb_serial *serial); ++static void kobil_release(struct usb_serial *serial); + static int kobil_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp); + static void kobil_close(struct tty_struct *tty, struct usb_serial_port *port, +@@ -118,7 +118,7 @@ static struct usb_serial_driver kobil_de + .id_table = id_table, + .num_ports = 1, + .attach = kobil_startup, +- .shutdown = kobil_shutdown, ++ .release = kobil_release, + .ioctl = kobil_ioctl, + .set_termios = kobil_set_termios, + .tiocmget = kobil_tiocmget, +@@ -202,17 +202,13 @@ static int kobil_startup(struct usb_seri + } + + +-static void kobil_shutdown(struct usb_serial *serial) ++static void kobil_release(struct usb_serial *serial) + { + int i; + dbg("%s - port %d", __func__, serial->port[0]->number); + +- for (i = 0; i < serial->num_ports; ++i) { +- while (serial->port[i]->port.count > 0) +- kobil_close(NULL, serial->port[i], NULL); ++ for (i = 0; i < serial->num_ports; ++i) + kfree(usb_get_serial_port_data(serial->port[i])); +- usb_set_serial_port_data(serial->port[i], NULL); +- } + } + + +--- a/drivers/usb/serial/mct_u232.c ++++ b/drivers/usb/serial/mct_u232.c +@@ -92,7 +92,7 @@ static int debug; + * Function prototypes + */ + static int mct_u232_startup(struct usb_serial *serial); +-static void mct_u232_shutdown(struct usb_serial *serial); ++static void mct_u232_release(struct usb_serial *serial); + static int mct_u232_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp); + static void mct_u232_close(struct tty_struct *tty, +@@ -148,7 +148,7 @@ static struct usb_serial_driver mct_u232 + .tiocmget = mct_u232_tiocmget, + .tiocmset = mct_u232_tiocmset, + .attach = mct_u232_startup, +- .shutdown = mct_u232_shutdown, ++ .release = mct_u232_release, + }; + + +@@ -406,7 +406,7 @@ static int mct_u232_startup(struct usb_s + } /* mct_u232_startup */ + + +-static void mct_u232_shutdown(struct usb_serial *serial) ++static void mct_u232_release(struct usb_serial *serial) + { + struct mct_u232_private *priv; + int i; +@@ -416,12 +416,9 @@ static void mct_u232_shutdown(struct usb + for (i = 0; i < serial->num_ports; ++i) { + /* My special items, the standard routines free my urbs */ + priv = usb_get_serial_port_data(serial->port[i]); +- if (priv) { +- usb_set_serial_port_data(serial->port[i], NULL); +- kfree(priv); +- } ++ kfree(priv); + } +-} /* mct_u232_shutdown */ ++} /* mct_u232_release */ + + static int mct_u232_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp) +--- a/drivers/usb/serial/mos7720.c ++++ b/drivers/usb/serial/mos7720.c +@@ -1522,19 +1522,16 @@ static int mos7720_startup(struct usb_se + return 0; + } + +-static void mos7720_shutdown(struct usb_serial *serial) ++static void mos7720_release(struct usb_serial *serial) + { + int i; + + /* free private structure allocated for serial port */ +- for (i = 0; i < serial->num_ports; ++i) { ++ for (i = 0; i < serial->num_ports; ++i) + kfree(usb_get_serial_port_data(serial->port[i])); +- usb_set_serial_port_data(serial->port[i], NULL); +- } + + /* free private structure allocated for serial device */ + kfree(usb_get_serial_data(serial)); +- usb_set_serial_data(serial, NULL); + } + + static struct usb_driver usb_driver = { +@@ -1559,7 +1556,7 @@ static struct usb_serial_driver moschip7 + .throttle = mos7720_throttle, + .unthrottle = mos7720_unthrottle, + .attach = mos7720_startup, +- .shutdown = mos7720_shutdown, ++ .release = mos7720_release, + .ioctl = mos7720_ioctl, + .set_termios = mos7720_set_termios, + .write = mos7720_write, +--- a/drivers/usb/serial/mos7840.c ++++ b/drivers/usb/serial/mos7840.c +@@ -2673,16 +2673,16 @@ error: + } + + /**************************************************************************** +- * mos7840_shutdown ++ * mos7840_disconnect + * This function is called whenever the device is removed from the usb bus. + ****************************************************************************/ + +-static void mos7840_shutdown(struct usb_serial *serial) ++static void mos7840_disconnect(struct usb_serial *serial) + { + int i; + unsigned long flags; + struct moschip_port *mos7840_port; +- dbg("%s \n", " shutdown :entering.........."); ++ dbg("%s \n", " disconnect :entering.........."); + + if (!serial) { + dbg("%s", "Invalid Handler \n"); +@@ -2702,11 +2702,42 @@ static void mos7840_shutdown(struct usb_ + mos7840_port->zombie = 1; + spin_unlock_irqrestore(&mos7840_port->pool_lock, flags); + usb_kill_urb(mos7840_port->control_urb); ++ } ++ } ++ ++ dbg("%s", "Thank u :: "); ++ ++} ++ ++/**************************************************************************** ++ * mos7840_release ++ * This function is called when the usb_serial structure is freed. ++ ****************************************************************************/ ++ ++static void mos7840_release(struct usb_serial *serial) ++{ ++ int i; ++ struct moschip_port *mos7840_port; ++ dbg("%s", " release :entering.........."); ++ ++ if (!serial) { ++ dbg("%s", "Invalid Handler"); ++ return; ++ } ++ ++ /* check for the ports to be closed,close the ports and disconnect */ ++ ++ /* free private structure allocated for serial port * ++ * stop reads and writes on all ports */ ++ ++ for (i = 0; i < serial->num_ports; ++i) { ++ mos7840_port = mos7840_get_port_private(serial->port[i]); ++ dbg("mos7840_port %d = %p", i, mos7840_port); ++ if (mos7840_port) { + kfree(mos7840_port->ctrl_buf); + kfree(mos7840_port->dr); + kfree(mos7840_port); + } +- mos7840_set_port_private(serial->port[i], NULL); + } + + dbg("%s\n", "Thank u :: "); +@@ -2747,7 +2778,8 @@ static struct usb_serial_driver moschip7 + .tiocmget = mos7840_tiocmget, + .tiocmset = mos7840_tiocmset, + .attach = mos7840_startup, +- .shutdown = mos7840_shutdown, ++ .disconnect = mos7840_disconnect, ++ .release = mos7840_release, + .read_bulk_callback = mos7840_bulk_in_callback, + .read_int_callback = mos7840_interrupt_callback, + }; +--- a/drivers/usb/serial/omninet.c ++++ b/drivers/usb/serial/omninet.c +@@ -73,7 +73,8 @@ static void omninet_write_bulk_callback( + static int omninet_write(struct tty_struct *tty, struct usb_serial_port *port, + const unsigned char *buf, int count); + static int omninet_write_room(struct tty_struct *tty); +-static void omninet_shutdown(struct usb_serial *serial); ++static void omninet_disconnect(struct usb_serial *serial); ++static void omninet_release(struct usb_serial *serial); + static int omninet_attach(struct usb_serial *serial); + + static struct usb_device_id id_table[] = { +@@ -109,7 +110,8 @@ static struct usb_serial_driver zyxel_om + .write_room = omninet_write_room, + .read_bulk_callback = omninet_read_bulk_callback, + .write_bulk_callback = omninet_write_bulk_callback, +- .shutdown = omninet_shutdown, ++ .disconnect = omninet_disconnect, ++ .release = omninet_release, + }; + + +@@ -347,13 +349,22 @@ static void omninet_write_bulk_callback( + } + + +-static void omninet_shutdown(struct usb_serial *serial) ++static void omninet_disconnect(struct usb_serial *serial) + { + struct usb_serial_port *wport = serial->port[1]; +- struct usb_serial_port *port = serial->port[0]; ++ + dbg("%s", __func__); + + usb_kill_urb(wport->write_urb); ++} ++ ++ ++static void omninet_release(struct usb_serial *serial) ++{ ++ struct usb_serial_port *port = serial->port[0]; ++ ++ dbg("%s", __func__); ++ + kfree(usb_get_serial_port_data(port)); + } + +--- a/drivers/usb/serial/opticon.c ++++ b/drivers/usb/serial/opticon.c +@@ -464,7 +464,7 @@ error: + return retval; + } + +-static void opticon_shutdown(struct usb_serial *serial) ++static void opticon_disconnect(struct usb_serial *serial) + { + struct opticon_private *priv = usb_get_serial_data(serial); + +@@ -472,9 +472,16 @@ static void opticon_shutdown(struct usb_ + + usb_kill_urb(priv->bulk_read_urb); + usb_free_urb(priv->bulk_read_urb); ++} ++ ++static void opticon_release(struct usb_serial *serial) ++{ ++ struct opticon_private *priv = usb_get_serial_data(serial); ++ ++ dbg("%s", __func__); ++ + kfree(priv->bulk_in_buffer); + kfree(priv); +- usb_set_serial_data(serial, NULL); + } + + static int opticon_suspend(struct usb_interface *intf, pm_message_t message) +@@ -525,7 +532,8 @@ static struct usb_serial_driver opticon_ + .close = opticon_close, + .write = opticon_write, + .write_room = opticon_write_room, +- .shutdown = opticon_shutdown, ++ .disconnect = opticon_disconnect, ++ .release = opticon_release, + .throttle = opticon_throttle, + .unthrottle = opticon_unthrottle, + .ioctl = opticon_ioctl, +--- a/drivers/usb/serial/option.c ++++ b/drivers/usb/serial/option.c +@@ -48,7 +48,8 @@ static int option_open(struct tty_struc + static void option_close(struct tty_struct *tty, struct usb_serial_port *port, + struct file *filp); + static int option_startup(struct usb_serial *serial); +-static void option_shutdown(struct usb_serial *serial); ++static void option_disconnect(struct usb_serial *serial); ++static void option_release(struct usb_serial *serial); + static int option_write_room(struct tty_struct *tty); + + static void option_instat_callback(struct urb *urb); +@@ -558,7 +559,8 @@ static struct usb_serial_driver option_1 + .tiocmget = option_tiocmget, + .tiocmset = option_tiocmset, + .attach = option_startup, +- .shutdown = option_shutdown, ++ .disconnect = option_disconnect, ++ .release = option_release, + .read_int_callback = option_instat_callback, + .suspend = option_suspend, + .resume = option_resume, +@@ -1129,7 +1131,14 @@ static void stop_read_write_urbs(struct + } + } + +-static void option_shutdown(struct usb_serial *serial) ++static void option_disconnect(struct usb_serial *serial) ++{ ++ dbg("%s", __func__); ++ ++ stop_read_write_urbs(serial); ++} ++ ++static void option_release(struct usb_serial *serial) + { + int i, j; + struct usb_serial_port *port; +@@ -1137,8 +1146,6 @@ static void option_shutdown(struct usb_s + + dbg("%s", __func__); + +- stop_read_write_urbs(serial); +- + /* Now free them */ + for (i = 0; i < serial->num_ports; ++i) { + port = serial->port[i]; +--- a/drivers/usb/serial/oti6858.c ++++ b/drivers/usb/serial/oti6858.c +@@ -160,7 +160,7 @@ static int oti6858_tiocmget(struct tty_s + static int oti6858_tiocmset(struct tty_struct *tty, struct file *file, + unsigned int set, unsigned int clear); + static int oti6858_startup(struct usb_serial *serial); +-static void oti6858_shutdown(struct usb_serial *serial); ++static void oti6858_release(struct usb_serial *serial); + + /* functions operating on buffers */ + static struct oti6858_buf *oti6858_buf_alloc(unsigned int size); +@@ -195,7 +195,7 @@ static struct usb_serial_driver oti6858_ + .write_room = oti6858_write_room, + .chars_in_buffer = oti6858_chars_in_buffer, + .attach = oti6858_startup, +- .shutdown = oti6858_shutdown, ++ .release = oti6858_release, + }; + + struct oti6858_private { +@@ -829,7 +829,7 @@ static int oti6858_ioctl(struct tty_stru + } + + +-static void oti6858_shutdown(struct usb_serial *serial) ++static void oti6858_release(struct usb_serial *serial) + { + struct oti6858_private *priv; + int i; +@@ -841,7 +841,6 @@ static void oti6858_shutdown(struct usb_ + if (priv) { + oti6858_buf_free(priv->buf); + kfree(priv); +- usb_set_serial_port_data(serial->port[i], NULL); + } + } + } +--- a/drivers/usb/serial/pl2303.c ++++ b/drivers/usb/serial/pl2303.c +@@ -897,7 +897,7 @@ static void pl2303_break_ctl(struct tty_ + dbg("%s - error sending break = %d", __func__, result); + } + +-static void pl2303_shutdown(struct usb_serial *serial) ++static void pl2303_release(struct usb_serial *serial) + { + int i; + struct pl2303_private *priv; +@@ -909,7 +909,6 @@ static void pl2303_shutdown(struct usb_s + if (priv) { + pl2303_buf_free(priv->buf); + kfree(priv); +- usb_set_serial_port_data(serial->port[i], NULL); + } + } + } +@@ -1137,7 +1136,7 @@ static struct usb_serial_driver pl2303_d + .write_room = pl2303_write_room, + .chars_in_buffer = pl2303_chars_in_buffer, + .attach = pl2303_startup, +- .shutdown = pl2303_shutdown, ++ .release = pl2303_release, + }; + + static int __init pl2303_init(void) +--- a/drivers/usb/serial/sierra.c ++++ b/drivers/usb/serial/sierra.c +@@ -699,7 +699,7 @@ static int sierra_startup(struct usb_ser + return 0; + } + +-static void sierra_shutdown(struct usb_serial *serial) ++static void sierra_disconnect(struct usb_serial *serial) + { + int i, j; + struct usb_serial_port *port; +@@ -718,10 +718,29 @@ static void sierra_shutdown(struct usb_s + for (j = 0; j < N_IN_URB; j++) { + usb_kill_urb(portdata->in_urbs[j]); + usb_free_urb(portdata->in_urbs[j]); +- kfree(portdata->in_buffer[j]); + } ++ } ++} ++ ++static void sierra_release(struct usb_serial *serial) ++{ ++ int i, j; ++ struct usb_serial_port *port; ++ struct sierra_port_private *portdata; ++ ++ dev_dbg(&serial->dev->dev, "%s\n", __func__); ++ ++ for (i = 0; i < serial->num_ports; ++i) { ++ port = serial->port[i]; ++ if (!port) ++ continue; ++ portdata = usb_get_serial_port_data(port); ++ if (!portdata) ++ continue; ++ ++ for (j = 0; j < N_IN_URB; j++) ++ kfree(portdata->in_buffer[j]); + kfree(portdata); +- usb_set_serial_port_data(port, NULL); + } + } + +@@ -743,7 +762,8 @@ static struct usb_serial_driver sierra_d + .tiocmget = sierra_tiocmget, + .tiocmset = sierra_tiocmset, + .attach = sierra_startup, +- .shutdown = sierra_shutdown, ++ .disconnect = sierra_disconnect, ++ .release = sierra_release, + .read_int_callback = sierra_instat_callback, + }; + +--- a/drivers/usb/serial/spcp8x5.c ++++ b/drivers/usb/serial/spcp8x5.c +@@ -356,7 +356,7 @@ cleanup: + } + + /* call when the device plug out. free all the memory alloced by probe */ +-static void spcp8x5_shutdown(struct usb_serial *serial) ++static void spcp8x5_release(struct usb_serial *serial) + { + int i; + struct spcp8x5_private *priv; +@@ -366,7 +366,6 @@ static void spcp8x5_shutdown(struct usb_ + if (priv) { + free_ringbuf(priv->buf); + kfree(priv); +- usb_set_serial_port_data(serial->port[i] , NULL); + } + } + } +@@ -1043,7 +1042,7 @@ static struct usb_serial_driver spcp8x5_ + .write_bulk_callback = spcp8x5_write_bulk_callback, + .chars_in_buffer = spcp8x5_chars_in_buffer, + .attach = spcp8x5_startup, +- .shutdown = spcp8x5_shutdown, ++ .release = spcp8x5_release, + }; + + static int __init spcp8x5_init(void) +--- a/drivers/usb/serial/symbolserial.c ++++ b/drivers/usb/serial/symbolserial.c +@@ -268,7 +268,7 @@ error: + return retval; + } + +-static void symbol_shutdown(struct usb_serial *serial) ++static void symbol_disconnect(struct usb_serial *serial) + { + struct symbol_private *priv = usb_get_serial_data(serial); + +@@ -276,9 +276,16 @@ static void symbol_shutdown(struct usb_s + + usb_kill_urb(priv->int_urb); + usb_free_urb(priv->int_urb); ++} ++ ++static void symbol_release(struct usb_serial *serial) ++{ ++ struct symbol_private *priv = usb_get_serial_data(serial); ++ ++ dbg("%s", __func__); ++ + kfree(priv->int_buffer); + kfree(priv); +- usb_set_serial_data(serial, NULL); + } + + static struct usb_driver symbol_driver = { +@@ -300,7 +307,8 @@ static struct usb_serial_driver symbol_d + .attach = symbol_startup, + .open = symbol_open, + .close = symbol_close, +- .shutdown = symbol_shutdown, ++ .disconnect = symbol_disconnect, ++ .release = symbol_release, + .throttle = symbol_throttle, + .unthrottle = symbol_unthrottle, + }; +--- a/drivers/usb/serial/ti_usb_3410_5052.c ++++ b/drivers/usb/serial/ti_usb_3410_5052.c +@@ -97,7 +97,7 @@ struct ti_device { + /* Function Declarations */ + + static int ti_startup(struct usb_serial *serial); +-static void ti_shutdown(struct usb_serial *serial); ++static void ti_release(struct usb_serial *serial); + static int ti_open(struct tty_struct *tty, struct usb_serial_port *port, + struct file *file); + static void ti_close(struct tty_struct *tty, struct usb_serial_port *port, +@@ -231,7 +231,7 @@ static struct usb_serial_driver ti_1port + .id_table = ti_id_table_3410, + .num_ports = 1, + .attach = ti_startup, +- .shutdown = ti_shutdown, ++ .release = ti_release, + .open = ti_open, + .close = ti_close, + .write = ti_write, +@@ -259,7 +259,7 @@ static struct usb_serial_driver ti_2port + .id_table = ti_id_table_5052, + .num_ports = 2, + .attach = ti_startup, +- .shutdown = ti_shutdown, ++ .release = ti_release, + .open = ti_open, + .close = ti_close, + .write = ti_write, +@@ -474,7 +474,7 @@ free_tdev: + } + + +-static void ti_shutdown(struct usb_serial *serial) ++static void ti_release(struct usb_serial *serial) + { + int i; + struct ti_device *tdev = usb_get_serial_data(serial); +@@ -487,12 +487,10 @@ static void ti_shutdown(struct usb_seria + if (tport) { + ti_buf_free(tport->tp_write_buf); + kfree(tport); +- usb_set_serial_port_data(serial->port[i], NULL); + } + } + + kfree(tdev); +- usb_set_serial_data(serial, NULL); + } + + +--- a/drivers/usb/serial/usb-serial.c ++++ b/drivers/usb/serial/usb-serial.c +@@ -141,6 +141,14 @@ static void destroy_serial(struct kref * + if (serial->minor != SERIAL_TTY_NO_MINOR) + return_serial(serial); + ++ serial->type->release(serial); ++ ++ for (i = 0; i < serial->num_ports; ++i) { ++ port = serial->port[i]; ++ if (port) ++ put_device(&port->dev); ++ } ++ + /* If this is a "fake" port, we have to clean it up here, as it will + * not get cleaned up in port_release() as it was never registered with + * the driver core */ +@@ -148,9 +156,8 @@ static void destroy_serial(struct kref * + for (i = serial->num_ports; + i < serial->num_port_pointers; ++i) { + port = serial->port[i]; +- if (!port) +- continue; +- port_free(port); ++ if (port) ++ port_free(port); + } + } + +@@ -1062,10 +1069,6 @@ void usb_serial_disconnect(struct usb_in + serial->disconnected = 1; + mutex_unlock(&serial->disc_mutex); + +- /* Unfortunately, many of the sub-drivers expect the port structures +- * to exist when their shutdown method is called, so we have to go +- * through this awkward two-step unregistration procedure. +- */ + for (i = 0; i < serial->num_ports; ++i) { + port = serial->port[i]; + if (port) { +@@ -1079,14 +1082,7 @@ void usb_serial_disconnect(struct usb_in + device_del(&port->dev); + } + } +- serial->type->shutdown(serial); +- for (i = 0; i < serial->num_ports; ++i) { +- port = serial->port[i]; +- if (port) { +- put_device(&port->dev); +- serial->port[i] = NULL; +- } +- } ++ serial->type->disconnect(serial); + + /* let the last holder of this object + * cause it to be cleaned up */ +@@ -1262,7 +1258,8 @@ static void fixup_generic(struct usb_ser + set_to_generic_if_null(device, chars_in_buffer); + set_to_generic_if_null(device, read_bulk_callback); + set_to_generic_if_null(device, write_bulk_callback); +- set_to_generic_if_null(device, shutdown); ++ set_to_generic_if_null(device, disconnect); ++ set_to_generic_if_null(device, release); + } + + int usb_serial_register(struct usb_serial_driver *driver) +--- a/drivers/usb/serial/visor.c ++++ b/drivers/usb/serial/visor.c +@@ -48,7 +48,7 @@ static void visor_unthrottle(struct tty_ + static int visor_probe(struct usb_serial *serial, + const struct usb_device_id *id); + static int visor_calc_num_ports(struct usb_serial *serial); +-static void visor_shutdown(struct usb_serial *serial); ++static void visor_release(struct usb_serial *serial); + static void visor_write_bulk_callback(struct urb *urb); + static void visor_read_bulk_callback(struct urb *urb); + static void visor_read_int_callback(struct urb *urb); +@@ -203,7 +203,7 @@ static struct usb_serial_driver handspri + .attach = treo_attach, + .probe = visor_probe, + .calc_num_ports = visor_calc_num_ports, +- .shutdown = visor_shutdown, ++ .release = visor_release, + .write = visor_write, + .write_room = visor_write_room, + .write_bulk_callback = visor_write_bulk_callback, +@@ -228,7 +228,7 @@ static struct usb_serial_driver clie_5_d + .attach = clie_5_attach, + .probe = visor_probe, + .calc_num_ports = visor_calc_num_ports, +- .shutdown = visor_shutdown, ++ .release = visor_release, + .write = visor_write, + .write_room = visor_write_room, + .write_bulk_callback = visor_write_bulk_callback, +@@ -920,7 +920,7 @@ static int clie_5_attach(struct usb_seri + return generic_startup(serial); + } + +-static void visor_shutdown(struct usb_serial *serial) ++static void visor_release(struct usb_serial *serial) + { + struct visor_private *priv; + int i; +@@ -929,10 +929,7 @@ static void visor_shutdown(struct usb_se + + for (i = 0; i < serial->num_ports; i++) { + priv = usb_get_serial_port_data(serial->port[i]); +- if (priv) { +- usb_set_serial_port_data(serial->port[i], NULL); +- kfree(priv); +- } ++ kfree(priv); + } + } + +--- a/drivers/usb/serial/whiteheat.c ++++ b/drivers/usb/serial/whiteheat.c +@@ -144,7 +144,7 @@ static int whiteheat_firmware_attach(st + + /* function prototypes for the Connect Tech WhiteHEAT serial converter */ + static int whiteheat_attach(struct usb_serial *serial); +-static void whiteheat_shutdown(struct usb_serial *serial); ++static void whiteheat_release(struct usb_serial *serial); + static int whiteheat_open(struct tty_struct *tty, + struct usb_serial_port *port, struct file *filp); + static void whiteheat_close(struct tty_struct *tty, +@@ -190,7 +190,7 @@ static struct usb_serial_driver whitehea + .id_table = id_table_std, + .num_ports = 4, + .attach = whiteheat_attach, +- .shutdown = whiteheat_shutdown, ++ .release = whiteheat_release, + .open = whiteheat_open, + .close = whiteheat_close, + .write = whiteheat_write, +@@ -618,7 +618,7 @@ no_command_buffer: + } + + +-static void whiteheat_shutdown(struct usb_serial *serial) ++static void whiteheat_release(struct usb_serial *serial) + { + struct usb_serial_port *command_port; + struct usb_serial_port *port; +--- a/include/linux/usb/serial.h ++++ b/include/linux/usb/serial.h +@@ -181,8 +181,10 @@ static inline void usb_set_serial_data(s + * This will be called when the struct usb_serial structure is fully set + * set up. Do any local initialization of the device, or any private + * memory structure allocation at this point in time. +- * @shutdown: pointer to the driver's shutdown function. This will be +- * called when the device is removed from the system. ++ * @disconnect: pointer to the driver's disconnect function. This will be ++ * called when the device is unplugged or unbound from the driver. ++ * @release: pointer to the driver's release function. This will be called ++ * when the usb_serial data structure is about to be destroyed. + * @usb_driver: pointer to the struct usb_driver that controls this + * device. This is necessary to allow dynamic ids to be added to + * the driver from sysfs. +@@ -212,7 +214,8 @@ struct usb_serial_driver { + int (*attach)(struct usb_serial *serial); + int (*calc_num_ports) (struct usb_serial *serial); + +- void (*shutdown)(struct usb_serial *serial); ++ void (*disconnect)(struct usb_serial *serial); ++ void (*release)(struct usb_serial *serial); + + int (*port_probe)(struct usb_serial_port *port); + int (*port_remove)(struct usb_serial_port *port); +@@ -292,7 +295,8 @@ extern void usb_serial_generic_read_bulk + extern void usb_serial_generic_write_bulk_callback(struct urb *urb); + extern void usb_serial_generic_throttle(struct tty_struct *tty); + extern void usb_serial_generic_unthrottle(struct tty_struct *tty); +-extern void usb_serial_generic_shutdown(struct usb_serial *serial); ++extern void usb_serial_generic_disconnect(struct usb_serial *serial); ++extern void usb_serial_generic_release(struct usb_serial *serial); + extern int usb_serial_generic_register(int debug); + extern void usb_serial_generic_deregister(void); +