Blame view

mm/zpool.c 10 KB
af8d417a0   Dan Streetman   mm/zpool: impleme...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
  /*
   * zpool memory storage api
   *
   * Copyright (C) 2014 Dan Streetman
   *
   * This is a common frontend for memory storage pool implementations.
   * Typically, this is used to store compressed memory.
   */
  
  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  
  #include <linux/list.h>
  #include <linux/types.h>
  #include <linux/mm.h>
  #include <linux/slab.h>
  #include <linux/spinlock.h>
  #include <linux/module.h>
  #include <linux/zpool.h>
  
  struct zpool {
af8d417a0   Dan Streetman   mm/zpool: impleme...
21
22
  	struct zpool_driver *driver;
  	void *pool;
786727799   Krzysztof Kozlowski   mm: zpool: consti...
23
  	const struct zpool_ops *ops;
af8d417a0   Dan Streetman   mm/zpool: impleme...
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
  
  	struct list_head list;
  };
  
  static LIST_HEAD(drivers_head);
  static DEFINE_SPINLOCK(drivers_lock);
  
  static LIST_HEAD(pools_head);
  static DEFINE_SPINLOCK(pools_lock);
  
  /**
   * zpool_register_driver() - register a zpool implementation.
   * @driver:	driver to register
   */
  void zpool_register_driver(struct zpool_driver *driver)
  {
  	spin_lock(&drivers_lock);
  	atomic_set(&driver->refcount, 0);
  	list_add(&driver->list, &drivers_head);
  	spin_unlock(&drivers_lock);
  }
  EXPORT_SYMBOL(zpool_register_driver);
  
  /**
   * zpool_unregister_driver() - unregister a zpool implementation.
   * @driver:	driver to unregister.
   *
   * Module usage counting is used to prevent using a driver
   * while/after unloading, so if this is called from module
   * exit function, this should never fail; if called from
   * other than the module exit function, and this returns
   * failure, the driver is in use and must remain available.
   */
  int zpool_unregister_driver(struct zpool_driver *driver)
  {
  	int ret = 0, refcount;
  
  	spin_lock(&drivers_lock);
  	refcount = atomic_read(&driver->refcount);
  	WARN_ON(refcount < 0);
  	if (refcount > 0)
  		ret = -EBUSY;
  	else
  		list_del(&driver->list);
  	spin_unlock(&drivers_lock);
  
  	return ret;
  }
  EXPORT_SYMBOL(zpool_unregister_driver);
69e18f4db   Dan Streetman   zpool: remove red...
73
  /* this assumes @type is null-terminated. */
6f3526d6d   Sergey SENOZHATSKY   mm: zsmalloc: con...
74
  static struct zpool_driver *zpool_get_driver(const char *type)
af8d417a0   Dan Streetman   mm/zpool: impleme...
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
  {
  	struct zpool_driver *driver;
  
  	spin_lock(&drivers_lock);
  	list_for_each_entry(driver, &drivers_head, list) {
  		if (!strcmp(driver->type, type)) {
  			bool got = try_module_get(driver->owner);
  
  			if (got)
  				atomic_inc(&driver->refcount);
  			spin_unlock(&drivers_lock);
  			return got ? driver : NULL;
  		}
  	}
  
  	spin_unlock(&drivers_lock);
  	return NULL;
  }
  
  static void zpool_put_driver(struct zpool_driver *driver)
  {
  	atomic_dec(&driver->refcount);
  	module_put(driver->owner);
  }
  
  /**
3f0e13122   Dan Streetman   zpool: add zpool_...
101
102
103
104
105
106
107
108
109
110
111
112
113
   * zpool_has_pool() - Check if the pool driver is available
   * @type	The type of the zpool to check (e.g. zbud, zsmalloc)
   *
   * This checks if the @type pool driver is available.  This will try to load
   * the requested module, if needed, but there is no guarantee the module will
   * still be loaded and available immediately after calling.  If this returns
   * true, the caller should assume the pool is available, but must be prepared
   * to handle the @zpool_create_pool() returning failure.  However if this
   * returns false, the caller should assume the requested pool type is not
   * available; either the requested pool type module does not exist, or could
   * not be loaded, and calling @zpool_create_pool() with the pool type will
   * fail.
   *
69e18f4db   Dan Streetman   zpool: remove red...
114
115
   * The @type string must be null-terminated.
   *
3f0e13122   Dan Streetman   zpool: add zpool_...
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
   * Returns: true if @type pool is available, false if not
   */
  bool zpool_has_pool(char *type)
  {
  	struct zpool_driver *driver = zpool_get_driver(type);
  
  	if (!driver) {
  		request_module("zpool-%s", type);
  		driver = zpool_get_driver(type);
  	}
  
  	if (!driver)
  		return false;
  
  	zpool_put_driver(driver);
  	return true;
  }
  EXPORT_SYMBOL(zpool_has_pool);
  
  /**
af8d417a0   Dan Streetman   mm/zpool: impleme...
136
137
   * zpool_create_pool() - Create a new zpool
   * @type	The type of the zpool to create (e.g. zbud, zsmalloc)
3eba0c6a5   Ganesh Mahendran   mm/zpool: add nam...
138
   * @name	The name of the zpool (e.g. zram0, zswap)
af8d417a0   Dan Streetman   mm/zpool: impleme...
139
140
141
142
143
144
145
146
147
   * @gfp		The GFP flags to use when allocating the pool.
   * @ops		The optional ops callback.
   *
   * This creates a new zpool of the specified type.  The gfp flags will be
   * used when allocating memory, if the implementation supports it.  If the
   * ops param is NULL, then the created zpool will not be shrinkable.
   *
   * Implementations must guarantee this to be thread-safe.
   *
69e18f4db   Dan Streetman   zpool: remove red...
148
149
   * The @type and @name strings must be null-terminated.
   *
af8d417a0   Dan Streetman   mm/zpool: impleme...
150
151
   * Returns: New zpool on success, NULL on failure.
   */
