diff options
author | Shaohua Li <shli@kernel.org> | 2013-02-22 19:34:37 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-02-23 20:50:17 -0500 |
commit | 33806f06da654092182410d974b6d3c5396ea3eb (patch) | |
tree | 7f7da99d94481a1d4c78ebf05b410fc8ba654a39 /mm/swap_state.c | |
parent | 9800339b5e0f0e24ab3dac349e0de80d2018832e (diff) |
swap: make each swap partition have one address_space
When I use several fast SSD to do swap, swapper_space.tree_lock is
heavily contended. This makes each swap partition have one
address_space to reduce the lock contention. There is an array of
address_space for swap. The swap entry type is the index to the array.
In my test with 3 SSD, this increases the swapout throughput 20%.
[akpm@linux-foundation.org: revert unneeded change to __add_to_swap_cache]
Signed-off-by: Shaohua Li <shli@fusionio.com>
Cc: Hugh Dickins <hughd@google.com>
Acked-by: Rik van Riel <riel@redhat.com>
Acked-by: Minchan Kim <minchan@kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm/swap_state.c')
-rw-r--r-- | mm/swap_state.c | 55 |
1 files changed, 38 insertions, 17 deletions
diff --git a/mm/swap_state.c b/mm/swap_state.c index 0cb36fb1f61c..8d6644c5d0cc 100644 --- a/mm/swap_state.c +++ b/mm/swap_state.c | |||
@@ -36,12 +36,12 @@ static struct backing_dev_info swap_backing_dev_info = { | |||
36 | .capabilities = BDI_CAP_NO_ACCT_AND_WRITEBACK | BDI_CAP_SWAP_BACKED, | 36 | .capabilities = BDI_CAP_NO_ACCT_AND_WRITEBACK | BDI_CAP_SWAP_BACKED, |
37 | }; | 37 | }; |
38 | 38 | ||
39 | struct address_space swapper_space = { | 39 | struct address_space swapper_spaces[MAX_SWAPFILES] = { |
40 | .page_tree = RADIX_TREE_INIT(GFP_ATOMIC|__GFP_NOWARN), | 40 | [0 ... MAX_SWAPFILES - 1] = { |
41 | .tree_lock = __SPIN_LOCK_UNLOCKED(swapper_space.tree_lock), | 41 | .page_tree = RADIX_TREE_INIT(GFP_ATOMIC|__GFP_NOWARN), |
42 | .a_ops = &swap_aops, | 42 | .a_ops = &swap_aops, |
43 | .i_mmap_nonlinear = LIST_HEAD_INIT(swapper_space.i_mmap_nonlinear), | 43 | .backing_dev_info = &swap_backing_dev_info, |
44 | .backing_dev_info = &swap_backing_dev_info, | 44 | } |
45 | }; | 45 | }; |
46 | 46 | ||
47 | #define INC_CACHE_INFO(x) do { swap_cache_info.x++; } while (0) | 47 | #define INC_CACHE_INFO(x) do { swap_cache_info.x++; } while (0) |
@@ -53,9 +53,19 @@ static struct { | |||
53 | unsigned long find_total; | 53 | unsigned long find_total; |
54 | } swap_cache_info; | 54 | } swap_cache_info; |
55 | 55 | ||
56 | unsigned long total_swapcache_pages(void) | ||
57 | { | ||
58 | int i; | ||
59 | unsigned long ret = 0; | ||
60 | |||
61 | for (i = 0; i < MAX_SWAPFILES; i++) | ||
62 | ret += swapper_spaces[i].nrpages; | ||
63 | return ret; | ||
64 | } | ||
65 | |||
56 | void show_swap_cache_info(void) | 66 | void show_swap_cache_info(void) |
57 | { | 67 | { |
58 | printk("%lu pages in swap cache\n", total_swapcache_pages); | 68 | printk("%lu pages in swap cache\n", total_swapcache_pages()); |
59 | printk("Swap cache stats: add %lu, delete %lu, find %lu/%lu\n", | 69 | printk("Swap cache stats: add %lu, delete %lu, find %lu/%lu\n", |
60 | swap_cache_info.add_total, swap_cache_info.del_total, | 70 | swap_cache_info.add_total, swap_cache_info.del_total, |
61 | swap_cache_info.find_success, swap_cache_info.find_total); | 71 | swap_cache_info.find_success, swap_cache_info.find_total); |
@@ -70,6 +80,7 @@ void show_swap_cache_info(void) | |||
70 | static int __add_to_swap_cache(struct page *page, swp_entry_t entry) | 80 | static int __add_to_swap_cache(struct page *page, swp_entry_t entry) |
71 | { | 81 | { |
72 | int error; | 82 | int error; |
83 | struct address_space *address_space; | ||
73 | 84 | ||
74 | VM_BUG_ON(!PageLocked(page)); | 85 | VM_BUG_ON(!PageLocked(page)); |
75 | VM_BUG_ON(PageSwapCache(page)); | 86 | VM_BUG_ON(PageSwapCache(page)); |
@@ -79,14 +90,16 @@ static int __add_to_swap_cache(struct page *page, swp_entry_t entry) | |||
79 | SetPageSwapCache(page); | 90 | SetPageSwapCache(page); |
80 | set_page_private(page, entry.val); | 91 | set_page_private(page, entry.val); |
81 | 92 | ||
82 | spin_lock_irq(&swapper_space.tree_lock); | 93 | address_space = swap_address_space(entry); |
83 | error = radix_tree_insert(&swapper_space.page_tree, entry.val, page); | 94 | spin_lock_irq(&address_space->tree_lock); |
95 | error = radix_tree_insert(&address_space->page_tree, | ||
96 | entry.val, page); | ||
84 | if (likely(!error)) { | 97 | if (likely(!error)) { |
85 | total_swapcache_pages++; | 98 | address_space->nrpages++; |
86 | __inc_zone_page_state(page, NR_FILE_PAGES); | 99 | __inc_zone_page_state(page, NR_FILE_PAGES); |
87 | INC_CACHE_INFO(add_total); | 100 | INC_CACHE_INFO(add_total); |
88 | } | 101 | } |
89 | spin_unlock_irq(&swapper_space.tree_lock); | 102 | spin_unlock_irq(&address_space->tree_lock); |
90 | 103 | ||
91 | if (unlikely(error)) { | 104 | if (unlikely(error)) { |
92 | /* | 105 | /* |
@@ -122,14 +135,19 @@ int add_to_swap_cache(struct page *page, swp_entry_t entry, gfp_t gfp_mask) | |||
122 | */ | 135 | */ |
123 | void __delete_from_swap_cache(struct page *page) | 136 | void __delete_from_swap_cache(struct page *page) |
124 | { | 137 | { |
138 | swp_entry_t entry; | ||
139 | struct address_space *address_space; | ||
140 | |||
125 | VM_BUG_ON(!PageLocked(page)); | 141 | VM_BUG_ON(!PageLocked(page)); |
126 | VM_BUG_ON(!PageSwapCache(page)); | 142 | VM_BUG_ON(!PageSwapCache(page)); |
127 | VM_BUG_ON(PageWriteback(page)); | 143 | VM_BUG_ON(PageWriteback(page)); |
128 | 144 | ||
129 | radix_tree_delete(&swapper_space.page_tree, page_private(page)); | 145 | entry.val = page_private(page); |
146 | address_space = swap_address_space(entry); | ||
147 | radix_tree_delete(&address_space->page_tree, page_private(page)); | ||
130 | set_page_private(page, 0); | 148 | set_page_private(page, 0); |
131 | ClearPageSwapCache(page); | 149 | ClearPageSwapCache(page); |
132 | total_swapcache_pages--; | 150 | address_space->nrpages--; |
133 | __dec_zone_page_state(page, NR_FILE_PAGES); | 151 | __dec_zone_page_state(page, NR_FILE_PAGES); |
134 | INC_CACHE_INFO(del_total); | 152 | INC_CACHE_INFO(del_total); |
135 | } | 153 | } |
@@ -195,12 +213,14 @@ int add_to_swap(struct page *page) | |||
195 | void delete_from_swap_cache(struct page *page) | 213 | void delete_from_swap_cache(struct page *page) |
196 | { | 214 | { |
197 | swp_entry_t entry; | 215 | swp_entry_t entry; |
216 | struct address_space *address_space; | ||
198 | 217 | ||
199 | entry.val = page_private(page); | 218 | entry.val = page_private(page); |
200 | 219 | ||
201 | spin_lock_irq(&swapper_space.tree_lock); | 220 | address_space = swap_address_space(entry); |
221 | spin_lock_irq(&address_space->tree_lock); | ||
202 | __delete_from_swap_cache(page); | 222 | __delete_from_swap_cache(page); |
203 | spin_unlock_irq(&swapper_space.tree_lock); | 223 | spin_unlock_irq(&address_space->tree_lock); |
204 | 224 | ||
205 | swapcache_free(entry, page); | 225 | swapcache_free(entry, page); |
206 | page_cache_release(page); | 226 | page_cache_release(page); |
@@ -263,7 +283,7 @@ struct page * lookup_swap_cache(swp_entry_t entry) | |||
263 | { | 283 | { |
264 | struct page *page; | 284 | struct page *page; |
265 | 285 | ||
266 | page = find_get_page(&swapper_space, entry.val); | 286 | page = find_get_page(swap_address_space(entry), entry.val); |
267 | 287 | ||
268 | if (page) | 288 | if (page) |
269 | INC_CACHE_INFO(find_success); | 289 | INC_CACHE_INFO(find_success); |
@@ -290,7 +310,8 @@ struct page *read_swap_cache_async(swp_entry_t entry, gfp_t gfp_mask, | |||
290 | * called after lookup_swap_cache() failed, re-calling | 310 | * called after lookup_swap_cache() failed, re-calling |
291 | * that would confuse statistics. | 311 | * that would confuse statistics. |
292 | */ | 312 | */ |
293 | found_page = find_get_page(&swapper_space, entry.val); | 313 | found_page = find_get_page(swap_address_space(entry), |
314 | entry.val); | ||
294 | if (found_page) | 315 | if (found_page) |
295 | break; | 316 | break; |
296 | 317 | ||