diff mbox

USB gadgets with configfs hang reboot

Message ID xa1tmvpdz2h4.fsf@mina86.com (mailing list archive)
State New, archived
Headers show

Commit Message

Michał Nazarewicz April 1, 2016, 5:02 p.m. UTC
On Thu, Mar 31 2016, Alan Stern wrote:
> Michal, I'm not sure how you intended to handle this.

For legacy/nokia setting no_configfs should be a valid solution:

---- >8 ----------------------------------------------------------------
---- >8 ----------------------------------------------------------------

With that, the whole fsg_common_run_thread call should be moved outside
of the if (!no_configfs) and legacy should no longer call it IMO.

Comments

Alan Stern April 1, 2016, 7:18 p.m. UTC | #1
On Fri, 1 Apr 2016, Michal Nazarewicz wrote:

> On Thu, Mar 31 2016, Alan Stern wrote:
> > Michal, I'm not sure how you intended to handle this.
> 
> For legacy/nokia setting no_configfs should be a valid solution:
> 
> ---- >8 ----------------------------------------------------------------
> diff --git a/drivers/usb/gadget/legacy/nokia.c b/drivers/usb/gadget/legacy/nokia.c
> index 0997504..e4bc607 100644
> --- a/drivers/usb/gadget/legacy/nokia.c
> +++ b/drivers/usb/gadget/legacy/nokia.c
> @@ -223,6 +223,7 @@ static int nokia_bind_config(struct usb_configuration *c)
>         }
>  
>         fsg_opts = fsg_opts_from_func_inst(fi_msg);
> +       fsg_opts->no_configfs = true;
>  
>         status = fsg_common_run_thread(fsg_opts->common);
>         if (status)
> ---- >8 ----------------------------------------------------------------
> 
> This is what other legacy gadgets do after all.

Okay, that makes sense.  Legacy gadgets aren't meant to be used with 
configfs anyway.

> For configfs, why not simply check if the thread has already been
> started:
> 
> ---- >8 ----------------------------------------------------------------
> diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c
> index acf210f..62854b6 100644
> --- a/drivers/usb/gadget/function/f_mass_storage.c
> +++ b/drivers/usb/gadget/function/f_mass_storage.c
> @@ -2984,8 +2984,10 @@ int fsg_common_run_thread(struct fsg_common *common)
>         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 PTR_ERR(common->thread_task);
> +               return ret;
>         }
>  
>         DBG(common, "I/O thread pid: %d\n", task_pid_nr(common->thread_task));
> @@ -3005,6 +3007,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 +3053,11 @@ 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)
> -                       return ret;
> +               if (common->thread_task) {

Do you mean: if (!common->thread_task)?

> +                       ret = fsg_common_run_thread(fsg->common);
> +                       if (ret)
> +                               return ret;
> +               }
>         }
>  
>         fsg->gadget = gadget;
> ---- >8 ----------------------------------------------------------------
> 
> With that, the whole fsg_common_run_thread call should be moved outside
> of the if (!no_configfs) and legacy should no longer call it IMO.

I'm not so sure about this.  Earlier I was undecided about what to do
if there are multiple mass-storage interfaces in the same config, but
now I realize that one thread won't work in that situation.  The
difficulty is exception handling.  If an error occurs in one of the
interfaces, it shouldn't affect the others -- but with only one thread
you can't make that work.

I don't know the details of how the composite core works (I don't even
remember the difference between a usb_function and a
usb_function_instance).  Suppose there are several interfaces in one
config, each bound to the mass-storage driver.  Then won't the driver's
bind routine get called several times whenever the config is installed?

If so, then each of those calls should create a new thread.  There's no
need to check if the thread has already been started.

If not, then there's no way to use f_mass_storage on multiple 
interfaces in a single config.  The driver should refuse to run on more 
than one interface at a time.

Alan Stern

--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Michał Nazarewicz April 1, 2016, 10:16 p.m. UTC | #2
> On Fri, 1 Apr 2016, Michal Nazarewicz wrote:
>> @@ -3050,9 +3053,11 @@ 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)
>> -                       return ret;
>> +               if (common->thread_task) {
>
On Fri, Apr 01 2016, Alan Stern wrote:
> Do you mean: if (!common->thread_task)?

Correct.

>> +                       ret = fsg_common_run_thread(fsg->common);
>> +                       if (ret)
>> +                               return ret;
>> +               }
>>         }
>>  
>>         fsg->gadget = gadget;
>> ---- >8 ----------------------------------------------------------------
>> 
>> With that, the whole fsg_common_run_thread call should be moved outside
>> of the if (!no_configfs) and legacy should no longer call it IMO.
>
> I'm not so sure about this.  Earlier I was undecided about what to do
> if there are multiple mass-storage interfaces in the same config, but
> now I realize that one thread won't work in that situation.  The
> difficulty is exception handling.  If an error occurs in one of the
> interfaces, it shouldn't affect the others -- but with only one thread
> you can't make that work.
>
> I don't know the details of how the composite core works (I don't even
> remember the difference between a usb_function and
> a usb_function_instance).

configfs made things quite confusing, yes.  IIRC, usb_function roughly
corresponds to an interface while usb_function_instance corresponds to
a module/function driver, except you can easily create multiple
instances.

> Suppose there are several interfaces in one config, each bound to the
> mass-storage driver.  Then won't the driver's bind routine get called
> several times whenever the config is installed?
>
> If so, then each of those calls should create a new thread.  There's no
> need to check if the thread has already been started.
>
> If not, then there's no way to use f_mass_storage on multiple
> interfaces in a single config.  The driver should refuse to run on
> more than one interface at a time.

