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 /fs/nfsd/nfs4recover.c | |
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()
...
Diffstat (limited to 'fs/nfsd/nfs4recover.c')
-rw-r--r-- | fs/nfsd/nfs4recover.c | 561 |
1 files changed, 461 insertions, 100 deletions
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 |