1 1.2 christos /* $NetBSD: leasechain.c,v 1.3 2022/04/03 01:10:59 christos Exp $ */ 2 1.1 christos 3 1.1 christos /* leasechain.c 4 1.1 christos 5 1.1 christos Additional support for in-memory database support */ 6 1.1 christos 7 1.1 christos /* 8 1.3 christos * Copyright (C) 2015-2022 Internet Systems Consortium, Inc. ("ISC") 9 1.1 christos * 10 1.1 christos * This Source Code Form is subject to the terms of the Mozilla Public 11 1.1 christos * License, v. 2.0. If a copy of the MPL was not distributed with this 12 1.1 christos * file, You can obtain one at http://mozilla.org/MPL/2.0/. 13 1.1 christos * 14 1.1 christos * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES 15 1.1 christos * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 16 1.1 christos * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR 17 1.1 christos * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 18 1.1 christos * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 19 1.1 christos * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 20 1.1 christos * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 21 1.1 christos * 22 1.1 christos * Internet Systems Consortium, Inc. 23 1.3 christos * PO Box 360 24 1.3 christos * Newmarket, NH 03857 USA 25 1.1 christos * <info (at) isc.org> 26 1.1 christos * https://www.isc.org/ 27 1.1 christos * 28 1.1 christos */ 29 1.1 christos 30 1.1 christos #include <sys/cdefs.h> 31 1.2 christos __RCSID("$NetBSD: leasechain.c,v 1.3 2022/04/03 01:10:59 christos Exp $"); 32 1.1 christos 33 1.1 christos /*! \file server\leasechaing.c 34 1.1 christos * 35 1.1 christos * \page leasechain structures overview 36 1.1 christos * 37 1.1 christos * A brief description of the leasechain structures 38 1.3 christos * 39 1.1 christos * This file provides additional data structures for a leasecain to 40 1.1 christos * provide faster access to leases on the queues associated with a pool 41 1.1 christos * than a linear walk. Each pool has a set of queues: active, free, backup, 42 1.1 christos * expired and abandoned to track leases as they are handed out and returned. 43 1.1 christos * The original code use a simply linear list for each of those pools but 44 1.1 christos * this can present performance issues if the pool is large and the lists are 45 1.1 christos * long. 46 1.1 christos * This code adds an array on top of the list allowing us to search the list 47 1.1 christos * in a binary fashion instead of a linear walk. 48 1.1 christos * 49 1.1 christos * \verbatim 50 1.1 christos * leasechain 51 1.1 christos * +------------+ +-------+-------+-------+-------+ 52 1.1 christos * | lease list |--> | lease | lease | lease | lease |.... 53 1.1 christos * | start | | ptr | ptr | ptr | ptr | 54 1.1 christos * | end | +-------+-------+-------+-------+ 55 1.1 christos * | max | | | 56 1.1 christos * +------------+ V V 57 1.1 christos * +-------+ +-------+ 58 1.1 christos * | lease | | lease | 59 1.1 christos * | | | | 60 1.1 christos * | next |->| next |->NULL 61 1.1 christos * NULL<- | prev |<-| prev | 62 1.1 christos * +-------+ +-------+ 63 1.3 christos * 64 1.1 christos * The linked list is maintained in an ordered state. Inserting an entry is 65 1.1 christos * accomplished by doing a binary search on the array to find the proper place 66 1.1 christos * in the list and then updating the pointers in the linked list to include the 67 1.3 christos * new entry. The entry is added into the array by copying the remainder of 68 1.1 christos * the array to provide space for the new entry. 69 1.1 christos * Removing an entry is the reverse. 70 1.1 christos * The arrays for the queues will be pre-allocated but not all of them will be 71 1.1 christos * large enough to hold all of the leases. If additional space is required the 72 1.1 christos * array will be grown. 73 1.1 christos */ 74 1.1 christos 75 1.1 christos #include "dhcpd.h" 76 1.1 christos 77 1.1 christos #if defined (BINARY_LEASES) 78 1.1 christos /* Default number number of lease pointers to add to the leasechain array 79 1.1 christos * everytime it grows beyond the current size 80 1.1 christos */ 81 1.1 christos #define LC_GROWTH_DELTA 256 82 1.1 christos 83 1.1 christos /*! 84 1.1 christos * 85 1.1 christos * \brief Check if leasechain isn't empty 86 1.1 christos * 87 1.1 christos * \param lc The leasechain to check 88 1.1 christos * 89 1.1 christos * \return 1 if leasechain isn't empty 90 1.1 christos */ 91 1.1 christos int 92 1.1 christos lc_not_empty( struct leasechain *lc ) { 93 1.1 christos #if defined (DEBUG_BINARY_LEASES) 94 1.1 christos log_debug("LC empty check %s:%d", MDL); 95 1.1 christos INSIST(lc != NULL); 96 1.1 christos #endif 97 1.1 christos 98 1.1 christos return (lc->nelem > 0 ? 1 : 0); 99 1.1 christos } 100 1.1 christos 101 1.1 christos /*! 102 1.1 christos * 103 1.1 christos * \brief Get the first lease from a leasechain 104 1.1 christos * 105 1.1 christos * \param lc The leasechain to check 106 1.1 christos * 107 1.1 christos * \return A pointer to the first lease from a lease chain, or NULL if none found 108 1.1 christos */ 109 1.1 christos struct lease * 110 1.1 christos lc_get_first_lease(struct leasechain *lc) { 111 1.1 christos #if defined (DEBUG_BINARY_LEASES) 112 1.1 christos log_debug("LC Get first %s:%d", MDL); 113 1.1 christos INSIST(lc != NULL); 114 1.1 christos INSIST(lc->total >= lc->nelem); 115 1.1 christos #endif 116 1.1 christos 117 1.1 christos if (lc->nelem > 0) { 118 1.1 christos return (lc->list)[0]; 119 1.1 christos } 120 1.1 christos return (NULL); 121 1.1 christos } 122 1.1 christos 123 1.1 christos /*! 124 1.1 christos * 125 1.1 christos * \brief Get the next lease from the chain, based on the lease passed in. 126 1.1 christos * 127 1.1 christos * \param lc The leasechain to check 128 1.1 christos * \param lp The lease to start from 129 1.1 christos * 130 1.1 christos * \return The next lease in the ordered list after lp 131 1.1 christos */ 132 1.1 christos struct lease * 133 1.1 christos lc_get_next(struct leasechain *lc, struct lease *lp) { 134 1.1 christos #if defined (DEBUG_BINARY_LEASES) 135 1.1 christos log_debug("LC Get next %s:%d", MDL); 136 1.1 christos INSIST(lc != NULL); 137 1.1 christos INSIST(lp != NULL); 138 1.1 christos #endif 139 1.1 christos 140 1.1 christos return lp->next; 141 1.1 christos } 142 1.1 christos 143 1.1 christos /*! 144 1.1 christos * 145 1.1 christos * \brief Find the best position for inserting a lease 146 1.1 christos * 147 1.1 christos * Given a potential range of the array to insert the lease into this routine 148 1.1 christos * will recursively examine the range to find the proper place in which to 149 1.1 christos * insert the lease. 150 1.3 christos * 151 1.1 christos * \param lc The leasechain to add the lease to 152 1.1 christos * \param lp The lease to insert 153 1.1 christos * \param min The minium index of the potential range for insertion 154 1.1 christos * \param max The maximum index of the potential range for insertion 155 1.1 christos * 156 1.1 christos * \return The index of the array entry to insert the lease 157 1.1 christos */ 158 1.3 christos size_t 159 1.1 christos lc_binary_search_insert_point(struct leasechain *lc, 160 1.1 christos struct lease *lp, 161 1.1 christos size_t min, size_t max) 162 1.1 christos { 163 1.1 christos size_t mid_index = ((max - min)/2) + min; 164 1.1 christos 165 1.1 christos if ((lc->list[mid_index]->sort_time > lp->sort_time) || 166 1.1 christos ((lc->list[mid_index]->sort_time == lp->sort_time) && 167 1.1 christos (lc->list[mid_index]->sort_tiebreaker > lp->sort_tiebreaker))) { 168 1.1 christos if (mid_index == min) { 169 1.1 christos /* insert in the min position, as sort_time is larger */ 170 1.1 christos return (min); 171 1.1 christos } 172 1.1 christos /* try again with lower half of list */ 173 1.1 christos return (lc_binary_search_insert_point(lc, lp, 174 1.1 christos min, mid_index - 1)); 175 1.1 christos } else if ((lc->list[mid_index]->sort_time < lp->sort_time) || 176 1.3 christos ((lc->list[mid_index]->sort_time == lp->sort_time) && 177 1.1 christos (lc->list[mid_index]->sort_tiebreaker < lp->sort_tiebreaker))) { 178 1.1 christos if (mid_index == max) { 179 1.1 christos /* insert in mid_index + 1 as sort_time is smaller */ 180 1.1 christos return (mid_index+1); 181 1.1 christos } 182 1.1 christos /* try again with upper half of list */ 183 1.1 christos return (lc_binary_search_insert_point(lc, lp, 184 1.1 christos mid_index + 1, max)); 185 1.1 christos } 186 1.1 christos 187 1.1 christos /* sort_time and sort_tiebreaker match, so insert in this position */ 188 1.1 christos return (mid_index); 189 1.1 christos } 190 1.1 christos 191 1.1 christos /*! 192 1.1 christos * 193 1.1 christos * \brief Find an exact match for a lease 194 1.1 christos * 195 1.1 christos * Given a potential range of the array to search this routine 196 1.1 christos * will recursively examine the range to find the proper lease 197 1.3 christos * 198 1.1 christos * \param lc The leasechain to check 199 1.1 christos * \param lp The lease to find 200 1.1 christos * \param min The minium index of the search range 201 1.1 christos * \param max The maximum index of the search range 202 1.1 christos * 203 1.1 christos * \return The index of the array entry for the lease, SIZE_MAX if the lease 204 1.1 christos * wasn't found 205 1.1 christos */ 206 1.1 christos 207 1.1 christos size_t 208 1.1 christos lc_binary_search_lease(struct leasechain *lc, 209 1.1 christos struct lease *lp, 210 1.1 christos size_t min, size_t max) 211 1.1 christos { 212 1.1 christos size_t mid_index; 213 1.1 christos size_t i; 214 1.1 christos 215 1.1 christos if (max < min) { 216 1.1 christos /* lease not found */ 217 1.1 christos return (SIZE_MAX); 218 1.1 christos } 219 1.1 christos 220 1.1 christos mid_index = ((max - min)/2) + min; 221 1.1 christos 222 1.1 christos if ((lc->list[mid_index]->sort_time > lp->sort_time) || 223 1.1 christos ((lc->list[mid_index]->sort_time == lp->sort_time) && 224 1.1 christos (lc->list[mid_index]->sort_tiebreaker > lp->sort_tiebreaker))) { 225 1.1 christos if (mid_index == min) { 226 1.1 christos /* lease not found */ 227 1.1 christos return (SIZE_MAX); 228 1.3 christos } 229 1.1 christos /* try the lower half of the list */ 230 1.1 christos return (lc_binary_search_lease(lc, lp, min, mid_index - 1)); 231 1.1 christos } else if ((lc->list[mid_index]->sort_time < lp->sort_time) || 232 1.1 christos ((lc->list[mid_index]->sort_time == lp->sort_time) && 233 1.1 christos (lc->list[mid_index]->sort_tiebreaker < lp->sort_tiebreaker))) { 234 1.3 christos /* try the upper half of the list */ 235 1.1 christos return (lc_binary_search_lease(lc, lp, mid_index + 1, max)); 236 1.1 christos } 237 1.1 christos 238 1.1 christos /* 239 1.1 christos * As sort_time/sort_tiebreaker may not be unique in the list, once we 240 1.1 christos * find a match, we need to look before and after from this position 241 1.1 christos * for all matching sort_time/sort_tiebreaker until we find the exact 242 1.1 christos * lease or until no matching lease is found 243 1.1 christos */ 244 1.1 christos if (lp == lc->list[mid_index]) { 245 1.1 christos return (mid_index); 246 1.1 christos } 247 1.1 christos 248 1.1 christos /* Check out entries below the mid_index */ 249 1.1 christos if (mid_index > min) { 250 1.1 christos /* We will break out of the loop if we either go past the 251 1.1 christos * canddiates or hit the end of the range when i == min. As 252 1.1 christos * i is unsigned we can't check it in the for loop itself. 253 1.1 christos */ 254 1.1 christos for (i = mid_index - 1; ; i--) { 255 1.1 christos if (lp == lc->list[i]) { 256 1.1 christos return (i); 257 1.1 christos } 258 1.1 christos 259 1.1 christos /* Are we done with this range? */ 260 1.1 christos if ((i == min) || 261 1.3 christos ((lc->list[i]->sort_time != lp->sort_time) || 262 1.1 christos ((lc->list[i]->sort_time == lp->sort_time) && 263 1.1 christos (lc->list[i]->sort_tiebreaker != lp->sort_tiebreaker)))) { 264 1.1 christos break; 265 1.1 christos } 266 1.1 christos } 267 1.1 christos } 268 1.1 christos 269 1.1 christos /* Check out entries above the mid_index */ 270 1.1 christos if (mid_index < max) { 271 1.3 christos /* We will break out of the loop if we either go past the 272 1.1 christos * canddiates or hit the end of the range when i == max. 273 1.1 christos */ 274 1.1 christos for (i = mid_index + 1; i <= max; i++) { 275 1.1 christos if (lp == lc->list[i]) { 276 1.1 christos return (i); 277 1.1 christos } 278 1.1 christos 279 1.1 christos if ((lc->list[i]->sort_time != lp->sort_time) || 280 1.1 christos ((lc->list[i]->sort_time == lp->sort_time) && 281 1.1 christos (lc->list[i]->sort_tiebreaker != lp->sort_tiebreaker))) { 282 1.1 christos break; 283 1.1 christos } 284 1.1 christos } 285 1.1 christos } 286 1.1 christos 287 1.1 christos /* Lease not found */ 288 1.1 christos return (SIZE_MAX); 289 1.1 christos } 290 1.1 christos 291 1.1 christos /*! 292 1.1 christos * 293 1.1 christos * \brief Increase the size of the array for the lease chain 294 1.1 christos * 295 1.1 christos * \param lc The leasechain to expand 296 1.1 christos * 297 1.1 christos * If we are unable to allocate memory we log a fatal error. There's 298 1.1 christos * not much else to do as we can't figure out where to put the lease. 299 1.1 christos * 300 1.1 christos * If we can allocate memory we copy the old lease chain to the new 301 1.1 christos * lease chain and free the old. 302 1.1 christos */ 303 1.1 christos void 304 1.1 christos lc_grow_chain(struct leasechain *lc) { 305 1.1 christos #if defined (DEBUG_BINARY_LEASES) 306 1.1 christos log_debug("LC grow lease chain max was %zu, %s:%d", lc->total, MDL); 307 1.1 christos #endif 308 1.1 christos 309 1.1 christos void *p; 310 1.1 christos size_t temp_size; 311 1.1 christos 312 1.3 christos if (lc->growth == 0) 313 1.1 christos temp_size = lc->total + LC_GROWTH_DELTA; 314 1.1 christos else 315 1.1 christos temp_size = lc->total + lc->growth; 316 1.1 christos 317 1.1 christos /* try to allocate the memory */ 318 1.1 christos p = dmalloc(sizeof(struct lease *) * temp_size, MDL); 319 1.1 christos if (p == NULL) { 320 1.1 christos log_fatal("LC grow, unable to allocated memory %s:%d", MDL); 321 1.1 christos } 322 1.1 christos 323 1.1 christos /* Success, copy the lease chain and install the new one */ 324 1.1 christos if (lc->list != NULL) { 325 1.1 christos memcpy(p, lc->list, sizeof(struct lease *) * lc->nelem); 326 1.1 christos dfree(lc->list, MDL); 327 1.1 christos } 328 1.1 christos lc->list = (struct lease **) p; 329 1.1 christos lc->total = temp_size; 330 1.1 christos 331 1.1 christos return; 332 1.1 christos } 333 1.1 christos 334 1.1 christos 335 1.1 christos /*! 336 1.1 christos * 337 1.1 christos * \brief Link a lease to a lease chain position 338 1.1 christos * 339 1.1 christos * This function may increase the size of the lease chain if necessary and will 340 1.1 christos * probably need to move entries in the lease chain around. 341 1.1 christos * 342 1.1 christos * \param lc The leasechain to update 343 1.1 christos * \param lp The lease to insert 344 1.1 christos * \param n The position in which to insert the lease 345 1.1 christos * 346 1.1 christos */ 347 1.1 christos void 348 1.1 christos lc_link_lcp(struct leasechain *lc, struct lease *lp, size_t n) { 349 1.1 christos #if defined (DEBUG_BINARY_LEASES) 350 1.1 christos log_debug("LC link lcp %s:%d", MDL); 351 1.1 christos INSIST (lc != NULL); 352 1.1 christos INSIST (lp != NULL); 353 1.1 christos #endif 354 1.1 christos 355 1.1 christos if (lc->nelem == lc->total) { 356 1.1 christos lc_grow_chain(lc); 357 1.1 christos } 358 1.1 christos 359 1.1 christos #if defined (DEBUG_BINARY_LEASES) 360 1.1 christos log_debug("LC Link lcp position %zu, elem %zu, %s:%d", 361 1.1 christos n, lc->nelem, MDL); 362 1.1 christos #endif 363 1.1 christos 364 1.1 christos /* create room for the new pointer */ 365 1.1 christos if (n < lc->nelem) { 366 1.1 christos #if defined (DEBUG_BINARY_LEASES) 367 1.1 christos log_debug("LC link lcp moving position %zu, moving %zu. %s:%d", 368 1.1 christos n, (lc->nelem-n), MDL); 369 1.1 christos #endif 370 1.1 christos memmove(lc->list + n + 1, lc->list + n, 371 1.1 christos sizeof(struct lease *) * (lc->nelem-n)); 372 1.1 christos } 373 1.1 christos 374 1.3 christos /* clean any stale pointer info from this position before calling 375 1.1 christos * lease_reference as it won't work if pointer is not NULL 376 1.1 christos */ 377 1.1 christos lc->list[n] = NULL; 378 1.1 christos lease_reference(&(lc->list[n]), lp, MDL); 379 1.1 christos 380 1.1 christos lc->nelem++; 381 1.1 christos 382 1.1 christos lp->lc = lc; 383 1.1 christos 384 1.1 christos return; 385 1.1 christos } 386 1.1 christos 387 1.1 christos /*! 388 1.3 christos * 389 1.1 christos * \brief Insert the lease at the specified position in both the lease chain 390 1.1 christos * and the linked list 391 1.1 christos * 392 1.1 christos * This function may increase the size of the lease chain if necessary and will 393 1.1 christos * probably need to move entries in the lease chain around. 394 1.1 christos * \param lc The leasechain to update 395 1.1 christos * \param lp The lease to insert 396 1.1 christos * \param n The position in which to insert the lease 397 1.1 christos * 398 1.1 christos */ 399 1.1 christos void 400 1.1 christos lc_add_lease_pos(struct leasechain *lc, struct lease *lp, size_t pos) { 401 1.1 christos #if defined (DEBUG_BINARY_LEASES) 402 1.1 christos log_debug("LC Add lease position %zu, %s:%d", pos, MDL); 403 1.1 christos INSIST (lc != NULL); 404 1.1 christos INSIST (lp != NULL); 405 1.1 christos #endif 406 1.1 christos lc_link_lcp(lc, lp, pos); 407 1.1 christos 408 1.1 christos #if 0 409 1.1 christos /* this shoudln't be necessary, if we still have pointers on 410 1.1 christos * the lease being inserted things are broken 411 1.1 christos */ 412 1.1 christos if (lp->prev) { 413 1.1 christos lease_dereference(&lp->prev, MDL); 414 1.1 christos } 415 1.1 christos if (lp->next) { 416 1.1 christos lease_dereference(&lp->next, MDL); 417 1.1 christos } 418 1.1 christos #endif 419 1.1 christos 420 1.1 christos /* not the first element? */ 421 1.1 christos if (pos > 0) { 422 1.1 christos if (lc->list[pos-1]->next) { 423 1.1 christos lease_dereference(&(lc->list[pos-1]->next), MDL); 424 1.1 christos } 425 1.1 christos lease_reference(&(lc->list[pos-1]->next), lp, MDL); 426 1.1 christos lease_reference(&lp->prev, lc->list[pos-1], MDL ); 427 1.1 christos } 428 1.1 christos 429 1.1 christos /* not the last element? we've already bumped nelem when linking 430 1.1 christos * into the lease chain so nelem should never be zero here */ 431 1.1 christos if (pos < (lc->nelem-1)) { 432 1.1 christos if (lc->list[pos+1]->prev) { 433 1.1 christos lease_dereference(&(lc->list[pos+1]->prev), MDL); 434 1.1 christos } 435 1.1 christos lease_reference(&(lc->list[pos+1]->prev), lp, MDL); 436 1.1 christos lease_reference(&lp->next, lc->list[pos+1], MDL); 437 1.1 christos } 438 1.1 christos 439 1.1 christos return; 440 1.1 christos } 441 1.1 christos 442 1.1 christos #ifdef POINTER_DEBUG 443 1.1 christos /*! 444 1.1 christos * 445 1.1 christos * \brief Debug only code, check the lease to verify it is sorted 446 1.1 christos * 447 1.1 christos * \param lc The leasechain to verify 448 1.1 christos * 449 1.1 christos * Calls log_fatal if the leasechain is not properly sorted 450 1.1 christos */ 451 1.1 christos void 452 1.1 christos lc_check_lc_sort_order(struct leasechain *lc) { 453 1.1 christos size_t i; 454 1.1 christos TIME t = 0; 455 1.1 christos long int tiebreak = 0; 456 1.1 christos 457 1.1 christos log_debug("LC check sort %s:%d", MDL); 458 1.1 christos for (i = 0; i < lc->nelem; i++ ) { 459 1.1 christos if ((lc->list[i]->sort_time < t) || 460 1.3 christos ((lc->list[i]->sort_time == t) && 461 1.1 christos (lc->list[i]->tiebreaker < tiebreaker))) { 462 1.1 christos if (i > 0) { 463 1.1 christos print_lease(lc->list[i-1]); 464 1.1 christos } 465 1.1 christos print_lease(lc->list[i]); 466 1.1 christos if (i < lc->nelem - 1) { 467 1.1 christos print_lease(lc->list[i+1]); 468 1.1 christos } 469 1.1 christos log_fatal("lc[%p] not sorted properly", lc); 470 1.1 christos } 471 1.1 christos 472 1.1 christos t = lc->list[i]->sort_time; 473 1.1 christos tiebreak = lc->list[i]->sort_tiebreaker; 474 1.1 christos } 475 1.1 christos } 476 1.1 christos #endif 477 1.1 christos 478 1.1 christos /*! 479 1.1 christos * 480 1.1 christos * \brief Add a lease into the sorted lease and lease chain 481 1.1 christos * The sort_time is set by the caller while the sort_tiebreaker is set here 482 1.1 christos * The value doesn't much matter as long as it prvoides a way to have different 483 1.1 christos * values in most of the leases. 484 1.3 christos * 485 1.1 christos * When choosing a value for tiebreak we choose: 486 1.1 christos * 0 for the first lease in the queue 487 1.1 christos * 0 if the lease is going to the end of the queue with a sort_time greater 488 1.1 christos * than that of the current last lease 489 1.1 christos * previous tiebreaker + 1 if it is going to the end of the queue with a 490 1.1 christos * sort_time equal to that of the current last lease 491 1.1 christos * random if none of the above fit 492 1.1 christos * 493 1.1 christos * During startup when we can take advantage of the fact that leases may already 494 1.1 christos * be sorted and so check the end of the list to see if we can simply add the 495 1.1 christos * lease to the end. 496 1.3 christos * 497 1.1 christos * \param lc The leasechain in which to insert the lease 498 1.1 christos * \param lp The lease to insert 499 1.1 christos * 500 1.1 christos */ 501 1.1 christos void 502 1.1 christos lc_add_sorted_lease(struct leasechain *lc, struct lease *lp) { 503 1.1 christos size_t pos; 504 1.1 christos 505 1.1 christos #if defined (DEBUG_BINARY_LEASES) 506 1.1 christos log_debug("LC add sorted %s:%d", MDL); 507 1.1 christos INSIST (lc != NULL); 508 1.1 christos INSIST (lp != NULL); 509 1.1 christos #endif 510 1.1 christos if (lc->nelem == 0) { 511 1.1 christos /* The first lease start with a tiebreak of 0 and add it at 512 1.1 christos * the first position */ 513 1.1 christos lp->sort_tiebreaker = 0; 514 1.1 christos 515 1.1 christos lc_add_lease_pos(lc, lp, 0); 516 1.1 christos /* log_debug("LC add sorted done, %s:%d", MDL); */ 517 1.1 christos 518 1.1 christos return; 519 1.1 christos } 520 1.1 christos 521 1.1 christos if (lp->sort_time > lc->list[lc->nelem-1]->sort_time) { 522 1.1 christos /* Adding to end of queue, with a different sort time */ 523 1.1 christos lp->sort_tiebreaker = 0; 524 1.1 christos pos = lc->nelem; 525 1.1 christos } else if (lp->sort_time == lc->list[lc->nelem-1]->sort_time) { 526 1.1 christos /* Adding to end of queue, with the same sort time */ 527 1.1 christos if (lc->list[lc->nelem-1]->sort_tiebreaker < LONG_MAX) 528 1.1 christos lp->sort_tiebreaker = 529 1.1 christos lc->list[lc->nelem-1]->sort_tiebreaker+1; 530 1.1 christos else 531 1.1 christos lp->sort_tiebreaker = LONG_MAX; 532 1.1 christos pos = lc->nelem; 533 1.1 christos } else { 534 1.1 christos /* Adding somewhere in the queue, just pick a random value */ 535 1.1 christos lp->sort_tiebreaker = random(); 536 1.1 christos pos = lc_binary_search_insert_point(lc, lp, 0, lc->nelem - 1); 537 1.1 christos } 538 1.1 christos 539 1.1 christos /* Finally add it to the queue */ 540 1.1 christos lc_add_lease_pos(lc, lp, pos); 541 1.1 christos 542 1.1 christos #if defined (DEBUG_BINARY_LEASES) 543 1.1 christos log_debug("LC add sorted complete position %zu, elements %zu, %s:%d", 544 1.1 christos pos, lc->nelem, MDL); 545 1.1 christos #endif 546 1.1 christos 547 1.1 christos #ifdef POINTER_DEBUG 548 1.1 christos lc_check_lc_sort_order(lc); 549 1.1 christos #endif 550 1.1 christos } 551 1.1 christos 552 1.1 christos /*! 553 1.1 christos * 554 1.1 christos * \brief Remove the Nth pointer from a leasechain structure and update counters. 555 1.1 christos * The pointers in the array will be moved to fill in the hole if necessary. 556 1.1 christos * 557 1.1 christos * \param lc The lease chain to update 558 1.1 christos * \param n the entry to remove from the lease chain 559 1.1 christos */ 560 1.1 christos void 561 1.1 christos lc_unlink_lcp(struct leasechain *lc, size_t n) { 562 1.1 christos #if defined (DEBUG_BINARY_LEASES) 563 1.1 christos log_debug("LC unlink lcp %s:%d", MDL); 564 1.1 christos 565 1.1 christos /* element index to remove must be less than the number of elements present */ 566 1.1 christos INSIST(n < lc->nelem); 567 1.1 christos #endif 568 1.1 christos 569 1.1 christos /* Clear the pointer from the lease back to the LC */ 570 1.1 christos lc->list[n]->lc = NULL; 571 1.1 christos 572 1.1 christos /* Clear the pointer from the LC to the lease */ 573 1.1 christos lease_dereference(&(lc->list[n]), MDL); 574 1.1 christos 575 1.1 christos /* memove unless we are removing the last element */ 576 1.1 christos if ((lc->nelem-1) > n) { 577 1.1 christos memmove(lc->list + n, lc->list + n + 1, 578 1.1 christos sizeof(struct lease *) * (lc->nelem-1-n)); 579 1.1 christos } 580 1.1 christos lc->nelem--; 581 1.1 christos } 582 1.1 christos 583 1.1 christos /*! 584 1.1 christos * 585 1.1 christos * \brief Remove a lease from a specific position. This will first unlink 586 1.1 christos * the lease from the lease chain and then update the linked list. 587 1.1 christos * 588 1.1 christos * \param lc The lease chain to update 589 1.1 christos * \param pos the entry to remove from the lease chain 590 1.1 christos */ 591 1.1 christos void 592 1.1 christos lc_unlink_lease_pos(struct leasechain *lc, size_t pos) 593 1.1 christos { 594 1.1 christos #if defined (DEBUG_BINARY_LEASES) 595 1.1 christos INSIST(lc != NULL); 596 1.1 christos #endif 597 1.1 christos 598 1.1 christos struct lease *lp = NULL; 599 1.1 christos lease_reference(&lp, lc->list[pos], MDL); 600 1.1 christos 601 1.1 christos /* unlink from lease chain list */ 602 1.1 christos lc_unlink_lcp(lc, pos); 603 1.1 christos 604 1.1 christos /* unlink from the linked list */ 605 1.1 christos if (lp->next) { 606 1.1 christos lease_dereference(&lp->next->prev, MDL); 607 1.1 christos if (lp->prev) 608 1.1 christos lease_reference(&lp->next->prev, lp->prev, MDL); 609 1.1 christos } 610 1.1 christos if (lp->prev) { 611 1.1 christos lease_dereference(&lp->prev->next, MDL); 612 1.1 christos if (lp->next) 613 1.1 christos lease_reference(&lp->prev->next, lp->next, MDL); 614 1.1 christos lease_dereference(&lp->prev, MDL); 615 1.1 christos } 616 1.1 christos if (lp->next) { 617 1.1 christos lease_dereference(&lp->next, MDL); 618 1.1 christos } 619 1.1 christos lease_dereference(&lp, MDL); 620 1.1 christos } 621 1.1 christos 622 1.1 christos /*! 623 1.1 christos * 624 1.1 christos * \brief Find a lease in the lease chain and then remove it 625 1.1 christos * If we can't find the lease on the given lease chain it's a fatal error. 626 1.1 christos * 627 1.1 christos * \param lc The lease chain to update 628 1.1 christos * \param lp The lease to remove 629 1.1 christos */ 630 1.1 christos void 631 1.1 christos lc_unlink_lease(struct leasechain *lc, struct lease *lp) { 632 1.1 christos #if defined (DEBUG_BINARY_LEASES) 633 1.1 christos log_debug("LC unlink lease %s:%d", MDL); 634 1.1 christos 635 1.1 christos INSIST(lc != NULL); 636 1.1 christos INSIST(lc->list != NULL); 637 1.1 christos INSIST(lp != NULL ); 638 1.1 christos INSIST(lp->lc != NULL ); 639 1.1 christos INSIST(lp->lc == lc ); 640 1.1 christos #endif 641 1.1 christos 642 1.1 christos size_t pos = lc_binary_search_lease(lc, lp, 0, lc->nelem-1); 643 1.1 christos if (pos == SIZE_MAX) { 644 1.1 christos /* fatal, lease not found in leasechain */ 645 1.1 christos log_fatal("Lease with binding state %s not on its queue.", 646 1.1 christos (lp->binding_state < 1 || 647 1.1 christos lp->binding_state > FTS_LAST) 648 1.1 christos ? "unknown" 649 1.1 christos : binding_state_names[lp->binding_state - 1]); 650 1.1 christos } 651 1.1 christos 652 1.1 christos lc_unlink_lease_pos(lc, pos); 653 1.1 christos } 654 1.1 christos 655 1.1 christos /*! 656 1.1 christos * 657 1.1 christos * \brief Unlink all the leases in the lease chain and free the 658 1.1 christos * lease chain structure. The leases will be freed if and when 659 1.1 christos * any other references to them are cleared. 660 1.1 christos * 661 1.1 christos * \param lc the lease chain to clear 662 1.1 christos */ 663 1.1 christos void 664 1.1 christos lc_delete_all(struct leasechain *lc) { 665 1.1 christos size_t i; 666 1.1 christos 667 1.1 christos if (lc->nelem > 0) { 668 1.1 christos /* better to delete from the last one, to avoid the memmove */ 669 1.1 christos for (i = lc->nelem - 1; ; i--) { 670 1.1 christos lc_unlink_lease_pos(lc, i); 671 1.1 christos if (i == 0) { 672 1.1 christos break; 673 1.1 christos } 674 1.1 christos } 675 1.1 christos } 676 1.1 christos 677 1.1 christos /* and then get rid of the list itself */ 678 1.1 christos if (lc->list != NULL) { 679 1.1 christos dfree(lc->list, MDL); 680 1.1 christos lc->list = NULL; 681 1.1 christos } 682 1.1 christos 683 1.1 christos lc->total = 0; 684 1.1 christos lc->nelem = 0; 685 1.1 christos } 686 1.1 christos 687 1.1 christos /*! 688 1.1 christos * 689 1.1 christos * \brief Set the growth value. This is the number of elements to 690 1.1 christos * add to the array whenever it needs to grow. 691 1.1 christos * 692 1.1 christos * \param lc the lease chain to set up 693 1.1 christos * \param growth the growth value to use 694 1.1 christos */ 695 1.1 christos void 696 1.1 christos lc_init_growth(struct leasechain *lc, size_t growth) { 697 1.1 christos lc->growth = growth; 698 1.1 christos } 699 1.1 christos 700 1.1 christos #endif /* #if defined (BINARY_LEASES) */ 701