diff options
author | dfr <dfr@FreeBSD.org> | 2008-07-23 09:18:08 +0000 |
---|---|---|
committer | dfr <dfr@FreeBSD.org> | 2008-07-23 09:18:08 +0000 |
commit | d27f65928a6e40f7d6391c4441edc752c1702212 (patch) | |
tree | 028a6d06e3b062550f56b8bed3425803c2aad92f | |
parent | 42aeaf36b0bea599f34b9841feaa487a33e73f4b (diff) | |
download | FreeBSD-src-d27f65928a6e40f7d6391c4441edc752c1702212.zip FreeBSD-src-d27f65928a6e40f7d6391c4441edc752c1702212.tar.gz |
Re-work the code slightly to avoid a possible livelock.
MFC after: 2 weeks
-rw-r--r-- | sys/rpc/auth_unix.c | 50 |
1 files changed, 27 insertions, 23 deletions
diff --git a/sys/rpc/auth_unix.c b/sys/rpc/auth_unix.c index 28e07f8..e30e59e 100644 --- a/sys/rpc/auth_unix.c +++ b/sys/rpc/auth_unix.c @@ -55,6 +55,7 @@ __FBSDID("$FreeBSD$"); #include <sys/lock.h> #include <sys/malloc.h> #include <sys/pcpu.h> +#include <sys/refcount.h> #include <sys/sx.h> #include <sys/ucred.h> @@ -86,7 +87,7 @@ static struct auth_ops authunix_ops = { struct audata { TAILQ_ENTRY(audata) au_link; TAILQ_ENTRY(audata) au_alllink; - int au_refs; + volatile u_int au_refs; struct xucred au_xcred; struct opaque_auth au_origcred; /* original credentials */ struct opaque_auth au_shcred; /* short hand cred */ @@ -157,6 +158,7 @@ again: sx_slock(&auth_unix_lock); TAILQ_FOREACH(au, &auth_unix_cache[h], au_link) { if (!memcmp(&xcr, &au->au_xcred, sizeof(xcr))) { + refcount_acquire(&au->au_refs); if (sx_try_upgrade(&auth_unix_lock)) { /* * Keep auth_unix_all LRU sorted. @@ -164,16 +166,16 @@ again: TAILQ_REMOVE(&auth_unix_all, au, au_alllink); TAILQ_INSERT_TAIL(&auth_unix_all, au, au_alllink); - au->au_refs++; sx_xunlock(&auth_unix_lock); - return (au->au_auth); } else { sx_sunlock(&auth_unix_lock); - goto again; } + return (au->au_auth); } } + sx_sunlock(&auth_unix_lock); + /* * Allocate and set up auth handle */ @@ -183,7 +185,7 @@ again: auth->ah_ops = &authunix_ops; auth->ah_private = (caddr_t)au; auth->ah_verf = au->au_shcred = _null_auth; - au->au_refs = 1; + refcount_init(&au->au_refs, 1); au->au_xcred = xcr; au->au_shfaults = 0; au->au_origcred.oa_base = NULL; @@ -210,18 +212,26 @@ again: auth->ah_cred = au->au_origcred; marshal_new_auth(auth); - if (sx_try_upgrade(&auth_unix_lock)) { - auth_unix_count++; - TAILQ_INSERT_TAIL(&auth_unix_cache[h], au, au_link); - TAILQ_INSERT_TAIL(&auth_unix_all, au, au_alllink); - au->au_refs++; /* one for the cache, one for user */ - sx_xunlock(&auth_unix_lock); - return (auth); - } else { - sx_sunlock(&auth_unix_lock); - AUTH_DESTROY(auth); - goto again; + sx_xlock(&auth_unix_lock); + TAILQ_FOREACH(tau, &auth_unix_cache[h], au_link) { + if (!memcmp(&xcr, &tau->au_xcred, sizeof(xcr))) { + /* + * We lost a race to create the AUTH that + * matches this cred. + */ + sx_xunlock(&auth_unix_lock); + AUTH_DESTROY(auth); + goto again; + } } + + auth_unix_count++; + TAILQ_INSERT_TAIL(&auth_unix_cache[h], au, au_link); + TAILQ_INSERT_TAIL(&auth_unix_all, au, au_alllink); + refcount_acquire(&au->au_refs); /* one for the cache, one for user */ + sx_xunlock(&auth_unix_lock); + + return (auth); } /* @@ -316,16 +326,10 @@ static void authunix_destroy(AUTH *auth) { struct audata *au; - int refs; au = AUTH_PRIVATE(auth); - sx_xlock(&auth_unix_lock); - au->au_refs--; - refs = au->au_refs; - sx_xunlock(&auth_unix_lock); - - if (refs > 0) + if (!refcount_release(&au->au_refs)) return; mem_free(au->au_origcred.oa_base, au->au_origcred.oa_length); |