operation.c 32.9 KB
Newer Older
Ondřej Kuzník's avatar
Ondřej Kuzník committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
 *
 * Copyright 1998-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>.
 */

#include "portable.h"

#include "lutil.h"
19
#include "lload.h"
Ondřej Kuzník's avatar
Ondřej Kuzník committed
20

Ondřej Kuzník's avatar
Ondřej Kuzník committed
21
22
23
ldap_pvt_thread_mutex_t lload_pin_mutex;
unsigned long lload_next_pin = 1;

24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
ber_tag_t
slap_req2res( ber_tag_t tag )
{
    switch ( tag ) {
        case LDAP_REQ_ADD:
        case LDAP_REQ_BIND:
        case LDAP_REQ_COMPARE:
        case LDAP_REQ_EXTENDED:
        case LDAP_REQ_MODIFY:
        case LDAP_REQ_MODRDN:
            tag++;
            break;

        case LDAP_REQ_DELETE:
            tag = LDAP_RES_DELETE;
            break;

        case LDAP_REQ_ABANDON:
        case LDAP_REQ_UNBIND:
            tag = LBER_SEQUENCE;
            break;

        case LDAP_REQ_SEARCH:
            tag = LDAP_RES_SEARCH_RESULT;
            break;

        default:
            tag = LBER_SEQUENCE;
    }

    return tag;
}

const char *
58
lload_msgtype2str( ber_tag_t tag )
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
{
    switch ( tag ) {
        case LDAP_REQ_ABANDON: return "abandon request";
        case LDAP_REQ_ADD: return "add request";
        case LDAP_REQ_BIND: return "bind request";
        case LDAP_REQ_COMPARE: return "compare request";
        case LDAP_REQ_DELETE: return "delete request";
        case LDAP_REQ_EXTENDED: return "extended request";
        case LDAP_REQ_MODIFY: return "modify request";
        case LDAP_REQ_RENAME: return "rename request";
        case LDAP_REQ_SEARCH: return "search request";
        case LDAP_REQ_UNBIND: return "unbind request";

        case LDAP_RES_ADD: return "add result";
        case LDAP_RES_BIND: return "bind result";
        case LDAP_RES_COMPARE: return "compare result";
        case LDAP_RES_DELETE: return "delete result";
        case LDAP_RES_EXTENDED: return "extended result";
        case LDAP_RES_INTERMEDIATE: return "intermediate response";
        case LDAP_RES_MODIFY: return "modify result";
        case LDAP_RES_RENAME: return "rename result";
        case LDAP_RES_SEARCH_ENTRY: return "search-entry response";
        case LDAP_RES_SEARCH_REFERENCE: return "search-reference response";
        case LDAP_RES_SEARCH_RESULT: return "search result";
    }
    return "unknown message";
}

Ondřej Kuzník's avatar
Ondřej Kuzník committed
87
88
89
int
operation_client_cmp( const void *left, const void *right )
{
90
    const LloadOperation *l = left, *r = right;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
91

92
    assert( l->o_client_connid == r->o_client_connid );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
93
94
95
96
97
98
99
100
    if ( l->o_client_msgid || r->o_client_msgid ) {
        return ( l->o_client_msgid < r->o_client_msgid ) ?
                -1 :
                ( l->o_client_msgid > r->o_client_msgid );
    } else {
        return ( l->o_pin_id < r->o_pin_id ) ? -1 :
                ( l->o_pin_id > r->o_pin_id );
    }
Ondřej Kuzník's avatar
Ondřej Kuzník committed
101
102
103
104
105
}

int
operation_upstream_cmp( const void *left, const void *right )
{
106
    const LloadOperation *l = left, *r = right;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
107

108
    assert( l->o_upstream_connid == r->o_upstream_connid );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
109
110
111
112
113
114
115
116
    if ( l->o_upstream_msgid || r->o_upstream_msgid ) {
        return ( l->o_upstream_msgid < r->o_upstream_msgid ) ?
                -1 :
                ( l->o_upstream_msgid > r->o_upstream_msgid );
    } else {
        return ( l->o_pin_id < r->o_pin_id ) ? -1 :
                ( l->o_pin_id > r->o_pin_id );
    }
Ondřej Kuzník's avatar
Ondřej Kuzník committed
117
118
}

Ondřej Kuzník's avatar
Ondřej Kuzník committed
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
/*
 * 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.
147
148
149
 *
 * Beware! This widget really touches all the mutexes we have and showcases the
 * issues with maintaining so many mutex ordering restrictions.
Ondřej Kuzník's avatar
Ondřej Kuzník committed
150
 */
