subr_extent.c revision 1.6 1 /* $NetBSD: subr_extent.c,v 1.6 1996/10/17 08:27:35 thorpej Exp $ */
2
3 /*-
4 * Copyright (c) 1996 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Jason R. Thorpe and Matthias Drochner.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the NetBSD
21 * Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 * contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
30 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39 /*
40 * General purpose extent manager.
41 */
42
43 #include <sys/param.h>
44 #include <sys/extent.h>
45 #include <sys/malloc.h>
46 #include <sys/time.h>
47 #include <sys/systm.h>
48 #include <sys/proc.h>
49
50 static void extent_insert_and_optimize __P((struct extent *, u_long, u_long,
51 int, struct extent_region *, struct extent_region *));
52 static struct extent_region *extent_alloc_region_descriptor
53 __P((struct extent *, int));
54 static void extent_free_region_descriptor __P((struct extent *,
55 struct extent_region *));
56
57 /*
58 * Macro to align to an arbitrary power-of-two boundary.
59 */
60 #define EXTENT_ALIGN(_start, _align) \
61 (((_start) + ((_align) - 1)) & (-(_align)))
62
63 /*
64 * Allocate and initialize an extent map.
65 */
66 struct extent *
67 extent_create(name, start, end, mtype, storage, storagesize, flags)
68 char *name;
69 u_long start, end;
70 int mtype;
71 caddr_t storage;
72 size_t storagesize;
73 int flags;
74 {
75 struct extent *ex;
76 caddr_t cp = storage;
77 size_t sz = storagesize;
78 struct extent_region *rp;
79 int fixed_extent = (storage != NULL);
80
81 #ifdef DIAGNOSTIC
82 /* Check arguments. */
83 if (name == NULL)
84 panic("extent_create: name == NULL");
85 if (end < start) {
86 printf("extent_create: extent `%s', start 0x%lx, end 0x%lx\n",
87 name, start, end);
88 panic("extent_create: end < start");
89 }
90 if (fixed_extent && (storagesize < sizeof(struct extent_fixed)))
91 panic("extent_create: fixed extent, bad storagesize 0x%x",
92 storagesize);
93 if (fixed_extent == 0 && (storagesize != 0 || storage != NULL))
94 panic("extent_create: storage provided for non-fixed");
95 #endif
96
97 /* Allocate extent descriptor. */
98 if (fixed_extent) {
99 struct extent_fixed *fex;
100
101 bzero(storage, storagesize);
102
103 /*
104 * Align all descriptors on "long" boundaries.
105 */
106 fex = (struct extent_fixed *)cp;
107 ex = (struct extent *)fex;
108 cp += ALIGN(sizeof(struct extent_fixed));
109 sz -= ALIGN(sizeof(struct extent_fixed));
110 fex->fex_storage = storage;
111 fex->fex_storagesize = storagesize;
112
113 /*
114 * In a fixed extent, we have to pre-allocate region
115 * descriptors and place them in the extent's freelist.
116 */
117 LIST_INIT(&fex->fex_freelist);
118 while (sz >= ALIGN(sizeof(struct extent_region))) {
119 rp = (struct extent_region *)cp;
120 cp += ALIGN(sizeof(struct extent_region));
121 sz -= ALIGN(sizeof(struct extent_region));
122 LIST_INSERT_HEAD(&fex->fex_freelist, rp, er_link);
123 }
124 } else {
125 ex = (struct extent *)malloc(sizeof(struct extent),
126 mtype, (flags & EX_WAITOK) ? M_WAITOK : M_NOWAIT);
127 if (ex == NULL)
128 return (NULL);
129 }
130
131 /* Fill in the extent descriptor and return it to the caller. */
132 LIST_INIT(&ex->ex_regions);
133 ex->ex_name = name;
134 ex->ex_start = start;
135 ex->ex_end = end;
136 ex->ex_mtype = mtype;
137 ex->ex_flags = 0;
138 if (fixed_extent)
139 ex->ex_flags |= EXF_FIXED;
140 if (flags & EX_NOCOALESCE)
141 ex->ex_flags |= EXF_NOCOALESCE;
142 return (ex);
143 }
144
145 /*
146 * Destroy an extent map.
147 */
148 void
149 extent_destroy(ex)
150 struct extent *ex;
151 {
152 struct extent_region *rp, *orp;
153
154 #ifdef DIAGNOSTIC
155 /* Check arguments. */
156 if (ex == NULL)
157 panic("extent_destroy: NULL extent");
158 #endif
159
160 /* Free all region descriptors in extent. */
161 for (rp = ex->ex_regions.lh_first; rp != NULL; ) {
162 orp = rp;
163 rp = rp->er_link.le_next;
164 LIST_REMOVE(orp, er_link);
165 extent_free_region_descriptor(ex, orp);
166 }
167
168 /* If we're not a fixed extent, free the extent descriptor itself. */
169 if ((ex->ex_flags & EXF_FIXED) == 0)
170 free(ex, ex->ex_mtype);
171 }
172
173 /*
174 * Insert a region descriptor into the sorted region list after the
175 * entry "after" or at the head of the list (if "after" is NULL).
176 * The region descriptor we insert is passed in "rp". We must
177 * allocate the region descriptor before calling this function!
178 * If we don't need the region descriptor, it will be freed here.
179 */
180 static void
181 extent_insert_and_optimize(ex, start, size, flags, after, rp)
182 struct extent *ex;
183 u_long start, size;
184 int flags;
185 struct extent_region *after, *rp;
186 {
187 int appended = 0;
188
189 if (after == NULL) {
190 /*
191 * We're the first in the region list. If there's
192 * a region after us, attempt to coalesce to save
193 * descriptor overhead.
194 */
195 if (((ex->ex_flags & EXF_NOCOALESCE) == 0) &&
196 (ex->ex_regions.lh_first != NULL) &&
197 ((start + size) == ex->ex_regions.lh_first->er_start)) {
198 /*
199 * We can coalesce. Prepend us to the first region.
200 */
201 ex->ex_regions.lh_first->er_start = start;
202 extent_free_region_descriptor(ex, rp);
203 return;
204 }
205
206 /*
207 * Can't coalesce. Fill in the region descriptor
208 * in, and insert us at the head of the region list.
209 */
210 rp->er_start = start;
211 rp->er_end = start + (size - 1);
212 LIST_INSERT_HEAD(&ex->ex_regions, rp, er_link);
213 return;
214 }
215
216 /*
217 * If EXF_NOCOALESCE is set, coalescing is disallowed.
218 */
219 if (ex->ex_flags & EXF_NOCOALESCE)
220 goto cant_coalesce;
221
222 /*
223 * Attempt to coalesce with the region before us.
224 */
225 if ((after->er_end + 1) == start) {
226 /*
227 * We can coalesce. Append ourselves and make
228 * note of it.
229 */
230 after->er_end = start + (size - 1);
231 appended = 1;
232 }
233
234 /*
235 * Attempt to coalesce with the region after us.
236 */
237 if ((after->er_link.le_next != NULL) &&
238 ((start + size) == after->er_link.le_next->er_start)) {
239 /*
240 * We can coalesce. Note that if we appended ourselves
241 * to the previous region, we exactly fit the gap, and
242 * can free the "next" region descriptor.
243 */
244 if (appended) {
245 /*
246 * Yup, we can free it up.
247 */
248 after->er_end = after->er_link.le_next->er_end;
249 LIST_REMOVE(after->er_link.le_next, er_link);
250 extent_free_region_descriptor(ex,
251 after->er_link.le_next);
252 } else {
253 /*
254 * Nope, just prepend us to the next region.
255 */
256 after->er_link.le_next->er_start = start;
257 }
258
259 extent_free_region_descriptor(ex, rp);
260 return;
261 }
262
263 /*
264 * We weren't able to coalesce with the next region, but
265 * we don't need to allocate a region descriptor if we
266 * appended ourselves to the previous region.
267 */
268 if (appended) {
269 extent_free_region_descriptor(ex, rp);
270 return;
271 }
272
273 cant_coalesce:
274
275 /*
276 * Fill in the region descriptor and insert ourselves
277 * into the region list.
278 */
279 rp->er_start = start;
280 rp->er_end = start + (size - 1);
281 LIST_INSERT_AFTER(after, rp, er_link);
282 }
283
284 /*
285 * Allocate a specific region in an extent map.
286 */
287 int
288 extent_alloc_region(ex, start, size, flags)
289 struct extent *ex;
290 u_long start, size;
291 int flags;
292 {
293 struct extent_region *rp, *last, *myrp;
294 u_long end = start + (size - 1);
295 int error;
296
297 #ifdef DIAGNOSTIC
298 /* Check arguments. */
299 if (ex == NULL)
300 panic("extent_alloc_region: NULL extent");
301 if (size < 1) {
302 printf("extent_alloc_region: extent `%s', size 0x%lx\n",
303 ex->ex_name, size);
304 panic("extent_alloc_region: bad size");
305 }
306 if (end < start) {
307 printf(
308 "extent_alloc_region: extent `%s', start 0x%lx, size 0x%lx\n",
309 ex->ex_name, start, size);
310 panic("extent_alloc_region: overflow");
311 }
312 #endif
313
314 /*
315 * Make sure the requested region lies within the
316 * extent.
317 */
318 if ((start < ex->ex_start) || (end > ex->ex_end)) {
319 #ifdef DIAGNOSTIC
320 printf("extent_alloc_region: extent `%s' (0x%lx - 0x%lx)\n",
321 ex->ex_name, ex->ex_start, ex->ex_end);
322 printf("extent_alloc_region: start 0x%lx, end 0x%lx\n",
323 start, end);
324 panic("extent_alloc_region: region lies outside extent");
325 #else
326 return (EINVAL);
327 #endif
328 }
329
330 /*
331 * Allocate the region descriptor. It will be freed later
332 * if we can coalesce with another region.
333 */
334 myrp = extent_alloc_region_descriptor(ex, flags);
335 if (myrp == NULL) {
336 #ifdef DIAGNOSTIC
337 printf(
338 "extent_alloc_region: can't allocate region descriptor\n");
339 #endif
340 return (ENOMEM);
341 }
342
343 alloc_start:
344 /*
345 * Attempt to place ourselves in the desired area of the
346 * extent. We save ourselves some work by keeping the list sorted.
347 * In other words, if the start of the current region is greater
348 * than the end of our region, we don't have to search any further.
349 */
350
351 /*
352 * Keep a pointer to the last region we looked at so
353 * that we don't have to traverse the list again when
354 * we insert ourselves. If "last" is NULL when we
355 * finally insert ourselves, we go at the head of the
356 * list. See extent_insert_and_optimize() for details.
357 */
358 last = NULL;
359
360 for (rp = ex->ex_regions.lh_first; rp != NULL;
361 rp = rp->er_link.le_next) {
362 if (rp->er_start > end) {
363 /*
364 * We lie before this region and don't
365 * conflict.
366 */
367 break;
368 }
369
370 /*
371 * The current region begins before we end.
372 * Check for a conflict.
373 */
374 if (rp->er_end >= start) {
375 /*
376 * We conflict. If we can (and want to) wait,
377 * do so.
378 */
379 if (flags & EX_WAITSPACE) {
380 ex->ex_flags |= EXF_WANTED;
381 error = tsleep(ex,
382 PRIBIO | ((flags & EX_CATCH) ? PCATCH : 0),
383 "extnt", 0);
384 if (error)
385 return (error);
386 goto alloc_start;
387 }
388 extent_free_region_descriptor(ex, myrp);
389 return (EAGAIN);
390 }
391 /*
392 * We don't conflict, but this region lies before
393 * us. Keep a pointer to this region, and keep
394 * trying.
395 */
396 last = rp;
397 }
398
399 /*
400 * We don't conflict with any regions. "last" points
401 * to the region we fall after, or is NULL if we belong
402 * at the beginning of the region list. Insert ourselves.
403 */
404 extent_insert_and_optimize(ex, start, size, flags, last, myrp);
405 return (0);
406 }
407
408 /*
409 * Macro to check (x + y) <= z. This check is designed to fail
410 * if an overflow occurs.
411 */
412 #define LE_OV(x, y, z) ((((x) + (y)) >= (x)) && (((x) + (y)) <= (z)))
413
414 /*
415 * Allocate a region in an extent map subregion.
416 *
417 * If EX_FAST is specified, we return the first fit in the map.
418 * Otherwise, we try to minimize fragmentation by finding the
419 * smallest gap that will hold the request.
420 *
421 * The allocated region is aligned to "alignment", which must be
422 * a power of 2.
423 */
424 int
425 extent_alloc_subregion(ex, substart, subend, size, alignment, boundary,
426 flags, result)
427 struct extent *ex;
428 u_long substart, subend, size, alignment, boundary;
429 int flags;
430 u_long *result;
431 {
432 struct extent_region *rp, *myrp, *last, *bestlast;
433 u_long newstart, newend, beststart, bestovh, ovh;
434 u_long dontcross, odontcross;
435 int error;
436
437 #ifdef DIAGNOSTIC
438 /* Check arguments. */
439 if (ex == NULL)
440 panic("extent_alloc_subregion: NULL extent");
441 if (result == NULL)
442 panic("extent_alloc_subregion: NULL result pointer");
443 if ((substart < ex->ex_start) || (substart >= ex->ex_end) ||
444 (subend > ex->ex_end) || (subend <= ex->ex_start)) {
445 printf("extent_alloc_subregion: extent `%s', ex_start 0x%lx, ex_end 0x%lx\n",
446 ex->ex_name, ex->ex_start, ex->ex_end);
447 printf("extent_alloc_subregion: substart 0x%lx, subend 0x%lx\n",
448 substart, subend);
449 panic("extent_alloc_subregion: bad subregion");
450 }
451 if ((size < 1) || (size > ((subend - substart) + 1))) {
452 printf("extent_alloc_subregion: extent `%s', size 0x%lx\n",
453 ex->ex_name, size);
454 panic("extent_alloc_subregion: bad size");
455 }
456 if (alignment == 0)
457 panic("extent_alloc_subregion: bad alignment");
458 if (boundary && (boundary < size)) {
459 printf(
460 "extent_alloc_subregion: extent `%s', size 0x%lx,
461 boundary 0x%lx\n", ex->ex_name, size, boundary);
462 panic("extent_alloc_subregion: bad boundary");
463 }
464 #endif
465
466 /*
467 * Allocate the region descriptor. It will be freed later
468 * if we can coalesce with another region.
469 */
470 myrp = extent_alloc_region_descriptor(ex, flags);
471 if (myrp == NULL) {
472 #ifdef DIAGNOSTIC
473 printf(
474 "extent_alloc_subregion: can't allocate region descriptor\n");
475 #endif
476 return (ENOMEM);
477 }
478
479 alloc_start:
480 /*
481 * Keep a pointer to the last region we looked at so
482 * that we don't have to traverse the list again when
483 * we insert ourselves. If "last" is NULL when we
484 * finally insert ourselves, we go at the head of the
485 * list. See extent_insert_and_optimize() for deatails.
486 */
487 last = NULL;
488
489 /*
490 * Initialize the "don't cross" boundary, a.k.a a line
491 * that a region should not cross. If the boundary lies
492 * before the region starts, we add the "boundary" argument
493 * until we get a meaningful comparison.
494 *
495 * Start the boundary lines at 0 if the caller requests it.
496 */
497 dontcross = 0;
498 if (boundary) {
499 dontcross =
500 ((flags & EX_BOUNDZERO) ? 0 : ex->ex_start) + boundary;
501 while (dontcross < substart)
502 dontcross += boundary;
503 }
504
505 /*
506 * Keep track of size and location of the smallest
507 * chunk we fit in.
508 *
509 * Since the extent can be as large as the numeric range
510 * of the CPU (0 - 0xffffffff for 32-bit systems), the
511 * best overhead value can be the maximum unsigned integer.
512 * Thus, we initialize "bestovh" to 0, since we insert ourselves
513 * into the region list immediately on an exact match (which
514 * is the only case where "bestovh" would be set to 0).
515 */
516 bestovh = 0;
517 beststart = 0;
518 bestlast = NULL;
519
520 /*
521 * For N allocated regions, we must make (N + 1)
522 * checks for unallocated space. The first chunk we
523 * check is the area from the beginning of the subregion
524 * to the first allocated region.
525 */
526 newstart = EXTENT_ALIGN(substart, alignment);
527 if (newstart < ex->ex_start) {
528 #ifdef DIAGNOSTIC
529 printf(
530 "extent_alloc_subregion: extent `%s' (0x%lx - 0x%lx), alignment 0x%lx\n",
531 ex->ex_name, ex->ex_start, ex->ex_end, alignment);
532 panic("extent_alloc_subregion: overflow after alignment");
533 #else
534 extent_free_region_descriptor(ex, myrp);
535 return (EINVAL);
536 #endif
537 }
538
539 for (rp = ex->ex_regions.lh_first; rp != NULL;
540 rp = rp->er_link.le_next) {
541 /*
542 * Check the chunk before "rp". Note that our
543 * comparison is safe from overflow conditions.
544 */
545 if (LE_OV(newstart, size, rp->er_start)) {
546 /*
547 * Do a boundary check, if necessary. Note
548 * that a region may *begin* on the boundary,
549 * but it must end before the boundary.
550 */
551 if (boundary) {
552 newend = newstart + (size - 1);
553
554 /*
555 * Adjust boundary for a meaningful
556 * comparison.
557 */
558 while (dontcross <= newstart) {
559 odontcross = dontcross;
560 dontcross += boundary;
561
562 /*
563 * If we run past the end of
564 * the extent or the boundary
565 * overflows, then the request
566 * can't fit.
567 */
568 if ((dontcross > ex->ex_end) ||
569 (dontcross < odontcross))
570 goto fail;
571 }
572
573 /* Do the boundary check. */
574 if (newend >= dontcross) {
575 /*
576 * Candidate region crosses
577 * boundary. Try again.
578 */
579 continue;
580 }
581 }
582
583 /*
584 * We would fit into this space. Calculate
585 * the overhead (wasted space). If we exactly
586 * fit, or we're taking the first fit, insert
587 * ourselves into the region list.
588 */
589 ovh = rp->er_start - newstart - size;
590 if ((flags & EX_FAST) || (ovh == 0))
591 goto found;
592
593 /*
594 * Don't exactly fit, but check to see
595 * if we're better than any current choice.
596 */
597 if ((bestovh == 0) || (ovh < bestovh)) {
598 bestovh = ovh;
599 beststart = newstart;
600 bestlast = last;
601 }
602 }
603
604 /*
605 * Skip past the current region and check again.
606 */
607 newstart = EXTENT_ALIGN((rp->er_end + 1), alignment);
608 if (newstart < rp->er_end) {
609 /*
610 * Overflow condition. Don't error out, since
611 * we might have a chunk of space that we can
612 * use.
613 */
614 goto fail;
615 }
616
617 last = rp;
618 }
619
620 /*
621 * The final check is from the current starting point to the
622 * end of the subregion. If there were no allocated regions,
623 * "newstart" is set to the beginning of the subregion, or
624 * just past the end of the last allocated region, adjusted
625 * for alignment in either case.
626 */
627 if (LE_OV(newstart, (size - 1), subend)) {
628 /*
629 * We would fit into this space. Calculate
630 * the overhead (wasted space). If we exactly
631 * fit, or we're taking the first fit, insert
632 * ourselves into the region list.
633 */
634 ovh = ex->ex_end - newstart - (size - 1);
635 if ((flags & EX_FAST) || (ovh == 0))
636 goto found;
637
638 /*
639 * Don't exactly fit, but check to see
640 * if we're better than any current choice.
641 */
642 if ((bestovh == 0) || (ovh < bestovh)) {
643 bestovh = ovh;
644 beststart = newstart;
645 bestlast = last;
646 }
647 }
648
649 fail:
650 /*
651 * One of the following two conditions have
652 * occurred:
653 *
654 * There is no chunk large enough to hold the request.
655 *
656 * If EX_FAST was not specified, there is not an
657 * exact match for the request.
658 *
659 * Note that if we reach this point and EX_FAST is
660 * set, then we know there is no space in the extent for
661 * the request.
662 */
663 if (((flags & EX_FAST) == 0) && (bestovh != 0)) {
664 /*
665 * We have a match that's "good enough".
666 */
667 newstart = beststart;
668 last = bestlast;
669 goto found;
670 }
671
672 /*
673 * No space currently available. Wait for it to free up,
674 * if possible.
675 */
676 if (flags & EX_WAITSPACE) {
677 ex->ex_flags |= EXF_WANTED;
678 error = tsleep(ex,
679 PRIBIO | ((flags & EX_CATCH) ? PCATCH : 0), "extnt", 0);
680 if (error)
681 return (error);
682 goto alloc_start;
683 }
684
685 extent_free_region_descriptor(ex, myrp);
686 return (EAGAIN);
687
688 found:
689 /*
690 * Insert ourselves into the region list.
691 */
692 extent_insert_and_optimize(ex, newstart, size, flags, last, myrp);
693 *result = newstart;
694 return (0);
695 }
696
697 int
698 extent_free(ex, start, size, flags)
699 struct extent *ex;
700 u_long start, size;
701 int flags;
702 {
703 struct extent_region *rp;
704 u_long end = start + (size - 1);
705
706 #ifdef DIAGNOSTIC
707 /* Check arguments. */
708 if (ex == NULL)
709 panic("extent_free: NULL extent");
710 if ((start < ex->ex_start) || (start > ex->ex_end)) {
711 extent_print(ex);
712 printf("extent_free: extent `%s', start 0x%lx, size 0x%lx\n",
713 ex->ex_name, start, size);
714 panic("extent_free: extent `%s', region not within extent",
715 ex->ex_name);
716 }
717 /* Check for an overflow. */
718 if (end < start) {
719 extent_print(ex);
720 printf("extent_free: extent `%s', start 0x%lx, size 0x%lx\n",
721 ex->ex_name, start, size);
722 panic("extent_free: overflow");
723 }
724 #endif
725
726 /*
727 * Find region and deallocate. Several possibilities:
728 *
729 * 1. (start == er_start) && (end == er_end):
730 * Free descriptor.
731 *
732 * 2. (start == er_start) && (end < er_end):
733 * Adjust er_start.
734 *
735 * 3. (start > er_start) && (end == er_end):
736 * Adjust er_end.
737 *
738 * 4. (start > er_start) && (end < er_end):
739 * Fragment region. Requires descriptor alloc.
740 *
741 * Cases 2, 3, and 4 require that the EXF_NOCOALESCE flag
742 * is not set.
743 */
744 for (rp = ex->ex_regions.lh_first; rp != NULL;
745 rp = rp->er_link.le_next) {
746 /*
747 * Save ourselves some comparisons; does the current
748 * region end before chunk to be freed begins? If so,
749 * then we haven't found the appropriate region descriptor.
750 */
751 if (rp->er_end < start)
752 continue;
753
754 /*
755 * Save ourselves some traversal; does the current
756 * region begin after the chunk to be freed ends? If so,
757 * then we've already passed any possible region descriptors
758 * that might have contained the chunk to be freed.
759 */
760 if (rp->er_start > end)
761 break;
762
763 /* Case 1. */
764 if ((start == rp->er_start) && (end == rp->er_end)) {
765 LIST_REMOVE(rp, er_link);
766 extent_free_region_descriptor(ex, rp);
767 goto done;
768 }
769
770 /*
771 * The following cases all require that EXF_NOCOALESCE
772 * is not set.
773 */
774 if (ex->ex_flags & EXF_NOCOALESCE)
775 continue;
776
777 /* Case 2. */
778 if ((start == rp->er_start) && (end < rp->er_end)) {
779 rp->er_start = (end + 1);
780 goto done;
781 }
782
783 /* Case 3. */
784 if ((start > rp->er_start) && (end == rp->er_end)) {
785 rp->er_end = (start - 1);
786 goto done;
787 }
788
789 /* Case 4. */
790 if ((start > rp->er_start) && (end < rp->er_end)) {
791 struct extent_region *nrp;
792
793 /* Allocate a region descriptor. */
794 nrp = extent_alloc_region_descriptor(ex, flags);
795 if (nrp == NULL)
796 return (ENOMEM);
797
798 /* Fill in new descriptor. */
799 nrp->er_start = end + 1;
800 nrp->er_end = rp->er_end;
801
802 /* Adjust current descriptor. */
803 rp->er_end = start - 1;
804
805 /* Instert new descriptor after current. */
806 LIST_INSERT_AFTER(rp, nrp, er_link);
807 goto done;
808 }
809 }
810
811 /* Region not found, or request otherwise invalid. */
812 extent_print(ex);
813 printf("extent_free: start 0x%lx, end 0x%lx\n", start, end);
814 panic("extent_free: region not found");
815
816 done:
817 if (ex->ex_flags & EXF_WANTED) {
818 ex->ex_flags &= ~EXF_WANTED;
819 wakeup(ex);
820 }
821 return (0);
822 }
823
824 static struct extent_region *
825 extent_alloc_region_descriptor(ex, flags)
826 struct extent *ex;
827 int flags;
828 {
829 struct extent_region *rp;
830
831 if (ex->ex_flags & EXF_FIXED) {
832 struct extent_fixed *fex = (struct extent_fixed *)ex;
833
834 while (fex->fex_freelist.lh_first == NULL) {
835 if (flags & EX_MALLOCOK)
836 goto alloc;
837
838 if ((flags & EX_WAITOK) == 0)
839 return (NULL);
840 ex->ex_flags |= EXF_FLWANTED;
841 if (tsleep(&fex->fex_freelist,
842 PRIBIO | ((flags & EX_CATCH) ? PCATCH : 0),
843 "extnt", 0))
844 return (NULL);
845 }
846 rp = fex->fex_freelist.lh_first;
847 LIST_REMOVE(rp, er_link);
848
849 /*
850 * Don't muck with flags after pulling it off the
851 * freelist; it may be a dynamiclly allocated
852 * region pointer that was kindly given to us,
853 * and we need to preserve that information.
854 */
855
856 return (rp);
857 }
858
859 alloc:
860 rp = (struct extent_region *)
861 malloc(sizeof(struct extent_region), ex->ex_mtype,
862 (flags & EX_WAITOK) ? M_WAITOK : M_NOWAIT);
863
864 rp->er_flags = ER_ALLOC;
865
866 return (rp);
867 }
868
869 static void
870 extent_free_region_descriptor(ex, rp)
871 struct extent *ex;
872 struct extent_region *rp;
873 {
874
875 if (ex->ex_flags & EXF_FIXED) {
876 struct extent_fixed *fex = (struct extent_fixed *)ex;
877
878 /*
879 * If someone's waiting for a region descriptor,
880 * be nice and give them this one, rather than
881 * just free'ing it back to the system.
882 */
883 if (rp->er_flags & ER_ALLOC) {
884 if (ex->ex_flags & EXF_FLWANTED) {
885 /* Clear all but ER_ALLOC flag. */
886 rp->er_flags = ER_ALLOC;
887 LIST_INSERT_HEAD(&fex->fex_freelist, rp,
888 er_link);
889 goto wake_em_up;
890 } else {
891 free(rp, ex->ex_mtype);
892 }
893 } else {
894 /* Clear all flags. */
895 rp->er_flags = 0;
896 LIST_INSERT_HEAD(&fex->fex_freelist, rp, er_link);
897 }
898
899 if (ex->ex_flags & EXF_FLWANTED) {
900 wake_em_up:
901 ex->ex_flags &= ~EXF_FLWANTED;
902 wakeup(&fex->fex_freelist);
903 }
904 return;
905 }
906
907 /*
908 * We know it's dynamically allocated if we get here.
909 */
910 free(rp, ex->ex_mtype);
911 }
912
913 void
914 extent_print(ex)
915 struct extent *ex;
916 {
917 struct extent_region *rp;
918
919 if (ex == NULL)
920 panic("extent_print: NULL extent");
921
922 printf("extent `%s' (0x%lx - 0x%lx), flags = 0x%x\n", ex->ex_name,
923 ex->ex_start, ex->ex_end, ex->ex_flags);
924
925 for (rp = ex->ex_regions.lh_first; rp != NULL;
926 rp = rp->er_link.le_next)
927 printf(" 0x%lx - 0x%lx\n", rp->er_start, rp->er_end);
928 }
929