summaryrefslogtreecommitdiffstats
path: root/sys/rpc/auth_unix.c
diff options
context:
space:
mode:
authordfr <dfr@FreeBSD.org>2008-06-26 10:21:54 +0000
committerdfr <dfr@FreeBSD.org>2008-06-26 10:21:54 +0000
commit41cea6d5ca71b8cf057f9face8055b218b30e18e (patch)
tree994a214037913bc4e44eaee5070c65aeadf53485 /sys/rpc/auth_unix.c
parentca3c788812715a263f83dcec4bdabaf6c10eb922 (diff)
downloadFreeBSD-src-41cea6d5ca71b8cf057f9face8055b218b30e18e.zip
FreeBSD-src-41cea6d5ca71b8cf057f9face8055b218b30e18e.tar.gz
Re-implement the client side of rpc.lockd in the kernel. This implementation
provides the correct semantics for flock(2) style locks which are used by the lockf(1) command line tool and the pidfile(3) library. It also implements recovery from server restarts and ensures that dirty cache blocks are written to the server before obtaining locks (allowing multiple clients to use file locking to safely share data). Sponsored by: Isilon Systems PR: 94256 MFC after: 2 weeks
Diffstat (limited to 'sys/rpc/auth_unix.c')
-rw-r--r--sys/rpc/auth_unix.c127
1 files changed, 95 insertions, 32 deletions
diff --git a/sys/rpc/auth_unix.c b/sys/rpc/auth_unix.c
index a2f5fd2..757d2dd 100644
--- a/sys/rpc/auth_unix.c
+++ b/sys/rpc/auth_unix.c
@@ -50,9 +50,11 @@ __FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
+#include <sys/hash.h>
+#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/malloc.h>
-#include <sys/mutex.h>
+#include <sys/sx.h>
#include <sys/ucred.h>
#include <rpc/types.h>
@@ -81,14 +83,39 @@ static struct auth_ops authunix_ops = {
* This struct is pointed to by the ah_private field of an auth_handle.
*/
struct audata {
+ TAILQ_ENTRY(audata) au_link;
+ TAILQ_ENTRY(audata) au_alllink;
+ int au_refs;
+ struct xucred au_xcred;
struct opaque_auth au_origcred; /* original credentials */
struct opaque_auth au_shcred; /* short hand cred */
u_long au_shfaults; /* short hand cache faults */
char au_marshed[MAX_AUTH_BYTES];
u_int au_mpos; /* xdr pos at end of marshed */
+ AUTH *au_auth; /* link back to AUTH */
};
+TAILQ_HEAD(audata_list, audata);
#define AUTH_PRIVATE(auth) ((struct audata *)auth->ah_private)
+#define AUTH_UNIX_HASH_SIZE 16
+#define AUTH_UNIX_MAX 256
+static struct audata_list auth_unix_cache[AUTH_UNIX_HASH_SIZE];
+static struct audata_list auth_unix_all;
+static struct sx auth_unix_lock;
+static int auth_unix_count;
+
+static void
+authunix_init(void *dummy)
+{
+ int i;
+
+ for (i = 0; i < AUTH_UNIX_HASH_SIZE; i++)
+ TAILQ_INIT(&auth_unix_cache[i]);
+ TAILQ_INIT(&auth_unix_all);
+ sx_init(&auth_unix_lock, "auth_unix_lock");
+}
+SYSINIT(authunix_init, SI_SUB_KMEM, SI_ORDER_ANY, authunix_init, NULL);
+
/*
* Create a unix style authenticator.
* Returns an auth handle with the given stuff in it.
@@ -96,38 +123,70 @@ struct audata {
AUTH *
authunix_create(struct ucred *cred)
{
+ uint32_t h, th;
struct xucred xcr;
char mymem[MAX_AUTH_BYTES];
XDR xdrs;
AUTH *auth;
- struct audata *au;
+ struct audata *au, *tau;
struct timeval now;
uint32_t time;
int len;
+ if (auth_unix_count > AUTH_UNIX_MAX) {
+ while (auth_unix_count > AUTH_UNIX_MAX) {
+ sx_xlock(&auth_unix_lock);
+ tau = TAILQ_FIRST(&auth_unix_all);
+ th = HASHSTEP(HASHINIT, tau->au_xcred.cr_uid)
+ % AUTH_UNIX_HASH_SIZE;
+ TAILQ_REMOVE(&auth_unix_cache[th], tau, au_link);
+ TAILQ_REMOVE(&auth_unix_all, tau, au_alllink);
+ auth_unix_count--;
+ sx_xunlock(&auth_unix_lock);
+ AUTH_DESTROY(tau->au_auth);
+ }
+ }
+
+ /*
+ * Hash the uid to see if we already have an AUTH with this cred.
+ */
+ h = HASHSTEP(HASHINIT, cred->cr_uid) % AUTH_UNIX_HASH_SIZE;
+ cru2x(cred, &xcr);
+again:
+ sx_slock(&auth_unix_lock);
+ TAILQ_FOREACH(au, &auth_unix_cache[h], au_link) {
+ if (!memcmp(&xcr, &au->au_xcred, sizeof(xcr))) {
+ if (sx_try_upgrade(&auth_unix_lock)) {
+ /*
+ * Keep auth_unix_all LRU sorted.
+ */
+ 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;
+ }
+ }
+ }
+
/*
* Allocate and set up auth handle
*/
au = NULL;
auth = mem_alloc(sizeof(*auth));
-#ifndef _KERNEL
- if (auth == NULL) {
- printf("authunix_create: out of memory");
- goto cleanup_authunix_create;
- }
-#endif
au = mem_alloc(sizeof(*au));
-#ifndef _KERNEL
- if (au == NULL) {
- printf("authunix_create: out of memory");
- goto cleanup_authunix_create;
- }
-#endif
auth->ah_ops = &authunix_ops;
auth->ah_private = (caddr_t)au;
auth->ah_verf = au->au_shcred = _null_auth;
+ au->au_refs = 1;
+ au->au_xcred = xcr;
au->au_shfaults = 0;
au->au_origcred.oa_base = NULL;
+ au->au_auth = auth;
getmicrotime(&now);
time = now.tv_sec;
@@ -141,14 +200,7 @@ authunix_create(struct ucred *cred)
panic("authunix_create: failed to encode creds");
au->au_origcred.oa_length = len = XDR_GETPOS(&xdrs);
au->au_origcred.oa_flavor = AUTH_UNIX;
-#ifdef _KERNEL
au->au_origcred.oa_base = mem_alloc((u_int) len);
-#else
- if ((au->au_origcred.oa_base = mem_alloc((u_int) len)) == NULL) {
- printf("authunix_create: out of memory");
- goto cleanup_authunix_create;
- }
-#endif
memcpy(au->au_origcred.oa_base, mymem, (size_t)len);
/*
@@ -156,18 +208,19 @@ authunix_create(struct ucred *cred)
*/
auth->ah_cred = au->au_origcred;
marshal_new_auth(auth);
- return (auth);
-#ifndef _KERNEL
- cleanup_authunix_create:
- if (auth)
- mem_free(auth, sizeof(*auth));
- if (au) {
- if (au->au_origcred.oa_base)
- mem_free(au->au_origcred.oa_base, (u_int)len);
- mem_free(au, sizeof(*au));
+
+ 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;
}
- return (NULL);
-#endif
}
/*
@@ -262,8 +315,18 @@ 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)
+ return;
+
mem_free(au->au_origcred.oa_base, au->au_origcred.oa_length);
if (au->au_shcred.oa_base != NULL)
OpenPOWER on IntegriCloud