The xpad driver has several races with respect to URB submission which
make it easy to end up with submission while active:

------------[ cut here ]------------
WARNING: CPU: 3 PID: 3563 at drivers/usb/core/urb.c:339
usb_submit_urb+0x2ad/0x5a0()
URB ffff8804078ac240 submitted while active
Modules linked in: fuse xt_CHECKSUM ipt_MASQUERADE nf_nat_masquerade_ipv4 tun
nf_conntrack_netbios_ns nf_conntrack_broadcast ip6t_rpfilter ip6t_REJECT
nf_reject_ipv6 xt_conntrack ebtable_nat ebtable_broute bridge ebtable_filter
ebtables ip6table_nat nf_conntrack_ipv6 nf_defrag_ipv6 nf_nat_ipv6
ip6table_mangle ip6table_security ip6table_raw ip6table_filter ip6_tables
iptable_nat nf_conntrack_ipv4 nf_defrag_ipv4 nf_nat_ipv4 nf_nat nf_conntrack
iptable_mangle iptable_security iptable_raw bnep xpadsnd_hda_codec_realtek
snd_hda_codec_hdmi snd_hda_codec_generic snd_hda_intel snd_hda_codec
snd_hda_core snd_hwdep snd_seq intel_rapl iosf_mbi snd_seq_device
x86_pkg_temp_thermal coretemp snd_pcm kvm uvcvideo iTCO_wdt iTCO_vendor_support
iwlwifi videobuf2_vmalloc videobuf2_core videobuf2_memops
v4l2_common btusb videodev btrtl btbcm thinkpad_acpi snd_timer btintel
mei_me rtsx_pci_ms cfg80211 bluetooth pcspkr mei media memstick joydev snd
tpm_tis shpchp ie31200_edac i2c_i801 tpm lpc_ich edac_core nfsd rfkill wmi
soundcore auth_rpcgss nfs_acl lockd grace sunrpc binfmt_misc dm_crypt i915 8021q
garp stp llc mrp i2c_algo_bit drm_kms_helper drm rtsx_pci_sdmmc mmc_core e1000e
crct10dif_pclmul crc32_pclmul crc32c_intel rtsx_pci ghash_clmulni_intel
ptp serio_raw pps_core mfd_core video
CPU: 3 PID: 3563 Comm: led_test.sh Not tainted 4.2.0-rc4-xpad+ #14
Hardware name: LENOVO 20BFS0EC00/20BFS0EC00, BIOS GMET62WW (2.10 )
03/19/2014
0000000000000000 0000000017a45bc6 ffff8800c9a0fbd8 ffffffff81758a11
0000000000000000 ffff8800c9a0fc30 ffff8800c9a0fc18 ffffffff8109b656
0000000000000002 ffff8804078ac240 00000000000000d0 ffff8800c9806d60
Call Trace:
[<ffffffff81758a11>] dump_stack+0x45/0x57
[<ffffffff8109b656>] warn_slowpath_common+0x86/0xc0
[<ffffffff8109b6e5>] warn_slowpath_fmt+0x55/0x70
[<ffffffff8120f218>] ? do_truncate+0x88/0xc0
[<ffffffff815427fd>] usb_submit_urb+0x2ad/0x5a0
[<ffffffff81230df4>] ? mntput+0x24/0x40
[<ffffffff8121b667>] ? terminate_walk+0xc7/0xe0
[<ffffffffa0430877>] xpad_send_led_command+0xc7/0x110 [xpad]
[<ffffffffa04308d5>] xpad_led_set+0x15/0x20 [xpad]
[<ffffffff815f9678>] led_set_brightness+0x88/0xc0
[<ffffffff815f9b0e>] brightness_store+0x7e/0xc0
[<ffffffff814b7478>] dev_attr_store+0x18/0x30
[<ffffffff8128bba7>] sysfs_kf_write+0x37/0x40
[<ffffffff8128b15d>] kernfs_fop_write+0x11d/0x170
[<ffffffff81210d17>] __vfs_write+0x37/0x100
[<ffffffff81213b28>] ? __sb_start_write+0x58/0x110
[<ffffffff813124dd>] ? security_file_permission+0x3d/0xc0
[<ffffffff81211696>] vfs_write+0xa6/0x1a0
[<ffffffff8120e93a>] ? filp_close+0x5a/0x80
[<ffffffff81212385>] SyS_write+0x55/0xc0
[<ffffffff8175f0ae>] entry_SYSCALL_64_fastpath+0x12/0x71
---[ end trace f573b768c94a66d6 ]---

