Re: [RFC] shmem: Add eventfd notification on utlilization level

From: Kyungmin Park
Date: Mon Mar 09 2015 - 21:51:57 EST


Any updates?

Thank you,
Kyungmin Park

On Wed, Feb 11, 2015 at 11:50 PM, Krzysztof Kozlowski
<k.kozlowski@xxxxxxxxxxx> wrote:
> Allow notifying user space when used space of tmpfs exceeds specified
> level.
>
> The utilization level is passed as mount option 'warn_used'. The kernel
> will notify user-space through eventfd after exceeding this number of
> used blocks.
>
> The eventfd descriptor has to be passed through sysfs file:
> /sys/fs/tmpfs/tmpfs-[0-9]+/warn_used_blocks_efd
>
> Signed-off-by: Krzysztof Kozlowski <k.kozlowski@xxxxxxxxxxx>
> ---
> include/linux/shmem_fs.h | 4 ++
> mm/shmem.c | 138 ++++++++++++++++++++++++++++++++++++++++++++++-
> 2 files changed, 140 insertions(+), 2 deletions(-)
>
> diff --git a/include/linux/shmem_fs.h b/include/linux/shmem_fs.h
> index 50777b5b1e4c..c2ec9909da95 100644
> --- a/include/linux/shmem_fs.h
> +++ b/include/linux/shmem_fs.h
> @@ -35,6 +35,10 @@ struct shmem_sb_info {
> kgid_t gid; /* Mount gid for root directory */
> umode_t mode; /* Mount mode for root directory */
> struct mempolicy *mpol; /* default memory policy for mappings */
> + unsigned long warn_used; /* Warn on reaching used blocks */
> + struct kobject s_kobj; /* kobj for sysfs attributes */
> + struct completion s_kobj_unregister; /* synchronization for put_super */
> + struct eventfd_ctx *warn_used_efd; /* user-space passed eventfd */
> };
>
> static inline struct shmem_inode_info *SHMEM_I(struct inode *inode)
> diff --git a/mm/shmem.c b/mm/shmem.c
> index f69d296bd0a3..b559adcef3b3 100644
> --- a/mm/shmem.c
> +++ b/mm/shmem.c
> @@ -34,6 +34,7 @@
> #include <linux/aio.h>
>
> static struct vfsmount *shm_mnt;
> +static struct kset *shmem_kset;
>
> #ifdef CONFIG_SHMEM
> /*
> @@ -69,6 +70,7 @@ static struct vfsmount *shm_mnt;
> #include <linux/syscalls.h>
> #include <linux/fcntl.h>
> #include <uapi/linux/memfd.h>
> +#include <linux/eventfd.h>
>
> #include <asm/uaccess.h>
> #include <asm/pgtable.h>
> @@ -199,6 +201,106 @@ static struct backing_dev_info shmem_backing_dev_info __read_mostly = {
> static LIST_HEAD(shmem_swaplist);
> static DEFINE_MUTEX(shmem_swaplist_mutex);
>
> +
> +struct shmem_attr {
> + struct attribute attr;
> + ssize_t (*show)(struct shmem_attr *, struct shmem_sb_info *, char *);
> + ssize_t (*store)(struct shmem_attr *, struct shmem_sb_info *,
> + const char *, size_t);
> +};
> +
> +static int shmem_setup_warn_used_blocks_eventfd(struct shmem_sb_info *sbinfo,
> + unsigned int efd)
> +{
> + int ret = 0;
> +
> + if (sbinfo->warn_used_efd) {
> + eventfd_ctx_put(sbinfo->warn_used_efd);
> + sbinfo->warn_used_efd = NULL;
> + }
> +
> + sbinfo->warn_used_efd = eventfd_ctx_fdget(efd);
> + if (IS_ERR(sbinfo->warn_used_efd)) {
> + ret = PTR_ERR(sbinfo->warn_used_efd);
> + sbinfo->warn_used_efd = NULL;
> + }
> +
> + return ret;
> +}
> +
> +static int parse_strtouint(const char *buf,
> + unsigned long long max, unsigned int *value)
> +{
> + int ret;
> +
> + ret = kstrtouint(skip_spaces(buf), 0, value);
> + if (!ret && *value > max)
> + ret = -EINVAL;
> + return ret;
> +}
> +
> +static ssize_t warn_used_blocks_efd_store(struct shmem_attr *a,
> + struct shmem_sb_info *sbinfo,
> + const char *buf, size_t count)
> +{
> + unsigned int val;
> + int ret;
> +
> + if (parse_strtouint(buf, -1ULL, &val))
> + return -EINVAL;
> +
> + ret = shmem_setup_warn_used_blocks_eventfd(sbinfo, val);
> +
> + return ret ? ret : count;
> +}
> +
> +static struct shmem_attr
> +shmem_attr_warn_used_blocks_efd = __ATTR_WO(warn_used_blocks_efd);
> +
> +static struct attribute *shmem_attrs[] = {
> + &shmem_attr_warn_used_blocks_efd.attr,
> + NULL,
> +};
> +
> +static ssize_t shmem_attr_show(struct kobject *kobj,
> + struct attribute *attr, char *buf)
> +{
> + struct shmem_sb_info *sbinfo = container_of(kobj, struct shmem_sb_info,
> + s_kobj);
> + struct shmem_attr *a = container_of(attr, struct shmem_attr, attr);
> +
> + return a->show ? a->show(a, sbinfo, buf) : 0;
> +}
> +
> +static ssize_t shmem_attr_store(struct kobject *kobj,
> + struct attribute *attr,
> + const char *buf, size_t len)
> +{
> + struct shmem_sb_info *sbinfo = container_of(kobj, struct shmem_sb_info,
> + s_kobj);
> + struct shmem_attr *a = container_of(attr, struct shmem_attr, attr);
> +
> + return a->store ? a->store(a, sbinfo, buf, len) : 0;
> +}
> +
> +static void shmem_sb_release(struct kobject *kobj)
> +{
> + struct shmem_sb_info *sbinfo = container_of(kobj, struct shmem_sb_info,
> + s_kobj);
> + complete(&sbinfo->s_kobj_unregister);
> +}
> +
> +static const struct sysfs_ops shmem_attr_ops = {
> + .show = shmem_attr_show,
> + .store = shmem_attr_store,
> +};
> +
> +static struct kobj_type shmem_ktype = {
> + .default_attrs = shmem_attrs,
> + .sysfs_ops = &shmem_attr_ops,
> + .release = shmem_sb_release,
> +};
> +
> static int shmem_reserve_inode(struct super_block *sb)
> {
> struct shmem_sb_info *sbinfo = SHMEM_SB(sb);
> @@ -1170,6 +1272,13 @@ repeat:
> }
> percpu_counter_inc(&sbinfo->used_blocks);
> }
> + if (sbinfo->warn_used) {
> + if (percpu_counter_compare(&sbinfo->used_blocks,
> + sbinfo->warn_used) >= 0) {
> + if (sbinfo->warn_used_efd)
> + eventfd_signal(sbinfo->warn_used_efd, 1);
> + }
> + }
>
> page = shmem_alloc_page(gfp, info, index);
> if (!page) {
> @@ -2824,6 +2933,10 @@ static int shmem_parse_options(char *options, struct shmem_sb_info *sbinfo,
> mpol = NULL;
> if (mpol_parse_str(value, &mpol))
> goto bad_val;
> + } else if (!strcmp(this_char,"warn_used")) {
> + sbinfo->warn_used = memparse(value, &rest);
> + if (*rest)
> + goto bad_val;
> } else {
> printk(KERN_ERR "tmpfs: Bad mount option %s\n",
> this_char);
> @@ -2984,6 +3097,13 @@ static void shmem_put_super(struct super_block *sb)
> struct shmem_sb_info *sbinfo = SHMEM_SB(sb);
>
> percpu_counter_destroy(&sbinfo->used_blocks);
> + if (sbinfo->warn_used_efd) {
> + eventfd_ctx_put(sbinfo->warn_used_efd);
> + sbinfo->warn_used_efd = NULL;
> + }
> + kobject_del(&sbinfo->s_kobj);
> + kobject_put(&sbinfo->s_kobj);
> + wait_for_completion(&sbinfo->s_kobj_unregister);
> mpol_put(sbinfo->mpol);
> kfree(sbinfo);
> sb->s_fs_info = NULL;
> @@ -2994,6 +3114,7 @@ int shmem_fill_super(struct super_block *sb, void *data, int silent)
> struct inode *inode;
> struct shmem_sb_info *sbinfo;
> int err = -ENOMEM;
> + static unsigned int no;
>
> /* Round up to L1_CACHE_BYTES to resist false sharing */
> sbinfo = kzalloc(max((int)sizeof(struct shmem_sb_info),
> @@ -3045,17 +3166,25 @@ int shmem_fill_super(struct super_block *sb, void *data, int silent)
> #ifdef CONFIG_TMPFS_POSIX_ACL
> sb->s_flags |= MS_POSIXACL;
> #endif
> + sbinfo->s_kobj.kset = shmem_kset;
> + init_completion(&sbinfo->s_kobj_unregister);
> + err = kobject_init_and_add(&sbinfo->s_kobj, &shmem_ktype, NULL,
> + "%s-%u", sb->s_id, no++);
> + if (err)
> + goto failed;
>
> inode = shmem_get_inode(sb, NULL, S_IFDIR | sbinfo->mode, 0, VM_NORESERVE);
> if (!inode)
> - goto failed;
> + goto failed_kobj;
> inode->i_uid = sbinfo->uid;
> inode->i_gid = sbinfo->gid;
> sb->s_root = d_make_root(inode);
> if (!sb->s_root)
> - goto failed;
> + goto failed_kobj;
> return 0;
>
> +failed_kobj:
> + kobject_del(&sbinfo->s_kobj);
> failed:
> shmem_put_super(sb);
> return err;
> @@ -3225,6 +3354,10 @@ int __init shmem_init(void)
> if (shmem_inode_cachep)
> return 0;
>
> + shmem_kset = kset_create_and_add("tmpfs", NULL, fs_kobj);
> + if (!shmem_kset)
> + return -ENOMEM;
> +
> error = bdi_init(&shmem_backing_dev_info);
> if (error)
> goto out4;
> @@ -3255,6 +3388,7 @@ out3:
> bdi_destroy(&shmem_backing_dev_info);
> out4:
> shm_mnt = ERR_PTR(error);
> + kset_unregister(shmem_kset);
> return error;
> }
>
> --
> 1.9.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>
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/