Новость: Доступен новый стабильный Linux 2.6.11.11
(Категория: Linux)
Добавил Roman I Khimov
Пятница, 27 Май 2005, 21:12

Как обычно - ничего сверхестественного, только исправление обнаруженных ошибок. Особо выделяется, разве что, только серия патчей для архитектуры x86_64. Также Крис Райт (Chris Wright) сообщил об обновлении git-ветки 2.6.11.y, теперь ее можно обнаружить здесь:
rsync://rsync.kernel.org/pub/scm/linux/kernel/git/gregkh/linux-2.6.11.y.git
и/или просмотреть через веб-интерфейс.

Патч Linux 2.6.11 -> 2.6.11.11 (21 КБ). Патч относительно 2.6.11.10 следует далее вместе с описанием изменений.

Изменения:
[html]----------

 Makefile                              |    2 -
 arch/ppc64/kernel/pSeries_iommu.c     |   55 +++++++++++++++++++++++++++++++
 arch/x86_64/kernel/ptrace.c           |   13 +++++--
 arch/x86_64/mm/fault.c                |   11 +++++-
 arch/x86_64/mm/ioremap.c              |    2 -
 drivers/ide/ide-disk.c                |    4 +-
 drivers/net/3c59x.c                   |    9 +++--
 drivers/usb/serial/visor.c            |   38 +++++++++++++++------
 drivers/video/matrox/matroxfb_accel.c |   14 ++++++--
 drivers/video/matrox/matroxfb_base.h  |    4 +-
 fs/ext3/balloc.c                      |    3 +
 include/asm-x86_64/processor.h        |    4 +-
 include/linux/err.h                   |    4 +-
 mm/mmap.c                             |   59 +++++++++++++++++-----------------
 net/bridge/netfilter/ebtables.c       |    3 +
 net/rose/rose_route.c                 |    3 +
 sound/usb/usbaudio.c                  |    2 -
 sound/usb/usx2y/usbusx2y.c            |   11 ++++--
 18 files changed, 173 insertions(+), 68 deletions(-)

Summary of changes from v2.6.11.10 to v2.6.11.11
==============================================

Andi Kleen:
  o x86_64: Don't look up struct page pointer of physical address in iounmap
  o x86_64: When checking vmalloc mappings don't use pte_page
  o x86_64: Add a guard page at the end of the 47bit address space
  o x86_64: Fix canonical checking for segment registers in ptrace
  o x86_64: check if ptrace RIP is canonical

Bart De Schuymer:
  o Fix smp race

Bartlomiej Zolnierkiewicz:
  o ide-disk: Fix LBA8 DMA

Chris Wright:
  o Linux 2.6.11.11

Daniel Ritz:
  o 3c59x: only put the device into D3 when we're actually using WOL

Greg Kroah-Hartman:
  o USB: fix bug in visor driver with throttle/unthrottle causing oopses

Gregor Jasny:
  o usbusx2y: prevent oops & dead keyboard on usb unplugging while the device is being used
  o usbaudio: prevent oops & dead keyboard on usb unplugging while the device is being used

Linus Torvalds:
  o Fix get_unmapped_area sanity tests

Mingming Cao:
  o ext3: fix race between ext3 make block reservation and reservation window discard

Olof Johansson:
  o PPC64: Fix LPAR IOMMU setup code for p630

Petr Vandrovec:
  o Fix matroxfb on big-endian hardware

Ralf Bächle:
  o Fix minor security hole[/html]


Патч относительно 2.6.11.10:

[html]diff --git a/Makefile b/Makefile
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 6
 SUBLEVEL = 11
-EXTRAVERSION = .10
+EXTRAVERSION = .11
 NAME=Woozy Beaver
 
 # *DOCUMENTATION*
