diff options
Diffstat (limited to 'usr/src/lib/libidmap/common/utils.c')
-rw-r--r-- | usr/src/lib/libidmap/common/utils.c | 237 |
1 files changed, 213 insertions, 24 deletions
diff --git a/usr/src/lib/libidmap/common/utils.c b/usr/src/lib/libidmap/common/utils.c index 1c073b75d6..1068e0aa2c 100644 --- a/usr/src/lib/libidmap/common/utils.c +++ b/usr/src/lib/libidmap/common/utils.c @@ -19,12 +19,9 @@ * CDDL HEADER END */ /* - * Copyright 2008 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. + * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. */ -#pragma ident "%Z%%M% %I% %E% SMI" - /* * Utility routines */ @@ -33,6 +30,9 @@ #include <stdlib.h> #include <errno.h> #include <libintl.h> +#include <assert.h> +#include <ucontext.h> +#include <pthread.h> #include "idmap_impl.h" #define _UDT_SIZE_INCR 1 @@ -41,6 +41,21 @@ static struct timeval TIMEOUT = { 25, 0 }; +struct idmap_handle { + CLIENT *client; + boolean_t failed; + rwlock_t lock; +}; + +static struct idmap_handle idmap_handle = { + NULL, /* client */ + B_TRUE, /* failed */ + DEFAULTRWLOCK, /* lock */ +}; + +static idmap_stat _idmap_clnt_connect(void); +static void _idmap_clnt_disconnect(void); + idmap_retcode _udt_extend_batch(idmap_udt_handle_t *udthandle) { @@ -103,13 +118,10 @@ _iter_get_next_list(int type, idmap_iter_t *iter, void *arg, uchar_t **list, size_t valsize, xdrproc_t xdr_arg_proc, xdrproc_t xdr_res_proc) { - - CLIENT *clnt; - enum clnt_stat clntstat; + idmap_stat rc; iter->next = 0; iter->retlist = NULL; - _IDMAP_GET_CLIENT_HANDLE(iter->ih, clnt); /* init the result */ if (*list) { @@ -122,20 +134,25 @@ _iter_get_next_list(int type, idmap_iter_t *iter, } (void) memset(*list, 0, valsize); - clntstat = clnt_call(clnt, type, + rc = _idmap_clnt_call(type, xdr_arg_proc, (caddr_t)arg, xdr_res_proc, (caddr_t)*list, TIMEOUT); - if (clntstat != RPC_SUCCESS) { + if (rc != IDMAP_SUCCESS) { free(*list); - return (_idmap_rpc2stat(clnt)); + return (rc); } iter->retlist = *list; return (IDMAP_SUCCESS); } +/* + * Convert the return values from an RPC request into an idmap return code. + * Set errno on error. + */ +static idmap_stat -_idmap_rpc2stat(CLIENT *clnt) +_idmap_rpc2stat(enum clnt_stat clntstat, CLIENT *clnt) { /* * We only deal with door_call(3C) errors here. We look at @@ -144,19 +161,191 @@ _idmap_rpc2stat(CLIENT *clnt) * and others. */ struct rpc_err r_err; - if (clnt) { - clnt_geterr(clnt, &r_err); - errno = r_err.re_errno; - switch (r_err.re_errno) { - case ENOMEM: - return (IDMAP_ERR_MEMORY); - case EBADF: - return (IDMAP_ERR_RPC_HANDLE); - default: - return (IDMAP_ERR_RPC); + + if (clntstat == RPC_SUCCESS) + return (IDMAP_SUCCESS); + + clnt_geterr(clnt, &r_err); + errno = r_err.re_errno; + switch (r_err.re_errno) { + case ENOMEM: + return (IDMAP_ERR_MEMORY); + case EBADF: + return (IDMAP_ERR_RPC_HANDLE); + default: + return (IDMAP_ERR_RPC); + } +} + +/* + * Management of the connection to idmapd. + * + * The intent is that connections to idmapd are automatically maintained, + * reconnecting if necessary. No attempt is made to retry connnection + * attempts; a failure to connect yields an immediate error return. + * + * State of the connection is maintained through the "client" and "failed" + * elements of the handle structure: + * + * client failed + * NULL true Failed on a previous request and was not recovered. + * NULL false Should never happen. + * nonNULL true Structure exists, but an error has occurred. Waiting + * for a chance to attempt to reconnect. + * nonNULL false Connection is good. + * + * Note that the initial state is NULL/true, so that the first request + * will establish the initial connection. + * + * Concurrency is managed through the rw lock "lock". Only the writer is + * allowed to connect or disconnect, and thus only the writer can set + * "failed" to "false". Readers are allowed to use the "client" pointer, + * and to set "failed" to "true", indicating that they have encountered a + * failure. The "client" pointer is only valid while one holds a reader + * lock. Once "failed" has been set to "true", all requests (including + * the retry of the failing request) will attempt to gain the writer lock. + * When they succeed, indicating that there are no requests in flight and + * thus no outstanding references to the CLIENT structure, they check + * again to see if the connection is still failed (since another thread + * might have fixed it), and then if it is still failed they disconnect + * and reconnect. + */ + +/* + * Make an RPC call. Automatically reconnect if the connection to idmapd + * fails. Convert RPC results to idmap return codes. + */ +idmap_stat +_idmap_clnt_call( + const rpcproc_t procnum, + const xdrproc_t inproc, + const caddr_t in, + const xdrproc_t outproc, + caddr_t out, + const struct timeval tout) +{ + enum clnt_stat clntstat; + idmap_stat rc; + + (void) rw_rdlock(&idmap_handle.lock); + for (;;) { + if (idmap_handle.failed) { + /* No connection. Bid to see if we should fix it. */ + (void) rw_unlock(&idmap_handle.lock); + /* Somebody else might fix it here. */ + (void) rw_wrlock(&idmap_handle.lock); + /* + * At this point, everybody else is asleep waiting + * for us. Check to see if somebody else has already + * fixed the problem. + */ + if (idmap_handle.failed) { + /* It's our job to fix. */ + _idmap_clnt_disconnect(); + rc = _idmap_clnt_connect(); + if (rc != IDMAP_SUCCESS) { + /* We couldn't fix it. */ + assert(idmap_handle.failed); + assert(idmap_handle.client == NULL); + break; + } + /* We fixed it. */ + idmap_handle.failed = B_FALSE; + } + + /* It's fixed now. */ + (void) rw_unlock(&idmap_handle.lock); + /* + * Starting here, somebody might declare it failed + * again. + */ + (void) rw_rdlock(&idmap_handle.lock); + continue; + } + + clntstat = clnt_call(idmap_handle.client, procnum, inproc, in, + outproc, out, tout); + rc = _idmap_rpc2stat(clntstat, idmap_handle.client); + if (rc == IDMAP_ERR_RPC_HANDLE) { + /* Failed. Needs to be reconnected. */ + idmap_handle.failed = B_TRUE; + continue; + } + + /* Success or unrecoverable failure. */ + break; + } + (void) rw_unlock(&idmap_handle.lock); + return (rc); +} + +#define MIN_STACK_NEEDS 65536 + +/* + * Connect to idmapd. + * Must be single-threaded through rw_wrlock(&idmap_handle.lock). + */ +static +idmap_stat +_idmap_clnt_connect(void) +{ + uint_t sendsz = 0; + stack_t st; + + /* + * clnt_door_call() alloca()s sendsz bytes (twice too, once for + * the call args buffer and once for the call result buffer), so + * we want to pick a sendsz that will be large enough, but not + * too large. + */ + if (stack_getbounds(&st) == 0) { + /* + * Estimate how much stack space is left; + * st.ss_sp is the top of stack. + */ + if ((char *)&sendsz < (char *)st.ss_sp) + /* stack grows up */ + sendsz = ((char *)st.ss_sp - (char *)&sendsz); + else + /* stack grows down */ + sendsz = ((char *)&sendsz - (char *)st.ss_sp); + + if (sendsz <= MIN_STACK_NEEDS) { + sendsz = 0; /* RPC call may fail */ + } else { + /* Leave 64Kb (just a guess) for our needs */ + sendsz -= MIN_STACK_NEEDS; + + /* Divide the stack space left by two */ + sendsz = RNDUP(sendsz / 2); + + /* Limit sendsz to 256KB */ + if (sendsz > IDMAP_MAX_DOOR_RPC) + sendsz = IDMAP_MAX_DOOR_RPC; } } - /* null handle */ - return (IDMAP_ERR_RPC_HANDLE); + idmap_handle.client = clnt_door_create(IDMAP_PROG, IDMAP_V1, sendsz); + if (idmap_handle.client == NULL) + return (IDMAP_ERR_RPC); + + return (IDMAP_SUCCESS); +} + +/* + * Disconnect from idmapd, if we're connected. + */ +static +void +_idmap_clnt_disconnect(void) +{ + CLIENT *clnt; + + clnt = idmap_handle.client; + if (clnt != NULL) { + if (clnt->cl_auth) + auth_destroy(clnt->cl_auth); + clnt_destroy(clnt); + idmap_handle.client = NULL; + } } |