diff options
-rw-r--r-- | include/linux/sunrpc/auth.h | 1 | ||||
-rw-r--r-- | net/sunrpc/auth.c | 121 | ||||
-rw-r--r-- | net/sunrpc/auth_null.c | 1 |
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; | |||
34 | struct rpc_credops; | 34 | struct rpc_credops; |
35 | struct rpc_cred { | 35 | struct 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 | ||
28 | static LIST_HEAD(cred_unused); | ||
29 | |||
28 | static u32 | 30 | static u32 |
29 | pseudoflavor_to_flavor(u32 flavor) { | 31 | pseudoflavor_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 | */ |
136 | static inline | 138 | static inline |
137 | void rpcauth_destroy_credlist(struct hlist_head *head) | 139 | void 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) | |||
152 | void | 154 | void |
153 | rpcauth_clear_credcache(struct rpc_cred_cache *cache) | 155 | rpcauth_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 | */ | ||
187 | static void | 195 | static void |
188 | rpcauth_prune_expired(struct rpc_auth *auth, struct rpc_cred *cred, struct hlist_head *free) | 196 | rpcauth_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 | */ |
203 | static void | 218 | static void |
204 | rpcauth_gc_credcache(struct rpc_auth *auth, struct hlist_head *free) | 219 | rpcauth_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 * | |||
225 | rpcauth_lookup_credcache(struct rpc_auth *auth, struct auth_cred * acred, | 231 | rpcauth_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; |
237 | retry: | 244 | retry: |
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) | |||
353 | void | 356 | void |
354 | put_rpccred(struct rpc_cred *cred) | 357 | put_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 | |||
367 | need_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); | ||
381 | out_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 | ||
134 | static | 134 | static |
135 | struct rpc_cred null_cred = { | 135 | struct 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), |