Ondřej Kuzník's avatar
Ondřej Kuzník committed
151
void
152
operation_destroy_from_client( LloadOperation *op )
Ondřej Kuzník's avatar
Ondřej Kuzník committed
153
{
154
155
    LloadConnection *upstream = NULL, *client = op->o_client;
    LloadBackend *b = NULL;
156
    int race_state, detach_client = !client->c_live;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
157
158

    Debug( LDAP_DEBUG_TRACE, "operation_destroy_from_client: "
159
160
            "op=%p attempting to release operation%s\n",
            op, detach_client ? " and detach client" : "" );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
161
162
163
164
165

    /* 1. liveness/refcnt adjustment and test */
    op->o_client_refcnt -= op->o_client_live;
    op->o_client_live = 0;

166
    assert( op->o_client_refcnt <= client->c_refcnt );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
167
168
169
170
171
172
    if ( op->o_client_refcnt ) {
        Debug( LDAP_DEBUG_TRACE, "operation_destroy_from_client: "
                "op=%p not dead yet\n",
                op );
        return;
    }
Ondřej Kuzník's avatar
Ondřej Kuzník committed
173

Ondřej Kuzník's avatar
Ondřej Kuzník committed
174
175
    /* 2. Remove from the operation map and TODO adjust the pending op count */
    tavl_delete( &client->c_ops, op, operation_client_cmp );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
176

Ondřej Kuzník's avatar
Ondřej Kuzník committed
177
178
    /* 3. Detect whether we entered a race to free op and indicate that to any
     * others */
179
    ldap_pvt_thread_mutex_lock( &op->o_mutex );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
180
    race_state = op->o_freeing;
181
    op->o_freeing |= LLOAD_OP_FREEING_CLIENT;
182
    if ( detach_client ) {
183
        op->o_freeing |= LLOAD_OP_DETACHING_CLIENT;
184
    }
185
186
187
188
    ldap_pvt_thread_mutex_unlock( &op->o_mutex );

    CONNECTION_UNLOCK_INCREF(client);

189
    if ( detach_client ) {
Ondřej Kuzník's avatar
Ondřej Kuzník committed
190
        ldap_pvt_thread_mutex_lock( &op->o_link_mutex );
191
        op->o_client = NULL;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
192
        ldap_pvt_thread_mutex_unlock( &op->o_link_mutex );
193
    }
Ondřej Kuzník's avatar
Ondřej Kuzník committed
194

Ondřej Kuzník's avatar
Ondřej Kuzník committed
195
    /* 4. If we lost the race, deal with it straight away */
Ondřej Kuzník's avatar
Ondřej Kuzník committed
196
197
198
199
200
201
202
    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).
         */
Ondřej Kuzník's avatar
Ondřej Kuzník committed
203
204
205
206
207
        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);
208
209
        } else if ( (race_state & LLOAD_OP_FREEING_MASK) ==
                LLOAD_OP_FREEING_UPSTREAM ) {
210
            Debug( LDAP_DEBUG_TRACE, "operation_destroy_from_client: "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
211
212
213
                    "op=%p lost race, increased client refcnt connid=%lu "
                    "to refcnt=%d\n",
                    op, client->c_connid, client->c_refcnt );
214
215
216
217
            CONNECTION_LOCK(client);
        } else {
            Debug( LDAP_DEBUG_TRACE, "operation_destroy_from_client: "
                    "op=%p lost race with another "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
218
219
220
                    "operation_destroy_from_client, "
                    "client connid=%lu\n",
                    op, client->c_connid );
221
            CONNECTION_LOCK_DECREF(client);
222
        }
Ondřej Kuzník's avatar
Ondřej Kuzník committed
223
224
225
        return;
    }

226
227
228
    /* it seems we will be destroying the operation,
     * so update the global rejected cunter if needed */
    operation_update_global_rejected( op );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
229
    /* 5. If we raced the upstream side and won, reclaim the token */
Ondřej Kuzník's avatar
Ondřej Kuzník committed
230
    ldap_pvt_thread_mutex_lock( &op->o_link_mutex );
231
    if ( !(race_state & LLOAD_OP_DETACHING_UPSTREAM) ) {
232
233
234
235
        upstream = op->o_upstream;
        if ( upstream ) {
            CONNECTION_LOCK(upstream);
        }
Ondřej Kuzník's avatar
Ondřej Kuzník committed
236
    }
Ondřej Kuzník's avatar
Ondřej Kuzník committed
237
    ldap_pvt_thread_mutex_unlock( &op->o_link_mutex );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
238

239
    ldap_pvt_thread_mutex_lock( &op->o_mutex );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
240
241
    /* We don't actually resolve the race in full until we grab the other's
     * c_mutex+op->o_mutex here */
242
243
    if ( upstream && ( op->o_freeing & LLOAD_OP_FREEING_UPSTREAM ) ) {
        if ( op->o_freeing & LLOAD_OP_DETACHING_UPSTREAM ) {
Ondřej Kuzník's avatar
Ondřej Kuzník committed
244
245
246
247
248
249
250
251
252
253
254
255
256
            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 );
        }
257
258
259
    }
    ldap_pvt_thread_mutex_unlock( &op->o_mutex );

Ondřej Kuzník's avatar
Ondřej Kuzník committed
260
261
262
263
264
265
266
    /* 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 );
267

Ondřej Kuzník's avatar
Ondřej Kuzník committed
268
        /* There must have been no race if op is still alive */
269
        ldap_pvt_thread_mutex_lock( &op->o_mutex );
270
        op->o_freeing &= ~LLOAD_OP_FREEING_CLIENT;
271
        if ( detach_client ) {
272
            op->o_freeing &= ~LLOAD_OP_DETACHING_CLIENT;
273
        }
Ondřej Kuzník's avatar
Ondřej Kuzník committed
274
        assert( op->o_freeing == 0 );
275
276
277
        ldap_pvt_thread_mutex_unlock( &op->o_mutex );

        assert( upstream != NULL );
278
        CONNECTION_UNLOCK_OR_DESTROY(upstream);
Ondřej Kuzník's avatar
Ondřej Kuzník committed
279
280
        CONNECTION_LOCK_DECREF(client);
        return;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
281
282
    }

Ondřej Kuzník's avatar
Ondřej Kuzník committed
283
    /* 7. Remove from the operation map and adjust the pending op count */
284
285
286
    if ( upstream ) {
        if ( tavl_delete( &upstream->c_ops, op, operation_upstream_cmp ) ) {
            upstream->c_n_ops_executing--;
287
            operation_update_conn_counters( op );
288
            b = (LloadBackend *)upstream->c_private;
289
        }
290
        CONNECTION_UNLOCK_OR_DESTROY(upstream);
291

292
293
294
        if ( b ) {
            ldap_pvt_thread_mutex_lock( &b->b_mutex );
            b->b_n_ops_executing--;
295
            operation_update_backend_counters( op, b );
296
297
            ldap_pvt_thread_mutex_unlock( &b->b_mutex );
        }
Ondřej Kuzník's avatar
Ondřej Kuzník committed
298
    }
299

Ondřej Kuzník's avatar
Ondřej Kuzník committed
300
301
302
303
304
305
    /* 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 );
306
    ldap_pvt_thread_mutex_destroy( &op->o_mutex );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
307
    ldap_pvt_thread_mutex_destroy( &op->o_link_mutex );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
308
    ch_free( op );
309
310

    CONNECTION_LOCK_DECREF(client);
Ondřej Kuzník's avatar
Ondřej Kuzník committed
311
312
313
314
315
316
}

/*
 * See operation_destroy_from_client.
 */
void
317
operation_destroy_from_upstream( LloadOperation *op )
Ondřej Kuzník's avatar
Ondřej Kuzník committed
318
{
319
320
    LloadConnection *client = NULL, *upstream = op->o_upstream;
    LloadBackend *b = NULL;
321
    int race_state, detach_upstream = !upstream->c_live;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
322
323

    Debug( LDAP_DEBUG_TRACE, "operation_destroy_from_upstream: "
324
325
            "op=%p attempting to release operation%s\n",
            op, detach_upstream ? " and detach upstream" : "" );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
326
327
328
329
330

    /* 1. liveness/refcnt adjustment and test */
    op->o_upstream_refcnt -= op->o_upstream_live;
    op->o_upstream_live = 0;

331
    assert( op->o_upstream_refcnt <= upstream->c_refcnt );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
332
333
334
335
336
337
338
    if ( op->o_upstream_refcnt ) {
        Debug( LDAP_DEBUG_TRACE, "operation_destroy_from_upstream: "
                "op=%p not dead yet\n",
                op );
        return;
    }

339
340
341
    /* it seems we will be destroying the operation,
     * so update the global rejected cunter if needed */
    operation_update_global_rejected( op );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
342
343
344
    /* 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--;
345
        operation_update_conn_counters( op );
346
        b = (LloadBackend *)upstream->c_private;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
347
348
    }

349
350
    ldap_pvt_thread_mutex_lock( &op->o_mutex );
    race_state = op->o_freeing;
351
    op->o_freeing |= LLOAD_OP_FREEING_UPSTREAM;
352
    if ( detach_upstream ) {
353
        op->o_freeing |= LLOAD_OP_DETACHING_UPSTREAM;
354
    }
355
356
    ldap_pvt_thread_mutex_unlock( &op->o_mutex );

357
358
    CONNECTION_UNLOCK_INCREF(upstream);

Ondřej Kuzník's avatar
Ondřej Kuzník committed
359
    /* 3. Detect whether we entered a race to free op */
Ondřej Kuzník's avatar
Ondřej Kuzník committed
360
    ldap_pvt_thread_mutex_lock( &op->o_link_mutex );
361
362
363
    if ( detach_upstream ) {
        op->o_upstream = NULL;
    }
Ondřej Kuzník's avatar
Ondřej Kuzník committed
364
    ldap_pvt_thread_mutex_unlock( &op->o_link_mutex );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
365

366
367
368
    if ( b ) {
        ldap_pvt_thread_mutex_lock( &b->b_mutex );
        b->b_n_ops_executing--;
369
        operation_update_backend_counters( op, b );
370
371
372
        ldap_pvt_thread_mutex_unlock( &b->b_mutex );
    }

Ondřej Kuzník's avatar
Ondřej Kuzník committed
373
    /* 4. If we lost the race, deal with it straight away */
374
    if ( race_state ) {
Ondřej Kuzník's avatar
Ondřej Kuzník committed
375
376
377
378
379
380
        /*
         * 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).
         */
Ondřej Kuzník's avatar
Ondřej Kuzník committed
381
382
383
384
385
        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);
386
387
        } else if ( (race_state & LLOAD_OP_FREEING_MASK) ==
                LLOAD_OP_FREEING_CLIENT ) {
388
            Debug( LDAP_DEBUG_TRACE, "operation_destroy_from_upstream: "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
389
390
391
                    "op=%p lost race, increased upstream refcnt connid=%lu "
                    "to refcnt=%d\n",
                    op, upstream->c_connid, upstream->c_refcnt );
392
393
394
395
            CONNECTION_LOCK(upstream);
        } else {
            Debug( LDAP_DEBUG_TRACE, "operation_destroy_from_upstream: "
                    "op=%p lost race with another "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
396
397
398
                    "operation_destroy_from_upstream, "
                    "upstream connid=%lu\n",
                    op, upstream->c_connid );
399
400
            CONNECTION_LOCK_DECREF(upstream);
        }
Ondřej Kuzník's avatar
Ondřej Kuzník committed
401
        return;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
402
403
    }

Ondřej Kuzník's avatar
Ondřej Kuzník committed
404
    /* 5. If we raced the client side and won, reclaim the token */
Ondřej Kuzník's avatar
Ondřej Kuzník committed
405
    ldap_pvt_thread_mutex_lock( &op->o_link_mutex );
406
    if ( !(race_state & LLOAD_OP_DETACHING_CLIENT) ) {
407
408
409
410
        client = op->o_client;
        if ( client ) {
            CONNECTION_LOCK(client);
        }
Ondřej Kuzník's avatar
Ondřej Kuzník committed
411
    }
Ondřej Kuzník's avatar
Ondřej Kuzník committed
412
    ldap_pvt_thread_mutex_unlock( &op->o_link_mutex );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
413

Ondřej Kuzník's avatar
Ondřej Kuzník committed
414
415
    /* We don't actually resolve the race in full until we grab the other's
     * c_mutex+op->o_mutex here */
416
    ldap_pvt_thread_mutex_lock( &op->o_mutex );
417
418
    if ( client && ( op->o_freeing & LLOAD_OP_FREEING_CLIENT ) ) {
        if ( op->o_freeing & LLOAD_OP_DETACHING_CLIENT ) {
Ondřej Kuzník's avatar
Ondřej Kuzník committed
419
420
421
422
423
424
425
426
427
428
429
430
431
            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 );
        }
432
433
434
    }
    ldap_pvt_thread_mutex_unlock( &op->o_mutex );

Ondřej Kuzník's avatar
Ondřej Kuzník committed
435
436
437
438
439
440
441
442
    /* 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 */
443
        ldap_pvt_thread_mutex_lock( &op->o_mutex );
444
        op->o_freeing &= ~LLOAD_OP_FREEING_UPSTREAM;
445
        if ( detach_upstream ) {
446
            op->o_freeing &= ~LLOAD_OP_DETACHING_UPSTREAM;
447
        }
Ondřej Kuzník's avatar
Ondřej Kuzník committed
448
        assert( op->o_freeing == 0 );
449
450
451
        ldap_pvt_thread_mutex_unlock( &op->o_mutex );

        assert( client != NULL );
452
        CONNECTION_UNLOCK_OR_DESTROY(client);
Ondřej Kuzník's avatar
Ondřej Kuzník committed
453
454
455
456
457
        CONNECTION_LOCK_DECREF(upstream);
        return;
    }

    /* 7. Remove from the operation map and TODO adjust the pending op count */
458
459
    if ( client ) {
        tavl_delete( &client->c_ops, op, operation_client_cmp );
460
        CONNECTION_UNLOCK_OR_DESTROY(client);
461
    }
Ondřej Kuzník's avatar
Ondřej Kuzník committed
462
463
464
465
466
467
468

    /* 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 );
469
    ldap_pvt_thread_mutex_destroy( &op->o_mutex );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
470
    ldap_pvt_thread_mutex_destroy( &op->o_link_mutex );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
471
    ch_free( op );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
472
473

    CONNECTION_LOCK_DECREF(upstream);
Ondřej Kuzník's avatar
Ondřej Kuzník committed
474
475
}

476
477
478
/*
 * Entered holding c_mutex for now.
 */
479
480
LloadOperation *
operation_init( LloadConnection *c, BerElement *ber )
Ondřej Kuzník's avatar
Ondřej Kuzník committed
481
{
482
    LloadOperation *op;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
483
484
485
486
    ber_tag_t tag;
    ber_len_t len;
    int rc;

487
    op = ch_calloc( 1, sizeof(LloadOperation) );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
488
    op->o_client = c;
489
    op->o_client_connid = c->c_connid;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
490
    op->o_ber = ber;
491
    op->o_start = slap_get_time();
Ondřej Kuzník's avatar
Ondřej Kuzník committed
492

493
    ldap_pvt_thread_mutex_init( &op->o_mutex );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
494
    ldap_pvt_thread_mutex_init( &op->o_link_mutex );
495

Ondřej Kuzník's avatar
Ondřej Kuzník committed
496
497
498
    op->o_client_live = op->o_client_refcnt = 1;
    op->o_upstream_live = op->o_upstream_refcnt = 1;

Ondřej Kuzník's avatar
Ondřej Kuzník committed
499
500
501
502
503
504
505
506
    tag = ber_get_int( ber, &op->o_client_msgid );
    if ( tag != LDAP_TAG_MSGID ) {
        goto fail;
    }

    rc = tavl_insert( &c->c_ops, op, operation_client_cmp, avl_dup_error );
    if ( rc ) {
        Debug( LDAP_DEBUG_PACKETS, "operation_init: "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
507
508
                "several operations with same msgid=%d in-flight "
                "from client connid=%lu\n",
509
                op->o_client_msgid, op->o_client_connid );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
        goto fail;
    }

    tag = op->o_tag = ber_skip_element( ber, &op->o_request );
    switch ( tag ) {
        case LBER_ERROR:
            rc = -1;
            break;
    }
    if ( rc ) {
        tavl_delete( &c->c_ops, op, operation_client_cmp );
        goto fail;
    }

    tag = ber_peek_tag( ber, &len );
    if ( tag == LDAP_TAG_CONTROLS ) {
        ber_skip_element( ber, &op->o_ctrls );
    }

529
530
531
532
533
534
535
536
537
    switch ( op->o_tag ) {
        case LDAP_REQ_BIND:
            lload_stats.counters[LLOAD_STATS_OPS_BIND].lc_ops_received++;
            break;
        default:
            lload_stats.counters[LLOAD_STATS_OPS_OTHER].lc_ops_received++;
            break;
    }

Ondřej Kuzník's avatar
Ondřej Kuzník committed
538
539
540
    Debug( LDAP_DEBUG_STATS, "operation_init: "
            "received a new operation, %s with msgid=%d for client "
            "connid=%lu\n",
541
            lload_msgtype2str( op->o_tag ), op->o_client_msgid,
542
            op->o_client_connid );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
543

544
    c->c_n_ops_executing++;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
545
546
547
548
549
550
551
    return op;

fail:
    ch_free( op );
    return NULL;
}

552
int
553
operation_send_abandon( LloadOperation *op )
554
{
555
    LloadConnection *upstream = op->o_upstream;
556
557
558
559
560
561
562
563
564
565
566
567
    BerElement *ber;
    int rc = -1;

    ldap_pvt_thread_mutex_lock( &upstream->c_io_mutex );
    ber = upstream->c_pendingber;
    if ( ber == NULL && (ber = ber_alloc()) == NULL ) {
        Debug( LDAP_DEBUG_ANY, "operation_send_abandon: "
                "ber_alloc failed\n" );
        goto done;
    }
    upstream->c_pendingber = ber;

568
569
570
571
572
573
574
575
576
577
578
579
580
581
    Debug( LDAP_DEBUG_TRACE, "operation_send_abandon: "
            "abandoning %s msgid=%d on connid=%lu\n",
            lload_msgtype2str( op->o_tag ), op->o_upstream_msgid,
            op->o_upstream_connid );

    if ( op->o_tag == LDAP_REQ_BIND ) {
        rc = ber_printf( ber, "t{tit{ist{s}}}", LDAP_TAG_MESSAGE,
                LDAP_TAG_MSGID, upstream->c_next_msgid++,
                LDAP_REQ_BIND, LDAP_VERSION3, "", LDAP_AUTH_SASL, "" );
    } else {
        rc = ber_printf( ber, "t{titi}", LDAP_TAG_MESSAGE,
                LDAP_TAG_MSGID, upstream->c_next_msgid++,
                LDAP_REQ_ABANDON, op->o_upstream_msgid );
    }
582
583
584
585
586
587
588
589
590
591
592
593
594

    if ( rc < 0 ) {
        ber_free( ber, 1 );
        upstream->c_pendingber = NULL;
        goto done;
    }
    rc = LDAP_SUCCESS;

done:
    ldap_pvt_thread_mutex_unlock( &upstream->c_io_mutex );
    return rc;
}

595
596
597
598
599
/*
 * Will remove the operation from its upstream and if it was still there,
 * sends an abandon request.
 *
 * Being called from client_reset or request_abandon, the following hold:
600
601
602
 * - noone else is processing the read part of the client connection (no new
 *   operations come in there - relevant for the c_state checks)
 * - op->o_client_refcnt > op->o_client_live (and it follows that op->o_client != NULL)
603
 */
Ondřej Kuzník's avatar
Ondřej Kuzník committed
604
void
605
operation_abandon( LloadOperation *op )
Ondřej Kuzník's avatar
Ondřej Kuzník committed
606
{
607
608
    LloadConnection *c;
    LloadBackend *b;
609
    int rc = LDAP_SUCCESS;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
610

Ondřej Kuzník's avatar
Ondřej Kuzník committed
611
    ldap_pvt_thread_mutex_lock( &op->o_link_mutex );
612
    c = op->o_upstream;
613
    if ( !c || !c->c_live ) {
Ondřej Kuzník's avatar
Ondřej Kuzník committed
614
        ldap_pvt_thread_mutex_unlock( &op->o_link_mutex );
615
        goto done;
616
    }
Ondřej Kuzník's avatar
Ondřej Kuzník committed
617

618
    CONNECTION_LOCK(c);
Ondřej Kuzník's avatar
Ondřej Kuzník committed
619
    ldap_pvt_thread_mutex_unlock( &op->o_link_mutex );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
620
621
622
623
624

    /* for now consider all abandoned operations completed,
     * perhaps add a separate counter later */
    op->o_res = LLOAD_OP_COMPLETED;

625
626
    if ( tavl_delete( &c->c_ops, op, operation_upstream_cmp ) == NULL ) {
        /* The operation has already been abandoned or finished */
627
628
629
630
631
        Debug( LDAP_DEBUG_TRACE, "operation_abandon: "
                "%s from connid=%lu msgid=%d not present in connid=%lu any "
                "more\n",
                lload_msgtype2str( op->o_tag ), op->o_client_connid,
                op->o_client_msgid, op->o_upstream_connid );
632
        goto unlock;
633
    }
634
635
    if ( c->c_state == LLOAD_C_BINDING ) {
        c->c_state = LLOAD_C_READY;
636
637
638
639
        if ( !BER_BVISNULL( &c->c_sasl_bind_mech ) ) {
            ber_memfree( c->c_sasl_bind_mech.bv_val );
            BER_BVZERO( &c->c_sasl_bind_mech );
        }
640
    }
641
    c->c_n_ops_executing--;
642
    b = (LloadBackend *)c->c_private;
643
644

    op->o_upstream_refcnt++;
645
    CONNECTION_UNLOCK_INCREF(c);
Ondřej Kuzník's avatar
Ondřej Kuzník committed
646

647
648
    ldap_pvt_thread_mutex_lock( &b->b_mutex );
    b->b_n_ops_executing--;
649
    operation_update_backend_counters( op, b );
650
    ldap_pvt_thread_mutex_unlock( &b->b_mutex );
651

652
    if ( operation_send_abandon( op ) == LDAP_SUCCESS ) {
653
        connection_write_cb( -1, 0, c );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
654
655
    }

656
    CONNECTION_LOCK_DECREF(c);
657
658
    op->o_upstream_refcnt--;

659
unlock:
660
    if ( !c->c_live || !op->o_upstream_refcnt ) {
661
662
        operation_destroy_from_upstream( op );
    }
663
664
665
666
667
    if ( rc ) {
        CONNECTION_DESTROY(c);
    } else {
        CONNECTION_UNLOCK_OR_DESTROY(c);
    }
668
669
670
671
672
673
674

done:
    c = op->o_client;
    assert( c );

    /* Caller should hold a reference on client */
    CONNECTION_LOCK(c);
675
676
    if ( c->c_state == LLOAD_C_BINDING ) {
        c->c_state = LLOAD_C_READY;
677
678
679
680
681
682
683
684
        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 );
        }
Ondřej Kuzník's avatar
Ondřej Kuzník committed
685
686
687
        if ( op->o_pin_id ) {
            c->c_pin_id = 0;
        }
688
    }
689
    assert( op->o_client_refcnt > op->o_client_live );
690
691
692
    op->o_client_refcnt--;
    operation_destroy_from_client( op );
    CONNECTION_UNLOCK(c);
Ondřej Kuzník's avatar
Ondřej Kuzník committed
693
694
}

695
696
697
698
699
/*
 * Called with op->o_client non-NULL and already locked.
 */
int
operation_send_reject_locked(
700
        LloadOperation *op,
701
702
703
        int result,
        const char *msg,
        int send_anyway )
Ondřej Kuzník's avatar
Ondřej Kuzník committed
704
{
705
    LloadConnection *c = op->o_client;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
706
707
708
    BerElement *ber;
    int found;

709
    Debug( LDAP_DEBUG_TRACE, "operation_send_reject_locked: "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
710
            "rejecting %s from client connid=%lu with message: \"%s\"\n",
711
            lload_msgtype2str( op->o_tag ), c->c_connid, msg );
712

Ondřej Kuzník's avatar
Ondřej Kuzník committed
713
    found = ( tavl_delete( &c->c_ops, op, operation_client_cmp ) == op );
714
    if ( !found && !send_anyway ) {
715
        Debug( LDAP_DEBUG_TRACE, "operation_send_reject_locked: "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
716
717
718
                "msgid=%d not scheduled for client connid=%lu anymore, "
                "not sending\n",
                op->o_client_msgid, c->c_connid );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
719
        goto done;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
720
721
    }

722
723
724
725
726
727
728
729
    if ( op->o_client_msgid == 0 ) {
        assert( op->o_saved_msgid == 0 && op->o_pin_id );
        Debug( LDAP_DEBUG_TRACE, "operation_send_reject_locked: "
                "operation pin=%lu is just a pin, not sending\n",
                op->o_pin_id );
        goto done;
    }

730
    CONNECTION_UNLOCK_INCREF(c);
Ondřej Kuzník's avatar
Ondřej Kuzník committed
731
732
733
734
735
    ldap_pvt_thread_mutex_lock( &c->c_io_mutex );

    ber = c->c_pendingber;
    if ( ber == NULL && (ber = ber_alloc()) == NULL ) {
        ldap_pvt_thread_mutex_unlock( &c->c_io_mutex );
736
        Debug( LDAP_DEBUG_ANY, "operation_send_reject_locked: "
Ondřej Kuzník's avatar
Ondřej Kuzník committed
737
738
                "ber_alloc failed, closing connid=%lu\n",
                c->c_connid );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
739
740
        CONNECTION_LOCK_DECREF(c);
        operation_destroy_from_client( op );
741
        CONNECTION_DESTROY(c);
742
        return -1;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
743
744
745
746
747
748
749
750
751
    }
    c->c_pendingber = ber;

    ber_printf( ber, "t{tit{ess}}", LDAP_TAG_MESSAGE,
            LDAP_TAG_MSGID, op->o_client_msgid,
            slap_req2res( op->o_tag ), result, "", msg );

    ldap_pvt_thread_mutex_unlock( &c->c_io_mutex );

752
    connection_write_cb( -1, 0, c );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
753

754
    CONNECTION_LOCK_DECREF(c);
Ondřej Kuzník's avatar
Ondřej Kuzník committed
755
756
done:
    operation_destroy_from_client( op );
757
758
759
760
761
    return LDAP_SUCCESS;
}

void
operation_send_reject(
762
        LloadOperation *op,
763
764
765
766
        int result,
        const char *msg,
        int send_anyway )
{
767
    LloadConnection *c;
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793

    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);
    }
Ondřej Kuzník's avatar
Ondřej Kuzník committed
794
795
}

796
797
798
799
800
801
802
/*
 * Upstream is shutting down, signal the client if necessary, but we have to
 * call operation_destroy_from_upstream ourselves to detach upstream from the
 * op.
 *
 * Only called from upstream_destroy.
 */
Ondřej Kuzník's avatar
Ondřej Kuzník committed
803
void
804
operation_lost_upstream( LloadOperation *op )
Ondřej Kuzník's avatar
Ondřej Kuzník committed
805
{
806
    LloadConnection *c = op->o_upstream;
807

808
    operation_send_reject( op, LDAP_OTHER,
809
            "connection to the remote server has been severed", 0 );
810

811
    CONNECTION_LOCK(c);
812
813
814
    op->o_upstream_refcnt--;
    operation_destroy_from_upstream( op );
    CONNECTION_UNLOCK(c);
Ondřej Kuzník's avatar
Ondřej Kuzník committed
815
}
816

Ondřej Kuzník's avatar
Ondřej Kuzník committed
817
818
int
connection_timeout( LloadConnection *upstream, void *arg )
819
{
820
    LloadOperation *op;
821
    TAvlnode *ops = NULL, *node;
822
    LloadBackend *b = upstream->c_private;
Ondřej Kuzník's avatar
Ondřej Kuzník committed
823
    time_t threshold = *(time_t *)arg;
824
825
826
    int rc, nops = 0;

    for ( node = tavl_end( upstream->c_ops, TAVL_DIR_LEFT ); node &&
827
828
            ((LloadOperation *)node->avl_data)->o_start <
                    threshold; /* shortcut */
829
            node = tavl_next( node, TAVL_DIR_RIGHT ) ) {
830
        LloadOperation *found_op;
831
832
833
834
835
836
837
838
839

        op = node->avl_data;

        /* Have we received another response since? */
        if ( op->o_last_response && op->o_last_response >= threshold ) {
            continue;
        }

        op->o_upstream_refcnt++;
840
        op->o_res = LLOAD_OP_FAILED;
841
842
843
        found_op = tavl_delete( &upstream->c_ops, op, operation_upstream_cmp );
        assert( op == found_op );

844
845
846
847
848
849
850
851
852
        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 );
            }
        }