6f3526d6d   Sergey SENOZHATSKY   mm: zsmalloc: con...
152
  struct zpool *zpool_create_pool(const char *type, const char *name, gfp_t gfp,
786727799   Krzysztof Kozlowski   mm: zpool: consti...
153
  		const struct zpool_ops *ops)
af8d417a0   Dan Streetman   mm/zpool: impleme...
154
155
156
  {
  	struct zpool_driver *driver;
  	struct zpool *zpool;
cf41f5f49   Dan Streetman   zpool: change pr_...
157
158
  	pr_debug("creating pool type %s
  ", type);
af8d417a0   Dan Streetman   mm/zpool: impleme...
159
160
161
162
  
  	driver = zpool_get_driver(type);
  
  	if (!driver) {
137f8cff5   Kees Cook   mm/zpool: use pre...
163
  		request_module("zpool-%s", type);
af8d417a0   Dan Streetman   mm/zpool: impleme...
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
  		driver = zpool_get_driver(type);
  	}
  
  	if (!driver) {
  		pr_err("no driver for type %s
  ", type);
  		return NULL;
  	}
  
  	zpool = kmalloc(sizeof(*zpool), gfp);
  	if (!zpool) {
  		pr_err("couldn't create zpool - out of memory
  ");
  		zpool_put_driver(driver);
  		return NULL;
  	}
af8d417a0   Dan Streetman   mm/zpool: impleme...
180
  	zpool->driver = driver;
479305fd7   Dan Streetman   zpool: remove zpo...
181
  	zpool->pool = driver->create(name, gfp, ops, zpool);
af8d417a0   Dan Streetman   mm/zpool: impleme...
182
183
184
185
186
187
188
189
190
  	zpool->ops = ops;
  
  	if (!zpool->pool) {
  		pr_err("couldn't create %s pool
  ", type);
  		zpool_put_driver(driver);
  		kfree(zpool);
  		return NULL;
  	}
cf41f5f49   Dan Streetman   zpool: change pr_...
191
192
  	pr_debug("created pool type %s
  ", type);
af8d417a0   Dan Streetman   mm/zpool: impleme...
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
  
  	spin_lock(&pools_lock);
  	list_add(&zpool->list, &pools_head);
  	spin_unlock(&pools_lock);
  
  	return zpool;
  }
  
  /**
   * zpool_destroy_pool() - Destroy a zpool
   * @pool	The zpool to destroy.
   *
   * Implementations must guarantee this to be thread-safe,
   * however only when destroying different pools.  The same
   * pool should only be destroyed once, and should not be used
   * after it is destroyed.
   *
   * This destroys an existing zpool.  The zpool should not be in use.
   */
  void zpool_destroy_pool(struct zpool *zpool)
  {
69e18f4db   Dan Streetman   zpool: remove red...
214
215
  	pr_debug("destroying pool type %s
  ", zpool->driver->type);
af8d417a0   Dan Streetman   mm/zpool: impleme...
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
  
  	spin_lock(&pools_lock);
  	list_del(&zpool->list);
  	spin_unlock(&pools_lock);
  	zpool->driver->destroy(zpool->pool);
  	zpool_put_driver(zpool->driver);
  	kfree(zpool);
  }
  
  /**
   * zpool_get_type() - Get the type of the zpool
   * @pool	The zpool to check
   *
   * This returns the type of the pool.
   *
   * Implementations must guarantee this to be thread-safe.
   *
   * Returns: The type of zpool.
   */
69e18f4db   Dan Streetman   zpool: remove red...
235
  const char *zpool_get_type(struct zpool *zpool)
af8d417a0   Dan Streetman   mm/zpool: impleme...
236
  {
69e18f4db   Dan Streetman   zpool: remove red...
237
  	return zpool->driver->type;
af8d417a0   Dan Streetman   mm/zpool: impleme...
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
  }
  
  /**
   * zpool_malloc() - Allocate memory
   * @pool	The zpool to allocate from.
   * @size	The amount of memory to allocate.
   * @gfp		The GFP flags to use when allocating memory.
   * @handle	Pointer to the handle to set
   *
   * This allocates the requested amount of memory from the pool.
   * The gfp flags will be used when allocating memory, if the
   * implementation supports it.  The provided @handle will be
   * set to the allocated object handle.
   *
   * Implementations must guarantee this to be thread-safe.
   *
   * Returns: 0 on success, negative value on error.
   */
  int zpool_malloc(struct zpool *zpool, size_t size, gfp_t gfp,
  			unsigned long *handle)
  {
  	return zpool->driver->malloc(zpool->pool, size, gfp, handle);
  }
  
  /**
   * zpool_free() - Free previously allocated memory
   * @pool	The zpool that allocated the memory.
   * @handle	The handle to the memory to free.
   *
   * This frees previously allocated memory.  This does not guarantee
   * that the pool will actually free memory, only that the memory
   * in the pool will become available for use by the pool.
   *
   * Implementations must guarantee this to be thread-safe,
   * however only when freeing different handles.  The same
   * handle should only be freed once, and should not be used
   * after freeing.
   */
  void zpool_free(struct zpool *zpool, unsigned long handle)
  {
  	zpool->driver->free(zpool->pool, handle);
  }
  
  /**
   * zpool_shrink() - Shrink the pool size
   * @pool	The zpool to shrink.
   * @pages	The number of pages to shrink the pool.
   * @reclaimed	The number of pages successfully evicted.
   *
   * This attempts to shrink the actual memory size of the pool
   * by evicting currently used handle(s).  If the pool was
   * created with no zpool_ops, or the evict call fails for any
   * of the handles, this will fail.  If non-NULL, the @reclaimed
   * parameter will be set to the number of pages reclaimed,
   * which may be more than the number of pages requested.
   *
   * Implementations must guarantee this to be thread-safe.
   *
   * Returns: 0 on success, negative value on error/failure.
   */
  int zpool_shrink(struct zpool *zpool, unsigned int pages,
  			unsigned int *reclaimed)
  {
  	return zpool->driver->shrink(zpool->pool, pages, reclaimed);
  }
  
  /**
   * zpool_map_handle() - Map a previously allocated handle into memory
   * @pool	The zpool that the handle was allocated from
   * @handle	The handle to map
   * @mm		How the memory should be mapped
   *
   * This maps a previously allocated handle into memory.  The @mm
   * param indicates to the implementation how the memory will be
   * used, i.e. read-only, write-only, read-write.  If the
   * implementation does not support it, the memory will be treated
   * as read-write.
   *
   * This may hold locks, disable interrupts, and/or preemption,
   * and the zpool_unmap_handle() must be called to undo those
   * actions.  The code that uses the mapped handle should complete
   * its operatons on the mapped handle memory quickly and unmap
   * as soon as possible.  As the implementation may use per-cpu
   * data, multiple handles should not be mapped concurrently on
   * any cpu.
   *
   * Returns: A pointer to the handle's mapped memory area.
   */
  void *zpool_map_handle(struct zpool *zpool, unsigned long handle,
  			enum zpool_mapmode mapmode)
  {
  	return zpool->driver->map(zpool->pool, handle, mapmode);
  }
  
  /**
   * zpool_unmap_handle() - Unmap a previously mapped handle
   * @pool	The zpool that the handle was allocated from
   * @handle	The handle to unmap
   *
   * This unmaps a previously mapped handle.  Any locks or other
   * actions that the implementation took in zpool_map_handle()
   * will be undone here.  The memory area returned from
   * zpool_map_handle() should no longer be used after this.
   */
  void zpool_unmap_handle(struct zpool *zpool, unsigned long handle)
  {
  	zpool->driver->unmap(zpool->pool, handle);
  }
  
  /**
   * zpool_get_total_size() - The total size of the pool
   * @pool	The zpool to check
   *
   * This returns the total size in bytes of the pool.
   *
   * Returns: Total size of the zpool in bytes.
   */
  u64 zpool_get_total_size(struct zpool *zpool)
  {
  	return zpool->driver->total_size(zpool->pool);
  }
af8d417a0   Dan Streetman   mm/zpool: impleme...
359
360
361
  MODULE_LICENSE("GPL");
  MODULE_AUTHOR("Dan Streetman <ddstreet@ieee.org>");
  MODULE_DESCRIPTION("Common API for compressed memory storage");