xref: /csrg-svn/sys/kern/subr_rmap.c (revision 65771)
149589Sbostic /*-
263176Sbostic  * Copyright (c) 1982, 1986, 1993
363176Sbostic  *	The Regents of the University of California.  All rights reserved.
4*65771Sbostic  * (c) UNIX System Laboratories, Inc.
5*65771Sbostic  * All or some portions of this file are derived from material licensed
6*65771Sbostic  * to the University of California by American Telephone and Telegraph
7*65771Sbostic  * Co. or Unix System Laboratories, Inc. and are reproduced herein with
8*65771Sbostic  * the permission of UNIX System Laboratories, Inc.
923382Smckusick  *
1049589Sbostic  * %sccs.include.proprietary.c%
1149589Sbostic  *
12*65771Sbostic  *	@(#)subr_rmap.c	8.2 (Berkeley) 01/21/94
1323382Smckusick  */
1427Sbill 
1556517Sbostic #include <sys/param.h>
1656517Sbostic #include <sys/systm.h>
1756517Sbostic #include <sys/map.h>
1856517Sbostic #include <sys/dmap.h>		/* XXX */
1956517Sbostic #include <sys/proc.h>
2056517Sbostic #include <sys/kernel.h>
2127Sbill 
2227Sbill /*
232781Swnj  * Resource map handling routines.
242781Swnj  *
252781Swnj  * A resource map is an array of structures each
262781Swnj  * of which describes a segment of the address space of an available
272781Swnj  * resource.  The segments are described by their base address and
282781Swnj  * length, and sorted in address order.  Each resource map has a fixed
292781Swnj  * maximum number of segments allowed.  Resources are allocated
302781Swnj  * by taking part or all of one of the segments of the map.
312781Swnj  *
322781Swnj  * Returning of resources will require another segment if
332781Swnj  * the returned resources are not adjacent in the address
342781Swnj  * space to an existing segment.  If the return of a segment
352781Swnj  * would require a slot which is not available, then one of
362781Swnj  * the resource map segments is discarded after a warning is printed.
372781Swnj  * Returning of resources may also cause the map to collapse
382781Swnj  * by coalescing two existing segments and the returned space
392781Swnj  * into a single segment.  In this case the resource map is
402781Swnj  * made smaller by copying together to fill the resultant gap.
412781Swnj  *
422781Swnj  * N.B.: the current implementation uses a dense array and does
432781Swnj  * not admit the value ``0'' as a legal address, since that is used
442781Swnj  * as a delimiter.
452781Swnj  */
462781Swnj 
472781Swnj /*
482781Swnj  * Initialize map mp to have (mapsize-2) segments
492781Swnj  * and to be called ``name'', which we print if
502781Swnj  * the slots become so fragmented that we lose space.
512781Swnj  * The map itself is initialized with size elements free
522781Swnj  * starting at addr.
532781Swnj  */
5453470Sbostic void
rminit(mp,size,addr,name,mapsize)552781Swnj rminit(mp, size, addr, name, mapsize)
562781Swnj 	register struct map *mp;
578790Sroot 	long size, addr;
582781Swnj 	char *name;
592781Swnj 	int mapsize;
602781Swnj {
612781Swnj 	register struct mapent *ep = (struct mapent *)(mp+1);
622781Swnj 
632781Swnj 	mp->m_name = name;
642781Swnj /* N.B.: WE ASSUME HERE THAT sizeof (struct map) == sizeof (struct mapent) */
652781Swnj 	/*
662781Swnj 	 * One of the mapsize slots is taken by the map structure,
672781Swnj 	 * segments has size 0 and addr 0, and acts as a delimiter.
682781Swnj 	 * We insure that we never use segments past the end of
692781Swnj 	 * the array which is given by mp->m_limit.
702781Swnj 	 * Instead, when excess segments occur we discard some resources.
712781Swnj 	 */
722781Swnj 	mp->m_limit = (struct mapent *)&mp[mapsize];
732781Swnj 	/*
742781Swnj 	 * Simulate a rmfree(), but with the option to
752781Swnj 	 * call with size 0 and addr 0 when we just want
762781Swnj 	 * to initialize without freeing.
772781Swnj 	 */
782781Swnj 	ep->m_size = size;
792781Swnj 	ep->m_addr = addr;
8033605Skarels 	(++ep)->m_size = 0;
8133605Skarels 	ep->m_addr = 0;
822781Swnj }
832781Swnj 
842781Swnj /*
8549673Smckusick  * A piece of memory of at least size units is allocated from the
8649673Smckusick  * specified map using a first-fit algorithm. It returns the starting
8749673Smckusick  * address of the allocated space.
882781Swnj  *
8949673Smckusick  * This routine knows about and handles the interleaving of the swapmap.
9027Sbill  */
918766Sroot long
rmalloc(mp,size)922781Swnj rmalloc(mp, size)
932781Swnj 	register struct map *mp;
948766Sroot 	long size;
9527Sbill {
962781Swnj 	register struct mapent *ep = (struct mapent *)(mp+1);
972781Swnj 	register int addr;
982781Swnj 	register struct mapent *bp;
99307Sbill 	swblk_t first, rest;
10027Sbill 
10112489Ssam 	if (size <= 0 || mp == swapmap && size > dmmax)
1022781Swnj 		panic("rmalloc");
1032781Swnj 	/*
1042781Swnj 	 * Search for a piece of the resource map which has enough
1052781Swnj 	 * free space to accomodate the request.
1062781Swnj 	 */
1072781Swnj 	for (bp = ep; bp->m_size; bp++) {
10827Sbill 		if (bp->m_size >= size) {
1092781Swnj 			/*
1102781Swnj 			 * If allocating from swapmap,
1112781Swnj 			 * then have to respect interleaving
1122781Swnj 			 * boundaries.
1132781Swnj 			 */
11412489Ssam 			if (mp == swapmap && nswdev > 1 &&
11541988Smckusick 			    (first = dmmax - bp->m_addr%dmmax) < size) {
116307Sbill 				if (bp->m_size - first < size)
117307Sbill 					continue;
1182781Swnj 				addr = bp->m_addr + first;
119307Sbill 				rest = bp->m_size - first - size;
120307Sbill 				bp->m_size = first;
121307Sbill 				if (rest)
1222781Swnj 					rmfree(swapmap, rest, addr+size);
1232781Swnj 				return (addr);
124307Sbill 			}
1252781Swnj 			/*
1262781Swnj 			 * Allocate from the map.
1272781Swnj 			 * If there is no space left of the piece
1282781Swnj 			 * we allocated from, move the rest of
1292781Swnj 			 * the pieces to the left.
1302781Swnj 			 */
1312781Swnj 			addr = bp->m_addr;
13227Sbill 			bp->m_addr += size;
13327Sbill 			if ((bp->m_size -= size) == 0) {
13427Sbill 				do {
13527Sbill 					bp++;
13627Sbill 					(bp-1)->m_addr = bp->m_addr;
13727Sbill 				} while ((bp-1)->m_size = bp->m_size);
13827Sbill 			}
1392781Swnj 			if (mp == swapmap && addr % CLSIZE)
1402781Swnj 				panic("rmalloc swapmap");
1412781Swnj 			return (addr);
14227Sbill 		}
14327Sbill 	}
1442781Swnj 	return (0);
14527Sbill }
14627Sbill 
14727Sbill /*
14849673Smckusick  * The previously allocated space at addr of size units is freed
14949673Smckusick  * into the specified map. This routine is responsible for sorting
15049673Smckusick  * the frred space into the correct location in the map, and coalescing
15149673Smckusick  * it with free space on either side if they adjoin.
15227Sbill  */
15353470Sbostic void
rmfree(mp,size,addr)1542781Swnj rmfree(mp, size, addr)
1552781Swnj 	struct map *mp;
1568766Sroot 	long size, addr;
15727Sbill {
1582781Swnj 	struct mapent *firstbp;
1592781Swnj 	register struct mapent *bp;
16027Sbill 	register int t;
16127Sbill 
1622781Swnj 	/*
1632781Swnj 	 * Both address and size must be
1642781Swnj 	 * positive, or the protocol has broken down.
1652781Swnj 	 */
1662781Swnj 	if (addr <= 0 || size <= 0)
1672781Swnj 		goto badrmfree;
1682781Swnj 	/*
1692781Swnj 	 * Locate the piece of the map which starts after the
1702781Swnj 	 * returned space (or the end of the map).
1712781Swnj 	 */
1722781Swnj 	firstbp = bp = (struct mapent *)(mp + 1);
1732781Swnj 	for (; bp->m_addr <= addr && bp->m_size != 0; bp++)
17427Sbill 		continue;
1752781Swnj 	/*
1762781Swnj 	 * If the piece on the left abuts us,
1772781Swnj 	 * then we should combine with it.
1782781Swnj 	 */
1792781Swnj 	if (bp > firstbp && (bp-1)->m_addr+(bp-1)->m_size >= addr) {
1802781Swnj 		/*
1812781Swnj 		 * Check no overlap (internal error).
1822781Swnj 		 */
1832781Swnj 		if ((bp-1)->m_addr+(bp-1)->m_size > addr)
1842781Swnj 			goto badrmfree;
1852781Swnj 		/*
1862781Swnj 		 * Add into piece on the left by increasing its size.
1872781Swnj 		 */
18827Sbill 		(bp-1)->m_size += size;
1892781Swnj 		/*
1902781Swnj 		 * If the combined piece abuts the piece on
1912781Swnj 		 * the right now, compress it in also,
1922781Swnj 		 * by shifting the remaining pieces of the map over.
1932781Swnj 		 */
1942781Swnj 		if (bp->m_addr && addr+size >= bp->m_addr) {
1952781Swnj 			if (addr+size > bp->m_addr)
1962781Swnj 				goto badrmfree;
19727Sbill 			(bp-1)->m_size += bp->m_size;
19827Sbill 			while (bp->m_size) {
19927Sbill 				bp++;
20027Sbill 				(bp-1)->m_addr = bp->m_addr;
20127Sbill 				(bp-1)->m_size = bp->m_size;
20227Sbill 			}
20327Sbill 		}
20449281Shibler 		return;
20527Sbill 	}
2062781Swnj 	/*
2072781Swnj 	 * Don't abut on the left, check for abutting on
2082781Swnj 	 * the right.
2092781Swnj 	 */
2102781Swnj 	if (addr+size >= bp->m_addr && bp->m_size) {
2112781Swnj 		if (addr+size > bp->m_addr)
2122781Swnj 			goto badrmfree;
2132781Swnj 		bp->m_addr -= size;
2142781Swnj 		bp->m_size += size;
21549281Shibler 		return;
2162781Swnj 	}
2172781Swnj 	/*
2182781Swnj 	 * Don't abut at all.  Make a new entry
2192781Swnj 	 * and check for map overflow.
2202781Swnj 	 */
2212781Swnj 	do {
2222781Swnj 		t = bp->m_addr;
2232781Swnj 		bp->m_addr = addr;
2242781Swnj 		addr = t;
2252781Swnj 		t = bp->m_size;
2262781Swnj 		bp->m_size = size;
2272781Swnj 		bp++;
2282781Swnj 	} while (size = t);
2292781Swnj 	/*
2302781Swnj 	 * Segment at bp is to be the delimiter;
2312781Swnj 	 * If there is not room for it
2322781Swnj 	 * then the table is too full
2332781Swnj 	 * and we must discard something.
2342781Swnj 	 */
2352781Swnj 	if (bp+1 > mp->m_limit) {
2362781Swnj 		/*
2372781Swnj 		 * Back bp up to last available segment.
2382781Swnj 		 * which contains a segment already and must
2392781Swnj 		 * be made into the delimiter.
2402781Swnj 		 * Discard second to last entry,
2412781Swnj 		 * since it is presumably smaller than the last
2422781Swnj 		 * and move the last entry back one.
2432781Swnj 		 */
2442781Swnj 		bp--;
2452937Swnj 		printf("%s: rmap ovflo, lost [%d,%d)\n", mp->m_name,
2462781Swnj 		    (bp-1)->m_addr, (bp-1)->m_addr+(bp-1)->m_size);
2472781Swnj 		bp[-1] = bp[0];
2482781Swnj 		bp[0].m_size = bp[0].m_addr = 0;
2492781Swnj 	}
2502781Swnj 	return;
2512781Swnj badrmfree:
2522781Swnj 	panic("bad rmfree");
25327Sbill }
254