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