Merge tag 'pinctrl-for-3.7-late' of git://git.kernel.org/pub/scm/linux/kernel/git...
[profile/ivi/kernel-adaptation-intel-automotive.git] / fs / lockd / mon.c
index 7ef14b3..e4fb3ba 100644 (file)
@@ -7,7 +7,6 @@
  */
 
 #include <linux/types.h>
-#include <linux/utsname.h>
 #include <linux/kernel.h>
 #include <linux/ktime.h>
 #include <linux/slab.h>
@@ -19,6 +18,8 @@
 
 #include <asm/unaligned.h>
 
+#include "netns.h"
+
 #define NLMDBG_FACILITY                NLMDBG_MONITOR
 #define NSM_PROGRAM            100024
 #define NSM_VERSION            1
@@ -40,6 +41,7 @@ struct nsm_args {
        u32                     proc;
 
        char                    *mon_name;
+       char                    *nodename;
 };
 
 struct nsm_res {
@@ -70,7 +72,7 @@ static struct rpc_clnt *nsm_create(struct net *net)
        };
        struct rpc_create_args args = {
                .net                    = net,
-               .protocol               = XPRT_TRANSPORT_UDP,
+               .protocol               = XPRT_TRANSPORT_TCP,
                .address                = (struct sockaddr *)&sin,
                .addrsize               = sizeof(sin),
                .servername             = "rpc.statd",
@@ -83,10 +85,54 @@ static struct rpc_clnt *nsm_create(struct net *net)
        return rpc_create(&args);
 }
 
-static int nsm_mon_unmon(struct nsm_handle *nsm, u32 proc, struct nsm_res *res,
-                        struct net *net)
+static struct rpc_clnt *nsm_client_get(struct net *net)
 {
+       static DEFINE_MUTEX(nsm_create_mutex);
        struct rpc_clnt *clnt;
+       struct lockd_net *ln = net_generic(net, lockd_net_id);
+
+       spin_lock(&ln->nsm_clnt_lock);
+       if (ln->nsm_users) {
+               ln->nsm_users++;
+               clnt = ln->nsm_clnt;
+               spin_unlock(&ln->nsm_clnt_lock);
+               goto out;
+       }
+       spin_unlock(&ln->nsm_clnt_lock);
+
+       mutex_lock(&nsm_create_mutex);
+       clnt = nsm_create(net);
+       if (!IS_ERR(clnt)) {
+               ln->nsm_clnt = clnt;
+               smp_wmb();
+               ln->nsm_users = 1;
+       }
+       mutex_unlock(&nsm_create_mutex);
+out:
+       return clnt;
+}
+
+static void nsm_client_put(struct net *net)
+{
+       struct lockd_net *ln = net_generic(net, lockd_net_id);
+       struct rpc_clnt *clnt = ln->nsm_clnt;
+       int shutdown = 0;
+
+       spin_lock(&ln->nsm_clnt_lock);
+       if (ln->nsm_users) {
+               if (--ln->nsm_users)
+                       ln->nsm_clnt = NULL;
+               shutdown = !ln->nsm_users;
+       }
+       spin_unlock(&ln->nsm_clnt_lock);
+
+       if (shutdown)
+               rpc_shutdown_client(clnt);
+}
+
+static int nsm_mon_unmon(struct nsm_handle *nsm, u32 proc, struct nsm_res *res,
+                        struct rpc_clnt *clnt)
+{
        int             status;
        struct nsm_args args = {
                .priv           = &nsm->sm_priv,
@@ -94,31 +140,24 @@ static int nsm_mon_unmon(struct nsm_handle *nsm, u32 proc, struct nsm_res *res,
                .vers           = 3,
                .proc           = NLMPROC_NSM_NOTIFY,
                .mon_name       = nsm->sm_mon_name,
+               .nodename       = clnt->cl_nodename,
        };
        struct rpc_message msg = {
                .rpc_argp       = &args,
                .rpc_resp       = res,
        };
 
-       clnt = nsm_create(net);
-       if (IS_ERR(clnt)) {
-               status = PTR_ERR(clnt);
-               dprintk("lockd: failed to create NSM upcall transport, "
-                               "status=%d\n", status);
-               goto out;
-       }
+       BUG_ON(clnt == NULL);
 
        memset(res, 0, sizeof(*res));
 
        msg.rpc_proc = &clnt->cl_procinfo[proc];
-       status = rpc_call_sync(clnt, &msg, 0);
+       status = rpc_call_sync(clnt, &msg, RPC_TASK_SOFTCONN);
        if (status < 0)
                dprintk("lockd: NSM upcall RPC failed, status=%d\n",
                                status);
        else
                status = 0;
-       rpc_shutdown_client(clnt);
- out:
        return status;
 }
 
@@ -138,6 +177,7 @@ int nsm_monitor(const struct nlm_host *host)
        struct nsm_handle *nsm = host->h_nsmhandle;
        struct nsm_res  res;
        int             status;
+       struct rpc_clnt *clnt;
 
        dprintk("lockd: nsm_monitor(%s)\n", nsm->sm_name);
 
@@ -150,7 +190,15 @@ int nsm_monitor(const struct nlm_host *host)
         */
        nsm->sm_mon_name = nsm_use_hostnames ? nsm->sm_name : nsm->sm_addrbuf;
 
-       status = nsm_mon_unmon(nsm, NSMPROC_MON, &res, host->net);
+       clnt = nsm_client_get(host->net);
+       if (IS_ERR(clnt)) {
+               status = PTR_ERR(clnt);
+               dprintk("lockd: failed to create NSM upcall transport, "
+                               "status=%d, net=%p\n", status, host->net);
+               return status;
+       }
+
+       status = nsm_mon_unmon(nsm, NSMPROC_MON, &res, clnt);
        if (unlikely(res.status != 0))
                status = -EIO;
        if (unlikely(status < 0)) {
@@ -182,9 +230,11 @@ void nsm_unmonitor(const struct nlm_host *host)
 
        if (atomic_read(&nsm->sm_count) == 1
         && nsm->sm_monitored && !nsm->sm_sticky) {
+               struct lockd_net *ln = net_generic(host->net, lockd_net_id);
+
                dprintk("lockd: nsm_unmonitor(%s)\n", nsm->sm_name);
 
-               status = nsm_mon_unmon(nsm, NSMPROC_UNMON, &res, host->net);
+               status = nsm_mon_unmon(nsm, NSMPROC_UNMON, &res, ln->nsm_clnt);
                if (res.status != 0)
                        status = -EIO;
                if (status < 0)
@@ -192,6 +242,8 @@ void nsm_unmonitor(const struct nlm_host *host)
                                        nsm->sm_name);
                else
                        nsm->sm_monitored = 0;
+
+               nsm_client_put(host->net);
        }
 }
 
@@ -430,7 +482,7 @@ static void encode_my_id(struct xdr_stream *xdr, const struct nsm_args *argp)
 {
        __be32 *p;
 
-       encode_nsm_string(xdr, utsname()->nodename);
+       encode_nsm_string(xdr, argp->nodename);
        p = xdr_reserve_space(xdr, 4 + 4 + 4);
        *p++ = cpu_to_be32(argp->prog);
        *p++ = cpu_to_be32(argp->vers);