aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nfsd
diff options
context:
space:
mode:
authorJeff Layton <jlayton@redhat.com>2012-03-21 09:52:07 -0400
committerJ. Bruce Fields <bfields@redhat.com>2012-03-26 11:49:48 -0400
commitf3f8014862d813cca81a597c83bd1dbf0fb2b8f6 (patch)
treef9d41fd42d38690d3f8e9e70a3542928706065c7 /fs/nfsd
parent0ab628d856a63d63b47307b09851d1e955c706ac (diff)
nfsd: add the infrastructure to handle the cld upcall
...and add a mechanism for switching between the "legacy" tracker and the new one. The decision is made by looking to see whether the v4recoverydir exists. If it does, then the legacy client tracker is used. If it's not, then the kernel will create a "cld" pipe in rpc_pipefs. That pipe is used to talk to a daemon for handling the upcall. Most of the data structures for the new client tracker are handled on a per-namespace basis, so this upcall should be essentially ready for containerization. For now however, nfsd just starts it by calling the initialization and exit functions for init_net. I'm making the assumption that at some point in the future we'll be able to determine the net namespace from the nfs4_client. Until then, this patch hardcodes init_net in those places. I've sprinkled some "FIXME" comments around that code to attempt to make it clear where we'll need to fix that up later. Signed-off-by: Jeff Layton <jlayton@redhat.com> Signed-off-by: J. Bruce Fields <bfields@redhat.com>
Diffstat (limited to 'fs/nfsd')
-rw-r--r--fs/nfsd/nfs4recover.c444
1 files changed, 443 insertions, 1 deletions
diff --git a/fs/nfsd/nfs4recover.c b/fs/nfsd/nfs4recover.c
index e616f88b7f19..cec62ed2a064 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,10 +37,16 @@
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 <net/net_namespace.h>
42#include <linux/sunrpc/rpc_pipe_fs.h>
43#include <linux/sunrpc/clnt.h>
44#include <linux/nfsd/cld.h>
39 45
40#include "nfsd.h" 46#include "nfsd.h"
41#include "state.h" 47#include "state.h"
42#include "vfs.h" 48#include "vfs.h"
49#include "netns.h"
43 50
44#define NFSDDBG_FACILITY NFSDDBG_PROC 51#define NFSDDBG_FACILITY NFSDDBG_PROC
45 52
@@ -486,12 +493,447 @@ static struct nfsd4_client_tracking_ops nfsd4_legacy_tracking_ops = {
486 .grace_done = nfsd4_recdir_purge_old, 493 .grace_done = nfsd4_recdir_purge_old,
487}; 494};
488 495
496/* Globals */
497#define NFSD_PIPE_DIR "nfsd"
498#define NFSD_CLD_PIPE "cld"
499
500/* per-net-ns structure for holding cld upcall info */
501struct cld_net {
502 struct rpc_pipe *cn_pipe;
503 spinlock_t cn_lock;
504 struct list_head cn_list;
505 unsigned int cn_xid;
506};
507
508struct cld_upcall {
509 struct list_head cu_list;
510 struct cld_net *cu_net;
511 struct task_struct *cu_task;
512 struct cld_msg cu_msg;
513};
514
515static int
516__cld_pipe_upcall(struct rpc_pipe *pipe, struct cld_msg *cmsg)
517{
518 int ret;
519 struct rpc_pipe_msg msg;
520
521 memset(&msg, 0, sizeof(msg));
522 msg.data = cmsg;
523 msg.len = sizeof(*cmsg);
524
525 /*
526 * Set task state before we queue the upcall. That prevents
527 * wake_up_process in the downcall from racing with schedule.
528 */
529 set_current_state(TASK_UNINTERRUPTIBLE);
530 ret = rpc_queue_upcall(pipe, &msg);
531 if (ret < 0) {
532 set_current_state(TASK_RUNNING);
533 goto out;
534 }
535
536 schedule();
537 set_current_state(TASK_RUNNING);
538
539 if (msg.errno < 0)
540 ret = msg.errno;
541out:
542 return ret;
543}
544
545static int
546cld_pipe_upcall(struct rpc_pipe *pipe, struct cld_msg *cmsg)
547{
548 int ret;
549
550 /*
551 * -EAGAIN occurs when pipe is closed and reopened while there are
552 * upcalls queued.
553 */
554 do {
555 ret = __cld_pipe_upcall(pipe, cmsg);
556 } while (ret == -EAGAIN);
557
558 return ret;
559}
560
561static ssize_t
562cld_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)
563{
564 struct cld_upcall *tmp, *cup;
565 struct cld_msg *cmsg = (struct cld_msg *)src;
566 uint32_t xid;
567 struct nfsd_net *nn = net_generic(filp->f_dentry->d_sb->s_fs_info,
568 nfsd_net_id);
569 struct cld_net *cn = nn->cld_net;
570
571 if (mlen != sizeof(*cmsg)) {
572 dprintk("%s: got %lu bytes, expected %lu\n", __func__, mlen,
573 sizeof(*cmsg));
574 return -EINVAL;
575 }
576
577 /* copy just the xid so we can try to find that */
578 if (copy_from_user(&xid, &cmsg->cm_xid, sizeof(xid)) != 0) {
579 dprintk("%s: error when copying xid from userspace", __func__);
580 return -EFAULT;
581 }
582
583 /* walk the list and find corresponding xid */
584 cup = NULL;
585 spin_lock(&cn->cn_lock);
586 list_for_each_entry(tmp, &cn->cn_list, cu_list) {
587 if (get_unaligned(&tmp->cu_msg.cm_xid) == xid) {
588 cup = tmp;
589 list_del_init(&cup->cu_list);
590 break;
591 }
592 }
593 spin_unlock(&cn->cn_lock);
594
595 /* couldn't find upcall? */
596 if (!cup) {
597 dprintk("%s: couldn't find upcall -- xid=%u\n", __func__,
598 cup->cu_msg.cm_xid);
599 return -EINVAL;
600 }
601
602 if (copy_from_user(&cup->cu_msg, src, mlen) != 0)
603 return -EFAULT;
604
605 wake_up_process(cup->cu_task);
606 return mlen;
607}
608
609static void
610cld_pipe_destroy_msg(struct rpc_pipe_msg *msg)
611{
612 struct cld_msg *cmsg = msg->data;
613 struct cld_upcall *cup = container_of(cmsg, struct cld_upcall,
614 cu_msg);
615
616 /* errno >= 0 means we got a downcall */
617 if (msg->errno >= 0)
618 return;
619
620 wake_up_process(cup->cu_task);
621}
622
623static const struct rpc_pipe_ops cld_upcall_ops = {
624 .upcall = rpc_pipe_generic_upcall,
625 .downcall = cld_pipe_downcall,
626 .destroy_msg = cld_pipe_destroy_msg,
627};
628
629static struct dentry *
630nfsd4_cld_register_sb(struct super_block *sb, struct rpc_pipe *pipe)
631{
632 struct dentry *dir, *dentry;
633
634 dir = rpc_d_lookup_sb(sb, NFSD_PIPE_DIR);
635 if (dir == NULL)
636 return ERR_PTR(-ENOENT);
637 dentry = rpc_mkpipe_dentry(dir, NFSD_CLD_PIPE, NULL, pipe);
638 dput(dir);
639 return dentry;
640}
641
642static void
643nfsd4_cld_unregister_sb(struct rpc_pipe *pipe)
644{
645 if (pipe->dentry)
646 rpc_unlink(pipe->dentry);
647}
648
649static struct dentry *
650nfsd4_cld_register_net(struct net *net, struct rpc_pipe *pipe)
651{
652 struct super_block *sb;
653 struct dentry *dentry;
654
655 sb = rpc_get_sb_net(net);
656 if (!sb)
657 return NULL;
658 dentry = nfsd4_cld_register_sb(sb, pipe);
659 rpc_put_sb_net(net);
660 return dentry;
661}
662
663static void
664nfsd4_cld_unregister_net(struct net *net, struct rpc_pipe *pipe)
665{
666 struct super_block *sb;
667
668 sb = rpc_get_sb_net(net);
669 if (sb) {
670 nfsd4_cld_unregister_sb(pipe);
671 rpc_put_sb_net(net);
672 }
673}
674
675/* Initialize rpc_pipefs pipe for communication with client tracking daemon */
676static int
677nfsd4_init_cld_pipe(struct net *net)
678{
679 int ret;
680 struct dentry *dentry;
681 struct nfsd_net *nn = net_generic(net, nfsd_net_id);
682 struct cld_net *cn;
683
684 if (nn->cld_net)
685 return 0;
686
687 cn = kzalloc(sizeof(*cn), GFP_KERNEL);
688 if (!cn) {
689 ret = -ENOMEM;
690 goto err;
691 }
692
693 cn->cn_pipe = rpc_mkpipe_data(&cld_upcall_ops, RPC_PIPE_WAIT_FOR_OPEN);
694 if (IS_ERR(cn->cn_pipe)) {
695 ret = PTR_ERR(cn->cn_pipe);
696 goto err;
697 }
698 spin_lock_init(&cn->cn_lock);
699 INIT_LIST_HEAD(&cn->cn_list);
700
701 dentry = nfsd4_cld_register_net(net, cn->cn_pipe);
702 if (IS_ERR(dentry)) {
703 ret = PTR_ERR(dentry);
704 goto err_destroy_data;
705 }
706
707 cn->cn_pipe->dentry = dentry;
708 nn->cld_net = cn;
709 return 0;
710
711err_destroy_data:
712 rpc_destroy_pipe_data(cn->cn_pipe);
713err:
714 kfree(cn);
715 printk(KERN_ERR "NFSD: unable to create nfsdcld upcall pipe (%d)\n",
716 ret);
717 return ret;
718}
719
720static void
721nfsd4_remove_cld_pipe(struct net *net)
722{
723 struct nfsd_net *nn = net_generic(net, nfsd_net_id);
724 struct cld_net *cn = nn->cld_net;
725
726 nfsd4_cld_unregister_net(net, cn->cn_pipe);
727 rpc_destroy_pipe_data(cn->cn_pipe);
728 kfree(nn->cld_net);
729 nn->cld_net = NULL;
730}
731
732static struct cld_upcall *
733alloc_cld_upcall(struct cld_net *cn)
734{
735 struct cld_upcall *new, *tmp;
736
737 new = kzalloc(sizeof(*new), GFP_KERNEL);
738 if (!new)
739 return new;
740
741 /* FIXME: hard cap on number in flight? */
742restart_search:
743 spin_lock(&cn->cn_lock);
744 list_for_each_entry(tmp, &cn->cn_list, cu_list) {
745 if (tmp->cu_msg.cm_xid == cn->cn_xid) {
746 cn->cn_xid++;
747 spin_unlock(&cn->cn_lock);
748 goto restart_search;
749 }
750 }
751 new->cu_task = current;
752 new->cu_msg.cm_vers = CLD_UPCALL_VERSION;
753 put_unaligned(cn->cn_xid++, &new->cu_msg.cm_xid);
754 new->cu_net = cn;
755 list_add(&new->cu_list, &cn->cn_list);
756 spin_unlock(&cn->cn_lock);
757
758 dprintk("%s: allocated xid %u\n", __func__, new->cu_msg.cm_xid);
759
760 return new;
761}
762
763static void
764free_cld_upcall(struct cld_upcall *victim)
765{
766 struct cld_net *cn = victim->cu_net;
767
768 spin_lock(&cn->cn_lock);
769 list_del(&victim->cu_list);
770 spin_unlock(&cn->cn_lock);
771 kfree(victim);
772}
773
774/* Ask daemon to create a new record */
775static void
776nfsd4_cld_create(struct nfs4_client *clp)
777{
778 int ret;
779 struct cld_upcall *cup;
780 /* FIXME: determine net from clp */
781 struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id);
782 struct cld_net *cn = nn->cld_net;
783
784 /* Don't upcall if it's already stored */
785 if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags))
786 return;
787
788 cup = alloc_cld_upcall(cn);
789 if (!cup) {
790 ret = -ENOMEM;
791 goto out_err;
792 }
793
794 cup->cu_msg.cm_cmd = Cld_Create;
795 cup->cu_msg.cm_u.cm_name.cn_len = clp->cl_name.len;
796 memcpy(cup->cu_msg.cm_u.cm_name.cn_id, clp->cl_name.data,
797 clp->cl_name.len);
798
799 ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_msg);
800 if (!ret) {
801 ret = cup->cu_msg.cm_status;
802 set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags);
803 }
804
805 free_cld_upcall(cup);
806out_err:
807 if (ret)
808 printk(KERN_ERR "NFSD: Unable to create client "
809 "record on stable storage: %d\n", ret);
810}
811
812/* Ask daemon to create a new record */
813static void
814nfsd4_cld_remove(struct nfs4_client *clp)
815{
816 int ret;
817 struct cld_upcall *cup;
818 /* FIXME: determine net from clp */
819 struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id);
820 struct cld_net *cn = nn->cld_net;
821
822 /* Don't upcall if it's already removed */
823 if (!test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags))
824 return;
825
826 cup = alloc_cld_upcall(cn);
827 if (!cup) {
828 ret = -ENOMEM;
829 goto out_err;
830 }
831
832 cup->cu_msg.cm_cmd = Cld_Remove;
833 cup->cu_msg.cm_u.cm_name.cn_len = clp->cl_name.len;
834 memcpy(cup->cu_msg.cm_u.cm_name.cn_id, clp->cl_name.data,
835 clp->cl_name.len);
836
837 ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_msg);
838 if (!ret) {
839 ret = cup->cu_msg.cm_status;
840 clear_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags);
841 }
842
843 free_cld_upcall(cup);
844out_err:
845 if (ret)
846 printk(KERN_ERR "NFSD: Unable to remove client "
847 "record from stable storage: %d\n", ret);
848}
849
850/* Check for presence of a record, and update its timestamp */
851static int
852nfsd4_cld_check(struct nfs4_client *clp)
853{
854 int ret;
855 struct cld_upcall *cup;
856 /* FIXME: determine net from clp */
857 struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id);
858 struct cld_net *cn = nn->cld_net;
859
860 /* Don't upcall if one was already stored during this grace pd */
861 if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags))
862 return 0;
863
864 cup = alloc_cld_upcall(cn);
865 if (!cup) {
866 printk(KERN_ERR "NFSD: Unable to check client record on "
867 "stable storage: %d\n", -ENOMEM);
868 return -ENOMEM;
869 }
870
871 cup->cu_msg.cm_cmd = Cld_Check;
872 cup->cu_msg.cm_u.cm_name.cn_len = clp->cl_name.len;
873 memcpy(cup->cu_msg.cm_u.cm_name.cn_id, clp->cl_name.data,
874 clp->cl_name.len);
875
876 ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_msg);
877 if (!ret) {
878 ret = cup->cu_msg.cm_status;
879 set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags);
880 }
881
882 free_cld_upcall(cup);
883 return ret;
884}
885
886static void
887nfsd4_cld_grace_done(struct net *net, time_t boot_time)
888{
889 int ret;
890 struct cld_upcall *cup;
891 struct nfsd_net *nn = net_generic(net, nfsd_net_id);
892 struct cld_net *cn = nn->cld_net;
893
894 cup = alloc_cld_upcall(cn);
895 if (!cup) {
896 ret = -ENOMEM;
897 goto out_err;
898 }
899
900 cup->cu_msg.cm_cmd = Cld_GraceDone;
901 cup->cu_msg.cm_u.cm_gracetime = (int64_t)boot_time;
902 ret = cld_pipe_upcall(cn->cn_pipe, &cup->cu_msg);
903 if (!ret)
904 ret = cup->cu_msg.cm_status;
905
906 free_cld_upcall(cup);
907out_err:
908 if (ret)
909 printk(KERN_ERR "NFSD: Unable to end grace period: %d\n", ret);
910}
911
912static struct nfsd4_client_tracking_ops nfsd4_cld_tracking_ops = {
913 .init = nfsd4_init_cld_pipe,
914 .exit = nfsd4_remove_cld_pipe,
915 .create = nfsd4_cld_create,
916 .remove = nfsd4_cld_remove,
917 .check = nfsd4_cld_check,
918 .grace_done = nfsd4_cld_grace_done,
919};
920
489int 921int
490nfsd4_client_tracking_init(struct net *net) 922nfsd4_client_tracking_init(struct net *net)
491{ 923{
492 int status; 924 int status;
925 struct path path;
493 926
494 client_tracking_ops = &nfsd4_legacy_tracking_ops; 927 if (!client_tracking_ops) {
928 client_tracking_ops = &nfsd4_cld_tracking_ops;
929 status = kern_path(nfs4_recoverydir(), LOOKUP_FOLLOW, &path);
930 if (!status) {
931 if (S_ISDIR(path.dentry->d_inode->i_mode))
932 client_tracking_ops =
933 &nfsd4_legacy_tracking_ops;
934 path_put(&path);
935 }
936 }
495 937
496 status = client_tracking_ops->init(net); 938 status = client_tracking_ops->init(net);
497 if (status) { 939 if (status) {