diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2013-06-04 20:03:31 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-06-04 20:03:31 -0400 |
commit | 8764d86100fe58e69877753faa44fc1d9276c624 (patch) | |
tree | aeef71d9cf639aa79ea7776440eea351f8e552b8 /fs | |
parent | 1dc735bdec9a86d8be1122c43ba52c67aa4e9b30 (diff) | |
parent | e5c5f05dca0cf90f0f3bb1aea85dcf658baff185 (diff) |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse
Pull fuse fixes from Miklos Szeredi:
"One patch fixes an Oops introduced in 3.9 with the readdirplus
feature. The rest are fixes for async-dio in 3.10"
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse:
fuse: fix alignment in short read optimization for async_dio
fuse: return -EIOCBQUEUED from fuse_direct_IO() for all async requests
fuse: fix readdirplus Oops in fuse_dentry_revalidate
fuse: update inode size and invalidate attributes on fallocate
fuse: truncate pagecache range on hole punch
fuse: allocate for_background dio requests based on io->async state
Diffstat (limited to 'fs')
-rw-r--r-- | fs/fuse/dir.c | 12 | ||||
-rw-r--r-- | fs/fuse/file.c | 58 | ||||
-rw-r--r-- | fs/fuse/inode.c | 7 |
3 files changed, 62 insertions, 15 deletions
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 254df56b847b..f3f783dc4f75 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c | |||
@@ -180,6 +180,8 @@ u64 fuse_get_attr_version(struct fuse_conn *fc) | |||
180 | static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags) | 180 | static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags) |
181 | { | 181 | { |
182 | struct inode *inode; | 182 | struct inode *inode; |
183 | struct dentry *parent; | ||
184 | struct fuse_conn *fc; | ||
183 | 185 | ||
184 | inode = ACCESS_ONCE(entry->d_inode); | 186 | inode = ACCESS_ONCE(entry->d_inode); |
185 | if (inode && is_bad_inode(inode)) | 187 | if (inode && is_bad_inode(inode)) |
@@ -187,10 +189,8 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags) | |||
187 | else if (fuse_dentry_time(entry) < get_jiffies_64()) { | 189 | else if (fuse_dentry_time(entry) < get_jiffies_64()) { |
188 | int err; | 190 | int err; |
189 | struct fuse_entry_out outarg; | 191 | struct fuse_entry_out outarg; |
190 | struct fuse_conn *fc; | ||
191 | struct fuse_req *req; | 192 | struct fuse_req *req; |
192 | struct fuse_forget_link *forget; | 193 | struct fuse_forget_link *forget; |
193 | struct dentry *parent; | ||
194 | u64 attr_version; | 194 | u64 attr_version; |
195 | 195 | ||
196 | /* For negative dentries, always do a fresh lookup */ | 196 | /* For negative dentries, always do a fresh lookup */ |
@@ -241,8 +241,14 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags) | |||
241 | entry_attr_timeout(&outarg), | 241 | entry_attr_timeout(&outarg), |
242 | attr_version); | 242 | attr_version); |
243 | fuse_change_entry_timeout(entry, &outarg); | 243 | fuse_change_entry_timeout(entry, &outarg); |
244 | } else if (inode) { | ||
245 | fc = get_fuse_conn(inode); | ||
246 | if (fc->readdirplus_auto) { | ||
247 | parent = dget_parent(entry); | ||
248 | fuse_advise_use_readdirplus(parent->d_inode); | ||
249 | dput(parent); | ||
250 | } | ||
244 | } | 251 | } |
245 | fuse_advise_use_readdirplus(inode); | ||
246 | return 1; | 252 | return 1; |
247 | } | 253 | } |
248 | 254 | ||
diff --git a/fs/fuse/file.c b/fs/fuse/file.c index d1c9b85b3f58..e570081f9f76 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c | |||
@@ -16,6 +16,7 @@ | |||
16 | #include <linux/compat.h> | 16 | #include <linux/compat.h> |
17 | #include <linux/swap.h> | 17 | #include <linux/swap.h> |
18 | #include <linux/aio.h> | 18 | #include <linux/aio.h> |
19 | #include <linux/falloc.h> | ||
19 | 20 | ||
20 | static const struct file_operations fuse_direct_io_file_operations; | 21 | static const struct file_operations fuse_direct_io_file_operations; |
21 | 22 | ||
@@ -1278,7 +1279,10 @@ ssize_t fuse_direct_io(struct fuse_io_priv *io, const struct iovec *iov, | |||
1278 | 1279 | ||
1279 | iov_iter_init(&ii, iov, nr_segs, count, 0); | 1280 | iov_iter_init(&ii, iov, nr_segs, count, 0); |
1280 | 1281 | ||
1281 | req = fuse_get_req(fc, fuse_iter_npages(&ii)); | 1282 | if (io->async) |
1283 | req = fuse_get_req_for_background(fc, fuse_iter_npages(&ii)); | ||
1284 | else | ||
1285 | req = fuse_get_req(fc, fuse_iter_npages(&ii)); | ||
1282 | if (IS_ERR(req)) | 1286 | if (IS_ERR(req)) |
1283 | return PTR_ERR(req); | 1287 | return PTR_ERR(req); |
1284 | 1288 | ||
@@ -1314,7 +1318,11 @@ ssize_t fuse_direct_io(struct fuse_io_priv *io, const struct iovec *iov, | |||
1314 | break; | 1318 | break; |
1315 | if (count) { | 1319 | if (count) { |
1316 | fuse_put_request(fc, req); | 1320 | fuse_put_request(fc, req); |
1317 | req = fuse_get_req(fc, fuse_iter_npages(&ii)); | 1321 | if (io->async) |
1322 | req = fuse_get_req_for_background(fc, | ||
1323 | fuse_iter_npages(&ii)); | ||
1324 | else | ||
1325 | req = fuse_get_req(fc, fuse_iter_npages(&ii)); | ||
1318 | if (IS_ERR(req)) | 1326 | if (IS_ERR(req)) |
1319 | break; | 1327 | break; |
1320 | } | 1328 | } |
@@ -2365,6 +2373,11 @@ static void fuse_do_truncate(struct file *file) | |||
2365 | fuse_do_setattr(inode, &attr, file); | 2373 | fuse_do_setattr(inode, &attr, file); |
2366 | } | 2374 | } |
2367 | 2375 | ||
2376 | static inline loff_t fuse_round_up(loff_t off) | ||
2377 | { | ||
2378 | return round_up(off, FUSE_MAX_PAGES_PER_REQ << PAGE_SHIFT); | ||
2379 | } | ||
2380 | |||
2368 | static ssize_t | 2381 | static ssize_t |
2369 | fuse_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, | 2382 | fuse_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, |
2370 | loff_t offset, unsigned long nr_segs) | 2383 | loff_t offset, unsigned long nr_segs) |
@@ -2372,6 +2385,7 @@ fuse_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, | |||
2372 | ssize_t ret = 0; | 2385 | ssize_t ret = 0; |
2373 | struct file *file = iocb->ki_filp; | 2386 | struct file *file = iocb->ki_filp; |
2374 | struct fuse_file *ff = file->private_data; | 2387 | struct fuse_file *ff = file->private_data; |
2388 | bool async_dio = ff->fc->async_dio; | ||
2375 | loff_t pos = 0; | 2389 | loff_t pos = 0; |
2376 | struct inode *inode; | 2390 | struct inode *inode; |
2377 | loff_t i_size; | 2391 | loff_t i_size; |
@@ -2383,10 +2397,10 @@ fuse_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, | |||
2383 | i_size = i_size_read(inode); | 2397 | i_size = i_size_read(inode); |
2384 | 2398 | ||
2385 | /* optimization for short read */ | 2399 | /* optimization for short read */ |
2386 | if (rw != WRITE && offset + count > i_size) { | 2400 | if (async_dio && rw != WRITE && offset + count > i_size) { |
2387 | if (offset >= i_size) | 2401 | if (offset >= i_size) |
2388 | return 0; | 2402 | return 0; |
2389 | count = i_size - offset; | 2403 | count = min_t(loff_t, count, fuse_round_up(i_size - offset)); |
2390 | } | 2404 | } |
2391 | 2405 | ||
2392 | io = kmalloc(sizeof(struct fuse_io_priv), GFP_KERNEL); | 2406 | io = kmalloc(sizeof(struct fuse_io_priv), GFP_KERNEL); |
@@ -2404,7 +2418,7 @@ fuse_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, | |||
2404 | * By default, we want to optimize all I/Os with async request | 2418 | * By default, we want to optimize all I/Os with async request |
2405 | * submission to the client filesystem if supported. | 2419 | * submission to the client filesystem if supported. |
2406 | */ | 2420 | */ |
2407 | io->async = ff->fc->async_dio; | 2421 | io->async = async_dio; |
2408 | io->iocb = iocb; | 2422 | io->iocb = iocb; |
2409 | 2423 | ||
2410 | /* | 2424 | /* |
@@ -2412,7 +2426,7 @@ fuse_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, | |||
2412 | * to wait on real async I/O requests, so we must submit this request | 2426 | * to wait on real async I/O requests, so we must submit this request |
2413 | * synchronously. | 2427 | * synchronously. |
2414 | */ | 2428 | */ |
2415 | if (!is_sync_kiocb(iocb) && (offset + count > i_size)) | 2429 | if (!is_sync_kiocb(iocb) && (offset + count > i_size) && rw == WRITE) |
2416 | io->async = false; | 2430 | io->async = false; |
2417 | 2431 | ||
2418 | if (rw == WRITE) | 2432 | if (rw == WRITE) |
@@ -2424,7 +2438,7 @@ fuse_direct_IO(int rw, struct kiocb *iocb, const struct iovec *iov, | |||
2424 | fuse_aio_complete(io, ret < 0 ? ret : 0, -1); | 2438 | fuse_aio_complete(io, ret < 0 ? ret : 0, -1); |
2425 | 2439 | ||
2426 | /* we have a non-extending, async request, so return */ | 2440 | /* we have a non-extending, async request, so return */ |
2427 | if (ret > 0 && !is_sync_kiocb(iocb)) | 2441 | if (!is_sync_kiocb(iocb)) |
2428 | return -EIOCBQUEUED; | 2442 | return -EIOCBQUEUED; |
2429 | 2443 | ||
2430 | ret = wait_on_sync_kiocb(iocb); | 2444 | ret = wait_on_sync_kiocb(iocb); |
@@ -2446,6 +2460,7 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset, | |||
2446 | loff_t length) | 2460 | loff_t length) |
2447 | { | 2461 | { |
2448 | struct fuse_file *ff = file->private_data; | 2462 | struct fuse_file *ff = file->private_data; |
2463 | struct inode *inode = file->f_inode; | ||
2449 | struct fuse_conn *fc = ff->fc; | 2464 | struct fuse_conn *fc = ff->fc; |
2450 | struct fuse_req *req; | 2465 | struct fuse_req *req; |
2451 | struct fuse_fallocate_in inarg = { | 2466 | struct fuse_fallocate_in inarg = { |
@@ -2459,9 +2474,16 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset, | |||
2459 | if (fc->no_fallocate) | 2474 | if (fc->no_fallocate) |
2460 | return -EOPNOTSUPP; | 2475 | return -EOPNOTSUPP; |
2461 | 2476 | ||
2477 | if (mode & FALLOC_FL_PUNCH_HOLE) { | ||
2478 | mutex_lock(&inode->i_mutex); | ||
2479 | fuse_set_nowrite(inode); | ||
2480 | } | ||
2481 | |||
2462 | req = fuse_get_req_nopages(fc); | 2482 | req = fuse_get_req_nopages(fc); |
2463 | if (IS_ERR(req)) | 2483 | if (IS_ERR(req)) { |
2464 | return PTR_ERR(req); | 2484 | err = PTR_ERR(req); |
2485 | goto out; | ||
2486 | } | ||
2465 | 2487 | ||
2466 | req->in.h.opcode = FUSE_FALLOCATE; | 2488 | req->in.h.opcode = FUSE_FALLOCATE; |
2467 | req->in.h.nodeid = ff->nodeid; | 2489 | req->in.h.nodeid = ff->nodeid; |
@@ -2476,6 +2498,24 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset, | |||
2476 | } | 2498 | } |
2477 | fuse_put_request(fc, req); | 2499 | fuse_put_request(fc, req); |
2478 | 2500 | ||
2501 | if (err) | ||
2502 | goto out; | ||
2503 | |||
2504 | /* we could have extended the file */ | ||
2505 | if (!(mode & FALLOC_FL_KEEP_SIZE)) | ||
2506 | fuse_write_update_size(inode, offset + length); | ||
2507 | |||
2508 | if (mode & FALLOC_FL_PUNCH_HOLE) | ||
2509 | truncate_pagecache_range(inode, offset, offset + length - 1); | ||
2510 | |||
2511 | fuse_invalidate_attr(inode); | ||
2512 | |||
2513 | out: | ||
2514 | if (mode & FALLOC_FL_PUNCH_HOLE) { | ||
2515 | fuse_release_nowrite(inode); | ||
2516 | mutex_unlock(&inode->i_mutex); | ||
2517 | } | ||
2518 | |||
2479 | return err; | 2519 | return err; |
2480 | } | 2520 | } |
2481 | 2521 | ||
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index 6201f81e4d3a..9a0cdde14a08 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c | |||
@@ -867,10 +867,11 @@ static void process_init_reply(struct fuse_conn *fc, struct fuse_req *req) | |||
867 | fc->dont_mask = 1; | 867 | fc->dont_mask = 1; |
868 | if (arg->flags & FUSE_AUTO_INVAL_DATA) | 868 | if (arg->flags & FUSE_AUTO_INVAL_DATA) |
869 | fc->auto_inval_data = 1; | 869 | fc->auto_inval_data = 1; |
870 | if (arg->flags & FUSE_DO_READDIRPLUS) | 870 | if (arg->flags & FUSE_DO_READDIRPLUS) { |
871 | fc->do_readdirplus = 1; | 871 | fc->do_readdirplus = 1; |
872 | if (arg->flags & FUSE_READDIRPLUS_AUTO) | 872 | if (arg->flags & FUSE_READDIRPLUS_AUTO) |
873 | fc->readdirplus_auto = 1; | 873 | fc->readdirplus_auto = 1; |
874 | } | ||
874 | if (arg->flags & FUSE_ASYNC_DIO) | 875 | if (arg->flags & FUSE_ASYNC_DIO) |
875 | fc->async_dio = 1; | 876 | fc->async_dio = 1; |
876 | } else { | 877 | } else { |