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