lockd: Rename nlm_hosts
[firefly-linux-kernel-4.4.55.git] / fs / lockd / host.c
index 25e21e4023b22a3f51ceb9d1c1869c2b7518f6d0..87fbde1d1a1fde7b34f5f1ea57e7e1d527f21d5d 100644 (file)
 #define NLM_HOST_EXPIRE                (300 * HZ)
 #define NLM_HOST_COLLECT       (120 * HZ)
 
-static struct hlist_head       nlm_hosts[NLM_HOST_NRHASH];
+static struct hlist_head       nlm_server_hosts[NLM_HOST_NRHASH];
+static struct hlist_head       nlm_client_hosts[NLM_HOST_NRHASH];
+
+#define for_each_host(host, pos, chain, table) \
+       for ((chain) = (table); \
+            (chain) < (table) + NLM_HOST_NRHASH; ++(chain)) \
+               hlist_for_each_entry((host), (pos), (chain), h_hash)
+
+#define for_each_host_safe(host, pos, next, chain, table) \
+       for ((chain) = (table); \
+            (chain) < (table) + NLM_HOST_NRHASH; ++(chain)) \
+               hlist_for_each_entry_safe((host), (pos), (next), \
+                                               (chain), h_hash)
+
 static unsigned long           next_gc;
 static int                     nrhosts;
 static DEFINE_MUTEX(nlm_host_mutex);
@@ -87,6 +100,68 @@ static unsigned int nlm_hash_address(const struct sockaddr *sap)
        return hash & (NLM_HOST_NRHASH - 1);
 }
 
+/*
+ * Allocate and initialize an nlm_host.  Common to both client and server.
+ */
+static struct nlm_host *nlm_alloc_host(struct nlm_lookup_host_info *ni,
+                                      struct nsm_handle *nsm)
+{
+       struct nlm_host *host = NULL;
+       unsigned long now = jiffies;
+
+       if (nsm != NULL)
+               atomic_inc(&nsm->sm_count);
+       else {
+               host = NULL;
+               nsm = nsm_get_handle(ni->sap, ni->salen,
+                                       ni->hostname, ni->hostname_len);
+               if (unlikely(nsm == NULL)) {
+                       dprintk("lockd: %s failed; no nsm handle\n",
+                               __func__);
+                       goto out;
+               }
+       }
+
+       host = kmalloc(sizeof(*host), GFP_KERNEL);
+       if (unlikely(host == NULL)) {
+               dprintk("lockd: %s failed; no memory\n", __func__);
+               nsm_release(nsm);
+               goto out;
+       }
+
+       memcpy(nlm_addr(host), ni->sap, ni->salen);
+       host->h_addrlen    = ni->salen;
+       rpc_set_port(nlm_addr(host), 0);
+       host->h_srcaddrlen = 0;
+
+       host->h_rpcclnt    = NULL;
+       host->h_name       = nsm->sm_name;
+       host->h_version    = ni->version;
+       host->h_proto      = ni->protocol;
+       host->h_reclaiming = 0;
+       host->h_server     = ni->server;
+       host->h_noresvport = ni->noresvport;
+       host->h_inuse      = 0;
+       init_waitqueue_head(&host->h_gracewait);
+       init_rwsem(&host->h_rwsem);
+       host->h_state      = 0;
+       host->h_nsmstate   = 0;
+       host->h_pidcount   = 0;
+       atomic_set(&host->h_count, 1);
+       mutex_init(&host->h_mutex);
+       host->h_nextrebind = now + NLM_HOST_REBIND;
+       host->h_expires    = now + NLM_HOST_EXPIRE;
+       INIT_LIST_HEAD(&host->h_lockowners);
+       spin_lock_init(&host->h_lock);
+       INIT_LIST_HEAD(&host->h_granted);
+       INIT_LIST_HEAD(&host->h_reclaim);
+       host->h_nsmhandle  = nsm;
+       host->h_addrbuf    = nsm->sm_addrbuf;
+
+out:
+       return host;
+}
+
 /*
  * Common host lookup routine for server & client
  */
@@ -109,7 +184,7 @@ static struct nlm_host *nlm_lookup_host(struct nlm_lookup_host_info *ni)
         * different NLM rpc_clients into one single nlm_host object.
         * This would allow us to have one nlm_host per address.
         */