diff --git a/arch/ppc64/kernel/pSeries_iommu.c b/arch/ppc64/kernel/pSeries_iommu.c
--- a/arch/ppc64/kernel/pSeries_iommu.c
+++ b/arch/ppc64/kernel/pSeries_iommu.c
@@ -401,6 +401,8 @@ static void iommu_bus_setup_pSeriesLP(st
        struct device_node *dn, *pdn;
        unsigned int *dma_window = NULL;
 
+       DBG("iommu_bus_setup_pSeriesLP, bus %p, bus->self %p\n", bus, bus->self);
+
        dn = pci_bus_to_OF_node(bus);
 
        /* Find nearest ibm,dma-window, walking up the device tree */
@@ -455,6 +457,56 @@ static void iommu_dev_setup_pSeries(stru
        }
 }
 
+static void iommu_dev_setup_pSeriesLP(struct pci_dev *dev)
+{
+       struct device_node *pdn, *dn;
+       struct iommu_table *tbl;
+       int *dma_window = NULL;
+
+       DBG("iommu_dev_setup_pSeriesLP, dev %p (%s)\n", dev, dev->pretty_name);
+
+       /* dev setup for LPAR is a little tricky, since the device tree might
+        * contain the dma-window properties per-device and not neccesarily
+        * for the bus. So we need to search upwards in the tree until we
+        * either hit a dma-window property, OR find a parent with a table
+        * already allocated.
+        */
+       dn = pci_device_to_OF_node(dev);
+
+       for (pdn = dn; pdn && !pdn->iommu_table; pdn = pdn->parent) {
+               dma_window = (unsigned int *)get_property(pdn, "ibm,dma-window", NULL);
+               if (dma_window)
+                       break;
+       }
+
+       /* Check for parent == NULL so we don't try to setup the empty EADS
+        * slots on POWER4 machines.
+        */
+       if (dma_window == NULL || pdn->parent == NULL) {
+               /* Fall back to regular (non-LPAR) dev setup */
+               DBG("No dma window for device, falling back to regular setup\n");
+               iommu_dev_setup_pSeries(dev);
+               return;
+       } else {
+               DBG("Found DMA window, allocating table\n");
+       }
+
+       if (!pdn->iommu_table) {
+               /* iommu_table_setparms_lpar needs bussubno. */
+               pdn->bussubno = pdn->phb->bus->number;
+
+               tbl = (struct iommu_table *)kmalloc(sizeof(struct iommu_table),
+                                                   GFP_KERNEL);
+
+               iommu_table_setparms_lpar(pdn->phb, pdn, tbl, dma_window);
+
+               pdn->iommu_table = iommu_init_table(tbl);
+       }
+
+       if (pdn != dn)
+               dn->iommu_table = pdn->iommu_table;
+}
+
 static void iommu_bus_setup_null(struct pci_bus *b) { }
 static void iommu_dev_setup_null(struct pci_dev *d) { }
 
@@ -479,13 +531,14 @@ void iommu_init_early_pSeries(void)
                        ppc_md.tce_free  = tce_free_pSeriesLP;
                }
                ppc_md.iommu_bus_setup = iommu_bus_setup_pSeriesLP;
+               ppc_md.iommu_dev_setup = iommu_dev_setup_pSeriesLP;
        } else {
                ppc_md.tce_build = tce_build_pSeries;
                ppc_md.tce_free  = tce_free_pSeries;
                ppc_md.iommu_bus_setup = iommu_bus_setup_pSeries;
+               ppc_md.iommu_dev_setup = iommu_dev_setup_pSeries;
        }
 
-       ppc_md.iommu_dev_setup = iommu_dev_setup_pSeries;
 
        pci_iommu_init();
 }
