diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-12-20 17:04:11 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-12-20 17:04:11 -0500 |
commit | 982197277c85018cc6eb77f1d3bef17933b0c5fd (patch) | |
tree | 805fcef9ec7c1e83867b89332fd37f751594fae3 | |
parent | 40889e8d9fc6355980cf2bc94ef4356c10dec4ec (diff) | |
parent | 24ffb93872f7363a01ad639e3c8a9889b46c3f0a (diff) |
Merge branch 'for-3.8' of git://linux-nfs.org/~bfields/linux
Pull nfsd update from Bruce Fields:
"Included this time:
- more nfsd containerization work from Stanislav Kinsbursky: we're
not quite there yet, but should be by 3.9.
- NFSv4.1 progress: implementation of basic backchannel security
negotiation and the mandatory BACKCHANNEL_CTL operation. See
http://wiki.linux-nfs.org/wiki/index.php/Server_4.0_and_4.1_issues
for remaining TODO's
- Fixes for some bugs that could be triggered by unusual compounds.
Our xdr code wasn't designed with v4 compounds in mind, and it
shows. A more thorough rewrite is still a todo.
- If you've ever seen "RPC: multiple fragments per record not
supported" logged while using some sort of odd userland NFS client,
that should now be fixed.
- Further work from Jeff Layton on our mechanism for storing
information about NFSv4 clients across reboots.
- Further work from Bryan Schumaker on his fault-injection mechanism
(which allows us to discard selective NFSv4 state, to excercise
rarely-taken recovery code paths in the client.)
- The usual mix of miscellaneous bugs and cleanup.
Thanks to everyone who tested or contributed this cycle."
* 'for-3.8' of git://linux-nfs.org/~bfields/linux: (111 commits)
nfsd4: don't leave freed stateid hashed
nfsd4: free_stateid can use the current stateid
nfsd4: cleanup: replace rq_resused count by rq_next_page pointer
nfsd: warn on odd reply state in nfsd_vfs_read
nfsd4: fix oops on unusual readlike compound
nfsd4: disable zero-copy on non-final read ops
svcrpc: fix some printks
NFSD: Correct the size calculation in fault_inject_write
NFSD: Pass correct buffer size to rpc_ntop
nfsd: pass proper net to nfsd_destroy() from NFSd kthreads
nfsd: simplify service shutdown
nfsd: replace boolean nfsd_up flag by users counter
nfsd: simplify NFSv4 state init and shutdown
nfsd: introduce helpers for generic resources init and shutdown
nfsd: make NFSd service structure allocated per net
nfsd: make NFSd service boot time per-net
nfsd: per-net NFSd up flag introduced
nfsd: move per-net startup code to separated function
nfsd: pass net to __write_ports() and down
nfsd: pass net to nfsd_set_nrthreads()
...
32 files changed, 1911 insertions, 1067 deletions
diff --git a/Documentation/filesystems/nfs/nfs41-server.txt b/Documentation/filesystems/nfs/nfs41-server.txt index 092fad92a3f0..01c2db769791 100644 --- a/Documentation/filesystems/nfs/nfs41-server.txt +++ b/Documentation/filesystems/nfs/nfs41-server.txt | |||
@@ -39,21 +39,10 @@ interoperability problems with future clients. Known issues: | |||
39 | from a linux client are possible, but we aren't really | 39 | from a linux client are possible, but we aren't really |
40 | conformant with the spec (for example, we don't use kerberos | 40 | conformant with the spec (for example, we don't use kerberos |
41 | on the backchannel correctly). | 41 | on the backchannel correctly). |
42 | - Incomplete backchannel support: incomplete backchannel gss | ||
43 | support and no support for BACKCHANNEL_CTL mean that | ||
44 | callbacks (hence delegations and layouts) may not be | ||
45 | available and clients confused by the incomplete | ||
46 | implementation may fail. | ||
47 | - We do not support SSV, which provides security for shared | 42 | - We do not support SSV, which provides security for shared |
48 | client-server state (thus preventing unauthorized tampering | 43 | client-server state (thus preventing unauthorized tampering |
49 | with locks and opens, for example). It is mandatory for | 44 | with locks and opens, for example). It is mandatory for |
50 | servers to support this, though no clients use it yet. | 45 | servers to support this, though no clients use it yet. |
51 | - Mandatory operations which we do not support, such as | ||
52 | DESTROY_CLIENTID, are not currently used by clients, but will be | ||
53 | (and the spec recommends their uses in common cases), and | ||
54 | clients should not be expected to know how to recover from the | ||
55 | case where they are not supported. This will eventually cause | ||
56 | interoperability failures. | ||
57 | 46 | ||
58 | In addition, some limitations are inherited from the current NFSv4 | 47 | In addition, some limitations are inherited from the current NFSv4 |
59 | implementation: | 48 | implementation: |
@@ -89,7 +78,7 @@ Operations | |||
89 | | | MNI | or OPT) | | | 78 | | | MNI | or OPT) | | |
90 | +----------------------+------------+--------------+----------------+ | 79 | +----------------------+------------+--------------+----------------+ |
91 | | ACCESS | REQ | | Section 18.1 | | 80 | | ACCESS | REQ | | Section 18.1 | |
92 | NS | BACKCHANNEL_CTL | REQ | | Section 18.33 | | 81 | I | BACKCHANNEL_CTL | REQ | | Section 18.33 | |
93 | I | BIND_CONN_TO_SESSION | REQ | | Section 18.34 | | 82 | I | BIND_CONN_TO_SESSION | REQ | | Section 18.34 | |
94 | | CLOSE | REQ | | Section 18.2 | | 83 | | CLOSE | REQ | | Section 18.2 | |
95 | | COMMIT | REQ | | Section 18.3 | | 84 | | COMMIT | REQ | | Section 18.3 | |
@@ -99,7 +88,7 @@ NS*| DELEGPURGE | OPT | FDELG (REQ) | Section 18.5 | | |||
99 | | DELEGRETURN | OPT | FDELG, | Section 18.6 | | 88 | | DELEGRETURN | OPT | FDELG, | Section 18.6 | |
100 | | | | DDELG, pNFS | | | 89 | | | | DDELG, pNFS | | |
101 | | | | (REQ) | | | 90 | | | | (REQ) | | |
102 | NS | DESTROY_CLIENTID | REQ | | Section 18.50 | | 91 | I | DESTROY_CLIENTID | REQ | | Section 18.50 | |
103 | I | DESTROY_SESSION | REQ | | Section 18.37 | | 92 | I | DESTROY_SESSION | REQ | | Section 18.37 | |
104 | I | EXCHANGE_ID | REQ | | Section 18.35 | | 93 | I | EXCHANGE_ID | REQ | | Section 18.35 | |
105 | I | FREE_STATEID | REQ | | Section 18.38 | | 94 | I | FREE_STATEID | REQ | | Section 18.38 | |
@@ -192,7 +181,6 @@ EXCHANGE_ID: | |||
192 | 181 | ||
193 | CREATE_SESSION: | 182 | CREATE_SESSION: |
194 | * backchannel attributes are ignored | 183 | * backchannel attributes are ignored |
195 | * backchannel security parameters are ignored | ||
196 | 184 | ||
197 | SEQUENCE: | 185 | SEQUENCE: |
198 | * no support for dynamic slot table renegotiation (optional) | 186 | * no support for dynamic slot table renegotiation (optional) |
@@ -202,7 +190,7 @@ Nonstandard compound limitations: | |||
202 | ca_maxrequestsize request and a ca_maxresponsesize reply, so we may | 190 | ca_maxrequestsize request and a ca_maxresponsesize reply, so we may |
203 | fail to live up to the promise we made in CREATE_SESSION fore channel | 191 | fail to live up to the promise we made in CREATE_SESSION fore channel |
204 | negotiation. | 192 | negotiation. |
205 | * No more than one IO operation (read, write, readdir) allowed per | 193 | * No more than one read-like operation allowed per compound; encoding |
206 | compound. | 194 | replies that cross page boundaries (except for read data) not handled. |
207 | 195 | ||
208 | See also http://wiki.linux-nfs.org/wiki/index.php/Server_4.0_and_4.1_issues. | 196 | See also http://wiki.linux-nfs.org/wiki/index.php/Server_4.0_and_4.1_issues. |
diff --git a/fs/exportfs/expfs.c b/fs/exportfs/expfs.c index 606bb074c501..5df4bb4aab14 100644 --- a/fs/exportfs/expfs.c +++ b/fs/exportfs/expfs.c | |||
@@ -322,10 +322,10 @@ static int export_encode_fh(struct inode *inode, struct fid *fid, | |||
322 | 322 | ||
323 | if (parent && (len < 4)) { | 323 | if (parent && (len < 4)) { |
324 | *max_len = 4; | 324 | *max_len = 4; |
325 | return 255; | 325 | return FILEID_INVALID; |
326 | } else if (len < 2) { | 326 | } else if (len < 2) { |
327 | *max_len = 2; | 327 | *max_len = 2; |
328 | return 255; | 328 | return FILEID_INVALID; |
329 | } | 329 | } |
330 | 330 | ||
331 | len = 2; | 331 | len = 2; |
diff --git a/fs/fhandle.c b/fs/fhandle.c index cccdc874bb55..999ff5c3cab0 100644 --- a/fs/fhandle.c +++ b/fs/fhandle.c | |||
@@ -52,7 +52,7 @@ static long do_sys_name_to_handle(struct path *path, | |||
52 | handle_bytes = handle_dwords * sizeof(u32); | 52 | handle_bytes = handle_dwords * sizeof(u32); |
53 | handle->handle_bytes = handle_bytes; | 53 | handle->handle_bytes = handle_bytes; |
54 | if ((handle->handle_bytes > f_handle.handle_bytes) || | 54 | if ((handle->handle_bytes > f_handle.handle_bytes) || |
55 | (retval == 255) || (retval == -ENOSPC)) { | 55 | (retval == FILEID_INVALID) || (retval == -ENOSPC)) { |
56 | /* As per old exportfs_encode_fh documentation | 56 | /* As per old exportfs_encode_fh documentation |
57 | * we could return ENOSPC to indicate overflow | 57 | * we could return ENOSPC to indicate overflow |
58 | * But file system returned 255 always. So handle | 58 | * But file system returned 255 always. So handle |
diff --git a/fs/nfsd/fault_inject.c b/fs/nfsd/fault_inject.c index e6c38159622f..e761ee95617f 100644 --- a/fs/nfsd/fault_inject.c +++ b/fs/nfsd/fault_inject.c | |||
@@ -8,61 +8,144 @@ | |||
8 | #include <linux/fs.h> | 8 | #include <linux/fs.h> |
9 | #include <linux/debugfs.h> | 9 | #include <linux/debugfs.h> |
10 | #include <linux/module.h> | 10 | #include <linux/module.h> |
11 | #include <linux/nsproxy.h> | ||
12 | #include <linux/sunrpc/clnt.h> | ||
13 | #include <asm/uaccess.h> | ||
11 | 14 | ||
12 | #include "state.h" | 15 | #include "state.h" |
13 | #include "fault_inject.h" | 16 | #include "netns.h" |
14 | 17 | ||
15 | struct nfsd_fault_inject_op { | 18 | struct nfsd_fault_inject_op { |
16 | char *file; | 19 | char *file; |
17 | void (*func)(u64); | 20 | u64 (*forget)(struct nfs4_client *, u64); |
21 | u64 (*print)(struct nfs4_client *, u64); | ||
18 | }; | 22 | }; |
19 | 23 | ||
20 | static struct nfsd_fault_inject_op inject_ops[] = { | 24 | static struct nfsd_fault_inject_op inject_ops[] = { |
21 | { | 25 | { |
22 | .file = "forget_clients", | 26 | .file = "forget_clients", |
23 | .func = nfsd_forget_clients, | 27 | .forget = nfsd_forget_client, |
28 | .print = nfsd_print_client, | ||
24 | }, | 29 | }, |
25 | { | 30 | { |
26 | .file = "forget_locks", | 31 | .file = "forget_locks", |
27 | .func = nfsd_forget_locks, | 32 | .forget = nfsd_forget_client_locks, |
33 | .print = nfsd_print_client_locks, | ||
28 | }, | 34 | }, |
29 | { | 35 | { |
30 | .file = "forget_openowners", | 36 | .file = "forget_openowners", |
31 | .func = nfsd_forget_openowners, | 37 | .forget = nfsd_forget_client_openowners, |
38 | .print = nfsd_print_client_openowners, | ||
32 | }, | 39 | }, |
33 | { | 40 | { |
34 | .file = "forget_delegations", | 41 | .file = "forget_delegations", |
35 | .func = nfsd_forget_delegations, | 42 | .forget = nfsd_forget_client_delegations, |
43 | .print = nfsd_print_client_delegations, | ||
36 | }, | 44 | }, |
37 | { | 45 | { |
38 | .file = "recall_delegations", | 46 | .file = "recall_delegations", |
39 | .func = nfsd_recall_delegations, | 47 | .forget = nfsd_recall_client_delegations, |
48 | .print = nfsd_print_client_delegations, | ||
40 | }, | 49 | }, |
41 | }; | 50 | }; |
42 | 51 | ||
43 | static long int NUM_INJECT_OPS = sizeof(inject_ops) / sizeof(struct nfsd_fault_inject_op); | 52 | static long int NUM_INJECT_OPS = sizeof(inject_ops) / sizeof(struct nfsd_fault_inject_op); |
44 | static struct dentry *debug_dir; | 53 | static struct dentry *debug_dir; |
45 | 54 | ||
46 | static int nfsd_inject_set(void *op_ptr, u64 val) | 55 | static void nfsd_inject_set(struct nfsd_fault_inject_op *op, u64 val) |
47 | { | 56 | { |
48 | struct nfsd_fault_inject_op *op = op_ptr; | 57 | u64 count = 0; |
49 | 58 | ||
50 | if (val == 0) | 59 | if (val == 0) |
51 | printk(KERN_INFO "NFSD Fault Injection: %s (all)", op->file); | 60 | printk(KERN_INFO "NFSD Fault Injection: %s (all)", op->file); |
52 | else | 61 | else |
53 | printk(KERN_INFO "NFSD Fault Injection: %s (n = %llu)", op->file, val); | 62 | printk(KERN_INFO "NFSD Fault Injection: %s (n = %llu)", op->file, val); |
54 | 63 | ||
55 | op->func(val); | 64 | nfs4_lock_state(); |
56 | return 0; | 65 | count = nfsd_for_n_state(val, op->forget); |
66 | nfs4_unlock_state(); | ||
67 | printk(KERN_INFO "NFSD: %s: found %llu", op->file, count); | ||
57 | } | 68 | } |
58 | 69 | ||
59 | static int nfsd_inject_get(void *data, u64 *val) | 70 | static void nfsd_inject_set_client(struct nfsd_fault_inject_op *op, |
71 | struct sockaddr_storage *addr, | ||
72 | size_t addr_size) | ||
60 | { | 73 | { |
61 | *val = 0; | 74 | char buf[INET6_ADDRSTRLEN]; |
62 | return 0; | 75 | struct nfs4_client *clp; |
76 | u64 count; | ||
77 | |||
78 | nfs4_lock_state(); | ||
79 | clp = nfsd_find_client(addr, addr_size); | ||
80 | if (clp) { | ||
81 | count = op->forget(clp, 0); | ||
82 | rpc_ntop((struct sockaddr *)&clp->cl_addr, buf, sizeof(buf)); | ||
83 | printk(KERN_INFO "NFSD [%s]: Client %s had %llu state object(s)\n", op->file, buf, count); | ||
84 | } | ||
85 | nfs4_unlock_state(); | ||
86 | } | ||
87 | |||
88 | static void nfsd_inject_get(struct nfsd_fault_inject_op *op, u64 *val) | ||
89 | { | ||
90 | nfs4_lock_state(); | ||
91 | *val = nfsd_for_n_state(0, op->print); | ||
92 | nfs4_unlock_state(); | ||
63 | } | 93 | } |
64 | 94 | ||
65 | DEFINE_SIMPLE_ATTRIBUTE(fops_nfsd, nfsd_inject_get, nfsd_inject_set, "%llu\n"); | 95 | static ssize_t fault_inject_read(struct file *file, char __user *buf, |
96 | size_t len, loff_t *ppos) | ||
97 | { | ||
98 | static u64 val; | ||
99 | char read_buf[25]; | ||
100 | size_t size, ret; | ||
101 | loff_t pos = *ppos; | ||
102 | |||
103 | if (!pos) | ||
104 | nfsd_inject_get(file->f_dentry->d_inode->i_private, &val); | ||
105 | size = scnprintf(read_buf, sizeof(read_buf), "%llu\n", val); | ||
106 | |||
107 | if (pos < 0) | ||
108 | return -EINVAL; | ||
109 | if (pos >= size || !len) | ||
110 | return 0; | ||
111 | if (len > size - pos) | ||
112 | len = size - pos; | ||
113 | ret = copy_to_user(buf, read_buf + pos, len); | ||
114 | if (ret == len) | ||
115 | return -EFAULT; | ||
116 | len -= ret; | ||
117 | *ppos = pos + len; | ||
118 | return len; | ||
119 | } | ||
120 | |||
121 | static ssize_t fault_inject_write(struct file *file, const char __user *buf, | ||
122 | size_t len, loff_t *ppos) | ||
123 | { | ||
124 | char write_buf[INET6_ADDRSTRLEN]; | ||
125 | size_t size = min(sizeof(write_buf) - 1, len); | ||
126 | struct net *net = current->nsproxy->net_ns; | ||
127 | struct sockaddr_storage sa; | ||
128 | u64 val; | ||
129 | |||
130 | if (copy_from_user(write_buf, buf, size)) | ||
131 | return -EFAULT; | ||
132 | write_buf[size] = '\0'; | ||
133 | |||
134 | size = rpc_pton(net, write_buf, size, (struct sockaddr *)&sa, sizeof(sa)); | ||
135 | if (size > 0) | ||
136 | nfsd_inject_set_client(file->f_dentry->d_inode->i_private, &sa, size); | ||
137 | else { | ||
138 | val = simple_strtoll(write_buf, NULL, 0); | ||
139 | nfsd_inject_set(file->f_dentry->d_inode->i_private, val); | ||
140 | } | ||
141 | return len; /* on success, claim we got the whole input */ | ||
142 | } | ||
143 | |||
144 | static const struct file_operations fops_nfsd = { | ||
145 | .owner = THIS_MODULE, | ||
146 | .read = fault_inject_read, | ||
147 | .write = fault_inject_write, | ||
148 | }; | ||
66 | 149 | ||
67 | void nfsd_fault_inject_cleanup(void) | 150 | void nfsd_fault_inject_cleanup(void) |
68 | { | 151 | { |
diff --git a/fs/nfsd/fault_inject.h b/fs/nfsd/fault_inject.h deleted file mode 100644 index 90bd0570956c..000000000000 --- a/fs/nfsd/fault_inject.h +++ /dev/null | |||
@@ -1,28 +0,0 @@ | |||
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/netns.h b/fs/nfsd/netns.h index 65c2431ea32f..1051bebff1b0 100644 --- a/fs/nfsd/netns.h +++ b/fs/nfsd/netns.h | |||
@@ -24,7 +24,18 @@ | |||
24 | #include <net/net_namespace.h> | 24 | #include <net/net_namespace.h> |
25 | #include <net/netns/generic.h> | 25 | #include <net/netns/generic.h> |
26 | 26 | ||
27 | /* Hash tables for nfs4_clientid state */ | ||
28 | #define CLIENT_HASH_BITS 4 | ||
29 | #define CLIENT_HASH_SIZE (1 << CLIENT_HASH_BITS) | ||
30 | #define CLIENT_HASH_MASK (CLIENT_HASH_SIZE - 1) | ||
31 | |||
32 | #define LOCKOWNER_INO_HASH_BITS 8 | ||
33 | #define LOCKOWNER_INO_HASH_SIZE (1 << LOCKOWNER_INO_HASH_BITS) | ||
34 | |||
35 | #define SESSION_HASH_SIZE 512 | ||
36 | |||
27 | struct cld_net; | 37 | struct cld_net; |
38 | struct nfsd4_client_tracking_ops; | ||
28 | 39 | ||
29 | struct nfsd_net { | 40 | struct nfsd_net { |
30 | struct cld_net *cld_net; | 41 | struct cld_net *cld_net; |
@@ -38,7 +49,62 @@ struct nfsd_net { | |||
38 | struct lock_manager nfsd4_manager; | 49 | struct lock_manager nfsd4_manager; |
39 | bool grace_ended; | 50 | bool grace_ended; |
40 | time_t boot_time; | 51 | time_t boot_time; |
52 | |||
53 | /* | ||
54 | * reclaim_str_hashtbl[] holds known client info from previous reset/reboot | ||
55 | * used in reboot/reset lease grace period processing | ||
56 | * | ||
57 | * conf_id_hashtbl[], and conf_name_tree hold confirmed | ||
58 | * setclientid_confirmed info. | ||
59 | * | ||
60 | * unconf_str_hastbl[] and unconf_name_tree hold unconfirmed | ||
61 | * setclientid info. | ||
62 | */ | ||
63 | struct list_head *reclaim_str_hashtbl; | ||
64 | int reclaim_str_hashtbl_size; | ||
65 | struct list_head *conf_id_hashtbl; | ||
66 | struct rb_root conf_name_tree; | ||
67 | struct list_head *unconf_id_hashtbl; | ||
68 | struct rb_root unconf_name_tree; | ||
69 | struct list_head *ownerstr_hashtbl; | ||
70 | struct list_head *lockowner_ino_hashtbl; | ||
71 | struct list_head *sessionid_hashtbl; | ||
72 | /* | ||
73 | * client_lru holds client queue ordered by nfs4_client.cl_time | ||
74 | * for lease renewal. | ||
75 | * | ||
76 | * close_lru holds (open) stateowner queue ordered by nfs4_stateowner.so_time | ||
77 | * for last close replay. | ||
78 | * | ||
79 | * All of the above fields are protected by the client_mutex. | ||
80 | */ | ||
81 | struct list_head client_lru; | ||
82 | struct list_head close_lru; | ||
83 | |||
84 | struct delayed_work laundromat_work; | ||
85 | |||
86 | /* client_lock protects the client lru list and session hash table */ | ||
87 | spinlock_t client_lock; | ||
88 | |||
89 | struct file *rec_file; | ||
90 | bool in_grace; | ||
91 | struct nfsd4_client_tracking_ops *client_tracking_ops; | ||
92 | |||
93 | time_t nfsd4_lease; | ||
94 | time_t nfsd4_grace; | ||
95 | |||
96 | bool nfsd_net_up; | ||
97 | |||
98 | /* | ||
99 | * Time of server startup | ||
100 | */ | ||
101 | struct timeval nfssvc_boot; | ||
102 | |||
103 | struct svc_serv *nfsd_serv; | ||
41 | }; | 104 | }; |
42 | 105 | ||
106 | /* Simple check to find out if a given net was properly initialized */ | ||
107 | #define nfsd_netns_ready(nn) ((nn)->sessionid_hashtbl) | ||
108 | |||
43 | extern int nfsd_net_id; | 109 | extern int nfsd_net_id; |
44 | #endif /* __NFSD_NETNS_H__ */ | 110 | #endif /* __NFSD_NETNS_H__ */ |
diff --git a/fs/nfsd/nfs2acl.c b/fs/nfsd/nfs2acl.c index b314888825d5..9170861c804a 100644 --- a/fs/nfsd/nfs2acl.c +++ b/fs/nfsd/nfs2acl.c | |||
@@ -253,7 +253,7 @@ static int nfsaclsvc_encode_getaclres(struct svc_rqst *rqstp, __be32 *p, | |||
253 | (resp->mask & NFS_ACL) ? resp->acl_access : NULL, | 253 | (resp->mask & NFS_ACL) ? resp->acl_access : NULL, |
254 | (resp->mask & NFS_DFACL) ? resp->acl_default : NULL); | 254 | (resp->mask & NFS_DFACL) ? resp->acl_default : NULL); |
255 | while (w > 0) { | 255 | while (w > 0) { |
256 | if (!rqstp->rq_respages[rqstp->rq_resused++]) | 256 | if (!*(rqstp->rq_next_page++)) |
257 | return 0; | 257 | return 0; |
258 | w -= PAGE_SIZE; | 258 | w -= PAGE_SIZE; |
259 | } | 259 | } |
diff --git a/fs/nfsd/nfs3acl.c b/fs/nfsd/nfs3acl.c index a596e9d987e4..9cbc1a841f87 100644 --- a/fs/nfsd/nfs3acl.c +++ b/fs/nfsd/nfs3acl.c | |||
@@ -184,7 +184,7 @@ static int nfs3svc_encode_getaclres(struct svc_rqst *rqstp, __be32 *p, | |||
184 | (resp->mask & NFS_ACL) ? resp->acl_access : NULL, | 184 | (resp->mask & NFS_ACL) ? resp->acl_access : NULL, |
185 | (resp->mask & NFS_DFACL) ? resp->acl_default : NULL); | 185 | (resp->mask & NFS_DFACL) ? resp->acl_default : NULL); |
186 | while (w > 0) { | 186 | while (w > 0) { |
187 | if (!rqstp->rq_respages[rqstp->rq_resused++]) | 187 | if (!*(rqstp->rq_next_page++)) |
188 | return 0; | 188 | return 0; |
189 | w -= PAGE_SIZE; | 189 | w -= PAGE_SIZE; |
190 | } | 190 | } |
diff --git a/fs/nfsd/nfs3proc.c b/fs/nfsd/nfs3proc.c index 97d90d1c8608..1fc02dfdc5c4 100644 --- a/fs/nfsd/nfs3proc.c +++ b/fs/nfsd/nfs3proc.c | |||
@@ -460,7 +460,7 @@ nfsd3_proc_readdirplus(struct svc_rqst *rqstp, struct nfsd3_readdirargs *argp, | |||
460 | __be32 nfserr; | 460 | __be32 nfserr; |
461 | int count = 0; | 461 | int count = 0; |
462 | loff_t offset; | 462 | loff_t offset; |
463 | int i; | 463 | struct page **p; |
464 | caddr_t page_addr = NULL; | 464 | caddr_t page_addr = NULL; |
465 | 465 | ||
466 | dprintk("nfsd: READDIR+(3) %s %d bytes at %d\n", | 466 | dprintk("nfsd: READDIR+(3) %s %d bytes at %d\n", |
@@ -484,8 +484,8 @@ nfsd3_proc_readdirplus(struct svc_rqst *rqstp, struct nfsd3_readdirargs *argp, | |||
484 | &resp->common, | 484 | &resp->common, |
485 | nfs3svc_encode_entry_plus); | 485 | nfs3svc_encode_entry_plus); |
486 | memcpy(resp->verf, argp->verf, 8); | 486 | memcpy(resp->verf, argp->verf, 8); |
487 | for (i=1; i<rqstp->rq_resused ; i++) { | 487 | for (p = rqstp->rq_respages + 1; p < rqstp->rq_next_page; p++) { |
488 | page_addr = page_address(rqstp->rq_respages[i]); | 488 | page_addr = page_address(*p); |
489 | 489 | ||
490 | if (((caddr_t)resp->buffer >= page_addr) && | 490 | if (((caddr_t)resp->buffer >= page_addr) && |
491 | ((caddr_t)resp->buffer < page_addr + PAGE_SIZE)) { | 491 | ((caddr_t)resp->buffer < page_addr + PAGE_SIZE)) { |
diff --git a/fs/nfsd/nfs3xdr.c b/fs/nfsd/nfs3xdr.c index 43f46cd9edea..324c0baf7cda 100644 --- a/fs/nfsd/nfs3xdr.c +++ b/fs/nfsd/nfs3xdr.c | |||
@@ -7,8 +7,10 @@ | |||
7 | */ | 7 | */ |
8 | 8 | ||
9 | #include <linux/namei.h> | 9 | #include <linux/namei.h> |
10 | #include <linux/sunrpc/svc_xprt.h> | ||
10 | #include "xdr3.h" | 11 | #include "xdr3.h" |
11 | #include "auth.h" | 12 | #include "auth.h" |
13 | #include "netns.h" | ||
12 | 14 | ||
13 | #define NFSDDBG_FACILITY NFSDDBG_XDR | 15 | #define NFSDDBG_FACILITY NFSDDBG_XDR |
14 | 16 | ||
@@ -323,7 +325,7 @@ nfs3svc_decode_readargs(struct svc_rqst *rqstp, __be32 *p, | |||
323 | struct nfsd3_readargs *args) | 325 | struct nfsd3_readargs *args) |
324 | { | 326 | { |
325 | unsigned int len; | 327 | unsigned int len; |
326 | int v,pn; | 328 | int v; |
327 | u32 max_blocksize = svc_max_payload(rqstp); | 329 | u32 max_blocksize = svc_max_payload(rqstp); |
328 | 330 | ||
329 | if (!(p = decode_fh(p, &args->fh))) | 331 | if (!(p = decode_fh(p, &args->fh))) |
@@ -338,8 +340,9 @@ nfs3svc_decode_readargs(struct svc_rqst *rqstp, __be32 *p, | |||
338 | /* set up the kvec */ | 340 | /* set up the kvec */ |
339 | v=0; | 341 | v=0; |
340 | while (len > 0) { | 342 | while (len > 0) { |
341 | pn = rqstp->rq_resused++; | 343 | struct page *p = *(rqstp->rq_next_page++); |
342 | rqstp->rq_vec[v].iov_base = page_address(rqstp->rq_respages[pn]); | 344 | |
345 | rqstp->rq_vec[v].iov_base = page_address(p); | ||
343 | rqstp->rq_vec[v].iov_len = len < PAGE_SIZE? len : PAGE_SIZE; | 346 | rqstp->rq_vec[v].iov_len = len < PAGE_SIZE? len : PAGE_SIZE; |
344 | len -= rqstp->rq_vec[v].iov_len; | 347 | len -= rqstp->rq_vec[v].iov_len; |
345 | v++; | 348 | v++; |
@@ -461,8 +464,7 @@ nfs3svc_decode_symlinkargs(struct svc_rqst *rqstp, __be32 *p, | |||
461 | len = ntohl(*p++); | 464 | len = ntohl(*p++); |
462 | if (len == 0 || len > NFS3_MAXPATHLEN || len >= PAGE_SIZE) | 465 | if (len == 0 || len > NFS3_MAXPATHLEN || len >= PAGE_SIZE) |
463 | return 0; | 466 | return 0; |
464 | args->tname = new = | 467 | args->tname = new = page_address(*(rqstp->rq_next_page++)); |
465 | page_address(rqstp->rq_respages[rqstp->rq_resused++]); | ||
466 | args->tlen = len; | 468 | args->tlen = len; |
467 | /* first copy and check from the first page */ | 469 | /* first copy and check from the first page */ |
468 | old = (char*)p; | 470 | old = (char*)p; |
@@ -533,8 +535,7 @@ nfs3svc_decode_readlinkargs(struct svc_rqst *rqstp, __be32 *p, | |||
533 | { | 535 | { |
534 | if (!(p = decode_fh(p, &args->fh))) | 536 | if (!(p = decode_fh(p, &args->fh))) |
535 | return 0; | 537 | return 0; |
536 | args->buffer = | 538 | args->buffer = page_address(*(rqstp->rq_next_page++)); |
537 | page_address(rqstp->rq_respages[rqstp->rq_resused++]); | ||
538 | 539 | ||
539 | return xdr_argsize_check(rqstp, p); | 540 | return xdr_argsize_check(rqstp, p); |
540 | } | 541 | } |
@@ -565,8 +566,7 @@ nfs3svc_decode_readdirargs(struct svc_rqst *rqstp, __be32 *p, | |||
565 | if (args->count > PAGE_SIZE) | 566 | if (args->count > PAGE_SIZE) |
566 | args->count = PAGE_SIZE; | 567 | args->count = PAGE_SIZE; |
567 | 568 | ||
568 | args->buffer = | 569 | args->buffer = page_address(*(rqstp->rq_next_page++)); |
569 | page_address(rqstp->rq_respages[rqstp->rq_resused++]); | ||
570 | 570 | ||
571 | return xdr_argsize_check(rqstp, p); | 571 | return xdr_argsize_check(rqstp, p); |
572 | } | 572 | } |
@@ -575,7 +575,7 @@ int | |||
575 | nfs3svc_decode_readdirplusargs(struct svc_rqst *rqstp, __be32 *p, | 575 | nfs3svc_decode_readdirplusargs(struct svc_rqst *rqstp, __be32 *p, |
576 | struct nfsd3_readdirargs *args) | 576 | struct nfsd3_readdirargs *args) |
577 | { | 577 | { |
578 | int len, pn; | 578 | int len; |
579 | u32 max_blocksize = svc_max_payload(rqstp); | 579 | u32 max_blocksize = svc_max_payload(rqstp); |
580 | 580 | ||
581 | if (!(p = decode_fh(p, &args->fh))) | 581 | if (!(p = decode_fh(p, &args->fh))) |
@@ -590,9 +590,9 @@ nfs3svc_decode_readdirplusargs(struct svc_rqst *rqstp, __be32 *p, | |||
590 | args->count = len; | 590 | args->count = len; |
591 | 591 | ||
592 | while (len > 0) { | 592 | while (len > 0) { |
593 | pn = rqstp->rq_resused++; | 593 | struct page *p = *(rqstp->rq_next_page++); |
594 | if (!args->buffer) | 594 | if (!args->buffer) |
595 | args->buffer = page_address(rqstp->rq_respages[pn]); | 595 | args->buffer = page_address(p); |
596 | len -= PAGE_SIZE; | 596 | len -= PAGE_SIZE; |
597 | } | 597 | } |
598 | 598 | ||
@@ -720,12 +720,14 @@ int | |||
720 | nfs3svc_encode_writeres(struct svc_rqst *rqstp, __be32 *p, | 720 | nfs3svc_encode_writeres(struct svc_rqst *rqstp, __be32 *p, |
721 | struct nfsd3_writeres *resp) | 721 | struct nfsd3_writeres *resp) |
722 | { | 722 | { |
723 | struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); | ||
724 | |||
723 | p = encode_wcc_data(rqstp, p, &resp->fh); | 725 | p = encode_wcc_data(rqstp, p, &resp->fh); |
724 | if (resp->status == 0) { | 726 | if (resp->status == 0) { |
725 | *p++ = htonl(resp->count); | 727 | *p++ = htonl(resp->count); |
726 | *p++ = htonl(resp->committed); | 728 | *p++ = htonl(resp->committed); |
727 | *p++ = htonl(nfssvc_boot.tv_sec); | 729 | *p++ = htonl(nn->nfssvc_boot.tv_sec); |
728 | *p++ = htonl(nfssvc_boot.tv_usec); | 730 | *p++ = htonl(nn->nfssvc_boot.tv_usec); |
729 | } | 731 | } |
730 | return xdr_ressize_check(rqstp, p); | 732 | return xdr_ressize_check(rqstp, p); |
731 | } | 733 | } |
@@ -876,7 +878,7 @@ encode_entry(struct readdir_cd *ccd, const char *name, int namlen, | |||
876 | common); | 878 | common); |
877 | __be32 *p = cd->buffer; | 879 | __be32 *p = cd->buffer; |
878 | caddr_t curr_page_addr = NULL; | 880 | caddr_t curr_page_addr = NULL; |
879 | int pn; /* current page number */ | 881 | struct page ** page; |
880 | int slen; /* string (name) length */ | 882 | int slen; /* string (name) length */ |
881 | int elen; /* estimated entry length in words */ | 883 | int elen; /* estimated entry length in words */ |
882 | int num_entry_words = 0; /* actual number of words */ | 884 | int num_entry_words = 0; /* actual number of words */ |
@@ -913,8 +915,9 @@ encode_entry(struct readdir_cd *ccd, const char *name, int namlen, | |||
913 | } | 915 | } |
914 | 916 | ||
915 | /* determine which page in rq_respages[] we are currently filling */ | 917 | /* determine which page in rq_respages[] we are currently filling */ |
916 | for (pn=1; pn < cd->rqstp->rq_resused; pn++) { | 918 | for (page = cd->rqstp->rq_respages + 1; |
917 | curr_page_addr = page_address(cd->rqstp->rq_respages[pn]); | 919 | page < cd->rqstp->rq_next_page; page++) { |
920 | curr_page_addr = page_address(*page); | ||
918 | 921 | ||
919 | if (((caddr_t)cd->buffer >= curr_page_addr) && | 922 | if (((caddr_t)cd->buffer >= curr_page_addr) && |
920 | ((caddr_t)cd->buffer < curr_page_addr + PAGE_SIZE)) | 923 | ((caddr_t)cd->buffer < curr_page_addr + PAGE_SIZE)) |
@@ -929,14 +932,14 @@ encode_entry(struct readdir_cd *ccd, const char *name, int namlen, | |||
929 | if (plus) | 932 | if (plus) |
930 | p = encode_entryplus_baggage(cd, p, name, namlen); | 933 | p = encode_entryplus_baggage(cd, p, name, namlen); |
931 | num_entry_words = p - cd->buffer; | 934 | num_entry_words = p - cd->buffer; |
932 | } else if (cd->rqstp->rq_respages[pn+1] != NULL) { | 935 | } else if (*(page+1) != NULL) { |
933 | /* temporarily encode entry into next page, then move back to | 936 | /* temporarily encode entry into next page, then move back to |
934 | * current and next page in rq_respages[] */ | 937 | * current and next page in rq_respages[] */ |
935 | __be32 *p1, *tmp; | 938 | __be32 *p1, *tmp; |
936 | int len1, len2; | 939 | int len1, len2; |
937 | 940 | ||
938 | /* grab next page for temporary storage of entry */ | 941 | /* grab next page for temporary storage of entry */ |
939 | p1 = tmp = page_address(cd->rqstp->rq_respages[pn+1]); | 942 | p1 = tmp = page_address(*(page+1)); |
940 | 943 | ||
941 | p1 = encode_entry_baggage(cd, p1, name, namlen, ino); | 944 | p1 = encode_entry_baggage(cd, p1, name, namlen, ino); |
942 | 945 | ||
@@ -1082,11 +1085,13 @@ int | |||
1082 | nfs3svc_encode_commitres(struct svc_rqst *rqstp, __be32 *p, | 1085 | nfs3svc_encode_commitres(struct svc_rqst *rqstp, __be32 *p, |
1083 | struct nfsd3_commitres *resp) | 1086 | struct nfsd3_commitres *resp) |
1084 | { | 1087 | { |
1088 | struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); | ||
1089 | |||
1085 | p = encode_wcc_data(rqstp, p, &resp->fh); | 1090 | p = encode_wcc_data(rqstp, p, &resp->fh); |
1086 | /* Write verifier */ | 1091 | /* Write verifier */ |
1087 | if (resp->status == 0) { | 1092 | if (resp->status == 0) { |
1088 | *p++ = htonl(nfssvc_boot.tv_sec); | 1093 | *p++ = htonl(nn->nfssvc_boot.tv_sec); |
1089 | *p++ = htonl(nfssvc_boot.tv_usec); | 1094 | *p++ = htonl(nn->nfssvc_boot.tv_usec); |
1090 | } | 1095 | } |
1091 | return xdr_ressize_check(rqstp, p); | 1096 | return xdr_ressize_check(rqstp, p); |
1092 | } | 1097 | } |
diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c index bdf29c96e4cd..99bc85ff0217 100644 --- a/fs/nfsd/nfs4callback.c +++ b/fs/nfsd/nfs4callback.c | |||
@@ -36,6 +36,7 @@ | |||
36 | #include <linux/slab.h> | 36 | #include <linux/slab.h> |
37 | #include "nfsd.h" | 37 | #include "nfsd.h" |
38 | #include "state.h" | 38 | #include "state.h" |
39 | #include "netns.h" | ||
39 | 40 | ||
40 | #define NFSDDBG_FACILITY NFSDDBG_PROC | 41 | #define NFSDDBG_FACILITY NFSDDBG_PROC |
41 | 42 | ||
@@ -625,20 +626,46 @@ static const struct rpc_program cb_program = { | |||
625 | .pipe_dir_name = "nfsd4_cb", | 626 | .pipe_dir_name = "nfsd4_cb", |
626 | }; | 627 | }; |
627 | 628 | ||
628 | static int max_cb_time(void) | 629 | static int max_cb_time(struct net *net) |
629 | { | 630 | { |
630 | return max(nfsd4_lease/10, (time_t)1) * HZ; | 631 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); |
632 | return max(nn->nfsd4_lease/10, (time_t)1) * HZ; | ||
631 | } | 633 | } |
632 | 634 | ||
635 | static struct rpc_cred *callback_cred; | ||
636 | |||
637 | int set_callback_cred(void) | ||
638 | { | ||
639 | if (callback_cred) | ||
640 | return 0; | ||
641 | callback_cred = rpc_lookup_machine_cred("nfs"); | ||
642 | if (!callback_cred) | ||
643 | return -ENOMEM; | ||
644 | return 0; | ||
645 | } | ||
646 | |||
647 | static struct rpc_cred *get_backchannel_cred(struct nfs4_client *clp, struct rpc_clnt *client, struct nfsd4_session *ses) | ||
648 | { | ||
649 | if (clp->cl_minorversion == 0) { | ||
650 | return get_rpccred(callback_cred); | ||
651 | } else { | ||
652 | struct rpc_auth *auth = client->cl_auth; | ||
653 | struct auth_cred acred = {}; | ||
654 | |||
655 | acred.uid = ses->se_cb_sec.uid; | ||
656 | acred.gid = ses->se_cb_sec.gid; | ||
657 | return auth->au_ops->lookup_cred(client->cl_auth, &acred, 0); | ||
658 | } | ||
659 | } | ||
633 | 660 | ||
634 | static int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *conn, struct nfsd4_session *ses) | 661 | static int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *conn, struct nfsd4_session *ses) |
635 | { | 662 | { |
636 | struct rpc_timeout timeparms = { | 663 | struct rpc_timeout timeparms = { |
637 | .to_initval = max_cb_time(), | 664 | .to_initval = max_cb_time(clp->net), |
638 | .to_retries = 0, | 665 | .to_retries = 0, |
639 | }; | 666 | }; |
640 | struct rpc_create_args args = { | 667 | struct rpc_create_args args = { |
641 | .net = &init_net, | 668 | .net = clp->net, |
642 | .address = (struct sockaddr *) &conn->cb_addr, | 669 | .address = (struct sockaddr *) &conn->cb_addr, |
643 | .addrsize = conn->cb_addrlen, | 670 | .addrsize = conn->cb_addrlen, |
644 | .saddress = (struct sockaddr *) &conn->cb_saddr, | 671 | .saddress = (struct sockaddr *) &conn->cb_saddr, |
@@ -648,6 +675,7 @@ static int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *c | |||
648 | .flags = (RPC_CLNT_CREATE_NOPING | RPC_CLNT_CREATE_QUIET), | 675 | .flags = (RPC_CLNT_CREATE_NOPING | RPC_CLNT_CREATE_QUIET), |
649 | }; | 676 | }; |
650 | struct rpc_clnt *client; | 677 | struct rpc_clnt *client; |
678 | struct rpc_cred *cred; | ||
651 | 679 | ||
652 | if (clp->cl_minorversion == 0) { | 680 | if (clp->cl_minorversion == 0) { |
653 | if (!clp->cl_cred.cr_principal && | 681 | if (!clp->cl_cred.cr_principal && |
@@ -666,7 +694,7 @@ static int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *c | |||
666 | args.bc_xprt = conn->cb_xprt; | 694 | args.bc_xprt = conn->cb_xprt; |
667 | args.prognumber = clp->cl_cb_session->se_cb_prog; | 695 | args.prognumber = clp->cl_cb_session->se_cb_prog; |
668 | args.protocol = XPRT_TRANSPORT_BC_TCP; | 696 | args.protocol = XPRT_TRANSPORT_BC_TCP; |
669 | args.authflavor = RPC_AUTH_UNIX; | 697 | args.authflavor = ses->se_cb_sec.flavor; |
670 | } | 698 | } |
671 | /* Create RPC client */ | 699 | /* Create RPC client */ |
672 | client = rpc_create(&args); | 700 | client = rpc_create(&args); |
@@ -675,9 +703,14 @@ static int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *c | |||
675 | PTR_ERR(client)); | 703 | PTR_ERR(client)); |
676 | return PTR_ERR(client); | 704 | return PTR_ERR(client); |
677 | } | 705 | } |
706 | cred = get_backchannel_cred(clp, client, ses); | ||
707 | if (IS_ERR(cred)) { | ||
708 | rpc_shutdown_client(client); | ||
709 | return PTR_ERR(cred); | ||
710 | } | ||
678 | clp->cl_cb_client = client; | 711 | clp->cl_cb_client = client; |
712 | clp->cl_cb_cred = cred; | ||
679 | return 0; | 713 | return 0; |
680 | |||
681 | } | 714 | } |
682 | 715 | ||
683 | static void warn_no_callback_path(struct nfs4_client *clp, int reason) | 716 | static void warn_no_callback_path(struct nfs4_client *clp, int reason) |
@@ -714,18 +747,6 @@ static const struct rpc_call_ops nfsd4_cb_probe_ops = { | |||
714 | .rpc_call_done = nfsd4_cb_probe_done, | 747 | .rpc_call_done = nfsd4_cb_probe_done, |
715 | }; | 748 | }; |
716 | 749 | ||
717 | static struct rpc_cred *callback_cred; | ||
718 | |||
719 | int set_callback_cred(void) | ||
720 | { | ||
721 | if (callback_cred) | ||
722 | return 0; | ||
723 | callback_cred = rpc_lookup_machine_cred("nfs"); | ||
724 | if (!callback_cred) | ||
725 | return -ENOMEM; | ||
726 | return 0; | ||
727 | } | ||
728 | |||
729 | static struct workqueue_struct *callback_wq; | 750 | static struct workqueue_struct *callback_wq; |
730 | 751 | ||
731 | static void run_nfsd4_cb(struct nfsd4_callback *cb) | 752 | static void run_nfsd4_cb(struct nfsd4_callback *cb) |
@@ -743,7 +764,6 @@ static void do_probe_callback(struct nfs4_client *clp) | |||
743 | cb->cb_msg.rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_NULL]; | 764 | cb->cb_msg.rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_NULL]; |
744 | cb->cb_msg.rpc_argp = NULL; | 765 | cb->cb_msg.rpc_argp = NULL; |
745 | cb->cb_msg.rpc_resp = NULL; | 766 | cb->cb_msg.rpc_resp = NULL; |
746 | cb->cb_msg.rpc_cred = callback_cred; | ||
747 | 767 | ||
748 | cb->cb_ops = &nfsd4_cb_probe_ops; | 768 | cb->cb_ops = &nfsd4_cb_probe_ops; |
749 | 769 | ||
@@ -962,6 +982,8 @@ static void nfsd4_process_cb_update(struct nfsd4_callback *cb) | |||
962 | if (clp->cl_cb_client) { | 982 | if (clp->cl_cb_client) { |
963 | rpc_shutdown_client(clp->cl_cb_client); | 983 | rpc_shutdown_client(clp->cl_cb_client); |
964 | clp->cl_cb_client = NULL; | 984 | clp->cl_cb_client = NULL; |
985 | put_rpccred(clp->cl_cb_cred); | ||
986 | clp->cl_cb_cred = NULL; | ||
965 | } | 987 | } |
966 | if (clp->cl_cb_conn.cb_xprt) { | 988 | if (clp->cl_cb_conn.cb_xprt) { |
967 | svc_xprt_put(clp->cl_cb_conn.cb_xprt); | 989 | svc_xprt_put(clp->cl_cb_conn.cb_xprt); |
@@ -995,7 +1017,7 @@ static void nfsd4_process_cb_update(struct nfsd4_callback *cb) | |||
995 | run_nfsd4_cb(cb); | 1017 | run_nfsd4_cb(cb); |
996 | } | 1018 | } |
997 | 1019 | ||
998 | void nfsd4_do_callback_rpc(struct work_struct *w) | 1020 | static void nfsd4_do_callback_rpc(struct work_struct *w) |
999 | { | 1021 | { |
1000 | struct nfsd4_callback *cb = container_of(w, struct nfsd4_callback, cb_work); | 1022 | struct nfsd4_callback *cb = container_of(w, struct nfsd4_callback, cb_work); |
1001 | struct nfs4_client *clp = cb->cb_clp; | 1023 | struct nfs4_client *clp = cb->cb_clp; |
@@ -1010,10 +1032,16 @@ void nfsd4_do_callback_rpc(struct work_struct *w) | |||
1010 | nfsd4_release_cb(cb); | 1032 | nfsd4_release_cb(cb); |
1011 | return; | 1033 | return; |
1012 | } | 1034 | } |
1035 | cb->cb_msg.rpc_cred = clp->cl_cb_cred; | ||
1013 | rpc_call_async(clnt, &cb->cb_msg, RPC_TASK_SOFT | RPC_TASK_SOFTCONN, | 1036 | rpc_call_async(clnt, &cb->cb_msg, RPC_TASK_SOFT | RPC_TASK_SOFTCONN, |
1014 | cb->cb_ops, cb); | 1037 | cb->cb_ops, cb); |
1015 | } | 1038 | } |
1016 | 1039 | ||
1040 | void nfsd4_init_callback(struct nfsd4_callback *cb) | ||
1041 | { | ||
1042 | INIT_WORK(&cb->cb_work, nfsd4_do_callback_rpc); | ||
1043 | } | ||
1044 | |||
1017 | void nfsd4_cb_recall(struct nfs4_delegation *dp) | 1045 | void nfsd4_cb_recall(struct nfs4_delegation *dp) |
1018 | { | 1046 | { |
1019 | struct nfsd4_callback *cb = &dp->dl_recall; | 1047 | struct nfsd4_callback *cb = &dp->dl_recall; |
@@ -1025,7 +1053,6 @@ void nfsd4_cb_recall(struct nfs4_delegation *dp) | |||
1025 | cb->cb_msg.rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_RECALL]; | 1053 | cb->cb_msg.rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_RECALL]; |
1026 | cb->cb_msg.rpc_argp = cb; | 1054 | cb->cb_msg.rpc_argp = cb; |
1027 | cb->cb_msg.rpc_resp = cb; | 1055 | cb->cb_msg.rpc_resp = cb; |
1028 | cb->cb_msg.rpc_cred = callback_cred; | ||
1029 | 1056 | ||
1030 | cb->cb_ops = &nfsd4_cb_recall_ops; | 1057 | cb->cb_ops = &nfsd4_cb_recall_ops; |
1031 | 1058 | ||
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index 6c9a4b291dba..9d1c5dba2bbb 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c | |||
@@ -40,6 +40,7 @@ | |||
40 | #include "xdr4.h" | 40 | #include "xdr4.h" |
41 | #include "vfs.h" | 41 | #include "vfs.h" |
42 | #include "current_stateid.h" | 42 | #include "current_stateid.h" |
43 | #include "netns.h" | ||
43 | 44 | ||
44 | #define NFSDDBG_FACILITY NFSDDBG_PROC | 45 | #define NFSDDBG_FACILITY NFSDDBG_PROC |
45 | 46 | ||
@@ -194,6 +195,7 @@ static __be32 | |||
194 | do_open_lookup(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open *open) | 195 | do_open_lookup(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open *open) |
195 | { | 196 | { |
196 | struct svc_fh *resfh; | 197 | struct svc_fh *resfh; |
198 | int accmode; | ||
197 | __be32 status; | 199 | __be32 status; |
198 | 200 | ||
199 | resfh = kmalloc(sizeof(struct svc_fh), GFP_KERNEL); | 201 | resfh = kmalloc(sizeof(struct svc_fh), GFP_KERNEL); |
@@ -253,9 +255,10 @@ do_open_lookup(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_o | |||
253 | /* set reply cache */ | 255 | /* set reply cache */ |
254 | fh_copy_shallow(&open->op_openowner->oo_owner.so_replay.rp_openfh, | 256 | fh_copy_shallow(&open->op_openowner->oo_owner.so_replay.rp_openfh, |
255 | &resfh->fh_handle); | 257 | &resfh->fh_handle); |
256 | if (!open->op_created) | 258 | accmode = NFSD_MAY_NOP; |
257 | status = do_open_permission(rqstp, resfh, open, | 259 | if (open->op_created) |
258 | NFSD_MAY_NOP); | 260 | accmode |= NFSD_MAY_OWNER_OVERRIDE; |
261 | status = do_open_permission(rqstp, resfh, open, accmode); | ||
259 | set_change_info(&open->op_cinfo, current_fh); | 262 | set_change_info(&open->op_cinfo, current_fh); |
260 | fh_dup2(current_fh, resfh); | 263 | fh_dup2(current_fh, resfh); |
261 | out: | 264 | out: |
@@ -304,6 +307,8 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
304 | { | 307 | { |
305 | __be32 status; | 308 | __be32 status; |
306 | struct nfsd4_compoundres *resp; | 309 | struct nfsd4_compoundres *resp; |
310 | struct net *net = SVC_NET(rqstp); | ||
311 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); | ||
307 | 312 | ||
308 | dprintk("NFSD: nfsd4_open filename %.*s op_openowner %p\n", | 313 | dprintk("NFSD: nfsd4_open filename %.*s op_openowner %p\n", |
309 | (int)open->op_fname.len, open->op_fname.data, | 314 | (int)open->op_fname.len, open->op_fname.data, |
@@ -331,7 +336,7 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
331 | 336 | ||
332 | /* check seqid for replay. set nfs4_owner */ | 337 | /* check seqid for replay. set nfs4_owner */ |
333 | resp = rqstp->rq_resp; | 338 | resp = rqstp->rq_resp; |
334 | status = nfsd4_process_open1(&resp->cstate, open); | 339 | status = nfsd4_process_open1(&resp->cstate, open, nn); |
335 | if (status == nfserr_replay_me) { | 340 | if (status == nfserr_replay_me) { |
336 | struct nfs4_replay *rp = &open->op_openowner->oo_owner.so_replay; | 341 | struct nfs4_replay *rp = &open->op_openowner->oo_owner.so_replay; |
337 | fh_put(&cstate->current_fh); | 342 | fh_put(&cstate->current_fh); |
@@ -354,10 +359,10 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
354 | /* Openowner is now set, so sequence id will get bumped. Now we need | 359 | /* Openowner is now set, so sequence id will get bumped. Now we need |
355 | * these checks before we do any creates: */ | 360 | * these checks before we do any creates: */ |
356 | status = nfserr_grace; | 361 | status = nfserr_grace; |
357 | if (locks_in_grace(SVC_NET(rqstp)) && open->op_claim_type != NFS4_OPEN_CLAIM_PREVIOUS) | 362 | if (locks_in_grace(net) && open->op_claim_type != NFS4_OPEN_CLAIM_PREVIOUS) |
358 | goto out; | 363 | goto out; |
359 | status = nfserr_no_grace; | 364 | status = nfserr_no_grace; |
360 | if (!locks_in_grace(SVC_NET(rqstp)) && open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS) | 365 | if (!locks_in_grace(net) && open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS) |
361 | goto out; | 366 | goto out; |
362 | 367 | ||
363 | switch (open->op_claim_type) { | 368 | switch (open->op_claim_type) { |
@@ -370,7 +375,9 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
370 | break; | 375 | break; |
371 | case NFS4_OPEN_CLAIM_PREVIOUS: | 376 | case NFS4_OPEN_CLAIM_PREVIOUS: |
372 | open->op_openowner->oo_flags |= NFS4_OO_CONFIRMED; | 377 | open->op_openowner->oo_flags |= NFS4_OO_CONFIRMED; |
373 | status = nfs4_check_open_reclaim(&open->op_clientid, cstate->minorversion); | 378 | status = nfs4_check_open_reclaim(&open->op_clientid, |
379 | cstate->minorversion, | ||
380 | nn); | ||
374 | if (status) | 381 | if (status) |
375 | goto out; | 382 | goto out; |
376 | case NFS4_OPEN_CLAIM_FH: | 383 | case NFS4_OPEN_CLAIM_FH: |
@@ -490,12 +497,13 @@ nfsd4_access(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
490 | &access->ac_supported); | 497 | &access->ac_supported); |
491 | } | 498 | } |
492 | 499 | ||
493 | static void gen_boot_verifier(nfs4_verifier *verifier) | 500 | static void gen_boot_verifier(nfs4_verifier *verifier, struct net *net) |
494 | { | 501 | { |
495 | __be32 verf[2]; | 502 | __be32 verf[2]; |
503 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); | ||
496 | 504 | ||
497 | verf[0] = (__be32)nfssvc_boot.tv_sec; | 505 | verf[0] = (__be32)nn->nfssvc_boot.tv_sec; |
498 | verf[1] = (__be32)nfssvc_boot.tv_usec; | 506 | verf[1] = (__be32)nn->nfssvc_boot.tv_usec; |
499 | memcpy(verifier->data, verf, sizeof(verifier->data)); | 507 | memcpy(verifier->data, verf, sizeof(verifier->data)); |
500 | } | 508 | } |
501 | 509 | ||
@@ -503,7 +511,7 @@ static __be32 | |||
503 | nfsd4_commit(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | 511 | nfsd4_commit(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, |
504 | struct nfsd4_commit *commit) | 512 | struct nfsd4_commit *commit) |
505 | { | 513 | { |
506 | gen_boot_verifier(&commit->co_verf); | 514 | gen_boot_verifier(&commit->co_verf, SVC_NET(rqstp)); |
507 | return nfsd_commit(rqstp, &cstate->current_fh, commit->co_offset, | 515 | return nfsd_commit(rqstp, &cstate->current_fh, commit->co_offset, |
508 | commit->co_count); | 516 | commit->co_count); |
509 | } | 517 | } |
@@ -684,6 +692,17 @@ nfsd4_read(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
684 | if (read->rd_offset >= OFFSET_MAX) | 692 | if (read->rd_offset >= OFFSET_MAX) |
685 | return nfserr_inval; | 693 | return nfserr_inval; |
686 | 694 | ||
695 | /* | ||
696 | * If we do a zero copy read, then a client will see read data | ||
697 | * that reflects the state of the file *after* performing the | ||
698 | * following compound. | ||
699 | * | ||
700 | * To ensure proper ordering, we therefore turn off zero copy if | ||
701 | * the client wants us to do more in this compound: | ||
702 | */ | ||
703 | if (!nfsd4_last_compound_op(rqstp)) | ||
704 | rqstp->rq_splice_ok = false; | ||
705 | |||
687 | nfs4_lock_state(); | 706 | nfs4_lock_state(); |
688 | /* check stateid */ | 707 | /* check stateid */ |
689 | if ((status = nfs4_preprocess_stateid_op(SVC_NET(rqstp), | 708 | if ((status = nfs4_preprocess_stateid_op(SVC_NET(rqstp), |
@@ -876,6 +895,24 @@ out: | |||
876 | return status; | 895 | return status; |
877 | } | 896 | } |
878 | 897 | ||
898 | static int fill_in_write_vector(struct kvec *vec, struct nfsd4_write *write) | ||
899 | { | ||
900 | int i = 1; | ||
901 | int buflen = write->wr_buflen; | ||
902 | |||
903 | vec[0].iov_base = write->wr_head.iov_base; | ||
904 | vec[0].iov_len = min_t(int, buflen, write->wr_head.iov_len); | ||
905 | buflen -= vec[0].iov_len; | ||
906 | |||
907 | while (buflen) { | ||
908 | vec[i].iov_base = page_address(write->wr_pagelist[i - 1]); | ||
909 | vec[i].iov_len = min_t(int, PAGE_SIZE, buflen); | ||
910 | buflen -= vec[i].iov_len; | ||
911 | i++; | ||
912 | } | ||
913 | return i; | ||
914 | } | ||
915 | |||
879 | static __be32 | 916 | static __be32 |
880 | nfsd4_write(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | 917 | nfsd4_write(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, |
881 | struct nfsd4_write *write) | 918 | struct nfsd4_write *write) |
@@ -884,6 +921,7 @@ nfsd4_write(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
884 | struct file *filp = NULL; | 921 | struct file *filp = NULL; |
885 | __be32 status = nfs_ok; | 922 | __be32 status = nfs_ok; |
886 | unsigned long cnt; | 923 | unsigned long cnt; |
924 | int nvecs; | ||
887 | 925 | ||
888 | /* no need to check permission - this will be done in nfsd_write() */ | 926 | /* no need to check permission - this will be done in nfsd_write() */ |
889 | 927 | ||
@@ -904,10 +942,13 @@ nfsd4_write(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
904 | 942 | ||
905 | cnt = write->wr_buflen; | 943 | cnt = write->wr_buflen; |
906 | write->wr_how_written = write->wr_stable_how; | 944 | write->wr_how_written = write->wr_stable_how; |
907 | gen_boot_verifier(&write->wr_verifier); | 945 | gen_boot_verifier(&write->wr_verifier, SVC_NET(rqstp)); |
946 | |||
947 | nvecs = fill_in_write_vector(rqstp->rq_vec, write); | ||
948 | WARN_ON_ONCE(nvecs > ARRAY_SIZE(rqstp->rq_vec)); | ||
908 | 949 | ||
909 | status = nfsd_write(rqstp, &cstate->current_fh, filp, | 950 | status = nfsd_write(rqstp, &cstate->current_fh, filp, |
910 | write->wr_offset, rqstp->rq_vec, write->wr_vlen, | 951 | write->wr_offset, rqstp->rq_vec, nvecs, |
911 | &cnt, &write->wr_how_written); | 952 | &cnt, &write->wr_how_written); |
912 | if (filp) | 953 | if (filp) |
913 | fput(filp); | 954 | fput(filp); |
@@ -1666,6 +1707,12 @@ static struct nfsd4_operation nfsd4_ops[] = { | |||
1666 | .op_name = "OP_EXCHANGE_ID", | 1707 | .op_name = "OP_EXCHANGE_ID", |
1667 | .op_rsize_bop = (nfsd4op_rsize)nfsd4_exchange_id_rsize, | 1708 | .op_rsize_bop = (nfsd4op_rsize)nfsd4_exchange_id_rsize, |
1668 | }, | 1709 | }, |
1710 | [OP_BACKCHANNEL_CTL] = { | ||
1711 | .op_func = (nfsd4op_func)nfsd4_backchannel_ctl, | ||
1712 | .op_flags = ALLOWED_WITHOUT_FH | OP_MODIFIES_SOMETHING, | ||
1713 | .op_name = "OP_BACKCHANNEL_CTL", | ||
1714 | .op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize, | ||
1715 | }, | ||
1669 | [OP_BIND_CONN_TO_SESSION] = { | 1716 | [OP_BIND_CONN_TO_SESSION] = { |
1670 | .op_func = (nfsd4op_func)nfsd4_bind_conn_to_session, | 1717 | .op_func = (nfsd4op_func)nfsd4_bind_conn_to_session, |
1671 | .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP | 1718 | .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP |
@@ -1719,6 +1766,7 @@ static struct nfsd4_operation nfsd4_ops[] = { | |||
1719 | .op_func = (nfsd4op_func)nfsd4_free_stateid, | 1766 | .op_func = (nfsd4op_func)nfsd4_free_stateid, |
1720 | .op_flags = ALLOWED_WITHOUT_FH | OP_MODIFIES_SOMETHING, | 1767 | .op_flags = ALLOWED_WITHOUT_FH | OP_MODIFIES_SOMETHING, |
1721 | .op_name = "OP_FREE_STATEID", | 1768 | .op_name = "OP_FREE_STATEID", |
1769 | .op_get_currentstateid = (stateid_getter)nfsd4_get_freestateid, | ||
1722 | .op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize, | 1770 | .op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize, |
1723 | }, | 1771 | }, |
1724 | }; | 1772 | }; |
diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c index 43295d45cc2b..ba6fdd4a0455 100644 --- a/fs/nfsd/nfs4recover.c +++ b/fs/nfsd/nfs4recover.c | |||
@@ -58,13 +58,11 @@ struct nfsd4_client_tracking_ops { | |||
58 | void (*create)(struct nfs4_client *); | 58 | void (*create)(struct nfs4_client *); |
59 | void (*remove)(struct nfs4_client *); | 59 | void (*remove)(struct nfs4_client *); |
60 | int (*check)(struct nfs4_client *); | 60 | int (*check)(struct nfs4_client *); |
61 | void (*grace_done)(struct net *, time_t); | 61 | void (*grace_done)(struct nfsd_net *, time_t); |
62 | }; | 62 | }; |
63 | 63 | ||
64 | /* Globals */ | 64 | /* Globals */ |
65 | static struct file *rec_file; | ||
66 | static char user_recovery_dirname[PATH_MAX] = "/var/lib/nfs/v4recovery"; | 65 | static char user_recovery_dirname[PATH_MAX] = "/var/lib/nfs/v4recovery"; |
67 | static struct nfsd4_client_tracking_ops *client_tracking_ops; | ||
68 | 66 | ||
69 | static int | 67 | static int |
70 | nfs4_save_creds(const struct cred **original_creds) | 68 | nfs4_save_creds(const struct cred **original_creds) |
@@ -102,33 +100,39 @@ md5_to_hex(char *out, char *md5) | |||
102 | *out = '\0'; | 100 | *out = '\0'; |
103 | } | 101 | } |
104 | 102 | ||
105 | __be32 | 103 | static int |
106 | nfs4_make_rec_clidname(char *dname, struct xdr_netobj *clname) | 104 | nfs4_make_rec_clidname(char *dname, const struct xdr_netobj *clname) |
107 | { | 105 | { |
108 | struct xdr_netobj cksum; | 106 | struct xdr_netobj cksum; |
109 | struct hash_desc desc; | 107 | struct hash_desc desc; |
110 | struct scatterlist sg; | 108 | struct scatterlist sg; |
111 | __be32 status = nfserr_jukebox; | 109 | int status; |
112 | 110 | ||
113 | dprintk("NFSD: nfs4_make_rec_clidname for %.*s\n", | 111 | dprintk("NFSD: nfs4_make_rec_clidname for %.*s\n", |
114 | clname->len, clname->data); | 112 | clname->len, clname->data); |
115 | desc.flags = CRYPTO_TFM_REQ_MAY_SLEEP; | 113 | desc.flags = CRYPTO_TFM_REQ_MAY_SLEEP; |
116 | desc.tfm = crypto_alloc_hash("md5", 0, CRYPTO_ALG_ASYNC); | 114 | desc.tfm = crypto_alloc_hash("md5", 0, CRYPTO_ALG_ASYNC); |
117 | if (IS_ERR(desc.tfm)) | 115 | if (IS_ERR(desc.tfm)) { |
116 | status = PTR_ERR(desc.tfm); | ||
118 | goto out_no_tfm; | 117 | goto out_no_tfm; |
118 | } | ||
119 | |||
119 | cksum.len = crypto_hash_digestsize(desc.tfm); | 120 | cksum.len = crypto_hash_digestsize(desc.tfm); |
120 | cksum.data = kmalloc(cksum.len, GFP_KERNEL); | 121 | cksum.data = kmalloc(cksum.len, GFP_KERNEL); |
121 | if (cksum.data == NULL) | 122 | if (cksum.data == NULL) { |
123 | status = -ENOMEM; | ||
122 | goto out; | 124 | goto out; |
125 | } | ||
123 | 126 | ||
124 | sg_init_one(&sg, clname->data, clname->len); | 127 | sg_init_one(&sg, clname->data, clname->len); |
125 | 128 | ||
126 | if (crypto_hash_digest(&desc, &sg, sg.length, cksum.data)) | 129 | status = crypto_hash_digest(&desc, &sg, sg.length, cksum.data); |
130 | if (status) | ||
127 | goto out; | 131 | goto out; |
128 | 132 | ||
129 | md5_to_hex(dname, cksum.data); | 133 | md5_to_hex(dname, cksum.data); |
130 | 134 | ||
131 | status = nfs_ok; | 135 | status = 0; |
132 | out: | 136 | out: |
133 | kfree(cksum.data); | 137 | kfree(cksum.data); |
134 | crypto_free_hash(desc.tfm); | 138 | crypto_free_hash(desc.tfm); |
@@ -136,29 +140,61 @@ out_no_tfm: | |||
136 | return status; | 140 | return status; |
137 | } | 141 | } |
138 | 142 | ||
143 | /* | ||
144 | * If we had an error generating the recdir name for the legacy tracker | ||
145 | * then warn the admin. If the error doesn't appear to be transient, | ||
146 | * then disable recovery tracking. | ||
147 | */ | ||
148 | static void | ||
149 | legacy_recdir_name_error(int error) | ||
150 | { | ||
151 | printk(KERN_ERR "NFSD: unable to generate recoverydir " | ||
152 | "name (%d).\n", error); | ||
153 | |||
154 | /* | ||
155 | * if the algorithm just doesn't exist, then disable the recovery | ||
156 | * tracker altogether. The crypto libs will generally return this if | ||
157 | * FIPS is enabled as well. | ||
158 | */ | ||
159 | if (error == -ENOENT) { | ||
160 | printk(KERN_ERR "NFSD: disabling legacy clientid tracking. " | ||
161 | "Reboot recovery will not function correctly!\n"); | ||
162 | |||
163 | /* the argument is ignored by the legacy exit function */ | ||
164 | nfsd4_client_tracking_exit(NULL); | ||
165 | } | ||
166 | } | ||
167 | |||
139 | static void | 168 | static void |
140 | nfsd4_create_clid_dir(struct nfs4_client *clp) | 169 | nfsd4_create_clid_dir(struct nfs4_client *clp) |
141 | { | 170 | { |
142 | const struct cred *original_cred; | 171 | const struct cred *original_cred; |
143 | char *dname = clp->cl_recdir; | 172 | char dname[HEXDIR_LEN]; |
144 | struct dentry *dir, *dentry; | 173 | struct dentry *dir, *dentry; |
174 | struct nfs4_client_reclaim *crp; | ||
145 | int status; | 175 | int status; |
176 | struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); | ||
146 | 177 | ||
147 | dprintk("NFSD: nfsd4_create_clid_dir for \"%s\"\n", dname); | 178 | dprintk("NFSD: nfsd4_create_clid_dir for \"%s\"\n", dname); |
148 | 179 | ||
149 | if (test_and_set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags)) | 180 | if (test_and_set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags)) |
150 | return; | 181 | return; |
151 | if (!rec_file) | 182 | if (!nn->rec_file) |
152 | return; | 183 | return; |
184 | |||
185 | status = nfs4_make_rec_clidname(dname, &clp->cl_name); | ||
186 | if (status) | ||
187 | return legacy_recdir_name_error(status); | ||
188 | |||
153 | status = nfs4_save_creds(&original_cred); | 189 | status = nfs4_save_creds(&original_cred); |
154 | if (status < 0) | 190 | if (status < 0) |
155 | return; | 191 | return; |
156 | 192 | ||
157 | status = mnt_want_write_file(rec_file); | 193 | status = mnt_want_write_file(nn->rec_file); |
158 | if (status) | 194 | if (status) |
159 | return; | 195 | return; |
160 | 196 | ||
161 | dir = rec_file->f_path.dentry; | 197 | dir = nn->rec_file->f_path.dentry; |
162 | /* lock the parent */ | 198 | /* lock the parent */ |
163 | mutex_lock(&dir->d_inode->i_mutex); | 199 | mutex_lock(&dir->d_inode->i_mutex); |
164 | 200 | ||
@@ -182,18 +218,24 @@ out_put: | |||
182 | dput(dentry); | 218 | dput(dentry); |
183 | out_unlock: | 219 | out_unlock: |
184 | mutex_unlock(&dir->d_inode->i_mutex); | 220 | mutex_unlock(&dir->d_inode->i_mutex); |
185 | if (status == 0) | 221 | if (status == 0) { |
186 | vfs_fsync(rec_file, 0); | 222 | if (nn->in_grace) { |
187 | else | 223 | crp = nfs4_client_to_reclaim(dname, nn); |
224 | if (crp) | ||
225 | crp->cr_clp = clp; | ||
226 | } | ||
227 | vfs_fsync(nn->rec_file, 0); | ||
228 | } else { | ||
188 | printk(KERN_ERR "NFSD: failed to write recovery record" | 229 | printk(KERN_ERR "NFSD: failed to write recovery record" |
189 | " (err %d); please check that %s exists" | 230 | " (err %d); please check that %s exists" |
190 | " and is writeable", status, | 231 | " and is writeable", status, |
191 | user_recovery_dirname); | 232 | user_recovery_dirname); |
192 | mnt_drop_write_file(rec_file); | 233 | } |
234 | mnt_drop_write_file(nn->rec_file); | ||
193 | nfs4_reset_creds(original_cred); | 235 | nfs4_reset_creds(original_cred); |
194 | } | 236 | } |
195 | 237 | ||
196 | typedef int (recdir_func)(struct dentry *, struct dentry *); | 238 | typedef int (recdir_func)(struct dentry *, struct dentry *, struct nfsd_net *); |
197 | 239 | ||
198 | struct name_list { | 240 | struct name_list { |
199 | char name[HEXDIR_LEN]; | 241 | char name[HEXDIR_LEN]; |
@@ -219,10 +261,10 @@ nfsd4_build_namelist(void *arg, const char *name, int namlen, | |||
219 | } | 261 | } |
220 | 262 | ||
221 | static int | 263 | static int |
222 | nfsd4_list_rec_dir(recdir_func *f) | 264 | nfsd4_list_rec_dir(recdir_func *f, struct nfsd_net *nn) |
223 | { | 265 | { |
224 | const struct cred *original_cred; | 266 | const struct cred *original_cred; |
225 | struct dentry *dir = rec_file->f_path.dentry; | 267 | struct dentry *dir = nn->rec_file->f_path.dentry; |
226 | LIST_HEAD(names); | 268 | LIST_HEAD(names); |
227 | int status; | 269 | int status; |
228 | 270 | ||
@@ -230,13 +272,13 @@ nfsd4_list_rec_dir(recdir_func *f) | |||
230 | if (status < 0) | 272 | if (status < 0) |
231 | return status; | 273 | return status; |
232 | 274 | ||
233 | status = vfs_llseek(rec_file, 0, SEEK_SET); | 275 | status = vfs_llseek(nn->rec_file, 0, SEEK_SET); |
234 | if (status < 0) { | 276 | if (status < 0) { |
235 | nfs4_reset_creds(original_cred); | 277 | nfs4_reset_creds(original_cred); |
236 | return status; | 278 | return status; |
237 | } | 279 | } |
238 | 280 | ||
239 | status = vfs_readdir(rec_file, nfsd4_build_namelist, &names); | 281 | status = vfs_readdir(nn->rec_file, nfsd4_build_namelist, &names); |
240 | mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_PARENT); | 282 | mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_PARENT); |
241 | while (!list_empty(&names)) { | 283 | while (!list_empty(&names)) { |
242 | struct name_list *entry; | 284 | struct name_list *entry; |
@@ -248,7 +290,7 @@ nfsd4_list_rec_dir(recdir_func *f) | |||
248 | status = PTR_ERR(dentry); | 290 | status = PTR_ERR(dentry); |
249 | break; | 291 | break; |
250 | } | 292 | } |
251 | status = f(dir, dentry); | 293 | status = f(dir, dentry, nn); |
252 | dput(dentry); | 294 | dput(dentry); |
253 | } | 295 | } |
254 | list_del(&entry->list); | 296 | list_del(&entry->list); |
@@ -260,14 +302,14 @@ nfsd4_list_rec_dir(recdir_func *f) | |||
260 | } | 302 | } |
261 | 303 | ||
262 | static int | 304 | static int |
263 | nfsd4_unlink_clid_dir(char *name, int namlen) | 305 | nfsd4_unlink_clid_dir(char *name, int namlen, struct nfsd_net *nn) |
264 | { | 306 | { |
265 | struct dentry *dir, *dentry; | 307 | struct dentry *dir, *dentry; |
266 | int status; | 308 | int status; |
267 | 309 | ||
268 | dprintk("NFSD: nfsd4_unlink_clid_dir. name %.*s\n", namlen, name); | 310 | dprintk("NFSD: nfsd4_unlink_clid_dir. name %.*s\n", namlen, name); |
269 | 311 | ||
270 | dir = rec_file->f_path.dentry; | 312 | dir = nn->rec_file->f_path.dentry; |
271 | mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_PARENT); | 313 | mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_PARENT); |
272 | dentry = lookup_one_len(name, dir, namlen); | 314 | dentry = lookup_one_len(name, dir, namlen); |
273 | if (IS_ERR(dentry)) { | 315 | if (IS_ERR(dentry)) { |
@@ -289,37 +331,52 @@ static void | |||
289 | nfsd4_remove_clid_dir(struct nfs4_client *clp) | 331 | nfsd4_remove_clid_dir(struct nfs4_client *clp) |
290 | { | 332 | { |
291 | const struct cred *original_cred; | 333 | const struct cred *original_cred; |
334 | struct nfs4_client_reclaim *crp; | ||
335 | char dname[HEXDIR_LEN]; | ||
292 | int status; | 336 | int status; |
337 | struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); | ||
293 | 338 | ||
294 | if (!rec_file || !test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags)) | 339 | if (!nn->rec_file || !test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags)) |
295 | return; | 340 | return; |
296 | 341 | ||
297 | status = mnt_want_write_file(rec_file); | 342 | status = nfs4_make_rec_clidname(dname, &clp->cl_name); |
343 | if (status) | ||
344 | return legacy_recdir_name_error(status); | ||
345 | |||
346 | status = mnt_want_write_file(nn->rec_file); | ||
298 | if (status) | 347 | if (status) |
299 | goto out; | 348 | goto out; |
300 | clear_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags); | 349 | clear_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags); |
301 | 350 | ||
302 | status = nfs4_save_creds(&original_cred); | 351 | status = nfs4_save_creds(&original_cred); |
303 | if (status < 0) | 352 | if (status < 0) |
304 | goto out; | 353 | goto out_drop_write; |
305 | 354 | ||
306 | status = nfsd4_unlink_clid_dir(clp->cl_recdir, HEXDIR_LEN-1); | 355 | status = nfsd4_unlink_clid_dir(dname, HEXDIR_LEN-1, nn); |
307 | nfs4_reset_creds(original_cred); | 356 | nfs4_reset_creds(original_cred); |
308 | if (status == 0) | 357 | if (status == 0) { |
309 | vfs_fsync(rec_file, 0); | 358 | vfs_fsync(nn->rec_file, 0); |
310 | mnt_drop_write_file(rec_file); | 359 | if (nn->in_grace) { |
360 | /* remove reclaim record */ | ||
361 | crp = nfsd4_find_reclaim_client(dname, nn); | ||
362 | if (crp) | ||
363 | nfs4_remove_reclaim_record(crp, nn); | ||
364 | } | ||
365 | } | ||
366 | out_drop_write: | ||
367 | mnt_drop_write_file(nn->rec_file); | ||
311 | out: | 368 | out: |
312 | if (status) | 369 | if (status) |
313 | printk("NFSD: Failed to remove expired client state directory" | 370 | printk("NFSD: Failed to remove expired client state directory" |
314 | " %.*s\n", HEXDIR_LEN, clp->cl_recdir); | 371 | " %.*s\n", HEXDIR_LEN, dname); |
315 | } | 372 | } |
316 | 373 | ||
317 | static int | 374 | static int |
318 | purge_old(struct dentry *parent, struct dentry *child) | 375 | purge_old(struct dentry *parent, struct dentry *child, struct nfsd_net *nn) |
319 | { | 376 | { |
320 | int status; | 377 | int status; |
321 | 378 | ||
322 | if (nfs4_has_reclaimed_state(child->d_name.name, false)) | 379 | if (nfs4_has_reclaimed_state(child->d_name.name, nn)) |
323 | return 0; | 380 | return 0; |
324 | 381 | ||
325 | status = vfs_rmdir(parent->d_inode, child); | 382 | status = vfs_rmdir(parent->d_inode, child); |
@@ -331,27 +388,29 @@ purge_old(struct dentry *parent, struct dentry *child) | |||
331 | } | 388 | } |
332 | 389 | ||
333 | static void | 390 | static void |
334 | nfsd4_recdir_purge_old(struct net *net, time_t boot_time) | 391 | nfsd4_recdir_purge_old(struct nfsd_net *nn, time_t boot_time) |
335 | { | 392 | { |
336 | int status; | 393 | int status; |
337 | 394 | ||
338 | if (!rec_file) | 395 | nn->in_grace = false; |
396 | if (!nn->rec_file) | ||
339 | return; | 397 | return; |
340 | status = mnt_want_write_file(rec_file); | 398 | status = mnt_want_write_file(nn->rec_file); |
341 | if (status) | 399 | if (status) |
342 | goto out; | 400 | goto out; |
343 | status = nfsd4_list_rec_dir(purge_old); | 401 | status = nfsd4_list_rec_dir(purge_old, nn); |
344 | if (status == 0) | 402 | if (status == 0) |
345 | vfs_fsync(rec_file, 0); | 403 | vfs_fsync(nn->rec_file, 0); |
346 | mnt_drop_write_file(rec_file); | 404 | mnt_drop_write_file(nn->rec_file); |
347 | out: | 405 | out: |
406 | nfs4_release_reclaim(nn); | ||
348 | if (status) | 407 | if (status) |
349 | printk("nfsd4: failed to purge old clients from recovery" | 408 | printk("nfsd4: failed to purge old clients from recovery" |
350 | " directory %s\n", rec_file->f_path.dentry->d_name.name); | 409 | " directory %s\n", nn->rec_file->f_path.dentry->d_name.name); |
351 | } | 410 | } |
352 | 411 | ||
353 | static int | 412 | static int |
354 | load_recdir(struct dentry *parent, struct dentry *child) | 413 | load_recdir(struct dentry *parent, struct dentry *child, struct nfsd_net *nn) |
355 | { | 414 | { |
356 | if (child->d_name.len != HEXDIR_LEN - 1) { | 415 | if (child->d_name.len != HEXDIR_LEN - 1) { |
357 | printk("nfsd4: illegal name %s in recovery directory\n", | 416 | printk("nfsd4: illegal name %s in recovery directory\n", |
@@ -359,21 +418,22 @@ load_recdir(struct dentry *parent, struct dentry *child) | |||
359 | /* Keep trying; maybe the others are OK: */ | 418 | /* Keep trying; maybe the others are OK: */ |
360 | return 0; | 419 | return 0; |
361 | } | 420 | } |
362 | nfs4_client_to_reclaim(child->d_name.name); | 421 | nfs4_client_to_reclaim(child->d_name.name, nn); |
363 | return 0; | 422 | return 0; |
364 | } | 423 | } |
365 | 424 | ||
366 | static int | 425 | static int |
367 | nfsd4_recdir_load(void) { | 426 | nfsd4_recdir_load(struct net *net) { |
368 | int status; | 427 | int status; |
428 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); | ||
369 | 429 | ||
370 | if (!rec_file) | 430 | if (!nn->rec_file) |
371 | return 0; | 431 | return 0; |
372 | 432 | ||
373 | status = nfsd4_list_rec_dir(load_recdir); | 433 | status = nfsd4_list_rec_dir(load_recdir, nn); |
374 | if (status) | 434 | if (status) |
375 | printk("nfsd4: failed loading clients from recovery" | 435 | printk("nfsd4: failed loading clients from recovery" |
376 | " directory %s\n", rec_file->f_path.dentry->d_name.name); | 436 | " directory %s\n", nn->rec_file->f_path.dentry->d_name.name); |
377 | return status; | 437 | return status; |
378 | } | 438 | } |
379 | 439 | ||
@@ -382,15 +442,16 @@ nfsd4_recdir_load(void) { | |||
382 | */ | 442 | */ |
383 | 443 | ||
384 | static int | 444 | static int |
385 | nfsd4_init_recdir(void) | 445 | nfsd4_init_recdir(struct net *net) |
386 | { | 446 | { |
447 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); | ||
387 | const struct cred *original_cred; | 448 | const struct cred *original_cred; |
388 | int status; | 449 | int status; |
389 | 450 | ||
390 | printk("NFSD: Using %s as the NFSv4 state recovery directory\n", | 451 | printk("NFSD: Using %s as the NFSv4 state recovery directory\n", |
391 | user_recovery_dirname); | 452 | user_recovery_dirname); |
392 | 453 | ||
393 | BUG_ON(rec_file); | 454 | BUG_ON(nn->rec_file); |
394 | 455 | ||
395 | status = nfs4_save_creds(&original_cred); | 456 | status = nfs4_save_creds(&original_cred); |
396 | if (status < 0) { | 457 | if (status < 0) { |
@@ -400,23 +461,65 @@ nfsd4_init_recdir(void) | |||
400 | return status; | 461 | return status; |
401 | } | 462 | } |
402 | 463 | ||
403 | rec_file = filp_open(user_recovery_dirname, O_RDONLY | O_DIRECTORY, 0); | 464 | nn->rec_file = filp_open(user_recovery_dirname, O_RDONLY | O_DIRECTORY, 0); |
404 | if (IS_ERR(rec_file)) { | 465 | if (IS_ERR(nn->rec_file)) { |
405 | printk("NFSD: unable to find recovery directory %s\n", | 466 | printk("NFSD: unable to find recovery directory %s\n", |
406 | user_recovery_dirname); | 467 | user_recovery_dirname); |
407 | status = PTR_ERR(rec_file); | 468 | status = PTR_ERR(nn->rec_file); |
408 | rec_file = NULL; | 469 | nn->rec_file = NULL; |
409 | } | 470 | } |
410 | 471 | ||
411 | nfs4_reset_creds(original_cred); | 472 | nfs4_reset_creds(original_cred); |
473 | if (!status) | ||
474 | nn->in_grace = true; | ||
412 | return status; | 475 | return status; |
413 | } | 476 | } |
414 | 477 | ||
478 | |||
479 | static int | ||
480 | nfs4_legacy_state_init(struct net *net) | ||
481 | { | ||
482 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); | ||
483 | int i; | ||
484 | |||
485 | nn->reclaim_str_hashtbl = kmalloc(sizeof(struct list_head) * | ||
486 | CLIENT_HASH_SIZE, GFP_KERNEL); | ||
487 | if (!nn->reclaim_str_hashtbl) | ||
488 | return -ENOMEM; | ||
489 | |||
490 | for (i = 0; i < CLIENT_HASH_SIZE; i++) | ||
491 | INIT_LIST_HEAD(&nn->reclaim_str_hashtbl[i]); | ||
492 | nn->reclaim_str_hashtbl_size = 0; | ||
493 | |||
494 | return 0; | ||
495 | } | ||
496 | |||
497 | static void | ||
498 | nfs4_legacy_state_shutdown(struct net *net) | ||
499 | { | ||
500 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); | ||
501 | |||
502 | kfree(nn->reclaim_str_hashtbl); | ||
503 | } | ||
504 | |||
415 | static int | 505 | static int |
416 | nfsd4_load_reboot_recovery_data(struct net *net) | 506 | nfsd4_load_reboot_recovery_data(struct net *net) |
417 | { | 507 | { |
418 | int status; | 508 | int status; |
419 | 509 | ||
510 | status = nfsd4_init_recdir(net); | ||
511 | if (!status) | ||
512 | status = nfsd4_recdir_load(net); | ||
513 | if (status) | ||
514 | printk(KERN_ERR "NFSD: Failure reading reboot recovery data\n"); | ||
515 | return status; | ||
516 | } | ||
517 | |||
518 | static int | ||
519 | nfsd4_legacy_tracking_init(struct net *net) | ||
520 | { | ||
521 | int status; | ||
522 | |||
420 | /* XXX: The legacy code won't work in a container */ | 523 | /* XXX: The legacy code won't work in a container */ |
421 | if (net != &init_net) { | 524 | if (net != &init_net) { |
422 | WARN(1, KERN_ERR "NFSD: attempt to initialize legacy client " | 525 | WARN(1, KERN_ERR "NFSD: attempt to initialize legacy client " |
@@ -424,30 +527,37 @@ nfsd4_load_reboot_recovery_data(struct net *net) | |||
424 | return -EINVAL; | 527 | return -EINVAL; |
425 | } | 528 | } |
426 | 529 | ||
427 | nfs4_lock_state(); | 530 | status = nfs4_legacy_state_init(net); |
428 | status = nfsd4_init_recdir(); | ||
429 | if (!status) | ||
430 | status = nfsd4_recdir_load(); | ||
431 | nfs4_unlock_state(); | ||
432 | if (status) | 531 | if (status) |
433 | printk(KERN_ERR "NFSD: Failure reading reboot recovery data\n"); | 532 | return status; |
533 | |||
534 | status = nfsd4_load_reboot_recovery_data(net); | ||
535 | if (status) | ||
536 | goto err; | ||
537 | return 0; | ||
538 | |||
539 | err: | ||
540 | nfs4_legacy_state_shutdown(net); | ||
434 | return status; | 541 | return status; |
435 | } | 542 | } |
436 | 543 | ||
437 | static void | 544 | static void |
438 | nfsd4_shutdown_recdir(void) | 545 | nfsd4_shutdown_recdir(struct nfsd_net *nn) |
439 | { | 546 | { |
440 | if (!rec_file) | 547 | if (!nn->rec_file) |
441 | return; | 548 | return; |
442 | fput(rec_file); | 549 | fput(nn->rec_file); |
443 | rec_file = NULL; | 550 | nn->rec_file = NULL; |
444 | } | 551 | } |
445 | 552 | ||
446 | static void | 553 | static void |
447 | nfsd4_legacy_tracking_exit(struct net *net) | 554 | nfsd4_legacy_tracking_exit(struct net *net) |
448 | { | 555 | { |
449 | nfs4_release_reclaim(); | 556 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); |
450 | nfsd4_shutdown_recdir(); | 557 | |
558 | nfs4_release_reclaim(nn); | ||
559 | nfsd4_shutdown_recdir(nn); | ||
560 | nfs4_legacy_state_shutdown(net); | ||
451 | } | 561 | } |
452 | 562 | ||
453 | /* | 563 | /* |
@@ -480,13 +590,26 @@ nfs4_recoverydir(void) | |||
480 | static int | 590 | static int |
481 | nfsd4_check_legacy_client(struct nfs4_client *clp) | 591 | nfsd4_check_legacy_client(struct nfs4_client *clp) |
482 | { | 592 | { |
593 | int status; | ||
594 | char dname[HEXDIR_LEN]; | ||
595 | struct nfs4_client_reclaim *crp; | ||
596 | struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); | ||
597 | |||
483 | /* did we already find that this client is stable? */ | 598 | /* did we already find that this client is stable? */ |
484 | if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags)) | 599 | if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags)) |
485 | return 0; | 600 | return 0; |
486 | 601 | ||
602 | status = nfs4_make_rec_clidname(dname, &clp->cl_name); | ||
603 | if (status) { | ||
604 | legacy_recdir_name_error(status); | ||
605 | return status; | ||
606 | } | ||
607 | |||
487 | /* look for it in the reclaim hashtable otherwise */ | 608 | /* look for it in the reclaim hashtable otherwise */ |
488 | if (nfsd4_find_reclaim_client(clp)) { | 609 | crp = nfsd4_find_reclaim_client(dname, nn); |
610 | if (crp) { | ||
489 | set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags); | 611 | set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags); |
612 | crp->cr_clp = clp; | ||
490 | return 0; | 613 | return 0; |
491 | } | 614 | } |
492 | 615 | ||
@@ -494,7 +617,7 @@ nfsd4_check_legacy_client(struct nfs4_client *clp) | |||
494 | } | 617 | } |
495 | 618 | ||
496 | static struct nfsd4_client_tracking_ops nfsd4_legacy_tracking_ops = { | 619 | static struct nfsd4_client_tracking_ops nfsd4_legacy_tracking_ops = { |
497 | .init = nfsd4_load_reboot_recovery_data, | 620 | .init = nfsd4_legacy_tracking_init, |
498 | .exit = nfsd4_legacy_tracking_exit, | 621 | .exit = nfsd4_legacy_tracking_exit, |
499 | .create = nfsd4_create_clid_dir, | 622 | .create = nfsd4_create_clid_dir, |
500 | .remove = nfsd4_remove_clid_dir, | 623 | .remove = nfsd4_remove_clid_dir, |
@@ -785,8 +908,7 @@ nfsd4_cld_create(struct nfs4_client *clp) | |||
785 | { | 908 | { |
786 | int ret; | 909 | int ret; |
787 | struct cld_upcall *cup; | 910 | struct cld_upcall *cup; |
788 | /* FIXME: determine net from clp */ | 911 | struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); |
789 | struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); | ||
790 | struct cld_net *cn = nn->cld_net; | 912 | struct cld_net *cn = nn->cld_net; |
791 | 913 | ||
792 | /* Don't upcall if it's already stored */ | 914 | /* Don't upcall if it's already stored */ |
@@ -823,8 +945,7 @@ nfsd4_cld_remove(struct nfs4_client *clp) | |||
823 | { | 945 | { |
824 | int ret; | 946 | int ret; |
825 | struct cld_upcall *cup; | 947 | struct cld_upcall *cup; |
826 | /* FIXME: determine net from clp */ | 948 | struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); |
827 | struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); | ||
828 | struct cld_net *cn = nn->cld_net; | 949 | struct cld_net *cn = nn->cld_net; |
829 | 950 | ||
830 | /* Don't upcall if it's already removed */ | 951 | /* Don't upcall if it's already removed */ |
@@ -861,8 +982,7 @@ nfsd4_cld_check(struct nfs4_client *clp) | |||
861 | { | 982 | { |
862 | int ret; | 983 | int ret; |
863 | struct cld_upcall *cup; | 984 | struct cld_upcall *cup; |
864 | /* FIXME: determine net from clp */ | 985 | struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); |
865 | struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); | ||
866 | struct cld_net *cn = nn->cld_net; | 986 | struct cld_net *cn = nn->cld_net; |
867 | 987 | ||
868 | /* Don't upcall if one was already stored during this grace pd */ | 988 | /* Don't upcall if one was already stored during this grace pd */ |
@@ -892,11 +1012,10 @@ nfsd4_cld_check(struct nfs4_client *clp) | |||
892 | } | 1012 | } |
893 | 1013 | ||
894 | static void | 1014 | static void |
895 | nfsd4_cld_grace_done(struct net *net, time_t boot_time) | 1015 | nfsd4_cld_grace_done(struct nfsd_net *nn, time_t boot_time) |
896 | { | 1016 | { |
897 | int ret; | 1017 | int ret; |
898 | struct cld_upcall *cup; | 1018 | struct cld_upcall *cup; |
899 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); | ||
900 | struct cld_net *cn = nn->cld_net; | 1019 | struct cld_net *cn = nn->cld_net; |
901 | 1020 | ||
902 | cup = alloc_cld_upcall(cn); | 1021 | cup = alloc_cld_upcall(cn); |
@@ -926,28 +1045,261 @@ static struct nfsd4_client_tracking_ops nfsd4_cld_tracking_ops = { | |||
926 | .grace_done = nfsd4_cld_grace_done, | 1045 | .grace_done = nfsd4_cld_grace_done, |
927 | }; | 1046 | }; |
928 | 1047 | ||
1048 | /* upcall via usermodehelper */ | ||
1049 | static char cltrack_prog[PATH_MAX] = "/sbin/nfsdcltrack"; | ||
1050 | module_param_string(cltrack_prog, cltrack_prog, sizeof(cltrack_prog), | ||
1051 | S_IRUGO|S_IWUSR); | ||
1052 | MODULE_PARM_DESC(cltrack_prog, "Path to the nfsdcltrack upcall program"); | ||
1053 | |||
1054 | static bool cltrack_legacy_disable; | ||
1055 | module_param(cltrack_legacy_disable, bool, S_IRUGO|S_IWUSR); | ||
1056 | MODULE_PARM_DESC(cltrack_legacy_disable, | ||
1057 | "Disable legacy recoverydir conversion. Default: false"); | ||
1058 | |||
1059 | #define LEGACY_TOPDIR_ENV_PREFIX "NFSDCLTRACK_LEGACY_TOPDIR=" | ||
1060 | #define LEGACY_RECDIR_ENV_PREFIX "NFSDCLTRACK_LEGACY_RECDIR=" | ||
1061 | |||
1062 | static char * | ||
1063 | nfsd4_cltrack_legacy_topdir(void) | ||
1064 | { | ||
1065 | int copied; | ||
1066 | size_t len; | ||
1067 | char *result; | ||
1068 | |||
1069 | if (cltrack_legacy_disable) | ||
1070 | return NULL; | ||
1071 | |||
1072 | len = strlen(LEGACY_TOPDIR_ENV_PREFIX) + | ||
1073 | strlen(nfs4_recoverydir()) + 1; | ||
1074 | |||
1075 | result = kmalloc(len, GFP_KERNEL); | ||
1076 | if (!result) | ||
1077 | return result; | ||
1078 | |||
1079 | copied = snprintf(result, len, LEGACY_TOPDIR_ENV_PREFIX "%s", | ||
1080 | nfs4_recoverydir()); | ||
1081 | if (copied >= len) { | ||
1082 | /* just return nothing if output was truncated */ | ||
1083 | kfree(result); | ||
1084 | return NULL; | ||
1085 | } | ||
1086 | |||
1087 | return result; | ||
1088 | } | ||
1089 | |||
1090 | static char * | ||
1091 | nfsd4_cltrack_legacy_recdir(const struct xdr_netobj *name) | ||
1092 | { | ||
1093 | int copied; | ||
1094 | size_t len; | ||
1095 | char *result; | ||
1096 | |||
1097 | if (cltrack_legacy_disable) | ||
1098 | return NULL; | ||
1099 | |||
1100 | /* +1 is for '/' between "topdir" and "recdir" */ | ||
1101 | len = strlen(LEGACY_RECDIR_ENV_PREFIX) + | ||
1102 | strlen(nfs4_recoverydir()) + 1 + HEXDIR_LEN; | ||
1103 | |||
1104 | result = kmalloc(len, GFP_KERNEL); | ||
1105 | if (!result) | ||
1106 | return result; | ||
1107 | |||
1108 | copied = snprintf(result, len, LEGACY_RECDIR_ENV_PREFIX "%s/", | ||
1109 | nfs4_recoverydir()); | ||
1110 | if (copied > (len - HEXDIR_LEN)) { | ||
1111 | /* just return nothing if output will be truncated */ | ||
1112 | kfree(result); | ||
1113 | return NULL; | ||
1114 | } | ||
1115 | |||
1116 | copied = nfs4_make_rec_clidname(result + copied, name); | ||
1117 | if (copied) { | ||
1118 | kfree(result); | ||
1119 | return NULL; | ||
1120 | } | ||
1121 | |||
1122 | return result; | ||
1123 | } | ||
1124 | |||
1125 | static int | ||
1126 | nfsd4_umh_cltrack_upcall(char *cmd, char *arg, char *legacy) | ||
1127 | { | ||
1128 | char *envp[2]; | ||
1129 | char *argv[4]; | ||
1130 | int ret; | ||
1131 | |||
1132 | if (unlikely(!cltrack_prog[0])) { | ||
1133 | dprintk("%s: cltrack_prog is disabled\n", __func__); | ||
1134 | return -EACCES; | ||
1135 | } | ||
1136 | |||
1137 | dprintk("%s: cmd: %s\n", __func__, cmd); | ||
1138 | dprintk("%s: arg: %s\n", __func__, arg ? arg : "(null)"); | ||
1139 | dprintk("%s: legacy: %s\n", __func__, legacy ? legacy : "(null)"); | ||
1140 | |||
1141 | envp[0] = legacy; | ||
1142 | envp[1] = NULL; | ||
1143 | |||
1144 | argv[0] = (char *)cltrack_prog; | ||
1145 | argv[1] = cmd; | ||
1146 | argv[2] = arg; | ||
1147 | argv[3] = NULL; | ||
1148 | |||
1149 | ret = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC); | ||
1150 | /* | ||
1151 | * Disable the upcall mechanism if we're getting an ENOENT or EACCES | ||
1152 | * error. The admin can re-enable it on the fly by using sysfs | ||
1153 | * once the problem has been fixed. | ||
1154 | */ | ||
1155 | if (ret == -ENOENT || ret == -EACCES) { | ||
1156 | dprintk("NFSD: %s was not found or isn't executable (%d). " | ||
1157 | "Setting cltrack_prog to blank string!", | ||
1158 | cltrack_prog, ret); | ||
1159 | cltrack_prog[0] = '\0'; | ||
1160 | } | ||
1161 | dprintk("%s: %s return value: %d\n", __func__, cltrack_prog, ret); | ||
1162 | |||
1163 | return ret; | ||
1164 | } | ||
1165 | |||
1166 | static char * | ||
1167 | bin_to_hex_dup(const unsigned char *src, int srclen) | ||
1168 | { | ||
1169 | int i; | ||
1170 | char *buf, *hex; | ||
1171 | |||
1172 | /* +1 for terminating NULL */ | ||
1173 | buf = kmalloc((srclen * 2) + 1, GFP_KERNEL); | ||
1174 | if (!buf) | ||
1175 | return buf; | ||
1176 | |||
1177 | hex = buf; | ||
1178 | for (i = 0; i < srclen; i++) { | ||
1179 | sprintf(hex, "%2.2x", *src++); | ||
1180 | hex += 2; | ||
1181 | } | ||
1182 | return buf; | ||
1183 | } | ||
1184 | |||
1185 | static int | ||
1186 | nfsd4_umh_cltrack_init(struct net __attribute__((unused)) *net) | ||
1187 | { | ||
1188 | return nfsd4_umh_cltrack_upcall("init", NULL, NULL); | ||
1189 | } | ||
1190 | |||
1191 | static void | ||
1192 | nfsd4_umh_cltrack_create(struct nfs4_client *clp) | ||
1193 | { | ||
1194 | char *hexid; | ||
1195 | |||
1196 | hexid = bin_to_hex_dup(clp->cl_name.data, clp->cl_name.len); | ||
1197 | if (!hexid) { | ||
1198 | dprintk("%s: can't allocate memory for upcall!\n", __func__); | ||
1199 | return; | ||
1200 | } | ||
1201 | nfsd4_umh_cltrack_upcall("create", hexid, NULL); | ||
1202 | kfree(hexid); | ||
1203 | } | ||
1204 | |||
1205 | static void | ||
1206 | nfsd4_umh_cltrack_remove(struct nfs4_client *clp) | ||
1207 | { | ||
1208 | char *hexid; | ||
1209 | |||
1210 | hexid = bin_to_hex_dup(clp->cl_name.data, clp->cl_name.len); | ||
1211 | if (!hexid) { | ||
1212 | dprintk("%s: can't allocate memory for upcall!\n", __func__); | ||
1213 | return; | ||
1214 | } | ||
1215 | nfsd4_umh_cltrack_upcall("remove", hexid, NULL); | ||
1216 | kfree(hexid); | ||
1217 | } | ||
1218 | |||
1219 | static int | ||
1220 | nfsd4_umh_cltrack_check(struct nfs4_client *clp) | ||
1221 | { | ||
1222 | int ret; | ||
1223 | char *hexid, *legacy; | ||
1224 | |||
1225 | hexid = bin_to_hex_dup(clp->cl_name.data, clp->cl_name.len); | ||
1226 | if (!hexid) { | ||
1227 | dprintk("%s: can't allocate memory for upcall!\n", __func__); | ||
1228 | return -ENOMEM; | ||
1229 | } | ||
1230 | legacy = nfsd4_cltrack_legacy_recdir(&clp->cl_name); | ||
1231 | ret = nfsd4_umh_cltrack_upcall("check", hexid, legacy); | ||
1232 | kfree(legacy); | ||
1233 | kfree(hexid); | ||
1234 | return ret; | ||
1235 | } | ||
1236 | |||
1237 | static void | ||
1238 | nfsd4_umh_cltrack_grace_done(struct nfsd_net __attribute__((unused)) *nn, | ||
1239 | time_t boot_time) | ||
1240 | { | ||
1241 | char *legacy; | ||
1242 | char timestr[22]; /* FIXME: better way to determine max size? */ | ||
1243 | |||
1244 | sprintf(timestr, "%ld", boot_time); | ||
1245 | legacy = nfsd4_cltrack_legacy_topdir(); | ||
1246 | nfsd4_umh_cltrack_upcall("gracedone", timestr, legacy); | ||
1247 | kfree(legacy); | ||
1248 | } | ||
1249 | |||
1250 | static struct nfsd4_client_tracking_ops nfsd4_umh_tracking_ops = { | ||
1251 | .init = nfsd4_umh_cltrack_init, | ||
1252 | .exit = NULL, | ||
1253 | .create = nfsd4_umh_cltrack_create, | ||
1254 | .remove = nfsd4_umh_cltrack_remove, | ||
1255 | .check = nfsd4_umh_cltrack_check, | ||
1256 | .grace_done = nfsd4_umh_cltrack_grace_done, | ||
1257 | }; | ||
1258 | |||
929 | int | 1259 | int |
930 | nfsd4_client_tracking_init(struct net *net) | 1260 | nfsd4_client_tracking_init(struct net *net) |
931 | { | 1261 | { |
932 | int status; | 1262 | int status; |
933 | struct path path; | 1263 | struct path path; |
1264 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); | ||
934 | 1265 | ||
935 | if (!client_tracking_ops) { | 1266 | /* just run the init if it the method is already decided */ |
936 | client_tracking_ops = &nfsd4_cld_tracking_ops; | 1267 | if (nn->client_tracking_ops) |
937 | status = kern_path(nfs4_recoverydir(), LOOKUP_FOLLOW, &path); | 1268 | goto do_init; |
938 | if (!status) { | 1269 | |
939 | if (S_ISDIR(path.dentry->d_inode->i_mode)) | 1270 | /* |
940 | client_tracking_ops = | 1271 | * First, try a UMH upcall. It should succeed or fail quickly, so |
941 | &nfsd4_legacy_tracking_ops; | 1272 | * there's little harm in trying that first. |
942 | path_put(&path); | 1273 | */ |
943 | } | 1274 | nn->client_tracking_ops = &nfsd4_umh_tracking_ops; |
1275 | status = nn->client_tracking_ops->init(net); | ||
1276 | if (!status) | ||
1277 | return status; | ||
1278 | |||
1279 | /* | ||
1280 | * See if the recoverydir exists and is a directory. If it is, | ||
1281 | * then use the legacy ops. | ||
1282 | */ | ||
1283 | nn->client_tracking_ops = &nfsd4_legacy_tracking_ops; | ||
1284 | status = kern_path(nfs4_recoverydir(), LOOKUP_FOLLOW, &path); | ||
1285 | if (!status) { | ||
1286 | status = S_ISDIR(path.dentry->d_inode->i_mode); | ||
1287 | path_put(&path); | ||
1288 | if (status) | ||
1289 | goto do_init; | ||
944 | } | 1290 | } |
945 | 1291 | ||
946 | status = client_tracking_ops->init(net); | 1292 | /* Finally, try to use nfsdcld */ |
1293 | nn->client_tracking_ops = &nfsd4_cld_tracking_ops; | ||
1294 | printk(KERN_WARNING "NFSD: the nfsdcld client tracking upcall will be " | ||
1295 | "removed in 3.10. Please transition to using " | ||
1296 | "nfsdcltrack.\n"); | ||
1297 | do_init: | ||
1298 | status = nn->client_tracking_ops->init(net); | ||
947 | if (status) { | 1299 | if (status) { |
948 | printk(KERN_WARNING "NFSD: Unable to initialize client " | 1300 | printk(KERN_WARNING "NFSD: Unable to initialize client " |
949 | "recovery tracking! (%d)\n", status); | 1301 | "recovery tracking! (%d)\n", status); |
950 | client_tracking_ops = NULL; | 1302 | nn->client_tracking_ops = NULL; |
951 | } | 1303 | } |
952 | return status; | 1304 | return status; |
953 | } | 1305 | } |
@@ -955,40 +1307,49 @@ nfsd4_client_tracking_init(struct net *net) | |||
955 | void | 1307 | void |
956 | nfsd4_client_tracking_exit(struct net *net) | 1308 | nfsd4_client_tracking_exit(struct net *net) |
957 | { | 1309 | { |
958 | if (client_tracking_ops) { | 1310 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); |
959 | client_tracking_ops->exit(net); | 1311 | |
960 | client_tracking_ops = NULL; | 1312 | if (nn->client_tracking_ops) { |
1313 | if (nn->client_tracking_ops->exit) | ||
1314 | nn->client_tracking_ops->exit(net); | ||
1315 | nn->client_tracking_ops = NULL; | ||
961 | } | 1316 | } |
962 | } | 1317 | } |
963 | 1318 | ||
964 | void | 1319 | void |
965 | nfsd4_client_record_create(struct nfs4_client *clp) | 1320 | nfsd4_client_record_create(struct nfs4_client *clp) |
966 | { | 1321 | { |
967 | if (client_tracking_ops) | 1322 | struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); |
968 | client_tracking_ops->create(clp); | 1323 | |
1324 | if (nn->client_tracking_ops) | ||
1325 | nn->client_tracking_ops->create(clp); | ||
969 | } | 1326 | } |
970 | 1327 | ||
971 | void | 1328 | void |
972 | nfsd4_client_record_remove(struct nfs4_client *clp) | 1329 | nfsd4_client_record_remove(struct nfs4_client *clp) |
973 | { | 1330 | { |
974 | if (client_tracking_ops) | 1331 | struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); |
975 | client_tracking_ops->remove(clp); | 1332 | |
1333 | if (nn->client_tracking_ops) | ||
1334 | nn->client_tracking_ops->remove(clp); | ||
976 | } | 1335 | } |
977 | 1336 | ||
978 | int | 1337 | int |
979 | nfsd4_client_record_check(struct nfs4_client *clp) | 1338 | nfsd4_client_record_check(struct nfs4_client *clp) |
980 | { | 1339 | { |
981 | if (client_tracking_ops) | 1340 | struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); |
982 | return client_tracking_ops->check(clp); | 1341 | |
1342 | if (nn->client_tracking_ops) | ||
1343 | return nn->client_tracking_ops->check(clp); | ||
983 | 1344 | ||
984 | return -EOPNOTSUPP; | 1345 | return -EOPNOTSUPP; |
985 | } | 1346 | } |
986 | 1347 | ||
987 | void | 1348 | void |
988 | nfsd4_record_grace_done(struct net *net, time_t boot_time) | 1349 | nfsd4_record_grace_done(struct nfsd_net *nn, time_t boot_time) |
989 | { | 1350 | { |
990 | if (client_tracking_ops) | 1351 | if (nn->client_tracking_ops) |
991 | client_tracking_ops->grace_done(net, boot_time); | 1352 | nn->client_tracking_ops->grace_done(nn, boot_time); |
992 | } | 1353 | } |
993 | 1354 | ||
994 | static int | 1355 | static int |
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index d0237f872cc4..ac8ed96c4199 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c | |||
@@ -44,16 +44,11 @@ | |||
44 | #include "xdr4.h" | 44 | #include "xdr4.h" |
45 | #include "vfs.h" | 45 | #include "vfs.h" |
46 | #include "current_stateid.h" | 46 | #include "current_stateid.h" |
47 | #include "fault_inject.h" | ||
48 | 47 | ||
49 | #include "netns.h" | 48 | #include "netns.h" |
50 | 49 | ||
51 | #define NFSDDBG_FACILITY NFSDDBG_PROC | 50 | #define NFSDDBG_FACILITY NFSDDBG_PROC |
52 | 51 | ||
53 | /* Globals */ | ||
54 | time_t nfsd4_lease = 90; /* default lease time */ | ||
55 | time_t nfsd4_grace = 90; | ||
56 | |||
57 | #define all_ones {{~0,~0},~0} | 52 | #define all_ones {{~0,~0},~0} |
58 | static const stateid_t one_stateid = { | 53 | static const stateid_t one_stateid = { |
59 | .si_generation = ~0, | 54 | .si_generation = ~0, |
@@ -176,8 +171,6 @@ static unsigned int ownerstr_hashval(u32 clientid, struct xdr_netobj *ownername) | |||
176 | return ret & OWNER_HASH_MASK; | 171 | return ret & OWNER_HASH_MASK; |
177 | } | 172 | } |
178 | 173 | ||
179 | static struct list_head ownerstr_hashtbl[OWNER_HASH_SIZE]; | ||
180 | |||
181 | /* hash table for nfs4_file */ | 174 | /* hash table for nfs4_file */ |
182 | #define FILE_HASH_BITS 8 | 175 | #define FILE_HASH_BITS 8 |
183 | #define FILE_HASH_SIZE (1 << FILE_HASH_BITS) | 176 | #define FILE_HASH_SIZE (1 << FILE_HASH_BITS) |
@@ -192,7 +185,7 @@ static struct list_head file_hashtbl[FILE_HASH_SIZE]; | |||
192 | 185 | ||
193 | static void __nfs4_file_get_access(struct nfs4_file *fp, int oflag) | 186 | static void __nfs4_file_get_access(struct nfs4_file *fp, int oflag) |
194 | { | 187 | { |
195 | BUG_ON(!(fp->fi_fds[oflag] || fp->fi_fds[O_RDWR])); | 188 | WARN_ON_ONCE(!(fp->fi_fds[oflag] || fp->fi_fds[O_RDWR])); |
196 | atomic_inc(&fp->fi_access[oflag]); | 189 | atomic_inc(&fp->fi_access[oflag]); |
197 | } | 190 | } |
198 | 191 | ||
@@ -251,7 +244,7 @@ static inline int get_new_stid(struct nfs4_stid *stid) | |||
251 | * preallocations that can exist at a time, but the state lock | 244 | * preallocations that can exist at a time, but the state lock |
252 | * prevents anyone from using ours before we get here: | 245 | * prevents anyone from using ours before we get here: |
253 | */ | 246 | */ |
254 | BUG_ON(error); | 247 | WARN_ON_ONCE(error); |
255 | /* | 248 | /* |
256 | * It shouldn't be a problem to reuse an opaque stateid value. | 249 | * It shouldn't be a problem to reuse an opaque stateid value. |
257 | * I don't think it is for 4.1. But with 4.0 I worry that, for | 250 | * I don't think it is for 4.1. But with 4.0 I worry that, for |
@@ -340,7 +333,7 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_ol_stateid *stp, struct sv | |||
340 | fh_copy_shallow(&dp->dl_fh, ¤t_fh->fh_handle); | 333 | fh_copy_shallow(&dp->dl_fh, ¤t_fh->fh_handle); |
341 | dp->dl_time = 0; | 334 | dp->dl_time = 0; |
342 | atomic_set(&dp->dl_count, 1); | 335 | atomic_set(&dp->dl_count, 1); |
343 | INIT_WORK(&dp->dl_recall.cb_work, nfsd4_do_callback_rpc); | 336 | nfsd4_init_callback(&dp->dl_recall); |
344 | return dp; | 337 | return dp; |
345 | } | 338 | } |
346 | 339 | ||
@@ -390,14 +383,6 @@ unhash_delegation(struct nfs4_delegation *dp) | |||
390 | * SETCLIENTID state | 383 | * SETCLIENTID state |
391 | */ | 384 | */ |
392 | 385 | ||
393 | /* client_lock protects the client lru list and session hash table */ | ||
394 | static DEFINE_SPINLOCK(client_lock); | ||
395 | |||
396 | /* Hash tables for nfs4_clientid state */ | ||
397 | #define CLIENT_HASH_BITS 4 | ||
398 | #define CLIENT_HASH_SIZE (1 << CLIENT_HASH_BITS) | ||
399 | #define CLIENT_HASH_MASK (CLIENT_HASH_SIZE - 1) | ||
400 | |||
401 | static unsigned int clientid_hashval(u32 id) | 386 | static unsigned int clientid_hashval(u32 id) |
402 | { | 387 | { |
403 | return id & CLIENT_HASH_MASK; | 388 | return id & CLIENT_HASH_MASK; |
@@ -409,31 +394,6 @@ static unsigned int clientstr_hashval(const char *name) | |||
409 | } | 394 | } |
410 | 395 | ||
411 | /* | 396 | /* |
412 | * reclaim_str_hashtbl[] holds known client info from previous reset/reboot | ||
413 | * used in reboot/reset lease grace period processing | ||
414 | * | ||
415 | * conf_id_hashtbl[], and conf_str_hashtbl[] hold confirmed | ||
416 | * setclientid_confirmed info. | ||
417 | * | ||
418 | * unconf_str_hastbl[] and unconf_id_hashtbl[] hold unconfirmed | ||
419 | * setclientid info. | ||
420 | * | ||
421 | * client_lru holds client queue ordered by nfs4_client.cl_time | ||
422 | * for lease renewal. | ||
423 | * | ||
424 | * close_lru holds (open) stateowner queue ordered by nfs4_stateowner.so_time | ||
425 | * for last close replay. | ||
426 | */ | ||
427 | static struct list_head reclaim_str_hashtbl[CLIENT_HASH_SIZE]; | ||
428 | static int reclaim_str_hashtbl_size = 0; | ||
429 | static struct list_head conf_id_hashtbl[CLIENT_HASH_SIZE]; | ||
430 | static struct list_head conf_str_hashtbl[CLIENT_HASH_SIZE]; | ||
431 | static struct list_head unconf_str_hashtbl[CLIENT_HASH_SIZE]; | ||
432 | static struct list_head unconf_id_hashtbl[CLIENT_HASH_SIZE]; | ||
433 | static struct list_head client_lru; | ||
434 | static struct list_head close_lru; | ||
435 | |||
436 | /* | ||
437 | * We store the NONE, READ, WRITE, and BOTH bits separately in the | 397 | * We store the NONE, READ, WRITE, and BOTH bits separately in the |
438 | * st_{access,deny}_bmap field of the stateid, in order to track not | 398 | * st_{access,deny}_bmap field of the stateid, in order to track not |
439 | * only what share bits are currently in force, but also what | 399 | * only what share bits are currently in force, but also what |
@@ -526,7 +486,8 @@ static int nfs4_access_to_omode(u32 access) | |||
526 | case NFS4_SHARE_ACCESS_BOTH: | 486 | case NFS4_SHARE_ACCESS_BOTH: |
527 | return O_RDWR; | 487 | return O_RDWR; |
528 | } | 488 | } |
529 | BUG(); | 489 | WARN_ON_ONCE(1); |
490 | return O_RDONLY; | ||
530 | } | 491 | } |
531 | 492 | ||
532 | /* release all access and file references for a given stateid */ | 493 | /* release all access and file references for a given stateid */ |
@@ -652,9 +613,6 @@ static void release_openowner(struct nfs4_openowner *oo) | |||
652 | nfs4_free_openowner(oo); | 613 | nfs4_free_openowner(oo); |
653 | } | 614 | } |
654 | 615 | ||
655 | #define SESSION_HASH_SIZE 512 | ||
656 | static struct list_head sessionid_hashtbl[SESSION_HASH_SIZE]; | ||
657 | |||
658 | static inline int | 616 | static inline int |
659 | hash_sessionid(struct nfs4_sessionid *sessionid) | 617 | hash_sessionid(struct nfs4_sessionid *sessionid) |
660 | { | 618 | { |
@@ -785,9 +743,12 @@ out_free: | |||
785 | return NULL; | 743 | return NULL; |
786 | } | 744 | } |
787 | 745 | ||
788 | static void init_forechannel_attrs(struct nfsd4_channel_attrs *new, struct nfsd4_channel_attrs *req, int numslots, int slotsize) | 746 | static void init_forechannel_attrs(struct nfsd4_channel_attrs *new, |
747 | struct nfsd4_channel_attrs *req, | ||
748 | int numslots, int slotsize, | ||
749 | struct nfsd_net *nn) | ||
789 | { | 750 | { |
790 | u32 maxrpc = nfsd_serv->sv_max_mesg; | 751 | u32 maxrpc = nn->nfsd_serv->sv_max_mesg; |
791 | 752 | ||
792 | new->maxreqs = numslots; | 753 | new->maxreqs = numslots; |
793 | new->maxresp_cached = min_t(u32, req->maxresp_cached, | 754 | new->maxresp_cached = min_t(u32, req->maxresp_cached, |
@@ -906,21 +867,27 @@ static void __free_session(struct nfsd4_session *ses) | |||
906 | static void free_session(struct kref *kref) | 867 | static void free_session(struct kref *kref) |
907 | { | 868 | { |
908 | struct nfsd4_session *ses; | 869 | struct nfsd4_session *ses; |
870 | struct nfsd_net *nn; | ||
909 | 871 | ||
910 | lockdep_assert_held(&client_lock); | ||
911 | ses = container_of(kref, struct nfsd4_session, se_ref); | 872 | ses = container_of(kref, struct nfsd4_session, se_ref); |
873 | nn = net_generic(ses->se_client->net, nfsd_net_id); | ||
874 | |||
875 | lockdep_assert_held(&nn->client_lock); | ||
912 | nfsd4_del_conns(ses); | 876 | nfsd4_del_conns(ses); |
913 | __free_session(ses); | 877 | __free_session(ses); |
914 | } | 878 | } |
915 | 879 | ||
916 | void nfsd4_put_session(struct nfsd4_session *ses) | 880 | void nfsd4_put_session(struct nfsd4_session *ses) |
917 | { | 881 | { |
918 | spin_lock(&client_lock); | 882 | struct nfsd_net *nn = net_generic(ses->se_client->net, nfsd_net_id); |
883 | |||
884 | spin_lock(&nn->client_lock); | ||
919 | nfsd4_put_session_locked(ses); | 885 | nfsd4_put_session_locked(ses); |
920 | spin_unlock(&client_lock); | 886 | spin_unlock(&nn->client_lock); |
921 | } | 887 | } |
922 | 888 | ||
923 | static struct nfsd4_session *alloc_session(struct nfsd4_channel_attrs *fchan) | 889 | static struct nfsd4_session *alloc_session(struct nfsd4_channel_attrs *fchan, |
890 | struct nfsd_net *nn) | ||
924 | { | 891 | { |
925 | struct nfsd4_session *new; | 892 | struct nfsd4_session *new; |
926 | int numslots, slotsize; | 893 | int numslots, slotsize; |
@@ -941,13 +908,14 @@ static struct nfsd4_session *alloc_session(struct nfsd4_channel_attrs *fchan) | |||
941 | nfsd4_put_drc_mem(slotsize, fchan->maxreqs); | 908 | nfsd4_put_drc_mem(slotsize, fchan->maxreqs); |
942 | return NULL; | 909 | return NULL; |
943 | } | 910 | } |
944 | init_forechannel_attrs(&new->se_fchannel, fchan, numslots, slotsize); | 911 | init_forechannel_attrs(&new->se_fchannel, fchan, numslots, slotsize, nn); |
945 | return new; | 912 | return new; |
946 | } | 913 | } |
947 | 914 | ||
948 | static struct nfsd4_session *init_session(struct svc_rqst *rqstp, struct nfsd4_session *new, struct nfs4_client *clp, struct nfsd4_create_session *cses) | 915 | static void init_session(struct svc_rqst *rqstp, struct nfsd4_session *new, struct nfs4_client *clp, struct nfsd4_create_session *cses) |
949 | { | 916 | { |
950 | int idx; | 917 | int idx; |
918 | struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); | ||
951 | 919 | ||
952 | new->se_client = clp; | 920 | new->se_client = clp; |
953 | gen_sessionid(new); | 921 | gen_sessionid(new); |
@@ -957,14 +925,15 @@ static struct nfsd4_session *init_session(struct svc_rqst *rqstp, struct nfsd4_s | |||
957 | new->se_cb_seq_nr = 1; | 925 | new->se_cb_seq_nr = 1; |
958 | new->se_flags = cses->flags; | 926 | new->se_flags = cses->flags; |
959 | new->se_cb_prog = cses->callback_prog; | 927 | new->se_cb_prog = cses->callback_prog; |
928 | new->se_cb_sec = cses->cb_sec; | ||
960 | kref_init(&new->se_ref); | 929 | kref_init(&new->se_ref); |
961 | idx = hash_sessionid(&new->se_sessionid); | 930 | idx = hash_sessionid(&new->se_sessionid); |
962 | spin_lock(&client_lock); | 931 | spin_lock(&nn->client_lock); |
963 | list_add(&new->se_hash, &sessionid_hashtbl[idx]); | 932 | list_add(&new->se_hash, &nn->sessionid_hashtbl[idx]); |
964 | spin_lock(&clp->cl_lock); | 933 | spin_lock(&clp->cl_lock); |
965 | list_add(&new->se_perclnt, &clp->cl_sessions); | 934 | list_add(&new->se_perclnt, &clp->cl_sessions); |
966 | spin_unlock(&clp->cl_lock); | 935 | spin_unlock(&clp->cl_lock); |
967 | spin_unlock(&client_lock); | 936 | spin_unlock(&nn->client_lock); |
968 | 937 | ||
969 | if (cses->flags & SESSION4_BACK_CHAN) { | 938 | if (cses->flags & SESSION4_BACK_CHAN) { |
970 | struct sockaddr *sa = svc_addr(rqstp); | 939 | struct sockaddr *sa = svc_addr(rqstp); |
@@ -978,20 +947,20 @@ static struct nfsd4_session *init_session(struct svc_rqst *rqstp, struct nfsd4_s | |||
978 | rpc_copy_addr((struct sockaddr *)&clp->cl_cb_conn.cb_addr, sa); | 947 | rpc_copy_addr((struct sockaddr *)&clp->cl_cb_conn.cb_addr, sa); |
979 | clp->cl_cb_conn.cb_addrlen = svc_addr_len(sa); | 948 | clp->cl_cb_conn.cb_addrlen = svc_addr_len(sa); |
980 | } | 949 | } |
981 | return new; | ||
982 | } | 950 | } |
983 | 951 | ||
984 | /* caller must hold client_lock */ | 952 | /* caller must hold client_lock */ |
985 | static struct nfsd4_session * | 953 | static struct nfsd4_session * |
986 | find_in_sessionid_hashtbl(struct nfs4_sessionid *sessionid) | 954 | find_in_sessionid_hashtbl(struct nfs4_sessionid *sessionid, struct net *net) |
987 | { | 955 | { |
988 | struct nfsd4_session *elem; | 956 | struct nfsd4_session *elem; |
989 | int idx; | 957 | int idx; |
958 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); | ||
990 | 959 | ||
991 | dump_sessionid(__func__, sessionid); | 960 | dump_sessionid(__func__, sessionid); |
992 | idx = hash_sessionid(sessionid); | 961 | idx = hash_sessionid(sessionid); |
993 | /* Search in the appropriate list */ | 962 | /* Search in the appropriate list */ |
994 | list_for_each_entry(elem, &sessionid_hashtbl[idx], se_hash) { | 963 | list_for_each_entry(elem, &nn->sessionid_hashtbl[idx], se_hash) { |
995 | if (!memcmp(elem->se_sessionid.data, sessionid->data, | 964 | if (!memcmp(elem->se_sessionid.data, sessionid->data, |
996 | NFS4_MAX_SESSIONID_LEN)) { | 965 | NFS4_MAX_SESSIONID_LEN)) { |
997 | return elem; | 966 | return elem; |
@@ -1016,6 +985,8 @@ unhash_session(struct nfsd4_session *ses) | |||
1016 | static inline void | 985 | static inline void |
1017 | renew_client_locked(struct nfs4_client *clp) | 986 | renew_client_locked(struct nfs4_client *clp) |
1018 | { | 987 | { |
988 | struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); | ||
989 | |||
1019 | if (is_client_expired(clp)) { | 990 | if (is_client_expired(clp)) { |
1020 | WARN_ON(1); | 991 | WARN_ON(1); |
1021 | printk("%s: client (clientid %08x/%08x) already expired\n", | 992 | printk("%s: client (clientid %08x/%08x) already expired\n", |
@@ -1028,16 +999,18 @@ renew_client_locked(struct nfs4_client *clp) | |||
1028 | dprintk("renewing client (clientid %08x/%08x)\n", | 999 | dprintk("renewing client (clientid %08x/%08x)\n", |
1029 | clp->cl_clientid.cl_boot, | 1000 | clp->cl_clientid.cl_boot, |
1030 | clp->cl_clientid.cl_id); | 1001 | clp->cl_clientid.cl_id); |
1031 | list_move_tail(&clp->cl_lru, &client_lru); | 1002 | list_move_tail(&clp->cl_lru, &nn->client_lru); |
1032 | clp->cl_time = get_seconds(); | 1003 | clp->cl_time = get_seconds(); |
1033 | } | 1004 | } |
1034 | 1005 | ||
1035 | static inline void | 1006 | static inline void |
1036 | renew_client(struct nfs4_client *clp) | 1007 | renew_client(struct nfs4_client *clp) |
1037 | { | 1008 | { |
1038 | spin_lock(&client_lock); | 1009 | struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); |
1010 | |||
1011 | spin_lock(&nn->client_lock); | ||
1039 | renew_client_locked(clp); | 1012 | renew_client_locked(clp); |
1040 | spin_unlock(&client_lock); | 1013 | spin_unlock(&nn->client_lock); |
1041 | } | 1014 | } |
1042 | 1015 | ||
1043 | /* SETCLIENTID and SETCLIENTID_CONFIRM Helper functions */ | 1016 | /* SETCLIENTID and SETCLIENTID_CONFIRM Helper functions */ |
@@ -1075,7 +1048,9 @@ static struct nfs4_client *alloc_client(struct xdr_netobj name) | |||
1075 | static inline void | 1048 | static inline void |
1076 | free_client(struct nfs4_client *clp) | 1049 | free_client(struct nfs4_client *clp) |
1077 | { | 1050 | { |
1078 | lockdep_assert_held(&client_lock); | 1051 | struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); |
1052 | |||
1053 | lockdep_assert_held(&nn->client_lock); | ||
1079 | while (!list_empty(&clp->cl_sessions)) { | 1054 | while (!list_empty(&clp->cl_sessions)) { |
1080 | struct nfsd4_session *ses; | 1055 | struct nfsd4_session *ses; |
1081 | ses = list_entry(clp->cl_sessions.next, struct nfsd4_session, | 1056 | ses = list_entry(clp->cl_sessions.next, struct nfsd4_session, |
@@ -1092,15 +1067,16 @@ void | |||
1092 | release_session_client(struct nfsd4_session *session) | 1067 | release_session_client(struct nfsd4_session *session) |
1093 | { | 1068 | { |
1094 | struct nfs4_client *clp = session->se_client; | 1069 | struct nfs4_client *clp = session->se_client; |
1070 | struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); | ||
1095 | 1071 | ||
1096 | if (!atomic_dec_and_lock(&clp->cl_refcount, &client_lock)) | 1072 | if (!atomic_dec_and_lock(&clp->cl_refcount, &nn->client_lock)) |
1097 | return; | 1073 | return; |
1098 | if (is_client_expired(clp)) { | 1074 | if (is_client_expired(clp)) { |
1099 | free_client(clp); | 1075 | free_client(clp); |
1100 | session->se_client = NULL; | 1076 | session->se_client = NULL; |
1101 | } else | 1077 | } else |
1102 | renew_client_locked(clp); | 1078 | renew_client_locked(clp); |
1103 | spin_unlock(&client_lock); | 1079 | spin_unlock(&nn->client_lock); |
1104 | } | 1080 | } |
1105 | 1081 | ||
1106 | /* must be called under the client_lock */ | 1082 | /* must be called under the client_lock */ |
@@ -1123,6 +1099,7 @@ destroy_client(struct nfs4_client *clp) | |||
1123 | struct nfs4_openowner *oo; | 1099 | struct nfs4_openowner *oo; |
1124 | struct nfs4_delegation *dp; | 1100 | struct nfs4_delegation *dp; |
1125 | struct list_head reaplist; | 1101 | struct list_head reaplist; |
1102 | struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); | ||
1126 | 1103 | ||
1127 | INIT_LIST_HEAD(&reaplist); | 1104 | INIT_LIST_HEAD(&reaplist); |
1128 | spin_lock(&recall_lock); | 1105 | spin_lock(&recall_lock); |
@@ -1144,12 +1121,15 @@ destroy_client(struct nfs4_client *clp) | |||
1144 | if (clp->cl_cb_conn.cb_xprt) | 1121 | if (clp->cl_cb_conn.cb_xprt) |
1145 | svc_xprt_put(clp->cl_cb_conn.cb_xprt); | 1122 | svc_xprt_put(clp->cl_cb_conn.cb_xprt); |
1146 | list_del(&clp->cl_idhash); | 1123 | list_del(&clp->cl_idhash); |
1147 | list_del(&clp->cl_strhash); | 1124 | if (test_bit(NFSD4_CLIENT_CONFIRMED, &clp->cl_flags)) |
1148 | spin_lock(&client_lock); | 1125 | rb_erase(&clp->cl_namenode, &nn->conf_name_tree); |
1126 | else | ||
1127 | rb_erase(&clp->cl_namenode, &nn->unconf_name_tree); | ||
1128 | spin_lock(&nn->client_lock); | ||
1149 | unhash_client_locked(clp); | 1129 | unhash_client_locked(clp); |
1150 | if (atomic_read(&clp->cl_refcount) == 0) | 1130 | if (atomic_read(&clp->cl_refcount) == 0) |
1151 | free_client(clp); | 1131 | free_client(clp); |
1152 | spin_unlock(&client_lock); | 1132 | spin_unlock(&nn->client_lock); |
1153 | } | 1133 | } |
1154 | 1134 | ||
1155 | static void expire_client(struct nfs4_client *clp) | 1135 | static void expire_client(struct nfs4_client *clp) |
@@ -1187,6 +1167,17 @@ static int copy_cred(struct svc_cred *target, struct svc_cred *source) | |||
1187 | return 0; | 1167 | return 0; |
1188 | } | 1168 | } |
1189 | 1169 | ||
1170 | static long long | ||
1171 | compare_blob(const struct xdr_netobj *o1, const struct xdr_netobj *o2) | ||
1172 | { | ||
1173 | long long res; | ||
1174 | |||
1175 | res = o1->len - o2->len; | ||
1176 | if (res) | ||
1177 | return res; | ||
1178 | return (long long)memcmp(o1->data, o2->data, o1->len); | ||
1179 | } | ||
1180 | |||
1190 | static int same_name(const char *n1, const char *n2) | 1181 | static int same_name(const char *n1, const char *n2) |
1191 | { | 1182 | { |
1192 | return 0 == memcmp(n1, n2, HEXDIR_LEN); | 1183 | return 0 == memcmp(n1, n2, HEXDIR_LEN); |
@@ -1247,10 +1238,9 @@ same_creds(struct svc_cred *cr1, struct svc_cred *cr2) | |||
1247 | return 0 == strcmp(cr1->cr_principal, cr2->cr_principal); | 1238 | return 0 == strcmp(cr1->cr_principal, cr2->cr_principal); |
1248 | } | 1239 | } |
1249 | 1240 | ||
1250 | static void gen_clid(struct nfs4_client *clp) | 1241 | static void gen_clid(struct nfs4_client *clp, struct nfsd_net *nn) |
1251 | { | 1242 | { |
1252 | static u32 current_clientid = 1; | 1243 | static u32 current_clientid = 1; |
1253 | struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); | ||
1254 | 1244 | ||
1255 | clp->cl_clientid.cl_boot = nn->boot_time; | 1245 | clp->cl_clientid.cl_boot = nn->boot_time; |
1256 | clp->cl_clientid.cl_id = current_clientid++; | 1246 | clp->cl_clientid.cl_id = current_clientid++; |
@@ -1283,12 +1273,14 @@ static struct nfs4_stid *find_stateid_by_type(struct nfs4_client *cl, stateid_t | |||
1283 | return NULL; | 1273 | return NULL; |
1284 | } | 1274 | } |
1285 | 1275 | ||
1286 | static struct nfs4_client *create_client(struct xdr_netobj name, char *recdir, | 1276 | static struct nfs4_client *create_client(struct xdr_netobj name, |
1287 | struct svc_rqst *rqstp, nfs4_verifier *verf) | 1277 | struct svc_rqst *rqstp, nfs4_verifier *verf) |
1288 | { | 1278 | { |
1289 | struct nfs4_client *clp; | 1279 | struct nfs4_client *clp; |
1290 | struct sockaddr *sa = svc_addr(rqstp); | 1280 | struct sockaddr *sa = svc_addr(rqstp); |
1291 | int ret; | 1281 | int ret; |
1282 | struct net *net = SVC_NET(rqstp); | ||
1283 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); | ||
1292 | 1284 | ||
1293 | clp = alloc_client(name); | 1285 | clp = alloc_client(name); |
1294 | if (clp == NULL) | 1286 | if (clp == NULL) |
@@ -1297,23 +1289,21 @@ static struct nfs4_client *create_client(struct xdr_netobj name, char *recdir, | |||
1297 | INIT_LIST_HEAD(&clp->cl_sessions); | 1289 | INIT_LIST_HEAD(&clp->cl_sessions); |
1298 | ret = copy_cred(&clp->cl_cred, &rqstp->rq_cred); | 1290 | ret = copy_cred(&clp->cl_cred, &rqstp->rq_cred); |
1299 | if (ret) { | 1291 | if (ret) { |
1300 | spin_lock(&client_lock); | 1292 | spin_lock(&nn->client_lock); |
1301 | free_client(clp); | 1293 | free_client(clp); |
1302 | spin_unlock(&client_lock); | 1294 | spin_unlock(&nn->client_lock); |
1303 | return NULL; | 1295 | return NULL; |
1304 | } | 1296 | } |
1305 | idr_init(&clp->cl_stateids); | 1297 | idr_init(&clp->cl_stateids); |
1306 | memcpy(clp->cl_recdir, recdir, HEXDIR_LEN); | ||
1307 | atomic_set(&clp->cl_refcount, 0); | 1298 | atomic_set(&clp->cl_refcount, 0); |
1308 | clp->cl_cb_state = NFSD4_CB_UNKNOWN; | 1299 | clp->cl_cb_state = NFSD4_CB_UNKNOWN; |
1309 | INIT_LIST_HEAD(&clp->cl_idhash); | 1300 | INIT_LIST_HEAD(&clp->cl_idhash); |
1310 | INIT_LIST_HEAD(&clp->cl_strhash); | ||
1311 | INIT_LIST_HEAD(&clp->cl_openowners); | 1301 | INIT_LIST_HEAD(&clp->cl_openowners); |
1312 | INIT_LIST_HEAD(&clp->cl_delegations); | 1302 | INIT_LIST_HEAD(&clp->cl_delegations); |
1313 | INIT_LIST_HEAD(&clp->cl_lru); | 1303 | INIT_LIST_HEAD(&clp->cl_lru); |
1314 | INIT_LIST_HEAD(&clp->cl_callbacks); | 1304 | INIT_LIST_HEAD(&clp->cl_callbacks); |
1315 | spin_lock_init(&clp->cl_lock); | 1305 | spin_lock_init(&clp->cl_lock); |
1316 | INIT_WORK(&clp->cl_cb_null.cb_work, nfsd4_do_callback_rpc); | 1306 | nfsd4_init_callback(&clp->cl_cb_null); |
1317 | clp->cl_time = get_seconds(); | 1307 | clp->cl_time = get_seconds(); |
1318 | clear_bit(0, &clp->cl_cb_slot_busy); | 1308 | clear_bit(0, &clp->cl_cb_slot_busy); |
1319 | rpc_init_wait_queue(&clp->cl_cb_waitq, "Backchannel slot table"); | 1309 | rpc_init_wait_queue(&clp->cl_cb_waitq, "Backchannel slot table"); |
@@ -1321,17 +1311,60 @@ static struct nfs4_client *create_client(struct xdr_netobj name, char *recdir, | |||
1321 | rpc_copy_addr((struct sockaddr *) &clp->cl_addr, sa); | 1311 | rpc_copy_addr((struct sockaddr *) &clp->cl_addr, sa); |
1322 | gen_confirm(clp); | 1312 | gen_confirm(clp); |
1323 | clp->cl_cb_session = NULL; | 1313 | clp->cl_cb_session = NULL; |
1314 | clp->net = net; | ||
1324 | return clp; | 1315 | return clp; |
1325 | } | 1316 | } |
1326 | 1317 | ||
1327 | static void | 1318 | static void |
1328 | add_to_unconfirmed(struct nfs4_client *clp, unsigned int strhashval) | 1319 | add_clp_to_name_tree(struct nfs4_client *new_clp, struct rb_root *root) |
1320 | { | ||
1321 | struct rb_node **new = &(root->rb_node), *parent = NULL; | ||
1322 | struct nfs4_client *clp; | ||
1323 | |||
1324 | while (*new) { | ||
1325 | clp = rb_entry(*new, struct nfs4_client, cl_namenode); | ||
1326 | parent = *new; | ||
1327 | |||
1328 | if (compare_blob(&clp->cl_name, &new_clp->cl_name) > 0) | ||
1329 | new = &((*new)->rb_left); | ||
1330 | else | ||
1331 | new = &((*new)->rb_right); | ||
1332 | } | ||
1333 | |||
1334 | rb_link_node(&new_clp->cl_namenode, parent, new); | ||
1335 | rb_insert_color(&new_clp->cl_namenode, root); | ||
1336 | } | ||
1337 | |||
1338 | static struct nfs4_client * | ||
1339 | find_clp_in_name_tree(struct xdr_netobj *name, struct rb_root *root) | ||
1340 | { | ||
1341 | long long cmp; | ||
1342 | struct rb_node *node = root->rb_node; | ||
1343 | struct nfs4_client *clp; | ||
1344 | |||
1345 | while (node) { | ||
1346 | clp = rb_entry(node, struct nfs4_client, cl_namenode); | ||
1347 | cmp = compare_blob(&clp->cl_name, name); | ||
1348 | if (cmp > 0) | ||
1349 | node = node->rb_left; | ||
1350 | else if (cmp < 0) | ||
1351 | node = node->rb_right; | ||
1352 | else | ||
1353 | return clp; | ||
1354 | } | ||
1355 | return NULL; | ||
1356 | } | ||
1357 | |||
1358 | static void | ||
1359 | add_to_unconfirmed(struct nfs4_client *clp) | ||
1329 | { | 1360 | { |
1330 | unsigned int idhashval; | 1361 | unsigned int idhashval; |
1362 | struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); | ||
1331 | 1363 | ||
1332 | list_add(&clp->cl_strhash, &unconf_str_hashtbl[strhashval]); | 1364 | clear_bit(NFSD4_CLIENT_CONFIRMED, &clp->cl_flags); |
1365 | add_clp_to_name_tree(clp, &nn->unconf_name_tree); | ||
1333 | idhashval = clientid_hashval(clp->cl_clientid.cl_id); | 1366 | idhashval = clientid_hashval(clp->cl_clientid.cl_id); |
1334 | list_add(&clp->cl_idhash, &unconf_id_hashtbl[idhashval]); | 1367 | list_add(&clp->cl_idhash, &nn->unconf_id_hashtbl[idhashval]); |
1335 | renew_client(clp); | 1368 | renew_client(clp); |
1336 | } | 1369 | } |
1337 | 1370 | ||
@@ -1339,22 +1372,23 @@ static void | |||
1339 | move_to_confirmed(struct nfs4_client *clp) | 1372 | move_to_confirmed(struct nfs4_client *clp) |
1340 | { | 1373 | { |
1341 | unsigned int idhashval = clientid_hashval(clp->cl_clientid.cl_id); | 1374 | unsigned int idhashval = clientid_hashval(clp->cl_clientid.cl_id); |
1342 | unsigned int strhashval; | 1375 | struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); |
1343 | 1376 | ||
1344 | dprintk("NFSD: move_to_confirm nfs4_client %p\n", clp); | 1377 | dprintk("NFSD: move_to_confirm nfs4_client %p\n", clp); |
1345 | list_move(&clp->cl_idhash, &conf_id_hashtbl[idhashval]); | 1378 | list_move(&clp->cl_idhash, &nn->conf_id_hashtbl[idhashval]); |
1346 | strhashval = clientstr_hashval(clp->cl_recdir); | 1379 | rb_erase(&clp->cl_namenode, &nn->unconf_name_tree); |
1347 | list_move(&clp->cl_strhash, &conf_str_hashtbl[strhashval]); | 1380 | add_clp_to_name_tree(clp, &nn->conf_name_tree); |
1381 | set_bit(NFSD4_CLIENT_CONFIRMED, &clp->cl_flags); | ||
1348 | renew_client(clp); | 1382 | renew_client(clp); |
1349 | } | 1383 | } |
1350 | 1384 | ||
1351 | static struct nfs4_client * | 1385 | static struct nfs4_client * |
1352 | find_confirmed_client(clientid_t *clid, bool sessions) | 1386 | find_confirmed_client(clientid_t *clid, bool sessions, struct nfsd_net *nn) |
1353 | { | 1387 | { |
1354 | struct nfs4_client *clp; | 1388 | struct nfs4_client *clp; |
1355 | unsigned int idhashval = clientid_hashval(clid->cl_id); | 1389 | unsigned int idhashval = clientid_hashval(clid->cl_id); |
1356 | 1390 | ||
1357 | list_for_each_entry(clp, &conf_id_hashtbl[idhashval], cl_idhash) { | 1391 | list_for_each_entry(clp, &nn->conf_id_hashtbl[idhashval], cl_idhash) { |
1358 | if (same_clid(&clp->cl_clientid, clid)) { | 1392 | if (same_clid(&clp->cl_clientid, clid)) { |
1359 | if ((bool)clp->cl_minorversion != sessions) | 1393 | if ((bool)clp->cl_minorversion != sessions) |
1360 | return NULL; | 1394 | return NULL; |
@@ -1366,12 +1400,12 @@ find_confirmed_client(clientid_t *clid, bool sessions) | |||
1366 | } | 1400 | } |
1367 | 1401 | ||
1368 | static struct nfs4_client * | 1402 | static struct nfs4_client * |
1369 | find_unconfirmed_client(clientid_t *clid, bool sessions) | 1403 | find_unconfirmed_client(clientid_t *clid, bool sessions, struct nfsd_net *nn) |
1370 | { | 1404 | { |
1371 | struct nfs4_client *clp; | 1405 | struct nfs4_client *clp; |
1372 | unsigned int idhashval = clientid_hashval(clid->cl_id); | 1406 | unsigned int idhashval = clientid_hashval(clid->cl_id); |
1373 | 1407 | ||
1374 | list_for_each_entry(clp, &unconf_id_hashtbl[idhashval], cl_idhash) { | 1408 | list_for_each_entry(clp, &nn->unconf_id_hashtbl[idhashval], cl_idhash) { |
1375 | if (same_clid(&clp->cl_clientid, clid)) { | 1409 | if (same_clid(&clp->cl_clientid, clid)) { |
1376 | if ((bool)clp->cl_minorversion != sessions) | 1410 | if ((bool)clp->cl_minorversion != sessions) |
1377 | return NULL; | 1411 | return NULL; |
@@ -1387,27 +1421,15 @@ static bool clp_used_exchangeid(struct nfs4_client *clp) | |||
1387 | } | 1421 | } |
1388 | 1422 | ||
1389 | static struct nfs4_client * | 1423 | static struct nfs4_client * |
1390 | find_confirmed_client_by_str(const char *dname, unsigned int hashval) | 1424 | find_confirmed_client_by_name(struct xdr_netobj *name, struct nfsd_net *nn) |
1391 | { | 1425 | { |
1392 | struct nfs4_client *clp; | 1426 | return find_clp_in_name_tree(name, &nn->conf_name_tree); |
1393 | |||
1394 | list_for_each_entry(clp, &conf_str_hashtbl[hashval], cl_strhash) { | ||
1395 | if (same_name(clp->cl_recdir, dname)) | ||
1396 | return clp; | ||
1397 | } | ||
1398 | return NULL; | ||
1399 | } | 1427 | } |
1400 | 1428 | ||
1401 | static struct nfs4_client * | 1429 | static struct nfs4_client * |
1402 | find_unconfirmed_client_by_str(const char *dname, unsigned int hashval) | 1430 | find_unconfirmed_client_by_name(struct xdr_netobj *name, struct nfsd_net *nn) |
1403 | { | 1431 | { |
1404 | struct nfs4_client *clp; | 1432 | return find_clp_in_name_tree(name, &nn->unconf_name_tree); |
1405 | |||
1406 | list_for_each_entry(clp, &unconf_str_hashtbl[hashval], cl_strhash) { | ||
1407 | if (same_name(clp->cl_recdir, dname)) | ||
1408 | return clp; | ||
1409 | } | ||
1410 | return NULL; | ||
1411 | } | 1433 | } |
1412 | 1434 | ||
1413 | static void | 1435 | static void |
@@ -1428,7 +1450,7 @@ gen_callback(struct nfs4_client *clp, struct nfsd4_setclientid *se, struct svc_r | |||
1428 | else | 1450 | else |
1429 | goto out_err; | 1451 | goto out_err; |
1430 | 1452 | ||
1431 | conn->cb_addrlen = rpc_uaddr2sockaddr(&init_net, se->se_callback_addr_val, | 1453 | conn->cb_addrlen = rpc_uaddr2sockaddr(clp->net, se->se_callback_addr_val, |
1432 | se->se_callback_addr_len, | 1454 | se->se_callback_addr_len, |
1433 | (struct sockaddr *)&conn->cb_addr, | 1455 | (struct sockaddr *)&conn->cb_addr, |
1434 | sizeof(conn->cb_addr)); | 1456 | sizeof(conn->cb_addr)); |
@@ -1572,12 +1594,11 @@ nfsd4_exchange_id(struct svc_rqst *rqstp, | |||
1572 | { | 1594 | { |
1573 | struct nfs4_client *unconf, *conf, *new; | 1595 | struct nfs4_client *unconf, *conf, *new; |
1574 | __be32 status; | 1596 | __be32 status; |
1575 | unsigned int strhashval; | ||
1576 | char dname[HEXDIR_LEN]; | ||
1577 | char addr_str[INET6_ADDRSTRLEN]; | 1597 | char addr_str[INET6_ADDRSTRLEN]; |
1578 | nfs4_verifier verf = exid->verifier; | 1598 | nfs4_verifier verf = exid->verifier; |
1579 | struct sockaddr *sa = svc_addr(rqstp); | 1599 | struct sockaddr *sa = svc_addr(rqstp); |
1580 | bool update = exid->flags & EXCHGID4_FLAG_UPD_CONFIRMED_REC_A; | 1600 | bool update = exid->flags & EXCHGID4_FLAG_UPD_CONFIRMED_REC_A; |
1601 | struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); | ||
1581 | 1602 | ||
1582 | rpc_ntop(sa, addr_str, sizeof(addr_str)); | 1603 | rpc_ntop(sa, addr_str, sizeof(addr_str)); |
1583 | dprintk("%s rqstp=%p exid=%p clname.len=%u clname.data=%p " | 1604 | dprintk("%s rqstp=%p exid=%p clname.len=%u clname.data=%p " |
@@ -1592,24 +1613,16 @@ nfsd4_exchange_id(struct svc_rqst *rqstp, | |||
1592 | switch (exid->spa_how) { | 1613 | switch (exid->spa_how) { |
1593 | case SP4_NONE: | 1614 | case SP4_NONE: |
1594 | break; | 1615 | break; |
1616 | default: /* checked by xdr code */ | ||
1617 | WARN_ON_ONCE(1); | ||
1595 | case SP4_SSV: | 1618 | case SP4_SSV: |
1596 | return nfserr_serverfault; | ||
1597 | default: | ||
1598 | BUG(); /* checked by xdr code */ | ||
1599 | case SP4_MACH_CRED: | 1619 | case SP4_MACH_CRED: |
1600 | return nfserr_serverfault; /* no excuse :-/ */ | 1620 | return nfserr_serverfault; /* no excuse :-/ */ |
1601 | } | 1621 | } |
1602 | 1622 | ||
1603 | status = nfs4_make_rec_clidname(dname, &exid->clname); | ||
1604 | |||
1605 | if (status) | ||
1606 | return status; | ||
1607 | |||
1608 | strhashval = clientstr_hashval(dname); | ||
1609 | |||
1610 | /* Cases below refer to rfc 5661 section 18.35.4: */ | 1623 | /* Cases below refer to rfc 5661 section 18.35.4: */ |
1611 | nfs4_lock_state(); | 1624 | nfs4_lock_state(); |
1612 | conf = find_confirmed_client_by_str(dname, strhashval); | 1625 | conf = find_confirmed_client_by_name(&exid->clname, nn); |
1613 | if (conf) { | 1626 | if (conf) { |
1614 | bool creds_match = same_creds(&conf->cl_cred, &rqstp->rq_cred); | 1627 | bool creds_match = same_creds(&conf->cl_cred, &rqstp->rq_cred); |
1615 | bool verfs_match = same_verf(&verf, &conf->cl_verifier); | 1628 | bool verfs_match = same_verf(&verf, &conf->cl_verifier); |
@@ -1654,21 +1667,21 @@ nfsd4_exchange_id(struct svc_rqst *rqstp, | |||
1654 | goto out; | 1667 | goto out; |
1655 | } | 1668 | } |
1656 | 1669 | ||
1657 | unconf = find_unconfirmed_client_by_str(dname, strhashval); | 1670 | unconf = find_unconfirmed_client_by_name(&exid->clname, nn); |
1658 | if (unconf) /* case 4, possible retry or client restart */ | 1671 | if (unconf) /* case 4, possible retry or client restart */ |
1659 | expire_client(unconf); | 1672 | expire_client(unconf); |
1660 | 1673 | ||
1661 | /* case 1 (normal case) */ | 1674 | /* case 1 (normal case) */ |
1662 | out_new: | 1675 | out_new: |
1663 | new = create_client(exid->clname, dname, rqstp, &verf); | 1676 | new = create_client(exid->clname, rqstp, &verf); |
1664 | if (new == NULL) { | 1677 | if (new == NULL) { |
1665 | status = nfserr_jukebox; | 1678 | status = nfserr_jukebox; |
1666 | goto out; | 1679 | goto out; |
1667 | } | 1680 | } |
1668 | new->cl_minorversion = 1; | 1681 | new->cl_minorversion = 1; |
1669 | 1682 | ||
1670 | gen_clid(new); | 1683 | gen_clid(new, nn); |
1671 | add_to_unconfirmed(new, strhashval); | 1684 | add_to_unconfirmed(new); |
1672 | out_copy: | 1685 | out_copy: |
1673 | exid->clientid.cl_boot = new->cl_clientid.cl_boot; | 1686 | exid->clientid.cl_boot = new->cl_clientid.cl_boot; |
1674 | exid->clientid.cl_id = new->cl_clientid.cl_id; | 1687 | exid->clientid.cl_id = new->cl_clientid.cl_id; |
@@ -1761,12 +1774,13 @@ nfsd4_create_session(struct svc_rqst *rqstp, | |||
1761 | struct nfsd4_conn *conn; | 1774 | struct nfsd4_conn *conn; |
1762 | struct nfsd4_clid_slot *cs_slot = NULL; | 1775 | struct nfsd4_clid_slot *cs_slot = NULL; |
1763 | __be32 status = 0; | 1776 | __be32 status = 0; |
1777 | struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); | ||
1764 | 1778 | ||
1765 | if (cr_ses->flags & ~SESSION4_FLAG_MASK_A) | 1779 | if (cr_ses->flags & ~SESSION4_FLAG_MASK_A) |
1766 | return nfserr_inval; | 1780 | return nfserr_inval; |
1767 | if (check_forechannel_attrs(cr_ses->fore_channel)) | 1781 | if (check_forechannel_attrs(cr_ses->fore_channel)) |
1768 | return nfserr_toosmall; | 1782 | return nfserr_toosmall; |
1769 | new = alloc_session(&cr_ses->fore_channel); | 1783 | new = alloc_session(&cr_ses->fore_channel, nn); |
1770 | if (!new) | 1784 | if (!new) |
1771 | return nfserr_jukebox; | 1785 | return nfserr_jukebox; |
1772 | status = nfserr_jukebox; | 1786 | status = nfserr_jukebox; |
@@ -1775,8 +1789,8 @@ nfsd4_create_session(struct svc_rqst *rqstp, | |||
1775 | goto out_free_session; | 1789 | goto out_free_session; |
1776 | 1790 | ||
1777 | nfs4_lock_state(); | 1791 | nfs4_lock_state(); |
1778 | unconf = find_unconfirmed_client(&cr_ses->clientid, true); | 1792 | unconf = find_unconfirmed_client(&cr_ses->clientid, true, nn); |
1779 | conf = find_confirmed_client(&cr_ses->clientid, true); | 1793 | conf = find_confirmed_client(&cr_ses->clientid, true, nn); |
1780 | 1794 | ||
1781 | if (conf) { | 1795 | if (conf) { |
1782 | cs_slot = &conf->cl_cs_slot; | 1796 | cs_slot = &conf->cl_cs_slot; |
@@ -1789,7 +1803,6 @@ nfsd4_create_session(struct svc_rqst *rqstp, | |||
1789 | goto out_free_conn; | 1803 | goto out_free_conn; |
1790 | } | 1804 | } |
1791 | } else if (unconf) { | 1805 | } else if (unconf) { |
1792 | unsigned int hash; | ||
1793 | struct nfs4_client *old; | 1806 | struct nfs4_client *old; |
1794 | if (!same_creds(&unconf->cl_cred, &rqstp->rq_cred) || | 1807 | if (!same_creds(&unconf->cl_cred, &rqstp->rq_cred) || |
1795 | !rpc_cmp_addr(sa, (struct sockaddr *) &unconf->cl_addr)) { | 1808 | !rpc_cmp_addr(sa, (struct sockaddr *) &unconf->cl_addr)) { |
@@ -1803,8 +1816,7 @@ nfsd4_create_session(struct svc_rqst *rqstp, | |||
1803 | status = nfserr_seq_misordered; | 1816 | status = nfserr_seq_misordered; |
1804 | goto out_free_conn; | 1817 | goto out_free_conn; |
1805 | } | 1818 | } |
1806 | hash = clientstr_hashval(unconf->cl_recdir); | 1819 | old = find_confirmed_client_by_name(&unconf->cl_name, nn); |
1807 | old = find_confirmed_client_by_str(unconf->cl_recdir, hash); | ||
1808 | if (old) | 1820 | if (old) |
1809 | expire_client(old); | 1821 | expire_client(old); |
1810 | move_to_confirmed(unconf); | 1822 | move_to_confirmed(unconf); |
@@ -1843,14 +1855,6 @@ out_free_session: | |||
1843 | goto out; | 1855 | goto out; |
1844 | } | 1856 | } |
1845 | 1857 | ||
1846 | static bool nfsd4_last_compound_op(struct svc_rqst *rqstp) | ||
1847 | { | ||
1848 | struct nfsd4_compoundres *resp = rqstp->rq_resp; | ||
1849 | struct nfsd4_compoundargs *argp = rqstp->rq_argp; | ||
1850 | |||
1851 | return argp->opcnt == resp->opcnt; | ||
1852 | } | ||
1853 | |||
1854 | static __be32 nfsd4_map_bcts_dir(u32 *dir) | 1858 | static __be32 nfsd4_map_bcts_dir(u32 *dir) |
1855 | { | 1859 | { |
1856 | switch (*dir) { | 1860 | switch (*dir) { |
@@ -1865,24 +1869,40 @@ static __be32 nfsd4_map_bcts_dir(u32 *dir) | |||
1865 | return nfserr_inval; | 1869 | return nfserr_inval; |
1866 | } | 1870 | } |
1867 | 1871 | ||
1872 | __be32 nfsd4_backchannel_ctl(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_backchannel_ctl *bc) | ||
1873 | { | ||
1874 | struct nfsd4_session *session = cstate->session; | ||
1875 | struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); | ||
1876 | |||
1877 | spin_lock(&nn->client_lock); | ||
1878 | session->se_cb_prog = bc->bc_cb_program; | ||
1879 | session->se_cb_sec = bc->bc_cb_sec; | ||
1880 | spin_unlock(&nn->client_lock); | ||
1881 | |||
1882 | nfsd4_probe_callback(session->se_client); | ||
1883 | |||
1884 | return nfs_ok; | ||
1885 | } | ||
1886 | |||
1868 | __be32 nfsd4_bind_conn_to_session(struct svc_rqst *rqstp, | 1887 | __be32 nfsd4_bind_conn_to_session(struct svc_rqst *rqstp, |
1869 | struct nfsd4_compound_state *cstate, | 1888 | struct nfsd4_compound_state *cstate, |
1870 | struct nfsd4_bind_conn_to_session *bcts) | 1889 | struct nfsd4_bind_conn_to_session *bcts) |
1871 | { | 1890 | { |
1872 | __be32 status; | 1891 | __be32 status; |
1873 | struct nfsd4_conn *conn; | 1892 | struct nfsd4_conn *conn; |
1893 | struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); | ||
1874 | 1894 | ||
1875 | if (!nfsd4_last_compound_op(rqstp)) | 1895 | if (!nfsd4_last_compound_op(rqstp)) |
1876 | return nfserr_not_only_op; | 1896 | return nfserr_not_only_op; |
1877 | spin_lock(&client_lock); | 1897 | spin_lock(&nn->client_lock); |
1878 | cstate->session = find_in_sessionid_hashtbl(&bcts->sessionid); | 1898 | cstate->session = find_in_sessionid_hashtbl(&bcts->sessionid, SVC_NET(rqstp)); |
1879 | /* Sorta weird: we only need the refcnt'ing because new_conn acquires | 1899 | /* Sorta weird: we only need the refcnt'ing because new_conn acquires |
1880 | * client_lock iself: */ | 1900 | * client_lock iself: */ |
1881 | if (cstate->session) { | 1901 | if (cstate->session) { |
1882 | nfsd4_get_session(cstate->session); | 1902 | nfsd4_get_session(cstate->session); |
1883 | atomic_inc(&cstate->session->se_client->cl_refcount); | 1903 | atomic_inc(&cstate->session->se_client->cl_refcount); |
1884 | } | 1904 | } |
1885 | spin_unlock(&client_lock); | 1905 | spin_unlock(&nn->client_lock); |
1886 | if (!cstate->session) | 1906 | if (!cstate->session) |
1887 | return nfserr_badsession; | 1907 | return nfserr_badsession; |
1888 | 1908 | ||
@@ -1910,6 +1930,7 @@ nfsd4_destroy_session(struct svc_rqst *r, | |||
1910 | { | 1930 | { |
1911 | struct nfsd4_session *ses; | 1931 | struct nfsd4_session *ses; |
1912 | __be32 status = nfserr_badsession; | 1932 | __be32 status = nfserr_badsession; |
1933 | struct nfsd_net *nn = net_generic(SVC_NET(r), nfsd_net_id); | ||
1913 | 1934 | ||
1914 | /* Notes: | 1935 | /* Notes: |
1915 | * - The confirmed nfs4_client->cl_sessionid holds destroyed sessinid | 1936 | * - The confirmed nfs4_client->cl_sessionid holds destroyed sessinid |
@@ -1923,24 +1944,24 @@ nfsd4_destroy_session(struct svc_rqst *r, | |||
1923 | return nfserr_not_only_op; | 1944 | return nfserr_not_only_op; |
1924 | } | 1945 | } |
1925 | dump_sessionid(__func__, &sessionid->sessionid); | 1946 | dump_sessionid(__func__, &sessionid->sessionid); |
1926 | spin_lock(&client_lock); | 1947 | spin_lock(&nn->client_lock); |
1927 | ses = find_in_sessionid_hashtbl(&sessionid->sessionid); | 1948 | ses = find_in_sessionid_hashtbl(&sessionid->sessionid, SVC_NET(r)); |
1928 | if (!ses) { | 1949 | if (!ses) { |
1929 | spin_unlock(&client_lock); | 1950 | spin_unlock(&nn->client_lock); |
1930 | goto out; | 1951 | goto out; |
1931 | } | 1952 | } |
1932 | 1953 | ||
1933 | unhash_session(ses); | 1954 | unhash_session(ses); |
1934 | spin_unlock(&client_lock); | 1955 | spin_unlock(&nn->client_lock); |
1935 | 1956 | ||
1936 | nfs4_lock_state(); | 1957 | nfs4_lock_state(); |
1937 | nfsd4_probe_callback_sync(ses->se_client); | 1958 | nfsd4_probe_callback_sync(ses->se_client); |
1938 | nfs4_unlock_state(); | 1959 | nfs4_unlock_state(); |
1939 | 1960 | ||
1940 | spin_lock(&client_lock); | 1961 | spin_lock(&nn->client_lock); |
1941 | nfsd4_del_conns(ses); | 1962 | nfsd4_del_conns(ses); |
1942 | nfsd4_put_session_locked(ses); | 1963 | nfsd4_put_session_locked(ses); |
1943 | spin_unlock(&client_lock); | 1964 | spin_unlock(&nn->client_lock); |
1944 | status = nfs_ok; | 1965 | status = nfs_ok; |
1945 | out: | 1966 | out: |
1946 | dprintk("%s returns %d\n", __func__, ntohl(status)); | 1967 | dprintk("%s returns %d\n", __func__, ntohl(status)); |
@@ -2006,6 +2027,7 @@ nfsd4_sequence(struct svc_rqst *rqstp, | |||
2006 | struct nfsd4_slot *slot; | 2027 | struct nfsd4_slot *slot; |
2007 | struct nfsd4_conn *conn; | 2028 | struct nfsd4_conn *conn; |
2008 | __be32 status; | 2029 | __be32 status; |
2030 | struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); | ||
2009 | 2031 | ||
2010 | if (resp->opcnt != 1) | 2032 | if (resp->opcnt != 1) |
2011 | return nfserr_sequence_pos; | 2033 | return nfserr_sequence_pos; |
@@ -2018,9 +2040,9 @@ nfsd4_sequence(struct svc_rqst *rqstp, | |||
2018 | if (!conn) | 2040 | if (!conn) |
2019 | return nfserr_jukebox; | 2041 | return nfserr_jukebox; |
2020 | 2042 | ||
2021 | spin_lock(&client_lock); | 2043 | spin_lock(&nn->client_lock); |
2022 | status = nfserr_badsession; | 2044 | status = nfserr_badsession; |
2023 | session = find_in_sessionid_hashtbl(&seq->sessionid); | 2045 | session = find_in_sessionid_hashtbl(&seq->sessionid, SVC_NET(rqstp)); |
2024 | if (!session) | 2046 | if (!session) |
2025 | goto out; | 2047 | goto out; |
2026 | 2048 | ||
@@ -2094,7 +2116,7 @@ out: | |||
2094 | } | 2116 | } |
2095 | } | 2117 | } |
2096 | kfree(conn); | 2118 | kfree(conn); |
2097 | spin_unlock(&client_lock); | 2119 | spin_unlock(&nn->client_lock); |
2098 | dprintk("%s: return %d\n", __func__, ntohl(status)); | 2120 | dprintk("%s: return %d\n", __func__, ntohl(status)); |
2099 | return status; | 2121 | return status; |
2100 | } | 2122 | } |
@@ -2104,10 +2126,11 @@ nfsd4_destroy_clientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *csta | |||
2104 | { | 2126 | { |
2105 | struct nfs4_client *conf, *unconf, *clp; | 2127 | struct nfs4_client *conf, *unconf, *clp; |
2106 | __be32 status = 0; | 2128 | __be32 status = 0; |
2129 | struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); | ||
2107 | 2130 | ||
2108 | nfs4_lock_state(); | 2131 | nfs4_lock_state(); |
2109 | unconf = find_unconfirmed_client(&dc->clientid, true); | 2132 | unconf = find_unconfirmed_client(&dc->clientid, true, nn); |
2110 | conf = find_confirmed_client(&dc->clientid, true); | 2133 | conf = find_confirmed_client(&dc->clientid, true, nn); |
2111 | 2134 | ||
2112 | if (conf) { | 2135 | if (conf) { |
2113 | clp = conf; | 2136 | clp = conf; |
@@ -2181,20 +2204,13 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
2181 | { | 2204 | { |
2182 | struct xdr_netobj clname = setclid->se_name; | 2205 | struct xdr_netobj clname = setclid->se_name; |
2183 | nfs4_verifier clverifier = setclid->se_verf; | 2206 | nfs4_verifier clverifier = setclid->se_verf; |
2184 | unsigned int strhashval; | ||
2185 | struct nfs4_client *conf, *unconf, *new; | 2207 | struct nfs4_client *conf, *unconf, *new; |
2186 | __be32 status; | 2208 | __be32 status; |
2187 | char dname[HEXDIR_LEN]; | 2209 | struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); |
2188 | |||
2189 | status = nfs4_make_rec_clidname(dname, &clname); | ||
2190 | if (status) | ||
2191 | return status; | ||
2192 | |||
2193 | strhashval = clientstr_hashval(dname); | ||
2194 | 2210 | ||
2195 | /* Cases below refer to rfc 3530 section 14.2.33: */ | 2211 | /* Cases below refer to rfc 3530 section 14.2.33: */ |
2196 | nfs4_lock_state(); | 2212 | nfs4_lock_state(); |
2197 | conf = find_confirmed_client_by_str(dname, strhashval); | 2213 | conf = find_confirmed_client_by_name(&clname, nn); |
2198 | if (conf) { | 2214 | if (conf) { |
2199 | /* case 0: */ | 2215 | /* case 0: */ |
2200 | status = nfserr_clid_inuse; | 2216 | status = nfserr_clid_inuse; |
@@ -2209,21 +2225,21 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
2209 | goto out; | 2225 | goto out; |
2210 | } | 2226 | } |
2211 | } | 2227 | } |
2212 | unconf = find_unconfirmed_client_by_str(dname, strhashval); | 2228 | unconf = find_unconfirmed_client_by_name(&clname, nn); |
2213 | if (unconf) | 2229 | if (unconf) |
2214 | expire_client(unconf); | 2230 | expire_client(unconf); |
2215 | status = nfserr_jukebox; | 2231 | status = nfserr_jukebox; |
2216 | new = create_client(clname, dname, rqstp, &clverifier); | 2232 | new = create_client(clname, rqstp, &clverifier); |
2217 | if (new == NULL) | 2233 | if (new == NULL) |
2218 | goto out; | 2234 | goto out; |
2219 | if (conf && same_verf(&conf->cl_verifier, &clverifier)) | 2235 | if (conf && same_verf(&conf->cl_verifier, &clverifier)) |
2220 | /* case 1: probable callback update */ | 2236 | /* case 1: probable callback update */ |
2221 | copy_clid(new, conf); | 2237 | copy_clid(new, conf); |
2222 | else /* case 4 (new client) or cases 2, 3 (client reboot): */ | 2238 | else /* case 4 (new client) or cases 2, 3 (client reboot): */ |
2223 | gen_clid(new); | 2239 | gen_clid(new, nn); |
2224 | new->cl_minorversion = 0; | 2240 | new->cl_minorversion = 0; |
2225 | gen_callback(new, setclid, rqstp); | 2241 | gen_callback(new, setclid, rqstp); |
2226 | add_to_unconfirmed(new, strhashval); | 2242 | add_to_unconfirmed(new); |
2227 | setclid->se_clientid.cl_boot = new->cl_clientid.cl_boot; | 2243 | setclid->se_clientid.cl_boot = new->cl_clientid.cl_boot; |
2228 | setclid->se_clientid.cl_id = new->cl_clientid.cl_id; | 2244 | setclid->se_clientid.cl_id = new->cl_clientid.cl_id; |
2229 | memcpy(setclid->se_confirm.data, new->cl_confirm.data, sizeof(setclid->se_confirm.data)); | 2245 | memcpy(setclid->se_confirm.data, new->cl_confirm.data, sizeof(setclid->se_confirm.data)); |
@@ -2243,14 +2259,14 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp, | |||
2243 | nfs4_verifier confirm = setclientid_confirm->sc_confirm; | 2259 | nfs4_verifier confirm = setclientid_confirm->sc_confirm; |
2244 | clientid_t * clid = &setclientid_confirm->sc_clientid; | 2260 | clientid_t * clid = &setclientid_confirm->sc_clientid; |
2245 | __be32 status; | 2261 | __be32 status; |
2246 | struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); | 2262 | struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); |
2247 | 2263 | ||
2248 | if (STALE_CLIENTID(clid, nn)) | 2264 | if (STALE_CLIENTID(clid, nn)) |
2249 | return nfserr_stale_clientid; | 2265 | return nfserr_stale_clientid; |
2250 | nfs4_lock_state(); | 2266 | nfs4_lock_state(); |
2251 | 2267 | ||
2252 | conf = find_confirmed_client(clid, false); | 2268 | conf = find_confirmed_client(clid, false, nn); |
2253 | unconf = find_unconfirmed_client(clid, false); | 2269 | unconf = find_unconfirmed_client(clid, false, nn); |
2254 | /* | 2270 | /* |
2255 | * We try hard to give out unique clientid's, so if we get an | 2271 | * We try hard to give out unique clientid's, so if we get an |
2256 | * attempt to confirm the same clientid with a different cred, | 2272 | * attempt to confirm the same clientid with a different cred, |
@@ -2276,9 +2292,7 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp, | |||
2276 | nfsd4_probe_callback(conf); | 2292 | nfsd4_probe_callback(conf); |
2277 | expire_client(unconf); | 2293 | expire_client(unconf); |
2278 | } else { /* case 3: normal case; new or rebooted client */ | 2294 | } else { /* case 3: normal case; new or rebooted client */ |
2279 | unsigned int hash = clientstr_hashval(unconf->cl_recdir); | 2295 | conf = find_confirmed_client_by_name(&unconf->cl_name, nn); |
2280 | |||
2281 | conf = find_confirmed_client_by_str(unconf->cl_recdir, hash); | ||
2282 | if (conf) | 2296 | if (conf) |
2283 | expire_client(conf); | 2297 | expire_client(conf); |
2284 | move_to_confirmed(unconf); | 2298 | move_to_confirmed(unconf); |
@@ -2340,7 +2354,7 @@ nfsd4_init_slabs(void) | |||
2340 | if (openowner_slab == NULL) | 2354 | if (openowner_slab == NULL) |
2341 | goto out_nomem; | 2355 | goto out_nomem; |
2342 | lockowner_slab = kmem_cache_create("nfsd4_lockowners", | 2356 | lockowner_slab = kmem_cache_create("nfsd4_lockowners", |
2343 | sizeof(struct nfs4_openowner), 0, 0, NULL); | 2357 | sizeof(struct nfs4_lockowner), 0, 0, NULL); |
2344 | if (lockowner_slab == NULL) | 2358 | if (lockowner_slab == NULL) |
2345 | goto out_nomem; | 2359 | goto out_nomem; |
2346 | file_slab = kmem_cache_create("nfsd4_files", | 2360 | file_slab = kmem_cache_create("nfsd4_files", |
@@ -2404,7 +2418,9 @@ static inline void *alloc_stateowner(struct kmem_cache *slab, struct xdr_netobj | |||
2404 | 2418 | ||
2405 | static void hash_openowner(struct nfs4_openowner *oo, struct nfs4_client *clp, unsigned int strhashval) | 2419 | static void hash_openowner(struct nfs4_openowner *oo, struct nfs4_client *clp, unsigned int strhashval) |
2406 | { | 2420 | { |
2407 | list_add(&oo->oo_owner.so_strhash, &ownerstr_hashtbl[strhashval]); | 2421 | struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); |
2422 | |||
2423 | list_add(&oo->oo_owner.so_strhash, &nn->ownerstr_hashtbl[strhashval]); | ||
2408 | list_add(&oo->oo_perclient, &clp->cl_openowners); | 2424 | list_add(&oo->oo_perclient, &clp->cl_openowners); |
2409 | } | 2425 | } |
2410 | 2426 | ||
@@ -2444,11 +2460,13 @@ static void init_open_stateid(struct nfs4_ol_stateid *stp, struct nfs4_file *fp, | |||
2444 | } | 2460 | } |
2445 | 2461 | ||
2446 | static void | 2462 | static void |
2447 | move_to_close_lru(struct nfs4_openowner *oo) | 2463 | move_to_close_lru(struct nfs4_openowner *oo, struct net *net) |
2448 | { | 2464 | { |
2465 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); | ||
2466 | |||
2449 | dprintk("NFSD: move_to_close_lru nfs4_openowner %p\n", oo); | 2467 | dprintk("NFSD: move_to_close_lru nfs4_openowner %p\n", oo); |
2450 | 2468 | ||
2451 | list_move_tail(&oo->oo_close_lru, &close_lru); | 2469 | list_move_tail(&oo->oo_close_lru, &nn->close_lru); |
2452 | oo->oo_time = get_seconds(); | 2470 | oo->oo_time = get_seconds(); |
2453 | } | 2471 | } |
2454 | 2472 | ||
@@ -2462,13 +2480,14 @@ same_owner_str(struct nfs4_stateowner *sop, struct xdr_netobj *owner, | |||
2462 | } | 2480 | } |
2463 | 2481 | ||
2464 | static struct nfs4_openowner * | 2482 | static struct nfs4_openowner * |
2465 | find_openstateowner_str(unsigned int hashval, struct nfsd4_open *open, bool sessions) | 2483 | find_openstateowner_str(unsigned int hashval, struct nfsd4_open *open, |
2484 | bool sessions, struct nfsd_net *nn) | ||
2466 | { | 2485 | { |
2467 | struct nfs4_stateowner *so; | 2486 | struct nfs4_stateowner *so; |
2468 | struct nfs4_openowner *oo; | 2487 | struct nfs4_openowner *oo; |
2469 | struct nfs4_client *clp; | 2488 | struct nfs4_client *clp; |
2470 | 2489 | ||
2471 | list_for_each_entry(so, &ownerstr_hashtbl[hashval], so_strhash) { | 2490 | list_for_each_entry(so, &nn->ownerstr_hashtbl[hashval], so_strhash) { |
2472 | if (!so->so_is_open_owner) | 2491 | if (!so->so_is_open_owner) |
2473 | continue; | 2492 | continue; |
2474 | if (same_owner_str(so, &open->op_owner, &open->op_clientid)) { | 2493 | if (same_owner_str(so, &open->op_owner, &open->op_clientid)) { |
@@ -2555,9 +2574,14 @@ static void nfsd_break_deleg_cb(struct file_lock *fl) | |||
2555 | struct nfs4_file *fp = (struct nfs4_file *)fl->fl_owner; | 2574 | struct nfs4_file *fp = (struct nfs4_file *)fl->fl_owner; |
2556 | struct nfs4_delegation *dp; | 2575 | struct nfs4_delegation *dp; |
2557 | 2576 | ||
2558 | BUG_ON(!fp); | 2577 | if (!fp) { |
2559 | /* We assume break_lease is only called once per lease: */ | 2578 | WARN(1, "(%p)->fl_owner NULL\n", fl); |
2560 | BUG_ON(fp->fi_had_conflict); | 2579 | return; |
2580 | } | ||
2581 | if (fp->fi_had_conflict) { | ||
2582 | WARN(1, "duplicate break on %p\n", fp); | ||
2583 | return; | ||
2584 | } | ||
2561 | /* | 2585 | /* |
2562 | * We don't want the locks code to timeout the lease for us; | 2586 | * We don't want the locks code to timeout the lease for us; |
2563 | * we'll remove it ourself if a delegation isn't returned | 2587 | * we'll remove it ourself if a delegation isn't returned |
@@ -2599,14 +2623,13 @@ static __be32 nfsd4_check_seqid(struct nfsd4_compound_state *cstate, struct nfs4 | |||
2599 | 2623 | ||
2600 | __be32 | 2624 | __be32 |
2601 | nfsd4_process_open1(struct nfsd4_compound_state *cstate, | 2625 | nfsd4_process_open1(struct nfsd4_compound_state *cstate, |
2602 | struct nfsd4_open *open) | 2626 | struct nfsd4_open *open, struct nfsd_net *nn) |
2603 | { | 2627 | { |
2604 | clientid_t *clientid = &open->op_clientid; | 2628 | clientid_t *clientid = &open->op_clientid; |
2605 | struct nfs4_client *clp = NULL; | 2629 | struct nfs4_client *clp = NULL; |
2606 | unsigned int strhashval; | 2630 | unsigned int strhashval; |
2607 | struct nfs4_openowner *oo = NULL; | 2631 | struct nfs4_openowner *oo = NULL; |
2608 | __be32 status; | 2632 | __be32 status; |
2609 | struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); | ||
2610 | 2633 | ||
2611 | if (STALE_CLIENTID(&open->op_clientid, nn)) | 2634 | if (STALE_CLIENTID(&open->op_clientid, nn)) |
2612 | return nfserr_stale_clientid; | 2635 | return nfserr_stale_clientid; |
@@ -2619,10 +2642,11 @@ nfsd4_process_open1(struct nfsd4_compound_state *cstate, | |||
2619 | return nfserr_jukebox; | 2642 | return nfserr_jukebox; |
2620 | 2643 | ||
2621 | strhashval = ownerstr_hashval(clientid->cl_id, &open->op_owner); | 2644 | strhashval = ownerstr_hashval(clientid->cl_id, &open->op_owner); |
2622 | oo = find_openstateowner_str(strhashval, open, cstate->minorversion); | 2645 | oo = find_openstateowner_str(strhashval, open, cstate->minorversion, nn); |
2623 | open->op_openowner = oo; | 2646 | open->op_openowner = oo; |
2624 | if (!oo) { | 2647 | if (!oo) { |
2625 | clp = find_confirmed_client(clientid, cstate->minorversion); | 2648 | clp = find_confirmed_client(clientid, cstate->minorversion, |
2649 | nn); | ||
2626 | if (clp == NULL) | 2650 | if (clp == NULL) |
2627 | return nfserr_expired; | 2651 | return nfserr_expired; |
2628 | goto new_owner; | 2652 | goto new_owner; |
@@ -2891,7 +2915,7 @@ static void nfsd4_open_deleg_none_ext(struct nfsd4_open *open, int status) | |||
2891 | open->op_why_no_deleg = WND4_CANCELLED; | 2915 | open->op_why_no_deleg = WND4_CANCELLED; |
2892 | break; | 2916 | break; |
2893 | case NFS4_SHARE_WANT_NO_DELEG: | 2917 | case NFS4_SHARE_WANT_NO_DELEG: |
2894 | BUG(); /* not supposed to get here */ | 2918 | WARN_ON_ONCE(1); |
2895 | } | 2919 | } |
2896 | } | 2920 | } |
2897 | } | 2921 | } |
@@ -2959,6 +2983,7 @@ out: | |||
2959 | } | 2983 | } |
2960 | return; | 2984 | return; |
2961 | out_free: | 2985 | out_free: |
2986 | unhash_stid(&dp->dl_stid); | ||
2962 | nfs4_put_delegation(dp); | 2987 | nfs4_put_delegation(dp); |
2963 | out_no_deleg: | 2988 | out_no_deleg: |
2964 | flag = NFS4_OPEN_DELEGATE_NONE; | 2989 | flag = NFS4_OPEN_DELEGATE_NONE; |
@@ -3104,27 +3129,32 @@ void nfsd4_cleanup_open_state(struct nfsd4_open *open, __be32 status) | |||
3104 | free_generic_stateid(open->op_stp); | 3129 | free_generic_stateid(open->op_stp); |
3105 | } | 3130 | } |
3106 | 3131 | ||
3132 | static __be32 lookup_clientid(clientid_t *clid, bool session, struct nfsd_net *nn, struct nfs4_client **clp) | ||
3133 | { | ||
3134 | struct nfs4_client *found; | ||
3135 | |||
3136 | if (STALE_CLIENTID(clid, nn)) | ||
3137 | return nfserr_stale_clientid; | ||
3138 | found = find_confirmed_client(clid, session, nn); | ||
3139 | if (clp) | ||
3140 | *clp = found; | ||
3141 | return found ? nfs_ok : nfserr_expired; | ||
3142 | } | ||
3143 | |||
3107 | __be32 | 3144 | __be32 |
3108 | nfsd4_renew(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | 3145 | nfsd4_renew(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, |
3109 | clientid_t *clid) | 3146 | clientid_t *clid) |
3110 | { | 3147 | { |
3111 | struct nfs4_client *clp; | 3148 | struct nfs4_client *clp; |
3112 | __be32 status; | 3149 | __be32 status; |
3113 | struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); | 3150 | struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); |
3114 | 3151 | ||
3115 | nfs4_lock_state(); | 3152 | nfs4_lock_state(); |
3116 | dprintk("process_renew(%08x/%08x): starting\n", | 3153 | dprintk("process_renew(%08x/%08x): starting\n", |
3117 | clid->cl_boot, clid->cl_id); | 3154 | clid->cl_boot, clid->cl_id); |
3118 | status = nfserr_stale_clientid; | 3155 | status = lookup_clientid(clid, cstate->minorversion, nn, &clp); |
3119 | if (STALE_CLIENTID(clid, nn)) | 3156 | if (status) |
3120 | goto out; | ||
3121 | clp = find_confirmed_client(clid, cstate->minorversion); | ||
3122 | status = nfserr_expired; | ||
3123 | if (clp == NULL) { | ||
3124 | /* We assume the client took too long to RENEW. */ | ||
3125 | dprintk("nfsd4_renew: clientid not found!\n"); | ||
3126 | goto out; | 3157 | goto out; |
3127 | } | ||
3128 | status = nfserr_cb_path_down; | 3158 | status = nfserr_cb_path_down; |
3129 | if (!list_empty(&clp->cl_delegations) | 3159 | if (!list_empty(&clp->cl_delegations) |
3130 | && clp->cl_cb_state != NFSD4_CB_UP) | 3160 | && clp->cl_cb_state != NFSD4_CB_UP) |
@@ -3136,44 +3166,42 @@ out: | |||
3136 | } | 3166 | } |
3137 | 3167 | ||
3138 | static void | 3168 | static void |
3139 | nfsd4_end_grace(struct net *net) | 3169 | nfsd4_end_grace(struct nfsd_net *nn) |
3140 | { | 3170 | { |
3141 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); | ||
3142 | |||
3143 | /* do nothing if grace period already ended */ | 3171 | /* do nothing if grace period already ended */ |
3144 | if (nn->grace_ended) | 3172 | if (nn->grace_ended) |
3145 | return; | 3173 | return; |
3146 | 3174 | ||
3147 | dprintk("NFSD: end of grace period\n"); | 3175 | dprintk("NFSD: end of grace period\n"); |
3148 | nn->grace_ended = true; | 3176 | nn->grace_ended = true; |
3149 | nfsd4_record_grace_done(net, nn->boot_time); | 3177 | nfsd4_record_grace_done(nn, nn->boot_time); |
3150 | locks_end_grace(&nn->nfsd4_manager); | 3178 | locks_end_grace(&nn->nfsd4_manager); |
3151 | /* | 3179 | /* |
3152 | * Now that every NFSv4 client has had the chance to recover and | 3180 | * Now that every NFSv4 client has had the chance to recover and |
3153 | * to see the (possibly new, possibly shorter) lease time, we | 3181 | * to see the (possibly new, possibly shorter) lease time, we |
3154 | * can safely set the next grace time to the current lease time: | 3182 | * can safely set the next grace time to the current lease time: |
3155 | */ | 3183 | */ |
3156 | nfsd4_grace = nfsd4_lease; | 3184 | nn->nfsd4_grace = nn->nfsd4_lease; |
3157 | } | 3185 | } |
3158 | 3186 | ||
3159 | static time_t | 3187 | static time_t |
3160 | nfs4_laundromat(void) | 3188 | nfs4_laundromat(struct nfsd_net *nn) |
3161 | { | 3189 | { |
3162 | struct nfs4_client *clp; | 3190 | struct nfs4_client *clp; |
3163 | struct nfs4_openowner *oo; | 3191 | struct nfs4_openowner *oo; |
3164 | struct nfs4_delegation *dp; | 3192 | struct nfs4_delegation *dp; |
3165 | struct list_head *pos, *next, reaplist; | 3193 | struct list_head *pos, *next, reaplist; |
3166 | time_t cutoff = get_seconds() - nfsd4_lease; | 3194 | time_t cutoff = get_seconds() - nn->nfsd4_lease; |
3167 | time_t t, clientid_val = nfsd4_lease; | 3195 | time_t t, clientid_val = nn->nfsd4_lease; |
3168 | time_t u, test_val = nfsd4_lease; | 3196 | time_t u, test_val = nn->nfsd4_lease; |
3169 | 3197 | ||
3170 | nfs4_lock_state(); | 3198 | nfs4_lock_state(); |
3171 | 3199 | ||
3172 | dprintk("NFSD: laundromat service - starting\n"); | 3200 | dprintk("NFSD: laundromat service - starting\n"); |
3173 | nfsd4_end_grace(&init_net); | 3201 | nfsd4_end_grace(nn); |
3174 | INIT_LIST_HEAD(&reaplist); | 3202 | INIT_LIST_HEAD(&reaplist); |
3175 | spin_lock(&client_lock); | 3203 | spin_lock(&nn->client_lock); |
3176 | list_for_each_safe(pos, next, &client_lru) { | 3204 | list_for_each_safe(pos, next, &nn->client_lru) { |
3177 | clp = list_entry(pos, struct nfs4_client, cl_lru); | 3205 | clp = list_entry(pos, struct nfs4_client, cl_lru); |
3178 | if (time_after((unsigned long)clp->cl_time, (unsigned long)cutoff)) { | 3206 | if (time_after((unsigned long)clp->cl_time, (unsigned long)cutoff)) { |
3179 | t = clp->cl_time - cutoff; | 3207 | t = clp->cl_time - cutoff; |
@@ -3189,7 +3217,7 @@ nfs4_laundromat(void) | |||
3189 | unhash_client_locked(clp); | 3217 | unhash_client_locked(clp); |
3190 | list_add(&clp->cl_lru, &reaplist); | 3218 | list_add(&clp->cl_lru, &reaplist); |
3191 | } | 3219 | } |
3192 | spin_unlock(&client_lock); | 3220 | spin_unlock(&nn->client_lock); |
3193 | list_for_each_safe(pos, next, &reaplist) { | 3221 | list_for_each_safe(pos, next, &reaplist) { |
3194 | clp = list_entry(pos, struct nfs4_client, cl_lru); | 3222 | clp = list_entry(pos, struct nfs4_client, cl_lru); |
3195 | dprintk("NFSD: purging unused client (clientid %08x)\n", | 3223 | dprintk("NFSD: purging unused client (clientid %08x)\n", |
@@ -3199,6 +3227,8 @@ nfs4_laundromat(void) | |||
3199 | spin_lock(&recall_lock); | 3227 | spin_lock(&recall_lock); |
3200 | list_for_each_safe(pos, next, &del_recall_lru) { | 3228 | list_for_each_safe(pos, next, &del_recall_lru) { |
3201 | dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru); | 3229 | dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru); |
3230 | if (net_generic(dp->dl_stid.sc_client->net, nfsd_net_id) != nn) | ||
3231 | continue; | ||
3202 | if (time_after((unsigned long)dp->dl_time, (unsigned long)cutoff)) { | 3232 | if (time_after((unsigned long)dp->dl_time, (unsigned long)cutoff)) { |
3203 | u = dp->dl_time - cutoff; | 3233 | u = dp->dl_time - cutoff; |
3204 | if (test_val > u) | 3234 | if (test_val > u) |
@@ -3212,8 +3242,8 @@ nfs4_laundromat(void) | |||
3212 | dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru); | 3242 | dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru); |
3213 | unhash_delegation(dp); | 3243 | unhash_delegation(dp); |
3214 | } | 3244 | } |
3215 | test_val = nfsd4_lease; | 3245 | test_val = nn->nfsd4_lease; |
3216 | list_for_each_safe(pos, next, &close_lru) { | 3246 | list_for_each_safe(pos, next, &nn->close_lru) { |
3217 | oo = container_of(pos, struct nfs4_openowner, oo_close_lru); | 3247 | oo = container_of(pos, struct nfs4_openowner, oo_close_lru); |
3218 | if (time_after((unsigned long)oo->oo_time, (unsigned long)cutoff)) { | 3248 | if (time_after((unsigned long)oo->oo_time, (unsigned long)cutoff)) { |
3219 | u = oo->oo_time - cutoff; | 3249 | u = oo->oo_time - cutoff; |
@@ -3231,16 +3261,19 @@ nfs4_laundromat(void) | |||
3231 | 3261 | ||
3232 | static struct workqueue_struct *laundry_wq; | 3262 | static struct workqueue_struct *laundry_wq; |
3233 | static void laundromat_main(struct work_struct *); | 3263 | static void laundromat_main(struct work_struct *); |
3234 | static DECLARE_DELAYED_WORK(laundromat_work, laundromat_main); | ||
3235 | 3264 | ||
3236 | static void | 3265 | static void |
3237 | laundromat_main(struct work_struct *not_used) | 3266 | laundromat_main(struct work_struct *laundry) |
3238 | { | 3267 | { |
3239 | time_t t; | 3268 | time_t t; |
3269 | struct delayed_work *dwork = container_of(laundry, struct delayed_work, | ||
3270 | work); | ||
3271 | struct nfsd_net *nn = container_of(dwork, struct nfsd_net, | ||
3272 | laundromat_work); | ||
3240 | 3273 | ||
3241 | t = nfs4_laundromat(); | 3274 | t = nfs4_laundromat(nn); |
3242 | dprintk("NFSD: laundromat_main - sleeping for %ld seconds\n", t); | 3275 | dprintk("NFSD: laundromat_main - sleeping for %ld seconds\n", t); |
3243 | queue_delayed_work(laundry_wq, &laundromat_work, t*HZ); | 3276 | queue_delayed_work(laundry_wq, &nn->laundromat_work, t*HZ); |
3244 | } | 3277 | } |
3245 | 3278 | ||
3246 | static inline __be32 nfs4_check_fh(struct svc_fh *fhp, struct nfs4_ol_stateid *stp) | 3279 | static inline __be32 nfs4_check_fh(struct svc_fh *fhp, struct nfs4_ol_stateid *stp) |
@@ -3385,16 +3418,17 @@ static __be32 nfsd4_validate_stateid(struct nfs4_client *cl, stateid_t *stateid) | |||
3385 | return nfs_ok; | 3418 | return nfs_ok; |
3386 | } | 3419 | } |
3387 | 3420 | ||
3388 | static __be32 nfsd4_lookup_stateid(stateid_t *stateid, unsigned char typemask, struct nfs4_stid **s, bool sessions) | 3421 | static __be32 nfsd4_lookup_stateid(stateid_t *stateid, unsigned char typemask, |
3422 | struct nfs4_stid **s, bool sessions, | ||
3423 | struct nfsd_net *nn) | ||
3389 | { | 3424 | { |
3390 | struct nfs4_client *cl; | 3425 | struct nfs4_client *cl; |
3391 | struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); | ||
3392 | 3426 | ||
3393 | if (ZERO_STATEID(stateid) || ONE_STATEID(stateid)) | 3427 | if (ZERO_STATEID(stateid) || ONE_STATEID(stateid)) |
3394 | return nfserr_bad_stateid; | 3428 | return nfserr_bad_stateid; |
3395 | if (STALE_STATEID(stateid, nn)) | 3429 | if (STALE_STATEID(stateid, nn)) |
3396 | return nfserr_stale_stateid; | 3430 | return nfserr_stale_stateid; |
3397 | cl = find_confirmed_client(&stateid->si_opaque.so_clid, sessions); | 3431 | cl = find_confirmed_client(&stateid->si_opaque.so_clid, sessions, nn); |
3398 | if (!cl) | 3432 | if (!cl) |
3399 | return nfserr_expired; | 3433 | return nfserr_expired; |
3400 | *s = find_stateid_by_type(cl, stateid, typemask); | 3434 | *s = find_stateid_by_type(cl, stateid, typemask); |
@@ -3416,6 +3450,7 @@ nfs4_preprocess_stateid_op(struct net *net, struct nfsd4_compound_state *cstate, | |||
3416 | struct nfs4_delegation *dp = NULL; | 3450 | struct nfs4_delegation *dp = NULL; |
3417 | struct svc_fh *current_fh = &cstate->current_fh; | 3451 | struct svc_fh *current_fh = &cstate->current_fh; |
3418 | struct inode *ino = current_fh->fh_dentry->d_inode; | 3452 | struct inode *ino = current_fh->fh_dentry->d_inode; |
3453 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); | ||
3419 | __be32 status; | 3454 | __be32 status; |
3420 | 3455 | ||
3421 | if (filpp) | 3456 | if (filpp) |
@@ -3427,7 +3462,8 @@ nfs4_preprocess_stateid_op(struct net *net, struct nfsd4_compound_state *cstate, | |||
3427 | if (ZERO_STATEID(stateid) || ONE_STATEID(stateid)) | 3462 | if (ZERO_STATEID(stateid) || ONE_STATEID(stateid)) |
3428 | return check_special_stateids(net, current_fh, stateid, flags); | 3463 | return check_special_stateids(net, current_fh, stateid, flags); |
3429 | 3464 | ||
3430 | status = nfsd4_lookup_stateid(stateid, NFS4_DELEG_STID|NFS4_OPEN_STID|NFS4_LOCK_STID, &s, cstate->minorversion); | 3465 | status = nfsd4_lookup_stateid(stateid, NFS4_DELEG_STID|NFS4_OPEN_STID|NFS4_LOCK_STID, |
3466 | &s, cstate->minorversion, nn); | ||
3431 | if (status) | 3467 | if (status) |
3432 | return status; | 3468 | return status; |
3433 | status = check_stateid_generation(stateid, &s->sc_stateid, nfsd4_has_session(cstate)); | 3469 | status = check_stateid_generation(stateid, &s->sc_stateid, nfsd4_has_session(cstate)); |
@@ -3441,7 +3477,11 @@ nfs4_preprocess_stateid_op(struct net *net, struct nfsd4_compound_state *cstate, | |||
3441 | goto out; | 3477 | goto out; |
3442 | if (filpp) { | 3478 | if (filpp) { |
3443 | *filpp = dp->dl_file->fi_deleg_file; | 3479 | *filpp = dp->dl_file->fi_deleg_file; |
3444 | BUG_ON(!*filpp); | 3480 | if (!*filpp) { |
3481 | WARN_ON_ONCE(1); | ||
3482 | status = nfserr_serverfault; | ||
3483 | goto out; | ||
3484 | } | ||
3445 | } | 3485 | } |
3446 | break; | 3486 | break; |
3447 | case NFS4_OPEN_STID: | 3487 | case NFS4_OPEN_STID: |
@@ -3568,7 +3608,8 @@ static __be32 nfs4_seqid_op_checks(struct nfsd4_compound_state *cstate, stateid_ | |||
3568 | static __be32 | 3608 | static __be32 |
3569 | nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid, | 3609 | nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid, |
3570 | stateid_t *stateid, char typemask, | 3610 | stateid_t *stateid, char typemask, |
3571 | struct nfs4_ol_stateid **stpp) | 3611 | struct nfs4_ol_stateid **stpp, |
3612 | struct nfsd_net *nn) | ||
3572 | { | 3613 | { |
3573 | __be32 status; | 3614 | __be32 status; |
3574 | struct nfs4_stid *s; | 3615 | struct nfs4_stid *s; |
@@ -3577,7 +3618,8 @@ nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid, | |||
3577 | seqid, STATEID_VAL(stateid)); | 3618 | seqid, STATEID_VAL(stateid)); |
3578 | 3619 | ||
3579 | *stpp = NULL; | 3620 | *stpp = NULL; |
3580 | status = nfsd4_lookup_stateid(stateid, typemask, &s, cstate->minorversion); | 3621 | status = nfsd4_lookup_stateid(stateid, typemask, &s, |
3622 | cstate->minorversion, nn); | ||
3581 | if (status) | 3623 | if (status) |
3582 | return status; | 3624 | return status; |
3583 | *stpp = openlockstateid(s); | 3625 | *stpp = openlockstateid(s); |
@@ -3586,13 +3628,14 @@ nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid, | |||
3586 | return nfs4_seqid_op_checks(cstate, stateid, seqid, *stpp); | 3628 | return nfs4_seqid_op_checks(cstate, stateid, seqid, *stpp); |
3587 | } | 3629 | } |
3588 | 3630 | ||
3589 | static __be32 nfs4_preprocess_confirmed_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid, stateid_t *stateid, struct nfs4_ol_stateid **stpp) | 3631 | static __be32 nfs4_preprocess_confirmed_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid, |
3632 | stateid_t *stateid, struct nfs4_ol_stateid **stpp, struct nfsd_net *nn) | ||
3590 | { | 3633 | { |
3591 | __be32 status; | 3634 | __be32 status; |
3592 | struct nfs4_openowner *oo; | 3635 | struct nfs4_openowner *oo; |
3593 | 3636 | ||
3594 | status = nfs4_preprocess_seqid_op(cstate, seqid, stateid, | 3637 | status = nfs4_preprocess_seqid_op(cstate, seqid, stateid, |
3595 | NFS4_OPEN_STID, stpp); | 3638 | NFS4_OPEN_STID, stpp, nn); |
3596 | if (status) | 3639 | if (status) |
3597 | return status; | 3640 | return status; |
3598 | oo = openowner((*stpp)->st_stateowner); | 3641 | oo = openowner((*stpp)->st_stateowner); |
@@ -3608,6 +3651,7 @@ nfsd4_open_confirm(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
3608 | __be32 status; | 3651 | __be32 status; |
3609 | struct nfs4_openowner *oo; | 3652 | struct nfs4_openowner *oo; |
3610 | struct nfs4_ol_stateid *stp; | 3653 | struct nfs4_ol_stateid *stp; |
3654 | struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); | ||
3611 | 3655 | ||
3612 | dprintk("NFSD: nfsd4_open_confirm on file %.*s\n", | 3656 | dprintk("NFSD: nfsd4_open_confirm on file %.*s\n", |
3613 | (int)cstate->current_fh.fh_dentry->d_name.len, | 3657 | (int)cstate->current_fh.fh_dentry->d_name.len, |
@@ -3621,7 +3665,7 @@ nfsd4_open_confirm(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
3621 | 3665 | ||
3622 | status = nfs4_preprocess_seqid_op(cstate, | 3666 | status = nfs4_preprocess_seqid_op(cstate, |
3623 | oc->oc_seqid, &oc->oc_req_stateid, | 3667 | oc->oc_seqid, &oc->oc_req_stateid, |
3624 | NFS4_OPEN_STID, &stp); | 3668 | NFS4_OPEN_STID, &stp, nn); |
3625 | if (status) | 3669 | if (status) |
3626 | goto out; | 3670 | goto out; |
3627 | oo = openowner(stp->st_stateowner); | 3671 | oo = openowner(stp->st_stateowner); |
@@ -3664,7 +3708,7 @@ static inline void nfs4_stateid_downgrade(struct nfs4_ol_stateid *stp, u32 to_ac | |||
3664 | case NFS4_SHARE_ACCESS_BOTH: | 3708 | case NFS4_SHARE_ACCESS_BOTH: |
3665 | break; | 3709 | break; |
3666 | default: | 3710 | default: |
3667 | BUG(); | 3711 | WARN_ON_ONCE(1); |
3668 | } | 3712 | } |
3669 | } | 3713 | } |
3670 | 3714 | ||
@@ -3685,6 +3729,7 @@ nfsd4_open_downgrade(struct svc_rqst *rqstp, | |||
3685 | { | 3729 | { |
3686 | __be32 status; | 3730 | __be32 status; |
3687 | struct nfs4_ol_stateid *stp; | 3731 | struct nfs4_ol_stateid *stp; |
3732 | struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); | ||
3688 | 3733 | ||
3689 | dprintk("NFSD: nfsd4_open_downgrade on file %.*s\n", | 3734 | dprintk("NFSD: nfsd4_open_downgrade on file %.*s\n", |
3690 | (int)cstate->current_fh.fh_dentry->d_name.len, | 3735 | (int)cstate->current_fh.fh_dentry->d_name.len, |
@@ -3697,7 +3742,7 @@ nfsd4_open_downgrade(struct svc_rqst *rqstp, | |||
3697 | 3742 | ||
3698 | nfs4_lock_state(); | 3743 | nfs4_lock_state(); |
3699 | status = nfs4_preprocess_confirmed_seqid_op(cstate, od->od_seqid, | 3744 | status = nfs4_preprocess_confirmed_seqid_op(cstate, od->od_seqid, |
3700 | &od->od_stateid, &stp); | 3745 | &od->od_stateid, &stp, nn); |
3701 | if (status) | 3746 | if (status) |
3702 | goto out; | 3747 | goto out; |
3703 | status = nfserr_inval; | 3748 | status = nfserr_inval; |
@@ -3760,6 +3805,8 @@ nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
3760 | __be32 status; | 3805 | __be32 status; |
3761 | struct nfs4_openowner *oo; | 3806 | struct nfs4_openowner *oo; |
3762 | struct nfs4_ol_stateid *stp; | 3807 | struct nfs4_ol_stateid *stp; |
3808 | struct net *net = SVC_NET(rqstp); | ||
3809 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); | ||
3763 | 3810 | ||
3764 | dprintk("NFSD: nfsd4_close on file %.*s\n", | 3811 | dprintk("NFSD: nfsd4_close on file %.*s\n", |
3765 | (int)cstate->current_fh.fh_dentry->d_name.len, | 3812 | (int)cstate->current_fh.fh_dentry->d_name.len, |
@@ -3769,7 +3816,7 @@ nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
3769 | status = nfs4_preprocess_seqid_op(cstate, close->cl_seqid, | 3816 | status = nfs4_preprocess_seqid_op(cstate, close->cl_seqid, |
3770 | &close->cl_stateid, | 3817 | &close->cl_stateid, |
3771 | NFS4_OPEN_STID|NFS4_CLOSED_STID, | 3818 | NFS4_OPEN_STID|NFS4_CLOSED_STID, |
3772 | &stp); | 3819 | &stp, nn); |
3773 | if (status) | 3820 | if (status) |
3774 | goto out; | 3821 | goto out; |
3775 | oo = openowner(stp->st_stateowner); | 3822 | oo = openowner(stp->st_stateowner); |
@@ -3791,7 +3838,7 @@ nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
3791 | * little while to handle CLOSE replay. | 3838 | * little while to handle CLOSE replay. |
3792 | */ | 3839 | */ |
3793 | if (list_empty(&oo->oo_owner.so_stateids)) | 3840 | if (list_empty(&oo->oo_owner.so_stateids)) |
3794 | move_to_close_lru(oo); | 3841 | move_to_close_lru(oo, SVC_NET(rqstp)); |
3795 | } | 3842 | } |
3796 | } | 3843 | } |
3797 | out: | 3844 | out: |
@@ -3807,15 +3854,15 @@ nfsd4_delegreturn(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
3807 | struct nfs4_delegation *dp; | 3854 | struct nfs4_delegation *dp; |
3808 | stateid_t *stateid = &dr->dr_stateid; | 3855 | stateid_t *stateid = &dr->dr_stateid; |
3809 | struct nfs4_stid *s; | 3856 | struct nfs4_stid *s; |
3810 | struct inode *inode; | ||
3811 | __be32 status; | 3857 | __be32 status; |
3858 | struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); | ||
3812 | 3859 | ||
3813 | if ((status = fh_verify(rqstp, &cstate->current_fh, S_IFREG, 0))) | 3860 | if ((status = fh_verify(rqstp, &cstate->current_fh, S_IFREG, 0))) |
3814 | return status; | 3861 | return status; |
3815 | inode = cstate->current_fh.fh_dentry->d_inode; | ||
3816 | 3862 | ||
3817 | nfs4_lock_state(); | 3863 | nfs4_lock_state(); |
3818 | status = nfsd4_lookup_stateid(stateid, NFS4_DELEG_STID, &s, cstate->minorversion); | 3864 | status = nfsd4_lookup_stateid(stateid, NFS4_DELEG_STID, &s, |
3865 | cstate->minorversion, nn); | ||
3819 | if (status) | 3866 | if (status) |
3820 | goto out; | 3867 | goto out; |
3821 | dp = delegstateid(s); | 3868 | dp = delegstateid(s); |
@@ -3833,8 +3880,6 @@ out: | |||
3833 | 3880 | ||
3834 | #define LOFF_OVERFLOW(start, len) ((u64)(len) > ~(u64)(start)) | 3881 | #define LOFF_OVERFLOW(start, len) ((u64)(len) > ~(u64)(start)) |
3835 | 3882 | ||
3836 | #define LOCKOWNER_INO_HASH_BITS 8 | ||
3837 | #define LOCKOWNER_INO_HASH_SIZE (1 << LOCKOWNER_INO_HASH_BITS) | ||
3838 | #define LOCKOWNER_INO_HASH_MASK (LOCKOWNER_INO_HASH_SIZE - 1) | 3883 | #define LOCKOWNER_INO_HASH_MASK (LOCKOWNER_INO_HASH_SIZE - 1) |
3839 | 3884 | ||
3840 | static inline u64 | 3885 | static inline u64 |
@@ -3852,7 +3897,7 @@ last_byte_offset(u64 start, u64 len) | |||
3852 | { | 3897 | { |
3853 | u64 end; | 3898 | u64 end; |
3854 | 3899 | ||
3855 | BUG_ON(!len); | 3900 | WARN_ON_ONCE(!len); |
3856 | end = start + len; | 3901 | end = start + len; |
3857 | return end > start ? end - 1: NFS4_MAX_UINT64; | 3902 | return end > start ? end - 1: NFS4_MAX_UINT64; |
3858 | } | 3903 | } |
@@ -3864,8 +3909,6 @@ static unsigned int lockowner_ino_hashval(struct inode *inode, u32 cl_id, struct | |||
3864 | & LOCKOWNER_INO_HASH_MASK; | 3909 | & LOCKOWNER_INO_HASH_MASK; |
3865 | } | 3910 | } |
3866 | 3911 | ||
3867 | static struct list_head lockowner_ino_hashtbl[LOCKOWNER_INO_HASH_SIZE]; | ||
3868 | |||
3869 | /* | 3912 | /* |
3870 | * TODO: Linux file offsets are _signed_ 64-bit quantities, which means that | 3913 | * TODO: Linux file offsets are _signed_ 64-bit quantities, which means that |
3871 | * we can't properly handle lock requests that go beyond the (2^63 - 1)-th | 3914 | * we can't properly handle lock requests that go beyond the (2^63 - 1)-th |
@@ -3931,12 +3974,12 @@ static bool same_lockowner_ino(struct nfs4_lockowner *lo, struct inode *inode, c | |||
3931 | 3974 | ||
3932 | static struct nfs4_lockowner * | 3975 | static struct nfs4_lockowner * |
3933 | find_lockowner_str(struct inode *inode, clientid_t *clid, | 3976 | find_lockowner_str(struct inode *inode, clientid_t *clid, |
3934 | struct xdr_netobj *owner) | 3977 | struct xdr_netobj *owner, struct nfsd_net *nn) |
3935 | { | 3978 | { |
3936 | unsigned int hashval = lockowner_ino_hashval(inode, clid->cl_id, owner); | 3979 | unsigned int hashval = lockowner_ino_hashval(inode, clid->cl_id, owner); |
3937 | struct nfs4_lockowner *lo; | 3980 | struct nfs4_lockowner *lo; |
3938 | 3981 | ||
3939 | list_for_each_entry(lo, &lockowner_ino_hashtbl[hashval], lo_owner_ino_hash) { | 3982 | list_for_each_entry(lo, &nn->lockowner_ino_hashtbl[hashval], lo_owner_ino_hash) { |
3940 | if (same_lockowner_ino(lo, inode, clid, owner)) | 3983 | if (same_lockowner_ino(lo, inode, clid, owner)) |
3941 | return lo; | 3984 | return lo; |
3942 | } | 3985 | } |
@@ -3948,9 +3991,10 @@ static void hash_lockowner(struct nfs4_lockowner *lo, unsigned int strhashval, s | |||
3948 | struct inode *inode = open_stp->st_file->fi_inode; | 3991 | struct inode *inode = open_stp->st_file->fi_inode; |
3949 | unsigned int inohash = lockowner_ino_hashval(inode, | 3992 | unsigned int inohash = lockowner_ino_hashval(inode, |
3950 | clp->cl_clientid.cl_id, &lo->lo_owner.so_owner); | 3993 | clp->cl_clientid.cl_id, &lo->lo_owner.so_owner); |
3994 | struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); | ||
3951 | 3995 | ||
3952 | list_add(&lo->lo_owner.so_strhash, &ownerstr_hashtbl[strhashval]); | 3996 | list_add(&lo->lo_owner.so_strhash, &nn->ownerstr_hashtbl[strhashval]); |
3953 | list_add(&lo->lo_owner_ino_hash, &lockowner_ino_hashtbl[inohash]); | 3997 | list_add(&lo->lo_owner_ino_hash, &nn->lockowner_ino_hashtbl[inohash]); |
3954 | list_add(&lo->lo_perstateid, &open_stp->st_lockowners); | 3998 | list_add(&lo->lo_perstateid, &open_stp->st_lockowners); |
3955 | } | 3999 | } |
3956 | 4000 | ||
@@ -4024,8 +4068,10 @@ static __be32 lookup_or_create_lock_state(struct nfsd4_compound_state *cstate, s | |||
4024 | struct nfs4_client *cl = oo->oo_owner.so_client; | 4068 | struct nfs4_client *cl = oo->oo_owner.so_client; |
4025 | struct nfs4_lockowner *lo; | 4069 | struct nfs4_lockowner *lo; |
4026 | unsigned int strhashval; | 4070 | unsigned int strhashval; |
4071 | struct nfsd_net *nn = net_generic(cl->net, nfsd_net_id); | ||
4027 | 4072 | ||
4028 | lo = find_lockowner_str(fi->fi_inode, &cl->cl_clientid, &lock->v.new.owner); | 4073 | lo = find_lockowner_str(fi->fi_inode, &cl->cl_clientid, |
4074 | &lock->v.new.owner, nn); | ||
4029 | if (lo) { | 4075 | if (lo) { |
4030 | if (!cstate->minorversion) | 4076 | if (!cstate->minorversion) |
4031 | return nfserr_bad_seqid; | 4077 | return nfserr_bad_seqid; |
@@ -4065,7 +4111,8 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
4065 | bool new_state = false; | 4111 | bool new_state = false; |
4066 | int lkflg; | 4112 | int lkflg; |
4067 | int err; | 4113 | int err; |
4068 | struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); | 4114 | struct net *net = SVC_NET(rqstp); |
4115 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); | ||
4069 | 4116 | ||
4070 | dprintk("NFSD: nfsd4_lock: start=%Ld length=%Ld\n", | 4117 | dprintk("NFSD: nfsd4_lock: start=%Ld length=%Ld\n", |
4071 | (long long) lock->lk_offset, | 4118 | (long long) lock->lk_offset, |
@@ -4099,7 +4146,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
4099 | status = nfs4_preprocess_confirmed_seqid_op(cstate, | 4146 | status = nfs4_preprocess_confirmed_seqid_op(cstate, |
4100 | lock->lk_new_open_seqid, | 4147 | lock->lk_new_open_seqid, |
4101 | &lock->lk_new_open_stateid, | 4148 | &lock->lk_new_open_stateid, |
4102 | &open_stp); | 4149 | &open_stp, nn); |
4103 | if (status) | 4150 | if (status) |
4104 | goto out; | 4151 | goto out; |
4105 | open_sop = openowner(open_stp->st_stateowner); | 4152 | open_sop = openowner(open_stp->st_stateowner); |
@@ -4113,7 +4160,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
4113 | status = nfs4_preprocess_seqid_op(cstate, | 4160 | status = nfs4_preprocess_seqid_op(cstate, |
4114 | lock->lk_old_lock_seqid, | 4161 | lock->lk_old_lock_seqid, |
4115 | &lock->lk_old_lock_stateid, | 4162 | &lock->lk_old_lock_stateid, |
4116 | NFS4_LOCK_STID, &lock_stp); | 4163 | NFS4_LOCK_STID, &lock_stp, nn); |
4117 | if (status) | 4164 | if (status) |
4118 | goto out; | 4165 | goto out; |
4119 | lock_sop = lockowner(lock_stp->st_stateowner); | 4166 | lock_sop = lockowner(lock_stp->st_stateowner); |
@@ -4124,10 +4171,10 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
4124 | goto out; | 4171 | goto out; |
4125 | 4172 | ||
4126 | status = nfserr_grace; | 4173 | status = nfserr_grace; |
4127 | if (locks_in_grace(SVC_NET(rqstp)) && !lock->lk_reclaim) | 4174 | if (locks_in_grace(net) && !lock->lk_reclaim) |
4128 | goto out; | 4175 | goto out; |
4129 | status = nfserr_no_grace; | 4176 | status = nfserr_no_grace; |
4130 | if (!locks_in_grace(SVC_NET(rqstp)) && lock->lk_reclaim) | 4177 | if (!locks_in_grace(net) && lock->lk_reclaim) |
4131 | goto out; | 4178 | goto out; |
4132 | 4179 | ||
4133 | file_lock = locks_alloc_lock(); | 4180 | file_lock = locks_alloc_lock(); |
@@ -4238,7 +4285,7 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
4238 | struct file_lock *file_lock = NULL; | 4285 | struct file_lock *file_lock = NULL; |
4239 | struct nfs4_lockowner *lo; | 4286 | struct nfs4_lockowner *lo; |
4240 | __be32 status; | 4287 | __be32 status; |
4241 | struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); | 4288 | struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); |
4242 | 4289 | ||
4243 | if (locks_in_grace(SVC_NET(rqstp))) | 4290 | if (locks_in_grace(SVC_NET(rqstp))) |
4244 | return nfserr_grace; | 4291 | return nfserr_grace; |
@@ -4248,9 +4295,11 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
4248 | 4295 | ||
4249 | nfs4_lock_state(); | 4296 | nfs4_lock_state(); |
4250 | 4297 | ||
4251 | status = nfserr_stale_clientid; | 4298 | if (!nfsd4_has_session(cstate)) { |
4252 | if (!nfsd4_has_session(cstate) && STALE_CLIENTID(&lockt->lt_clientid, nn)) | 4299 | status = lookup_clientid(&lockt->lt_clientid, false, nn, NULL); |
4253 | goto out; | 4300 | if (status) |
4301 | goto out; | ||
4302 | } | ||
4254 | 4303 | ||
4255 | if ((status = fh_verify(rqstp, &cstate->current_fh, S_IFREG, 0))) | 4304 | if ((status = fh_verify(rqstp, &cstate->current_fh, S_IFREG, 0))) |
4256 | goto out; | 4305 | goto out; |
@@ -4278,7 +4327,7 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
4278 | goto out; | 4327 | goto out; |
4279 | } | 4328 | } |
4280 | 4329 | ||
4281 | lo = find_lockowner_str(inode, &lockt->lt_clientid, &lockt->lt_owner); | 4330 | lo = find_lockowner_str(inode, &lockt->lt_clientid, &lockt->lt_owner, nn); |
4282 | if (lo) | 4331 | if (lo) |
4283 | file_lock->fl_owner = (fl_owner_t)lo; | 4332 | file_lock->fl_owner = (fl_owner_t)lo; |
4284 | file_lock->fl_pid = current->tgid; | 4333 | file_lock->fl_pid = current->tgid; |
@@ -4313,7 +4362,8 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
4313 | struct file_lock *file_lock = NULL; | 4362 | struct file_lock *file_lock = NULL; |
4314 | __be32 status; | 4363 | __be32 status; |
4315 | int err; | 4364 | int err; |
4316 | 4365 | struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); | |
4366 | |||
4317 | dprintk("NFSD: nfsd4_locku: start=%Ld length=%Ld\n", | 4367 | dprintk("NFSD: nfsd4_locku: start=%Ld length=%Ld\n", |
4318 | (long long) locku->lu_offset, | 4368 | (long long) locku->lu_offset, |
4319 | (long long) locku->lu_length); | 4369 | (long long) locku->lu_length); |
@@ -4324,7 +4374,8 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
4324 | nfs4_lock_state(); | 4374 | nfs4_lock_state(); |
4325 | 4375 | ||
4326 | status = nfs4_preprocess_seqid_op(cstate, locku->lu_seqid, | 4376 | status = nfs4_preprocess_seqid_op(cstate, locku->lu_seqid, |
4327 | &locku->lu_stateid, NFS4_LOCK_STID, &stp); | 4377 | &locku->lu_stateid, NFS4_LOCK_STID, |
4378 | &stp, nn); | ||
4328 | if (status) | 4379 | if (status) |
4329 | goto out; | 4380 | goto out; |
4330 | filp = find_any_file(stp->st_file); | 4381 | filp = find_any_file(stp->st_file); |
@@ -4414,23 +4465,21 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp, | |||
4414 | struct list_head matches; | 4465 | struct list_head matches; |
4415 | unsigned int hashval = ownerstr_hashval(clid->cl_id, owner); | 4466 | unsigned int hashval = ownerstr_hashval(clid->cl_id, owner); |
4416 | __be32 status; | 4467 | __be32 status; |
4417 | struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); | 4468 | struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); |
4418 | 4469 | ||
4419 | dprintk("nfsd4_release_lockowner clientid: (%08x/%08x):\n", | 4470 | dprintk("nfsd4_release_lockowner clientid: (%08x/%08x):\n", |
4420 | clid->cl_boot, clid->cl_id); | 4471 | clid->cl_boot, clid->cl_id); |
4421 | 4472 | ||
4422 | /* XXX check for lease expiration */ | ||
4423 | |||
4424 | status = nfserr_stale_clientid; | ||
4425 | if (STALE_CLIENTID(clid, nn)) | ||
4426 | return status; | ||
4427 | |||
4428 | nfs4_lock_state(); | 4473 | nfs4_lock_state(); |
4429 | 4474 | ||
4475 | status = lookup_clientid(clid, cstate->minorversion, nn, NULL); | ||
4476 | if (status) | ||
4477 | goto out; | ||
4478 | |||
4430 | status = nfserr_locks_held; | 4479 | status = nfserr_locks_held; |
4431 | INIT_LIST_HEAD(&matches); | 4480 | INIT_LIST_HEAD(&matches); |
4432 | 4481 | ||
4433 | list_for_each_entry(sop, &ownerstr_hashtbl[hashval], so_strhash) { | 4482 | list_for_each_entry(sop, &nn->ownerstr_hashtbl[hashval], so_strhash) { |
4434 | if (sop->so_is_open_owner) | 4483 | if (sop->so_is_open_owner) |
4435 | continue; | 4484 | continue; |
4436 | if (!same_owner_str(sop, owner, clid)) | 4485 | if (!same_owner_str(sop, owner, clid)) |
@@ -4466,73 +4515,74 @@ alloc_reclaim(void) | |||
4466 | return kmalloc(sizeof(struct nfs4_client_reclaim), GFP_KERNEL); | 4515 | return kmalloc(sizeof(struct nfs4_client_reclaim), GFP_KERNEL); |
4467 | } | 4516 | } |
4468 | 4517 | ||
4469 | int | 4518 | bool |
4470 | nfs4_has_reclaimed_state(const char *name, bool use_exchange_id) | 4519 | nfs4_has_reclaimed_state(const char *name, struct nfsd_net *nn) |
4471 | { | 4520 | { |
4472 | unsigned int strhashval = clientstr_hashval(name); | 4521 | struct nfs4_client_reclaim *crp; |
4473 | struct nfs4_client *clp; | ||
4474 | 4522 | ||
4475 | clp = find_confirmed_client_by_str(name, strhashval); | 4523 | crp = nfsd4_find_reclaim_client(name, nn); |
4476 | if (!clp) | 4524 | return (crp && crp->cr_clp); |
4477 | return 0; | ||
4478 | return test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags); | ||
4479 | } | 4525 | } |
4480 | 4526 | ||
4481 | /* | 4527 | /* |
4482 | * failure => all reset bets are off, nfserr_no_grace... | 4528 | * failure => all reset bets are off, nfserr_no_grace... |
4483 | */ | 4529 | */ |
4484 | int | 4530 | struct nfs4_client_reclaim * |
4485 | nfs4_client_to_reclaim(const char *name) | 4531 | nfs4_client_to_reclaim(const char *name, struct nfsd_net *nn) |
4486 | { | 4532 | { |
4487 | unsigned int strhashval; | 4533 | unsigned int strhashval; |
4488 | struct nfs4_client_reclaim *crp = NULL; | 4534 | struct nfs4_client_reclaim *crp; |
4489 | 4535 | ||
4490 | dprintk("NFSD nfs4_client_to_reclaim NAME: %.*s\n", HEXDIR_LEN, name); | 4536 | dprintk("NFSD nfs4_client_to_reclaim NAME: %.*s\n", HEXDIR_LEN, name); |
4491 | crp = alloc_reclaim(); | 4537 | crp = alloc_reclaim(); |
4492 | if (!crp) | 4538 | if (crp) { |
4493 | return 0; | 4539 | strhashval = clientstr_hashval(name); |
4494 | strhashval = clientstr_hashval(name); | 4540 | INIT_LIST_HEAD(&crp->cr_strhash); |
4495 | INIT_LIST_HEAD(&crp->cr_strhash); | 4541 | list_add(&crp->cr_strhash, &nn->reclaim_str_hashtbl[strhashval]); |
4496 | list_add(&crp->cr_strhash, &reclaim_str_hashtbl[strhashval]); | 4542 | memcpy(crp->cr_recdir, name, HEXDIR_LEN); |
4497 | memcpy(crp->cr_recdir, name, HEXDIR_LEN); | 4543 | crp->cr_clp = NULL; |
4498 | reclaim_str_hashtbl_size++; | 4544 | nn->reclaim_str_hashtbl_size++; |
4499 | return 1; | 4545 | } |
4546 | return crp; | ||
4547 | } | ||
4548 | |||
4549 | void | ||
4550 | nfs4_remove_reclaim_record(struct nfs4_client_reclaim *crp, struct nfsd_net *nn) | ||
4551 | { | ||
4552 | list_del(&crp->cr_strhash); | ||
4553 | kfree(crp); | ||
4554 | nn->reclaim_str_hashtbl_size--; | ||
4500 | } | 4555 | } |
4501 | 4556 | ||
4502 | void | 4557 | void |
4503 | nfs4_release_reclaim(void) | 4558 | nfs4_release_reclaim(struct nfsd_net *nn) |
4504 | { | 4559 | { |
4505 | struct nfs4_client_reclaim *crp = NULL; | 4560 | struct nfs4_client_reclaim *crp = NULL; |
4506 | int i; | 4561 | int i; |
4507 | 4562 | ||
4508 | for (i = 0; i < CLIENT_HASH_SIZE; i++) { | 4563 | for (i = 0; i < CLIENT_HASH_SIZE; i++) { |
4509 | while (!list_empty(&reclaim_str_hashtbl[i])) { | 4564 | while (!list_empty(&nn->reclaim_str_hashtbl[i])) { |
4510 | crp = list_entry(reclaim_str_hashtbl[i].next, | 4565 | crp = list_entry(nn->reclaim_str_hashtbl[i].next, |
4511 | struct nfs4_client_reclaim, cr_strhash); | 4566 | struct nfs4_client_reclaim, cr_strhash); |
4512 | list_del(&crp->cr_strhash); | 4567 | nfs4_remove_reclaim_record(crp, nn); |
4513 | kfree(crp); | ||
4514 | reclaim_str_hashtbl_size--; | ||
4515 | } | 4568 | } |
4516 | } | 4569 | } |
4517 | BUG_ON(reclaim_str_hashtbl_size); | 4570 | WARN_ON_ONCE(nn->reclaim_str_hashtbl_size); |
4518 | } | 4571 | } |
4519 | 4572 | ||
4520 | /* | 4573 | /* |
4521 | * called from OPEN, CLAIM_PREVIOUS with a new clientid. */ | 4574 | * called from OPEN, CLAIM_PREVIOUS with a new clientid. */ |
4522 | struct nfs4_client_reclaim * | 4575 | struct nfs4_client_reclaim * |
4523 | nfsd4_find_reclaim_client(struct nfs4_client *clp) | 4576 | nfsd4_find_reclaim_client(const char *recdir, struct nfsd_net *nn) |
4524 | { | 4577 | { |
4525 | unsigned int strhashval; | 4578 | unsigned int strhashval; |
4526 | struct nfs4_client_reclaim *crp = NULL; | 4579 | struct nfs4_client_reclaim *crp = NULL; |
4527 | 4580 | ||
4528 | dprintk("NFSD: nfs4_find_reclaim_client for %.*s with recdir %s\n", | 4581 | dprintk("NFSD: nfs4_find_reclaim_client for recdir %s\n", recdir); |
4529 | clp->cl_name.len, clp->cl_name.data, | ||
4530 | clp->cl_recdir); | ||
4531 | 4582 | ||
4532 | /* find clp->cl_name in reclaim_str_hashtbl */ | 4583 | strhashval = clientstr_hashval(recdir); |
4533 | strhashval = clientstr_hashval(clp->cl_recdir); | 4584 | list_for_each_entry(crp, &nn->reclaim_str_hashtbl[strhashval], cr_strhash) { |
4534 | list_for_each_entry(crp, &reclaim_str_hashtbl[strhashval], cr_strhash) { | 4585 | if (same_name(crp->cr_recdir, recdir)) { |
4535 | if (same_name(crp->cr_recdir, clp->cl_recdir)) { | ||
4536 | return crp; | 4586 | return crp; |
4537 | } | 4587 | } |
4538 | } | 4588 | } |
@@ -4543,12 +4593,12 @@ nfsd4_find_reclaim_client(struct nfs4_client *clp) | |||
4543 | * Called from OPEN. Look for clientid in reclaim list. | 4593 | * Called from OPEN. Look for clientid in reclaim list. |
4544 | */ | 4594 | */ |
4545 | __be32 | 4595 | __be32 |
4546 | nfs4_check_open_reclaim(clientid_t *clid, bool sessions) | 4596 | nfs4_check_open_reclaim(clientid_t *clid, bool sessions, struct nfsd_net *nn) |
4547 | { | 4597 | { |
4548 | struct nfs4_client *clp; | 4598 | struct nfs4_client *clp; |
4549 | 4599 | ||
4550 | /* find clientid in conf_id_hashtbl */ | 4600 | /* find clientid in conf_id_hashtbl */ |
4551 | clp = find_confirmed_client(clid, sessions); | 4601 | clp = find_confirmed_client(clid, sessions, nn); |
4552 | if (clp == NULL) | 4602 | if (clp == NULL) |
4553 | return nfserr_reclaim_bad; | 4603 | return nfserr_reclaim_bad; |
4554 | 4604 | ||
@@ -4557,124 +4607,177 @@ nfs4_check_open_reclaim(clientid_t *clid, bool sessions) | |||
4557 | 4607 | ||
4558 | #ifdef CONFIG_NFSD_FAULT_INJECTION | 4608 | #ifdef CONFIG_NFSD_FAULT_INJECTION |
4559 | 4609 | ||
4560 | void nfsd_forget_clients(u64 num) | 4610 | u64 nfsd_forget_client(struct nfs4_client *clp, u64 max) |
4561 | { | 4611 | { |
4562 | struct nfs4_client *clp, *next; | 4612 | expire_client(clp); |
4563 | int count = 0; | 4613 | return 1; |
4564 | |||
4565 | nfs4_lock_state(); | ||
4566 | list_for_each_entry_safe(clp, next, &client_lru, cl_lru) { | ||
4567 | expire_client(clp); | ||
4568 | if (++count == num) | ||
4569 | break; | ||
4570 | } | ||
4571 | nfs4_unlock_state(); | ||
4572 | |||
4573 | printk(KERN_INFO "NFSD: Forgot %d clients", count); | ||
4574 | } | 4614 | } |
4575 | 4615 | ||
4576 | static void release_lockowner_sop(struct nfs4_stateowner *sop) | 4616 | u64 nfsd_print_client(struct nfs4_client *clp, u64 num) |
4577 | { | 4617 | { |
4578 | release_lockowner(lockowner(sop)); | 4618 | char buf[INET6_ADDRSTRLEN]; |
4619 | rpc_ntop((struct sockaddr *)&clp->cl_addr, buf, sizeof(buf)); | ||
4620 | printk(KERN_INFO "NFS Client: %s\n", buf); | ||
4621 | return 1; | ||
4579 | } | 4622 | } |
4580 | 4623 | ||
4581 | static void release_openowner_sop(struct nfs4_stateowner *sop) | 4624 | static void nfsd_print_count(struct nfs4_client *clp, unsigned int count, |
4625 | const char *type) | ||
4582 | { | 4626 | { |
4583 | release_openowner(openowner(sop)); | 4627 | char buf[INET6_ADDRSTRLEN]; |
4628 | rpc_ntop((struct sockaddr *)&clp->cl_addr, buf, sizeof(buf)); | ||
4629 | printk(KERN_INFO "NFS Client: %s has %u %s\n", buf, count, type); | ||
4584 | } | 4630 | } |
4585 | 4631 | ||
4586 | static int nfsd_release_n_owners(u64 num, bool is_open_owner, | 4632 | static u64 nfsd_foreach_client_lock(struct nfs4_client *clp, u64 max, void (*func)(struct nfs4_lockowner *)) |
4587 | void (*release_sop)(struct nfs4_stateowner *)) | ||
4588 | { | 4633 | { |
4589 | int i, count = 0; | 4634 | struct nfs4_openowner *oop; |
4590 | struct nfs4_stateowner *sop, *next; | 4635 | struct nfs4_lockowner *lop, *lo_next; |
4636 | struct nfs4_ol_stateid *stp, *st_next; | ||
4637 | u64 count = 0; | ||
4591 | 4638 | ||
4592 | for (i = 0; i < OWNER_HASH_SIZE; i++) { | 4639 | list_for_each_entry(oop, &clp->cl_openowners, oo_perclient) { |
4593 | list_for_each_entry_safe(sop, next, &ownerstr_hashtbl[i], so_strhash) { | 4640 | list_for_each_entry_safe(stp, st_next, &oop->oo_owner.so_stateids, st_perstateowner) { |
4594 | if (sop->so_is_open_owner != is_open_owner) | 4641 | list_for_each_entry_safe(lop, lo_next, &stp->st_lockowners, lo_perstateid) { |
4595 | continue; | 4642 | if (func) |
4596 | release_sop(sop); | 4643 | func(lop); |
4597 | if (++count == num) | 4644 | if (++count == max) |
4598 | return count; | 4645 | return count; |
4646 | } | ||
4599 | } | 4647 | } |
4600 | } | 4648 | } |
4649 | |||
4601 | return count; | 4650 | return count; |
4602 | } | 4651 | } |
4603 | 4652 | ||
4604 | void nfsd_forget_locks(u64 num) | 4653 | u64 nfsd_forget_client_locks(struct nfs4_client *clp, u64 max) |
4605 | { | 4654 | { |
4606 | int count; | 4655 | return nfsd_foreach_client_lock(clp, max, release_lockowner); |
4607 | 4656 | } | |
4608 | nfs4_lock_state(); | ||
4609 | count = nfsd_release_n_owners(num, false, release_lockowner_sop); | ||
4610 | nfs4_unlock_state(); | ||
4611 | 4657 | ||
4612 | printk(KERN_INFO "NFSD: Forgot %d locks", count); | 4658 | u64 nfsd_print_client_locks(struct nfs4_client *clp, u64 max) |
4659 | { | ||
4660 | u64 count = nfsd_foreach_client_lock(clp, max, NULL); | ||
4661 | nfsd_print_count(clp, count, "locked files"); | ||
4662 | return count; | ||
4613 | } | 4663 | } |
4614 | 4664 | ||
4615 | void nfsd_forget_openowners(u64 num) | 4665 | static u64 nfsd_foreach_client_open(struct nfs4_client *clp, u64 max, void (*func)(struct nfs4_openowner *)) |
4616 | { | 4666 | { |
4617 | int count; | 4667 | struct nfs4_openowner *oop, *next; |
4668 | u64 count = 0; | ||
4618 | 4669 | ||
4619 | nfs4_lock_state(); | 4670 | list_for_each_entry_safe(oop, next, &clp->cl_openowners, oo_perclient) { |
4620 | count = nfsd_release_n_owners(num, true, release_openowner_sop); | 4671 | if (func) |
4621 | nfs4_unlock_state(); | 4672 | func(oop); |
4673 | if (++count == max) | ||
4674 | break; | ||
4675 | } | ||
4622 | 4676 | ||
4623 | printk(KERN_INFO "NFSD: Forgot %d open owners", count); | 4677 | return count; |
4624 | } | 4678 | } |
4625 | 4679 | ||
4626 | static int nfsd_process_n_delegations(u64 num, struct list_head *list) | 4680 | u64 nfsd_forget_client_openowners(struct nfs4_client *clp, u64 max) |
4627 | { | 4681 | { |
4628 | int i, count = 0; | 4682 | return nfsd_foreach_client_open(clp, max, release_openowner); |
4629 | struct nfs4_file *fp, *fnext; | 4683 | } |
4630 | struct nfs4_delegation *dp, *dnext; | ||
4631 | 4684 | ||
4632 | for (i = 0; i < FILE_HASH_SIZE; i++) { | 4685 | u64 nfsd_print_client_openowners(struct nfs4_client *clp, u64 max) |
4633 | list_for_each_entry_safe(fp, fnext, &file_hashtbl[i], fi_hash) { | 4686 | { |
4634 | list_for_each_entry_safe(dp, dnext, &fp->fi_delegations, dl_perfile) { | 4687 | u64 count = nfsd_foreach_client_open(clp, max, NULL); |
4635 | list_move(&dp->dl_recall_lru, list); | 4688 | nfsd_print_count(clp, count, "open files"); |
4636 | if (++count == num) | 4689 | return count; |
4637 | return count; | 4690 | } |
4638 | } | 4691 | |
4639 | } | 4692 | static u64 nfsd_find_all_delegations(struct nfs4_client *clp, u64 max, |
4640 | } | 4693 | struct list_head *victims) |
4694 | { | ||
4695 | struct nfs4_delegation *dp, *next; | ||
4696 | u64 count = 0; | ||
4641 | 4697 | ||
4698 | list_for_each_entry_safe(dp, next, &clp->cl_delegations, dl_perclnt) { | ||
4699 | if (victims) | ||
4700 | list_move(&dp->dl_recall_lru, victims); | ||
4701 | if (++count == max) | ||
4702 | break; | ||
4703 | } | ||
4642 | return count; | 4704 | return count; |
4643 | } | 4705 | } |
4644 | 4706 | ||
4645 | void nfsd_forget_delegations(u64 num) | 4707 | u64 nfsd_forget_client_delegations(struct nfs4_client *clp, u64 max) |
4646 | { | 4708 | { |
4647 | unsigned int count; | 4709 | struct nfs4_delegation *dp, *next; |
4648 | LIST_HEAD(victims); | 4710 | LIST_HEAD(victims); |
4649 | struct nfs4_delegation *dp, *dnext; | 4711 | u64 count; |
4650 | 4712 | ||
4651 | spin_lock(&recall_lock); | 4713 | spin_lock(&recall_lock); |
4652 | count = nfsd_process_n_delegations(num, &victims); | 4714 | count = nfsd_find_all_delegations(clp, max, &victims); |
4653 | spin_unlock(&recall_lock); | 4715 | spin_unlock(&recall_lock); |
4654 | 4716 | ||
4655 | nfs4_lock_state(); | 4717 | list_for_each_entry_safe(dp, next, &victims, dl_recall_lru) |
4656 | list_for_each_entry_safe(dp, dnext, &victims, dl_recall_lru) | ||
4657 | unhash_delegation(dp); | 4718 | unhash_delegation(dp); |
4658 | nfs4_unlock_state(); | ||
4659 | 4719 | ||
4660 | printk(KERN_INFO "NFSD: Forgot %d delegations", count); | 4720 | return count; |
4661 | } | 4721 | } |
4662 | 4722 | ||
4663 | void nfsd_recall_delegations(u64 num) | 4723 | u64 nfsd_recall_client_delegations(struct nfs4_client *clp, u64 max) |
4664 | { | 4724 | { |
4665 | unsigned int count; | 4725 | struct nfs4_delegation *dp, *next; |
4666 | LIST_HEAD(victims); | 4726 | LIST_HEAD(victims); |
4667 | struct nfs4_delegation *dp, *dnext; | 4727 | u64 count; |
4668 | 4728 | ||
4669 | spin_lock(&recall_lock); | 4729 | spin_lock(&recall_lock); |
4670 | count = nfsd_process_n_delegations(num, &victims); | 4730 | count = nfsd_find_all_delegations(clp, max, &victims); |
4671 | list_for_each_entry_safe(dp, dnext, &victims, dl_recall_lru) { | 4731 | list_for_each_entry_safe(dp, next, &victims, dl_recall_lru) |
4672 | list_del(&dp->dl_recall_lru); | ||
4673 | nfsd_break_one_deleg(dp); | 4732 | nfsd_break_one_deleg(dp); |
4674 | } | ||
4675 | spin_unlock(&recall_lock); | 4733 | spin_unlock(&recall_lock); |
4676 | 4734 | ||
4677 | printk(KERN_INFO "NFSD: Recalled %d delegations", count); | 4735 | return count; |
4736 | } | ||
4737 | |||
4738 | u64 nfsd_print_client_delegations(struct nfs4_client *clp, u64 max) | ||
4739 | { | ||
4740 | u64 count = 0; | ||
4741 | |||
4742 | spin_lock(&recall_lock); | ||
4743 | count = nfsd_find_all_delegations(clp, max, NULL); | ||
4744 | spin_unlock(&recall_lock); | ||
4745 | |||
4746 | nfsd_print_count(clp, count, "delegations"); | ||
4747 | return count; | ||
4748 | } | ||
4749 | |||
4750 | u64 nfsd_for_n_state(u64 max, u64 (*func)(struct nfs4_client *, u64)) | ||
4751 | { | ||
4752 | struct nfs4_client *clp, *next; | ||
4753 | u64 count = 0; | ||
4754 | struct nfsd_net *nn = net_generic(current->nsproxy->net_ns, nfsd_net_id); | ||
4755 | |||
4756 | if (!nfsd_netns_ready(nn)) | ||
4757 | return 0; | ||
4758 | |||
4759 | list_for_each_entry_safe(clp, next, &nn->client_lru, cl_lru) { | ||
4760 | count += func(clp, max - count); | ||
4761 | if ((max != 0) && (count >= max)) | ||
4762 | break; | ||
4763 | } | ||
4764 | |||
4765 | return count; | ||
4766 | } | ||
4767 | |||
4768 | struct nfs4_client *nfsd_find_client(struct sockaddr_storage *addr, size_t addr_size) | ||
4769 | { | ||
4770 | struct nfs4_client *clp; | ||
4771 | struct nfsd_net *nn = net_generic(current->nsproxy->net_ns, nfsd_net_id); | ||
4772 | |||
4773 | if (!nfsd_netns_ready(nn)) | ||
4774 | return NULL; | ||
4775 | |||
4776 | list_for_each_entry(clp, &nn->client_lru, cl_lru) { | ||
4777 | if (memcmp(&clp->cl_addr, addr, addr_size) == 0) | ||
4778 | return clp; | ||
4779 | } | ||
4780 | return NULL; | ||
4678 | } | 4781 | } |
4679 | 4782 | ||
4680 | #endif /* CONFIG_NFSD_FAULT_INJECTION */ | 4783 | #endif /* CONFIG_NFSD_FAULT_INJECTION */ |
@@ -4686,27 +4789,10 @@ nfs4_state_init(void) | |||
4686 | { | 4789 | { |
4687 | int i; | 4790 | int i; |
4688 | 4791 | ||
4689 | for (i = 0; i < CLIENT_HASH_SIZE; i++) { | ||
4690 | INIT_LIST_HEAD(&conf_id_hashtbl[i]); | ||
4691 | INIT_LIST_HEAD(&conf_str_hashtbl[i]); | ||
4692 | INIT_LIST_HEAD(&unconf_str_hashtbl[i]); | ||
4693 | INIT_LIST_HEAD(&unconf_id_hashtbl[i]); | ||
4694 | INIT_LIST_HEAD(&reclaim_str_hashtbl[i]); | ||
4695 | } | ||
4696 | for (i = 0; i < SESSION_HASH_SIZE; i++) | ||
4697 | INIT_LIST_HEAD(&sessionid_hashtbl[i]); | ||
4698 | for (i = 0; i < FILE_HASH_SIZE; i++) { | 4792 | for (i = 0; i < FILE_HASH_SIZE; i++) { |
4699 | INIT_LIST_HEAD(&file_hashtbl[i]); | 4793 | INIT_LIST_HEAD(&file_hashtbl[i]); |
4700 | } | 4794 | } |
4701 | for (i = 0; i < OWNER_HASH_SIZE; i++) { | ||
4702 | INIT_LIST_HEAD(&ownerstr_hashtbl[i]); | ||
4703 | } | ||
4704 | for (i = 0; i < LOCKOWNER_INO_HASH_SIZE; i++) | ||
4705 | INIT_LIST_HEAD(&lockowner_ino_hashtbl[i]); | ||
4706 | INIT_LIST_HEAD(&close_lru); | ||
4707 | INIT_LIST_HEAD(&client_lru); | ||
4708 | INIT_LIST_HEAD(&del_recall_lru); | 4795 | INIT_LIST_HEAD(&del_recall_lru); |
4709 | reclaim_str_hashtbl_size = 0; | ||
4710 | } | 4796 | } |
4711 | 4797 | ||
4712 | /* | 4798 | /* |
@@ -4730,12 +4816,100 @@ set_max_delegations(void) | |||
4730 | max_delegations = nr_free_buffer_pages() >> (20 - 2 - PAGE_SHIFT); | 4816 | max_delegations = nr_free_buffer_pages() >> (20 - 2 - PAGE_SHIFT); |
4731 | } | 4817 | } |
4732 | 4818 | ||
4733 | /* initialization to perform when the nfsd service is started: */ | 4819 | static int nfs4_state_create_net(struct net *net) |
4820 | { | ||
4821 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); | ||
4822 | int i; | ||
4823 | |||
4824 | nn->conf_id_hashtbl = kmalloc(sizeof(struct list_head) * | ||
4825 | CLIENT_HASH_SIZE, GFP_KERNEL); | ||
4826 | if (!nn->conf_id_hashtbl) | ||
4827 | goto err; | ||
4828 | nn->unconf_id_hashtbl = kmalloc(sizeof(struct list_head) * | ||
4829 | CLIENT_HASH_SIZE, GFP_KERNEL); | ||
4830 | if (!nn->unconf_id_hashtbl) | ||
4831 | goto err_unconf_id; | ||
4832 | nn->ownerstr_hashtbl = kmalloc(sizeof(struct list_head) * | ||
4833 | OWNER_HASH_SIZE, GFP_KERNEL); | ||
4834 | if (!nn->ownerstr_hashtbl) | ||
4835 | goto err_ownerstr; | ||
4836 | nn->lockowner_ino_hashtbl = kmalloc(sizeof(struct list_head) * | ||
4837 | LOCKOWNER_INO_HASH_SIZE, GFP_KERNEL); | ||
4838 | if (!nn->lockowner_ino_hashtbl) | ||
4839 | goto err_lockowner_ino; | ||
4840 | nn->sessionid_hashtbl = kmalloc(sizeof(struct list_head) * | ||
4841 | SESSION_HASH_SIZE, GFP_KERNEL); | ||
4842 | if (!nn->sessionid_hashtbl) | ||
4843 | goto err_sessionid; | ||
4844 | |||
4845 | for (i = 0; i < CLIENT_HASH_SIZE; i++) { | ||
4846 | INIT_LIST_HEAD(&nn->conf_id_hashtbl[i]); | ||
4847 | INIT_LIST_HEAD(&nn->unconf_id_hashtbl[i]); | ||
4848 | } | ||
4849 | for (i = 0; i < OWNER_HASH_SIZE; i++) | ||
4850 | INIT_LIST_HEAD(&nn->ownerstr_hashtbl[i]); | ||
4851 | for (i = 0; i < LOCKOWNER_INO_HASH_SIZE; i++) | ||
4852 | INIT_LIST_HEAD(&nn->lockowner_ino_hashtbl[i]); | ||
4853 | for (i = 0; i < SESSION_HASH_SIZE; i++) | ||
4854 | INIT_LIST_HEAD(&nn->sessionid_hashtbl[i]); | ||
4855 | nn->conf_name_tree = RB_ROOT; | ||
4856 | nn->unconf_name_tree = RB_ROOT; | ||
4857 | INIT_LIST_HEAD(&nn->client_lru); | ||
4858 | INIT_LIST_HEAD(&nn->close_lru); | ||
4859 | spin_lock_init(&nn->client_lock); | ||
4860 | |||
4861 | INIT_DELAYED_WORK(&nn->laundromat_work, laundromat_main); | ||
4862 | get_net(net); | ||
4863 | |||
4864 | return 0; | ||
4865 | |||
4866 | err_sessionid: | ||
4867 | kfree(nn->lockowner_ino_hashtbl); | ||
4868 | err_lockowner_ino: | ||
4869 | kfree(nn->ownerstr_hashtbl); | ||
4870 | err_ownerstr: | ||
4871 | kfree(nn->unconf_id_hashtbl); | ||
4872 | err_unconf_id: | ||
4873 | kfree(nn->conf_id_hashtbl); | ||
4874 | err: | ||
4875 | return -ENOMEM; | ||
4876 | } | ||
4877 | |||
4878 | static void | ||
4879 | nfs4_state_destroy_net(struct net *net) | ||
4880 | { | ||
4881 | int i; | ||
4882 | struct nfs4_client *clp = NULL; | ||
4883 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); | ||
4884 | struct rb_node *node, *tmp; | ||
4885 | |||
4886 | for (i = 0; i < CLIENT_HASH_SIZE; i++) { | ||
4887 | while (!list_empty(&nn->conf_id_hashtbl[i])) { | ||
4888 | clp = list_entry(nn->conf_id_hashtbl[i].next, struct nfs4_client, cl_idhash); | ||
4889 | destroy_client(clp); | ||
4890 | } | ||
4891 | } | ||
4892 | |||
4893 | node = rb_first(&nn->unconf_name_tree); | ||
4894 | while (node != NULL) { | ||
4895 | tmp = node; | ||
4896 | node = rb_next(tmp); | ||
4897 | clp = rb_entry(tmp, struct nfs4_client, cl_namenode); | ||
4898 | rb_erase(tmp, &nn->unconf_name_tree); | ||
4899 | destroy_client(clp); | ||
4900 | } | ||
4901 | |||
4902 | kfree(nn->sessionid_hashtbl); | ||
4903 | kfree(nn->lockowner_ino_hashtbl); | ||
4904 | kfree(nn->ownerstr_hashtbl); | ||
4905 | kfree(nn->unconf_id_hashtbl); | ||
4906 | kfree(nn->conf_id_hashtbl); | ||
4907 | put_net(net); | ||
4908 | } | ||
4734 | 4909 | ||
4735 | int | 4910 | int |
4736 | nfs4_state_start(void) | 4911 | nfs4_state_start_net(struct net *net) |
4737 | { | 4912 | { |
4738 | struct net *net = &init_net; | ||
4739 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); | 4913 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); |
4740 | int ret; | 4914 | int ret; |
4741 | 4915 | ||
@@ -4746,18 +4920,32 @@ nfs4_state_start(void) | |||
4746 | * to that instead and then do most of the rest of this on a per-net | 4920 | * to that instead and then do most of the rest of this on a per-net |
4747 | * basis. | 4921 | * basis. |
4748 | */ | 4922 | */ |
4749 | get_net(net); | 4923 | if (net != &init_net) |
4924 | return -EINVAL; | ||
4925 | |||
4926 | ret = nfs4_state_create_net(net); | ||
4927 | if (ret) | ||
4928 | return ret; | ||
4750 | nfsd4_client_tracking_init(net); | 4929 | nfsd4_client_tracking_init(net); |
4751 | nn->boot_time = get_seconds(); | 4930 | nn->boot_time = get_seconds(); |
4752 | locks_start_grace(net, &nn->nfsd4_manager); | 4931 | locks_start_grace(net, &nn->nfsd4_manager); |
4753 | nn->grace_ended = false; | 4932 | nn->grace_ended = false; |
4754 | printk(KERN_INFO "NFSD: starting %ld-second grace period\n", | 4933 | printk(KERN_INFO "NFSD: starting %ld-second grace period (net %p)\n", |
4755 | nfsd4_grace); | 4934 | nn->nfsd4_grace, net); |
4935 | queue_delayed_work(laundry_wq, &nn->laundromat_work, nn->nfsd4_grace * HZ); | ||
4936 | return 0; | ||
4937 | } | ||
4938 | |||
4939 | /* initialization to perform when the nfsd service is started: */ | ||
4940 | |||
4941 | int | ||
4942 | nfs4_state_start(void) | ||
4943 | { | ||
4944 | int ret; | ||
4945 | |||
4756 | ret = set_callback_cred(); | 4946 | ret = set_callback_cred(); |
4757 | if (ret) { | 4947 | if (ret) |
4758 | ret = -ENOMEM; | 4948 | return -ENOMEM; |
4759 | goto out_recovery; | ||
4760 | } | ||
4761 | laundry_wq = create_singlethread_workqueue("nfsd4"); | 4949 | laundry_wq = create_singlethread_workqueue("nfsd4"); |
4762 | if (laundry_wq == NULL) { | 4950 | if (laundry_wq == NULL) { |
4763 | ret = -ENOMEM; | 4951 | ret = -ENOMEM; |
@@ -4766,39 +4954,34 @@ nfs4_state_start(void) | |||
4766 | ret = nfsd4_create_callback_queue(); | 4954 | ret = nfsd4_create_callback_queue(); |
4767 | if (ret) | 4955 | if (ret) |
4768 | goto out_free_laundry; | 4956 | goto out_free_laundry; |
4769 | queue_delayed_work(laundry_wq, &laundromat_work, nfsd4_grace * HZ); | 4957 | |
4770 | set_max_delegations(); | 4958 | set_max_delegations(); |
4959 | |||
4771 | return 0; | 4960 | return 0; |
4961 | |||
4772 | out_free_laundry: | 4962 | out_free_laundry: |
4773 | destroy_workqueue(laundry_wq); | 4963 | destroy_workqueue(laundry_wq); |
4774 | out_recovery: | 4964 | out_recovery: |
4775 | nfsd4_client_tracking_exit(net); | ||
4776 | put_net(net); | ||
4777 | return ret; | 4965 | return ret; |
4778 | } | 4966 | } |
4779 | 4967 | ||
4780 | static void | 4968 | /* should be called with the state lock held */ |
4781 | __nfs4_state_shutdown(void) | 4969 | void |
4970 | nfs4_state_shutdown_net(struct net *net) | ||
4782 | { | 4971 | { |
4783 | int i; | ||
4784 | struct nfs4_client *clp = NULL; | ||
4785 | struct nfs4_delegation *dp = NULL; | 4972 | struct nfs4_delegation *dp = NULL; |
4786 | struct list_head *pos, *next, reaplist; | 4973 | struct list_head *pos, *next, reaplist; |
4974 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); | ||
4975 | |||
4976 | cancel_delayed_work_sync(&nn->laundromat_work); | ||
4977 | locks_end_grace(&nn->nfsd4_manager); | ||
4787 | 4978 | ||
4788 | for (i = 0; i < CLIENT_HASH_SIZE; i++) { | ||
4789 | while (!list_empty(&conf_id_hashtbl[i])) { | ||
4790 | clp = list_entry(conf_id_hashtbl[i].next, struct nfs4_client, cl_idhash); | ||
4791 | destroy_client(clp); | ||
4792 | } | ||
4793 | while (!list_empty(&unconf_str_hashtbl[i])) { | ||
4794 | clp = list_entry(unconf_str_hashtbl[i].next, struct nfs4_client, cl_strhash); | ||
4795 | destroy_client(clp); | ||
4796 | } | ||
4797 | } | ||
4798 | INIT_LIST_HEAD(&reaplist); | 4979 | INIT_LIST_HEAD(&reaplist); |
4799 | spin_lock(&recall_lock); | 4980 | spin_lock(&recall_lock); |
4800 | list_for_each_safe(pos, next, &del_recall_lru) { | 4981 | list_for_each_safe(pos, next, &del_recall_lru) { |
4801 | dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru); | 4982 | dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru); |
4983 | if (dp->dl_stid.sc_client->net != net) | ||
4984 | continue; | ||
4802 | list_move(&dp->dl_recall_lru, &reaplist); | 4985 | list_move(&dp->dl_recall_lru, &reaplist); |
4803 | } | 4986 | } |
4804 | spin_unlock(&recall_lock); | 4987 | spin_unlock(&recall_lock); |
@@ -4807,22 +4990,14 @@ __nfs4_state_shutdown(void) | |||
4807 | unhash_delegation(dp); | 4990 | unhash_delegation(dp); |
4808 | } | 4991 | } |
4809 | 4992 | ||
4810 | nfsd4_client_tracking_exit(&init_net); | 4993 | nfsd4_client_tracking_exit(net); |
4811 | put_net(&init_net); | 4994 | nfs4_state_destroy_net(net); |
4812 | } | 4995 | } |
4813 | 4996 | ||
4814 | void | 4997 | void |
4815 | nfs4_state_shutdown(void) | 4998 | nfs4_state_shutdown(void) |
4816 | { | 4999 | { |
4817 | struct net *net = &init_net; | ||
4818 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); | ||
4819 | |||
4820 | cancel_delayed_work_sync(&laundromat_work); | ||
4821 | destroy_workqueue(laundry_wq); | 5000 | destroy_workqueue(laundry_wq); |
4822 | locks_end_grace(&nn->nfsd4_manager); | ||
4823 | nfs4_lock_state(); | ||
4824 | __nfs4_state_shutdown(); | ||
4825 | nfs4_unlock_state(); | ||
4826 | nfsd4_destroy_callback_queue(); | 5001 | nfsd4_destroy_callback_queue(); |
4827 | } | 5002 | } |
4828 | 5003 | ||
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index fd548d155088..0dc11586682f 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c | |||
@@ -53,6 +53,7 @@ | |||
53 | #include "vfs.h" | 53 | #include "vfs.h" |
54 | #include "state.h" | 54 | #include "state.h" |
55 | #include "cache.h" | 55 | #include "cache.h" |
56 | #include "netns.h" | ||
56 | 57 | ||
57 | #define NFSDDBG_FACILITY NFSDDBG_XDR | 58 | #define NFSDDBG_FACILITY NFSDDBG_XDR |
58 | 59 | ||
@@ -65,17 +66,17 @@ | |||
65 | #define NFS4_REFERRAL_FSID_MINOR 0x8000000ULL | 66 | #define NFS4_REFERRAL_FSID_MINOR 0x8000000ULL |
66 | 67 | ||
67 | static __be32 | 68 | static __be32 |
68 | check_filename(char *str, int len, __be32 err) | 69 | check_filename(char *str, int len) |
69 | { | 70 | { |
70 | int i; | 71 | int i; |
71 | 72 | ||
72 | if (len == 0) | 73 | if (len == 0) |
73 | return nfserr_inval; | 74 | return nfserr_inval; |
74 | if (isdotent(str, len)) | 75 | if (isdotent(str, len)) |
75 | return err; | 76 | return nfserr_badname; |
76 | for (i = 0; i < len; i++) | 77 | for (i = 0; i < len; i++) |
77 | if (str[i] == '/') | 78 | if (str[i] == '/') |
78 | return err; | 79 | return nfserr_badname; |
79 | return 0; | 80 | return 0; |
80 | } | 81 | } |
81 | 82 | ||
@@ -422,6 +423,86 @@ nfsd4_decode_access(struct nfsd4_compoundargs *argp, struct nfsd4_access *access | |||
422 | DECODE_TAIL; | 423 | DECODE_TAIL; |
423 | } | 424 | } |
424 | 425 | ||
426 | static __be32 nfsd4_decode_cb_sec(struct nfsd4_compoundargs *argp, struct nfsd4_cb_sec *cbs) | ||
427 | { | ||
428 | DECODE_HEAD; | ||
429 | u32 dummy, uid, gid; | ||
430 | char *machine_name; | ||
431 | int i; | ||
432 | int nr_secflavs; | ||
433 | |||
434 | /* callback_sec_params4 */ | ||
435 | READ_BUF(4); | ||
436 | READ32(nr_secflavs); | ||
437 | cbs->flavor = (u32)(-1); | ||
438 | for (i = 0; i < nr_secflavs; ++i) { | ||
439 | READ_BUF(4); | ||
440 | READ32(dummy); | ||
441 | switch (dummy) { | ||
442 | case RPC_AUTH_NULL: | ||
443 | /* Nothing to read */ | ||
444 | if (cbs->flavor == (u32)(-1)) | ||
445 | cbs->flavor = RPC_AUTH_NULL; | ||
446 | break; | ||
447 | case RPC_AUTH_UNIX: | ||
448 | READ_BUF(8); | ||
449 | /* stamp */ | ||
450 | READ32(dummy); | ||
451 | |||
452 | /* machine name */ | ||
453 | READ32(dummy); | ||
454 | READ_BUF(dummy); | ||
455 | SAVEMEM(machine_name, dummy); | ||
456 | |||
457 | /* uid, gid */ | ||
458 | READ_BUF(8); | ||
459 | READ32(uid); | ||
460 | READ32(gid); | ||
461 | |||
462 | /* more gids */ | ||
463 | READ_BUF(4); | ||
464 | READ32(dummy); | ||
465 | READ_BUF(dummy * 4); | ||
466 | if (cbs->flavor == (u32)(-1)) { | ||
467 | cbs->uid = uid; | ||
468 | cbs->gid = gid; | ||
469 | cbs->flavor = RPC_AUTH_UNIX; | ||
470 | } | ||
471 | break; | ||
472 | case RPC_AUTH_GSS: | ||
473 | dprintk("RPC_AUTH_GSS callback secflavor " | ||
474 | "not supported!\n"); | ||
475 | READ_BUF(8); | ||
476 | /* gcbp_service */ | ||
477 | READ32(dummy); | ||
478 | /* gcbp_handle_from_server */ | ||
479 | READ32(dummy); | ||
480 | READ_BUF(dummy); | ||
481 | p += XDR_QUADLEN(dummy); | ||
482 | /* gcbp_handle_from_client */ | ||
483 | READ_BUF(4); | ||
484 | READ32(dummy); | ||
485 | READ_BUF(dummy); | ||
486 | break; | ||
487 | default: | ||
488 | dprintk("Illegal callback secflavor\n"); | ||
489 | return nfserr_inval; | ||
490 | } | ||
491 | } | ||
492 | DECODE_TAIL; | ||
493 | } | ||
494 | |||
495 | static __be32 nfsd4_decode_backchannel_ctl(struct nfsd4_compoundargs *argp, struct nfsd4_backchannel_ctl *bc) | ||
496 | { | ||
497 | DECODE_HEAD; | ||
498 | |||
499 | READ_BUF(4); | ||
500 | READ32(bc->bc_cb_program); | ||
501 | nfsd4_decode_cb_sec(argp, &bc->bc_cb_sec); | ||
502 | |||
503 | DECODE_TAIL; | ||
504 | } | ||
505 | |||
425 | static __be32 nfsd4_decode_bind_conn_to_session(struct nfsd4_compoundargs *argp, struct nfsd4_bind_conn_to_session *bcts) | 506 | static __be32 nfsd4_decode_bind_conn_to_session(struct nfsd4_compoundargs *argp, struct nfsd4_bind_conn_to_session *bcts) |
426 | { | 507 | { |
427 | DECODE_HEAD; | 508 | DECODE_HEAD; |
@@ -490,7 +571,7 @@ nfsd4_decode_create(struct nfsd4_compoundargs *argp, struct nfsd4_create *create | |||
490 | READ32(create->cr_namelen); | 571 | READ32(create->cr_namelen); |
491 | READ_BUF(create->cr_namelen); | 572 | READ_BUF(create->cr_namelen); |
492 | SAVEMEM(create->cr_name, create->cr_namelen); | 573 | SAVEMEM(create->cr_name, create->cr_namelen); |
493 | if ((status = check_filename(create->cr_name, create->cr_namelen, nfserr_inval))) | 574 | if ((status = check_filename(create->cr_name, create->cr_namelen))) |
494 | return status; | 575 | return status; |
495 | 576 | ||
496 | status = nfsd4_decode_fattr(argp, create->cr_bmval, &create->cr_iattr, | 577 | status = nfsd4_decode_fattr(argp, create->cr_bmval, &create->cr_iattr, |
@@ -522,7 +603,7 @@ nfsd4_decode_link(struct nfsd4_compoundargs *argp, struct nfsd4_link *link) | |||
522 | READ32(link->li_namelen); | 603 | READ32(link->li_namelen); |
523 | READ_BUF(link->li_namelen); | 604 | READ_BUF(link->li_namelen); |
524 | SAVEMEM(link->li_name, link->li_namelen); | 605 | SAVEMEM(link->li_name, link->li_namelen); |
525 | if ((status = check_filename(link->li_name, link->li_namelen, nfserr_inval))) | 606 | if ((status = check_filename(link->li_name, link->li_namelen))) |
526 | return status; | 607 | return status; |
527 | 608 | ||
528 | DECODE_TAIL; | 609 | DECODE_TAIL; |
@@ -616,7 +697,7 @@ nfsd4_decode_lookup(struct nfsd4_compoundargs *argp, struct nfsd4_lookup *lookup | |||
616 | READ32(lookup->lo_len); | 697 | READ32(lookup->lo_len); |
617 | READ_BUF(lookup->lo_len); | 698 | READ_BUF(lookup->lo_len); |
618 | SAVEMEM(lookup->lo_name, lookup->lo_len); | 699 | SAVEMEM(lookup->lo_name, lookup->lo_len); |
619 | if ((status = check_filename(lookup->lo_name, lookup->lo_len, nfserr_noent))) | 700 | if ((status = check_filename(lookup->lo_name, lookup->lo_len))) |
620 | return status; | 701 | return status; |
621 | 702 | ||
622 | DECODE_TAIL; | 703 | DECODE_TAIL; |
@@ -780,7 +861,7 @@ nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open) | |||
780 | READ32(open->op_fname.len); | 861 | READ32(open->op_fname.len); |
781 | READ_BUF(open->op_fname.len); | 862 | READ_BUF(open->op_fname.len); |
782 | SAVEMEM(open->op_fname.data, open->op_fname.len); | 863 | SAVEMEM(open->op_fname.data, open->op_fname.len); |
783 | if ((status = check_filename(open->op_fname.data, open->op_fname.len, nfserr_inval))) | 864 | if ((status = check_filename(open->op_fname.data, open->op_fname.len))) |
784 | return status; | 865 | return status; |
785 | break; | 866 | break; |
786 | case NFS4_OPEN_CLAIM_PREVIOUS: | 867 | case NFS4_OPEN_CLAIM_PREVIOUS: |
@@ -795,7 +876,7 @@ nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open) | |||
795 | READ32(open->op_fname.len); | 876 | READ32(open->op_fname.len); |
796 | READ_BUF(open->op_fname.len); | 877 | READ_BUF(open->op_fname.len); |
797 | SAVEMEM(open->op_fname.data, open->op_fname.len); | 878 | SAVEMEM(open->op_fname.data, open->op_fname.len); |
798 | if ((status = check_filename(open->op_fname.data, open->op_fname.len, nfserr_inval))) | 879 | if ((status = check_filename(open->op_fname.data, open->op_fname.len))) |
799 | return status; | 880 | return status; |
800 | break; | 881 | break; |
801 | case NFS4_OPEN_CLAIM_FH: | 882 | case NFS4_OPEN_CLAIM_FH: |
@@ -907,7 +988,7 @@ nfsd4_decode_remove(struct nfsd4_compoundargs *argp, struct nfsd4_remove *remove | |||
907 | READ32(remove->rm_namelen); | 988 | READ32(remove->rm_namelen); |
908 | READ_BUF(remove->rm_namelen); | 989 | READ_BUF(remove->rm_namelen); |
909 | SAVEMEM(remove->rm_name, remove->rm_namelen); | 990 | SAVEMEM(remove->rm_name, remove->rm_namelen); |
910 | if ((status = check_filename(remove->rm_name, remove->rm_namelen, nfserr_noent))) | 991 | if ((status = check_filename(remove->rm_name, remove->rm_namelen))) |
911 | return status; | 992 | return status; |
912 | 993 | ||
913 | DECODE_TAIL; | 994 | DECODE_TAIL; |
@@ -925,9 +1006,9 @@ nfsd4_decode_rename(struct nfsd4_compoundargs *argp, struct nfsd4_rename *rename | |||
925 | READ32(rename->rn_tnamelen); | 1006 | READ32(rename->rn_tnamelen); |
926 | READ_BUF(rename->rn_tnamelen); | 1007 | READ_BUF(rename->rn_tnamelen); |
927 | SAVEMEM(rename->rn_tname, rename->rn_tnamelen); | 1008 | SAVEMEM(rename->rn_tname, rename->rn_tnamelen); |
928 | if ((status = check_filename(rename->rn_sname, rename->rn_snamelen, nfserr_noent))) | 1009 | if ((status = check_filename(rename->rn_sname, rename->rn_snamelen))) |
929 | return status; | 1010 | return status; |
930 | if ((status = check_filename(rename->rn_tname, rename->rn_tnamelen, nfserr_inval))) | 1011 | if ((status = check_filename(rename->rn_tname, rename->rn_tnamelen))) |
931 | return status; | 1012 | return status; |
932 | 1013 | ||
933 | DECODE_TAIL; | 1014 | DECODE_TAIL; |
@@ -954,8 +1035,7 @@ nfsd4_decode_secinfo(struct nfsd4_compoundargs *argp, | |||
954 | READ32(secinfo->si_namelen); | 1035 | READ32(secinfo->si_namelen); |
955 | READ_BUF(secinfo->si_namelen); | 1036 | READ_BUF(secinfo->si_namelen); |
956 | SAVEMEM(secinfo->si_name, secinfo->si_namelen); | 1037 | SAVEMEM(secinfo->si_name, secinfo->si_namelen); |
957 | status = check_filename(secinfo->si_name, secinfo->si_namelen, | 1038 | status = check_filename(secinfo->si_name, secinfo->si_namelen); |
958 | nfserr_noent); | ||
959 | if (status) | 1039 | if (status) |
960 | return status; | 1040 | return status; |
961 | DECODE_TAIL; | 1041 | DECODE_TAIL; |
@@ -1026,31 +1106,14 @@ nfsd4_decode_setclientid_confirm(struct nfsd4_compoundargs *argp, struct nfsd4_s | |||
1026 | static __be32 | 1106 | static __be32 |
1027 | nfsd4_decode_verify(struct nfsd4_compoundargs *argp, struct nfsd4_verify *verify) | 1107 | nfsd4_decode_verify(struct nfsd4_compoundargs *argp, struct nfsd4_verify *verify) |
1028 | { | 1108 | { |
1029 | #if 0 | ||
1030 | struct nfsd4_compoundargs save = { | ||
1031 | .p = argp->p, | ||
1032 | .end = argp->end, | ||
1033 | .rqstp = argp->rqstp, | ||
1034 | }; | ||
1035 | u32 ve_bmval[2]; | ||
1036 | struct iattr ve_iattr; /* request */ | ||
1037 | struct nfs4_acl *ve_acl; /* request */ | ||
1038 | #endif | ||
1039 | DECODE_HEAD; | 1109 | DECODE_HEAD; |
1040 | 1110 | ||
1041 | if ((status = nfsd4_decode_bitmap(argp, verify->ve_bmval))) | 1111 | if ((status = nfsd4_decode_bitmap(argp, verify->ve_bmval))) |
1042 | goto out; | 1112 | goto out; |
1043 | 1113 | ||
1044 | /* For convenience's sake, we compare raw xdr'd attributes in | 1114 | /* For convenience's sake, we compare raw xdr'd attributes in |
1045 | * nfsd4_proc_verify; however we still decode here just to return | 1115 | * nfsd4_proc_verify */ |
1046 | * correct error in case of bad xdr. */ | 1116 | |
1047 | #if 0 | ||
1048 | status = nfsd4_decode_fattr(ve_bmval, &ve_iattr, &ve_acl); | ||
1049 | if (status == nfserr_inval) { | ||
1050 | status = nfserrno(status); | ||
1051 | goto out; | ||
1052 | } | ||
1053 | #endif | ||
1054 | READ_BUF(4); | 1117 | READ_BUF(4); |
1055 | READ32(verify->ve_attrlen); | 1118 | READ32(verify->ve_attrlen); |
1056 | READ_BUF(verify->ve_attrlen); | 1119 | READ_BUF(verify->ve_attrlen); |
@@ -1063,7 +1126,6 @@ static __be32 | |||
1063 | nfsd4_decode_write(struct nfsd4_compoundargs *argp, struct nfsd4_write *write) | 1126 | nfsd4_decode_write(struct nfsd4_compoundargs *argp, struct nfsd4_write *write) |
1064 | { | 1127 | { |
1065 | int avail; | 1128 | int avail; |
1066 | int v; | ||
1067 | int len; | 1129 | int len; |
1068 | DECODE_HEAD; | 1130 | DECODE_HEAD; |
1069 | 1131 | ||
@@ -1087,27 +1149,26 @@ nfsd4_decode_write(struct nfsd4_compoundargs *argp, struct nfsd4_write *write) | |||
1087 | __FILE__, __LINE__); | 1149 | __FILE__, __LINE__); |
1088 | goto xdr_error; | 1150 | goto xdr_error; |
1089 | } | 1151 | } |
1090 | argp->rqstp->rq_vec[0].iov_base = p; | 1152 | write->wr_head.iov_base = p; |
1091 | argp->rqstp->rq_vec[0].iov_len = avail; | 1153 | write->wr_head.iov_len = avail; |
1092 | v = 0; | 1154 | WARN_ON(avail != (XDR_QUADLEN(avail) << 2)); |
1093 | len = write->wr_buflen; | 1155 | write->wr_pagelist = argp->pagelist; |
1094 | while (len > argp->rqstp->rq_vec[v].iov_len) { | 1156 | |
1095 | len -= argp->rqstp->rq_vec[v].iov_len; | 1157 | len = XDR_QUADLEN(write->wr_buflen) << 2; |
1096 | v++; | 1158 | if (len >= avail) { |
1097 | argp->rqstp->rq_vec[v].iov_base = page_address(argp->pagelist[0]); | 1159 | int pages; |
1098 | argp->pagelist++; | 1160 | |
1099 | if (argp->pagelen >= PAGE_SIZE) { | 1161 | len -= avail; |
1100 | argp->rqstp->rq_vec[v].iov_len = PAGE_SIZE; | 1162 | |
1101 | argp->pagelen -= PAGE_SIZE; | 1163 | pages = len >> PAGE_SHIFT; |
1102 | } else { | 1164 | argp->pagelist += pages; |
1103 | argp->rqstp->rq_vec[v].iov_len = argp->pagelen; | 1165 | argp->pagelen -= pages * PAGE_SIZE; |
1104 | argp->pagelen -= len; | 1166 | len -= pages * PAGE_SIZE; |
1105 | } | 1167 | |
1168 | argp->p = (__be32 *)page_address(argp->pagelist[0]); | ||
1169 | argp->end = argp->p + XDR_QUADLEN(PAGE_SIZE); | ||
1106 | } | 1170 | } |
1107 | argp->end = (__be32*) (argp->rqstp->rq_vec[v].iov_base + argp->rqstp->rq_vec[v].iov_len); | 1171 | argp->p += XDR_QUADLEN(len); |
1108 | argp->p = (__be32*) (argp->rqstp->rq_vec[v].iov_base + (XDR_QUADLEN(len) << 2)); | ||
1109 | argp->rqstp->rq_vec[v].iov_len = len; | ||
1110 | write->wr_vlen = v+1; | ||
1111 | 1172 | ||
1112 | DECODE_TAIL; | 1173 | DECODE_TAIL; |
1113 | } | 1174 | } |
@@ -1237,11 +1298,7 @@ nfsd4_decode_create_session(struct nfsd4_compoundargs *argp, | |||
1237 | struct nfsd4_create_session *sess) | 1298 | struct nfsd4_create_session *sess) |
1238 | { | 1299 | { |
1239 | DECODE_HEAD; | 1300 | DECODE_HEAD; |
1240 | |||
1241 | u32 dummy; | 1301 | u32 dummy; |
1242 | char *machine_name; | ||
1243 | int i; | ||
1244 | int nr_secflavs; | ||
1245 | 1302 | ||
1246 | READ_BUF(16); | 1303 | READ_BUF(16); |
1247 | COPYMEM(&sess->clientid, 8); | 1304 | COPYMEM(&sess->clientid, 8); |
@@ -1282,58 +1339,9 @@ nfsd4_decode_create_session(struct nfsd4_compoundargs *argp, | |||
1282 | goto xdr_error; | 1339 | goto xdr_error; |
1283 | } | 1340 | } |
1284 | 1341 | ||
1285 | READ_BUF(8); | 1342 | READ_BUF(4); |
1286 | READ32(sess->callback_prog); | 1343 | READ32(sess->callback_prog); |
1287 | 1344 | nfsd4_decode_cb_sec(argp, &sess->cb_sec); | |
1288 | /* callback_sec_params4 */ | ||
1289 | READ32(nr_secflavs); | ||
1290 | for (i = 0; i < nr_secflavs; ++i) { | ||
1291 | READ_BUF(4); | ||
1292 | READ32(dummy); | ||
1293 | switch (dummy) { | ||
1294 | case RPC_AUTH_NULL: | ||
1295 | /* Nothing to read */ | ||
1296 | break; | ||
1297 | case RPC_AUTH_UNIX: | ||
1298 | READ_BUF(8); | ||
1299 | /* stamp */ | ||
1300 | READ32(dummy); | ||
1301 | |||
1302 | /* machine name */ | ||
1303 | READ32(dummy); | ||
1304 | READ_BUF(dummy); | ||
1305 | SAVEMEM(machine_name, dummy); | ||
1306 | |||
1307 | /* uid, gid */ | ||
1308 | READ_BUF(8); | ||
1309 | READ32(sess->uid); | ||
1310 | READ32(sess->gid); | ||
1311 | |||
1312 | /* more gids */ | ||
1313 | READ_BUF(4); | ||
1314 | READ32(dummy); | ||
1315 | READ_BUF(dummy * 4); | ||
1316 | break; | ||
1317 | case RPC_AUTH_GSS: | ||
1318 | dprintk("RPC_AUTH_GSS callback secflavor " | ||
1319 | "not supported!\n"); | ||
1320 | READ_BUF(8); | ||
1321 | /* gcbp_service */ | ||
1322 | READ32(dummy); | ||
1323 | /* gcbp_handle_from_server */ | ||
1324 | READ32(dummy); | ||
1325 | READ_BUF(dummy); | ||
1326 | p += XDR_QUADLEN(dummy); | ||
1327 | /* gcbp_handle_from_client */ | ||
1328 | READ_BUF(4); | ||
1329 | READ32(dummy); | ||
1330 | READ_BUF(dummy); | ||
1331 | break; | ||
1332 | default: | ||
1333 | dprintk("Illegal callback secflavor\n"); | ||
1334 | return nfserr_inval; | ||
1335 | } | ||
1336 | } | ||
1337 | DECODE_TAIL; | 1345 | DECODE_TAIL; |
1338 | } | 1346 | } |
1339 | 1347 | ||
@@ -1528,7 +1536,7 @@ static nfsd4_dec nfsd41_dec_ops[] = { | |||
1528 | [OP_RELEASE_LOCKOWNER] = (nfsd4_dec)nfsd4_decode_notsupp, | 1536 | [OP_RELEASE_LOCKOWNER] = (nfsd4_dec)nfsd4_decode_notsupp, |
1529 | 1537 | ||
1530 | /* new operations for NFSv4.1 */ | 1538 | /* new operations for NFSv4.1 */ |
1531 | [OP_BACKCHANNEL_CTL] = (nfsd4_dec)nfsd4_decode_notsupp, | 1539 | [OP_BACKCHANNEL_CTL] = (nfsd4_dec)nfsd4_decode_backchannel_ctl, |
1532 | [OP_BIND_CONN_TO_SESSION]= (nfsd4_dec)nfsd4_decode_bind_conn_to_session, | 1540 | [OP_BIND_CONN_TO_SESSION]= (nfsd4_dec)nfsd4_decode_bind_conn_to_session, |
1533 | [OP_EXCHANGE_ID] = (nfsd4_dec)nfsd4_decode_exchange_id, | 1541 | [OP_EXCHANGE_ID] = (nfsd4_dec)nfsd4_decode_exchange_id, |
1534 | [OP_CREATE_SESSION] = (nfsd4_dec)nfsd4_decode_create_session, | 1542 | [OP_CREATE_SESSION] = (nfsd4_dec)nfsd4_decode_create_session, |
@@ -1568,12 +1576,6 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp) | |||
1568 | bool cachethis = false; | 1576 | bool cachethis = false; |
1569 | int i; | 1577 | int i; |
1570 | 1578 | ||
1571 | /* | ||
1572 | * XXX: According to spec, we should check the tag | ||
1573 | * for UTF-8 compliance. I'm postponing this for | ||
1574 | * now because it seems that some clients do use | ||
1575 | * binary tags. | ||
1576 | */ | ||
1577 | READ_BUF(4); | 1579 | READ_BUF(4); |
1578 | READ32(argp->taglen); | 1580 | READ32(argp->taglen); |
1579 | READ_BUF(argp->taglen + 8); | 1581 | READ_BUF(argp->taglen + 8); |
@@ -1603,38 +1605,8 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp) | |||
1603 | op = &argp->ops[i]; | 1605 | op = &argp->ops[i]; |
1604 | op->replay = NULL; | 1606 | op->replay = NULL; |
1605 | 1607 | ||
1606 | /* | 1608 | READ_BUF(4); |
1607 | * We can't use READ_BUF() here because we need to handle | 1609 | READ32(op->opnum); |
1608 | * a missing opcode as an OP_WRITE + 1. So we need to check | ||
1609 | * to see if we're truly at the end of our buffer or if there | ||
1610 | * is another page we need to flip to. | ||
1611 | */ | ||
1612 | |||
1613 | if (argp->p == argp->end) { | ||
1614 | if (argp->pagelen < 4) { | ||
1615 | /* There isn't an opcode still on the wire */ | ||
1616 | op->opnum = OP_WRITE + 1; | ||
1617 | op->status = nfserr_bad_xdr; | ||
1618 | argp->opcnt = i+1; | ||
1619 | break; | ||
1620 | } | ||
1621 | |||
1622 | /* | ||
1623 | * False alarm. We just hit a page boundary, but there | ||
1624 | * is still data available. Move pointer across page | ||
1625 | * boundary. *snip from READ_BUF* | ||
1626 | */ | ||
1627 | argp->p = page_address(argp->pagelist[0]); | ||
1628 | argp->pagelist++; | ||
1629 | if (argp->pagelen < PAGE_SIZE) { | ||
1630 | argp->end = argp->p + (argp->pagelen>>2); | ||
1631 | argp->pagelen = 0; | ||
1632 | } else { | ||
1633 | argp->end = argp->p + (PAGE_SIZE>>2); | ||
1634 | argp->pagelen -= PAGE_SIZE; | ||
1635 | } | ||
1636 | } | ||
1637 | op->opnum = ntohl(*argp->p++); | ||
1638 | 1610 | ||
1639 | if (op->opnum >= FIRST_NFS4_OP && op->opnum <= LAST_NFS4_OP) | 1611 | if (op->opnum >= FIRST_NFS4_OP && op->opnum <= LAST_NFS4_OP) |
1640 | op->status = ops->decoders[op->opnum](argp, &op->u); | 1612 | op->status = ops->decoders[op->opnum](argp, &op->u); |
@@ -2014,6 +1986,22 @@ static __be32 fattr_handle_absent_fs(u32 *bmval0, u32 *bmval1, u32 *rdattr_err) | |||
2014 | return 0; | 1986 | return 0; |
2015 | } | 1987 | } |
2016 | 1988 | ||
1989 | |||
1990 | static int get_parent_attributes(struct svc_export *exp, struct kstat *stat) | ||
1991 | { | ||
1992 | struct path path = exp->ex_path; | ||
1993 | int err; | ||
1994 | |||
1995 | path_get(&path); | ||
1996 | while (follow_up(&path)) { | ||
1997 | if (path.dentry != path.mnt->mnt_root) | ||
1998 | break; | ||
1999 | } | ||
2000 | err = vfs_getattr(path.mnt, path.dentry, stat); | ||
2001 | path_put(&path); | ||
2002 | return err; | ||
2003 | } | ||
2004 | |||
2017 | /* | 2005 | /* |
2018 | * Note: @fhp can be NULL; in this case, we might have to compose the filehandle | 2006 | * Note: @fhp can be NULL; in this case, we might have to compose the filehandle |
2019 | * ourselves. | 2007 | * ourselves. |
@@ -2048,6 +2036,7 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp, | |||
2048 | .mnt = exp->ex_path.mnt, | 2036 | .mnt = exp->ex_path.mnt, |
2049 | .dentry = dentry, | 2037 | .dentry = dentry, |
2050 | }; | 2038 | }; |
2039 | struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); | ||
2051 | 2040 | ||
2052 | BUG_ON(bmval1 & NFSD_WRITEONLY_ATTRS_WORD1); | 2041 | BUG_ON(bmval1 & NFSD_WRITEONLY_ATTRS_WORD1); |
2053 | BUG_ON(bmval0 & ~nfsd_suppattrs0(minorversion)); | 2042 | BUG_ON(bmval0 & ~nfsd_suppattrs0(minorversion)); |
@@ -2208,7 +2197,7 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp, | |||
2208 | if (bmval0 & FATTR4_WORD0_LEASE_TIME) { | 2197 | if (bmval0 & FATTR4_WORD0_LEASE_TIME) { |
2209 | if ((buflen -= 4) < 0) | 2198 | if ((buflen -= 4) < 0) |
2210 | goto out_resource; | 2199 | goto out_resource; |
2211 | WRITE32(nfsd4_lease); | 2200 | WRITE32(nn->nfsd4_lease); |
2212 | } | 2201 | } |
2213 | if (bmval0 & FATTR4_WORD0_RDATTR_ERROR) { | 2202 | if (bmval0 & FATTR4_WORD0_RDATTR_ERROR) { |
2214 | if ((buflen -= 4) < 0) | 2203 | if ((buflen -= 4) < 0) |
@@ -2430,18 +2419,8 @@ out_acl: | |||
2430 | * and this is the root of a cross-mounted filesystem. | 2419 | * and this is the root of a cross-mounted filesystem. |
2431 | */ | 2420 | */ |
2432 | if (ignore_crossmnt == 0 && | 2421 | if (ignore_crossmnt == 0 && |
2433 | dentry == exp->ex_path.mnt->mnt_root) { | 2422 | dentry == exp->ex_path.mnt->mnt_root) |
2434 | struct path path = exp->ex_path; | 2423 | get_parent_attributes(exp, &stat); |
2435 | path_get(&path); | ||
2436 | while (follow_up(&path)) { | ||
2437 | if (path.dentry != path.mnt->mnt_root) | ||
2438 | break; | ||
2439 | } | ||
2440 | err = vfs_getattr(path.mnt, path.dentry, &stat); | ||
2441 | path_put(&path); | ||
2442 | if (err) | ||
2443 | goto out_nfserr; | ||
2444 | } | ||
2445 | WRITE64(stat.ino); | 2424 | WRITE64(stat.ino); |
2446 | } | 2425 | } |
2447 | if (bmval2 & FATTR4_WORD2_SUPPATTR_EXCLCREAT) { | 2426 | if (bmval2 & FATTR4_WORD2_SUPPATTR_EXCLCREAT) { |
@@ -2927,7 +2906,8 @@ nfsd4_encode_read(struct nfsd4_compoundres *resp, __be32 nfserr, | |||
2927 | struct nfsd4_read *read) | 2906 | struct nfsd4_read *read) |
2928 | { | 2907 | { |
2929 | u32 eof; | 2908 | u32 eof; |
2930 | int v, pn; | 2909 | int v; |
2910 | struct page *page; | ||
2931 | unsigned long maxcount; | 2911 | unsigned long maxcount; |
2932 | long len; | 2912 | long len; |
2933 | __be32 *p; | 2913 | __be32 *p; |
@@ -2946,11 +2926,15 @@ nfsd4_encode_read(struct nfsd4_compoundres *resp, __be32 nfserr, | |||
2946 | len = maxcount; | 2926 | len = maxcount; |
2947 | v = 0; | 2927 | v = 0; |
2948 | while (len > 0) { | 2928 | while (len > 0) { |
2949 | pn = resp->rqstp->rq_resused++; | 2929 | page = *(resp->rqstp->rq_next_page); |
2950 | resp->rqstp->rq_vec[v].iov_base = | 2930 | if (!page) { /* ran out of pages */ |
2951 | page_address(resp->rqstp->rq_respages[pn]); | 2931 | maxcount -= len; |
2932 | break; | ||
2933 | } | ||
2934 | resp->rqstp->rq_vec[v].iov_base = page_address(page); | ||
2952 | resp->rqstp->rq_vec[v].iov_len = | 2935 | resp->rqstp->rq_vec[v].iov_len = |
2953 | len < PAGE_SIZE ? len : PAGE_SIZE; | 2936 | len < PAGE_SIZE ? len : PAGE_SIZE; |
2937 | resp->rqstp->rq_next_page++; | ||
2954 | v++; | 2938 | v++; |
2955 | len -= PAGE_SIZE; | 2939 | len -= PAGE_SIZE; |
2956 | } | 2940 | } |
@@ -2996,8 +2980,10 @@ nfsd4_encode_readlink(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd | |||
2996 | return nfserr; | 2980 | return nfserr; |
2997 | if (resp->xbuf->page_len) | 2981 | if (resp->xbuf->page_len) |
2998 | return nfserr_resource; | 2982 | return nfserr_resource; |
2983 | if (!*resp->rqstp->rq_next_page) | ||
2984 | return nfserr_resource; | ||
2999 | 2985 | ||
3000 | page = page_address(resp->rqstp->rq_respages[resp->rqstp->rq_resused++]); | 2986 | page = page_address(*(resp->rqstp->rq_next_page++)); |
3001 | 2987 | ||
3002 | maxcount = PAGE_SIZE; | 2988 | maxcount = PAGE_SIZE; |
3003 | RESERVE_SPACE(4); | 2989 | RESERVE_SPACE(4); |
@@ -3045,6 +3031,8 @@ nfsd4_encode_readdir(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4 | |||
3045 | return nfserr; | 3031 | return nfserr; |
3046 | if (resp->xbuf->page_len) | 3032 | if (resp->xbuf->page_len) |
3047 | return nfserr_resource; | 3033 | return nfserr_resource; |
3034 | if (!*resp->rqstp->rq_next_page) | ||
3035 | return nfserr_resource; | ||
3048 | 3036 | ||
3049 | RESERVE_SPACE(NFS4_VERIFIER_SIZE); | 3037 | RESERVE_SPACE(NFS4_VERIFIER_SIZE); |
3050 | savep = p; | 3038 | savep = p; |
@@ -3071,7 +3059,7 @@ nfsd4_encode_readdir(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4 | |||
3071 | goto err_no_verf; | 3059 | goto err_no_verf; |
3072 | } | 3060 | } |
3073 | 3061 | ||
3074 | page = page_address(resp->rqstp->rq_respages[resp->rqstp->rq_resused++]); | 3062 | page = page_address(*(resp->rqstp->rq_next_page++)); |
3075 | readdir->common.err = 0; | 3063 | readdir->common.err = 0; |
3076 | readdir->buflen = maxcount; | 3064 | readdir->buflen = maxcount; |
3077 | readdir->buffer = page; | 3065 | readdir->buffer = page; |
@@ -3094,8 +3082,8 @@ nfsd4_encode_readdir(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4 | |||
3094 | p = readdir->buffer; | 3082 | p = readdir->buffer; |
3095 | *p++ = 0; /* no more entries */ | 3083 | *p++ = 0; /* no more entries */ |
3096 | *p++ = htonl(readdir->common.err == nfserr_eof); | 3084 | *p++ = htonl(readdir->common.err == nfserr_eof); |
3097 | resp->xbuf->page_len = ((char*)p) - (char*)page_address( | 3085 | resp->xbuf->page_len = ((char*)p) - |
3098 | resp->rqstp->rq_respages[resp->rqstp->rq_resused-1]); | 3086 | (char*)page_address(*(resp->rqstp->rq_next_page-1)); |
3099 | 3087 | ||
3100 | /* Use rest of head for padding and remaining ops: */ | 3088 | /* Use rest of head for padding and remaining ops: */ |
3101 | resp->xbuf->tail[0].iov_base = tailbase; | 3089 | resp->xbuf->tail[0].iov_base = tailbase; |
diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index dab350dfc376..74934284d9a7 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c | |||
@@ -19,7 +19,7 @@ | |||
19 | #include "idmap.h" | 19 | #include "idmap.h" |
20 | #include "nfsd.h" | 20 | #include "nfsd.h" |
21 | #include "cache.h" | 21 | #include "cache.h" |
22 | #include "fault_inject.h" | 22 | #include "state.h" |
23 | #include "netns.h" | 23 | #include "netns.h" |
24 | 24 | ||
25 | /* | 25 | /* |
@@ -186,9 +186,6 @@ static struct file_operations supported_enctypes_ops = { | |||
186 | }; | 186 | }; |
187 | #endif /* CONFIG_SUNRPC_GSS or CONFIG_SUNRPC_GSS_MODULE */ | 187 | #endif /* CONFIG_SUNRPC_GSS or CONFIG_SUNRPC_GSS_MODULE */ |
188 | 188 | ||
189 | extern int nfsd_pool_stats_open(struct inode *inode, struct file *file); | ||
190 | extern int nfsd_pool_stats_release(struct inode *inode, struct file *file); | ||
191 | |||
192 | static const struct file_operations pool_stats_operations = { | 189 | static const struct file_operations pool_stats_operations = { |
193 | .open = nfsd_pool_stats_open, | 190 | .open = nfsd_pool_stats_open, |
194 | .read = seq_read, | 191 | .read = seq_read, |
@@ -399,6 +396,8 @@ static ssize_t write_threads(struct file *file, char *buf, size_t size) | |||
399 | { | 396 | { |
400 | char *mesg = buf; | 397 | char *mesg = buf; |
401 | int rv; | 398 | int rv; |
399 | struct net *net = &init_net; | ||
400 | |||
402 | if (size > 0) { | 401 | if (size > 0) { |
403 | int newthreads; | 402 | int newthreads; |
404 | rv = get_int(&mesg, &newthreads); | 403 | rv = get_int(&mesg, &newthreads); |
@@ -406,11 +405,11 @@ static ssize_t write_threads(struct file *file, char *buf, size_t size) | |||
406 | return rv; | 405 | return rv; |
407 | if (newthreads < 0) | 406 | if (newthreads < 0) |
408 | return -EINVAL; | 407 | return -EINVAL; |
409 | rv = nfsd_svc(newthreads); | 408 | rv = nfsd_svc(newthreads, net); |
410 | if (rv < 0) | 409 | if (rv < 0) |
411 | return rv; | 410 | return rv; |
412 | } else | 411 | } else |
413 | rv = nfsd_nrthreads(); | 412 | rv = nfsd_nrthreads(net); |
414 | 413 | ||
415 | return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%d\n", rv); | 414 | return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%d\n", rv); |
416 | } | 415 | } |
@@ -448,9 +447,10 @@ static ssize_t write_pool_threads(struct file *file, char *buf, size_t size) | |||
448 | int len; | 447 | int len; |
449 | int npools; | 448 | int npools; |
450 | int *nthreads; | 449 | int *nthreads; |
450 | struct net *net = &init_net; | ||
451 | 451 | ||
452 | mutex_lock(&nfsd_mutex); | 452 | mutex_lock(&nfsd_mutex); |
453 | npools = nfsd_nrpools(); | 453 | npools = nfsd_nrpools(net); |
454 | if (npools == 0) { | 454 | if (npools == 0) { |
455 | /* | 455 | /* |
456 | * NFS is shut down. The admin can start it by | 456 | * NFS is shut down. The admin can start it by |
@@ -478,12 +478,12 @@ static ssize_t write_pool_threads(struct file *file, char *buf, size_t size) | |||
478 | if (nthreads[i] < 0) | 478 | if (nthreads[i] < 0) |
479 | goto out_free; | 479 | goto out_free; |
480 | } | 480 | } |
481 | rv = nfsd_set_nrthreads(i, nthreads); | 481 | rv = nfsd_set_nrthreads(i, nthreads, net); |
482 | if (rv) | 482 | if (rv) |
483 | goto out_free; | 483 | goto out_free; |
484 | } | 484 | } |
485 | 485 | ||
486 | rv = nfsd_get_nrthreads(npools, nthreads); | 486 | rv = nfsd_get_nrthreads(npools, nthreads, net); |
487 | if (rv) | 487 | if (rv) |
488 | goto out_free; | 488 | goto out_free; |
489 | 489 | ||
@@ -510,11 +510,13 @@ static ssize_t __write_versions(struct file *file, char *buf, size_t size) | |||
510 | unsigned minor; | 510 | unsigned minor; |
511 | ssize_t tlen = 0; | 511 | ssize_t tlen = 0; |
512 | char *sep; | 512 | char *sep; |
513 | struct net *net = &init_net; | ||
514 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); | ||
513 | 515 | ||
514 | if (size>0) { | 516 | if (size>0) { |
515 | if (nfsd_serv) | 517 | if (nn->nfsd_serv) |
516 | /* Cannot change versions without updating | 518 | /* Cannot change versions without updating |
517 | * nfsd_serv->sv_xdrsize, and reallocing | 519 | * nn->nfsd_serv->sv_xdrsize, and reallocing |
518 | * rq_argp and rq_resp | 520 | * rq_argp and rq_resp |
519 | */ | 521 | */ |
520 | return -EBUSY; | 522 | return -EBUSY; |
@@ -645,11 +647,13 @@ static ssize_t write_versions(struct file *file, char *buf, size_t size) | |||
645 | * Zero-length write. Return a list of NFSD's current listener | 647 | * Zero-length write. Return a list of NFSD's current listener |
646 | * transports. | 648 | * transports. |
647 | */ | 649 | */ |
648 | static ssize_t __write_ports_names(char *buf) | 650 | static ssize_t __write_ports_names(char *buf, struct net *net) |
649 | { | 651 | { |
650 | if (nfsd_serv == NULL) | 652 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); |
653 | |||
654 | if (nn->nfsd_serv == NULL) | ||
651 | return 0; | 655 | return 0; |
652 | return svc_xprt_names(nfsd_serv, buf, SIMPLE_TRANSACTION_LIMIT); | 656 | return svc_xprt_names(nn->nfsd_serv, buf, SIMPLE_TRANSACTION_LIMIT); |
653 | } | 657 | } |
654 | 658 | ||
655 | /* | 659 | /* |
@@ -657,28 +661,28 @@ static ssize_t __write_ports_names(char *buf) | |||
657 | * a socket of a supported family/protocol, and we use it as an | 661 | * a socket of a supported family/protocol, and we use it as an |
658 | * nfsd listener. | 662 | * nfsd listener. |
659 | */ | 663 | */ |
660 | static ssize_t __write_ports_addfd(char *buf) | 664 | static ssize_t __write_ports_addfd(char *buf, struct net *net) |
661 | { | 665 | { |
662 | char *mesg = buf; | 666 | char *mesg = buf; |
663 | int fd, err; | 667 | int fd, err; |
664 | struct net *net = &init_net; | 668 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); |
665 | 669 | ||
666 | err = get_int(&mesg, &fd); | 670 | err = get_int(&mesg, &fd); |
667 | if (err != 0 || fd < 0) | 671 | if (err != 0 || fd < 0) |
668 | return -EINVAL; | 672 | return -EINVAL; |
669 | 673 | ||
670 | err = nfsd_create_serv(); | 674 | err = nfsd_create_serv(net); |
671 | if (err != 0) | 675 | if (err != 0) |
672 | return err; | 676 | return err; |
673 | 677 | ||
674 | err = svc_addsock(nfsd_serv, fd, buf, SIMPLE_TRANSACTION_LIMIT); | 678 | err = svc_addsock(nn->nfsd_serv, fd, buf, SIMPLE_TRANSACTION_LIMIT); |
675 | if (err < 0) { | 679 | if (err < 0) { |
676 | nfsd_destroy(net); | 680 | nfsd_destroy(net); |
677 | return err; | 681 | return err; |
678 | } | 682 | } |
679 | 683 | ||
680 | /* Decrease the count, but don't shut down the service */ | 684 | /* Decrease the count, but don't shut down the service */ |
681 | nfsd_serv->sv_nrthreads--; | 685 | nn->nfsd_serv->sv_nrthreads--; |
682 | return err; | 686 | return err; |
683 | } | 687 | } |
684 | 688 | ||
@@ -686,12 +690,12 @@ static ssize_t __write_ports_addfd(char *buf) | |||
686 | * A transport listener is added by writing it's transport name and | 690 | * A transport listener is added by writing it's transport name and |
687 | * a port number. | 691 | * a port number. |
688 | */ | 692 | */ |
689 | static ssize_t __write_ports_addxprt(char *buf) | 693 | static ssize_t __write_ports_addxprt(char *buf, struct net *net) |
690 | { | 694 | { |
691 | char transport[16]; | 695 | char transport[16]; |
692 | struct svc_xprt *xprt; | 696 | struct svc_xprt *xprt; |
693 | int port, err; | 697 | int port, err; |
694 | struct net *net = &init_net; | 698 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); |
695 | 699 | ||
696 | if (sscanf(buf, "%15s %5u", transport, &port) != 2) | 700 | if (sscanf(buf, "%15s %5u", transport, &port) != 2) |
697 | return -EINVAL; | 701 | return -EINVAL; |
@@ -699,25 +703,25 @@ static ssize_t __write_ports_addxprt(char *buf) | |||
699 | if (port < 1 || port > USHRT_MAX) | 703 | if (port < 1 || port > USHRT_MAX) |
700 | return -EINVAL; | 704 | return -EINVAL; |
701 | 705 | ||
702 | err = nfsd_create_serv(); | 706 | err = nfsd_create_serv(net); |
703 | if (err != 0) | 707 | if (err != 0) |
704 | return err; | 708 | return err; |
705 | 709 | ||
706 | err = svc_create_xprt(nfsd_serv, transport, net, | 710 | err = svc_create_xprt(nn->nfsd_serv, transport, net, |
707 | PF_INET, port, SVC_SOCK_ANONYMOUS); | 711 | PF_INET, port, SVC_SOCK_ANONYMOUS); |
708 | if (err < 0) | 712 | if (err < 0) |
709 | goto out_err; | 713 | goto out_err; |
710 | 714 | ||
711 | err = svc_create_xprt(nfsd_serv, transport, net, | 715 | err = svc_create_xprt(nn->nfsd_serv, transport, net, |
712 | PF_INET6, port, SVC_SOCK_ANONYMOUS); | 716 | PF_INET6, port, SVC_SOCK_ANONYMOUS); |
713 | if (err < 0 && err != -EAFNOSUPPORT) | 717 | if (err < 0 && err != -EAFNOSUPPORT) |
714 | goto out_close; | 718 | goto out_close; |
715 | 719 | ||
716 | /* Decrease the count, but don't shut down the service */ | 720 | /* Decrease the count, but don't shut down the service */ |
717 | nfsd_serv->sv_nrthreads--; | 721 | nn->nfsd_serv->sv_nrthreads--; |
718 | return 0; | 722 | return 0; |
719 | out_close: | 723 | out_close: |
720 | xprt = svc_find_xprt(nfsd_serv, transport, net, PF_INET, port); | 724 | xprt = svc_find_xprt(nn->nfsd_serv, transport, net, PF_INET, port); |
721 | if (xprt != NULL) { | 725 | if (xprt != NULL) { |
722 | svc_close_xprt(xprt); | 726 | svc_close_xprt(xprt); |
723 | svc_xprt_put(xprt); | 727 | svc_xprt_put(xprt); |
@@ -727,16 +731,17 @@ out_err: | |||
727 | return err; | 731 | return err; |
728 | } | 732 | } |
729 | 733 | ||
730 | static ssize_t __write_ports(struct file *file, char *buf, size_t size) | 734 | static ssize_t __write_ports(struct file *file, char *buf, size_t size, |
735 | struct net *net) | ||
731 | { | 736 | { |
732 | if (size == 0) | 737 | if (size == 0) |
733 | return __write_ports_names(buf); | 738 | return __write_ports_names(buf, net); |
734 | 739 | ||
735 | if (isdigit(buf[0])) | 740 | if (isdigit(buf[0])) |
736 | return __write_ports_addfd(buf); | 741 | return __write_ports_addfd(buf, net); |
737 | 742 | ||
738 | if (isalpha(buf[0])) | 743 | if (isalpha(buf[0])) |
739 | return __write_ports_addxprt(buf); | 744 | return __write_ports_addxprt(buf, net); |
740 | 745 | ||
741 | return -EINVAL; | 746 | return -EINVAL; |
742 | } | 747 | } |
@@ -787,9 +792,10 @@ static ssize_t __write_ports(struct file *file, char *buf, size_t size) | |||
787 | static ssize_t write_ports(struct file *file, char *buf, size_t size) | 792 | static ssize_t write_ports(struct file *file, char *buf, size_t size) |
788 | { | 793 | { |
789 | ssize_t rv; | 794 | ssize_t rv; |
795 | struct net *net = &init_net; | ||
790 | 796 | ||
791 | mutex_lock(&nfsd_mutex); | 797 | mutex_lock(&nfsd_mutex); |
792 | rv = __write_ports(file, buf, size); | 798 | rv = __write_ports(file, buf, size, net); |
793 | mutex_unlock(&nfsd_mutex); | 799 | mutex_unlock(&nfsd_mutex); |
794 | return rv; | 800 | return rv; |
795 | } | 801 | } |
@@ -821,6 +827,9 @@ int nfsd_max_blksize; | |||
821 | static ssize_t write_maxblksize(struct file *file, char *buf, size_t size) | 827 | static ssize_t write_maxblksize(struct file *file, char *buf, size_t size) |
822 | { | 828 | { |
823 | char *mesg = buf; | 829 | char *mesg = buf; |
830 | struct net *net = &init_net; | ||
831 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); | ||
832 | |||
824 | if (size > 0) { | 833 | if (size > 0) { |
825 | int bsize; | 834 | int bsize; |
826 | int rv = get_int(&mesg, &bsize); | 835 | int rv = get_int(&mesg, &bsize); |
@@ -835,7 +844,7 @@ static ssize_t write_maxblksize(struct file *file, char *buf, size_t size) | |||
835 | bsize = NFSSVC_MAXBLKSIZE; | 844 | bsize = NFSSVC_MAXBLKSIZE; |
836 | bsize &= ~(1024-1); | 845 | bsize &= ~(1024-1); |
837 | mutex_lock(&nfsd_mutex); | 846 | mutex_lock(&nfsd_mutex); |
838 | if (nfsd_serv) { | 847 | if (nn->nfsd_serv) { |
839 | mutex_unlock(&nfsd_mutex); | 848 | mutex_unlock(&nfsd_mutex); |
840 | return -EBUSY; | 849 | return -EBUSY; |
841 | } | 850 | } |
@@ -848,13 +857,14 @@ static ssize_t write_maxblksize(struct file *file, char *buf, size_t size) | |||
848 | } | 857 | } |
849 | 858 | ||
850 | #ifdef CONFIG_NFSD_V4 | 859 | #ifdef CONFIG_NFSD_V4 |
851 | static ssize_t __nfsd4_write_time(struct file *file, char *buf, size_t size, time_t *time) | 860 | static ssize_t __nfsd4_write_time(struct file *file, char *buf, size_t size, |
861 | time_t *time, struct nfsd_net *nn) | ||
852 | { | 862 | { |
853 | char *mesg = buf; | 863 | char *mesg = buf; |
854 | int rv, i; | 864 | int rv, i; |
855 | 865 | ||
856 | if (size > 0) { | 866 | if (size > 0) { |
857 | if (nfsd_serv) | 867 | if (nn->nfsd_serv) |
858 | return -EBUSY; | 868 | return -EBUSY; |
859 | rv = get_int(&mesg, &i); | 869 | rv = get_int(&mesg, &i); |
860 | if (rv) | 870 | if (rv) |
@@ -879,12 +889,13 @@ static ssize_t __nfsd4_write_time(struct file *file, char *buf, size_t size, tim | |||
879 | return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%ld\n", *time); | 889 | return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%ld\n", *time); |
880 | } | 890 | } |
881 | 891 | ||
882 | static ssize_t nfsd4_write_time(struct file *file, char *buf, size_t size, time_t *time) | 892 | static ssize_t nfsd4_write_time(struct file *file, char *buf, size_t size, |
893 | time_t *time, struct nfsd_net *nn) | ||
883 | { | 894 | { |
884 | ssize_t rv; | 895 | ssize_t rv; |
885 | 896 | ||
886 | mutex_lock(&nfsd_mutex); | 897 | mutex_lock(&nfsd_mutex); |
887 | rv = __nfsd4_write_time(file, buf, size, time); | 898 | rv = __nfsd4_write_time(file, buf, size, time, nn); |
888 | mutex_unlock(&nfsd_mutex); | 899 | mutex_unlock(&nfsd_mutex); |
889 | return rv; | 900 | return rv; |
890 | } | 901 | } |
@@ -912,7 +923,8 @@ static ssize_t nfsd4_write_time(struct file *file, char *buf, size_t size, time_ | |||
912 | */ | 923 | */ |
913 | static ssize_t write_leasetime(struct file *file, char *buf, size_t size) | 924 | static ssize_t write_leasetime(struct file *file, char *buf, size_t size) |
914 | { | 925 | { |
915 | return nfsd4_write_time(file, buf, size, &nfsd4_lease); | 926 | struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); |
927 | return nfsd4_write_time(file, buf, size, &nn->nfsd4_lease, nn); | ||
916 | } | 928 | } |
917 | 929 | ||
918 | /** | 930 | /** |
@@ -927,17 +939,19 @@ static ssize_t write_leasetime(struct file *file, char *buf, size_t size) | |||
927 | */ | 939 | */ |
928 | static ssize_t write_gracetime(struct file *file, char *buf, size_t size) | 940 | static ssize_t write_gracetime(struct file *file, char *buf, size_t size) |
929 | { | 941 | { |
930 | return nfsd4_write_time(file, buf, size, &nfsd4_grace); | 942 | struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); |
943 | return nfsd4_write_time(file, buf, size, &nn->nfsd4_grace, nn); | ||
931 | } | 944 | } |
932 | 945 | ||
933 | static ssize_t __write_recoverydir(struct file *file, char *buf, size_t size) | 946 | static ssize_t __write_recoverydir(struct file *file, char *buf, size_t size, |
947 | struct nfsd_net *nn) | ||
934 | { | 948 | { |
935 | char *mesg = buf; | 949 | char *mesg = buf; |
936 | char *recdir; | 950 | char *recdir; |
937 | int len, status; | 951 | int len, status; |
938 | 952 | ||
939 | if (size > 0) { | 953 | if (size > 0) { |
940 | if (nfsd_serv) | 954 | if (nn->nfsd_serv) |
941 | return -EBUSY; | 955 | return -EBUSY; |
942 | if (size > PATH_MAX || buf[size-1] != '\n') | 956 | if (size > PATH_MAX || buf[size-1] != '\n') |
943 | return -EINVAL; | 957 | return -EINVAL; |
@@ -981,9 +995,10 @@ static ssize_t __write_recoverydir(struct file *file, char *buf, size_t size) | |||
981 | static ssize_t write_recoverydir(struct file *file, char *buf, size_t size) | 995 | static ssize_t write_recoverydir(struct file *file, char *buf, size_t size) |
982 | { | 996 | { |
983 | ssize_t rv; | 997 | ssize_t rv; |
998 | struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); | ||
984 | 999 | ||
985 | mutex_lock(&nfsd_mutex); | 1000 | mutex_lock(&nfsd_mutex); |
986 | rv = __write_recoverydir(file, buf, size); | 1001 | rv = __write_recoverydir(file, buf, size, nn); |
987 | mutex_unlock(&nfsd_mutex); | 1002 | mutex_unlock(&nfsd_mutex); |
988 | return rv; | 1003 | return rv; |
989 | } | 1004 | } |
@@ -1063,6 +1078,7 @@ int nfsd_net_id; | |||
1063 | static __net_init int nfsd_init_net(struct net *net) | 1078 | static __net_init int nfsd_init_net(struct net *net) |
1064 | { | 1079 | { |
1065 | int retval; | 1080 | int retval; |
1081 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); | ||
1066 | 1082 | ||
1067 | retval = nfsd_export_init(net); | 1083 | retval = nfsd_export_init(net); |
1068 | if (retval) | 1084 | if (retval) |
@@ -1070,6 +1086,8 @@ static __net_init int nfsd_init_net(struct net *net) | |||
1070 | retval = nfsd_idmap_init(net); | 1086 | retval = nfsd_idmap_init(net); |
1071 | if (retval) | 1087 | if (retval) |
1072 | goto out_idmap_error; | 1088 | goto out_idmap_error; |
1089 | nn->nfsd4_lease = 90; /* default lease time */ | ||
1090 | nn->nfsd4_grace = 90; | ||
1073 | return 0; | 1091 | return 0; |
1074 | 1092 | ||
1075 | out_idmap_error: | 1093 | out_idmap_error: |
diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h index 80d5ce40aadb..de23db255c69 100644 --- a/fs/nfsd/nfsd.h +++ b/fs/nfsd/nfsd.h | |||
@@ -55,7 +55,6 @@ extern struct svc_version nfsd_version2, nfsd_version3, | |||
55 | nfsd_version4; | 55 | nfsd_version4; |
56 | extern u32 nfsd_supported_minorversion; | 56 | extern u32 nfsd_supported_minorversion; |
57 | extern struct mutex nfsd_mutex; | 57 | extern struct mutex nfsd_mutex; |
58 | extern struct svc_serv *nfsd_serv; | ||
59 | extern spinlock_t nfsd_drc_lock; | 58 | extern spinlock_t nfsd_drc_lock; |
60 | extern unsigned int nfsd_drc_max_mem; | 59 | extern unsigned int nfsd_drc_max_mem; |
61 | extern unsigned int nfsd_drc_mem_used; | 60 | extern unsigned int nfsd_drc_mem_used; |
@@ -65,26 +64,17 @@ extern const struct seq_operations nfs_exports_op; | |||
65 | /* | 64 | /* |
66 | * Function prototypes. | 65 | * Function prototypes. |
67 | */ | 66 | */ |
68 | int nfsd_svc(int nrservs); | 67 | int nfsd_svc(int nrservs, struct net *net); |
69 | int nfsd_dispatch(struct svc_rqst *rqstp, __be32 *statp); | 68 | int nfsd_dispatch(struct svc_rqst *rqstp, __be32 *statp); |
70 | 69 | ||
71 | int nfsd_nrthreads(void); | 70 | int nfsd_nrthreads(struct net *); |
72 | int nfsd_nrpools(void); | 71 | int nfsd_nrpools(struct net *); |
73 | int nfsd_get_nrthreads(int n, int *); | 72 | int nfsd_get_nrthreads(int n, int *, struct net *); |
74 | int nfsd_set_nrthreads(int n, int *); | 73 | int nfsd_set_nrthreads(int n, int *, struct net *); |
75 | int nfsd_pool_stats_open(struct inode *, struct file *); | 74 | int nfsd_pool_stats_open(struct inode *, struct file *); |
76 | int nfsd_pool_stats_release(struct inode *, struct file *); | 75 | int nfsd_pool_stats_release(struct inode *, struct file *); |
77 | 76 | ||
78 | static inline void nfsd_destroy(struct net *net) | 77 | void nfsd_destroy(struct net *net); |
79 | { | ||
80 | int destroy = (nfsd_serv->sv_nrthreads == 1); | ||
81 | |||
82 | if (destroy) | ||
83 | svc_shutdown_net(nfsd_serv, net); | ||
84 | svc_destroy(nfsd_serv); | ||
85 | if (destroy) | ||
86 | nfsd_serv = NULL; | ||
87 | } | ||
88 | 78 | ||
89 | #if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL) | 79 | #if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL) |
90 | #ifdef CONFIG_NFSD_V2_ACL | 80 | #ifdef CONFIG_NFSD_V2_ACL |
@@ -103,7 +93,7 @@ enum vers_op {NFSD_SET, NFSD_CLEAR, NFSD_TEST, NFSD_AVAIL }; | |||
103 | int nfsd_vers(int vers, enum vers_op change); | 93 | int nfsd_vers(int vers, enum vers_op change); |
104 | int nfsd_minorversion(u32 minorversion, enum vers_op change); | 94 | int nfsd_minorversion(u32 minorversion, enum vers_op change); |
105 | void nfsd_reset_versions(void); | 95 | void nfsd_reset_versions(void); |
106 | int nfsd_create_serv(void); | 96 | int nfsd_create_serv(struct net *net); |
107 | 97 | ||
108 | extern int nfsd_max_blksize; | 98 | extern int nfsd_max_blksize; |
109 | 99 | ||
@@ -121,7 +111,9 @@ void nfs4_state_init(void); | |||
121 | int nfsd4_init_slabs(void); | 111 | int nfsd4_init_slabs(void); |
122 | void nfsd4_free_slabs(void); | 112 | void nfsd4_free_slabs(void); |
123 | int nfs4_state_start(void); | 113 | int nfs4_state_start(void); |
114 | int nfs4_state_start_net(struct net *net); | ||
124 | void nfs4_state_shutdown(void); | 115 | void nfs4_state_shutdown(void); |
116 | void nfs4_state_shutdown_net(struct net *net); | ||
125 | void nfs4_reset_lease(time_t leasetime); | 117 | void nfs4_reset_lease(time_t leasetime); |
126 | int nfs4_reset_recoverydir(char *recdir); | 118 | int nfs4_reset_recoverydir(char *recdir); |
127 | char * nfs4_recoverydir(void); | 119 | char * nfs4_recoverydir(void); |
@@ -130,7 +122,9 @@ static inline void nfs4_state_init(void) { } | |||
130 | static inline int nfsd4_init_slabs(void) { return 0; } | 122 | static inline int nfsd4_init_slabs(void) { return 0; } |
131 | static inline void nfsd4_free_slabs(void) { } | 123 | static inline void nfsd4_free_slabs(void) { } |
132 | static inline int nfs4_state_start(void) { return 0; } | 124 | static inline int nfs4_state_start(void) { return 0; } |
125 | static inline int nfs4_state_start_net(struct net *net) { return 0; } | ||
133 | static inline void nfs4_state_shutdown(void) { } | 126 | static inline void nfs4_state_shutdown(void) { } |
127 | static inline void nfs4_state_shutdown_net(struct net *net) { } | ||
134 | static inline void nfs4_reset_lease(time_t leasetime) { } | 128 | static inline void nfs4_reset_lease(time_t leasetime) { } |
135 | static inline int nfs4_reset_recoverydir(char *recdir) { return 0; } | 129 | static inline int nfs4_reset_recoverydir(char *recdir) { return 0; } |
136 | static inline char * nfs4_recoverydir(void) {return NULL; } | 130 | static inline char * nfs4_recoverydir(void) {return NULL; } |
@@ -265,16 +259,8 @@ void nfsd_lockd_shutdown(void); | |||
265 | /* Check for dir entries '.' and '..' */ | 259 | /* Check for dir entries '.' and '..' */ |
266 | #define isdotent(n, l) (l < 3 && n[0] == '.' && (l == 1 || n[1] == '.')) | 260 | #define isdotent(n, l) (l < 3 && n[0] == '.' && (l == 1 || n[1] == '.')) |
267 | 261 | ||
268 | /* | ||
269 | * Time of server startup | ||
270 | */ | ||
271 | extern struct timeval nfssvc_boot; | ||
272 | |||
273 | #ifdef CONFIG_NFSD_V4 | 262 | #ifdef CONFIG_NFSD_V4 |
274 | 263 | ||
275 | extern time_t nfsd4_lease; | ||
276 | extern time_t nfsd4_grace; | ||
277 | |||
278 | /* before processing a COMPOUND operation, we have to check that there | 264 | /* before processing a COMPOUND operation, we have to check that there |
279 | * is enough space in the buffer for XDR encode to succeed. otherwise, | 265 | * is enough space in the buffer for XDR encode to succeed. otherwise, |
280 | * we might process an operation with side effects, and be unable to | 266 | * we might process an operation with side effects, and be unable to |
diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c index 032af381b3aa..814afaa4458a 100644 --- a/fs/nfsd/nfsfh.c +++ b/fs/nfsd/nfsfh.c | |||
@@ -572,7 +572,7 @@ fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry, | |||
572 | 572 | ||
573 | if (inode) | 573 | if (inode) |
574 | _fh_update(fhp, exp, dentry); | 574 | _fh_update(fhp, exp, dentry); |
575 | if (fhp->fh_handle.fh_fileid_type == 255) { | 575 | if (fhp->fh_handle.fh_fileid_type == FILEID_INVALID) { |
576 | fh_put(fhp); | 576 | fh_put(fhp); |
577 | return nfserr_opnotsupp; | 577 | return nfserr_opnotsupp; |
578 | } | 578 | } |
@@ -603,7 +603,7 @@ fh_update(struct svc_fh *fhp) | |||
603 | goto out; | 603 | goto out; |
604 | 604 | ||
605 | _fh_update(fhp, fhp->fh_export, dentry); | 605 | _fh_update(fhp, fhp->fh_export, dentry); |
606 | if (fhp->fh_handle.fh_fileid_type == 255) | 606 | if (fhp->fh_handle.fh_fileid_type == FILEID_INVALID) |
607 | return nfserr_opnotsupp; | 607 | return nfserr_opnotsupp; |
608 | } | 608 | } |
609 | out: | 609 | out: |
diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c index 2013aa001dab..cee62ab9d4a3 100644 --- a/fs/nfsd/nfssvc.c +++ b/fs/nfsd/nfssvc.c | |||
@@ -11,7 +11,6 @@ | |||
11 | #include <linux/module.h> | 11 | #include <linux/module.h> |
12 | #include <linux/fs_struct.h> | 12 | #include <linux/fs_struct.h> |
13 | #include <linux/swap.h> | 13 | #include <linux/swap.h> |
14 | #include <linux/nsproxy.h> | ||
15 | 14 | ||
16 | #include <linux/sunrpc/stats.h> | 15 | #include <linux/sunrpc/stats.h> |
17 | #include <linux/sunrpc/svcsock.h> | 16 | #include <linux/sunrpc/svcsock.h> |
@@ -22,19 +21,19 @@ | |||
22 | #include "nfsd.h" | 21 | #include "nfsd.h" |
23 | #include "cache.h" | 22 | #include "cache.h" |
24 | #include "vfs.h" | 23 | #include "vfs.h" |
24 | #include "netns.h" | ||
25 | 25 | ||
26 | #define NFSDDBG_FACILITY NFSDDBG_SVC | 26 | #define NFSDDBG_FACILITY NFSDDBG_SVC |
27 | 27 | ||
28 | extern struct svc_program nfsd_program; | 28 | extern struct svc_program nfsd_program; |
29 | static int nfsd(void *vrqstp); | 29 | static int nfsd(void *vrqstp); |
30 | struct timeval nfssvc_boot; | ||
31 | 30 | ||
32 | /* | 31 | /* |
33 | * nfsd_mutex protects nfsd_serv -- both the pointer itself and the members | 32 | * nfsd_mutex protects nn->nfsd_serv -- both the pointer itself and the members |
34 | * of the svc_serv struct. In particular, ->sv_nrthreads but also to some | 33 | * of the svc_serv struct. In particular, ->sv_nrthreads but also to some |
35 | * extent ->sv_temp_socks and ->sv_permsocks. It also protects nfsdstats.th_cnt | 34 | * extent ->sv_temp_socks and ->sv_permsocks. It also protects nfsdstats.th_cnt |
36 | * | 35 | * |
37 | * If (out side the lock) nfsd_serv is non-NULL, then it must point to a | 36 | * If (out side the lock) nn->nfsd_serv is non-NULL, then it must point to a |
38 | * properly initialised 'struct svc_serv' with ->sv_nrthreads > 0. That number | 37 | * properly initialised 'struct svc_serv' with ->sv_nrthreads > 0. That number |
39 | * of nfsd threads must exist and each must listed in ->sp_all_threads in each | 38 | * of nfsd threads must exist and each must listed in ->sp_all_threads in each |
40 | * entry of ->sv_pools[]. | 39 | * entry of ->sv_pools[]. |
@@ -52,7 +51,6 @@ struct timeval nfssvc_boot; | |||
52 | * nfsd_versions | 51 | * nfsd_versions |
53 | */ | 52 | */ |
54 | DEFINE_MUTEX(nfsd_mutex); | 53 | DEFINE_MUTEX(nfsd_mutex); |
55 | struct svc_serv *nfsd_serv; | ||
56 | 54 | ||
57 | /* | 55 | /* |
58 | * nfsd_drc_lock protects nfsd_drc_max_pages and nfsd_drc_pages_used. | 56 | * nfsd_drc_lock protects nfsd_drc_max_pages and nfsd_drc_pages_used. |
@@ -173,28 +171,32 @@ int nfsd_minorversion(u32 minorversion, enum vers_op change) | |||
173 | */ | 171 | */ |
174 | #define NFSD_MAXSERVS 8192 | 172 | #define NFSD_MAXSERVS 8192 |
175 | 173 | ||
176 | int nfsd_nrthreads(void) | 174 | int nfsd_nrthreads(struct net *net) |
177 | { | 175 | { |
178 | int rv = 0; | 176 | int rv = 0; |
177 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); | ||
178 | |||
179 | mutex_lock(&nfsd_mutex); | 179 | mutex_lock(&nfsd_mutex); |
180 | if (nfsd_serv) | 180 | if (nn->nfsd_serv) |
181 | rv = nfsd_serv->sv_nrthreads; | 181 | rv = nn->nfsd_serv->sv_nrthreads; |
182 | mutex_unlock(&nfsd_mutex); | 182 | mutex_unlock(&nfsd_mutex); |
183 | return rv; | 183 | return rv; |
184 | } | 184 | } |
185 | 185 | ||
186 | static int nfsd_init_socks(void) | 186 | static int nfsd_init_socks(struct net *net) |
187 | { | 187 | { |
188 | int error; | 188 | int error; |
189 | if (!list_empty(&nfsd_serv->sv_permsocks)) | 189 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); |
190 | |||
191 | if (!list_empty(&nn->nfsd_serv->sv_permsocks)) | ||
190 | return 0; | 192 | return 0; |
191 | 193 | ||
192 | error = svc_create_xprt(nfsd_serv, "udp", &init_net, PF_INET, NFS_PORT, | 194 | error = svc_create_xprt(nn->nfsd_serv, "udp", net, PF_INET, NFS_PORT, |
193 | SVC_SOCK_DEFAULTS); | 195 | SVC_SOCK_DEFAULTS); |
194 | if (error < 0) | 196 | if (error < 0) |
195 | return error; | 197 | return error; |
196 | 198 | ||
197 | error = svc_create_xprt(nfsd_serv, "tcp", &init_net, PF_INET, NFS_PORT, | 199 | error = svc_create_xprt(nn->nfsd_serv, "tcp", net, PF_INET, NFS_PORT, |
198 | SVC_SOCK_DEFAULTS); | 200 | SVC_SOCK_DEFAULTS); |
199 | if (error < 0) | 201 | if (error < 0) |
200 | return error; | 202 | return error; |
@@ -202,14 +204,15 @@ static int nfsd_init_socks(void) | |||
202 | return 0; | 204 | return 0; |
203 | } | 205 | } |
204 | 206 | ||
205 | static bool nfsd_up = false; | 207 | static int nfsd_users = 0; |
206 | 208 | ||
207 | static int nfsd_startup(int nrservs) | 209 | static int nfsd_startup_generic(int nrservs) |
208 | { | 210 | { |
209 | int ret; | 211 | int ret; |
210 | 212 | ||
211 | if (nfsd_up) | 213 | if (nfsd_users++) |
212 | return 0; | 214 | return 0; |
215 | |||
213 | /* | 216 | /* |
214 | * Readahead param cache - will no-op if it already exists. | 217 | * Readahead param cache - will no-op if it already exists. |
215 | * (Note therefore results will be suboptimal if number of | 218 | * (Note therefore results will be suboptimal if number of |
@@ -218,43 +221,79 @@ static int nfsd_startup(int nrservs) | |||
218 | ret = nfsd_racache_init(2*nrservs); | 221 | ret = nfsd_racache_init(2*nrservs); |
219 | if (ret) | 222 | if (ret) |
220 | return ret; | 223 | return ret; |
221 | ret = nfsd_init_socks(); | 224 | ret = nfs4_state_start(); |
222 | if (ret) | 225 | if (ret) |
223 | goto out_racache; | 226 | goto out_racache; |
224 | ret = lockd_up(&init_net); | 227 | return 0; |
228 | |||
229 | out_racache: | ||
230 | nfsd_racache_shutdown(); | ||
231 | return ret; | ||
232 | } | ||
233 | |||
234 | static void nfsd_shutdown_generic(void) | ||
235 | { | ||
236 | if (--nfsd_users) | ||
237 | return; | ||
238 | |||
239 | nfs4_state_shutdown(); | ||
240 | nfsd_racache_shutdown(); | ||
241 | } | ||
242 | |||
243 | static int nfsd_startup_net(int nrservs, struct net *net) | ||
244 | { | ||
245 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); | ||
246 | int ret; | ||
247 | |||
248 | if (nn->nfsd_net_up) | ||
249 | return 0; | ||
250 | |||
251 | ret = nfsd_startup_generic(nrservs); | ||
225 | if (ret) | 252 | if (ret) |
226 | goto out_racache; | 253 | return ret; |
227 | ret = nfs4_state_start(); | 254 | ret = nfsd_init_socks(net); |
255 | if (ret) | ||
256 | goto out_socks; | ||
257 | ret = lockd_up(net); | ||
258 | if (ret) | ||
259 | goto out_socks; | ||
260 | ret = nfs4_state_start_net(net); | ||
228 | if (ret) | 261 | if (ret) |
229 | goto out_lockd; | 262 | goto out_lockd; |
230 | nfsd_up = true; | 263 | |
264 | nn->nfsd_net_up = true; | ||
231 | return 0; | 265 | return 0; |
266 | |||
232 | out_lockd: | 267 | out_lockd: |
233 | lockd_down(&init_net); | 268 | lockd_down(net); |
234 | out_racache: | 269 | out_socks: |
235 | nfsd_racache_shutdown(); | 270 | nfsd_shutdown_generic(); |
236 | return ret; | 271 | return ret; |
237 | } | 272 | } |
238 | 273 | ||
239 | static void nfsd_shutdown(void) | 274 | static void nfsd_shutdown_net(struct net *net) |
240 | { | 275 | { |
276 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); | ||
277 | |||
278 | nfs4_state_shutdown_net(net); | ||
279 | lockd_down(net); | ||
280 | nn->nfsd_net_up = false; | ||
281 | nfsd_shutdown_generic(); | ||
282 | } | ||
283 | |||
284 | static void nfsd_last_thread(struct svc_serv *serv, struct net *net) | ||
285 | { | ||
286 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); | ||
287 | |||
241 | /* | 288 | /* |
242 | * write_ports can create the server without actually starting | 289 | * write_ports can create the server without actually starting |
243 | * any threads--if we get shut down before any threads are | 290 | * any threads--if we get shut down before any threads are |
244 | * started, then nfsd_last_thread will be run before any of this | 291 | * started, then nfsd_last_thread will be run before any of this |
245 | * other initialization has been done. | 292 | * other initialization has been done. |
246 | */ | 293 | */ |
247 | if (!nfsd_up) | 294 | if (!nn->nfsd_net_up) |
248 | return; | 295 | return; |
249 | nfs4_state_shutdown(); | 296 | nfsd_shutdown_net(net); |
250 | lockd_down(&init_net); | ||
251 | nfsd_racache_shutdown(); | ||
252 | nfsd_up = false; | ||
253 | } | ||
254 | |||
255 | static void nfsd_last_thread(struct svc_serv *serv, struct net *net) | ||
256 | { | ||
257 | nfsd_shutdown(); | ||
258 | 297 | ||
259 | svc_rpcb_cleanup(serv, net); | 298 | svc_rpcb_cleanup(serv, net); |
260 | 299 | ||
@@ -327,69 +366,84 @@ static int nfsd_get_default_max_blksize(void) | |||
327 | return ret; | 366 | return ret; |
328 | } | 367 | } |
329 | 368 | ||
330 | int nfsd_create_serv(void) | 369 | int nfsd_create_serv(struct net *net) |
331 | { | 370 | { |
332 | int error; | 371 | int error; |
333 | struct net *net = current->nsproxy->net_ns; | 372 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); |
334 | 373 | ||
335 | WARN_ON(!mutex_is_locked(&nfsd_mutex)); | 374 | WARN_ON(!mutex_is_locked(&nfsd_mutex)); |
336 | if (nfsd_serv) { | 375 | if (nn->nfsd_serv) { |
337 | svc_get(nfsd_serv); | 376 | svc_get(nn->nfsd_serv); |
338 | return 0; | 377 | return 0; |
339 | } | 378 | } |
340 | if (nfsd_max_blksize == 0) | 379 | if (nfsd_max_blksize == 0) |
341 | nfsd_max_blksize = nfsd_get_default_max_blksize(); | 380 | nfsd_max_blksize = nfsd_get_default_max_blksize(); |
342 | nfsd_reset_versions(); | 381 | nfsd_reset_versions(); |
343 | nfsd_serv = svc_create_pooled(&nfsd_program, nfsd_max_blksize, | 382 | nn->nfsd_serv = svc_create_pooled(&nfsd_program, nfsd_max_blksize, |
344 | nfsd_last_thread, nfsd, THIS_MODULE); | 383 | nfsd_last_thread, nfsd, THIS_MODULE); |
345 | if (nfsd_serv == NULL) | 384 | if (nn->nfsd_serv == NULL) |
346 | return -ENOMEM; | 385 | return -ENOMEM; |
347 | 386 | ||
348 | error = svc_bind(nfsd_serv, net); | 387 | error = svc_bind(nn->nfsd_serv, net); |
349 | if (error < 0) { | 388 | if (error < 0) { |
350 | svc_destroy(nfsd_serv); | 389 | svc_destroy(nn->nfsd_serv); |
351 | return error; | 390 | return error; |
352 | } | 391 | } |
353 | 392 | ||
354 | set_max_drc(); | 393 | set_max_drc(); |
355 | do_gettimeofday(&nfssvc_boot); /* record boot time */ | 394 | do_gettimeofday(&nn->nfssvc_boot); /* record boot time */ |
356 | return 0; | 395 | return 0; |
357 | } | 396 | } |
358 | 397 | ||
359 | int nfsd_nrpools(void) | 398 | int nfsd_nrpools(struct net *net) |
360 | { | 399 | { |
361 | if (nfsd_serv == NULL) | 400 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); |
401 | |||
402 | if (nn->nfsd_serv == NULL) | ||
362 | return 0; | 403 | return 0; |
363 | else | 404 | else |
364 | return nfsd_serv->sv_nrpools; | 405 | return nn->nfsd_serv->sv_nrpools; |
365 | } | 406 | } |
366 | 407 | ||
367 | int nfsd_get_nrthreads(int n, int *nthreads) | 408 | int nfsd_get_nrthreads(int n, int *nthreads, struct net *net) |
368 | { | 409 | { |
369 | int i = 0; | 410 | int i = 0; |
411 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); | ||
370 | 412 | ||
371 | if (nfsd_serv != NULL) { | 413 | if (nn->nfsd_serv != NULL) { |
372 | for (i = 0; i < nfsd_serv->sv_nrpools && i < n; i++) | 414 | for (i = 0; i < nn->nfsd_serv->sv_nrpools && i < n; i++) |
373 | nthreads[i] = nfsd_serv->sv_pools[i].sp_nrthreads; | 415 | nthreads[i] = nn->nfsd_serv->sv_pools[i].sp_nrthreads; |
374 | } | 416 | } |
375 | 417 | ||
376 | return 0; | 418 | return 0; |
377 | } | 419 | } |
378 | 420 | ||
379 | int nfsd_set_nrthreads(int n, int *nthreads) | 421 | void nfsd_destroy(struct net *net) |
422 | { | ||
423 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); | ||
424 | int destroy = (nn->nfsd_serv->sv_nrthreads == 1); | ||
425 | |||
426 | if (destroy) | ||
427 | svc_shutdown_net(nn->nfsd_serv, net); | ||
428 | svc_destroy(nn->nfsd_serv); | ||
429 | if (destroy) | ||
430 | nn->nfsd_serv = NULL; | ||
431 | } | ||
432 | |||
433 | int nfsd_set_nrthreads(int n, int *nthreads, struct net *net) | ||
380 | { | 434 | { |
381 | int i = 0; | 435 | int i = 0; |
382 | int tot = 0; | 436 | int tot = 0; |
383 | int err = 0; | 437 | int err = 0; |
384 | struct net *net = &init_net; | 438 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); |
385 | 439 | ||
386 | WARN_ON(!mutex_is_locked(&nfsd_mutex)); | 440 | WARN_ON(!mutex_is_locked(&nfsd_mutex)); |
387 | 441 | ||
388 | if (nfsd_serv == NULL || n <= 0) | 442 | if (nn->nfsd_serv == NULL || n <= 0) |
389 | return 0; | 443 | return 0; |
390 | 444 | ||
391 | if (n > nfsd_serv->sv_nrpools) | 445 | if (n > nn->nfsd_serv->sv_nrpools) |
392 | n = nfsd_serv->sv_nrpools; | 446 | n = nn->nfsd_serv->sv_nrpools; |
393 | 447 | ||
394 | /* enforce a global maximum number of threads */ | 448 | /* enforce a global maximum number of threads */ |
395 | tot = 0; | 449 | tot = 0; |
@@ -419,9 +473,9 @@ int nfsd_set_nrthreads(int n, int *nthreads) | |||
419 | nthreads[0] = 1; | 473 | nthreads[0] = 1; |
420 | 474 | ||
421 | /* apply the new numbers */ | 475 | /* apply the new numbers */ |
422 | svc_get(nfsd_serv); | 476 | svc_get(nn->nfsd_serv); |
423 | for (i = 0; i < n; i++) { | 477 | for (i = 0; i < n; i++) { |
424 | err = svc_set_num_threads(nfsd_serv, &nfsd_serv->sv_pools[i], | 478 | err = svc_set_num_threads(nn->nfsd_serv, &nn->nfsd_serv->sv_pools[i], |
425 | nthreads[i]); | 479 | nthreads[i]); |
426 | if (err) | 480 | if (err) |
427 | break; | 481 | break; |
@@ -436,11 +490,11 @@ int nfsd_set_nrthreads(int n, int *nthreads) | |||
436 | * this is the first time nrservs is nonzero. | 490 | * this is the first time nrservs is nonzero. |
437 | */ | 491 | */ |
438 | int | 492 | int |
439 | nfsd_svc(int nrservs) | 493 | nfsd_svc(int nrservs, struct net *net) |
440 | { | 494 | { |
441 | int error; | 495 | int error; |
442 | bool nfsd_up_before; | 496 | bool nfsd_up_before; |
443 | struct net *net = &init_net; | 497 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); |
444 | 498 | ||
445 | mutex_lock(&nfsd_mutex); | 499 | mutex_lock(&nfsd_mutex); |
446 | dprintk("nfsd: creating service\n"); | 500 | dprintk("nfsd: creating service\n"); |
@@ -449,29 +503,29 @@ nfsd_svc(int nrservs) | |||
449 | if (nrservs > NFSD_MAXSERVS) | 503 | if (nrservs > NFSD_MAXSERVS) |
450 | nrservs = NFSD_MAXSERVS; | 504 | nrservs = NFSD_MAXSERVS; |
451 | error = 0; | 505 | error = 0; |
452 | if (nrservs == 0 && nfsd_serv == NULL) | 506 | if (nrservs == 0 && nn->nfsd_serv == NULL) |
453 | goto out; | 507 | goto out; |
454 | 508 | ||
455 | error = nfsd_create_serv(); | 509 | error = nfsd_create_serv(net); |
456 | if (error) | 510 | if (error) |
457 | goto out; | 511 | goto out; |
458 | 512 | ||
459 | nfsd_up_before = nfsd_up; | 513 | nfsd_up_before = nn->nfsd_net_up; |
460 | 514 | ||
461 | error = nfsd_startup(nrservs); | 515 | error = nfsd_startup_net(nrservs, net); |
462 | if (error) | 516 | if (error) |
463 | goto out_destroy; | 517 | goto out_destroy; |
464 | error = svc_set_num_threads(nfsd_serv, NULL, nrservs); | 518 | error = svc_set_num_threads(nn->nfsd_serv, NULL, nrservs); |
465 | if (error) | 519 | if (error) |
466 | goto out_shutdown; | 520 | goto out_shutdown; |
467 | /* We are holding a reference to nfsd_serv which | 521 | /* We are holding a reference to nn->nfsd_serv which |
468 | * we don't want to count in the return value, | 522 | * we don't want to count in the return value, |
469 | * so subtract 1 | 523 | * so subtract 1 |
470 | */ | 524 | */ |
471 | error = nfsd_serv->sv_nrthreads - 1; | 525 | error = nn->nfsd_serv->sv_nrthreads - 1; |
472 | out_shutdown: | 526 | out_shutdown: |
473 | if (error < 0 && !nfsd_up_before) | 527 | if (error < 0 && !nfsd_up_before) |
474 | nfsd_shutdown(); | 528 | nfsd_shutdown_net(net); |
475 | out_destroy: | 529 | out_destroy: |
476 | nfsd_destroy(net); /* Release server */ | 530 | nfsd_destroy(net); /* Release server */ |
477 | out: | 531 | out: |
@@ -487,6 +541,8 @@ static int | |||
487 | nfsd(void *vrqstp) | 541 | nfsd(void *vrqstp) |
488 | { | 542 | { |
489 | struct svc_rqst *rqstp = (struct svc_rqst *) vrqstp; | 543 | struct svc_rqst *rqstp = (struct svc_rqst *) vrqstp; |
544 | struct svc_xprt *perm_sock = list_entry(rqstp->rq_server->sv_permsocks.next, typeof(struct svc_xprt), xpt_list); | ||
545 | struct net *net = perm_sock->xpt_net; | ||
490 | int err; | 546 | int err; |
491 | 547 | ||
492 | /* Lock module and set up kernel thread */ | 548 | /* Lock module and set up kernel thread */ |
@@ -551,7 +607,7 @@ out: | |||
551 | /* Release the thread */ | 607 | /* Release the thread */ |
552 | svc_exit_thread(rqstp); | 608 | svc_exit_thread(rqstp); |
553 | 609 | ||
554 | nfsd_destroy(&init_net); | 610 | nfsd_destroy(net); |
555 | 611 | ||
556 | /* Release module */ | 612 | /* Release module */ |
557 | mutex_unlock(&nfsd_mutex); | 613 | mutex_unlock(&nfsd_mutex); |
@@ -640,21 +696,24 @@ nfsd_dispatch(struct svc_rqst *rqstp, __be32 *statp) | |||
640 | } | 696 | } |
641 | 697 | ||
642 | /* Store reply in cache. */ | 698 | /* Store reply in cache. */ |
643 | nfsd_cache_update(rqstp, proc->pc_cachetype, statp + 1); | 699 | nfsd_cache_update(rqstp, rqstp->rq_cachetype, statp + 1); |
644 | return 1; | 700 | return 1; |
645 | } | 701 | } |
646 | 702 | ||
647 | int nfsd_pool_stats_open(struct inode *inode, struct file *file) | 703 | int nfsd_pool_stats_open(struct inode *inode, struct file *file) |
648 | { | 704 | { |
649 | int ret; | 705 | int ret; |
706 | struct net *net = &init_net; | ||
707 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); | ||
708 | |||
650 | mutex_lock(&nfsd_mutex); | 709 | mutex_lock(&nfsd_mutex); |
651 | if (nfsd_serv == NULL) { | 710 | if (nn->nfsd_serv == NULL) { |
652 | mutex_unlock(&nfsd_mutex); | 711 | mutex_unlock(&nfsd_mutex); |
653 | return -ENODEV; | 712 | return -ENODEV; |
654 | } | 713 | } |
655 | /* bump up the psudo refcount while traversing */ | 714 | /* bump up the psudo refcount while traversing */ |
656 | svc_get(nfsd_serv); | 715 | svc_get(nn->nfsd_serv); |
657 | ret = svc_pool_stats_open(nfsd_serv, file); | 716 | ret = svc_pool_stats_open(nn->nfsd_serv, file); |
658 | mutex_unlock(&nfsd_mutex); | 717 | mutex_unlock(&nfsd_mutex); |
659 | return ret; | 718 | return ret; |
660 | } | 719 | } |
diff --git a/fs/nfsd/nfsxdr.c b/fs/nfsd/nfsxdr.c index 65ec595e2226..979b42106979 100644 --- a/fs/nfsd/nfsxdr.c +++ b/fs/nfsd/nfsxdr.c | |||
@@ -246,7 +246,7 @@ nfssvc_decode_readargs(struct svc_rqst *rqstp, __be32 *p, | |||
246 | struct nfsd_readargs *args) | 246 | struct nfsd_readargs *args) |
247 | { | 247 | { |
248 | unsigned int len; | 248 | unsigned int len; |
249 | int v,pn; | 249 | int v; |
250 | if (!(p = decode_fh(p, &args->fh))) | 250 | if (!(p = decode_fh(p, &args->fh))) |
251 | return 0; | 251 | return 0; |
252 | 252 | ||
@@ -262,8 +262,9 @@ nfssvc_decode_readargs(struct svc_rqst *rqstp, __be32 *p, | |||
262 | */ | 262 | */ |
263 | v=0; | 263 | v=0; |
264 | while (len > 0) { | 264 | while (len > 0) { |
265 | pn = rqstp->rq_resused++; | 265 | struct page *p = *(rqstp->rq_next_page++); |
266 | rqstp->rq_vec[v].iov_base = page_address(rqstp->rq_respages[pn]); | 266 | |
267 | rqstp->rq_vec[v].iov_base = page_address(p); | ||
267 | rqstp->rq_vec[v].iov_len = len < PAGE_SIZE?len:PAGE_SIZE; | 268 | rqstp->rq_vec[v].iov_len = len < PAGE_SIZE?len:PAGE_SIZE; |
268 | len -= rqstp->rq_vec[v].iov_len; | 269 | len -= rqstp->rq_vec[v].iov_len; |
269 | v++; | 270 | v++; |
@@ -355,7 +356,7 @@ nfssvc_decode_readlinkargs(struct svc_rqst *rqstp, __be32 *p, struct nfsd_readli | |||
355 | { | 356 | { |
356 | if (!(p = decode_fh(p, &args->fh))) | 357 | if (!(p = decode_fh(p, &args->fh))) |
357 | return 0; | 358 | return 0; |
358 | args->buffer = page_address(rqstp->rq_respages[rqstp->rq_resused++]); | 359 | args->buffer = page_address(*(rqstp->rq_next_page++)); |
359 | 360 | ||
360 | return xdr_argsize_check(rqstp, p); | 361 | return xdr_argsize_check(rqstp, p); |
361 | } | 362 | } |
@@ -396,7 +397,7 @@ nfssvc_decode_readdirargs(struct svc_rqst *rqstp, __be32 *p, | |||
396 | if (args->count > PAGE_SIZE) | 397 | if (args->count > PAGE_SIZE) |
397 | args->count = PAGE_SIZE; | 398 | args->count = PAGE_SIZE; |
398 | 399 | ||
399 | args->buffer = page_address(rqstp->rq_respages[rqstp->rq_resused++]); | 400 | args->buffer = page_address(*(rqstp->rq_next_page++)); |
400 | 401 | ||
401 | return xdr_argsize_check(rqstp, p); | 402 | return xdr_argsize_check(rqstp, p); |
402 | } | 403 | } |
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index e036894bce57..d1c229feed52 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h | |||
@@ -150,6 +150,12 @@ struct nfsd4_channel_attrs { | |||
150 | u32 rdma_attrs; | 150 | u32 rdma_attrs; |
151 | }; | 151 | }; |
152 | 152 | ||
153 | struct nfsd4_cb_sec { | ||
154 | u32 flavor; /* (u32)(-1) used to mean "no valid flavor" */ | ||
155 | u32 uid; | ||
156 | u32 gid; | ||
157 | }; | ||
158 | |||
153 | struct nfsd4_create_session { | 159 | struct nfsd4_create_session { |
154 | clientid_t clientid; | 160 | clientid_t clientid; |
155 | struct nfs4_sessionid sessionid; | 161 | struct nfs4_sessionid sessionid; |
@@ -158,8 +164,12 @@ struct nfsd4_create_session { | |||
158 | struct nfsd4_channel_attrs fore_channel; | 164 | struct nfsd4_channel_attrs fore_channel; |
159 | struct nfsd4_channel_attrs back_channel; | 165 | struct nfsd4_channel_attrs back_channel; |
160 | u32 callback_prog; | 166 | u32 callback_prog; |
161 | u32 uid; | 167 | struct nfsd4_cb_sec cb_sec; |
162 | u32 gid; | 168 | }; |
169 | |||
170 | struct nfsd4_backchannel_ctl { | ||
171 | u32 bc_cb_program; | ||
172 | struct nfsd4_cb_sec bc_cb_sec; | ||
163 | }; | 173 | }; |
164 | 174 | ||
165 | struct nfsd4_bind_conn_to_session { | 175 | struct nfsd4_bind_conn_to_session { |
@@ -192,6 +202,7 @@ struct nfsd4_session { | |||
192 | struct nfs4_sessionid se_sessionid; | 202 | struct nfs4_sessionid se_sessionid; |
193 | struct nfsd4_channel_attrs se_fchannel; | 203 | struct nfsd4_channel_attrs se_fchannel; |
194 | struct nfsd4_channel_attrs se_bchannel; | 204 | struct nfsd4_channel_attrs se_bchannel; |
205 | struct nfsd4_cb_sec se_cb_sec; | ||
195 | struct list_head se_conns; | 206 | struct list_head se_conns; |
196 | u32 se_cb_prog; | 207 | u32 se_cb_prog; |
197 | u32 se_cb_seq_nr; | 208 | u32 se_cb_seq_nr; |
@@ -221,13 +232,12 @@ struct nfsd4_sessionid { | |||
221 | */ | 232 | */ |
222 | struct nfs4_client { | 233 | struct nfs4_client { |
223 | struct list_head cl_idhash; /* hash by cl_clientid.id */ | 234 | struct list_head cl_idhash; /* hash by cl_clientid.id */ |
224 | struct list_head cl_strhash; /* hash by cl_name */ | 235 | struct rb_node cl_namenode; /* link into by-name trees */ |
225 | struct list_head cl_openowners; | 236 | struct list_head cl_openowners; |
226 | struct idr cl_stateids; /* stateid lookup */ | 237 | struct idr cl_stateids; /* stateid lookup */ |
227 | struct list_head cl_delegations; | 238 | struct list_head cl_delegations; |
228 | struct list_head cl_lru; /* tail queue */ | 239 | struct list_head cl_lru; /* tail queue */ |
229 | struct xdr_netobj cl_name; /* id generated by client */ | 240 | struct xdr_netobj cl_name; /* id generated by client */ |
230 | char cl_recdir[HEXDIR_LEN]; /* recovery dir */ | ||
231 | nfs4_verifier cl_verifier; /* generated by client */ | 241 | nfs4_verifier cl_verifier; /* generated by client */ |
232 | time_t cl_time; /* time of last lease renewal */ | 242 | time_t cl_time; /* time of last lease renewal */ |
233 | struct sockaddr_storage cl_addr; /* client ipaddress */ | 243 | struct sockaddr_storage cl_addr; /* client ipaddress */ |
@@ -242,9 +252,11 @@ struct nfs4_client { | |||
242 | #define NFSD4_CLIENT_CB_KILL (1) | 252 | #define NFSD4_CLIENT_CB_KILL (1) |
243 | #define NFSD4_CLIENT_STABLE (2) /* client on stable storage */ | 253 | #define NFSD4_CLIENT_STABLE (2) /* client on stable storage */ |
244 | #define NFSD4_CLIENT_RECLAIM_COMPLETE (3) /* reclaim_complete done */ | 254 | #define NFSD4_CLIENT_RECLAIM_COMPLETE (3) /* reclaim_complete done */ |
255 | #define NFSD4_CLIENT_CONFIRMED (4) /* client is confirmed */ | ||
245 | #define NFSD4_CLIENT_CB_FLAG_MASK (1 << NFSD4_CLIENT_CB_UPDATE | \ | 256 | #define NFSD4_CLIENT_CB_FLAG_MASK (1 << NFSD4_CLIENT_CB_UPDATE | \ |
246 | 1 << NFSD4_CLIENT_CB_KILL) | 257 | 1 << NFSD4_CLIENT_CB_KILL) |
247 | unsigned long cl_flags; | 258 | unsigned long cl_flags; |
259 | struct rpc_cred *cl_cb_cred; | ||
248 | struct rpc_clnt *cl_cb_client; | 260 | struct rpc_clnt *cl_cb_client; |
249 | u32 cl_cb_ident; | 261 | u32 cl_cb_ident; |
250 | #define NFSD4_CB_UP 0 | 262 | #define NFSD4_CB_UP 0 |
@@ -271,6 +283,7 @@ struct nfs4_client { | |||
271 | unsigned long cl_cb_slot_busy; | 283 | unsigned long cl_cb_slot_busy; |
272 | struct rpc_wait_queue cl_cb_waitq; /* backchannel callers may */ | 284 | struct rpc_wait_queue cl_cb_waitq; /* backchannel callers may */ |
273 | /* wait here for slots */ | 285 | /* wait here for slots */ |
286 | struct net *net; | ||
274 | }; | 287 | }; |
275 | 288 | ||
276 | static inline void | 289 | static inline void |
@@ -292,6 +305,7 @@ is_client_expired(struct nfs4_client *clp) | |||
292 | */ | 305 | */ |
293 | struct nfs4_client_reclaim { | 306 | struct nfs4_client_reclaim { |
294 | struct list_head cr_strhash; /* hash by cr_name */ | 307 | struct list_head cr_strhash; /* hash by cr_name */ |
308 | struct nfs4_client *cr_clp; /* pointer to associated clp */ | ||
295 | char cr_recdir[HEXDIR_LEN]; /* recover dir */ | 309 | char cr_recdir[HEXDIR_LEN]; /* recover dir */ |
296 | }; | 310 | }; |
297 | 311 | ||
@@ -452,25 +466,26 @@ extern __be32 nfs4_preprocess_stateid_op(struct net *net, | |||
452 | stateid_t *stateid, int flags, struct file **filp); | 466 | stateid_t *stateid, int flags, struct file **filp); |
453 | extern void nfs4_lock_state(void); | 467 | extern void nfs4_lock_state(void); |
454 | extern void nfs4_unlock_state(void); | 468 | extern void nfs4_unlock_state(void); |
455 | extern int nfs4_in_grace(void); | 469 | void nfs4_remove_reclaim_record(struct nfs4_client_reclaim *, struct nfsd_net *); |
456 | extern void nfs4_release_reclaim(void); | 470 | extern void nfs4_release_reclaim(struct nfsd_net *); |
457 | extern struct nfs4_client_reclaim *nfsd4_find_reclaim_client(struct nfs4_client *crp); | 471 | extern struct nfs4_client_reclaim *nfsd4_find_reclaim_client(const char *recdir, |
458 | extern __be32 nfs4_check_open_reclaim(clientid_t *clid, bool sessions); | 472 | struct nfsd_net *nn); |
473 | extern __be32 nfs4_check_open_reclaim(clientid_t *clid, bool sessions, struct nfsd_net *nn); | ||
459 | extern void nfs4_free_openowner(struct nfs4_openowner *); | 474 | extern void nfs4_free_openowner(struct nfs4_openowner *); |
460 | extern void nfs4_free_lockowner(struct nfs4_lockowner *); | 475 | extern void nfs4_free_lockowner(struct nfs4_lockowner *); |
461 | extern int set_callback_cred(void); | 476 | extern int set_callback_cred(void); |
477 | extern void nfsd4_init_callback(struct nfsd4_callback *); | ||
462 | extern void nfsd4_probe_callback(struct nfs4_client *clp); | 478 | extern void nfsd4_probe_callback(struct nfs4_client *clp); |
463 | extern void nfsd4_probe_callback_sync(struct nfs4_client *clp); | 479 | extern void nfsd4_probe_callback_sync(struct nfs4_client *clp); |
464 | extern void nfsd4_change_callback(struct nfs4_client *clp, struct nfs4_cb_conn *); | 480 | extern void nfsd4_change_callback(struct nfs4_client *clp, struct nfs4_cb_conn *); |
465 | extern void nfsd4_do_callback_rpc(struct work_struct *); | ||
466 | extern void nfsd4_cb_recall(struct nfs4_delegation *dp); | 481 | extern void nfsd4_cb_recall(struct nfs4_delegation *dp); |
467 | extern int nfsd4_create_callback_queue(void); | 482 | extern int nfsd4_create_callback_queue(void); |
468 | extern void nfsd4_destroy_callback_queue(void); | 483 | extern void nfsd4_destroy_callback_queue(void); |
469 | extern void nfsd4_shutdown_callback(struct nfs4_client *); | 484 | extern void nfsd4_shutdown_callback(struct nfs4_client *); |
470 | extern void nfs4_put_delegation(struct nfs4_delegation *dp); | 485 | extern void nfs4_put_delegation(struct nfs4_delegation *dp); |
471 | extern __be32 nfs4_make_rec_clidname(char *clidname, struct xdr_netobj *clname); | 486 | extern struct nfs4_client_reclaim *nfs4_client_to_reclaim(const char *name, |
472 | extern int nfs4_client_to_reclaim(const char *name); | 487 | struct nfsd_net *nn); |
473 | extern int nfs4_has_reclaimed_state(const char *name, bool use_exchange_id); | 488 | extern bool nfs4_has_reclaimed_state(const char *name, struct nfsd_net *nn); |
474 | extern void release_session_client(struct nfsd4_session *); | 489 | extern void release_session_client(struct nfsd4_session *); |
475 | extern void nfsd4_purge_closed_stateid(struct nfs4_stateowner *); | 490 | extern void nfsd4_purge_closed_stateid(struct nfs4_stateowner *); |
476 | 491 | ||
@@ -480,5 +495,28 @@ extern void nfsd4_client_tracking_exit(struct net *net); | |||
480 | extern void nfsd4_client_record_create(struct nfs4_client *clp); | 495 | extern void nfsd4_client_record_create(struct nfs4_client *clp); |
481 | extern void nfsd4_client_record_remove(struct nfs4_client *clp); | 496 | extern void nfsd4_client_record_remove(struct nfs4_client *clp); |
482 | extern int nfsd4_client_record_check(struct nfs4_client *clp); | 497 | extern int nfsd4_client_record_check(struct nfs4_client *clp); |
483 | extern void nfsd4_record_grace_done(struct net *net, time_t boot_time); | 498 | extern void nfsd4_record_grace_done(struct nfsd_net *nn, time_t boot_time); |
499 | |||
500 | /* nfs fault injection functions */ | ||
501 | #ifdef CONFIG_NFSD_FAULT_INJECTION | ||
502 | int nfsd_fault_inject_init(void); | ||
503 | void nfsd_fault_inject_cleanup(void); | ||
504 | u64 nfsd_for_n_state(u64, u64 (*)(struct nfs4_client *, u64)); | ||
505 | struct nfs4_client *nfsd_find_client(struct sockaddr_storage *, size_t); | ||
506 | |||
507 | u64 nfsd_forget_client(struct nfs4_client *, u64); | ||
508 | u64 nfsd_forget_client_locks(struct nfs4_client*, u64); | ||
509 | u64 nfsd_forget_client_openowners(struct nfs4_client *, u64); | ||
510 | u64 nfsd_forget_client_delegations(struct nfs4_client *, u64); | ||
511 | u64 nfsd_recall_client_delegations(struct nfs4_client *, u64); | ||
512 | |||
513 | u64 nfsd_print_client(struct nfs4_client *, u64); | ||
514 | u64 nfsd_print_client_locks(struct nfs4_client *, u64); | ||
515 | u64 nfsd_print_client_openowners(struct nfs4_client *, u64); | ||
516 | u64 nfsd_print_client_delegations(struct nfs4_client *, u64); | ||
517 | #else /* CONFIG_NFSD_FAULT_INJECTION */ | ||
518 | static inline int nfsd_fault_inject_init(void) { return 0; } | ||
519 | static inline void nfsd_fault_inject_cleanup(void) {} | ||
520 | #endif /* CONFIG_NFSD_FAULT_INJECTION */ | ||
521 | |||
484 | #endif /* NFSD4_STATE_H */ | 522 | #endif /* NFSD4_STATE_H */ |
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index c120b48ec305..f0a6d88d7fff 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c | |||
@@ -886,7 +886,7 @@ nfsd_splice_actor(struct pipe_inode_info *pipe, struct pipe_buffer *buf, | |||
886 | struct splice_desc *sd) | 886 | struct splice_desc *sd) |
887 | { | 887 | { |
888 | struct svc_rqst *rqstp = sd->u.data; | 888 | struct svc_rqst *rqstp = sd->u.data; |
889 | struct page **pp = rqstp->rq_respages + rqstp->rq_resused; | 889 | struct page **pp = rqstp->rq_next_page; |
890 | struct page *page = buf->page; | 890 | struct page *page = buf->page; |
891 | size_t size; | 891 | size_t size; |
892 | 892 | ||
@@ -894,17 +894,15 @@ nfsd_splice_actor(struct pipe_inode_info *pipe, struct pipe_buffer *buf, | |||
894 | 894 | ||
895 | if (rqstp->rq_res.page_len == 0) { | 895 | if (rqstp->rq_res.page_len == 0) { |
896 | get_page(page); | 896 | get_page(page); |
897 | put_page(*pp); | 897 | put_page(*rqstp->rq_next_page); |
898 | *pp = page; | 898 | *(rqstp->rq_next_page++) = page; |
899 | rqstp->rq_resused++; | ||
900 | rqstp->rq_res.page_base = buf->offset; | 899 | rqstp->rq_res.page_base = buf->offset; |
901 | rqstp->rq_res.page_len = size; | 900 | rqstp->rq_res.page_len = size; |
902 | } else if (page != pp[-1]) { | 901 | } else if (page != pp[-1]) { |
903 | get_page(page); | 902 | get_page(page); |
904 | if (*pp) | 903 | if (*rqstp->rq_next_page) |
905 | put_page(*pp); | 904 | put_page(*rqstp->rq_next_page); |
906 | *pp = page; | 905 | *(rqstp->rq_next_page++) = page; |
907 | rqstp->rq_resused++; | ||
908 | rqstp->rq_res.page_len += size; | 906 | rqstp->rq_res.page_len += size; |
909 | } else | 907 | } else |
910 | rqstp->rq_res.page_len += size; | 908 | rqstp->rq_res.page_len += size; |
@@ -936,7 +934,8 @@ nfsd_vfs_read(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, | |||
936 | .u.data = rqstp, | 934 | .u.data = rqstp, |
937 | }; | 935 | }; |
938 | 936 | ||
939 | rqstp->rq_resused = 1; | 937 | WARN_ON_ONCE(rqstp->rq_next_page != rqstp->rq_respages + 1); |
938 | rqstp->rq_next_page = rqstp->rq_respages + 1; | ||
940 | host_err = splice_direct_to_actor(file, &sd, nfsd_direct_splice_actor); | 939 | host_err = splice_direct_to_actor(file, &sd, nfsd_direct_splice_actor); |
941 | } else { | 940 | } else { |
942 | oldfs = get_fs(); | 941 | oldfs = get_fs(); |
@@ -1020,28 +1019,10 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, | |||
1020 | inode = dentry->d_inode; | 1019 | inode = dentry->d_inode; |
1021 | exp = fhp->fh_export; | 1020 | exp = fhp->fh_export; |
1022 | 1021 | ||
1023 | /* | ||
1024 | * Request sync writes if | ||
1025 | * - the sync export option has been set, or | ||
1026 | * - the client requested O_SYNC behavior (NFSv3 feature). | ||
1027 | * - The file system doesn't support fsync(). | ||
1028 | * When NFSv2 gathered writes have been configured for this volume, | ||
1029 | * flushing the data to disk is handled separately below. | ||
1030 | */ | ||
1031 | use_wgather = (rqstp->rq_vers == 2) && EX_WGATHER(exp); | 1022 | use_wgather = (rqstp->rq_vers == 2) && EX_WGATHER(exp); |
1032 | 1023 | ||
1033 | if (!file->f_op->fsync) {/* COMMIT3 cannot work */ | ||
1034 | stable = 2; | ||
1035 | *stablep = 2; /* FILE_SYNC */ | ||
1036 | } | ||
1037 | |||
1038 | if (!EX_ISSYNC(exp)) | 1024 | if (!EX_ISSYNC(exp)) |
1039 | stable = 0; | 1025 | stable = 0; |
1040 | if (stable && !use_wgather) { | ||
1041 | spin_lock(&file->f_lock); | ||
1042 | file->f_flags |= O_SYNC; | ||
1043 | spin_unlock(&file->f_lock); | ||
1044 | } | ||
1045 | 1026 | ||
1046 | /* Write the data. */ | 1027 | /* Write the data. */ |
1047 | oldfs = get_fs(); set_fs(KERNEL_DS); | 1028 | oldfs = get_fs(); set_fs(KERNEL_DS); |
@@ -1057,8 +1038,12 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, | |||
1057 | if (inode->i_mode & (S_ISUID | S_ISGID)) | 1038 | if (inode->i_mode & (S_ISUID | S_ISGID)) |
1058 | kill_suid(dentry); | 1039 | kill_suid(dentry); |
1059 | 1040 | ||
1060 | if (stable && use_wgather) | 1041 | if (stable) { |
1061 | host_err = wait_for_concurrent_writes(file); | 1042 | if (use_wgather) |
1043 | host_err = wait_for_concurrent_writes(file); | ||
1044 | else | ||
1045 | host_err = vfs_fsync_range(file, offset, offset+*cnt, 0); | ||
1046 | } | ||
1062 | 1047 | ||
1063 | out_nfserr: | 1048 | out_nfserr: |
1064 | dprintk("nfsd: write complete host_err=%d\n", host_err); | 1049 | dprintk("nfsd: write complete host_err=%d\n", host_err); |
@@ -1485,13 +1470,19 @@ do_nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp, | |||
1485 | case NFS3_CREATE_EXCLUSIVE: | 1470 | case NFS3_CREATE_EXCLUSIVE: |
1486 | if ( dchild->d_inode->i_mtime.tv_sec == v_mtime | 1471 | if ( dchild->d_inode->i_mtime.tv_sec == v_mtime |
1487 | && dchild->d_inode->i_atime.tv_sec == v_atime | 1472 | && dchild->d_inode->i_atime.tv_sec == v_atime |
1488 | && dchild->d_inode->i_size == 0 ) | 1473 | && dchild->d_inode->i_size == 0 ) { |
1474 | if (created) | ||
1475 | *created = 1; | ||
1489 | break; | 1476 | break; |
1477 | } | ||
1490 | case NFS4_CREATE_EXCLUSIVE4_1: | 1478 | case NFS4_CREATE_EXCLUSIVE4_1: |
1491 | if ( dchild->d_inode->i_mtime.tv_sec == v_mtime | 1479 | if ( dchild->d_inode->i_mtime.tv_sec == v_mtime |
1492 | && dchild->d_inode->i_atime.tv_sec == v_atime | 1480 | && dchild->d_inode->i_atime.tv_sec == v_atime |
1493 | && dchild->d_inode->i_size == 0 ) | 1481 | && dchild->d_inode->i_size == 0 ) { |
1482 | if (created) | ||
1483 | *created = 1; | ||
1494 | goto set_attr; | 1484 | goto set_attr; |
1485 | } | ||
1495 | /* fallthru */ | 1486 | /* fallthru */ |
1496 | case NFS3_CREATE_GUARDED: | 1487 | case NFS3_CREATE_GUARDED: |
1497 | err = nfserr_exist; | 1488 | err = nfserr_exist; |
diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h index acd127d4ee82..0889bfb43dc9 100644 --- a/fs/nfsd/xdr4.h +++ b/fs/nfsd/xdr4.h | |||
@@ -385,7 +385,8 @@ struct nfsd4_write { | |||
385 | u64 wr_offset; /* request */ | 385 | u64 wr_offset; /* request */ |
386 | u32 wr_stable_how; /* request */ | 386 | u32 wr_stable_how; /* request */ |
387 | u32 wr_buflen; /* request */ | 387 | u32 wr_buflen; /* request */ |
388 | int wr_vlen; | 388 | struct kvec wr_head; |
389 | struct page ** wr_pagelist; /* request */ | ||
389 | 390 | ||
390 | u32 wr_bytes_written; /* response */ | 391 | u32 wr_bytes_written; /* response */ |
391 | u32 wr_how_written; /* response */ | 392 | u32 wr_how_written; /* response */ |
@@ -462,6 +463,7 @@ struct nfsd4_op { | |||
462 | 463 | ||
463 | /* NFSv4.1 */ | 464 | /* NFSv4.1 */ |
464 | struct nfsd4_exchange_id exchange_id; | 465 | struct nfsd4_exchange_id exchange_id; |
466 | struct nfsd4_backchannel_ctl backchannel_ctl; | ||
465 | struct nfsd4_bind_conn_to_session bind_conn_to_session; | 467 | struct nfsd4_bind_conn_to_session bind_conn_to_session; |
466 | struct nfsd4_create_session create_session; | 468 | struct nfsd4_create_session create_session; |
467 | struct nfsd4_destroy_session destroy_session; | 469 | struct nfsd4_destroy_session destroy_session; |
@@ -526,6 +528,14 @@ static inline bool nfsd4_not_cached(struct nfsd4_compoundres *resp) | |||
526 | || nfsd4_is_solo_sequence(resp); | 528 | || nfsd4_is_solo_sequence(resp); |
527 | } | 529 | } |
528 | 530 | ||
531 | static inline bool nfsd4_last_compound_op(struct svc_rqst *rqstp) | ||
532 | { | ||
533 | struct nfsd4_compoundres *resp = rqstp->rq_resp; | ||
534 | struct nfsd4_compoundargs *argp = rqstp->rq_argp; | ||
535 | |||
536 | return argp->opcnt == resp->opcnt; | ||
537 | } | ||
538 | |||
529 | #define NFS4_SVC_XDRSIZE sizeof(struct nfsd4_compoundargs) | 539 | #define NFS4_SVC_XDRSIZE sizeof(struct nfsd4_compoundargs) |
530 | 540 | ||
531 | static inline void | 541 | static inline void |
@@ -566,6 +576,7 @@ extern __be32 nfsd4_replay_cache_entry(struct nfsd4_compoundres *resp, | |||
566 | struct nfsd4_sequence *seq); | 576 | struct nfsd4_sequence *seq); |
567 | extern __be32 nfsd4_exchange_id(struct svc_rqst *rqstp, | 577 | extern __be32 nfsd4_exchange_id(struct svc_rqst *rqstp, |
568 | struct nfsd4_compound_state *, struct nfsd4_exchange_id *); | 578 | struct nfsd4_compound_state *, struct nfsd4_exchange_id *); |
579 | extern __be32 nfsd4_backchannel_ctl(struct svc_rqst *, struct nfsd4_compound_state *, struct nfsd4_backchannel_ctl *); | ||
569 | extern __be32 nfsd4_bind_conn_to_session(struct svc_rqst *, struct nfsd4_compound_state *, struct nfsd4_bind_conn_to_session *); | 580 | extern __be32 nfsd4_bind_conn_to_session(struct svc_rqst *, struct nfsd4_compound_state *, struct nfsd4_bind_conn_to_session *); |
570 | extern __be32 nfsd4_create_session(struct svc_rqst *, | 581 | extern __be32 nfsd4_create_session(struct svc_rqst *, |
571 | struct nfsd4_compound_state *, | 582 | struct nfsd4_compound_state *, |
@@ -579,7 +590,7 @@ extern __be32 nfsd4_destroy_session(struct svc_rqst *, | |||
579 | extern __be32 nfsd4_destroy_clientid(struct svc_rqst *, struct nfsd4_compound_state *, struct nfsd4_destroy_clientid *); | 590 | extern __be32 nfsd4_destroy_clientid(struct svc_rqst *, struct nfsd4_compound_state *, struct nfsd4_destroy_clientid *); |
580 | __be32 nfsd4_reclaim_complete(struct svc_rqst *, struct nfsd4_compound_state *, struct nfsd4_reclaim_complete *); | 591 | __be32 nfsd4_reclaim_complete(struct svc_rqst *, struct nfsd4_compound_state *, struct nfsd4_reclaim_complete *); |
581 | extern __be32 nfsd4_process_open1(struct nfsd4_compound_state *, | 592 | extern __be32 nfsd4_process_open1(struct nfsd4_compound_state *, |
582 | struct nfsd4_open *open); | 593 | struct nfsd4_open *open, struct nfsd_net *nn); |
583 | extern __be32 nfsd4_process_open2(struct svc_rqst *rqstp, | 594 | extern __be32 nfsd4_process_open2(struct svc_rqst *rqstp, |
584 | struct svc_fh *current_fh, struct nfsd4_open *open); | 595 | struct svc_fh *current_fh, struct nfsd4_open *open); |
585 | extern void nfsd4_cleanup_open_state(struct nfsd4_open *open, __be32 status); | 596 | extern void nfsd4_cleanup_open_state(struct nfsd4_open *open, __be32 status); |
diff --git a/include/linux/exportfs.h b/include/linux/exportfs.h index c7e6b6392ab8..5b9b5b317180 100644 --- a/include/linux/exportfs.h +++ b/include/linux/exportfs.h | |||
@@ -83,6 +83,11 @@ enum fid_type { | |||
83 | * 64 bit parent inode number. | 83 | * 64 bit parent inode number. |
84 | */ | 84 | */ |
85 | FILEID_NILFS_WITH_PARENT = 0x62, | 85 | FILEID_NILFS_WITH_PARENT = 0x62, |
86 | |||
87 | /* | ||
88 | * Filesystems must not use 0xff file ID. | ||
89 | */ | ||
90 | FILEID_INVALID = 0xff, | ||
86 | }; | 91 | }; |
87 | 92 | ||
88 | struct fid { | 93 | struct fid { |
diff --git a/include/linux/sunrpc/cache.h b/include/linux/sunrpc/cache.h index f792794f6634..5dc9ee4d616e 100644 --- a/include/linux/sunrpc/cache.h +++ b/include/linux/sunrpc/cache.h | |||
@@ -217,6 +217,8 @@ extern int qword_get(char **bpp, char *dest, int bufsize); | |||
217 | static inline int get_int(char **bpp, int *anint) | 217 | static inline int get_int(char **bpp, int *anint) |
218 | { | 218 | { |
219 | char buf[50]; | 219 | char buf[50]; |
220 | char *ep; | ||
221 | int rv; | ||
220 | int len = qword_get(bpp, buf, sizeof(buf)); | 222 | int len = qword_get(bpp, buf, sizeof(buf)); |
221 | 223 | ||
222 | if (len < 0) | 224 | if (len < 0) |
@@ -224,9 +226,11 @@ static inline int get_int(char **bpp, int *anint) | |||
224 | if (len == 0) | 226 | if (len == 0) |
225 | return -ENOENT; | 227 | return -ENOENT; |
226 | 228 | ||
227 | if (kstrtoint(buf, 0, anint)) | 229 | rv = simple_strtol(buf, &ep, 0); |
230 | if (*ep) | ||
228 | return -EINVAL; | 231 | return -EINVAL; |
229 | 232 | ||
233 | *anint = rv; | ||
230 | return 0; | 234 | return 0; |
231 | } | 235 | } |
232 | 236 | ||
diff --git a/include/linux/sunrpc/svc.h b/include/linux/sunrpc/svc.h index d83db800fe02..676ddf53b3ee 100644 --- a/include/linux/sunrpc/svc.h +++ b/include/linux/sunrpc/svc.h | |||
@@ -243,6 +243,7 @@ struct svc_rqst { | |||
243 | struct page * rq_pages[RPCSVC_MAXPAGES]; | 243 | struct page * rq_pages[RPCSVC_MAXPAGES]; |
244 | struct page * *rq_respages; /* points into rq_pages */ | 244 | struct page * *rq_respages; /* points into rq_pages */ |
245 | int rq_resused; /* number of pages used for result */ | 245 | int rq_resused; /* number of pages used for result */ |
246 | struct page * *rq_next_page; /* next reply page to use */ | ||
246 | 247 | ||
247 | struct kvec rq_vec[RPCSVC_MAXPAGES]; /* generally useful.. */ | 248 | struct kvec rq_vec[RPCSVC_MAXPAGES]; /* generally useful.. */ |
248 | 249 | ||
@@ -338,9 +339,8 @@ xdr_ressize_check(struct svc_rqst *rqstp, __be32 *p) | |||
338 | 339 | ||
339 | static inline void svc_free_res_pages(struct svc_rqst *rqstp) | 340 | static inline void svc_free_res_pages(struct svc_rqst *rqstp) |
340 | { | 341 | { |
341 | while (rqstp->rq_resused) { | 342 | while (rqstp->rq_next_page != rqstp->rq_respages) { |
342 | struct page **pp = (rqstp->rq_respages + | 343 | struct page **pp = --rqstp->rq_next_page; |
343 | --rqstp->rq_resused); | ||
344 | if (*pp) { | 344 | if (*pp) { |
345 | put_page(*pp); | 345 | put_page(*pp); |
346 | *pp = NULL; | 346 | *pp = NULL; |
diff --git a/include/linux/sunrpc/svcsock.h b/include/linux/sunrpc/svcsock.h index 92ad02f0dcc0..62fd1b756e99 100644 --- a/include/linux/sunrpc/svcsock.h +++ b/include/linux/sunrpc/svcsock.h | |||
@@ -26,11 +26,28 @@ struct svc_sock { | |||
26 | void (*sk_owspace)(struct sock *); | 26 | void (*sk_owspace)(struct sock *); |
27 | 27 | ||
28 | /* private TCP part */ | 28 | /* private TCP part */ |
29 | u32 sk_reclen; /* length of record */ | 29 | /* On-the-wire fragment header: */ |
30 | u32 sk_tcplen; /* current read length */ | 30 | __be32 sk_reclen; |
31 | /* As we receive a record, this includes the length received so | ||
32 | * far (including the fragment header): */ | ||
33 | u32 sk_tcplen; | ||
34 | /* Total length of the data (not including fragment headers) | ||
35 | * received so far in the fragments making up this rpc: */ | ||
36 | u32 sk_datalen; | ||
37 | |||
31 | struct page * sk_pages[RPCSVC_MAXPAGES]; /* received data */ | 38 | struct page * sk_pages[RPCSVC_MAXPAGES]; /* received data */ |
32 | }; | 39 | }; |
33 | 40 | ||
41 | static inline u32 svc_sock_reclen(struct svc_sock *svsk) | ||
42 | { | ||
43 | return ntohl(svsk->sk_reclen) & RPC_FRAGMENT_SIZE_MASK; | ||
44 | } | ||
45 | |||
46 | static inline u32 svc_sock_final_rec(struct svc_sock *svsk) | ||
47 | { | ||
48 | return ntohl(svsk->sk_reclen) & RPC_LAST_STREAM_FRAGMENT; | ||
49 | } | ||
50 | |||
34 | /* | 51 | /* |
35 | * Function prototypes. | 52 | * Function prototypes. |
36 | */ | 53 | */ |
diff --git a/net/sunrpc/rpcb_clnt.c b/net/sunrpc/rpcb_clnt.c index 411f332de0b3..795a0f4e920b 100644 --- a/net/sunrpc/rpcb_clnt.c +++ b/net/sunrpc/rpcb_clnt.c | |||
@@ -23,7 +23,6 @@ | |||
23 | #include <linux/errno.h> | 23 | #include <linux/errno.h> |
24 | #include <linux/mutex.h> | 24 | #include <linux/mutex.h> |
25 | #include <linux/slab.h> | 25 | #include <linux/slab.h> |
26 | #include <linux/nsproxy.h> | ||
27 | #include <net/ipv6.h> | 26 | #include <net/ipv6.h> |
28 | 27 | ||
29 | #include <linux/sunrpc/clnt.h> | 28 | #include <linux/sunrpc/clnt.h> |
diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index dfa4ba69ff45..dbf12ac5ecb7 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c | |||
@@ -20,7 +20,6 @@ | |||
20 | #include <linux/module.h> | 20 | #include <linux/module.h> |
21 | #include <linux/kthread.h> | 21 | #include <linux/kthread.h> |
22 | #include <linux/slab.h> | 22 | #include <linux/slab.h> |
23 | #include <linux/nsproxy.h> | ||
24 | 23 | ||
25 | #include <linux/sunrpc/types.h> | 24 | #include <linux/sunrpc/types.h> |
26 | #include <linux/sunrpc/xdr.h> | 25 | #include <linux/sunrpc/xdr.h> |
@@ -1041,7 +1040,7 @@ static void svc_unregister(const struct svc_serv *serv, struct net *net) | |||
1041 | } | 1040 | } |
1042 | 1041 | ||
1043 | /* | 1042 | /* |
1044 | * Printk the given error with the address of the client that caused it. | 1043 | * dprintk the given error with the address of the client that caused it. |
1045 | */ | 1044 | */ |
1046 | static __printf(2, 3) | 1045 | static __printf(2, 3) |
1047 | void svc_printk(struct svc_rqst *rqstp, const char *fmt, ...) | 1046 | void svc_printk(struct svc_rqst *rqstp, const char *fmt, ...) |
@@ -1055,8 +1054,7 @@ void svc_printk(struct svc_rqst *rqstp, const char *fmt, ...) | |||
1055 | vaf.fmt = fmt; | 1054 | vaf.fmt = fmt; |
1056 | vaf.va = &args; | 1055 | vaf.va = &args; |
1057 | 1056 | ||
1058 | net_warn_ratelimited("svc: %s: %pV", | 1057 | dprintk("svc: %s: %pV", svc_print_addr(rqstp, buf, sizeof(buf)), &vaf); |
1059 | svc_print_addr(rqstp, buf, sizeof(buf)), &vaf); | ||
1060 | 1058 | ||
1061 | va_end(args); | 1059 | va_end(args); |
1062 | } | 1060 | } |
@@ -1305,7 +1303,7 @@ svc_process(struct svc_rqst *rqstp) | |||
1305 | * Setup response xdr_buf. | 1303 | * Setup response xdr_buf. |
1306 | * Initially it has just one page | 1304 | * Initially it has just one page |
1307 | */ | 1305 | */ |
1308 | rqstp->rq_resused = 1; | 1306 | rqstp->rq_next_page = &rqstp->rq_respages[1]; |
1309 | resv->iov_base = page_address(rqstp->rq_respages[0]); | 1307 | resv->iov_base = page_address(rqstp->rq_respages[0]); |
1310 | resv->iov_len = 0; | 1308 | resv->iov_len = 0; |
1311 | rqstp->rq_res.pages = rqstp->rq_respages + 1; | 1309 | rqstp->rq_res.pages = rqstp->rq_respages + 1; |
diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c index cc3020d16789..0a148c9d2a5c 100644 --- a/net/sunrpc/svcsock.c +++ b/net/sunrpc/svcsock.c | |||
@@ -605,6 +605,7 @@ static int svc_udp_recvfrom(struct svc_rqst *rqstp) | |||
605 | rqstp->rq_respages = rqstp->rq_pages + 1 + | 605 | rqstp->rq_respages = rqstp->rq_pages + 1 + |
606 | DIV_ROUND_UP(rqstp->rq_arg.page_len, PAGE_SIZE); | 606 | DIV_ROUND_UP(rqstp->rq_arg.page_len, PAGE_SIZE); |
607 | } | 607 | } |
608 | rqstp->rq_next_page = rqstp->rq_respages+1; | ||
608 | 609 | ||
609 | if (serv->sv_stats) | 610 | if (serv->sv_stats) |
610 | serv->sv_stats->netudpcnt++; | 611 | serv->sv_stats->netudpcnt++; |
@@ -878,9 +879,9 @@ static unsigned int svc_tcp_restore_pages(struct svc_sock *svsk, struct svc_rqst | |||
878 | { | 879 | { |
879 | unsigned int i, len, npages; | 880 | unsigned int i, len, npages; |
880 | 881 | ||
881 | if (svsk->sk_tcplen <= sizeof(rpc_fraghdr)) | 882 | if (svsk->sk_datalen == 0) |
882 | return 0; | 883 | return 0; |
883 | len = svsk->sk_tcplen - sizeof(rpc_fraghdr); | 884 | len = svsk->sk_datalen; |
884 | npages = (len + PAGE_SIZE - 1) >> PAGE_SHIFT; | 885 | npages = (len + PAGE_SIZE - 1) >> PAGE_SHIFT; |
885 | for (i = 0; i < npages; i++) { | 886 | for (i = 0; i < npages; i++) { |
886 | if (rqstp->rq_pages[i] != NULL) | 887 | if (rqstp->rq_pages[i] != NULL) |
@@ -897,9 +898,9 @@ static void svc_tcp_save_pages(struct svc_sock *svsk, struct svc_rqst *rqstp) | |||
897 | { | 898 | { |
898 | unsigned int i, len, npages; | 899 | unsigned int i, len, npages; |
899 | 900 | ||
900 | if (svsk->sk_tcplen <= sizeof(rpc_fraghdr)) | 901 | if (svsk->sk_datalen == 0) |
901 | return; | 902 | return; |
902 | len = svsk->sk_tcplen - sizeof(rpc_fraghdr); | 903 | len = svsk->sk_datalen; |
903 | npages = (len + PAGE_SIZE - 1) >> PAGE_SHIFT; | 904 | npages = (len + PAGE_SIZE - 1) >> PAGE_SHIFT; |
904 | for (i = 0; i < npages; i++) { | 905 | for (i = 0; i < npages; i++) { |
905 | svsk->sk_pages[i] = rqstp->rq_pages[i]; | 906 | svsk->sk_pages[i] = rqstp->rq_pages[i]; |
@@ -911,9 +912,9 @@ static void svc_tcp_clear_pages(struct svc_sock *svsk) | |||
911 | { | 912 | { |
912 | unsigned int i, len, npages; | 913 | unsigned int i, len, npages; |
913 | 914 | ||
914 | if (svsk->sk_tcplen <= sizeof(rpc_fraghdr)) | 915 | if (svsk->sk_datalen == 0) |
915 | goto out; | 916 | goto out; |
916 | len = svsk->sk_tcplen - sizeof(rpc_fraghdr); | 917 | len = svsk->sk_datalen; |
917 | npages = (len + PAGE_SIZE - 1) >> PAGE_SHIFT; | 918 | npages = (len + PAGE_SIZE - 1) >> PAGE_SHIFT; |
918 | for (i = 0; i < npages; i++) { | 919 | for (i = 0; i < npages; i++) { |
919 | BUG_ON(svsk->sk_pages[i] == NULL); | 920 | BUG_ON(svsk->sk_pages[i] == NULL); |
@@ -922,13 +923,12 @@ static void svc_tcp_clear_pages(struct svc_sock *svsk) | |||
922 | } | 923 | } |
923 | out: | 924 | out: |
924 | svsk->sk_tcplen = 0; | 925 | svsk->sk_tcplen = 0; |
926 | svsk->sk_datalen = 0; | ||
925 | } | 927 | } |
926 | 928 | ||
927 | /* | 929 | /* |
928 | * Receive data. | 930 | * Receive fragment record header. |
929 | * If we haven't gotten the record length yet, get the next four bytes. | 931 | * If we haven't gotten the record length yet, get the next four bytes. |
930 | * Otherwise try to gobble up as much as possible up to the complete | ||
931 | * record length. | ||
932 | */ | 932 | */ |
933 | static int svc_tcp_recv_record(struct svc_sock *svsk, struct svc_rqst *rqstp) | 933 | static int svc_tcp_recv_record(struct svc_sock *svsk, struct svc_rqst *rqstp) |
934 | { | 934 | { |
@@ -954,32 +954,16 @@ static int svc_tcp_recv_record(struct svc_sock *svsk, struct svc_rqst *rqstp) | |||
954 | return -EAGAIN; | 954 | return -EAGAIN; |
955 | } | 955 | } |
956 | 956 | ||
957 | svsk->sk_reclen = ntohl(svsk->sk_reclen); | 957 | dprintk("svc: TCP record, %d bytes\n", svc_sock_reclen(svsk)); |
958 | if (!(svsk->sk_reclen & RPC_LAST_STREAM_FRAGMENT)) { | 958 | if (svc_sock_reclen(svsk) + svsk->sk_datalen > |
959 | /* FIXME: technically, a record can be fragmented, | 959 | serv->sv_max_mesg) { |
960 | * and non-terminal fragments will not have the top | 960 | net_notice_ratelimited("RPC: fragment too large: %d\n", |
961 | * bit set in the fragment length header. | 961 | svc_sock_reclen(svsk)); |
962 | * But apparently no known nfs clients send fragmented | ||
963 | * records. */ | ||
964 | net_notice_ratelimited("RPC: multiple fragments per record not supported\n"); | ||
965 | goto err_delete; | ||
966 | } | ||
967 | |||
968 | svsk->sk_reclen &= RPC_FRAGMENT_SIZE_MASK; | ||
969 | dprintk("svc: TCP record, %d bytes\n", svsk->sk_reclen); | ||
970 | if (svsk->sk_reclen > serv->sv_max_mesg) { | ||
971 | net_notice_ratelimited("RPC: fragment too large: 0x%08lx\n", | ||
972 | (unsigned long)svsk->sk_reclen); | ||
973 | goto err_delete; | 962 | goto err_delete; |
974 | } | 963 | } |
975 | } | 964 | } |
976 | 965 | ||
977 | if (svsk->sk_reclen < 8) | 966 | return svc_sock_reclen(svsk); |
978 | goto err_delete; /* client is nuts. */ | ||
979 | |||
980 | len = svsk->sk_reclen; | ||
981 | |||
982 | return len; | ||
983 | error: | 967 | error: |
984 | dprintk("RPC: TCP recv_record got %d\n", len); | 968 | dprintk("RPC: TCP recv_record got %d\n", len); |
985 | return len; | 969 | return len; |
@@ -1023,7 +1007,7 @@ static int receive_cb_reply(struct svc_sock *svsk, struct svc_rqst *rqstp) | |||
1023 | if (dst->iov_len < src->iov_len) | 1007 | if (dst->iov_len < src->iov_len) |
1024 | return -EAGAIN; /* whatever; just giving up. */ | 1008 | return -EAGAIN; /* whatever; just giving up. */ |
1025 | memcpy(dst->iov_base, src->iov_base, src->iov_len); | 1009 | memcpy(dst->iov_base, src->iov_base, src->iov_len); |
1026 | xprt_complete_rqst(req->rq_task, svsk->sk_reclen); | 1010 | xprt_complete_rqst(req->rq_task, rqstp->rq_arg.len); |
1027 | rqstp->rq_arg.len = 0; | 1011 | rqstp->rq_arg.len = 0; |
1028 | return 0; | 1012 | return 0; |
1029 | } | 1013 | } |
@@ -1042,6 +1026,17 @@ static int copy_pages_to_kvecs(struct kvec *vec, struct page **pages, int len) | |||
1042 | return i; | 1026 | return i; |
1043 | } | 1027 | } |
1044 | 1028 | ||
1029 | static void svc_tcp_fragment_received(struct svc_sock *svsk) | ||
1030 | { | ||
1031 | /* If we have more data, signal svc_xprt_enqueue() to try again */ | ||
1032 | if (svc_recv_available(svsk) > sizeof(rpc_fraghdr)) | ||
1033 | set_bit(XPT_DATA, &svsk->sk_xprt.xpt_flags); | ||
1034 | dprintk("svc: TCP %s record (%d bytes)\n", | ||
1035 | svc_sock_final_rec(svsk) ? "final" : "nonfinal", | ||
1036 | svc_sock_reclen(svsk)); | ||
1037 | svsk->sk_tcplen = 0; | ||
1038 | svsk->sk_reclen = 0; | ||
1039 | } | ||
1045 | 1040 | ||
1046 | /* | 1041 | /* |
1047 | * Receive data from a TCP socket. | 1042 | * Receive data from a TCP socket. |
@@ -1068,29 +1063,39 @@ static int svc_tcp_recvfrom(struct svc_rqst *rqstp) | |||
1068 | goto error; | 1063 | goto error; |
1069 | 1064 | ||
1070 | base = svc_tcp_restore_pages(svsk, rqstp); | 1065 | base = svc_tcp_restore_pages(svsk, rqstp); |
1071 | want = svsk->sk_reclen - base; | 1066 | want = svc_sock_reclen(svsk) - (svsk->sk_tcplen - sizeof(rpc_fraghdr)); |
1072 | 1067 | ||
1073 | vec = rqstp->rq_vec; | 1068 | vec = rqstp->rq_vec; |
1074 | 1069 | ||
1075 | pnum = copy_pages_to_kvecs(&vec[0], &rqstp->rq_pages[0], | 1070 | pnum = copy_pages_to_kvecs(&vec[0], &rqstp->rq_pages[0], |
1076 | svsk->sk_reclen); | 1071 | svsk->sk_datalen + want); |
1077 | 1072 | ||
1078 | rqstp->rq_respages = &rqstp->rq_pages[pnum]; | 1073 | rqstp->rq_respages = &rqstp->rq_pages[pnum]; |
1074 | rqstp->rq_next_page = rqstp->rq_respages + 1; | ||
1079 | 1075 | ||
1080 | /* Now receive data */ | 1076 | /* Now receive data */ |
1081 | len = svc_partial_recvfrom(rqstp, vec, pnum, want, base); | 1077 | len = svc_partial_recvfrom(rqstp, vec, pnum, want, base); |
1082 | if (len >= 0) | 1078 | if (len >= 0) { |
1083 | svsk->sk_tcplen += len; | 1079 | svsk->sk_tcplen += len; |
1084 | if (len != want) { | 1080 | svsk->sk_datalen += len; |
1081 | } | ||
1082 | if (len != want || !svc_sock_final_rec(svsk)) { | ||
1085 | svc_tcp_save_pages(svsk, rqstp); | 1083 | svc_tcp_save_pages(svsk, rqstp); |
1086 | if (len < 0 && len != -EAGAIN) | 1084 | if (len < 0 && len != -EAGAIN) |
1087 | goto err_other; | 1085 | goto err_delete; |
1088 | dprintk("svc: incomplete TCP record (%d of %d)\n", | 1086 | if (len == want) |
1089 | svsk->sk_tcplen, svsk->sk_reclen); | 1087 | svc_tcp_fragment_received(svsk); |
1088 | else | ||
1089 | dprintk("svc: incomplete TCP record (%d of %d)\n", | ||
1090 | (int)(svsk->sk_tcplen - sizeof(rpc_fraghdr)), | ||
1091 | svc_sock_reclen(svsk)); | ||
1090 | goto err_noclose; | 1092 | goto err_noclose; |
1091 | } | 1093 | } |
1092 | 1094 | ||
1093 | rqstp->rq_arg.len = svsk->sk_reclen; | 1095 | if (svc_sock_reclen(svsk) < 8) |
1096 | goto err_delete; /* client is nuts. */ | ||
1097 | |||
1098 | rqstp->rq_arg.len = svsk->sk_datalen; | ||
1094 | rqstp->rq_arg.page_base = 0; | 1099 | rqstp->rq_arg.page_base = 0; |
1095 | if (rqstp->rq_arg.len <= rqstp->rq_arg.head[0].iov_len) { | 1100 | if (rqstp->rq_arg.len <= rqstp->rq_arg.head[0].iov_len) { |
1096 | rqstp->rq_arg.head[0].iov_len = rqstp->rq_arg.len; | 1101 | rqstp->rq_arg.head[0].iov_len = rqstp->rq_arg.len; |
@@ -1107,11 +1112,8 @@ static int svc_tcp_recvfrom(struct svc_rqst *rqstp) | |||
1107 | len = receive_cb_reply(svsk, rqstp); | 1112 | len = receive_cb_reply(svsk, rqstp); |
1108 | 1113 | ||
1109 | /* Reset TCP read info */ | 1114 | /* Reset TCP read info */ |
1110 | svsk->sk_reclen = 0; | 1115 | svsk->sk_datalen = 0; |
1111 | svsk->sk_tcplen = 0; | 1116 | svc_tcp_fragment_received(svsk); |
1112 | /* If we have more data, signal svc_xprt_enqueue() to try again */ | ||
1113 | if (svc_recv_available(svsk) > sizeof(rpc_fraghdr)) | ||
1114 | set_bit(XPT_DATA, &svsk->sk_xprt.xpt_flags); | ||
1115 | 1117 | ||
1116 | if (len < 0) | 1118 | if (len < 0) |
1117 | goto error; | 1119 | goto error; |
@@ -1120,15 +1122,14 @@ static int svc_tcp_recvfrom(struct svc_rqst *rqstp) | |||
1120 | if (serv->sv_stats) | 1122 | if (serv->sv_stats) |
1121 | serv->sv_stats->nettcpcnt++; | 1123 | serv->sv_stats->nettcpcnt++; |
1122 | 1124 | ||
1123 | dprintk("svc: TCP complete record (%d bytes)\n", rqstp->rq_arg.len); | ||
1124 | return rqstp->rq_arg.len; | 1125 | return rqstp->rq_arg.len; |
1125 | 1126 | ||
1126 | error: | 1127 | error: |
1127 | if (len != -EAGAIN) | 1128 | if (len != -EAGAIN) |
1128 | goto err_other; | 1129 | goto err_delete; |
1129 | dprintk("RPC: TCP recvfrom got EAGAIN\n"); | 1130 | dprintk("RPC: TCP recvfrom got EAGAIN\n"); |
1130 | return 0; | 1131 | return 0; |
1131 | err_other: | 1132 | err_delete: |
1132 | printk(KERN_NOTICE "%s: recvfrom returned errno %d\n", | 1133 | printk(KERN_NOTICE "%s: recvfrom returned errno %d\n", |
1133 | svsk->sk_xprt.xpt_server->sv_name, -len); | 1134 | svsk->sk_xprt.xpt_server->sv_name, -len); |
1134 | set_bit(XPT_CLOSE, &svsk->sk_xprt.xpt_flags); | 1135 | set_bit(XPT_CLOSE, &svsk->sk_xprt.xpt_flags); |
@@ -1305,6 +1306,7 @@ static void svc_tcp_init(struct svc_sock *svsk, struct svc_serv *serv) | |||
1305 | 1306 | ||
1306 | svsk->sk_reclen = 0; | 1307 | svsk->sk_reclen = 0; |
1307 | svsk->sk_tcplen = 0; | 1308 | svsk->sk_tcplen = 0; |
1309 | svsk->sk_datalen = 0; | ||
1308 | memset(&svsk->sk_pages[0], 0, sizeof(svsk->sk_pages)); | 1310 | memset(&svsk->sk_pages[0], 0, sizeof(svsk->sk_pages)); |
1309 | 1311 | ||
1310 | tcp_sk(sk)->nonagle |= TCP_NAGLE_OFF; | 1312 | tcp_sk(sk)->nonagle |= TCP_NAGLE_OFF; |
diff --git a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c index 41cb63b623df..0ce75524ed21 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c +++ b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c | |||
@@ -521,11 +521,11 @@ next_sge: | |||
521 | rqstp->rq_pages[ch_no] = NULL; | 521 | rqstp->rq_pages[ch_no] = NULL; |
522 | 522 | ||
523 | /* | 523 | /* |
524 | * Detach res pages. svc_release must see a resused count of | 524 | * Detach res pages. If svc_release sees any it will attempt to |
525 | * zero or it will attempt to put them. | 525 | * put them. |
526 | */ | 526 | */ |
527 | while (rqstp->rq_resused) | 527 | while (rqstp->rq_next_page != rqstp->rq_respages) |
528 | rqstp->rq_respages[--rqstp->rq_resused] = NULL; | 528 | *(--rqstp->rq_next_page) = NULL; |
529 | 529 | ||
530 | return err; | 530 | return err; |
531 | } | 531 | } |
@@ -550,7 +550,7 @@ static int rdma_read_complete(struct svc_rqst *rqstp, | |||
550 | 550 | ||
551 | /* rq_respages starts after the last arg page */ | 551 | /* rq_respages starts after the last arg page */ |
552 | rqstp->rq_respages = &rqstp->rq_arg.pages[page_no]; | 552 | rqstp->rq_respages = &rqstp->rq_arg.pages[page_no]; |
553 | rqstp->rq_resused = 0; | 553 | rqstp->rq_next_page = &rqstp->rq_arg.pages[page_no]; |
554 | 554 | ||
555 | /* Rebuild rq_arg head and tail. */ | 555 | /* Rebuild rq_arg head and tail. */ |
556 | rqstp->rq_arg.head[0] = head->arg.head[0]; | 556 | rqstp->rq_arg.head[0] = head->arg.head[0]; |
diff --git a/net/sunrpc/xprtrdma/svc_rdma_sendto.c b/net/sunrpc/xprtrdma/svc_rdma_sendto.c index 42eb7ba0b903..c1d124dc772b 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_sendto.c +++ b/net/sunrpc/xprtrdma/svc_rdma_sendto.c | |||
@@ -548,6 +548,7 @@ static int send_reply(struct svcxprt_rdma *rdma, | |||
548 | int sge_no; | 548 | int sge_no; |
549 | int sge_bytes; | 549 | int sge_bytes; |
550 | int page_no; | 550 | int page_no; |
551 | int pages; | ||
551 | int ret; | 552 | int ret; |
552 | 553 | ||
553 | /* Post a recv buffer to handle another request. */ | 554 | /* Post a recv buffer to handle another request. */ |
@@ -611,7 +612,8 @@ static int send_reply(struct svcxprt_rdma *rdma, | |||
611 | * respages array. They are our pages until the I/O | 612 | * respages array. They are our pages until the I/O |
612 | * completes. | 613 | * completes. |
613 | */ | 614 | */ |
614 | for (page_no = 0; page_no < rqstp->rq_resused; page_no++) { | 615 | pages = rqstp->rq_next_page - rqstp->rq_respages; |
616 | for (page_no = 0; page_no < pages; page_no++) { | ||
615 | ctxt->pages[page_no+1] = rqstp->rq_respages[page_no]; | 617 | ctxt->pages[page_no+1] = rqstp->rq_respages[page_no]; |
616 | ctxt->count++; | 618 | ctxt->count++; |
617 | rqstp->rq_respages[page_no] = NULL; | 619 | rqstp->rq_respages[page_no] = NULL; |