This is easily reproducible with

while true; do
        for i in $(seq 0 5); do
                echo $i > /sys/class/leds/xpad0/subsystem/xpad0/brightness
        done
done

xpad_send_led_command attempts to protect against races by locking
around usb_submit_urb. This is not sufficent since usb_submit_urb
is asynchronous; the urb is considered to be in use until the callback
(xpad_irq_out) returns appropriately. Additionally, there is no
protection at all in xpad_play_effect which uses the same urb. Fix this
by creating a queue for urb submission. This will serialize requests to
ensure only one is submitted at a time.

Signed-off-by: Laura Abbott <labb...@fedoraproject.org>
---
v2: Created a proper queue for events instead of just dropping them
---
 drivers/input/joystick/xpad.c | 273 ++++++++++++++++++++++++++++++------------
 1 file changed, 194 insertions(+), 79 deletions(-)

diff --git a/drivers/input/joystick/xpad.c b/drivers/input/joystick/xpad.c
index f8850f9..e94cc49 100644
--- a/drivers/input/joystick/xpad.c
+++ b/drivers/input/joystick/xpad.c
@@ -100,6 +100,9 @@
 #define XTYPE_XBOXONE     3
 #define XTYPE_UNKNOWN     4
 
+#define OUT_IRQ_SUBMITTED      0
+#define OUT_IRQ_QUEUE_EMPTY    1
+
 static bool dpad_to_buttons;
 module_param(dpad_to_buttons, bool, S_IRUGO);
 MODULE_PARM_DESC(dpad_to_buttons, "Map D-PAD to buttons rather than axes for 
unknown pads");
@@ -334,7 +337,9 @@ struct usb_xpad {
        struct urb *irq_out;            /* urb for interrupt out report */
        unsigned char *odata;           /* output data */
        dma_addr_t odata_dma;
-       struct mutex odata_mutex;
+       unsigned long odata_flags;
+       spinlock_t queue_lock;
+       struct list_head odata_queue;   /* queue of commands */
 
 #if defined(CONFIG_JOYSTICK_XPAD_LEDS)
        struct xpad_led *led;
@@ -347,6 +352,94 @@ struct usb_xpad {
        unsigned long led_no;           /* led to lit on xbox360 controllers */
 };
 
+struct xpad_queue_item {
+       char odata[XPAD_PKT_LEN];
+       int transfer_length;
+       struct list_head entry;
+};
+
+static void __xpad_remove_urb(struct usb_xpad *xpad, struct xpad_queue_item 
*item)
+{
+       list_del(&item->entry);
+       kfree(item);
+}
+
+/*
+ * This function must be called with the queue lock held
+ *
+ * returns
+ *  0 - urb successfully submitted
+ *  OUT_IRQ_QUEUE_EMPTY - queue was empty
+ *  negative - error submitting urb
+ */
+static int __xpad_submit_urb(struct usb_xpad *xpad, bool safe_submit)
+{
+       int ret = 0;
+
+       if (list_empty(&xpad->odata_queue))
+               return OUT_IRQ_QUEUE_EMPTY;
+
+       if (safe_submit || !test_and_set_bit( OUT_IRQ_SUBMITTED, 
&xpad->odata_flags)) {
+               struct xpad_queue_item *head =
+                       list_first_entry(&xpad->odata_queue,
+                                        struct xpad_queue_item, entry);
+
+               memcpy(xpad->odata, head->odata, head->transfer_length);
+               xpad->irq_out->transfer_buffer_length = head->transfer_length;
+               ret = usb_submit_urb(xpad->irq_out, GFP_ATOMIC);
+               if (ret)
+                       __xpad_remove_urb(xpad, head);
+       }
+
+       return ret;
+}
+
+static int xpad_add_and_submit_urb(struct usb_xpad *xpad,
+                                  struct xpad_queue_item *item)
+{
+       unsigned long flags;
+       int ret;
+
+       spin_lock_irqsave(&xpad->queue_lock, flags);
+       list_add_tail(&item->entry, &xpad->odata_queue);
+       ret = __xpad_submit_urb(xpad, false);
+       spin_unlock_irqrestore(&xpad->queue_lock, flags);
+
+       return ret;
+}
+
+static int xpad_remove_urb(struct usb_xpad *xpad)
+{
+       unsigned long flags;
+       struct xpad_queue_item *head;
+       int ret = 0;
+
+       spin_lock_irqsave(&xpad->queue_lock, flags);
+       if (list_empty(&xpad->odata_queue)) {
+               ret = OUT_IRQ_QUEUE_EMPTY;
+               goto out;
+       }
+
+       head = list_first_entry(&xpad->odata_queue, struct xpad_queue_item,
+                                       entry);
+       __xpad_remove_urb(xpad, head);
+out:
+       spin_unlock_irqrestore(&xpad->queue_lock, flags);
+       return ret;
+}
+
+static int xpad_resubmit_urb(struct usb_xpad *xpad)
+{
+       unsigned long flags;
+       int ret;
+
+       spin_lock_irqsave(&xpad->queue_lock, flags);
+       ret = __xpad_submit_urb(xpad, true);
+       spin_unlock_irqrestore(&xpad->queue_lock, flags);
+
+       return ret;
+}
+
 /*
  *     xpad_process_packet
  *
@@ -707,7 +800,8 @@ static void xpad_irq_out(struct urb *urb)
        switch (status) {
        case 0:
                /* success */
-               return;
+               xpad_remove_urb(xpad);
+               break;
 
        case -ECONNRESET:
        case -ENOENT:
@@ -720,14 +814,21 @@ static void xpad_irq_out(struct urb *urb)
        default:
                dev_dbg(dev, "%s - nonzero urb status received: %d\n",
                        __func__, status);
-               goto exit;
+               break;
        }
 
-exit:
-       retval = usb_submit_urb(urb, GFP_ATOMIC);
-       if (retval)
-               dev_err(dev, "%s - usb_submit_urb failed with result %d\n",
-                       __func__, retval);
+
+       /*
+        * We must keep resubmitting, otherwise the other items in the
+        * queue will stall until until the next urb submit which may
+        * be indefinitely
+        */
+       do {
+               retval = xpad_resubmit_urb(xpad);
+       } while (retval < 0);
+
+       if (retval == OUT_IRQ_QUEUE_EMPTY)
+               clear_bit(OUT_IRQ_SUBMITTED, &xpad->odata_flags);
 }
 
 static int xpad_init_output(struct usb_interface *intf, struct usb_xpad *xpad)
