1 /* $OpenLDAP$ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 1998-2021 The OpenLDAP Foundation.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted only as authorized by the OpenLDAP
9  * Public License.
10  *
11  * A copy of this license is available in the file LICENSE in the
12  * top-level directory of the distribution or, alternatively, at
13  * <http://www.OpenLDAP.org/license.html>.
14  */
15 
16 #include "portable.h"
17 
18 #include "lutil.h"
19 #include "lload.h"
20 
21 ldap_pvt_thread_mutex_t lload_pin_mutex;
22 unsigned long lload_next_pin = 1;
23 
24 TAvlnode *lload_control_actions = NULL;
25 TAvlnode *lload_exop_actions = NULL;
26 enum op_restriction lload_default_exop_action = LLOAD_OP_NOT_RESTRICTED;
27 
28 ber_tag_t
slap_req2res(ber_tag_t tag)29 slap_req2res( ber_tag_t tag )
30 {
31     switch ( tag ) {
32         case LDAP_REQ_ADD:
33         case LDAP_REQ_BIND:
34         case LDAP_REQ_COMPARE:
35         case LDAP_REQ_EXTENDED:
36         case LDAP_REQ_MODIFY:
37         case LDAP_REQ_MODRDN:
38             tag++;
39             break;
40 
41         case LDAP_REQ_DELETE:
42             tag = LDAP_RES_DELETE;
43             break;
44 
45         case LDAP_REQ_ABANDON:
46         case LDAP_REQ_UNBIND:
47             tag = LBER_SEQUENCE;
48             break;
49 
50         case LDAP_REQ_SEARCH:
51             tag = LDAP_RES_SEARCH_RESULT;
52             break;
53 
54         default:
55             tag = LBER_SEQUENCE;
56     }
57 
58     return tag;
59 }
60 
61 const char *
lload_msgtype2str(ber_tag_t tag)62 lload_msgtype2str( ber_tag_t tag )
63 {
64     switch ( tag ) {
65         case LDAP_REQ_ABANDON: return "abandon request";
66         case LDAP_REQ_ADD: return "add request";
67         case LDAP_REQ_BIND: return "bind request";
68         case LDAP_REQ_COMPARE: return "compare request";
69         case LDAP_REQ_DELETE: return "delete request";
70         case LDAP_REQ_EXTENDED: return "extended request";
71         case LDAP_REQ_MODIFY: return "modify request";
72         case LDAP_REQ_RENAME: return "rename request";
73         case LDAP_REQ_SEARCH: return "search request";
74         case LDAP_REQ_UNBIND: return "unbind request";
75 
76         case LDAP_RES_ADD: return "add result";
77         case LDAP_RES_BIND: return "bind result";
78         case LDAP_RES_COMPARE: return "compare result";
79         case LDAP_RES_DELETE: return "delete result";
80         case LDAP_RES_EXTENDED: return "extended result";
81         case LDAP_RES_INTERMEDIATE: return "intermediate response";
82         case LDAP_RES_MODIFY: return "modify result";
83         case LDAP_RES_RENAME: return "rename result";
84         case LDAP_RES_SEARCH_ENTRY: return "search-entry response";
85         case LDAP_RES_SEARCH_REFERENCE: return "search-reference response";
86         case LDAP_RES_SEARCH_RESULT: return "search result";
87     }
88     return "unknown message";
89 }
90 
91 int
lload_restriction_cmp(const void * left,const void * right)92 lload_restriction_cmp( const void *left, const void *right )
93 {
94     const struct restriction_entry *l = left, *r = right;
95     return ber_bvcmp( &l->oid, &r->oid );
96 }
97 
98 int
operation_client_cmp(const void * left,const void * right)99 operation_client_cmp( const void *left, const void *right )
100 {
101     const LloadOperation *l = left, *r = right;
102 
103     assert( l->o_client_connid == r->o_client_connid );
104     if ( l->o_client_msgid || r->o_client_msgid ) {
105         return ( l->o_client_msgid < r->o_client_msgid ) ?
106                 -1 :
107                 ( l->o_client_msgid > r->o_client_msgid );
108     } else {
109         return ( l->o_pin_id < r->o_pin_id ) ? -1 :
110                 ( l->o_pin_id > r->o_pin_id );
111     }
112 }
113 
114 int
operation_upstream_cmp(const void * left,const void * right)115 operation_upstream_cmp( const void *left, const void *right )
116 {
117     const LloadOperation *l = left, *r = right;
118 
119     assert( l->o_upstream_connid == r->o_upstream_connid );
120     if ( l->o_upstream_msgid || r->o_upstream_msgid ) {
121         return ( l->o_upstream_msgid < r->o_upstream_msgid ) ?
122                 -1 :
123                 ( l->o_upstream_msgid > r->o_upstream_msgid );
124     } else {
125         return ( l->o_pin_id < r->o_pin_id ) ? -1 :
126                 ( l->o_pin_id > r->o_pin_id );
127     }
128 }
129 
130 /*
131  * Entered holding c_mutex for now.
132  */
133 LloadOperation *
operation_init(LloadConnection * c,BerElement * ber)134 operation_init( LloadConnection *c, BerElement *ber )
135 {
136     LloadOperation *op;
137     ber_tag_t tag;
138     ber_len_t len;
139     int rc;
140 
141     if ( !IS_ALIVE( c, c_live ) ) {
142         return NULL;
143     }
144 
145     op = ch_calloc( 1, sizeof(LloadOperation) );
146     op->o_client = c;
147     op->o_client_connid = c->c_connid;
148     op->o_ber = ber;
149     gettimeofday( &op->o_start, NULL );
150 
151     ldap_pvt_thread_mutex_init( &op->o_link_mutex );
152 
153     op->o_refcnt = 1;
154 
155     tag = ber_get_int( ber, &op->o_client_msgid );
156     if ( tag != LDAP_TAG_MSGID ) {
157         goto fail;
158     }
159 
160     if ( !op->o_client_msgid ) {
161         goto fail;
162     }
163 
164     CONNECTION_ASSERT_LOCKED(c);
165     rc = ldap_tavl_insert( &c->c_ops, op, operation_client_cmp, ldap_avl_dup_error );
166     if ( rc ) {
167         Debug( LDAP_DEBUG_PACKETS, "operation_init: "
168                 "several operations with same msgid=%d in-flight "
169                 "from client connid=%lu\n",
170                 op->o_client_msgid, op->o_client_connid );
171         goto fail;
172     }
173 
174     tag = op->o_tag = ber_skip_element( ber, &op->o_request );
175     switch ( tag ) {
176         case LBER_ERROR:
177             rc = -1;
178             break;
179     }
180     if ( rc ) {
181         ldap_tavl_delete( &c->c_ops, op, operation_client_cmp );
182         goto fail;
183     }
184 
185     tag = ber_peek_tag( ber, &len );
186     if ( tag == LDAP_TAG_CONTROLS ) {
187         ber_skip_element( ber, &op->o_ctrls );
188     }
189 
190     switch ( op->o_tag ) {
191         case LDAP_REQ_BIND:
192             lload_stats.counters[LLOAD_STATS_OPS_BIND].lc_ops_received++;
193             break;
194         default:
195             lload_stats.counters[LLOAD_STATS_OPS_OTHER].lc_ops_received++;
196             break;
197     }
198 
199     Debug( LDAP_DEBUG_STATS, "operation_init: "
200             "received a new operation, %s with msgid=%d for client "
201             "connid=%lu\n",
202             lload_msgtype2str( op->o_tag ), op->o_client_msgid,
203             op->o_client_connid );
204 
205     c->c_n_ops_executing++;
206     return op;
207 
208 fail:
209     ch_free( op );
210     return NULL;
211 }
212 
213 void
operation_destroy(LloadOperation * op)214 operation_destroy( LloadOperation *op )
215 {
216     Debug( LDAP_DEBUG_TRACE, "operation_destroy: "
217             "op=%p destroyed operation from client connid=%lu, "
218             "client msgid=%d\n",
219             op, op->o_client_connid, op->o_client_msgid );
220 
221     assert( op->o_refcnt == 0 );
222     assert( op->o_client == NULL );
223     assert( op->o_upstream == NULL );
224 
225     ber_free( op->o_ber, 1 );
226     ldap_pvt_thread_mutex_destroy( &op->o_link_mutex );
227     ch_free( op );
228 }
229 
230 int
operation_unlink(LloadOperation * op)231 operation_unlink( LloadOperation *op )
232 {
233     LloadConnection *client, *upstream;
234     uintptr_t prev_refcnt;
235     int result = 0;
236 
237     if ( !( prev_refcnt = try_release_ref(
238                     &op->o_refcnt, op, (dispose_cb *)operation_destroy ) ) ) {
239         return result;
240     }
241 
242     assert( prev_refcnt == 1 );
243 
244     Debug( LDAP_DEBUG_TRACE, "operation_unlink: "
245             "unlinking operation between client connid=%lu and upstream "
246             "connid=%lu "
247             "client msgid=%d\n",
248             op->o_client_connid, op->o_upstream_connid, op->o_client_msgid );
249 
250     checked_lock( &op->o_link_mutex );
251     client = op->o_client;
252     upstream = op->o_upstream;
253 
254     op->o_client = NULL;
255     op->o_upstream = NULL;
256     checked_unlock( &op->o_link_mutex );
257 
258     assert( client || upstream );
259 
260     if ( client ) {
261         result |= operation_unlink_client( op, client );
262         operation_update_global_rejected( op );
263     }
264 
265     if ( upstream ) {
266         result |= operation_unlink_upstream( op, upstream );
267     }
268 
269     return result;
270 }
271 
272 int
operation_unlink_client(LloadOperation * op,LloadConnection * client)273 operation_unlink_client( LloadOperation *op, LloadConnection *client )
274 {
275     LloadOperation *removed;
276     int result = 0;
277 
278     Debug( LDAP_DEBUG_TRACE, "operation_unlink_client: "
279             "unlinking operation op=%p msgid=%d client connid=%lu\n",
280             op, op->o_client_msgid, op->o_client_connid );
281 
282     CONNECTION_LOCK(client);
283     if ( (removed = ldap_tavl_delete(
284                    &client->c_ops, op, operation_client_cmp )) ) {
285         result = LLOAD_OP_DETACHING_CLIENT;
286 
287         assert( op == removed );
288         client->c_n_ops_executing--;
289 
290         if ( op->o_restricted == LLOAD_OP_RESTRICTED_WRITE ) {
291             if ( !--client->c_restricted_inflight &&
292                     client->c_restricted_at >= 0 ) {
293                 if ( lload_write_coherence < 0 ) {
294                     client->c_restricted_at = -1;
295                 } else if ( timerisset( &op->o_last_response ) ) {
296                     client->c_restricted_at = op->o_last_response.tv_sec;
297                 } else {
298                     /* We have to default to o_start just in case we abandoned an
299                      * operation that the backend actually processed */
300                     client->c_restricted_at = op->o_start.tv_sec;
301                 }
302             }
303         }
304 
305         if ( op->o_tag == LDAP_REQ_BIND &&
306                 client->c_state == LLOAD_C_BINDING ) {
307             client->c_state = LLOAD_C_READY;
308             if ( !BER_BVISNULL( &client->c_auth ) ) {
309                 ber_memfree( client->c_auth.bv_val );
310                 BER_BVZERO( &client->c_auth );
311             }
312             if ( !BER_BVISNULL( &client->c_sasl_bind_mech ) ) {
313                 ber_memfree( client->c_sasl_bind_mech.bv_val );
314                 BER_BVZERO( &client->c_sasl_bind_mech );
315             }
316             if ( op->o_pin_id ) {
317                 client->c_pin_id = 0;
318             }
319         }
320     }
321     if ( client->c_state == LLOAD_C_CLOSING && !client->c_ops ) {
322         CONNECTION_DESTROY(client);
323     } else {
324         CONNECTION_UNLOCK(client);
325     }
326 
327     return result;
328 }
329 
330 int
operation_unlink_upstream(LloadOperation * op,LloadConnection * upstream)331 operation_unlink_upstream( LloadOperation *op, LloadConnection *upstream )
332 {
333     LloadOperation *removed;
334     LloadBackend *b = NULL;
335     int result = 0;
336 
337     Debug( LDAP_DEBUG_TRACE, "operation_unlink_upstream: "
338             "unlinking operation op=%p msgid=%d upstream connid=%lu\n",
339             op, op->o_upstream_msgid, op->o_upstream_connid );
340 
341     CONNECTION_LOCK(upstream);
342     if ( (removed = ldap_tavl_delete(
343                    &upstream->c_ops, op, operation_upstream_cmp )) ) {
344         result |= LLOAD_OP_DETACHING_UPSTREAM;
345 
346         assert( op == removed );
347         upstream->c_n_ops_executing--;
348 
349         if ( upstream->c_state == LLOAD_C_BINDING ) {
350             assert( op->o_tag == LDAP_REQ_BIND && upstream->c_ops == NULL );
351             upstream->c_state = LLOAD_C_READY;
352             if ( !BER_BVISNULL( &upstream->c_sasl_bind_mech ) ) {
353                 ber_memfree( upstream->c_sasl_bind_mech.bv_val );
354                 BER_BVZERO( &upstream->c_sasl_bind_mech );
355             }
356         }
357         operation_update_conn_counters( op, upstream );
358         b = upstream->c_backend;
359     }
360     if ( upstream->c_state == LLOAD_C_CLOSING && !upstream->c_ops ) {
361         CONNECTION_DESTROY(upstream);
362     } else {
363         CONNECTION_UNLOCK(upstream);
364     }
365 
366     if ( b ) {
367         checked_lock( &b->b_mutex );
368         b->b_n_ops_executing--;
369         operation_update_backend_counters( op, b );
370         checked_unlock( &b->b_mutex );
371     }
372 
373     return result;
374 }
375 
376 int
operation_send_abandon(LloadOperation * op,LloadConnection * upstream)377 operation_send_abandon( LloadOperation *op, LloadConnection *upstream )
378 {
379     BerElement *ber;
380     int rc = -1;
381 
382     if ( !IS_ALIVE( upstream, c_live ) ) {
383         return rc;
384     }
385 
386     checked_lock( &upstream->c_io_mutex );
387     ber = upstream->c_pendingber;
388     if ( ber == NULL && (ber = ber_alloc()) == NULL ) {
389         Debug( LDAP_DEBUG_ANY, "operation_send_abandon: "
390                 "ber_alloc failed\n" );
391         goto done;
392     }
393     upstream->c_pendingber = ber;
394 
395     Debug( LDAP_DEBUG_TRACE, "operation_send_abandon: "
396             "abandoning %s msgid=%d on connid=%lu\n",
397             lload_msgtype2str( op->o_tag ), op->o_upstream_msgid,
398             op->o_upstream_connid );
399 
400     if ( op->o_tag == LDAP_REQ_BIND ) {
401         rc = ber_printf( ber, "t{tit{ist{s}}}", LDAP_TAG_MESSAGE,
402                 LDAP_TAG_MSGID, upstream->c_next_msgid++,
403                 LDAP_REQ_BIND, LDAP_VERSION3, "", LDAP_AUTH_SASL, "" );
404     } else {
405         rc = ber_printf( ber, "t{titi}", LDAP_TAG_MESSAGE,
406                 LDAP_TAG_MSGID, upstream->c_next_msgid++,
407                 LDAP_REQ_ABANDON, op->o_upstream_msgid );
408     }
409 
410     if ( rc < 0 ) {
411         ber_free( ber, 1 );
412         upstream->c_pendingber = NULL;
413         goto done;
414     }
415     rc = LDAP_SUCCESS;
416 
417 done:
418     checked_unlock( &upstream->c_io_mutex );
419     return rc;
420 }
421 
422 /*
423  * Will remove the operation from its upstream and if it was still there,
424  * sends an abandon request.
425  *
426  * Being called from client_reset or request_abandon, the following hold:
427  * - noone else is processing the read part of the client connection (no new
428  *   operations come in there - relevant for the c_state checks)
429  * - op->o_client_refcnt > op->o_client_live (and it follows that op->o_client != NULL)
430  */
431 void
operation_abandon(LloadOperation * op)432 operation_abandon( LloadOperation *op )
433 {
434     LloadConnection *c;
435 
436     checked_lock( &op->o_link_mutex );
437     c = op->o_upstream;
438     checked_unlock( &op->o_link_mutex );
439     if ( !c || !IS_ALIVE( c, c_live ) ) {
440         goto done;
441     }
442 
443     /* for now consider all abandoned operations completed,
444      * perhaps add a separate counter later */
445     op->o_res = LLOAD_OP_COMPLETED;
446     if ( !operation_unlink_upstream( op, c ) ) {
447         /* The operation has already been abandoned or finished */
448         Debug( LDAP_DEBUG_TRACE, "operation_abandon: "
449                 "%s from connid=%lu msgid=%d not present in connid=%lu any "
450                 "more\n",
451                 lload_msgtype2str( op->o_tag ), op->o_client_connid,
452                 op->o_client_msgid, op->o_upstream_connid );
453         goto done;
454     }
455 
456     if ( operation_send_abandon( op, c ) == LDAP_SUCCESS ) {
457         connection_write_cb( -1, 0, c );
458     }
459 
460 done:
461     operation_unlink( op );
462 }
463 
464 void
operation_send_reject(LloadOperation * op,int result,const char * msg,int send_anyway)465 operation_send_reject(
466         LloadOperation *op,
467         int result,
468         const char *msg,
469         int send_anyway )
470 {
471     LloadConnection *c;
472     BerElement *ber;
473     int found;
474 
475     Debug( LDAP_DEBUG_TRACE, "operation_send_reject: "
476             "rejecting %s from client connid=%lu with message: \"%s\"\n",
477             lload_msgtype2str( op->o_tag ), op->o_client_connid, msg );
478 
479     checked_lock( &op->o_link_mutex );
480     c = op->o_client;
481     checked_unlock( &op->o_link_mutex );
482     if ( !c || !IS_ALIVE( c, c_live ) ) {
483         Debug( LDAP_DEBUG_TRACE, "operation_send_reject: "
484                 "not sending msgid=%d, client connid=%lu is dead\n",
485                 op->o_client_msgid, op->o_client_connid );
486 
487         goto done;
488     }
489 
490     found = operation_unlink_client( op, c );
491     if ( !found && !send_anyway ) {
492         Debug( LDAP_DEBUG_TRACE, "operation_send_reject: "
493                 "msgid=%d not scheduled for client connid=%lu anymore, "
494                 "not sending\n",
495                 op->o_client_msgid, c->c_connid );
496         goto done;
497     }
498 
499     if ( op->o_client_msgid == 0 ) {
500         assert( op->o_saved_msgid == 0 && op->o_pin_id );
501         Debug( LDAP_DEBUG_TRACE, "operation_send_reject: "
502                 "operation pin=%lu is just a pin, not sending\n",
503                 op->o_pin_id );
504         goto done;
505     }
506 
507     checked_lock( &c->c_io_mutex );
508     ber = c->c_pendingber;
509     if ( ber == NULL && (ber = ber_alloc()) == NULL ) {
510         checked_unlock( &c->c_io_mutex );
511         Debug( LDAP_DEBUG_ANY, "operation_send_reject: "
512                 "ber_alloc failed, closing connid=%lu\n",
513                 c->c_connid );
514         CONNECTION_LOCK_DESTROY(c);
515         goto done;
516     }
517     c->c_pendingber = ber;
518 
519     ber_printf( ber, "t{tit{ess}}", LDAP_TAG_MESSAGE,
520             LDAP_TAG_MSGID, op->o_client_msgid,
521             slap_req2res( op->o_tag ), result, "", msg );
522 
523     checked_unlock( &c->c_io_mutex );
524 
525     connection_write_cb( -1, 0, c );
526 
527 done:
528     operation_unlink( op );
529 }
530 
531 /*
532  * Upstream is shutting down, signal the client if necessary, but we have to
533  * call operation_destroy_from_upstream ourselves to detach upstream from the
534  * op.
535  *
536  * Only called from upstream_destroy.
537  */
538 void
operation_lost_upstream(LloadOperation * op)539 operation_lost_upstream( LloadOperation *op )
540 {
541     operation_send_reject( op, LDAP_OTHER,
542             "connection to the remote server has been severed", 0 );
543 }
544 
545 int
connection_timeout(LloadConnection * upstream,void * arg)546 connection_timeout( LloadConnection *upstream, void *arg )
547 {
548     LloadOperation *op;
549     TAvlnode *ops = NULL, *node, *next;
550     LloadBackend *b = upstream->c_backend;
551     struct timeval *threshold = arg;
552     int rc, nops = 0;
553 
554     CONNECTION_LOCK(upstream);
555     for ( node = ldap_tavl_end( upstream->c_ops, TAVL_DIR_LEFT );
556             node && timercmp( &((LloadOperation *)node->avl_data)->o_start,
557                     threshold, < ); /* shortcut */
558             node = next ) {
559         LloadOperation *found_op;
560 
561         next = ldap_tavl_next( node, TAVL_DIR_RIGHT );
562         op = node->avl_data;
563 
564         /* Have we received another response since? */
565         if ( timerisset( &op->o_last_response ) &&
566                 !timercmp( &op->o_last_response, threshold, < ) ) {
567             continue;
568         }
569 
570         op->o_res = LLOAD_OP_FAILED;
571         found_op = ldap_tavl_delete( &upstream->c_ops, op, operation_upstream_cmp );
572         assert( op == found_op );
573 
574         if ( upstream->c_state == LLOAD_C_BINDING ) {
575             assert( op->o_tag == LDAP_REQ_BIND && upstream->c_ops == NULL );
576             upstream->c_state = LLOAD_C_READY;
577             if ( !BER_BVISNULL( &upstream->c_sasl_bind_mech ) ) {
578                 ber_memfree( upstream->c_sasl_bind_mech.bv_val );
579                 BER_BVZERO( &upstream->c_sasl_bind_mech );
580             }
581         }
582 
583         rc = ldap_tavl_insert( &ops, op, operation_upstream_cmp, ldap_avl_dup_error );
584         assert( rc == LDAP_SUCCESS );
585 
586         Debug( LDAP_DEBUG_STATS2, "connection_timeout: "
587                 "timing out %s from connid=%lu msgid=%d sent to connid=%lu as "
588                 "msgid=%d\n",
589                 lload_msgtype2str( op->o_tag ), op->o_client_connid,
590                 op->o_client_msgid, op->o_upstream_connid,
591                 op->o_upstream_msgid );
592         nops++;
593     }
594 
595     if ( nops == 0 ) {
596         CONNECTION_UNLOCK(upstream);
597         return LDAP_SUCCESS;
598     }
599     upstream->c_n_ops_executing -= nops;
600     upstream->c_counters.lc_ops_failed += nops;
601     Debug( LDAP_DEBUG_STATS, "connection_timeout: "
602             "timing out %d operations for connid=%lu\n",
603             nops, upstream->c_connid );
604     CONNECTION_UNLOCK(upstream);
605 
606     checked_lock( &b->b_mutex );
607     b->b_n_ops_executing -= nops;
608     checked_unlock( &b->b_mutex );
609 
610     for ( node = ldap_tavl_end( ops, TAVL_DIR_LEFT ); node;
611             node = ldap_tavl_next( node, TAVL_DIR_RIGHT ) ) {
612         op = node->avl_data;
613 
614         operation_send_reject( op,
615                 op->o_tag == LDAP_REQ_SEARCH ? LDAP_TIMELIMIT_EXCEEDED :
616                                                LDAP_ADMINLIMIT_EXCEEDED,
617                 "upstream did not respond in time", 0 );
618 
619         if ( rc == LDAP_SUCCESS ) {
620             rc = operation_send_abandon( op, upstream );
621         }
622         operation_unlink( op );
623     }
624 
625     /* TODO: if operation_send_abandon failed, we need to kill the upstream */
626     if ( rc == LDAP_SUCCESS ) {
627         connection_write_cb( -1, 0, upstream );
628     }
629 
630     CONNECTION_LOCK(upstream);
631     if ( upstream->c_state == LLOAD_C_CLOSING && !upstream->c_ops ) {
632         CONNECTION_DESTROY(upstream);
633     } else {
634         CONNECTION_UNLOCK(upstream);
635     }
636 
637     /* just dispose of the AVL, most operations should already be gone */
638     ldap_tavl_free( ops, NULL );
639     return LDAP_SUCCESS;
640 }
641 
642 void
operations_timeout(evutil_socket_t s,short what,void * arg)643 operations_timeout( evutil_socket_t s, short what, void *arg )
644 {
645     struct event *self = arg;
646     LloadTier *tier;
647     time_t threshold;
648 
649     Debug( LDAP_DEBUG_TRACE, "operations_timeout: "
650             "running timeout task\n" );
651     if ( !lload_timeout_api ) goto done;
652 
653     threshold = slap_get_time() - lload_timeout_api->tv_sec;
654 
655     LDAP_STAILQ_FOREACH ( tier, &tiers, t_next ) {
656         LloadBackend *b;
657 
658         LDAP_CIRCLEQ_FOREACH ( b, &tier->t_backends, b_next ) {
659             epoch_t epoch;
660 
661             checked_lock( &b->b_mutex );
662             if ( b->b_n_ops_executing == 0 ) {
663                 checked_unlock( &b->b_mutex );
664                 continue;
665             }
666 
667             epoch = epoch_join();
668 
669             Debug( LDAP_DEBUG_TRACE, "operations_timeout: "
670                     "timing out binds for backend uri=%s\n",
671                     b->b_uri.bv_val );
672             connections_walk_last( &b->b_mutex, &b->b_bindconns,
673                     b->b_last_bindconn, connection_timeout, &threshold );
674 
675             Debug( LDAP_DEBUG_TRACE, "operations_timeout: "
676                     "timing out other operations for backend uri=%s\n",
677                     b->b_uri.bv_val );
678             connections_walk_last( &b->b_mutex, &b->b_conns, b->b_last_conn,
679                     connection_timeout, &threshold );
680 
681             epoch_leave( epoch );
682             checked_unlock( &b->b_mutex );
683         }
684     }
685 done:
686     Debug( LDAP_DEBUG_TRACE, "operations_timeout: "
687             "timeout task finished\n" );
688     evtimer_add( self, lload_timeout_api );
689 }
690 
691 void
operation_update_global_rejected(LloadOperation * op)692 operation_update_global_rejected( LloadOperation *op )
693 {
694     if ( op->o_res == LLOAD_OP_REJECTED ) {
695         assert( op->o_upstream_connid == 0 );
696         switch ( op->o_tag ) {
697             case LDAP_REQ_BIND:
698                 lload_stats.counters[LLOAD_STATS_OPS_BIND].lc_ops_rejected++;
699                 break;
700             default:
701                 lload_stats.counters[LLOAD_STATS_OPS_OTHER].lc_ops_rejected++;
702                 break;
703         }
704     }
705 }
706 
707 void
operation_update_conn_counters(LloadOperation * op,LloadConnection * upstream)708 operation_update_conn_counters( LloadOperation *op, LloadConnection *upstream )
709 {
710     if ( op->o_res == LLOAD_OP_COMPLETED ) {
711         upstream->c_counters.lc_ops_completed++;
712     } else {
713         upstream->c_counters.lc_ops_failed++;
714     }
715 }
716 
717 void
operation_update_backend_counters(LloadOperation * op,LloadBackend * b)718 operation_update_backend_counters( LloadOperation *op, LloadBackend *b )
719 {
720     int stat_type = op->o_tag == LDAP_REQ_BIND ? LLOAD_STATS_OPS_BIND :
721                                                  LLOAD_STATS_OPS_OTHER;
722 
723     assert( b != NULL );
724     if ( op->o_res == LLOAD_OP_COMPLETED ) {
725         b->b_counters[stat_type].lc_ops_completed++;
726     } else {
727         b->b_counters[stat_type].lc_ops_failed++;
728     }
729 }
730