提交 4da56b99 编写于 作者: C Chris Wilson

mm/vmap: Add a notifier for when we run out of vmap address space

vmaps are temporary kernel mappings that may be of long duration.
Reusing a vmap on an object is preferrable for a driver as the cost of
setting up the vmap can otherwise dominate the operation on the object.
However, the vmap address space is rather limited on 32bit systems and
so we add a notification for vmap pressure in order for the driver to
release any cached vmappings.

The interface is styled after the oom-notifier where the callees are
passed a pointer to an unsigned long counter for them to indicate if they
have freed any space.

v2: Guard the blocking notifier call with gfpflags_allow_blocking()
v3: Correct typo in forward declaration and move to head of file
Signed-off-by: NChris Wilson <chris@chris-wilson.co.uk>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: David Rientjes <rientjes@google.com>
Cc: Roman Peniaev <r.peniaev@gmail.com>
Cc: Mel Gorman <mgorman@techsingularity.net>
Cc: linux-mm@kvack.org
Cc: linux-kernel@vger.kernel.org
Acked-by: Andrew Morton <akpm@linux-foundation.org> # for inclusion via DRM
Cc: Joonas Lahtinen <joonas.lahtinen@linux.intel.com>
Cc: Tvrtko Ursulin <tvrtko.ursulin@intel.com>
Link: http://patchwork.freedesktop.org/patch/msgid/1459777603-23618-3-git-send-email-chris@chris-wilson.co.ukReviewed-by: NJoonas Lahtinen <joonas.lahtinen@linux.intel.com>
上级 6f0ac204
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <linux/rbtree.h> #include <linux/rbtree.h>
struct vm_area_struct; /* vma defining user mapping in mm_types.h */ struct vm_area_struct; /* vma defining user mapping in mm_types.h */
struct notifier_block; /* in notifier.h */
/* bits in flags of vmalloc's vm_struct below */ /* bits in flags of vmalloc's vm_struct below */
#define VM_IOREMAP 0x00000001 /* ioremap() and friends */ #define VM_IOREMAP 0x00000001 /* ioremap() and friends */
...@@ -187,4 +188,7 @@ pcpu_free_vm_areas(struct vm_struct **vms, int nr_vms) ...@@ -187,4 +188,7 @@ pcpu_free_vm_areas(struct vm_struct **vms, int nr_vms)
#define VMALLOC_TOTAL 0UL #define VMALLOC_TOTAL 0UL
#endif #endif
int register_vmap_purge_notifier(struct notifier_block *nb);
int unregister_vmap_purge_notifier(struct notifier_block *nb);
#endif /* _LINUX_VMALLOC_H */ #endif /* _LINUX_VMALLOC_H */
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include <linux/debugobjects.h> #include <linux/debugobjects.h>
#include <linux/kallsyms.h> #include <linux/kallsyms.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/notifier.h>
#include <linux/rbtree.h> #include <linux/rbtree.h>
#include <linux/radix-tree.h> #include <linux/radix-tree.h>
#include <linux/rcupdate.h> #include <linux/rcupdate.h>
...@@ -344,6 +345,8 @@ static void __insert_vmap_area(struct vmap_area *va) ...@@ -344,6 +345,8 @@ static void __insert_vmap_area(struct vmap_area *va)
static void purge_vmap_area_lazy(void); static void purge_vmap_area_lazy(void);
static BLOCKING_NOTIFIER_HEAD(vmap_notify_list);
/* /*
* Allocate a region of KVA of the specified size and alignment, within the * Allocate a region of KVA of the specified size and alignment, within the
* vstart and vend. * vstart and vend.
...@@ -363,6 +366,8 @@ static struct vmap_area *alloc_vmap_area(unsigned long size, ...@@ -363,6 +366,8 @@ static struct vmap_area *alloc_vmap_area(unsigned long size,
BUG_ON(offset_in_page(size)); BUG_ON(offset_in_page(size));
BUG_ON(!is_power_of_2(align)); BUG_ON(!is_power_of_2(align));
might_sleep_if(gfpflags_allow_blocking(gfp_mask));
va = kmalloc_node(sizeof(struct vmap_area), va = kmalloc_node(sizeof(struct vmap_area),
gfp_mask & GFP_RECLAIM_MASK, node); gfp_mask & GFP_RECLAIM_MASK, node);
if (unlikely(!va)) if (unlikely(!va))
...@@ -468,6 +473,16 @@ static struct vmap_area *alloc_vmap_area(unsigned long size, ...@@ -468,6 +473,16 @@ static struct vmap_area *alloc_vmap_area(unsigned long size,
purged = 1; purged = 1;
goto retry; goto retry;
} }
if (gfpflags_allow_blocking(gfp_mask)) {
unsigned long freed = 0;
blocking_notifier_call_chain(&vmap_notify_list, 0, &freed);
if (freed > 0) {
purged = 0;
goto retry;
}
}
if (printk_ratelimit()) if (printk_ratelimit())
pr_warn("vmap allocation for size %lu failed: " pr_warn("vmap allocation for size %lu failed: "
"use vmalloc=<size> to increase size.\n", size); "use vmalloc=<size> to increase size.\n", size);
...@@ -475,6 +490,18 @@ static struct vmap_area *alloc_vmap_area(unsigned long size, ...@@ -475,6 +490,18 @@ static struct vmap_area *alloc_vmap_area(unsigned long size,
return ERR_PTR(-EBUSY); return ERR_PTR(-EBUSY);
} }
int register_vmap_purge_notifier(struct notifier_block *nb)
{
return blocking_notifier_chain_register(&vmap_notify_list, nb);
}
EXPORT_SYMBOL_GPL(register_vmap_purge_notifier);
int unregister_vmap_purge_notifier(struct notifier_block *nb)
{
return blocking_notifier_chain_unregister(&vmap_notify_list, nb);
}
EXPORT_SYMBOL_GPL(unregister_vmap_purge_notifier);
static void __free_vmap_area(struct vmap_area *va) static void __free_vmap_area(struct vmap_area *va)
{ {
BUG_ON(RB_EMPTY_NODE(&va->rb_node)); BUG_ON(RB_EMPTY_NODE(&va->rb_node));
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册