SUNRPC: Add a framework to clean up management of rpc_pipefs directories
authorTrond Myklebust <Trond.Myklebust@netapp.com>
Mon, 26 Aug 2013 19:38:11 +0000 (15:38 -0400)
committerTrond Myklebust <Trond.Myklebust@netapp.com>
Fri, 30 Aug 2013 13:19:38 +0000 (09:19 -0400)
The current system requires everyone to set up notifiers, manage directory
locking, etc.
What we really want to do is have the rpc_client create its directory,
and then create all the entries.

This patch will allow the RPCSEC_GSS and NFS code to register all the
objects that they want to have appear in the directory, and then have
the sunrpc code call them back to actually create/destroy their pipefs
dentries when the rpc_client creates/destroys the parent.

Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
include/linux/sunrpc/clnt.h
include/linux/sunrpc/rpc_pipe_fs.h
net/sunrpc/clnt.c
net/sunrpc/rpc_pipe.c

index b93b4aa..0dccd01 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/sunrpc/stats.h>
 #include <linux/sunrpc/xdr.h>
 #include <linux/sunrpc/timer.h>
+#include <linux/sunrpc/rpc_pipe_fs.h>
 #include <asm/signal.h>
 #include <linux/path.h>
 #include <net/ipv6.h>
@@ -55,6 +56,7 @@ struct rpc_clnt {
 
        int                     cl_nodelen;     /* nodename length */
        char                    cl_nodename[UNX_MAXNODENAME];
+       struct rpc_pipe_dir_head cl_pipedir_objects;
        struct dentry *         cl_dentry;
        struct rpc_clnt *       cl_parent;      /* Points to parent of clones */
        struct rpc_rtt          cl_rtt_default;
index aa5b582..188e7fc 100644 (file)
@@ -5,6 +5,26 @@
 
 #include <linux/workqueue.h>
 
+struct rpc_pipe_dir_head {
+       struct list_head pdh_entries;
+       struct dentry *pdh_dentry;
+};
+
+struct rpc_pipe_dir_object_ops;
+struct rpc_pipe_dir_object {
+       struct list_head pdo_head;
+       const struct rpc_pipe_dir_object_ops *pdo_ops;
+
+       void *pdo_data;
+};
+
+struct rpc_pipe_dir_object_ops {
+       int (*create)(struct dentry *dir,
+                       struct rpc_pipe_dir_object *pdo);
+       void (*destroy)(struct dentry *dir,
+                       struct rpc_pipe_dir_object *pdo);
+};
+
 struct rpc_pipe_msg {
        struct list_head list;
        void *data;
@@ -74,7 +94,18 @@ extern int rpc_queue_upcall(struct rpc_pipe *, struct rpc_pipe_msg *);
 
 struct rpc_clnt;
 extern struct dentry *rpc_create_client_dir(struct dentry *, const char *, struct rpc_clnt *);
-extern int rpc_remove_client_dir(struct dentry *);
+extern int rpc_remove_client_dir(struct dentry *, struct rpc_clnt *);
+
+extern void rpc_init_pipe_dir_head(struct rpc_pipe_dir_head *pdh);
+extern void rpc_init_pipe_dir_object(struct rpc_pipe_dir_object *pdo,
+               const struct rpc_pipe_dir_object_ops *pdo_ops,
+               void *pdo_data);
+extern int rpc_add_pipe_dir_object(struct net *net,
+               struct rpc_pipe_dir_head *pdh,
+               struct rpc_pipe_dir_object *pdo);
+extern void rpc_remove_pipe_dir_object(struct net *net,
+               struct rpc_pipe_dir_head *pdh,
+               struct rpc_pipe_dir_object *pdo);
 
 struct cache_detail;
 extern struct dentry *rpc_create_cache_dir(struct dentry *,
index 7407f1d..01d2296 100644 (file)
@@ -105,7 +105,7 @@ static void __rpc_clnt_remove_pipedir(struct rpc_clnt *clnt)
        if (clnt->cl_dentry) {
                if (clnt->cl_auth && clnt->cl_auth->au_ops->pipes_destroy)
                        clnt->cl_auth->au_ops->pipes_destroy(clnt->cl_auth);
-               rpc_remove_client_dir(clnt->cl_dentry);
+               rpc_remove_client_dir(clnt->cl_dentry, clnt);
        }
        clnt->cl_dentry = NULL;
 }
@@ -355,6 +355,7 @@ static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args, stru
        clnt->cl_vers     = version->number;
        clnt->cl_stats    = program->stats;
        clnt->cl_metrics  = rpc_alloc_iostats(clnt);
+       rpc_init_pipe_dir_head(&clnt->cl_pipedir_objects);
        err = -ENOMEM;
        if (clnt->cl_metrics == NULL)
                goto out_no_stats;
index b36bfb9..cfeba77 100644 (file)
@@ -884,6 +884,124 @@ rpc_unlink(struct dentry *dentry)
 }
 EXPORT_SYMBOL_GPL(rpc_unlink);
 
+/**
+ * rpc_init_pipe_dir_head - initialise a struct rpc_pipe_dir_head
+ * @pdh: pointer to struct rpc_pipe_dir_head
+ */
+void rpc_init_pipe_dir_head(struct rpc_pipe_dir_head *pdh)
+{
+       INIT_LIST_HEAD(&pdh->pdh_entries);
+       pdh->pdh_dentry = NULL;
+}
+EXPORT_SYMBOL_GPL(rpc_init_pipe_dir_head);
+
+/**
+ * rpc_init_pipe_dir_object - initialise a struct rpc_pipe_dir_object
+ * @pdo: pointer to struct rpc_pipe_dir_object
+ * @pdo_ops: pointer to const struct rpc_pipe_dir_object_ops
+ * @pdo_data: pointer to caller-defined data
+ */
+void rpc_init_pipe_dir_object(struct rpc_pipe_dir_object *pdo,
+               const struct rpc_pipe_dir_object_ops *pdo_ops,
+               void *pdo_data)
+{
+       INIT_LIST_HEAD(&pdo->pdo_head);
+       pdo->pdo_ops = pdo_ops;
+       pdo->pdo_data = pdo_data;
+}
+EXPORT_SYMBOL_GPL(rpc_init_pipe_dir_object);
+
+static int
+rpc_add_pipe_dir_object_locked(struct net *net,
+               struct rpc_pipe_dir_head *pdh,
+               struct rpc_pipe_dir_object *pdo)
+{
+       int ret = 0;
+
+       if (pdh->pdh_dentry)
+               ret = pdo->pdo_ops->create(pdh->pdh_dentry, pdo);
+       if (ret == 0)
+               list_add_tail(&pdo->pdo_head, &pdh->pdh_entries);
+       return ret;
+}
+
+static void
+rpc_remove_pipe_dir_object_locked(struct net *net,
+               struct rpc_pipe_dir_head *pdh,
+               struct rpc_pipe_dir_object *pdo)
+{
+       if (pdh->pdh_dentry)
+               pdo->pdo_ops->destroy(pdh->pdh_dentry, pdo);
+       list_del_init(&pdo->pdo_head);
+}
+
+/**
+ * rpc_add_pipe_dir_object - associate a rpc_pipe_dir_object to a directory
+ * @net: pointer to struct net
+ * @pdh: pointer to struct rpc_pipe_dir_head
+ * @pdo: pointer to struct rpc_pipe_dir_object
+ *
+ */
+int
+rpc_add_pipe_dir_object(struct net *net,
+               struct rpc_pipe_dir_head *pdh,
+               struct rpc_pipe_dir_object *pdo)
+{
+       int ret = 0;
+
+       if (list_empty(&pdo->pdo_head)) {
+               struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
+
+               mutex_lock(&sn->pipefs_sb_lock);
+               ret = rpc_add_pipe_dir_object_locked(net, pdh, pdo);
+               mutex_unlock(&sn->pipefs_sb_lock);
+       }
+       return ret;
+}
+EXPORT_SYMBOL_GPL(rpc_add_pipe_dir_object);
+
+/**
+ * rpc_remove_pipe_dir_object - remove a rpc_pipe_dir_object from a directory
+ * @net: pointer to struct net
+ * @pdh: pointer to struct rpc_pipe_dir_head
+ * @pdo: pointer to struct rpc_pipe_dir_object
+ *
+ */
+void
+rpc_remove_pipe_dir_object(struct net *net,
+               struct rpc_pipe_dir_head *pdh,
+               struct rpc_pipe_dir_object *pdo)
+{
+       if (!list_empty(&pdo->pdo_head)) {
+               struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);
+
+               mutex_lock(&sn->pipefs_sb_lock);
+               rpc_remove_pipe_dir_object_locked(net, pdh, pdo);
+               mutex_unlock(&sn->pipefs_sb_lock);
+       }
+}
+EXPORT_SYMBOL_GPL(rpc_remove_pipe_dir_object);
+
+static void
+rpc_create_pipe_dir_objects(struct rpc_pipe_dir_head *pdh)
+{
+       struct rpc_pipe_dir_object *pdo;
+       struct dentry *dir = pdh->pdh_dentry;
+
+       list_for_each_entry(pdo, &pdh->pdh_entries, pdo_head)
+               pdo->pdo_ops->create(dir, pdo);
+}
+
+static void
+rpc_destroy_pipe_dir_objects(struct rpc_pipe_dir_head *pdh)
+{
+       struct rpc_pipe_dir_object *pdo;
+       struct dentry *dir = pdh->pdh_dentry;
+
+       list_for_each_entry(pdo, &pdh->pdh_entries, pdo_head)
+               pdo->pdo_ops->destroy(dir, pdo);
+}
+
 enum {
        RPCAUTH_info,
        RPCAUTH_EOF
@@ -924,16 +1042,28 @@ struct dentry *rpc_create_client_dir(struct dentry *dentry,
                                   const char *name,
                                   struct rpc_clnt *rpc_client)
 {
-       return rpc_mkdir_populate(dentry, name, S_IRUGO | S_IXUGO, NULL,
+       struct dentry *ret;
+
+       ret = rpc_mkdir_populate(dentry, name, S_IRUGO | S_IXUGO, NULL,
                        rpc_clntdir_populate, rpc_client);
+       if (!IS_ERR(ret)) {
+               rpc_client->cl_pipedir_objects.pdh_dentry = ret;
+               rpc_create_pipe_dir_objects(&rpc_client->cl_pipedir_objects);
+       }
+       return ret;
 }
 
 /**
  * rpc_remove_client_dir - Remove a directory created with rpc_create_client_dir()
  * @dentry: dentry for the pipe
+ * @rpc_client: rpc_client for the pipe
  */
-int rpc_remove_client_dir(struct dentry *dentry)
+int rpc_remove_client_dir(struct dentry *dentry, struct rpc_clnt *rpc_client)
 {
+       if (rpc_client->cl_pipedir_objects.pdh_dentry) {
+               rpc_destroy_pipe_dir_objects(&rpc_client->cl_pipedir_objects);
+               rpc_client->cl_pipedir_objects.pdh_dentry = NULL;
+       }
        return rpc_rmdir_depopulate(dentry, rpc_clntdir_depopulate);
 }