]> xenbits.xensource.com Git - people/aperard/linux-chromebook.git/commitdiff
CHROMIUM: bluetooth/usb: hack to cover up dma after free corruption
authorSonny Rao <sonnyrao@chromium.org>
Thu, 21 Feb 2013 01:45:06 +0000 (17:45 -0800)
committerSonny Rao <sonnyrao@chromium.org>
Mon, 25 Feb 2013 19:56:20 +0000 (11:56 -0800)
This is a horrible, gross hack to cover up the fact that we
sometimes get an apparent DMA from the interrupt endpoint on the
bluetooth module after shutting it down.  This ends up manifesting as
random kernel crashes, often from other users of kmalloc-16.

Since we don't have root cause (yet?) and can't do much about it, put
in this band-aid which will leak around 64 bytes of memory every time we
suspend/resume a usb bluetooth device.  We make no attempt to free
this memory because we don't know when the DMA could come in and it's
a relatively small amount so probably not worth trying to free it
later.

Another issue is that SLUB will mix together objects from different
slabs if they are all the same size.  So, in order to get a separate
slab in /proc/slabinfo, we turn on some of the slab debugging options.
The main reason for doing this is to let us track how much memory got
leaked through slabinfo.  This will trigger a BUG_ON() in
kmem_cache_create() if the kernel is compiled with
CONFIG_SLAB && !CONFIG_DEBUG_SLAB.

One day, when we have a real fix, we should remove this abomination.

Signed-off-by: Sonny Rao <sonnyrao@chromium.org>
BUG=chrome-os-partner:12507
BUG=chrome-os-partner:17614
TEST=repeated suspend resume, should not see a kernel crash

Change-Id: Iebdfe033d178c664a91fce1164b255aa1c322ad0
Reviewed-on: https://gerrit.chromium.org/gerrit/43682
Commit-Queue: Sonny Rao <sonnyrao@chromium.org>
Reviewed-by: Sonny Rao <sonnyrao@chromium.org>
Tested-by: Sonny Rao <sonnyrao@chromium.org>
Reviewed-by: Vincent Palatin <vpalatin@chromium.org>
Reviewed-on: https://gerrit.chromium.org/gerrit/43918

drivers/bluetooth/btusb.c

index b5cbe27c6e230543e165ad8879bdfc027b5c95e3..3df1d5c624e078795f3d60f34f8e56ad16ac15d1 100644 (file)
@@ -56,6 +56,13 @@ static struct usb_driver btusb_driver;
 #define BTUSB_WRONG_SCO_MTU    0x40
 #define BTUSB_ATH3012          0x80
 
+/* Hack to deal with USB controllers/devices which */
+/* DMA after we shut the device down and kill the URB */
+static struct kmem_cache *btusb_intr_buf_cache;
+
+/* Max interrupt packet size for a full-speed device is 64 bytes */
+#define BTUSB_INTR_BUF_SIZE (64)
+
 static struct usb_device_id btusb_table[] = {
        /* Generic Bluetooth USB device */
        { USB_DEVICE_INFO(0xe0, 0x01, 0x01) },
@@ -316,7 +323,8 @@ static int btusb_submit_intr_urb(struct hci_dev *hdev, gfp_t mem_flags)
 
        size = le16_to_cpu(data->intr_ep->wMaxPacketSize);
 
-       buf = kmalloc(size, mem_flags);
+       BUG_ON(size > BTUSB_INTR_BUF_SIZE);
+       buf = kmem_cache_alloc(btusb_intr_buf_cache, mem_flags);
        if (!buf) {
                usb_free_urb(urb);
                return -ENOMEM;
@@ -328,7 +336,8 @@ static int btusb_submit_intr_urb(struct hci_dev *hdev, gfp_t mem_flags)
                                                btusb_intr_complete, hdev,
                                                data->intr_ep->bInterval);
 
-       urb->transfer_flags |= URB_FREE_BUFFER;
+       /* HACK: intentionally leak this buffer due to DMA after free */
+       /* urb->transfer_flags |= URB_FREE_BUFFER; */
 
        usb_anchor_urb(urb, &data->intr_anchor);
 
@@ -919,6 +928,16 @@ static int btusb_probe(struct usb_interface *intf,
 
        BT_DBG("intf %p id %p", intf, id);
 
+       if (!btusb_intr_buf_cache) {
+               btusb_intr_buf_cache = kmem_cache_create("btusb_intr_buf",
+                                                        BTUSB_INTR_BUF_SIZE,
+                                                        BTUSB_INTR_BUF_SIZE,
+                                                        SLAB_RED_ZONE |
+                                                        SLAB_POISON,
+                                                        NULL);
+               BUG_ON(!btusb_intr_buf_cache);
+       }
+
        /* interface numbers are hardcoded in the spec */
        if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
                return -ENODEV;