@@ -746,7 +847,8 @@ static int xpad_init_output(struct usb_interface *intf, 
struct usb_xpad *xpad)
                goto fail1;
        }
 
-       mutex_init(&xpad->odata_mutex);
+       xpad->odata_flags = 0;
+       INIT_LIST_HEAD(&xpad->odata_queue);
 
        xpad->irq_out = usb_alloc_urb(0, GFP_KERNEL);
        if (!xpad->irq_out) {
@@ -780,9 +882,14 @@ static void xpad_stop_output(struct usb_xpad *xpad)
 static void xpad_deinit_output(struct usb_xpad *xpad)
 {
        if (xpad->xtype != XTYPE_UNKNOWN) {
+               int ret;
+
                usb_free_urb(xpad->irq_out);
                usb_free_coherent(xpad->udev, XPAD_PKT_LEN,
                                xpad->odata, xpad->odata_dma);
+               do {
+                       ret = xpad_remove_urb(xpad);
+               } while (ret != OUT_IRQ_QUEUE_EMPTY);
        }
 }
 
@@ -790,6 +897,11 @@ static void xpad_deinit_output(struct usb_xpad *xpad)
 static int xpad_play_effect(struct input_dev *dev, void *data, struct 
ff_effect *effect)
 {
        struct usb_xpad *xpad = input_get_drvdata(dev);
+       struct xpad_queue_item *item;
+
+       item = kzalloc(sizeof(*item), GFP_ATOMIC);
+       if (!item)
+               return -ENOMEM;
 
        if (effect->type == FF_RUMBLE) {
                __u16 strong = effect->u.rumble.strong_magnitude;
@@ -798,62 +910,62 @@ static int xpad_play_effect(struct input_dev *dev, void 
*data, struct ff_effect
                switch (xpad->xtype) {
 
                case XTYPE_XBOX:
-                       xpad->odata[0] = 0x00;
-                       xpad->odata[1] = 0x06;
-                       xpad->odata[2] = 0x00;
-                       xpad->odata[3] = strong / 256;  /* left actuator */
-                       xpad->odata[4] = 0x00;
-                       xpad->odata[5] = weak / 256;    /* right actuator */
-                       xpad->irq_out->transfer_buffer_length = 6;
+                       item->odata[0] = 0x00;
+                       item->odata[1] = 0x06;
+                       item->odata[2] = 0x00;
+                       item->odata[3] = strong / 256;  /* left actuator */
+                       item->odata[4] = 0x00;
+                       item->odata[5] = weak / 256;    /* right actuator */
+                       item->transfer_length = 6;
 
-                       return usb_submit_urb(xpad->irq_out, GFP_ATOMIC);
+                       return xpad_add_and_submit_urb(xpad, item);
 
                case XTYPE_XBOX360:
-                       xpad->odata[0] = 0x00;
-                       xpad->odata[1] = 0x08;
-                       xpad->odata[2] = 0x00;
-                       xpad->odata[3] = strong / 256;  /* left actuator? */
-                       xpad->odata[4] = weak / 256;    /* right actuator? */
-                       xpad->odata[5] = 0x00;
-                       xpad->odata[6] = 0x00;
-                       xpad->odata[7] = 0x00;
-                       xpad->irq_out->transfer_buffer_length = 8;
-
-                       return usb_submit_urb(xpad->irq_out, GFP_ATOMIC);
+                       item->odata[0] = 0x00;
+                       item->odata[1] = 0x08;
+                       item->odata[2] = 0x00;
+                       item->odata[3] = strong / 256;  /* left actuator? */
+                       item->odata[4] = weak / 256;    /* right actuator? */
+                       item->odata[5] = 0x00;
+                       item->odata[6] = 0x00;
+                       item->odata[7] = 0x00;
+                       item->transfer_length = 8;
+
+                       return xpad_add_and_submit_urb(xpad, item);
 
                case XTYPE_XBOX360W:
-                       xpad->odata[0] = 0x00;
-                       xpad->odata[1] = 0x01;
-                       xpad->odata[2] = 0x0F;
-                       xpad->odata[3] = 0xC0;
-                       xpad->odata[4] = 0x00;
-                       xpad->odata[5] = strong / 256;
-                       xpad->odata[6] = weak / 256;
-                       xpad->odata[7] = 0x00;
-                       xpad->odata[8] = 0x00;
-                       xpad->odata[9] = 0x00;
-                       xpad->odata[10] = 0x00;
-                       xpad->odata[11] = 0x00;
-                       xpad->irq_out->transfer_buffer_length = 12;
-
-                       return usb_submit_urb(xpad->irq_out, GFP_ATOMIC);
+                       item->odata[0] = 0x00;
+                       item->odata[1] = 0x01;
+                       item->odata[2] = 0x0F;
+                       item->odata[3] = 0xC0;
+                       item->odata[4] = 0x00;
+                       item->odata[5] = strong / 256;
+                       item->odata[6] = weak / 256;
+                       item->odata[7] = 0x00;
+                       item->odata[8] = 0x00;
+                       item->odata[9] = 0x00;
+                       item->odata[10] = 0x00;
+                       item->odata[11] = 0x00;
+                       item->transfer_length = 12;
+
+                       return pad_add_and_submit_urb(xpad, item);
 
                case XTYPE_XBOXONE:
-                       xpad->odata[0] = 0x09; /* activate rumble */
-                       xpad->odata[1] = 0x08;
-                       xpad->odata[2] = 0x00;
-                       xpad->odata[3] = 0x08; /* continuous effect */
-                       xpad->odata[4] = 0x00; /* simple rumble mode */
-                       xpad->odata[5] = 0x03; /* L and R actuator only */
-                       xpad->odata[6] = 0x00; /* TODO: LT actuator */
-                       xpad->odata[7] = 0x00; /* TODO: RT actuator */
-                       xpad->odata[8] = strong / 256;  /* left actuator */
-                       xpad->odata[9] = weak / 256;    /* right actuator */
-                       xpad->odata[10] = 0x80; /* length of pulse */
-                       xpad->odata[11] = 0x00; /* stop period of pulse */
-                       xpad->irq_out->transfer_buffer_length = 12;
-
-                       return usb_submit_urb(xpad->irq_out, GFP_ATOMIC);
+                       item->odata[0] = 0x09; /* activate rumble */
+                       item->odata[1] = 0x08;
+                       item->odata[2] = 0x00;
+                       item->odata[3] = 0x08; /* continuous effect */
+                       item->odata[4] = 0x00; /* simple rumble mode */
+                       item->odata[5] = 0x03; /* L and R actuator only */
+                       item->odata[6] = 0x00; /* TODO: LT actuator */
+                       item->odata[7] = 0x00; /* TODO: RT actuator */
+                       item->odata[8] = strong / 256;  /* left actuator */
+                       item->odata[9] = weak / 256;    /* right actuator */
+                       item->odata[10] = 0x80; /* length of pulse */
+                       item->odata[11] = 0x00; /* stop period of pulse */
+                       item->transfer_length = 12;
+
+                       return xpad_add_and_submit_urb(xpad, item);
 
                default:
                        dev_dbg(&xpad->dev->dev,
@@ -910,36 +1022,39 @@ struct xpad_led {
  */
 static void xpad_send_led_command(struct usb_xpad *xpad, int command)
 {
-       command %= 16;
+       struct xpad_queue_item *item;
 
-       mutex_lock(&xpad->odata_mutex);
+       item = kzalloc(sizeof(*item), GFP_KERNEL);
+       if (!item)
+               return;
+
+       command %= 16;
 
        switch (xpad->xtype) {
        case XTYPE_XBOX360:
-               xpad->odata[0] = 0x01;
-               xpad->odata[1] = 0x03;
-               xpad->odata[2] = command;
-               xpad->irq_out->transfer_buffer_length = 3;
+               item->odata[0] = 0x01;
+               item->odata[1] = 0x03;
+               item->odata[2] = command;
+               item->transfer_length = 3;
                break;
        case XTYPE_XBOX360W:
-               xpad->odata[0] = 0x00;
-               xpad->odata[1] = 0x00;
-               xpad->odata[2] = 0x08;
-               xpad->odata[3] = 0x40 + command;
-               xpad->odata[4] = 0x00;
-               xpad->odata[5] = 0x00;
-               xpad->odata[6] = 0x00;
-               xpad->odata[7] = 0x00;
-               xpad->odata[8] = 0x00;
-               xpad->odata[9] = 0x00;
-               xpad->odata[10] = 0x00;
-               xpad->odata[11] = 0x00;
-               xpad->irq_out->transfer_buffer_length = 12;
+               item->odata[0] = 0x00;
+               item->odata[1] = 0x00;
+               item->odata[2] = 0x08;
+               item->odata[3] = 0x40 + command;
+               item->odata[4] = 0x00;
+               item->odata[5] = 0x00;
+               item->odata[6] = 0x00;
+               item->odata[7] = 0x00;
+               item->odata[8] = 0x00;
+               item->odata[9] = 0x00;
+               item->odata[10] = 0x00;
+               item->odata[11] = 0x00;
+               item->transfer_length = 12;
                break;
        }
 
-       usb_submit_urb(xpad->irq_out, GFP_KERNEL);
-       mutex_unlock(&xpad->odata_mutex);
+       xpad_add_and_submit_urb(xpad, item);
 }
 
 static void xpad_identify_controller(struct usb_xpad *xpad)
-- 
2.4.3

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to