]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
kcov, usb: Don't disable interrupts in kcov_remote_start_usb_softirq()
authorSebastian Andrzej Siewior <bigeasy@linutronix.de>
Mon, 11 Aug 2025 08:27:45 +0000 (10:27 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 13 Aug 2025 15:18:43 +0000 (17:18 +0200)
kcov_remote_start_usb_softirq() the begin of urb's completion callback.
HCDs marked HCD_BH will invoke this function from the softirq and
in_serving_softirq() will detect this properly.
Root-HUB (RH) requests will not be delayed to softirq but complete
immediately in IRQ context.
This will confuse kcov because in_serving_softirq() will report true if
the softirq is served after the hardirq and if the softirq got
interrupted by the hardirq in which currently runs.

This was addressed by simply disabling interrupts in
kcov_remote_start_usb_softirq() which avoided the interruption by the RH
while a regular completion callback was invoked.
This not only changes the behaviour while kconv is enabled but also
breaks PREEMPT_RT because now sleeping locks can no longer be acquired.

Revert the previous fix. Address the issue by invoking
kcov_remote_start_usb() only if the context is just "serving softirqs"
which is identified by checking in_serving_softirq() and in_hardirq()
must be false.

Fixes: f85d39dd7ed89 ("kcov, usb: disable interrupts in kcov_remote_start_usb_softirq")
Cc: stable <stable@kernel.org>
Reported-by: Yunseong Kim <ysk@kzalloc.com>
Closes: https://lore.kernel.org/all/20250725201400.1078395-2-ysk@kzalloc.com/
Tested-by: Yunseong Kim <ysk@kzalloc.com>
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Link: https://lore.kernel.org/r/20250811082745.ycJqBXMs@linutronix.de
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/core/hcd.c
include/linux/kcov.h

index c4a1875b5d3d0c76b170c410b7844fc731335f81..6270fbb5c699881a66c0cd6a4501177e8a1e52fa 100644 (file)
@@ -1636,7 +1636,6 @@ static void __usb_hcd_giveback_urb(struct urb *urb)
        struct usb_hcd *hcd = bus_to_hcd(urb->dev->bus);
        struct usb_anchor *anchor = urb->anchor;
        int status = urb->unlinked;
-       unsigned long flags;
 
        urb->hcpriv = NULL;
        if (unlikely((urb->transfer_flags & URB_SHORT_NOT_OK) &&
@@ -1654,14 +1653,13 @@ static void __usb_hcd_giveback_urb(struct urb *urb)
        /* pass ownership to the completion handler */
        urb->status = status;
        /*
-        * Only collect coverage in the softirq context and disable interrupts
-        * to avoid scenarios with nested remote coverage collection sections
-        * that KCOV does not support.
-        * See the comment next to kcov_remote_start_usb_softirq() for details.
+        * This function can be called in task context inside another remote
+        * coverage collection section, but kcov doesn't support that kind of
+        * recursion yet. Only collect coverage in softirq context for now.
         */
-       flags = kcov_remote_start_usb_softirq((u64)urb->dev->bus->busnum);
+       kcov_remote_start_usb_softirq((u64)urb->dev->bus->busnum);
        urb->complete(urb);
-       kcov_remote_stop_softirq(flags);
+       kcov_remote_stop_softirq();
 
        usb_anchor_resume_wakeups(anchor);
        atomic_dec(&urb->use_count);
index 75a2fb8b16c32917817b8ec7f5e45421793431ae..0143358874b07b6896625395f2b649f63cb298ba 100644 (file)
@@ -57,47 +57,21 @@ static inline void kcov_remote_start_usb(u64 id)
 
 /*
  * The softirq flavor of kcov_remote_*() functions is introduced as a temporary
- * workaround for KCOV's lack of nested remote coverage sections support.
- *
- * Adding support is tracked in https://bugzilla.kernel.org/show_bug.cgi?id=210337.
- *
- * kcov_remote_start_usb_softirq():
- *
- * 1. Only collects coverage when called in the softirq context. This allows
- *    avoiding nested remote coverage collection sections in the task context.
- *    For example, USB/IP calls usb_hcd_giveback_urb() in the task context
- *    within an existing remote coverage collection section. Thus, KCOV should
- *    not attempt to start collecting coverage within the coverage collection
- *    section in __usb_hcd_giveback_urb() in this case.
- *
- * 2. Disables interrupts for the duration of the coverage collection section.
- *    This allows avoiding nested remote coverage collection sections in the
- *    softirq context (a softirq might occur during the execution of a work in
- *    the BH workqueue, which runs with in_serving_softirq() > 0).
- *    For example, usb_giveback_urb_bh() runs in the BH workqueue with
- *    interrupts enabled, so __usb_hcd_giveback_urb() might be interrupted in
- *    the middle of its remote coverage collection section, and the interrupt
- *    handler might invoke __usb_hcd_giveback_urb() again.
+ * work around for kcov's lack of nested remote coverage sections support in
+ * task context. Adding support for nested sections is tracked in:
+ * https://bugzilla.kernel.org/show_bug.cgi?id=210337
  */
 
-static inline unsigned long kcov_remote_start_usb_softirq(u64 id)
+static inline void kcov_remote_start_usb_softirq(u64 id)
 {
-       unsigned long flags = 0;
-
-       if (in_serving_softirq()) {
-               local_irq_save(flags);
+       if (in_serving_softirq() && !in_hardirq())
                kcov_remote_start_usb(id);
-       }
-
-       return flags;
 }
 
-static inline void kcov_remote_stop_softirq(unsigned long flags)
+static inline void kcov_remote_stop_softirq(void)
 {
-       if (in_serving_softirq()) {
+       if (in_serving_softirq() && !in_hardirq())
                kcov_remote_stop();
-               local_irq_restore(flags);
-       }
 }
 
 #ifdef CONFIG_64BIT
@@ -131,11 +105,8 @@ static inline u64 kcov_common_handle(void)
 }
 static inline void kcov_remote_start_common(u64 id) {}
 static inline void kcov_remote_start_usb(u64 id) {}
-static inline unsigned long kcov_remote_start_usb_softirq(u64 id)
-{
-       return 0;
-}
-static inline void kcov_remote_stop_softirq(unsigned long flags) {}
+static inline void kcov_remote_start_usb_softirq(u64 id) {}
+static inline void kcov_remote_stop_softirq(void) {}
 
 #endif /* CONFIG_KCOV */
 #endif /* _LINUX_KCOV_H */