usb: gadget: f_hid: wake up readers on disable/unbind
[ Upstream commit 937a8a3a8d46a3377b4195cd8f2aa656666ebc8b ] Similar to how it is done in the write path. Add a disabled flag to track the function state and use it to exit the read loops to ensure no readers get stuck when the function is disabled/unbound, protecting against corruption when the waitq and spinlocks are reinitialized in hidg_bind(). Signed-off-by: Peter Korsgaard <peter@korsgaard.com> Link: https://lore.kernel.org/r/20250318152207.330997-1-peter@korsgaard.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
43ea23645b
commit
cf69fedbb1
@@ -75,6 +75,7 @@ struct f_hidg {
|
||||
/* recv report */
|
||||
spinlock_t read_spinlock;
|
||||
wait_queue_head_t read_queue;
|
||||
bool disabled;
|
||||
/* recv report - interrupt out only (use_out_ep == 1) */
|
||||
struct list_head completed_out_req;
|
||||
unsigned int qlen;
|
||||
@@ -329,7 +330,7 @@ static ssize_t f_hidg_intout_read(struct file *file, char __user *buffer,
|
||||
|
||||
spin_lock_irqsave(&hidg->read_spinlock, flags);
|
||||
|
||||
#define READ_COND_INTOUT (!list_empty(&hidg->completed_out_req))
|
||||
#define READ_COND_INTOUT (!list_empty(&hidg->completed_out_req) || hidg->disabled)
|
||||
|
||||
/* wait for at least one buffer to complete */
|
||||
while (!READ_COND_INTOUT) {
|
||||
@@ -343,6 +344,11 @@ static ssize_t f_hidg_intout_read(struct file *file, char __user *buffer,
|
||||
spin_lock_irqsave(&hidg->read_spinlock, flags);
|
||||
}
|
||||
|
||||
if (hidg->disabled) {
|
||||
spin_unlock_irqrestore(&hidg->read_spinlock, flags);
|
||||
return -ESHUTDOWN;
|
||||
}
|
||||
|
||||
/* pick the first one */
|
||||
list = list_first_entry(&hidg->completed_out_req,
|
||||
struct f_hidg_req_list, list);
|
||||
@@ -387,7 +393,7 @@ static ssize_t f_hidg_intout_read(struct file *file, char __user *buffer,
|
||||
return count;
|
||||
}
|
||||
|
||||
#define READ_COND_SSREPORT (hidg->set_report_buf != NULL)
|
||||
#define READ_COND_SSREPORT (hidg->set_report_buf != NULL || hidg->disabled)
|
||||
|
||||
static ssize_t f_hidg_ssreport_read(struct file *file, char __user *buffer,
|
||||
size_t count, loff_t *ptr)
|
||||
@@ -1012,6 +1018,11 @@ static void hidg_disable(struct usb_function *f)
|
||||
}
|
||||
spin_unlock_irqrestore(&hidg->get_report_spinlock, flags);
|
||||
|
||||
spin_lock_irqsave(&hidg->read_spinlock, flags);
|
||||
hidg->disabled = true;
|
||||
spin_unlock_irqrestore(&hidg->read_spinlock, flags);
|
||||
wake_up(&hidg->read_queue);
|
||||
|
||||
spin_lock_irqsave(&hidg->write_spinlock, flags);
|
||||
if (!hidg->write_pending) {
|
||||
free_ep_req(hidg->in_ep, hidg->req);
|
||||
@@ -1097,6 +1108,10 @@ static int hidg_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
|
||||
}
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&hidg->read_spinlock, flags);
|
||||
hidg->disabled = false;
|
||||
spin_unlock_irqrestore(&hidg->read_spinlock, flags);
|
||||
|
||||
if (hidg->in_ep != NULL) {
|
||||
spin_lock_irqsave(&hidg->write_spinlock, flags);
|
||||
hidg->req = req_in;
|
||||
|
||||
Reference in New Issue
Block a user