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 | |
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...
-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 | ||||
-rw-r--r-- | include/linux/nfs_fs.h | 8 |
10 files changed, 183 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) |
diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index c5164c257f71..e82a6ebc725d 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h | |||
@@ -160,6 +160,12 @@ struct nfs_inode { | |||
160 | /* Open contexts for shared mmap writes */ | 160 | /* Open contexts for shared mmap writes */ |
161 | struct list_head open_files; | 161 | struct list_head open_files; |
162 | 162 | ||
163 | /* Number of in-flight sillydelete RPC calls */ | ||
164 | atomic_t silly_count; | ||
165 | /* List of deferred sillydelete requests */ | ||
166 | struct hlist_head silly_list; | ||
167 | wait_queue_head_t waitqueue; | ||
168 | |||
163 | #ifdef CONFIG_NFS_V4 | 169 | #ifdef CONFIG_NFS_V4 |
164 | struct nfs4_cached_acl *nfs4_acl; | 170 | struct nfs4_cached_acl *nfs4_acl; |
165 | /* NFSv4 state */ | 171 | /* NFSv4 state */ |
@@ -394,6 +400,8 @@ extern void nfs_release_automount_timer(void); | |||
394 | */ | 400 | */ |
395 | extern int nfs_async_unlink(struct inode *dir, struct dentry *dentry); | 401 | extern int nfs_async_unlink(struct inode *dir, struct dentry *dentry); |
396 | extern void nfs_complete_unlink(struct dentry *dentry, struct inode *); | 402 | extern void nfs_complete_unlink(struct dentry *dentry, struct inode *); |
403 | extern void nfs_block_sillyrename(struct dentry *dentry); | ||
404 | extern void nfs_unblock_sillyrename(struct dentry *dentry); | ||
397 | 405 | ||
398 | /* | 406 | /* |
399 | * linux/fs/nfs/write.c | 407 | * linux/fs/nfs/write.c |