aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/zbud.h22
-rw-r--r--mm/Kconfig10
-rw-r--r--mm/Makefile1
-rw-r--r--mm/zbud.c527
4 files changed, 560 insertions, 0 deletions
diff --git a/include/linux/zbud.h b/include/linux/zbud.h
new file mode 100644
index 000000000000..2571a5cfa5fc
--- /dev/null
+++ b/include/linux/zbud.h
@@ -0,0 +1,22 @@
1#ifndef _ZBUD_H_
2#define _ZBUD_H_
3
4#include <linux/types.h>
5
6struct zbud_pool;
7
8struct zbud_ops {
9 int (*evict)(struct zbud_pool *pool, unsigned long handle);
10};
11
12struct zbud_pool *zbud_create_pool(gfp_t gfp, struct zbud_ops *ops);
13void zbud_destroy_pool(struct zbud_pool *pool);
14int zbud_alloc(struct zbud_pool *pool, int size, gfp_t gfp,
15 unsigned long *handle);
16void zbud_free(struct zbud_pool *pool, unsigned long handle);
17int zbud_reclaim_page(struct zbud_pool *pool, unsigned int retries);
18void *zbud_map(struct zbud_pool *pool, unsigned long handle);
19void zbud_unmap(struct zbud_pool *pool, unsigned long handle);
20u64 zbud_get_pool_size(struct zbud_pool *pool);
21
22#endif /* _ZBUD_H_ */
diff --git a/mm/Kconfig b/mm/Kconfig
index 7e28ecfa8aa4..45503ed5f3aa 100644
--- a/mm/Kconfig
+++ b/mm/Kconfig
@@ -478,6 +478,16 @@ config FRONTSWAP
478 478
479 If unsure, say Y to enable frontswap. 479 If unsure, say Y to enable frontswap.
480 480
481config ZBUD
482 tristate
483 default n
484 help
485 A special purpose allocator for storing compressed pages.
486 It is designed to store up to two compressed pages per physical
487 page. While this design limits storage density, it has simple and
488 deterministic reclaim properties that make it preferable to a higher
489 density approach when reclaim will be used.
490
481config MEM_SOFT_DIRTY 491config MEM_SOFT_DIRTY
482 bool "Track memory changes" 492 bool "Track memory changes"
483 depends on CHECKPOINT_RESTORE && HAVE_ARCH_SOFT_DIRTY 493 depends on CHECKPOINT_RESTORE && HAVE_ARCH_SOFT_DIRTY
diff --git a/mm/Makefile b/mm/Makefile
index 72c5acb9345f..95f0197ce3d3 100644
--- a/mm/Makefile
+++ b/mm/Makefile
@@ -58,3 +58,4 @@ obj-$(CONFIG_DEBUG_KMEMLEAK) += kmemleak.o
58obj-$(CONFIG_DEBUG_KMEMLEAK_TEST) += kmemleak-test.o 58obj-$(CONFIG_DEBUG_KMEMLEAK_TEST) += kmemleak-test.o
59obj-$(CONFIG_CLEANCACHE) += cleancache.o 59obj-$(CONFIG_CLEANCACHE) += cleancache.o
60obj-$(CONFIG_MEMORY_ISOLATION) += page_isolation.o 60obj-$(CONFIG_MEMORY_ISOLATION) += page_isolation.o
61obj-$(CONFIG_ZBUD) += zbud.o
diff --git a/mm/zbud.c b/mm/zbud.c
new file mode 100644
index 000000000000..9bb4710e3589
--- /dev/null
+++ b/mm/zbud.c
@@ -0,0 +1,527 @@
1/*
2 * zbud.c
3 *
4 * Copyright (C) 2013, Seth Jennings, IBM
5 *
6 * Concepts based on zcache internal zbud allocator by Dan Magenheimer.
7 *
8 * zbud is an special purpose allocator for storing compressed pages. Contrary
9 * to what its name may suggest, zbud is not a buddy allocator, but rather an
10 * allocator that "buddies" two compressed pages together in a single memory
11 * page.
12 *
13 * While this design limits storage density, it has simple and deterministic
14 * reclaim properties that make it preferable to a higher density approach when
15 * reclaim will be used.
16 *
17 * zbud works by storing compressed pages, or "zpages", together in pairs in a
18 * single memory page called a "zbud page". The first buddy is "left
19 * justifed" at the beginning of the zbud page, and the last buddy is "right
20 * justified" at the end of the zbud page. The benefit is that if either
21 * buddy is freed, the freed buddy space, coalesced with whatever slack space
22 * that existed between the buddies, results in the largest possible free region
23 * within the zbud page.
24 *
25 * zbud also provides an attractive lower bound on density. The ratio of zpages
26 * to zbud pages can not be less than 1. This ensures that zbud can never "do
27 * harm" by using more pages to store zpages than the uncompressed zpages would
28 * have used on their own.
29 *
30 * zbud pages are divided into "chunks". The size of the chunks is fixed at
31 * compile time and determined by NCHUNKS_ORDER below. Dividing zbud pages
32 * into chunks allows organizing unbuddied zbud pages into a manageable number
33 * of unbuddied lists according to the number of free chunks available in the
34 * zbud page.
35 *
36 * The zbud API differs from that of conventional allocators in that the
37 * allocation function, zbud_alloc(), returns an opaque handle to the user,
38 * not a dereferenceable pointer. The user must map the handle using
39 * zbud_map() in order to get a usable pointer by which to access the
40 * allocation data and unmap the handle with zbud_unmap() when operations
41 * on the allocation data are complete.
42 */
43
44#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
45
46#include <linux/atomic.h>
47#include <linux/list.h>
48#include <linux/mm.h>
49#include <linux/module.h>
50#include <linux/preempt.h>
51#include <linux/slab.h>
52#include <linux/spinlock.h>
53#include <linux/zbud.h>
54
55/*****************
56 * Structures
57*****************/
58/*
59 * NCHUNKS_ORDER determines the internal allocation granularity, effectively
60 * adjusting internal fragmentation. It also determines the number of
61 * freelists maintained in each pool. NCHUNKS_ORDER of 6 means that the
62 * allocation granularity will be in chunks of size PAGE_SIZE/64, and there
63 * will be 64 freelists per pool.
64 */
65#define NCHUNKS_ORDER 6
66
67#define CHUNK_SHIFT (PAGE_SHIFT - NCHUNKS_ORDER)
68#define CHUNK_SIZE (1 << CHUNK_SHIFT)
69#define NCHUNKS (PAGE_SIZE >> CHUNK_SHIFT)
70#define ZHDR_SIZE_ALIGNED CHUNK_SIZE
71
72/**
73 * struct zbud_pool - stores metadata for each zbud pool
74 * @lock: protects all pool fields and first|last_chunk fields of any
75 * zbud page in the pool
76 * @unbuddied: array of lists tracking zbud pages that only contain one buddy;
77 * the lists each zbud page is added to depends on the size of
78 * its free region.
79 * @buddied: list tracking the zbud pages that contain two buddies;
80 * these zbud pages are full
81 * @lru: list tracking the zbud pages in LRU order by most recently
82 * added buddy.
83 * @pages_nr: number of zbud pages in the pool.
84 * @ops: pointer to a structure of user defined operations specified at
85 * pool creation time.
86 *
87 * This structure is allocated at pool creation time and maintains metadata
88 * pertaining to a particular zbud pool.
89 */
90struct zbud_pool {
91 spinlock_t lock;
92 struct list_head unbuddied[NCHUNKS];
93 struct list_head buddied;
94 struct list_head lru;
95 u64 pages_nr;
96 struct zbud_ops *ops;
97};
98
99/*
100 * struct zbud_header - zbud page metadata occupying the first chunk of each
101 * zbud page.
102 * @buddy: links the zbud page into the unbuddied/buddied lists in the pool
103 * @lru: links the zbud page into the lru list in the pool
104 * @first_chunks: the size of the first buddy in chunks, 0 if free
105 * @last_chunks: the size of the last buddy in chunks, 0 if free
106 */
107struct zbud_header {
108 struct list_head buddy;
109 struct list_head lru;
110 unsigned int first_chunks;
111 unsigned int last_chunks;
112 bool under_reclaim;
113};
114
115/*****************
116 * Helpers
117*****************/
118/* Just to make the code easier to read */
119enum buddy {
120 FIRST,
121 LAST
122};
123
124/* Converts an allocation size in bytes to size in zbud chunks */
125static int size_to_chunks(int size)
126{
127 return (size + CHUNK_SIZE - 1) >> CHUNK_SHIFT;
128}
129
130#define for_each_unbuddied_list(_iter, _begin) \
131 for ((_iter) = (_begin); (_iter) < NCHUNKS; (_iter)++)
132
133/* Initializes the zbud header of a newly allocated zbud page */
134static struct zbud_header *init_zbud_page(struct page *page)
135{
136 struct zbud_header *zhdr = page_address(page);
137 zhdr->first_chunks = 0;
138 zhdr->last_chunks = 0;
139 INIT_LIST_HEAD(&zhdr->buddy);
140 INIT_LIST_HEAD(&zhdr->lru);
141 zhdr->under_reclaim = 0;
142 return zhdr;
143}
144
145/* Resets the struct page fields and frees the page */
146static void free_zbud_page(struct zbud_header *zhdr)
147{
148 __free_page(virt_to_page(zhdr));
149}
150
151/*
152 * Encodes the handle of a particular buddy within a zbud page
153 * Pool lock should be held as this function accesses first|last_chunks
154 */
155static unsigned long encode_handle(struct zbud_header *zhdr, enum buddy bud)
156{
157</