summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--usr/src/cmd/idmap/idmapd/adutils.c232
-rw-r--r--usr/src/cmd/idmap/idmapd/idmapd.c79
2 files changed, 269 insertions, 42 deletions
diff --git a/usr/src/cmd/idmap/idmapd/adutils.c b/usr/src/cmd/idmap/idmapd/adutils.c
index a66f64c750..3bf4453f64 100644
--- a/usr/src/cmd/idmap/idmapd/adutils.c
+++ b/usr/src/cmd/idmap/idmapd/adutils.c
@@ -99,6 +99,10 @@ typedef struct ad_host {
/* hardwired to SASL GSSAPI only for now */
char *saslmech;
unsigned saslflags;
+
+ /* Number of outstanding search requests */
+ uint32_t max_requests;
+ uint32_t num_requests;
} ad_host_t;
/* A set of DSs for a given AD partition; ad_t typedef comes from adutil.h */
@@ -138,6 +142,11 @@ typedef struct idmap_q {
/* lookup state */
int msgid; /* LDAP message ID */
+ /*
+ * The LDAP search entry result is placed here to be processed
+ * when the search done result is received.
+ */
+ LDAPMessage *search_res; /* The LDAP search result */
} idmap_q_t;
/* Batch context structure; typedef is in header file */
@@ -469,6 +478,7 @@ idmap_open_conn(ad_host_t *adh, int timeoutsecs)
(void) ldap_unbind(adh->ld);
adh->ld = NULL;
}
+ adh->num_requests = 0;
atomic_inc_64(&adh->generation);
@@ -645,6 +655,8 @@ idmap_add_ds(ad_t *ad, const char *host, int port)
new->owner = ad;
new->port = port;
new->dead = 0;
+ new->max_requests = 80;
+ new->num_requests = 0;
if ((new->host = strdup(host)) == NULL)
goto err;
@@ -1049,8 +1061,9 @@ int
idmap_msgid2query(ad_host_t *adh, int msgid,
idmap_query_state_t **state, int *qid)
{
- idmap_query_state_t *p;
- int i;
+ idmap_query_state_t *p;
+ int i;
+ int ret;
(void) pthread_mutex_lock(&qstatelock);
for (p = qstatehead; p != NULL; p = p->next) {
@@ -1058,11 +1071,15 @@ idmap_msgid2query(ad_host_t *adh, int msgid,
continue;
for (i = 0; i < p->qcount; i++) {
if ((p->queries[i]).msgid == msgid) {
- p->ref_cnt++;
- *state = p;
- *qid = i;
+ if (!p->qdead) {
+ p->ref_cnt++;
+ *state = p;
+ *qid = i;
+ ret = 1;
+ } else
+ ret = 0;
(void) pthread_mutex_unlock(&qstatelock);
- return (1);
+ return (ret);
}
}
}
@@ -1071,6 +1088,46 @@ idmap_msgid2query(ad_host_t *adh, int msgid,
}
/*
+ * Put the the search result onto the correct idmap_q_t given the LDAP result
+ * msgid
+ * Returns: 0 success
+ * -1 already has a search result
+ * -2 cant find message id
+ */
+static
+int
+idmap_quesearchresbymsgid(ad_host_t *adh, int msgid, LDAPMessage *search_res)
+{
+ idmap_query_state_t *p;
+ int i;
+ int res;
+
+ (void) pthread_mutex_lock(&qstatelock);
+ for (p = qstatehead; p != NULL; p = p->next) {
+ if (p->qadh != adh || adh->generation != p->qadh_gen)
+ continue;
+ for (i = 0; i < p->qcount; i++) {
+ if ((p->queries[i]).msgid == msgid) {
+ if (p->queries[i].search_res == NULL) {
+ if (!p->qdead) {
+ p->queries[i].search_res =
+ search_res;
+ res = 0;
+ } else
+ res = -2;
+ } else
+ res = -1;
+ (void) pthread_mutex_unlock(&qstatelock);
+ return (res);
+ }
+ }
+ }
+ (void) pthread_mutex_unlock(&qstatelock);
+ return (-2);
+}
+
+
+/*
* Take parsed attribute values from a search result entry and check if
* it is the result that was desired and, if so, set the result fields
* of the given idmap_q_t.
@@ -1263,6 +1320,7 @@ idmap_extract_object(idmap_query_state_t *state, int qid, LDAPMessage *res)
int sid_type = _IDMAP_T_UNDEF;
int has_class, has_san, has_sid;
int has_unixuser, has_unixgroup;
+ int num;
adh = state->qadh;
@@ -1272,9 +1330,12 @@ idmap_extract_object(idmap_query_state_t *state, int qid, LDAPMessage *res)
assert(q->rc != NULL);
- if (*q->rc == IDMAP_SUCCESS || adh->dead ||
- (dn = ldap_get_dn(adh->ld, res)) == NULL) {
+ if (adh->dead || (dn = ldap_get_dn(adh->ld, res)) == NULL) {
+ num = adh->num_requests;
(void) pthread_mutex_unlock(&adh->lock);
+ idmapdlog(LOG_DEBUG,
+ "AD error decoding search result - %d queued requests",
+ num);
return;
}
@@ -1387,6 +1448,8 @@ idmap_extract_object(idmap_query_state_t *state, int qid, LDAPMessage *res)
}
}
+ (void) pthread_mutex_unlock(&adh->lock);
+
if (!has_class) {
/*
* Didn't find objectclass. Something's wrong with our
@@ -1409,8 +1472,6 @@ idmap_extract_object(idmap_query_state_t *state, int qid, LDAPMessage *res)
(unixuser != NULL) ? unixuser : unixgroup);
}
- (void) pthread_mutex_unlock(&adh->lock);
-
if (ber != NULL)
ber_free(ber, 0);
@@ -1420,6 +1481,10 @@ idmap_extract_object(idmap_query_state_t *state, int qid, LDAPMessage *res)
/*
* Try to get a result; if there is one, find the corresponding
* idmap_q_t and process the result.
+ *
+ * Returns: 0 success
+ * -1 error
+ * -2 queue empty
*/
static
int
@@ -1428,11 +1493,17 @@ idmap_get_adobject_batch(ad_host_t *adh, struct timeval *timeout)
idmap_query_state_t *query_state;
LDAPMessage *res = NULL;
int rc, ret, msgid, qid;
+ idmap_q_t *que;
+ int num;
(void) pthread_mutex_lock(&adh->lock);
- if (adh->dead) {
+ if (adh->dead || adh->num_requests == 0) {
+ if (adh->dead)
+ ret = -1;
+ else
+ ret = -2;
(void) pthread_mutex_unlock(&adh->lock);
- return (-1);
+ return (ret);
}
/* Get one result */
@@ -1441,28 +1512,45 @@ idmap_get_adobject_batch(ad_host_t *adh, struct timeval *timeout)
if ((timeout != NULL && timeout->tv_sec > 0 && rc == LDAP_SUCCESS) ||
rc < 0)
adh->dead = 1;
- (void) pthread_mutex_unlock(&adh->lock);
- if (adh->dead)
- return (-1);
+ if (rc == LDAP_RES_SEARCH_RESULT && adh->num_requests > 0)
+ adh->num_requests--;
+ if (adh->dead) {
+ num = adh->num_requests;
+ (void) pthread_mutex_unlock(&adh->lock);
+ idmapdlog(LOG_DEBUG,
+ "AD ldap_result error - %d queued requests", num);
+ return (-1);
+ }
switch (rc) {
case LDAP_RES_SEARCH_RESULT:
- /* We have all the LDAP replies for some search... */
+ /* We should have the LDAP replies for some search... */
msgid = ldap_msgid(res);
- if (idmap_msgid2query(adh, msgid,
- &query_state, &qid)) {
+ if (idmap_msgid2query(adh, msgid, &query_state, &qid)) {
+ (void) pthread_mutex_unlock(&adh->lock);
+ que = &(query_state->queries[qid]);
+ if (que->search_res != NULL) {
+ idmap_extract_object(query_state, qid,
+ que->search_res);
+ (void) ldap_msgfree(que->search_res);
+ que->search_res = NULL;
+ } else
+ *que->rc = IDMAP_ERR_NOTFOUND;
/* ...so we can decrement qinflight */
atomic_dec_32(&query_state->qinflight);
- /* We've seen all the result entries we'll see */
- if (*query_state->queries[qid].rc != IDMAP_SUCCESS)
- *query_state->queries[qid].rc =
- IDMAP_ERR_NOTFOUND;
idmap_lookup_unlock_batch(&query_state);
+ } else {
+ num = adh->num_requests;
+ (void) pthread_mutex_unlock(&adh->lock);
+ idmapdlog(LOG_DEBUG,
+ "AD cannot find message ID - %d queued requests",
+ num);
}
(void) ldap_msgfree(res);
ret = 0;
break;
+
case LDAP_RES_SEARCH_REFERENCE:
/*
* We have no need for these at the moment. Eventually,
@@ -1470,23 +1558,34 @@ idmap_get_adobject_batch(ad_host_t *adh, struct timeval *timeout)
* the Global Catalog then we'll need to learn to follow
* references.
*/
+ (void) pthread_mutex_unlock(&adh->lock);
(void) ldap_msgfree(res);
ret = 0;
break;
+
case LDAP_RES_SEARCH_ENTRY:
- /* Got a result */
+ /* Got a result - queue it */
msgid = ldap_msgid(res);
- if (idmap_msgid2query(adh, msgid,
- &query_state, &qid)) {
- idmap_extract_object(query_state, qid, res);
- /* we saw at least one result */
- idmap_lookup_unlock_batch(&query_state);
+ rc = idmap_quesearchresbymsgid(adh, msgid, res);
+ num = adh->num_requests;
+ (void) pthread_mutex_unlock(&adh->lock);
+ if (rc == -1) {
+ idmapdlog(LOG_DEBUG,
+ "AD already has search result - %d queued requests",
+ num);
+ (void) ldap_msgfree(res);
+ } else if (rc == -2) {
+ idmapdlog(LOG_DEBUG,
+ "AD cannot queue by message ID "
+ "- %d queued requests", num);
+ (void) ldap_msgfree(res);
}
- (void) ldap_msgfree(res);
ret = 0;
break;
+
default:
/* timeout or error; treat the same */
+ (void) pthread_mutex_unlock(&adh->lock);
ret = -1;
break;
}
@@ -1509,7 +1608,7 @@ idmap_lookup_unlock_batch(idmap_query_state_t **state)
/*
* If there are no references wakup the allocating thread
*/
- if ((*state)->ref_cnt == 0)
+ if ((*state)->ref_cnt <= 1)
(void) pthread_cond_signal(&(*state)->cv);
(void) pthread_mutex_unlock(&qstatelock);
*state = NULL;
@@ -1542,12 +1641,13 @@ idmap_lookup_release_batch(idmap_query_state_t **state)
idmap_query_state_t **p;
/*
- * Decrement reference count with qstatelock locked
- * and wait for reference count to get to zero
+ * Set state to dead to stop further operations.
+ * Wait for reference count with qstatelock locked
+ * to get to one.
*/
(void) pthread_mutex_lock(&qstatelock);
- (*state)->ref_cnt--;
- while ((*state)->ref_cnt > 0) {
+ (*state)->qdead = 1;
+ while ((*state)->ref_cnt > 1) {
(void) pthread_cond_wait(&(*state)->cv, &qstatelock);
}
@@ -1558,11 +1658,10 @@ idmap_lookup_release_batch(idmap_query_state_t **state)
break;
}
}
+ (void) pthread_mutex_unlock(&qstatelock);
idmap_cleanup_batch(*state);
- (void) pthread_mutex_unlock(&qstatelock);
-
(void) pthread_cond_destroy(&(*state)->cv);
idmap_release_conn((*state)->qadh);
@@ -1571,6 +1670,32 @@ idmap_lookup_release_batch(idmap_query_state_t **state)
*state = NULL;
}
+
+/*
+ * This routine waits for other threads using the
+ * idmap_query_state_t structure to finish.
+ * If the reference count is greater than 1 it waits
+ * for the other threads to finish using it.
+ */
+static
+void
+idmap_lookup_wait_batch(idmap_query_state_t *state)
+{
+ /*
+ * Set state to dead to stop further operation.
+ * stating.
+ * Wait for reference count to get to one
+ * with qstatelock locked.
+ */
+ (void) pthread_mutex_lock(&qstatelock);
+ state->qdead = 1;
+ while (state->ref_cnt > 1) {
+ (void) pthread_cond_wait(&state->cv, &qstatelock);
+ }
+ (void) pthread_mutex_unlock(&qstatelock);
+}
+
+
idmap_retcode
idmap_lookup_batch_end(idmap_query_state_t **state)
{
@@ -1578,7 +1703,6 @@ idmap_lookup_batch_end(idmap_query_state_t **state)
idmap_retcode retcode = IDMAP_SUCCESS;
struct timeval timeout;
- (*state)->qdead = 1;
timeout.tv_sec = IDMAPD_SEARCH_TIMEOUT;
timeout.tv_usec = 0;
@@ -1588,8 +1712,11 @@ idmap_lookup_batch_end(idmap_query_state_t **state)
&timeout)) != 0)
break;
}
+ (*state)->qdead = 1;
+ /* Wait for other threads proceesing search result to finish */
+ idmap_lookup_wait_batch(*state);
- if (rc != 0)
+ if (rc == -1 || (*state)->qinflight != 0)
retcode = IDMAP_ERR_RETRIABLE_NET_ERR;
idmap_lookup_release_batch(state);
@@ -1612,6 +1739,8 @@ idmap_batch_add1(idmap_query_state_t *state, const char *filter,
{
idmap_retcode retcode = IDMAP_SUCCESS;
int lrc, qid, i;
+ int num;
+ int dead;
struct timeval tv;
idmap_q_t *q;
static char *attrs[] = {
@@ -1687,7 +1816,17 @@ idmap_batch_add1(idmap_query_state_t *state, const char *filter,
if (value != NULL)
*value = NULL;
+ /* Check the number of queued requests first */
+ tv.tv_sec = IDMAPD_SEARCH_TIMEOUT;
+ tv.tv_usec = 0;
+ while (!state->qadh->dead &&
+ state->qadh->num_requests > state->qadh->max_requests) {
+ if (idmap_get_adobject_batch(state->qadh, &tv) != 0)
+ break;
+ }
+
/* Send this lookup, don't wait for a result here */
+ lrc = LDAP_SUCCESS;
(void) pthread_mutex_lock(&state->qadh->lock);
if (!state->qadh->dead) {
@@ -1695,20 +1834,31 @@ idmap_batch_add1(idmap_query_state_t *state, const char *filter,
lrc = ldap_search_ext(state->qadh->ld, "",
LDAP_SCOPE_SUBTREE, filter, attrs, 0, NULL, NULL,
NULL, -1, &q->msgid);
- if (lrc == LDAP_BUSY || lrc == LDAP_UNAVAILABLE ||
+
+ if (lrc == LDAP_SUCCESS) {
+ state->qadh->num_requests++;
+ } else if (lrc == LDAP_BUSY || lrc == LDAP_UNAVAILABLE ||
lrc == LDAP_CONNECT_ERROR || lrc == LDAP_SERVER_DOWN ||
lrc == LDAP_UNWILLING_TO_PERFORM) {
retcode = IDMAP_ERR_RETRIABLE_NET_ERR;
state->qadh->dead = 1;
- } else if (lrc != LDAP_SUCCESS) {
+ } else {
retcode = IDMAP_ERR_OTHER;
state->qadh->dead = 1;
}
}
+ dead = state->qadh->dead;
+ num = state->qadh->num_requests;
(void) pthread_mutex_unlock(&state->qadh->lock);
- if (state->qadh->dead)
+ if (dead) {
+ if (lrc != LDAP_SUCCESS)
+ idmapdlog(LOG_DEBUG,
+ "AD ldap_search_ext error (%s) "
+ "- %d queued requests",
+ ldap_err2string(lrc), num);
return (retcode);
+ }
atomic_inc_32(&state->qinflight);
diff --git a/usr/src/cmd/idmap/idmapd/idmapd.c b/usr/src/cmd/idmap/idmapd/idmapd.c
index 01abe06181..be045d299f 100644
--- a/usr/src/cmd/idmap/idmapd/idmapd.c
+++ b/usr/src/cmd/idmap/idmapd/idmapd.c
@@ -55,6 +55,7 @@
#include <sys/resource.h>
#include <sys/sid.h>
#include <sys/idmap.h>
+#include <pthread.h>
static void term_handler(int);
static void init_idmapd();
@@ -72,6 +73,73 @@ SVCXPRT *xprt = NULL;
static int dfd = -1; /* our door server fildes, for unregistration */
static int degraded = 0; /* whether the FMRI has been marked degraded */
+
+static uint32_t num_threads = 0;
+static pthread_key_t create_threads_key;
+static uint32_t max_threads = 40;
+
+/*
+ * Server door thread start routine.
+ *
+ * Set a TSD value to the door thread. This enables the destructor to
+ * be called when this thread exits.
+ */
+/*ARGSUSED*/
+static void *
+idmapd_door_thread_start(void *arg)
+{
+ static void *value = 0;
+
+ /*
+ * Disable cancellation to avoid memory leaks from not running
+ * the thread cleanup code.
+ */
+ (void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
+ (void) pthread_setspecific(create_threads_key, value);
+ (void) door_return(NULL, 0, NULL, 0);
+
+ /* make lint happy */
+ return (NULL);
+}
+
+/*
+ * Server door threads creation
+ */
+/*ARGSUSED*/
+static void
+idmapd_door_thread_create(door_info_t *dip)
+{
+ int num;
+ pthread_t thread_id;
+
+ if ((num = atomic_inc_32_nv(&num_threads)) > max_threads) {
+ atomic_dec_32(&num_threads);
+ idmapdlog(LOG_DEBUG,
+ "thread creation refused - %d threads currently active",
+ num - 1);
+ return;
+ }
+ (void) pthread_create(&thread_id, NULL, idmapd_door_thread_start, NULL);
+ idmapdlog(LOG_DEBUG,
+ "created thread ID %d - %d threads currently active",
+ thread_id, num);
+}
+
+/*
+ * Server door thread cleanup
+ */
+/*ARGSUSED*/
+static void
+idmapd_door_thread_cleanup(void *arg)
+{
+ int num;
+
+ num = atomic_dec_32_nv(&num_threads);
+ idmapdlog(LOG_DEBUG,
+ "exiting thread ID %d - %d threads currently active",
+ pthread_self(), num);
+}
+
/*
* This is needed for mech_krb5 -- we run as daemon, yes, but we want
* mech_krb5 to think we're root so it can get host/nodename.fqdn
@@ -269,7 +337,8 @@ static void
init_idmapd()
{
int error;
- int connmaxrec = IDMAP_MAX_DOOR_RPC;
+ int connmaxrec = IDMAP_MAX_DOOR_RPC;
+
/* create directories as root and chown to daemon uid */
if (create_directory(IDMAP_DBDIR, DAEMON_UID, DAEMON_GID) < 0)
@@ -298,6 +367,14 @@ init_idmapd()
exit(error < -2 ? SMF_EXIT_ERR_CONFIG : 1);
}
+ (void) door_server_create(idmapd_door_thread_create);
+ if ((error = pthread_key_create(&create_threads_key,
+ idmapd_door_thread_cleanup)) != 0) {
+ idmapdlog(LOG_ERR, "unable to create threads key (%s)",
+ strerror(error));
+ goto errout;
+ }
+
xprt = svc_door_create(idmap_prog_1, IDMAP_PROG, IDMAP_V1, connmaxrec);
if (xprt == NULL) {
idmapdlog(LOG_ERR, "unable to create door RPC service");