From: Jeff Layton <jlayton@redhat.com> Date: Fri, 31 Jul 2009 08:38:47 -0400 Subject: [fs] nlm: track local address and bind to it for CBs Message-id: 1249043927-10558-4-git-send-email-jlayton@redhat.com O-Subject: [RHEL5.5 PATCH 3/3] BZ#500653: NLM: track local address in nlm_host and bind to it for callbacks Bugzilla: 500653 RH-Acked-by: Peter Staubach <staubach@redhat.com> Have server side lockd keep track of the address to which a call was sent. Track nlm_hosts for each destination address separately and bind to that address when doing callbacks to clients. Signed-off-by: Jeff Layton <jlayton@redhat.com> diff --git a/fs/lockd/host.c b/fs/lockd/host.c index 419002e..9c16eec 100644 --- a/fs/lockd/host.c +++ b/fs/lockd/host.c @@ -42,7 +42,9 @@ static void nlm_gc_hosts(void); struct nlm_host * nlmclnt_lookup_host(struct sockaddr_in *sin, int proto, int version) { - return nlm_lookup_host(0, sin, proto, version); + struct sockaddr_in ssin = {0}; + + return nlm_lookup_host(0, sin, proto, version, &ssin); } /* @@ -51,20 +53,24 @@ nlmclnt_lookup_host(struct sockaddr_in *sin, int proto, int version) struct nlm_host * nlmsvc_lookup_host(struct svc_rqst *rqstp) { + struct sockaddr_in ssin = {0}; + + ssin.sin_addr.s_addr = rqstp->rq_daddr; return nlm_lookup_host(1, &rqstp->rq_addr, - rqstp->rq_prot, rqstp->rq_vers); + rqstp->rq_prot, rqstp->rq_vers, &ssin); } /* * Common host lookup routine for server & client */ struct nlm_host * -nlm_lookup_host(int server, struct sockaddr_in *sin, - int proto, int version) +nlm_lookup_host(int server, struct sockaddr_in *sin, int proto, int version, + struct sockaddr_in *ssin) { struct nlm_host *host, **hp; u32 addr; int hash; + int cmp_src = 1; dprintk("lockd: nlm_lookup_host(%08x, p=%d, v=%d)\n", (unsigned)(sin? ntohl(sin->sin_addr.s_addr) : 0), proto, version); @@ -77,6 +83,9 @@ nlm_lookup_host(int server, struct sockaddr_in *sin, if (time_after_eq(jiffies, next_gc)) nlm_gc_hosts(); + if (!server || ssin->sin_addr.s_addr == INADDR_ANY) + cmp_src = 0; + for (hp = &nlm_hosts[hash]; (host = *hp) != 0; hp = &host->h_next) { if (host->h_killed) continue; @@ -86,6 +95,8 @@ nlm_lookup_host(int server, struct sockaddr_in *sin, continue; if (host->h_server != server) continue; + if (cmp_src && !nlm_cmp_addr(&host->h_saddr, ssin)) + continue; if (nlm_cmp_addr(&host->h_addr, sin)) { if (hp != nlm_hosts + hash) { @@ -111,6 +122,7 @@ nlm_lookup_host(int server, struct sockaddr_in *sin, host->h_addr = *sin; host->h_addr.sin_port = 0; /* ouch! */ + host->h_saddr = *ssin; host->h_version = version; host->h_proto = proto; host->h_rpcclnt = NULL; @@ -195,6 +207,8 @@ nlm_bind_host(struct nlm_host *host) xprt_set_timeout(&xprt->timeout, 5, nlmsvc_timeout); xprt->resvport = 1; /* NLM requires a reserved port */ + if (xprt->tcp_flags & XPRT_SRCADDR_PRESENT) + xprt->srcaddr = host->h_saddr; /* Existing NLM servers accept AUTH_UNIX only */ clnt = rpc_new_client(xprt, host->h_name, &nlm_program, diff --git a/fs/lockd/svc4proc.c b/fs/lockd/svc4proc.c index ba8e9df..8857384 100644 --- a/fs/lockd/svc4proc.c +++ b/fs/lockd/svc4proc.c @@ -438,6 +438,7 @@ nlm4svc_proc_sm_notify(struct svc_rqst *rqstp, struct nlm_reboot *argp, void *resp) { struct sockaddr_in saddr = rqstp->rq_addr; + struct sockaddr_in anyaddr = {0}; int vers = argp->vers; int prot = argp->proto >> 1; @@ -466,7 +467,7 @@ nlm4svc_proc_sm_notify(struct svc_rqst *rqstp, struct nlm_reboot *argp, } else { /* If we run on an NFS server, delete all locks held by the client */ - if ((host = nlm_lookup_host(1, &saddr, prot, vers)) != NULL) { + if ((host = nlm_lookup_host(1, &saddr, prot, vers, &anyaddr)) != NULL) { nlmsvc_free_host_resources(host); nlm_release_host(host); } diff --git a/fs/lockd/svcproc.c b/fs/lockd/svcproc.c index aeb8f64..25b6eaa 100644 --- a/fs/lockd/svcproc.c +++ b/fs/lockd/svcproc.c @@ -470,6 +470,7 @@ nlmsvc_proc_sm_notify(struct svc_rqst *rqstp, struct nlm_reboot *argp, void *resp) { struct sockaddr_in saddr = rqstp->rq_addr; + struct sockaddr_in anyaddr = {0}; int vers = argp->vers; int prot = argp->proto >> 1; struct nlm_host *host; @@ -495,7 +496,7 @@ nlmsvc_proc_sm_notify(struct svc_rqst *rqstp, struct nlm_reboot *argp, } } else { /* If we run on an NFS server, delete all locks held by the client */ - if ((host = nlm_lookup_host(1, &saddr, prot, vers)) != NULL) { + if ((host = nlm_lookup_host(1, &saddr, prot, vers, &anyaddr)) != NULL) { nlmsvc_free_host_resources(host); nlm_release_host(host); } diff --git a/include/linux/lockd/lockd.h b/include/linux/lockd/lockd.h index 71fc946..8b01f68 100644 --- a/include/linux/lockd/lockd.h +++ b/include/linux/lockd/lockd.h @@ -61,6 +61,9 @@ struct nlm_host { spinlock_t h_lock; struct list_head h_granted; /* Locks in GRANTED state */ struct list_head h_reclaim; /* Locks in RECLAIM state */ +#ifndef __GENKSYMS__ + struct sockaddr_in h_saddr; /* our address (optional) */ +#endif }; /* @@ -174,7 +177,7 @@ int nlmclnt_reclaim(struct nlm_host *, struct file_lock *); */ struct nlm_host * nlmclnt_lookup_host(struct sockaddr_in *, int, int); struct nlm_host * nlmsvc_lookup_host(struct svc_rqst *); -struct nlm_host * nlm_lookup_host(int server, struct sockaddr_in *, int, int); +struct nlm_host * nlm_lookup_host(int server, struct sockaddr_in *, int, int, struct sockaddr_in *); struct rpc_clnt * nlm_bind_host(struct nlm_host *); void nlm_rebind_host(struct nlm_host *); struct nlm_host * nlm_get_host(struct nlm_host *);