Merge tag 'v4.6-rc3' into drm-intel-next-queued
[cascardo/linux.git] / mm / vmalloc.c
index ae7d20b..293889d 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/debugobjects.h>
 #include <linux/kallsyms.h>
 #include <linux/list.h>
+#include <linux/notifier.h>
 #include <linux/rbtree.h>
 #include <linux/radix-tree.h>
 #include <linux/rcupdate.h>
@@ -344,6 +345,8 @@ static void __insert_vmap_area(struct vmap_area *va)
 
 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
  * vstart and vend.
@@ -363,6 +366,8 @@ static struct vmap_area *alloc_vmap_area(unsigned long size,
        BUG_ON(offset_in_page(size));
        BUG_ON(!is_power_of_2(align));
 
+       might_sleep_if(gfpflags_allow_blocking(gfp_mask));
+
        va = kmalloc_node(sizeof(struct vmap_area),
                        gfp_mask & GFP_RECLAIM_MASK, node);
        if (unlikely(!va))
@@ -468,6 +473,16 @@ overflow:
                purged = 1;
                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())
                pr_warn("vmap allocation for size %lu failed: use vmalloc=<size> to increase size\n",
                        size);
@@ -475,6 +490,18 @@ overflow:
        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)
 {
        BUG_ON(RB_EMPTY_NODE(&va->rb_node));