summaryrefslogtreecommitdiff
path: root/mm/percpu-vm.c
diff options
context:
space:
mode:
Diffstat (limited to 'mm/percpu-vm.c')
-rw-r--r--mm/percpu-vm.c49
1 files changed, 35 insertions, 14 deletions
diff --git a/mm/percpu-vm.c b/mm/percpu-vm.c
index 4f5937090590..509d8901835c 100644
--- a/mm/percpu-vm.c
+++ b/mm/percpu-vm.c
@@ -9,6 +9,7 @@
* This is the default chunk allocator.
*/
#include "internal.h"
+#include "vmalloc.h"
static struct page *pcpu_chunk_page(struct pcpu_chunk *chunk,
unsigned int cpu, int page_idx)
@@ -21,6 +22,8 @@ static struct page *pcpu_chunk_page(struct pcpu_chunk *chunk,
/**
* pcpu_get_pages - get temp pages array
+ * @gfp: allocation flags passed to the underlying allocator, 0 to only
+ * return the cached array
*
* Returns pointer to array of pointers to struct page which can be indexed
* with pcpu_page_idx(). Note that there is only one array and accesses
@@ -29,18 +32,23 @@ static struct page *pcpu_chunk_page(struct pcpu_chunk *chunk,
* RETURNS:
* Pointer to temp pages array on success.
*/
-static struct page **pcpu_get_pages(void)
+static struct page **pcpu_get_pages(gfp_t gfp)
{
static struct page **pages;
size_t pages_size = pcpu_nr_units * pcpu_unit_pages * sizeof(pages[0]);
lockdep_assert_held(&pcpu_alloc_mutex);
- if (!pages)
- pages = pcpu_mem_zalloc(pages_size, GFP_KERNEL);
+ if (!pages && gfp)
+ pages = pcpu_mem_zalloc(pages_size, gfp);
return pages;
}
+static struct page **pcpu_get_pages_cached(void)
+{
+ return pcpu_get_pages(0);
+}
+
/**
* pcpu_free_pages - free pages which were allocated for @chunk
* @chunk: chunk pages were allocated for
@@ -191,10 +199,22 @@ static void pcpu_post_unmap_tlb_flush(struct pcpu_chunk *chunk,
}
static int __pcpu_map_pages(unsigned long addr, struct page **pages,
- int nr_pages)
+ int nr_pages, gfp_t gfp)
{
- return vmap_pages_range_noflush(addr, addr + (nr_pages << PAGE_SHIFT),
- PAGE_KERNEL, pages, PAGE_SHIFT, GFP_KERNEL);
+ unsigned int flags;
+ int ret;
+
+ /*
+ * The vmalloc page table allocation path does not pass @gfp down
+ * explicitly. Apply the corresponding memalloc scope so implicit
+ * page table allocations preserve NOFS/NOIO constraints.
+ */
+ flags = memalloc_apply_gfp_scope(gfp);
+ ret = vmap_pages_range_noflush(addr, addr + (nr_pages << PAGE_SHIFT),
+ PAGE_KERNEL, pages, PAGE_SHIFT, gfp);
+ memalloc_restore_scope(flags);
+
+ return ret;
}
/**
@@ -203,6 +223,7 @@ static int __pcpu_map_pages(unsigned long addr, struct page **pages,
* @pages: pages array containing pages to be mapped
* @page_start: page index of the first page to map
* @page_end: page index of the last page to map + 1
+ * @gfp: allocation flags passed to the underlying allocator
*
* For each cpu, map pages [@page_start,@page_end) into @chunk. The
* caller is responsible for calling pcpu_post_map_flush() after all
@@ -211,8 +232,8 @@ static int __pcpu_map_pages(unsigned long addr, struct page **pages,
* This function is responsible for setting up whatever is necessary for
* reverse lookup (addr -> chunk).
*/
-static int pcpu_map_pages(struct pcpu_chunk *chunk,
- struct page **pages, int page_start, int page_end)
+static int pcpu_map_pages(struct pcpu_chunk *chunk, struct page **pages,
+ int page_start, int page_end, gfp_t gfp)
{
unsigned int cpu, tcpu;
int i, err;
@@ -220,7 +241,7 @@ static int pcpu_map_pages(struct pcpu_chunk *chunk,
for_each_possible_cpu(cpu) {
err = __pcpu_map_pages(pcpu_chunk_addr(chunk, cpu, page_start),
&pages[pcpu_page_idx(cpu, page_start)],
- page_end - page_start);
+ page_end - page_start, gfp);
if (err < 0)
goto err;
@@ -271,21 +292,21 @@ static void pcpu_post_map_flush(struct pcpu_chunk *chunk,
* @chunk.
*
* CONTEXT:
- * pcpu_alloc_mutex, does GFP_KERNEL allocation.
+ * pcpu_alloc_mutex, does @gfp allocation.
*/
static int pcpu_populate_chunk(struct pcpu_chunk *chunk,
int page_start, int page_end, gfp_t gfp)
{
struct page **pages;
- pages = pcpu_get_pages();
+ pages = pcpu_get_pages(gfp);
if (!pages)
return -ENOMEM;
if (pcpu_alloc_pages(chunk, pages, page_start, page_end, gfp))
return -ENOMEM;
- if (pcpu_map_pages(chunk, pages, page_start, page_end)) {
+ if (pcpu_map_pages(chunk, pages, page_start, page_end, gfp)) {
pcpu_free_pages(chunk, pages, page_start, page_end);
return -ENOMEM;
}
@@ -319,7 +340,7 @@ static void pcpu_depopulate_chunk(struct pcpu_chunk *chunk,
* successful population attempt so the temp pages array must
* be available now.
*/
- pages = pcpu_get_pages();
+ pages = pcpu_get_pages_cached();
BUG_ON(!pages);
/* unmap and free */
@@ -340,7 +361,7 @@ static struct pcpu_chunk *pcpu_create_chunk(gfp_t gfp)
return NULL;
vms = pcpu_get_vm_areas(pcpu_group_offsets, pcpu_group_sizes,
- pcpu_nr_groups, pcpu_atom_size);
+ pcpu_nr_groups, pcpu_atom_size, gfp);
if (!vms) {
pcpu_free_chunk(chunk);
return NULL;