diff options
-rw-r--r-- | include/linux/sunrpc/auth.h | 3 | ||||
-rw-r--r-- | net/sunrpc/auth.c | 91 | ||||
-rw-r--r-- | net/sunrpc/auth_gss/auth_gss.c | 22 | ||||
-rw-r--r-- | net/sunrpc/auth_unix.c | 18 |
4 files changed, 91 insertions, 43 deletions
diff --git a/include/linux/sunrpc/auth.h b/include/linux/sunrpc/auth.h index 4e78f0c5f014..5974e8a493c4 100644 --- a/include/linux/sunrpc/auth.h +++ b/include/linux/sunrpc/auth.h | |||
@@ -16,6 +16,7 @@ | |||
16 | #include <linux/sunrpc/xdr.h> | 16 | #include <linux/sunrpc/xdr.h> |
17 | 17 | ||
18 | #include <asm/atomic.h> | 18 | #include <asm/atomic.h> |
19 | #include <linux/rcupdate.h> | ||
19 | 20 | ||
20 | /* size of the nodename buffer */ | 21 | /* size of the nodename buffer */ |
21 | #define UNX_MAXNODENAME 32 | 22 | #define UNX_MAXNODENAME 32 |
@@ -35,6 +36,7 @@ struct rpc_credops; | |||
35 | struct rpc_cred { | 36 | struct rpc_cred { |
36 | struct hlist_node cr_hash; /* hash chain */ | 37 | struct hlist_node cr_hash; /* hash chain */ |
37 | struct list_head cr_lru; /* lru garbage collection */ | 38 | struct list_head cr_lru; /* lru garbage collection */ |
39 | struct rcu_head cr_rcu; | ||
38 | struct rpc_auth * cr_auth; | 40 | struct rpc_auth * cr_auth; |
39 | const struct rpc_credops *cr_ops; | 41 | const struct rpc_credops *cr_ops; |
40 | #ifdef RPC_DEBUG | 42 | #ifdef RPC_DEBUG |
@@ -50,6 +52,7 @@ struct rpc_cred { | |||
50 | }; | 52 | }; |
51 | #define RPCAUTH_CRED_NEW 0 | 53 | #define RPCAUTH_CRED_NEW 0 |
52 | #define RPCAUTH_CRED_UPTODATE 1 | 54 | #define RPCAUTH_CRED_UPTODATE 1 |
55 | #define RPCAUTH_CRED_HASHED 2 | ||
53 | 56 | ||
54 | #define RPCAUTH_CRED_MAGIC 0x0f4aa4f0 | 57 | #define RPCAUTH_CRED_MAGIC 0x0f4aa4f0 |
55 | 58 | ||
diff --git a/net/sunrpc/auth.c b/net/sunrpc/auth.c index 00f9649b0901..ad7bde2c437e 100644 --- a/net/sunrpc/auth.c +++ b/net/sunrpc/auth.c | |||
@@ -112,6 +112,14 @@ rpcauth_release(struct rpc_auth *auth) | |||
112 | 112 | ||
113 | static DEFINE_SPINLOCK(rpc_credcache_lock); | 113 | static DEFINE_SPINLOCK(rpc_credcache_lock); |
114 | 114 | ||
115 | static void | ||
116 | rpcauth_unhash_cred_locked(struct rpc_cred *cred) | ||
117 | { | ||
118 | hlist_del_rcu(&cred->cr_hash); | ||
119 | smp_mb__before_clear_bit(); | ||
120 | clear_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags); | ||
121 | } | ||
122 | |||
115 | /* | 123 | /* |
116 | * Initialize RPC credential cache | 124 | * Initialize RPC credential cache |
117 | */ | 125 | */ |
@@ -166,8 +174,7 @@ rpcauth_clear_credcache(struct rpc_cred_cache *cache) | |||
166 | cred = hlist_entry(head->first, struct rpc_cred, cr_hash); | 174 | cred = hlist_entry(head->first, struct rpc_cred, cr_hash); |
167 | get_rpccred(cred); | 175 | get_rpccred(cred); |
168 | list_move_tail(&cred->cr_lru, &free); | 176 | list_move_tail(&cred->cr_lru, &free); |
169 | smp_wmb(); | 177 | rpcauth_unhash_cred_locked(cred); |
170 | hlist_del_init(&cred->cr_hash); | ||
171 | } | 178 | } |
172 | } | 179 | } |
173 | spin_unlock(&rpc_credcache_lock); | 180 | spin_unlock(&rpc_credcache_lock); |
@@ -207,8 +214,7 @@ rpcauth_prune_expired(struct list_head *free) | |||
207 | continue; | 214 | continue; |
208 | get_rpccred(cred); | 215 | get_rpccred(cred); |
209 | list_add_tail(&cred->cr_lru, free); | 216 | list_add_tail(&cred->cr_lru, free); |
210 | smp_wmb(); | 217 | rpcauth_unhash_cred_locked(cred); |
211 | hlist_del_init(&cred->cr_hash); | ||
212 | } | 218 | } |
213 | } | 219 | } |
214 | 220 | ||
@@ -218,10 +224,12 @@ rpcauth_prune_expired(struct list_head *free) | |||
218 | static void | 224 | static void |
219 | rpcauth_gc_credcache(struct rpc_cred_cache *cache, struct list_head *free) | 225 | rpcauth_gc_credcache(struct rpc_cred_cache *cache, struct list_head *free) |
220 | { | 226 | { |
221 | if (time_before(jiffies, cache->nextgc)) | 227 | if (list_empty(&cred_unused) || time_before(jiffies, cache->nextgc)) |
222 | return; | 228 | return; |
229 | spin_lock(&rpc_credcache_lock); | ||
223 | cache->nextgc = jiffies + cache->expire; | 230 | cache->nextgc = jiffies + cache->expire; |
224 | rpcauth_prune_expired(free); | 231 | rpcauth_prune_expired(free); |
232 | spin_unlock(&rpc_credcache_lock); | ||
225 | } | 233 | } |
226 | 234 | ||
227 | /* | 235 | /* |
@@ -234,42 +242,57 @@ rpcauth_lookup_credcache(struct rpc_auth *auth, struct auth_cred * acred, | |||
234 | LIST_HEAD(free); | 242 | LIST_HEAD(free); |
235 | struct rpc_cred_cache *cache = auth->au_credcache; | 243 | struct rpc_cred_cache *cache = auth->au_credcache; |
236 | struct hlist_node *pos; | 244 | struct hlist_node *pos; |
237 | struct rpc_cred *new = NULL, | 245 | struct rpc_cred *cred = NULL, |
238 | *cred = NULL, | 246 | *entry, *new; |
239 | *entry; | ||
240 | int nr = 0; | 247 | int nr = 0; |
241 | 248 | ||
242 | if (!(flags & RPCAUTH_LOOKUP_ROOTCREDS)) | 249 | if (!(flags & RPCAUTH_LOOKUP_ROOTCREDS)) |
243 | nr = acred->uid & RPC_CREDCACHE_MASK; | 250 | nr = acred->uid & RPC_CREDCACHE_MASK; |
244 | retry: | 251 | |
245 | spin_lock(&rpc_credcache_lock); | 252 | rcu_read_lock(); |
246 | hlist_for_each_entry(entry, pos, &cache->hashtable[nr], cr_hash) { | 253 | hlist_for_each_entry_rcu(entry, pos, &cache->hashtable[nr], cr_hash) { |
247 | if (!entry->cr_ops->crmatch(acred, entry, flags)) | 254 | if (!entry->cr_ops->crmatch(acred, entry, flags)) |
248 | continue; | 255 | continue; |
256 | spin_lock(&rpc_credcache_lock); | ||
257 | if (test_bit(RPCAUTH_CRED_HASHED, &entry->cr_flags) == 0) { | ||
258 | spin_unlock(&rpc_credcache_lock); | ||
259 | continue; | ||
260 | } | ||
249 | cred = get_rpccred(entry); | 261 | cred = get_rpccred(entry); |
250 | hlist_del(&entry->cr_hash); | 262 | spin_unlock(&rpc_credcache_lock); |
251 | break; | 263 | break; |
252 | } | 264 | } |
253 | if (new) { | 265 | rcu_read_unlock(); |
254 | if (cred) | 266 | |
255 | list_add_tail(&new->cr_lru, &free); | 267 | if (cred != NULL) { |
256 | else | 268 | rpcauth_gc_credcache(cache, &free); |
257 | cred = new; | 269 | goto found; |
258 | } | ||
259 | if (cred) { | ||
260 | hlist_add_head(&cred->cr_hash, &cache->hashtable[nr]); | ||
261 | } | 270 | } |
262 | rpcauth_gc_credcache(cache, &free); | ||
263 | spin_unlock(&rpc_credcache_lock); | ||
264 | 271 | ||
265 | rpcauth_destroy_credlist(&free); | 272 | new = auth->au_ops->crcreate(auth, acred, flags); |
273 | if (IS_ERR(new)) { | ||
274 | cred = new; | ||
275 | goto out; | ||
276 | } | ||
266 | 277 | ||
267 | if (!cred) { | 278 | spin_lock(&rpc_credcache_lock); |
268 | new = auth->au_ops->crcreate(auth, acred, flags); | 279 | hlist_for_each_entry(entry, pos, &cache->hashtable[nr], cr_hash) { |
269 | if (!IS_ERR(new)) | 280 | if (!entry->cr_ops->crmatch(acred, entry, flags)) |
270 | goto retry; | 281 | continue; |
282 | cred = get_rpccred(entry); | ||
283 | break; | ||
284 | } | ||
285 | if (cred == NULL) { | ||
271 | cred = new; | 286 | cred = new; |
272 | } else if (test_bit(RPCAUTH_CRED_NEW, &cred->cr_flags) | 287 | set_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags); |
288 | hlist_add_head_rcu(&cred->cr_hash, &cache->hashtable[nr]); | ||
289 | } else | ||
290 | list_add_tail(&new->cr_lru, &free); | ||
291 | rpcauth_prune_expired(&free); | ||
292 | cache->nextgc = jiffies + cache->expire; | ||
293 | spin_unlock(&rpc_credcache_lock); | ||
294 | found: | ||
295 | if (test_bit(RPCAUTH_CRED_NEW, &cred->cr_flags) | ||
273 | && cred->cr_ops->cr_init != NULL | 296 | && cred->cr_ops->cr_init != NULL |
274 | && !(flags & RPCAUTH_LOOKUP_NEW)) { | 297 | && !(flags & RPCAUTH_LOOKUP_NEW)) { |
275 | int res = cred->cr_ops->cr_init(auth, cred); | 298 | int res = cred->cr_ops->cr_init(auth, cred); |
@@ -278,8 +301,9 @@ retry: | |||
278 | cred = ERR_PTR(res); | 301 | cred = ERR_PTR(res); |
279 | } | 302 | } |
280 | } | 303 | } |
281 | 304 | rpcauth_destroy_credlist(&free); | |
282 | return (struct rpc_cred *) cred; | 305 | out: |
306 | return cred; | ||
283 | } | 307 | } |
284 | 308 | ||
285 | struct rpc_cred * | 309 | struct rpc_cred * |
@@ -357,21 +381,20 @@ void | |||
357 | put_rpccred(struct rpc_cred *cred) | 381 | put_rpccred(struct rpc_cred *cred) |
358 | { | 382 | { |
359 | /* Fast path for unhashed credentials */ | 383 | /* Fast path for unhashed credentials */ |
360 | if (!hlist_unhashed(&cred->cr_hash)) | 384 | if (test_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags) != 0) |
361 | goto need_lock; | 385 | goto need_lock; |
362 | 386 | ||
363 | if (!atomic_dec_and_test(&cred->cr_count)) | 387 | if (!atomic_dec_and_test(&cred->cr_count)) |
364 | return; | 388 | return; |
365 | goto out_destroy; | 389 | goto out_destroy; |
366 | |||
367 | need_lock: | 390 | need_lock: |
368 | if (!atomic_dec_and_lock(&cred->cr_count, &rpc_credcache_lock)) | 391 | if (!atomic_dec_and_lock(&cred->cr_count, &rpc_credcache_lock)) |
369 | return; | 392 | return; |
370 | if (!list_empty(&cred->cr_lru)) | 393 | if (!list_empty(&cred->cr_lru)) |
371 | list_del_init(&cred->cr_lru); | 394 | list_del_init(&cred->cr_lru); |
372 | if (test_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags) == 0) | 395 | if (test_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags) == 0) |
373 | hlist_del(&cred->cr_hash); | 396 | rpcauth_unhash_cred_locked(cred); |
374 | else if (!hlist_unhashed(&cred->cr_hash)) { | 397 | else if (test_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags) != 0) { |
375 | cred->cr_expire = jiffies; | 398 | cred->cr_expire = jiffies; |
376 | list_add_tail(&cred->cr_lru, &cred_unused); | 399 | list_add_tail(&cred->cr_lru, &cred_unused); |
377 | spin_unlock(&rpc_credcache_lock); | 400 | spin_unlock(&rpc_credcache_lock); |
diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index 55c47ae0a258..068fa6dfb64e 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c | |||
@@ -694,15 +694,25 @@ gss_destroy_ctx(struct gss_cl_ctx *ctx) | |||
694 | } | 694 | } |
695 | 695 | ||
696 | static void | 696 | static void |
697 | gss_destroy_cred(struct rpc_cred *rc) | 697 | gss_free_cred(struct gss_cred *gss_cred) |
698 | { | 698 | { |
699 | struct gss_cred *cred = container_of(rc, struct gss_cred, gc_base); | 699 | dprintk("RPC: gss_free_cred %p\n", gss_cred); |
700 | if (gss_cred->gc_ctx) | ||
701 | gss_put_ctx(gss_cred->gc_ctx); | ||
702 | kfree(gss_cred); | ||
703 | } | ||
700 | 704 | ||
701 | dprintk("RPC: gss_destroy_cred \n"); | 705 | static void |
706 | gss_free_cred_callback(struct rcu_head *head) | ||
707 | { | ||
708 | struct gss_cred *gss_cred = container_of(head, struct gss_cred, gc_base.cr_rcu); | ||
709 | gss_free_cred(gss_cred); | ||
710 | } | ||
702 | 711 | ||
703 | if (cred->gc_ctx) | 712 | static void |
704 | gss_put_ctx(cred->gc_ctx); | 713 | gss_destroy_cred(struct rpc_cred *cred) |
705 | kfree(cred); | 714 | { |
715 | call_rcu(&cred->cr_rcu, gss_free_cred_callback); | ||
706 | } | 716 | } |
707 | 717 | ||
708 | /* | 718 | /* |
diff --git a/net/sunrpc/auth_unix.c b/net/sunrpc/auth_unix.c index 29d50ffa69d6..f7ff6ad3259e 100644 --- a/net/sunrpc/auth_unix.c +++ b/net/sunrpc/auth_unix.c | |||
@@ -93,11 +93,23 @@ unx_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags) | |||
93 | } | 93 | } |
94 | 94 | ||
95 | static void | 95 | static void |
96 | unx_destroy_cred(struct rpc_cred *rcred) | 96 | unx_free_cred(struct unx_cred *unx_cred) |
97 | { | 97 | { |
98 | struct unx_cred *cred = container_of(rcred, struct unx_cred, uc_base); | 98 | dprintk("RPC: unx_free_cred %p\n", unx_cred); |
99 | kfree(unx_cred); | ||
100 | } | ||
101 | |||
102 | static void | ||
103 | unx_free_cred_callback(struct rcu_head *head) | ||
104 | { | ||
105 | struct unx_cred *unx_cred = container_of(head, struct unx_cred, uc_base.cr_rcu); | ||
106 | unx_free_cred(unx_cred); | ||
107 | } | ||
99 | 108 | ||
100 | kfree(cred); | 109 | static void |
110 | unx_destroy_cred(struct rpc_cred *cred) | ||
111 | { | ||
112 | call_rcu(&cred->cr_rcu, unx_free_cred_callback); | ||
101 | } | 113 | } |
102 | 114 | ||
103 | /* | 115 | /* |