diff options
-rw-r--r-- | mm/percpu.c | 48 |
1 files changed, 38 insertions, 10 deletions
diff --git a/mm/percpu.c b/mm/percpu.c index 7d9bc35e8ed2..4c8a419119da 100644 --- a/mm/percpu.c +++ b/mm/percpu.c | |||
@@ -63,6 +63,7 @@ | |||
63 | #include <linux/rbtree.h> | 63 | #include <linux/rbtree.h> |
64 | #include <linux/slab.h> | 64 | #include <linux/slab.h> |
65 | #include <linux/vmalloc.h> | 65 | #include <linux/vmalloc.h> |
66 | #include <linux/workqueue.h> | ||
66 | 67 | ||
67 | #include <asm/cacheflush.h> | 68 | #include <asm/cacheflush.h> |
68 | #include <asm/tlbflush.h> | 69 | #include <asm/tlbflush.h> |
@@ -118,6 +119,10 @@ static DEFINE_MUTEX(pcpu_mutex); | |||
118 | static struct list_head *pcpu_slot __read_mostly; /* chunk list slots */ | 119 | static struct list_head *pcpu_slot __read_mostly; /* chunk list slots */ |
119 | static struct rb_root pcpu_addr_root = RB_ROOT; /* chunks by address */ | 120 | static struct rb_root pcpu_addr_root = RB_ROOT; /* chunks by address */ |
120 | 121 | ||
122 | /* reclaim work to release fully free chunks, scheduled from free path */ | ||
123 | static void pcpu_reclaim(struct work_struct *work); | ||
124 | static DECLARE_WORK(pcpu_reclaim_work, pcpu_reclaim); | ||
125 | |||
121 | static int __pcpu_size_to_slot(int size) | 126 | static int __pcpu_size_to_slot(int size) |
122 | { | 127 | { |
123 | int highbit = fls(size); /* size is in bytes */ | 128 | int highbit = fls(size); /* size is in bytes */ |
@@ -846,13 +851,37 @@ void *__alloc_reserved_percpu(size_t size, size_t align) | |||
846 | return pcpu_alloc(size, align, true); | 851 | return pcpu_alloc(size, align, true); |
847 | } | 852 | } |
848 | 853 | ||
849 | static void pcpu_kill_chunk(struct pcpu_chunk *chunk) | 854 | /** |
855 | * pcpu_reclaim - reclaim fully free chunks, workqueue function | ||
856 | * @work: unused | ||
857 | * | ||
858 | * Reclaim all fully free chunks except for the first one. | ||
859 | */ | ||
860 | static void pcpu_reclaim(struct work_struct *work) | ||
850 | { | 861 | { |
851 | WARN_ON(chunk->immutable); | 862 | LIST_HEAD(todo); |
852 | pcpu_depopulate_chunk(chunk, 0, pcpu_unit_size, false); | 863 | struct list_head *head = &pcpu_slot[pcpu_nr_slots - 1]; |
853 | list_del(&chunk->list); | 864 | struct pcpu_chunk *chunk, *next; |
854 | rb_erase(&chunk->rb_node, &pcpu_addr_root); | 865 | |
855 | free_pcpu_chunk(chunk); | 866 | mutex_lock(&pcpu_mutex); |
867 | |||
868 | list_for_each_entry_safe(chunk, next, head, list) { | ||
869 | WARN_ON(chunk->immutable); | ||
870 | |||
871 | /* spare the first one */ | ||
872 | if (chunk == list_first_entry(head, struct pcpu_chunk, list)) | ||
873 | continue; | ||
874 | |||
875 | rb_erase(&chunk->rb_node, &pcpu_addr_root); | ||
876 | list_move(&chunk->list, &todo); | ||
877 | } | ||
878 | |||
879 | mutex_unlock(&pcpu_mutex); | ||
880 | |||
881 | list_for_each_entry_safe(chunk, next, &todo, list) { | ||
882 | pcpu_depopulate_chunk(chunk, 0, pcpu_unit_size, false); | ||
883 | free_pcpu_chunk(chunk); | ||
884 | } | ||
856 | } | 885 | } |
857 | 886 | ||
858 | /** | 887 | /** |
@@ -877,14 +906,13 @@ void free_percpu(void *ptr) | |||
877 | 906 | ||
878 | pcpu_free_area(chunk, off); | 907 | pcpu_free_area(chunk, off); |
879 | 908 | ||
880 | /* the chunk became fully free, kill one if there are other free ones */ | 909 | /* if there are more than one fully free chunks, wake up grim reaper */ |
881 | if (chunk->free_size == pcpu_unit_size) { | 910 | if (chunk->free_size == pcpu_unit_size) { |
882 | struct pcpu_chunk *pos; | 911 | struct pcpu_chunk *pos; |
883 | 912 | ||
884 | list_for_each_entry(pos, | 913 | list_for_each_entry(pos, &pcpu_slot[pcpu_nr_slots - 1], list) |
885 | &pcpu_slot[pcpu_chunk_slot(chunk)], list) | ||
886 | if (pos != chunk) { | 914 | if (pos != chunk) { |
887 | pcpu_kill_chunk(pos); | 915 | schedule_work(&pcpu_reclaim_work); |
888 | break; | 916 | break; |
889 | } | 917 | } |
890 | } | 918 | } |