aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNeilBrown <neilb@suse.de>2013-06-12 22:53:42 -0400
committerJ. Bruce Fields <bfields@redhat.com>2013-07-01 17:53:28 -0400
commit7715cde86857d4bb40f43f1ee971cf906eaf1b9c (patch)
treed807a28170bb5036d0717857c6304912756581a2
parent013920eb5db97e99a4c30c8400f1c616e2a8b0a2 (diff)
net/sunrpc: xpt_auth_cache should be ignored when expired.
commit d202cce8963d9268ff355a386e20243e8332b308 sunrpc: never return expired entries in sunrpc_cache_lookup moved the 'entry is expired' test from cache_check to sunrpc_cache_lookup, so that it happened early and some races could safely be ignored. However the ip_map (in svcauth_unix.c) has a separate single-item cache which allows quick lookup without locking. An entry in this case would not be subject to the expiry test and so could be used well after it has expired. This is not normally a big problem because the first time it is used after it is expired an up-call will be scheduled to refresh the entry (if it hasn't been scheduled already) and the old entry will then be invalidated. So on the second attempt to use it after it has expired, ip_map_cached_get will discard it. However that is subtle and not ideal, so replace the "!cache_valid" test with "cache_is_expired". In doing this we drop the test on the "CACHE_VALID" bit. This is unnecessary as the bit is never cleared, and an entry will only be cached if the bit is set. Reported-by: Bodo Stroesser <bstroesser@ts.fujitsu.com> Signed-off-by: NeilBrown <neilb@suse.de> Signed-off-by: J. Bruce Fields <bfields@redhat.com>
-rw-r--r--include/linux/sunrpc/cache.h48
-rw-r--r--net/sunrpc/cache.c6
-rw-r--r--net/sunrpc/svcauth_unix.c4
3 files changed, 23 insertions, 35 deletions
diff --git a/include/linux/sunrpc/cache.h b/include/linux/sunrpc/cache.h
index 8419f7dbdab2..6ce690de447f 100644
--- a/include/linux/sunrpc/cache.h
+++ b/include/linux/sunrpc/cache.h
@@ -149,6 +149,24 @@ struct cache_deferred_req {
149 int too_many); 149 int too_many);
150}; 150};
151 151
152/*
153 * timestamps kept in the cache are expressed in seconds
154 * since boot. This is the best for measuring differences in
155 * real time.
156 */
157static inline time_t seconds_since_boot(void)
158{
159 struct timespec boot;
160 getboottime(&boot);
161 return get_seconds() - boot.tv_sec;
162}
163
164static inline time_t convert_to_wallclock(time_t sinceboot)
165{
166 struct timespec boot;
167 getboottime(&boot);
168 return boot.tv_sec + sinceboot;
169}
152 170
153extern const struct file_operations cache_file_operations_pipefs; 171extern const struct file_operations cache_file_operations_pipefs;
154extern const struct file_operations content_file_operations_pipefs; 172extern const struct file_operations content_file_operations_pipefs;
@@ -182,15 +200,10 @@ static inline void cache_put(struct cache_head *h, struct cache_detail *cd)
182 kref_put(&h->ref, cd->cache_put); 200 kref_put(&h->ref, cd->cache_put);
183} 201}
184 202
185static inline int cache_valid(struct cache_head *h) 203static inline int cache_is_expired(struct cache_detail *detail, struct cache_head *h)
186{ 204{
187 /* If an item has been unhashed pending removal when 205 return (h->expiry_time < seconds_since_boot()) ||
188 * the refcount drops to 0, the expiry_time will be 206 (detail->flush_time > h->last_refresh);
189 * set to 0. We don't want to consider such items
190 * valid in this context even though CACHE_VALID is
191 * set.
192 */
193 return (h->expiry_time != 0 && test_bit(CACHE_VALID, &h->flags));
194} 207}
195 208
196extern int cache_check(struct cache_detail *detail, 209extern int cache_check(struct cache_detail *detail,
@@ -251,25 +264,6 @@ static inline int get_uint(char **bpp, unsigned int *anint)
251 return 0; 264 return 0;
252} 265}
253 266
254/*
255 * timestamps kept in the cache are expressed in seconds
256 * since boot. This is the best for measuring differences in
257 * real time.
258 */
259static inline time_t seconds_since_boot(void)
260{
261 struct timespec boot;
262 getboottime(&boot);
263 return get_seconds() - boot.tv_sec;
264}
265
266static inline time_t convert_to_wallclock(time_t sinceboot)
267{
268 struct timespec boot;
269 getboottime(&boot);
270 return boot.tv_sec + sinceboot;
271}
272
273static inline time_t get_expiry(char **bpp) 267static inline time_t get_expiry(char **bpp)
274{ 268{
275 int rv; 269 int rv;
diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c
index b12144c5edd0..5478a015ab00 100644
--- a/net/sunrpc/cache.c
+++ b/net/sunrpc/cache.c
@@ -50,12 +50,6 @@ static void cache_init(struct cache_head *h)
50 h->last_refresh = now; 50 h->last_refresh = now;
51} 51}
52 52
53static inline int cache_is_expired(struct cache_detail *detail, struct cache_head *h)
54{
55 return (h->expiry_time < seconds_since_boot()) ||
56 (detail->flush_time > h->last_refresh);
57}
58
59struct cache_head *sunrpc_cache_lookup(struct cache_detail *detail, 53struct cache_head *sunrpc_cache_lookup(struct cache_detail *detail,
60 struct cache_head *key, int hash) 54 struct cache_head *key, int hash)
61{ 55{
diff --git a/net/sunrpc/svcauth_unix.c b/net/sunrpc/svcauth_unix.c
index 06bdf5a1082c..a98853dfccdc 100644
--- a/net/sunrpc/svcauth_unix.c
+++ b/net/sunrpc/svcauth_unix.c
@@ -347,13 +347,13 @@ ip_map_cached_get(struct svc_xprt *xprt)
347 spin_lock(&xprt->xpt_lock); 347 spin_lock(&xprt->xpt_lock);
348 ipm = xprt->xpt_auth_cache; 348 ipm = xprt->xpt_auth_cache;
349 if (ipm != NULL) { 349 if (ipm != NULL) {
350 if (!cache_valid(&ipm->h)) { 350 sn = net_generic(xprt->xpt_net, sunrpc_net_id);
351 if (cache_is_expired(sn->ip_map_cache, &ipm->h)) {
351 /* 352 /*
352 * The entry has been invalidated since it was 353 * The entry has been invalidated since it was
353 * remembered, e.g. by a second mount from the 354 * remembered, e.g. by a second mount from the
354 * same IP address. 355 * same IP address.
355 */ 356 */
356 sn = net_generic(xprt->xpt_net, sunrpc_net_id);
357 xprt->xpt_auth_cache = NULL; 357 xprt->xpt_auth_cache = NULL;
358 spin_unlock(&xprt->xpt_lock); 358 spin_unlock(&xprt->xpt_lock);
359 cache_put(&ipm->h, sn->ip_map_cache); 359 cache_put(&ipm->h, sn->ip_map_cache);