diff options
author | Andrzej Pietrasiewicz <andrzej.p@samsung.com> | 2014-06-11 14:09:23 +0900 |
---|---|---|
committer | Chanho Park <chanho61.park@samsung.com> | 2014-11-18 12:00:17 +0900 |
commit | 1b2eaaf22e5170effff06fc0384df21488ac2a8f (patch) | |
tree | e670bfee90d09e90dbeb8ff32bf1c55c052eb255 /drivers/usb | |
parent | 4648d43cada6321f5383e7a807b3578847990da4 (diff) | |
download | linux-3.10-1b2eaaf22e5170effff06fc0384df21488ac2a8f.tar.gz linux-3.10-1b2eaaf22e5170effff06fc0384df21488ac2a8f.tar.bz2 linux-3.10-1b2eaaf22e5170effff06fc0384df21488ac2a8f.zip |
usb: gadget: Use usb_put_function instead of usb_remove_function
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>
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/gadget/slp.c | 31 |
1 files changed, 17 insertions, 14 deletions
diff --git a/drivers/usb/gadget/slp.c b/drivers/usb/gadget/slp.c index f4b6d1f804a..d08fe29b9bc 100644 --- a/drivers/usb/gadget/slp.c +++ b/drivers/usb/gadget/slp.c @@ -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, |