Skip to content

Commit 50966da

Browse files
Badhri Jagan Sridharangregkh
authored andcommitted
usb: gadget: udc: core: Offload usb_udc_vbus_handler processing
usb_udc_vbus_handler() can be invoked from interrupt context by irq handlers of the gadget drivers, however, usb_udc_connect_control() has to run in non-atomic context due to the following: a. Some of the gadget driver implementations expect the ->pullup callback to be invoked in non-atomic context. b. usb_gadget_disconnect() acquires udc_lock which is a mutex. Hence offload invocation of usb_udc_connect_control() to workqueue. UDC should not be pulled up unless gadget driver is bound. The new flag "allow_connect" is now set by gadget_bind_driver() and cleared by gadget_unbind_driver(). This prevents work item to pull up the gadget even if queued when the gadget driver is already unbound. Cc: stable@vger.kernel.org Fixes: 1016fc0 ("USB: gadget: Fix obscure lockdep violation for udc_mutex") Signed-off-by: Badhri Jagan Sridharan <badhri@google.com> Reviewed-by: Alan Stern <stern@rowland.harvard.edu> Message-ID: <20230609010227.978661-1-badhri@google.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent 92c9c3b commit 50966da

File tree

1 file changed

+27
-2
lines changed

1 file changed

+27
-2
lines changed

drivers/usb/gadget/udc/core.c

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ static const struct bus_type gadget_bus_type;
3737
* @vbus: for udcs who care about vbus status, this value is real vbus status;
3838
* for udcs who do not care about vbus status, this value is always true
3939
* @started: the UDC's started state. True if the UDC had started.
40+
* @allow_connect: Indicates whether UDC is allowed to be pulled up.
41+
* Set/cleared by gadget_(un)bind_driver() after gadget driver is bound or
42+
* unbound.
4043
*
4144
* This represents the internal data structure which is used by the UDC-class
4245
* to hold information about udc driver and gadget together.
@@ -48,6 +51,8 @@ struct usb_udc {
4851
struct list_head list;
4952
bool vbus;
5053
bool started;
54+
bool allow_connect;
55+
struct work_struct vbus_work;
5156
};
5257

5358
static struct class *udc_class;
@@ -706,7 +711,7 @@ int usb_gadget_connect(struct usb_gadget *gadget)
706711
goto out;
707712
}
708713

709-
if (gadget->deactivated) {
714+
if (gadget->deactivated || !gadget->udc->allow_connect) {
710715
/*
711716
* If gadget is deactivated we only save new state.
712717
* Gadget will be connected automatically after activation.
@@ -1086,6 +1091,13 @@ static void usb_udc_connect_control(struct usb_udc *udc)
10861091
usb_gadget_disconnect(udc->gadget);
10871092
}
10881093

1094+
static void vbus_event_work(struct work_struct *work)
1095+
{
1096+
struct usb_udc *udc = container_of(work, struct usb_udc, vbus_work);
1097+
1098+
usb_udc_connect_control(udc);
1099+
}
1100+
10891101
/**
10901102
* usb_udc_vbus_handler - updates the udc core vbus status, and try to
10911103
* connect or disconnect gadget
@@ -1094,14 +1106,22 @@ static void usb_udc_connect_control(struct usb_udc *udc)
10941106
*
10951107
* The udc driver calls it when it wants to connect or disconnect gadget
10961108
* according to vbus status.
1109+
*
1110+
* This function can be invoked from interrupt context by irq handlers of
1111+
* the gadget drivers, however, usb_udc_connect_control() has to run in
1112+
* non-atomic context due to the following:
1113+
* a. Some of the gadget driver implementations expect the ->pullup
1114+
* callback to be invoked in non-atomic context.
1115+
* b. usb_gadget_disconnect() acquires udc_lock which is a mutex.
1116+
* Hence offload invocation of usb_udc_connect_control() to workqueue.
10971117
*/
10981118
void usb_udc_vbus_handler(struct usb_gadget *gadget, bool status)
10991119
{
11001120
struct usb_udc *udc = gadget->udc;
11011121

11021122
if (udc) {
11031123
udc->vbus = status;
1104-
usb_udc_connect_control(udc);
1124+
schedule_work(&udc->vbus_work);
11051125
}
11061126
}
11071127
EXPORT_SYMBOL_GPL(usb_udc_vbus_handler);
@@ -1328,6 +1348,7 @@ int usb_add_gadget(struct usb_gadget *gadget)
13281348
mutex_lock(&udc_lock);
13291349
list_add_tail(&udc->list, &udc_list);
13301350
mutex_unlock(&udc_lock);
1351+
INIT_WORK(&udc->vbus_work, vbus_event_work);
13311352

13321353
ret = device_add(&udc->dev);
13331354
if (ret)
@@ -1459,6 +1480,7 @@ void usb_del_gadget(struct usb_gadget *gadget)
14591480
flush_work(&gadget->work);
14601481
device_del(&gadget->dev);
14611482
ida_free(&gadget_id_numbers, gadget->id_number);
1483+
cancel_work_sync(&udc->vbus_work);
14621484
device_unregister(&udc->dev);
14631485
}
14641486
EXPORT_SYMBOL_GPL(usb_del_gadget);
@@ -1527,6 +1549,7 @@ static int gadget_bind_driver(struct device *dev)
15271549
if (ret)
15281550
goto err_start;
15291551
usb_gadget_enable_async_callbacks(udc);
1552+
udc->allow_connect = true;
15301553
usb_udc_connect_control(udc);
15311554

15321555
kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
@@ -1558,6 +1581,8 @@ static void gadget_unbind_driver(struct device *dev)
15581581

15591582
kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
15601583

1584+
udc->allow_connect = false;
1585+
cancel_work_sync(&udc->vbus_work);
15611586
usb_gadget_disconnect(gadget);
15621587
usb_gadget_disable_async_callbacks(udc);
15631588
if (gadget->irq)

0 commit comments

Comments
 (0)