leasechain.c revision 1.2.6.1 1 1.2.6.1 martin /* $NetBSD: leasechain.c,v 1.2.6.1 2024/02/29 11:39:58 martin 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.2.6.1 martin * 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.2.6.1 martin * PO Box 360
24 1.2.6.1 martin * 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.6.1 martin __RCSID("$NetBSD: leasechain.c,v 1.2.6.1 2024/02/29 11:39:58 martin 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.2.6.1 martin *
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.2.6.1 martin *
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.2.6.1 martin * 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.2.6.1 martin *
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.2.6.1 martin 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.2.6.1 martin ((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.2.6.1 martin *
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.2.6.1 martin }
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.2.6.1 martin /* 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.2.6.1 martin ((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.2.6.1 martin /* 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.2.6.1 martin 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.2.6.1 martin /* 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.2.6.1 martin *
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.2.6.1 martin ((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.2.6.1 martin *
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.2.6.1 martin *
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