-       chain = &nlm_hosts[nlm_hash_address(ni->sap)];
+       chain = &nlm_server_hosts[nlm_hash_address(ni->sap)];
        hlist_for_each_entry(host, pos, chain, h_hash) {
                if (!rpc_cmp_addr(nlm_addr(host), ni->sap))
                        continue;
@@ -124,7 +199,7 @@ static struct nlm_host *nlm_lookup_host(struct nlm_lookup_host_info *ni)
                        continue;
                if (host->h_server != ni->server)
                        continue;
-               if (ni->server &&
+               if (ni->server && ni->src_len != 0 &&
                    !rpc_cmp_addr(nlm_srcaddr(host), ni->src_sap))
                        continue;
 
@@ -138,54 +213,13 @@ static struct nlm_host *nlm_lookup_host(struct nlm_lookup_host_info *ni)
                goto out;
        }
 
-       /*
-        * The host wasn't in our hash table.  If we don't
-        * have an NSM handle for it yet, create one.
-        */
-       if (nsm)
-               atomic_inc(&nsm->sm_count);
-       else {
-               host = NULL;
-               nsm = nsm_get_handle(ni->sap, ni->salen,
-                                       ni->hostname, ni->hostname_len);
-               if (!nsm) {
-                       dprintk("lockd: nlm_lookup_host failed; "
-                               "no nsm handle\n");
-                       goto out;
-               }
-       }
-
-       host = kzalloc(sizeof(*host), GFP_KERNEL);
-       if (!host) {
-               nsm_release(nsm);
-               dprintk("lockd: nlm_lookup_host failed; no memory\n");
+       host = nlm_alloc_host(ni, nsm);
+       if (unlikely(host == NULL))
                goto out;
-       }
-       host->h_name       = nsm->sm_name;
-       host->h_addrbuf    = nsm->sm_addrbuf;
-       memcpy(nlm_addr(host), ni->sap, ni->salen);
-       host->h_addrlen = ni->salen;
-       rpc_set_port(nlm_addr(host), 0);
+
        memcpy(nlm_srcaddr(host), ni->src_sap, ni->src_len);
-       host->h_version    = ni->version;
-       host->h_proto      = ni->protocol;
-       host->h_rpcclnt    = NULL;
-       mutex_init(&host->h_mutex);
-       host->h_nextrebind = jiffies + NLM_HOST_REBIND;
-       host->h_expires    = jiffies + NLM_HOST_EXPIRE;
-       atomic_set(&host->h_count, 1);
-       init_waitqueue_head(&host->h_gracewait);
-       init_rwsem(&host->h_rwsem);
-       host->h_state      = 0;                 /* pseudo NSM state */
-       host->h_nsmstate   = 0;                 /* real NSM state */
-       host->h_nsmhandle  = nsm;
-       host->h_server     = ni->server;
-       host->h_noresvport = ni->noresvport;
+       host->h_srcaddrlen = ni->src_len;
        hlist_add_head(&host->h_hash, chain);
-       INIT_LIST_HEAD(&host->h_lockowners);
-       spin_lock_init(&host->h_lock);
-       INIT_LIST_HEAD(&host->h_granted);
-       INIT_LIST_HEAD(&host->h_reclaim);
 
        nrhosts++;
 
@@ -198,16 +232,21 @@ out:
 }
 
 /*
- * Destroy a host
+ * Destroy an nlm_host and free associated resources
+ *
+ * Caller must hold nlm_host_mutex.
  */
