diff options
author | Dan Streetman <ddstreet@ieee.org> | 2014-08-06 19:08:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-08-06 21:01:23 -0400 |
commit | af8d417a04564bca0348e7e3c749ab12a3e837ad (patch) | |
tree | ce222a9643ba4a0afa675ed1f2d1b1447ec5174b /mm | |
parent | 99eef8e9369abe009006b4fa7f6ca5086c09cf46 (diff) |
mm/zpool: implement common zpool api to zbud/zsmalloc
Add zpool api.
zpool provides an interface for memory storage, typically of compressed
memory. Users can select what backend to use; currently the only
implementations are zbud, a low density implementation with up to two
compressed pages per storage page, and zsmalloc, a higher density
implementation with multiple compressed pages per storage page.
Signed-off-by: Dan Streetman <ddstreet@ieee.org>
Tested-by: Seth Jennings <sjennings@variantweb.net>
Cc: Minchan Kim <minchan@kernel.org>
Cc: Nitin Gupta <ngupta@vflare.org>
Cc: Weijie Yang <weijie.yang@samsung.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm')
-rw-r--r-- | mm/Kconfig | 41 | ||||
-rw-r--r-- | mm/Makefile | 1 | ||||
-rw-r--r-- | mm/zpool.c | 364 | ||||
-rw-r--r-- | mm/zsmalloc.c | 1 |
4 files changed, 389 insertions, 18 deletions
diff --git a/mm/Kconfig b/mm/Kconfig index f4899ec39cf4..12179b8c3b89 100644 --- a/mm/Kconfig +++ b/mm/Kconfig | |||
@@ -519,15 +519,17 @@ config CMA_AREAS | |||
519 | 519 | ||
520 | If unsure, leave the default value "7". | 520 | If unsure, leave the default value "7". |
521 | 521 | ||
522 | config ZBUD | 522 | config MEM_SOFT_DIRTY |
523 | tristate | 523 | bool "Track memory changes" |
524 | default n | 524 | depends on CHECKPOINT_RESTORE && HAVE_ARCH_SOFT_DIRTY && PROC_FS |
525 | select PROC_PAGE_MONITOR | ||
525 | help | 526 | help |
526 | A special purpose allocator for storing compressed pages. | 527 | This option enables memory changes tracking by introducing a |
527 | It is designed to store up to two compressed pages per physical | 528 | soft-dirty bit on pte-s. This bit it set when someone writes |
528 | page. While this design limits storage density, it has simple and | 529 | into a page just as regular dirty bit, but unlike the latter |
529 | deterministic reclaim properties that make it preferable to a higher | 530 | it can be cleared by hands. |
530 | density approach when reclaim will be used. | 531 | |
532 | See Documentation/vm/soft-dirty.txt for more details. | ||
531 | 533 | ||
532 | config ZSWAP | 534 | config ZSWAP |
533 | bool "Compressed cache for swap pages (EXPERIMENTAL)" | 535 | bool "Compressed cache for swap pages (EXPERIMENTAL)" |
@@ -549,17 +551,22 @@ config ZSWAP | |||
549 | they have not be fully explored on the large set of potential | 551 | they have not be fully explored on the large set of potential |
550 | configurations and workloads that exist. | 552 | configurations and workloads that exist. |
551 | 553 | ||
552 | config MEM_SOFT_DIRTY | 554 | config ZPOOL |
553 | bool "Track memory changes" | 555 | tristate "Common API for compressed memory storage" |
554 | depends on CHECKPOINT_RESTORE && HAVE_ARCH_SOFT_DIRTY && PROC_FS | 556 | default n |
555 | select PROC_PAGE_MONITOR | ||
556 | help | 557 | help |
557 | This option enables memory changes tracking by introducing a | 558 | Compressed memory storage API. This allows using either zbud or |
558 | soft-dirty bit on pte-s. This bit it set when someone writes | 559 | zsmalloc. |
559 | into a page just as regular dirty bit, but unlike the latter | ||
560 | it can be cleared by hands. | ||
561 | 560 | ||
562 | See Documentation/vm/soft-dirty.txt for more details. | 561 | config ZBUD |
562 | tristate "Low density storage for compressed pages" | ||
563 | default n | ||
564 | help | ||
565 | A special purpose allocator for storing compressed pages. | ||
566 | It is designed to store up to two compressed pages per physical | ||
567 | page. While this design limits storage density, it has simple and | ||
568 | deterministic reclaim properties that make it preferable to a higher | ||
569 | density approach when reclaim will be used. | ||
563 | 570 | ||
564 | config ZSMALLOC | 571 | config ZSMALLOC |
565 | tristate "Memory allocator for compressed pages" | 572 | tristate "Memory allocator for compressed pages" |
diff --git a/mm/Makefile b/mm/Makefile index 8338473c329a..632ae77e6070 100644 --- a/mm/Makefile +++ b/mm/Makefile | |||
@@ -59,6 +59,7 @@ obj-$(CONFIG_DEBUG_KMEMLEAK) += kmemleak.o | |||
59 | obj-$(CONFIG_DEBUG_KMEMLEAK_TEST) += kmemleak-test.o | 59 | obj-$(CONFIG_DEBUG_KMEMLEAK_TEST) += kmemleak-test.o |
60 | obj-$(CONFIG_CLEANCACHE) += cleancache.o | 60 | obj-$(CONFIG_CLEANCACHE) += cleancache.o |
61 | obj-$(CONFIG_MEMORY_ISOLATION) += page_isolation.o | 61 | obj-$(CONFIG_MEMORY_ISOLATION) += page_isolation.o |
62 | obj-$(CONFIG_ZPOOL) += zpool.o | ||
62 | obj-$(CONFIG_ZBUD) += zbud.o | 63 | obj-$(CONFIG_ZBUD) += zbud.o |
63 | obj-$(CONFIG_ZSMALLOC) += zsmalloc.o | 64 | obj-$(CONFIG_ZSMALLOC) += zsmalloc.o |
64 | obj-$(CONFIG_GENERIC_EARLY_IOREMAP) += early_ioremap.o | 65 | obj-$(CONFIG_GENERIC_EARLY_IOREMAP) += early_ioremap.o |
diff --git a/mm/zpool.c b/mm/zpool.c new file mode 100644 index 000000000000..e40612a1df00 --- /dev/null +++ b/mm/zpool.c | |||
@@ -0,0 +1,364 @@ | |||
1 | /* | ||
2 | * zpool memory storage api | ||
3 | * | ||
4 | * Copyright (C) 2014 Dan Streetman | ||
5 | * | ||
6 | * This is a common frontend for memory storage pool implementations. | ||
7 | * Typically, this is used to store compressed memory. | ||
8 | */ | ||
9 | |||
10 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
11 | |||
12 | #include <linux/list.h> | ||
13 | #include <linux/types.h> | ||
14 | #include <linux/mm.h> | ||
15 | #include <linux/slab.h> | ||
16 | #include <linux/spinlock.h> | ||
17 | #include <linux/module.h> | ||
18 | #include <linux/zpool.h> | ||
19 | |||
20 | struct zpool { | ||
21 | char *type; | ||
22 | |||
23 | struct zpool_driver *driver; | ||
24 | void *pool; | ||
25 | struct zpool_ops *ops; | ||
26 | |||
27 | struct list_head list; | ||
28 | }; | ||
29 | |||
30 | static LIST_HEAD(drivers_head); | ||
31 | static DEFINE_SPINLOCK(drivers_lock); | ||
32 | |||
33 | static LIST_HEAD(pools_head); | ||
34 | static DEFINE_SPINLOCK(pools_lock); | ||
35 | |||
36 | /** | ||
37 | * zpool_register_driver() - register a zpool implementation. | ||
38 | * @driver: driver to register | ||
39 | */ | ||
40 | void zpool_register_driver(struct zpool_driver *driver) | ||
41 | { | ||
42 | spin_lock(&drivers_lock); | ||
43 | atomic_set(&driver->refcount, 0); | ||
44 | list_add(&driver->list, &drivers_head); | ||
45 | spin_unlock(&drivers_lock); | ||
46 | } | ||
47 | EXPORT_SYMBOL(zpool_register_driver); | ||
48 | |||
49 | /** | ||
50 | * zpool_unregister_driver() - unregister a zpool implementation. | ||
51 | * @driver: driver to unregister. | ||
52 | * | ||
53 | * Module usage counting is used to prevent using a driver | ||
54 | * while/after unloading, so if this is called from module | ||
55 | * exit function, this should never fail; if called from | ||
56 | * other than the module exit function, and this returns | ||
57 | * failure, the driver is in use and must remain available. | ||
58 | */ | ||
59 | int zpool_unregister_driver(struct zpool_driver *driver) | ||
60 | { | ||
61 | int ret = 0, refcount; | ||
62 | |||
63 | spin_lock(&drivers_lock); | ||
64 | refcount = atomic_read(&driver->refcount); | ||
65 | WARN_ON(refcount < 0); | ||
66 | if (refcount > 0) | ||
67 | ret = -EBUSY; | ||
68 | else | ||
69 | list_del(&driver->list); | ||
70 | spin_unlock(&drivers_lock); | ||
71 | |||
72 | return ret; | ||
73 | } | ||
74 | EXPORT_SYMBOL(zpool_unregister_driver); | ||
75 | |||
76 | /** | ||
77 | * zpool_evict() - evict callback from a zpool implementation. | ||
78 | * @pool: pool to evict from. | ||
79 | * @handle: handle to evict. | ||
80 | * | ||
81 | * This can be used by zpool implementations to call the | ||
82 | * user's evict zpool_ops struct evict callback. | ||
83 | */ | ||
84 | int zpool_evict(void *pool, unsigned long handle) | ||
85 | { | ||
86 | struct zpool *zpool; | ||
87 | |||
88 | spin_lock(&pools_lock); | ||
89 | list_for_each_entry(zpool, &pools_head, list) { | ||
90 | if (zpool->pool == pool) { | ||
91 | spin_unlock(&pools_lock); | ||
92 | if (!zpool->ops || !zpool->ops->evict) | ||
93 | return -EINVAL; | ||
94 | return zpool->ops->evict(zpool, handle); | ||
95 | } | ||
96 | } | ||
97 | spin_unlock(&pools_lock); | ||
98 | |||
99 | return -ENOENT; | ||
100 | } | ||
101 | EXPORT_SYMBOL(zpool_evict); | ||
102 | |||
103 | static struct zpool_driver *zpool_get_driver(char *type) | ||
104 | { | ||
105 | struct zpool_driver *driver; | ||
106 | |||
107 | spin_lock(&drivers_lock); | ||
108 | list_for_each_entry(driver, &drivers_head, list) { | ||
109 | if (!strcmp(driver->type, type)) { | ||
110 | bool got = try_module_get(driver->owner); | ||
111 | |||
112 | if (got) | ||
113 | atomic_inc(&driver->refcount); | ||
114 | spin_unlock(&drivers_lock); | ||
115 | return got ? driver : NULL; | ||
116 | } | ||
117 | } | ||
118 | |||
119 | spin_unlock(&drivers_lock); | ||
120 | return NULL; | ||
121 | } | ||
122 | |||
123 | static void zpool_put_driver(struct zpool_driver *driver) | ||
124 | { | ||
125 | atomic_dec(&driver->refcount); | ||
126 | module_put(driver->owner); | ||
127 | } | ||
128 | |||
129 | /** | ||
130 | * zpool_create_pool() - Create a new zpool | ||
131 | * @type The type of the zpool to create (e.g. zbud, zsmalloc) | ||
132 | * @gfp The GFP flags to use when allocating the pool. | ||
133 | * @ops The optional ops callback. | ||
134 | * | ||
135 | * This creates a new zpool of the specified type. The gfp flags will be | ||
136 | * used when allocating memory, if the implementation supports it. If the | ||
137 | * ops param is NULL, then the created zpool will not be shrinkable. | ||
138 | * | ||
139 | * Implementations must guarantee this to be thread-safe. | ||
140 | * | ||
141 | * Returns: New zpool on success, NULL on failure. | ||
142 | */ | ||
143 | struct zpool *zpool_create_pool(char *type, gfp_t gfp, struct zpool_ops *ops) | ||
144 | { | ||
145 | struct zpool_driver *driver; | ||
146 | struct zpool *zpool; | ||
147 | |||
148 | pr_info("creating pool type %s\n", type); | ||
149 | |||
150 | driver = zpool_get_driver(type); | ||
151 | |||
152 | if (!driver) { | ||
153 | request_module(type); | ||
154 | driver = zpool_get_driver(type); | ||
155 | } | ||
156 | |||
157 | if (!driver) { | ||
158 | pr_err("no driver for type %s\n", type); | ||
159 | return NULL; | ||
160 | } | ||
161 | |||
162 | zpool = kmalloc(sizeof(*zpool), gfp); | ||
163 | if (!zpool) { | ||
164 | pr_err("couldn't create zpool - out of memory\n"); | ||
165 | zpool_put_driver(driver); | ||
166 | return NULL; | ||
167 | } | ||
168 | |||
169 | zpool->type = driver->type; | ||
170 | zpool->driver = driver; | ||
171 | zpool->pool = driver->create(gfp, ops); | ||
172 | zpool->ops = ops; | ||
173 | |||
174 | if (!zpool->pool) { | ||
175 | pr_err("couldn't create %s pool\n", type); | ||
176 | zpool_put_driver(driver); | ||
177 | kfree(zpool); | ||
178 | return NULL; | ||
179 | } | ||
180 | |||
181 | pr_info("created %s pool\n", type); | ||
182 | |||
183 | spin_lock(&pools_lock); | ||
184 | list_add(&zpool->list, &pools_head); | ||
185 | spin_unlock(&pools_lock); | ||
186 | |||
187 | return zpool; | ||
188 | } | ||
189 | |||
190 | /** | ||
191 | * zpool_destroy_pool() - Destroy a zpool | ||
192 | * @pool The zpool to destroy. | ||
193 | * | ||
194 | * Implementations must guarantee this to be thread-safe, | ||
195 | * however only when destroying different pools. The same | ||
196 | * pool should only be destroyed once, and should not be used | ||
197 | * after it is destroyed. | ||
198 | * | ||
199 | * This destroys an existing zpool. The zpool should not be in use. | ||
200 | */ | ||
201 | void zpool_destroy_pool(struct zpool *zpool) | ||
202 | { | ||
203 | pr_info("destroying pool type %s\n", zpool->type); | ||
204 | |||
205 | spin_lock(&pools_lock); | ||
206 | list_del(&zpool->list); | ||
207 | spin_unlock(&pools_lock); | ||
208 | zpool->driver->destroy(zpool->pool); | ||
209 | zpool_put_driver(zpool->driver); | ||
210 | kfree(zpool); | ||
211 | } | ||
212 | |||
213 | /** | ||
214 | * zpool_get_type() - Get the type of the zpool | ||
215 | * @pool The zpool to check | ||
216 | * | ||
217 | * This returns the type of the pool. | ||
218 | * | ||
219 | * Implementations must guarantee this to be thread-safe. | ||
220 | * | ||
221 | * Returns: The type of zpool. | ||
222 | */ | ||
223 | char *zpool_get_type(struct zpool *zpool) | ||
224 | { | ||
225 | return zpool->type; | ||
226 | } | ||
227 | |||
228 | /** | ||
229 | * zpool_malloc() - Allocate memory | ||
230 | * @pool The zpool to allocate from. | ||
231 | * @size The amount of memory to allocate. | ||
232 | * @gfp The GFP flags to use when allocating memory. | ||
233 | * @handle Pointer to the handle to set | ||
234 | * | ||
235 | * This allocates the requested amount of memory from the pool. | ||
236 | * The gfp flags will be used when allocating memory, if the | ||
237 | * implementation supports it. The provided @handle will be | ||
238 | * set to the allocated object handle. | ||
239 | * | ||
240 | * Implementations must guarantee this to be thread-safe. | ||
241 | * | ||
242 | * Returns: 0 on success, negative value on error. | ||
243 | */ | ||
244 | int zpool_malloc(struct zpool *zpool, size_t size, gfp_t gfp, | ||
245 | unsigned long *handle) | ||
246 | { | ||
247 | return zpool->driver->malloc(zpool->pool, size, gfp, handle); | ||
248 | } | ||
249 | |||
250 | /** | ||
251 | * zpool_free() - Free previously allocated memory | ||
252 | * @pool The zpool that allocated the memory. | ||
253 | * @handle The handle to the memory to free. | ||
254 | * | ||
255 | * This frees previously allocated memory. This does not guarantee | ||
256 | * that the pool will actually free memory, only that the memory | ||
257 | * in the pool will become available for use by the pool. | ||
258 | * | ||
259 | * Implementations must guarantee this to be thread-safe, | ||
260 | * however only when freeing different handles. The same | ||
261 | * handle should only be freed once, and should not be used | ||
262 | * after freeing. | ||
263 | */ | ||
264 | void zpool_free(struct zpool *zpool, unsigned long handle) | ||
265 | { | ||
266 | zpool->driver->free(zpool->pool, handle); | ||
267 | } | ||
268 | |||
269 | /** | ||
270 | * zpool_shrink() - Shrink the pool size | ||
271 | * @pool The zpool to shrink. | ||
272 | * @pages The number of pages to shrink the pool. | ||
273 | * @reclaimed The number of pages successfully evicted. | ||
274 | * | ||
275 | * This attempts to shrink the actual memory size of the pool | ||
276 | * by evicting currently used handle(s). If the pool was | ||
277 | * created with no zpool_ops, or the evict call fails for any | ||
278 | * of the handles, this will fail. If non-NULL, the @reclaimed | ||
279 | * parameter will be set to the number of pages reclaimed, | ||
280 | * which may be more than the number of pages requested. | ||
281 | * | ||
282 | * Implementations must guarantee this to be thread-safe. | ||
283 | * | ||
284 | * Returns: 0 on success, negative value on error/failure. | ||
285 | */ | ||
286 | int zpool_shrink(struct zpool *zpool, unsigned int pages, | ||
287 | unsigned int *reclaimed) | ||
288 | { | ||
289 | return zpool->driver->shrink(zpool->pool, pages, reclaimed); | ||
290 | } | ||
291 | |||
292 | /** | ||
293 | * zpool_map_handle() - Map a previously allocated handle into memory | ||
294 | * @pool The zpool that the handle was allocated from | ||
295 | * @handle The handle to map | ||
296 | * @mm How the memory should be mapped | ||
297 | * | ||
298 | * This maps a previously allocated handle into memory. The @mm | ||
299 | * param indicates to the implementation how the memory will be | ||
300 | * used, i.e. read-only, write-only, read-write. If the | ||
301 | * implementation does not support it, the memory will be treated | ||
302 | * as read-write. | ||
303 | * | ||
304 | * This may hold locks, disable interrupts, and/or preemption, | ||
305 | * and the zpool_unmap_handle() must be called to undo those | ||
306 | * actions. The code that uses the mapped handle should complete | ||
307 | * its operatons on the mapped handle memory quickly and unmap | ||
308 | * as soon as possible. As the implementation may use per-cpu | ||
309 | * data, multiple handles should not be mapped concurrently on | ||
310 | * any cpu. | ||
311 | * | ||
312 | * Returns: A pointer to the handle's mapped memory area. | ||
313 | */ | ||
314 | void *zpool_map_handle(struct zpool *zpool, unsigned long handle, | ||
315 | enum zpool_mapmode mapmode) | ||
316 | { | ||
317 | return zpool->driver->map(zpool->pool, handle, mapmode); | ||
318 | } | ||
319 | |||
320 | /** | ||
321 | * zpool_unmap_handle() - Unmap a previously mapped handle | ||
322 | * @pool The zpool that the handle was allocated from | ||
323 | * @handle The handle to unmap | ||
324 | * | ||
325 | * This unmaps a previously mapped handle. Any locks or other | ||
326 | * actions that the implementation took in zpool_map_handle() | ||
327 | * will be undone here. The memory area returned from | ||
328 | * zpool_map_handle() should no longer be used after this. | ||
329 | */ | ||
330 | void zpool_unmap_handle(struct zpool *zpool, unsigned long handle) | ||
331 | { | ||
332 | zpool->driver->unmap(zpool->pool, handle); | ||
333 | } | ||
334 | |||
335 | /** | ||
336 | * zpool_get_total_size() - The total size of the pool | ||
337 | * @pool The zpool to check | ||
338 | * | ||
339 | * This returns the total size in bytes of the pool. | ||
340 | * | ||
341 | * Returns: Total size of the zpool in bytes. | ||
342 | */ | ||
343 | u64 zpool_get_total_size(struct zpool *zpool) | ||
344 | { | ||
345 | return zpool->driver->total_size(zpool->pool); | ||
346 | } | ||
347 | |||
348 | static int __init init_zpool(void) | ||
349 | { | ||
350 | pr_info("loaded\n"); | ||
351 | return 0; | ||
352 | } | ||
353 | |||
354 | static void __exit exit_zpool(void) | ||
355 | { | ||
356 | pr_info("unloaded\n"); | ||
357 | } | ||
358 | |||
359 | module_init(init_zpool); | ||
360 | module_exit(exit_zpool); | ||
361 | |||
362 | MODULE_LICENSE("GPL"); | ||
363 | MODULE_AUTHOR("Dan Streetman <ddstreet@ieee.org>"); | ||
364 | MODULE_DESCRIPTION("Common API for compressed memory storage"); | ||
diff --git a/mm/zsmalloc.c b/mm/zsmalloc.c index bb62a4adc328..6a1827d3d231 100644 --- a/mm/zsmalloc.c +++ b/mm/zsmalloc.c | |||
@@ -240,7 +240,6 @@ struct mapping_area { | |||
240 | enum zs_mapmode vm_mm; /* mapping mode */ | 240 | enum zs_mapmode vm_mm; /* mapping mode */ |
241 | }; | 241 | }; |
242 | 242 | ||
243 | |||
244 | /* per-cpu VM mapping areas for zspage accesses that cross page boundaries */ | 243 | /* per-cpu VM mapping areas for zspage accesses that cross page boundaries */ |
245 | static DEFINE_PER_CPU(struct mapping_area, zs_map_area); | 244 | static DEFINE_PER_CPU(struct mapping_area, zs_map_area); |
246 | 245 | ||