All of lore.kernel.org
 help / color / mirror / Atom feed
From: Neil Brown <neilb@suse.de>
To: "J. Bruce Fields" <bfields@fieldses.org>
Cc: linux-nfs@vger.kernel.org
Subject: Re: [PATCH 6/9] sunrpc/cache: retry cache lookups that return -ETIMEDOUT
Date: Fri, 4 Dec 2009 15:38:45 +1100	[thread overview]
Message-ID: <20091204153845.1ec83de5@notabene.brown> (raw)
In-Reply-To: <20091203165729.GB1393@fieldses.org>

On Thu, 3 Dec 2009 11:57:29 -0500
"J. Bruce Fields" <bfields@fieldses.org> wrote:

> On Wed, Dec 02, 2009 at 05:11:27PM -0500, J. Bruce Fields wrote:
> > On Wed, Sep 09, 2009 at 04:32:54PM +1000, NeilBrown wrote:
> > > @@ -793,9 +793,18 @@ exp_find_key(svc_client *clp, int fsid_type, u32 *fsidv, struct cache_req *reqp)
> > >  	memcpy(key.ek_fsid, fsidv, key_len(fsid_type));
> > >  
> > >  	ek = svc_expkey_lookup(&key);
> > > + again:
> > >  	if (ek == NULL)
> > >  		return ERR_PTR(-ENOMEM);
> > >  	err = cache_check(&svc_expkey_cache, &ek->h, reqp);
> > > +	if (err == -ETIMEDOUT) {
> > > +		struct svc_expkey *prev_ek = ek;
> > > +		ek = svc_expkey_lookup(&key);
> > > +		if (ek != prev_ek)
> > > +			goto again;
> > > +		if (ek)
> > > +			cache_put(&ek->h, &svc_expkey_cache);
> > > +	}
> > 
> > This is very subtle.  (We're comparing to a pointer which may not
> > actually point to anything any more.)  And it's repeated in every
> > caller.  Is there any simpler way to handle this?
> 
> Actually, is it even right?  After the cache_check:
> 
> > >  	err = cache_check(&svc_expkey_cache, &ek->h, reqp);
> 
> we no longer hold a reference on ek, so it could be freed.  There's no
> reason that address couldn't then be reused for something else.  It's
> even possible that a new entry could be created at the same address
> here.  So:
> 
> > > +	if (err == -ETIMEDOUT) {
> > > +		struct svc_expkey *prev_ek = ek;
> > > +		ek = svc_expkey_lookup(&key);
> 
> the "ek" that's returned might well equal prev_ek,
> 
> > > +		if (ek != prev_ek)
> > > +			goto again;
> 
> but that doesn't necessarily imply that this is the same object that
> used to exist at that address.  So we could still return an ek which
> isn't actually a positive cache entry.
> 
> Am I missing something?

No, I don't think so.

How about this as an alternate.  I have only compile tested it, nothing more.
But if it looks good to you I'll make sure it really works.

I don't much like the way that the ipmap lookups came out, but they are a bit
awkward because the previous value is cached for improved performance.

Thanks for the review.
NeilBrown

>From 6eb6129ee478951ba1f77cdef0f13cf5a7e3f2cd Mon Sep 17 00:00:00 2001
From: NeilBrown <neilb@suse.de>
Date: Wed, 9 Sep 2009 16:22:53 +1000
Subject: [PATCH] sunrpc/cache: retry cache lookups that return -ETIMEDOUT

If cache_check returns -ETIMEDOUT, then the cache item is not
up-to-date, but there is no pending upcall.
This could mean the data is not available, or it could mean that the
good data has been stored in a new cache item.

So re-do the lookup and if that returns a new item, proceed using that
item.

Signed-off-by: NeilBrown <neilb@suse.de>
---
 fs/nfsd/export.c                  |   74 ++++++++++++++++++++----------------
 include/linux/sunrpc/cache.h      |    5 ++
 net/sunrpc/auth_gss/svcauth_gss.c |   50 +++++++++++++------------
 net/sunrpc/cache.c                |   30 +++++++++++++++
 net/sunrpc/svcauth_unix.c         |   47 +++++++++++++++++++----
 5 files changed, 140 insertions(+), 66 deletions(-)

diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c
index 984a5eb..99144d5 100644
--- a/fs/nfsd/export.c
+++ b/fs/nfsd/export.c
@@ -273,10 +273,9 @@ static struct cache_detail svc_expkey_cache = {
 	.alloc		= expkey_alloc,
 };
 
-static struct svc_expkey *
-svc_expkey_lookup(struct svc_expkey *item)
+static int
+svc_expkey_hash(struct svc_expkey *item)
 {
-	struct cache_head *ch;
 	int hash = item->ek_fsidtype;
 	char * cp = (char*)item->ek_fsid;
 	int len = key_len(item->ek_fsidtype);
@@ -284,6 +283,14 @@ svc_expkey_lookup(struct svc_expkey *item)
 	hash ^= hash_mem(cp, len, EXPKEY_HASHBITS);
 	hash ^= hash_ptr(item->ek_client, EXPKEY_HASHBITS);
 	hash &= EXPKEY_HASHMASK;
+	return hash;
+}
+
+static struct svc_expkey *
+svc_expkey_lookup(struct svc_expkey *item)
+{
+	struct cache_head *ch;
+	int hash = svc_expkey_hash(item);
 
 	ch = sunrpc_cache_lookup(&svc_expkey_cache, &item->h,
 				 hash);
@@ -297,13 +304,7 @@ static struct svc_expkey *
 svc_expkey_update(struct svc_expkey *new, struct svc_expkey *old)
 {
 	struct cache_head *ch;
-	int hash = new->ek_fsidtype;
-	char * cp = (char*)new->ek_fsid;
-	int len = key_len(new->ek_fsidtype);
-
-	hash ^= hash_mem(cp, len, EXPKEY_HASHBITS);
-	hash ^= hash_ptr(new->ek_client, EXPKEY_HASHBITS);
-	hash &= EXPKEY_HASHMASK;
+	int hash = svc_expkey_hash(new);
 
 	ch = sunrpc_cache_update(&svc_expkey_cache, &new->h,
 				 &old->h, hash);
@@ -743,14 +744,22 @@ struct cache_detail svc_export_cache = {
 	.alloc		= svc_export_alloc,
 };
 
-static struct svc_export *
-svc_export_lookup(struct svc_export *exp)
+static int
+svc_export_hash(struct svc_export *exp)
 {
-	struct cache_head *ch;
 	int hash;
+
 	hash = hash_ptr(exp->ex_client, EXPORT_HASHBITS);
 	hash ^= hash_ptr(exp->ex_path.dentry, EXPORT_HASHBITS);
 	hash ^= hash_ptr(exp->ex_path.mnt, EXPORT_HASHBITS);
+	return hash;
+}
+
+static struct svc_export *
+svc_export_lookup(struct svc_export *exp)
+{
+	struct cache_head *ch;
+	int hash = svc_export_hash(exp);
 
 	ch = sunrpc_cache_lookup(&svc_export_cache, &exp->h,
 				 hash);
@@ -764,10 +773,7 @@ static struct svc_export *
 svc_export_update(struct svc_export *new, struct svc_export *old)
 {
 	struct cache_head *ch;
-	int hash;
-	hash = hash_ptr(old->ex_client, EXPORT_HASHBITS);
-	hash ^= hash_ptr(old->ex_path.dentry, EXPORT_HASHBITS);
-	hash ^= hash_ptr(old->ex_path.mnt, EXPORT_HASHBITS);
+	int hash = svc_export_hash(old);
 
 	ch = sunrpc_cache_update(&svc_export_cache, &new->h,
 				 &old->h,
@@ -782,8 +788,8 @@ svc_export_update(struct svc_export *new, struct svc_export *old)
 static struct svc_expkey *
 exp_find_key(svc_client *clp, int fsid_type, u32 *fsidv, struct cache_req *reqp)
 {
-	struct svc_expkey key, *ek;
-	int err;
+	struct svc_expkey key;
+	struct cache_head *h;
 	
 	if (!clp)
 		return ERR_PTR(-ENOENT);
@@ -792,13 +798,14 @@ exp_find_key(svc_client *clp, int fsid_type, u32 *fsidv, struct cache_req *reqp)
 	key.ek_fsidtype = fsid_type;
 	memcpy(key.ek_fsid, fsidv, key_len(fsid_type));
 
-	ek = svc_expkey_lookup(&key);
-	if (ek == NULL)
+	h = sunrpc_lookup_check(&svc_expkey_cache, &key.h,
+				reqp, svc_expkey_hash(&key));
+
+	if (h == NULL)
 		return ERR_PTR(-ENOMEM);
-	err = cache_check(&svc_expkey_cache, &ek->h, reqp);
-	if (err)
-		return ERR_PTR(err);
-	return ek;
+	if (IS_ERR(h))
+		return ERR_PTR(PTR_ERR(h));
+	return container_of(h, struct svc_expkey, h);
 }
 
 static int exp_set_key(svc_client *clp, int fsid_type, u32 *fsidv,
@@ -855,8 +862,8 @@ exp_get_fsid_key(svc_client *clp, int fsid)
 static svc_export *exp_get_by_name(svc_client *clp, const struct path *path,
 				     struct cache_req *reqp)
 {
-	struct svc_export *exp, key;
-	int err;
+	struct svc_export key;
+	struct cache_head *h;
 
 	if (!clp)
 		return ERR_PTR(-ENOENT);
@@ -864,13 +871,14 @@ static svc_export *exp_get_by_name(svc_client *clp, const struct path *path,
 	key.ex_client = clp;
 	key.ex_path = *path;
 
-	exp = svc_export_lookup(&key);
-	if (exp == NULL)
+	h = sunrpc_lookup_check(&svc_export_cache, &key.h,
+				reqp, svc_export_hash(&key));
+	if (h == NULL)
 		return ERR_PTR(-ENOMEM);
-	err = cache_check(&svc_export_cache, &exp->h, reqp);
-	if (err)
-		return ERR_PTR(err);
-	return exp;
+	if (IS_ERR(h))
+		return ERR_PTR(PTR_ERR(h));
+
+	return container_of(h, struct svc_export, h);
 }
 
 /*
diff --git a/include/linux/sunrpc/cache.h b/include/linux/sunrpc/cache.h
index ef3db11..31d1687 100644
--- a/include/linux/sunrpc/cache.h
+++ b/include/linux/sunrpc/cache.h
@@ -154,6 +154,11 @@ extern struct cache_head *
 sunrpc_cache_update(struct cache_detail *detail,
 		    struct cache_head *new, struct cache_head *old, int hash);
 
+extern struct cache_head *
+sunrpc_lookup_check(struct cache_detail *detail,
+		    struct cache_head *item,
+		    struct cache_req *rqstp,
+		    int hash);
 extern int
 sunrpc_cache_pipe_upcall(struct cache_detail *detail, struct cache_head *h,
 		void (*cache_request)(struct cache_detail *,
diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c
index f6c51e5..ab05580 100644
--- a/net/sunrpc/auth_gss/svcauth_gss.c
+++ b/net/sunrpc/auth_gss/svcauth_gss.c
@@ -1000,6 +1000,7 @@ static int svcauth_gss_handle_init(struct svc_rqst *rqstp,
 	struct kvec *resv = &rqstp->rq_res.head[0];
 	struct xdr_netobj tmpobj;
 	struct rsi *rsip, rsikey;
+	struct cache_head *h;
 	int ret;
 
 	/* Read the verifier; should be NULL: */
@@ -1029,34 +1030,35 @@ static int svcauth_gss_handle_init(struct svc_rqst *rqstp,
 	}
 
 	/* Perform upcall, or find upcall result: */
-	rsip = rsi_lookup(&rsikey);
+	h = sunrpc_lookup_check(&rsi_cache, &rsikey.h, &rqstp->rq_chandle,
+				rsi_hash(&rsikey));
 	rsi_free(&rsikey);
-	if (!rsip)
+
+	if (!h)
 		return SVC_DROP;
-	switch (cache_check(&rsi_cache, &rsip->h, &rqstp->rq_chandle)) {
-	case -EAGAIN:
-	case -ETIMEDOUT:
-	case -ENOENT:
+
+	if (IS_ERR(h))
 		/* No upcall result: */
 		return SVC_DROP;
-	case 0:
-		ret = SVC_DROP;
-		/* Got an answer to the upcall; use it: */
-		if (gss_write_init_verf(rqstp, rsip))
-			goto out;
-		if (resv->iov_len + 4 > PAGE_SIZE)
-			goto out;
-		svc_putnl(resv, RPC_SUCCESS);
-		if (svc_safe_putnetobj(resv, &rsip->out_handle))
-			goto out;
-		if (resv->iov_len + 3 * 4 > PAGE_SIZE)
-			goto out;
-		svc_putnl(resv, rsip->major_status);
-		svc_putnl(resv, rsip->minor_status);
-		svc_putnl(resv, GSS_SEQ_WIN);
-		if (svc_safe_putnetobj(resv, &rsip->out_token))
-			goto out;
-	}
+
+	rsip = container_of(h, struct rsi, h);
+	ret = SVC_DROP;
+	/* Got an answer to the upcall; use it: */
+	if (gss_write_init_verf(rqstp, rsip))
+		goto out;
+	if (resv->iov_len + 4 > PAGE_SIZE)
+		goto out;
+	svc_putnl(resv, RPC_SUCCESS);
+	if (svc_safe_putnetobj(resv, &rsip->out_handle))
+		goto out;
+	if (resv->iov_len + 3 * 4 > PAGE_SIZE)
+		goto out;
+	svc_putnl(resv, rsip->major_status);
+	svc_putnl(resv, rsip->minor_status);
+	svc_putnl(resv, GSS_SEQ_WIN);
+	if (svc_safe_putnetobj(resv, &rsip->out_token))
+		goto out;
+
 	ret = SVC_COMPLETE;
 out:
 	cache_put(&rsip->h, &rsi_cache);
diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c
index 46e9e2b..fbd38d4 100644
--- a/net/sunrpc/cache.c
+++ b/net/sunrpc/cache.c
@@ -265,6 +265,36 @@ int cache_check(struct cache_detail *detail,
 }
 EXPORT_SYMBOL_GPL(cache_check);
 
+struct cache_head *sunrpc_lookup_check(struct cache_detail *detail,
+				       struct cache_head *item,
+				       struct cache_req *rqstp,
+				       int hash)
+{
+	struct cache_head *rv;
+	struct cache_head *prev = NULL;
+	int err = -ETIMEDOUT;
+
+	while (err == -ETIMEDOUT) {
+		rv = sunrpc_cache_lookup(detail, item, hash);
+		if (rv && rv == prev)
+			break;
+		if (prev)
+			cache_put(prev, detail);
+		if (rv == NULL)
+			return NULL;
+
+		prev = cache_get(rv);
+		err = cache_check(detail, rv, rqstp);
+	}
+
+
+	if (prev)
+		cache_put(prev, detail);
+	if (err)
+		return PTR_ERR(err);
+	return rv;
+}
+EXPORT_SYMBOL_GPL(sunrpc_lookup_check);
 /*
  * caches need to be periodically cleaned.
  * For this we maintain a list of cache_detail and
diff --git a/net/sunrpc/svcauth_unix.c b/net/sunrpc/svcauth_unix.c
index 117f68a..f4e5908 100644
--- a/net/sunrpc/svcauth_unix.c
+++ b/net/sunrpc/svcauth_unix.c
@@ -658,18 +658,27 @@ static struct unix_gid *unix_gid_lookup(uid_t uid)
 static int unix_gid_find(uid_t uid, struct group_info **gip,
 			 struct svc_rqst *rqstp)
 {
-	struct unix_gid *ug = unix_gid_lookup(uid);
-	if (!ug)
+	struct unix_gid ugid;
+	struct cache_head *h;
+
+	ugid.uid = uid;
+	h = sunrpc_lookup_check(&unix_gid_cache, &ugid.h, 
+				&rqstp->rq_chandle, hash_long(uid, GID_HASHBITS));
+
+	if (!h)
 		return -EAGAIN;
-	switch (cache_check(&unix_gid_cache, &ug->h, &rqstp->rq_chandle)) {
-	case -ENOENT:
-		*gip = NULL;
-		return 0;
-	case 0:
+	if (!IS_ERR(h)) {
+		struct unix_gid *ug = container_of(h, struct unix_gid, h);
 		*gip = ug->gi;
 		get_group_info(*gip);
 		cache_put(&ug->h, &unix_gid_cache);
 		return 0;
+	}
+
+	switch (PTR_ERR(h)) {
+	case -ENOENT:
+		*gip = NULL;
+		return 0;
 	default:
 		return -EAGAIN;
 	}
@@ -681,6 +690,7 @@ svcauth_unix_set_client(struct svc_rqst *rqstp)
 	struct sockaddr_in *sin;
 	struct sockaddr_in6 *sin6, sin6_storage;
 	struct ip_map *ipm;
+	int err;
 
 	switch (rqstp->rq_addr.ss_family) {
 	case AF_INET:
@@ -708,11 +718,30 @@ svcauth_unix_set_client(struct svc_rqst *rqstp)
 	if (ipm == NULL)
 		return SVC_DENIED;
 
-	switch (cache_check(&ip_map_cache, &ipm->h, &rqstp->rq_chandle)) {
+	err = cache_check(&ip_map_cache, &ipm->h, &rqstp->rq_chandle);
+
+	if (err == -ETIMEDOUT) {
+		struct ip_map ip;
+		struct cache_head *h;
+
+		strcpy(ip.m_class, rqstp->rq_server->sv_program->pg_class);
+		ipv6_addr_copy(&ip.m_addr, &sin6->sin6_addr);
+
+		h = sunrpc_lookup_check(&ip_map_cache, &ip.h,
+					&rqstp->rq_chandle, hash_ip6(sin6->sin6_addr));
+		if (h == NULL)
+			return SVC_DENIED;
+		if (IS_ERR(h))
+			err = PTR_ERR(h);
+		else {
+			err = 0;
+			ipm = container_of(h, struct ip_map, h);
+		}
+	}
+	switch(err) {
 		default:
 			BUG();
 		case -EAGAIN:
-		case -ETIMEDOUT:
 			return SVC_DROP;
 		case -ENOENT:
 			return SVC_DENIED;
-- 
1.6.5.3


  reply	other threads:[~2009-12-04  4:38 UTC|newest]

Thread overview: 25+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2009-09-09  6:32 [PATCH 0/9] Some improvements to request deferral and related code NeilBrown
     [not found] ` <20090909062539.20462.67466.stgit-wvvUuzkyo1EYVZTmpyfIwg@public.gmane.org>
2009-09-09  6:32   ` [PATCH 4/9] sunrpc/cache: avoid variable over-loading in cache_defer_req NeilBrown
     [not found]     ` <20090909063254.20462.68582.stgit-wvvUuzkyo1EYVZTmpyfIwg@public.gmane.org>
2009-09-18 21:24       ` J. Bruce Fields
2009-09-09  6:32   ` [PATCH 6/9] sunrpc/cache: retry cache lookups that return -ETIMEDOUT NeilBrown
     [not found]     ` <20090909063254.20462.41616.stgit-wvvUuzkyo1EYVZTmpyfIwg@public.gmane.org>
2009-12-02 22:11       ` J. Bruce Fields
2009-12-03 16:57         ` J. Bruce Fields
2009-12-04  4:38           ` Neil Brown [this message]
     [not found]             ` <20091204153845.1ec83de5-wvvUuzkyo1EYVZTmpyfIwg@public.gmane.org>
2009-12-05  1:17               ` J. Bruce Fields
2009-12-15  6:27                 ` Neil Brown
     [not found]                   ` <20091215172729.5e1d0190-wvvUuzkyo1EYVZTmpyfIwg@public.gmane.org>
2010-02-01 17:11                     ` J. Bruce Fields
2010-02-02 21:33                       ` Neil Brown
2009-09-09  6:32   ` [PATCH 3/9] sunrpc/cache: use list_del_init for the list_head entries in cache_deferred_req NeilBrown
     [not found]     ` <20090909063254.20462.7969.stgit-wvvUuzkyo1EYVZTmpyfIwg@public.gmane.org>
2009-09-18 15:48       ` J. Bruce Fields
2009-09-09  6:32   ` [PATCH 2/9] sunrpc/cache: simplify cache_fresh_locked and cache_fresh_unlocked NeilBrown
2009-09-09  6:32   ` [PATCH 7/9] nfsd/idmap: drop special request deferal in favour of improved default NeilBrown
     [not found]     ` <20090909063254.20462.80299.stgit-wvvUuzkyo1EYVZTmpyfIwg@public.gmane.org>
2009-12-02 22:18       ` J. Bruce Fields
2009-09-09  6:32   ` [PATCH 1/9] sunrpc/cache: change cache_defer_req to return -ve error, not boolean NeilBrown
     [not found]     ` <20090909063254.20462.57204.stgit-wvvUuzkyo1EYVZTmpyfIwg@public.gmane.org>
2009-09-11 21:03       ` J. Bruce Fields
2009-09-09  6:32   ` [PATCH 5/9] sunrpc/cache: allow threads to block while waiting for cache update NeilBrown
     [not found]     ` <20090909063254.20462.99277.stgit-wvvUuzkyo1EYVZTmpyfIwg@public.gmane.org>
2009-12-02 20:59       ` J. Bruce Fields
2009-12-02 21:23         ` Trond Myklebust
2009-12-02 21:50           ` Trond Myklebust
2009-09-09  6:32   ` [PATCH 8/9] sunrpc/cache: change deferred-request hash table to use hlist NeilBrown
2009-09-09  6:32   ` [PATCH 9/9] sunrpc: close connection when a request is irretrievably lost NeilBrown
2009-09-11 21:07   ` [PATCH 0/9] Some improvements to request deferral and related code J. Bruce Fields

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20091204153845.1ec83de5@notabene.brown \
    --to=neilb@suse.de \
    --cc=bfields@fieldses.org \
    --cc=linux-nfs@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.