diff options
Diffstat (limited to 'fs/nfsd/nfs4recover.c')
-rw-r--r-- | fs/nfsd/nfs4recover.c | 647 |
1 files changed, 633 insertions, 14 deletions
diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c index 0b3e875d1abd..4767429264a2 100644 --- a/fs/nfsd/nfs4recover.c +++ b/fs/nfsd/nfs4recover.c | |||
@@ -1,5 +1,6 @@ | |||
1 | /* | 1 | /* |
2 | * Copyright (c) 2004 The Regents of the University of Michigan. | 2 | * Copyright (c) 2004 The Regents of the University of Michigan. |
3 | * Copyright (c) 2012 Jeff Layton <jlayton@redhat.com> | ||
3 | * All rights reserved. | 4 | * All rights reserved. |
4 | * | 5 | * |
5 | * Andy Adamson <andros@citi.umich.edu> | 6 | * Andy Adamson <andros@citi.umich.edu> |
@@ -36,16 +37,34 @@ | |||
36 | #include <linux/namei.h> | 37 | #include <linux/namei.h> |
37 | #include <linux/crypto.h> | 38 | #include <linux/crypto.h> |
38 | #include <linux/sched.h> | 39 | #include <linux/sched.h> |
40 | #include <linux/fs.h> | ||
41 | #include <linux/module.h> | ||
42 | #include <net/net_namespace.h> | ||
43 | #include <linux/sunrpc/rpc_pipe_fs.h> | ||
44 | #include <linux/sunrpc/clnt.h> | ||
45 | #include <linux/nfsd/cld.h> | ||
39 | 46 | ||
40 | #include "nfsd.h" | 47 | #include "nfsd.h" |
41 | #include "state.h" | 48 | #include "state.h" |
42 | #include "vfs.h" | 49 | #include "vfs.h" |
50 | #include "netns.h" | ||
43 | 51 | ||
44 | #define NFSDDBG_FACILITY NFSDDBG_PROC | 52 | #define NFSDDBG_FACILITY NFSDDBG_PROC |
45 | 53 | ||
54 | /* Declarations */ | ||
55 | struct nfsd4_client_tracking_ops { | ||
56 | int (*init)(struct net *); | ||
57 | void (*exit)(struct net *); | ||
58 | void (*create)(struct nfs4_client *); | ||
59 | void (*remove)(struct nfs4_client *); | ||
60 | int (*check)(struct nfs4_client *); | ||
61 | void (*grace_done)(struct net *, time_t); | ||
62 | }; | ||
63 | |||
46 | /* Globals */ | 64 | /* Globals */ |
47 | static struct file *rec_file; | 65 | static struct file *rec_file; |
48 | static char user_recovery_dirname[PATH_MAX] = "/var/lib/nfs/v4recovery"; | 66 | static char user_recovery_dirname[PATH_MAX] = "/var/lib/nfs/v4recovery"; |
67 | static struct nfsd4_client_tracking_ops *client_tracking_ops; | ||
49 | 68 | ||
50 | static int | 69 | static int |
51 | nfs4_save_creds(const struct cred **original_creds) | 70 | nfs4_save_creds(const struct cred **original_creds) |
@@ -117,7 +136,8 @@ out_no_tfm: | |||
117 | return status; | 136 | return status; |
118 | } | 137 | } |
119 | 138 | ||
120 | void nfsd4_create_clid_dir(struct nfs4_client *clp) | 139 | static void |
140 | nfsd4_create_clid_dir(struct nfs4_client *clp) | ||
121 | { | 141 | { |
122 | const struct cred *original_cred; | 142 | const struct cred *original_cred; |
123 | char *dname = clp->cl_recdir; | 143 | char *dname = clp->cl_recdir; |
@@ -126,9 +146,8 @@ void nfsd4_create_clid_dir(struct nfs4_client *clp) | |||
126 | 146 | ||
127 | dprintk("NFSD: nfsd4_create_clid_dir for \"%s\"\n", dname); | 147 | dprintk("NFSD: nfsd4_create_clid_dir for \"%s\"\n", dname); |
128 | 148 | ||
129 | if (clp->cl_firststate) | 149 | if (test_and_set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags)) |
130 | return; | 150 | return; |
131 | clp->cl_firststate = 1; | ||
132 | if (!rec_file) | 151 | if (!rec_file) |
133 | return; | 152 | return; |
134 | status = nfs4_save_creds(&original_cred); | 153 | status = nfs4_save_creds(&original_cred); |
@@ -265,19 +284,19 @@ out_unlock: | |||
265 | return status; | 284 | return status; |
266 | } | 285 | } |
267 | 286 | ||
268 | void | 287 | static void |
269 | nfsd4_remove_clid_dir(struct nfs4_client *clp) | 288 | nfsd4_remove_clid_dir(struct nfs4_client *clp) |
270 | { | 289 | { |
271 | const struct cred *original_cred; | 290 | const struct cred *original_cred; |
272 | int status; | 291 | int status; |
273 | 292 | ||
274 | if (!rec_file || !clp->cl_firststate) | 293 | if (!rec_file || !test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags)) |
275 | return; | 294 | return; |
276 | 295 | ||
277 | status = mnt_want_write_file(rec_file); | 296 | status = mnt_want_write_file(rec_file); |
278 | if (status) | 297 | if (status) |
279 | goto out; | 298 | goto out; |
280 | clp->cl_firststate = 0; | 299 | clear_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags); |
281 | 300 | ||
282 | status = nfs4_save_creds(&original_cred); | 301 | status = nfs4_save_creds(&original_cred); |
283 | if (status < 0) | 302 | if (status < 0) |
@@ -292,7 +311,6 @@ out: | |||
292 | if (status) | 311 | if (status) |
293 | printk("NFSD: Failed to remove expired client state directory" | 312 | printk("NFSD: Failed to remove expired client state directory" |
294 | " %.*s\n", HEXDIR_LEN, clp->cl_recdir); | 313 | " %.*s\n", HEXDIR_LEN, clp->cl_recdir); |
295 | return; | ||
296 | } | 314 | } |
297 | 315 | ||
298 | static int | 316 | static int |
@@ -311,8 +329,9 @@ purge_old(struct dentry *parent, struct dentry *child) | |||
311 | return 0; | 329 | return 0; |
312 | } | 330 | } |
313 | 331 | ||
314 | void | 332 | static void |
315 | nfsd4_recdir_purge_old(void) { | 333 | nfsd4_recdir_purge_old(struct net *net, time_t boot_time) |
334 | { | ||
316 | int status; | 335 | int status; |
317 | 336 | ||
318 | if (!rec_file) | 337 | if (!rec_file) |
@@ -343,7 +362,7 @@ load_recdir(struct dentry *parent, struct dentry *child) | |||
343 | return 0; | 362 | return 0; |
344 | } | 363 | } |
345 | 364 | ||
346 | int | 365 | static int |
347 | nfsd4_recdir_load(void) { | 366 | nfsd4_recdir_load(void) { |
348 | int status; | 367 | int status; |
349 | 368 | ||
@@ -361,8 +380,8 @@ nfsd4_recdir_load(void) { | |||
361 | * Hold reference to the recovery directory. | 380 | * Hold reference to the recovery directory. |
362 | */ | 381 | */ |
363 | 382 | ||
364 | void | 383 | static int |
365 | nfsd4_init_recdir() | 384 | nfsd4_init_recdir(void) |
366 | { | 385 | { |
367 | const struct cred *original_cred; | 386 | const struct cred *original_cred; |
368 | int status; | 387 | int status; |
@@ -377,20 +396,44 @@ nfsd4_init_recdir() | |||
377 | printk("NFSD: Unable to change credentials to find recovery" | 396 | printk("NFSD: Unable to change credentials to find recovery" |
378 | " directory: error %d\n", | 397 | " directory: error %d\n", |
379 | status); | 398 | status); |
380 | return; | 399 | return status; |
381 | } | 400 | } |
382 | 401 | ||
383 | rec_file = filp_open(user_recovery_dirname, O_RDONLY | O_DIRECTORY, 0); | 402 | rec_file = filp_open(user_recovery_dirname, O_RDONLY | O_DIRECTORY, 0); |
384 | if (IS_ERR(rec_file)) { | 403 | if (IS_ERR(rec_file)) { |
385 | printk("NFSD: unable to find recovery directory %s\n", | 404 | printk("NFSD: unable to find recovery directory %s\n", |
386 | user_recovery_dirname); | 405 | user_recovery_dirname); |
406 | status = PTR_ERR(rec_file); | ||
387 | rec_file = NULL; | 407 | rec_file = NULL; |
388 | } | 408 | } |
389 | 409 | ||
390 | nfs4_reset_creds(original_cred); | 410 | nfs4_reset_creds(original_cred); |
411 | return status; | ||
391 | } | 412 | } |
392 | 413 | ||
393 | void | 414 | static int |
415 | nfsd4_load_reboot_recovery_data(struct net *net) | ||
416 | { | ||
417 | int status; | ||
418 | |||
419 | /* XXX: The legacy code won't work in a container */ | ||
420 | if (net != &init_net) { | ||
421 | WARN(1, KERN_ERR "NFSD: attempt to initialize legacy client " | ||
422 | "tracking in a container!\n"); | ||
423 | return -EINVAL; | ||
424 | } | ||
425 | |||
426 | nfs4_lock_state(); | ||
427 | status = nfsd4_init_recdir(); | ||
428 | if (!status) | ||
429 | status = nfsd4_recdir_load(); | ||
430 | nfs4_unlock_state(); | ||
431 | if (status) | ||
432 | printk(KERN_ERR "NFSD: Failure reading reboot recovery data\n"); | ||
433 | return status; | ||
434 | } | ||
435 | |||
436 | static void | ||
394 | nfsd4_shutdown_recdir(void) | 437 | nfsd4_shutdown_recdir(void) |
395 | { | 438 | { |
396 | if (!rec_file) | 439 | if (!rec_file) |
@@ -399,6 +442,13 @@ nfsd4_shutdown_recdir(void) | |||
399 | rec_file = NULL; | 442 | rec_file = NULL; |
400 | } | 443 | } |
401 | 444 | ||
445 | static void | ||
446 | nfsd4_legacy_tracking_exit(struct net *net) | ||
447 | { | ||
448 | nfs4_release_reclaim(); | ||
449 | nfsd4_shutdown_recdir(); | ||
450 | } | ||
451 | |||
402 | /* | 452 | /* |
403 | * Change the NFSv4 recovery directory to recdir. | 453 | * Change the NFSv4 recovery directory to recdir. |
404 | */ | 454 | */ |
@@ -425,3 +475,572 @@ nfs4_recoverydir(void) | |||
425 | { | 475 | { |
426 | return user_recovery_dirname; | 476 | return user_recovery_dirname; |
427 | } | 477 | } |
478 | |||
479 | static int | ||
480 | nfsd4_check_legacy_client(struct nfs4_client *clp) | ||
481 | { | ||
482 | /* did we already find that this client is stable? */ | ||
483 | if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags)) | ||
484 | return 0; | ||
485 | |||
486 | /* look for it in the reclaim hashtable otherwise */ | ||
487 | if (nfsd4_find_reclaim_client(clp)) { | ||
488 | set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags); | ||
489 | return 0; | ||
490 | } | ||
491 | |||
492 | return -ENOENT; | ||
493 | } | ||
494 | |||
495 | static struct nfsd4_client_tracking_ops nfsd4_legacy_tracking_ops = { | ||
496 | .init = nfsd4_load_reboot_recovery_data, | ||
497 | .exit = nfsd4_legacy_tracking_exit, | ||
498 | .create = nfsd4_create_clid_dir, | ||
499 | .remove = nfsd4_remove_clid_dir, | ||
500 | .check = nfsd4_check_legacy_client, | ||
501 | .grace_done = nfsd4_recdir_purge_old, | ||
502 | }; | ||
503 | |||
504 | /* Globals */ | ||
505 | #define NFSD_PIPE_DIR "nfsd" | ||
506 | #define NFSD_CLD_PIPE "cld" | ||
507 | |||
508 | /* per-net-ns structure for holding cld upcall info */ | ||
509 | struct cld_net { | ||
510 | struct rpc_pipe *cn_pipe; | ||
511 | spinlock_t cn_lock; | ||
512 | struct list_head cn_list; | ||
513 | unsigned int cn_xid; | ||
514 | }; | ||
515 | |||
516 | struct cld_upcall { | ||
517 | struct list_head cu_list; | ||
518 | struct cld_net *cu_net; | ||
519 | struct task_struct *cu_task; | ||
520 | struct cld_msg cu_msg; | ||
521 | }; | ||
522 | |||
523 | static int | ||
524 | __cld_pipe_upcall(struct rpc_pipe *pipe, struct cld_msg *cmsg) | ||
525 | { | ||
526 | int ret; | ||
527 | struct rpc_pipe_msg msg; | ||
528 | |||
529 | memset(&msg, 0, sizeof(msg)); | ||
530 | msg.data = cmsg; | ||
531 | msg.len = sizeof(*cmsg); | ||
532 | |||
533 | /* | ||
534 | * Set task state before we queue the upcall. That prevents | ||
535 | * wake_up_process in the downcall from racing with schedule. | ||
536 | */ | ||
537 | set_current_state(TASK_UNINTERRUPTIBLE); | ||
538 | ret = rpc_queue_upcall(pipe, &msg); | ||
539 | if (ret < 0) { | ||
540 | set_current_state(TASK_RUNNING); | ||
541 | goto out; | ||
542 | } | ||
543 | |||
544 | schedule(); | ||
545 | set_current_state(TASK_RUNNING); | ||
546 | |||
547 | if (msg.errno < 0) | ||
548 | ret = msg.errno; | ||
549 | out: | ||
550 | return ret; | ||
551 | } | ||
552 | |||
553 | static int | ||
554 | cld_pipe_upcall(struct rpc_pipe *pipe, struct cld_msg *cmsg) | ||
555 | { | ||
556 | int ret; | ||
557 | |||
558 | /* | ||
559 | * -EAGAIN occurs when pipe is closed and reopened while there are | ||
560 | * upcalls queued. | ||
561 | */ | ||
562 | do { | ||
563 | ret = __cld_pipe_upcall(pipe, cmsg); | ||
564 | } while (ret == -EAGAIN); | ||
565 | |||
566 | return ret; | ||
567 | } | ||
568 | |||
569 | static ssize_t | ||
570 | cld_pipe_downcall(struct file *filp, const char __user *src, size_t mlen) | ||
571 | { | ||
572 | struct cld_upcall *tmp, *cup; | ||
573 | struct cld_msg *cmsg = (struct cld_msg *)src; | ||
574 | uint32_t xid; | ||
575 | struct nfsd_net *nn = net_generic(filp->f_dentry->d_sb->s_fs_info, | ||
576 | nfsd_net_id); | ||
577 | struct cld_net *cn = nn->cld_net; | ||
578 | |||
579 | if (mlen != sizeof(*cmsg)) { | ||
580 | dprintk("%s: got %lu bytes, expected %lu\n", __func__, mlen, | ||
581 | sizeof(*cmsg)); | ||
582 | return -EINVAL; | ||
583 | } | ||
584 | |||
585 | /* copy just the xid so we can try to find that */ | ||
586 | if (copy_from_user(&xid, &cmsg->cm_xid, sizeof(xid)) != 0) { | ||
587 | dprintk("%s: error when copying xid from userspace", __func__); | ||
588 | return -EFAULT; | ||
589 | } | ||
590 | |||
591 | /* walk the list and find corresponding xid */ | ||
592 | cup = NULL; | ||
593 | spin_lock(&cn->cn_lock); | ||
594 | list_for_each_entry(tmp, &cn->cn_list, cu_list) { | ||
595 | if (get_unaligned(&tmp->cu_msg.cm_xid) == xid) { | ||
596 | cup = tmp; | ||
597 | list_del_init(&cup->cu_list); | ||
598 | break; | ||
599 | } | ||
600 | } | ||
601 | spin_unlock(&cn->cn_lock); | ||
602 | |||
603 | /* couldn't find upcall? */ | ||
604 | if (!cup) { | ||
605 | dprintk("%s: couldn't find upcall -- xid=%u\n", __func__, xid); | ||
606 | return -EINVAL; | ||
607 | } | ||
608 | |||
609 | if (copy_from_user(&cup->cu_msg, src, mlen) != 0) | ||
610 | return -EFAULT; | ||
611 | |||
612 | wake_up_process(cup->cu_task); | ||
613 | return mlen; | ||
614 | } | ||
615 | |||
616 | static void | ||
617 | cld_pipe_destroy_msg(struct rpc_pipe_msg *msg) | ||
618 | { | ||
619 | struct cld_msg *cmsg = msg->data; | ||
620 | struct cld_upcall *cup = container_of(cmsg, struct cld_upcall, | ||
621 | cu_msg); | ||
622 | |||
623 | /* errno >= 0 means we got a downcall */ | ||
624 | if (msg->errno >= 0) | ||
625 | return; | ||
626 | |||
627 | wake_up_process(cup->cu_task); | ||
628 | } | ||
629 | |||
630 | static const struct rpc_pipe_ops cld_upcall_ops = { | ||
631 | .upcall = rpc_pipe_generic_upcall, | ||
632 | .downcall = cld_pipe_downcall, | ||
633 | .destroy_msg = cld_pipe_destroy_msg, | ||
634 | }; | ||
635 | |||
636 | static struct dentry * | ||
637 | nfsd4_cld_register_sb(struct super_block *sb, struct rpc_pipe *pipe) | ||
638 | { | ||
639 | struct dentry *dir, *dentry; | ||
640 | |||
641 | dir = rpc_d_lookup_sb(sb, NFSD_PIPE_DIR); | ||
642 | if (dir == NULL) | ||
643 | return ERR_PTR(-ENOENT); | ||
644 | dentry = rpc_mkpipe_dentry(dir, NFSD_CLD_PIPE, NULL, pipe); | ||
645 | dput(dir); | ||
646 | return dentry; | ||
647 | } | ||
648 | |||
649 | static void | ||
650 | nfsd4_cld_unregister_sb(struct rpc_pipe *pipe) | ||
651 | { | ||
652 | if (pipe->dentry) | ||
653 | rpc_unlink(pipe->dentry); | ||
654 | } | ||
655 | |||
656 | static struct dentry * | ||
657 | nfsd4_cld_register_net(struct net *net, struct rpc_pipe *pipe) | ||
658 | { | ||
659 | struct super_block *sb; | ||
660 | struct dentry *dentry; | ||
661 | |||
662 | sb = rpc_get_sb_net(net); | ||
663 | if (!sb) | ||
664 | return NULL; | ||
665 | dentry = nfsd4_cld_register_sb(sb, pipe); | ||
666 | rpc_put_sb_net(net); | ||
667 | return dentry; | ||
668 | } | ||
669 | |||
670 | static void | ||
671 | nfsd4_cld_unregister_net(struct net *net, struct rpc_pipe *pipe) | ||
672 | { | ||
673 | struct super_block *sb; | ||
674 | |||
675 | sb = rpc_get_sb_net(net); | ||
676 | if (sb) { | ||
677 | nfsd4_cld_unregister_sb(pipe); | ||
678 | rpc_put_sb_net(net); | ||
679 | } | ||
680 | } | ||
681 | |||
682 | /* Initialize rpc_pipefs pipe for communication with client tracking daemon */ | ||
683 | static int | ||
684 | nfsd4_init_cld_pipe(struct net *net) | ||
685 | { | ||
686 | int ret; | ||
687 | struct dentry *dentry; | ||
688 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); | ||
689 | struct cld_net *cn; | ||
690 | |||
691 | if (nn->cld_net) | ||
692 | return 0; | ||
693 | |||
694 | cn = kzalloc(sizeof(*cn), GFP_KERNEL); | ||
695 | if (!cn) { | ||
696 | ret = -ENOMEM; | ||
697 | goto err; | ||
698 | } | ||
699 | |||
700 | cn->cn_pipe = rpc_mkpipe_data(&cld_upcall_ops, RPC_PIPE_WAIT_FOR_OPEN); | ||
701 | if (IS_ERR(cn->cn_pipe)) { | ||
702 | ret = PTR_ERR(cn->cn_pipe); | ||
703 | goto err; | ||
704 | } | ||
705 | spin_lock_init(&cn->cn_lock); | ||
706 | INIT_LIST_HEAD(&cn->cn_list); | ||
707 | |||
708 | dentry = nfsd4_cld_register_net(net, cn->cn_pipe); | ||
709 | if (IS_ERR(dentry)) { | ||
710 | ret = PTR_ERR(dentry); | ||
711 | goto err_destroy_data; | ||
712 | } | ||
713 | |||
714 | cn->cn_pipe->dentry = dentry; | ||
715 | nn->cld_net = cn; | ||
716 | return 0; | ||
717 | |||
718 | err_destroy_data: | ||
719 | rpc_destroy_pipe_data(cn->cn_pipe); | ||
720 | err: | ||
721 | kfree(cn); | ||
722 | printk(KERN_ERR "NFSD: unable to create nfsdcld upcall pipe (%d)\n", | ||
723 | ret); | ||
724 | return ret; | ||
725 | } | ||
726 | |||
727 | static void | ||
728 | nfsd4_remove_cld_pipe(struct net *net) | ||
729 | { | ||
730 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); | ||
731 | struct cld_net *cn = nn->cld_net; | ||
732 | |||
733 | nfsd4_cld_unregister_net(net, cn->cn_pipe); | ||
734 | rpc_destroy_pipe_data(cn->cn_pipe); | ||
735 | kfree(nn->cld_net); | ||
736 | nn->cld_net = NULL; | ||
737 | } | ||
738 | |||
739 | static struct cld_upcall * | ||
740 | alloc_cld_upcall(struct cld_net *cn) | ||
741 | { | ||
742 | struct cld_upcall *new, *tmp; | ||
743 | |||
744 | new = kzalloc(sizeof(*new), GFP_KERNEL); | ||
745 | if (!new) | ||
746 | return new; | ||
747 | |||
748 | /* FIXME: hard cap on number in flight? */ | ||
749 | restart_search: | ||
750 | spin_lock(&cn->cn_lock); | ||
751 | list_for_each_entry(tmp, &cn->cn_list, cu_list) { | ||
752 | if (tmp->cu_msg.cm_xid == cn->cn_xid) { | ||
753 | cn->cn_xid++; | ||
754 | spin_unlock(&cn->cn_lock); | ||
755 | goto restart_search; | ||
756 | } | ||
757 | } | ||
758 | new->cu_task = current; | ||
759 | new->cu_msg.cm_vers = CLD_UPCALL_VERSION; | ||
760 | put_unaligned(cn->cn_xid++, &new->cu_msg.cm_xid); | ||
761 | new->cu_net = cn; | ||
762 | list_add(&new->cu_list, &cn->cn_list); | ||
763 | spin_unlock(&cn->cn_lock); | ||
764 | |||
765 | dprintk("%s: allocated xid %u\n", __func__, new->cu_msg.cm_xid); | ||
766 | |||
767 | return new; | ||
768 | } | ||
769 | |||
770 | static void | ||
771 | free_cld_upcall(struct cld_upcall *victim) | ||
772 | { | ||
773 | struct cld_net *cn = victim->cu_net; | ||
774 | |||
775 | spin_lock(&cn->cn_lock); | ||
776 | list_del(&victim->cu_list); | ||
777 | spin_unlock(&cn->cn_lock); | ||
778 | kfree(victim); | ||
779 | } | ||
780 | |||
781 | /* Ask daemon to create a new record */ | ||
782 | static void | ||
783 | nfsd4_cld_create(struct nfs4_client *clp) | ||
784 | { | ||
785 | int ret; | ||
786 | struct cld_upcall *cup; | ||
787 | /* FIXME: determine net from clp */ | ||
788 | struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); | ||
789 | struct cld_net *cn = nn->cld_net; | ||
790 | |||
791 | /* Don't upcall if it's already stored */ | ||
792 | if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags)) | ||
793 | return; | ||
794 | |||
795 | cup = alloc_cld_upcall(cn); | ||
796 | if (!cup) { | ||
797 | ret = -ENOMEM; | ||
798 | goto out_err; | ||
799 | } | ||
800 | |||
801 | cup->cu_msg.cm_cmd = Cld_Create; | ||
802 | cup->cu_msg.cm_u.cm_name.cn_len = clp->cl_name.len; | ||
803 | memcpy(cup->cu_msg.cm_u.cm_name.cn_id, clp->cl_name.data, | ||
804 | clp->cl_name.len); | ||
805 | |||
806 | ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_msg); | ||
807 | if (!ret) { | ||
808 | ret = cup->cu_msg.cm_status; | ||
809 | set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags); | ||
810 | } | ||
811 | |||
812 | free_cld_upcall(cup); | ||
813 | out_err: | ||
814 | if (ret) | ||
815 | printk(KERN_ERR "NFSD: Unable to create client " | ||
816 | "record on stable storage: %d\n", ret); | ||
817 | } | ||
818 | |||
819 | /* Ask daemon to create a new record */ | ||
820 | static void | ||
821 | nfsd4_cld_remove(struct nfs4_client *clp) | ||
822 | { | ||
823 | int ret; | ||
824 | struct cld_upcall *cup; | ||
825 | /* FIXME: determine net from clp */ | ||
826 | struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); | ||
827 | struct cld_net *cn = nn->cld_net; | ||
828 | |||
829 | /* Don't upcall if it's already removed */ | ||
830 | if (!test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags)) | ||
831 | return; | ||
832 | |||
833 | cup = alloc_cld_upcall(cn); | ||
834 | if (!cup) { | ||
835 | ret = -ENOMEM; | ||
836 | goto out_err; | ||
837 | } | ||
838 | |||
839 | cup->cu_msg.cm_cmd = Cld_Remove; | ||
840 | cup->cu_msg.cm_u.cm_name.cn_len = clp->cl_name.len; | ||
841 | memcpy(cup->cu_msg.cm_u.cm_name.cn_id, clp->cl_name.data, | ||
842 | clp->cl_name.len); | ||
843 | |||
844 | ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_msg); | ||
845 | if (!ret) { | ||
846 | ret = cup->cu_msg.cm_status; | ||
847 | clear_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags); | ||
848 | } | ||
849 | |||
850 | free_cld_upcall(cup); | ||
851 | out_err: | ||
852 | if (ret) | ||
853 | printk(KERN_ERR "NFSD: Unable to remove client " | ||
854 | "record from stable storage: %d\n", ret); | ||
855 | } | ||
856 | |||
857 | /* Check for presence of a record, and update its timestamp */ | ||
858 | static int | ||
859 | nfsd4_cld_check(struct nfs4_client *clp) | ||
860 | { | ||
861 | int ret; | ||
862 | struct cld_upcall *cup; | ||
863 | /* FIXME: determine net from clp */ | ||
864 | struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); | ||
865 | struct cld_net *cn = nn->cld_net; | ||
866 | |||
867 | /* Don't upcall if one was already stored during this grace pd */ | ||
868 | if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags)) | ||
869 | return 0; | ||
870 | |||
871 | cup = alloc_cld_upcall(cn); | ||
872 | if (!cup) { | ||
873 | printk(KERN_ERR "NFSD: Unable to check client record on " | ||
874 | "stable storage: %d\n", -ENOMEM); | ||
875 | return -ENOMEM; | ||
876 | } | ||
877 | |||
878 | cup->cu_msg.cm_cmd = Cld_Check; | ||
879 | cup->cu_msg.cm_u.cm_name.cn_len = clp->cl_name.len; | ||
880 | memcpy(cup->cu_msg.cm_u.cm_name.cn_id, clp->cl_name.data, | ||
881 | clp->cl_name.len); | ||
882 | |||
883 | ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_msg); | ||
884 | if (!ret) { | ||
885 | ret = cup->cu_msg.cm_status; | ||
886 | set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags); | ||
887 | } | ||
888 | |||
889 | free_cld_upcall(cup); | ||
890 | return ret; | ||
891 | } | ||
892 | |||
893 | static void | ||
894 | nfsd4_cld_grace_done(struct net *net, time_t boot_time) | ||
895 | { | ||
896 | int ret; | ||
897 | struct cld_upcall *cup; | ||
898 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); | ||
899 | struct cld_net *cn = nn->cld_net; | ||
900 | |||
901 | cup = alloc_cld_upcall(cn); | ||
902 | if (!cup) { | ||
903 | ret = -ENOMEM; | ||
904 | goto out_err; | ||
905 | } | ||
906 | |||
907 | cup->cu_msg.cm_cmd = Cld_GraceDone; | ||
908 | cup->cu_msg.cm_u.cm_gracetime = (int64_t)boot_time; | ||
909 | ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_msg); | ||
910 | if (!ret) | ||
911 | ret = cup->cu_msg.cm_status; | ||
912 | |||
913 | free_cld_upcall(cup); | ||
914 | out_err: | ||
915 | if (ret) | ||
916 | printk(KERN_ERR "NFSD: Unable to end grace period: %d\n", ret); | ||
917 | } | ||
918 | |||
919 | static struct nfsd4_client_tracking_ops nfsd4_cld_tracking_ops = { | ||
920 | .init = nfsd4_init_cld_pipe, | ||
921 | .exit = nfsd4_remove_cld_pipe, | ||
922 | .create = nfsd4_cld_create, | ||
923 | .remove = nfsd4_cld_remove, | ||
924 | .check = nfsd4_cld_check, | ||
925 | .grace_done = nfsd4_cld_grace_done, | ||
926 | }; | ||
927 | |||
928 | int | ||
929 | nfsd4_client_tracking_init(struct net *net) | ||
930 | { | ||
931 | int status; | ||
932 | struct path path; | ||
933 | |||
934 | if (!client_tracking_ops) { | ||
935 | client_tracking_ops = &nfsd4_cld_tracking_ops; | ||
936 | status = kern_path(nfs4_recoverydir(), LOOKUP_FOLLOW, &path); | ||
937 | if (!status) { | ||
938 | if (S_ISDIR(path.dentry->d_inode->i_mode)) | ||
939 | client_tracking_ops = | ||
940 | &nfsd4_legacy_tracking_ops; | ||
941 | path_put(&path); | ||
942 | } | ||
943 | } | ||
944 | |||
945 | status = client_tracking_ops->init(net); | ||
946 | if (status) { | ||
947 | printk(KERN_WARNING "NFSD: Unable to initialize client " | ||
948 | "recovery tracking! (%d)\n", status); | ||
949 | client_tracking_ops = NULL; | ||
950 | } | ||
951 | return status; | ||
952 | } | ||
953 | |||
954 | void | ||
955 | nfsd4_client_tracking_exit(struct net *net) | ||
956 | { | ||
957 | if (client_tracking_ops) { | ||
958 | client_tracking_ops->exit(net); | ||
959 | client_tracking_ops = NULL; | ||
960 | } | ||
961 | } | ||
962 | |||
963 | void | ||
964 | nfsd4_client_record_create(struct nfs4_client *clp) | ||
965 | { | ||
966 | if (client_tracking_ops) | ||
967 | client_tracking_ops->create(clp); | ||
968 | } | ||
969 | |||
970 | void | ||
971 | nfsd4_client_record_remove(struct nfs4_client *clp) | ||
972 | { | ||
973 | if (client_tracking_ops) | ||
974 | client_tracking_ops->remove(clp); | ||
975 | } | ||
976 | |||
977 | int | ||
978 | nfsd4_client_record_check(struct nfs4_client *clp) | ||
979 | { | ||
980 | if (client_tracking_ops) | ||
981 | return client_tracking_ops->check(clp); | ||
982 | |||
983 | return -EOPNOTSUPP; | ||
984 | } | ||
985 | |||
986 | void | ||
987 | nfsd4_record_grace_done(struct net *net, time_t boot_time) | ||
988 | { | ||
989 | if (client_tracking_ops) | ||
990 | client_tracking_ops->grace_done(net, boot_time); | ||
991 | } | ||
992 | |||
993 | static int | ||
994 | rpc_pipefs_event(struct notifier_block *nb, unsigned long event, void *ptr) | ||
995 | { | ||
996 | struct super_block *sb = ptr; | ||
997 | struct net *net = sb->s_fs_info; | ||
998 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); | ||
999 | struct cld_net *cn = nn->cld_net; | ||
1000 | struct dentry *dentry; | ||
1001 | int ret = 0; | ||
1002 | |||
1003 | if (!try_module_get(THIS_MODULE)) | ||
1004 | return 0; | ||
1005 | |||
1006 | if (!cn) { | ||
1007 | module_put(THIS_MODULE); | ||
1008 | return 0; | ||
1009 | } | ||
1010 | |||
1011 | switch (event) { | ||
1012 | case RPC_PIPEFS_MOUNT: | ||
1013 | dentry = nfsd4_cld_register_sb(sb, cn->cn_pipe); | ||
1014 | if (IS_ERR(dentry)) { | ||
1015 | ret = PTR_ERR(dentry); | ||
1016 | break; | ||
1017 | } | ||
1018 | cn->cn_pipe->dentry = dentry; | ||
1019 | break; | ||
1020 | case RPC_PIPEFS_UMOUNT: | ||
1021 | if (cn->cn_pipe->dentry) | ||
1022 | nfsd4_cld_unregister_sb(cn->cn_pipe); | ||
1023 | break; | ||
1024 | default: | ||
1025 | ret = -ENOTSUPP; | ||
1026 | break; | ||
1027 | } | ||
1028 | module_put(THIS_MODULE); | ||
1029 | return ret; | ||
1030 | } | ||
1031 | |||
1032 | struct notifier_block nfsd4_cld_block = { | ||
1033 | .notifier_call = rpc_pipefs_event, | ||
1034 | }; | ||
1035 | |||
1036 | int | ||
1037 | register_cld_notifier(void) | ||
1038 | { | ||
1039 | return rpc_pipefs_notifier_register(&nfsd4_cld_block); | ||
1040 | } | ||
1041 | |||
1042 | void | ||
1043 | unregister_cld_notifier(void) | ||
1044 | { | ||
1045 | rpc_pipefs_notifier_unregister(&nfsd4_cld_block); | ||
1046 | } | ||