After fsg_unbind() is called on UMS teardown, the fsg_main_thread()
which also invokes handle_exception() is unlikely to run afterward.
Therefore, any exceptions raised in fsg_unbind(), and also exceptions
raised in fsg_common_release() which is called from both fsg_unbind()
and fsg_common_init() fail path, will remain unhandled.

This leads to a problem where FSG_STATE_TERMINATED/EXIT handling is
not performed on UMS exit and the do_set_interface(..., NULL) which
shuts down endpoints is also not performed, leaving the endpoints
enabled.

The CI UDC driver does check for repeated enablement of endpoints and
returns -EBUSY if an endpoint is enabled repeatedly. That prevents
UMS from being started after it was exited once already. That is,
running "ums 0 mmc 0 ; ums 0 mmc 0" on U-Boot command line will lead
to one successful and one failed UMS session. The later session ends
up triggering fsg_is_set() assertion in fsg_setup() and triggers a
panic().

Fix this by synchronously invoking the do_set_interface() from both
fsg_unbind() and fsg_common_release(). That makes the unbind behave
as intended, as if the fsg_main_thread() ran after fsg_unbind() and
handled the outstanding raised exceptions.

Signed-off-by: Marek Vasut <[email protected]>
---
Cc: Mattijs Korpershoek <[email protected]>
Cc: Tom Rini <[email protected]>
Cc: [email protected]
---
 drivers/usb/gadget/f_mass_storage.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/usb/gadget/f_mass_storage.c 
b/drivers/usb/gadget/f_mass_storage.c
index 7eb667c130d..76ee08dc5e8 100644
--- a/drivers/usb/gadget/f_mass_storage.c
+++ b/drivers/usb/gadget/f_mass_storage.c
@@ -2565,8 +2565,8 @@ static void fsg_common_release(struct fsg_common *common)
 {
        /* If the thread isn't already dead, tell it to exit now */
        if (common->state != FSG_STATE_TERMINATED) {
-               raise_exception(common, FSG_STATE_EXIT);
-               wait_for_completion(&common->thread_notifier);
+               do_set_interface(common, NULL);
+               common->state = FSG_STATE_EXIT;
        }
 
        if (likely(common->luns)) {
@@ -2647,8 +2647,8 @@ static void fsg_unbind(struct usb_configuration *c, 
struct usb_function *f)
 
        DBG(fsg, "unbind\n");
        if (fsg->common->fsg == fsg) {
-               fsg->common->new_fsg = NULL;
-               raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE);
+               do_set_interface(fsg->common, NULL);
+               fsg->common->state = FSG_STATE_CONFIG_CHANGE;
        }
 
        fsg_common_release(fsg->common);
-- 
2.53.0

Reply via email to