xref: /original-bsd/sys/kern/subr_rmap.c.sav (revision e59fb703)
1/*
2 * Copyright (c) 1982, 1986 Regents of the University of California.
3 * All rights reserved.  The Berkeley software License Agreement
4 * specifies the terms and conditions for redistribution.
5 *
6 *	@(#)subr_rmap.c.sav	7.1 (Berkeley) 06/05/86
7 */
8
9#include "param.h"
10#include "systm.h"
11#include "map.h"
12#include "dir.h"
13#include "user.h"
14#include "proc.h"
15#include "text.h"
16#include "kernel.h"
17
18/*
19 * Resource map handling routines.
20 *
21 * A resource map is an array of structures each
22 * of which describes a segment of the address space of an available
23 * resource.  The segments are described by their base address and
24 * length, and sorted in address order.  Each resource map has a fixed
25 * maximum number of segments allowed.  Resources are allocated
26 * by taking part or all of one of the segments of the map.
27 *
28 * Returning of resources will require another segment if
29 * the returned resources are not adjacent in the address
30 * space to an existing segment.  If the return of a segment
31 * would require a slot which is not available, then one of
32 * the resource map segments is discarded after a warning is printed.
33 * Returning of resources may also cause the map to collapse
34 * by coalescing two existing segments and the returned space
35 * into a single segment.  In this case the resource map is
36 * made smaller by copying together to fill the resultant gap.
37 *
38 * N.B.: the current implementation uses a dense array and does
39 * not admit the value ``0'' as a legal address, since that is used
40 * as a delimiter.
41 */
42
43/*
44 * Initialize map mp to have (mapsize-2) segments
45 * and to be called ``name'', which we print if
46 * the slots become so fragmented that we lose space.
47 * The map itself is initialized with size elements free
48 * starting at addr.
49 */
50rminit(mp, size, addr, name, mapsize)
51	register struct map *mp;
52	long size, addr;
53	char *name;
54	int mapsize;
55{
56	register struct mapent *ep = (struct mapent *)(mp+1);
57
58	mp->m_name = name;
59/* N.B.: WE ASSUME HERE THAT sizeof (struct map) == sizeof (struct mapent) */
60	/*
61	 * One of the mapsize slots is taken by the map structure,
62	 * segments has size 0 and addr 0, and acts as a delimiter.
63	 * We insure that we never use segments past the end of
64	 * the array which is given by mp->m_limit.
65	 * Instead, when excess segments occur we discard some resources.
66	 */
67	mp->m_limit = (struct mapent *)&mp[mapsize];
68	/*
69	 * Simulate a rmfree(), but with the option to
70	 * call with size 0 and addr 0 when we just want
71	 * to initialize without freeing.
72	 */
73	ep->m_size = size;
74	ep->m_addr = addr;
75}
76
77/*
78 * Allocate 'size' units from the given
79 * map. Return the base of the allocated space.
80 * In a map, the addresses are increasing and the
81 * list is terminated by a 0 size.
82 *
83 * Algorithm is first-fit.
84 *
85 * This routine knows about the interleaving of the swapmap
86 * and handles that.
87 */
88long
89rmalloc(mp, size)
90	register struct map *mp;
91	long size;
92{
93	register struct mapent *ep = (struct mapent *)(mp+1);
94	register int addr;
95	register struct mapent *bp;
96	swblk_t first, rest;
97
98	if (size <= 0 || mp == swapmap && size > dmmax)
99		panic("rmalloc");
100	/*
101	 * Search for a piece of the resource map which has enough
102	 * free space to accomodate the request.
103	 */
104	for (bp = ep; bp->m_size; bp++) {
105		if (bp->m_size >= size) {
106			/*
107			 * If allocating from swapmap,
108			 * then have to respect interleaving
109			 * boundaries.
110			 */
111			if (mp == swapmap && nswdev > 1 &&
112			    (first = dmmax - bp->m_addr%dmmax) < bp->m_size) {
113				if (bp->m_size - first < size)
114					continue;
115				addr = bp->m_addr + first;
116				rest = bp->m_size - first - size;
117				bp->m_size = first;
118				if (rest)
119					rmfree(swapmap, rest, addr+size);
120				return (addr);
121			}
122			/*
123			 * Allocate from the map.
124			 * If there is no space left of the piece
125			 * we allocated from, move the rest of
126			 * the pieces to the left.
127			 */
128			addr = bp->m_addr;
129			bp->m_addr += size;
130			if ((bp->m_size -= size) == 0) {
131				do {
132					bp++;
133					(bp-1)->m_addr = bp->m_addr;
134				} while ((bp-1)->m_size = bp->m_size);
135			}
136			if (mp == swapmap && addr % CLSIZE)
137				panic("rmalloc swapmap");
138			return (addr);
139		}
140	}
141	return (0);
142}
143
144/*
145 * Free the previously allocated space at addr
146 * of size units into the specified map.
147 * Sort addr into map and combine on
148 * one or both ends if possible.
149 */
150rmfree(mp, size, addr)
151	struct map *mp;
152	long size, addr;
153{
154	struct mapent *firstbp;
155	register struct mapent *bp;
156	register int t;
157
158	/*
159	 * Both address and size must be
160	 * positive, or the protocol has broken down.
161	 */
162	if (addr <= 0 || size <= 0)
163		goto badrmfree;
164	/*
165	 * Locate the piece of the map which starts after the
166	 * returned space (or the end of the map).
167	 */
168	firstbp = bp = (struct mapent *)(mp + 1);
169	for (; bp->m_addr <= addr && bp->m_size != 0; bp++)
170		continue;
171	/*
172	 * If the piece on the left abuts us,
173	 * then we should combine with it.
174	 */
175	if (bp > firstbp && (bp-1)->m_addr+(bp-1)->m_size >= addr) {
176		/*
177		 * Check no overlap (internal error).
178		 */
179		if ((bp-1)->m_addr+(bp-1)->m_size > addr)
180			goto badrmfree;
181		/*
182		 * Add into piece on the left by increasing its size.
183		 */
184		(bp-1)->m_size += size;
185		/*
186		 * If the combined piece abuts the piece on
187		 * the right now, compress it in also,
188		 * by shifting the remaining pieces of the map over.
189		 */
190		if (bp->m_addr && addr+size >= bp->m_addr) {
191			if (addr+size > bp->m_addr)
192				goto badrmfree;
193			(bp-1)->m_size += bp->m_size;
194			while (bp->m_size) {
195				bp++;
196				(bp-1)->m_addr = bp->m_addr;
197				(bp-1)->m_size = bp->m_size;
198			}
199		}
200		goto done;
201	}
202	/*
203	 * Don't abut on the left, check for abutting on
204	 * the right.
205	 */
206	if (addr+size >= bp->m_addr && bp->m_size) {
207		if (addr+size > bp->m_addr)
208			goto badrmfree;
209		bp->m_addr -= size;
210		bp->m_size += size;
211		goto done;
212	}
213	/*
214	 * Don't abut at all.  Make a new entry
215	 * and check for map overflow.
216	 */
217	do {
218		t = bp->m_addr;
219		bp->m_addr = addr;
220		addr = t;
221		t = bp->m_size;
222		bp->m_size = size;
223		bp++;
224	} while (size = t);
225	/*
226	 * Segment at bp is to be the delimiter;
227	 * If there is not room for it
228	 * then the table is too full
229	 * and we must discard something.
230	 */
231	if (bp+1 > mp->m_limit) {
232		/*
233		 * Back bp up to last available segment.
234		 * which contains a segment already and must
235		 * be made into the delimiter.
236		 * Discard second to last entry,
237		 * since it is presumably smaller than the last
238		 * and move the last entry back one.
239		 */
240		bp--;
241		printf("%s: rmap ovflo, lost [%d,%d)\n", mp->m_name,
242		    (bp-1)->m_addr, (bp-1)->m_addr+(bp-1)->m_size);
243		bp[-1] = bp[0];
244		bp[0].m_size = bp[0].m_addr = 0;
245	}
246done:
247	/*
248	 * THIS IS RIDICULOUS... IT DOESN'T BELONG HERE!
249	 */
250	if ((mp == kernelmap) && kmapwnt) {
251		kmapwnt = 0;
252		wakeup((caddr_t)kernelmap);
253	}
254	return;
255badrmfree:
256	panic("bad rmfree");
257}
258
259/*
260 * Allocate 'size' units from the given map, starting at address 'addr'.
261 * Return 'addr' if successful, 0 if not.
262 * This may cause the creation or destruction of a resource map segment.
263 *
264 * This routine will return failure status if there is not enough room
265 * for a required additional map segment.
266 *
267 * An attempt to use this on 'swapmap' will result in
268 * a failure return.  This is due mainly to laziness and could be fixed
269 * to do the right thing, although it probably will never be used.
270 */
271rmget(mp, size, addr)
272	register struct map *mp;
273{
274	register struct mapent *ep = (struct mapent *)(mp+1);
275	register struct mapent *bp, *bp2;
276
277	if (size <= 0)
278		panic("rmget");
279	if (mp == swapmap)
280		return (0);
281	/*
282	 * Look for a map segment containing the requested address.
283	 * If none found, return failure.
284	 */
285	for (bp = ep; bp->m_size; bp++)
286		if (bp->m_addr <= addr && bp->m_addr + bp->m_size > addr)
287			break;
288	if (bp->m_size == 0)
289		return (0);
290
291	/*
292	 * If segment is too small, return failure.
293	 * If big enough, allocate the block, compressing or expanding
294	 * the map as necessary.
295	 */
296	if (bp->m_addr + bp->m_size < addr + size)
297		return (0);
298	if (bp->m_addr == addr)
299		if (bp->m_addr + bp->m_size == addr + size) {
300			/*
301			 * Allocate entire segment and compress map
302			 */
303			bp2 = bp;
304			while (bp2->m_size) {
305				bp2++;
306				(bp2-1)->m_addr = bp2->m_addr;
307				(bp2-1)->m_size = bp2->m_size;
308			}
309		} else {
310			/*
311			 * Allocate first part of segment
312			 */
313			bp->m_addr += size;
314			bp->m_size -= size;
315		}
316	else
317		if (bp->m_addr + bp->m_size == addr + size) {
318			/*
319			 * Allocate last part of segment
320			 */
321			bp->m_size -= size;
322		} else {
323			/*
324			 * Allocate from middle of segment, but only
325			 * if table can be expanded.
326			 */
327			for (bp2=bp; bp2->m_size; bp2++)
328				;
329			if (bp2 + 1 >= mp->m_limit)
330				return (0);
331			while (bp2 > bp) {
332				(bp2+1)->m_addr = bp2->m_addr;
333				(bp2+1)->m_size = bp2->m_size;
334				bp2--;
335			}
336			(bp+1)->m_addr = addr + size;
337			(bp+1)->m_size =
338			    bp->m_addr + bp->m_size - (addr + size);
339			bp->m_size = addr - bp->m_addr;
340		}
341	return (addr);
342}
343