Skip to content

Commit

Permalink
firmware: Factor out the paged buffer handling code
Browse files Browse the repository at this point in the history
This is merely a preparation for the upcoming compressed firmware
support and no functional changes.  It moves the code to handle the
paged buffer allocation and mapping out of fallback.c into the main
code, so that they can be used commonly.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
Takashi Iwai authored and Greg Kroah-Hartman committed Jun 18, 2019
1 parent 3aa6980 commit 5342e70
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 54 deletions.
61 changes: 7 additions & 54 deletions drivers/base/firmware_loader/fallback.c
Original file line number Diff line number Diff line change
Expand Up @@ -219,25 +219,6 @@ static ssize_t firmware_loading_show(struct device *dev,
return sprintf(buf, "%d\n", loading);
}

/* one pages buffer should be mapped/unmapped only once */
static int map_fw_priv_pages(struct fw_priv *fw_priv)
{
if (!fw_priv->pages)
return 0;

vunmap(fw_priv->data);
fw_priv->data = vmap(fw_priv->pages, fw_priv->nr_pages, 0,
PAGE_KERNEL_RO);
if (!fw_priv->data)
return -ENOMEM;

/* page table is no longer needed after mapping, let's free */
kvfree(fw_priv->pages);
fw_priv->pages = NULL;

return 0;
}

/**
* firmware_loading_store() - set value in the 'loading' control file
* @dev: device pointer
Expand Down Expand Up @@ -283,7 +264,7 @@ static ssize_t firmware_loading_store(struct device *dev,
* see the mapped 'buf->data' once the loading
* is completed.
* */
rc = map_fw_priv_pages(fw_priv);
rc = fw_map_paged_buf(fw_priv);
if (rc)
dev_err(dev, "%s: map pages failed\n",
__func__);
Expand Down Expand Up @@ -388,41 +369,13 @@ static ssize_t firmware_data_read(struct file *filp, struct kobject *kobj,

static int fw_realloc_pages(struct fw_sysfs *fw_sysfs, int min_size)
{
struct fw_priv *fw_priv= fw_sysfs->fw_priv;
int pages_needed = PAGE_ALIGN(min_size) >> PAGE_SHIFT;

/* If the array of pages is too small, grow it... */
if (fw_priv->page_array_size < pages_needed) {
int new_array_size = max(pages_needed,
fw_priv->page_array_size * 2);
struct page **new_pages;
int err;

new_pages = kvmalloc_array(new_array_size, sizeof(void *),
GFP_KERNEL);
if (!new_pages) {
fw_load_abort(fw_sysfs);
return -ENOMEM;
}
memcpy(new_pages, fw_priv->pages,
fw_priv->page_array_size * sizeof(void *));
memset(&new_pages[fw_priv->page_array_size], 0, sizeof(void *) *
(new_array_size - fw_priv->page_array_size));
kvfree(fw_priv->pages);
fw_priv->pages = new_pages;
fw_priv->page_array_size = new_array_size;
}

while (fw_priv->nr_pages < pages_needed) {
fw_priv->pages[fw_priv->nr_pages] =
alloc_page(GFP_KERNEL | __GFP_HIGHMEM);

if (!fw_priv->pages[fw_priv->nr_pages]) {
fw_load_abort(fw_sysfs);
return -ENOMEM;
}
fw_priv->nr_pages++;
}
return 0;
err = fw_grow_paged_buf(fw_sysfs->fw_priv,
PAGE_ALIGN(min_size) >> PAGE_SHIFT);
if (err)
fw_load_abort(fw_sysfs);
return err;
}

/**
Expand Down
4 changes: 4 additions & 0 deletions drivers/base/firmware_loader/firmware.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,8 +135,12 @@ int assign_fw(struct firmware *fw, struct device *device,

#ifdef CONFIG_FW_LOADER_USER_HELPER
void fw_free_paged_buf(struct fw_priv *fw_priv);
int fw_grow_paged_buf(struct fw_priv *fw_priv, int pages_needed);
int fw_map_paged_buf(struct fw_priv *fw_priv);
#else
static inline void fw_free_paged_buf(struct fw_priv *fw_priv) {}
int fw_grow_paged_buf(struct fw_priv *fw_priv, int pages_needed) { return -ENXIO; }
int fw_map_paged_buf(struct fw_priv *fw_priv) { return -ENXIO; }
#endif

#endif /* __FIRMWARE_LOADER_H */
52 changes: 52 additions & 0 deletions drivers/base/firmware_loader/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,58 @@ void fw_free_paged_buf(struct fw_priv *fw_priv)
fw_priv->page_array_size = 0;
fw_priv->nr_pages = 0;
}

int fw_grow_paged_buf(struct fw_priv *fw_priv, int pages_needed)
{
/* If the array of pages is too small, grow it */
if (fw_priv->page_array_size < pages_needed) {
int new_array_size = max(pages_needed,
fw_priv->page_array_size * 2);
struct page **new_pages;

new_pages = kvmalloc_array(new_array_size, sizeof(void *),
GFP_KERNEL);
if (!new_pages)
return -ENOMEM;
memcpy(new_pages, fw_priv->pages,
fw_priv->page_array_size * sizeof(void *));
memset(&new_pages[fw_priv->page_array_size], 0, sizeof(void *) *
(new_array_size - fw_priv->page_array_size));
kvfree(fw_priv->pages);
fw_priv->pages = new_pages;
fw_priv->page_array_size = new_array_size;
}

while (fw_priv->nr_pages < pages_needed) {
fw_priv->pages[fw_priv->nr_pages] =
alloc_page(GFP_KERNEL | __GFP_HIGHMEM);

if (!fw_priv->pages[fw_priv->nr_pages])
return -ENOMEM;
fw_priv->nr_pages++;
}

return 0;
}

int fw_map_paged_buf(struct fw_priv *fw_priv)
{
/* one pages buffer should be mapped/unmapped only once */
if (!fw_priv->pages)
return 0;

vunmap(fw_priv->data);
fw_priv->data = vmap(fw_priv->pages, fw_priv->nr_pages, 0,
PAGE_KERNEL_RO);
if (!fw_priv->data)
return -ENOMEM;

/* page table is no longer needed after mapping, let's free */
kvfree(fw_priv->pages);
fw_priv->pages = NULL;

return 0;
}
#endif

/* direct firmware loading support */
Expand Down

0 comments on commit 5342e70

Please sign in to comment.