Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > fc11cd6e1c513a17304da94a5390f3cd > files > 909

kernel-2.6.18-194.11.1.el5.src.rpm

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 *);