diff options
author | Dan Magenheimer <dan.magenheimer@oracle.com> | 2013-04-30 18:26:50 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-04-30 20:04:00 -0400 |
commit | 905cd0e1bf9ffe82d6906a01fd974ea0f70be97a (patch) | |
tree | e0ca87ed9b6e05b452d548d5d3ebbb6b48c70a34 | |
parent | 421348f1ca0bf17769dee0aed4d991845ae0536d (diff) |
mm: frontswap: lazy initialization to allow tmem backends to build/run as modules
With the goal of allowing tmem backends (zcache, ramster, Xen tmem) to
be built/loaded as modules rather than built-in and enabled by a boot
parameter, this patch provides "lazy initialization", allowing backends
to register to frontswap even after swapon was run. Before a backend
registers all calls to init are recorded and the creation of tmem_pools
delayed until a backend registers or until a frontswap store is
attempted.
Signed-off-by: Stefan Hengelein <ilendir@googlemail.com>
Signed-off-by: Florian Schmaus <fschmaus@gmail.com>
Signed-off-by: Andor Daam <andor.daam@googlemail.com>
Signed-off-by: Dan Magenheimer <dan.magenheimer@oracle.com>
[v1: Fixes per Seth Jennings suggestions]
[v2: Removed FRONTSWAP_HAS_.. ]
[v3: Fix up per Bob Liu <lliubbo@gmail.com> recommendations]
[v4: Fix up per Andrew's comments]
Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Signed-off-by: Bob Liu <lliubbo@gmail.com>
Cc: Wanpeng Li <liwanp@linux.vnet.ibm.com>
Cc: Dan Magenheimer <dan.magenheimer@oracle.com>
Cc: Minchan Kim <minchan@kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r-- | mm/frontswap.c | 94 |
1 files changed, 84 insertions, 10 deletions
diff --git a/mm/frontswap.c b/mm/frontswap.c index 2890e67d6026..cbd2b8af8129 100644 --- a/mm/frontswap.c +++ b/mm/frontswap.c | |||
@@ -80,6 +80,46 @@ static inline void inc_frontswap_succ_stores(void) { } | |||
80 | static inline void inc_frontswap_failed_stores(void) { } | 80 | static inline void inc_frontswap_failed_stores(void) { } |
81 | static inline void inc_frontswap_invalidates(void) { } | 81 | static inline void inc_frontswap_invalidates(void) { } |
82 | #endif | 82 | #endif |
83 | |||
84 | /* | ||
85 | * Due to the asynchronous nature of the backends loading potentially | ||
86 | * _after_ the swap system has been activated, we have chokepoints | ||
87 | * on all frontswap functions to not call the backend until the backend | ||
88 | * has registered. | ||
89 | * | ||
90 | * Specifically when no backend is registered (nobody called | ||
91 | * frontswap_register_ops) all calls to frontswap_init (which is done via | ||
92 | * swapon -> enable_swap_info -> frontswap_init) are registered and remembered | ||
93 | * (via the setting of need_init bitmap) but fail to create tmem_pools. When a | ||
94 | * backend registers with frontswap at some later point the previous | ||
95 | * calls to frontswap_init are executed (by iterating over the need_init | ||
96 | * bitmap) to create tmem_pools and set the respective poolids. All of that is | ||
97 | * guarded by us using atomic bit operations on the 'need_init' bitmap. | ||
98 | * | ||
99 | * This would not guards us against the user deciding to call swapoff right as | ||
100 | * we are calling the backend to initialize (so swapon is in action). | ||
101 | * Fortunatly for us, the swapon_mutex has been taked by the callee so we are | ||
102 | * OK. The other scenario where calls to frontswap_store (called via | ||
103 | * swap_writepage) is racing with frontswap_invalidate_area (called via | ||
104 | * swapoff) is again guarded by the swap subsystem. | ||
105 | * | ||
106 | * While no backend is registered all calls to frontswap_[store|load| | ||
107 | * invalidate_area|invalidate_page] are ignored or fail. | ||
108 | * | ||
109 | * The time between the backend being registered and the swap file system | ||
110 | * calling the backend (via the frontswap_* functions) is indeterminate as | ||
111 | * backend_registered is not atomic_t (or a value guarded by a spinlock). | ||
112 | * That is OK as we are comfortable missing some of these calls to the newly | ||
113 | * registered backend. | ||
114 | * | ||
115 | * Obviously the opposite (unloading the backend) must be done after all | ||
116 | * the frontswap_[store|load|invalidate_area|invalidate_page] start | ||
117 | * ignorning or failing the requests - at which point backend_registered | ||
118 | * would have to be made in some fashion atomic. | ||
119 | */ | ||
120 | static DECLARE_BITMAP(need_init, MAX_SWAPFILES); | ||
121 | static bool backend_registered __read_mostly; | ||
122 | |||
83 | /* | 123 | /* |
84 | * Register operations for frontswap, returning previous thus allowing | 124 | * Register operations for frontswap, returning previous thus allowing |
85 | * detection of multiple backends and possible nesting. | 125 | * detection of multiple backends and possible nesting. |
@@ -87,9 +127,22 @@ static inline void inc_frontswap_invalidates(void) { } | |||
87 | struct frontswap_ops frontswap_register_ops(struct frontswap_ops *ops) | 127 | struct frontswap_ops frontswap_register_ops(struct frontswap_ops *ops) |
88 | { | 128 | { |
89 | struct frontswap_ops old = frontswap_ops; | 129 | struct frontswap_ops old = frontswap_ops; |
130 | int i; | ||
90 | 131 | ||
91 | frontswap_ops = *ops; | 132 | frontswap_ops = *ops; |
92 | frontswap_enabled = true; | 133 | frontswap_enabled = true; |
134 | |||
135 | for (i = 0; i < MAX_SWAPFILES; i++) { | ||
136 | if (test_and_clear_bit(i, need_init)) | ||
137 | (*frontswap_ops.init)(i); | ||
138 | } | ||
139 | /* | ||
140 | * We MUST have backend_registered set _after_ the frontswap_init's | ||
141 | * have been called. Otherwise __frontswap_store might fail. Hence | ||
142 | * the barrier to make sure compiler does not re-order us. | ||
143 | */ | ||
144 | barrier(); | ||
145 | backend_registered = true; | ||
93 | return old; | 146 | return old; |
94 | } | 147 | } |
95 | EXPORT_SYMBOL(frontswap_register_ops); | 148 | EXPORT_SYMBOL(frontswap_register_ops); |
@@ -119,10 +172,16 @@ void __frontswap_init(unsigned type) | |||
119 | { | 172 | { |
120 | struct swap_info_struct *sis = swap_info[type]; | 173 | struct swap_info_struct *sis = swap_info[type]; |
121 | 174 | ||
122 | BUG_ON(sis == NULL); | 175 | if (backend_registered) { |
123 | if (sis->frontswap_map == NULL) | 176 | BUG_ON(sis == NULL); |
124 | return; | 177 | if (sis->frontswap_map == NULL) |
125 | frontswap_ops.init(type); | 178 | return; |
179 | (*frontswap_ops.init)(type); | ||
180 | } else { | ||
181 | BUG_ON(type > MAX_SWAPFILES); | ||
182 | set_bit(type, need_init); | ||
183 | } | ||
184 | |||
126 | } | 185 | } |
127 | EXPORT_SYMBOL(__frontswap_init); | 186 | EXPORT_SYMBOL(__frontswap_init); |
128 | 187 | ||
@@ -147,6 +206,11 @@ int __frontswap_store(struct page *page) | |||
147 | struct swap_info_struct *sis = swap_info[type]; | 206 | struct swap_info_struct *sis = swap_info[type]; |
148 | pgoff_t offset = swp_offset(entry); | 207 | pgoff_t offset = swp_offset(entry); |
149 | 208 | ||
209 | if (!backend_registered) { | ||
210 | inc_frontswap_failed_stores(); | ||
211 | return ret; | ||
212 | } | ||
213 | |||
150 | BUG_ON(!PageLocked(page)); | 214 | BUG_ON(!PageLocked(page)); |
151 | BUG_ON(sis == NULL); | 215 | BUG_ON(sis == NULL); |
152 | if (frontswap_test(sis, offset)) | 216 | if (frontswap_test(sis, offset)) |
@@ -186,6 +250,9 @@ int __frontswap_load(struct page *page) | |||
186 | struct swap_info_struct *sis = swap_info[type]; | 250 | struct swap_info_struct *sis = swap_info[type]; |
187 | pgoff_t offset = swp_offset(entry); | 251 | pgoff_t offset = swp_offset(entry); |
188 | 252 | ||
253 | if (!backend_registered) | ||
254 | return ret; | ||
255 | |||
189 | BUG_ON(!PageLocked(page)); | 256 | BUG_ON(!PageLocked(page)); |
190 | BUG_ON(sis == NULL); | 257 | BUG_ON(sis == NULL); |
191 | if (frontswap_test(sis, offset)) | 258 | if (frontswap_test(sis, offset)) |
@@ -209,6 +276,9 @@ void __frontswap_invalidate_page(unsigned type, pgoff_t offset) | |||
209 | { | 276 | { |
210 | struct swap_info_struct *sis = swap_info[type]; | 277 | struct swap_info_struct *sis = swap_info[type]; |
211 | 278 | ||
279 | if (!backend_registered) | ||
280 | return; | ||
281 | |||
212 | BUG_ON(sis == NULL); | 282 | BUG_ON(sis == NULL); |
213 | if (frontswap_test(sis, offset)) { | 283 | if (frontswap_test(sis, offset)) { |
214 | frontswap_ops.invalidate_page(type, offset); | 284 | frontswap_ops.invalidate_page(type, offset); |
@@ -226,12 +296,15 @@ void __frontswap_invalidate_area(unsigned type) | |||
226 | { | 296 | { |
227 | struct swap_info_struct *sis = swap_info[type]; | 297 | struct swap_info_struct *sis = swap_info[type]; |
228 | 298 | ||
229 | BUG_ON(sis == NULL); | 299 | if (backend_registered) { |
230 | if (sis->frontswap_map == NULL) | 300 | BUG_ON(sis == NULL); |
231 | return; | 301 | if (sis->frontswap_map == NULL) |
232 | frontswap_ops.invalidate_area(type); | 302 | return; |
233 | atomic_set(&sis->frontswap_pages, 0); | 303 | (*frontswap_ops.invalidate_area)(type); |
234 | memset(sis->frontswap_map, 0, sis->max / sizeof(long)); | 304 | atomic_set(&sis->frontswap_pages, 0); |
305 | memset(sis->frontswap_map, 0, sis->max / sizeof(long)); | ||
306 | } | ||
307 | clear_bit(type, need_init); | ||
235 | } | 308 | } |
236 | EXPORT_SYMBOL(__frontswap_invalidate_area); | 309 | EXPORT_SYMBOL(__frontswap_invalidate_area); |
237 | 310 | ||
@@ -364,6 +437,7 @@ static int __init init_frontswap(void) | |||
364 | debugfs_create_u64("invalidates", S_IRUGO, | 437 | debugfs_create_u64("invalidates", S_IRUGO, |
365 | root, &frontswap_invalidates); | 438 | root, &frontswap_invalidates); |
366 | #endif | 439 | #endif |
440 | frontswap_enabled = 1; | ||
367 | return 0; | 441 | return 0; |
368 | } | 442 | } |
369 | 443 | ||