diff --git a/arch/x86_64/kernel/ptrace.c b/arch/x86_64/kernel/ptrace.c
--- a/arch/x86_64/kernel/ptrace.c
+++ b/arch/x86_64/kernel/ptrace.c
@@ -129,13 +129,13 @@ static int putreg(struct task_struct *ch
                        value &= 0xffff;
                        return 0;
                case offsetof(struct user_regs_struct,fs_base):
-                       if (!((value >> 48) == 0 || (value >> 48) == 0xffff))
-                               return -EIO; 
+                       if (value >= TASK_SIZE)
+                               return -EIO;
                        child->thread.fs = value;
                        return 0;
                case offsetof(struct user_regs_struct,gs_base):
-                       if (!((value >> 48) == 0 || (value >> 48) == 0xffff))
-                               return -EIO; 
+                       if (value >= TASK_SIZE)
+                               return -EIO;
                        child->thread.gs = value;
                        return 0;
                case offsetof(struct user_regs_struct, eflags):
@@ -149,6 +149,11 @@ static int putreg(struct task_struct *ch
                                return -EIO;
                        value &= 0xffff;
                        break;
+               case offsetof(struct user_regs_struct, rip):
+                       /* Check if the new RIP address is canonical */
+                       if (value >= TASK_SIZE)
+                               return -EIO;
+                       break;
        }
        put_stack_long(child, regno - sizeof(struct pt_regs), value);
        return 0;
diff --git a/arch/x86_64/mm/fault.c b/arch/x86_64/mm/fault.c
--- a/arch/x86_64/mm/fault.c
+++ b/arch/x86_64/mm/fault.c
@@ -236,6 +236,8 @@ static noinline void pgtable_bad(unsigne
 
 /*
  * Handle a fault on the vmalloc or module mapping area
+ *
+ * This assumes no large pages in there.
  */
 static int vmalloc_fault(unsigned long address)
 {
@@ -274,7 +276,10 @@ static int vmalloc_fault(unsigned long a
        if (!pte_present(*pte_ref))
                return -1;
        pte = pte_offset_kernel(pmd, address);
-       if (!pte_present(*pte) || pte_page(*pte) != pte_page(*pte_ref))
+       /* Don't use pte_page here, because the mappings can point
+          outside mem_map, and the NUMA hash lookup cannot handle
+          that. */
+       if (!pte_present(*pte) || pte_pfn(*pte) != pte_pfn(*pte_ref))
                BUG();
        __flush_tlb_all();
        return 0;
@@ -348,7 +353,9 @@ asmlinkage void do_page_fault(struct pt_
         * protection error (error_code & 1) == 0.
         */
        if (unlikely(address >= TASK_SIZE)) {
-               if (!(error_code & 5)) {
+               if (!(error_code & 5) &&
+                     ((address >= VMALLOC_START && address < VMALLOC_END) ||
+                      (address >= MODULES_VADDR && address < MODULES_END))) {
                        if (vmalloc_fault(address) < 0)
                                goto bad_area_nosemaphore;
                        return;
diff --git a/arch/x86_64/mm/ioremap.c b/arch/x86_64/mm/ioremap.c
--- a/arch/x86_64/mm/ioremap.c
+++ b/arch/x86_64/mm/ioremap.c
@@ -266,7 +266,7 @@ void iounmap(volatile void __iomem *addr
        if ((p->flags >> 20) &&
                p->phys_addr + p->size - 1 < virt_to_phys(high_memory)) {
                /* p->size includes the guard page, but cpa doesn't like that */
-               change_page_attr(virt_to_page(__va(p->phys_addr)),
+               change_page_attr_addr((unsigned long)(__va(p->phys_addr)),
                                 (p->size - PAGE_SIZE) >> PAGE_SHIFT,
                                 PAGE_KERNEL);                           
                global_flush_tlb();
diff --git a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c
--- a/drivers/ide/ide-disk.c
+++ b/drivers/ide/ide-disk.c
@@ -133,6 +133,8 @@ static ide_startstop_t __ide_do_rw_disk(
        if (hwif->no_lba48_dma && lba48 && dma) {
                if (block + rq->nr_sectors > 1ULL << 28)
                        dma = 0;
+               else
+                       lba48 = 0;
        }
 
        if (!dma) {
@@ -146,7 +148,7 @@ static ide_startstop_t __ide_do_rw_disk(
        /* FIXME: SELECT_MASK(drive, 0) ? */
 
        if (drive->select.b.lba) {
-               if (drive->addressing == 1) {
+               if (lba48) {
                        task_ioreg_t tasklets[10];
 
                        pr_debug("%s: LBA=0x%012llx\n", drive->name, block);
diff --git a/drivers/net/3c59x.c b/drivers/net/3c59x.c
--- a/drivers/net/3c59x.c
+++ b/drivers/net/3c59x.c
@@ -1581,7 +1581,8 @@ vortex_up(struct net_device *dev)
 
        if (VORTEX_PCI(vp)) {
                pci_set_power_state(VORTEX_PCI(vp), PCI_D0);    /* Go active */
-               pci_restore_state(VORTEX_PCI(vp));
+               if (vp->pm_state_valid)
+                       pci_restore_state(VORTEX_PCI(vp));
                pci_enable_device(VORTEX_PCI(vp));
        }
 
@@ -2741,6 +2742,7 @@ vortex_down(struct net_device *dev, int 
                outl(0, ioaddr + DownListPtr);
 
        if (final_down && VORTEX_PCI(vp)) {
+               vp->pm_state_valid = 1;
                pci_save_state(VORTEX_PCI(vp));
                acpi_set_WOL(dev);
        }
@@ -3243,9 +3245,10 @@ static void acpi_set_WOL(struct net_devi
                outw(RxEnable, ioaddr + EL3_CMD);
 
                pci_enable_wake(VORTEX_PCI(vp), 0, 1);
+
+               /* Change the power state to D3; RxEnable doesn't take effect. */
+               pci_set_power_state(VORTEX_PCI(vp), PCI_D3hot);
        }
-       /* Change the power state to D3; RxEnable doesn't take effect. */
-       pci_set_power_state(VORTEX_PCI(vp), PCI_D3hot);
 }
 
 
diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c
--- a/drivers/usb/serial/visor.c
+++ b/drivers/usb/serial/visor.c
@@ -386,6 +386,7 @@ struct visor_private {
        int bytes_in;
        int bytes_out;
        int outstanding_urbs;
+       int throttled;
 };
 
 /* number of outstanding urbs to prevent userspace DoS from happening */
@@ -415,6 +416,7 @@ static int visor_open (struct usb_serial
        priv->bytes_in = 0;
        priv->bytes_out = 0;
        priv->outstanding_urbs = 0;
+       priv->throttled = 0;
        spin_unlock_irqrestore(&priv->lock, flags);
 
        /*
@@ -602,6 +604,7 @@ static void visor_read_bulk_callback (st
        struct tty_struct *tty;
        unsigned long flags;
        int i;
+       int throttled;
        int result;
 
        dbg("%s - port %d", __FUNCTION__, port->number);
@@ -627,18 +630,21 @@ static void visor_read_bulk_callback (st
        }
        spin_lock_irqsave(&priv->lock, flags);
        priv->bytes_in += urb->actual_length;
+       throttled = priv->throttled;
        spin_unlock_irqrestore(&priv->lock, flags);
 
-       /* Continue trying to always read  */
-       usb_fill_bulk_urb (port->read_urb, port->serial->dev,
-                          usb_rcvbulkpipe(port->serial->dev,
-                                          port->bulk_in_endpointAddress),
-                          port->read_urb->transfer_buffer,
-                          port->read_urb->transfer_buffer_length,
-                          visor_read_bulk_callback, port);
-       result = usb_submit_urb(port->read_urb, GFP_ATOMIC);
-       if (result)
-               dev_err(&port->dev, "%s - failed resubmitting read urb, error %d\n", __FUNCTION__, result);
+       /* Continue trying to always read if we should */
+       if (!throttled) {
+               usb_fill_bulk_urb (port->read_urb, port->serial->dev,
+                                  usb_rcvbulkpipe(port->serial->dev,
+                                                  port->bulk_in_endpointAddress),
+                                  port->read_urb->transfer_buffer,
+                                  port->read_urb->transfer_buffer_length,
+                                  visor_read_bulk_callback, port);
+               result = usb_submit_urb(port->read_urb, GFP_ATOMIC);
+               if (result)
+                       dev_err(&port->dev, "%s - failed resubmitting read urb, error %d\n", __FUNCTION__, result);
+       }
        return;
 }
 
@@ -683,16 +689,26 @@ exit:
 
 static void visor_throttle (struct usb_serial_port *port)
 {
+       struct visor_private *priv = usb_get_serial_port_data(port);
+       unsigned long flags;
+
        dbg("%s - port %d", __FUNCTION__, port->number);
-       usb_kill_urb(port->read_urb);
+       spin_lock_irqsave(&priv->lock, flags);
+       priv->throttled = 1;
+       spin_unlock_irqrestore(&priv->lock, flags);
 }
 
 
 static void visor_unthrottle (struct usb_serial_port *port)
 {
+       struct visor_private *priv = usb_get_serial_port_data(port);
+       unsigned long flags;
        int result;
 
        dbg("%s - port %d", __FUNCTION__, port->number);
+       spin_lock_irqsave(&priv->lock, flags);
+       priv->throttled = 0;
+       spin_unlock_irqrestore(&priv->lock, flags);
 
        port->read_urb->dev = port->serial->dev;
        result = usb_submit_urb(port->read_urb, GFP_ATOMIC);
diff --git a/drivers/video/matrox/matroxfb_accel.c b/drivers/video/matrox/matroxfb_accel.c
--- a/drivers/video/matrox/matroxfb_accel.c
+++ b/drivers/video/matrox/matroxfb_accel.c
@@ -438,13 +438,21 @@ static void matroxfb_1bpp_imageblit(WPMI
                } else if (step == 1) {
                        /* Special case for 1..8bit widths */
                        while (height--) {
-                               mga_writel(mmio, 0, *chardata);
+#if defined(__BIG_ENDIAN)
+                               fb_writel((*chardata) << 24, mmio.vaddr);
+#else
+                               fb_writel(*chardata, mmio.vaddr);
+#endif
                                chardata++;
                        }
                } else if (step == 2) {
                        /* Special case for 9..15bit widths */
                        while (height--) {
-                               mga_writel(mmio, 0, *(u_int16_t*)chardata);
+#if defined(__BIG_ENDIAN)
+                               fb_writel((*(u_int16_t*)chardata) << 16, mmio.vaddr);
+#else
+                               fb_writel(*(u_int16_t*)chardata, mmio.vaddr);
+#endif
                                chardata += 2;
                        }
                } else {
@@ -454,7 +462,7 @@ static void matroxfb_1bpp_imageblit(WPMI
                                
                                for (i = 0; i < step; i += 4) {
                                        /* Hope that there are at least three readable bytes beyond the end of bitmap */
-                                       mga_writel(mmio, 0, get_unaligned((u_int32_t*)(chardata + i)));
+                                       fb_writel(get_unaligned((u_int32_t*)(chardata + i)),mmio.vaddr);
                                }
                                chardata += step;
                        }
diff --git a/drivers/video/matrox/matroxfb_base.h b/drivers/video/matrox/matroxfb_base.h
--- a/drivers/video/matrox/matroxfb_base.h
+++ b/drivers/video/matrox/matroxfb_base.h
@@ -170,14 +170,14 @@ static inline void mga_memcpy_toio(vaddr
 
        if ((unsigned long)src & 3) {
                while (len >= 4) {
-                       writel(get_unaligned((u32 *)src), addr);
+                       fb_writel(get_unaligned((u32 *)src), addr);
                        addr++;
                        len -= 4;
                        src += 4;
                }
        } else {
                while (len >= 4) {
-                       writel(*(u32 *)src, addr);
+                       fb_writel(*(u32 *)src, addr);
                        addr++;
                        len -= 4;
                        src += 4;
diff --git a/fs/ext3/balloc.c b/fs/ext3/balloc.c
--- a/fs/ext3/balloc.c
+++ b/fs/ext3/balloc.c
@@ -268,7 +268,8 @@ void ext3_discard_reservation(struct ino
 
        if (!rsv_is_empty(&rsv->rsv_window)) {
                spin_lock(rsv_lock);
-               rsv_window_remove(inode->i_sb, rsv);
+               if (!rsv_is_empty(&rsv->rsv_window))
+                       rsv_window_remove(inode->i_sb, rsv);
                spin_unlock(rsv_lock);
        }
 }
diff --git a/include/asm-x86_64/processor.h b/include/asm-x86_64/processor.h
--- a/include/asm-x86_64/processor.h
+++ b/include/asm-x86_64/processor.h
@@ -160,9 +160,9 @@ static inline void clear_in_cr4 (unsigne
 
 
 /*
- * User space process size. 47bits.
+ * User space process size. 47bits minus one guard page.
  */
-#define TASK_SIZE      (0x800000000000UL)
+#define TASK_SIZE      (0x800000000000UL - 4096)
 
 /* This decides where the kernel will search for a free chunk of vm
  * space during mmap's.
diff --git a/include/linux/err.h b/include/linux/err.h
--- a/include/linux/err.h
+++ b/include/linux/err.h
@@ -13,6 +13,8 @@
  * This should be a per-architecture thing, to allow different
  * error and pointer decisions.
  */
+#define IS_ERR_VALUE(x) unlikely((x) > (unsigned long)-1000L)
+
 static inline void *ERR_PTR(long error)
 {
        return (void *) error;
@@ -25,7 +27,7 @@ static inline long PTR_ERR(const void *p
 
 static inline long IS_ERR(const void *ptr)
 {
-       return unlikely((unsigned long)ptr > (unsigned long)-1000L);
+       return IS_ERR_VALUE((unsigned long)ptr);
 }
 
 #endif /* _LINUX_ERR_H */
diff --git a/mm/mmap.c b/mm/mmap.c
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -1315,37 +1315,40 @@ unsigned long
 get_unmapped_area(struct file *file, unsigned long addr, unsigned long len,
                unsigned long pgoff, unsigned long flags)
 {
-       if (flags & MAP_FIXED) {
-               unsigned long ret;
+       unsigned long ret;
 
-               if (addr > TASK_SIZE - len)
-                       return -ENOMEM;
-               if (addr & ~PAGE_MASK)
-                       return -EINVAL;
-               if (file && is_file_hugepages(file))  {
-                       /*
-                        * Check if the given range is hugepage aligned, and
-                        * can be made suitable for hugepages.
-                        */
-                       ret = prepare_hugepage_range(addr, len);
-               } else {
-                       /*
-                        * Ensure that a normal request is not falling in a
-                        * reserved hugepage range.  For some archs like IA-64,
-                        * there is a separate region for hugepages.
-                        */
-                       ret = is_hugepage_only_range(addr, len);
-               }
-               if (ret)
-                       return -EINVAL;
-               return addr;
-       }
+       if (!(flags & MAP_FIXED)) {
+               unsigned long (*get_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
 
-       if (file && file->f_op && file->f_op->get_unmapped_area)
-               return file->f_op->get_unmapped_area(file, addr, len,
-                                               pgoff, flags);
+               get_area = current->mm->get_unmapped_area;
+               if (file && file->f_op && file->f_op->get_unmapped_area)
+                       get_area = file->f_op->get_unmapped_area;
+               addr = get_area(file, addr, len, pgoff, flags);
+               if (IS_ERR_VALUE(addr))
+                       return addr;
+       }
 
-       return current->mm->get_unmapped_area(file, addr, len, pgoff, flags);
+       if (addr > TASK_SIZE - len)
+               return -ENOMEM;
+       if (addr & ~PAGE_MASK)
+               return -EINVAL;
+       if (file && is_file_hugepages(file))  {
+               /*
+                * Check if the given range is hugepage aligned, and
+                * can be made suitable for hugepages.
+                */
+               ret = prepare_hugepage_range(addr, len);
+       } else {
+               /*
+                * Ensure that a normal request is not falling in a
+                * reserved hugepage range.  For some archs like IA-64,
+                * there is a separate region for hugepages.
+                */
+               ret = is_hugepage_only_range(addr, len);
+       }
+       if (ret)
+               return -EINVAL;
+       return addr;
 }
 
 EXPORT_SYMBOL(get_unmapped_area);
diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c
--- a/net/bridge/netfilter/ebtables.c
+++ b/net/bridge/netfilter/ebtables.c
@@ -179,9 +179,10 @@ unsigned int ebt_do_table (unsigned int 
        struct ebt_chainstack *cs;
        struct ebt_entries *chaininfo;
        char *base;
-       struct ebt_table_info *private = table->private;
+       struct ebt_table_info *private;
 
        read_lock_bh(&table->lock);
+       private = table->private;
        cb_base = COUNTER_BASE(private->counters, private->nentries,
           smp_processor_id());
        if (private->chainstack)
diff --git a/net/rose/rose_route.c b/net/rose/rose_route.c
--- a/net/rose/rose_route.c
+++ b/net/rose/rose_route.c
@@ -727,7 +727,8 @@ int rose_rt_ioctl(unsigned int cmd, void
                }
                if (rose_route.mask > 10) /* Mask can't be more than 10 digits */
                        return -EINVAL;
-
+               if (rose_route.ndigis > 8) /* No more than 8 digipeats */
+                       return -EINVAL;
                err = rose_add_node(&rose_route, dev);
                dev_put(dev);
                return err;
diff --git a/sound/usb/usbaudio.c b/sound/usb/usbaudio.c
--- a/sound/usb/usbaudio.c
+++ b/sound/usb/usbaudio.c
@@ -3276,7 +3276,7 @@ static void snd_usb_audio_disconnect(str
                }
                usb_chip[chip->index] = NULL;
                up(&register_mutex);
-               snd_card_free_in_thread(card);
+               snd_card_free(card);
        } else {
                up(&register_mutex);
        }
diff --git a/sound/usb/usx2y/usbusx2y.c b/sound/usb/usx2y/usbusx2y.c
--- a/sound/usb/usx2y/usbusx2y.c
+++ b/sound/usb/usx2y/usbusx2y.c
@@ -1,6 +1,11 @@
 /*
  * usbusy2y.c - ALSA USB US-428 Driver
  *
+2005-04-14 Karsten Wiese
+       Version 0.8.7.2:
+       Call snd_card_free() instead of snd_card_free_in_thread() to prevent oops with dead keyboard symptom.
+       Tested ok with kernel 2.6.12-rc2.
+
 2004-12-14 Karsten Wiese
        Version 0.8.7.1:
        snd_pcm_open for rawusb pcm-devices now returns -EBUSY if called without rawusb's hwdep device being open.
@@ -143,7 +148,7 @@
 
 
 MODULE_AUTHOR("Karsten Wiese <annabellesgarden@yahoo.de>");
-MODULE_DESCRIPTION("TASCAM "NAME_ALLCAPS" Version 0.8.7.1");
+MODULE_DESCRIPTION("TASCAM "NAME_ALLCAPS" Version 0.8.7.2");
 MODULE_LICENSE("GPL");
 MODULE_SUPPORTED_DEVICE("{{TASCAM(0x1604), "NAME_ALLCAPS"(0x8001)(0x8005)(0x8007) }}");
 
@@ -430,8 +435,6 @@ static void usX2Y_usb_disconnect(struct 
        if (ptr) {
                usX2Ydev_t* usX2Y = usX2Y((snd_card_t*)ptr);
                struct list_head* p;
-               if (usX2Y->chip_status == USX2Y_STAT_CHIP_HUP)  // on 2.6.1 kernel snd_usbmidi_disconnect()
-                       return;                                 // calls us back. better leave :) .
                usX2Y->chip.shutdown = 1;
                usX2Y->chip_status = USX2Y_STAT_CHIP_HUP;
                usX2Y_unlinkSeq(&usX2Y->AS04);
@@ -443,7 +446,7 @@ static void usX2Y_usb_disconnect(struct 
                }
                if (usX2Y->us428ctls_sharedmem) 
                        wake_up(&usX2Y->us428ctls_wait_queue_head);
-               snd_card_free_in_thread((snd_card_t*)ptr);
+               snd_card_free((snd_card_t*)ptr);
        }
 }
[/html]



Источник этой новости Центр информации по операционным системам
( http://www.osrc.info/news.php?extend.2488 )