]> git.itanic.dy.fi Git - linux-stable/commitdiff
Input: iforce - wake up after clearing IFORCE_XMIT_RUNNING flag
authorTetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Sun, 28 Aug 2022 03:36:27 +0000 (20:36 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 15 Sep 2022 10:04:51 +0000 (12:04 +0200)
commit 98e01215708b6d416345465c09dce2bd4868c67a upstream.

syzbot is reporting hung task at __input_unregister_device() [1], for
iforce_close() waiting at wait_event_interruptible() with dev->mutex held
is blocking input_disconnect_device() from __input_unregister_device().

It seems that the cause is simply that commit c2b27ef672992a20 ("Input:
iforce - wait for command completion when closing the device") forgot to
call wake_up() after clear_bit().

Fix this problem by introducing a helper that calls clear_bit() followed
by wake_up_all().

Reported-by: syzbot <syzbot+deb6abc36aad4008f407@syzkaller.appspotmail.com>
Fixes: c2b27ef672992a20 ("Input: iforce - wait for command completion when closing the device")
Tested-by: syzbot <syzbot+deb6abc36aad4008f407@syzkaller.appspotmail.com>
Suggested-by: Fabio M. De Francesco <fmdefrancesco@gmail.com>
Co-developed-by: Hillf Danton <hdanton@sina.com>
Signed-off-by: Hillf Danton <hdanton@sina.com>
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Link: https://lore.kernel.org/r/887021c3-4f13-40ce-c8b9-aa6e09faa3a7@I-love.SAKURA.ne.jp
Cc: stable@vger.kernel.org
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/input/joystick/iforce/iforce-serio.c
drivers/input/joystick/iforce/iforce-usb.c
drivers/input/joystick/iforce/iforce.h

index f95a81b9fac72ce2050cac3c27df2b65166c6f13..2380546d79782d5b5dee309a65818f7299b6bff4 100644 (file)
@@ -39,7 +39,7 @@ static void iforce_serio_xmit(struct iforce *iforce)
 
 again:
        if (iforce->xmit.head == iforce->xmit.tail) {
-               clear_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags);
+               iforce_clear_xmit_and_wake(iforce);
                spin_unlock_irqrestore(&iforce->xmit_lock, flags);
                return;
        }
@@ -64,7 +64,7 @@ static void iforce_serio_xmit(struct iforce *iforce)
        if (test_and_clear_bit(IFORCE_XMIT_AGAIN, iforce->xmit_flags))
                goto again;
 
-       clear_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags);
+       iforce_clear_xmit_and_wake(iforce);
 
        spin_unlock_irqrestore(&iforce->xmit_lock, flags);
 }
@@ -169,7 +169,7 @@ static irqreturn_t iforce_serio_irq(struct serio *serio,
                        iforce_serio->cmd_response_len = iforce_serio->len;
 
                        /* Signal that command is done */
-                       wake_up(&iforce->wait);
+                       wake_up_all(&iforce->wait);
                } else if (likely(iforce->type)) {
                        iforce_process_packet(iforce, iforce_serio->id,
                                              iforce_serio->data_in,
index ea58805c480fa91c9d2dfdfec4d857200986021f..cba92bd590a8d83b4ded836059b5d882fb8751b2 100644 (file)
@@ -30,7 +30,7 @@ static void __iforce_usb_xmit(struct iforce *iforce)
        spin_lock_irqsave(&iforce->xmit_lock, flags);
 
        if (iforce->xmit.head == iforce->xmit.tail) {
-               clear_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags);
+               iforce_clear_xmit_and_wake(iforce);
                spin_unlock_irqrestore(&iforce->xmit_lock, flags);
                return;
        }
@@ -58,9 +58,9 @@ static void __iforce_usb_xmit(struct iforce *iforce)
        XMIT_INC(iforce->xmit.tail, n);
 
        if ( (n=usb_submit_urb(iforce_usb->out, GFP_ATOMIC)) ) {
-               clear_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags);
                dev_warn(&iforce_usb->intf->dev,
                         "usb_submit_urb failed %d\n", n);
+               iforce_clear_xmit_and_wake(iforce);
        }
 
        /* The IFORCE_XMIT_RUNNING bit is not cleared here. That's intended.
@@ -175,15 +175,15 @@ static void iforce_usb_out(struct urb *urb)
        struct iforce *iforce = &iforce_usb->iforce;
 
        if (urb->status) {
-               clear_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags);
                dev_dbg(&iforce_usb->intf->dev, "urb->status %d, exiting\n",
                        urb->status);
+               iforce_clear_xmit_and_wake(iforce);
                return;
        }
 
        __iforce_usb_xmit(iforce);
 
-       wake_up(&iforce->wait);
+       wake_up_all(&iforce->wait);
 }
 
 static int iforce_usb_probe(struct usb_interface *intf,
index 6aa761ebbdf7725e639ccd7cb974fb36bcd3ad45..9ccb9107ccbef09651a7980023cbd7f30067e0c0 100644 (file)
@@ -119,6 +119,12 @@ static inline int iforce_get_id_packet(struct iforce *iforce, u8 id,
                                         response_data, response_len);
 }
 
+static inline void iforce_clear_xmit_and_wake(struct iforce *iforce)
+{
+       clear_bit(IFORCE_XMIT_RUNNING, iforce->xmit_flags);
+       wake_up_all(&iforce->wait);
+}
+
 /* Public functions */
 /* iforce-main.c */
 int iforce_init_device(struct device *parent, u16 bustype,