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