mirror of
https://git.openldap.org/openldap/openldap.git
synced 2025-12-20 22:59:34 -05:00
Epoch based memory reclamation
Similar to the algorithm presented in https://www.cl.cam.ac.uk/techreports/UCAM-CL-TR-579.pdf Not completely lock-free at the moment. Also the problems with epoch based memory reclamation are still present - a thread actively observing an epoch getting stuck will prevent LloadConnections and LloadOperations being freed, potentially running out of memory.
This commit is contained in:
parent
aab6af1c4e
commit
dc1961cb15
15 changed files with 1127 additions and 1120 deletions
|
|
@ -20,7 +20,7 @@ NT_SRCS = nt_svc.c
|
||||||
NT_OBJS = nt_svc.o ../../libraries/liblutil/slapdmsg.res
|
NT_OBJS = nt_svc.o ../../libraries/liblutil/slapdmsg.res
|
||||||
|
|
||||||
SRCS = backend.c bind.c config.c connection.c client.c \
|
SRCS = backend.c bind.c config.c connection.c client.c \
|
||||||
daemon.c extended.c init.c operation.c \
|
daemon.c epoch.c extended.c init.c operation.c \
|
||||||
upstream.c libevent_support.c \
|
upstream.c libevent_support.c \
|
||||||
$(@PLAT@_SRCS)
|
$(@PLAT@_SRCS)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ upstream_connect_cb( evutil_socket_t s, short what, void *arg )
|
||||||
LloadPendingConnection *conn = arg;
|
LloadPendingConnection *conn = arg;
|
||||||
LloadBackend *b = conn->backend;
|
LloadBackend *b = conn->backend;
|
||||||
int error = 0, rc = -1;
|
int error = 0, rc = -1;
|
||||||
|
epoch_t epoch;
|
||||||
|
|
||||||
ldap_pvt_thread_mutex_lock( &b->b_mutex );
|
ldap_pvt_thread_mutex_lock( &b->b_mutex );
|
||||||
Debug( LDAP_DEBUG_CONNS, "upstream_connect_cb: "
|
Debug( LDAP_DEBUG_CONNS, "upstream_connect_cb: "
|
||||||
|
|
@ -44,6 +45,8 @@ upstream_connect_cb( evutil_socket_t s, short what, void *arg )
|
||||||
goto preempted;
|
goto preempted;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
epoch = epoch_join();
|
||||||
|
|
||||||
if ( what == EV_WRITE ) {
|
if ( what == EV_WRITE ) {
|
||||||
socklen_t optlen = sizeof(error);
|
socklen_t optlen = sizeof(error);
|
||||||
|
|
||||||
|
|
@ -53,6 +56,7 @@ upstream_connect_cb( evutil_socket_t s, short what, void *arg )
|
||||||
}
|
}
|
||||||
if ( error == EINTR || error == EINPROGRESS || error == EWOULDBLOCK ) {
|
if ( error == EINTR || error == EINPROGRESS || error == EWOULDBLOCK ) {
|
||||||
ldap_pvt_thread_mutex_unlock( &b->b_mutex );
|
ldap_pvt_thread_mutex_unlock( &b->b_mutex );
|
||||||
|
epoch_leave( epoch );
|
||||||
return;
|
return;
|
||||||
} else if ( error ) {
|
} else if ( error ) {
|
||||||
goto done;
|
goto done;
|
||||||
|
|
@ -63,6 +67,8 @@ upstream_connect_cb( evutil_socket_t s, short what, void *arg )
|
||||||
}
|
}
|
||||||
|
|
||||||
done:
|
done:
|
||||||
|
epoch_leave( epoch );
|
||||||
|
|
||||||
LDAP_LIST_REMOVE( conn, next );
|
LDAP_LIST_REMOVE( conn, next );
|
||||||
if ( rc ) {
|
if ( rc ) {
|
||||||
evutil_closesocket( conn->fd );
|
evutil_closesocket( conn->fd );
|
||||||
|
|
@ -93,6 +99,7 @@ upstream_name_cb( int result, struct evutil_addrinfo *res, void *arg )
|
||||||
{
|
{
|
||||||
LloadBackend *b = arg;
|
LloadBackend *b = arg;
|
||||||
ber_socket_t s = AC_SOCKET_INVALID;
|
ber_socket_t s = AC_SOCKET_INVALID;
|
||||||
|
epoch_t epoch;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
if ( result == EVUTIL_EAI_CANCEL ) {
|
if ( result == EVUTIL_EAI_CANCEL ) {
|
||||||
|
|
@ -111,6 +118,7 @@ upstream_name_cb( int result, struct evutil_addrinfo *res, void *arg )
|
||||||
}
|
}
|
||||||
b->b_dns_req = NULL;
|
b->b_dns_req = NULL;
|
||||||
|
|
||||||
|
epoch = epoch_join();
|
||||||
if ( result || !res ) {
|
if ( result || !res ) {
|
||||||
Debug( LDAP_DEBUG_ANY, "upstream_name_cb: "
|
Debug( LDAP_DEBUG_ANY, "upstream_name_cb: "
|
||||||
"name resolution failed for backend '%s': %s\n",
|
"name resolution failed for backend '%s': %s\n",
|
||||||
|
|
@ -176,6 +184,7 @@ upstream_name_cb( int result, struct evutil_addrinfo *res, void *arg )
|
||||||
|
|
||||||
ldap_pvt_thread_mutex_unlock( &b->b_mutex );
|
ldap_pvt_thread_mutex_unlock( &b->b_mutex );
|
||||||
evutil_freeaddrinfo( res );
|
evutil_freeaddrinfo( res );
|
||||||
|
epoch_leave( epoch );
|
||||||
return;
|
return;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
|
|
@ -189,6 +198,7 @@ fail:
|
||||||
if ( res ) {
|
if ( res ) {
|
||||||
evutil_freeaddrinfo( res );
|
evutil_freeaddrinfo( res );
|
||||||
}
|
}
|
||||||
|
epoch_leave( epoch );
|
||||||
}
|
}
|
||||||
|
|
||||||
LloadConnection *
|
LloadConnection *
|
||||||
|
|
@ -268,7 +278,6 @@ backend_select( LloadOperation *op, int *res )
|
||||||
}
|
}
|
||||||
c->c_n_ops_executing++;
|
c->c_n_ops_executing++;
|
||||||
c->c_counters.lc_ops_received++;
|
c->c_counters.lc_ops_received++;
|
||||||
CONNECTION_UNLOCK_INCREF(c);
|
|
||||||
|
|
||||||
ldap_pvt_thread_mutex_unlock( &b->b_mutex );
|
ldap_pvt_thread_mutex_unlock( &b->b_mutex );
|
||||||
*res = LDAP_SUCCESS;
|
*res = LDAP_SUCCESS;
|
||||||
|
|
@ -356,6 +365,7 @@ backend_connect( evutil_socket_t s, short what, void *arg )
|
||||||
LloadBackend *b = arg;
|
LloadBackend *b = arg;
|
||||||
struct evdns_getaddrinfo_request *request, *placeholder;
|
struct evdns_getaddrinfo_request *request, *placeholder;
|
||||||
char *hostname;
|
char *hostname;
|
||||||
|
epoch_t epoch;
|
||||||
|
|
||||||
ldap_pvt_thread_mutex_lock( &b->b_mutex );
|
ldap_pvt_thread_mutex_lock( &b->b_mutex );
|
||||||
assert( b->b_dns_req == NULL );
|
assert( b->b_dns_req == NULL );
|
||||||
|
|
@ -372,6 +382,8 @@ backend_connect( evutil_socket_t s, short what, void *arg )
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
epoch = epoch_join();
|
||||||
|
|
||||||
Debug( LDAP_DEBUG_CONNS, "backend_connect: "
|
Debug( LDAP_DEBUG_CONNS, "backend_connect: "
|
||||||
"%sattempting connection to %s\n",
|
"%sattempting connection to %s\n",
|
||||||
(what & EV_TIMEOUT) ? "retry timeout finished, " : "",
|
(what & EV_TIMEOUT) ? "retry timeout finished, " : "",
|
||||||
|
|
@ -438,6 +450,7 @@ backend_connect( evutil_socket_t s, short what, void *arg )
|
||||||
}
|
}
|
||||||
|
|
||||||
ldap_pvt_thread_mutex_unlock( &b->b_mutex );
|
ldap_pvt_thread_mutex_unlock( &b->b_mutex );
|
||||||
|
epoch_leave( epoch );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#endif /* LDAP_PF_LOCAL */
|
#endif /* LDAP_PF_LOCAL */
|
||||||
|
|
@ -473,6 +486,7 @@ backend_connect( evutil_socket_t s, short what, void *arg )
|
||||||
b->b_dns_req = request;
|
b->b_dns_req = request;
|
||||||
}
|
}
|
||||||
ldap_pvt_thread_mutex_unlock( &b->b_mutex );
|
ldap_pvt_thread_mutex_unlock( &b->b_mutex );
|
||||||
|
epoch_leave( epoch );
|
||||||
return;
|
return;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
|
|
@ -480,6 +494,7 @@ fail:
|
||||||
b->b_failed++;
|
b->b_failed++;
|
||||||
backend_retry( b );
|
backend_retry( b );
|
||||||
ldap_pvt_thread_mutex_unlock( &b->b_mutex );
|
ldap_pvt_thread_mutex_unlock( &b->b_mutex );
|
||||||
|
epoch_leave( epoch );
|
||||||
}
|
}
|
||||||
|
|
||||||
void *
|
void *
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,8 @@ bind_mech_external(
|
||||||
{
|
{
|
||||||
BerValue binddn;
|
BerValue binddn;
|
||||||
void *ssl;
|
void *ssl;
|
||||||
char *ptr;
|
char *ptr, *message = "";
|
||||||
|
int result = LDAP_SUCCESS;
|
||||||
|
|
||||||
client->c_state = LLOAD_C_READY;
|
client->c_state = LLOAD_C_READY;
|
||||||
client->c_type = LLOAD_C_OPEN;
|
client->c_type = LLOAD_C_OPEN;
|
||||||
|
|
@ -49,14 +50,16 @@ bind_mech_external(
|
||||||
* allow that.
|
* allow that.
|
||||||
*/
|
*/
|
||||||
if ( !BER_BVISEMPTY( credentials ) ) {
|
if ( !BER_BVISEMPTY( credentials ) ) {
|
||||||
return operation_send_reject_locked( op, LDAP_UNWILLING_TO_PERFORM,
|
result = LDAP_UNWILLING_TO_PERFORM;
|
||||||
"proxy authorization is not supported", 1 );
|
message = "proxy authorization is not supported";
|
||||||
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
ssl = ldap_pvt_tls_sb_ctx( client->c_sb );
|
ssl = ldap_pvt_tls_sb_ctx( client->c_sb );
|
||||||
if ( !ssl || ldap_pvt_tls_get_peer_dn( ssl, &binddn, NULL, 0 ) ) {
|
if ( !ssl || ldap_pvt_tls_get_peer_dn( ssl, &binddn, NULL, 0 ) ) {
|
||||||
return operation_send_reject_locked( op, LDAP_INVALID_CREDENTIALS,
|
result = LDAP_INVALID_CREDENTIALS;
|
||||||
"no externally negotiated identity", 1 );
|
message = "no externally negotiated identity";
|
||||||
|
goto done;
|
||||||
}
|
}
|
||||||
client->c_auth.bv_len = binddn.bv_len + STRLENOF("dn:");
|
client->c_auth.bv_len = binddn.bv_len + STRLENOF("dn:");
|
||||||
client->c_auth.bv_val = ch_malloc( client->c_auth.bv_len + 1 );
|
client->c_auth.bv_val = ch_malloc( client->c_auth.bv_len + 1 );
|
||||||
|
|
@ -71,22 +74,20 @@ bind_mech_external(
|
||||||
client->c_type = LLOAD_C_PRIVILEGED;
|
client->c_type = LLOAD_C_PRIVILEGED;
|
||||||
}
|
}
|
||||||
|
|
||||||
return operation_send_reject_locked( op, LDAP_SUCCESS, "", 1 );
|
done:
|
||||||
|
CONNECTION_UNLOCK(client);
|
||||||
|
operation_send_reject( op, result, message, 1 );
|
||||||
|
return LDAP_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* On entering the function, we've put a reference on both connections and hold
|
|
||||||
* upstream's c_io_mutex.
|
|
||||||
*/
|
|
||||||
static int
|
static int
|
||||||
client_bind(
|
client_bind(
|
||||||
LloadOperation *op,
|
LloadOperation *op,
|
||||||
|
LloadConnection *upstream,
|
||||||
struct berval *binddn,
|
struct berval *binddn,
|
||||||
ber_tag_t tag,
|
ber_tag_t tag,
|
||||||
struct berval *auth )
|
struct berval *auth )
|
||||||
{
|
{
|
||||||
LloadConnection *upstream = op->o_upstream;
|
|
||||||
|
|
||||||
ber_printf( upstream->c_pendingber, "t{titOtO}", LDAP_TAG_MESSAGE,
|
ber_printf( upstream->c_pendingber, "t{titOtO}", LDAP_TAG_MESSAGE,
|
||||||
LDAP_TAG_MSGID, op->o_upstream_msgid,
|
LDAP_TAG_MSGID, op->o_upstream_msgid,
|
||||||
LDAP_REQ_BIND, &op->o_request,
|
LDAP_REQ_BIND, &op->o_request,
|
||||||
|
|
@ -96,19 +97,14 @@ client_bind(
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef LDAP_API_FEATURE_VERIFY_CREDENTIALS
|
#ifdef LDAP_API_FEATURE_VERIFY_CREDENTIALS
|
||||||
/*
|
|
||||||
* On entering the function, we've put a reference on both connections and hold
|
|
||||||
* upstream's c_io_mutex.
|
|
||||||
*/
|
|
||||||
static int
|
static int
|
||||||
client_bind_as_vc(
|
client_bind_as_vc(
|
||||||
LloadOperation *op,
|
LloadOperation *op,
|
||||||
|
LloadConnection *upstream,
|
||||||
struct berval *binddn,
|
struct berval *binddn,
|
||||||
ber_tag_t tag,
|
ber_tag_t tag,
|
||||||
struct berval *auth )
|
struct berval *auth )
|
||||||
{
|
{
|
||||||
LloadConnection *upstream = op->o_upstream;
|
|
||||||
|
|
||||||
CONNECTION_LOCK(upstream);
|
CONNECTION_LOCK(upstream);
|
||||||
ber_printf( upstream->c_pendingber, "t{tit{tst{{tOOtOtO}}}}", LDAP_TAG_MESSAGE,
|
ber_printf( upstream->c_pendingber, "t{tit{tst{{tOOtOtO}}}}", LDAP_TAG_MESSAGE,
|
||||||
LDAP_TAG_MSGID, op->o_upstream_msgid,
|
LDAP_TAG_MSGID, op->o_upstream_msgid,
|
||||||
|
|
@ -192,9 +188,12 @@ request_bind( LloadConnection *client, LloadOperation *op )
|
||||||
struct berval binddn, auth, mech = BER_BVNULL;
|
struct berval binddn, auth, mech = BER_BVNULL;
|
||||||
ber_int_t version;
|
ber_int_t version;
|
||||||
ber_tag_t tag;
|
ber_tag_t tag;
|
||||||
unsigned long pin = client->c_pin_id;
|
unsigned long pin;
|
||||||
int res, rc = LDAP_SUCCESS;
|
int res, rc = LDAP_SUCCESS;
|
||||||
|
|
||||||
|
CONNECTION_LOCK(client);
|
||||||
|
pin = client->c_pin_id;
|
||||||
|
|
||||||
if ( pin ) {
|
if ( pin ) {
|
||||||
LloadOperation *pinned_op, needle = {
|
LloadOperation *pinned_op, needle = {
|
||||||
.o_client_connid = client->c_connid,
|
.o_client_connid = client->c_connid,
|
||||||
|
|
@ -222,25 +221,28 @@ request_bind( LloadConnection *client, LloadOperation *op )
|
||||||
pinned_op->o_request = op->o_request;
|
pinned_op->o_request = op->o_request;
|
||||||
pinned_op->o_ctrls = op->o_ctrls;
|
pinned_op->o_ctrls = op->o_ctrls;
|
||||||
|
|
||||||
/*
|
/* Noone has seen this operation yet, plant the pin back in its stead */
|
||||||
* pinned_op is accessible from the upstream, protect it since we
|
client->c_n_ops_executing--;
|
||||||
* lose the client lock in operation_destroy_from_client temporarily
|
|
||||||
*/
|
|
||||||
pinned_op->o_client_refcnt++;
|
|
||||||
op->o_res = LLOAD_OP_COMPLETED;
|
op->o_res = LLOAD_OP_COMPLETED;
|
||||||
|
tavl_delete( &client->c_ops, op, operation_client_cmp );
|
||||||
|
op->o_client = NULL;
|
||||||
|
assert( op->o_upstream == NULL );
|
||||||
|
|
||||||
|
rc = tavl_insert( &client->c_ops, pinned_op, operation_client_cmp,
|
||||||
|
avl_dup_error );
|
||||||
|
assert( rc == LDAP_SUCCESS );
|
||||||
|
|
||||||
|
/* Noone has seen this operation yet */
|
||||||
|
op->o_refcnt--;
|
||||||
|
operation_destroy( op );
|
||||||
|
|
||||||
/* We didn't start a new operation, just continuing an existing one */
|
/* We didn't start a new operation, just continuing an existing one */
|
||||||
lload_stats.counters[LLOAD_STATS_OPS_BIND].lc_ops_received--;
|
lload_stats.counters[LLOAD_STATS_OPS_BIND].lc_ops_received--;
|
||||||
|
|
||||||
operation_destroy_from_client( op );
|
|
||||||
pinned_op->o_client_refcnt--;
|
|
||||||
|
|
||||||
op = pinned_op;
|
op = pinned_op;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* protect the Bind operation */
|
|
||||||
op->o_client_refcnt++;
|
|
||||||
tavl_delete( &client->c_ops, op, operation_client_cmp );
|
tavl_delete( &client->c_ops, op, operation_client_cmp );
|
||||||
|
|
||||||
client_reset( client );
|
client_reset( client );
|
||||||
|
|
@ -259,10 +261,11 @@ request_bind( LloadConnection *client, LloadOperation *op )
|
||||||
"failed to parse version field\n" );
|
"failed to parse version field\n" );
|
||||||
goto fail;
|
goto fail;
|
||||||
} else if ( version != LDAP_VERSION3 ) {
|
} else if ( version != LDAP_VERSION3 ) {
|
||||||
operation_send_reject_locked(
|
CONNECTION_UNLOCK(client);
|
||||||
|
operation_send_reject(
|
||||||
op, LDAP_PROTOCOL_ERROR, "LDAP version unsupported", 1 );
|
op, LDAP_PROTOCOL_ERROR, "LDAP version unsupported", 1 );
|
||||||
ber_free( copy, 0 );
|
CONNECTION_LOCK(client);
|
||||||
return LDAP_SUCCESS;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
tag = ber_get_stringbv( copy, &binddn, LBER_BV_NOTERM );
|
tag = ber_get_stringbv( copy, &binddn, LBER_BV_NOTERM );
|
||||||
|
|
@ -307,10 +310,7 @@ request_bind( LloadConnection *client, LloadOperation *op )
|
||||||
|
|
||||||
/* terminate the upstream side if client switched mechanisms */
|
/* terminate the upstream side if client switched mechanisms */
|
||||||
if ( pin ) {
|
if ( pin ) {
|
||||||
op->o_client_refcnt++;
|
|
||||||
CONNECTION_UNLOCK_INCREF(client);
|
|
||||||
operation_abandon( op );
|
operation_abandon( op );
|
||||||
CONNECTION_LOCK_DECREF(client);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ber_free( copy, 0 );
|
ber_free( copy, 0 );
|
||||||
|
|
@ -326,26 +326,28 @@ request_bind( LloadConnection *client, LloadOperation *op )
|
||||||
|
|
||||||
rc = tavl_insert( &client->c_ops, op, operation_client_cmp, avl_dup_error );
|
rc = tavl_insert( &client->c_ops, op, operation_client_cmp, avl_dup_error );
|
||||||
assert( rc == LDAP_SUCCESS );
|
assert( rc == LDAP_SUCCESS );
|
||||||
CONNECTION_UNLOCK_INCREF(client);
|
CONNECTION_UNLOCK(client);
|
||||||
|
|
||||||
if ( pin ) {
|
if ( pin ) {
|
||||||
ldap_pvt_thread_mutex_lock( &op->o_link_mutex );
|
ldap_pvt_thread_mutex_lock( &op->o_link_mutex );
|
||||||
upstream = op->o_upstream;
|
upstream = op->o_upstream;
|
||||||
|
ldap_pvt_thread_mutex_unlock( &op->o_link_mutex );
|
||||||
|
|
||||||
if ( upstream ) {
|
if ( upstream ) {
|
||||||
|
ldap_pvt_thread_mutex_lock( &upstream->c_io_mutex );
|
||||||
CONNECTION_LOCK(upstream);
|
CONNECTION_LOCK(upstream);
|
||||||
if ( !upstream->c_live ) {
|
if ( !upstream->c_live ) {
|
||||||
CONNECTION_UNLOCK(upstream);
|
CONNECTION_UNLOCK(upstream);
|
||||||
|
ldap_pvt_thread_mutex_unlock( &upstream->c_io_mutex );
|
||||||
upstream = NULL;
|
upstream = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ldap_pvt_thread_mutex_unlock( &op->o_link_mutex );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If we were pinned but lost the link, don't look for a new upstream, we
|
/* If we were pinned but lost the link, don't look for a new upstream, we
|
||||||
* have to reject the op and clear pin */
|
* have to reject the op and clear pin */
|
||||||
if ( upstream ) {
|
if ( upstream ) {
|
||||||
CONNECTION_UNLOCK_INCREF(upstream);
|
/* No need to do anything */
|
||||||
ldap_pvt_thread_mutex_lock( &upstream->c_io_mutex );
|
|
||||||
} else if ( !pin ) {
|
} else if ( !pin ) {
|
||||||
upstream = backend_select( op, &res );
|
upstream = backend_select( op, &res );
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -377,18 +379,27 @@ request_bind( LloadConnection *client, LloadOperation *op )
|
||||||
|
|
||||||
ber = upstream->c_pendingber;
|
ber = upstream->c_pendingber;
|
||||||
if ( ber == NULL && (ber = ber_alloc()) == NULL ) {
|
if ( ber == NULL && (ber = ber_alloc()) == NULL ) {
|
||||||
|
ldap_pvt_thread_mutex_unlock( &upstream->c_io_mutex );
|
||||||
|
if ( !pin ) {
|
||||||
|
LloadBackend *b = upstream->c_private;
|
||||||
|
|
||||||
|
upstream->c_n_ops_executing--;
|
||||||
|
CONNECTION_UNLOCK(upstream);
|
||||||
|
|
||||||
|
ldap_pvt_thread_mutex_lock( &b->b_mutex );
|
||||||
|
b->b_n_ops_executing--;
|
||||||
|
operation_update_backend_counters( op, b );
|
||||||
|
ldap_pvt_thread_mutex_unlock( &b->b_mutex );
|
||||||
|
} else {
|
||||||
|
CONNECTION_UNLOCK(upstream);
|
||||||
|
}
|
||||||
|
|
||||||
Debug( LDAP_DEBUG_ANY, "request_bind: "
|
Debug( LDAP_DEBUG_ANY, "request_bind: "
|
||||||
"ber_alloc failed\n" );
|
"ber_alloc failed\n" );
|
||||||
CONNECTION_LOCK_DECREF(upstream);
|
|
||||||
ldap_pvt_thread_mutex_unlock( &upstream->c_io_mutex );
|
|
||||||
upstream->c_state = LLOAD_C_READY;
|
|
||||||
if ( !BER_BVISNULL( &upstream->c_sasl_bind_mech ) ) {
|
|
||||||
ber_memfree( upstream->c_sasl_bind_mech.bv_val );
|
|
||||||
BER_BVZERO( &upstream->c_sasl_bind_mech );
|
|
||||||
}
|
|
||||||
CONNECTION_UNLOCK_OR_DESTROY(upstream);
|
|
||||||
|
|
||||||
CONNECTION_LOCK_DECREF(client);
|
operation_unlink( op );
|
||||||
|
|
||||||
|
CONNECTION_LOCK(client);
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
upstream->c_pendingber = ber;
|
upstream->c_pendingber = ber;
|
||||||
|
|
@ -397,7 +408,6 @@ request_bind( LloadConnection *client, LloadOperation *op )
|
||||||
lload_stats.counters[LLOAD_STATS_OPS_BIND].lc_ops_forwarded++;
|
lload_stats.counters[LLOAD_STATS_OPS_BIND].lc_ops_forwarded++;
|
||||||
}
|
}
|
||||||
|
|
||||||
CONNECTION_LOCK(upstream);
|
|
||||||
if ( pin ) {
|
if ( pin ) {
|
||||||
tavl_delete( &upstream->c_ops, op, operation_upstream_cmp );
|
tavl_delete( &upstream->c_ops, op, operation_upstream_cmp );
|
||||||
if ( tag == LDAP_AUTH_SIMPLE ) {
|
if ( tag == LDAP_AUTH_SIMPLE ) {
|
||||||
|
|
@ -440,52 +450,28 @@ request_bind( LloadConnection *client, LloadOperation *op )
|
||||||
|
|
||||||
#ifdef LDAP_API_FEATURE_VERIFY_CREDENTIALS
|
#ifdef LDAP_API_FEATURE_VERIFY_CREDENTIALS
|
||||||
if ( lload_features & LLOAD_FEATURE_VC ) {
|
if ( lload_features & LLOAD_FEATURE_VC ) {
|
||||||
rc = client_bind_as_vc( op, &binddn, tag, &auth );
|
rc = client_bind_as_vc( op, upstream, &binddn, tag, &auth );
|
||||||
} else
|
} else
|
||||||
#endif /* LDAP_API_FEATURE_VERIFY_CREDENTIALS */
|
#endif /* LDAP_API_FEATURE_VERIFY_CREDENTIALS */
|
||||||
{
|
{
|
||||||
rc = client_bind( op, &binddn, tag, &auth );
|
rc = client_bind( op, upstream, &binddn, tag, &auth );
|
||||||
}
|
}
|
||||||
|
ldap_pvt_thread_mutex_unlock( &upstream->c_io_mutex );
|
||||||
|
|
||||||
done:
|
done:
|
||||||
if ( rc == LDAP_SUCCESS ) {
|
|
||||||
CONNECTION_LOCK(client);
|
|
||||||
if ( upstream ) {
|
|
||||||
ldap_pvt_thread_mutex_unlock( &upstream->c_io_mutex );
|
|
||||||
}
|
|
||||||
|
|
||||||
|
CONNECTION_LOCK(client);
|
||||||
|
if ( rc == LDAP_SUCCESS ) {
|
||||||
client->c_pin_id = pin;
|
client->c_pin_id = pin;
|
||||||
if ( !--op->o_client_refcnt || !upstream ) {
|
|
||||||
operation_destroy_from_client( op );
|
|
||||||
if ( client->c_state == LLOAD_C_BINDING ) {
|
|
||||||
client->c_state = LLOAD_C_READY;
|
|
||||||
client->c_type = LLOAD_C_OPEN;
|
|
||||||
client->c_pin_id = 0;
|
|
||||||
if ( !BER_BVISNULL( &client->c_auth ) ) {
|
|
||||||
ch_free( client->c_auth.bv_val );
|
|
||||||
BER_BVZERO( &client->c_auth );
|
|
||||||
}
|
|
||||||
if ( !BER_BVISNULL( &client->c_sasl_bind_mech ) ) {
|
|
||||||
ber_memfree( client->c_sasl_bind_mech.bv_val );
|
|
||||||
BER_BVZERO( &client->c_sasl_bind_mech );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
CONNECTION_UNLOCK(client);
|
CONNECTION_UNLOCK(client);
|
||||||
|
|
||||||
if ( upstream ) {
|
if ( upstream ) {
|
||||||
connection_write_cb( -1, 0, upstream );
|
connection_write_cb( -1, 0, upstream );
|
||||||
CONNECTION_LOCK_DECREF(upstream);
|
|
||||||
CONNECTION_UNLOCK_OR_DESTROY(upstream);
|
|
||||||
}
|
}
|
||||||
CONNECTION_LOCK_DECREF(client);
|
|
||||||
} else {
|
} else {
|
||||||
fail:
|
fail:
|
||||||
rc = -1;
|
rc = -1;
|
||||||
|
|
||||||
CONNECTION_LOCK_DECREF(client);
|
|
||||||
op->o_client_refcnt--;
|
|
||||||
operation_destroy_from_client( op );
|
|
||||||
client->c_pin_id = 0;
|
client->c_pin_id = 0;
|
||||||
CONNECTION_DESTROY(client);
|
CONNECTION_DESTROY(client);
|
||||||
}
|
}
|
||||||
|
|
@ -508,42 +494,26 @@ finish_sasl_bind(
|
||||||
LloadOperation *op,
|
LloadOperation *op,
|
||||||
BerElement *ber )
|
BerElement *ber )
|
||||||
{
|
{
|
||||||
LloadConnection *client = op->o_client;
|
|
||||||
BerElement *output;
|
BerElement *output;
|
||||||
LloadOperation *removed;
|
LloadOperation *removed;
|
||||||
ber_int_t msgid;
|
ber_int_t msgid;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
if ( !(lload_features & LLOAD_FEATURE_PROXYAUTHZ) ) {
|
|
||||||
Debug( LDAP_DEBUG_TRACE, "finish_sasl_bind: "
|
|
||||||
"connid=%lu not configured to do proxyauthz, making no "
|
|
||||||
"attempt to resolve final authzid name\n",
|
|
||||||
op->o_client_connid );
|
|
||||||
CONNECTION_UNLOCK(upstream);
|
|
||||||
return forward_final_response( client, op, ber );
|
|
||||||
}
|
|
||||||
|
|
||||||
removed = tavl_delete( &upstream->c_ops, op, operation_upstream_cmp );
|
removed = tavl_delete( &upstream->c_ops, op, operation_upstream_cmp );
|
||||||
if ( !removed ) {
|
if ( !removed ) {
|
||||||
assert( upstream->c_state != LLOAD_C_BINDING );
|
assert( upstream->c_state != LLOAD_C_BINDING );
|
||||||
/* FIXME: has client replaced this bind since? */
|
/* FIXME: has client replaced this bind since? */
|
||||||
assert(0);
|
assert(0);
|
||||||
|
|
||||||
operation_destroy_from_upstream( op );
|
|
||||||
}
|
}
|
||||||
assert( removed == op && upstream->c_state == LLOAD_C_BINDING );
|
assert( removed == op && upstream->c_state == LLOAD_C_BINDING );
|
||||||
|
|
||||||
CONNECTION_UNLOCK(upstream);
|
CONNECTION_UNLOCK(upstream);
|
||||||
|
|
||||||
Debug( LDAP_DEBUG_TRACE, "finish_sasl_bind: "
|
|
||||||
"SASL exchange in lieu of client connid=%lu to upstream "
|
|
||||||
"connid=%lu finished, resolving final authzid name\n",
|
|
||||||
op->o_client_connid, op->o_upstream_connid );
|
|
||||||
|
|
||||||
ldap_pvt_thread_mutex_lock( &upstream->c_io_mutex );
|
ldap_pvt_thread_mutex_lock( &upstream->c_io_mutex );
|
||||||
output = upstream->c_pendingber;
|
output = upstream->c_pendingber;
|
||||||
if ( output == NULL && (output = ber_alloc()) == NULL ) {
|
if ( output == NULL && (output = ber_alloc()) == NULL ) {
|
||||||
ldap_pvt_thread_mutex_unlock( &upstream->c_io_mutex );
|
ldap_pvt_thread_mutex_unlock( &upstream->c_io_mutex );
|
||||||
|
CONNECTION_LOCK_DESTROY(upstream);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
upstream->c_pendingber = output;
|
upstream->c_pendingber = output;
|
||||||
|
|
@ -564,12 +534,18 @@ finish_sasl_bind(
|
||||||
ber_free( op->o_ber, 1 );
|
ber_free( op->o_ber, 1 );
|
||||||
op->o_ber = ber;
|
op->o_ber = ber;
|
||||||
|
|
||||||
|
/* Could we have been unlinked in the meantime? */
|
||||||
rc = tavl_insert(
|
rc = tavl_insert(
|
||||||
&upstream->c_ops, op, operation_upstream_cmp, avl_dup_error );
|
&upstream->c_ops, op, operation_upstream_cmp, avl_dup_error );
|
||||||
assert( rc == LDAP_SUCCESS );
|
assert( rc == LDAP_SUCCESS );
|
||||||
|
|
||||||
CONNECTION_UNLOCK(upstream);
|
CONNECTION_UNLOCK(upstream);
|
||||||
|
|
||||||
|
Debug( LDAP_DEBUG_TRACE, "finish_sasl_bind: "
|
||||||
|
"SASL exchange in lieu of client connid=%lu to upstream "
|
||||||
|
"connid=%lu finished, resolving final authzid name msgid=%d\n",
|
||||||
|
op->o_client_connid, op->o_upstream_connid, op->o_upstream_msgid );
|
||||||
|
|
||||||
connection_write_cb( -1, 0, upstream );
|
connection_write_cb( -1, 0, upstream );
|
||||||
return LDAP_SUCCESS;
|
return LDAP_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
@ -580,7 +556,7 @@ handle_bind_response(
|
||||||
LloadOperation *op,
|
LloadOperation *op,
|
||||||
BerElement *ber )
|
BerElement *ber )
|
||||||
{
|
{
|
||||||
LloadConnection *upstream = op->o_upstream;
|
LloadConnection *upstream;
|
||||||
BerValue response;
|
BerValue response;
|
||||||
BerElement *copy;
|
BerElement *copy;
|
||||||
LloadOperation *removed;
|
LloadOperation *removed;
|
||||||
|
|
@ -611,6 +587,13 @@ handle_bind_response(
|
||||||
"connid=%lu, result=%d\n",
|
"connid=%lu, result=%d\n",
|
||||||
op->o_client_msgid, op->o_client_connid, result );
|
op->o_client_msgid, op->o_client_connid, result );
|
||||||
|
|
||||||
|
ldap_pvt_thread_mutex_lock( &op->o_link_mutex );
|
||||||
|
upstream = op->o_upstream;
|
||||||
|
ldap_pvt_thread_mutex_unlock( &op->o_link_mutex );
|
||||||
|
if ( !upstream ) {
|
||||||
|
return LDAP_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
CONNECTION_LOCK(upstream);
|
CONNECTION_LOCK(upstream);
|
||||||
if ( !tavl_find( upstream->c_ops, op, operation_upstream_cmp ) ) {
|
if ( !tavl_find( upstream->c_ops, op, operation_upstream_cmp ) ) {
|
||||||
/*
|
/*
|
||||||
|
|
@ -621,7 +604,6 @@ handle_bind_response(
|
||||||
* no response is expected
|
* no response is expected
|
||||||
* - ???
|
* - ???
|
||||||
*/
|
*/
|
||||||
operation_destroy_from_upstream( op );
|
|
||||||
CONNECTION_UNLOCK(upstream);
|
CONNECTION_UNLOCK(upstream);
|
||||||
return LDAP_SUCCESS;
|
return LDAP_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
@ -650,7 +632,6 @@ handle_bind_response(
|
||||||
} else if ( result == LDAP_SASL_BIND_IN_PROGRESS ) {
|
} else if ( result == LDAP_SASL_BIND_IN_PROGRESS ) {
|
||||||
tavl_delete( &upstream->c_ops, op, operation_upstream_cmp );
|
tavl_delete( &upstream->c_ops, op, operation_upstream_cmp );
|
||||||
op->o_upstream_msgid = 0;
|
op->o_upstream_msgid = 0;
|
||||||
op->o_upstream_refcnt++;
|
|
||||||
rc = tavl_insert(
|
rc = tavl_insert(
|
||||||
&upstream->c_ops, op, operation_upstream_cmp, avl_dup_error );
|
&upstream->c_ops, op, operation_upstream_cmp, avl_dup_error );
|
||||||
assert( rc == LDAP_SUCCESS );
|
assert( rc == LDAP_SUCCESS );
|
||||||
|
|
@ -665,13 +646,18 @@ handle_bind_response(
|
||||||
assert( op->o_client_msgid && op->o_upstream_msgid );
|
assert( op->o_client_msgid && op->o_upstream_msgid );
|
||||||
op->o_pin_id = 0;
|
op->o_pin_id = 0;
|
||||||
|
|
||||||
if ( sasl_finished && result == LDAP_SUCCESS ) {
|
if ( (lload_features & LLOAD_FEATURE_PROXYAUTHZ) && sasl_finished &&
|
||||||
|
result == LDAP_SUCCESS ) {
|
||||||
return finish_sasl_bind( upstream, op, ber );
|
return finish_sasl_bind( upstream, op, ber );
|
||||||
}
|
}
|
||||||
upstream->c_state = LLOAD_C_READY;
|
op->o_res = LLOAD_OP_COMPLETED;
|
||||||
}
|
}
|
||||||
CONNECTION_UNLOCK(upstream);
|
CONNECTION_UNLOCK(upstream);
|
||||||
|
|
||||||
|
if ( !op->o_pin_id ) {
|
||||||
|
operation_unlink_upstream( op, upstream );
|
||||||
|
}
|
||||||
|
|
||||||
CONNECTION_LOCK(client);
|
CONNECTION_LOCK(client);
|
||||||
removed = tavl_delete( &client->c_ops, op, operation_client_cmp );
|
removed = tavl_delete( &client->c_ops, op, operation_client_cmp );
|
||||||
assert( !removed || op == removed );
|
assert( !removed || op == removed );
|
||||||
|
|
@ -687,7 +673,6 @@ handle_bind_response(
|
||||||
break;
|
break;
|
||||||
case LDAP_SUCCESS:
|
case LDAP_SUCCESS:
|
||||||
default: {
|
default: {
|
||||||
op->o_client = NULL;
|
|
||||||
client->c_state = LLOAD_C_READY;
|
client->c_state = LLOAD_C_READY;
|
||||||
client->c_type = LLOAD_C_OPEN;
|
client->c_type = LLOAD_C_OPEN;
|
||||||
client->c_pin_id = 0;
|
client->c_pin_id = 0;
|
||||||
|
|
@ -708,7 +693,7 @@ handle_bind_response(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
assert( client->c_state == LLOAD_C_INVALID ||
|
assert( client->c_state == LLOAD_C_DYING ||
|
||||||
client->c_state == LLOAD_C_CLOSING );
|
client->c_state == LLOAD_C_CLOSING );
|
||||||
}
|
}
|
||||||
CONNECTION_UNLOCK(client);
|
CONNECTION_UNLOCK(client);
|
||||||
|
|
@ -729,7 +714,7 @@ handle_whoami_response(
|
||||||
LloadOperation *op,
|
LloadOperation *op,
|
||||||
BerElement *ber )
|
BerElement *ber )
|
||||||
{
|
{
|
||||||
LloadConnection *upstream = op->o_upstream;
|
LloadConnection *upstream;
|
||||||
BerValue matched, diagmsg;
|
BerValue matched, diagmsg;
|
||||||
BerElement *saved_response = op->o_ber;
|
BerElement *saved_response = op->o_ber;
|
||||||
LloadOperation *removed;
|
LloadOperation *removed;
|
||||||
|
|
@ -739,7 +724,7 @@ handle_whoami_response(
|
||||||
|
|
||||||
Debug( LDAP_DEBUG_TRACE, "handle_whoami_response: "
|
Debug( LDAP_DEBUG_TRACE, "handle_whoami_response: "
|
||||||
"connid=%ld received whoami response in lieu of connid=%ld\n",
|
"connid=%ld received whoami response in lieu of connid=%ld\n",
|
||||||
upstream->c_connid, client->c_connid );
|
op->o_upstream_connid, client->c_connid );
|
||||||
|
|
||||||
tag = ber_scanf( ber, "{emm" /* "}" */,
|
tag = ber_scanf( ber, "{emm" /* "}" */,
|
||||||
&result, &matched, &diagmsg );
|
&result, &matched, &diagmsg );
|
||||||
|
|
@ -748,38 +733,40 @@ handle_whoami_response(
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
CONNECTION_LOCK_DECREF(upstream);
|
ldap_pvt_thread_mutex_lock( &op->o_link_mutex );
|
||||||
|
upstream = op->o_upstream;
|
||||||
|
ldap_pvt_thread_mutex_unlock( &op->o_link_mutex );
|
||||||
|
if ( !upstream ) {
|
||||||
|
return LDAP_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
op->o_res = LLOAD_OP_COMPLETED;
|
||||||
|
/* Clear upstream status */
|
||||||
|
operation_unlink_upstream( op, upstream );
|
||||||
|
|
||||||
if ( result == LDAP_PROTOCOL_ERROR ) {
|
if ( result == LDAP_PROTOCOL_ERROR ) {
|
||||||
LloadBackend *b;
|
LloadBackend *b;
|
||||||
|
|
||||||
|
CONNECTION_LOCK(upstream);
|
||||||
b = (LloadBackend *)upstream->c_private;
|
b = (LloadBackend *)upstream->c_private;
|
||||||
Debug( LDAP_DEBUG_ANY, "handle_whoami_response: "
|
Debug( LDAP_DEBUG_ANY, "handle_whoami_response: "
|
||||||
"Who Am I? extended operation not supported on backend %s, "
|
"Who Am I? extended operation not supported on backend %s, "
|
||||||
"proxyauthz with clients that do SASL binds will not work "
|
"proxyauthz with clients that do SASL binds will not work "
|
||||||
"msg=%s!\n",
|
"msg=%s!\n",
|
||||||
b->b_uri.bv_val, diagmsg.bv_val );
|
b->b_uri.bv_val, diagmsg.bv_val );
|
||||||
CONNECTION_UNLOCK_INCREF(upstream);
|
CONNECTION_UNLOCK(upstream);
|
||||||
operation_send_reject( op, LDAP_OTHER, "upstream protocol error", 0 );
|
operation_send_reject( op, LDAP_OTHER, "upstream protocol error", 0 );
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( upstream->c_state != LLOAD_C_CLOSING ) {
|
|
||||||
assert( upstream->c_state == LLOAD_C_BINDING );
|
|
||||||
upstream->c_state = LLOAD_C_READY;
|
|
||||||
}
|
|
||||||
if ( !BER_BVISNULL( &upstream->c_sasl_bind_mech ) ) {
|
|
||||||
ber_memfree( upstream->c_sasl_bind_mech.bv_val );
|
|
||||||
BER_BVZERO( &upstream->c_sasl_bind_mech );
|
|
||||||
}
|
|
||||||
|
|
||||||
CONNECTION_UNLOCK_INCREF(upstream);
|
|
||||||
|
|
||||||
tag = ber_peek_tag( ber, &len );
|
tag = ber_peek_tag( ber, &len );
|
||||||
|
|
||||||
CONNECTION_LOCK_DECREF(client);
|
CONNECTION_LOCK(client);
|
||||||
|
|
||||||
assert( client->c_state == LLOAD_C_BINDING &&
|
assert( client->c_state == LLOAD_C_BINDING ||
|
||||||
BER_BVISNULL( &client->c_auth ) );
|
client->c_state == LLOAD_C_CLOSING );
|
||||||
|
|
||||||
|
assert( BER_BVISNULL( &client->c_auth ) );
|
||||||
if ( !BER_BVISNULL( &client->c_auth ) ) {
|
if ( !BER_BVISNULL( &client->c_auth ) ) {
|
||||||
ber_memfree( client->c_auth.bv_val );
|
ber_memfree( client->c_auth.bv_val );
|
||||||
BER_BVZERO( &client->c_auth );
|
BER_BVZERO( &client->c_auth );
|
||||||
|
|
@ -788,8 +775,6 @@ handle_whoami_response(
|
||||||
if ( tag == LDAP_TAG_EXOP_RES_VALUE ) {
|
if ( tag == LDAP_TAG_EXOP_RES_VALUE ) {
|
||||||
tag = ber_scanf( ber, "o", &client->c_auth );
|
tag = ber_scanf( ber, "o", &client->c_auth );
|
||||||
if ( tag == LBER_ERROR ) {
|
if ( tag == LBER_ERROR ) {
|
||||||
operation_send_reject_locked(
|
|
||||||
op, LDAP_OTHER, "upstream protocol error", 0 );
|
|
||||||
CONNECTION_DESTROY(client);
|
CONNECTION_DESTROY(client);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
@ -797,13 +782,13 @@ handle_whoami_response(
|
||||||
|
|
||||||
removed = tavl_delete( &client->c_ops, op, operation_client_cmp );
|
removed = tavl_delete( &client->c_ops, op, operation_client_cmp );
|
||||||
assert( !removed || op == removed );
|
assert( !removed || op == removed );
|
||||||
|
op->o_pin_id = 0;
|
||||||
|
|
||||||
Debug( LDAP_DEBUG_TRACE, "handle_whoami_response: "
|
Debug( LDAP_DEBUG_TRACE, "handle_whoami_response: "
|
||||||
"connid=%ld new authid=%s\n",
|
"connid=%ld new authid=%s\n",
|
||||||
client->c_connid, client->c_auth.bv_val );
|
client->c_connid, client->c_auth.bv_val );
|
||||||
|
|
||||||
if ( client->c_state == LLOAD_C_BINDING ) {
|
if ( client->c_state == LLOAD_C_BINDING ) {
|
||||||
op->o_client = NULL;
|
|
||||||
client->c_state = LLOAD_C_READY;
|
client->c_state = LLOAD_C_READY;
|
||||||
client->c_type = LLOAD_C_OPEN;
|
client->c_type = LLOAD_C_OPEN;
|
||||||
client->c_pin_id = 0;
|
client->c_pin_id = 0;
|
||||||
|
|
@ -817,10 +802,11 @@ handle_whoami_response(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CONNECTION_UNLOCK_INCREF(client);
|
CONNECTION_UNLOCK(client);
|
||||||
|
|
||||||
/* defer the disposal of ber to operation_destroy_* */
|
/* defer the disposal of ber to operation_destroy */
|
||||||
op->o_ber = ber;
|
op->o_ber = ber;
|
||||||
|
|
||||||
return forward_final_response( client, op, saved_response );
|
return forward_final_response( client, op, saved_response );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -847,7 +833,12 @@ handle_vc_bind_response(
|
||||||
|
|
||||||
tag = ber_peek_tag( ber, &len );
|
tag = ber_peek_tag( ber, &len );
|
||||||
if ( result == LDAP_PROTOCOL_ERROR ) {
|
if ( result == LDAP_PROTOCOL_ERROR ) {
|
||||||
LloadConnection *upstream = op->o_upstream;
|
LloadConnection *upstream;
|
||||||
|
|
||||||
|
ldap_pvt_thread_mutex_lock( &op->o_link_mutex );
|
||||||
|
upstream = op->o_upstream;
|
||||||
|
ldap_pvt_thread_mutex_unlock( &op->o_link_mutex );
|
||||||
|
if ( upstream ) {
|
||||||
LloadBackend *b;
|
LloadBackend *b;
|
||||||
|
|
||||||
CONNECTION_LOCK(upstream);
|
CONNECTION_LOCK(upstream);
|
||||||
|
|
@ -857,6 +848,7 @@ handle_vc_bind_response(
|
||||||
b->b_uri.bv_val );
|
b->b_uri.bv_val );
|
||||||
CONNECTION_UNLOCK(upstream);
|
CONNECTION_UNLOCK(upstream);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Debug( LDAP_DEBUG_STATS, "handle_vc_bind_response: "
|
Debug( LDAP_DEBUG_STATS, "handle_vc_bind_response: "
|
||||||
"received response for bind request msgid=%d by client "
|
"received response for bind request msgid=%d by client "
|
||||||
|
|
@ -872,7 +864,7 @@ handle_vc_bind_response(
|
||||||
tag = ber_scanf( ber, "o", &client->c_vc_cookie );
|
tag = ber_scanf( ber, "o", &client->c_vc_cookie );
|
||||||
if ( tag == LBER_ERROR ) {
|
if ( tag == LBER_ERROR ) {
|
||||||
rc = -1;
|
rc = -1;
|
||||||
CONNECTION_UNLOCK_INCREF(client);
|
CONNECTION_UNLOCK(client);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
tag = ber_peek_tag( ber, &len );
|
tag = ber_peek_tag( ber, &len );
|
||||||
|
|
@ -882,7 +874,7 @@ handle_vc_bind_response(
|
||||||
tag = ber_scanf( ber, "m", &creds );
|
tag = ber_scanf( ber, "m", &creds );
|
||||||
if ( tag == LBER_ERROR ) {
|
if ( tag == LBER_ERROR ) {
|
||||||
rc = -1;
|
rc = -1;
|
||||||
CONNECTION_UNLOCK_INCREF(client);
|
CONNECTION_UNLOCK(client);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
tag = ber_peek_tag( ber, &len );
|
tag = ber_peek_tag( ber, &len );
|
||||||
|
|
@ -892,7 +884,7 @@ handle_vc_bind_response(
|
||||||
tag = ber_scanf( ber, "m", &controls );
|
tag = ber_scanf( ber, "m", &controls );
|
||||||
if ( tag == LBER_ERROR ) {
|
if ( tag == LBER_ERROR ) {
|
||||||
rc = -1;
|
rc = -1;
|
||||||
CONNECTION_UNLOCK_INCREF(client);
|
CONNECTION_UNLOCK(client);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -928,7 +920,7 @@ handle_vc_bind_response(
|
||||||
assert( client->c_state == LLOAD_C_INVALID ||
|
assert( client->c_state == LLOAD_C_INVALID ||
|
||||||
client->c_state == LLOAD_C_CLOSING );
|
client->c_state == LLOAD_C_CLOSING );
|
||||||
}
|
}
|
||||||
CONNECTION_UNLOCK_INCREF(client);
|
CONNECTION_UNLOCK(client);
|
||||||
|
|
||||||
ldap_pvt_thread_mutex_lock( &client->c_io_mutex );
|
ldap_pvt_thread_mutex_lock( &client->c_io_mutex );
|
||||||
output = client->c_pendingber;
|
output = client->c_pendingber;
|
||||||
|
|
@ -952,9 +944,7 @@ handle_vc_bind_response(
|
||||||
}
|
}
|
||||||
|
|
||||||
done:
|
done:
|
||||||
CONNECTION_LOCK_DECREF(client);
|
operation_unlink( op );
|
||||||
operation_destroy_from_client( op );
|
|
||||||
CONNECTION_UNLOCK_OR_DESTROY(client);
|
|
||||||
ber_free( ber, 1 );
|
ber_free( ber, 1 );
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,8 @@ lload_c_head clients = LDAP_CIRCLEQ_HEAD_INITIALIZER( clients );
|
||||||
|
|
||||||
ldap_pvt_thread_mutex_t clients_mutex;
|
ldap_pvt_thread_mutex_t clients_mutex;
|
||||||
|
|
||||||
|
static void client_unlink( LloadConnection *upstream );
|
||||||
|
|
||||||
int
|
int
|
||||||
request_abandon( LloadConnection *c, LloadOperation *op )
|
request_abandon( LloadConnection *c, LloadOperation *op )
|
||||||
{
|
{
|
||||||
|
|
@ -41,17 +43,19 @@ request_abandon( LloadConnection *c, LloadOperation *op )
|
||||||
"connid=%lu msgid=%d invalid integer sent in abandon request\n",
|
"connid=%lu msgid=%d invalid integer sent in abandon request\n",
|
||||||
c->c_connid, op->o_client_msgid );
|
c->c_connid, op->o_client_msgid );
|
||||||
|
|
||||||
operation_destroy_from_client( op );
|
operation_unlink( op );
|
||||||
CONNECTION_DESTROY(c);
|
CONNECTION_LOCK_DESTROY(c);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CONNECTION_LOCK(c);
|
||||||
request = tavl_find( c->c_ops, &needle, operation_client_cmp );
|
request = tavl_find( c->c_ops, &needle, operation_client_cmp );
|
||||||
if ( !request ) {
|
if ( !request ) {
|
||||||
Debug( LDAP_DEBUG_STATS, "request_abandon: "
|
Debug( LDAP_DEBUG_STATS, "request_abandon: "
|
||||||
"connid=%lu msgid=%d requests abandon of an operation "
|
"connid=%lu msgid=%d requests abandon of an operation "
|
||||||
"msgid=%d not being processed anymore\n",
|
"msgid=%d not being processed anymore\n",
|
||||||
c->c_connid, op->o_client_msgid, needle.o_client_msgid );
|
c->c_connid, op->o_client_msgid, needle.o_client_msgid );
|
||||||
|
CONNECTION_UNLOCK(c);
|
||||||
goto done;
|
goto done;
|
||||||
} else if ( request->o_tag == LDAP_REQ_BIND ) {
|
} else if ( request->o_tag == LDAP_REQ_BIND ) {
|
||||||
/* RFC 4511 states we must not allow Abandon on Binds */
|
/* RFC 4511 states we must not allow Abandon on Binds */
|
||||||
|
|
@ -59,6 +63,7 @@ request_abandon( LloadConnection *c, LloadOperation *op )
|
||||||
"connid=%lu msgid=%d requests abandon of a bind operation "
|
"connid=%lu msgid=%d requests abandon of a bind operation "
|
||||||
"msgid=%d\n",
|
"msgid=%d\n",
|
||||||
c->c_connid, op->o_client_msgid, needle.o_client_msgid );
|
c->c_connid, op->o_client_msgid, needle.o_client_msgid );
|
||||||
|
CONNECTION_UNLOCK(c);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
Debug( LDAP_DEBUG_STATS, "request_abandon: "
|
Debug( LDAP_DEBUG_STATS, "request_abandon: "
|
||||||
|
|
@ -67,20 +72,14 @@ request_abandon( LloadConnection *c, LloadOperation *op )
|
||||||
lload_msgtype2str( request->o_tag ), needle.o_client_msgid );
|
lload_msgtype2str( request->o_tag ), needle.o_client_msgid );
|
||||||
|
|
||||||
if ( c->c_state == LLOAD_C_BINDING ) {
|
if ( c->c_state == LLOAD_C_BINDING ) {
|
||||||
/* We have found the request and we are binding, it must be a bind
|
assert(0);
|
||||||
* request */
|
|
||||||
assert( request->o_tag == LDAP_REQ_BIND );
|
|
||||||
c->c_state = LLOAD_C_READY;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* operation_abandon requires a reference since it is passed with c unlocked */
|
CONNECTION_UNLOCK(c);
|
||||||
request->o_client_refcnt++;
|
|
||||||
CONNECTION_UNLOCK_INCREF(c);
|
|
||||||
operation_abandon( request );
|
operation_abandon( request );
|
||||||
CONNECTION_LOCK_DECREF(c);
|
|
||||||
|
|
||||||
done:
|
done:
|
||||||
operation_destroy_from_client( op );
|
operation_unlink( op );
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -92,9 +91,6 @@ request_process( LloadConnection *client, LloadOperation *op )
|
||||||
ber_int_t msgid;
|
ber_int_t msgid;
|
||||||
int res, rc = LDAP_SUCCESS;
|
int res, rc = LDAP_SUCCESS;
|
||||||
|
|
||||||
op->o_client_refcnt++;
|
|
||||||
CONNECTION_UNLOCK_INCREF(client);
|
|
||||||
|
|
||||||
upstream = backend_select( op, &res );
|
upstream = backend_select( op, &res );
|
||||||
if ( !upstream ) {
|
if ( !upstream ) {
|
||||||
Debug( LDAP_DEBUG_STATS, "request_process: "
|
Debug( LDAP_DEBUG_STATS, "request_process: "
|
||||||
|
|
@ -110,16 +106,29 @@ request_process( LloadConnection *client, LloadOperation *op )
|
||||||
|
|
||||||
output = upstream->c_pendingber;
|
output = upstream->c_pendingber;
|
||||||
if ( output == NULL && (output = ber_alloc()) == NULL ) {
|
if ( output == NULL && (output = ber_alloc()) == NULL ) {
|
||||||
|
LloadBackend *b = upstream->c_private;
|
||||||
|
|
||||||
|
upstream->c_n_ops_executing--;
|
||||||
|
CONNECTION_UNLOCK(upstream);
|
||||||
|
ldap_pvt_thread_mutex_unlock( &upstream->c_io_mutex );
|
||||||
|
|
||||||
|
ldap_pvt_thread_mutex_lock( &b->b_mutex );
|
||||||
|
b->b_n_ops_executing--;
|
||||||
|
operation_update_backend_counters( op, b );
|
||||||
|
ldap_pvt_thread_mutex_unlock( &b->b_mutex );
|
||||||
|
|
||||||
|
Debug( LDAP_DEBUG_ANY, "request_process: "
|
||||||
|
"ber_alloc failed\n" );
|
||||||
|
|
||||||
rc = -1;
|
rc = -1;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
upstream->c_pendingber = output;
|
upstream->c_pendingber = output;
|
||||||
|
|
||||||
CONNECTION_LOCK_DECREF(upstream);
|
|
||||||
op->o_upstream_msgid = msgid = upstream->c_next_msgid++;
|
op->o_upstream_msgid = msgid = upstream->c_next_msgid++;
|
||||||
rc = tavl_insert(
|
rc = tavl_insert(
|
||||||
&upstream->c_ops, op, operation_upstream_cmp, avl_dup_error );
|
&upstream->c_ops, op, operation_upstream_cmp, avl_dup_error );
|
||||||
CONNECTION_UNLOCK_INCREF(upstream);
|
CONNECTION_UNLOCK(upstream);
|
||||||
|
|
||||||
Debug( LDAP_DEBUG_TRACE, "request_process: "
|
Debug( LDAP_DEBUG_TRACE, "request_process: "
|
||||||
"client connid=%lu added %s msgid=%d to upstream connid=%lu as "
|
"client connid=%lu added %s msgid=%d to upstream connid=%lu as "
|
||||||
|
|
@ -132,7 +141,7 @@ request_process( LloadConnection *client, LloadOperation *op )
|
||||||
|
|
||||||
if ( (lload_features & LLOAD_FEATURE_PROXYAUTHZ) &&
|
if ( (lload_features & LLOAD_FEATURE_PROXYAUTHZ) &&
|
||||||
client->c_type != LLOAD_C_PRIVILEGED ) {
|
client->c_type != LLOAD_C_PRIVILEGED ) {
|
||||||
CONNECTION_LOCK_DECREF(client);
|
CONNECTION_LOCK(client);
|
||||||
Debug( LDAP_DEBUG_TRACE, "request_process: "
|
Debug( LDAP_DEBUG_TRACE, "request_process: "
|
||||||
"proxying identity %s to upstream\n",
|
"proxying identity %s to upstream\n",
|
||||||
client->c_auth.bv_val );
|
client->c_auth.bv_val );
|
||||||
|
|
@ -141,7 +150,7 @@ request_process( LloadConnection *client, LloadOperation *op )
|
||||||
op->o_tag, &op->o_request,
|
op->o_tag, &op->o_request,
|
||||||
LDAP_TAG_CONTROLS,
|
LDAP_TAG_CONTROLS,
|
||||||
LDAP_CONTROL_PROXY_AUTHZ, 1, &client->c_auth );
|
LDAP_CONTROL_PROXY_AUTHZ, 1, &client->c_auth );
|
||||||
CONNECTION_UNLOCK_INCREF(client);
|
CONNECTION_UNLOCK(client);
|
||||||
|
|
||||||
if ( !BER_BVISNULL( &op->o_ctrls ) ) {
|
if ( !BER_BVISNULL( &op->o_ctrls ) ) {
|
||||||
ber_write( output, op->o_ctrls.bv_val, op->o_ctrls.bv_len, 0 );
|
ber_write( output, op->o_ctrls.bv_val, op->o_ctrls.bv_len, 0 );
|
||||||
|
|
@ -157,37 +166,18 @@ request_process( LloadConnection *client, LloadOperation *op )
|
||||||
ldap_pvt_thread_mutex_unlock( &upstream->c_io_mutex );
|
ldap_pvt_thread_mutex_unlock( &upstream->c_io_mutex );
|
||||||
|
|
||||||
connection_write_cb( -1, 0, upstream );
|
connection_write_cb( -1, 0, upstream );
|
||||||
|
|
||||||
CONNECTION_LOCK_DECREF(upstream);
|
|
||||||
CONNECTION_UNLOCK_OR_DESTROY(upstream);
|
|
||||||
|
|
||||||
CONNECTION_LOCK_DECREF(client);
|
|
||||||
if ( !--op->o_client_refcnt ) {
|
|
||||||
operation_destroy_from_client( op );
|
|
||||||
}
|
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
if ( upstream ) {
|
if ( upstream ) {
|
||||||
LloadBackend *b;
|
CONNECTION_LOCK_DESTROY(upstream);
|
||||||
|
|
||||||
ldap_pvt_thread_mutex_unlock( &upstream->c_io_mutex );
|
|
||||||
CONNECTION_LOCK_DECREF(upstream);
|
|
||||||
upstream->c_n_ops_executing--;
|
|
||||||
b = (LloadBackend *)upstream->c_private;
|
|
||||||
CONNECTION_UNLOCK_OR_DESTROY(upstream);
|
|
||||||
|
|
||||||
ldap_pvt_thread_mutex_lock( &b->b_mutex );
|
|
||||||
b->b_n_ops_executing--;
|
|
||||||
ldap_pvt_thread_mutex_unlock( &b->b_mutex );
|
|
||||||
|
|
||||||
operation_send_reject( op, LDAP_OTHER, "internal error", 0 );
|
operation_send_reject( op, LDAP_OTHER, "internal error", 0 );
|
||||||
}
|
}
|
||||||
CONNECTION_LOCK_DECREF(client);
|
|
||||||
op->o_client_refcnt--;
|
operation_unlink( op );
|
||||||
operation_destroy_from_client( op );
|
|
||||||
if ( rc ) {
|
if ( rc ) {
|
||||||
CONNECTION_DESTROY(client);
|
CONNECTION_LOCK_DESTROY(client);
|
||||||
}
|
}
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
@ -202,6 +192,7 @@ handle_one_request( LloadConnection *c )
|
||||||
ber = c->c_currentber;
|
ber = c->c_currentber;
|
||||||
c->c_currentber = NULL;
|
c->c_currentber = NULL;
|
||||||
|
|
||||||
|
CONNECTION_LOCK(c);
|
||||||
op = operation_init( c, ber );
|
op = operation_init( c, ber );
|
||||||
if ( !op ) {
|
if ( !op ) {
|
||||||
Debug( LDAP_DEBUG_ANY, "handle_one_request: "
|
Debug( LDAP_DEBUG_ANY, "handle_one_request: "
|
||||||
|
|
@ -211,16 +202,18 @@ handle_one_request( LloadConnection *c )
|
||||||
ber_free( ber, 1 );
|
ber_free( ber, 1 );
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
CONNECTION_UNLOCK(c);
|
||||||
|
|
||||||
switch ( op->o_tag ) {
|
switch ( op->o_tag ) {
|
||||||
case LDAP_REQ_UNBIND:
|
case LDAP_REQ_UNBIND:
|
||||||
/* There is never a response for this operation */
|
/* There is never a response for this operation */
|
||||||
op->o_res = LLOAD_OP_COMPLETED;
|
op->o_res = LLOAD_OP_COMPLETED;
|
||||||
operation_destroy_from_client( op );
|
operation_unlink( op );
|
||||||
|
|
||||||
Debug( LDAP_DEBUG_STATS, "handle_one_request: "
|
Debug( LDAP_DEBUG_STATS, "handle_one_request: "
|
||||||
"received unbind, closing client connid=%lu\n",
|
"received unbind, closing client connid=%lu\n",
|
||||||
c->c_connid );
|
c->c_connid );
|
||||||
CONNECTION_DESTROY(c);
|
CONNECTION_LOCK_DESTROY(c);
|
||||||
return -1;
|
return -1;
|
||||||
case LDAP_REQ_BIND:
|
case LDAP_REQ_BIND:
|
||||||
handler = request_bind;
|
handler = request_bind;
|
||||||
|
|
@ -234,16 +227,18 @@ handle_one_request( LloadConnection *c )
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if ( c->c_state == LLOAD_C_BINDING ) {
|
if ( c->c_state == LLOAD_C_BINDING ) {
|
||||||
return operation_send_reject_locked(
|
operation_send_reject(
|
||||||
op, LDAP_PROTOCOL_ERROR, "bind in progress", 0 );
|
op, LDAP_PROTOCOL_ERROR, "bind in progress", 0 );
|
||||||
|
return LDAP_SUCCESS;
|
||||||
}
|
}
|
||||||
handler = request_process;
|
handler = request_process;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( c->c_state == LLOAD_C_CLOSING ) {
|
if ( c->c_state == LLOAD_C_CLOSING ) {
|
||||||
return operation_send_reject_locked(
|
operation_send_reject(
|
||||||
op, LDAP_UNAVAILABLE, "connection is shutting down", 0 );
|
op, LDAP_UNAVAILABLE, "connection is shutting down", 0 );
|
||||||
|
return LDAP_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
return handler( c, op );
|
return handler( c, op );
|
||||||
|
|
@ -256,9 +251,9 @@ void
|
||||||
client_tls_handshake_cb( evutil_socket_t s, short what, void *arg )
|
client_tls_handshake_cb( evutil_socket_t s, short what, void *arg )
|
||||||
{
|
{
|
||||||
LloadConnection *c = arg;
|
LloadConnection *c = arg;
|
||||||
|
epoch_t epoch;
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
|
|
||||||
CONNECTION_LOCK_DECREF(c);
|
|
||||||
if ( what & EV_TIMEOUT ) {
|
if ( what & EV_TIMEOUT ) {
|
||||||
Debug( LDAP_DEBUG_CONNS, "client_tls_handshake_cb: "
|
Debug( LDAP_DEBUG_CONNS, "client_tls_handshake_cb: "
|
||||||
"connid=%lu, timeout reached, destroying\n",
|
"connid=%lu, timeout reached, destroying\n",
|
||||||
|
|
@ -274,27 +269,26 @@ client_tls_handshake_cb( evutil_socket_t s, short what, void *arg )
|
||||||
ldap_pvt_thread_mutex_lock( &c->c_io_mutex );
|
ldap_pvt_thread_mutex_lock( &c->c_io_mutex );
|
||||||
if ( c->c_pendingber ) {
|
if ( c->c_pendingber ) {
|
||||||
ldap_pvt_thread_mutex_unlock( &c->c_io_mutex );
|
ldap_pvt_thread_mutex_unlock( &c->c_io_mutex );
|
||||||
CONNECTION_UNLOCK_INCREF(c);
|
|
||||||
connection_write_cb( s, what, arg );
|
connection_write_cb( s, what, arg );
|
||||||
ldap_pvt_thread_mutex_lock( &c->c_io_mutex );
|
|
||||||
CONNECTION_LOCK_DECREF(c);
|
|
||||||
|
|
||||||
|
CONNECTION_LOCK(c);
|
||||||
if ( !c->c_live ) {
|
if ( !c->c_live ) {
|
||||||
ldap_pvt_thread_mutex_unlock( &c->c_io_mutex );
|
CONNECTION_UNLOCK(c);
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
CONNECTION_UNLOCK(c);
|
||||||
|
|
||||||
/* Do we still have data pending? If so, connection_write_cb would
|
/* Do we still have data pending? If so, connection_write_cb would
|
||||||
* already have arranged the write callback to trigger again */
|
* already have arranged the write callback to trigger again */
|
||||||
|
ldap_pvt_thread_mutex_lock( &c->c_io_mutex );
|
||||||
if ( c->c_pendingber ) {
|
if ( c->c_pendingber ) {
|
||||||
ldap_pvt_thread_mutex_unlock( &c->c_io_mutex );
|
ldap_pvt_thread_mutex_unlock( &c->c_io_mutex );
|
||||||
CONNECTION_UNLOCK_INCREF(c);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ldap_pvt_thread_mutex_unlock( &c->c_io_mutex );
|
|
||||||
|
|
||||||
rc = ldap_pvt_tls_accept( c->c_sb, LLOAD_TLS_CTX );
|
rc = ldap_pvt_tls_accept( c->c_sb, LLOAD_TLS_CTX );
|
||||||
|
ldap_pvt_thread_mutex_unlock( &c->c_io_mutex );
|
||||||
if ( rc < 0 ) {
|
if ( rc < 0 ) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
@ -308,13 +302,16 @@ client_tls_handshake_cb( evutil_socket_t s, short what, void *arg )
|
||||||
* This is deadlock-safe, since both share the same base - the one
|
* This is deadlock-safe, since both share the same base - the one
|
||||||
* that's just running us.
|
* that's just running us.
|
||||||
*/
|
*/
|
||||||
|
CONNECTION_LOCK(c);
|
||||||
event_del( c->c_read_event );
|
event_del( c->c_read_event );
|
||||||
event_del( c->c_write_event );
|
event_del( c->c_write_event );
|
||||||
|
|
||||||
c->c_read_timeout = NULL;
|
c->c_read_timeout = NULL;
|
||||||
event_assign( c->c_read_event, base, c->c_fd, EV_READ|EV_PERSIST,
|
event_assign( c->c_read_event, base, c->c_fd, EV_READ|EV_PERSIST,
|
||||||
connection_read_cb, c );
|
connection_read_cb, c );
|
||||||
|
if ( c->c_live ) {
|
||||||
event_add( c->c_read_event, c->c_read_timeout );
|
event_add( c->c_read_event, c->c_read_timeout );
|
||||||
|
}
|
||||||
|
|
||||||
event_assign( c->c_write_event, base, c->c_fd, EV_WRITE,
|
event_assign( c->c_write_event, base, c->c_fd, EV_WRITE,
|
||||||
connection_write_cb, c );
|
connection_write_cb, c );
|
||||||
|
|
@ -323,24 +320,29 @@ client_tls_handshake_cb( evutil_socket_t s, short what, void *arg )
|
||||||
c->c_connid );
|
c->c_connid );
|
||||||
|
|
||||||
c->c_is_tls = LLOAD_TLS_ESTABLISHED;
|
c->c_is_tls = LLOAD_TLS_ESTABLISHED;
|
||||||
|
CONNECTION_UNLOCK(c);
|
||||||
/* The temporary reference established for us is no longer needed */
|
|
||||||
CONNECTION_UNLOCK_OR_DESTROY(c);
|
|
||||||
return;
|
return;
|
||||||
} else if ( ber_sockbuf_ctrl( c->c_sb, LBER_SB_OPT_NEEDS_WRITE, NULL ) ) {
|
} else if ( ber_sockbuf_ctrl( c->c_sb, LBER_SB_OPT_NEEDS_WRITE, NULL ) ) {
|
||||||
|
CONNECTION_LOCK(c);
|
||||||
|
if ( c->c_live ) {
|
||||||
event_add( c->c_write_event, lload_write_timeout );
|
event_add( c->c_write_event, lload_write_timeout );
|
||||||
|
}
|
||||||
|
CONNECTION_UNLOCK(c);
|
||||||
Debug( LDAP_DEBUG_CONNS, "client_tls_handshake_cb: "
|
Debug( LDAP_DEBUG_CONNS, "client_tls_handshake_cb: "
|
||||||
"connid=%lu need write rc=%d\n",
|
"connid=%lu need write rc=%d\n",
|
||||||
c->c_connid, rc );
|
c->c_connid, rc );
|
||||||
}
|
}
|
||||||
CONNECTION_UNLOCK_INCREF(c);
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
Debug( LDAP_DEBUG_CONNS, "client_tls_handshake_cb: "
|
Debug( LDAP_DEBUG_CONNS, "client_tls_handshake_cb: "
|
||||||
"connid=%lu failed rc=%d\n",
|
"connid=%lu failed rc=%d\n",
|
||||||
c->c_connid, rc );
|
c->c_connid, rc );
|
||||||
CONNECTION_DESTROY(c);
|
|
||||||
|
assert( c->c_ops == NULL );
|
||||||
|
epoch = epoch_join();
|
||||||
|
CONNECTION_LOCK_DESTROY(c);
|
||||||
|
epoch_leave( epoch );
|
||||||
}
|
}
|
||||||
|
|
||||||
LloadConnection *
|
LloadConnection *
|
||||||
|
|
@ -379,11 +381,11 @@ client_init(
|
||||||
Debug( LDAP_DEBUG_CONNS, "client_init: "
|
Debug( LDAP_DEBUG_CONNS, "client_init: "
|
||||||
"connid=%lu failed initial TLS accept rc=%d\n",
|
"connid=%lu failed initial TLS accept rc=%d\n",
|
||||||
c->c_connid, rc );
|
c->c_connid, rc );
|
||||||
|
CONNECTION_LOCK(c);
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( rc ) {
|
if ( rc ) {
|
||||||
c->c_refcnt++;
|
|
||||||
c->c_read_timeout = lload_timeout_net;
|
c->c_read_timeout = lload_timeout_net;
|
||||||
read_cb = write_cb = client_tls_handshake_cb;
|
read_cb = write_cb = client_tls_handshake_cb;
|
||||||
}
|
}
|
||||||
|
|
@ -393,30 +395,32 @@ client_init(
|
||||||
if ( !event ) {
|
if ( !event ) {
|
||||||
Debug( LDAP_DEBUG_ANY, "client_init: "
|
Debug( LDAP_DEBUG_ANY, "client_init: "
|
||||||
"Read event could not be allocated\n" );
|
"Read event could not be allocated\n" );
|
||||||
|
CONNECTION_LOCK(c);
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
c->c_read_event = event;
|
c->c_read_event = event;
|
||||||
event_add( c->c_read_event, c->c_read_timeout );
|
|
||||||
|
|
||||||
event = event_new( base, s, EV_WRITE, write_cb, c );
|
event = event_new( base, s, EV_WRITE, write_cb, c );
|
||||||
if ( !event ) {
|
if ( !event ) {
|
||||||
Debug( LDAP_DEBUG_ANY, "client_init: "
|
Debug( LDAP_DEBUG_ANY, "client_init: "
|
||||||
"Write event could not be allocated\n" );
|
"Write event could not be allocated\n" );
|
||||||
|
CONNECTION_LOCK(c);
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
/* We only register the write event when we have data pending */
|
|
||||||
c->c_write_event = event;
|
c->c_write_event = event;
|
||||||
|
|
||||||
c->c_private = listener;
|
c->c_private = listener;
|
||||||
c->c_destroy = client_destroy;
|
c->c_destroy = client_destroy;
|
||||||
|
c->c_unlink = client_unlink;
|
||||||
c->c_pdu_cb = handle_one_request;
|
c->c_pdu_cb = handle_one_request;
|
||||||
|
|
||||||
/* There should be no lock inversion yet since no other thread could
|
CONNECTION_LOCK(c);
|
||||||
* approach it from clients side */
|
/* We only register the write event when we have data pending */
|
||||||
|
event_add( c->c_read_event, c->c_read_timeout );
|
||||||
|
|
||||||
ldap_pvt_thread_mutex_lock( &clients_mutex );
|
ldap_pvt_thread_mutex_lock( &clients_mutex );
|
||||||
LDAP_CIRCLEQ_INSERT_TAIL( &clients, c, c_next );
|
LDAP_CIRCLEQ_INSERT_TAIL( &clients, c, c_next );
|
||||||
ldap_pvt_thread_mutex_unlock( &clients_mutex );
|
ldap_pvt_thread_mutex_unlock( &clients_mutex );
|
||||||
|
|
||||||
CONNECTION_UNLOCK(c);
|
CONNECTION_UNLOCK(c);
|
||||||
|
|
||||||
return c;
|
return c;
|
||||||
|
|
@ -431,8 +435,9 @@ fail:
|
||||||
}
|
}
|
||||||
|
|
||||||
c->c_state = LLOAD_C_INVALID;
|
c->c_state = LLOAD_C_INVALID;
|
||||||
CONNECTION_DESTROY(c);
|
c->c_live--;
|
||||||
assert( c == NULL );
|
c->c_refcnt--;
|
||||||
|
connection_destroy( c );
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -444,19 +449,6 @@ client_reset( LloadConnection *c )
|
||||||
root = c->c_ops;
|
root = c->c_ops;
|
||||||
c->c_ops = NULL;
|
c->c_ops = NULL;
|
||||||
|
|
||||||
/* unless op->o_client_refcnt > op->o_client_live, there is noone using the
|
|
||||||
* operation from the client side and noone new will now that we've removed
|
|
||||||
* it from client's c_ops */
|
|
||||||
if ( root ) {
|
|
||||||
TAvlnode *node = tavl_end( root, TAVL_DIR_LEFT );
|
|
||||||
do {
|
|
||||||
LloadOperation *op = node->avl_data;
|
|
||||||
|
|
||||||
/* make sure it's useable after we've unlocked the connection */
|
|
||||||
op->o_client_refcnt++;
|
|
||||||
} while ( (node = tavl_next( node, TAVL_DIR_RIGHT )) );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( !BER_BVISNULL( &c->c_auth ) ) {
|
if ( !BER_BVISNULL( &c->c_auth ) ) {
|
||||||
ch_free( c->c_auth.bv_val );
|
ch_free( c->c_auth.bv_val );
|
||||||
BER_BVZERO( &c->c_auth );
|
BER_BVZERO( &c->c_auth );
|
||||||
|
|
@ -465,7 +457,7 @@ client_reset( LloadConnection *c )
|
||||||
ch_free( c->c_sasl_bind_mech.bv_val );
|
ch_free( c->c_sasl_bind_mech.bv_val );
|
||||||
BER_BVZERO( &c->c_sasl_bind_mech );
|
BER_BVZERO( &c->c_sasl_bind_mech );
|
||||||
}
|
}
|
||||||
CONNECTION_UNLOCK_INCREF(c);
|
CONNECTION_UNLOCK(c);
|
||||||
|
|
||||||
if ( root ) {
|
if ( root ) {
|
||||||
int freed;
|
int freed;
|
||||||
|
|
@ -475,38 +467,29 @@ client_reset( LloadConnection *c )
|
||||||
freed );
|
freed );
|
||||||
}
|
}
|
||||||
|
|
||||||
CONNECTION_LOCK_DECREF(c);
|
CONNECTION_LOCK(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
client_destroy( LloadConnection *c )
|
client_unlink( LloadConnection *c )
|
||||||
{
|
{
|
||||||
enum sc_state state;
|
enum sc_state state;
|
||||||
struct event *read_event, *write_event;
|
struct event *read_event, *write_event;
|
||||||
|
|
||||||
Debug( LDAP_DEBUG_CONNS, "client_destroy: "
|
Debug( LDAP_DEBUG_CONNS, "client_unlink: "
|
||||||
"destroying client connid=%lu\n",
|
"removing client connid=%lu\n",
|
||||||
c->c_connid );
|
c->c_connid );
|
||||||
|
|
||||||
assert( c->c_state != LLOAD_C_INVALID );
|
assert( c->c_state != LLOAD_C_INVALID );
|
||||||
|
assert( c->c_state != LLOAD_C_DYING );
|
||||||
|
|
||||||
state = c->c_state;
|
state = c->c_state;
|
||||||
c->c_state = LLOAD_C_INVALID;
|
c->c_state = LLOAD_C_DYING;
|
||||||
|
|
||||||
read_event = c->c_read_event;
|
read_event = c->c_read_event;
|
||||||
write_event = c->c_write_event;
|
write_event = c->c_write_event;
|
||||||
|
CONNECTION_UNLOCK(c);
|
||||||
|
|
||||||
/*
|
|
||||||
* FIXME: operation_destroy_from_upstream might copy op->o_client and bump
|
|
||||||
* c_refcnt, it is then responsible to call destroy_client again, does that
|
|
||||||
* mean that we can be triggered for recursion over all connections?
|
|
||||||
*/
|
|
||||||
CONNECTION_UNLOCK_INCREF(c);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Avoid a deadlock:
|
|
||||||
* event_del will block if the event is currently executing its callback,
|
|
||||||
* that callback might be waiting to lock c->c_mutex
|
|
||||||
*/
|
|
||||||
if ( read_event ) {
|
if ( read_event ) {
|
||||||
event_del( read_event );
|
event_del( read_event );
|
||||||
}
|
}
|
||||||
|
|
@ -521,7 +504,20 @@ client_destroy( LloadConnection *c )
|
||||||
ldap_pvt_thread_mutex_unlock( &clients_mutex );
|
ldap_pvt_thread_mutex_unlock( &clients_mutex );
|
||||||
}
|
}
|
||||||
|
|
||||||
CONNECTION_LOCK_DECREF(c);
|
CONNECTION_LOCK(c);
|
||||||
|
client_reset( c );
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
client_destroy( LloadConnection *c )
|
||||||
|
{
|
||||||
|
Debug( LDAP_DEBUG_CONNS, "client_destroy: "
|
||||||
|
"destroying client connid=%lu\n",
|
||||||
|
c->c_connid );
|
||||||
|
|
||||||
|
CONNECTION_LOCK(c);
|
||||||
|
assert( c->c_state == LLOAD_C_DYING );
|
||||||
|
c->c_state = LLOAD_C_INVALID;
|
||||||
|
|
||||||
if ( c->c_read_event ) {
|
if ( c->c_read_event ) {
|
||||||
event_free( c->c_read_event );
|
event_free( c->c_read_event );
|
||||||
|
|
@ -533,23 +529,7 @@ client_destroy( LloadConnection *c )
|
||||||
c->c_write_event = NULL;
|
c->c_write_event = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
client_reset( c );
|
assert( c->c_refcnt == 0 );
|
||||||
|
|
||||||
/*
|
|
||||||
* If we attempted to destroy any operations, we might have lent a new
|
|
||||||
* refcnt token for a thread that raced us to that, let them call us again
|
|
||||||
* later
|
|
||||||
*/
|
|
||||||
assert( c->c_refcnt >= 0 );
|
|
||||||
if ( c->c_refcnt ) {
|
|
||||||
c->c_state = LLOAD_C_DYING;
|
|
||||||
Debug( LDAP_DEBUG_CONNS, "client_destroy: "
|
|
||||||
"connid=%lu aborting with refcnt=%d\n",
|
|
||||||
c->c_connid, c->c_refcnt );
|
|
||||||
CONNECTION_UNLOCK(c);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
connection_destroy( c );
|
connection_destroy( c );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -41,15 +41,12 @@
|
||||||
#include "lutil.h"
|
#include "lutil.h"
|
||||||
#include "lutil_ldap.h"
|
#include "lutil_ldap.h"
|
||||||
|
|
||||||
static ldap_pvt_thread_mutex_t conn_nextid_mutex;
|
|
||||||
static unsigned long conn_nextid = 0;
|
static unsigned long conn_nextid = 0;
|
||||||
|
|
||||||
static void
|
static void
|
||||||
lload_connection_assign_nextid( LloadConnection *conn )
|
lload_connection_assign_nextid( LloadConnection *conn )
|
||||||
{
|
{
|
||||||
ldap_pvt_thread_mutex_lock( &conn_nextid_mutex );
|
conn->c_connid = __atomic_fetch_add( &conn_nextid, 1, __ATOMIC_RELAXED );
|
||||||
conn->c_connid = conn_nextid++;
|
|
||||||
ldap_pvt_thread_mutex_unlock( &conn_nextid_mutex );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -73,37 +70,40 @@ handle_pdus( void *ctx, void *arg )
|
||||||
{
|
{
|
||||||
LloadConnection *c = arg;
|
LloadConnection *c = arg;
|
||||||
int pdus_handled = 0;
|
int pdus_handled = 0;
|
||||||
|
epoch_t epoch;
|
||||||
|
|
||||||
CONNECTION_LOCK_DECREF(c);
|
/* A reference was passed on to us */
|
||||||
|
assert( IS_ALIVE( c, c_refcnt ) );
|
||||||
|
|
||||||
|
epoch = epoch_join();
|
||||||
for ( ;; ) {
|
for ( ;; ) {
|
||||||
BerElement *ber;
|
BerElement *ber;
|
||||||
ber_tag_t tag;
|
ber_tag_t tag;
|
||||||
ber_len_t len;
|
ber_len_t len;
|
||||||
|
|
||||||
/* handle_one_response may unlock the connection in the process, we
|
|
||||||
* need to expect that might be our responsibility to destroy it */
|
|
||||||
if ( c->c_pdu_cb( c ) ) {
|
if ( c->c_pdu_cb( c ) ) {
|
||||||
/* Error, connection is unlocked and might already have been
|
/* Error/reset, get rid ouf our reference and bail */
|
||||||
* destroyed */
|
goto done;
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
/* Otherwise, handle_one_request leaves the connection locked */
|
|
||||||
|
|
||||||
if ( ++pdus_handled >= lload_conn_max_pdus_per_cycle ) {
|
if ( ++pdus_handled >= lload_conn_max_pdus_per_cycle ) {
|
||||||
/* Do not read now, re-enable read event instead */
|
/* Do not read now, re-enable read event instead */
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( (ber = ber_alloc()) == NULL ) {
|
ber = c->c_currentber;
|
||||||
|
if ( ber == NULL && (ber = ber_alloc()) == NULL ) {
|
||||||
Debug( LDAP_DEBUG_ANY, "handle_pdus: "
|
Debug( LDAP_DEBUG_ANY, "handle_pdus: "
|
||||||
"connid=%lu, ber_alloc failed\n",
|
"connid=%lu, ber_alloc failed\n",
|
||||||
c->c_connid );
|
c->c_connid );
|
||||||
CONNECTION_DESTROY(c);
|
CONNECTION_LOCK_DESTROY(c);
|
||||||
return NULL;
|
goto done;
|
||||||
}
|
}
|
||||||
c->c_currentber = ber;
|
c->c_currentber = ber;
|
||||||
|
|
||||||
|
ldap_pvt_thread_mutex_lock( &c->c_io_mutex );
|
||||||
tag = ber_get_next( c->c_sb, &len, ber );
|
tag = ber_get_next( c->c_sb, &len, ber );
|
||||||
|
ldap_pvt_thread_mutex_unlock( &c->c_io_mutex );
|
||||||
if ( tag != LDAP_TAG_MESSAGE ) {
|
if ( tag != LDAP_TAG_MESSAGE ) {
|
||||||
int err = sock_errno();
|
int err = sock_errno();
|
||||||
|
|
||||||
|
|
@ -123,8 +123,8 @@ handle_pdus( void *ctx, void *arg )
|
||||||
|
|
||||||
c->c_currentber = NULL;
|
c->c_currentber = NULL;
|
||||||
ber_free( ber, 1 );
|
ber_free( ber, 1 );
|
||||||
CONNECTION_DESTROY(c);
|
CONNECTION_LOCK_DESTROY(c);
|
||||||
return NULL;
|
goto done;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -134,7 +134,9 @@ handle_pdus( void *ctx, void *arg )
|
||||||
Debug( LDAP_DEBUG_CONNS, "handle_pdus: "
|
Debug( LDAP_DEBUG_CONNS, "handle_pdus: "
|
||||||
"re-enabled read event on connid=%lu\n",
|
"re-enabled read event on connid=%lu\n",
|
||||||
c->c_connid );
|
c->c_connid );
|
||||||
CONNECTION_UNLOCK_OR_DESTROY(c);
|
done:
|
||||||
|
RELEASE_REF( c, c_refcnt, c->c_destroy );
|
||||||
|
epoch_leave( epoch );
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -152,25 +154,35 @@ connection_read_cb( evutil_socket_t s, short what, void *arg )
|
||||||
BerElement *ber;
|
BerElement *ber;
|
||||||
ber_tag_t tag;
|
ber_tag_t tag;
|
||||||
ber_len_t len;
|
ber_len_t len;
|
||||||
|
epoch_t epoch;
|
||||||
|
|
||||||
CONNECTION_LOCK(c);
|
CONNECTION_LOCK(c);
|
||||||
if ( !c->c_live ) {
|
if ( !c->c_live ) {
|
||||||
event_del( c->c_read_event );
|
event_del( c->c_read_event );
|
||||||
|
CONNECTION_UNLOCK(c);
|
||||||
Debug( LDAP_DEBUG_CONNS, "connection_read_cb: "
|
Debug( LDAP_DEBUG_CONNS, "connection_read_cb: "
|
||||||
"suspended read event on a dead connid=%lu\n",
|
"suspended read event on a dead connid=%lu\n",
|
||||||
c->c_connid );
|
c->c_connid );
|
||||||
CONNECTION_UNLOCK(c);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
CONNECTION_UNLOCK(c);
|
||||||
|
|
||||||
if ( what & EV_TIMEOUT ) {
|
if ( what & EV_TIMEOUT ) {
|
||||||
Debug( LDAP_DEBUG_CONNS, "connection_read_cb: "
|
Debug( LDAP_DEBUG_CONNS, "connection_read_cb: "
|
||||||
"connid=%lu, timeout reached, destroying\n",
|
"connid=%lu, timeout reached, destroying\n",
|
||||||
c->c_connid );
|
c->c_connid );
|
||||||
CONNECTION_DESTROY(c);
|
/* Make sure the connection stays around for us to unlock it */
|
||||||
|
epoch = epoch_join();
|
||||||
|
CONNECTION_LOCK_DESTROY(c);
|
||||||
|
epoch_leave( epoch );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( !acquire_ref( &c->c_refcnt ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
epoch = epoch_join();
|
||||||
|
|
||||||
Debug( LDAP_DEBUG_CONNS, "connection_read_cb: "
|
Debug( LDAP_DEBUG_CONNS, "connection_read_cb: "
|
||||||
"connection connid=%lu ready to read\n",
|
"connection connid=%lu ready to read\n",
|
||||||
c->c_connid );
|
c->c_connid );
|
||||||
|
|
@ -180,12 +192,14 @@ connection_read_cb( evutil_socket_t s, short what, void *arg )
|
||||||
Debug( LDAP_DEBUG_ANY, "connection_read_cb: "
|
Debug( LDAP_DEBUG_ANY, "connection_read_cb: "
|
||||||
"connid=%lu, ber_alloc failed\n",
|
"connid=%lu, ber_alloc failed\n",
|
||||||
c->c_connid );
|
c->c_connid );
|
||||||
CONNECTION_DESTROY(c);
|
goto out;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
c->c_currentber = ber;
|
c->c_currentber = ber;
|
||||||
|
|
||||||
|
ldap_pvt_thread_mutex_lock( &c->c_io_mutex );
|
||||||
tag = ber_get_next( c->c_sb, &len, ber );
|
tag = ber_get_next( c->c_sb, &len, ber );
|
||||||
|
ldap_pvt_thread_mutex_unlock( &c->c_io_mutex );
|
||||||
|
|
||||||
if ( tag != LDAP_TAG_MESSAGE ) {
|
if ( tag != LDAP_TAG_MESSAGE ) {
|
||||||
int err = sock_errno();
|
int err = sock_errno();
|
||||||
|
|
||||||
|
|
@ -210,65 +224,78 @@ connection_read_cb( evutil_socket_t s, short what, void *arg )
|
||||||
Debug( LDAP_DEBUG_CONNS, "connection_read_cb: "
|
Debug( LDAP_DEBUG_CONNS, "connection_read_cb: "
|
||||||
"suspended read event on dying connid=%lu\n",
|
"suspended read event on dying connid=%lu\n",
|
||||||
c->c_connid );
|
c->c_connid );
|
||||||
CONNECTION_DESTROY(c);
|
CONNECTION_LOCK_DESTROY(c);
|
||||||
return;
|
goto out;
|
||||||
}
|
}
|
||||||
event_add( c->c_read_event, c->c_read_timeout );
|
event_add( c->c_read_event, c->c_read_timeout );
|
||||||
Debug( LDAP_DEBUG_CONNS, "connection_read_cb: "
|
Debug( LDAP_DEBUG_CONNS, "connection_read_cb: "
|
||||||
"re-enabled read event on connid=%lu\n",
|
"re-enabled read event on connid=%lu\n",
|
||||||
c->c_connid );
|
c->c_connid );
|
||||||
CONNECTION_UNLOCK(c);
|
goto out;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( !lload_conn_max_pdus_per_cycle ||
|
|
||||||
ldap_pvt_thread_pool_submit( &connection_pool, handle_pdus, c ) ) {
|
|
||||||
/* If we're overloaded or configured as such, process one and resume in
|
|
||||||
* the next cycle.
|
|
||||||
*
|
|
||||||
* handle_one_request re-locks the mutex in the
|
|
||||||
* process, need to test it's still alive */
|
|
||||||
if ( c->c_pdu_cb( c ) == LDAP_SUCCESS ) {
|
|
||||||
CONNECTION_UNLOCK_OR_DESTROY(c);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
event_del( c->c_read_event );
|
event_del( c->c_read_event );
|
||||||
|
if ( !lload_conn_max_pdus_per_cycle ||
|
||||||
|
ldap_pvt_thread_pool_submit( &connection_pool, handle_pdus, c ) ) {
|
||||||
|
/* If we're overloaded or configured as such, process one and resume in
|
||||||
|
* the next cycle. */
|
||||||
|
event_add( c->c_read_event, c->c_read_timeout );
|
||||||
|
c->c_pdu_cb( c );
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
Debug( LDAP_DEBUG_CONNS, "connection_read_cb: "
|
Debug( LDAP_DEBUG_CONNS, "connection_read_cb: "
|
||||||
"suspended read event on connid=%lu\n",
|
"suspended read event on connid=%lu\n",
|
||||||
c->c_connid );
|
c->c_connid );
|
||||||
|
|
||||||
/* We have scheduled a call to handle_requests which takes care of
|
/*
|
||||||
* handling further requests, just make sure the connection sticks around
|
* We have scheduled a call to handle_pdus to take care of handling this
|
||||||
* for that */
|
* and further requests, its reference is now owned by that task.
|
||||||
CONNECTION_UNLOCK_INCREF(c);
|
*/
|
||||||
|
epoch_leave( epoch );
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
out:
|
||||||
|
RELEASE_REF( c, c_refcnt, c->c_destroy );
|
||||||
|
epoch_leave( epoch );
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
connection_write_cb( evutil_socket_t s, short what, void *arg )
|
connection_write_cb( evutil_socket_t s, short what, void *arg )
|
||||||
{
|
{
|
||||||
LloadConnection *c = arg;
|
LloadConnection *c = arg;
|
||||||
|
epoch_t epoch;
|
||||||
|
|
||||||
CONNECTION_LOCK(c);
|
CONNECTION_LOCK(c);
|
||||||
|
Debug( LDAP_DEBUG_CONNS, "connection_write_cb: "
|
||||||
|
"considering writing to%s connid=%lu what=%hd\n",
|
||||||
|
c->c_live ? " live" : " dead", c->c_connid, what );
|
||||||
if ( !c->c_live ) {
|
if ( !c->c_live ) {
|
||||||
CONNECTION_UNLOCK(c);
|
CONNECTION_UNLOCK(c);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
CONNECTION_UNLOCK(c);
|
||||||
|
|
||||||
if ( what & EV_TIMEOUT ) {
|
if ( what & EV_TIMEOUT ) {
|
||||||
Debug( LDAP_DEBUG_CONNS, "connection_write_cb: "
|
Debug( LDAP_DEBUG_CONNS, "connection_write_cb: "
|
||||||
"connid=%lu, timeout reached, destroying\n",
|
"connid=%lu, timeout reached, destroying\n",
|
||||||
c->c_connid );
|
c->c_connid );
|
||||||
CONNECTION_DESTROY(c);
|
/* Make sure the connection stays around for us to unlock it */
|
||||||
|
epoch = epoch_join();
|
||||||
|
CONNECTION_LOCK_DESTROY(c);
|
||||||
|
epoch_leave( epoch );
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
CONNECTION_UNLOCK_INCREF(c);
|
|
||||||
|
|
||||||
/* Before we acquire any locks */
|
/* Before we acquire any locks */
|
||||||
event_del( c->c_write_event );
|
event_del( c->c_write_event );
|
||||||
|
|
||||||
|
if ( !acquire_ref( &c->c_refcnt ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
epoch = epoch_join();
|
||||||
|
|
||||||
ldap_pvt_thread_mutex_lock( &c->c_io_mutex );
|
ldap_pvt_thread_mutex_lock( &c->c_io_mutex );
|
||||||
Debug( LDAP_DEBUG_CONNS, "connection_write_cb: "
|
Debug( LDAP_DEBUG_CONNS, "connection_write_cb: "
|
||||||
"have something to write to connection connid=%lu\n",
|
"have something to write to connection connid=%lu\n",
|
||||||
|
|
@ -285,7 +312,7 @@ connection_write_cb( evutil_socket_t s, short what, void *arg )
|
||||||
"ber_flush on fd=%d failed errno=%d (%s)\n",
|
"ber_flush on fd=%d failed errno=%d (%s)\n",
|
||||||
c->c_fd, err, sock_errstr( err, ebuf, sizeof(ebuf) ) );
|
c->c_fd, err, sock_errstr( err, ebuf, sizeof(ebuf) ) );
|
||||||
CONNECTION_LOCK_DESTROY(c);
|
CONNECTION_LOCK_DESTROY(c);
|
||||||
return;
|
goto done;
|
||||||
}
|
}
|
||||||
event_add( c->c_write_event, lload_write_timeout );
|
event_add( c->c_write_event, lload_write_timeout );
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -293,8 +320,9 @@ connection_write_cb( evutil_socket_t s, short what, void *arg )
|
||||||
}
|
}
|
||||||
ldap_pvt_thread_mutex_unlock( &c->c_io_mutex );
|
ldap_pvt_thread_mutex_unlock( &c->c_io_mutex );
|
||||||
|
|
||||||
CONNECTION_LOCK_DECREF(c);
|
done:
|
||||||
CONNECTION_UNLOCK_OR_DESTROY(c);
|
RELEASE_REF( c, c_refcnt, c->c_destroy );
|
||||||
|
epoch_leave( epoch );
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -356,85 +384,54 @@ connections_walk_last(
|
||||||
CONNCB cb,
|
CONNCB cb,
|
||||||
void *arg )
|
void *arg )
|
||||||
{
|
{
|
||||||
LloadConnection *c, *old;
|
LloadConnection *c = cq_last;
|
||||||
unsigned long last_connid;
|
uintptr_t last_connid;
|
||||||
|
|
||||||
if ( LDAP_CIRCLEQ_EMPTY( cq ) ) {
|
if ( LDAP_CIRCLEQ_EMPTY( cq ) ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
last_connid = cq_last->c_connid;
|
last_connid = c->c_connid;
|
||||||
c = LDAP_CIRCLEQ_LOOP_NEXT( cq, cq_last, c_next );
|
c = LDAP_CIRCLEQ_LOOP_NEXT( cq, c, c_next );
|
||||||
assert( c->c_connid <= last_connid );
|
|
||||||
|
|
||||||
CONNECTION_LOCK(c);
|
while ( !acquire_ref( &c->c_refcnt ) ) {
|
||||||
ldap_pvt_thread_mutex_unlock( cq_mutex );
|
c = LDAP_CIRCLEQ_LOOP_NEXT( cq, c, c_next );
|
||||||
|
if ( c->c_connid >= last_connid ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Ugh... concurrency is annoying:
|
* Notes:
|
||||||
* - we maintain the connections in the cq CIRCLEQ_ in ascending c_connid
|
* - we maintain the connections in the cq CIRCLEQ_ in ascending c_connid
|
||||||
* order
|
* order
|
||||||
* - the connection with the highest c_connid is maintained at cq_last
|
* - the connection with the highest c_connid is passed in cq_last
|
||||||
* - we can only use cq when we hold cq_mutex
|
* - we can only use cq when we hold cq_mutex
|
||||||
* - connections might be added to or removed from cq while we're busy
|
* - connections might be added to or removed from cq while we're busy
|
||||||
* processing connections
|
* processing connections
|
||||||
* - connection_destroy touches cq
|
|
||||||
* - we can't even hold locks of two different connections
|
|
||||||
* - we need a way to detect we've finished looping around cq for some
|
* - we need a way to detect we've finished looping around cq for some
|
||||||
* definition of looping around
|
* definition of looping around
|
||||||
*
|
|
||||||
* So as a result, 90% of the code below is spent navigating that...
|
|
||||||
*/
|
*/
|
||||||
while ( c->c_connid <= last_connid ) {
|
do {
|
||||||
/* Do not permit the callback to actually free the connection even if
|
int rc;
|
||||||
* it wants to, we need it to traverse cq */
|
|
||||||
c->c_refcnt++;
|
|
||||||
if ( cb( c, arg ) ) {
|
|
||||||
c->c_refcnt--;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
c->c_refcnt--;
|
|
||||||
|
|
||||||
if ( c->c_connid == last_connid ) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
CONNECTION_UNLOCK_INCREF(c);
|
|
||||||
|
|
||||||
ldap_pvt_thread_mutex_lock( cq_mutex );
|
|
||||||
old = c;
|
|
||||||
retry:
|
|
||||||
c = LDAP_CIRCLEQ_LOOP_NEXT( cq, c, c_next );
|
|
||||||
|
|
||||||
if ( c->c_connid <= old->c_connid ) {
|
|
||||||
ldap_pvt_thread_mutex_unlock( cq_mutex );
|
ldap_pvt_thread_mutex_unlock( cq_mutex );
|
||||||
|
|
||||||
CONNECTION_LOCK_DECREF(old);
|
rc = cb( c, arg );
|
||||||
CONNECTION_UNLOCK_OR_DESTROY(old);
|
RELEASE_REF( c, c_refcnt, c->c_destroy );
|
||||||
|
|
||||||
ldap_pvt_thread_mutex_lock( cq_mutex );
|
ldap_pvt_thread_mutex_lock( cq_mutex );
|
||||||
|
if ( rc || LDAP_CIRCLEQ_EMPTY( cq ) ) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
LloadConnection *old = c;
|
||||||
|
c = LDAP_CIRCLEQ_LOOP_NEXT( cq, c, c_next );
|
||||||
|
if ( c->c_connid <= old->c_connid || c->c_connid > last_connid ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
} while ( !acquire_ref( &c->c_refcnt ) );
|
||||||
CONNECTION_LOCK(c);
|
} while ( c->c_connid <= last_connid );
|
||||||
assert( c->c_state != LLOAD_C_DYING );
|
|
||||||
if ( c->c_state == LLOAD_C_INVALID ) {
|
|
||||||
/* This dying connection will be unlinked once we release cq_mutex
|
|
||||||
* and it wouldn't be safe to iterate further, skip over it */
|
|
||||||
CONNECTION_UNLOCK(c);
|
|
||||||
goto retry;
|
|
||||||
}
|
|
||||||
CONNECTION_UNLOCK_INCREF(c);
|
|
||||||
ldap_pvt_thread_mutex_unlock( cq_mutex );
|
|
||||||
|
|
||||||
CONNECTION_LOCK_DECREF(old);
|
|
||||||
CONNECTION_UNLOCK_OR_DESTROY(old);
|
|
||||||
|
|
||||||
CONNECTION_LOCK_DECREF(c);
|
|
||||||
assert( c->c_state != LLOAD_C_DYING );
|
|
||||||
assert( c->c_state != LLOAD_C_INVALID );
|
|
||||||
}
|
|
||||||
CONNECTION_UNLOCK_OR_DESTROY(c);
|
|
||||||
ldap_pvt_thread_mutex_lock( cq_mutex );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -448,44 +445,44 @@ connections_walk(
|
||||||
return connections_walk_last( cq_mutex, cq, cq_last, cb, arg );
|
return connections_walk_last( cq_mutex, cq, cq_last, cb, arg );
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Caller is expected to hold the lock.
|
|
||||||
*/
|
|
||||||
int
|
int
|
||||||
lload_connection_close( LloadConnection *c, void *arg )
|
lload_connection_close( LloadConnection *c, void *arg )
|
||||||
{
|
{
|
||||||
TAvlnode *node;
|
|
||||||
int gentle = *(int *)arg;
|
int gentle = *(int *)arg;
|
||||||
|
LloadOperation *op;
|
||||||
|
|
||||||
if ( !c->c_live ) {
|
Debug( LDAP_DEBUG_CONNS, "lload_connection_close: "
|
||||||
return LDAP_SUCCESS;
|
"marking connection connid=%lu closing\n",
|
||||||
}
|
c->c_connid );
|
||||||
|
|
||||||
|
/* We were approached from the connection list */
|
||||||
|
assert( IS_ALIVE( c, c_refcnt ) );
|
||||||
|
|
||||||
if ( !gentle ) {
|
|
||||||
/* Caller has a reference on this connection,
|
|
||||||
* it doesn't actually die here */
|
|
||||||
CONNECTION_DESTROY(c);
|
|
||||||
assert( c );
|
|
||||||
CONNECTION_LOCK(c);
|
CONNECTION_LOCK(c);
|
||||||
|
if ( !gentle || !c->c_ops ) {
|
||||||
|
CONNECTION_DESTROY(c);
|
||||||
return LDAP_SUCCESS;
|
return LDAP_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The first thing we do is make sure we don't get new Operations in */
|
/* The first thing we do is make sure we don't get new Operations in */
|
||||||
c->c_state = LLOAD_C_CLOSING;
|
c->c_state = LLOAD_C_CLOSING;
|
||||||
|
|
||||||
for ( node = tavl_end( c->c_ops, TAVL_DIR_LEFT ); node;
|
do {
|
||||||
node = tavl_next( node, TAVL_DIR_RIGHT ) ) {
|
TAvlnode *node = tavl_end( c->c_ops, TAVL_DIR_LEFT );
|
||||||
LloadOperation *op = node->avl_data;
|
op = node->avl_data;
|
||||||
|
|
||||||
if ( op->o_client_msgid == 0 ) {
|
/* Close operations that would need client action to resolve,
|
||||||
if ( op->o_client == c ) {
|
* only SASL binds in progress do that right now */
|
||||||
operation_destroy_from_client( op );
|
if ( op->o_client_msgid || op->o_upstream_msgid ) {
|
||||||
} else {
|
break;
|
||||||
assert( op->o_upstream == c );
|
|
||||||
operation_destroy_from_upstream( op );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CONNECTION_UNLOCK(c);
|
||||||
|
operation_unlink( op );
|
||||||
|
CONNECTION_LOCK(c);
|
||||||
|
} while ( c->c_ops );
|
||||||
|
|
||||||
|
CONNECTION_UNLOCK(c);
|
||||||
return LDAP_SUCCESS;
|
return LDAP_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -550,7 +547,6 @@ lload_connection_init( ber_socket_t s, const char *peername, int flags )
|
||||||
"connection connid=%lu allocated for socket fd=%d peername=%s\n",
|
"connection connid=%lu allocated for socket fd=%d peername=%s\n",
|
||||||
c->c_connid, s, peername );
|
c->c_connid, s, peername );
|
||||||
|
|
||||||
CONNECTION_LOCK(c);
|
|
||||||
c->c_state = LLOAD_C_ACTIVE;
|
c->c_state = LLOAD_C_ACTIVE;
|
||||||
|
|
||||||
return c;
|
return c;
|
||||||
|
|
|
||||||
|
|
@ -759,6 +759,7 @@ lloadd_listeners_init( const char *urls )
|
||||||
int
|
int
|
||||||
lloadd_daemon_destroy( void )
|
lloadd_daemon_destroy( void )
|
||||||
{
|
{
|
||||||
|
epoch_shutdown();
|
||||||
if ( lloadd_inited ) {
|
if ( lloadd_inited ) {
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
|
@ -1674,8 +1675,7 @@ lload_handle_global_invalidation( LloadChange *change )
|
||||||
LloadConnection *next =
|
LloadConnection *next =
|
||||||
LDAP_CIRCLEQ_LOOP_NEXT( &clients, c, c_next );
|
LDAP_CIRCLEQ_LOOP_NEXT( &clients, c, c_next );
|
||||||
if ( c->c_is_tls ) {
|
if ( c->c_is_tls ) {
|
||||||
CONNECTION_LOCK(c);
|
CONNECTION_LOCK_DESTROY(c);
|
||||||
CONNECTION_DESTROY(c);
|
|
||||||
assert( c == NULL );
|
assert( c == NULL );
|
||||||
}
|
}
|
||||||
c = next;
|
c = next;
|
||||||
|
|
|
||||||
228
servers/lloadd/epoch.c
Normal file
228
servers/lloadd/epoch.c
Normal file
|
|
@ -0,0 +1,228 @@
|
||||||
|
/* epoch.c - epoch based memory reclamation */
|
||||||
|
/* $OpenLDAP$ */
|
||||||
|
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
|
||||||
|
*
|
||||||
|
* Copyright 2018-2020 The OpenLDAP Foundation.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted only as authorized by the OpenLDAP
|
||||||
|
* Public License.
|
||||||
|
*
|
||||||
|
* A copy of this license is available in the file LICENSE in the
|
||||||
|
* top-level directory of the distribution or, alternatively, at
|
||||||
|
* <http://www.OpenLDAP.org/license.html>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @file epoch.c
|
||||||
|
*
|
||||||
|
* Implementation of epoch based memory reclamation, in principle
|
||||||
|
* similar to the algorithm presented in
|
||||||
|
* https://www.cl.cam.ac.uk/techreports/UCAM-CL-TR-579.pdf
|
||||||
|
*
|
||||||
|
* Not completely lock-free at the moment.
|
||||||
|
*
|
||||||
|
* Also the problems with epoch based memory reclamation are still
|
||||||
|
* present - a thread actively observing an epoch getting stuck will
|
||||||
|
* prevent managed objects (in our case connections and operations)
|
||||||
|
* from being freed, potentially running out of memory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "portable.h"
|
||||||
|
|
||||||
|
#include "lload.h"
|
||||||
|
#include <epoch.h>
|
||||||
|
|
||||||
|
/* Has to be >= 3 */
|
||||||
|
#define EPOCH_MASK ( 1 << 2 )
|
||||||
|
#define EPOCH_PREV(epoch) ( ( (epoch) + EPOCH_MASK - 1 ) % EPOCH_MASK )
|
||||||
|
#define EPOCH_NEXT(epoch) ( ( (epoch) + 1 ) % EPOCH_MASK )
|
||||||
|
|
||||||
|
struct pending_ref {
|
||||||
|
void *object;
|
||||||
|
dispose_cb *dispose;
|
||||||
|
struct pending_ref *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
ldap_pvt_thread_rdwr_t epoch_mutex;
|
||||||
|
|
||||||
|
static epoch_t current_epoch;
|
||||||
|
static uintptr_t epoch_threads[EPOCH_MASK];
|
||||||
|
static struct pending_ref *references[EPOCH_MASK];
|
||||||
|
|
||||||
|
void
|
||||||
|
epoch_init( void )
|
||||||
|
{
|
||||||
|
epoch_t epoch;
|
||||||
|
|
||||||
|
current_epoch = 0;
|
||||||
|
for ( epoch = 0; epoch < EPOCH_MASK; epoch++ ) {
|
||||||
|
assert( !epoch_threads[epoch] );
|
||||||
|
assert( !references[epoch] );
|
||||||
|
}
|
||||||
|
|
||||||
|
ldap_pvt_thread_rdwr_init( &epoch_mutex );
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
epoch_shutdown( void )
|
||||||
|
{
|
||||||
|
epoch_t epoch;
|
||||||
|
struct pending_ref *old, *next;
|
||||||
|
|
||||||
|
for ( epoch = 0; epoch < EPOCH_MASK; epoch++ ) {
|
||||||
|
assert( !epoch_threads[epoch] );
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Free pending references */
|
||||||
|
epoch = EPOCH_PREV(current_epoch);
|
||||||
|
next = references[epoch];
|
||||||
|
references[epoch] = NULL;
|
||||||
|
for ( old = next; old; old = next ) {
|
||||||
|
next = old->next;
|
||||||
|
|
||||||
|
old->dispose( old->object );
|
||||||
|
ch_free( old );
|
||||||
|
}
|
||||||
|
|
||||||
|
epoch = current_epoch;
|
||||||
|
next = references[epoch];
|
||||||
|
references[epoch] = NULL;
|
||||||
|
for ( old = next; old; old = next ) {
|
||||||
|
next = old->next;
|
||||||
|
|
||||||
|
old->dispose( old->object );
|
||||||
|
ch_free( old );
|
||||||
|
}
|
||||||
|
|
||||||
|
/* No references should exist anywhere now */
|
||||||
|
for ( epoch = 0; epoch < EPOCH_MASK; epoch++ ) {
|
||||||
|
assert( !references[epoch] );
|
||||||
|
}
|
||||||
|
|
||||||
|
ldap_pvt_thread_rdwr_destroy( &epoch_mutex );
|
||||||
|
}
|
||||||
|
|
||||||
|
epoch_t
|
||||||
|
epoch_join( void )
|
||||||
|
{
|
||||||
|
epoch_t epoch;
|
||||||
|
struct pending_ref *old, *ref = NULL;
|
||||||
|
|
||||||
|
/* TODO: make this completely lock-free */
|
||||||
|
ldap_pvt_thread_rdwr_rlock( &epoch_mutex );
|
||||||
|
epoch = current_epoch;
|
||||||
|
__atomic_add_fetch( &epoch_threads[epoch], 1, __ATOMIC_ACQ_REL );
|
||||||
|
ldap_pvt_thread_rdwr_runlock( &epoch_mutex );
|
||||||
|
|
||||||
|
if ( __atomic_load_n(
|
||||||
|
&epoch_threads[EPOCH_PREV(epoch)], __ATOMIC_ACQUIRE ) ) {
|
||||||
|
return epoch;
|
||||||
|
}
|
||||||
|
|
||||||
|
__atomic_exchange(
|
||||||
|
&references[EPOCH_PREV(epoch)], &ref, &ref, __ATOMIC_ACQ_REL );
|
||||||
|
|
||||||
|
Debug( LDAP_DEBUG_TRACE, "epoch_join: "
|
||||||
|
"advancing epoch to %zu with %s objects to free\n",
|
||||||
|
EPOCH_NEXT(epoch), ref ? "some" : "no" );
|
||||||
|
|
||||||
|
ldap_pvt_thread_rdwr_wlock( &epoch_mutex );
|
||||||
|
current_epoch = EPOCH_NEXT(epoch);
|
||||||
|
ldap_pvt_thread_rdwr_wunlock( &epoch_mutex );
|
||||||
|
|
||||||
|
for ( old = ref; old; old = ref ) {
|
||||||
|
ref = old->next;
|
||||||
|
|
||||||
|
old->dispose( old->object );
|
||||||
|
ch_free( old );
|
||||||
|
}
|
||||||
|
|
||||||
|
return epoch;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
epoch_leave( epoch_t epoch )
|
||||||
|
{
|
||||||
|
__atomic_sub_fetch( &epoch_threads[epoch], 1, __ATOMIC_ACQ_REL );
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add the object to the "current global epoch", not the epoch our thread
|
||||||
|
* entered.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
epoch_append( void *ptr, dispose_cb *cb )
|
||||||
|
{
|
||||||
|
struct pending_ref *new;
|
||||||
|
epoch_t epoch = __atomic_load_n( ¤t_epoch, __ATOMIC_ACQUIRE );
|
||||||
|
|
||||||
|
/*
|
||||||
|
* BTW, the following is not appropriate here:
|
||||||
|
* assert( __atomic_load_n( &epoch_threads[epoch], __ATOMIC_RELAXED ) );
|
||||||
|
*
|
||||||
|
* We might be a thread lagging behind in the "previous epoch" with no
|
||||||
|
* other threads executing at all.
|
||||||
|
*/
|
||||||
|
|
||||||
|
new = ch_malloc( sizeof(struct pending_ref) );
|
||||||
|
new->object = ptr;
|
||||||
|
new->dispose = cb;
|
||||||
|
new->next = __atomic_load_n( &references[epoch], __ATOMIC_ACQUIRE );
|
||||||
|
|
||||||
|
while ( !__atomic_compare_exchange( &references[epoch], &new->next, &new, 0,
|
||||||
|
__ATOMIC_RELEASE, __ATOMIC_RELAXED ) )
|
||||||
|
/* iterate until we succeed */;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
acquire_ref( uintptr_t *refp )
|
||||||
|
{
|
||||||
|
uintptr_t refcnt, new_refcnt;
|
||||||
|
|
||||||
|
refcnt = __atomic_load_n( refp, __ATOMIC_ACQUIRE );
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we just incremented the refcnt and checked for zero after, another
|
||||||
|
* thread might falsely believe the object was going to stick around.
|
||||||
|
*
|
||||||
|
* Checking whether the object is still dead at disposal time might not be
|
||||||
|
* able to distinguish it from being freed in a later epoch.
|
||||||
|
*/
|
||||||
|
do {
|
||||||
|
if ( !refcnt ) {
|
||||||
|
return refcnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
new_refcnt = refcnt + 1;
|
||||||
|
} while ( !__atomic_compare_exchange( refp, &refcnt, &new_refcnt, 0,
|
||||||
|
__ATOMIC_RELEASE, __ATOMIC_RELAXED ) );
|
||||||
|
assert( new_refcnt == refcnt + 1 );
|
||||||
|
|
||||||
|
return refcnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
try_release_ref( uintptr_t *refp, void *object, dispose_cb *cb )
|
||||||
|
{
|
||||||
|
uintptr_t refcnt, new_refcnt;
|
||||||
|
|
||||||
|
refcnt = __atomic_load_n( refp, __ATOMIC_ACQUIRE );
|
||||||
|
|
||||||
|
/* We promise the caller that we won't decrease refcnt below 0 */
|
||||||
|
do {
|
||||||
|
if ( !refcnt ) {
|
||||||
|
return refcnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
new_refcnt = refcnt - 1;
|
||||||
|
} while ( !__atomic_compare_exchange( refp, &refcnt, &new_refcnt, 0,
|
||||||
|
__ATOMIC_RELEASE, __ATOMIC_RELAXED ) );
|
||||||
|
assert( new_refcnt == refcnt - 1 );
|
||||||
|
|
||||||
|
if ( !new_refcnt ) {
|
||||||
|
epoch_append( object, cb );
|
||||||
|
}
|
||||||
|
|
||||||
|
return refcnt;
|
||||||
|
}
|
||||||
143
servers/lloadd/epoch.h
Normal file
143
servers/lloadd/epoch.h
Normal file
|
|
@ -0,0 +1,143 @@
|
||||||
|
/* epoch.h - epoch based memory reclamation */
|
||||||
|
/* $OpenLDAP$ */
|
||||||
|
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
|
||||||
|
*
|
||||||
|
* Copyright 2018-2020 The OpenLDAP Foundation.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted only as authorized by the OpenLDAP
|
||||||
|
* Public License.
|
||||||
|
*
|
||||||
|
* A copy of this license is available in the file LICENSE in the
|
||||||
|
* top-level directory of the distribution or, alternatively, at
|
||||||
|
* <http://www.OpenLDAP.org/license.html>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef __LLOAD_EPOCH_H
|
||||||
|
#define __LLOAD_EPOCH_H
|
||||||
|
|
||||||
|
/** @file epoch.h
|
||||||
|
*
|
||||||
|
* Implementation of epoch based memory reclamation, in principle
|
||||||
|
* similar to the algorithm presented in
|
||||||
|
* https://www.cl.cam.ac.uk/techreports/UCAM-CL-TR-579.pdf
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef uintptr_t epoch_t;
|
||||||
|
|
||||||
|
/** @brief A callback function used to free object and associated data */
|
||||||
|
typedef void (dispose_cb)( void *object );
|
||||||
|
|
||||||
|
/** @brief Initiate global state */
|
||||||
|
void epoch_init( void );
|
||||||
|
|
||||||
|
/** @brief Finalise global state and free any objects still pending */
|
||||||
|
void epoch_shutdown( void );
|
||||||
|
|
||||||
|
/** @brief Register thread as active
|
||||||
|
*
|
||||||
|
* In order to safely access managed objects, a thread should call
|
||||||
|
* this function or make sure no other thread is running (e.g. config
|
||||||
|
* pause, late shutdown). After calling this, it is guaranteed that no
|
||||||
|
* reachable objects will be freed before all threads have called
|
||||||
|
* `epoch_leave( current_epoch + 1 )` so it is essential that there
|
||||||
|
* is an upper limit to the amount of time between #epoch_join and
|
||||||
|
* corresponding #epoch_leave or the number of unfreed objects might
|
||||||
|
* grow without bounds.
|
||||||
|
*
|
||||||
|
* To simplify locking, memory is only freed when the current epoch
|
||||||
|
* is advanced rather than on leaving it.
|
||||||
|
*
|
||||||
|
* Can be safely called multiple times by the same thread as long as
|
||||||
|
* a matching #epoch_leave() call is made eventually.
|
||||||
|
*
|
||||||
|
* @return The observed epoch, to be passed to #epoch_leave()
|
||||||
|
*/
|
||||||
|
epoch_t epoch_join( void );
|
||||||
|
|
||||||
|
/** @brief Register thread as inactive
|
||||||
|
*
|
||||||
|
* A thread should call this after they are finished with work
|
||||||
|
* performed since matching call to #epoch_join(). It is not safe
|
||||||
|
* to keep a local reference to managed objects after this call
|
||||||
|
* unless other precautions have been made to prevent it being
|
||||||
|
* released.
|
||||||
|
*
|
||||||
|
* @param[in] epoch Epoch identifier returned by a previous call to
|
||||||
|
* #epoch_join().
|
||||||
|
*/
|
||||||
|
void epoch_leave( epoch_t epoch );
|
||||||
|
|
||||||
|
/** @brief Return an unreachable object to be freed
|
||||||
|
*
|
||||||
|
* The object should already be unreachable at the point of call and
|
||||||
|
* cb will be invoked when no other thread that could have seen it
|
||||||
|
* is active any more. This happens when we have advanced by two
|
||||||
|
* epochs.
|
||||||
|
*
|
||||||
|
* @param[in] ptr Object to be released/freed
|
||||||
|
* @param[in] cb Callback to invoke when safe to do so
|
||||||
|
*/
|
||||||
|
void epoch_append( void *ptr, dispose_cb *cb );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \defgroup Reference counting helpers
|
||||||
|
*/
|
||||||
|
/**@{*/
|
||||||
|
|
||||||
|
/** @brief Acquire a reference if possible
|
||||||
|
*
|
||||||
|
* Atomically, check reference count is non-zero and increment if so.
|
||||||
|
* Returns old reference count.
|
||||||
|
*
|
||||||
|
* @param[in] refp Pointer to a reference counter
|
||||||
|
* @return 0 if reference was already zero, non-zero if reference
|
||||||
|
* count was successfully incremented
|
||||||
|
*/
|
||||||
|
int acquire_ref( uintptr_t *refp );
|
||||||
|
|
||||||
|
/** @brief Check reference count and try to decrement
|
||||||
|
*
|
||||||
|
* Atomically, decrement reference count if non-zero and register
|
||||||
|
* object if decremented to zero. Returning previous reference count.
|
||||||
|
*
|
||||||
|
* @param[in] refp Pointer to a reference counter
|
||||||
|
* @param[in] object The managed object
|
||||||
|
* @param[in] cb Callback to invoke when safe to do so
|
||||||
|
* @return 0 if reference was already zero, non-zero if reference
|
||||||
|
* count was non-zero at the time of call
|
||||||
|
*/
|
||||||
|
int try_release_ref( uintptr_t *refp, void *object, dispose_cb *cb );
|
||||||
|
|
||||||
|
/** @brief Read reference count
|
||||||
|
*
|
||||||
|
* @param[in] object Pointer to the managed object
|
||||||
|
* @param[in] ref_field Member where reference count is stored in
|
||||||
|
* the object
|
||||||
|
* @return Current value of reference counter
|
||||||
|
*/
|
||||||
|
#define IS_ALIVE( object, ref_field ) \
|
||||||
|
__atomic_load_n( &(object)->ref_field, __ATOMIC_ACQUIRE )
|
||||||
|
|
||||||
|
/** @brief Release reference
|
||||||
|
*
|
||||||
|
* A cheaper alternative to #try_release_ref(), safe only when we know
|
||||||
|
* reference count was already non-zero.
|
||||||
|
*
|
||||||
|
* @param[in] object The managed object
|
||||||
|
* @param[in] ref_field Member where reference count is stored in
|
||||||
|
* the object
|
||||||
|
* @param[in] cb Callback to invoke when safe to do so
|
||||||
|
*/
|
||||||
|
#define RELEASE_REF( object, ref_field, cb ) \
|
||||||
|
do { \
|
||||||
|
if ( !__atomic_sub_fetch( \
|
||||||
|
&(object)->ref_field, 1, __ATOMIC_ACQ_REL ) ) { \
|
||||||
|
epoch_append( object, (dispose_cb *)cb ); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/**@}*/
|
||||||
|
|
||||||
|
#endif /* __LLOAD_EPOCH_H */
|
||||||
|
|
@ -36,6 +36,7 @@ handle_starttls( LloadConnection *c, LloadOperation *op )
|
||||||
char *msg = NULL;
|
char *msg = NULL;
|
||||||
int rc = LDAP_SUCCESS;
|
int rc = LDAP_SUCCESS;
|
||||||
|
|
||||||
|
CONNECTION_LOCK(c);
|
||||||
tavl_delete( &c->c_ops, op, operation_client_cmp );
|
tavl_delete( &c->c_ops, op, operation_client_cmp );
|
||||||
|
|
||||||
if ( c->c_is_tls == LLOAD_TLS_ESTABLISHED ) {
|
if ( c->c_is_tls == LLOAD_TLS_ESTABLISHED ) {
|
||||||
|
|
@ -51,6 +52,7 @@ handle_starttls( LloadConnection *c, LloadOperation *op )
|
||||||
rc = LDAP_UNAVAILABLE;
|
rc = LDAP_UNAVAILABLE;
|
||||||
msg = "Could not initialize TLS";
|
msg = "Could not initialize TLS";
|
||||||
}
|
}
|
||||||
|
CONNECTION_UNLOCK(c);
|
||||||
|
|
||||||
Debug( LDAP_DEBUG_STATS, "handle_starttls: "
|
Debug( LDAP_DEBUG_STATS, "handle_starttls: "
|
||||||
"handling StartTLS exop connid=%lu rc=%d msg=%s\n",
|
"handling StartTLS exop connid=%lu rc=%d msg=%s\n",
|
||||||
|
|
@ -58,11 +60,10 @@ handle_starttls( LloadConnection *c, LloadOperation *op )
|
||||||
|
|
||||||
if ( rc ) {
|
if ( rc ) {
|
||||||
/* We've already removed the operation from the queue */
|
/* We've already removed the operation from the queue */
|
||||||
return operation_send_reject_locked( op, rc, msg, 1 );
|
operation_send_reject( op, rc, msg, 1 );
|
||||||
|
return LDAP_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
CONNECTION_UNLOCK_INCREF(c);
|
|
||||||
|
|
||||||
event_del( c->c_read_event );
|
event_del( c->c_read_event );
|
||||||
event_del( c->c_write_event );
|
event_del( c->c_write_event );
|
||||||
/*
|
/*
|
||||||
|
|
@ -77,9 +78,8 @@ handle_starttls( LloadConnection *c, LloadOperation *op )
|
||||||
output = c->c_pendingber;
|
output = c->c_pendingber;
|
||||||
if ( output == NULL && (output = ber_alloc()) == NULL ) {
|
if ( output == NULL && (output = ber_alloc()) == NULL ) {
|
||||||
ldap_pvt_thread_mutex_unlock( &c->c_io_mutex );
|
ldap_pvt_thread_mutex_unlock( &c->c_io_mutex );
|
||||||
CONNECTION_LOCK_DECREF(c);
|
operation_unlink( op );
|
||||||
operation_destroy_from_client( op );
|
CONNECTION_LOCK_DESTROY(c);
|
||||||
CONNECTION_DESTROY(c);
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
c->c_pendingber = output;
|
c->c_pendingber = output;
|
||||||
|
|
@ -88,7 +88,7 @@ handle_starttls( LloadConnection *c, LloadOperation *op )
|
||||||
LDAP_RES_EXTENDED, LDAP_SUCCESS, "", "" );
|
LDAP_RES_EXTENDED, LDAP_SUCCESS, "", "" );
|
||||||
ldap_pvt_thread_mutex_unlock( &c->c_io_mutex );
|
ldap_pvt_thread_mutex_unlock( &c->c_io_mutex );
|
||||||
|
|
||||||
CONNECTION_LOCK_DECREF(c);
|
CONNECTION_LOCK(c);
|
||||||
c->c_read_timeout = lload_timeout_net;
|
c->c_read_timeout = lload_timeout_net;
|
||||||
event_assign( c->c_read_event, base, c->c_fd, EV_READ|EV_PERSIST,
|
event_assign( c->c_read_event, base, c->c_fd, EV_READ|EV_PERSIST,
|
||||||
client_tls_handshake_cb, c );
|
client_tls_handshake_cb, c );
|
||||||
|
|
@ -100,8 +100,9 @@ handle_starttls( LloadConnection *c, LloadOperation *op )
|
||||||
event_add( c->c_write_event, lload_write_timeout );
|
event_add( c->c_write_event, lload_write_timeout );
|
||||||
|
|
||||||
op->o_res = LLOAD_OP_COMPLETED;
|
op->o_res = LLOAD_OP_COMPLETED;
|
||||||
operation_destroy_from_client( op );
|
CONNECTION_UNLOCK(c);
|
||||||
CONNECTION_UNLOCK_INCREF(c);
|
|
||||||
|
operation_unlink( op );
|
||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
@ -115,10 +116,8 @@ request_extended( LloadConnection *c, LloadOperation *op )
|
||||||
ber_tag_t tag;
|
ber_tag_t tag;
|
||||||
|
|
||||||
if ( (copy = ber_alloc()) == NULL ) {
|
if ( (copy = ber_alloc()) == NULL ) {
|
||||||
if ( operation_send_reject_locked(
|
operation_send_reject( op, LDAP_OTHER, "internal error", 0 );
|
||||||
op, LDAP_OTHER, "internal error", 0 ) == LDAP_SUCCESS ) {
|
CONNECTION_LOCK_DESTROY(c);
|
||||||
CONNECTION_DESTROY(c);
|
|
||||||
}
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -128,8 +127,9 @@ request_extended( LloadConnection *c, LloadOperation *op )
|
||||||
if ( tag != LDAP_TAG_EXOP_REQ_OID ) {
|
if ( tag != LDAP_TAG_EXOP_REQ_OID ) {
|
||||||
Debug( LDAP_DEBUG_STATS, "request_extended: "
|
Debug( LDAP_DEBUG_STATS, "request_extended: "
|
||||||
"no OID present in extended request\n" );
|
"no OID present in extended request\n" );
|
||||||
return operation_send_reject_locked(
|
operation_send_reject( op, LDAP_PROTOCOL_ERROR, "decoding error", 0 );
|
||||||
op, LDAP_PROTOCOL_ERROR, "decoding error", 0 );
|
CONNECTION_LOCK_DESTROY(c);
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
needle.oid = bv;
|
needle.oid = bv;
|
||||||
|
|
@ -145,8 +145,8 @@ request_extended( LloadConnection *c, LloadOperation *op )
|
||||||
ber_free( copy, 0 );
|
ber_free( copy, 0 );
|
||||||
|
|
||||||
if ( c->c_state == LLOAD_C_BINDING ) {
|
if ( c->c_state == LLOAD_C_BINDING ) {
|
||||||
return operation_send_reject_locked(
|
operation_send_reject( op, LDAP_PROTOCOL_ERROR, "bind in progress", 0 );
|
||||||
op, LDAP_PROTOCOL_ERROR, "bind in progress", 0 );
|
return LDAP_SUCCESS;
|
||||||
}
|
}
|
||||||
return request_process( c, op );
|
return request_process( c, op );
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -84,6 +84,8 @@ LDAP_BEGIN_DECL
|
||||||
|
|
||||||
#define BER_BV_OPTIONAL( bv ) ( BER_BVISNULL( bv ) ? NULL : ( bv ) )
|
#define BER_BV_OPTIONAL( bv ) ( BER_BVISNULL( bv ) ? NULL : ( bv ) )
|
||||||
|
|
||||||
|
#include <epoch.h>
|
||||||
|
|
||||||
typedef struct LloadBackend LloadBackend;
|
typedef struct LloadBackend LloadBackend;
|
||||||
typedef struct LloadPendingConnection LloadPendingConnection;
|
typedef struct LloadPendingConnection LloadPendingConnection;
|
||||||
typedef struct LloadConnection LloadConnection;
|
typedef struct LloadConnection LloadConnection;
|
||||||
|
|
@ -280,58 +282,39 @@ struct LloadConnection {
|
||||||
* - also a liveness/validity token is added to c_refcnt during
|
* - also a liveness/validity token is added to c_refcnt during
|
||||||
* lload_connection_init, its existence is tracked in c_live and is usually the
|
* lload_connection_init, its existence is tracked in c_live and is usually the
|
||||||
* only one that prevents it from being destroyed
|
* only one that prevents it from being destroyed
|
||||||
* - anyone who needs to be able to lock the connection after unlocking it has
|
* - anyone who needs to be able to relock the connection after unlocking it has
|
||||||
* to use CONNECTION_UNLOCK_INCREF, they are then responsible that
|
* to use acquire_ref(), they need to make sure a matching
|
||||||
* CONNECTION_LOCK_DECREF+CONNECTION_UNLOCK_OR_DESTROY is used when they are
|
* RELEASE_REF( c, c_refcnt, c->c_destroy ); is run eventually
|
||||||
* done with it
|
|
||||||
* - when a connection is considered dead, use CONNECTION_DESTROY on a locked
|
* - when a connection is considered dead, use CONNECTION_DESTROY on a locked
|
||||||
* connection, it might get disposed of or if anyone still holds a token, it
|
* connection, it will be made unreachable from normal places and either
|
||||||
* just gets unlocked and it's the last token holder's responsibility to run
|
* scheduled for reclamation when safe to do so or if anyone still holds a
|
||||||
* CONNECTION_UNLOCK_OR_DESTROY
|
* reference, it just gets unlocked and reclaimed after the last ref is
|
||||||
* - CONNECTION_LOCK_DESTROY is a shorthand for locking, decreasing refcount
|
* released
|
||||||
* and CONNECTION_DESTROY
|
* - CONNECTION_LOCK_DESTROY is a shorthand for locking and CONNECTION_DESTROY
|
||||||
*/
|
*/
|
||||||
ldap_pvt_thread_mutex_t c_mutex; /* protect the connection */
|
ldap_pvt_thread_mutex_t c_mutex; /* protect the connection */
|
||||||
int c_refcnt, c_live;
|
uintptr_t c_refcnt, c_live;
|
||||||
|
CONNECTION_DESTROY_CB c_unlink;
|
||||||
CONNECTION_DESTROY_CB c_destroy;
|
CONNECTION_DESTROY_CB c_destroy;
|
||||||
CONNECTION_PDU_CB c_pdu_cb;
|
CONNECTION_PDU_CB c_pdu_cb;
|
||||||
#define CONNECTION_LOCK(c) ldap_pvt_thread_mutex_lock( &(c)->c_mutex )
|
#define CONNECTION_LOCK(c) ldap_pvt_thread_mutex_lock( &(c)->c_mutex )
|
||||||
#define CONNECTION_UNLOCK(c) ldap_pvt_thread_mutex_unlock( &(c)->c_mutex )
|
#define CONNECTION_UNLOCK(c) ldap_pvt_thread_mutex_unlock( &(c)->c_mutex )
|
||||||
#define CONNECTION_LOCK_DECREF(c) \
|
#define CONNECTION_UNLINK_(c) \
|
||||||
do { \
|
do { \
|
||||||
CONNECTION_LOCK(c); \
|
if ( (c)->c_live ) { \
|
||||||
(c)->c_refcnt--; \
|
|
||||||
} while (0)
|
|
||||||
#define CONNECTION_UNLOCK_INCREF(c) \
|
|
||||||
do { \
|
|
||||||
(c)->c_refcnt++; \
|
|
||||||
CONNECTION_UNLOCK(c); \
|
|
||||||
} while (0)
|
|
||||||
#define CONNECTION_UNLOCK_OR_DESTROY(c) \
|
|
||||||
do { \
|
|
||||||
assert( (c)->c_refcnt >= 0 ); \
|
|
||||||
if ( (c)->c_state == LLOAD_C_CLOSING && !( c )->c_ops ) { \
|
|
||||||
(c)->c_refcnt -= (c)->c_live; \
|
|
||||||
(c)->c_live = 0; \
|
(c)->c_live = 0; \
|
||||||
} \
|
RELEASE_REF( (c), c_refcnt, c->c_destroy ); \
|
||||||
if ( !( c )->c_refcnt ) { \
|
(c)->c_unlink( (c) ); \
|
||||||
Debug( LDAP_DEBUG_TRACE, "%s: destroying connection connid=%lu\n", \
|
|
||||||
__func__, (c)->c_connid ); \
|
|
||||||
(c)->c_destroy( (c) ); \
|
|
||||||
(c) = NULL; \
|
|
||||||
} else { \
|
|
||||||
CONNECTION_UNLOCK(c); \
|
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} while (0)
|
||||||
#define CONNECTION_DESTROY(c) \
|
#define CONNECTION_DESTROY(c) \
|
||||||
do { \
|
do { \
|
||||||
(c)->c_refcnt -= (c)->c_live; \
|
CONNECTION_UNLINK_(c); \
|
||||||
(c)->c_live = 0; \
|
CONNECTION_UNLOCK(c); \
|
||||||
CONNECTION_UNLOCK_OR_DESTROY(c); \
|
|
||||||
} while (0)
|
} while (0)
|
||||||
#define CONNECTION_LOCK_DESTROY(c) \
|
#define CONNECTION_LOCK_DESTROY(c) \
|
||||||
do { \
|
do { \
|
||||||
CONNECTION_LOCK_DECREF(c); \
|
CONNECTION_LOCK(c); \
|
||||||
CONNECTION_DESTROY(c); \
|
CONNECTION_DESTROY(c); \
|
||||||
} while (0);
|
} while (0);
|
||||||
|
|
||||||
|
|
@ -393,12 +376,13 @@ struct LloadConnection {
|
||||||
|
|
||||||
enum op_state {
|
enum op_state {
|
||||||
LLOAD_OP_NOT_FREEING = 0,
|
LLOAD_OP_NOT_FREEING = 0,
|
||||||
LLOAD_OP_FREEING_UPSTREAM = 1 << 0,
|
LLOAD_OP_DETACHING_CLIENT = 1 << 1,
|
||||||
LLOAD_OP_FREEING_CLIENT = 1 << 1,
|
LLOAD_OP_DETACHING_UPSTREAM = 1 << 0,
|
||||||
LLOAD_OP_DETACHING_UPSTREAM = 1 << 2,
|
|
||||||
LLOAD_OP_DETACHING_CLIENT = 1 << 3,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define LLOAD_OP_DETACHING_MASK \
|
||||||
|
( LLOAD_OP_DETACHING_UPSTREAM | LLOAD_OP_DETACHING_CLIENT )
|
||||||
|
|
||||||
/* operation result for monitoring purposes */
|
/* operation result for monitoring purposes */
|
||||||
enum op_result {
|
enum op_result {
|
||||||
LLOAD_OP_REJECTED, /* operation was not forwarded */
|
LLOAD_OP_REJECTED, /* operation was not forwarded */
|
||||||
|
|
@ -406,32 +390,28 @@ enum op_result {
|
||||||
LLOAD_OP_FAILED, /* operation was forwarded, but no response was received */
|
LLOAD_OP_FAILED, /* operation was forwarded, but no response was received */
|
||||||
};
|
};
|
||||||
|
|
||||||
#define LLOAD_OP_FREEING_MASK \
|
/*
|
||||||
( LLOAD_OP_FREEING_UPSTREAM | LLOAD_OP_FREEING_CLIENT )
|
* Operation reference tracking:
|
||||||
#define LLOAD_OP_DETACHING_MASK \
|
* - o_refcnt is set to 1, never incremented
|
||||||
( LLOAD_OP_DETACHING_UPSTREAM | LLOAD_OP_DETACHING_CLIENT )
|
* - operation_unlink sets it to 0 and on transition from 1 clears both
|
||||||
|
* connection links (o_client, o_upstream)
|
||||||
|
*/
|
||||||
struct LloadOperation {
|
struct LloadOperation {
|
||||||
|
uintptr_t o_refcnt;
|
||||||
|
|
||||||
LloadConnection *o_client;
|
LloadConnection *o_client;
|
||||||
unsigned long o_client_connid;
|
unsigned long o_client_connid;
|
||||||
int o_client_live, o_client_refcnt;
|
|
||||||
ber_int_t o_client_msgid;
|
ber_int_t o_client_msgid;
|
||||||
ber_int_t o_saved_msgid;
|
ber_int_t o_saved_msgid;
|
||||||
|
|
||||||
LloadConnection *o_upstream;
|
LloadConnection *o_upstream;
|
||||||
unsigned long o_upstream_connid;
|
unsigned long o_upstream_connid;
|
||||||
int o_upstream_live, o_upstream_refcnt;
|
|
||||||
ber_int_t o_upstream_msgid;
|
ber_int_t o_upstream_msgid;
|
||||||
time_t o_last_response;
|
time_t o_last_response;
|
||||||
|
|
||||||
/* Protects o_client, o_upstream pointers before we lock their c_mutex if
|
/* Protects o_client, o_upstream links */
|
||||||
* we don't know they are still alive */
|
|
||||||
ldap_pvt_thread_mutex_t o_link_mutex;
|
ldap_pvt_thread_mutex_t o_link_mutex;
|
||||||
/* Protects o_freeing, can be locked while holding c_mutex */
|
|
||||||
ldap_pvt_thread_mutex_t o_mutex;
|
|
||||||
/* Consistent w.r.t. o_mutex, only written to while holding
|
|
||||||
* op->o_{client,upstream}->c_mutex */
|
|
||||||
enum op_state o_freeing;
|
|
||||||
ber_tag_t o_tag;
|
ber_tag_t o_tag;
|
||||||
time_t o_start;
|
time_t o_start;
|
||||||
unsigned long o_pin_id;
|
unsigned long o_pin_id;
|
||||||
|
|
|
||||||
|
|
@ -424,6 +424,8 @@ main( int argc, char **argv )
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
epoch_init();
|
||||||
|
|
||||||
while ( (i = getopt( argc, argv,
|
while ( (i = getopt( argc, argv,
|
||||||
"c:d:f:F:h:n:o:s:tV"
|
"c:d:f:F:h:n:o:s:tV"
|
||||||
#ifdef LDAP_PF_INET6
|
#ifdef LDAP_PF_INET6
|
||||||
|
|
|
||||||
|
|
@ -85,6 +85,10 @@ lload_back_open( BackendInfo *bi )
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* This will fail if we ever try to instantiate more than one lloadd within
|
||||||
|
* the process */
|
||||||
|
epoch_init();
|
||||||
|
|
||||||
if ( lload_tls_init() != 0 ) {
|
if ( lload_tls_init() != 0 ) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -116,363 +116,6 @@ operation_upstream_cmp( const void *left, const void *right )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Free the operation, subject to there being noone else holding a reference
|
|
||||||
* to it.
|
|
||||||
*
|
|
||||||
* Both operation_destroy_from_* functions are the same, two implementations
|
|
||||||
* exist to cater for the fact that either side (client or upstream) might
|
|
||||||
* decide to destroy it and each holds a different mutex.
|
|
||||||
*
|
|
||||||
* Due to the fact that we rely on mutexes on both connections which have a
|
|
||||||
* different timespan from the operation, we have to take the following race
|
|
||||||
* into account:
|
|
||||||
*
|
|
||||||
* Trigger
|
|
||||||
* - both operation_destroy_from_client and operation_destroy_from_upstream
|
|
||||||
* are called at the same time (each holding its mutex), several times
|
|
||||||
* before one of them finishes
|
|
||||||
* - either or both connections might have started the process of being
|
|
||||||
* destroyed
|
|
||||||
*
|
|
||||||
* We need to detect that the race has happened and only allow one of them to
|
|
||||||
* free the operation (we use o_freeing != 0 to announce+detect that).
|
|
||||||
*
|
|
||||||
* In case the caller was in the process of destroying the connection and the
|
|
||||||
* race had been won by the mirror caller, it will increment c_refcnt on its
|
|
||||||
* connection and make sure to postpone the final step in
|
|
||||||
* client/upstream_destroy(). Testing o_freeing for the mirror side's token
|
|
||||||
* allows the winner to detect that it has been a party to the race and a token
|
|
||||||
* in c_refcnt has been deposited on its behalf.
|
|
||||||
*
|
|
||||||
* Beware! This widget really touches all the mutexes we have and showcases the
|
|
||||||
* issues with maintaining so many mutex ordering restrictions.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
operation_destroy_from_client( LloadOperation *op )
|
|
||||||
{
|
|
||||||
LloadConnection *upstream = NULL, *client = op->o_client;
|
|
||||||
LloadBackend *b = NULL;
|
|
||||||
int race_state, detach_client = !client->c_live;
|
|
||||||
|
|
||||||
Debug( LDAP_DEBUG_TRACE, "operation_destroy_from_client: "
|
|
||||||
"op=%p attempting to release operation%s\n",
|
|
||||||
op, detach_client ? " and detach client" : "" );
|
|
||||||
|
|
||||||
/* 1. liveness/refcnt adjustment and test */
|
|
||||||
op->o_client_refcnt -= op->o_client_live;
|
|
||||||
op->o_client_live = 0;
|
|
||||||
|
|
||||||
assert( op->o_client_refcnt <= client->c_refcnt );
|
|
||||||
if ( op->o_client_refcnt ) {
|
|
||||||
Debug( LDAP_DEBUG_TRACE, "operation_destroy_from_client: "
|
|
||||||
"op=%p not dead yet\n",
|
|
||||||
op );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 2. Remove from the operation map and TODO adjust the pending op count */
|
|
||||||
tavl_delete( &client->c_ops, op, operation_client_cmp );
|
|
||||||
|
|
||||||
/* 3. Detect whether we entered a race to free op and indicate that to any
|
|
||||||
* others */
|
|
||||||
ldap_pvt_thread_mutex_lock( &op->o_mutex );
|
|
||||||
race_state = op->o_freeing;
|
|
||||||
op->o_freeing |= LLOAD_OP_FREEING_CLIENT;
|
|
||||||
if ( detach_client ) {
|
|
||||||
op->o_freeing |= LLOAD_OP_DETACHING_CLIENT;
|
|
||||||
}
|
|
||||||
ldap_pvt_thread_mutex_unlock( &op->o_mutex );
|
|
||||||
|
|
||||||
CONNECTION_UNLOCK_INCREF(client);
|
|
||||||
|
|
||||||
if ( detach_client ) {
|
|
||||||
ldap_pvt_thread_mutex_lock( &op->o_link_mutex );
|
|
||||||
op->o_client = NULL;
|
|
||||||
ldap_pvt_thread_mutex_unlock( &op->o_link_mutex );
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 4. If we lost the race, deal with it straight away */
|
|
||||||
if ( race_state ) {
|
|
||||||
/*
|
|
||||||
* We have raced to destroy op and the first one to lose on this side,
|
|
||||||
* leave a refcnt token on client so we don't destroy it before the
|
|
||||||
* other side has finished (it knows we did that when it examines
|
|
||||||
* o_freeing again).
|
|
||||||
*/
|
|
||||||
if ( detach_client ) {
|
|
||||||
Debug( LDAP_DEBUG_TRACE, "operation_destroy_from_client: "
|
|
||||||
"op=%p lost race but client connid=%lu is going down\n",
|
|
||||||
op, client->c_connid );
|
|
||||||
CONNECTION_LOCK_DECREF(client);
|
|
||||||
} else if ( (race_state & LLOAD_OP_FREEING_MASK) ==
|
|
||||||
LLOAD_OP_FREEING_UPSTREAM ) {
|
|
||||||
Debug( LDAP_DEBUG_TRACE, "operation_destroy_from_client: "
|
|
||||||
"op=%p lost race, increased client refcnt connid=%lu "
|
|
||||||
"to refcnt=%d\n",
|
|
||||||
op, client->c_connid, client->c_refcnt );
|
|
||||||
CONNECTION_LOCK(client);
|
|
||||||
} else {
|
|
||||||
Debug( LDAP_DEBUG_TRACE, "operation_destroy_from_client: "
|
|
||||||
"op=%p lost race with another "
|
|
||||||
"operation_destroy_from_client, "
|
|
||||||
"client connid=%lu\n",
|
|
||||||
op, client->c_connid );
|
|
||||||
CONNECTION_LOCK_DECREF(client);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* it seems we will be destroying the operation,
|
|
||||||
* so update the global rejected cunter if needed */
|
|
||||||
operation_update_global_rejected( op );
|
|
||||||
/* 5. If we raced the upstream side and won, reclaim the token */
|
|
||||||
ldap_pvt_thread_mutex_lock( &op->o_link_mutex );
|
|
||||||
if ( !(race_state & LLOAD_OP_DETACHING_UPSTREAM) ) {
|
|
||||||
upstream = op->o_upstream;
|
|
||||||
if ( upstream ) {
|
|
||||||
CONNECTION_LOCK(upstream);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ldap_pvt_thread_mutex_unlock( &op->o_link_mutex );
|
|
||||||
|
|
||||||
ldap_pvt_thread_mutex_lock( &op->o_mutex );
|
|
||||||
/* We don't actually resolve the race in full until we grab the other's
|
|
||||||
* c_mutex+op->o_mutex here */
|
|
||||||
if ( upstream && ( op->o_freeing & LLOAD_OP_FREEING_UPSTREAM ) ) {
|
|
||||||
if ( op->o_freeing & LLOAD_OP_DETACHING_UPSTREAM ) {
|
|
||||||
CONNECTION_UNLOCK(upstream);
|
|
||||||
upstream = NULL;
|
|
||||||
} else {
|
|
||||||
/*
|
|
||||||
* We have raced to destroy op and won. To avoid freeing the connection
|
|
||||||
* under us, a refcnt token has been left over for us on the upstream,
|
|
||||||
* decref and see whether we are in charge of freeing it
|
|
||||||
*/
|
|
||||||
upstream->c_refcnt--;
|
|
||||||
Debug( LDAP_DEBUG_TRACE, "operation_destroy_from_client: "
|
|
||||||
"op=%p other side lost race with us, upstream connid=%lu\n",
|
|
||||||
op, upstream->c_connid );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ldap_pvt_thread_mutex_unlock( &op->o_mutex );
|
|
||||||
|
|
||||||
/* 6. liveness/refcnt adjustment and test */
|
|
||||||
op->o_upstream_refcnt -= op->o_upstream_live;
|
|
||||||
op->o_upstream_live = 0;
|
|
||||||
if ( op->o_upstream_refcnt ) {
|
|
||||||
Debug( LDAP_DEBUG_TRACE, "operation_destroy_from_client: "
|
|
||||||
"op=%p other side still alive, refcnt=%d\n",
|
|
||||||
op, op->o_upstream_refcnt );
|
|
||||||
|
|
||||||
/* There must have been no race if op is still alive */
|
|
||||||
ldap_pvt_thread_mutex_lock( &op->o_mutex );
|
|
||||||
op->o_freeing &= ~LLOAD_OP_FREEING_CLIENT;
|
|
||||||
if ( detach_client ) {
|
|
||||||
op->o_freeing &= ~LLOAD_OP_DETACHING_CLIENT;
|
|
||||||
}
|
|
||||||
assert( op->o_freeing == 0 );
|
|
||||||
ldap_pvt_thread_mutex_unlock( &op->o_mutex );
|
|
||||||
|
|
||||||
assert( upstream != NULL );
|
|
||||||
CONNECTION_UNLOCK_OR_DESTROY(upstream);
|
|
||||||
CONNECTION_LOCK_DECREF(client);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 7. Remove from the operation map and adjust the pending op count */
|
|
||||||
if ( upstream ) {
|
|
||||||
if ( tavl_delete( &upstream->c_ops, op, operation_upstream_cmp ) ) {
|
|
||||||
upstream->c_n_ops_executing--;
|
|
||||||
operation_update_conn_counters( op );
|
|
||||||
b = (LloadBackend *)upstream->c_private;
|
|
||||||
}
|
|
||||||
CONNECTION_UNLOCK_OR_DESTROY(upstream);
|
|
||||||
|
|
||||||
if ( b ) {
|
|
||||||
ldap_pvt_thread_mutex_lock( &b->b_mutex );
|
|
||||||
b->b_n_ops_executing--;
|
|
||||||
operation_update_backend_counters( op, b );
|
|
||||||
ldap_pvt_thread_mutex_unlock( &b->b_mutex );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 8. Release the operation */
|
|
||||||
Debug( LDAP_DEBUG_TRACE, "operation_destroy_from_client: "
|
|
||||||
"op=%p destroyed operation from client connid=%lu, "
|
|
||||||
"client msgid=%d\n",
|
|
||||||
op, op->o_client_connid, op->o_client_msgid );
|
|
||||||
ber_free( op->o_ber, 1 );
|
|
||||||
ldap_pvt_thread_mutex_destroy( &op->o_mutex );
|
|
||||||
ldap_pvt_thread_mutex_destroy( &op->o_link_mutex );
|
|
||||||
ch_free( op );
|
|
||||||
|
|
||||||
CONNECTION_LOCK_DECREF(client);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* See operation_destroy_from_client.
|
|
||||||
*/
|
|
||||||
void
|
|
||||||
operation_destroy_from_upstream( LloadOperation *op )
|
|
||||||
{
|
|
||||||
LloadConnection *client = NULL, *upstream = op->o_upstream;
|
|
||||||
LloadBackend *b = NULL;
|
|
||||||
int race_state, detach_upstream = !upstream->c_live;
|
|
||||||
|
|
||||||
Debug( LDAP_DEBUG_TRACE, "operation_destroy_from_upstream: "
|
|
||||||
"op=%p attempting to release operation%s\n",
|
|
||||||
op, detach_upstream ? " and detach upstream" : "" );
|
|
||||||
|
|
||||||
/* 1. liveness/refcnt adjustment and test */
|
|
||||||
op->o_upstream_refcnt -= op->o_upstream_live;
|
|
||||||
op->o_upstream_live = 0;
|
|
||||||
|
|
||||||
assert( op->o_upstream_refcnt <= upstream->c_refcnt );
|
|
||||||
if ( op->o_upstream_refcnt ) {
|
|
||||||
Debug( LDAP_DEBUG_TRACE, "operation_destroy_from_upstream: "
|
|
||||||
"op=%p not dead yet\n",
|
|
||||||
op );
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* it seems we will be destroying the operation,
|
|
||||||
* so update the global rejected cunter if needed */
|
|
||||||
operation_update_global_rejected( op );
|
|
||||||
/* 2. Remove from the operation map and adjust the pending op count */
|
|
||||||
if ( tavl_delete( &upstream->c_ops, op, operation_upstream_cmp ) ) {
|
|
||||||
upstream->c_n_ops_executing--;
|
|
||||||
operation_update_conn_counters( op );
|
|
||||||
b = (LloadBackend *)upstream->c_private;
|
|
||||||
}
|
|
||||||
|
|
||||||
ldap_pvt_thread_mutex_lock( &op->o_mutex );
|
|
||||||
race_state = op->o_freeing;
|
|
||||||
op->o_freeing |= LLOAD_OP_FREEING_UPSTREAM;
|
|
||||||
if ( detach_upstream ) {
|
|
||||||
op->o_freeing |= LLOAD_OP_DETACHING_UPSTREAM;
|
|
||||||
}
|
|
||||||
ldap_pvt_thread_mutex_unlock( &op->o_mutex );
|
|
||||||
|
|
||||||
CONNECTION_UNLOCK_INCREF(upstream);
|
|
||||||
|
|
||||||
/* 3. Detect whether we entered a race to free op */
|
|
||||||
ldap_pvt_thread_mutex_lock( &op->o_link_mutex );
|
|
||||||
if ( detach_upstream ) {
|
|
||||||
op->o_upstream = NULL;
|
|
||||||
}
|
|
||||||
ldap_pvt_thread_mutex_unlock( &op->o_link_mutex );
|
|
||||||
|
|
||||||
if ( b ) {
|
|
||||||
ldap_pvt_thread_mutex_lock( &b->b_mutex );
|
|
||||||
b->b_n_ops_executing--;
|
|
||||||
operation_update_backend_counters( op, b );
|
|
||||||
ldap_pvt_thread_mutex_unlock( &b->b_mutex );
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 4. If we lost the race, deal with it straight away */
|
|
||||||
if ( race_state ) {
|
|
||||||
/*
|
|
||||||
* We have raced to destroy op and the first one to lose on this side,
|
|
||||||
* leave a refcnt token on upstream so we don't destroy it before the
|
|
||||||
* other side has finished (it knows we did that when it examines
|
|
||||||
* o_freeing again).
|
|
||||||
*/
|
|
||||||
if ( detach_upstream ) {
|
|
||||||
Debug( LDAP_DEBUG_TRACE, "operation_destroy_from_upstream: "
|
|
||||||
"op=%p lost race but upstream connid=%lu is going down\n",
|
|
||||||
op, upstream->c_connid );
|
|
||||||
CONNECTION_LOCK_DECREF(upstream);
|
|
||||||
} else if ( (race_state & LLOAD_OP_FREEING_MASK) ==
|
|
||||||
LLOAD_OP_FREEING_CLIENT ) {
|
|
||||||
Debug( LDAP_DEBUG_TRACE, "operation_destroy_from_upstream: "
|
|
||||||
"op=%p lost race, increased upstream refcnt connid=%lu "
|
|
||||||
"to refcnt=%d\n",
|
|
||||||
op, upstream->c_connid, upstream->c_refcnt );
|
|
||||||
CONNECTION_LOCK(upstream);
|
|
||||||
} else {
|
|
||||||
Debug( LDAP_DEBUG_TRACE, "operation_destroy_from_upstream: "
|
|
||||||
"op=%p lost race with another "
|
|
||||||
"operation_destroy_from_upstream, "
|
|
||||||
"upstream connid=%lu\n",
|
|
||||||
op, upstream->c_connid );
|
|
||||||
CONNECTION_LOCK_DECREF(upstream);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 5. If we raced the client side and won, reclaim the token */
|
|
||||||
ldap_pvt_thread_mutex_lock( &op->o_link_mutex );
|
|
||||||
if ( !(race_state & LLOAD_OP_DETACHING_CLIENT) ) {
|
|
||||||
client = op->o_client;
|
|
||||||
if ( client ) {
|
|
||||||
CONNECTION_LOCK(client);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ldap_pvt_thread_mutex_unlock( &op->o_link_mutex );
|
|
||||||
|
|
||||||
/* We don't actually resolve the race in full until we grab the other's
|
|
||||||
* c_mutex+op->o_mutex here */
|
|
||||||
ldap_pvt_thread_mutex_lock( &op->o_mutex );
|
|
||||||
if ( client && ( op->o_freeing & LLOAD_OP_FREEING_CLIENT ) ) {
|
|
||||||
if ( op->o_freeing & LLOAD_OP_DETACHING_CLIENT ) {
|
|
||||||
CONNECTION_UNLOCK(client);
|
|
||||||
client = NULL;
|
|
||||||
} else {
|
|
||||||
/*
|
|
||||||
* We have raced to destroy op and won. To avoid freeing the connection
|
|
||||||
* under us, a refcnt token has been left over for us on the client,
|
|
||||||
* decref and see whether we are in charge of freeing it
|
|
||||||
*/
|
|
||||||
client->c_refcnt--;
|
|
||||||
Debug( LDAP_DEBUG_TRACE, "operation_destroy_from_upstream: "
|
|
||||||
"op=%p other side lost race with us, client connid=%lu\n",
|
|
||||||
op, client->c_connid );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ldap_pvt_thread_mutex_unlock( &op->o_mutex );
|
|
||||||
|
|
||||||
/* 6. liveness/refcnt adjustment and test */
|
|
||||||
op->o_client_refcnt -= op->o_client_live;
|
|
||||||
op->o_client_live = 0;
|
|
||||||
if ( op->o_client_refcnt ) {
|
|
||||||
Debug( LDAP_DEBUG_TRACE, "operation_destroy_from_upstream: "
|
|
||||||
"op=%p other side still alive, refcnt=%d\n",
|
|
||||||
op, op->o_client_refcnt );
|
|
||||||
/* There must have been no race if op is still alive */
|
|
||||||
ldap_pvt_thread_mutex_lock( &op->o_mutex );
|
|
||||||
op->o_freeing &= ~LLOAD_OP_FREEING_UPSTREAM;
|
|
||||||
if ( detach_upstream ) {
|
|
||||||
op->o_freeing &= ~LLOAD_OP_DETACHING_UPSTREAM;
|
|
||||||
}
|
|
||||||
assert( op->o_freeing == 0 );
|
|
||||||
ldap_pvt_thread_mutex_unlock( &op->o_mutex );
|
|
||||||
|
|
||||||
assert( client != NULL );
|
|
||||||
CONNECTION_UNLOCK_OR_DESTROY(client);
|
|
||||||
CONNECTION_LOCK_DECREF(upstream);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 7. Remove from the operation map and TODO adjust the pending op count */
|
|
||||||
if ( client ) {
|
|
||||||
tavl_delete( &client->c_ops, op, operation_client_cmp );
|
|
||||||
CONNECTION_UNLOCK_OR_DESTROY(client);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 8. Release the operation */
|
|
||||||
Debug( LDAP_DEBUG_TRACE, "operation_destroy_from_upstream: "
|
|
||||||
"op=%p destroyed operation from client connid=%lu, "
|
|
||||||
"client msgid=%d\n",
|
|
||||||
op, op->o_client_connid, op->o_client_msgid );
|
|
||||||
ber_free( op->o_ber, 1 );
|
|
||||||
ldap_pvt_thread_mutex_destroy( &op->o_mutex );
|
|
||||||
ldap_pvt_thread_mutex_destroy( &op->o_link_mutex );
|
|
||||||
ch_free( op );
|
|
||||||
|
|
||||||
CONNECTION_LOCK_DECREF(upstream);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Entered holding c_mutex for now.
|
* Entered holding c_mutex for now.
|
||||||
*/
|
*/
|
||||||
|
|
@ -490,11 +133,9 @@ operation_init( LloadConnection *c, BerElement *ber )
|
||||||
op->o_ber = ber;
|
op->o_ber = ber;
|
||||||
op->o_start = slap_get_time();
|
op->o_start = slap_get_time();
|
||||||
|
|
||||||
ldap_pvt_thread_mutex_init( &op->o_mutex );
|
|
||||||
ldap_pvt_thread_mutex_init( &op->o_link_mutex );
|
ldap_pvt_thread_mutex_init( &op->o_link_mutex );
|
||||||
|
|
||||||
op->o_client_live = op->o_client_refcnt = 1;
|
op->o_refcnt = 1;
|
||||||
op->o_upstream_live = op->o_upstream_refcnt = 1;
|
|
||||||
|
|
||||||
tag = ber_get_int( ber, &op->o_client_msgid );
|
tag = ber_get_int( ber, &op->o_client_msgid );
|
||||||
if ( tag != LDAP_TAG_MSGID ) {
|
if ( tag != LDAP_TAG_MSGID ) {
|
||||||
|
|
@ -549,13 +190,163 @@ fail:
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
void
|
||||||
operation_send_abandon( LloadOperation *op )
|
operation_destroy( LloadOperation *op )
|
||||||
|
{
|
||||||
|
Debug( LDAP_DEBUG_TRACE, "operation_destroy: "
|
||||||
|
"op=%p destroyed operation from client connid=%lu, "
|
||||||
|
"client msgid=%d\n",
|
||||||
|
op, op->o_client_connid, op->o_client_msgid );
|
||||||
|
|
||||||
|
assert( op->o_refcnt == 0 );
|
||||||
|
assert( op->o_client == NULL );
|
||||||
|
assert( op->o_upstream == NULL );
|
||||||
|
|
||||||
|
ber_free( op->o_ber, 1 );
|
||||||
|
ldap_pvt_thread_mutex_destroy( &op->o_link_mutex );
|
||||||
|
ch_free( op );
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
operation_unlink( LloadOperation *op )
|
||||||
|
{
|
||||||
|
LloadConnection *client, *upstream;
|
||||||
|
uintptr_t prev_refcnt;
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
|
if ( !( prev_refcnt = try_release_ref(
|
||||||
|
&op->o_refcnt, op, (dispose_cb *)operation_destroy ) ) ) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert( prev_refcnt == 1 );
|
||||||
|
|
||||||
|
Debug( LDAP_DEBUG_TRACE, "operation_unlink: "
|
||||||
|
"unlinking operation between client connid=%lu and upstream "
|
||||||
|
"connid=%lu "
|
||||||
|
"client msgid=%d\n",
|
||||||
|
op->o_client_connid, op->o_upstream_connid, op->o_client_msgid );
|
||||||
|
|
||||||
|
ldap_pvt_thread_mutex_lock( &op->o_link_mutex );
|
||||||
|
client = op->o_client;
|
||||||
|
upstream = op->o_upstream;
|
||||||
|
|
||||||
|
op->o_client = NULL;
|
||||||
|
op->o_upstream = NULL;
|
||||||
|
ldap_pvt_thread_mutex_unlock( &op->o_link_mutex );
|
||||||
|
|
||||||
|
assert( client || upstream );
|
||||||
|
|
||||||
|
if ( client ) {
|
||||||
|
result |= operation_unlink_client( op, client );
|
||||||
|
operation_update_global_rejected( op );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( upstream ) {
|
||||||
|
result |= operation_unlink_upstream( op, upstream );
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
operation_unlink_client( LloadOperation *op, LloadConnection *client )
|
||||||
|
{
|
||||||
|
LloadOperation *removed;
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
|
Debug( LDAP_DEBUG_TRACE, "operation_unlink_client: "
|
||||||
|
"unlinking operation op=%p msgid=%d client connid=%lu\n",
|
||||||
|
op, op->o_client_msgid, op->o_client_connid );
|
||||||
|
|
||||||
|
CONNECTION_LOCK(client);
|
||||||
|
if ( (removed = tavl_delete(
|
||||||
|
&client->c_ops, op, operation_client_cmp )) ) {
|
||||||
|
result = LLOAD_OP_DETACHING_CLIENT;
|
||||||
|
|
||||||
|
assert( op == removed );
|
||||||
|
client->c_n_ops_executing--;
|
||||||
|
|
||||||
|
if ( client->c_state == LLOAD_C_BINDING ) {
|
||||||
|
client->c_state = LLOAD_C_READY;
|
||||||
|
if ( !BER_BVISNULL( &client->c_auth ) ) {
|
||||||
|
ber_memfree( client->c_auth.bv_val );
|
||||||
|
BER_BVZERO( &client->c_auth );
|
||||||
|
}
|
||||||
|
if ( !BER_BVISNULL( &client->c_sasl_bind_mech ) ) {
|
||||||
|
ber_memfree( client->c_sasl_bind_mech.bv_val );
|
||||||
|
BER_BVZERO( &client->c_sasl_bind_mech );
|
||||||
|
}
|
||||||
|
if ( op->o_pin_id ) {
|
||||||
|
client->c_pin_id = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( client->c_state == LLOAD_C_CLOSING && !client->c_ops ) {
|
||||||
|
CONNECTION_DESTROY(client);
|
||||||
|
} else {
|
||||||
|
CONNECTION_UNLOCK(client);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
operation_unlink_upstream( LloadOperation *op, LloadConnection *upstream )
|
||||||
|
{
|
||||||
|
LloadOperation *removed;
|
||||||
|
LloadBackend *b = NULL;
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
|
Debug( LDAP_DEBUG_TRACE, "operation_unlink_upstream: "
|
||||||
|
"unlinking operation op=%p msgid=%d upstream connid=%lu\n",
|
||||||
|
op, op->o_upstream_msgid, op->o_upstream_connid );
|
||||||
|
|
||||||
|
CONNECTION_LOCK(upstream);
|
||||||
|
if ( (removed = tavl_delete(
|
||||||
|
&upstream->c_ops, op, operation_upstream_cmp )) ) {
|
||||||
|
result |= LLOAD_OP_DETACHING_UPSTREAM;
|
||||||
|
|
||||||
|
assert( op == removed );
|
||||||
|
upstream->c_n_ops_executing--;
|
||||||
|
|
||||||
|
if ( upstream->c_state == LLOAD_C_BINDING ) {
|
||||||
|
assert( op->o_tag == LDAP_REQ_BIND && upstream->c_ops == NULL );
|
||||||
|
upstream->c_state = LLOAD_C_READY;
|
||||||
|
if ( !BER_BVISNULL( &upstream->c_sasl_bind_mech ) ) {
|
||||||
|
ber_memfree( upstream->c_sasl_bind_mech.bv_val );
|
||||||
|
BER_BVZERO( &upstream->c_sasl_bind_mech );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
operation_update_conn_counters( op, upstream );
|
||||||
|
b = (LloadBackend *)upstream->c_private;
|
||||||
|
}
|
||||||
|
if ( upstream->c_state == LLOAD_C_CLOSING && !upstream->c_ops ) {
|
||||||
|
CONNECTION_DESTROY(upstream);
|
||||||
|
} else {
|
||||||
|
CONNECTION_UNLOCK(upstream);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( b ) {
|
||||||
|
ldap_pvt_thread_mutex_lock( &b->b_mutex );
|
||||||
|
b->b_n_ops_executing--;
|
||||||
|
operation_update_backend_counters( op, b );
|
||||||
|
ldap_pvt_thread_mutex_unlock( &b->b_mutex );
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
operation_send_abandon( LloadOperation *op, LloadConnection *upstream )
|
||||||
{
|
{
|
||||||
LloadConnection *upstream = op->o_upstream;
|
|
||||||
BerElement *ber;
|
BerElement *ber;
|
||||||
int rc = -1;
|
int rc = -1;
|
||||||
|
|
||||||
|
if ( !IS_ALIVE( upstream, c_refcnt ) ) {
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
ldap_pvt_thread_mutex_lock( &upstream->c_io_mutex );
|
ldap_pvt_thread_mutex_lock( &upstream->c_io_mutex );
|
||||||
ber = upstream->c_pendingber;
|
ber = upstream->c_pendingber;
|
||||||
if ( ber == NULL && (ber = ber_alloc()) == NULL ) {
|
if ( ber == NULL && (ber = ber_alloc()) == NULL ) {
|
||||||
|
|
@ -605,114 +396,64 @@ void
|
||||||
operation_abandon( LloadOperation *op )
|
operation_abandon( LloadOperation *op )
|
||||||
{
|
{
|
||||||
LloadConnection *c;
|
LloadConnection *c;
|
||||||
LloadBackend *b;
|
|
||||||
int rc = LDAP_SUCCESS;
|
|
||||||
|
|
||||||
ldap_pvt_thread_mutex_lock( &op->o_link_mutex );
|
ldap_pvt_thread_mutex_lock( &op->o_link_mutex );
|
||||||
c = op->o_upstream;
|
c = op->o_upstream;
|
||||||
if ( !c || !c->c_live ) {
|
|
||||||
ldap_pvt_thread_mutex_unlock( &op->o_link_mutex );
|
ldap_pvt_thread_mutex_unlock( &op->o_link_mutex );
|
||||||
|
if ( !c || !IS_ALIVE( c, c_refcnt ) ) {
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
CONNECTION_LOCK(c);
|
|
||||||
ldap_pvt_thread_mutex_unlock( &op->o_link_mutex );
|
|
||||||
|
|
||||||
/* for now consider all abandoned operations completed,
|
/* for now consider all abandoned operations completed,
|
||||||
* perhaps add a separate counter later */
|
* perhaps add a separate counter later */
|
||||||
op->o_res = LLOAD_OP_COMPLETED;
|
op->o_res = LLOAD_OP_COMPLETED;
|
||||||
|
if ( !operation_unlink_upstream( op, c ) ) {
|
||||||
if ( tavl_delete( &c->c_ops, op, operation_upstream_cmp ) == NULL ) {
|
|
||||||
/* The operation has already been abandoned or finished */
|
/* The operation has already been abandoned or finished */
|
||||||
Debug( LDAP_DEBUG_TRACE, "operation_abandon: "
|
Debug( LDAP_DEBUG_TRACE, "operation_abandon: "
|
||||||
"%s from connid=%lu msgid=%d not present in connid=%lu any "
|
"%s from connid=%lu msgid=%d not present in connid=%lu any "
|
||||||
"more\n",
|
"more\n",
|
||||||
lload_msgtype2str( op->o_tag ), op->o_client_connid,
|
lload_msgtype2str( op->o_tag ), op->o_client_connid,
|
||||||
op->o_client_msgid, op->o_upstream_connid );
|
op->o_client_msgid, op->o_upstream_connid );
|
||||||
goto unlock;
|
goto done;
|
||||||
}
|
}
|
||||||
if ( c->c_state == LLOAD_C_BINDING ) {
|
|
||||||
c->c_state = LLOAD_C_READY;
|
|
||||||
if ( !BER_BVISNULL( &c->c_sasl_bind_mech ) ) {
|
|
||||||
ber_memfree( c->c_sasl_bind_mech.bv_val );
|
|
||||||
BER_BVZERO( &c->c_sasl_bind_mech );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
c->c_n_ops_executing--;
|
|
||||||
b = (LloadBackend *)c->c_private;
|
|
||||||
|
|
||||||
op->o_upstream_refcnt++;
|
if ( operation_send_abandon( op, c ) == LDAP_SUCCESS ) {
|
||||||
CONNECTION_UNLOCK_INCREF(c);
|
|
||||||
|
|
||||||
ldap_pvt_thread_mutex_lock( &b->b_mutex );
|
|
||||||
b->b_n_ops_executing--;
|
|
||||||
operation_update_backend_counters( op, b );
|
|
||||||
ldap_pvt_thread_mutex_unlock( &b->b_mutex );
|
|
||||||
|
|
||||||
if ( operation_send_abandon( op ) == LDAP_SUCCESS ) {
|
|
||||||
connection_write_cb( -1, 0, c );
|
connection_write_cb( -1, 0, c );
|
||||||
}
|
}
|
||||||
|
|
||||||
CONNECTION_LOCK_DECREF(c);
|
|
||||||
op->o_upstream_refcnt--;
|
|
||||||
|
|
||||||
unlock:
|
|
||||||
if ( !c->c_live || !op->o_upstream_refcnt ) {
|
|
||||||
operation_destroy_from_upstream( op );
|
|
||||||
}
|
|
||||||
if ( rc ) {
|
|
||||||
CONNECTION_DESTROY(c);
|
|
||||||
} else {
|
|
||||||
CONNECTION_UNLOCK_OR_DESTROY(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
done:
|
done:
|
||||||
c = op->o_client;
|
operation_unlink( op );
|
||||||
assert( c );
|
|
||||||
|
|
||||||
/* Caller should hold a reference on client */
|
|
||||||
CONNECTION_LOCK(c);
|
|
||||||
if ( c->c_state == LLOAD_C_BINDING ) {
|
|
||||||
c->c_state = LLOAD_C_READY;
|
|
||||||
if ( !BER_BVISNULL( &c->c_auth ) ) {
|
|
||||||
ber_memfree( c->c_auth.bv_val );
|
|
||||||
BER_BVZERO( &c->c_auth );
|
|
||||||
}
|
|
||||||
if ( !BER_BVISNULL( &c->c_sasl_bind_mech ) ) {
|
|
||||||
ber_memfree( c->c_sasl_bind_mech.bv_val );
|
|
||||||
BER_BVZERO( &c->c_sasl_bind_mech );
|
|
||||||
}
|
|
||||||
if ( op->o_pin_id ) {
|
|
||||||
c->c_pin_id = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert( op->o_client_refcnt > op->o_client_live );
|
|
||||||
op->o_client_refcnt--;
|
|
||||||
operation_destroy_from_client( op );
|
|
||||||
CONNECTION_UNLOCK(c);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
void
|
||||||
* Called with op->o_client non-NULL and already locked.
|
operation_send_reject(
|
||||||
*/
|
|
||||||
int
|
|
||||||
operation_send_reject_locked(
|
|
||||||
LloadOperation *op,
|
LloadOperation *op,
|
||||||
int result,
|
int result,
|
||||||
const char *msg,
|
const char *msg,
|
||||||
int send_anyway )
|
int send_anyway )
|
||||||
{
|
{
|
||||||
LloadConnection *c = op->o_client;
|
LloadConnection *c;
|
||||||
BerElement *ber;
|
BerElement *ber;
|
||||||
int found;
|
int found;
|
||||||
|
|
||||||
Debug( LDAP_DEBUG_TRACE, "operation_send_reject_locked: "
|
Debug( LDAP_DEBUG_TRACE, "operation_send_reject: "
|
||||||
"rejecting %s from client connid=%lu with message: \"%s\"\n",
|
"rejecting %s from client connid=%lu with message: \"%s\"\n",
|
||||||
lload_msgtype2str( op->o_tag ), c->c_connid, msg );
|
lload_msgtype2str( op->o_tag ), op->o_client_connid, msg );
|
||||||
|
|
||||||
found = ( tavl_delete( &c->c_ops, op, operation_client_cmp ) == op );
|
ldap_pvt_thread_mutex_lock( &op->o_link_mutex );
|
||||||
|
c = op->o_client;
|
||||||
|
ldap_pvt_thread_mutex_unlock( &op->o_link_mutex );
|
||||||
|
if ( !c || !IS_ALIVE( c, c_refcnt ) ) {
|
||||||
|
Debug( LDAP_DEBUG_TRACE, "operation_send_reject: "
|
||||||
|
"not sending msgid=%d, client connid=%lu is dead\n",
|
||||||
|
op->o_client_msgid, op->o_client_connid );
|
||||||
|
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
found = operation_unlink_client( op, c );
|
||||||
if ( !found && !send_anyway ) {
|
if ( !found && !send_anyway ) {
|
||||||
Debug( LDAP_DEBUG_TRACE, "operation_send_reject_locked: "
|
Debug( LDAP_DEBUG_TRACE, "operation_send_reject: "
|
||||||
"msgid=%d not scheduled for client connid=%lu anymore, "
|
"msgid=%d not scheduled for client connid=%lu anymore, "
|
||||||
"not sending\n",
|
"not sending\n",
|
||||||
op->o_client_msgid, c->c_connid );
|
op->o_client_msgid, c->c_connid );
|
||||||
|
|
@ -721,25 +462,21 @@ operation_send_reject_locked(
|
||||||
|
|
||||||
if ( op->o_client_msgid == 0 ) {
|
if ( op->o_client_msgid == 0 ) {
|
||||||
assert( op->o_saved_msgid == 0 && op->o_pin_id );
|
assert( op->o_saved_msgid == 0 && op->o_pin_id );
|
||||||
Debug( LDAP_DEBUG_TRACE, "operation_send_reject_locked: "
|
Debug( LDAP_DEBUG_TRACE, "operation_send_reject: "
|
||||||
"operation pin=%lu is just a pin, not sending\n",
|
"operation pin=%lu is just a pin, not sending\n",
|
||||||
op->o_pin_id );
|
op->o_pin_id );
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
CONNECTION_UNLOCK_INCREF(c);
|
|
||||||
ldap_pvt_thread_mutex_lock( &c->c_io_mutex );
|
ldap_pvt_thread_mutex_lock( &c->c_io_mutex );
|
||||||
|
|
||||||
ber = c->c_pendingber;
|
ber = c->c_pendingber;
|
||||||
if ( ber == NULL && (ber = ber_alloc()) == NULL ) {
|
if ( ber == NULL && (ber = ber_alloc()) == NULL ) {
|
||||||
ldap_pvt_thread_mutex_unlock( &c->c_io_mutex );
|
ldap_pvt_thread_mutex_unlock( &c->c_io_mutex );
|
||||||
Debug( LDAP_DEBUG_ANY, "operation_send_reject_locked: "
|
Debug( LDAP_DEBUG_ANY, "operation_send_reject: "
|
||||||
"ber_alloc failed, closing connid=%lu\n",
|
"ber_alloc failed, closing connid=%lu\n",
|
||||||
c->c_connid );
|
c->c_connid );
|
||||||
CONNECTION_LOCK_DECREF(c);
|
CONNECTION_LOCK_DESTROY(c);
|
||||||
operation_destroy_from_client( op );
|
goto done;
|
||||||
CONNECTION_DESTROY(c);
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
c->c_pendingber = ber;
|
c->c_pendingber = ber;
|
||||||
|
|
||||||
|
|
@ -751,46 +488,8 @@ operation_send_reject_locked(
|
||||||
|
|
||||||
connection_write_cb( -1, 0, c );
|
connection_write_cb( -1, 0, c );
|
||||||
|
|
||||||
CONNECTION_LOCK_DECREF(c);
|
|
||||||
done:
|
done:
|
||||||
operation_destroy_from_client( op );
|
operation_unlink( op );
|
||||||
return LDAP_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
operation_send_reject(
|
|
||||||
LloadOperation *op,
|
|
||||||
int result,
|
|
||||||
const char *msg,
|
|
||||||
int send_anyway )
|
|
||||||
{
|
|
||||||
LloadConnection *c;
|
|
||||||
|
|
||||||
ldap_pvt_thread_mutex_lock( &op->o_link_mutex );
|
|
||||||
c = op->o_client;
|
|
||||||
if ( !c ) {
|
|
||||||
c = op->o_upstream;
|
|
||||||
/* One of the connections has initiated this and keeps a reference, if
|
|
||||||
* client is dead, it must have been the upstream */
|
|
||||||
assert( c );
|
|
||||||
CONNECTION_LOCK(c);
|
|
||||||
ldap_pvt_thread_mutex_unlock( &op->o_link_mutex );
|
|
||||||
Debug( LDAP_DEBUG_TRACE, "operation_send_reject: "
|
|
||||||
"not sending msgid=%d, client connid=%lu is dead\n",
|
|
||||||
op->o_client_msgid, op->o_client_connid );
|
|
||||||
operation_destroy_from_upstream( op );
|
|
||||||
CONNECTION_UNLOCK_OR_DESTROY(c);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
CONNECTION_LOCK(c);
|
|
||||||
ldap_pvt_thread_mutex_unlock( &op->o_link_mutex );
|
|
||||||
|
|
||||||
/* Non-zero return means connection has been unlocked and might be
|
|
||||||
* destroyed */
|
|
||||||
if ( operation_send_reject_locked( op, result, msg, send_anyway ) ==
|
|
||||||
LDAP_SUCCESS ) {
|
|
||||||
CONNECTION_UNLOCK_OR_DESTROY(c);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -803,32 +502,27 @@ operation_send_reject(
|
||||||
void
|
void
|
||||||
operation_lost_upstream( LloadOperation *op )
|
operation_lost_upstream( LloadOperation *op )
|
||||||
{
|
{
|
||||||
LloadConnection *c = op->o_upstream;
|
|
||||||
|
|
||||||
operation_send_reject( op, LDAP_OTHER,
|
operation_send_reject( op, LDAP_OTHER,
|
||||||
"connection to the remote server has been severed", 0 );
|
"connection to the remote server has been severed", 0 );
|
||||||
|
|
||||||
CONNECTION_LOCK(c);
|
|
||||||
op->o_upstream_refcnt--;
|
|
||||||
operation_destroy_from_upstream( op );
|
|
||||||
CONNECTION_UNLOCK(c);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
connection_timeout( LloadConnection *upstream, void *arg )
|
connection_timeout( LloadConnection *upstream, void *arg )
|
||||||
{
|
{
|
||||||
LloadOperation *op;
|
LloadOperation *op;
|
||||||
TAvlnode *ops = NULL, *node;
|
TAvlnode *ops = NULL, *node, *next;
|
||||||
LloadBackend *b = upstream->c_private;
|
LloadBackend *b = upstream->c_private;
|
||||||
time_t threshold = *(time_t *)arg;
|
time_t threshold = *(time_t *)arg;
|
||||||
int rc, nops = 0;
|
int rc, nops = 0;
|
||||||
|
|
||||||
|
CONNECTION_LOCK(upstream);
|
||||||
for ( node = tavl_end( upstream->c_ops, TAVL_DIR_LEFT ); node &&
|
for ( node = tavl_end( upstream->c_ops, TAVL_DIR_LEFT ); node &&
|
||||||
((LloadOperation *)node->avl_data)->o_start <
|
((LloadOperation *)node->avl_data)->o_start <
|
||||||
threshold; /* shortcut */
|
threshold; /* shortcut */
|
||||||
node = tavl_next( node, TAVL_DIR_RIGHT ) ) {
|
node = next ) {
|
||||||
LloadOperation *found_op;
|
LloadOperation *found_op;
|
||||||
|
|
||||||
|
next = tavl_next( node, TAVL_DIR_RIGHT );
|
||||||
op = node->avl_data;
|
op = node->avl_data;
|
||||||
|
|
||||||
/* Have we received another response since? */
|
/* Have we received another response since? */
|
||||||
|
|
@ -836,7 +530,6 @@ connection_timeout( LloadConnection *upstream, void *arg )
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
op->o_upstream_refcnt++;
|
|
||||||
op->o_res = LLOAD_OP_FAILED;
|
op->o_res = LLOAD_OP_FAILED;
|
||||||
found_op = tavl_delete( &upstream->c_ops, op, operation_upstream_cmp );
|
found_op = tavl_delete( &upstream->c_ops, op, operation_upstream_cmp );
|
||||||
assert( op == found_op );
|
assert( op == found_op );
|
||||||
|
|
@ -863,13 +556,15 @@ connection_timeout( LloadConnection *upstream, void *arg )
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( nops == 0 ) {
|
if ( nops == 0 ) {
|
||||||
|
CONNECTION_UNLOCK(upstream);
|
||||||
return LDAP_SUCCESS;
|
return LDAP_SUCCESS;
|
||||||
}
|
}
|
||||||
upstream->c_n_ops_executing -= nops;
|
upstream->c_n_ops_executing -= nops;
|
||||||
|
upstream->c_counters.lc_ops_failed += nops;
|
||||||
Debug( LDAP_DEBUG_STATS, "connection_timeout: "
|
Debug( LDAP_DEBUG_STATS, "connection_timeout: "
|
||||||
"timing out %d operations for connid=%lu\n",
|
"timing out %d operations for connid=%lu\n",
|
||||||
nops, upstream->c_connid );
|
nops, upstream->c_connid );
|
||||||
CONNECTION_UNLOCK_INCREF(upstream);
|
CONNECTION_UNLOCK(upstream);
|
||||||
|
|
||||||
ldap_pvt_thread_mutex_lock( &b->b_mutex );
|
ldap_pvt_thread_mutex_lock( &b->b_mutex );
|
||||||
b->b_n_ops_executing -= nops;
|
b->b_n_ops_executing -= nops;
|
||||||
|
|
@ -877,36 +572,17 @@ connection_timeout( LloadConnection *upstream, void *arg )
|
||||||
|
|
||||||
for ( node = tavl_end( ops, TAVL_DIR_LEFT ); node;
|
for ( node = tavl_end( ops, TAVL_DIR_LEFT ); node;
|
||||||
node = tavl_next( node, TAVL_DIR_RIGHT ) ) {
|
node = tavl_next( node, TAVL_DIR_RIGHT ) ) {
|
||||||
LloadConnection *client;
|
|
||||||
|
|
||||||
op = node->avl_data;
|
op = node->avl_data;
|
||||||
|
|
||||||
ldap_pvt_thread_mutex_lock( &op->o_link_mutex );
|
operation_send_reject( op,
|
||||||
client = op->o_client;
|
|
||||||
if ( !client ) {
|
|
||||||
ldap_pvt_thread_mutex_unlock( &op->o_link_mutex );
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
CONNECTION_LOCK(client);
|
|
||||||
ldap_pvt_thread_mutex_unlock( &op->o_link_mutex );
|
|
||||||
|
|
||||||
/* operation_send_reject_locked unlocks and destroys client on
|
|
||||||
* failure */
|
|
||||||
if ( operation_send_reject_locked( op,
|
|
||||||
op->o_tag == LDAP_REQ_SEARCH ? LDAP_TIMELIMIT_EXCEEDED :
|
op->o_tag == LDAP_REQ_SEARCH ? LDAP_TIMELIMIT_EXCEEDED :
|
||||||
LDAP_ADMINLIMIT_EXCEEDED,
|
LDAP_ADMINLIMIT_EXCEEDED,
|
||||||
"upstream did not respond in time", 0 ) == LDAP_SUCCESS ) {
|
"upstream did not respond in time", 0 );
|
||||||
CONNECTION_UNLOCK_OR_DESTROY(client);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( rc == LDAP_SUCCESS ) {
|
if ( rc == LDAP_SUCCESS ) {
|
||||||
rc = operation_send_abandon( op );
|
rc = operation_send_abandon( op, upstream );
|
||||||
}
|
}
|
||||||
|
operation_unlink( op );
|
||||||
CONNECTION_LOCK(upstream);
|
|
||||||
op->o_upstream_refcnt--;
|
|
||||||
operation_destroy_from_upstream( op );
|
|
||||||
CONNECTION_UNLOCK(upstream);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO: if operation_send_abandon failed, we need to kill the upstream */
|
/* TODO: if operation_send_abandon failed, we need to kill the upstream */
|
||||||
|
|
@ -914,7 +590,13 @@ connection_timeout( LloadConnection *upstream, void *arg )
|
||||||
connection_write_cb( -1, 0, upstream );
|
connection_write_cb( -1, 0, upstream );
|
||||||
}
|
}
|
||||||
|
|
||||||
CONNECTION_LOCK_DECREF(upstream);
|
CONNECTION_LOCK(upstream);
|
||||||
|
if ( upstream->c_state == LLOAD_C_CLOSING && !upstream->c_ops ) {
|
||||||
|
CONNECTION_DESTROY(upstream);
|
||||||
|
} else {
|
||||||
|
CONNECTION_UNLOCK(upstream);
|
||||||
|
}
|
||||||
|
|
||||||
/* just dispose of the AVL, most operations should already be gone */
|
/* just dispose of the AVL, most operations should already be gone */
|
||||||
tavl_free( ops, NULL );
|
tavl_free( ops, NULL );
|
||||||
return LDAP_SUCCESS;
|
return LDAP_SUCCESS;
|
||||||
|
|
@ -933,12 +615,16 @@ operations_timeout( evutil_socket_t s, short what, void *arg )
|
||||||
threshold = slap_get_time() - lload_timeout_api->tv_sec;
|
threshold = slap_get_time() - lload_timeout_api->tv_sec;
|
||||||
|
|
||||||
LDAP_CIRCLEQ_FOREACH ( b, &backend, b_next ) {
|
LDAP_CIRCLEQ_FOREACH ( b, &backend, b_next ) {
|
||||||
|
epoch_t epoch;
|
||||||
|
|
||||||
ldap_pvt_thread_mutex_lock( &b->b_mutex );
|
ldap_pvt_thread_mutex_lock( &b->b_mutex );
|
||||||
if ( b->b_n_ops_executing == 0 ) {
|
if ( b->b_n_ops_executing == 0 ) {
|
||||||
ldap_pvt_thread_mutex_unlock( &b->b_mutex );
|
ldap_pvt_thread_mutex_unlock( &b->b_mutex );
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
epoch = epoch_join();
|
||||||
|
|
||||||
Debug( LDAP_DEBUG_TRACE, "operations_timeout: "
|
Debug( LDAP_DEBUG_TRACE, "operations_timeout: "
|
||||||
"timing out binds for backend uri=%s\n",
|
"timing out binds for backend uri=%s\n",
|
||||||
b->b_uri.bv_val );
|
b->b_uri.bv_val );
|
||||||
|
|
@ -951,6 +637,7 @@ operations_timeout( evutil_socket_t s, short what, void *arg )
|
||||||
connections_walk_last( &b->b_mutex, &b->b_conns, b->b_last_conn,
|
connections_walk_last( &b->b_mutex, &b->b_conns, b->b_last_conn,
|
||||||
connection_timeout, &threshold );
|
connection_timeout, &threshold );
|
||||||
|
|
||||||
|
epoch_leave( epoch );
|
||||||
ldap_pvt_thread_mutex_unlock( &b->b_mutex );
|
ldap_pvt_thread_mutex_unlock( &b->b_mutex );
|
||||||
}
|
}
|
||||||
done:
|
done:
|
||||||
|
|
@ -976,13 +663,12 @@ operation_update_global_rejected( LloadOperation *op )
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
operation_update_conn_counters( LloadOperation *op )
|
operation_update_conn_counters( LloadOperation *op, LloadConnection *upstream )
|
||||||
{
|
{
|
||||||
assert( op->o_upstream != NULL );
|
|
||||||
if ( op->o_res == LLOAD_OP_COMPLETED ) {
|
if ( op->o_res == LLOAD_OP_COMPLETED ) {
|
||||||
op->o_upstream->c_counters.lc_ops_completed++;
|
upstream->c_counters.lc_ops_completed++;
|
||||||
} else {
|
} else {
|
||||||
op->o_upstream->c_counters.lc_ops_failed++;
|
upstream->c_counters.lc_ops_failed++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -172,15 +172,17 @@ LDAP_SLAPD_F (const char *) lload_msgtype2str( ber_tag_t tag );
|
||||||
LDAP_SLAPD_F (int) operation_upstream_cmp( const void *l, const void *r );
|
LDAP_SLAPD_F (int) operation_upstream_cmp( const void *l, const void *r );
|
||||||
LDAP_SLAPD_F (int) operation_client_cmp( const void *l, const void *r );
|
LDAP_SLAPD_F (int) operation_client_cmp( const void *l, const void *r );
|
||||||
LDAP_SLAPD_F (LloadOperation *) operation_init( LloadConnection *c, BerElement *ber );
|
LDAP_SLAPD_F (LloadOperation *) operation_init( LloadConnection *c, BerElement *ber );
|
||||||
LDAP_SLAPD_F (int) operation_send_abandon( LloadOperation *op );
|
LDAP_SLAPD_F (int) operation_send_abandon( LloadOperation *op, LloadConnection *c );
|
||||||
LDAP_SLAPD_F (void) operation_abandon( LloadOperation *op );
|
LDAP_SLAPD_F (void) operation_abandon( LloadOperation *op );
|
||||||
LDAP_SLAPD_F (void) operation_send_reject( LloadOperation *op, int result, const char *msg, int send_anyway );
|
LDAP_SLAPD_F (void) operation_send_reject( LloadOperation *op, int result, const char *msg, int send_anyway );
|
||||||
LDAP_SLAPD_F (int) operation_send_reject_locked( LloadOperation *op, int result, const char *msg, int send_anyway );
|
LDAP_SLAPD_F (int) operation_send_reject_locked( LloadOperation *op, int result, const char *msg, int send_anyway );
|
||||||
LDAP_SLAPD_F (void) operation_lost_upstream( LloadOperation *op );
|
LDAP_SLAPD_F (void) operation_lost_upstream( LloadOperation *op );
|
||||||
LDAP_SLAPD_F (void) operation_destroy_from_client( LloadOperation *op );
|
LDAP_SLAPD_F (void) operation_destroy( LloadOperation *op );
|
||||||
LDAP_SLAPD_F (void) operation_destroy_from_upstream( LloadOperation *op );
|
LDAP_SLAPD_F (int) operation_unlink( LloadOperation *op );
|
||||||
|
LDAP_SLAPD_F (int) operation_unlink_client( LloadOperation *op, LloadConnection *client );
|
||||||
|
LDAP_SLAPD_F (int) operation_unlink_upstream( LloadOperation *op, LloadConnection *upstream );
|
||||||
LDAP_SLAPD_F (void) operations_timeout( evutil_socket_t s, short what, void *arg );
|
LDAP_SLAPD_F (void) operations_timeout( evutil_socket_t s, short what, void *arg );
|
||||||
LDAP_SLAPD_F (void) operation_update_conn_counters( LloadOperation *op );
|
LDAP_SLAPD_F (void) operation_update_conn_counters( LloadOperation *op, LloadConnection *upstream );
|
||||||
LDAP_SLAPD_F (void) operation_update_backend_counters( LloadOperation *op, LloadBackend *b );
|
LDAP_SLAPD_F (void) operation_update_backend_counters( LloadOperation *op, LloadBackend *b );
|
||||||
LDAP_SLAPD_F (void) operation_update_global_rejected( LloadOperation *op );
|
LDAP_SLAPD_F (void) operation_update_global_rejected( LloadOperation *op );
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,8 @@ static const sasl_callback_t client_callbacks[] = {
|
||||||
};
|
};
|
||||||
#endif /* HAVE_CYRUS_SASL */
|
#endif /* HAVE_CYRUS_SASL */
|
||||||
|
|
||||||
|
static void upstream_unlink( LloadConnection *upstream );
|
||||||
|
|
||||||
int
|
int
|
||||||
forward_response( LloadConnection *client, LloadOperation *op, BerElement *ber )
|
forward_response( LloadConnection *client, LloadOperation *op, BerElement *ber )
|
||||||
{
|
{
|
||||||
|
|
@ -101,13 +103,13 @@ forward_final_response(
|
||||||
"connid=%lu msgid=%d finishing up with a request for "
|
"connid=%lu msgid=%d finishing up with a request for "
|
||||||
"client connid=%lu\n",
|
"client connid=%lu\n",
|
||||||
op->o_upstream_connid, op->o_upstream_msgid, op->o_client_connid );
|
op->o_upstream_connid, op->o_upstream_msgid, op->o_client_connid );
|
||||||
|
|
||||||
rc = forward_response( client, op, ber );
|
rc = forward_response( client, op, ber );
|
||||||
CONNECTION_LOCK(op->o_upstream);
|
|
||||||
op->o_res = LLOAD_OP_COMPLETED;
|
op->o_res = LLOAD_OP_COMPLETED;
|
||||||
if ( !op->o_pin_id || !op->o_upstream_refcnt-- ) {
|
if ( !op->o_pin_id ) {
|
||||||
operation_destroy_from_upstream( op );
|
operation_unlink( op );
|
||||||
}
|
}
|
||||||
CONNECTION_UNLOCK(op->o_upstream);
|
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
@ -177,11 +179,13 @@ handle_one_response( LloadConnection *c )
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CONNECTION_LOCK(c);
|
||||||
if ( needle.o_upstream_msgid == 0 ) {
|
if ( needle.o_upstream_msgid == 0 ) {
|
||||||
return handle_unsolicited( c, ber );
|
return handle_unsolicited( c, ber );
|
||||||
} else if ( !( op = tavl_find(
|
} else if ( !( op = tavl_find(
|
||||||
c->c_ops, &needle, operation_upstream_cmp ) ) ) {
|
c->c_ops, &needle, operation_upstream_cmp ) ) ) {
|
||||||
/* Already abandoned, do nothing */
|
/* Already abandoned, do nothing */
|
||||||
|
CONNECTION_UNLOCK(c);
|
||||||
ber_free( ber, 1 );
|
ber_free( ber, 1 );
|
||||||
return rc;
|
return rc;
|
||||||
/*
|
/*
|
||||||
|
|
@ -190,6 +194,7 @@ handle_one_response( LloadConnection *c )
|
||||||
event_del( c->c_read_event );
|
event_del( c->c_read_event );
|
||||||
*/
|
*/
|
||||||
} else {
|
} else {
|
||||||
|
CONNECTION_UNLOCK(c);
|
||||||
/*
|
/*
|
||||||
op->o_response_pending = ber;
|
op->o_response_pending = ber;
|
||||||
*/
|
*/
|
||||||
|
|
@ -239,40 +244,14 @@ handle_one_response( LloadConnection *c )
|
||||||
if ( handler ) {
|
if ( handler ) {
|
||||||
LloadConnection *client;
|
LloadConnection *client;
|
||||||
|
|
||||||
op->o_upstream_refcnt++;
|
|
||||||
CONNECTION_UNLOCK_INCREF(c);
|
|
||||||
|
|
||||||
ldap_pvt_thread_mutex_lock( &op->o_link_mutex );
|
ldap_pvt_thread_mutex_lock( &op->o_link_mutex );
|
||||||
client = op->o_client;
|
client = op->o_client;
|
||||||
if ( client ) {
|
|
||||||
CONNECTION_LOCK(client);
|
|
||||||
if ( client->c_live ) {
|
|
||||||
op->o_client_refcnt++;
|
|
||||||
CONNECTION_UNLOCK_INCREF(client);
|
|
||||||
} else {
|
|
||||||
CONNECTION_UNLOCK(client);
|
|
||||||
client = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ldap_pvt_thread_mutex_unlock( &op->o_link_mutex );
|
ldap_pvt_thread_mutex_unlock( &op->o_link_mutex );
|
||||||
|
if ( client && IS_ALIVE( client, c_refcnt ) ) {
|
||||||
if ( client ) {
|
|
||||||
rc = handler( client, op, ber );
|
rc = handler( client, op, ber );
|
||||||
CONNECTION_LOCK_DECREF(client);
|
|
||||||
op->o_client_refcnt--;
|
|
||||||
if ( !op->o_client_refcnt ) {
|
|
||||||
operation_destroy_from_client( op );
|
|
||||||
}
|
|
||||||
CONNECTION_UNLOCK_OR_DESTROY(client);
|
|
||||||
} else {
|
} else {
|
||||||
ber_free( ber, 1 );
|
ber_free( ber, 1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
CONNECTION_LOCK_DECREF(c);
|
|
||||||
op->o_upstream_refcnt--;
|
|
||||||
if ( !client || !op->o_upstream_refcnt ) {
|
|
||||||
operation_destroy_from_upstream( op );
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
assert(0);
|
assert(0);
|
||||||
ber_free( ber, 1 );
|
ber_free( ber, 1 );
|
||||||
|
|
@ -284,9 +263,8 @@ fail:
|
||||||
"error on processing a response (%s) on upstream connection "
|
"error on processing a response (%s) on upstream connection "
|
||||||
"connid=%ld, tag=%lx\n",
|
"connid=%ld, tag=%lx\n",
|
||||||
lload_msgtype2str( tag ), c->c_connid, tag );
|
lload_msgtype2str( tag ), c->c_connid, tag );
|
||||||
CONNECTION_DESTROY(c);
|
CONNECTION_LOCK_DESTROY(c);
|
||||||
}
|
}
|
||||||
/* We leave the connection locked */
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -459,19 +437,16 @@ upstream_bind_cb( LloadConnection *c )
|
||||||
ber_len_t len;
|
ber_len_t len;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
CONNECTION_UNLOCK_INCREF(c);
|
|
||||||
if ( ber_peek_tag( ber, &len ) == LDAP_TAG_SASL_RES_CREDS &&
|
if ( ber_peek_tag( ber, &len ) == LDAP_TAG_SASL_RES_CREDS &&
|
||||||
ber_scanf( ber, "m", &scred ) == LBER_ERROR ) {
|
ber_scanf( ber, "m", &scred ) == LBER_ERROR ) {
|
||||||
Debug( LDAP_DEBUG_ANY, "upstream_bind_cb: "
|
Debug( LDAP_DEBUG_ANY, "upstream_bind_cb: "
|
||||||
"sasl bind response malformed\n" );
|
"sasl bind response malformed\n" );
|
||||||
CONNECTION_LOCK_DECREF(c);
|
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = sasl_bind_step( c, &scred, &ccred );
|
rc = sasl_bind_step( c, &scred, &ccred );
|
||||||
if ( rc != SASL_OK &&
|
if ( rc != SASL_OK &&
|
||||||
( rc != SASL_CONTINUE || result == LDAP_SUCCESS ) ) {
|
( rc != SASL_CONTINUE || result == LDAP_SUCCESS ) ) {
|
||||||
CONNECTION_LOCK_DECREF(c);
|
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -482,7 +457,6 @@ upstream_bind_cb( LloadConnection *c )
|
||||||
outber = c->c_pendingber;
|
outber = c->c_pendingber;
|
||||||
if ( outber == NULL && (outber = ber_alloc()) == NULL ) {
|
if ( outber == NULL && (outber = ber_alloc()) == NULL ) {
|
||||||
ldap_pvt_thread_mutex_unlock( &c->c_io_mutex );
|
ldap_pvt_thread_mutex_unlock( &c->c_io_mutex );
|
||||||
CONNECTION_LOCK_DECREF(c);
|
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
c->c_pendingber = outber;
|
c->c_pendingber = outber;
|
||||||
|
|
@ -496,27 +470,25 @@ upstream_bind_cb( LloadConnection *c )
|
||||||
|
|
||||||
connection_write_cb( -1, 0, c );
|
connection_write_cb( -1, 0, c );
|
||||||
|
|
||||||
CONNECTION_LOCK_DECREF(c);
|
|
||||||
if ( rc == SASL_OK ) {
|
if ( rc == SASL_OK ) {
|
||||||
BER_BVZERO( &c->c_sasl_bind_mech );
|
BER_BVZERO( &c->c_sasl_bind_mech );
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
CONNECTION_LOCK_DECREF(c);
|
|
||||||
}
|
}
|
||||||
if ( result == LDAP_SASL_BIND_IN_PROGRESS ) {
|
if ( result == LDAP_SASL_BIND_IN_PROGRESS ) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
#endif /* HAVE_CYRUS_SASL */
|
#endif /* HAVE_CYRUS_SASL */
|
||||||
|
CONNECTION_LOCK(c);
|
||||||
c->c_pdu_cb = handle_one_response;
|
c->c_pdu_cb = handle_one_response;
|
||||||
c->c_state = LLOAD_C_READY;
|
c->c_state = LLOAD_C_READY;
|
||||||
c->c_type = LLOAD_C_OPEN;
|
c->c_type = LLOAD_C_OPEN;
|
||||||
c->c_read_timeout = NULL;
|
c->c_read_timeout = NULL;
|
||||||
event_add( c->c_read_event, c->c_read_timeout );
|
|
||||||
Debug( LDAP_DEBUG_CONNS, "upstream_bind_cb: "
|
Debug( LDAP_DEBUG_CONNS, "upstream_bind_cb: "
|
||||||
"connid=%lu finished binding, now active\n",
|
"connid=%lu finished binding, now active\n",
|
||||||
c->c_connid );
|
c->c_connid );
|
||||||
CONNECTION_UNLOCK_INCREF(c);
|
CONNECTION_UNLOCK(c);
|
||||||
ldap_pvt_thread_mutex_lock( &b->b_mutex );
|
ldap_pvt_thread_mutex_lock( &b->b_mutex );
|
||||||
LDAP_CIRCLEQ_REMOVE( &b->b_preparing, c, c_next );
|
LDAP_CIRCLEQ_REMOVE( &b->b_preparing, c, c_next );
|
||||||
b->b_active++;
|
b->b_active++;
|
||||||
|
|
@ -531,7 +503,6 @@ upstream_bind_cb( LloadConnection *c )
|
||||||
b->b_last_conn = c;
|
b->b_last_conn = c;
|
||||||
backend_retry( b );
|
backend_retry( b );
|
||||||
ldap_pvt_thread_mutex_unlock( &b->b_mutex );
|
ldap_pvt_thread_mutex_unlock( &b->b_mutex );
|
||||||
CONNECTION_LOCK_DECREF(c);
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
Debug( LDAP_DEBUG_ANY, "upstream_bind_cb: "
|
Debug( LDAP_DEBUG_ANY, "upstream_bind_cb: "
|
||||||
|
|
@ -540,12 +511,13 @@ upstream_bind_cb( LloadConnection *c )
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
event_add( c->c_read_event, c->c_read_timeout );
|
||||||
ber_free( ber, 1 );
|
ber_free( ber, 1 );
|
||||||
return LDAP_SUCCESS;
|
return -1;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
|
CONNECTION_LOCK_DESTROY(c);
|
||||||
ber_free( ber, 1 );
|
ber_free( ber, 1 );
|
||||||
CONNECTION_DESTROY(c);
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -556,9 +528,17 @@ upstream_bind( void *ctx, void *arg )
|
||||||
BerElement *ber;
|
BerElement *ber;
|
||||||
ber_int_t msgid;
|
ber_int_t msgid;
|
||||||
|
|
||||||
CONNECTION_LOCK_DECREF(c);
|
/* A reference was passed on to us */
|
||||||
|
assert( IS_ALIVE( c, c_refcnt ) );
|
||||||
|
|
||||||
|
if ( !IS_ALIVE( c, c_live ) ) {
|
||||||
|
RELEASE_REF( c, c_refcnt, c->c_destroy );
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
CONNECTION_LOCK(c);
|
||||||
c->c_pdu_cb = upstream_bind_cb;
|
c->c_pdu_cb = upstream_bind_cb;
|
||||||
CONNECTION_UNLOCK_INCREF(c);
|
CONNECTION_UNLOCK(c);
|
||||||
|
|
||||||
ldap_pvt_thread_mutex_lock( &c->c_io_mutex );
|
ldap_pvt_thread_mutex_lock( &c->c_io_mutex );
|
||||||
ber = c->c_pendingber;
|
ber = c->c_pendingber;
|
||||||
|
|
@ -599,16 +579,18 @@ upstream_bind( void *ctx, void *arg )
|
||||||
|
|
||||||
connection_write_cb( -1, 0, c );
|
connection_write_cb( -1, 0, c );
|
||||||
|
|
||||||
CONNECTION_LOCK_DECREF(c);
|
CONNECTION_LOCK(c);
|
||||||
c->c_read_timeout = lload_timeout_net;
|
c->c_read_timeout = lload_timeout_net;
|
||||||
event_add( c->c_read_event, c->c_read_timeout );
|
event_add( c->c_read_event, c->c_read_timeout );
|
||||||
CONNECTION_UNLOCK_OR_DESTROY(c);
|
CONNECTION_UNLOCK(c);
|
||||||
|
|
||||||
|
RELEASE_REF( c, c_refcnt, c->c_destroy );
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
ldap_pvt_thread_mutex_unlock( &c->c_io_mutex );
|
ldap_pvt_thread_mutex_unlock( &c->c_io_mutex );
|
||||||
CONNECTION_LOCK_DESTROY(c);
|
CONNECTION_LOCK_DESTROY(c);
|
||||||
|
RELEASE_REF( c, c_refcnt, c->c_destroy );
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -621,6 +603,7 @@ upstream_finish( LloadConnection *c )
|
||||||
LloadBackend *b = c->c_private;
|
LloadBackend *b = c->c_private;
|
||||||
int is_bindconn = 0;
|
int is_bindconn = 0;
|
||||||
|
|
||||||
|
assert( c->c_live );
|
||||||
c->c_pdu_cb = handle_one_response;
|
c->c_pdu_cb = handle_one_response;
|
||||||
|
|
||||||
/* Unless we are configured to use the VC exop, consider allocating the
|
/* Unless we are configured to use the VC exop, consider allocating the
|
||||||
|
|
@ -675,7 +658,8 @@ upstream_finish( LloadConnection *c )
|
||||||
c->c_connid );
|
c->c_connid );
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
c->c_refcnt++;
|
/* keep a reference for upstream_bind */
|
||||||
|
acquire_ref( &c->c_refcnt );
|
||||||
|
|
||||||
Debug( LDAP_DEBUG_CONNS, "upstream_finish: "
|
Debug( LDAP_DEBUG_CONNS, "upstream_finish: "
|
||||||
"scheduled a bind callback for connid=%lu\n",
|
"scheduled a bind callback for connid=%lu\n",
|
||||||
|
|
@ -697,6 +681,7 @@ upstream_tls_handshake_cb( evutil_socket_t s, short what, void *arg )
|
||||||
{
|
{
|
||||||
LloadConnection *c = arg;
|
LloadConnection *c = arg;
|
||||||
LloadBackend *b;
|
LloadBackend *b;
|
||||||
|
epoch_t epoch;
|
||||||
int rc = LDAP_SUCCESS;
|
int rc = LDAP_SUCCESS;
|
||||||
|
|
||||||
CONNECTION_LOCK(c);
|
CONNECTION_LOCK(c);
|
||||||
|
|
@ -737,9 +722,9 @@ upstream_tls_handshake_cb( evutil_socket_t s, short what, void *arg )
|
||||||
c->c_connid );
|
c->c_connid );
|
||||||
c->c_is_tls = LLOAD_TLS_ESTABLISHED;
|
c->c_is_tls = LLOAD_TLS_ESTABLISHED;
|
||||||
|
|
||||||
CONNECTION_UNLOCK_INCREF(c);
|
CONNECTION_UNLOCK(c);
|
||||||
ldap_pvt_thread_mutex_lock( &b->b_mutex );
|
ldap_pvt_thread_mutex_lock( &b->b_mutex );
|
||||||
CONNECTION_LOCK_DECREF(c);
|
CONNECTION_LOCK(c);
|
||||||
|
|
||||||
rc = upstream_finish( c );
|
rc = upstream_finish( c );
|
||||||
ldap_pvt_thread_mutex_unlock( &b->b_mutex );
|
ldap_pvt_thread_mutex_unlock( &b->b_mutex );
|
||||||
|
|
@ -753,14 +738,18 @@ upstream_tls_handshake_cb( evutil_socket_t s, short what, void *arg )
|
||||||
"connid=%lu need write rc=%d\n",
|
"connid=%lu need write rc=%d\n",
|
||||||
c->c_connid, rc );
|
c->c_connid, rc );
|
||||||
}
|
}
|
||||||
CONNECTION_UNLOCK_OR_DESTROY(c);
|
CONNECTION_UNLOCK(c);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
Debug( LDAP_DEBUG_CONNS, "upstream_tls_handshake_cb: "
|
Debug( LDAP_DEBUG_CONNS, "upstream_tls_handshake_cb: "
|
||||||
"connid=%lu failed rc=%d\n",
|
"connid=%lu failed rc=%d\n",
|
||||||
c->c_connid, rc );
|
c->c_connid, rc );
|
||||||
|
|
||||||
|
assert( c->c_ops == NULL );
|
||||||
|
epoch = epoch_join();
|
||||||
CONNECTION_DESTROY(c);
|
CONNECTION_DESTROY(c);
|
||||||
|
epoch_leave( epoch );
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
|
@ -774,6 +763,7 @@ upstream_starttls( LloadConnection *c )
|
||||||
ber_tag_t tag;
|
ber_tag_t tag;
|
||||||
|
|
||||||
c->c_currentber = NULL;
|
c->c_currentber = NULL;
|
||||||
|
CONNECTION_LOCK(c);
|
||||||
|
|
||||||
if ( ber_scanf( ber, "it", &msgid, &tag ) == LBER_ERROR ) {
|
if ( ber_scanf( ber, "it", &msgid, &tag ) == LBER_ERROR ) {
|
||||||
Debug( LDAP_DEBUG_ANY, "upstream_starttls: "
|
Debug( LDAP_DEBUG_ANY, "upstream_starttls: "
|
||||||
|
|
@ -824,9 +814,9 @@ upstream_starttls( LloadConnection *c )
|
||||||
}
|
}
|
||||||
c->c_is_tls = LLOAD_CLEARTEXT;
|
c->c_is_tls = LLOAD_CLEARTEXT;
|
||||||
|
|
||||||
CONNECTION_UNLOCK_INCREF(c);
|
CONNECTION_UNLOCK(c);
|
||||||
ldap_pvt_thread_mutex_lock( &b->b_mutex );
|
ldap_pvt_thread_mutex_lock( &b->b_mutex );
|
||||||
CONNECTION_LOCK_DECREF(c);
|
CONNECTION_LOCK(c);
|
||||||
|
|
||||||
rc = upstream_finish( c );
|
rc = upstream_finish( c );
|
||||||
ldap_pvt_thread_mutex_unlock( &b->b_mutex );
|
ldap_pvt_thread_mutex_unlock( &b->b_mutex );
|
||||||
|
|
@ -836,7 +826,7 @@ upstream_starttls( LloadConnection *c )
|
||||||
}
|
}
|
||||||
|
|
||||||
ber_free( ber, 1 );
|
ber_free( ber, 1 );
|
||||||
CONNECTION_UNLOCK_OR_DESTROY(c);
|
CONNECTION_UNLOCK(c);
|
||||||
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
@ -884,6 +874,7 @@ upstream_init( ber_socket_t s, LloadBackend *b )
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CONNECTION_LOCK(c);
|
||||||
c->c_private = b;
|
c->c_private = b;
|
||||||
c->c_is_tls = b->b_tls;
|
c->c_is_tls = b->b_tls;
|
||||||
c->c_pdu_cb = handle_one_response;
|
c->c_pdu_cb = handle_one_response;
|
||||||
|
|
@ -939,14 +930,15 @@ upstream_init( ber_socket_t s, LloadBackend *b )
|
||||||
ldap_pvt_thread_mutex_unlock( &c->c_io_mutex );
|
ldap_pvt_thread_mutex_unlock( &c->c_io_mutex );
|
||||||
|
|
||||||
c->c_pdu_cb = upstream_starttls;
|
c->c_pdu_cb = upstream_starttls;
|
||||||
CONNECTION_UNLOCK_INCREF(c);
|
CONNECTION_UNLOCK(c);
|
||||||
connection_write_cb( s, 0, c );
|
connection_write_cb( s, 0, c );
|
||||||
CONNECTION_LOCK_DECREF(c);
|
CONNECTION_LOCK(c);
|
||||||
}
|
}
|
||||||
event_add( c->c_read_event, c->c_read_timeout );
|
event_add( c->c_read_event, c->c_read_timeout );
|
||||||
|
|
||||||
c->c_destroy = upstream_destroy;
|
c->c_destroy = upstream_destroy;
|
||||||
CONNECTION_UNLOCK_OR_DESTROY(c);
|
c->c_unlink = upstream_unlink;
|
||||||
|
CONNECTION_UNLOCK(c);
|
||||||
|
|
||||||
return c;
|
return c;
|
||||||
|
|
||||||
|
|
@ -961,46 +953,39 @@ fail:
|
||||||
}
|
}
|
||||||
|
|
||||||
c->c_state = LLOAD_C_INVALID;
|
c->c_state = LLOAD_C_INVALID;
|
||||||
CONNECTION_DESTROY(c);
|
c->c_live--;
|
||||||
assert( c == NULL );
|
c->c_refcnt--;
|
||||||
|
connection_destroy( c );
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
static void
|
||||||
upstream_destroy( LloadConnection *c )
|
upstream_unlink( LloadConnection *c )
|
||||||
{
|
{
|
||||||
LloadBackend *b = c->c_private;
|
LloadBackend *b = c->c_private;
|
||||||
struct event *read_event, *write_event;
|
struct event *read_event, *write_event;
|
||||||
TAvlnode *root, *node;
|
TAvlnode *root;
|
||||||
long freed, executing;
|
long freed, executing;
|
||||||
enum sc_state state;
|
|
||||||
|
|
||||||
Debug( LDAP_DEBUG_CONNS, "upstream_destroy: "
|
Debug( LDAP_DEBUG_CONNS, "upstream_unlink: "
|
||||||
"freeing connection connid=%lu\n",
|
"removing upstream connid=%lu\n",
|
||||||
c->c_connid );
|
c->c_connid );
|
||||||
|
|
||||||
assert( c->c_state != LLOAD_C_INVALID );
|
assert( c->c_state != LLOAD_C_INVALID );
|
||||||
state = c->c_state;
|
assert( c->c_state != LLOAD_C_DYING );
|
||||||
c->c_state = LLOAD_C_INVALID;
|
|
||||||
|
c->c_state = LLOAD_C_DYING;
|
||||||
|
|
||||||
|
read_event = c->c_read_event;
|
||||||
|
write_event = c->c_write_event;
|
||||||
|
|
||||||
root = c->c_ops;
|
root = c->c_ops;
|
||||||
c->c_ops = NULL;
|
c->c_ops = NULL;
|
||||||
executing = c->c_n_ops_executing;
|
executing = c->c_n_ops_executing;
|
||||||
c->c_n_ops_executing = 0;
|
c->c_n_ops_executing = 0;
|
||||||
|
|
||||||
read_event = c->c_read_event;
|
CONNECTION_UNLOCK(c);
|
||||||
write_event = c->c_write_event;
|
|
||||||
|
|
||||||
for ( node = tavl_end( root, TAVL_DIR_LEFT ); node;
|
|
||||||
node = tavl_next( node, TAVL_DIR_RIGHT ) ) {
|
|
||||||
LloadOperation *op = node->avl_data;
|
|
||||||
|
|
||||||
op->o_res = LLOAD_OP_FAILED;
|
|
||||||
op->o_upstream_refcnt++;
|
|
||||||
}
|
|
||||||
|
|
||||||
CONNECTION_UNLOCK_INCREF(c);
|
|
||||||
|
|
||||||
freed = tavl_free( root, (AVL_FREE)operation_lost_upstream );
|
freed = tavl_free( root, (AVL_FREE)operation_lost_upstream );
|
||||||
assert( freed == executing );
|
assert( freed == executing );
|
||||||
|
|
@ -1018,8 +1003,6 @@ upstream_destroy( LloadConnection *c )
|
||||||
event_del( write_event );
|
event_del( write_event );
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Remove from the backend on first pass */
|
|
||||||
if ( state != LLOAD_C_DYING ) {
|
|
||||||
ldap_pvt_thread_mutex_lock( &b->b_mutex );
|
ldap_pvt_thread_mutex_lock( &b->b_mutex );
|
||||||
if ( c->c_type == LLOAD_C_PREPARING ) {
|
if ( c->c_type == LLOAD_C_PREPARING ) {
|
||||||
LDAP_CIRCLEQ_REMOVE( &b->b_preparing, c, c_next );
|
LDAP_CIRCLEQ_REMOVE( &b->b_preparing, c, c_next );
|
||||||
|
|
@ -1053,9 +1036,20 @@ upstream_destroy( LloadConnection *c )
|
||||||
b->b_n_ops_executing -= executing;
|
b->b_n_ops_executing -= executing;
|
||||||
backend_retry( b );
|
backend_retry( b );
|
||||||
ldap_pvt_thread_mutex_unlock( &b->b_mutex );
|
ldap_pvt_thread_mutex_unlock( &b->b_mutex );
|
||||||
}
|
|
||||||
|
|
||||||
CONNECTION_LOCK_DECREF(c);
|
CONNECTION_LOCK(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
upstream_destroy( LloadConnection *c )
|
||||||
|
{
|
||||||
|
Debug( LDAP_DEBUG_CONNS, "upstream_destroy: "
|
||||||
|
"freeing connection connid=%lu\n",
|
||||||
|
c->c_connid );
|
||||||
|
|
||||||
|
CONNECTION_LOCK(c);
|
||||||
|
assert( c->c_state == LLOAD_C_DYING );
|
||||||
|
c->c_state = LLOAD_C_INVALID;
|
||||||
|
|
||||||
if ( c->c_read_event ) {
|
if ( c->c_read_event ) {
|
||||||
event_free( c->c_read_event );
|
event_free( c->c_read_event );
|
||||||
|
|
@ -1067,21 +1061,8 @@ upstream_destroy( LloadConnection *c )
|
||||||
c->c_write_event = NULL;
|
c->c_write_event = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
if ( c->c_type != LLOAD_C_BIND ) {
|
||||||
* If we attempted to destroy any operations, we might have lent a new
|
|
||||||
* refcnt token for a thread that raced us to that, let them call us again
|
|
||||||
* later
|
|
||||||
*/
|
|
||||||
assert( c->c_refcnt >= 0 );
|
|
||||||
if ( c->c_refcnt ) {
|
|
||||||
c->c_state = LLOAD_C_DYING;
|
|
||||||
Debug( LDAP_DEBUG_CONNS, "upstream_destroy: "
|
|
||||||
"connid=%lu aborting with refcnt=%d\n",
|
|
||||||
c->c_connid, c->c_refcnt );
|
|
||||||
CONNECTION_UNLOCK(c);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
BER_BVZERO( &c->c_sasl_bind_mech );
|
BER_BVZERO( &c->c_sasl_bind_mech );
|
||||||
|
}
|
||||||
connection_destroy( c );
|
connection_destroy( c );
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue