aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndreas Rohner <andreas.rohner@gmx.net>2014-04-03 17:50:28 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2014-04-03 19:21:25 -0400
commit2cc88f3a5f16ae9a3c8f54de1b2fd4a397b36075 (patch)
treeeccf104b99fbaca714acd78ebe90b574f408f0fe
parent00e9ffcd27cc5d0af9076383c6242c32335546f8 (diff)
nilfs2: implementation of NILFS_IOCTL_SET_SUINFO ioctl
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>
-rw-r--r--Documentation/filesystems/nilfs2.txt7
-rw-r--r--fs/nilfs2/ioctl.c92
-rw-r--r--include/linux/nilfs2_fs.h2
3 files changed, 101 insertions, 0 deletions
diff --git a/Documentation/filesystems/nilfs2.txt b/Documentation/filesystems/nilfs2.txt
index 06887d46ccf2..8b887ae4e39e 100644
--- a/Documentation/filesystems/nilfs2.txt
+++ b/Documentation/filesystems/nilfs2.txt
@@ -111,6 +111,13 @@ Table of NILFS2 specific ioctls
111 nilfs_resize utilities and by nilfs_cleanerd 111 nilfs_resize utilities and by nilfs_cleanerd
112 daemon. 112 daemon.
113 113
114 NILFS_IOCTL_SET_SUINFO Modify segment usage info of requested
115 segments. This ioctl is used by
116 nilfs_cleanerd daemon to skip unnecessary
117 cleaning operation of segments and reduce
118 performance penalty or wear of flash device
119 due to redundant move of in-use blocks.
120
114 NILFS_IOCTL_GET_SUSTAT Return segment usage statistics. This ioctl 121 NILFS_IOCTL_GET_SUSTAT Return segment usage statistics. This ioctl
115 is used in lssu, nilfs_resize utilities and 122 is used in lssu, nilfs_resize utilities and
116 by nilfs_cleanerd daemon. 123 by nilfs_cleanerd daemon.
diff --git a/fs/nilfs2/ioctl.c b/fs/nilfs2/ioctl.c
index 2b34021948e4..c19a23158487 100644
--- a/fs/nilfs2/ioctl.c
+++ b/fs/nilfs2/ioctl.c
@@ -1163,6 +1163,95 @@ static int nilfs_ioctl_get_info(struct inode *inode, struct file *filp,
1163 return ret; 1163 return ret;
1164} 1164}
1165 1165
1166/**
1167 * nilfs_ioctl_set_suinfo - set segment usage info
1168 * @inode: inode object
1169 * @filp: file object
1170 * @cmd: ioctl's request code
1171 * @argp: pointer on argument from userspace
1172 *
1173 * Description: Expects an array of nilfs_suinfo_update structures
1174 * encapsulated in nilfs_argv and updates the segment usage info
1175 * according to the flags in nilfs_suinfo_update.
1176 *
1177 * Return Value: On success, 0 is returned. On error, one of the
1178 * following negative error codes is returned.
1179 *
1180 * %-EPERM - Not enough permissions
1181 *
1182 * %-EFAULT - Error copying input data
1183 *
1184 * %-EIO - I/O error.
1185 *
1186 * %-ENOMEM - Insufficient amount of memory available.
1187 *
1188 * %-EINVAL - Invalid values in input (segment number, flags or nblocks)
1189 */
1190static int nilfs_ioctl_set_suinfo(struct inode *inode, struct file *filp,
1191 unsigned int cmd, void __user *argp)
1192{
1193 struct the_nilfs *nilfs = inode->i_sb->s_fs_info;
1194 struct nilfs_transaction_info ti;
1195 struct nilfs_argv argv;
1196 size_t len;
1197 void __user *base;
1198 void *kbuf;
1199 int ret;
1200
1201 if (!capable(CAP_SYS_ADMIN))
1202 return -EPERM;
1203
1204 ret = mnt_want_write_file(filp);
1205 if (ret)
1206 return ret;
1207
1208 ret = -EFAULT;
1209 if (copy_from_user(&argv, argp, sizeof(argv)))
1210 goto out;
1211
1212 ret = -EINVAL;
1213 if (argv.v_size < sizeof(struct nilfs_suinfo_update))
1214 goto out;
1215
1216 if (argv.v_nmembs > nilfs->ns_nsegments)
1217 goto out;
1218
1219 if (argv.v_nmembs >= UINT_MAX / argv.v_size)
1220 goto out;
1221
1222 len = argv.v_size * argv.v_nmembs;
1223 if (!len) {
1224 ret = 0;
1225 goto out;
1226 }
1227
1228 base = (void __user *)(unsigned long)argv.v_base;
1229 kbuf = vmalloc(len);
1230 if (!kbuf) {
1231 ret = -ENOMEM;
1232 goto out;
1233 }
1234
1235 if (copy_from_user(kbuf, base, len)) {
1236 ret = -EFAULT;
1237 goto out_free;
1238 }
1239
1240 nilfs_transaction_begin(inode->i_sb, &ti, 0);
1241 ret = nilfs_sufile_set_suinfo(nilfs->ns_sufile, kbuf, argv.v_size,
1242 argv.v_nmembs);
1243 if (unlikely(ret < 0))
1244 nilfs_transaction_abort(inode->i_sb);
1245 else
1246 nilfs_transaction_commit(inode->i_sb); /* never fails */
1247
1248out_free:
1249 vfree(kbuf);
1250out:
1251 mnt_drop_write_file(filp);
1252 return ret;
1253}
1254
1166long nilfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) 1255long nilfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
1167{ 1256{
1168 struct inode *inode = file_inode(filp); 1257 struct inode *inode = file_inode(filp);
@@ -1189,6 +1278,8 @@ long nilfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
1189 return nilfs_ioctl_get_info(inode, filp, cmd, argp, 1278 return nilfs_ioctl_get_info(inode, filp, cmd, argp,
1190 sizeof(struct nilfs_suinfo), 1279 sizeof(struct nilfs_suinfo),
1191 nilfs_ioctl_do_get_suinfo); 1280 nilfs_ioctl_do_get_suinfo);
1281 case NILFS_IOCTL_SET_SUINFO:
1282 return nilfs_ioctl_set_suinfo(inode, filp, cmd, argp);
1192 case NILFS_IOCTL_GET_SUSTAT: 1283 case NILFS_IOCTL_GET_SUSTAT:
1193 return nilfs_ioctl_get_sustat(inode, filp, cmd, argp); 1284 return nilfs_ioctl_get_sustat(inode, filp, cmd, argp);
1194 case NILFS_IOCTL_GET_VINFO: 1285 case NILFS_IOCTL_GET_VINFO:
@@ -1228,6 +1319,7 @@ long nilfs_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
1228 case NILFS_IOCTL_GET_CPINFO: 1319 case NILFS_IOCTL_GET_CPINFO:
1229 case NILFS_IOCTL_GET_CPSTAT: 1320 case NILFS_IOCTL_GET_CPSTAT:
1230 case NILFS_IOCTL_GET_SUINFO: 1321 case NILFS_IOCTL_GET_SUINFO:
1322 case NILFS_IOCTL_SET_SUINFO:
1231 case NILFS_IOCTL_GET_SUSTAT: 1323 case NILFS_IOCTL_GET_SUSTAT:
1232 case NILFS_IOCTL_GET_VINFO: 1324 case NILFS_IOCTL_GET_VINFO:
1233 case NILFS_IOCTL_GET_BDESCS: 1325 case NILFS_IOCTL_GET_BDESCS:
diff --git a/include/linux/nilfs2_fs.h b/include/linux/nilfs2_fs.h
index 252657874a19..1fb465f9baf2 100644
--- a/include/linux/nilfs2_fs.h
+++ b/include/linux/nilfs2_fs.h
@@ -905,5 +905,7 @@ struct nilfs_bdesc {
905 _IOW(NILFS_IOCTL_IDENT, 0x8B, __u64) 905 _IOW(NILFS_IOCTL_IDENT, 0x8B, __u64)
906#define NILFS_IOCTL_SET_ALLOC_RANGE \ 906#define NILFS_IOCTL_SET_ALLOC_RANGE \
907 _IOW(NILFS_IOCTL_IDENT, 0x8C, __u64[2]) 907 _IOW(NILFS_IOCTL_IDENT, 0x8C, __u64[2])
908#define NILFS_IOCTL_SET_SUINFO \
909 _IOW(NILFS_IOCTL_IDENT, 0x8D, struct nilfs_argv)
908 910
909#endif /* _LINUX_NILFS_FS_H */ 911#endif /* _LINUX_NILFS_FS_H */