Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

kcov, usb: Don't disable interrupts in kcov_remote_start_usb_softirq()

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>

authored by

Sebastian Andrzej Siewior and committed by
Greg Kroah-Hartman
9528d328 86f390ba

+14 -45
+5 -7
drivers/usb/core/hcd.c
··· 1636 1636 struct usb_hcd *hcd = bus_to_hcd(urb->dev->bus); 1637 1637 struct usb_anchor *anchor = urb->anchor; 1638 1638 int status = urb->unlinked; 1639 - unsigned long flags; 1640 1639 1641 1640 urb->hcpriv = NULL; 1642 1641 if (unlikely((urb->transfer_flags & URB_SHORT_NOT_OK) && ··· 1653 1654 /* pass ownership to the completion handler */ 1654 1655 urb->status = status; 1655 1656 /* 1656 - * Only collect coverage in the softirq context and disable interrupts 1657 - * to avoid scenarios with nested remote coverage collection sections 1658 - * that KCOV does not support. 1659 - * See the comment next to kcov_remote_start_usb_softirq() for details. 1657 + * This function can be called in task context inside another remote 1658 + * coverage collection section, but kcov doesn't support that kind of 1659 + * recursion yet. Only collect coverage in softirq context for now. 1660 1660 */ 1661 - flags = kcov_remote_start_usb_softirq((u64)urb->dev->bus->busnum); 1661 + kcov_remote_start_usb_softirq((u64)urb->dev->bus->busnum); 1662 1662 urb->complete(urb); 1663 - kcov_remote_stop_softirq(flags); 1663 + kcov_remote_stop_softirq(); 1664 1664 1665 1665 usb_anchor_resume_wakeups(anchor); 1666 1666 atomic_dec(&urb->use_count);
+9 -38
include/linux/kcov.h
··· 57 57 58 58 /* 59 59 * The softirq flavor of kcov_remote_*() functions is introduced as a temporary 60 - * workaround for KCOV's lack of nested remote coverage sections support. 61 - * 62 - * Adding support is tracked in https://bugzilla.kernel.org/show_bug.cgi?id=210337. 63 - * 64 - * kcov_remote_start_usb_softirq(): 65 - * 66 - * 1. Only collects coverage when called in the softirq context. This allows 67 - * avoiding nested remote coverage collection sections in the task context. 68 - * For example, USB/IP calls usb_hcd_giveback_urb() in the task context 69 - * within an existing remote coverage collection section. Thus, KCOV should 70 - * not attempt to start collecting coverage within the coverage collection 71 - * section in __usb_hcd_giveback_urb() in this case. 72 - * 73 - * 2. Disables interrupts for the duration of the coverage collection section. 74 - * This allows avoiding nested remote coverage collection sections in the 75 - * softirq context (a softirq might occur during the execution of a work in 76 - * the BH workqueue, which runs with in_serving_softirq() > 0). 77 - * For example, usb_giveback_urb_bh() runs in the BH workqueue with 78 - * interrupts enabled, so __usb_hcd_giveback_urb() might be interrupted in 79 - * the middle of its remote coverage collection section, and the interrupt 80 - * handler might invoke __usb_hcd_giveback_urb() again. 60 + * work around for kcov's lack of nested remote coverage sections support in 61 + * task context. Adding support for nested sections is tracked in: 62 + * https://bugzilla.kernel.org/show_bug.cgi?id=210337 81 63 */ 82 64 83 - static inline unsigned long kcov_remote_start_usb_softirq(u64 id) 65 + static inline void kcov_remote_start_usb_softirq(u64 id) 84 66 { 85 - unsigned long flags = 0; 86 - 87 - if (in_serving_softirq()) { 88 - local_irq_save(flags); 67 + if (in_serving_softirq() && !in_hardirq()) 89 68 kcov_remote_start_usb(id); 90 - } 91 - 92 - return flags; 93 69 } 94 70 95 - static inline void kcov_remote_stop_softirq(unsigned long flags) 71 + static inline void kcov_remote_stop_softirq(void) 96 72 { 97 - if (in_serving_softirq()) { 73 + if (in_serving_softirq() && !in_hardirq()) 98 74 kcov_remote_stop(); 99 - local_irq_restore(flags); 100 - } 101 75 } 102 76 103 77 #ifdef CONFIG_64BIT ··· 105 131 } 106 132 static inline void kcov_remote_start_common(u64 id) {} 107 133 static inline void kcov_remote_start_usb(u64 id) {} 108 - static inline unsigned long kcov_remote_start_usb_softirq(u64 id) 109 - { 110 - return 0; 111 - } 112 - static inline void kcov_remote_stop_softirq(unsigned long flags) {} 134 + static inline void kcov_remote_start_usb_softirq(u64 id) {} 135 + static inline void kcov_remote_stop_softirq(void) {} 113 136 114 137 #endif /* CONFIG_KCOV */ 115 138 #endif /* _LINUX_KCOV_H */