mirror of
https://github.com/hardkernel/linux.git
synced 2026-03-25 03:50:24 +09:00
PD#112115: zram: use lz4 algorithm for zram
lz4 compress/decompress has better performance at speed than lzo and nearly same compress rate as lzo. The algorithm for zram is porting from kernel version 3.18.20 Change-Id: I2bea1f128b67bde85443631dfcf268e9b28be709
This commit is contained in:
@@ -290,6 +290,7 @@ CONFIG_MTD_CFI_INTELEXT=y
|
||||
CONFIG_MTD_NAND=y
|
||||
CONFIG_PROC_DEVICETREE=y
|
||||
CONFIG_ZRAM=y
|
||||
CONFIG_ZRAM_LZ4_COMPRESS=y
|
||||
CONFIG_BLK_DEV_LOOP=y
|
||||
CONFIG_VIRTIO_BLK=y
|
||||
CONFIG_UID_STAT=y
|
||||
|
||||
@@ -15,6 +15,16 @@ config ZRAM
|
||||
|
||||
See zram.txt for more information.
|
||||
|
||||
config ZRAM_LZ4_COMPRESS
|
||||
bool "Enable LZ4 algorithm support"
|
||||
depends on ZRAM
|
||||
select LZ4_COMPRESS
|
||||
select LZ4_DECOMPRESS
|
||||
default n
|
||||
help
|
||||
This option enables LZ4 compression algorithm support. Compression
|
||||
algorithm can be changed using `comp_algorithm' device attribute.
|
||||
|
||||
config ZRAM_DEBUG
|
||||
bool "Compressed RAM block device debug support"
|
||||
depends on ZRAM
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
zram-y := zram_drv.o
|
||||
zram-y := zcomp_lzo.o zcomp.o zram_drv.o
|
||||
|
||||
zram-$(CONFIG_ZRAM_LZ4_COMPRESS) += zcomp_lz4.o
|
||||
|
||||
obj-$(CONFIG_ZRAM) += zram.o
|
||||
|
||||
355
drivers/block/zram/zcomp.c
Normal file
355
drivers/block/zram/zcomp.c
Normal file
@@ -0,0 +1,355 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Sergey Senozhatsky.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/sched.h>
|
||||
|
||||
#include "zcomp.h"
|
||||
#include "zcomp_lzo.h"
|
||||
#ifdef CONFIG_ZRAM_LZ4_COMPRESS
|
||||
#include "zcomp_lz4.h"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* single zcomp_strm backend
|
||||
*/
|
||||
struct zcomp_strm_single {
|
||||
struct mutex strm_lock;
|
||||
struct zcomp_strm *zstrm;
|
||||
};
|
||||
|
||||
/*
|
||||
* multi zcomp_strm backend
|
||||
*/
|
||||
struct zcomp_strm_multi {
|
||||
/* protect strm list */
|
||||
spinlock_t strm_lock;
|
||||
/* max possible number of zstrm streams */
|
||||
int max_strm;
|
||||
/* number of available zstrm streams */
|
||||
int avail_strm;
|
||||
/* list of available strms */
|
||||
struct list_head idle_strm;
|
||||
wait_queue_head_t strm_wait;
|
||||
};
|
||||
|
||||
static struct zcomp_backend *backends[] = {
|
||||
&zcomp_lzo,
|
||||
#ifdef CONFIG_ZRAM_LZ4_COMPRESS
|
||||
&zcomp_lz4,
|
||||
#endif
|
||||
NULL
|
||||
};
|
||||
|
||||
static struct zcomp_backend *find_backend(const char *compress)
|
||||
{
|
||||
int i = 0;
|
||||
while (backends[i]) {
|
||||
if (sysfs_streq(compress, backends[i]->name))
|
||||
break;
|
||||
i++;
|
||||
}
|
||||
return backends[i];
|
||||
}
|
||||
|
||||
static void zcomp_strm_free(struct zcomp *comp, struct zcomp_strm *zstrm)
|
||||
{
|
||||
if (zstrm->private)
|
||||
comp->backend->destroy(zstrm->private);
|
||||
free_pages((unsigned long)zstrm->buffer, 1);
|
||||
kfree(zstrm);
|
||||
}
|
||||
|
||||
/*
|
||||
* allocate new zcomp_strm structure with ->private initialized by
|
||||
* backend, return NULL on error
|
||||
*/
|
||||
static struct zcomp_strm *zcomp_strm_alloc(struct zcomp *comp)
|
||||
{
|
||||
struct zcomp_strm *zstrm = kmalloc(sizeof(*zstrm), GFP_KERNEL);
|
||||
if (!zstrm)
|
||||
return NULL;
|
||||
|
||||
zstrm->private = comp->backend->create();
|
||||
/*
|
||||
* allocate 2 pages. 1 for compressed data, plus 1 extra for the
|
||||
* case when compressed size is larger than the original one
|
||||
*/
|
||||
zstrm->buffer = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, 1);
|
||||
if (!zstrm->private || !zstrm->buffer) {
|
||||
zcomp_strm_free(comp, zstrm);
|
||||
zstrm = NULL;
|
||||
}
|
||||
return zstrm;
|
||||
}
|
||||
|
||||
/*
|
||||
* get idle zcomp_strm or wait until other process release
|
||||
* (zcomp_strm_release()) one for us
|
||||
*/
|
||||
static struct zcomp_strm *zcomp_strm_multi_find(struct zcomp *comp)
|
||||
{
|
||||
struct zcomp_strm_multi *zs = comp->stream;
|
||||
struct zcomp_strm *zstrm;
|
||||
|
||||
while (1) {
|
||||
spin_lock(&zs->strm_lock);
|
||||
if (!list_empty(&zs->idle_strm)) {
|
||||
zstrm = list_entry(zs->idle_strm.next,
|
||||
struct zcomp_strm, list);
|
||||
list_del(&zstrm->list);
|
||||
spin_unlock(&zs->strm_lock);
|
||||
return zstrm;
|
||||
}
|
||||
/* zstrm streams limit reached, wait for idle stream */
|
||||
if (zs->avail_strm >= zs->max_strm) {
|
||||
spin_unlock(&zs->strm_lock);
|
||||
wait_event(zs->strm_wait, !list_empty(&zs->idle_strm));
|
||||
continue;
|
||||
}
|
||||
/* allocate new zstrm stream */
|
||||
zs->avail_strm++;
|
||||
spin_unlock(&zs->strm_lock);
|
||||
|
||||
zstrm = zcomp_strm_alloc(comp);
|
||||
if (!zstrm) {
|
||||
spin_lock(&zs->strm_lock);
|
||||
zs->avail_strm--;
|
||||
spin_unlock(&zs->strm_lock);
|
||||
wait_event(zs->strm_wait, !list_empty(&zs->idle_strm));
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return zstrm;
|
||||
}
|
||||
|
||||
/* add stream back to idle list and wake up waiter or free the stream */
|
||||
static void zcomp_strm_multi_release(struct zcomp *comp,
|
||||
struct zcomp_strm *zstrm)
|
||||
{
|
||||
struct zcomp_strm_multi *zs = comp->stream;
|
||||
|
||||
spin_lock(&zs->strm_lock);
|
||||
if (zs->avail_strm <= zs->max_strm) {
|
||||
list_add(&zstrm->list, &zs->idle_strm);
|
||||
spin_unlock(&zs->strm_lock);
|
||||
wake_up(&zs->strm_wait);
|
||||
return;
|
||||
}
|
||||
|
||||
zs->avail_strm--;
|
||||
spin_unlock(&zs->strm_lock);
|
||||
zcomp_strm_free(comp, zstrm);
|
||||
}
|
||||
|
||||
/* change max_strm limit */
|
||||
static bool zcomp_strm_multi_set_max_streams(struct zcomp *comp, int num_strm)
|
||||
{
|
||||
struct zcomp_strm_multi *zs = comp->stream;
|
||||
struct zcomp_strm *zstrm;
|
||||
|
||||
spin_lock(&zs->strm_lock);
|
||||
zs->max_strm = num_strm;
|
||||
/*
|
||||
* if user has lowered the limit and there are idle streams,
|
||||
* immediately free as much streams (and memory) as we can.
|
||||
*/
|
||||
while (zs->avail_strm > num_strm && !list_empty(&zs->idle_strm)) {
|
||||
zstrm = list_entry(zs->idle_strm.next,
|
||||
struct zcomp_strm, list);
|
||||
list_del(&zstrm->list);
|
||||
zcomp_strm_free(comp, zstrm);
|
||||
zs->avail_strm--;
|
||||
}
|
||||
spin_unlock(&zs->strm_lock);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void zcomp_strm_multi_destroy(struct zcomp *comp)
|
||||
{
|
||||
struct zcomp_strm_multi *zs = comp->stream;
|
||||
struct zcomp_strm *zstrm;
|
||||
|
||||
while (!list_empty(&zs->idle_strm)) {
|
||||
zstrm = list_entry(zs->idle_strm.next,
|
||||
struct zcomp_strm, list);
|
||||
list_del(&zstrm->list);
|
||||
zcomp_strm_free(comp, zstrm);
|
||||
}
|
||||
kfree(zs);
|
||||
}
|
||||
|
||||
static int zcomp_strm_multi_create(struct zcomp *comp, int max_strm)
|
||||
{
|
||||
struct zcomp_strm *zstrm;
|
||||
struct zcomp_strm_multi *zs;
|
||||
|
||||
comp->destroy = zcomp_strm_multi_destroy;
|
||||
comp->strm_find = zcomp_strm_multi_find;
|
||||
comp->strm_release = zcomp_strm_multi_release;
|
||||
comp->set_max_streams = zcomp_strm_multi_set_max_streams;
|
||||
zs = kmalloc(sizeof(struct zcomp_strm_multi), GFP_KERNEL);
|
||||
if (!zs)
|
||||
return -ENOMEM;
|
||||
|
||||
comp->stream = zs;
|
||||
spin_lock_init(&zs->strm_lock);
|
||||
INIT_LIST_HEAD(&zs->idle_strm);
|
||||
init_waitqueue_head(&zs->strm_wait);
|
||||
zs->max_strm = max_strm;
|
||||
zs->avail_strm = 1;
|
||||
|
||||
zstrm = zcomp_strm_alloc(comp);
|
||||
if (!zstrm) {
|
||||
kfree(zs);
|
||||
return -ENOMEM;
|
||||
}
|
||||
list_add(&zstrm->list, &zs->idle_strm);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct zcomp_strm *zcomp_strm_single_find(struct zcomp *comp)
|
||||
{
|
||||
struct zcomp_strm_single *zs = comp->stream;
|
||||
mutex_lock(&zs->strm_lock);
|
||||
return zs->zstrm;
|
||||
}
|
||||
|
||||
static void zcomp_strm_single_release(struct zcomp *comp,
|
||||
struct zcomp_strm *zstrm)
|
||||
{
|
||||
struct zcomp_strm_single *zs = comp->stream;
|
||||
mutex_unlock(&zs->strm_lock);
|
||||
}
|
||||
|
||||
static bool zcomp_strm_single_set_max_streams(struct zcomp *comp, int num_strm)
|
||||
{
|
||||
/* zcomp_strm_single support only max_comp_streams == 1 */
|
||||
return false;
|
||||
}
|
||||
|
||||
static void zcomp_strm_single_destroy(struct zcomp *comp)
|
||||
{
|
||||
struct zcomp_strm_single *zs = comp->stream;
|
||||
zcomp_strm_free(comp, zs->zstrm);
|
||||
kfree(zs);
|
||||
}
|
||||
|
||||
static int zcomp_strm_single_create(struct zcomp *comp)
|
||||
{
|
||||
struct zcomp_strm_single *zs;
|
||||
|
||||
comp->destroy = zcomp_strm_single_destroy;
|
||||
comp->strm_find = zcomp_strm_single_find;
|
||||
comp->strm_release = zcomp_strm_single_release;
|
||||
comp->set_max_streams = zcomp_strm_single_set_max_streams;
|
||||
zs = kmalloc(sizeof(struct zcomp_strm_single), GFP_KERNEL);
|
||||
if (!zs)
|
||||
return -ENOMEM;
|
||||
|
||||
comp->stream = zs;
|
||||
mutex_init(&zs->strm_lock);
|
||||
zs->zstrm = zcomp_strm_alloc(comp);
|
||||
if (!zs->zstrm) {
|
||||
kfree(zs);
|
||||
return -ENOMEM;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* show available compressors */
|
||||
ssize_t zcomp_available_show(const char *comp, char *buf)
|
||||
{
|
||||
ssize_t sz = 0;
|
||||
int i = 0;
|
||||
|
||||
while (backends[i]) {
|
||||
if (sysfs_streq(comp, backends[i]->name))
|
||||
sz += scnprintf(buf + sz, PAGE_SIZE - sz - 2,
|
||||
"[%s] ", backends[i]->name);
|
||||
else
|
||||
sz += scnprintf(buf + sz, PAGE_SIZE - sz - 2,
|
||||
"%s ", backends[i]->name);
|
||||
i++;
|
||||
}
|
||||
sz += scnprintf(buf + sz, PAGE_SIZE - sz, "\n");
|
||||
return sz;
|
||||
}
|
||||
|
||||
bool zcomp_set_max_streams(struct zcomp *comp, int num_strm)
|
||||
{
|
||||
return comp->set_max_streams(comp, num_strm);
|
||||
}
|
||||
|
||||
struct zcomp_strm *zcomp_strm_find(struct zcomp *comp)
|
||||
{
|
||||
return comp->strm_find(comp);
|
||||
}
|
||||
|
||||
void zcomp_strm_release(struct zcomp *comp, struct zcomp_strm *zstrm)
|
||||
{
|
||||
comp->strm_release(comp, zstrm);
|
||||
}
|
||||
|
||||
int zcomp_compress(struct zcomp *comp, struct zcomp_strm *zstrm,
|
||||
const unsigned char *src, size_t *dst_len)
|
||||
{
|
||||
return comp->backend->compress(src, zstrm->buffer, dst_len,
|
||||
zstrm->private);
|
||||
}
|
||||
|
||||
int zcomp_decompress(struct zcomp *comp, const unsigned char *src,
|
||||
size_t src_len, unsigned char *dst)
|
||||
{
|
||||
return comp->backend->decompress(src, src_len, dst);
|
||||
}
|
||||
|
||||
void zcomp_destroy(struct zcomp *comp)
|
||||
{
|
||||
comp->destroy(comp);
|
||||
kfree(comp);
|
||||
}
|
||||
|
||||
/*
|
||||
* search available compressors for requested algorithm.
|
||||
* allocate new zcomp and initialize it. return compressing
|
||||
* backend pointer or ERR_PTR if things went bad. ERR_PTR(-EINVAL)
|
||||
* if requested algorithm is not supported, ERR_PTR(-ENOMEM) in
|
||||
* case of allocation error.
|
||||
*/
|
||||
struct zcomp *zcomp_create(const char *compress, int max_strm)
|
||||
{
|
||||
struct zcomp *comp;
|
||||
struct zcomp_backend *backend;
|
||||
|
||||
backend = find_backend(compress);
|
||||
pr_info("%s, comress:%s\n", __func__, compress);
|
||||
if (!backend)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
comp = kzalloc(sizeof(struct zcomp), GFP_KERNEL);
|
||||
if (!comp)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
comp->backend = backend;
|
||||
if (max_strm > 1)
|
||||
zcomp_strm_multi_create(comp, max_strm);
|
||||
else
|
||||
zcomp_strm_single_create(comp);
|
||||
if (!comp->stream) {
|
||||
kfree(comp);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
return comp;
|
||||
}
|
||||
68
drivers/block/zram/zcomp.h
Normal file
68
drivers/block/zram/zcomp.h
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Sergey Senozhatsky.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#ifndef _ZCOMP_H_
|
||||
#define _ZCOMP_H_
|
||||
|
||||
#include <linux/mutex.h>
|
||||
|
||||
struct zcomp_strm {
|
||||
/* compression/decompression buffer */
|
||||
void *buffer;
|
||||
/*
|
||||
* The private data of the compression stream, only compression
|
||||
* stream backend can touch this (e.g. compression algorithm
|
||||
* working memory)
|
||||
*/
|
||||
void *private;
|
||||
/* used in multi stream backend, protected by backend strm_lock */
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
/* static compression backend */
|
||||
struct zcomp_backend {
|
||||
int (*compress)(const unsigned char *src, unsigned char *dst,
|
||||
size_t *dst_len, void *private);
|
||||
|
||||
int (*decompress)(const unsigned char *src, size_t src_len,
|
||||
unsigned char *dst);
|
||||
|
||||
void * (*create)(void);
|
||||
void (*destroy)(void *private);
|
||||
|
||||
const char *name;
|
||||
};
|
||||
|
||||
/* dynamic per-device compression frontend */
|
||||
struct zcomp {
|
||||
void *stream;
|
||||
struct zcomp_backend *backend;
|
||||
|
||||
struct zcomp_strm * (*strm_find)(struct zcomp *comp);
|
||||
void (*strm_release)(struct zcomp *comp, struct zcomp_strm *zstrm);
|
||||
bool (*set_max_streams)(struct zcomp *comp, int num_strm);
|
||||
void (*destroy)(struct zcomp *comp);
|
||||
};
|
||||
|
||||
ssize_t zcomp_available_show(const char *comp, char *buf);
|
||||
|
||||
struct zcomp *zcomp_create(const char *comp, int max_strm);
|
||||
void zcomp_destroy(struct zcomp *comp);
|
||||
|
||||
struct zcomp_strm *zcomp_strm_find(struct zcomp *comp);
|
||||
void zcomp_strm_release(struct zcomp *comp, struct zcomp_strm *zstrm);
|
||||
|
||||
int zcomp_compress(struct zcomp *comp, struct zcomp_strm *zstrm,
|
||||
const unsigned char *src, size_t *dst_len);
|
||||
|
||||
int zcomp_decompress(struct zcomp *comp, const unsigned char *src,
|
||||
size_t src_len, unsigned char *dst);
|
||||
|
||||
bool zcomp_set_max_streams(struct zcomp *comp, int num_strm);
|
||||
#endif /* _ZCOMP_H_ */
|
||||
47
drivers/block/zram/zcomp_lz4.c
Normal file
47
drivers/block/zram/zcomp_lz4.c
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Sergey Senozhatsky.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/lz4.h>
|
||||
|
||||
#include "zcomp_lz4.h"
|
||||
|
||||
static void *zcomp_lz4_create(void)
|
||||
{
|
||||
return kzalloc(LZ4_MEM_COMPRESS, GFP_KERNEL);
|
||||
}
|
||||
|
||||
static void zcomp_lz4_destroy(void *private)
|
||||
{
|
||||
kfree(private);
|
||||
}
|
||||
|
||||
static int zcomp_lz4_compress(const unsigned char *src, unsigned char *dst,
|
||||
size_t *dst_len, void *private)
|
||||
{
|
||||
/* return : Success if return 0 */
|
||||
return lz4_compress(src, PAGE_SIZE, dst, dst_len, private);
|
||||
}
|
||||
|
||||
static int zcomp_lz4_decompress(const unsigned char *src, size_t src_len,
|
||||
unsigned char *dst)
|
||||
{
|
||||
size_t dst_len = PAGE_SIZE;
|
||||
/* return : Success if return 0 */
|
||||
return lz4_decompress_unknownoutputsize(src, src_len, dst, &dst_len);
|
||||
}
|
||||
|
||||
struct zcomp_backend zcomp_lz4 = {
|
||||
.compress = zcomp_lz4_compress,
|
||||
.decompress = zcomp_lz4_decompress,
|
||||
.create = zcomp_lz4_create,
|
||||
.destroy = zcomp_lz4_destroy,
|
||||
.name = "lz4",
|
||||
};
|
||||
17
drivers/block/zram/zcomp_lz4.h
Normal file
17
drivers/block/zram/zcomp_lz4.h
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Sergey Senozhatsky.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#ifndef _ZCOMP_LZ4_H_
|
||||
#define _ZCOMP_LZ4_H_
|
||||
|
||||
#include "zcomp.h"
|
||||
|
||||
extern struct zcomp_backend zcomp_lz4;
|
||||
|
||||
#endif /* _ZCOMP_LZ4_H_ */
|
||||
47
drivers/block/zram/zcomp_lzo.c
Normal file
47
drivers/block/zram/zcomp_lzo.c
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Sergey Senozhatsky.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/lzo.h>
|
||||
|
||||
#include "zcomp_lzo.h"
|
||||
|
||||
static void *lzo_create(void)
|
||||
{
|
||||
return kzalloc(LZO1X_MEM_COMPRESS, GFP_KERNEL);
|
||||
}
|
||||
|
||||
static void lzo_destroy(void *private)
|
||||
{
|
||||
kfree(private);
|
||||
}
|
||||
|
||||
static int lzo_compress(const unsigned char *src, unsigned char *dst,
|
||||
size_t *dst_len, void *private)
|
||||
{
|
||||
int ret = lzo1x_1_compress(src, PAGE_SIZE, dst, dst_len, private);
|
||||
return ret == LZO_E_OK ? 0 : ret;
|
||||
}
|
||||
|
||||
static int lzo_decompress(const unsigned char *src, size_t src_len,
|
||||
unsigned char *dst)
|
||||
{
|
||||
size_t dst_len = PAGE_SIZE;
|
||||
int ret = lzo1x_decompress_safe(src, src_len, dst, &dst_len);
|
||||
return ret == LZO_E_OK ? 0 : ret;
|
||||
}
|
||||
|
||||
struct zcomp_backend zcomp_lzo = {
|
||||
.compress = lzo_compress,
|
||||
.decompress = lzo_decompress,
|
||||
.create = lzo_create,
|
||||
.destroy = lzo_destroy,
|
||||
.name = "lzo",
|
||||
};
|
||||
17
drivers/block/zram/zcomp_lzo.h
Normal file
17
drivers/block/zram/zcomp_lzo.h
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Sergey Senozhatsky.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#ifndef _ZCOMP_LZO_H_
|
||||
#define _ZCOMP_LZO_H_
|
||||
|
||||
#include "zcomp.h"
|
||||
|
||||
extern struct zcomp_backend zcomp_lzo;
|
||||
|
||||
#endif /* _ZCOMP_LZO_H_ */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -16,9 +16,10 @@
|
||||
#define _ZRAM_DRV_H_
|
||||
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/zsmalloc.h>
|
||||
|
||||
#include "zcomp.h"
|
||||
|
||||
/*
|
||||
* Some arbitrary value. This is just to catch
|
||||
* invalid value for num_devices module parameter.
|
||||
@@ -42,7 +43,6 @@ static const size_t max_zpage_size = PAGE_SIZE / 4 * 3;
|
||||
/*-- End of configurable params */
|
||||
|
||||
#define SECTOR_SHIFT 9
|
||||
#define SECTOR_SIZE (1 << SECTOR_SHIFT)
|
||||
#define SECTORS_PER_PAGE_SHIFT (PAGE_SHIFT - SECTOR_SHIFT)
|
||||
#define SECTORS_PER_PAGE (1 << SECTORS_PER_PAGE_SHIFT)
|
||||
#define ZRAM_LOGICAL_BLOCK_SHIFT 12
|
||||
@@ -50,10 +50,24 @@ static const size_t max_zpage_size = PAGE_SIZE / 4 * 3;
|
||||
#define ZRAM_SECTOR_PER_LOGICAL_BLOCK \
|
||||
(1 << (ZRAM_LOGICAL_BLOCK_SHIFT - SECTOR_SHIFT))
|
||||
|
||||
/* Flags for zram pages (table[page_no].flags) */
|
||||
|
||||
/*
|
||||
* The lower ZRAM_FLAG_SHIFT bits of table.value is for
|
||||
* object size (excluding header), the higher bits is for
|
||||
* zram_pageflags.
|
||||
*
|
||||
* zram is mainly used for memory efficiency so we want to keep memory
|
||||
* footprint small so we can squeeze size and flags into a field.
|
||||
* The lower ZRAM_FLAG_SHIFT bits is for object size (excluding header),
|
||||
* the higher bits is for zram_pageflags.
|
||||
*/
|
||||
#define ZRAM_FLAG_SHIFT 24
|
||||
|
||||
/* Flags for zram pages (table[page_no].value) */
|
||||
enum zram_pageflags {
|
||||
/* Page consists entirely of zeros */
|
||||
ZRAM_ZERO,
|
||||
ZRAM_ZERO = ZRAM_FLAG_SHIFT + 1,
|
||||
ZRAM_ACCESS, /* page in now accessed */
|
||||
|
||||
__NR_ZRAM_PAGEFLAGS,
|
||||
};
|
||||
@@ -61,41 +75,35 @@ enum zram_pageflags {
|
||||
/*-- Data structures */
|
||||
|
||||
/* Allocated for each disk page */
|
||||
struct table {
|
||||
struct zram_table_entry {
|
||||
unsigned long handle;
|
||||
u16 size; /* object size (excluding header) */
|
||||
u8 count; /* object ref count (not yet used) */
|
||||
u8 flags;
|
||||
} __aligned(4);
|
||||
unsigned long value;
|
||||
};
|
||||
|
||||
struct zram_stats {
|
||||
atomic64_t compr_size; /* compressed size of pages stored */
|
||||
atomic64_t compr_data_size; /* compressed size of pages stored */
|
||||
atomic64_t num_reads; /* failed + successful */
|
||||
atomic64_t num_writes; /* --do-- */
|
||||
atomic64_t failed_reads; /* should NEVER! happen */
|
||||
atomic64_t failed_reads; /* can happen when memory is too low */
|
||||
atomic64_t failed_writes; /* can happen when memory is too low */
|
||||
atomic64_t invalid_io; /* non-page-aligned I/O requests */
|
||||
atomic64_t notify_free; /* no. of swap slot free notifications */
|
||||
atomic_t pages_zero; /* no. of zero filled pages */
|
||||
atomic_t pages_stored; /* no. of pages currently stored */
|
||||
atomic_t good_compress; /* % of pages with compression ratio<=50% */
|
||||
atomic_t bad_compress; /* % of pages with compression ratio>=75% */
|
||||
atomic64_t zero_pages; /* no. of zero filled pages */
|
||||
atomic64_t pages_stored; /* no. of pages currently stored */
|
||||
atomic_long_t max_used_pages; /* no. of maximum pages stored */
|
||||
};
|
||||
|
||||
struct zram_meta {
|
||||
rwlock_t tb_lock; /* protect table */
|
||||
void *compress_workmem;
|
||||
void *compress_buffer;
|
||||
struct table *table;
|
||||
struct zram_table_entry *table;
|
||||
struct zs_pool *mem_pool;
|
||||
struct mutex buffer_lock; /* protect compress buffers */
|
||||
};
|
||||
|
||||
struct zram {
|
||||
struct zram_meta *meta;
|
||||
struct request_queue *queue;
|
||||
struct gendisk *disk;
|
||||
int init_done;
|
||||
struct zcomp *comp;
|
||||
|
||||
/* Prevent concurrent execution of device init, reset and R/W request */
|
||||
struct rw_semaphore init_lock;
|
||||
/*
|
||||
@@ -103,7 +111,13 @@ struct zram {
|
||||
* we can store in a disk.
|
||||
*/
|
||||
u64 disksize; /* bytes */
|
||||
|
||||
int max_comp_streams;
|
||||
struct zram_stats stats;
|
||||
/*
|
||||
* the number of pages zram can consume for storing compressed data
|
||||
*/
|
||||
unsigned long limit_pages;
|
||||
|
||||
char compressor[10];
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -127,21 +127,33 @@ enum {
|
||||
#endif /* #else #if defined(CONFIG_HOTPLUG_CPU) || !defined(MODULE) */
|
||||
#ifdef CONFIG_HOTPLUG_CPU
|
||||
extern int register_cpu_notifier(struct notifier_block *nb);
|
||||
extern int __register_cpu_notifier(struct notifier_block *nb);
|
||||
extern void unregister_cpu_notifier(struct notifier_block *nb);
|
||||
extern void __unregister_cpu_notifier(struct notifier_block *nb);
|
||||
#else
|
||||
|
||||
#ifndef MODULE
|
||||
extern int register_cpu_notifier(struct notifier_block *nb);
|
||||
extern int __register_cpu_notifier(struct notifier_block *nb);
|
||||
#else
|
||||
static inline int register_cpu_notifier(struct notifier_block *nb)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int __register_cpu_notifier(struct notifier_block *nb)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline void unregister_cpu_notifier(struct notifier_block *nb)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void __unregister_cpu_notifier(struct notifier_block *nb)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
int cpu_up(unsigned int cpu);
|
||||
@@ -149,6 +161,9 @@ void notify_cpu_starting(unsigned int cpu);
|
||||
extern void cpu_maps_update_begin(void);
|
||||
extern void cpu_maps_update_done(void);
|
||||
|
||||
#define cpu_notifier_register_begin cpu_maps_update_begin
|
||||
#define cpu_notifier_register_done cpu_maps_update_done
|
||||
|
||||
#else /* CONFIG_SMP */
|
||||
|
||||
#define cpu_notifier(fn, pri) do { (void)(fn); } while (0)
|
||||
|
||||
106
include/linux/zpool.h
Normal file
106
include/linux/zpool.h
Normal file
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* zpool memory storage api
|
||||
*
|
||||
* Copyright (C) 2014 Dan Streetman
|
||||
*
|
||||
* This is a common frontend for the zbud and zsmalloc memory
|
||||
* storage pool implementations. Typically, this is used to
|
||||
* store compressed memory.
|
||||
*/
|
||||
|
||||
#ifndef _ZPOOL_H_
|
||||
#define _ZPOOL_H_
|
||||
|
||||
struct zpool;
|
||||
|
||||
struct zpool_ops {
|
||||
int (*evict)(struct zpool *pool, unsigned long handle);
|
||||
};
|
||||
|
||||
/*
|
||||
* Control how a handle is mapped. It will be ignored if the
|
||||
* implementation does not support it. Its use is optional.
|
||||
* Note that this does not refer to memory protection, it
|
||||
* refers to how the memory will be copied in/out if copying
|
||||
* is necessary during mapping; read-write is the safest as
|
||||
* it copies the existing memory in on map, and copies the
|
||||
* changed memory back out on unmap. Write-only does not copy
|
||||
* in the memory and should only be used for initialization.
|
||||
* If in doubt, use ZPOOL_MM_DEFAULT which is read-write.
|
||||
*/
|
||||
enum zpool_mapmode {
|
||||
ZPOOL_MM_RW, /* normal read-write mapping */
|
||||
ZPOOL_MM_RO, /* read-only (no copy-out at unmap time) */
|
||||
ZPOOL_MM_WO, /* write-only (no copy-in at map time) */
|
||||
|
||||
ZPOOL_MM_DEFAULT = ZPOOL_MM_RW
|
||||
};
|
||||
|
||||
struct zpool *zpool_create_pool(char *type, gfp_t gfp, struct zpool_ops *ops);
|
||||
|
||||
char *zpool_get_type(struct zpool *pool);
|
||||
|
||||
void zpool_destroy_pool(struct zpool *pool);
|
||||
|
||||
int zpool_malloc(struct zpool *pool, size_t size, gfp_t gfp,
|
||||
unsigned long *handle);
|
||||
|
||||
void zpool_free(struct zpool *pool, unsigned long handle);
|
||||
|
||||
int zpool_shrink(struct zpool *pool, unsigned int pages,
|
||||
unsigned int *reclaimed);
|
||||
|
||||
void *zpool_map_handle(struct zpool *pool, unsigned long handle,
|
||||
enum zpool_mapmode mm);
|
||||
|
||||
void zpool_unmap_handle(struct zpool *pool, unsigned long handle);
|
||||
|
||||
u64 zpool_get_total_size(struct zpool *pool);
|
||||
|
||||
|
||||
/**
|
||||
* struct zpool_driver - driver implementation for zpool
|
||||
* @type: name of the driver.
|
||||
* @list: entry in the list of zpool drivers.
|
||||
* @create: create a new pool.
|
||||
* @destroy: destroy a pool.
|
||||
* @malloc: allocate mem from a pool.
|
||||
* @free: free mem from a pool.
|
||||
* @shrink: shrink the pool.
|
||||
* @map: map a handle.
|
||||
* @unmap: unmap a handle.
|
||||
* @total_size: get total size of a pool.
|
||||
*
|
||||
* This is created by a zpool implementation and registered
|
||||
* with zpool.
|
||||
*/
|
||||
struct zpool_driver {
|
||||
char *type;
|
||||
struct module *owner;
|
||||
atomic_t refcount;
|
||||
struct list_head list;
|
||||
|
||||
void * (*create)(gfp_t gfp, struct zpool_ops *ops);
|
||||
void (*destroy)(void *pool);
|
||||
|
||||
int (*malloc)(void *pool, size_t size, gfp_t gfp,
|
||||
unsigned long *handle);
|
||||
void (*free)(void *pool, unsigned long handle);
|
||||
|
||||
int (*shrink)(void *pool, unsigned int pages,
|
||||
unsigned int *reclaimed);
|
||||
|
||||
void * (*map)(void *pool, unsigned long handle,
|
||||
enum zpool_mapmode mm);
|
||||
void (*unmap)(void *pool, unsigned long handle);
|
||||
|
||||
u64 (*total_size)(void *pool);
|
||||
};
|
||||
|
||||
void zpool_register_driver(struct zpool_driver *driver);
|
||||
|
||||
int zpool_unregister_driver(struct zpool_driver *driver);
|
||||
|
||||
int zpool_evict(void *pool, unsigned long handle);
|
||||
|
||||
#endif
|
||||
@@ -46,6 +46,6 @@ void *zs_map_object(struct zs_pool *pool, unsigned long handle,
|
||||
enum zs_mapmode mm);
|
||||
void zs_unmap_object(struct zs_pool *pool, unsigned long handle);
|
||||
|
||||
u64 zs_get_total_size_bytes(struct zs_pool *pool);
|
||||
unsigned long zs_get_total_pages(struct zs_pool *pool);
|
||||
|
||||
#endif
|
||||
|
||||
14
kernel/cpu.c
14
kernel/cpu.c
@@ -34,11 +34,13 @@ void cpu_maps_update_begin(void)
|
||||
{
|
||||
mutex_lock(&cpu_add_remove_lock);
|
||||
}
|
||||
EXPORT_SYMBOL(cpu_notifier_register_begin);
|
||||
|
||||
void cpu_maps_update_done(void)
|
||||
{
|
||||
mutex_unlock(&cpu_add_remove_lock);
|
||||
}
|
||||
EXPORT_SYMBOL(cpu_notifier_register_done);
|
||||
|
||||
static RAW_NOTIFIER_HEAD(cpu_chain);
|
||||
|
||||
@@ -166,6 +168,11 @@ int __ref register_cpu_notifier(struct notifier_block *nb)
|
||||
return ret;
|
||||
}
|
||||
|
||||
int __ref __register_cpu_notifier(struct notifier_block *nb)
|
||||
{
|
||||
return raw_notifier_chain_register(&cpu_chain, nb);
|
||||
}
|
||||
|
||||
static int __cpu_notify(unsigned long val, void *v, int nr_to_call,
|
||||
int *nr_calls)
|
||||
{
|
||||
@@ -189,6 +196,7 @@ static void cpu_notify_nofail(unsigned long val, void *v)
|
||||
BUG_ON(cpu_notify(val, v));
|
||||
}
|
||||
EXPORT_SYMBOL(register_cpu_notifier);
|
||||
EXPORT_SYMBOL(__register_cpu_notifier);
|
||||
|
||||
void __ref unregister_cpu_notifier(struct notifier_block *nb)
|
||||
{
|
||||
@@ -198,6 +206,12 @@ void __ref unregister_cpu_notifier(struct notifier_block *nb)
|
||||
}
|
||||
EXPORT_SYMBOL(unregister_cpu_notifier);
|
||||
|
||||
void __ref __unregister_cpu_notifier(struct notifier_block *nb)
|
||||
{
|
||||
raw_notifier_chain_unregister(&cpu_chain, nb);
|
||||
}
|
||||
EXPORT_SYMBOL(__unregister_cpu_notifier);
|
||||
|
||||
/**
|
||||
* clear_tasks_mm_cpumask - Safely clear tasks' mm_cpumask for a CPU
|
||||
* @cpu: a CPU id
|
||||
|
||||
@@ -139,6 +139,9 @@ static int lz4_uncompress(const char *source, char *dest, int osize)
|
||||
/* Error: request to write beyond destination buffer */
|
||||
if (cpy > oend)
|
||||
goto _output_error;
|
||||
if ((ref + COPYLENGTH) > oend ||
|
||||
(op + COPYLENGTH) > oend)
|
||||
goto _output_error;
|
||||
LZ4_SECURECOPY(ref, op, (oend - COPYLENGTH));
|
||||
while (op < cpy)
|
||||
*op++ = *ref++;
|
||||
|
||||
148
mm/zsmalloc.c
148
mm/zsmalloc.c
@@ -92,6 +92,7 @@
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/zsmalloc.h>
|
||||
#include <linux/zpool.h>
|
||||
|
||||
/*
|
||||
* This must be power of 2 and greater than of equal to sizeof(link_free).
|
||||
@@ -198,9 +199,6 @@ struct size_class {
|
||||
|
||||
spinlock_t lock;
|
||||
|
||||
/* stats */
|
||||
u64 pages_allocated;
|
||||
|
||||
struct page *fullness_list[_ZS_NR_FULLNESS_GROUPS];
|
||||
};
|
||||
|
||||
@@ -219,6 +217,7 @@ struct zs_pool {
|
||||
struct size_class size_class[ZS_SIZE_CLASSES];
|
||||
|
||||
gfp_t flags; /* allocation flags used when growing pool */
|
||||
atomic_long_t pages_allocated;
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -240,6 +239,82 @@ struct mapping_area {
|
||||
enum zs_mapmode vm_mm; /* mapping mode */
|
||||
};
|
||||
|
||||
/* zpool driver */
|
||||
|
||||
#ifdef CONFIG_ZPOOL
|
||||
|
||||
static void *zs_zpool_create(gfp_t gfp, struct zpool_ops *zpool_ops)
|
||||
{
|
||||
return zs_create_pool(gfp);
|
||||
}
|
||||
|
||||
static void zs_zpool_destroy(void *pool)
|
||||
{
|
||||
zs_destroy_pool(pool);
|
||||
}
|
||||
|
||||
static int zs_zpool_malloc(void *pool, size_t size, gfp_t gfp,
|
||||
unsigned long *handle)
|
||||
{
|
||||
*handle = zs_malloc(pool, size);
|
||||
return *handle ? 0 : -1;
|
||||
}
|
||||
static void zs_zpool_free(void *pool, unsigned long handle)
|
||||
{
|
||||
zs_free(pool, handle);
|
||||
}
|
||||
|
||||
static int zs_zpool_shrink(void *pool, unsigned int pages,
|
||||
unsigned int *reclaimed)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static void *zs_zpool_map(void *pool, unsigned long handle,
|
||||
enum zpool_mapmode mm)
|
||||
{
|
||||
enum zs_mapmode zs_mm;
|
||||
|
||||
switch (mm) {
|
||||
case ZPOOL_MM_RO:
|
||||
zs_mm = ZS_MM_RO;
|
||||
break;
|
||||
case ZPOOL_MM_WO:
|
||||
zs_mm = ZS_MM_WO;
|
||||
break;
|
||||
case ZPOOL_MM_RW: /* fallthru */
|
||||
default:
|
||||
zs_mm = ZS_MM_RW;
|
||||
break;
|
||||
}
|
||||
|
||||
return zs_map_object(pool, handle, zs_mm);
|
||||
}
|
||||
static void zs_zpool_unmap(void *pool, unsigned long handle)
|
||||
{
|
||||
zs_unmap_object(pool, handle);
|
||||
}
|
||||
|
||||
static u64 zs_zpool_total_size(void *pool)
|
||||
{
|
||||
return zs_get_total_pages(pool) << PAGE_SHIFT;
|
||||
}
|
||||
|
||||
static struct zpool_driver zs_zpool_driver = {
|
||||
.type = "zsmalloc",
|
||||
.owner = THIS_MODULE,
|
||||
.create = zs_zpool_create,
|
||||
.destroy = zs_zpool_destroy,
|
||||
.malloc = zs_zpool_malloc,
|
||||
.free = zs_zpool_free,
|
||||
.shrink = zs_zpool_shrink,
|
||||
.map = zs_zpool_map,
|
||||
.unmap = zs_zpool_unmap,
|
||||
.total_size = zs_zpool_total_size,
|
||||
};
|
||||
|
||||
MODULE_ALIAS("zpool-zsmalloc");
|
||||
#endif /* CONFIG_ZPOOL */
|
||||
|
||||
/* per-cpu VM mapping areas for zspage accesses that cross page boundaries */
|
||||
static DEFINE_PER_CPU(struct mapping_area, zs_map_area);
|
||||
@@ -553,7 +628,7 @@ static void init_zspage(struct page *first_page, struct size_class *class)
|
||||
while (page) {
|
||||
struct page *next_page;
|
||||
struct link_free *link;
|
||||
unsigned int i, objs_on_page;
|
||||
unsigned int i = 1;
|
||||
|
||||
/*
|
||||
* page->index stores offset of first object starting
|
||||
@@ -566,14 +641,10 @@ static void init_zspage(struct page *first_page, struct size_class *class)
|
||||
|
||||
link = (struct link_free *)kmap_atomic(page) +
|
||||
off / sizeof(*link);
|
||||
objs_on_page = (PAGE_SIZE - off) / class->size;
|
||||
|
||||
for (i = 1; i <= objs_on_page; i++) {
|
||||
off += class->size;
|
||||
if (off < PAGE_SIZE) {
|
||||
link->next = obj_location_to_handle(page, i);
|
||||
link += class->size / sizeof(*link);
|
||||
}
|
||||
while ((off += class->size) < PAGE_SIZE) {
|
||||
link->next = obj_location_to_handle(page, i++);
|
||||
link += class->size / sizeof(*link);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -585,7 +656,7 @@ static void init_zspage(struct page *first_page, struct size_class *class)
|
||||
link->next = obj_location_to_handle(next_page, 0);
|
||||
kunmap_atomic(link);
|
||||
page = next_page;
|
||||
off = (off + class->size) % PAGE_SIZE;
|
||||
off %= PAGE_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -690,7 +761,7 @@ static inline void __zs_cpu_down(struct mapping_area *area)
|
||||
static inline void *__zs_map_object(struct mapping_area *area,
|
||||
struct page *pages[2], int off, int size)
|
||||
{
|
||||
BUG_ON(map_vm_area(area->vm, PAGE_KERNEL, &pages));
|
||||
BUG_ON(map_vm_area(area->vm, PAGE_KERNEL, pages));
|
||||
area->vm_addr = area->vm->addr;
|
||||
return area->vm_addr + off;
|
||||
}
|
||||
@@ -814,21 +885,40 @@ static void zs_exit(void)
|
||||
{
|
||||
int cpu;
|
||||
|
||||
#ifdef CONFIG_ZPOOL
|
||||
zpool_unregister_driver(&zs_zpool_driver);
|
||||
#endif
|
||||
|
||||
cpu_notifier_register_begin();
|
||||
|
||||
for_each_online_cpu(cpu)
|
||||
zs_cpu_notifier(NULL, CPU_DEAD, (void *)(long)cpu);
|
||||
unregister_cpu_notifier(&zs_cpu_nb);
|
||||
__unregister_cpu_notifier(&zs_cpu_nb);
|
||||
|
||||
cpu_notifier_register_done();
|
||||
}
|
||||
|
||||
static int zs_init(void)
|
||||
{
|
||||
int cpu, ret;
|
||||
|
||||
register_cpu_notifier(&zs_cpu_nb);
|
||||
cpu_notifier_register_begin();
|
||||
|
||||
__register_cpu_notifier(&zs_cpu_nb);
|
||||
for_each_online_cpu(cpu) {
|
||||
ret = zs_cpu_notifier(NULL, CPU_UP_PREPARE, (void *)(long)cpu);
|
||||
if (notifier_to_errno(ret))
|
||||
if (notifier_to_errno(ret)) {
|
||||
cpu_notifier_register_done();
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
cpu_notifier_register_done();
|
||||
|
||||
#ifdef CONFIG_ZPOOL
|
||||
zpool_register_driver(&zs_zpool_driver);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
fail:
|
||||
zs_exit();
|
||||
@@ -932,8 +1022,9 @@ unsigned long zs_malloc(struct zs_pool *pool, size_t size)
|
||||
return 0;
|
||||
|
||||
set_zspage_mapping(first_page, class->index, ZS_EMPTY);
|
||||
atomic_long_add(class->pages_per_zspage,
|
||||
&pool->pages_allocated);
|
||||
spin_lock(&class->lock);
|
||||
class->pages_allocated += class->pages_per_zspage;
|
||||
}
|
||||
|
||||
obj = (unsigned long)first_page->freelist;
|
||||
@@ -986,14 +1077,13 @@ void zs_free(struct zs_pool *pool, unsigned long obj)
|
||||
|
||||
first_page->inuse--;
|
||||
fullness = fix_fullness_group(pool, first_page);
|
||||
|
||||
if (fullness == ZS_EMPTY)
|
||||
class->pages_allocated -= class->pages_per_zspage;
|
||||
|
||||
spin_unlock(&class->lock);
|
||||
|
||||
if (fullness == ZS_EMPTY)
|
||||
if (fullness == ZS_EMPTY) {
|
||||
atomic_long_sub(class->pages_per_zspage,
|
||||
&pool->pages_allocated);
|
||||
free_zspage(first_page);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(zs_free);
|
||||
|
||||
@@ -1071,7 +1161,7 @@ void zs_unmap_object(struct zs_pool *pool, unsigned long handle)
|
||||
class = &pool->size_class[class_idx];
|
||||
off = obj_idx_to_offset(page, obj_idx, class->size);
|
||||
|
||||
area = &__get_cpu_var(zs_map_area);
|
||||
area = this_cpu_ptr(&zs_map_area);
|
||||
if (off + class->size <= PAGE_SIZE)
|
||||
kunmap_atomic(area->vm_addr);
|
||||
else {
|
||||
@@ -1087,17 +1177,11 @@ void zs_unmap_object(struct zs_pool *pool, unsigned long handle)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(zs_unmap_object);
|
||||
|
||||
u64 zs_get_total_size_bytes(struct zs_pool *pool)
|
||||
unsigned long zs_get_total_pages(struct zs_pool *pool)
|
||||
{
|
||||
int i;
|
||||
u64 npages = 0;
|
||||
|
||||
for (i = 0; i < ZS_SIZE_CLASSES; i++)
|
||||
npages += pool->size_class[i].pages_allocated;
|
||||
|
||||
return npages << PAGE_SHIFT;
|
||||
return atomic_long_read(&pool->pages_allocated);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(zs_get_total_size_bytes);
|
||||
EXPORT_SYMBOL_GPL(zs_get_total_pages);
|
||||
|
||||
module_init(zs_init);
|
||||
module_exit(zs_exit);
|
||||
|
||||
Reference in New Issue
Block a user