usb: gadget: f_midi: Use refcount when freeing f_midi_opts
authorJerry Zhang <zhangjerry@google.com>
Sat, 31 Mar 2018 00:54:24 +0000 (17:54 -0700)
committerFelipe Balbi <felipe.balbi@linux.intel.com>
Tue, 15 May 2018 07:08:44 +0000 (10:08 +0300)
Currently, the midi function is not freed until it is
both removed from the config and released by the user.
Since the user could take a long time to release the
card, it's possible that the function could be unlinked
and thus f_midi_opts would be null when freeing f_midi.

Thus, refcount f_midi_opts and only free it when it is
unlinked and all f_midis have been freed.

Signed-off-by: Jerry Zhang <zhangjerry@google.com>
Signed-off-by: Felipe Balbi <felipe.balbi@linux.intel.com>
drivers/usb/gadget/function/f_midi.c

index e8f35db..f806997 100644 (file)
@@ -109,6 +109,7 @@ static inline struct f_midi *func_to_midi(struct usb_function *f)
 
 static void f_midi_transmit(struct f_midi *midi);
 static void f_midi_rmidi_free(struct snd_rawmidi *rmidi);
+static void f_midi_free_inst(struct usb_function_instance *f);
 
 DECLARE_UAC_AC_HEADER_DESCRIPTOR(1);
 DECLARE_USB_MIDI_OUT_JACK_DESCRIPTOR(1);
@@ -1102,7 +1103,7 @@ static ssize_t f_midi_opts_##name##_store(struct config_item *item,       \
        u32 num;                                                        \
                                                                        \
        mutex_lock(&opts->lock);                                        \
-       if (opts->refcnt) {                                             \
+       if (opts->refcnt > 1) {                                         \
                ret = -EBUSY;                                           \
                goto end;                                               \
        }                                                               \
@@ -1157,7 +1158,7 @@ static ssize_t f_midi_opts_id_store(struct config_item *item,
        char *c;
 
        mutex_lock(&opts->lock);
-       if (opts->refcnt) {
+       if (opts->refcnt > 1) {
                ret = -EBUSY;
                goto end;
        }
@@ -1198,13 +1199,21 @@ static const struct config_item_type midi_func_type = {
 static void f_midi_free_inst(struct usb_function_instance *f)
 {
        struct f_midi_opts *opts;
+       bool free = false;
 
        opts = container_of(f, struct f_midi_opts, func_inst);
 
-       if (opts->id_allocated)
-               kfree(opts->id);
+       mutex_lock(&opts->lock);
+       if (!--opts->refcnt) {
+               free = true;
+       }
+       mutex_unlock(&opts->lock);
 
-       kfree(opts);
+       if (free) {
+               if (opts->id_allocated)
+                       kfree(opts->id);
+               kfree(opts);
+       }
 }
 
 static struct usb_function_instance *f_midi_alloc_inst(void)
@@ -1223,6 +1232,7 @@ static struct usb_function_instance *f_midi_alloc_inst(void)
        opts->qlen = 32;
        opts->in_ports = 1;
        opts->out_ports = 1;
+       opts->refcnt = 1;
 
        config_group_init_type_name(&opts->func_inst.group, "",
                                    &midi_func_type);
@@ -1234,6 +1244,7 @@ static void f_midi_free(struct usb_function *f)
 {
        struct f_midi *midi;
        struct f_midi_opts *opts;
+       bool free = false;
 
        midi = func_to_midi(f);
        opts = container_of(f->fi, struct f_midi_opts, func_inst);
@@ -1242,9 +1253,12 @@ static void f_midi_free(struct usb_function *f)
                kfree(midi->id);
                kfifo_free(&midi->in_req_fifo);
                kfree(midi);
-               --opts->refcnt;
+               free = true;
        }
        mutex_unlock(&opts->lock);
+
+       if (free)
+               f_midi_free_inst(&opts->func_inst);
 }
 
 static void f_midi_rmidi_free(struct snd_rawmidi *rmidi)