nilfs2: implementation of NILFS_IOCTL_SET_SUINFO ioctl
authorAndreas Rohner <andreas.rohner@gmx.net>
Thu, 3 Apr 2014 21:50:28 +0000 (14:50 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 3 Apr 2014 23:21:25 +0000 (16:21 -0700)
With this ioctl the segment usage entries in the SUFILE can be updated
from userspace.

This is useful, because it allows the userspace GC to modify and update
segment usage entries for specific segments, which enables it to avoid
unnecessary write operations.

If a segment needs to be cleaned, but there is no or very little
reclaimable space in it, the cleaning operation basically degrades to a
useless moving operation.  In the end the only thing that changes is the
location of the data and a timestamp in the segment usage information.
With this ioctl the GC can skip the cleaning and update the segment
usage entries directly instead.

This is basically a shortcut to cleaning the segment.  It is still
necessary to read the segment summary information, but the writing of
the live blocks can be skipped if it's not worth it.

[konishi.ryusuke@lab.ntt.co.jp: add description of NILFS_IOCTL_SET_SUINFO ioctl]
Signed-off-by: Andreas Rohner <andreas.rohner@gmx.net>
Signed-off-by: Ryusuke Konishi <konishi.ryusuke@lab.ntt.co.jp>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Documentation/filesystems/nilfs2.txt
fs/nilfs2/ioctl.c
include/linux/nilfs2_fs.h

index 06887d46ccf2795e8a6f46b1528e985a5ac1975b..8b887ae4e39ec510398f82f77e2029ce68b569c7 100644 (file)
@@ -111,6 +111,13 @@ Table of NILFS2 specific ioctls
                                nilfs_resize utilities and by nilfs_cleanerd
                                daemon.
 
+ NILFS_IOCTL_SET_SUINFO         Modify segment usage info of requested
+                               segments. This ioctl is used by
+                               nilfs_cleanerd daemon to skip unnecessary
+                               cleaning operation of segments and reduce
+                               performance penalty or wear of flash device
+                               due to redundant move of in-use blocks.
+
  NILFS_IOCTL_GET_SUSTAT         Return segment usage statistics. This ioctl
                                is used in lssu, nilfs_resize utilities and
                                by nilfs_cleanerd daemon.
index 2b34021948e4d905f872e6c749614ee9d5500bae..c19a23158487f449ae4d777b8ce8249a0d796ca5 100644 (file)
@@ -1163,6 +1163,95 @@ static int nilfs_ioctl_get_info(struct inode *inode, struct file *filp,
        return ret;
 }
 
+/**
+ * nilfs_ioctl_set_suinfo - set segment usage info
+ * @inode: inode object
+ * @filp: file object
+ * @cmd: ioctl's request code
+ * @argp: pointer on argument from userspace
+ *
+ * Description: Expects an array of nilfs_suinfo_update structures
+ * encapsulated in nilfs_argv and updates the segment usage info
+ * according to the flags in nilfs_suinfo_update.
+ *
+ * Return Value: On success, 0 is returned. On error, one of the
+ * following negative error codes is returned.
+ *
+ * %-EPERM - Not enough permissions
+ *
+ * %-EFAULT - Error copying input data
+ *
+ * %-EIO - I/O error.
+ *
+ * %-ENOMEM - Insufficient amount of memory available.
+ *
+ * %-EINVAL - Invalid values in input (segment number, flags or nblocks)
+ */
+static int nilfs_ioctl_set_suinfo(struct inode *inode, struct file *filp,
+                               unsigned int cmd, void __user *argp)
+{
+       struct the_nilfs *nilfs = inode->i_sb->s_fs_info;
+       struct nilfs_transaction_info ti;
+       struct nilfs_argv argv;
+       size_t len;
+       void __user *base;
+       void *kbuf;
+       int ret;
+
+       if (!capable(CAP_SYS_ADMIN))
+               return -EPERM;
+
+       ret = mnt_want_write_file(filp);
+       if (ret)
+               return ret;
+
+       ret = -EFAULT;
+       if (copy_from_user(&argv, argp, sizeof(argv)))
+               goto out;
+
+       ret = -EINVAL;
+       if (argv.v_size < sizeof(struct nilfs_suinfo_update))
+               goto out;
+
+       if (argv.v_nmembs > nilfs->ns_nsegments)
+               goto out;
+
+       if (argv.v_nmembs >= UINT_MAX / argv.v_size)
+               goto out;
+
+       len = argv.v_size * argv.v_nmembs;
+       if (!len) {
+               ret = 0;
+               goto out;
+       }
+
+       base = (void __user *)(unsigned long)argv.v_base;
+       kbuf = vmalloc(len);
+       if (!kbuf) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       if (copy_from_user(kbuf, base, len)) {
+               ret = -EFAULT;
+               goto out_free;
+       }
+
+       nilfs_transaction_begin(inode->i_sb, &ti, 0);
+       ret = nilfs_sufile_set_suinfo(nilfs->ns_sufile, kbuf, argv.v_size,
+                       argv.v_nmembs);
+       if (unlikely(ret < 0))
+               nilfs_transaction_abort(inode->i_sb);
+       else
+               nilfs_transaction_commit(inode->i_sb); /* never fails */
+
+out_free:
+       vfree(kbuf);
+out:
+       mnt_drop_write_file(filp);
+       return ret;
+}
+
 long nilfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 {
        struct inode *inode = file_inode(filp);
@@ -1189,6 +1278,8 @@ long nilfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
                return nilfs_ioctl_get_info(inode, filp, cmd, argp,
                                            sizeof(struct nilfs_suinfo),
                                            nilfs_ioctl_do_get_suinfo);
+       case NILFS_IOCTL_SET_SUINFO:
+               return nilfs_ioctl_set_suinfo(inode, filp, cmd, argp);
        case NILFS_IOCTL_GET_SUSTAT:
                return nilfs_ioctl_get_sustat(inode, filp, cmd, argp);
        case NILFS_IOCTL_GET_VINFO:
@@ -1228,6 +1319,7 @@ long nilfs_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
        case NILFS_IOCTL_GET_CPINFO:
        case NILFS_IOCTL_GET_CPSTAT:
        case NILFS_IOCTL_GET_SUINFO:
+       case NILFS_IOCTL_SET_SUINFO:
        case NILFS_IOCTL_GET_SUSTAT:
        case NILFS_IOCTL_GET_VINFO:
        case NILFS_IOCTL_GET_BDESCS:
index 252657874a192ffd19170154a0b4a61031c80cd3..1fb465f9baf275c50d84c4dee7d8dac5b943f6ca 100644 (file)
@@ -905,5 +905,7 @@ struct nilfs_bdesc {
        _IOW(NILFS_IOCTL_IDENT, 0x8B, __u64)
 #define NILFS_IOCTL_SET_ALLOC_RANGE  \
        _IOW(NILFS_IOCTL_IDENT, 0x8C, __u64[2])
+#define NILFS_IOCTL_SET_SUINFO  \
+       _IOW(NILFS_IOCTL_IDENT, 0x8D, struct nilfs_argv)
 
 #endif /* _LINUX_NILFS_FS_H */