return tmp ? 0 : -ENOMEM;
}
-static struct firmware_buf *fw_lookup_buf(const char *fw_name)
-{
- struct firmware_buf *tmp;
- struct firmware_cache *fwc = &fw_cache;
-
- spin_lock(&fwc->lock);
- tmp = __fw_lookup_buf(fw_name);
- spin_unlock(&fwc->lock);
-
- return tmp;
-}
-
static void __fw_free_buf(struct kref *ref)
{
struct firmware_buf *buf = to_fwbuf(ref);
return container_of(dev, struct firmware_priv, dev);
}
-static void fw_load_abort(struct firmware_buf *buf)
+static void __fw_load_abort(struct firmware_buf *buf)
{
+ /*
+ * There is a small window in which user can write to 'loading'
+ * between loading done and disappearance of 'loading'
+ */
+ if (test_bit(FW_STATUS_DONE, &buf->status))
+ return;
+
list_del_init(&buf->pending_list);
set_bit(FW_STATUS_ABORT, &buf->status);
complete_all(&buf->completion);
}
+static void fw_load_abort(struct firmware_priv *fw_priv)
+{
+ struct firmware_buf *buf = fw_priv->buf;
+
+ __fw_load_abort(buf);
+
+ /* avoid user action after loading abort */
+ fw_priv->buf = NULL;
+}
+
#define is_fw_load_aborted(buf) \
test_bit(FW_STATUS_ABORT, &(buf)->status)
{
mutex_lock(&fw_lock);
while (!list_empty(&pending_fw_head))
- fw_load_abort(list_first_entry(&pending_fw_head,
+ __fw_load_abort(list_first_entry(&pending_fw_head,
struct firmware_buf,
pending_list));
mutex_unlock(&fw_lock);
struct firmware_priv *fw_priv = to_firmware_priv(dev);
kfree(fw_priv);
-
- module_put(THIS_MODULE);
}
static int firmware_uevent(struct device *dev, struct kobj_uevent_env *env)
struct device_attribute *attr, char *buf)
{
struct firmware_priv *fw_priv = to_firmware_priv(dev);
- int loading = test_bit(FW_STATUS_LOADING, &fw_priv->buf->status);
+ int loading = 0;
+
+ mutex_lock(&fw_lock);
+ if (fw_priv->buf)
+ loading = test_bit(FW_STATUS_LOADING, &fw_priv->buf->status);
+ mutex_unlock(&fw_lock);
return sprintf(buf, "%d\n", loading);
}
const char *buf, size_t count)
{
struct firmware_priv *fw_priv = to_firmware_priv(dev);
- struct firmware_buf *fw_buf = fw_priv->buf;
+ struct firmware_buf *fw_buf;
int loading = simple_strtol(buf, NULL, 10);
int i;
mutex_lock(&fw_lock);
-
+ fw_buf = fw_priv->buf;
if (!fw_buf)
goto out;
dev_err(dev, "%s: unexpected value (%d)\n", __func__, loading);
/* fallthrough */
case -1:
- fw_load_abort(fw_buf);
+ fw_load_abort(fw_priv);
break;
}
out:
new_pages = kmalloc(new_array_size * sizeof(void *),
GFP_KERNEL);
if (!new_pages) {
- fw_load_abort(buf);
+ fw_load_abort(fw_priv);
return -ENOMEM;
}
memcpy(new_pages, buf->pages,
alloc_page(GFP_KERNEL | __GFP_HIGHMEM);
if (!buf->pages[buf->nr_pages]) {
- fw_load_abort(buf);
+ fw_load_abort(fw_priv);
return -ENOMEM;
}
buf->nr_pages++;
struct firmware_priv, timeout_work.work);
mutex_lock(&fw_lock);
- if (test_bit(FW_STATUS_DONE, &(fw_priv->buf->status))) {
- mutex_unlock(&fw_lock);
- return;
- }
- fw_load_abort(fw_priv->buf);
+ fw_load_abort(fw_priv);
mutex_unlock(&fw_lock);
}
dev_set_uevent_suppress(f_dev, true);
- /* Need to pin this module until class device is destroyed */
- __module_get(THIS_MODULE);
-
retval = device_add(f_dev);
if (retval) {
dev_err(f_dev, "%s: device_register failed\n", __func__);
cancel_delayed_work_sync(&fw_priv->timeout_work);
- fw_priv->buf = NULL;
-
device_remove_file(f_dev, &dev_attr_loading);
err_del_bin_attr:
device_remove_bin_file(f_dev, &firmware_attr_data);
mutex_lock(&fw_lock);
list_for_each_entry_safe(buf, next, &pending_fw_head, pending_list) {
if (!buf->need_uevent)
- fw_load_abort(buf);
+ __fw_load_abort(buf);
}
mutex_unlock(&fw_lock);
}
request_firmware(const struct firmware **firmware_p, const char *name,
struct device *device)
{
- return _request_firmware(firmware_p, name, device, true, false);
+ int ret;
+
+ /* Need to pin this module until return */
+ __module_get(THIS_MODULE);
+ ret = _request_firmware(firmware_p, name, device, true, false);
+ module_put(THIS_MODULE);
+ return ret;
}
EXPORT_SYMBOL(request_firmware);
}
EXPORT_SYMBOL(request_firmware_nowait);
+#ifdef CONFIG_PM_SLEEP
+static ASYNC_DOMAIN_EXCLUSIVE(fw_cache_domain);
+
/**
* cache_firmware - cache one firmware image in kernel memory space
* @fw_name: the firmware image name
* Return !0 otherwise
*
*/
-int cache_firmware(const char *fw_name)
+static int cache_firmware(const char *fw_name)
{
int ret;
const struct firmware *fw;
return ret;
}
-EXPORT_SYMBOL_GPL(cache_firmware);
+
+static struct firmware_buf *fw_lookup_buf(const char *fw_name)
+{
+ struct firmware_buf *tmp;
+ struct firmware_cache *fwc = &fw_cache;
+
+ spin_lock(&fwc->lock);
+ tmp = __fw_lookup_buf(fw_name);
+ spin_unlock(&fwc->lock);
+
+ return tmp;
+}
/**
* uncache_firmware - remove one cached firmware image
* Return !0 otherwise
*
*/
-int uncache_firmware(const char *fw_name)
+static int uncache_firmware(const char *fw_name)
{
struct firmware_buf *buf;
struct firmware fw;
return -EINVAL;
}
-EXPORT_SYMBOL_GPL(uncache_firmware);
-
-#ifdef CONFIG_PM_SLEEP
-static ASYNC_DOMAIN_EXCLUSIVE(fw_cache_domain);
static struct fw_cache_entry *alloc_fw_cache_entry(const char *name)
{