usb: gadget: Use usb_put_function instead of usb_remove_function
authorAndrzej Pietrasiewicz <andrzej.p@samsung.com>
Wed, 11 Jun 2014 05:09:23 +0000 (14:09 +0900)
committerChanho Park <chanho61.park@samsung.com>
Tue, 18 Nov 2014 03:00:17 +0000 (12:00 +0900)
When the gadget is disabled with writing "0" to the "enable" attribute,
usb_remove_config() is called, and it calls remove_config(), which in
turn removes a function (list_del(&f->list)) from a list of functions
in the configuration, then calls e.g. acm function's unbind
(acm_unbind()),
then calls slp_multi_unbind_config(), which
calls slp_multi_unbind_enabled_functions(), which, for the acm function,
calls acm_function_unbind_config() which then does usb_remove_config()
and the latter attempts to once again remove a function
(list_del(&f->list)) from a list of functions in the configuration.
Since the list_head of an already deleted entry has LIST_POISON1 and
LIST_POISON2 in its prev and next pointers, dereferencing them in
list_del() causes a page fault.
The fix is to use usb_put_function instead, because when it is called
the function is already removed.
This patch also cleans up
the places usb_get/put_function_instance() and usb_get/put_function()
are called.

Signed-off-by: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
drivers/usb/gadget/slp.c

index f4b6d1f..d08fe29 100644 (file)
@@ -271,19 +271,12 @@ acm_function_init(struct slp_multi_usb_function *f,
                        ret = PTR_ERR(config->f_acm_inst[i]);
                        goto err_usb_get_function_instance;
                }
-               config->f_acm[i] = usb_get_function(config->f_acm_inst[i]);
-               if (IS_ERR(config->f_acm[i])) {
-                       ret = PTR_ERR(config->f_acm[i]);
-                       goto err_usb_get_function;
-               }
        }
        return 0;
+
 err_usb_get_function_instance:
-       while (i-- > 0) {
-               usb_put_function(config->f_acm[i]);
-err_usb_get_function:
+       while (i-- > 0)
                usb_put_function_instance(config->f_acm_inst[i]);
-       }
        kfree(f->config);
        f->config = NULL;
        return ret;
@@ -294,10 +287,8 @@ static void acm_function_cleanup(struct slp_multi_usb_function *f)
        int i;
        struct acm_function_config *config = f->config;
 
-       for (i = 0; i < MAX_ACM_INSTANCES; i++) {
-               usb_put_function(config->f_acm[i]);
+       for (i = 0; i < MAX_ACM_INSTANCES; i++)
                usb_put_function_instance(config->f_acm_inst[i]);
-       }
        kfree(f->config);
        f->config = NULL;
 }
@@ -310,6 +301,14 @@ acm_function_bind_config(struct slp_multi_usb_function *f,
        int ret = 0;
        struct acm_function_config *config = f->config;
 
+       for (i = 0; i < MAX_ACM_INSTANCES; i++) {
+               config->f_acm[i] = usb_get_function(config->f_acm_inst[i]);
+               if (IS_ERR(config->f_acm[i])) {
+                       ret = PTR_ERR(config->f_acm[i]);
+                       goto err_usb_get_function;
+               }
+       }
+
        config->instances_on = config->instances;
        for (i = 0; i < config->instances_on; i++) {
                ret = usb_add_function(c, config->f_acm[i]);
@@ -324,6 +323,10 @@ acm_function_bind_config(struct slp_multi_usb_function *f,
 err_usb_add_function:
        while (i-- > 0)
                usb_remove_function(c, config->f_acm[i]);
+       i = MAX_ACM_INSTANCES;
+err_usb_get_function:
+       while (i-- > 0)
+               usb_put_function(config->f_acm[i]);
        return ret;
 }
 
@@ -333,8 +336,8 @@ static void acm_function_unbind_config(struct slp_multi_usb_function *f,
        int i;
        struct acm_function_config *config = f->config;
 
-       for (i = 0; i < config->instances_on; i++)
-               usb_remove_function(c, config->f_acm[i]);
+       for (i = 0; i < MAX_ACM_INSTANCES; i++)
+               usb_put_function(config->f_acm[i]);
 }
 
 static ssize_t acm_instances_show(struct device *dev,