diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index 8524450e75bd..abb939640bcc 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -163,6 +163,17 @@ struct fw_name_devm { const char *name; }; +struct fw_desc { + struct work_struct work; + const struct firmware **firmware_p; + const char *name; + struct device *device; + unsigned int opt_flags; + struct module *module; + void *context; + void (*cont)(const struct firmware *fw, void *context); +}; + #define to_fwbuf(d) container_of(d, struct firmware_buf, ref) #define FW_LOADER_NO_CACHE 0 @@ -268,6 +279,10 @@ static void __fw_free_buf(struct kref *ref) static void fw_free_buf(struct firmware_buf *buf) { struct firmware_cache *fwc = buf->fwc; + if (!fwc) { + kfree(buf); + return; + } spin_lock(&fwc->lock); if (!kref_put(&buf->ref, __fw_free_buf)) spin_unlock(&fwc->lock); @@ -885,25 +900,29 @@ static const struct attribute_group *fw_dev_attr_groups[] = { }; static struct firmware_priv * -fw_create_instance(struct firmware *firmware, const char *fw_name, - struct device *device, unsigned int opt_flags) +fw_create_instance(struct firmware *firmware, struct fw_desc *desc) { struct firmware_priv *fw_priv; struct device *f_dev; fw_priv = kzalloc(sizeof(*fw_priv), GFP_KERNEL); if (!fw_priv) { + dev_err(desc->device, "%s: kmalloc failed\n", __func__); fw_priv = ERR_PTR(-ENOMEM); goto exit; } - fw_priv->nowait = !!(opt_flags & FW_OPT_NOWAIT); + fw_priv->nowait = !!(desc->opt_flags & FW_OPT_NOWAIT); fw_priv->fw = firmware; + + INIT_DELAYED_WORK(&fw_priv->timeout_work, + firmware_class_timeout_work); + f_dev = &fw_priv->dev; device_initialize(f_dev); - dev_set_name(f_dev, "%s", fw_name); - f_dev->parent = device; + dev_set_name(f_dev, "%s", desc->name); + f_dev->parent = desc->device; f_dev->class = &firmware_class; f_dev->groups = fw_dev_attr_groups; exit: @@ -964,17 +983,16 @@ err_put_dev: } static int fw_load_from_user_helper(struct firmware *firmware, - const char *name, struct device *device, - unsigned int opt_flags, long timeout) + struct fw_desc *desc, long timeout) { struct firmware_priv *fw_priv; - fw_priv = fw_create_instance(firmware, name, device, opt_flags); + fw_priv = fw_create_instance(firmware, desc); if (IS_ERR(fw_priv)) return PTR_ERR(fw_priv); fw_priv->buf = firmware->priv; - return _request_firmware_load(fw_priv, opt_flags, timeout); + return _request_firmware_load(fw_priv, desc->opt_flags, timeout); } #ifdef CONFIG_PM_SLEEP @@ -995,9 +1013,8 @@ static void kill_requests_without_uevent(void) #else /* CONFIG_FW_LOADER_USER_HELPER */ static inline int -fw_load_from_user_helper(struct firmware *firmware, const char *name, - struct device *device, unsigned int opt_flags, - long timeout) +fw_load_from_user_helper(struct firmware *firmware, + struct fw_desc *desc, long timeout) { return -ENOENT; } @@ -1036,8 +1053,7 @@ static int sync_cached_firmware_buf(struct firmware_buf *buf) * or a negative error code */ static int -_request_firmware_prepare(struct firmware **firmware_p, const char *name, - struct device *device) +_request_firmware_prepare(struct firmware **firmware_p, struct fw_desc *desc) { struct firmware *firmware; struct firmware_buf *buf; @@ -1045,17 +1061,18 @@ _request_firmware_prepare(struct firmware **firmware_p, const char *name, *firmware_p = firmware = kzalloc(sizeof(*firmware), GFP_KERNEL); if (!firmware) { - dev_err(device, "%s: kmalloc(struct firmware) failed\n", + dev_err(desc->device, "%s: kmalloc(struct firmware) failed\n", __func__); return -ENOMEM; } - if (fw_get_builtin_firmware(firmware, name)) { - dev_dbg(device, "firmware: using built-in firmware %s\n", name); + if (fw_get_builtin_firmware(firmware, desc->name)) { + dev_dbg(desc->device, "firmware: using built-in firmware %s\n", + desc->name); return 0; /* assigned */ } - ret = fw_lookup_and_allocate_buf(name, &fw_cache, &buf); + ret = fw_lookup_and_allocate_buf(desc->name, &fw_cache, &buf); /* * bind with 'buf' now to avoid warning in failure path @@ -1114,58 +1131,55 @@ static int assign_firmware_buf(struct firmware *fw, struct device *device, } /* called from request_firmware() and request_firmware_work_func() */ -static int -_request_firmware(const struct firmware **firmware_p, const char *name, - struct device *device, unsigned int opt_flags) +static int _request_firmware(struct fw_desc *desc) { struct firmware *fw; long timeout; int ret; - if (!firmware_p) + if (!desc->firmware_p) return -EINVAL; - if (!name || name[0] == '\0') + if (!desc->name || desc->name[0] == '\0') return -EINVAL; - ret = _request_firmware_prepare(&fw, name, device); + ret = _request_firmware_prepare(&fw, desc); if (ret <= 0) /* error or already assigned */ goto out; ret = 0; timeout = firmware_loading_timeout(); - if (opt_flags & FW_OPT_NOWAIT) { + if (desc->opt_flags & FW_OPT_NOWAIT) { timeout = usermodehelper_read_lock_wait(timeout); if (!timeout) { - dev_dbg(device, "firmware: %s loading timed out\n", - name); + dev_dbg(desc->device, "firmware: %s loading timed out\n", + desc->name); ret = -EBUSY; goto out; } } else { ret = usermodehelper_read_trylock(); if (WARN_ON(ret)) { - dev_err(device, "firmware: %s will not be loaded\n", - name); + dev_err(desc->device, "firmware: %s will not be loaded\n", + desc->name); goto out; } } - ret = fw_get_filesystem_firmware(device, fw->priv); + ret = fw_get_filesystem_firmware(desc->device, fw->priv); if (ret) { - if (!(opt_flags & FW_OPT_NO_WARN)) - dev_warn(device, + if (!(desc->opt_flags & FW_OPT_NO_WARN)) + dev_warn(desc->device, "Direct firmware load for %s failed with error %d\n", - name, ret); - if (opt_flags & FW_OPT_USERHELPER) { - dev_warn(device, "Falling back to user helper\n"); - ret = fw_load_from_user_helper(fw, name, device, - opt_flags, timeout); + desc->name, ret); + if (desc->opt_flags & FW_OPT_USERHELPER) { + dev_warn(desc->device, "Falling back to user helper\n"); + ret = fw_load_from_user_helper(fw, desc, timeout); } } if (!ret) - ret = assign_firmware_buf(fw, device, opt_flags); + ret = assign_firmware_buf(fw, desc->device, desc->opt_flags); usermodehelper_read_unlock(); @@ -1175,7 +1189,7 @@ _request_firmware(const struct firmware **firmware_p, const char *name, fw = NULL; } - *firmware_p = fw; + *desc->firmware_p = fw; return ret; } @@ -1203,13 +1217,19 @@ int request_firmware(const struct firmware **firmware_p, const char *name, struct device *device) { + struct fw_desc desc; int ret; + desc.firmware_p = firmware_p; + desc.name = name; + desc.device = device; + desc.opt_flags = FW_OPT_UEVENT | FW_OPT_FALLBACK; + /* Need to pin this module until return */ __module_get(THIS_MODULE); - ret = _request_firmware(firmware_p, name, device, - FW_OPT_UEVENT | FW_OPT_FALLBACK); + ret = _request_firmware(&desc); module_put(THIS_MODULE); + return ret; } EXPORT_SYMBOL(request_firmware); @@ -1228,12 +1248,19 @@ EXPORT_SYMBOL(request_firmware); int request_firmware_direct(const struct firmware **firmware_p, const char *name, struct device *device) { + struct fw_desc desc; int ret; + desc.firmware_p = firmware_p; + desc.name = name; + desc.device = device; + desc.opt_flags = FW_OPT_UEVENT | FW_OPT_NO_WARN; + + /* Need to pin this module until return */ __module_get(THIS_MODULE); - ret = _request_firmware(firmware_p, name, device, - FW_OPT_UEVENT | FW_OPT_NO_WARN); + ret = _request_firmware(&desc); module_put(THIS_MODULE); + return ret; } EXPORT_SYMBOL_GPL(request_firmware_direct); @@ -1253,31 +1280,54 @@ void release_firmware(const struct firmware *fw) EXPORT_SYMBOL(release_firmware); /* Async support */ -struct firmware_work { - struct work_struct work; - struct module *module; - const char *name; - struct device *device; - void *context; - void (*cont)(const struct firmware *fw, void *context); - unsigned int opt_flags; -}; - static void request_firmware_work_func(struct work_struct *work) { - struct firmware_work *fw_work; const struct firmware *fw; + struct fw_desc *desc; - fw_work = container_of(work, struct firmware_work, work); + desc = container_of(work, struct fw_desc, work); + desc->firmware_p = &fw; + _request_firmware(desc); + desc->cont(fw, desc->context); + put_device(desc->device); /* taken in request_firmware_nowait() */ - _request_firmware(&fw, fw_work->name, fw_work->device, - fw_work->opt_flags); - fw_work->cont(fw, fw_work->context); - put_device(fw_work->device); /* taken in request_firmware_nowait() */ + module_put(desc->module); + kfree(desc); +} - module_put(fw_work->module); - kfree_const(fw_work->name); - kfree(fw_work); +int +_request_firmware_nowait( + struct module *module, bool uevent, + const char *name, struct device *device, gfp_t gfp, void *context, + void (*cont)(const struct firmware *fw, void *context)) +{ + struct fw_desc *desc; + + desc = kzalloc(sizeof(struct fw_desc), gfp); + if (!desc) + return -ENOMEM; + + desc->module = module; + desc->name = name; + desc->device = device; + desc->context = context; + desc->cont = cont; + desc->opt_flags = FW_OPT_FALLBACK | FW_OPT_NOWAIT; + + if (uevent) + desc->opt_flags |= FW_OPT_UEVENT; + else + desc->opt_flags |= FW_OPT_USERHELPER; + + if (!try_module_get(module)) { + kfree(desc); + return -EFAULT; + } + + get_device(desc->device); + INIT_WORK(&desc->work, request_firmware_work_func); + schedule_work(&desc->work); + return 0; } /** @@ -1309,34 +1359,8 @@ request_firmware_nowait( const char *name, struct device *device, gfp_t gfp, void *context, void (*cont)(const struct firmware *fw, void *context)) { - struct firmware_work *fw_work; - - fw_work = kzalloc(sizeof(struct firmware_work), gfp); - if (!fw_work) - return -ENOMEM; - - fw_work->module = module; - fw_work->name = kstrdup_const(name, gfp); - if (!fw_work->name) { - kfree(fw_work); - return -ENOMEM; - } - fw_work->device = device; - fw_work->context = context; - fw_work->cont = cont; - fw_work->opt_flags = FW_OPT_NOWAIT | FW_OPT_FALLBACK | - (uevent ? FW_OPT_UEVENT : FW_OPT_USERHELPER); - - if (!try_module_get(module)) { - kfree_const(fw_work->name); - kfree(fw_work); - return -EFAULT; - } - - get_device(fw_work->device); - INIT_WORK(&fw_work->work, request_firmware_work_func); - schedule_work(&fw_work->work); - return 0; + return _request_firmware_nowait(module, uevent, name, device, gfp, + context, cont); } EXPORT_SYMBOL(request_firmware_nowait);