Creates per-pool sysfs nodes: /sys/kernel/vm/zcache/pool<id>/ (<id> = 0, 1, 2, ...) to export following statistics: - orig_data_size: Uncompressed worth of data stored in the pool. - memlimit: Memory limit of the pool. This also allows changing it at runtime (default: 10% of RAM). If memlimit is set to a value smaller than the current number of page stored, then excess pages are not freed immediately but further puts are blocked till sufficient number of pages are flushed/freed. Signed-off-by: Nitin Gupta <ngupta@xxxxxxxxxx> --- drivers/staging/zram/zcache_drv.c | 132 ++++++++++++++++++++++++++++++++++++- drivers/staging/zram/zcache_drv.h | 8 ++ 2 files changed, 137 insertions(+), 3 deletions(-) diff --git a/drivers/staging/zram/zcache_drv.c b/drivers/staging/zram/zcache_drv.c index 160c172..f680f19 100644 --- a/drivers/staging/zram/zcache_drv.c +++ b/drivers/staging/zram/zcache_drv.c @@ -440,6 +440,85 @@ static void zcache_free_inode_pages(struct zcache_inode_rb *znode) } while (count == FREE_BATCH); } +#ifdef CONFIG_SYSFS + +#define ZCACHE_POOL_ATTR_RO(_name) \ + static struct kobj_attribute _name##_attr = __ATTR_RO(_name) + +#define ZCACHE_POOL_ATTR(_name) \ + static struct kobj_attribute _name##_attr = \ + __ATTR(_name, 0644, _name##_show, _name##_store) + +static struct zcache_pool *zcache_kobj_to_pool(struct kobject *kobj) +{ + int i; + + spin_lock(&zcache->pool_lock); + for (i = 0; i < MAX_ZCACHE_POOLS; i++) + if (zcache->pools[i]->kobj == kobj) + break; + spin_unlock(&zcache->pool_lock); + + return zcache->pools[i]; +} + +static ssize_t orig_data_size_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct zcache_pool *zpool = zcache_kobj_to_pool(kobj); + + return sprintf(buf, "%llu\n", zcache_get_stat( + zpool, ZPOOL_STAT_PAGES_STORED) << PAGE_SHIFT); +} +ZCACHE_POOL_ATTR_RO(orig_data_size); + +static void memlimit_sysfs_common(struct kobject *kobj, u64 *value, int store) +{ + struct zcache_pool *zpool = zcache_kobj_to_pool(kobj); + + if (store) + zcache_set_memlimit(zpool, *value); + else + *value = zcache_get_memlimit(zpool); +} + +static ssize_t memlimit_store(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, size_t len) +{ + int ret; + u64 memlimit; + + ret = strict_strtoull(buf, 10, &memlimit); + if (ret) + return ret; + + memlimit &= PAGE_MASK; + memlimit_sysfs_common(kobj, &memlimit, 1); + + return len; +} + +static ssize_t memlimit_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + u64 memlimit; + + memlimit_sysfs_common(kobj, &memlimit, 0); + return sprintf(buf, "%llu\n", memlimit); +} +ZCACHE_POOL_ATTR(memlimit); + +static struct attribute *zcache_pool_attrs[] = { + &orig_data_size_attr.attr, + &memlimit_attr.attr, + NULL, +}; + +static struct attribute_group zcache_pool_attr_group = { + .attrs = zcache_pool_attrs, +}; +#endif /* CONFIG_SYSFS */ + /* * cleancache_ops.init_fs * @@ -451,7 +530,8 @@ static void zcache_free_inode_pages(struct zcache_inode_rb *znode) */ static int zcache_init_fs(size_t pagesize) { - int ret; + int ret, pool_id; + struct zcache_pool *zpool = NULL; /* * pagesize parameter probably makes sense only for Xen's @@ -469,14 +549,38 @@ static int zcache_init_fs(size_t pagesize) goto out; } - ret = zcache_create_pool(); - if (ret < 0) { + pool_id = zcache_create_pool(); + if (pool_id < 0) { pr_info("Failed to create new pool\n"); ret = -ENOMEM; goto out; } + zpool = zcache->pools[pool_id]; + +#ifdef CONFIG_SYSFS + snprintf(zpool->name, MAX_ZPOOL_NAME_LEN, "pool%d", pool_id); + + /* Create /sys/kernel/mm/zcache/pool<id> (<id> = 0, 1, ...) */ + zpool->kobj = kobject_create_and_add(zpool->name, zcache->kobj); + if (!zpool->kobj) { + ret = -ENOMEM; + goto out; + } + + /* Create various nodes under /sys/.../pool<id>/ */ + ret = sysfs_create_group(zpool->kobj, &zcache_pool_attr_group); + if (ret) { + kobject_put(zpool->kobj); + goto out; + } +#endif + + ret = pool_id; /* success */ out: + if (ret < 0) /* failure */ + zcache_destroy_pool(zpool); + return ret; } @@ -580,6 +684,13 @@ static void zcache_put_page(int pool_id, ino_t inode_no, */ zcache_inc_stat(zpool, ZPOOL_STAT_PAGES_STORED); + /* + * memlimit can be changed any time by user using sysfs. If + * it is set to a value smaller than current number of pages + * stored, then excess pages are not freed immediately but + * further puts are blocked till sufficient number of pages + * are flushed/freed. + */ if (zcache_get_stat(zpool, ZPOOL_STAT_PAGES_STORED) > zcache_get_memlimit(zpool) >> PAGE_SHIFT) { zcache_dec_stat(zpool, ZPOOL_STAT_PAGES_STORED); @@ -690,6 +801,12 @@ static void zcache_flush_fs(int pool_id) struct zcache_inode_rb *znode; struct zcache_pool *zpool = zcache->pools[pool_id]; +#ifdef CONFIG_SYSFS + /* Remove per-pool sysfs entries */ + sysfs_remove_group(zpool->kobj, &zcache_pool_attr_group); + kobject_put(zpool->kobj); +#endif + /* * At this point, there is no active I/O on this filesystem. * So we can free all its pages without holding any locks. @@ -722,6 +839,15 @@ static int __init zcache_init(void) if (!zcache) return -ENOMEM; +#ifdef CONFIG_SYSFS + /* Create /sys/kernel/mm/zcache/ */ + zcache->kobj = kobject_create_and_add("zcache", mm_kobj); + if (!zcache->kobj) { + kfree(zcache); + return -ENOMEM; + } +#endif + spin_lock_init(&zcache->pool_lock); cleancache_ops = ops; diff --git a/drivers/staging/zram/zcache_drv.h b/drivers/staging/zram/zcache_drv.h index bfba5d7..808cfb2 100644 --- a/drivers/staging/zram/zcache_drv.h +++ b/drivers/staging/zram/zcache_drv.h @@ -19,6 +19,7 @@ #include <linux/types.h> #define MAX_ZCACHE_POOLS 32 /* arbitrary */ +#define MAX_ZPOOL_NAME_LEN 8 /* "pool"+id (shown in sysfs) */ enum zcache_pool_stats_index { ZPOOL_STAT_PAGES_STORED, @@ -51,6 +52,10 @@ struct zcache_pool { seqlock_t memlimit_lock; /* protects memlimit */ u64 memlimit; /* bytes */ struct zcache_pool_stats_cpu *stats; /* percpu stats */ +#ifdef CONFIG_SYSFS + unsigned char name[MAX_ZPOOL_NAME_LEN]; + struct kobject *kobj; /* sysfs */ +#endif }; /* Manage all zcache pools */ @@ -58,6 +63,9 @@ struct zcache { struct zcache_pool *pools[MAX_ZCACHE_POOLS]; u32 num_pools; /* current no. of zcache pools */ spinlock_t pool_lock; /* protects pools[] and num_pools */ +#ifdef CONFIG_SYSFS + struct kobject *kobj; /* sysfs */ +#endif }; #endif -- 1.7.1.1 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to majordomo@xxxxxxxxxx For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"dont@xxxxxxxxx"> email@xxxxxxxxx </a>