xref: /csrg-svn/sys/kern/subr_rmap.c (revision 33605)
123382Smckusick /*
229102Smckusick  * Copyright (c) 1982, 1986 Regents of the University of California.
323382Smckusick  * All rights reserved.  The Berkeley software License Agreement
423382Smckusick  * specifies the terms and conditions for redistribution.
523382Smckusick  *
6*33605Skarels  *	@(#)subr_rmap.c	7.2 (Berkeley) 02/27/88
723382Smckusick  */
827Sbill 
917094Sbloom #include "param.h"
1017094Sbloom #include "systm.h"
1117094Sbloom #include "map.h"
1217094Sbloom #include "dir.h"
1317094Sbloom #include "user.h"
1417094Sbloom #include "proc.h"
1517094Sbloom #include "text.h"
1617094Sbloom #include "kernel.h"
1727Sbill 
1827Sbill /*
192781Swnj  * Resource map handling routines.
202781Swnj  *
212781Swnj  * A resource map is an array of structures each
222781Swnj  * of which describes a segment of the address space of an available
232781Swnj  * resource.  The segments are described by their base address and
242781Swnj  * length, and sorted in address order.  Each resource map has a fixed
252781Swnj  * maximum number of segments allowed.  Resources are allocated
262781Swnj  * by taking part or all of one of the segments of the map.
272781Swnj  *
282781Swnj  * Returning of resources will require another segment if
292781Swnj  * the returned resources are not adjacent in the address
302781Swnj  * space to an existing segment.  If the return of a segment
312781Swnj  * would require a slot which is not available, then one of
322781Swnj  * the resource map segments is discarded after a warning is printed.
332781Swnj  * Returning of resources may also cause the map to collapse
342781Swnj  * by coalescing two existing segments and the returned space
352781Swnj  * into a single segment.  In this case the resource map is
362781Swnj  * made smaller by copying together to fill the resultant gap.
372781Swnj  *
382781Swnj  * N.B.: the current implementation uses a dense array and does
392781Swnj  * not admit the value ``0'' as a legal address, since that is used
402781Swnj  * as a delimiter.
412781Swnj  */
422781Swnj 
432781Swnj /*
442781Swnj  * Initialize map mp to have (mapsize-2) segments
452781Swnj  * and to be called ``name'', which we print if
462781Swnj  * the slots become so fragmented that we lose space.
472781Swnj  * The map itself is initialized with size elements free
482781Swnj  * starting at addr.
492781Swnj  */
502781Swnj rminit(mp, size, addr, name, mapsize)
512781Swnj 	register struct map *mp;
528790Sroot 	long size, addr;
532781Swnj 	char *name;
542781Swnj 	int mapsize;
552781Swnj {
562781Swnj 	register struct mapent *ep = (struct mapent *)(mp+1);
572781Swnj 
582781Swnj 	mp->m_name = name;
592781Swnj /* N.B.: WE ASSUME HERE THAT sizeof (struct map) == sizeof (struct mapent) */
602781Swnj 	/*
612781Swnj 	 * One of the mapsize slots is taken by the map structure,
622781Swnj 	 * segments has size 0 and addr 0, and acts as a delimiter.
632781Swnj 	 * We insure that we never use segments past the end of
642781Swnj 	 * the array which is given by mp->m_limit.
652781Swnj 	 * Instead, when excess segments occur we discard some resources.
662781Swnj 	 */
672781Swnj 	mp->m_limit = (struct mapent *)&mp[mapsize];
682781Swnj 	/*
692781Swnj 	 * Simulate a rmfree(), but with the option to
702781Swnj 	 * call with size 0 and addr 0 when we just want
712781Swnj 	 * to initialize without freeing.
722781Swnj 	 */
732781Swnj 	ep->m_size = size;
742781Swnj 	ep->m_addr = addr;
75*33605Skarels 	(++ep)->m_size = 0;
76*33605Skarels 	ep->m_addr = 0;
772781Swnj }
782781Swnj 
792781Swnj /*
8027Sbill  * Allocate 'size' units from the given
812781Swnj  * map. Return the base of the allocated space.
8227Sbill  * In a map, the addresses are increasing and the
8327Sbill  * list is terminated by a 0 size.
842781Swnj  *
8527Sbill  * Algorithm is first-fit.
862781Swnj  *
872781Swnj  * This routine knows about the interleaving of the swapmap
882781Swnj  * and handles that.
8927Sbill  */
908766Sroot long
912781Swnj rmalloc(mp, size)
922781Swnj 	register struct map *mp;
938766Sroot 	long size;
9427Sbill {
952781Swnj 	register struct mapent *ep = (struct mapent *)(mp+1);
962781Swnj 	register int addr;
972781Swnj 	register struct mapent *bp;
98307Sbill 	swblk_t first, rest;
9927Sbill 
10012489Ssam 	if (size <= 0 || mp == swapmap && size > dmmax)
1012781Swnj 		panic("rmalloc");
1022781Swnj 	/*
1032781Swnj 	 * Search for a piece of the resource map which has enough
1042781Swnj 	 * free space to accomodate the request.
1052781Swnj 	 */
1062781Swnj 	for (bp = ep; bp->m_size; bp++) {
10727Sbill 		if (bp->m_size >= size) {
1082781Swnj 			/*
1092781Swnj 			 * If allocating from swapmap,
1102781Swnj 			 * then have to respect interleaving
1112781Swnj 			 * boundaries.
1122781Swnj 			 */
11312489Ssam 			if (mp == swapmap && nswdev > 1 &&
11412489Ssam 			    (first = dmmax - bp->m_addr%dmmax) < bp->m_size) {
115307Sbill 				if (bp->m_size - first < size)
116307Sbill 					continue;
1172781Swnj 				addr = bp->m_addr + first;
118307Sbill 				rest = bp->m_size - first - size;
119307Sbill 				bp->m_size = first;
120307Sbill 				if (rest)
1212781Swnj 					rmfree(swapmap, rest, addr+size);
1222781Swnj 				return (addr);
123307Sbill 			}
1242781Swnj 			/*
1252781Swnj 			 * Allocate from the map.
1262781Swnj 			 * If there is no space left of the piece
1272781Swnj 			 * we allocated from, move the rest of
1282781Swnj 			 * the pieces to the left.
1292781Swnj 			 */
1302781Swnj 			addr = bp->m_addr;
13127Sbill 			bp->m_addr += size;
13227Sbill 			if ((bp->m_size -= size) == 0) {
13327Sbill 				do {
13427Sbill 					bp++;
13527Sbill 					(bp-1)->m_addr = bp->m_addr;
13627Sbill 				} while ((bp-1)->m_size = bp->m_size);
13727Sbill 			}
1382781Swnj 			if (mp == swapmap && addr % CLSIZE)
1392781Swnj 				panic("rmalloc swapmap");
1402781Swnj 			return (addr);
14127Sbill 		}
14227Sbill 	}
1432781Swnj 	return (0);
14427Sbill }
14527Sbill 
14627Sbill /*
1472781Swnj  * Free the previously allocated space at addr
14827Sbill  * of size units into the specified map.
1492781Swnj  * Sort addr into map and combine on
15027Sbill  * one or both ends if possible.
15127Sbill  */
1522781Swnj rmfree(mp, size, addr)
1532781Swnj 	struct map *mp;
1548766Sroot 	long size, addr;
15527Sbill {
1562781Swnj 	struct mapent *firstbp;
1572781Swnj 	register struct mapent *bp;
15827Sbill 	register int t;
15927Sbill 
1602781Swnj 	/*
1612781Swnj 	 * Both address and size must be
1622781Swnj 	 * positive, or the protocol has broken down.
1632781Swnj 	 */
1642781Swnj 	if (addr <= 0 || size <= 0)
1652781Swnj 		goto badrmfree;
1662781Swnj 	/*
1672781Swnj 	 * Locate the piece of the map which starts after the
1682781Swnj 	 * returned space (or the end of the map).
1692781Swnj 	 */
1702781Swnj 	firstbp = bp = (struct mapent *)(mp + 1);
1712781Swnj 	for (; bp->m_addr <= addr && bp->m_size != 0; bp++)
17227Sbill 		continue;
1732781Swnj 	/*
1742781Swnj 	 * If the piece on the left abuts us,
1752781Swnj 	 * then we should combine with it.
1762781Swnj 	 */
1772781Swnj 	if (bp > firstbp && (bp-1)->m_addr+(bp-1)->m_size >= addr) {
1782781Swnj 		/*
1792781Swnj 		 * Check no overlap (internal error).
1802781Swnj 		 */
1812781Swnj 		if ((bp-1)->m_addr+(bp-1)->m_size > addr)
1822781Swnj 			goto badrmfree;
1832781Swnj 		/*
1842781Swnj 		 * Add into piece on the left by increasing its size.
1852781Swnj 		 */
18627Sbill 		(bp-1)->m_size += size;
1872781Swnj 		/*
1882781Swnj 		 * If the combined piece abuts the piece on
1892781Swnj 		 * the right now, compress it in also,
1902781Swnj 		 * by shifting the remaining pieces of the map over.
1912781Swnj 		 */
1922781Swnj 		if (bp->m_addr && addr+size >= bp->m_addr) {
1932781Swnj 			if (addr+size > bp->m_addr)
1942781Swnj 				goto badrmfree;
19527Sbill 			(bp-1)->m_size += bp->m_size;
19627Sbill 			while (bp->m_size) {
19727Sbill 				bp++;
19827Sbill 				(bp-1)->m_addr = bp->m_addr;
19927Sbill 				(bp-1)->m_size = bp->m_size;
20027Sbill 			}
20127Sbill 		}
2022781Swnj 		goto done;
20327Sbill 	}
2042781Swnj 	/*
2052781Swnj 	 * Don't abut on the left, check for abutting on
2062781Swnj 	 * the right.
2072781Swnj 	 */
2082781Swnj 	if (addr+size >= bp->m_addr && bp->m_size) {
2092781Swnj 		if (addr+size > bp->m_addr)
2102781Swnj 			goto badrmfree;
2112781Swnj 		bp->m_addr -= size;
2122781Swnj 		bp->m_size += size;
2132781Swnj 		goto done;
2142781Swnj 	}
2152781Swnj 	/*
2162781Swnj 	 * Don't abut at all.  Make a new entry
2172781Swnj 	 * and check for map overflow.
2182781Swnj 	 */
2192781Swnj 	do {
2202781Swnj 		t = bp->m_addr;
2212781Swnj 		bp->m_addr = addr;
2222781Swnj 		addr = t;
2232781Swnj 		t = bp->m_size;
2242781Swnj 		bp->m_size = size;
2252781Swnj 		bp++;
2262781Swnj 	} while (size = t);
2272781Swnj 	/*
2282781Swnj 	 * Segment at bp is to be the delimiter;
2292781Swnj 	 * If there is not room for it
2302781Swnj 	 * then the table is too full
2312781Swnj 	 * and we must discard something.
2322781Swnj 	 */
2332781Swnj 	if (bp+1 > mp->m_limit) {
2342781Swnj 		/*
2352781Swnj 		 * Back bp up to last available segment.
2362781Swnj 		 * which contains a segment already and must
2372781Swnj 		 * be made into the delimiter.
2382781Swnj 		 * Discard second to last entry,
2392781Swnj 		 * since it is presumably smaller than the last
2402781Swnj 		 * and move the last entry back one.
2412781Swnj 		 */
2422781Swnj 		bp--;
2432937Swnj 		printf("%s: rmap ovflo, lost [%d,%d)\n", mp->m_name,
2442781Swnj 		    (bp-1)->m_addr, (bp-1)->m_addr+(bp-1)->m_size);
2452781Swnj 		bp[-1] = bp[0];
2462781Swnj 		bp[0].m_size = bp[0].m_addr = 0;
2472781Swnj 	}
2482781Swnj done:
2492781Swnj 	/*
2502781Swnj 	 * THIS IS RIDICULOUS... IT DOESN'T BELONG HERE!
2512781Swnj 	 */
25227Sbill 	if ((mp == kernelmap) && kmapwnt) {
25327Sbill 		kmapwnt = 0;
25427Sbill 		wakeup((caddr_t)kernelmap);
25527Sbill 	}
2562781Swnj 	return;
2572781Swnj badrmfree:
2582781Swnj 	panic("bad rmfree");
25927Sbill }
2606516Sfeldman 
2616516Sfeldman /*
2626516Sfeldman  * Allocate 'size' units from the given map, starting at address 'addr'.
2636516Sfeldman  * Return 'addr' if successful, 0 if not.
2646516Sfeldman  * This may cause the creation or destruction of a resource map segment.
2656516Sfeldman  *
2666516Sfeldman  * This routine will return failure status if there is not enough room
2676516Sfeldman  * for a required additional map segment.
2686516Sfeldman  *
2696516Sfeldman  * An attempt to use this on 'swapmap' will result in
2706516Sfeldman  * a failure return.  This is due mainly to laziness and could be fixed
2716516Sfeldman  * to do the right thing, although it probably will never be used.
2726516Sfeldman  */
2736516Sfeldman rmget(mp, size, addr)
2746516Sfeldman 	register struct map *mp;
2756516Sfeldman {
2766516Sfeldman 	register struct mapent *ep = (struct mapent *)(mp+1);
2776516Sfeldman 	register struct mapent *bp, *bp2;
2786516Sfeldman 
2796516Sfeldman 	if (size <= 0)
2806516Sfeldman 		panic("rmget");
2816516Sfeldman 	if (mp == swapmap)
2826516Sfeldman 		return (0);
2836516Sfeldman 	/*
2846516Sfeldman 	 * Look for a map segment containing the requested address.
2856516Sfeldman 	 * If none found, return failure.
2866516Sfeldman 	 */
2876516Sfeldman 	for (bp = ep; bp->m_size; bp++)
2886516Sfeldman 		if (bp->m_addr <= addr && bp->m_addr + bp->m_size > addr)
2896516Sfeldman 			break;
2906516Sfeldman 	if (bp->m_size == 0)
2916516Sfeldman 		return (0);
2926516Sfeldman 
2936516Sfeldman 	/*
2946516Sfeldman 	 * If segment is too small, return failure.
2956516Sfeldman 	 * If big enough, allocate the block, compressing or expanding
2966516Sfeldman 	 * the map as necessary.
2976516Sfeldman 	 */
2986516Sfeldman 	if (bp->m_addr + bp->m_size < addr + size)
2996516Sfeldman 		return (0);
3006516Sfeldman 	if (bp->m_addr == addr)
3016516Sfeldman 		if (bp->m_addr + bp->m_size == addr + size) {
3026516Sfeldman 			/*
3036516Sfeldman 			 * Allocate entire segment and compress map
3046516Sfeldman 			 */
3056516Sfeldman 			bp2 = bp;
3066516Sfeldman 			while (bp2->m_size) {
3076516Sfeldman 				bp2++;
3086516Sfeldman 				(bp2-1)->m_addr = bp2->m_addr;
3096516Sfeldman 				(bp2-1)->m_size = bp2->m_size;
3106516Sfeldman 			}
3116516Sfeldman 		} else {
3126516Sfeldman 			/*
3136516Sfeldman 			 * Allocate first part of segment
3146516Sfeldman 			 */
3156516Sfeldman 			bp->m_addr += size;
3166516Sfeldman 			bp->m_size -= size;
3176516Sfeldman 		}
3186516Sfeldman 	else
3196516Sfeldman 		if (bp->m_addr + bp->m_size == addr + size) {
3206516Sfeldman 			/*
3216516Sfeldman 			 * Allocate last part of segment
3226516Sfeldman 			 */
3236516Sfeldman 			bp->m_size -= size;
3246516Sfeldman 		} else {
3256516Sfeldman 			/*
3266516Sfeldman 			 * Allocate from middle of segment, but only
3276516Sfeldman 			 * if table can be expanded.
3286516Sfeldman 			 */
3296516Sfeldman 			for (bp2=bp; bp2->m_size; bp2++)
3306516Sfeldman 				;
33125113Sbloom 			if (bp2 + 1 >= mp->m_limit)
3326516Sfeldman 				return (0);
3336516Sfeldman 			while (bp2 > bp) {
3346516Sfeldman 				(bp2+1)->m_addr = bp2->m_addr;
3356516Sfeldman 				(bp2+1)->m_size = bp2->m_size;
3366516Sfeldman 				bp2--;
3376516Sfeldman 			}
3386516Sfeldman 			(bp+1)->m_addr = addr + size;
3396516Sfeldman 			(bp+1)->m_size =
3406516Sfeldman 			    bp->m_addr + bp->m_size - (addr + size);
3416516Sfeldman 			bp->m_size = addr - bp->m_addr;
3426516Sfeldman 		}
3436516Sfeldman 	return (addr);
3446516Sfeldman }
345