-static void
-nlm_destroy_host(struct nlm_host *host)
+static void nlm_destroy_host_locked(struct nlm_host *host)
 {
        struct rpc_clnt *clnt;
 
+       dprintk("lockd: destroy host %s\n", host->h_name);
+
        BUG_ON(!list_empty(&host->h_lockowners));
        BUG_ON(atomic_read(&host->h_count));
 
+       hlist_del_init(&host->h_hash);
+
        nsm_unmonitor(host);
        nsm_release(host->h_nsmhandle);
 
@@ -215,6 +254,8 @@ nlm_destroy_host(struct nlm_host *host)
        if (clnt != NULL)
                rpc_shutdown_client(clnt);
        kfree(host);
+
+       nrhosts--;
 }
 
 /**
@@ -238,9 +279,6 @@ struct nlm_host *nlmclnt_lookup_host(const struct sockaddr *sap,
                                     const char *hostname,
                                     int noresvport)
 {
-       const struct sockaddr source = {
-               .sa_family      = AF_UNSPEC,
-       };
        struct nlm_lookup_host_info ni = {
                .server         = 0,
                .sap            = sap,
@@ -249,16 +287,78 @@ struct nlm_host *nlmclnt_lookup_host(const struct sockaddr *sap,
                .version        = version,
                .hostname       = hostname,
                .hostname_len   = strlen(hostname),
-               .src_sap        = &source,
-               .src_len        = sizeof(source),
                .noresvport     = noresvport,
        };
+       struct hlist_head *chain;
+       struct hlist_node *pos;
+       struct nlm_host *host;
+       struct nsm_handle *nsm = NULL;
 
        dprintk("lockd: %s(host='%s', vers=%u, proto=%s)\n", __func__,
                        (hostname ? hostname : "<none>"), version,
                        (protocol == IPPROTO_UDP ? "udp" : "tcp"));
 
-       return nlm_lookup_host(&ni);
+       mutex_lock(&nlm_host_mutex);
+
+       chain = &nlm_client_hosts[nlm_hash_address(sap)];
+       hlist_for_each_entry(host, pos, chain, h_hash) {
+               if (!rpc_cmp_addr(nlm_addr(host), sap))
+                       continue;
+
+               /* Same address. Share an NSM handle if we already have one */
+               if (nsm == NULL)
+                       nsm = host->h_nsmhandle;
+
+               if (host->h_proto != protocol)
+                       continue;
+               if (host->h_version != version)
+                       continue;
+
+               nlm_get_host(host);
+               dprintk("lockd: %s found host %s (%s)\n", __func__,
+                       host->h_name, host->h_addrbuf);
+               goto out;
+       }
+
+       host = nlm_alloc_host(&ni, nsm);
+       if (unlikely(host == NULL))
+               goto out;
+
+       hlist_add_head(&host->h_hash, chain);
+       nrhosts++;
+
+       dprintk("lockd: %s created host %s (%s)\n", __func__,
+               host->h_name, host->h_addrbuf);
+
+out:
+       mutex_unlock(&nlm_host_mutex);
+       return host;
+}
+
+/**
+ * nlmclnt_release_host - release client nlm_host
+ * @host: nlm_host to release
+ *
+ */
+void nlmclnt_release_host(struct nlm_host *host)
+{
+       if (host == NULL)
+               return;
+
+       dprintk("lockd: release client host %s\n", host->h_name);
+
+       BUG_ON(atomic_read(&host->h_count) < 0);
+       BUG_ON(host->h_server);
+
+       if (atomic_dec_and_test(&host->h_count)) {
+               BUG_ON(!list_empty(&host->h_lockowners));
+               BUG_ON(!list_empty(&host->h_granted));
+               BUG_ON(!list_empty(&host->h_reclaim));
+
+               mutex_lock(&nlm_host_mutex);
+               nlm_destroy_host_locked(host);
+               mutex_unlock(&nlm_host_mutex);
+       }
 }
 
 /**
@@ -283,6 +383,10 @@ struct nlm_host *nlmsvc_lookup_host(const struct svc_rqst *rqstp,
                                    const char *hostname,
                                    const size_t hostname_len)
 {
+       struct hlist_head *chain;
+       struct hlist_node *pos;
+       struct nlm_host *host = NULL;
+       struct nsm_handle *nsm = NULL;
        struct sockaddr_in sin = {
                .sin_family     = AF_INET,
        };
@@ -304,6 +408,8 @@ struct nlm_host *nlmsvc_lookup_host(const struct svc_rqst *rqstp,
                        (int)hostname_len, hostname, rqstp->rq_vers,
                        (rqstp->rq_prot == IPPROTO_UDP ? "udp" : "tcp"));
 
+       mutex_lock(&nlm_host_mutex);
+
        switch (ni.sap->sa_family) {
        case AF_INET:
                sin.sin_addr.s_addr = rqstp->rq_daddr.addr.s_addr;
@@ -314,10 +420,73 @@ struct nlm_host *nlmsvc_lookup_host(const struct svc_rqst *rqstp,
                ni.src_sap = (struct sockaddr *)&sin6;
                break;
        default:
-               return NULL;
+               dprintk("lockd: %s failed; unrecognized address family\n",
+                       __func__);
+               goto out;
+       }
+
+       if (time_after_eq(jiffies, next_gc))
+               nlm_gc_hosts();
+
+       chain = &nlm_server_hosts[nlm_hash_address(ni.sap)];
+       hlist_for_each_entry(host, pos, chain, h_hash) {
+               if (!rpc_cmp_addr(nlm_addr(host), ni.sap))
+                       continue;
+
+               /* Same address. Share an NSM handle if we already have one */
+               if (nsm == NULL)
+                       nsm = host->h_nsmhandle;
+
+               if (host->h_proto != ni.protocol)
+                       continue;
+               if (host->h_version != ni.version)
+                       continue;
+               if (!rpc_cmp_addr(nlm_srcaddr(host), ni.src_sap))
+                       continue;
+
+               /* Move to head of hash chain. */
+               hlist_del(&host->h_hash);
+               hlist_add_head(&host->h_hash, chain);
+
+               nlm_get_host(host);
+               dprintk("lockd: %s found host %s (%s)\n",
+                       __func__, host->h_name, host->h_addrbuf);
+               goto out;
        }
 
