firmware: Factor out the paged buffer handling code
[linux-2.6-block.git] / drivers / base / firmware_loader / main.c
index 2e74a1b73dae866f8c8b7926b26628e867351c10..7e12732f470548a5b6c2384f9d90cd9ea704af8b 100644 (file)
@@ -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 */