diff options
Diffstat (limited to 'mm/vmalloc.c')
-rw-r--r-- | mm/vmalloc.c | 45 |
1 files changed, 40 insertions, 5 deletions
diff --git a/mm/vmalloc.c b/mm/vmalloc.c index 72043d6c88c0..b12fd8612604 100644 --- a/mm/vmalloc.c +++ b/mm/vmalloc.c | |||
@@ -27,10 +27,30 @@ | |||
27 | #include <linux/pfn.h> | 27 | #include <linux/pfn.h> |
28 | #include <linux/kmemleak.h> | 28 | #include <linux/kmemleak.h> |
29 | #include <linux/atomic.h> | 29 | #include <linux/atomic.h> |
30 | #include <linux/llist.h> | ||
30 | #include <asm/uaccess.h> | 31 | #include <asm/uaccess.h> |
31 | #include <asm/tlbflush.h> | 32 | #include <asm/tlbflush.h> |
32 | #include <asm/shmparam.h> | 33 | #include <asm/shmparam.h> |
33 | 34 | ||
35 | struct vfree_deferred { | ||
36 | struct llist_head list; | ||
37 | struct work_struct wq; | ||
38 | }; | ||
39 | static DEFINE_PER_CPU(struct vfree_deferred, vfree_deferred); | ||
40 | |||
41 | static void __vunmap(const void *, int); | ||
42 | |||
43 | static void free_work(struct work_struct *w) | ||
44 | { | ||
45 | struct vfree_deferred *p = container_of(w, struct vfree_deferred, wq); | ||
46 | struct llist_node *llnode = llist_del_all(&p->list); | ||
47 | while (llnode) { | ||
48 | void *p = llnode; | ||
49 | llnode = llist_next(llnode); | ||
50 | __vunmap(p, 1); | ||
51 | } | ||
52 | } | ||
53 | |||
34 | /*** Page table manipulation functions ***/ | 54 | /*** Page table manipulation functions ***/ |
35 | 55 | ||
36 | static void vunmap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end) | 56 | static void vunmap_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end) |
@@ -1175,10 +1195,14 @@ void __init vmalloc_init(void) | |||
1175 | 1195 | ||
1176 | for_each_possible_cpu(i) { | 1196 | for_each_possible_cpu(i) { |
1177 | struct vmap_block_queue *vbq; | 1197 | struct vmap_block_queue *vbq; |
1198 | struct vfree_deferred *p; | ||
1178 | 1199 | ||
1179 | vbq = &per_cpu(vmap_block_queue, i); | 1200 | vbq = &per_cpu(vmap_block_queue, i); |
1180 | spin_lock_init(&vbq->lock); | 1201 | spin_lock_init(&vbq->lock); |
1181 | INIT_LIST_HEAD(&vbq->free); | 1202 | INIT_LIST_HEAD(&vbq->free); |
1203 | p = &per_cpu(vfree_deferred, i); | ||
1204 | init_llist_head(&p->list); | ||
1205 | INIT_WORK(&p->wq, free_work); | ||
1182 | } | 1206 | } |
1183 | 1207 | ||
1184 | /* Import existing vmlist entries. */ | 1208 | /* Import existing vmlist entries. */ |
@@ -1486,7 +1510,7 @@ static void __vunmap(const void *addr, int deallocate_pages) | |||
1486 | kfree(area); | 1510 | kfree(area); |
1487 | return; | 1511 | return; |
1488 | } | 1512 | } |
1489 | 1513 | ||
1490 | /** | 1514 | /** |
1491 | * vfree - release memory allocated by vmalloc() | 1515 | * vfree - release memory allocated by vmalloc() |
1492 | * @addr: memory base address | 1516 | * @addr: memory base address |
@@ -1495,15 +1519,25 @@ static void __vunmap(const void *addr, int deallocate_pages) | |||
1495 | * obtained from vmalloc(), vmalloc_32() or __vmalloc(). If @addr is | 1519 | * obtained from vmalloc(), vmalloc_32() or __vmalloc(). If @addr is |
1496 | * NULL, no operation is performed. | 1520 | * NULL, no operation is performed. |
1497 | * | 1521 | * |
1498 | * Must not be called in interrupt context. | 1522 | * Must not be called in NMI context (strictly speaking, only if we don't |
1523 | * have CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG, but making the calling | ||
1524 | * conventions for vfree() arch-depenedent would be a really bad idea) | ||
1525 | * | ||
1499 | */ | 1526 | */ |
1500 | void vfree(const void *addr) | 1527 | void vfree(const void *addr) |
1501 | { | 1528 | { |
1502 | BUG_ON(in_interrupt()); | 1529 | BUG_ON(in_nmi()); |
1503 | 1530 | ||
1504 | kmemleak_free(addr); | 1531 | kmemleak_free(addr); |
1505 | 1532 | ||
1506 | __vunmap(addr, 1); | 1533 | if (!addr) |
1534 | return; | ||
1535 | if (unlikely(in_interrupt())) { | ||
1536 | struct vfree_deferred *p = &__get_cpu_var(vfree_deferred); | ||
1537 | llist_add((struct llist_node *)addr, &p->list); | ||
1538 | schedule_work(&p->wq); | ||
1539 | } else | ||
1540 | __vunmap(addr, 1); | ||
1507 | } | 1541 | } |
1508 | EXPORT_SYMBOL(vfree); | 1542 | EXPORT_SYMBOL(vfree); |
1509 | 1543 | ||
@@ -1520,7 +1554,8 @@ void vunmap(const void *addr) | |||
1520 | { | 1554 | { |
1521 | BUG_ON(in_interrupt()); | 1555 | BUG_ON(in_interrupt()); |
1522 | might_sleep(); | 1556 | might_sleep(); |
1523 | __vunmap(addr, 0); | 1557 | if (addr) |
1558 | __vunmap(addr, 0); | ||
1524 | } | 1559 | } |
1525 | EXPORT_SYMBOL(vunmap); | 1560 | EXPORT_SYMBOL(vunmap); |
1526 | 1561 | ||