summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2019-06-11 08:26:24 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2019-06-18 03:11:21 -0400
commit5342e7093ff298d9cbd40f9342b607adb02b2dd0 (patch)
treed90cddffcb887e5ed8d360905530370dc7902d20
parent3aa6980139d19542d9204387fadadd3861e433ec (diff)
firmware: Factor out the paged buffer handling code
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>
-rw-r--r--drivers/base/firmware_loader/fallback.c61
-rw-r--r--drivers/base/firmware_loader/firmware.h4
-rw-r--r--drivers/base/firmware_loader/main.c52
3 files changed, 63 insertions, 54 deletions
diff --git a/drivers/base/firmware_loader/fallback.c b/drivers/base/firmware_loader/fallback.c
index 29becea1910d..62ee90b4db56 100644
--- a/drivers/base/firmware_loader/fallback.c
+++ b/drivers/base/firmware_loader/fallback.c
@@ -219,25 +219,6 @@ static ssize_t firmware_loading_show(struct device *dev,
219 return sprintf(buf, "%d\n", loading); 219 return sprintf(buf, "%d\n", loading);
220} 220}
221 221
222/* one pages buffer should be mapped/unmapped only once */
223static int map_fw_priv_pages(struct fw_priv *fw_priv)
224{
225 if (!fw_priv->pages)
226 return 0;
227
228 vunmap(fw_priv->data);
229 fw_priv->data = vmap(fw_priv->pages, fw_priv->nr_pages, 0,
230 PAGE_KERNEL_RO);
231 if (!fw_priv->data)
232 return -ENOMEM;
233
234 /* page table is no longer needed after mapping, let's free */
235 kvfree(fw_priv->pages);
236 fw_priv->pages = NULL;
237
238 return 0;
239}
240
241/** 222/**
242 * firmware_loading_store() - set value in the 'loading' control file 223 * firmware_loading_store() - set value in the 'loading' control file
243 * @dev: device pointer 224 * @dev: device pointer
@@ -283,7 +264,7 @@ static ssize_t firmware_loading_store(struct device *dev,
283 * see the mapped 'buf->data' once the loading 264 * see the mapped 'buf->data' once the loading
284 * is completed. 265 * is completed.
285 * */ 266 * */
286 rc = map_fw_priv_pages(fw_priv); 267 rc = fw_map_paged_buf(fw_priv);
287 if (rc) 268 if (rc)
288 dev_err(dev, "%s: map pages failed\n", 269 dev_err(dev, "%s: map pages failed\n",
289 __func__); 270 __func__);
@@ -388,41 +369,13 @@ out:
388 369
389static int fw_realloc_pages(struct fw_sysfs *fw_sysfs, int min_size) 370static int fw_realloc_pages(struct fw_sysfs *fw_sysfs, int min_size)
390{ 371{
391 struct fw_priv *fw_priv= fw_sysfs->fw_priv; 372 int err;
392 int pages_needed = PAGE_ALIGN(min_size) >> PAGE_SHIFT;
393
394 /* If the array of pages is too small, grow it... */
395 if (fw_priv->page_array_size < pages_needed) {
396 int new_array_size = max(pages_needed,
397 fw_priv->page_array_size * 2);
398 struct page **new_pages;
399 373
400 new_pages = kvmalloc_array(new_array_size, sizeof(void *), 374 err = fw_grow_paged_buf(fw_sysfs->fw_priv,
401 GFP_KERNEL); 375 PAGE_ALIGN(min_size) >> PAGE_SHIFT);
402 if (!new_pages) { 376 if (err)
403 fw_load_abort(fw_sysfs); 377 fw_load_abort(fw_sysfs);
404 return -ENOMEM; 378 return err;
405 }
406 memcpy(new_pages, fw_priv->pages,
407 fw_priv->page_array_size * sizeof(void *));
408 memset(&new_pages[fw_priv->page_array_size], 0, sizeof(void *) *
409 (new_array_size - fw_priv->page_array_size));
410 kvfree(fw_priv->pages);
411 fw_priv->pages = new_pages;
412 fw_priv->page_array_size = new_array_size;
413 }
414
415 while (fw_priv->nr_pages < pages_needed) {
416 fw_priv->pages[fw_priv->nr_pages] =
417 alloc_page(GFP_KERNEL | __GFP_HIGHMEM);
418
419 if (!fw_priv->pages[fw_priv->nr_pages]) {
420 fw_load_abort(fw_sysfs);
421 return -ENOMEM;
422 }
423 fw_priv->nr_pages++;
424 }
425 return 0;
426} 379}
427 380
428/** 381/**
diff --git a/drivers/base/firmware_loader/firmware.h b/drivers/base/firmware_loader/firmware.h
index d20d4e7f9e71..35f4e58b2d98 100644
--- a/drivers/base/firmware_loader/firmware.h
+++ b/drivers/base/firmware_loader/firmware.h
@@ -135,8 +135,12 @@ int assign_fw(struct firmware *fw, struct device *device,
135 135
136#ifdef CONFIG_FW_LOADER_USER_HELPER 136#ifdef CONFIG_FW_LOADER_USER_HELPER
137void fw_free_paged_buf(struct fw_priv *fw_priv); 137void fw_free_paged_buf(struct fw_priv *fw_priv);
138int fw_grow_paged_buf(struct fw_priv *fw_priv, int pages_needed);
139int fw_map_paged_buf(struct fw_priv *fw_priv);
138#else 140#else
139static inline void fw_free_paged_buf(struct fw_priv *fw_priv) {} 141static inline void fw_free_paged_buf(struct fw_priv *fw_priv) {}
142int fw_grow_paged_buf(struct fw_priv *fw_priv, int pages_needed) { return -ENXIO; }
143int fw_map_paged_buf(struct fw_priv *fw_priv) { return -ENXIO; }
140#endif 144#endif
141 145
142#endif /* __FIRMWARE_LOADER_H */ 146#endif /* __FIRMWARE_LOADER_H */
diff --git a/drivers/base/firmware_loader/main.c b/drivers/base/firmware_loader/main.c
index 2e74a1b73dae..7e12732f4705 100644
--- a/drivers/base/firmware_loader/main.c
+++ b/drivers/base/firmware_loader/main.c
@@ -281,6 +281,58 @@ void fw_free_paged_buf(struct fw_priv *fw_priv)
281 fw_priv->page_array_size = 0; 281 fw_priv->page_array_size = 0;
282 fw_priv->nr_pages = 0; 282 fw_priv->nr_pages = 0;
283} 283}
284
285int fw_grow_paged_buf(struct fw_priv *fw_priv, int pages_needed)
286{
287 /* If the array of pages is too small, grow it */
288 if (fw_priv->page_array_size < pages_needed) {
289 int new_array_size = max(pages_needed,
290 fw_priv->page_array_size * 2);
291 struct page **new_pages;
292
293 new_pages = kvmalloc_array(new_array_size, sizeof(void *),
294 GFP_KERNEL);
295 if (!new_pages)
296 return -ENOMEM;
297 memcpy(new_pages, fw_priv->pages,
298 fw_priv->page_array_size * sizeof(void *));
299 memset(&new_pages[fw_priv->page_array_size], 0, sizeof(void *) *
300 (new_array_size - fw_priv->page_array_size));
301 kvfree(fw_priv->pages);
302 fw_priv->pages = new_pages;
303 fw_priv->page_array_size = new_array_size;
304 }
305
306 while (fw_priv->nr_pages < pages_needed) {
307 fw_priv->pages[fw_priv->nr_pages] =
308 alloc_page(GFP_KERNEL | __GFP_HIGHMEM);
309
310 if (!fw_priv->pages[fw_priv->nr_pages])
311 return -ENOMEM;
312 fw_priv->nr_pages++;
313 }
314
315 return 0;
316}
317
318int fw_map_paged_buf(struct fw_priv *fw_priv)
319{
320 /* one pages buffer should be mapped/unmapped only once */
321 if (!fw_priv->pages)
322 return 0;
323
324 vunmap(fw_priv->data);
325 fw_priv->data = vmap(fw_priv->pages, fw_priv->nr_pages, 0,
326 PAGE_KERNEL_RO);
327 if (!fw_priv->data)
328 return -ENOMEM;
329
330 /* page table is no longer needed after mapping, let's free */
331 kvfree(fw_priv->pages);
332 fw_priv->pages = NULL;
333
334 return 0;
335}
284#endif 336#endif
285 337
286/* direct firmware loading support */ 338/* direct firmware loading support */