-       return nlm_lookup_host(&ni);
+       host = nlm_alloc_host(&ni, nsm);
+       if (unlikely(host == NULL))
+               goto out;
+
+       memcpy(nlm_srcaddr(host), ni.src_sap, ni.src_len);
+       host->h_srcaddrlen = ni.src_len;
+       hlist_add_head(&host->h_hash, chain);
+       nrhosts++;
+
+       dprintk("lockd: %s created host %s (%s)\n",
+               __func__, host->h_name, host->h_addrbuf);
+
+out:
+       mutex_unlock(&nlm_host_mutex);
+       return host;
+}
+
+/**
+ * nlmsvc_release_host - release server nlm_host
+ * @host: nlm_host to release
+ *
+ * Host is destroyed later in nlm_gc_host().
+ */
+void nlmsvc_release_host(struct nlm_host *host)
+{
+       if (host == NULL)
+               return;
+
+       dprintk("lockd: release server host %s\n", host->h_name);
+
+       BUG_ON(atomic_read(&host->h_count) < 0);
+       BUG_ON(!host->h_server);
+       atomic_dec(&host->h_count);
 }
 
 /*
@@ -357,7 +526,6 @@ nlm_bind_host(struct nlm_host *host)
                        .protocol       = host->h_proto,
                        .address        = nlm_addr(host),
                        .addrsize       = host->h_addrlen,
-                       .saddress       = nlm_srcaddr(host),
                        .timeout        = &timeparms,
                        .servername     = host->h_name,
                        .program        = &nlm_program,
@@ -376,6 +544,8 @@ nlm_bind_host(struct nlm_host *host)
                        args.flags |= RPC_CLNT_CREATE_HARDRTRY;
                if (host->h_noresvport)
                        args.flags |= RPC_CLNT_CREATE_NONPRIVPORT;
+               if (host->h_srcaddrlen)
+                       args.saddress = nlm_srcaddr(host);
 
                clnt = rpc_create(&args);
                if (!IS_ERR(clnt))
@@ -416,20 +586,29 @@ struct nlm_host * nlm_get_host(struct nlm_host *host)
        return host;
 }
 
-/*
- * Release NLM host after use
- */
-void nlm_release_host(struct nlm_host *host)
+static struct nlm_host *next_host_state(struct hlist_head *cache,
+                                       struct nsm_handle *nsm,
+                                       const struct nlm_reboot *info)
 {
-       if (host != NULL) {
-               dprintk("lockd: release host %s\n", host->h_name);
-               BUG_ON(atomic_read(&host->h_count) < 0);
-               if (atomic_dec_and_test(&host->h_count)) {
-                       BUG_ON(!list_empty(&host->h_lockowners));
-                       BUG_ON(!list_empty(&host->h_granted));
-                       BUG_ON(!list_empty(&host->h_reclaim));
+       struct nlm_host *host = NULL;
+       struct hlist_head *chain;
+       struct hlist_node *pos;
+
+       mutex_lock(&nlm_host_mutex);
+       for_each_host(host, pos, chain, cache) {
+               if (host->h_nsmhandle == nsm
+                   && host->h_nsmstate != info->state) {
+                       host->h_nsmstate = info->state;
+                       host->h_state++;
+
+                       nlm_get_host(host);
+                       mutex_unlock(&nlm_host_mutex);
+                       goto out;
                }
        }
+out:
+       mutex_unlock(&nlm_host_mutex);
+       return host;
 }
 
 /**
@@ -441,8 +620,6 @@ void nlm_release_host(struct nlm_host *host)
  */
 void nlm_host_rebooted(const struct nlm_reboot *info)
 {
-       struct hlist_head *chain;
-       struct hlist_node *pos;
        struct nsm_handle *nsm;
        struct nlm_host *host;
 
@@ -455,32 +632,15 @@ void nlm_host_rebooted(const struct nlm_reboot *info)
         * lock for this.
         * To avoid processing a host several times, we match the nsmstate.
         */
-again: mutex_lock(&nlm_host_mutex);
-       for (chain = nlm_hosts; chain < nlm_hosts + NLM_HOST_NRHASH; ++chain) {
-               hlist_for_each_entry(host, pos, chain, h_hash) {
-                       if (host->h_nsmhandle == nsm
-                        && host->h_nsmstate != info->state) {
-                               host->h_nsmstate = info->state;
-                               host->h_state++;
-
-                               nlm_get_host(host);
-                               mutex_unlock(&nlm_host_mutex);
-
-                               if (host->h_server) {
-                                       /* We're server for this guy, just ditch
-                                        * all the locks he held. */
-                                       nlmsvc_free_host_resources(host);
-                               } else {
-                                       /* He's the server, initiate lock recovery. */
-                                       nlmclnt_recovery(host);
-                               }
-
-                               nlm_release_host(host);
-                               goto again;
-                       }
-               }
+       while ((host = next_host_state(nlm_server_hosts, nsm, info)) != NULL) {
+               nlmsvc_free_host_resources(host);
+               nlmsvc_release_host(host);
        }
-       mutex_unlock(&nlm_host_mutex);
+       while ((host = next_host_state(nlm_client_hosts, nsm, info)) != NULL) {
+               nlmclnt_recovery(host);
+               nlmclnt_release_host(host);
+       }
+
        nsm_release(nsm);
 }
 
@@ -500,13 +660,11 @@ nlm_shutdown_hosts(void)
 
        /* First, make all hosts eligible for gc */
        dprintk("lockd: nuking all hosts...\n");
-       for (chain = nlm_hosts; chain < nlm_hosts + NLM_HOST_NRHASH; ++chain) {
-               hlist_for_each_entry(host, pos, chain, h_hash) {
-                       host->h_expires = jiffies - 1;
-                       if (host->h_rpcclnt) {
-                               rpc_shutdown_client(host->h_rpcclnt);
-                               host->h_rpcclnt = NULL;
-                       }
+       for_each_host(host, pos, chain, nlm_server_hosts) {
+               host->h_expires = jiffies - 1;
+               if (host->h_rpcclnt) {
+                       rpc_shutdown_client(host->h_rpcclnt);
+                       host->h_rpcclnt = NULL;
                }
        }
 
@@ -518,12 +676,10 @@ nlm_shutdown_hosts(void)
        if (nrhosts) {
                printk(KERN_WARNING "lockd: couldn't shutdown host module!\n");
                dprintk("lockd: %d hosts left:\n", nrhosts);
-               for (chain = nlm_hosts; chain < nlm_hosts + NLM_HOST_NRHASH; ++chain) {
-                       hlist_for_each_entry(host, pos, chain, h_hash) {
-                               dprintk("       %s (cnt %d use %d exp %ld)\n",
-                                       host->h_name, atomic_read(&host->h_count),
-                                       host->h_inuse, host->h_expires);
-                       }
+               for_each_host(host, pos, chain, nlm_server_hosts) {
+                       dprintk("       %s (cnt %d use %d exp %ld)\n",
+                               host->h_name, atomic_read(&host->h_count),
+                               host->h_inuse, host->h_expires);
                }
        }
 }
@@ -541,29 +697,22 @@ nlm_gc_hosts(void)
        struct nlm_host *host;
 
        dprintk("lockd: host garbage collection\n");
-       for (chain = nlm_hosts; chain < nlm_hosts + NLM_HOST_NRHASH; ++chain) {
-               hlist_for_each_entry(host, pos, chain, h_hash)
-                       host->h_inuse = 0;
-       }
+       for_each_host(host, pos, chain, nlm_server_hosts)
+               host->h_inuse = 0;
 
        /* Mark all hosts that hold locks, blocks or shares */
        nlmsvc_mark_resources();
 
-       for (chain = nlm_hosts; chain < nlm_hosts + NLM_HOST_NRHASH; ++chain) {
-               hlist_for_each_entry_safe(host, pos, next, chain, h_hash) {
-                       if (atomic_read(&host->h_count) || host->h_inuse
-                        || time_before(jiffies, host->h_expires)) {
-                               dprintk("nlm_gc_hosts skipping %s (cnt %d use %d exp %ld)\n",
-                                       host->h_name, atomic_read(&host->h_count),
-                                       host->h_inuse, host->h_expires);
-                               continue;
-                       }
-                       dprintk("lockd: delete host %s\n", host->h_name);
-                       hlist_del_init(&host->h_hash);
-
-                       nlm_destroy_host(host);
-                       nrhosts--;
+       for_each_host_safe(host, pos, next, chain, nlm_server_hosts) {
+               if (atomic_read(&host->h_count) || host->h_inuse
+                || time_before(jiffies, host->h_expires)) {
+                       dprintk("nlm_gc_hosts skipping %s "
+                               "(cnt %d use %d exp %ld)\n",
+                               host->h_name, atomic_read(&host->h_count),
+                               host->h_inuse, host->h_expires);
+                       continue;
                }
+               nlm_destroy_host_locked(host);
        }
 
        next_gc = jiffies + NLM_HOST_COLLECT;