Re: USB gadgets with configfs hang reboot

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Sat, Apr 02 2016, Alan Stern wrote:
> On Sat, 2 Apr 2016, Michal Nazarewicz wrote:
>> At the same time, mass storage should work fine if it’s bound to
>> multiple configurations.  Only one configuration can be active at any
>> given time so interfaces on different configurations cannot interfere
>> with each other.

> Yes, it _should_.  But it doesn't with the nokia legacy driver.
> I don't know if this has any connection with configfs; it could be
> a problem with the way f_mass_storage interacts with the composite
> core.

I believe the failure is related to the thread being started twice where
it indeed shouldn’t.

>> The problem we are having is that when mass storage is added to
>> a configuration, fsg_bind is called and it starts the thread.

> This is what I'm not sure about.  Which callbacks does the composite
> core invoke when a config is installed or uninstalled?

usb_add_config is what is called to create a usb_configuration.  It
initialises the structure and passes it to a callback bind function
(most code removed for brevity):

int usb_add_config(struct usb_composite_dev *cdev,
		struct usb_configuration *config,
		int (*bind)(struct usb_configuration *))
{
	usb_add_config_only(cdev, config);
	bind(config);
	/* set_alt(), or next bind(), sets up ep->claimed as needed */
	usb_ep_autoconfig_reset(cdev->gadget);
	return 0;
}

The bind callback calls usb_add_function to add usb_function to a newly
created usb_configuration (again, most code removed for brevity):

int usb_add_function(struct usb_configuration *config,
		struct usb_function *function)
{
	function->config = config;
	value = function->bind(config, function);
	return 0;
}

For mass_storage, function->bind is fsg_bind (ditto):

static struct usb_function *fsg_alloc(struct usb_function_instance *fi)
{
	struct fsg_dev *fsg kzalloc(sizeof(*fsg), GFP_KERNEL);
	fsg->function.name	= FSG_DRIVER_DESC;
	fsg->function.bind	= fsg_bind;	/* !!! */
	fsg->function.unbind	= fsg_unbind;
	fsg->function.setup	= fsg_setup;
	fsg->function.set_alt	= fsg_set_alt;
	fsg->function.disable	= fsg_disable;
	fsg->function.free_func	= fsg_free;
	fsg->common               = common;
	return &fsg->function;
}

> Those callbacks should be where the thread is started and stopped.

Starting thread in that callback is what is happening right now and
because single usb_function_instance can have multiple usb_function
structures each binding to separate usb_configuration, this causes
problem where the thread is started multiple times.

This is also why the thread is not stopped in fsg_unbind but only once
fsg_common structure is released.

Conceptually, the thread should be started when fsg_common structure is
created (which is at the same time when usb_function_instance is
created) and stopped when fsg_common is released.

At this point, I’m not entirely sure if there is a reason why this isn’t
the case.  The only reason I can think of is that starting the thread
right away may be considered wasteful since the thread won’t have
anything to do until the function is bound to a configuration.  In the
current code, there may also be issues where perhaps the thread would
not get stopped if fsg_bind has never been called.

Because of all that, I think the best course of action is to just check
whether the thread is running and conditionally start it in fsg_bind,
i.e.:

--- a/drivers/usb/gadget/function/f_mass_storage.c
+++ b/drivers/usb/gadget/function/f_mass_storage.c
@@ -2979,20 +2979,7 @@ EXPORT_SYMBOL_GPL(fsg_common_set_inquiry_string);
 
 int fsg_common_run_thread(struct fsg_common *common)
 {
-       common->state = FSG_STATE_IDLE;
-       /* Tell the thread to start working */
-       common->thread_task =
-               kthread_create(fsg_main_thread, common, "file-storage");
-       if (IS_ERR(common->thread_task)) {
-               common->state = FSG_STATE_TERMINATED;
-               return PTR_ERR(common->thread_task);
-       }
-
-       DBG(common, "I/O thread pid: %d\n", task_pid_nr(common->thread_task));
-
-       wake_up_process(common->thread_task);
-
-       return 0;
+       /* kill this function and all call sites */
 }
 EXPORT_SYMBOL_GPL(fsg_common_run_thread);
 
@@ -3005,6 +2992,7 @@ static void fsg_common_release(struct kref *ref)
        if (common->state != FSG_STATE_TERMINATED) {
                raise_exception(common, FSG_STATE_EXIT);
                wait_for_completion(&common->thread_notifier);
+               common->thread_task = NULL;
        }
 
        for (i = 0; i < ARRAY_SIZE(common->luns); ++i) {
@@ -3050,9 +3038,21 @@ static int fsg_bind(struct usb_configuration *c, struct usb_function *f)
                if (ret)
                        return ret;
                fsg_common_set_inquiry_string(fsg->common, NULL, NULL);
-               ret = fsg_common_run_thread(fsg->common);
-               if (ret)
+       }
+
+       if (!common->thread_task) {
+               common->state = FSG_STATE_IDLE;
+               common->thread_task =
+                       kthread_create(fsg_main_thread, common, "file-storage");
+               if (IS_ERR(common->thread_task)) {
+                       int ret = PTR_ERR(common->thread_task);
+                       common->thread_task = NULL;
+                       common->state = FSG_STATE_TERMINATED;
                        return ret;
+               }
+               DBG(common, "I/O thread pid: %d\n",
+                   task_pid_nr(common->thread_task));
+               wake_up_process(common->thread_task);
        }
 
        fsg->gadget = gadget;

This should get rid of all the confusion and just do the right thing.

-- 
Best regards
ミハウ “𝓶𝓲𝓷𝓪86” ナザレヴイツ
«If at first you don’t succeed, give up skydiving»
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux Arm (vger)]     [ARM Kernel]     [ARM MSM]     [Linux Tegra]     [Linux WPAN Networking]     [Linux Wireless Networking]     [Maemo Users]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite Trails]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux