1 1.1 christos /* $NetBSD: tier_roundrobin.c,v 1.2 2025/09/05 21:16:24 christos Exp $ */ 2 1.1 christos 3 1.1 christos /* $OpenLDAP$ */ 4 1.1 christos /* This work is part of OpenLDAP Software <http://www.openldap.org/>. 5 1.1 christos * 6 1.1 christos * Copyright 1998-2024 The OpenLDAP Foundation. 7 1.1 christos * All rights reserved. 8 1.1 christos * 9 1.1 christos * Redistribution and use in source and binary forms, with or without 10 1.1 christos * modification, are permitted only as authorized by the OpenLDAP 11 1.1 christos * Public License. 12 1.1 christos * 13 1.1 christos * A copy of this license is available in the file LICENSE in the 14 1.1 christos * top-level directory of the distribution or, alternatively, at 15 1.1 christos * <http://www.OpenLDAP.org/license.html>. 16 1.1 christos */ 17 1.1 christos 18 1.1 christos #include <sys/cdefs.h> 19 1.1 christos __RCSID("$NetBSD: tier_roundrobin.c,v 1.2 2025/09/05 21:16:24 christos Exp $"); 20 1.1 christos 21 1.1 christos #include "portable.h" 22 1.1 christos 23 1.1 christos #include "lload.h" 24 1.1 christos 25 1.1 christos static LloadTierInit roundrobin_init; 26 1.1 christos static LloadTierBackendCb roundrobin_add_backend; 27 1.1 christos static LloadTierBackendCb roundrobin_remove_backend; 28 1.1 christos static LloadTierSelect roundrobin_select; 29 1.1 christos 30 1.1 christos struct lload_tier_type roundrobin_tier; 31 1.1 christos 32 1.1 christos static LloadTier * 33 1.1 christos roundrobin_init( void ) 34 1.1 christos { 35 1.1 christos LloadTier *tier; 36 1.1 christos 37 1.1 christos tier = ch_calloc( 1, sizeof(LloadTier) ); 38 1.1 christos 39 1.1 christos tier->t_type = roundrobin_tier; 40 1.1 christos ldap_pvt_thread_mutex_init( &tier->t_mutex ); 41 1.1 christos LDAP_CIRCLEQ_INIT( &tier->t_backends ); 42 1.1 christos 43 1.1 christos return tier; 44 1.1 christos } 45 1.1 christos 46 1.1 christos static int 47 1.1 christos roundrobin_add_backend( LloadTier *tier, LloadBackend *b ) 48 1.1 christos { 49 1.1 christos assert( b->b_tier == tier ); 50 1.1 christos LDAP_CIRCLEQ_INSERT_TAIL( &tier->t_backends, b, b_next ); 51 1.1 christos if ( !tier->t_private ) { 52 1.1 christos tier->t_private = b; 53 1.1 christos } 54 1.1 christos tier->t_nbackends++; 55 1.1 christos return LDAP_SUCCESS; 56 1.1 christos } 57 1.1 christos 58 1.1 christos static int 59 1.1 christos roundrobin_remove_backend( LloadTier *tier, LloadBackend *b ) 60 1.1 christos { 61 1.1 christos LloadBackend *next = LDAP_CIRCLEQ_LOOP_NEXT( &tier->t_backends, b, b_next ); 62 1.1 christos 63 1.1 christos assert_locked( &tier->t_mutex ); 64 1.1 christos assert_locked( &b->b_mutex ); 65 1.1 christos 66 1.1 christos assert( b->b_tier == tier ); 67 1.1 christos 68 1.1 christos LDAP_CIRCLEQ_REMOVE( &tier->t_backends, b, b_next ); 69 1.1 christos if ( b == tier->t_private ) { 70 1.1 christos if ( tier->t_nbackends ) { 71 1.1 christos tier->t_private = next; 72 1.1 christos } else { 73 1.1 christos assert( b == next ); 74 1.1 christos tier->t_private = NULL; 75 1.1 christos } 76 1.1 christos } 77 1.1 christos tier->t_nbackends--; 78 1.1 christos return LDAP_SUCCESS; 79 1.1 christos } 80 1.1 christos 81 1.1 christos static int 82 1.1 christos roundrobin_select( 83 1.1 christos LloadTier *tier, 84 1.1 christos LloadOperation *op, 85 1.1 christos LloadConnection **cp, 86 1.1 christos int *res, 87 1.1 christos char **message ) 88 1.1 christos { 89 1.1 christos LloadBackend *b, *first, *next; 90 1.1 christos int rc = 0; 91 1.1 christos 92 1.1 christos checked_lock( &tier->t_mutex ); 93 1.1 christos first = b = tier->t_private; 94 1.1 christos checked_unlock( &tier->t_mutex ); 95 1.1 christos 96 1.1 christos if ( !first ) return rc; 97 1.1 christos 98 1.1 christos do { 99 1.1 christos int result; 100 1.1 christos 101 1.1 christos checked_lock( &b->b_mutex ); 102 1.1 christos next = LDAP_CIRCLEQ_LOOP_NEXT( &tier->t_backends, b, b_next ); 103 1.1 christos 104 1.1 christos result = backend_select( b, op, cp, res, message ); 105 1.1 christos checked_unlock( &b->b_mutex ); 106 1.1 christos 107 1.1 christos rc |= result; 108 1.1 christos if ( result && *cp ) { 109 1.1 christos /* 110 1.1 christos * Round-robin step: 111 1.1 christos * Rotate the queue to put this backend at the end. The race here 112 1.1 christos * is acceptable. 113 1.1 christos */ 114 1.1 christos checked_lock( &tier->t_mutex ); 115 1.1 christos tier->t_private = next; 116 1.1 christos checked_unlock( &tier->t_mutex ); 117 1.1 christos return rc; 118 1.1 christos } 119 1.1 christos 120 1.1 christos b = next; 121 1.1 christos } while ( b != first ); 122 1.1 christos 123 1.1 christos return rc; 124 1.1 christos } 125 1.1 christos 126 1.1 christos struct lload_tier_type roundrobin_tier = { 127 1.1 christos .tier_name = "roundrobin", 128 1.1 christos 129 1.1 christos .tier_init = roundrobin_init, 130 1.1 christos .tier_startup = tier_startup, 131 1.1 christos .tier_reset = tier_reset, 132 1.1 christos .tier_destroy = tier_destroy, 133 1.1 christos 134 1.1 christos .tier_oc = BER_BVC("olcBkLloadTierConfig"), 135 1.1 christos .tier_backend_oc = BER_BVC("olcBkLloadBackendConfig"), 136 1.1 christos 137 1.1 christos .tier_add_backend = roundrobin_add_backend, 138 1.1 christos .tier_remove_backend = roundrobin_remove_backend, 139 1.1 christos 140 1.1 christos .tier_select = roundrobin_select, 141 1.1 christos }; 142