diff options
author | Ravishankar N <ravishankar@redhat.com> | 2015-06-30 14:10:22 -0400 |
---|---|---|
committer | Miklos Szeredi <miklos@szeredi.hu> | 2015-11-10 04:32:37 -0500 |
commit | 0b5da8db145bfd44266ac964a2636a0cf8d7c286 (patch) | |
tree | 23e46abfcb58f4b744aeab42a8c3a62e141b399b | |
parent | 3ca8138f014a913f98e6ef40e939868e1e9ea876 (diff) |
fuse: add support for SEEK_HOLE and SEEK_DATA in lseek
A useful performance improvement for accessing virtual machine images
via FUSE mount.
See https://bugzilla.redhat.com/show_bug.cgi?id=1220173 for a use-case
for glusterFS.
Signed-off-by: Ravishankar N <ravishankar@redhat.com>
Signed-off-by: Miklos Szeredi <miklos@szeredi.hu>
-rw-r--r-- | fs/fuse/file.c | 73 | ||||
-rw-r--r-- | fs/fuse/fuse_i.h | 3 | ||||
-rw-r--r-- | include/uapi/linux/fuse.h | 17 |
3 files changed, 84 insertions, 9 deletions
diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 195476a24148..47f181191060 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c | |||
@@ -2231,20 +2231,77 @@ static sector_t fuse_bmap(struct address_space *mapping, sector_t block) | |||
2231 | return err ? 0 : outarg.block; | 2231 | return err ? 0 : outarg.block; |
2232 | } | 2232 | } |
2233 | 2233 | ||
2234 | static loff_t fuse_lseek(struct file *file, loff_t offset, int whence) | ||
2235 | { | ||
2236 | struct inode *inode = file->f_mapping->host; | ||
2237 | struct fuse_conn *fc = get_fuse_conn(inode); | ||
2238 | struct fuse_file *ff = file->private_data; | ||
2239 | FUSE_ARGS(args); | ||
2240 | struct fuse_lseek_in inarg = { | ||
2241 | .fh = ff->fh, | ||
2242 | .offset = offset, | ||
2243 | .whence = whence | ||
2244 | }; | ||
2245 | struct fuse_lseek_out outarg; | ||
2246 | int err; | ||
2247 | |||
2248 | if (fc->no_lseek) | ||
2249 | goto fallback; | ||
2250 | |||
2251 | args.in.h.opcode = FUSE_LSEEK; | ||
2252 | args.in.h.nodeid = ff->nodeid; | ||
2253 | args.in.numargs = 1; | ||
2254 | args.in.args[0].size = sizeof(inarg); | ||
2255 | args.in.args[0].value = &inarg; | ||
2256 | args.out.numargs = 1; | ||
2257 | args.out.args[0].size = sizeof(outarg); | ||
2258 | args.out.args[0].value = &outarg; | ||
2259 | err = fuse_simple_request(fc, &args); | ||
2260 | if (err) { | ||
2261 | if (err == -ENOSYS) { | ||
2262 | fc->no_lseek = 1; | ||
2263 | goto fallback; | ||
2264 | } | ||
2265 | return err; | ||
2266 | } | ||
2267 | |||
2268 | return vfs_setpos(file, outarg.offset, inode->i_sb->s_maxbytes); | ||
2269 | |||
2270 | fallback: | ||
2271 | err = fuse_update_attributes(inode, NULL, file, NULL); | ||
2272 | if (!err) | ||
2273 | return generic_file_llseek(file, offset, whence); | ||
2274 | else | ||
2275 | return err; | ||
2276 | } | ||
2277 | |||
2234 | static loff_t fuse_file_llseek(struct file *file, loff_t offset, int whence) | 2278 | static loff_t fuse_file_llseek(struct file *file, loff_t offset, int whence) |
2235 | { | 2279 | { |
2236 | loff_t retval; | 2280 | loff_t retval; |
2237 | struct inode *inode = file_inode(file); | 2281 | struct inode *inode = file_inode(file); |
2238 | 2282 | ||
2239 | /* No i_mutex protection necessary for SEEK_CUR and SEEK_SET */ | 2283 | switch (whence) { |
2240 | if (whence == SEEK_CUR || whence == SEEK_SET) | 2284 | case SEEK_SET: |
2241 | return generic_file_llseek(file, offset, whence); | 2285 | case SEEK_CUR: |
2242 | 2286 | /* No i_mutex protection necessary for SEEK_CUR and SEEK_SET */ | |
2243 | mutex_lock(&inode->i_mutex); | ||
2244 | retval = fuse_update_attributes(inode, NULL, file, NULL); | ||
2245 | if (!retval) | ||
2246 | retval = generic_file_llseek(file, offset, whence); | 2287 | retval = generic_file_llseek(file, offset, whence); |
2247 | mutex_unlock(&inode->i_mutex); | 2288 | break; |
2289 | case SEEK_END: | ||
2290 | mutex_lock(&inode->i_mutex); | ||
2291 | retval = fuse_update_attributes(inode, NULL, file, NULL); | ||
2292 | if (!retval) | ||
2293 | retval = generic_file_llseek(file, offset, whence); | ||
2294 | mutex_unlock(&inode->i_mutex); | ||
2295 | break; | ||
2296 | case SEEK_HOLE: | ||
2297 | case SEEK_DATA: | ||
2298 | mutex_lock(&inode->i_mutex); | ||
2299 | retval = fuse_lseek(file, offset, whence); | ||
2300 | mutex_unlock(&inode->i_mutex); | ||
2301 | break; | ||
2302 | default: | ||
2303 | retval = -EINVAL; | ||
2304 | } | ||
2248 | 2305 | ||
2249 | return retval; | 2306 | return retval; |
2250 | } | 2307 | } |
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 405113101db8..ce394b5fe6b4 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h | |||
@@ -605,6 +605,9 @@ struct fuse_conn { | |||
605 | /** Does the filesystem support asynchronous direct-IO submission? */ | 605 | /** Does the filesystem support asynchronous direct-IO submission? */ |
606 | unsigned async_dio:1; | 606 | unsigned async_dio:1; |
607 | 607 | ||
608 | /** Is lseek not implemented by fs? */ | ||
609 | unsigned no_lseek:1; | ||
610 | |||
608 | /** The number of requests waiting for completion */ | 611 | /** The number of requests waiting for completion */ |
609 | atomic_t num_waiting; | 612 | atomic_t num_waiting; |
610 | 613 | ||
diff --git a/include/uapi/linux/fuse.h b/include/uapi/linux/fuse.h index c9aca042e61d..5974fae54e12 100644 --- a/include/uapi/linux/fuse.h +++ b/include/uapi/linux/fuse.h | |||
@@ -102,6 +102,9 @@ | |||
102 | * - add ctime and ctimensec to fuse_setattr_in | 102 | * - add ctime and ctimensec to fuse_setattr_in |
103 | * - add FUSE_RENAME2 request | 103 | * - add FUSE_RENAME2 request |
104 | * - add FUSE_NO_OPEN_SUPPORT flag | 104 | * - add FUSE_NO_OPEN_SUPPORT flag |
105 | * | ||
106 | * 7.24 | ||
107 | * - add FUSE_LSEEK for SEEK_HOLE and SEEK_DATA support | ||
105 | */ | 108 | */ |
106 | 109 | ||
107 | #ifndef _LINUX_FUSE_H | 110 | #ifndef _LINUX_FUSE_H |
@@ -137,7 +140,7 @@ | |||
137 | #define FUSE_KERNEL_VERSION 7 | 140 | #define FUSE_KERNEL_VERSION 7 |
138 | 141 | ||
139 | /** Minor version number of this interface */ | 142 | /** Minor version number of this interface */ |
140 | #define FUSE_KERNEL_MINOR_VERSION 23 | 143 | #define FUSE_KERNEL_MINOR_VERSION 24 |
141 | 144 | ||
142 | /** The node ID of the root inode */ | 145 | /** The node ID of the root inode */ |
143 | #define FUSE_ROOT_ID 1 | 146 | #define FUSE_ROOT_ID 1 |
@@ -358,6 +361,7 @@ enum fuse_opcode { | |||
358 | FUSE_FALLOCATE = 43, | 361 | FUSE_FALLOCATE = 43, |
359 | FUSE_READDIRPLUS = 44, | 362 | FUSE_READDIRPLUS = 44, |
360 | FUSE_RENAME2 = 45, | 363 | FUSE_RENAME2 = 45, |
364 | FUSE_LSEEK = 46, | ||
361 | 365 | ||
362 | /* CUSE specific operations */ | 366 | /* CUSE specific operations */ |
363 | CUSE_INIT = 4096, | 367 | CUSE_INIT = 4096, |
@@ -758,4 +762,15 @@ struct fuse_notify_retrieve_in { | |||
758 | /* Device ioctls: */ | 762 | /* Device ioctls: */ |
759 | #define FUSE_DEV_IOC_CLONE _IOR(229, 0, uint32_t) | 763 | #define FUSE_DEV_IOC_CLONE _IOR(229, 0, uint32_t) |
760 | 764 | ||
765 | struct fuse_lseek_in { | ||
766 | uint64_t fh; | ||
767 | uint64_t offset; | ||
768 | uint32_t whence; | ||
769 | uint32_t padding; | ||
770 | }; | ||
771 | |||
772 | struct fuse_lseek_out { | ||
773 | uint64_t offset; | ||
774 | }; | ||
775 | |||
761 | #endif /* _LINUX_FUSE_H */ | 776 | #endif /* _LINUX_FUSE_H */ |