Right, but that’s not what is happening.  We have a situation where
single mass storage instance is bound to two interfaces on two different
configs:

	mkdir functions/mass_storage.0
	echo $file > functions/mass_storage.0/lun.0/file
	ln -s functions/mass_storage.0 configs/c.1
	ln -s functions/mass_storage.0 configs/c.2

configfs doesn’t even allows to express situation where a single
instance of a function is bound to multiple interfaces in a single
configuration (IIRC).

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.

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

If someone wanted to have two mass storage interfaces on a single
configuration they would have to create two mass storage instances and
each instance has it’s own fsg_common struct (see fsg_alloc_inst
function).

PS. It seems that legacy/multi is broken because it calls
    fsg_common_run_thread twice.
Alan Stern April 2, 2016, 2:55 p.m. UTC | #3
On Sat, 2 Apr 2016, Michal Nazarewicz wrote:

> > I'm not so sure about this.  Earlier I was undecided about what to do
> > if there are multiple mass-storage interfaces in the same config, but
> > now I realize that one thread won't work in that situation.  The
> > difficulty is exception handling.  If an error occurs in one of the
> > interfaces, it shouldn't affect the others -- but with only one thread
> > you can't make that work.
> >
> > I don't know the details of how the composite core works (I don't even
> > remember the difference between a usb_function and
> > a usb_function_instance).
> 
> configfs made things quite confusing, yes.  IIRC, usb_function roughly
> corresponds to an interface while usb_function_instance corresponds to
> a module/function driver, except you can easily create multiple
> instances.

That sounds right.  I vaguely remember when I did look at this stuff
years ago, the names were backward -- usb_function refers to an
instance while usb_function_instance refers to a driver (which
obviously doesn't have instances).

> > Suppose there are several interfaces in one config, each bound to the
> > mass-storage driver.  Then won't the driver's bind routine get called
> > several times whenever the config is installed?
> >
> > If so, then each of those calls should create a new thread.  There's no
> > need to check if the thread has already been started.
> >
> > If not, then there's no way to use f_mass_storage on multiple
> > interfaces in a single config.  The driver should refuse to run on
> > more than one interface at a time.
> 
> Right, but that’s not what is happening.  We have a situation where
> single mass storage instance is bound to two interfaces on two different
> configs:
> 
> 	mkdir functions/mass_storage.0
> 	echo $file > functions/mass_storage.0/lun.0/file
> 	ln -s functions/mass_storage.0 configs/c.1
> 	ln -s functions/mass_storage.0 configs/c.2
> 
> configfs doesn’t even allows to express situation where a single
> instance of a function is bound to multiple interfaces in a single
> configuration (IIRC).

That doesn't matter for our purposes now.

> 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.

> 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?  Those
callbacks should be where the thread is started and stopped.

> If someone wanted to have two mass storage interfaces on a single
> configuration they would have to create two mass storage instances and
> each instance has it’s own fsg_common struct (see fsg_alloc_inst
> function).
> 
> PS. It seems that legacy/multi is broken because it calls
>     fsg_common_run_thread twice.

Perhaps because f_mass_storage calls fsg_common_run_thread from the
wrong place.

Alan Stern

--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Michał Nazarewicz April 5, 2016, 3:18 p.m. UTC | #4
On Fri, Apr 01 2016, Michal Nazarewicz wrote:
> For legacy/nokia setting no_configfs should be a valid solution:
>
> ---- >8 ----------------------------------------------------------------
> diff --git a/drivers/usb/gadget/legacy/nokia.c b/drivers/usb/gadget/legacy/nokia.c
> index 0997504..e4bc607 100644
> --- a/drivers/usb/gadget/legacy/nokia.c
> +++ b/drivers/usb/gadget/legacy/nokia.c
> @@ -223,6 +223,7 @@ static int nokia_bind_config(struct usb_configuration *c)
>         }
>  
>         fsg_opts = fsg_opts_from_func_inst(fi_msg);
> +       fsg_opts->no_configfs = true;
>  
>         status = fsg_common_run_thread(fsg_opts->common);
>         if (status)
> ---- >8 ----------------------------------------------------------------
>
> This is what other legacy gadgets do after all.

Scratch that.  legacy/nokia sets no_configfs in nokia_bind so
legacy/nokia appears to be a different issue all together.
diff mbox

Patch

diff --git a/drivers/usb/gadget/legacy/nokia.c b/drivers/usb/gadget/legacy/nokia.c
index 0997504..e4bc607 100644
--- a/drivers/usb/gadget/legacy/nokia.c
+++ b/drivers/usb/gadget/legacy/nokia.c
@@ -223,6 +223,7 @@  static int nokia_bind_config(struct usb_configuration *c)
        }
 
        fsg_opts = fsg_opts_from_func_inst(fi_msg);
+       fsg_opts->no_configfs = true;
 
        status = fsg_common_run_thread(fsg_opts->common);
        if (status)
---- >8 ----------------------------------------------------------------

This is what other legacy gadgets do after all.

For configfs, why not simply check if the thread has already been
started:

---- >8 ----------------------------------------------------------------
diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c
index acf210f..62854b6 100644
--- a/drivers/usb/gadget/function/f_mass_storage.c
+++ b/drivers/usb/gadget/function/f_mass_storage.c
@@ -2984,8 +2984,10 @@  int fsg_common_run_thread(struct fsg_common *common)
        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 PTR_ERR(common->thread_task);
+               return ret;
        }
 
        DBG(common, "I/O thread pid: %d\n", task_pid_nr(common->thread_task));
@@ -3005,6 +3007,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 +3053,11 @@  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)
-                       return ret;
+               if (common->thread_task) {
+                       ret = fsg_common_run_thread(fsg->common);
+                       if (ret)
+                               return ret;
+               }
        }
 
        fsg->gadget = gadget;