aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/linux/sunrpc/auth.h1
-rw-r--r--net/sunrpc/auth.c121
-rw-r--r--net/sunrpc/auth_null.c1
3 files changed, 74 insertions, 49 deletions
diff --git a/include/linux/sunrpc/auth.h b/include/linux/sunrpc/auth.h
index 8586503d5ebd..4e78f0c5f014 100644
--- a/include/linux/sunrpc/auth.h
+++ b/include/linux/sunrpc/auth.h
@@ -34,6 +34,7 @@ struct rpc_auth;
34struct rpc_credops; 34struct rpc_credops;
35struct rpc_cred { 35struct rpc_cred {
36 struct hlist_node cr_hash; /* hash chain */ 36 struct hlist_node cr_hash; /* hash chain */
37 struct list_head cr_lru; /* lru garbage collection */
37 struct rpc_auth * cr_auth; 38 struct rpc_auth * cr_auth;
38 const struct rpc_credops *cr_ops; 39 const struct rpc_credops *cr_ops;
39#ifdef RPC_DEBUG 40#ifdef RPC_DEBUG
diff --git a/net/sunrpc/auth.c b/net/sunrpc/auth.c
index 4d7c78b05d1e..00f9649b0901 100644
--- a/net/sunrpc/auth.c
+++ b/net/sunrpc/auth.c
@@ -25,6 +25,8 @@ static const struct rpc_authops *auth_flavors[RPC_AUTH_MAXFLAVOR] = {
25 NULL, /* others can be loadable modules */ 25 NULL, /* others can be loadable modules */
26}; 26};
27 27
28static LIST_HEAD(cred_unused);
29
28static u32 30static u32
29pseudoflavor_to_flavor(u32 flavor) { 31pseudoflavor_to_flavor(u32 flavor) {
30 if (flavor >= RPC_AUTH_MAXFLAVOR) 32 if (flavor >= RPC_AUTH_MAXFLAVOR)
@@ -134,13 +136,13 @@ rpcauth_init_credcache(struct rpc_auth *auth, unsigned long expire)
134 * Destroy a list of credentials 136 * Destroy a list of credentials
135 */ 137 */
136static inline 138static inline
137void rpcauth_destroy_credlist(struct hlist_head *head) 139void rpcauth_destroy_credlist(struct list_head *head)
138{ 140{
139 struct rpc_cred *cred; 141 struct rpc_cred *cred;
140 142
141 while (!hlist_empty(head)) { 143 while (!list_empty(head)) {
142 cred = hlist_entry(head->first, struct rpc_cred, cr_hash); 144 cred = list_entry(head->next, struct rpc_cred, cr_lru);
143 hlist_del_init(&cred->cr_hash); 145 list_del_init(&cred->cr_lru);
144 put_rpccred(cred); 146 put_rpccred(cred);
145 } 147 }
146} 148}
@@ -152,17 +154,20 @@ void rpcauth_destroy_credlist(struct hlist_head *head)
152void 154void
153rpcauth_clear_credcache(struct rpc_cred_cache *cache) 155rpcauth_clear_credcache(struct rpc_cred_cache *cache)
154{ 156{
155 HLIST_HEAD(free); 157 LIST_HEAD(free);
156 struct hlist_node *pos, *next; 158 struct hlist_head *head;
157 struct rpc_cred *cred; 159 struct rpc_cred *cred;
158 int i; 160 int i;
159 161
160 spin_lock(&rpc_credcache_lock); 162 spin_lock(&rpc_credcache_lock);
161 for (i = 0; i < RPC_CREDCACHE_NR; i++) { 163 for (i = 0; i < RPC_CREDCACHE_NR; i++) {
162 hlist_for_each_safe(pos, next, &cache->hashtable[i]) { 164 head = &cache->hashtable[i];
163 cred = hlist_entry(pos, struct rpc_cred, cr_hash); 165 while (!hlist_empty(head)) {
164 __hlist_del(&cred->cr_hash); 166 cred = hlist_entry(head->first, struct rpc_cred, cr_hash);
165 hlist_add_head(&cred->cr_hash, &free); 167 get_rpccred(cred);
168 list_move_tail(&cred->cr_lru, &free);
169 smp_wmb();
170 hlist_del_init(&cred->cr_hash);
166 } 171 }
167 } 172 }
168 spin_unlock(&rpc_credcache_lock); 173 spin_unlock(&rpc_credcache_lock);
@@ -184,38 +189,39 @@ rpcauth_destroy_credcache(struct rpc_auth *auth)
184 } 189 }
185} 190}
186 191
192/*
193 * Remove stale credentials. Avoid sleeping inside the loop.
194 */
187static void 195static void
188rpcauth_prune_expired(struct rpc_auth *auth, struct rpc_cred *cred, struct hlist_head *free) 196rpcauth_prune_expired(struct list_head *free)
189{ 197{
190 if (atomic_read(&cred->cr_count) != 1) 198 struct rpc_cred *cred;
191 return; 199
192 if (time_after(jiffies, cred->cr_expire + auth->au_credcache->expire)) 200 while (!list_empty(&cred_unused)) {
193 clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); 201 cred = list_entry(cred_unused.next, struct rpc_cred, cr_lru);
194 if (test_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags) == 0) { 202 if (time_after(jiffies, cred->cr_expire +
195 __hlist_del(&cred->cr_hash); 203 cred->cr_auth->au_credcache->expire))
196 hlist_add_head(&cred->cr_hash, free); 204 break;
205 list_del_init(&cred->cr_lru);
206 if (atomic_read(&cred->cr_count) != 0)
207 continue;
208 get_rpccred(cred);
209 list_add_tail(&cred->cr_lru, free);
210 smp_wmb();
211 hlist_del_init(&cred->cr_hash);
197 } 212 }
198} 213}
199 214
200/* 215/*
201 * Remove stale credentials. Avoid sleeping inside the loop. 216 * Run garbage collector.
202 */ 217 */
203static void 218static void
204rpcauth_gc_credcache(struct rpc_auth *auth, struct hlist_head *free) 219rpcauth_gc_credcache(struct rpc_cred_cache *cache, struct list_head *free)
205{ 220{
206 struct rpc_cred_cache *cache = auth->au_credcache; 221 if (time_before(jiffies, cache->nextgc))
207 struct hlist_node *pos, *next; 222 return;
208 struct rpc_cred *cred;
209 int i;
210
211 dprintk("RPC: gc'ing RPC credentials for auth %p\n", auth);
212 for (i = 0; i < RPC_CREDCACHE_NR; i++) {
213 hlist_for_each_safe(pos, next, &cache->hashtable[i]) {
214 cred = hlist_entry(pos, struct rpc_cred, cr_hash);
215 rpcauth_prune_expired(auth, cred, free);
216 }
217 }
218 cache->nextgc = jiffies + cache->expire; 223 cache->nextgc = jiffies + cache->expire;
224 rpcauth_prune_expired(free);
219} 225}
220 226
221/* 227/*
@@ -225,39 +231,35 @@ struct rpc_cred *
225rpcauth_lookup_credcache(struct rpc_auth *auth, struct auth_cred * acred, 231rpcauth_lookup_credcache(struct rpc_auth *auth, struct auth_cred * acred,
226 int flags) 232 int flags)
227{ 233{
234 LIST_HEAD(free);
228 struct rpc_cred_cache *cache = auth->au_credcache; 235 struct rpc_cred_cache *cache = auth->au_credcache;
229 HLIST_HEAD(free); 236 struct hlist_node *pos;
230 struct hlist_node *pos, *next;
231 struct rpc_cred *new = NULL, 237 struct rpc_cred *new = NULL,
232 *cred = NULL; 238 *cred = NULL,
239 *entry;
233 int nr = 0; 240 int nr = 0;
234 241
235 if (!(flags & RPCAUTH_LOOKUP_ROOTCREDS)) 242 if (!(flags & RPCAUTH_LOOKUP_ROOTCREDS))
236 nr = acred->uid & RPC_CREDCACHE_MASK; 243 nr = acred->uid & RPC_CREDCACHE_MASK;
237retry: 244retry:
238 spin_lock(&rpc_credcache_lock); 245 spin_lock(&rpc_credcache_lock);
239 if (time_before(cache->nextgc, jiffies)) 246 hlist_for_each_entry(entry, pos, &cache->hashtable[nr], cr_hash) {
240 rpcauth_gc_credcache(auth, &free); 247 if (!entry->cr_ops->crmatch(acred, entry, flags))
241 hlist_for_each_safe(pos, next, &cache->hashtable[nr]) { 248 continue;
242 struct rpc_cred *entry; 249 cred = get_rpccred(entry);
243 entry = hlist_entry(pos, struct rpc_cred, cr_hash); 250 hlist_del(&entry->cr_hash);
244 if (entry->cr_ops->crmatch(acred, entry, flags)) { 251 break;
245 hlist_del(&entry->cr_hash);
246 cred = entry;
247 break;
248 }
249 rpcauth_prune_expired(auth, entry, &free);
250 } 252 }
251 if (new) { 253 if (new) {
252 if (cred) 254 if (cred)
253 hlist_add_head(&new->cr_hash, &free); 255 list_add_tail(&new->cr_lru, &free);
254 else 256 else
255 cred = new; 257 cred = new;
256 } 258 }
257 if (cred) { 259 if (cred) {
258 hlist_add_head(&cred->cr_hash, &cache->hashtable[nr]); 260 hlist_add_head(&cred->cr_hash, &cache->hashtable[nr]);
259 get_rpccred(cred);
260 } 261 }
262 rpcauth_gc_credcache(cache, &free);
261 spin_unlock(&rpc_credcache_lock); 263 spin_unlock(&rpc_credcache_lock);
262 264
263 rpcauth_destroy_credlist(&free); 265 rpcauth_destroy_credlist(&free);
@@ -303,6 +305,7 @@ rpcauth_init_cred(struct rpc_cred *cred, const struct auth_cred *acred,
303 struct rpc_auth *auth, const struct rpc_credops *ops) 305 struct rpc_auth *auth, const struct rpc_credops *ops)
304{ 306{
305 INIT_HLIST_NODE(&cred->cr_hash); 307 INIT_HLIST_NODE(&cred->cr_hash);
308 INIT_LIST_HEAD(&cred->cr_lru);
306 atomic_set(&cred->cr_count, 1); 309 atomic_set(&cred->cr_count, 1);
307 cred->cr_auth = auth; 310 cred->cr_auth = auth;
308 cred->cr_ops = ops; 311 cred->cr_ops = ops;
@@ -353,9 +356,29 @@ rpcauth_holdcred(struct rpc_task *task)
353void 356void
354put_rpccred(struct rpc_cred *cred) 357put_rpccred(struct rpc_cred *cred)
355{ 358{
356 cred->cr_expire = jiffies; 359 /* Fast path for unhashed credentials */
360 if (!hlist_unhashed(&cred->cr_hash))
361 goto need_lock;
362
357 if (!atomic_dec_and_test(&cred->cr_count)) 363 if (!atomic_dec_and_test(&cred->cr_count))
358 return; 364 return;
365 goto out_destroy;
366
367need_lock:
368 if (!atomic_dec_and_lock(&cred->cr_count, &rpc_credcache_lock))
369 return;
370 if (!list_empty(&cred->cr_lru))
371 list_del_init(&cred->cr_lru);
372 if (test_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags) == 0)
373 hlist_del(&cred->cr_hash);
374 else if (!hlist_unhashed(&cred->cr_hash)) {
375 cred->cr_expire = jiffies;
376 list_add_tail(&cred->cr_lru, &cred_unused);
377 spin_unlock(&rpc_credcache_lock);
378 return;
379 }
380 spin_unlock(&rpc_credcache_lock);
381out_destroy:
359 cred->cr_ops->crdestroy(cred); 382 cred->cr_ops->crdestroy(cred);
360} 383}
361 384
diff --git a/net/sunrpc/auth_null.c b/net/sunrpc/auth_null.c
index 6c905fb11c5d..537d0e8589dd 100644
--- a/net/sunrpc/auth_null.c
+++ b/net/sunrpc/auth_null.c
@@ -133,6 +133,7 @@ const struct rpc_credops null_credops = {
133 133
134static 134static
135struct rpc_cred null_cred = { 135struct rpc_cred null_cred = {
136 .cr_lru = LIST_HEAD_INIT(null_cred.cr_lru),
136 .cr_auth = &null_auth, 137 .cr_auth = &null_auth,
137 .cr_ops = &null_credops, 138 .cr_ops = &null_credops,
138 .cr_count = ATOMIC_INIT(1), 139 .cr_count = ATOMIC_INIT(1),