diff options
-rw-r--r-- | CREDITS | 5 | ||||
-rw-r--r-- | Documentation/filesystems/nfs/00-INDEX | 2 | ||||
-rw-r--r-- | Documentation/filesystems/nfs/fault_injection.txt | 69 | ||||
-rw-r--r-- | MAINTAINERS | 1 | ||||
-rw-r--r-- | fs/nfsd/Kconfig | 10 | ||||
-rw-r--r-- | fs/nfsd/Makefile | 1 | ||||
-rw-r--r-- | fs/nfsd/export.c | 12 | ||||
-rw-r--r-- | fs/nfsd/fault_inject.c | 91 | ||||
-rw-r--r-- | fs/nfsd/fault_inject.h | 28 | ||||
-rw-r--r-- | fs/nfsd/nfs4idmap.c | 11 | ||||
-rw-r--r-- | fs/nfsd/nfs4proc.c | 7 | ||||
-rw-r--r-- | fs/nfsd/nfs4recover.c | 22 | ||||
-rw-r--r-- | fs/nfsd/nfs4state.c | 328 | ||||
-rw-r--r-- | fs/nfsd/nfs4xdr.c | 3 | ||||
-rw-r--r-- | fs/nfsd/nfsctl.c | 10 | ||||
-rw-r--r-- | fs/nfsd/nfsd.h | 20 | ||||
-rw-r--r-- | fs/nfsd/state.h | 3 | ||||
-rw-r--r-- | fs/nfsd/vfs.c | 17 | ||||
-rw-r--r-- | include/linux/sunrpc/svc_xprt.h | 3 | ||||
-rw-r--r-- | include/linux/sunrpc/svcsock.h | 2 | ||||
-rw-r--r-- | net/sunrpc/cache.c | 2 | ||||
-rw-r--r-- | net/sunrpc/svc.c | 25 | ||||
-rw-r--r-- | net/sunrpc/svc_xprt.c | 62 | ||||
-rw-r--r-- | net/sunrpc/svcsock.c | 8 | ||||
-rw-r--r-- | net/sunrpc/xprtrdma/svc_rdma_transport.c | 2 | ||||
-rwxr-xr-x | tools/nfsd/inject_fault.sh | 49 |
26 files changed, 624 insertions, 169 deletions
@@ -514,6 +514,11 @@ S: Bessemerstraat 21 | |||
514 | S: Amsterdam | 514 | S: Amsterdam |
515 | S: The Netherlands | 515 | S: The Netherlands |
516 | 516 | ||
517 | N: NeilBrown | ||
518 | E: neil@brown.name | ||
519 | P: 4096R/566281B9 1BC6 29EB D390 D870 7B5F 497A 39EC 9EDD 5662 81B9 | ||
520 | D: NFSD Maintainer 2000-2007 | ||
521 | |||
517 | N: Zach Brown | 522 | N: Zach Brown |
518 | E: zab@zabbo.net | 523 | E: zab@zabbo.net |
519 | D: maestro pci sound | 524 | D: maestro pci sound |
diff --git a/Documentation/filesystems/nfs/00-INDEX b/Documentation/filesystems/nfs/00-INDEX index a57e12411d2a..1716874a651e 100644 --- a/Documentation/filesystems/nfs/00-INDEX +++ b/Documentation/filesystems/nfs/00-INDEX | |||
@@ -2,6 +2,8 @@ | |||
2 | - this file (nfs-related documentation). | 2 | - this file (nfs-related documentation). |
3 | Exporting | 3 | Exporting |
4 | - explanation of how to make filesystems exportable. | 4 | - explanation of how to make filesystems exportable. |
5 | fault_injection.txt | ||
6 | - information for using fault injection on the server | ||
5 | knfsd-stats.txt | 7 | knfsd-stats.txt |
6 | - statistics which the NFS server makes available to user space. | 8 | - statistics which the NFS server makes available to user space. |
7 | nfs.txt | 9 | nfs.txt |
diff --git a/Documentation/filesystems/nfs/fault_injection.txt b/Documentation/filesystems/nfs/fault_injection.txt new file mode 100644 index 000000000000..426d166089a3 --- /dev/null +++ b/Documentation/filesystems/nfs/fault_injection.txt | |||
@@ -0,0 +1,69 @@ | |||
1 | |||
2 | Fault Injection | ||
3 | =============== | ||
4 | Fault injection is a method for forcing errors that may not normally occur, or | ||
5 | may be difficult to reproduce. Forcing these errors in a controlled environment | ||
6 | can help the developer find and fix bugs before their code is shipped in a | ||
7 | production system. Injecting an error on the Linux NFS server will allow us to | ||
8 | observe how the client reacts and if it manages to recover its state correctly. | ||
9 | |||
10 | NFSD_FAULT_INJECTION must be selected when configuring the kernel to use this | ||
11 | feature. | ||
12 | |||
13 | |||
14 | Using Fault Injection | ||
15 | ===================== | ||
16 | On the client, mount the fault injection server through NFS v4.0+ and do some | ||
17 | work over NFS (open files, take locks, ...). | ||
18 | |||
19 | On the server, mount the debugfs filesystem to <debug_dir> and ls | ||
20 | <debug_dir>/nfsd. This will show a list of files that will be used for | ||
21 | injecting faults on the NFS server. As root, write a number n to the file | ||
22 | corresponding to the action you want the server to take. The server will then | ||
23 | process the first n items it finds. So if you want to forget 5 locks, echo '5' | ||
24 | to <debug_dir>/nfsd/forget_locks. A value of 0 will tell the server to forget | ||
25 | all corresponding items. A log message will be created containing the number | ||
26 | of items forgotten (check dmesg). | ||
27 | |||
28 | Go back to work on the client and check if the client recovered from the error | ||
29 | correctly. | ||
30 | |||
31 | |||
32 | Available Faults | ||
33 | ================ | ||
34 | forget_clients: | ||
35 | The NFS server keeps a list of clients that have placed a mount call. If | ||
36 | this list is cleared, the server will have no knowledge of who the client | ||
37 | is, forcing the client to reauthenticate with the server. | ||
38 | |||
39 | forget_openowners: | ||
40 | The NFS server keeps a list of what files are currently opened and who | ||
41 | they were opened by. Clearing this list will force the client to reopen | ||
42 | its files. | ||
43 | |||
44 | forget_locks: | ||
45 | The NFS server keeps a list of what files are currently locked in the VFS. | ||
46 | Clearing this list will force the client to reclaim its locks (files are | ||
47 | unlocked through the VFS as they are cleared from this list). | ||
48 | |||
49 | forget_delegations: | ||
50 | A delegation is used to assure the client that a file, or part of a file, | ||
51 | has not changed since the delegation was awarded. Clearing this list will | ||
52 | force the client to reaquire its delegation before accessing the file | ||
53 | again. | ||
54 | |||
55 | recall_delegations: | ||
56 | Delegations can be recalled by the server when another client attempts to | ||
57 | access a file. This test will notify the client that its delegation has | ||
58 | been revoked, forcing the client to reaquire the delegation before using | ||
59 | the file again. | ||
60 | |||
61 | |||
62 | tools/nfs/inject_faults.sh script | ||
63 | ================================= | ||
64 | This script has been created to ease the fault injection process. This script | ||
65 | will detect the mounted debugfs directory and write to the files located there | ||
66 | based on the arguments passed by the user. For example, running | ||
67 | `inject_faults.sh forget_locks 1` as root will instruct the server to forget | ||
68 | one lock. Running `inject_faults forget_locks` will instruct the server to | ||
69 | forgetall locks. | ||
diff --git a/MAINTAINERS b/MAINTAINERS index 7559c1ca56ba..4d1ba2022a95 100644 --- a/MAINTAINERS +++ b/MAINTAINERS | |||
@@ -3775,7 +3775,6 @@ S: Odd Fixes | |||
3775 | 3775 | ||
3776 | KERNEL NFSD, SUNRPC, AND LOCKD SERVERS | 3776 | KERNEL NFSD, SUNRPC, AND LOCKD SERVERS |
3777 | M: "J. Bruce Fields" <bfields@fieldses.org> | 3777 | M: "J. Bruce Fields" <bfields@fieldses.org> |
3778 | M: Neil Brown <neilb@suse.de> | ||
3779 | L: linux-nfs@vger.kernel.org | 3778 | L: linux-nfs@vger.kernel.org |
3780 | W: http://nfs.sourceforge.net/ | 3779 | W: http://nfs.sourceforge.net/ |
3781 | S: Supported | 3780 | S: Supported |
diff --git a/fs/nfsd/Kconfig b/fs/nfsd/Kconfig index 10e6366608f2..8df1ea4a6ff9 100644 --- a/fs/nfsd/Kconfig +++ b/fs/nfsd/Kconfig | |||
@@ -80,3 +80,13 @@ config NFSD_V4 | |||
80 | available from http://linux-nfs.org/. | 80 | available from http://linux-nfs.org/. |
81 | 81 | ||
82 | If unsure, say N. | 82 | If unsure, say N. |
83 | |||
84 | config NFSD_FAULT_INJECTION | ||
85 | bool "NFS server manual fault injection" | ||
86 | depends on NFSD_V4 && DEBUG_KERNEL | ||
87 | help | ||
88 | This option enables support for manually injecting faults | ||
89 | into the NFS server. This is intended to be used for | ||
90 | testing error recovery on the NFS client. | ||
91 | |||
92 | If unsure, say N. | ||
diff --git a/fs/nfsd/Makefile b/fs/nfsd/Makefile index 9b118ee20193..af32ef06b4fe 100644 --- a/fs/nfsd/Makefile +++ b/fs/nfsd/Makefile | |||
@@ -6,6 +6,7 @@ obj-$(CONFIG_NFSD) += nfsd.o | |||
6 | 6 | ||
7 | nfsd-y := nfssvc.o nfsctl.o nfsproc.o nfsfh.o vfs.o \ | 7 | nfsd-y := nfssvc.o nfsctl.o nfsproc.o nfsfh.o vfs.o \ |
8 | export.o auth.o lockd.o nfscache.o nfsxdr.o stats.o | 8 | export.o auth.o lockd.o nfscache.o nfsxdr.o stats.o |
9 | nfsd-$(CONFIG_NFSD_FAULT_INJECTION) += fault_inject.o | ||
9 | nfsd-$(CONFIG_NFSD_V2_ACL) += nfs2acl.o | 10 | nfsd-$(CONFIG_NFSD_V2_ACL) += nfs2acl.o |
10 | nfsd-$(CONFIG_NFSD_V3) += nfs3proc.o nfs3xdr.o | 11 | nfsd-$(CONFIG_NFSD_V3) += nfs3proc.o nfs3xdr.o |
11 | nfsd-$(CONFIG_NFSD_V3_ACL) += nfs3acl.o | 12 | nfsd-$(CONFIG_NFSD_V3_ACL) += nfs3acl.o |
diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c index 62f3b9074e84..cf8a6bd062fa 100644 --- a/fs/nfsd/export.c +++ b/fs/nfsd/export.c | |||
@@ -87,7 +87,7 @@ static int expkey_parse(struct cache_detail *cd, char *mesg, int mlen) | |||
87 | struct svc_expkey key; | 87 | struct svc_expkey key; |
88 | struct svc_expkey *ek = NULL; | 88 | struct svc_expkey *ek = NULL; |
89 | 89 | ||
90 | if (mesg[mlen-1] != '\n') | 90 | if (mlen < 1 || mesg[mlen-1] != '\n') |
91 | return -EINVAL; | 91 | return -EINVAL; |
92 | mesg[mlen-1] = 0; | 92 | mesg[mlen-1] = 0; |
93 | 93 | ||
@@ -1226,12 +1226,12 @@ nfsd_export_init(void) | |||
1226 | int rv; | 1226 | int rv; |
1227 | dprintk("nfsd: initializing export module.\n"); | 1227 | dprintk("nfsd: initializing export module.\n"); |
1228 | 1228 | ||
1229 | rv = cache_register(&svc_export_cache); | 1229 | rv = cache_register_net(&svc_export_cache, &init_net); |
1230 | if (rv) | 1230 | if (rv) |
1231 | return rv; | 1231 | return rv; |
1232 | rv = cache_register(&svc_expkey_cache); | 1232 | rv = cache_register_net(&svc_expkey_cache, &init_net); |
1233 | if (rv) | 1233 | if (rv) |
1234 | cache_unregister(&svc_export_cache); | 1234 | cache_unregister_net(&svc_export_cache, &init_net); |
1235 | return rv; | 1235 | return rv; |
1236 | 1236 | ||
1237 | } | 1237 | } |
@@ -1255,8 +1255,8 @@ nfsd_export_shutdown(void) | |||
1255 | 1255 | ||
1256 | dprintk("nfsd: shutting down export module.\n"); | 1256 | dprintk("nfsd: shutting down export module.\n"); |
1257 | 1257 | ||
1258 | cache_unregister(&svc_expkey_cache); | 1258 | cache_unregister_net(&svc_expkey_cache, &init_net); |
1259 | cache_unregister(&svc_export_cache); | 1259 | cache_unregister_net(&svc_export_cache, &init_net); |
1260 | svcauth_unix_purge(); | 1260 | svcauth_unix_purge(); |
1261 | 1261 | ||
1262 | dprintk("nfsd: export shutdown complete.\n"); | 1262 | dprintk("nfsd: export shutdown complete.\n"); |
diff --git a/fs/nfsd/fault_inject.c b/fs/nfsd/fault_inject.c new file mode 100644 index 000000000000..ce7f0758d84c --- /dev/null +++ b/fs/nfsd/fault_inject.c | |||
@@ -0,0 +1,91 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2011 Bryan Schumaker <bjschuma@netapp.com> | ||
3 | * | ||
4 | * Uses debugfs to create fault injection points for client testing | ||
5 | */ | ||
6 | |||
7 | #include <linux/types.h> | ||
8 | #include <linux/fs.h> | ||
9 | #include <linux/debugfs.h> | ||
10 | #include <linux/module.h> | ||
11 | |||
12 | #include "state.h" | ||
13 | #include "fault_inject.h" | ||
14 | |||
15 | struct nfsd_fault_inject_op { | ||
16 | char *file; | ||
17 | void (*func)(u64); | ||
18 | }; | ||
19 | |||
20 | static struct nfsd_fault_inject_op inject_ops[] = { | ||
21 | { | ||
22 | .file = "forget_clients", | ||
23 | .func = nfsd_forget_clients, | ||
24 | }, | ||
25 | { | ||
26 | .file = "forget_locks", | ||
27 | .func = nfsd_forget_locks, | ||
28 | }, | ||
29 | { | ||
30 | .file = "forget_openowners", | ||
31 | .func = nfsd_forget_openowners, | ||
32 | }, | ||
33 | { | ||
34 | .file = "forget_delegations", | ||
35 | .func = nfsd_forget_delegations, | ||
36 | }, | ||
37 | { | ||
38 | .file = "recall_delegations", | ||
39 | .func = nfsd_recall_delegations, | ||
40 | }, | ||
41 | }; | ||
42 | |||
43 | static long int NUM_INJECT_OPS = sizeof(inject_ops) / sizeof(struct nfsd_fault_inject_op); | ||
44 | static struct dentry *debug_dir; | ||
45 | |||
46 | static int nfsd_inject_set(void *op_ptr, u64 val) | ||
47 | { | ||
48 | struct nfsd_fault_inject_op *op = op_ptr; | ||
49 | |||
50 | if (val == 0) | ||
51 | printk(KERN_INFO "NFSD Fault Injection: %s (all)", op->file); | ||
52 | else | ||
53 | printk(KERN_INFO "NFSD Fault Injection: %s (n = %llu)", op->file, val); | ||
54 | |||
55 | op->func(val); | ||
56 | return 0; | ||
57 | } | ||
58 | |||
59 | static int nfsd_inject_get(void *data, u64 *val) | ||
60 | { | ||
61 | return 0; | ||
62 | } | ||
63 | |||
64 | DEFINE_SIMPLE_ATTRIBUTE(fops_nfsd, nfsd_inject_get, nfsd_inject_set, "%llu\n"); | ||
65 | |||
66 | void nfsd_fault_inject_cleanup(void) | ||
67 | { | ||
68 | debugfs_remove_recursive(debug_dir); | ||
69 | } | ||
70 | |||
71 | int nfsd_fault_inject_init(void) | ||
72 | { | ||
73 | unsigned int i; | ||
74 | struct nfsd_fault_inject_op *op; | ||
75 | mode_t mode = S_IFREG | S_IRUSR | S_IWUSR; | ||
76 | |||
77 | debug_dir = debugfs_create_dir("nfsd", NULL); | ||
78 | if (!debug_dir) | ||
79 | goto fail; | ||
80 | |||
81 | for (i = 0; i < NUM_INJECT_OPS; i++) { | ||
82 | op = &inject_ops[i]; | ||
83 | if (!debugfs_create_file(op->file, mode, debug_dir, op, &fops_nfsd)) | ||
84 | goto fail; | ||
85 | } | ||
86 | return 0; | ||
87 | |||
88 | fail: | ||
89 | nfsd_fault_inject_cleanup(); | ||
90 | return -ENOMEM; | ||
91 | } | ||
diff --git a/fs/nfsd/fault_inject.h b/fs/nfsd/fault_inject.h new file mode 100644 index 000000000000..90bd0570956c --- /dev/null +++ b/fs/nfsd/fault_inject.h | |||
@@ -0,0 +1,28 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2011 Bryan Schumaker <bjschuma@netapp.com> | ||
3 | * | ||
4 | * Function definitions for fault injection | ||
5 | */ | ||
6 | |||
7 | #ifndef LINUX_NFSD_FAULT_INJECT_H | ||
8 | #define LINUX_NFSD_FAULT_INJECT_H | ||
9 | |||
10 | #ifdef CONFIG_NFSD_FAULT_INJECTION | ||
11 | int nfsd_fault_inject_init(void); | ||
12 | void nfsd_fault_inject_cleanup(void); | ||
13 | void nfsd_forget_clients(u64); | ||
14 | void nfsd_forget_locks(u64); | ||
15 | void nfsd_forget_openowners(u64); | ||
16 | void nfsd_forget_delegations(u64); | ||
17 | void nfsd_recall_delegations(u64); | ||
18 | #else /* CONFIG_NFSD_FAULT_INJECTION */ | ||
19 | static inline int nfsd_fault_inject_init(void) { return 0; } | ||
20 | static inline void nfsd_fault_inject_cleanup(void) {} | ||
21 | static inline void nfsd_forget_clients(u64 num) {} | ||
22 | static inline void nfsd_forget_locks(u64 num) {} | ||
23 | static inline void nfsd_forget_openowners(u64 num) {} | ||
24 | static inline void nfsd_forget_delegations(u64 num) {} | ||
25 | static inline void nfsd_recall_delegations(u64 num) {} | ||
26 | #endif /* CONFIG_NFSD_FAULT_INJECTION */ | ||
27 | |||
28 | #endif /* LINUX_NFSD_FAULT_INJECT_H */ | ||
diff --git a/fs/nfsd/nfs4idmap.c b/fs/nfsd/nfs4idmap.c index 55780a22fdbd..94096273cd6c 100644 --- a/fs/nfsd/nfs4idmap.c +++ b/fs/nfsd/nfs4idmap.c | |||
@@ -36,6 +36,7 @@ | |||
36 | #include <linux/seq_file.h> | 36 | #include <linux/seq_file.h> |
37 | #include <linux/sched.h> | 37 | #include <linux/sched.h> |
38 | #include <linux/slab.h> | 38 | #include <linux/slab.h> |
39 | #include <net/net_namespace.h> | ||
39 | #include "idmap.h" | 40 | #include "idmap.h" |
40 | #include "nfsd.h" | 41 | #include "nfsd.h" |
41 | 42 | ||
@@ -466,20 +467,20 @@ nfsd_idmap_init(void) | |||
466 | { | 467 | { |
467 | int rv; | 468 | int rv; |
468 | 469 | ||
469 | rv = cache_register(&idtoname_cache); | 470 | rv = cache_register_net(&idtoname_cache, &init_net); |
470 | if (rv) | 471 | if (rv) |
471 | return rv; | 472 | return rv; |
472 | rv = cache_register(&nametoid_cache); | 473 | rv = cache_register_net(&nametoid_cache, &init_net); |
473 | if (rv) | 474 | if (rv) |
474 | cache_unregister(&idtoname_cache); | 475 | cache_unregister_net(&idtoname_cache, &init_net); |
475 | return rv; | 476 | return rv; |
476 | } | 477 | } |
477 | 478 | ||
478 | void | 479 | void |
479 | nfsd_idmap_shutdown(void) | 480 | nfsd_idmap_shutdown(void) |
480 | { | 481 | { |
481 | cache_unregister(&idtoname_cache); | 482 | cache_unregister_net(&idtoname_cache, &init_net); |
482 | cache_unregister(&nametoid_cache); | 483 | cache_unregister_net(&nametoid_cache, &init_net); |
483 | } | 484 | } |
484 | 485 | ||
485 | static int | 486 | static int |
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index c5e28ed8bca0..896da74ec563 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c | |||
@@ -266,10 +266,6 @@ do_open_fhandle(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_ | |||
266 | { | 266 | { |
267 | __be32 status; | 267 | __be32 status; |
268 | 268 | ||
269 | /* Only reclaims from previously confirmed clients are valid */ | ||
270 | if ((status = nfs4_check_open_reclaim(&open->op_clientid))) | ||
271 | return status; | ||
272 | |||
273 | /* We don't know the target directory, and therefore can not | 269 | /* We don't know the target directory, and therefore can not |
274 | * set the change info | 270 | * set the change info |
275 | */ | 271 | */ |
@@ -373,6 +369,9 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
373 | break; | 369 | break; |
374 | case NFS4_OPEN_CLAIM_PREVIOUS: | 370 | case NFS4_OPEN_CLAIM_PREVIOUS: |
375 | open->op_openowner->oo_flags |= NFS4_OO_CONFIRMED; | 371 | open->op_openowner->oo_flags |= NFS4_OO_CONFIRMED; |
372 | status = nfs4_check_open_reclaim(&open->op_clientid); | ||
373 | if (status) | ||
374 | goto out; | ||
376 | case NFS4_OPEN_CLAIM_FH: | 375 | case NFS4_OPEN_CLAIM_FH: |
377 | case NFS4_OPEN_CLAIM_DELEG_CUR_FH: | 376 | case NFS4_OPEN_CLAIM_DELEG_CUR_FH: |
378 | status = do_open_fhandle(rqstp, &cstate->current_fh, | 377 | status = do_open_fhandle(rqstp, &cstate->current_fh, |
diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c index 80a0be9ed008..0b3e875d1abd 100644 --- a/fs/nfsd/nfs4recover.c +++ b/fs/nfsd/nfs4recover.c | |||
@@ -117,8 +117,7 @@ out_no_tfm: | |||
117 | return status; | 117 | return status; |
118 | } | 118 | } |
119 | 119 | ||
120 | int | 120 | void nfsd4_create_clid_dir(struct nfs4_client *clp) |
121 | nfsd4_create_clid_dir(struct nfs4_client *clp) | ||
122 | { | 121 | { |
123 | const struct cred *original_cred; | 122 | const struct cred *original_cred; |
124 | char *dname = clp->cl_recdir; | 123 | char *dname = clp->cl_recdir; |
@@ -127,13 +126,14 @@ nfsd4_create_clid_dir(struct nfs4_client *clp) | |||
127 | 126 | ||
128 | dprintk("NFSD: nfsd4_create_clid_dir for \"%s\"\n", dname); | 127 | dprintk("NFSD: nfsd4_create_clid_dir for \"%s\"\n", dname); |
129 | 128 | ||
130 | if (!rec_file || clp->cl_firststate) | 129 | if (clp->cl_firststate) |
131 | return 0; | 130 | return; |
132 | |||
133 | clp->cl_firststate = 1; | 131 | clp->cl_firststate = 1; |
132 | if (!rec_file) | ||
133 | return; | ||
134 | status = nfs4_save_creds(&original_cred); | 134 | status = nfs4_save_creds(&original_cred); |
135 | if (status < 0) | 135 | if (status < 0) |
136 | return status; | 136 | return; |
137 | 137 | ||
138 | dir = rec_file->f_path.dentry; | 138 | dir = rec_file->f_path.dentry; |
139 | /* lock the parent */ | 139 | /* lock the parent */ |
@@ -144,8 +144,15 @@ nfsd4_create_clid_dir(struct nfs4_client *clp) | |||
144 | status = PTR_ERR(dentry); | 144 | status = PTR_ERR(dentry); |
145 | goto out_unlock; | 145 | goto out_unlock; |
146 | } | 146 | } |
147 | status = -EEXIST; | ||
148 | if (dentry->d_inode) | 147 | if (dentry->d_inode) |
148 | /* | ||
149 | * In the 4.1 case, where we're called from | ||
150 | * reclaim_complete(), records from the previous reboot | ||
151 | * may still be left, so this is OK. | ||
152 | * | ||
153 | * In the 4.0 case, we should never get here; but we may | ||
154 | * as well be forgiving and just succeed silently. | ||
155 | */ | ||
149 | goto out_put; | 156 | goto out_put; |
150 | status = mnt_want_write_file(rec_file); | 157 | status = mnt_want_write_file(rec_file); |
151 | if (status) | 158 | if (status) |
@@ -164,7 +171,6 @@ out_unlock: | |||
164 | " and is writeable", status, | 171 | " and is writeable", status, |
165 | user_recovery_dirname); | 172 | user_recovery_dirname); |
166 | nfs4_reset_creds(original_cred); | 173 | nfs4_reset_creds(original_cred); |
167 | return status; | ||
168 | } | 174 | } |
169 | 175 | ||
170 | typedef int (recdir_func)(struct dentry *, struct dentry *); | 176 | typedef int (recdir_func)(struct dentry *, struct dentry *); |
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 9ca16dc09e04..e8c98f009670 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c | |||
@@ -49,12 +49,20 @@ | |||
49 | time_t nfsd4_lease = 90; /* default lease time */ | 49 | time_t nfsd4_lease = 90; /* default lease time */ |
50 | time_t nfsd4_grace = 90; | 50 | time_t nfsd4_grace = 90; |
51 | static time_t boot_time; | 51 | static time_t boot_time; |
52 | static stateid_t zerostateid; /* bits all 0 */ | 52 | |
53 | static stateid_t onestateid; /* bits all 1 */ | 53 | #define all_ones {{~0,~0},~0} |
54 | static const stateid_t one_stateid = { | ||
55 | .si_generation = ~0, | ||
56 | .si_opaque = all_ones, | ||
57 | }; | ||
58 | static const stateid_t zero_stateid = { | ||
59 | /* all fields zero */ | ||
60 | }; | ||
61 | |||
54 | static u64 current_sessionid = 1; | 62 | static u64 current_sessionid = 1; |
55 | 63 | ||
56 | #define ZERO_STATEID(stateid) (!memcmp((stateid), &zerostateid, sizeof(stateid_t))) | 64 | #define ZERO_STATEID(stateid) (!memcmp((stateid), &zero_stateid, sizeof(stateid_t))) |
57 | #define ONE_STATEID(stateid) (!memcmp((stateid), &onestateid, sizeof(stateid_t))) | 65 | #define ONE_STATEID(stateid) (!memcmp((stateid), &one_stateid, sizeof(stateid_t))) |
58 | 66 | ||
59 | /* forward declarations */ | 67 | /* forward declarations */ |
60 | static int check_for_locks(struct nfs4_file *filp, struct nfs4_lockowner *lowner); | 68 | static int check_for_locks(struct nfs4_file *filp, struct nfs4_lockowner *lowner); |
@@ -133,21 +141,21 @@ unsigned int max_delegations; | |||
133 | * Open owner state (share locks) | 141 | * Open owner state (share locks) |
134 | */ | 142 | */ |
135 | 143 | ||
136 | /* hash tables for open owners */ | 144 | /* hash tables for lock and open owners */ |
137 | #define OPEN_OWNER_HASH_BITS 8 | 145 | #define OWNER_HASH_BITS 8 |
138 | #define OPEN_OWNER_HASH_SIZE (1 << OPEN_OWNER_HASH_BITS) | 146 | #define OWNER_HASH_SIZE (1 << OWNER_HASH_BITS) |
139 | #define OPEN_OWNER_HASH_MASK (OPEN_OWNER_HASH_SIZE - 1) | 147 | #define OWNER_HASH_MASK (OWNER_HASH_SIZE - 1) |
140 | 148 | ||
141 | static unsigned int open_ownerstr_hashval(u32 clientid, struct xdr_netobj *ownername) | 149 | static unsigned int ownerstr_hashval(u32 clientid, struct xdr_netobj *ownername) |
142 | { | 150 | { |
143 | unsigned int ret; | 151 | unsigned int ret; |
144 | 152 | ||
145 | ret = opaque_hashval(ownername->data, ownername->len); | 153 | ret = opaque_hashval(ownername->data, ownername->len); |
146 | ret += clientid; | 154 | ret += clientid; |
147 | return ret & OPEN_OWNER_HASH_MASK; | 155 | return ret & OWNER_HASH_MASK; |
148 | } | 156 | } |
149 | 157 | ||
150 | static struct list_head open_ownerstr_hashtbl[OPEN_OWNER_HASH_SIZE]; | 158 | static struct list_head ownerstr_hashtbl[OWNER_HASH_SIZE]; |
151 | 159 | ||
152 | /* hash table for nfs4_file */ | 160 | /* hash table for nfs4_file */ |
153 | #define FILE_HASH_BITS 8 | 161 | #define FILE_HASH_BITS 8 |
@@ -514,6 +522,7 @@ static void unhash_lockowner(struct nfs4_lockowner *lo) | |||
514 | 522 | ||
515 | list_del(&lo->lo_owner.so_strhash); | 523 | list_del(&lo->lo_owner.so_strhash); |
516 | list_del(&lo->lo_perstateid); | 524 | list_del(&lo->lo_perstateid); |
525 | list_del(&lo->lo_owner_ino_hash); | ||
517 | while (!list_empty(&lo->lo_owner.so_stateids)) { | 526 | while (!list_empty(&lo->lo_owner.so_stateids)) { |
518 | stp = list_first_entry(&lo->lo_owner.so_stateids, | 527 | stp = list_first_entry(&lo->lo_owner.so_stateids, |
519 | struct nfs4_ol_stateid, st_perstateowner); | 528 | struct nfs4_ol_stateid, st_perstateowner); |
@@ -985,12 +994,11 @@ static struct nfs4_client *alloc_client(struct xdr_netobj name) | |||
985 | clp = kzalloc(sizeof(struct nfs4_client), GFP_KERNEL); | 994 | clp = kzalloc(sizeof(struct nfs4_client), GFP_KERNEL); |
986 | if (clp == NULL) | 995 | if (clp == NULL) |
987 | return NULL; | 996 | return NULL; |
988 | clp->cl_name.data = kmalloc(name.len, GFP_KERNEL); | 997 | clp->cl_name.data = kmemdup(name.data, name.len, GFP_KERNEL); |
989 | if (clp->cl_name.data == NULL) { | 998 | if (clp->cl_name.data == NULL) { |
990 | kfree(clp); | 999 | kfree(clp); |
991 | return NULL; | 1000 | return NULL; |
992 | } | 1001 | } |
993 | memcpy(clp->cl_name.data, name.data, name.len); | ||
994 | clp->cl_name.len = name.len; | 1002 | clp->cl_name.len = name.len; |
995 | return clp; | 1003 | return clp; |
996 | } | 1004 | } |
@@ -1058,7 +1066,6 @@ expire_client(struct nfs4_client *clp) | |||
1058 | spin_unlock(&recall_lock); | 1066 | spin_unlock(&recall_lock); |
1059 | while (!list_empty(&reaplist)) { | 1067 | while (!list_empty(&reaplist)) { |
1060 | dp = list_entry(reaplist.next, struct nfs4_delegation, dl_recall_lru); | 1068 | dp = list_entry(reaplist.next, struct nfs4_delegation, dl_recall_lru); |
1061 | list_del_init(&dp->dl_recall_lru); | ||
1062 | unhash_delegation(dp); | 1069 | unhash_delegation(dp); |
1063 | } | 1070 | } |
1064 | while (!list_empty(&clp->cl_openowners)) { | 1071 | while (!list_empty(&clp->cl_openowners)) { |
@@ -2301,7 +2308,7 @@ nfsd4_free_slabs(void) | |||
2301 | nfsd4_free_slab(&deleg_slab); | 2308 | nfsd4_free_slab(&deleg_slab); |
2302 | } | 2309 | } |
2303 | 2310 | ||
2304 | static int | 2311 | int |
2305 | nfsd4_init_slabs(void) | 2312 | nfsd4_init_slabs(void) |
2306 | { | 2313 | { |
2307 | openowner_slab = kmem_cache_create("nfsd4_openowners", | 2314 | openowner_slab = kmem_cache_create("nfsd4_openowners", |
@@ -2373,7 +2380,7 @@ static inline void *alloc_stateowner(struct kmem_cache *slab, struct xdr_netobj | |||
2373 | 2380 | ||
2374 | static void hash_openowner(struct nfs4_openowner *oo, struct nfs4_client *clp, unsigned int strhashval) | 2381 | static void hash_openowner(struct nfs4_openowner *oo, struct nfs4_client *clp, unsigned int strhashval) |
2375 | { | 2382 | { |
2376 | list_add(&oo->oo_owner.so_strhash, &open_ownerstr_hashtbl[strhashval]); | 2383 | list_add(&oo->oo_owner.so_strhash, &ownerstr_hashtbl[strhashval]); |
2377 | list_add(&oo->oo_perclient, &clp->cl_openowners); | 2384 | list_add(&oo->oo_perclient, &clp->cl_openowners); |
2378 | } | 2385 | } |
2379 | 2386 | ||
@@ -2436,7 +2443,9 @@ find_openstateowner_str(unsigned int hashval, struct nfsd4_open *open) | |||
2436 | struct nfs4_stateowner *so; | 2443 | struct nfs4_stateowner *so; |
2437 | struct nfs4_openowner *oo; | 2444 | struct nfs4_openowner *oo; |
2438 | 2445 | ||
2439 | list_for_each_entry(so, &open_ownerstr_hashtbl[hashval], so_strhash) { | 2446 | list_for_each_entry(so, &ownerstr_hashtbl[hashval], so_strhash) { |
2447 | if (!so->so_is_open_owner) | ||
2448 | continue; | ||
2440 | if (same_owner_str(so, &open->op_owner, &open->op_clientid)) { | 2449 | if (same_owner_str(so, &open->op_owner, &open->op_clientid)) { |
2441 | oo = openowner(so); | 2450 | oo = openowner(so); |
2442 | renew_client(oo->oo_owner.so_client); | 2451 | renew_client(oo->oo_owner.so_client); |
@@ -2580,7 +2589,7 @@ nfsd4_process_open1(struct nfsd4_compound_state *cstate, | |||
2580 | if (open->op_file == NULL) | 2589 | if (open->op_file == NULL) |
2581 | return nfserr_jukebox; | 2590 | return nfserr_jukebox; |
2582 | 2591 | ||
2583 | strhashval = open_ownerstr_hashval(clientid->cl_id, &open->op_owner); | 2592 | strhashval = ownerstr_hashval(clientid->cl_id, &open->op_owner); |
2584 | oo = find_openstateowner_str(strhashval, open); | 2593 | oo = find_openstateowner_str(strhashval, open); |
2585 | open->op_openowner = oo; | 2594 | open->op_openowner = oo; |
2586 | if (!oo) { | 2595 | if (!oo) { |
@@ -3123,7 +3132,6 @@ nfs4_laundromat(void) | |||
3123 | spin_unlock(&recall_lock); | 3132 | spin_unlock(&recall_lock); |
3124 | list_for_each_safe(pos, next, &reaplist) { | 3133 | list_for_each_safe(pos, next, &reaplist) { |
3125 | dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru); | 3134 | dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru); |
3126 | list_del_init(&dp->dl_recall_lru); | ||
3127 | unhash_delegation(dp); | 3135 | unhash_delegation(dp); |
3128 | } | 3136 | } |
3129 | test_val = nfsd4_lease; | 3137 | test_val = nfsd4_lease; |
@@ -3718,13 +3726,11 @@ out: | |||
3718 | } | 3726 | } |
3719 | 3727 | ||
3720 | 3728 | ||
3721 | /* | ||
3722 | * Lock owner state (byte-range locks) | ||
3723 | */ | ||
3724 | #define LOFF_OVERFLOW(start, len) ((u64)(len) > ~(u64)(start)) | 3729 | #define LOFF_OVERFLOW(start, len) ((u64)(len) > ~(u64)(start)) |
3725 | #define LOCK_HASH_BITS 8 | 3730 | |
3726 | #define LOCK_HASH_SIZE (1 << LOCK_HASH_BITS) | 3731 | #define LOCKOWNER_INO_HASH_BITS 8 |
3727 | #define LOCK_HASH_MASK (LOCK_HASH_SIZE - 1) | 3732 | #define LOCKOWNER_INO_HASH_SIZE (1 << LOCKOWNER_INO_HASH_BITS) |
3733 | #define LOCKOWNER_INO_HASH_MASK (LOCKOWNER_INO_HASH_SIZE - 1) | ||
3728 | 3734 | ||
3729 | static inline u64 | 3735 | static inline u64 |
3730 | end_offset(u64 start, u64 len) | 3736 | end_offset(u64 start, u64 len) |
@@ -3746,16 +3752,14 @@ last_byte_offset(u64 start, u64 len) | |||
3746 | return end > start ? end - 1: NFS4_MAX_UINT64; | 3752 | return end > start ? end - 1: NFS4_MAX_UINT64; |
3747 | } | 3753 | } |
3748 | 3754 | ||
3749 | static inline unsigned int | 3755 | static unsigned int lockowner_ino_hashval(struct inode *inode, u32 cl_id, struct xdr_netobj *ownername) |
3750 | lock_ownerstr_hashval(struct inode *inode, u32 cl_id, | ||
3751 | struct xdr_netobj *ownername) | ||
3752 | { | 3756 | { |
3753 | return (file_hashval(inode) + cl_id | 3757 | return (file_hashval(inode) + cl_id |
3754 | + opaque_hashval(ownername->data, ownername->len)) | 3758 | + opaque_hashval(ownername->data, ownername->len)) |
3755 | & LOCK_HASH_MASK; | 3759 | & LOCKOWNER_INO_HASH_MASK; |
3756 | } | 3760 | } |
3757 | 3761 | ||
3758 | static struct list_head lock_ownerstr_hashtbl[LOCK_HASH_SIZE]; | 3762 | static struct list_head lockowner_ino_hashtbl[LOCKOWNER_INO_HASH_SIZE]; |
3759 | 3763 | ||
3760 | /* | 3764 | /* |
3761 | * TODO: Linux file offsets are _signed_ 64-bit quantities, which means that | 3765 | * TODO: Linux file offsets are _signed_ 64-bit quantities, which means that |
@@ -3809,23 +3813,39 @@ nevermind: | |||
3809 | deny->ld_type = NFS4_WRITE_LT; | 3813 | deny->ld_type = NFS4_WRITE_LT; |
3810 | } | 3814 | } |
3811 | 3815 | ||
3816 | static bool same_lockowner_ino(struct nfs4_lockowner *lo, struct inode *inode, clientid_t *clid, struct xdr_netobj *owner) | ||
3817 | { | ||
3818 | struct nfs4_ol_stateid *lst; | ||
3819 | |||
3820 | if (!same_owner_str(&lo->lo_owner, owner, clid)) | ||
3821 | return false; | ||
3822 | lst = list_first_entry(&lo->lo_owner.so_stateids, | ||
3823 | struct nfs4_ol_stateid, st_perstateowner); | ||
3824 | return lst->st_file->fi_inode == inode; | ||
3825 | } | ||
3826 | |||
3812 | static struct nfs4_lockowner * | 3827 | static struct nfs4_lockowner * |
3813 | find_lockowner_str(struct inode *inode, clientid_t *clid, | 3828 | find_lockowner_str(struct inode *inode, clientid_t *clid, |
3814 | struct xdr_netobj *owner) | 3829 | struct xdr_netobj *owner) |
3815 | { | 3830 | { |
3816 | unsigned int hashval = lock_ownerstr_hashval(inode, clid->cl_id, owner); | 3831 | unsigned int hashval = lockowner_ino_hashval(inode, clid->cl_id, owner); |
3817 | struct nfs4_stateowner *op; | 3832 | struct nfs4_lockowner *lo; |
3818 | 3833 | ||
3819 | list_for_each_entry(op, &lock_ownerstr_hashtbl[hashval], so_strhash) { | 3834 | list_for_each_entry(lo, &lockowner_ino_hashtbl[hashval], lo_owner_ino_hash) { |
3820 | if (same_owner_str(op, owner, clid)) | 3835 | if (same_lockowner_ino(lo, inode, clid, owner)) |
3821 | return lockowner(op); | 3836 | return lo; |
3822 | } | 3837 | } |
3823 | return NULL; | 3838 | return NULL; |
3824 | } | 3839 | } |
3825 | 3840 | ||
3826 | static void hash_lockowner(struct nfs4_lockowner *lo, unsigned int strhashval, struct nfs4_client *clp, struct nfs4_ol_stateid *open_stp) | 3841 | static void hash_lockowner(struct nfs4_lockowner *lo, unsigned int strhashval, struct nfs4_client *clp, struct nfs4_ol_stateid *open_stp) |
3827 | { | 3842 | { |
3828 | list_add(&lo->lo_owner.so_strhash, &lock_ownerstr_hashtbl[strhashval]); | 3843 | struct inode *inode = open_stp->st_file->fi_inode; |
3844 | unsigned int inohash = lockowner_ino_hashval(inode, | ||
3845 | clp->cl_clientid.cl_id, &lo->lo_owner.so_owner); | ||
3846 | |||
3847 | list_add(&lo->lo_owner.so_strhash, &ownerstr_hashtbl[strhashval]); | ||
3848 | list_add(&lo->lo_owner_ino_hash, &lockowner_ino_hashtbl[inohash]); | ||
3829 | list_add(&lo->lo_perstateid, &open_stp->st_lockowners); | 3849 | list_add(&lo->lo_perstateid, &open_stp->st_lockowners); |
3830 | } | 3850 | } |
3831 | 3851 | ||
@@ -3834,7 +3854,7 @@ static void hash_lockowner(struct nfs4_lockowner *lo, unsigned int strhashval, s | |||
3834 | * Called in nfsd4_lock - therefore, OPEN and OPEN_CONFIRM (if needed) has | 3854 | * Called in nfsd4_lock - therefore, OPEN and OPEN_CONFIRM (if needed) has |
3835 | * occurred. | 3855 | * occurred. |
3836 | * | 3856 | * |
3837 | * strhashval = lock_ownerstr_hashval | 3857 | * strhashval = ownerstr_hashval |
3838 | */ | 3858 | */ |
3839 | 3859 | ||
3840 | static struct nfs4_lockowner * | 3860 | static struct nfs4_lockowner * |
@@ -3892,6 +3912,37 @@ static void get_lock_access(struct nfs4_ol_stateid *lock_stp, u32 access) | |||
3892 | __set_bit(access, &lock_stp->st_access_bmap); | 3912 | __set_bit(access, &lock_stp->st_access_bmap); |
3893 | } | 3913 | } |
3894 | 3914 | ||
3915 | __be32 lookup_or_create_lock_state(struct nfsd4_compound_state *cstate, struct nfs4_ol_stateid *ost, struct nfsd4_lock *lock, struct nfs4_ol_stateid **lst, bool *new) | ||
3916 | { | ||
3917 | struct nfs4_file *fi = ost->st_file; | ||
3918 | struct nfs4_openowner *oo = openowner(ost->st_stateowner); | ||
3919 | struct nfs4_client *cl = oo->oo_owner.so_client; | ||
3920 | struct nfs4_lockowner *lo; | ||
3921 | unsigned int strhashval; | ||
3922 | |||
3923 | lo = find_lockowner_str(fi->fi_inode, &cl->cl_clientid, &lock->v.new.owner); | ||
3924 | if (lo) { | ||
3925 | if (!cstate->minorversion) | ||
3926 | return nfserr_bad_seqid; | ||
3927 | /* XXX: a lockowner always has exactly one stateid: */ | ||
3928 | *lst = list_first_entry(&lo->lo_owner.so_stateids, | ||
3929 | struct nfs4_ol_stateid, st_perstateowner); | ||
3930 | return nfs_ok; | ||
3931 | } | ||
3932 | strhashval = ownerstr_hashval(cl->cl_clientid.cl_id, | ||
3933 | &lock->v.new.owner); | ||
3934 | lo = alloc_init_lock_stateowner(strhashval, cl, ost, lock); | ||
3935 | if (lo == NULL) | ||
3936 | return nfserr_jukebox; | ||
3937 | *lst = alloc_init_lock_stateid(lo, fi, ost); | ||
3938 | if (*lst == NULL) { | ||
3939 | release_lockowner(lo); | ||
3940 | return nfserr_jukebox; | ||
3941 | } | ||
3942 | *new = true; | ||
3943 | return nfs_ok; | ||
3944 | } | ||
3945 | |||
3895 | /* | 3946 | /* |
3896 | * LOCK operation | 3947 | * LOCK operation |
3897 | */ | 3948 | */ |
@@ -3907,7 +3958,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
3907 | struct file_lock file_lock; | 3958 | struct file_lock file_lock; |
3908 | struct file_lock conflock; | 3959 | struct file_lock conflock; |
3909 | __be32 status = 0; | 3960 | __be32 status = 0; |
3910 | unsigned int strhashval; | 3961 | bool new_state = false; |
3911 | int lkflg; | 3962 | int lkflg; |
3912 | int err; | 3963 | int err; |
3913 | 3964 | ||
@@ -3933,10 +3984,15 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
3933 | * lock stateid. | 3984 | * lock stateid. |
3934 | */ | 3985 | */ |
3935 | struct nfs4_ol_stateid *open_stp = NULL; | 3986 | struct nfs4_ol_stateid *open_stp = NULL; |
3936 | 3987 | ||
3988 | if (nfsd4_has_session(cstate)) | ||
3989 | /* See rfc 5661 18.10.3: given clientid is ignored: */ | ||
3990 | memcpy(&lock->v.new.clientid, | ||
3991 | &cstate->session->se_client->cl_clientid, | ||
3992 | sizeof(clientid_t)); | ||
3993 | |||
3937 | status = nfserr_stale_clientid; | 3994 | status = nfserr_stale_clientid; |
3938 | if (!nfsd4_has_session(cstate) && | 3995 | if (STALE_CLIENTID(&lock->lk_new_clientid)) |
3939 | STALE_CLIENTID(&lock->lk_new_clientid)) | ||
3940 | goto out; | 3996 | goto out; |
3941 | 3997 | ||
3942 | /* validate and update open stateid and open seqid */ | 3998 | /* validate and update open stateid and open seqid */ |
@@ -3948,25 +4004,12 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
3948 | goto out; | 4004 | goto out; |
3949 | open_sop = openowner(open_stp->st_stateowner); | 4005 | open_sop = openowner(open_stp->st_stateowner); |
3950 | status = nfserr_bad_stateid; | 4006 | status = nfserr_bad_stateid; |
3951 | if (!nfsd4_has_session(cstate) && | 4007 | if (!same_clid(&open_sop->oo_owner.so_client->cl_clientid, |
3952 | !same_clid(&open_sop->oo_owner.so_client->cl_clientid, | ||
3953 | &lock->v.new.clientid)) | 4008 | &lock->v.new.clientid)) |
3954 | goto out; | 4009 | goto out; |
3955 | /* create lockowner and lock stateid */ | 4010 | status = lookup_or_create_lock_state(cstate, open_stp, lock, |
3956 | fp = open_stp->st_file; | 4011 | &lock_stp, &new_state); |
3957 | strhashval = lock_ownerstr_hashval(fp->fi_inode, | 4012 | if (status) |
3958 | open_sop->oo_owner.so_client->cl_clientid.cl_id, | ||
3959 | &lock->v.new.owner); | ||
3960 | /* XXX: Do we need to check for duplicate stateowners on | ||
3961 | * the same file, or should they just be allowed (and | ||
3962 | * create new stateids)? */ | ||
3963 | status = nfserr_jukebox; | ||
3964 | lock_sop = alloc_init_lock_stateowner(strhashval, | ||
3965 | open_sop->oo_owner.so_client, open_stp, lock); | ||
3966 | if (lock_sop == NULL) | ||
3967 | goto out; | ||
3968 | lock_stp = alloc_init_lock_stateid(lock_sop, fp, open_stp); | ||
3969 | if (lock_stp == NULL) | ||
3970 | goto out; | 4013 | goto out; |
3971 | } else { | 4014 | } else { |
3972 | /* lock (lock owner + lock stateid) already exists */ | 4015 | /* lock (lock owner + lock stateid) already exists */ |
@@ -3976,10 +4019,9 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
3976 | NFS4_LOCK_STID, &lock_stp); | 4019 | NFS4_LOCK_STID, &lock_stp); |
3977 | if (status) | 4020 | if (status) |
3978 | goto out; | 4021 | goto out; |
3979 | lock_sop = lockowner(lock_stp->st_stateowner); | ||
3980 | fp = lock_stp->st_file; | ||
3981 | } | 4022 | } |
3982 | /* lock_sop and lock_stp have been created or found */ | 4023 | lock_sop = lockowner(lock_stp->st_stateowner); |
4024 | fp = lock_stp->st_file; | ||
3983 | 4025 | ||
3984 | lkflg = setlkflg(lock->lk_type); | 4026 | lkflg = setlkflg(lock->lk_type); |
3985 | status = nfs4_check_openmode(lock_stp, lkflg); | 4027 | status = nfs4_check_openmode(lock_stp, lkflg); |
@@ -4054,7 +4096,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
4054 | break; | 4096 | break; |
4055 | } | 4097 | } |
4056 | out: | 4098 | out: |
4057 | if (status && lock->lk_is_new && lock_sop) | 4099 | if (status && new_state) |
4058 | release_lockowner(lock_sop); | 4100 | release_lockowner(lock_sop); |
4059 | if (!cstate->replay_owner) | 4101 | if (!cstate->replay_owner) |
4060 | nfs4_unlock_state(); | 4102 | nfs4_unlock_state(); |
@@ -4251,7 +4293,7 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp, | |||
4251 | struct nfs4_ol_stateid *stp; | 4293 | struct nfs4_ol_stateid *stp; |
4252 | struct xdr_netobj *owner = &rlockowner->rl_owner; | 4294 | struct xdr_netobj *owner = &rlockowner->rl_owner; |
4253 | struct list_head matches; | 4295 | struct list_head matches; |
4254 | int i; | 4296 | unsigned int hashval = ownerstr_hashval(clid->cl_id, owner); |
4255 | __be32 status; | 4297 | __be32 status; |
4256 | 4298 | ||
4257 | dprintk("nfsd4_release_lockowner clientid: (%08x/%08x):\n", | 4299 | dprintk("nfsd4_release_lockowner clientid: (%08x/%08x):\n", |
@@ -4266,22 +4308,19 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp, | |||
4266 | nfs4_lock_state(); | 4308 | nfs4_lock_state(); |
4267 | 4309 | ||
4268 | status = nfserr_locks_held; | 4310 | status = nfserr_locks_held; |
4269 | /* XXX: we're doing a linear search through all the lockowners. | ||
4270 | * Yipes! For now we'll just hope clients aren't really using | ||
4271 | * release_lockowner much, but eventually we have to fix these | ||
4272 | * data structures. */ | ||
4273 | INIT_LIST_HEAD(&matches); | 4311 | INIT_LIST_HEAD(&matches); |
4274 | for (i = 0; i < LOCK_HASH_SIZE; i++) { | 4312 | |
4275 | list_for_each_entry(sop, &lock_ownerstr_hashtbl[i], so_strhash) { | 4313 | list_for_each_entry(sop, &ownerstr_hashtbl[hashval], so_strhash) { |
4276 | if (!same_owner_str(sop, owner, clid)) | 4314 | if (sop->so_is_open_owner) |
4277 | continue; | 4315 | continue; |
4278 | list_for_each_entry(stp, &sop->so_stateids, | 4316 | if (!same_owner_str(sop, owner, clid)) |
4279 | st_perstateowner) { | 4317 | continue; |
4280 | lo = lockowner(sop); | 4318 | list_for_each_entry(stp, &sop->so_stateids, |
4281 | if (check_for_locks(stp->st_file, lo)) | 4319 | st_perstateowner) { |
4282 | goto out; | 4320 | lo = lockowner(sop); |
4283 | list_add(&lo->lo_list, &matches); | 4321 | if (check_for_locks(stp->st_file, lo)) |
4284 | } | 4322 | goto out; |
4323 | list_add(&lo->lo_list, &matches); | ||
4285 | } | 4324 | } |
4286 | } | 4325 | } |
4287 | /* Clients probably won't expect us to return with some (but not all) | 4326 | /* Clients probably won't expect us to return with some (but not all) |
@@ -4394,16 +4433,127 @@ nfs4_check_open_reclaim(clientid_t *clid) | |||
4394 | return nfs4_find_reclaim_client(clid) ? nfs_ok : nfserr_reclaim_bad; | 4433 | return nfs4_find_reclaim_client(clid) ? nfs_ok : nfserr_reclaim_bad; |
4395 | } | 4434 | } |
4396 | 4435 | ||
4436 | #ifdef CONFIG_NFSD_FAULT_INJECTION | ||
4437 | |||
4438 | void nfsd_forget_clients(u64 num) | ||
4439 | { | ||
4440 | struct nfs4_client *clp, *next; | ||
4441 | int count = 0; | ||
4442 | |||
4443 | nfs4_lock_state(); | ||
4444 | list_for_each_entry_safe(clp, next, &client_lru, cl_lru) { | ||
4445 | nfsd4_remove_clid_dir(clp); | ||
4446 | expire_client(clp); | ||
4447 | if (++count == num) | ||
4448 | break; | ||
4449 | } | ||
4450 | nfs4_unlock_state(); | ||
4451 | |||
4452 | printk(KERN_INFO "NFSD: Forgot %d clients", count); | ||
4453 | } | ||
4454 | |||
4455 | static void release_lockowner_sop(struct nfs4_stateowner *sop) | ||
4456 | { | ||
4457 | release_lockowner(lockowner(sop)); | ||
4458 | } | ||
4459 | |||
4460 | static void release_openowner_sop(struct nfs4_stateowner *sop) | ||
4461 | { | ||
4462 | release_openowner(openowner(sop)); | ||
4463 | } | ||
4464 | |||
4465 | static int nfsd_release_n_owners(u64 num, bool is_open_owner, | ||
4466 | void (*release_sop)(struct nfs4_stateowner *)) | ||
4467 | { | ||
4468 | int i, count = 0; | ||
4469 | struct nfs4_stateowner *sop, *next; | ||
4470 | |||
4471 | for (i = 0; i < OWNER_HASH_SIZE; i++) { | ||
4472 | list_for_each_entry_safe(sop, next, &ownerstr_hashtbl[i], so_strhash) { | ||
4473 | if (sop->so_is_open_owner != is_open_owner) | ||
4474 | continue; | ||
4475 | release_sop(sop); | ||
4476 | if (++count == num) | ||
4477 | return count; | ||
4478 | } | ||
4479 | } | ||
4480 | return count; | ||
4481 | } | ||
4482 | |||
4483 | void nfsd_forget_locks(u64 num) | ||
4484 | { | ||
4485 | int count; | ||
4486 | |||
4487 | nfs4_lock_state(); | ||
4488 | count = nfsd_release_n_owners(num, false, release_lockowner_sop); | ||
4489 | nfs4_unlock_state(); | ||
4490 | |||
4491 | printk(KERN_INFO "NFSD: Forgot %d locks", count); | ||
4492 | } | ||
4493 | |||
4494 | void nfsd_forget_openowners(u64 num) | ||
4495 | { | ||
4496 | int count; | ||
4497 | |||
4498 | nfs4_lock_state(); | ||
4499 | count = nfsd_release_n_owners(num, true, release_openowner_sop); | ||
4500 | nfs4_unlock_state(); | ||
4501 | |||
4502 | printk(KERN_INFO "NFSD: Forgot %d open owners", count); | ||
4503 | } | ||
4504 | |||
4505 | int nfsd_process_n_delegations(u64 num, void (*deleg_func)(struct nfs4_delegation *)) | ||
4506 | { | ||
4507 | int i, count = 0; | ||
4508 | struct nfs4_file *fp, *fnext; | ||
4509 | struct nfs4_delegation *dp, *dnext; | ||
4510 | |||
4511 | for (i = 0; i < FILE_HASH_SIZE; i++) { | ||
4512 | list_for_each_entry_safe(fp, fnext, &file_hashtbl[i], fi_hash) { | ||
4513 | list_for_each_entry_safe(dp, dnext, &fp->fi_delegations, dl_perfile) { | ||
4514 | deleg_func(dp); | ||
4515 | if (++count == num) | ||
4516 | return count; | ||
4517 | } | ||
4518 | } | ||
4519 | } | ||
4520 | |||
4521 | return count; | ||
4522 | } | ||
4523 | |||
4524 | void nfsd_forget_delegations(u64 num) | ||
4525 | { | ||
4526 | unsigned int count; | ||
4527 | |||
4528 | nfs4_lock_state(); | ||
4529 | count = nfsd_process_n_delegations(num, unhash_delegation); | ||
4530 | nfs4_unlock_state(); | ||
4531 | |||
4532 | printk(KERN_INFO "NFSD: Forgot %d delegations", count); | ||
4533 | } | ||
4534 | |||
4535 | void nfsd_recall_delegations(u64 num) | ||
4536 | { | ||
4537 | unsigned int count; | ||
4538 | |||
4539 | nfs4_lock_state(); | ||
4540 | spin_lock(&recall_lock); | ||
4541 | count = nfsd_process_n_delegations(num, nfsd_break_one_deleg); | ||
4542 | spin_unlock(&recall_lock); | ||
4543 | nfs4_unlock_state(); | ||
4544 | |||
4545 | printk(KERN_INFO "NFSD: Recalled %d delegations", count); | ||
4546 | } | ||
4547 | |||
4548 | #endif /* CONFIG_NFSD_FAULT_INJECTION */ | ||
4549 | |||
4397 | /* initialization to perform at module load time: */ | 4550 | /* initialization to perform at module load time: */ |
4398 | 4551 | ||
4399 | int | 4552 | void |
4400 | nfs4_state_init(void) | 4553 | nfs4_state_init(void) |
4401 | { | 4554 | { |
4402 | int i, status; | 4555 | int i; |
4403 | 4556 | ||
4404 | status = nfsd4_init_slabs(); | ||
4405 | if (status) | ||
4406 | return status; | ||
4407 | for (i = 0; i < CLIENT_HASH_SIZE; i++) { | 4557 | for (i = 0; i < CLIENT_HASH_SIZE; i++) { |
4408 | INIT_LIST_HEAD(&conf_id_hashtbl[i]); | 4558 | INIT_LIST_HEAD(&conf_id_hashtbl[i]); |
4409 | INIT_LIST_HEAD(&conf_str_hashtbl[i]); | 4559 | INIT_LIST_HEAD(&conf_str_hashtbl[i]); |
@@ -4416,18 +4566,15 @@ nfs4_state_init(void) | |||
4416 | for (i = 0; i < FILE_HASH_SIZE; i++) { | 4566 | for (i = 0; i < FILE_HASH_SIZE; i++) { |
4417 | INIT_LIST_HEAD(&file_hashtbl[i]); | 4567 | INIT_LIST_HEAD(&file_hashtbl[i]); |
4418 | } | 4568 | } |
4419 | for (i = 0; i < OPEN_OWNER_HASH_SIZE; i++) { | 4569 | for (i = 0; i < OWNER_HASH_SIZE; i++) { |
4420 | INIT_LIST_HEAD(&open_ownerstr_hashtbl[i]); | 4570 | INIT_LIST_HEAD(&ownerstr_hashtbl[i]); |
4421 | } | ||
4422 | for (i = 0; i < LOCK_HASH_SIZE; i++) { | ||
4423 | INIT_LIST_HEAD(&lock_ownerstr_hashtbl[i]); | ||
4424 | } | 4571 | } |
4425 | memset(&onestateid, ~0, sizeof(stateid_t)); | 4572 | for (i = 0; i < LOCKOWNER_INO_HASH_SIZE; i++) |
4573 | INIT_LIST_HEAD(&lockowner_ino_hashtbl[i]); | ||
4426 | INIT_LIST_HEAD(&close_lru); | 4574 | INIT_LIST_HEAD(&close_lru); |
4427 | INIT_LIST_HEAD(&client_lru); | 4575 | INIT_LIST_HEAD(&client_lru); |
4428 | INIT_LIST_HEAD(&del_recall_lru); | 4576 | INIT_LIST_HEAD(&del_recall_lru); |
4429 | reclaim_str_hashtbl_size = 0; | 4577 | reclaim_str_hashtbl_size = 0; |
4430 | return 0; | ||
4431 | } | 4578 | } |
4432 | 4579 | ||
4433 | static void | 4580 | static void |
@@ -4526,7 +4673,6 @@ __nfs4_state_shutdown(void) | |||
4526 | spin_unlock(&recall_lock); | 4673 | spin_unlock(&recall_lock); |
4527 | list_for_each_safe(pos, next, &reaplist) { | 4674 | list_for_each_safe(pos, next, &reaplist) { |
4528 | dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru); | 4675 | dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru); |
4529 | list_del_init(&dp->dl_recall_lru); | ||
4530 | unhash_delegation(dp); | 4676 | unhash_delegation(dp); |
4531 | } | 4677 | } |
4532 | 4678 | ||
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index b6fa792d6b85..0ec5a1b9700e 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c | |||
@@ -215,10 +215,9 @@ defer_free(struct nfsd4_compoundargs *argp, | |||
215 | static char *savemem(struct nfsd4_compoundargs *argp, __be32 *p, int nbytes) | 215 | static char *savemem(struct nfsd4_compoundargs *argp, __be32 *p, int nbytes) |
216 | { | 216 | { |
217 | if (p == argp->tmp) { | 217 | if (p == argp->tmp) { |
218 | p = kmalloc(nbytes, GFP_KERNEL); | 218 | p = kmemdup(argp->tmp, nbytes, GFP_KERNEL); |
219 | if (!p) | 219 | if (!p) |
220 | return NULL; | 220 | return NULL; |
221 | memcpy(p, argp->tmp, nbytes); | ||
222 | } else { | 221 | } else { |
223 | BUG_ON(p != argp->tmpp); | 222 | BUG_ON(p != argp->tmpp); |
224 | argp->tmpp = NULL; | 223 | argp->tmpp = NULL; |
diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index bb4a11d58a5a..748eda93ce59 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c | |||
@@ -18,6 +18,7 @@ | |||
18 | #include "idmap.h" | 18 | #include "idmap.h" |
19 | #include "nfsd.h" | 19 | #include "nfsd.h" |
20 | #include "cache.h" | 20 | #include "cache.h" |
21 | #include "fault_inject.h" | ||
21 | 22 | ||
22 | /* | 23 | /* |
23 | * We have a single directory with several nodes in it. | 24 | * We have a single directory with several nodes in it. |
@@ -1128,9 +1129,13 @@ static int __init init_nfsd(void) | |||
1128 | int retval; | 1129 | int retval; |
1129 | printk(KERN_INFO "Installing knfsd (copyright (C) 1996 okir@monad.swb.de).\n"); | 1130 | printk(KERN_INFO "Installing knfsd (copyright (C) 1996 okir@monad.swb.de).\n"); |
1130 | 1131 | ||
1131 | retval = nfs4_state_init(); /* nfs4 locking state */ | 1132 | retval = nfsd4_init_slabs(); |
1132 | if (retval) | 1133 | if (retval) |
1133 | return retval; | 1134 | return retval; |
1135 | nfs4_state_init(); | ||
1136 | retval = nfsd_fault_inject_init(); /* nfsd fault injection controls */ | ||
1137 | if (retval) | ||
1138 | goto out_free_slabs; | ||
1134 | nfsd_stat_init(); /* Statistics */ | 1139 | nfsd_stat_init(); /* Statistics */ |
1135 | retval = nfsd_reply_cache_init(); | 1140 | retval = nfsd_reply_cache_init(); |
1136 | if (retval) | 1141 | if (retval) |
@@ -1161,6 +1166,8 @@ out_free_cache: | |||
1161 | nfsd_reply_cache_shutdown(); | 1166 | nfsd_reply_cache_shutdown(); |
1162 | out_free_stat: | 1167 | out_free_stat: |
1163 | nfsd_stat_shutdown(); | 1168 | nfsd_stat_shutdown(); |
1169 | nfsd_fault_inject_cleanup(); | ||
1170 | out_free_slabs: | ||
1164 | nfsd4_free_slabs(); | 1171 | nfsd4_free_slabs(); |
1165 | return retval; | 1172 | return retval; |
1166 | } | 1173 | } |
@@ -1175,6 +1182,7 @@ static void __exit exit_nfsd(void) | |||
1175 | nfsd_lockd_shutdown(); | 1182 | nfsd_lockd_shutdown(); |
1176 | nfsd_idmap_shutdown(); | 1183 | nfsd_idmap_shutdown(); |
1177 | nfsd4_free_slabs(); | 1184 | nfsd4_free_slabs(); |
1185 | nfsd_fault_inject_cleanup(); | ||
1178 | unregister_filesystem(&nfsd_fs_type); | 1186 | unregister_filesystem(&nfsd_fs_type); |
1179 | } | 1187 | } |
1180 | 1188 | ||
diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h index 58134a23fdfb..1d1e8589b4ce 100644 --- a/fs/nfsd/nfsd.h +++ b/fs/nfsd/nfsd.h | |||
@@ -104,14 +104,16 @@ static inline int nfsd_v4client(struct svc_rqst *rq) | |||
104 | */ | 104 | */ |
105 | #ifdef CONFIG_NFSD_V4 | 105 | #ifdef CONFIG_NFSD_V4 |
106 | extern unsigned int max_delegations; | 106 | extern unsigned int max_delegations; |
107 | int nfs4_state_init(void); | 107 | void nfs4_state_init(void); |
108 | int nfsd4_init_slabs(void); | ||
108 | void nfsd4_free_slabs(void); | 109 | void nfsd4_free_slabs(void); |
109 | int nfs4_state_start(void); | 110 | int nfs4_state_start(void); |
110 | void nfs4_state_shutdown(void); | 111 | void nfs4_state_shutdown(void); |
111 | void nfs4_reset_lease(time_t leasetime); | 112 | void nfs4_reset_lease(time_t leasetime); |
112 | int nfs4_reset_recoverydir(char *recdir); | 113 | int nfs4_reset_recoverydir(char *recdir); |
113 | #else | 114 | #else |
114 | static inline int nfs4_state_init(void) { return 0; } | 115 | static inline void nfs4_state_init(void) { } |
116 | static inline int nfsd4_init_slabs(void) { return 0; } | ||
115 | static inline void nfsd4_free_slabs(void) { } | 117 | static inline void nfsd4_free_slabs(void) { } |
116 | static inline int nfs4_state_start(void) { return 0; } | 118 | static inline int nfs4_state_start(void) { return 0; } |
117 | static inline void nfs4_state_shutdown(void) { } | 119 | static inline void nfs4_state_shutdown(void) { } |
@@ -338,15 +340,15 @@ static inline u32 nfsd_suppattrs2(u32 minorversion) | |||
338 | } | 340 | } |
339 | 341 | ||
340 | /* These will return ERR_INVAL if specified in GETATTR or READDIR. */ | 342 | /* These will return ERR_INVAL if specified in GETATTR or READDIR. */ |
341 | #define NFSD_WRITEONLY_ATTRS_WORD1 \ | 343 | #define NFSD_WRITEONLY_ATTRS_WORD1 \ |
342 | (FATTR4_WORD1_TIME_ACCESS_SET | FATTR4_WORD1_TIME_MODIFY_SET) | 344 | (FATTR4_WORD1_TIME_ACCESS_SET | FATTR4_WORD1_TIME_MODIFY_SET) |
343 | 345 | ||
344 | /* These are the only attrs allowed in CREATE/OPEN/SETATTR. */ | 346 | /* These are the only attrs allowed in CREATE/OPEN/SETATTR. */ |
345 | #define NFSD_WRITEABLE_ATTRS_WORD0 \ | 347 | #define NFSD_WRITEABLE_ATTRS_WORD0 \ |
346 | (FATTR4_WORD0_SIZE | FATTR4_WORD0_ACL ) | 348 | (FATTR4_WORD0_SIZE | FATTR4_WORD0_ACL) |
347 | #define NFSD_WRITEABLE_ATTRS_WORD1 \ | 349 | #define NFSD_WRITEABLE_ATTRS_WORD1 \ |
348 | (FATTR4_WORD1_MODE | FATTR4_WORD1_OWNER | FATTR4_WORD1_OWNER_GROUP \ | 350 | (FATTR4_WORD1_MODE | FATTR4_WORD1_OWNER | FATTR4_WORD1_OWNER_GROUP \ |
349 | | FATTR4_WORD1_TIME_ACCESS_SET | FATTR4_WORD1_TIME_MODIFY_SET) | 351 | | FATTR4_WORD1_TIME_ACCESS_SET | FATTR4_WORD1_TIME_MODIFY_SET) |
350 | #define NFSD_WRITEABLE_ATTRS_WORD2 0 | 352 | #define NFSD_WRITEABLE_ATTRS_WORD2 0 |
351 | 353 | ||
352 | #define NFSD_SUPPATTR_EXCLCREAT_WORD0 \ | 354 | #define NFSD_SUPPATTR_EXCLCREAT_WORD0 \ |
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index a3cf38476a1b..ffb5df1db94f 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h | |||
@@ -366,6 +366,7 @@ struct nfs4_openowner { | |||
366 | 366 | ||
367 | struct nfs4_lockowner { | 367 | struct nfs4_lockowner { |
368 | struct nfs4_stateowner lo_owner; /* must be first element */ | 368 | struct nfs4_stateowner lo_owner; /* must be first element */ |
369 | struct list_head lo_owner_ino_hash; /* hash by owner,file */ | ||
369 | struct list_head lo_perstateid; /* for lockowners only */ | 370 | struct list_head lo_perstateid; /* for lockowners only */ |
370 | struct list_head lo_list; /* for temporary uses */ | 371 | struct list_head lo_list; /* for temporary uses */ |
371 | }; | 372 | }; |
@@ -482,7 +483,7 @@ extern void nfsd4_shutdown_recdir(void); | |||
482 | extern int nfs4_client_to_reclaim(const char *name); | 483 | extern int nfs4_client_to_reclaim(const char *name); |
483 | extern int nfs4_has_reclaimed_state(const char *name, bool use_exchange_id); | 484 | extern int nfs4_has_reclaimed_state(const char *name, bool use_exchange_id); |
484 | extern void nfsd4_recdir_purge_old(void); | 485 | extern void nfsd4_recdir_purge_old(void); |
485 | extern int nfsd4_create_clid_dir(struct nfs4_client *clp); | 486 | extern void nfsd4_create_clid_dir(struct nfs4_client *clp); |
486 | extern void nfsd4_remove_clid_dir(struct nfs4_client *clp); | 487 | extern void nfsd4_remove_clid_dir(struct nfs4_client *clp); |
487 | extern void release_session_client(struct nfsd4_session *); | 488 | extern void release_session_client(struct nfsd4_session *); |
488 | extern __be32 nfs4_validate_stateid(struct nfs4_client *, stateid_t *); | 489 | extern __be32 nfs4_validate_stateid(struct nfs4_client *, stateid_t *); |
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index d25a723b68ad..edf6d3ed8777 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c | |||
@@ -594,8 +594,19 @@ nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry, struct nfs4_ac | |||
594 | return error; | 594 | return error; |
595 | } | 595 | } |
596 | 596 | ||
597 | #define NFSD_XATTR_JUNCTION_PREFIX XATTR_TRUSTED_PREFIX "junction." | 597 | /* |
598 | #define NFSD_XATTR_JUNCTION_TYPE NFSD_XATTR_JUNCTION_PREFIX "type" | 598 | * NFS junction information is stored in an extended attribute. |
599 | */ | ||
600 | #define NFSD_JUNCTION_XATTR_NAME XATTR_TRUSTED_PREFIX "junction.nfs" | ||
601 | |||
602 | /** | ||
603 | * nfsd4_is_junction - Test if an object could be an NFS junction | ||
604 | * | ||
605 | * @dentry: object to test | ||
606 | * | ||
607 | * Returns 1 if "dentry" appears to contain NFS junction information. | ||
608 | * Otherwise 0 is returned. | ||
609 | */ | ||
599 | int nfsd4_is_junction(struct dentry *dentry) | 610 | int nfsd4_is_junction(struct dentry *dentry) |
600 | { | 611 | { |
601 | struct inode *inode = dentry->d_inode; | 612 | struct inode *inode = dentry->d_inode; |
@@ -606,7 +617,7 @@ int nfsd4_is_junction(struct dentry *dentry) | |||
606 | return 0; | 617 | return 0; |
607 | if (!(inode->i_mode & S_ISVTX)) | 618 | if (!(inode->i_mode & S_ISVTX)) |
608 | return 0; | 619 | return 0; |
609 | if (vfs_getxattr(dentry, NFSD_XATTR_JUNCTION_TYPE, NULL, 0) <= 0) | 620 | if (vfs_getxattr(dentry, NFSD_JUNCTION_XATTR_NAME, NULL, 0) <= 0) |
610 | return 0; | 621 | return 0; |
611 | return 1; | 622 | return 1; |
612 | } | 623 | } |
diff --git a/include/linux/sunrpc/svc_xprt.h b/include/linux/sunrpc/svc_xprt.h index 8620f79658d4..dfa900948af7 100644 --- a/include/linux/sunrpc/svc_xprt.h +++ b/include/linux/sunrpc/svc_xprt.h | |||
@@ -109,7 +109,7 @@ static inline int register_xpt_user(struct svc_xprt *xpt, struct svc_xpt_user *u | |||
109 | 109 | ||
110 | int svc_reg_xprt_class(struct svc_xprt_class *); | 110 | int svc_reg_xprt_class(struct svc_xprt_class *); |
111 | void svc_unreg_xprt_class(struct svc_xprt_class *); | 111 | void svc_unreg_xprt_class(struct svc_xprt_class *); |
112 | void svc_xprt_init(struct svc_xprt_class *, struct svc_xprt *, | 112 | void svc_xprt_init(struct net *, struct svc_xprt_class *, struct svc_xprt *, |
113 | struct svc_serv *); | 113 | struct svc_serv *); |
114 | int svc_create_xprt(struct svc_serv *, const char *, struct net *, | 114 | int svc_create_xprt(struct svc_serv *, const char *, struct net *, |
115 | const int, const unsigned short, int); | 115 | const int, const unsigned short, int); |
@@ -118,7 +118,6 @@ void svc_xprt_received(struct svc_xprt *); | |||
118 | void svc_xprt_put(struct svc_xprt *xprt); | 118 | void svc_xprt_put(struct svc_xprt *xprt); |
119 | void svc_xprt_copy_addrs(struct svc_rqst *rqstp, struct svc_xprt *xprt); | 119 | void svc_xprt_copy_addrs(struct svc_rqst *rqstp, struct svc_xprt *xprt); |
120 | void svc_close_xprt(struct svc_xprt *xprt); | 120 | void svc_close_xprt(struct svc_xprt *xprt); |
121 | void svc_delete_xprt(struct svc_xprt *xprt); | ||
122 | int svc_port_is_privileged(struct sockaddr *sin); | 121 | int svc_port_is_privileged(struct sockaddr *sin); |
123 | int svc_print_xprts(char *buf, int maxlen); | 122 | int svc_print_xprts(char *buf, int maxlen); |
124 | struct svc_xprt *svc_find_xprt(struct svc_serv *serv, const char *xcl_name, | 123 | struct svc_xprt *svc_find_xprt(struct svc_serv *serv, const char *xcl_name, |
diff --git a/include/linux/sunrpc/svcsock.h b/include/linux/sunrpc/svcsock.h index 85c50b40759d..c84e9741cb2a 100644 --- a/include/linux/sunrpc/svcsock.h +++ b/include/linux/sunrpc/svcsock.h | |||
@@ -34,7 +34,7 @@ struct svc_sock { | |||
34 | /* | 34 | /* |
35 | * Function prototypes. | 35 | * Function prototypes. |
36 | */ | 36 | */ |
37 | void svc_close_all(struct list_head *); | 37 | void svc_close_all(struct svc_serv *); |
38 | int svc_recv(struct svc_rqst *, long); | 38 | int svc_recv(struct svc_rqst *, long); |
39 | int svc_send(struct svc_rqst *); | 39 | int svc_send(struct svc_rqst *); |
40 | void svc_drop(struct svc_rqst *); | 40 | void svc_drop(struct svc_rqst *); |
diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c index 03b56bc3b659..465df9ae1046 100644 --- a/net/sunrpc/cache.c +++ b/net/sunrpc/cache.c | |||
@@ -1641,6 +1641,7 @@ int cache_register_net(struct cache_detail *cd, struct net *net) | |||
1641 | sunrpc_destroy_cache_detail(cd); | 1641 | sunrpc_destroy_cache_detail(cd); |
1642 | return ret; | 1642 | return ret; |
1643 | } | 1643 | } |
1644 | EXPORT_SYMBOL_GPL(cache_register_net); | ||
1644 | 1645 | ||
1645 | int cache_register(struct cache_detail *cd) | 1646 | int cache_register(struct cache_detail *cd) |
1646 | { | 1647 | { |
@@ -1653,6 +1654,7 @@ void cache_unregister_net(struct cache_detail *cd, struct net *net) | |||
1653 | remove_cache_proc_entries(cd, net); | 1654 | remove_cache_proc_entries(cd, net); |
1654 | sunrpc_destroy_cache_detail(cd); | 1655 | sunrpc_destroy_cache_detail(cd); |
1655 | } | 1656 | } |
1657 | EXPORT_SYMBOL_GPL(cache_unregister_net); | ||
1656 | 1658 | ||
1657 | void cache_unregister(struct cache_detail *cd) | 1659 | void cache_unregister(struct cache_detail *cd) |
1658 | { | 1660 | { |
diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index 9d01d46b05f3..e4aabc02368b 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c | |||
@@ -167,6 +167,7 @@ svc_pool_map_alloc_arrays(struct svc_pool_map *m, unsigned int maxpools) | |||
167 | 167 | ||
168 | fail_free: | 168 | fail_free: |
169 | kfree(m->to_pool); | 169 | kfree(m->to_pool); |
170 | m->to_pool = NULL; | ||
170 | fail: | 171 | fail: |
171 | return -ENOMEM; | 172 | return -ENOMEM; |
172 | } | 173 | } |
@@ -285,9 +286,10 @@ svc_pool_map_put(void) | |||
285 | mutex_lock(&svc_pool_map_mutex); | 286 | mutex_lock(&svc_pool_map_mutex); |
286 | 287 | ||
287 | if (!--m->count) { | 288 | if (!--m->count) { |
288 | m->mode = SVC_POOL_DEFAULT; | ||
289 | kfree(m->to_pool); | 289 | kfree(m->to_pool); |
290 | m->to_pool = NULL; | ||
290 | kfree(m->pool_to); | 291 | kfree(m->pool_to); |
292 | m->pool_to = NULL; | ||
291 | m->npools = 0; | 293 | m->npools = 0; |
292 | } | 294 | } |
293 | 295 | ||
@@ -527,17 +529,20 @@ svc_destroy(struct svc_serv *serv) | |||
527 | printk("svc_destroy: no threads for serv=%p!\n", serv); | 529 | printk("svc_destroy: no threads for serv=%p!\n", serv); |
528 | 530 | ||
529 | del_timer_sync(&serv->sv_temptimer); | 531 | del_timer_sync(&serv->sv_temptimer); |
530 | 532 | /* | |
531 | svc_close_all(&serv->sv_tempsocks); | 533 | * The set of xprts (contained in the sv_tempsocks and |
534 | * sv_permsocks lists) is now constant, since it is modified | ||
535 | * only by accepting new sockets (done by service threads in | ||
536 | * svc_recv) or aging old ones (done by sv_temptimer), or | ||
537 | * configuration changes (excluded by whatever locking the | ||
538 | * caller is using--nfsd_mutex in the case of nfsd). So it's | ||
539 | * safe to traverse those lists and shut everything down: | ||
540 | */ | ||
541 | svc_close_all(serv); | ||
532 | 542 | ||
533 | if (serv->sv_shutdown) | 543 | if (serv->sv_shutdown) |
534 | serv->sv_shutdown(serv); | 544 | serv->sv_shutdown(serv); |
535 | 545 | ||
536 | svc_close_all(&serv->sv_permsocks); | ||
537 | |||
538 | BUG_ON(!list_empty(&serv->sv_permsocks)); | ||
539 | BUG_ON(!list_empty(&serv->sv_tempsocks)); | ||
540 | |||
541 | cache_clean_deferred(serv); | 546 | cache_clean_deferred(serv); |
542 | 547 | ||
543 | if (svc_serv_is_pooled(serv)) | 548 | if (svc_serv_is_pooled(serv)) |
@@ -683,8 +688,8 @@ found_pool: | |||
683 | * Create or destroy enough new threads to make the number | 688 | * Create or destroy enough new threads to make the number |
684 | * of threads the given number. If `pool' is non-NULL, applies | 689 | * of threads the given number. If `pool' is non-NULL, applies |
685 | * only to threads in that pool, otherwise round-robins between | 690 | * only to threads in that pool, otherwise round-robins between |
686 | * all pools. Must be called with a svc_get() reference and | 691 | * all pools. Caller must ensure that mutual exclusion between this and |
687 | * the BKL or another lock to protect access to svc_serv fields. | 692 | * server startup or shutdown. |
688 | * | 693 | * |
689 | * Destroying threads relies on the service threads filling in | 694 | * Destroying threads relies on the service threads filling in |
690 | * rqstp->rq_task, which only the nfs ones do. Assumes the serv | 695 | * rqstp->rq_task, which only the nfs ones do. Assumes the serv |
diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c index 38649cfa4e81..74cb0d8e9ca1 100644 --- a/net/sunrpc/svc_xprt.c +++ b/net/sunrpc/svc_xprt.c | |||
@@ -22,6 +22,7 @@ static struct svc_deferred_req *svc_deferred_dequeue(struct svc_xprt *xprt); | |||
22 | static int svc_deferred_recv(struct svc_rqst *rqstp); | 22 | static int svc_deferred_recv(struct svc_rqst *rqstp); |
23 | static struct cache_deferred_req *svc_defer(struct cache_req *req); | 23 | static struct cache_deferred_req *svc_defer(struct cache_req *req); |
24 | static void svc_age_temp_xprts(unsigned long closure); | 24 | static void svc_age_temp_xprts(unsigned long closure); |
25 | static void svc_delete_xprt(struct svc_xprt *xprt); | ||
25 | 26 | ||
26 | /* apparently the "standard" is that clients close | 27 | /* apparently the "standard" is that clients close |
27 | * idle connections after 5 minutes, servers after | 28 | * idle connections after 5 minutes, servers after |
@@ -147,8 +148,8 @@ EXPORT_SYMBOL_GPL(svc_xprt_put); | |||
147 | * Called by transport drivers to initialize the transport independent | 148 | * Called by transport drivers to initialize the transport independent |
148 | * portion of the transport instance. | 149 | * portion of the transport instance. |
149 | */ | 150 | */ |
150 | void svc_xprt_init(struct svc_xprt_class *xcl, struct svc_xprt *xprt, | 151 | void svc_xprt_init(struct net *net, struct svc_xprt_class *xcl, |
151 | struct svc_serv *serv) | 152 | struct svc_xprt *xprt, struct svc_serv *serv) |
152 | { | 153 | { |
153 | memset(xprt, 0, sizeof(*xprt)); | 154 | memset(xprt, 0, sizeof(*xprt)); |
154 | xprt->xpt_class = xcl; | 155 | xprt->xpt_class = xcl; |
@@ -163,7 +164,7 @@ void svc_xprt_init(struct svc_xprt_class *xcl, struct svc_xprt *xprt, | |||
163 | spin_lock_init(&xprt->xpt_lock); | 164 | spin_lock_init(&xprt->xpt_lock); |
164 | set_bit(XPT_BUSY, &xprt->xpt_flags); | 165 | set_bit(XPT_BUSY, &xprt->xpt_flags); |
165 | rpc_init_wait_queue(&xprt->xpt_bc_pending, "xpt_bc_pending"); | 166 | rpc_init_wait_queue(&xprt->xpt_bc_pending, "xpt_bc_pending"); |
166 | xprt->xpt_net = get_net(&init_net); | 167 | xprt->xpt_net = get_net(net); |
167 | } | 168 | } |
168 | EXPORT_SYMBOL_GPL(svc_xprt_init); | 169 | EXPORT_SYMBOL_GPL(svc_xprt_init); |
169 | 170 | ||
@@ -878,7 +879,7 @@ static void call_xpt_users(struct svc_xprt *xprt) | |||
878 | /* | 879 | /* |
879 | * Remove a dead transport | 880 | * Remove a dead transport |
880 | */ | 881 | */ |
881 | void svc_delete_xprt(struct svc_xprt *xprt) | 882 | static void svc_delete_xprt(struct svc_xprt *xprt) |
882 | { | 883 | { |
883 | struct svc_serv *serv = xprt->xpt_server; | 884 | struct svc_serv *serv = xprt->xpt_server; |
884 | struct svc_deferred_req *dr; | 885 | struct svc_deferred_req *dr; |
@@ -893,14 +894,7 @@ void svc_delete_xprt(struct svc_xprt *xprt) | |||
893 | spin_lock_bh(&serv->sv_lock); | 894 | spin_lock_bh(&serv->sv_lock); |
894 | if (!test_and_set_bit(XPT_DETACHED, &xprt->xpt_flags)) | 895 | if (!test_and_set_bit(XPT_DETACHED, &xprt->xpt_flags)) |
895 | list_del_init(&xprt->xpt_list); | 896 | list_del_init(&xprt->xpt_list); |
896 | /* | 897 | BUG_ON(!list_empty(&xprt->xpt_ready)); |
897 | * The only time we're called while xpt_ready is still on a list | ||
898 | * is while the list itself is about to be destroyed (in | ||
899 | * svc_destroy). BUT svc_xprt_enqueue could still be attempting | ||
900 | * to add new entries to the sp_sockets list, so we can't leave | ||
901 | * a freed xprt on it. | ||
902 | */ | ||
903 | list_del_init(&xprt->xpt_ready); | ||
904 | if (test_bit(XPT_TEMP, &xprt->xpt_flags)) | 898 | if (test_bit(XPT_TEMP, &xprt->xpt_flags)) |
905 | serv->sv_tmpcnt--; | 899 | serv->sv_tmpcnt--; |
906 | spin_unlock_bh(&serv->sv_lock); | 900 | spin_unlock_bh(&serv->sv_lock); |
@@ -928,22 +922,48 @@ void svc_close_xprt(struct svc_xprt *xprt) | |||
928 | } | 922 | } |
929 | EXPORT_SYMBOL_GPL(svc_close_xprt); | 923 | EXPORT_SYMBOL_GPL(svc_close_xprt); |
930 | 924 | ||
931 | void svc_close_all(struct list_head *xprt_list) | 925 | static void svc_close_list(struct list_head *xprt_list) |
926 | { | ||
927 | struct svc_xprt *xprt; | ||
928 | |||
929 | list_for_each_entry(xprt, xprt_list, xpt_list) { | ||
930 | set_bit(XPT_CLOSE, &xprt->xpt_flags); | ||
931 | set_bit(XPT_BUSY, &xprt->xpt_flags); | ||
932 | } | ||
933 | } | ||
934 | |||
935 | void svc_close_all(struct svc_serv *serv) | ||
932 | { | 936 | { |
937 | struct svc_pool *pool; | ||
933 | struct svc_xprt *xprt; | 938 | struct svc_xprt *xprt; |
934 | struct svc_xprt *tmp; | 939 | struct svc_xprt *tmp; |
940 | int i; | ||
941 | |||
942 | svc_close_list(&serv->sv_tempsocks); | ||
943 | svc_close_list(&serv->sv_permsocks); | ||
935 | 944 | ||
945 | for (i = 0; i < serv->sv_nrpools; i++) { | ||
946 | pool = &serv->sv_pools[i]; | ||
947 | |||
948 | spin_lock_bh(&pool->sp_lock); | ||
949 | while (!list_empty(&pool->sp_sockets)) { | ||
950 | xprt = list_first_entry(&pool->sp_sockets, struct svc_xprt, xpt_ready); | ||
951 | list_del_init(&xprt->xpt_ready); | ||
952 | } | ||
953 | spin_unlock_bh(&pool->sp_lock); | ||
954 | } | ||
936 | /* | 955 | /* |
937 | * The server is shutting down, and no more threads are running. | 956 | * At this point the sp_sockets lists will stay empty, since |
938 | * svc_xprt_enqueue() might still be running, but at worst it | 957 | * svc_enqueue will not add new entries without taking the |
939 | * will re-add the xprt to sp_sockets, which will soon get | 958 | * sp_lock and checking XPT_BUSY. |
940 | * freed. So we don't bother with any more locking, and don't | ||
941 | * leave the close to the (nonexistent) server threads: | ||
942 | */ | 959 | */ |
943 | list_for_each_entry_safe(xprt, tmp, xprt_list, xpt_list) { | 960 | list_for_each_entry_safe(xprt, tmp, &serv->sv_tempsocks, xpt_list) |
944 | set_bit(XPT_CLOSE, &xprt->xpt_flags); | ||
945 | svc_delete_xprt(xprt); | 961 | svc_delete_xprt(xprt); |
946 | } | 962 | list_for_each_entry_safe(xprt, tmp, &serv->sv_permsocks, xpt_list) |
963 | svc_delete_xprt(xprt); | ||
964 | |||
965 | BUG_ON(!list_empty(&serv->sv_permsocks)); | ||
966 | BUG_ON(!list_empty(&serv->sv_tempsocks)); | ||
947 | } | 967 | } |
948 | 968 | ||
949 | /* | 969 | /* |
diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c index 4653286fcc9e..464570906f80 100644 --- a/net/sunrpc/svcsock.c +++ b/net/sunrpc/svcsock.c | |||
@@ -739,7 +739,8 @@ static void svc_udp_init(struct svc_sock *svsk, struct svc_serv *serv) | |||
739 | { | 739 | { |
740 | int err, level, optname, one = 1; | 740 | int err, level, optname, one = 1; |
741 | 741 | ||
742 | svc_xprt_init(&svc_udp_class, &svsk->sk_xprt, serv); | 742 | svc_xprt_init(sock_net(svsk->sk_sock->sk), &svc_udp_class, |
743 | &svsk->sk_xprt, serv); | ||
743 | clear_bit(XPT_CACHE_AUTH, &svsk->sk_xprt.xpt_flags); | 744 | clear_bit(XPT_CACHE_AUTH, &svsk->sk_xprt.xpt_flags); |
744 | svsk->sk_sk->sk_data_ready = svc_udp_data_ready; | 745 | svsk->sk_sk->sk_data_ready = svc_udp_data_ready; |
745 | svsk->sk_sk->sk_write_space = svc_write_space; | 746 | svsk->sk_sk->sk_write_space = svc_write_space; |
@@ -1343,7 +1344,8 @@ static void svc_tcp_init(struct svc_sock *svsk, struct svc_serv *serv) | |||
1343 | { | 1344 | { |
1344 | struct sock *sk = svsk->sk_sk; | 1345 | struct sock *sk = svsk->sk_sk; |
1345 | 1346 | ||
1346 | svc_xprt_init(&svc_tcp_class, &svsk->sk_xprt, serv); | 1347 | svc_xprt_init(sock_net(svsk->sk_sock->sk), &svc_tcp_class, |
1348 | &svsk->sk_xprt, serv); | ||
1347 | set_bit(XPT_CACHE_AUTH, &svsk->sk_xprt.xpt_flags); | 1349 | set_bit(XPT_CACHE_AUTH, &svsk->sk_xprt.xpt_flags); |
1348 | if (sk->sk_state == TCP_LISTEN) { | 1350 | if (sk->sk_state == TCP_LISTEN) { |
1349 | dprintk("setting up TCP socket for listening\n"); | 1351 | dprintk("setting up TCP socket for listening\n"); |
@@ -1659,7 +1661,7 @@ static struct svc_xprt *svc_bc_create_socket(struct svc_serv *serv, | |||
1659 | return ERR_PTR(-ENOMEM); | 1661 | return ERR_PTR(-ENOMEM); |
1660 | 1662 | ||
1661 | xprt = &svsk->sk_xprt; | 1663 | xprt = &svsk->sk_xprt; |
1662 | svc_xprt_init(&svc_tcp_bc_class, xprt, serv); | 1664 | svc_xprt_init(net, &svc_tcp_bc_class, xprt, serv); |
1663 | 1665 | ||
1664 | serv->sv_bc_xprt = xprt; | 1666 | serv->sv_bc_xprt = xprt; |
1665 | 1667 | ||
diff --git a/net/sunrpc/xprtrdma/svc_rdma_transport.c b/net/sunrpc/xprtrdma/svc_rdma_transport.c index ba1296d88de0..894cb42db91d 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_transport.c +++ b/net/sunrpc/xprtrdma/svc_rdma_transport.c | |||
@@ -453,7 +453,7 @@ static struct svcxprt_rdma *rdma_create_xprt(struct svc_serv *serv, | |||
453 | 453 | ||
454 | if (!cma_xprt) | 454 | if (!cma_xprt) |
455 | return NULL; | 455 | return NULL; |
456 | svc_xprt_init(&svc_rdma_class, &cma_xprt->sc_xprt, serv); | 456 | svc_xprt_init(&init_net, &svc_rdma_class, &cma_xprt->sc_xprt, serv); |
457 | INIT_LIST_HEAD(&cma_xprt->sc_accept_q); | 457 | INIT_LIST_HEAD(&cma_xprt->sc_accept_q); |
458 | INIT_LIST_HEAD(&cma_xprt->sc_dto_q); | 458 | INIT_LIST_HEAD(&cma_xprt->sc_dto_q); |
459 | INIT_LIST_HEAD(&cma_xprt->sc_rq_dto_q); | 459 | INIT_LIST_HEAD(&cma_xprt->sc_rq_dto_q); |
diff --git a/tools/nfsd/inject_fault.sh b/tools/nfsd/inject_fault.sh new file mode 100755 index 000000000000..06a399ac8b2f --- /dev/null +++ b/tools/nfsd/inject_fault.sh | |||
@@ -0,0 +1,49 @@ | |||
1 | #!/bin/bash | ||
2 | # | ||
3 | # Copyright (c) 2011 Bryan Schumaker <bjschuma@netapp.com> | ||
4 | # | ||
5 | # Script for easier NFSD fault injection | ||
6 | |||
7 | # Check that debugfs has been mounted | ||
8 | DEBUGFS=`cat /proc/mounts | grep debugfs` | ||
9 | if [ "$DEBUGFS" == "" ]; then | ||
10 | echo "debugfs does not appear to be mounted!" | ||
11 | echo "Please mount debugfs and try again" | ||
12 | exit 1 | ||
13 | fi | ||
14 | |||
15 | # Check that the fault injection directory exists | ||
16 | DEBUGDIR=`echo $DEBUGFS | awk '{print $2}'`/nfsd | ||
17 | if [ ! -d "$DEBUGDIR" ]; then | ||
18 | echo "$DEBUGDIR does not exist" | ||
19 | echo "Check that your .config selects CONFIG_NFSD_FAULT_INJECTION" | ||
20 | exit 1 | ||
21 | fi | ||
22 | |||
23 | function help() | ||
24 | { | ||
25 | echo "Usage $0 injection_type [count]" | ||
26 | echo "" | ||
27 | echo "Injection types are:" | ||
28 | ls $DEBUGDIR | ||
29 | exit 1 | ||
30 | } | ||
31 | |||
32 | if [ $# == 0 ]; then | ||
33 | help | ||
34 | elif [ ! -f $DEBUGDIR/$1 ]; then | ||
35 | help | ||
36 | elif [ $# != 2 ]; then | ||
37 | COUNT=0 | ||
38 | else | ||
39 | COUNT=$2 | ||
40 | fi | ||
41 | |||
42 | BEFORE=`mktemp` | ||
43 | AFTER=`mktemp` | ||
44 | dmesg > $BEFORE | ||
45 | echo $COUNT > $DEBUGDIR/$1 | ||
46 | dmesg > $AFTER | ||
47 | # Capture lines that only exist in the $AFTER file | ||
48 | diff $BEFORE $AFTER | grep ">" | ||
49 | rm -f $BEFORE $AFTER | ||