853
854
855
856
857
858
        rc = tavl_insert( &ops, op, operation_upstream_cmp, avl_dup_error );
        assert( rc == LDAP_SUCCESS );

        Debug( LDAP_DEBUG_STATS2, "connection_timeout: "
                "timing out %s from connid=%lu msgid=%d sent to connid=%lu as "
                "msgid=%d\n",
859
                lload_msgtype2str( op->o_tag ), op->o_client_connid,
860
861
862
863
864
865
                op->o_client_msgid, op->o_upstream_connid,
                op->o_upstream_msgid );
        nops++;
    }

    if ( nops == 0 ) {
Ondřej Kuzník's avatar
Ondřej Kuzník committed
866
        return LDAP_SUCCESS;
867
868
869
870
871
872
873
874
875
876
877
878
879
    }
    upstream->c_n_ops_executing -= nops;
    Debug( LDAP_DEBUG_STATS, "connection_timeout: "
            "timing out %d operations for connid=%lu\n",
            nops, upstream->c_connid );
    CONNECTION_UNLOCK_INCREF(upstream);

    ldap_pvt_thread_mutex_lock( &b->b_mutex );
    b->b_n_ops_executing -= nops;
    ldap_pvt_thread_mutex_unlock( &b->b_mutex );

    for ( node = tavl_end( ops, TAVL_DIR_LEFT ); node;
            node = tavl_next( node, TAVL_DIR_RIGHT ) ) {
880
        LloadConnection *client;
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919

        op = node->avl_data;

        ldap_pvt_thread_mutex_lock( &op->o_link_mutex );
        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 :
                                                    LDAP_ADMINLIMIT_EXCEEDED,
                     "upstream did not respond in time", 0 ) == LDAP_SUCCESS ) {
            CONNECTION_UNLOCK_OR_DESTROY(client);
        }

        if ( rc == LDAP_SUCCESS ) {
            rc = operation_send_abandon( 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 */
    if ( rc == LDAP_SUCCESS ) {
        connection_write_cb( -1, 0, upstream );
    }

    CONNECTION_LOCK_DECREF(upstream);
    /* just dispose of the AVL, most operations should already be gone */
    tavl_free( ops, NULL );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
920
    return LDAP_SUCCESS;
921
922
923
924
925
}

void
operations_timeout( evutil_socket_t s, short what, void *arg )
{
926
    LloadBackend *b;
927
928
929
930
931
932
933
934
935
    time_t threshold;

    Debug( LDAP_DEBUG_TRACE, "operations_timeout: "
            "running timeout task\n" );
    if ( !lload_timeout_api ) goto done;

    threshold = slap_get_time() - lload_timeout_api->tv_sec;

    LDAP_CIRCLEQ_FOREACH ( b, &backend, b_next ) {
Ondřej Kuzník's avatar
Ondřej Kuzník committed
936
937
938
939
940
        ldap_pvt_thread_mutex_lock( &b->b_mutex );
        if ( b->b_n_ops_executing == 0 ) {
            ldap_pvt_thread_mutex_unlock( &b->b_mutex );
            continue;
        }
941
942
943
944

        Debug( LDAP_DEBUG_TRACE, "operations_timeout: "
                "timing out binds for backend uri=%s\n",
                b->b_uri.bv_val );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
945
946
        connections_walk_last( &b->b_mutex, &b->b_bindconns, b->b_last_bindconn,
                connection_timeout, &threshold );
947
948
949
950

        Debug( LDAP_DEBUG_TRACE, "operations_timeout: "
                "timing out other operations for backend uri=%s\n",
                b->b_uri.bv_val );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
951
952
953
954
        connections_walk_last( &b->b_mutex, &b->b_conns, b->b_last_conn,
                connection_timeout, &threshold );

        ldap_pvt_thread_mutex_unlock( &b->b_mutex );
955
956
957
958
    }
done:
    Debug( LDAP_DEBUG_TRACE, "operations_timeout: "
            "timeout task finished\n" );
Ondřej Kuzník's avatar
Ondřej Kuzník committed
959
    evtimer_add( lload_timeout_event, lload_timeout_api );
960
}
961
962
963
964

void
operation_update_global_rejected( LloadOperation *op )
{
Ondřej Kuzník's avatar
Ondřej Kuzník committed
965
966
    if ( op->o_res == LLOAD_OP_REJECTED ) {
        assert( op->o_upstream_connid == 0 );
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
        switch ( op->o_tag ) {
            case LDAP_REQ_BIND:
                lload_stats.counters[LLOAD_STATS_OPS_BIND].lc_ops_rejected++;
                break;
            default:
                lload_stats.counters[LLOAD_STATS_OPS_OTHER].lc_ops_rejected++;
                break;
        }
    }
}

void
operation_update_conn_counters( LloadOperation *op )
{
    assert( op->o_upstream != NULL );
    if ( op->o_res == LLOAD_OP_COMPLETED ) {
        op->o_upstream->c_counters.lc_ops_completed++;
    } else {
        op->o_upstream->c_counters.lc_ops_failed++;
    }
}

void
operation_update_backend_counters( LloadOperation *op, LloadBackend *b )
{
    int stat_type = op->o_tag == LDAP_REQ_BIND ? LLOAD_STATS_OPS_BIND :
                                                 LLOAD_STATS_OPS_OTHER;

    assert( b != NULL );
    if ( op->o_res == LLOAD_OP_COMPLETED ) {
        b->b_counters[stat_type].lc_ops_completed++;
    } else {
        b->b_counters[stat_type].lc_ops_failed++;
    }
For faster browsing, not all history is shown. View entire blame