diff options
| author | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-10-19 17:31:42 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-10-19 17:31:42 -0400 |
| commit | b04cde34cf1d006dfaf8523640f3a18bbb15ebaa (patch) | |
| tree | a811f7a6db0de11f6c412548dfe081a57aba0451 /fs | |
| parent | 4800be295c34268fd3211d49828bfaa6bf62867f (diff) | |
| parent | 603c83da19cf42d0f94022ac2fa389a431e32b84 (diff) | |
Merge git://git.linux-nfs.org/pub/linux/nfs-2.6
* git://git.linux-nfs.org/pub/linux/nfs-2.6:
NFSv4: Fix an rpc_cred reference leakage in fs/nfs/delegation.c
NFSv4: Ensure that we wait for the CLOSE request to complete
NFS: Fix a race in sillyrename
NFS: Fix a writeback race...
Diffstat (limited to 'fs')
| -rw-r--r-- | fs/nfs/delegation.c | 3 | ||||
| -rw-r--r-- | fs/nfs/dir.c | 14 | ||||
| -rw-r--r-- | fs/nfs/file.c | 2 | ||||
| -rw-r--r-- | fs/nfs/inode.c | 25 | ||||
| -rw-r--r-- | fs/nfs/nfs4_fs.h | 3 | ||||
| -rw-r--r-- | fs/nfs/nfs4proc.c | 19 | ||||
| -rw-r--r-- | fs/nfs/nfs4state.c | 14 | ||||
| -rw-r--r-- | fs/nfs/unlink.c | 114 | ||||
| -rw-r--r-- | fs/nfs/write.c | 17 |
9 files changed, 175 insertions, 36 deletions
diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index af8b235d405d..11833f4caeaa 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c | |||
| @@ -168,7 +168,8 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct | |||
| 168 | spin_unlock(&inode->i_lock); | 168 | spin_unlock(&inode->i_lock); |
| 169 | 169 | ||
| 170 | spin_unlock(&clp->cl_lock); | 170 | spin_unlock(&clp->cl_lock); |
| 171 | kfree(delegation); | 171 | if (delegation != NULL) |
| 172 | nfs_free_delegation(delegation); | ||
| 172 | return status; | 173 | return status; |
| 173 | } | 174 | } |
| 174 | 175 | ||
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 8ec7fbd8240c..35334539d947 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c | |||
| @@ -562,6 +562,7 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir) | |||
| 562 | nfs_fattr_init(&fattr); | 562 | nfs_fattr_init(&fattr); |
| 563 | desc->entry = &my_entry; | 563 | desc->entry = &my_entry; |
| 564 | 564 | ||
| 565 | nfs_block_sillyrename(dentry); | ||
| 565 | while(!desc->entry->eof) { | 566 | while(!desc->entry->eof) { |
| 566 | res = readdir_search_pagecache(desc); | 567 | res = readdir_search_pagecache(desc); |
| 567 | 568 | ||
| @@ -592,6 +593,7 @@ static int nfs_readdir(struct file *filp, void *dirent, filldir_t filldir) | |||
| 592 | break; | 593 | break; |
| 593 | } | 594 | } |
| 594 | } | 595 | } |
| 596 | nfs_unblock_sillyrename(dentry); | ||
| 595 | unlock_kernel(); | 597 | unlock_kernel(); |
| 596 | if (res > 0) | 598 | if (res > 0) |
| 597 | res = 0; | 599 | res = 0; |
| @@ -866,6 +868,7 @@ struct dentry_operations nfs_dentry_operations = { | |||
| 866 | static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, struct nameidata *nd) | 868 | static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, struct nameidata *nd) |
| 867 | { | 869 | { |
| 868 | struct dentry *res; | 870 | struct dentry *res; |
| 871 | struct dentry *parent; | ||
| 869 | struct inode *inode = NULL; | 872 | struct inode *inode = NULL; |
| 870 | int error; | 873 | int error; |
| 871 | struct nfs_fh fhandle; | 874 | struct nfs_fh fhandle; |
| @@ -894,26 +897,31 @@ static struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, stru | |||
| 894 | goto out_unlock; | 897 | goto out_unlock; |
| 895 | } | 898 | } |
| 896 | 899 | ||
| 900 | parent = dentry->d_parent; | ||
| 901 | /* Protect against concurrent sillydeletes */ | ||
| 902 | nfs_block_sillyrename(parent); | ||
| 897 | error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, &fhandle, &fattr); | 903 | error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, &fhandle, &fattr); |
| 898 | if (error == -ENOENT) | 904 | if (error == -ENOENT) |
| 899 | goto no_entry; | 905 | goto no_entry; |
| 900 | if (error < 0) { | 906 | if (error < 0) { |
| 901 | res = ERR_PTR(error); | 907 | res = ERR_PTR(error); |
| 902 | goto out_unlock; | 908 | goto out_unblock_sillyrename; |
| 903 | } | 909 | } |
| 904 | inode = nfs_fhget(dentry->d_sb, &fhandle, &fattr); | 910 | inode = nfs_fhget(dentry->d_sb, &fhandle, &fattr); |
| 905 | res = (struct dentry *)inode; | 911 | res = (struct dentry *)inode; |
| 906 | if (IS_ERR(res)) | 912 | if (IS_ERR(res)) |
| 907 | goto out_unlock; | 913 | goto out_unblock_sillyrename; |
| 908 | 914 | ||
| 909 | no_entry: | 915 | no_entry: |
| 910 | res = d_materialise_unique(dentry, inode); | 916 | res = d_materialise_unique(dentry, inode); |
| 911 | if (res != NULL) { | 917 | if (res != NULL) { |
| 912 | if (IS_ERR(res)) | 918 | if (IS_ERR(res)) |
| 913 | goto out_unlock; | 919 | goto out_unblock_sillyrename; |
| 914 | dentry = res; | 920 | dentry = res; |
| 915 | } | 921 | } |
| 916 | nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); | 922 | nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); |
| 923 | out_unblock_sillyrename: | ||
| 924 | nfs_unblock_sillyrename(parent); | ||
| 917 | out_unlock: | 925 | out_unlock: |
| 918 | unlock_kernel(); | 926 | unlock_kernel(); |
| 919 | out: | 927 | out: |
diff --git a/fs/nfs/file.c b/fs/nfs/file.c index d29f90d00aa2..b3bb89f7d5d2 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c | |||
| @@ -131,7 +131,7 @@ nfs_file_release(struct inode *inode, struct file *filp) | |||
| 131 | { | 131 | { |
| 132 | /* Ensure that dirty pages are flushed out with the right creds */ | 132 | /* Ensure that dirty pages are flushed out with the right creds */ |
| 133 | if (filp->f_mode & FMODE_WRITE) | 133 | if (filp->f_mode & FMODE_WRITE) |
| 134 | filemap_fdatawrite(filp->f_mapping); | 134 | nfs_wb_all(filp->f_path.dentry->d_inode); |
| 135 | nfs_inc_stats(inode, NFSIOS_VFSRELEASE); | 135 | nfs_inc_stats(inode, NFSIOS_VFSRELEASE); |
| 136 | return NFS_PROTO(inode)->file_release(inode, filp); | 136 | return NFS_PROTO(inode)->file_release(inode, filp); |
| 137 | } | 137 | } |
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 6d2f2a3eccf8..db5d96dc6107 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c | |||
| @@ -514,7 +514,7 @@ struct nfs_open_context *get_nfs_open_context(struct nfs_open_context *ctx) | |||
| 514 | return ctx; | 514 | return ctx; |
| 515 | } | 515 | } |
| 516 | 516 | ||
| 517 | void put_nfs_open_context(struct nfs_open_context *ctx) | 517 | static void __put_nfs_open_context(struct nfs_open_context *ctx, int wait) |
| 518 | { | 518 | { |
| 519 | struct inode *inode = ctx->path.dentry->d_inode; | 519 | struct inode *inode = ctx->path.dentry->d_inode; |
| 520 | 520 | ||
| @@ -522,8 +522,12 @@ void put_nfs_open_context(struct nfs_open_context *ctx) | |||
| 522 | return; | 522 | return; |
| 523 | list_del(&ctx->list); | 523 | list_del(&ctx->list); |
| 524 | spin_unlock(&inode->i_lock); | 524 | spin_unlock(&inode->i_lock); |
| 525 | if (ctx->state != NULL) | 525 | if (ctx->state != NULL) { |
| 526 | nfs4_close_state(&ctx->path, ctx->state, ctx->mode); | 526 | if (wait) |
| 527 | nfs4_close_sync(&ctx->path, ctx->state, ctx->mode); | ||
| 528 | else | ||
| 529 | nfs4_close_state(&ctx->path, ctx->state, ctx->mode); | ||
| 530 | } | ||
| 527 | if (ctx->cred != NULL) | 531 | if (ctx->cred != NULL) |
| 528 | put_rpccred(ctx->cred); | 532 | put_rpccred(ctx->cred); |
| 529 | dput(ctx->path.dentry); | 533 | dput(ctx->path.dentry); |
| @@ -531,6 +535,16 @@ void put_nfs_open_context(struct nfs_open_context *ctx) | |||
| 531 | kfree(ctx); | 535 | kfree(ctx); |
| 532 | } | 536 | } |
| 533 | 537 | ||
| 538 | void put_nfs_open_context(struct nfs_open_context *ctx) | ||
| 539 | { | ||
| 540 | __put_nfs_open_context(ctx, 0); | ||
| 541 | } | ||
| 542 | |||
| 543 | static void put_nfs_open_context_sync(struct nfs_open_context *ctx) | ||
| 544 | { | ||
| 545 | __put_nfs_open_context(ctx, 1); | ||
| 546 | } | ||
| 547 | |||
| 534 | /* | 548 | /* |
| 535 | * Ensure that mmap has a recent RPC credential for use when writing out | 549 | * Ensure that mmap has a recent RPC credential for use when writing out |
| 536 | * shared pages | 550 | * shared pages |
| @@ -577,7 +591,7 @@ static void nfs_file_clear_open_context(struct file *filp) | |||
| 577 | spin_lock(&inode->i_lock); | 591 | spin_lock(&inode->i_lock); |
| 578 | list_move_tail(&ctx->list, &NFS_I(inode)->open_files); | 592 | list_move_tail(&ctx->list, &NFS_I(inode)->open_files); |
| 579 | spin_unlock(&inode->i_lock); | 593 | spin_unlock(&inode->i_lock); |
| 580 | put_nfs_open_context(ctx); | 594 | put_nfs_open_context_sync(ctx); |
| 581 | } | 595 | } |
| 582 | } | 596 | } |
| 583 | 597 | ||
| @@ -1169,6 +1183,9 @@ static void init_once(struct kmem_cache * cachep, void *foo) | |||
| 1169 | INIT_RADIX_TREE(&nfsi->nfs_page_tree, GFP_ATOMIC); | 1183 | INIT_RADIX_TREE(&nfsi->nfs_page_tree, GFP_ATOMIC); |
| 1170 | nfsi->ncommit = 0; | 1184 | nfsi->ncommit = 0; |
| 1171 | nfsi->npages = 0; | 1185 | nfsi->npages = 0; |
| 1186 | atomic_set(&nfsi->silly_count, 1); | ||
| 1187 | INIT_HLIST_HEAD(&nfsi->silly_list); | ||
| 1188 | init_waitqueue_head(&nfsi->waitqueue); | ||
| 1172 | nfs4_init_once(nfsi); | 1189 | nfs4_init_once(nfsi); |
| 1173 | } | 1190 | } |
| 1174 | 1191 | ||
diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index d2802b1ca3b9..a4e3b961b63b 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h | |||
| @@ -178,7 +178,7 @@ extern int nfs4_proc_setclientid(struct nfs_client *, u32, unsigned short, struc | |||
| 178 | extern int nfs4_proc_setclientid_confirm(struct nfs_client *, struct rpc_cred *); | 178 | extern int nfs4_proc_setclientid_confirm(struct nfs_client *, struct rpc_cred *); |
| 179 | extern int nfs4_proc_async_renew(struct nfs_client *, struct rpc_cred *); | 179 | extern int nfs4_proc_async_renew(struct nfs_client *, struct rpc_cred *); |
| 180 | extern int nfs4_proc_renew(struct nfs_client *, struct rpc_cred *); | 180 | extern int nfs4_proc_renew(struct nfs_client *, struct rpc_cred *); |
| 181 | extern int nfs4_do_close(struct path *path, struct nfs4_state *state); | 181 | extern int nfs4_do_close(struct path *path, struct nfs4_state *state, int wait); |
| 182 | extern struct dentry *nfs4_atomic_open(struct inode *, struct dentry *, struct nameidata *); | 182 | extern struct dentry *nfs4_atomic_open(struct inode *, struct dentry *, struct nameidata *); |
| 183 | extern int nfs4_open_revalidate(struct inode *, struct dentry *, int, struct nameidata *); | 183 | extern int nfs4_open_revalidate(struct inode *, struct dentry *, int, struct nameidata *); |
| 184 | extern int nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle); | 184 | extern int nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *fhandle); |
| @@ -209,6 +209,7 @@ extern void nfs4_drop_state_owner(struct nfs4_state_owner *); | |||
| 209 | extern struct nfs4_state * nfs4_get_open_state(struct inode *, struct nfs4_state_owner *); | 209 | extern struct nfs4_state * nfs4_get_open_state(struct inode *, struct nfs4_state_owner *); |
| 210 | extern void nfs4_put_open_state(struct nfs4_state *); | 210 | extern void nfs4_put_open_state(struct nfs4_state *); |
| 211 | extern void nfs4_close_state(struct path *, struct nfs4_state *, mode_t); | 211 | extern void nfs4_close_state(struct path *, struct nfs4_state *, mode_t); |
| 212 | extern void nfs4_close_sync(struct path *, struct nfs4_state *, mode_t); | ||
| 212 | extern void nfs4_state_set_mode_locked(struct nfs4_state *, mode_t); | 213 | extern void nfs4_state_set_mode_locked(struct nfs4_state *, mode_t); |
| 213 | extern void nfs4_schedule_state_recovery(struct nfs_client *); | 214 | extern void nfs4_schedule_state_recovery(struct nfs_client *); |
| 214 | extern void nfs4_put_lock_state(struct nfs4_lock_state *lsp); | 215 | extern void nfs4_put_lock_state(struct nfs4_lock_state *lsp); |
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index cb99fd90a9ac..f03d9d5f5ba4 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c | |||
| @@ -1305,7 +1305,7 @@ static const struct rpc_call_ops nfs4_close_ops = { | |||
| 1305 | * | 1305 | * |
| 1306 | * NOTE: Caller must be holding the sp->so_owner semaphore! | 1306 | * NOTE: Caller must be holding the sp->so_owner semaphore! |
| 1307 | */ | 1307 | */ |
| 1308 | int nfs4_do_close(struct path *path, struct nfs4_state *state) | 1308 | int nfs4_do_close(struct path *path, struct nfs4_state *state, int wait) |
| 1309 | { | 1309 | { |
| 1310 | struct nfs_server *server = NFS_SERVER(state->inode); | 1310 | struct nfs_server *server = NFS_SERVER(state->inode); |
| 1311 | struct nfs4_closedata *calldata; | 1311 | struct nfs4_closedata *calldata; |
| @@ -1333,8 +1333,11 @@ int nfs4_do_close(struct path *path, struct nfs4_state *state) | |||
| 1333 | task = rpc_run_task(server->client, RPC_TASK_ASYNC, &nfs4_close_ops, calldata); | 1333 | task = rpc_run_task(server->client, RPC_TASK_ASYNC, &nfs4_close_ops, calldata); |
| 1334 | if (IS_ERR(task)) | 1334 | if (IS_ERR(task)) |
| 1335 | return PTR_ERR(task); | 1335 | return PTR_ERR(task); |
| 1336 | status = 0; | ||
| 1337 | if (wait) | ||
| 1338 | status = rpc_wait_for_completion_task(task); | ||
| 1336 | rpc_put_task(task); | 1339 | rpc_put_task(task); |
| 1337 | return 0; | 1340 | return status; |
| 1338 | out_free_calldata: | 1341 | out_free_calldata: |
| 1339 | kfree(calldata); | 1342 | kfree(calldata); |
| 1340 | out: | 1343 | out: |
| @@ -1365,13 +1368,14 @@ static int nfs4_intent_set_file(struct nameidata *nd, struct path *path, struct | |||
| 1365 | } | 1368 | } |
| 1366 | ret = PTR_ERR(filp); | 1369 | ret = PTR_ERR(filp); |
| 1367 | out_close: | 1370 | out_close: |
| 1368 | nfs4_close_state(path, state, nd->intent.open.flags); | 1371 | nfs4_close_sync(path, state, nd->intent.open.flags); |
| 1369 | return ret; | 1372 | return ret; |
| 1370 | } | 1373 | } |
| 1371 | 1374 | ||
| 1372 | struct dentry * | 1375 | struct dentry * |
| 1373 | nfs4_atomic_open(struct inode *dir, struct dentry *dentry, struct nameidata *nd) | 1376 | nfs4_atomic_open(struct inode *dir, struct dentry *dentry, struct nameidata *nd) |
| 1374 | { | 1377 | { |
| 1378 | struct dentry *parent; | ||
| 1375 | struct path path = { | 1379 | struct path path = { |
| 1376 | .mnt = nd->mnt, | 1380 | .mnt = nd->mnt, |
| 1377 | .dentry = dentry, | 1381 | .dentry = dentry, |
| @@ -1394,6 +1398,9 @@ nfs4_atomic_open(struct inode *dir, struct dentry *dentry, struct nameidata *nd) | |||
| 1394 | cred = rpcauth_lookupcred(NFS_CLIENT(dir)->cl_auth, 0); | 1398 | cred = rpcauth_lookupcred(NFS_CLIENT(dir)->cl_auth, 0); |
| 1395 | if (IS_ERR(cred)) | 1399 | if (IS_ERR(cred)) |
| 1396 | return (struct dentry *)cred; | 1400 | return (struct dentry *)cred; |
| 1401 | parent = dentry->d_parent; | ||
| 1402 | /* Protect against concurrent sillydeletes */ | ||
| 1403 | nfs_block_sillyrename(parent); | ||
| 1397 | state = nfs4_do_open(dir, &path, nd->intent.open.flags, &attr, cred); | 1404 | state = nfs4_do_open(dir, &path, nd->intent.open.flags, &attr, cred); |
| 1398 | put_rpccred(cred); | 1405 | put_rpccred(cred); |
| 1399 | if (IS_ERR(state)) { | 1406 | if (IS_ERR(state)) { |
| @@ -1401,12 +1408,14 @@ nfs4_atomic_open(struct inode *dir, struct dentry *dentry, struct nameidata *nd) | |||
| 1401 | d_add(dentry, NULL); | 1408 | d_add(dentry, NULL); |
| 1402 | nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); | 1409 | nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); |
| 1403 | } | 1410 | } |
| 1411 | nfs_unblock_sillyrename(parent); | ||
| 1404 | return (struct dentry *)state; | 1412 | return (struct dentry *)state; |
| 1405 | } | 1413 | } |
| 1406 | res = d_add_unique(dentry, igrab(state->inode)); | 1414 | res = d_add_unique(dentry, igrab(state->inode)); |
| 1407 | if (res != NULL) | 1415 | if (res != NULL) |
| 1408 | path.dentry = res; | 1416 | path.dentry = res; |
| 1409 | nfs_set_verifier(path.dentry, nfs_save_change_attribute(dir)); | 1417 | nfs_set_verifier(path.dentry, nfs_save_change_attribute(dir)); |
| 1418 | nfs_unblock_sillyrename(parent); | ||
| 1410 | nfs4_intent_set_file(nd, &path, state); | 1419 | nfs4_intent_set_file(nd, &path, state); |
| 1411 | return res; | 1420 | return res; |
| 1412 | } | 1421 | } |
| @@ -1444,7 +1453,7 @@ nfs4_open_revalidate(struct inode *dir, struct dentry *dentry, int openflags, st | |||
| 1444 | nfs4_intent_set_file(nd, &path, state); | 1453 | nfs4_intent_set_file(nd, &path, state); |
| 1445 | return 1; | 1454 | return 1; |
| 1446 | } | 1455 | } |
| 1447 | nfs4_close_state(&path, state, openflags); | 1456 | nfs4_close_sync(&path, state, openflags); |
| 1448 | out_drop: | 1457 | out_drop: |
| 1449 | d_drop(dentry); | 1458 | d_drop(dentry); |
| 1450 | return 0; | 1459 | return 0; |
| @@ -1898,7 +1907,7 @@ nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr, | |||
| 1898 | if (status == 0 && (nd->flags & LOOKUP_OPEN) != 0) | 1907 | if (status == 0 && (nd->flags & LOOKUP_OPEN) != 0) |
| 1899 | status = nfs4_intent_set_file(nd, &path, state); | 1908 | status = nfs4_intent_set_file(nd, &path, state); |
| 1900 | else | 1909 | else |
| 1901 | nfs4_close_state(&path, state, flags); | 1910 | nfs4_close_sync(&path, state, flags); |
| 1902 | out: | 1911 | out: |
| 1903 | return status; | 1912 | return status; |
| 1904 | } | 1913 | } |
diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index bfb36261cecb..23a9a36556bf 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c | |||
| @@ -425,7 +425,7 @@ void nfs4_put_open_state(struct nfs4_state *state) | |||
| 425 | /* | 425 | /* |
| 426 | * Close the current file. | 426 | * Close the current file. |
| 427 | */ | 427 | */ |
| 428 | void nfs4_close_state(struct path *path, struct nfs4_state *state, mode_t mode) | 428 | static void __nfs4_close(struct path *path, struct nfs4_state *state, mode_t mode, int wait) |
| 429 | { | 429 | { |
| 430 | struct nfs4_state_owner *owner = state->owner; | 430 | struct nfs4_state_owner *owner = state->owner; |
| 431 | int call_close = 0; | 431 | int call_close = 0; |
| @@ -466,7 +466,17 @@ void nfs4_close_state(struct path *path, struct nfs4_state *state, mode_t mode) | |||
| 466 | nfs4_put_open_state(state); | 466 | nfs4_put_open_state(state); |
| 467 | nfs4_put_state_owner(owner); | 467 | nfs4_put_state_owner(owner); |
| 468 | } else | 468 | } else |
| 469 | nfs4_do_close(path, state); | 469 | nfs4_do_close(path, state, wait); |
| 470 | } | ||
| 471 | |||
| 472 | void nfs4_close_state(struct path *path, struct nfs4_state *state, mode_t mode) | ||
| 473 | { | ||
| 474 | __nfs4_close(path, state, mode, 0); | ||
| 475 | } | ||
| 476 | |||
| 477 | void nfs4_close_sync(struct path *path, struct nfs4_state *state, mode_t mode) | ||
| 478 | { | ||
| 479 | __nfs4_close(path, state, mode, 1); | ||
| 470 | } | 480 | } |
| 471 | 481 | ||
| 472 | /* | 482 | /* |
diff --git a/fs/nfs/unlink.c b/fs/nfs/unlink.c index 1aed850d18f2..6ecd46c967c8 100644 --- a/fs/nfs/unlink.c +++ b/fs/nfs/unlink.c | |||
| @@ -14,6 +14,7 @@ | |||
| 14 | 14 | ||
| 15 | 15 | ||
| 16 | struct nfs_unlinkdata { | 16 | struct nfs_unlinkdata { |
| 17 | struct hlist_node list; | ||
| 17 | struct nfs_removeargs args; | 18 | struct nfs_removeargs args; |
| 18 | struct nfs_removeres res; | 19 | struct nfs_removeres res; |
| 19 | struct inode *dir; | 20 | struct inode *dir; |
| @@ -52,6 +53,20 @@ static int nfs_copy_dname(struct dentry *dentry, struct nfs_unlinkdata *data) | |||
| 52 | return 0; | 53 | return 0; |
| 53 | } | 54 | } |
| 54 | 55 | ||
| 56 | static void nfs_free_dname(struct nfs_unlinkdata *data) | ||
| 57 | { | ||
| 58 | kfree(data->args.name.name); | ||
| 59 | data->args.name.name = NULL; | ||
| 60 | data->args.name.len = 0; | ||
| 61 | } | ||
| 62 | |||
| 63 | static void nfs_dec_sillycount(struct inode *dir) | ||
| 64 | { | ||
| 65 | struct nfs_inode *nfsi = NFS_I(dir); | ||
| 66 | if (atomic_dec_return(&nfsi->silly_count) == 1) | ||
| 67 | wake_up(&nfsi->waitqueue); | ||
| 68 | } | ||
| 69 | |||
| 55 | /** | 70 | /** |
| 56 | * nfs_async_unlink_init - Initialize the RPC info | 71 | * nfs_async_unlink_init - Initialize the RPC info |
| 57 | * task: rpc_task of the sillydelete | 72 | * task: rpc_task of the sillydelete |
| @@ -95,6 +110,8 @@ static void nfs_async_unlink_done(struct rpc_task *task, void *calldata) | |||
| 95 | static void nfs_async_unlink_release(void *calldata) | 110 | static void nfs_async_unlink_release(void *calldata) |
| 96 | { | 111 | { |
| 97 | struct nfs_unlinkdata *data = calldata; | 112 | struct nfs_unlinkdata *data = calldata; |
| 113 | |||
| 114 | nfs_dec_sillycount(data->dir); | ||
| 98 | nfs_free_unlinkdata(data); | 115 | nfs_free_unlinkdata(data); |
| 99 | } | 116 | } |
| 100 | 117 | ||
| @@ -104,33 +121,100 @@ static const struct rpc_call_ops nfs_unlink_ops = { | |||
| 104 | .rpc_release = nfs_async_unlink_release, | 121 | .rpc_release = nfs_async_unlink_release, |
| 105 | }; | 122 | }; |
| 106 | 123 | ||
| 107 | static int nfs_call_unlink(struct dentry *dentry, struct nfs_unlinkdata *data) | 124 | static int nfs_do_call_unlink(struct dentry *parent, struct inode *dir, struct nfs_unlinkdata *data) |
| 108 | { | 125 | { |
| 109 | struct rpc_task *task; | 126 | struct rpc_task *task; |
| 127 | struct dentry *alias; | ||
| 128 | |||
| 129 | alias = d_lookup(parent, &data->args.name); | ||
| 130 | if (alias != NULL) { | ||
| 131 | int ret = 0; | ||
| 132 | /* | ||
| 133 | * Hey, we raced with lookup... See if we need to transfer | ||
| 134 | * the sillyrename information to the aliased dentry. | ||
| 135 | */ | ||
| 136 | nfs_free_dname(data); | ||
| 137 | spin_lock(&alias->d_lock); | ||
| 138 | if (!(alias->d_flags & DCACHE_NFSFS_RENAMED)) { | ||
| 139 | alias->d_fsdata = data; | ||
| 140 | alias->d_flags ^= DCACHE_NFSFS_RENAMED; | ||
| 141 | ret = 1; | ||
| 142 | } | ||
| 143 | spin_unlock(&alias->d_lock); | ||
| 144 | nfs_dec_sillycount(dir); | ||
| 145 | dput(alias); | ||
| 146 | return ret; | ||
| 147 | } | ||
| 148 | data->dir = igrab(dir); | ||
| 149 | if (!data->dir) { | ||
| 150 | nfs_dec_sillycount(dir); | ||
| 151 | return 0; | ||
| 152 | } | ||
| 153 | data->args.fh = NFS_FH(dir); | ||
| 154 | nfs_fattr_init(&data->res.dir_attr); | ||
| 155 | |||
| 156 | task = rpc_run_task(NFS_CLIENT(dir), RPC_TASK_ASYNC, &nfs_unlink_ops, data); | ||
| 157 | if (!IS_ERR(task)) | ||
| 158 | rpc_put_task(task); | ||
| 159 | return 1; | ||
| 160 | } | ||
| 161 | |||
| 162 | static int nfs_call_unlink(struct dentry *dentry, struct nfs_unlinkdata *data) | ||
| 163 | { | ||
| 110 | struct dentry *parent; | 164 | struct dentry *parent; |
| 111 | struct inode *dir; | 165 | struct inode *dir; |
| 166 | int ret = 0; | ||
| 112 | 167 | ||
| 113 | if (nfs_copy_dname(dentry, data) < 0) | ||
| 114 | goto out_free; | ||
| 115 | 168 | ||
| 116 | parent = dget_parent(dentry); | 169 | parent = dget_parent(dentry); |
| 117 | if (parent == NULL) | 170 | if (parent == NULL) |
| 118 | goto out_free; | 171 | goto out_free; |
| 119 | dir = igrab(parent->d_inode); | 172 | dir = parent->d_inode; |
| 173 | if (nfs_copy_dname(dentry, data) == 0) | ||
| 174 | goto out_dput; | ||
| 175 | /* Non-exclusive lock protects against concurrent lookup() calls */ | ||
| 176 | spin_lock(&dir->i_lock); | ||
| 177 | if (atomic_inc_not_zero(&NFS_I(dir)->silly_count) == 0) { | ||
| 178 | /* Deferred delete */ | ||
| 179 | hlist_add_head(&data->list, &NFS_I(dir)->silly_list); | ||
| 180 | spin_unlock(&dir->i_lock); | ||
| 181 | ret = 1; | ||
| 182 | goto out_dput; | ||
| 183 | } | ||
| 184 | spin_unlock(&dir->i_lock); | ||
| 185 | ret = nfs_do_call_unlink(parent, dir, data); | ||
| 186 | out_dput: | ||
| 120 | dput(parent); | 187 | dput(parent); |
| 121 | if (dir == NULL) | 188 | out_free: |
| 122 | goto out_free; | 189 | return ret; |
| 190 | } | ||
| 123 | 191 | ||
| 124 | data->dir = dir; | 192 | void nfs_block_sillyrename(struct dentry *dentry) |
| 125 | data->args.fh = NFS_FH(dir); | 193 | { |
| 126 | nfs_fattr_init(&data->res.dir_attr); | 194 | struct nfs_inode *nfsi = NFS_I(dentry->d_inode); |
| 127 | 195 | ||
| 128 | task = rpc_run_task(NFS_CLIENT(dir), RPC_TASK_ASYNC, &nfs_unlink_ops, data); | 196 | wait_event(nfsi->waitqueue, atomic_cmpxchg(&nfsi->silly_count, 1, 0) == 1); |
| 129 | if (!IS_ERR(task)) | 197 | } |
| 130 | rpc_put_task(task); | 198 | |
| 131 | return 1; | 199 | void nfs_unblock_sillyrename(struct dentry *dentry) |
| 132 | out_free: | 200 | { |
| 133 | return 0; | 201 | struct inode *dir = dentry->d_inode; |
| 202 | struct nfs_inode *nfsi = NFS_I(dir); | ||
| 203 | struct nfs_unlinkdata *data; | ||
| 204 | |||
| 205 | atomic_inc(&nfsi->silly_count); | ||
| 206 | spin_lock(&dir->i_lock); | ||
| 207 | while (!hlist_empty(&nfsi->silly_list)) { | ||
| 208 | if (!atomic_inc_not_zero(&nfsi->silly_count)) | ||
| 209 | break; | ||
| 210 | data = hlist_entry(nfsi->silly_list.first, struct nfs_unlinkdata, list); | ||
| 211 | hlist_del(&data->list); | ||
| 212 | spin_unlock(&dir->i_lock); | ||
| 213 | if (nfs_do_call_unlink(dentry, dir, data) == 0) | ||
| 214 | nfs_free_unlinkdata(data); | ||
| 215 | spin_lock(&dir->i_lock); | ||
| 216 | } | ||
| 217 | spin_unlock(&dir->i_lock); | ||
| 134 | } | 218 | } |
| 135 | 219 | ||
| 136 | /** | 220 | /** |
diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 0cf9d1cd9bd2..89527a487ed7 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c | |||
| @@ -174,8 +174,6 @@ static void nfs_mark_uptodate(struct page *page, unsigned int base, unsigned int | |||
| 174 | return; | 174 | return; |
| 175 | if (count != nfs_page_length(page)) | 175 | if (count != nfs_page_length(page)) |
| 176 | return; | 176 | return; |
| 177 | if (count != PAGE_CACHE_SIZE) | ||
| 178 | zero_user_page(page, count, PAGE_CACHE_SIZE - count, KM_USER0); | ||
| 179 | SetPageUptodate(page); | 177 | SetPageUptodate(page); |
| 180 | } | 178 | } |
| 181 | 179 | ||
| @@ -627,7 +625,8 @@ static struct nfs_page * nfs_update_request(struct nfs_open_context* ctx, | |||
| 627 | return ERR_PTR(error); | 625 | return ERR_PTR(error); |
| 628 | } | 626 | } |
| 629 | spin_unlock(&inode->i_lock); | 627 | spin_unlock(&inode->i_lock); |
| 630 | return new; | 628 | req = new; |
| 629 | goto zero_page; | ||
| 631 | } | 630 | } |
| 632 | spin_unlock(&inode->i_lock); | 631 | spin_unlock(&inode->i_lock); |
| 633 | 632 | ||
| @@ -655,13 +654,23 @@ static struct nfs_page * nfs_update_request(struct nfs_open_context* ctx, | |||
| 655 | if (offset < req->wb_offset) { | 654 | if (offset < req->wb_offset) { |
| 656 | req->wb_offset = offset; | 655 | req->wb_offset = offset; |
| 657 | req->wb_pgbase = offset; | 656 | req->wb_pgbase = offset; |
| 658 | req->wb_bytes = rqend - req->wb_offset; | 657 | req->wb_bytes = max(end, rqend) - req->wb_offset; |
| 658 | goto zero_page; | ||
| 659 | } | 659 | } |
| 660 | 660 | ||
| 661 | if (end > rqend) | 661 | if (end > rqend) |
| 662 | req->wb_bytes = end - req->wb_offset; | 662 | req->wb_bytes = end - req->wb_offset; |
| 663 | 663 | ||
| 664 | return req; | 664 | return req; |
| 665 | zero_page: | ||
| 666 | /* If this page might potentially be marked as up to date, | ||
| 667 | * then we need to zero any uninitalised data. */ | ||
| 668 | if (req->wb_pgbase == 0 && req->wb_bytes != PAGE_CACHE_SIZE | ||
| 669 | && !PageUptodate(req->wb_page)) | ||
| 670 | zero_user_page(req->wb_page, req->wb_bytes, | ||
| 671 | PAGE_CACHE_SIZE - req->wb_bytes, | ||
| 672 | KM_USER0); | ||
| 673 | return req; | ||
| 665 | } | 674 | } |
| 666 | 675 | ||
| 667 | int nfs_flush_incompatible(struct file *file, struct page *page) | 676 | int nfs_flush_incompatible(struct file *file, struct page *page) |
