From 1b2eaaf22e5170effff06fc0384df21488ac2a8f Mon Sep 17 00:00:00 2001 From: Andrzej Pietrasiewicz Date: Wed, 11 Jun 2014 14:09:23 +0900 Subject: [PATCH] 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 --- drivers/usb/gadget/slp.c | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/drivers/usb/gadget/slp.c b/drivers/usb/gadget/slp.c index f4b6d1f..d08fe29 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, -- 2.7.4