firmware loader: fix one reqeust_firmware race
Several loading requests may be pending on one same firmware buf, and this patch moves fw_map_pages_buf() before complete_all(&fw_buf->completion) and let all requests see the mapped 'buf->data' once the loading is completed. Signed-off-by: Ming Lei <ming.lei@canonical.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
373304fe10
commit
253c9240ee
|
@ -423,6 +423,18 @@ static void firmware_free_data(const struct firmware *fw)
|
||||||
#ifndef PAGE_KERNEL_RO
|
#ifndef PAGE_KERNEL_RO
|
||||||
#define PAGE_KERNEL_RO PAGE_KERNEL
|
#define PAGE_KERNEL_RO PAGE_KERNEL
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* one pages buffer should be mapped/unmapped only once */
|
||||||
|
static int fw_map_pages_buf(struct firmware_buf *buf)
|
||||||
|
{
|
||||||
|
if (buf->data)
|
||||||
|
vunmap(buf->data);
|
||||||
|
buf->data = vmap(buf->pages, buf->nr_pages, 0, PAGE_KERNEL_RO);
|
||||||
|
if (!buf->data)
|
||||||
|
return -ENOMEM;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* firmware_loading_store - set value in the 'loading' control file
|
* firmware_loading_store - set value in the 'loading' control file
|
||||||
* @dev: device pointer
|
* @dev: device pointer
|
||||||
|
@ -467,6 +479,14 @@ static ssize_t firmware_loading_store(struct device *dev,
|
||||||
if (test_bit(FW_STATUS_LOADING, &fw_buf->status)) {
|
if (test_bit(FW_STATUS_LOADING, &fw_buf->status)) {
|
||||||
set_bit(FW_STATUS_DONE, &fw_buf->status);
|
set_bit(FW_STATUS_DONE, &fw_buf->status);
|
||||||
clear_bit(FW_STATUS_LOADING, &fw_buf->status);
|
clear_bit(FW_STATUS_LOADING, &fw_buf->status);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Several loading requests may be pending on
|
||||||
|
* one same firmware buf, so let all requests
|
||||||
|
* see the mapped 'buf->data' once the loading
|
||||||
|
* is completed.
|
||||||
|
* */
|
||||||
|
fw_map_pages_buf(fw_buf);
|
||||||
complete_all(&fw_buf->completion);
|
complete_all(&fw_buf->completion);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -670,15 +690,6 @@ exit:
|
||||||
return fw_priv;
|
return fw_priv;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* one pages buffer is mapped/unmapped only once */
|
|
||||||
static int fw_map_pages_buf(struct firmware_buf *buf)
|
|
||||||
{
|
|
||||||
buf->data = vmap(buf->pages, buf->nr_pages, 0, PAGE_KERNEL_RO);
|
|
||||||
if (!buf->data)
|
|
||||||
return -ENOMEM;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* store the pages buffer info firmware from buf */
|
/* store the pages buffer info firmware from buf */
|
||||||
static void fw_set_page_data(struct firmware_buf *buf, struct firmware *fw)
|
static void fw_set_page_data(struct firmware_buf *buf, struct firmware *fw)
|
||||||
{
|
{
|
||||||
|
@ -884,9 +895,6 @@ static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent,
|
||||||
if (!retval && f_dev->parent)
|
if (!retval && f_dev->parent)
|
||||||
fw_add_devm_name(f_dev->parent, buf->fw_id);
|
fw_add_devm_name(f_dev->parent, buf->fw_id);
|
||||||
|
|
||||||
if (!retval)
|
|
||||||
retval = fw_map_pages_buf(buf);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* After caching firmware image is started, let it piggyback
|
* After caching firmware image is started, let it piggyback
|
||||||
* on request firmware.
|
* on request firmware.
|
||||||
|
|
Loading…
Reference in a new issue