/* * Copyright (c) 1986 Regents of the University of California. * All rights reserved. The Berkeley software License Agreement * specifies the terms and conditions for redistribution. * * @(#)subr_rmap.c 1.2 (2.11BSD GTE) 12/24/92 */ #include "param.h" #include "systm.h" #include "map.h" #include "vm.h" /* * Resource map handling routines. * * A resource map is an array of structures each of which describes a * segment of the address space of an available resource. The segments * are described by their base address and length, and sorted in address * order. Each resource map has a fixed maximum number of segments * allowed. Resources are allocated by taking part or all of one of the * segments of the map. * * Returning of resources will require another segment if the returned * resources are not adjacent in the address space to an existing segment. * If the return of a segment would require a slot which is not available, * then one of the resource map segments is discarded after a warning is * printed. * * Returning of resources may also cause the map to collapse by coalescing * two existing segments and the returned space into a single segment. In * this case the resource map is made smaller by copying together to fill * the resultant gap. * * N.B.: the current implementation uses a dense array and does not admit * the value ``0'' as a legal address or size, since that is used as a * delimiter. */ /* * Allocate 'size' units from the given map. Return the base of the * allocated space. In a map, the addresses are increasing and the * list is terminated by a 0 size. * * Algorithm is first-fit. */ memaddr malloc(mp, size) struct map *mp; register size_t size; { register struct mapent *bp, *ep; memaddr addr; int retry; if (!size) panic("malloc: size = 0"); /* * Search for a piece of the resource map which has enough * free space to accomodate the request. */ retry = 0; again: for (bp = mp->m_map; bp->m_size; ++bp) if (bp->m_size >= size) { /* * Allocate from the map. If we allocated the entire * piece, move the rest of the map to the left. */ addr = bp->m_addr; bp->m_size -= size; if (bp->m_size) bp->m_addr += size; else for (ep = bp;; ++ep) { *ep = *++bp; if (!bp->m_size) break; } #ifdef UCB_METER if (mp == coremap) freemem -= size; #endif return(addr); } /* no entries big enough */ if (!retry++) { if (mp == swapmap) { printf("short of swap\n"); xumount(NODEV); goto again; } else if (mp == coremap) { xuncore(size); goto again; } } return((memaddr)NULL); } /* * Free the previously allocated size units at addr into the specified * map. Sort addr into map and combine on one or both ends if possible. */ mfree(mp, size, addr) struct map *mp; size_t size; register memaddr addr; { register struct mapent *bp, *ep; struct mapent *start; if (!size) return; /* the address must not be 0, or the protocol has broken down. */ if (!addr) panic("mfree: addr = 0"); if (mp == coremap) { if (runin) { runin = 0; wakeup((caddr_t)&runin); } #ifdef UCB_METER freemem += size; #endif } /* * locate the piece of the map which starts after the * returned space (or the end of the map). */ for (bp = mp->m_map; bp->m_size && bp->m_addr <= addr; ++bp); /* if there is a piece on the left abutting us, combine with it. */ ep = bp - 1; if (bp != mp->m_map && ep->m_addr + ep->m_size >= addr) { #ifdef DIAGNOSTIC /* any overlap is an internal error */ if (ep->m_addr + ep->m_size > addr) panic("mfree overlap #1"); #endif /* add into piece on the left by increasing its size. */ ep->m_size += size; /* * if the combined piece abuts the piece on the right now, * compress it in also, by shifting the remaining pieces * of the map over. */ if (bp->m_size && addr + size >= bp->m_addr) { #ifdef DIAGNOSTIC if (addr + size > bp->m_addr) panic("mfree overlap #2"); #endif ep->m_size += bp->m_size; do { *++ep = *++bp; } while (bp->m_size); } return; } /* if doesn't abut on the left, check for abutting on the right. */ if (bp->m_size && addr + size >= bp->m_addr) { #ifdef DIAGNOSTIC if (addr + size > bp->m_addr) panic("mfree overlap #3"); #endif bp->m_addr = addr; bp->m_size += size; return; } /* doesn't abut. Make a new entry and check for map overflow. */ for (start = bp; bp->m_size; ++bp); if (++bp > mp->m_limit) /* * too many segments; if this happens, the correct fix * is to make the map bigger; you can't afford to lose * chunks of the map. If you need to implement recovery, * use the above "for" loop to find the smallest entry * and toss it. */ printf("%s: overflow, lost %u clicks at 0%o\n", mp->m_name, size, addr); else { for (ep = bp - 1; ep >= start; *bp-- = *ep--); start->m_addr = addr; start->m_size = size; } } /* * Allocate resources for the three segments of a process (data, stack * and u.), attempting to minimize the cost of failure part-way through. * Since the segments are located successively, it is best for the sizes * to be in decreasing order; generally, data, stack, then u. will be * best. Returns NULL on failure, address of u. on success. */ memaddr malloc3(mp, d_size, s_size, u_size, a) struct map *mp; size_t d_size, s_size, u_size; memaddr a[3]; { register struct mapent *bp, *remap; register int next; struct mapent *madd[3]; size_t sizes[3]; int found, retry; sizes[0] = d_size; sizes[1] = s_size; sizes[2] = u_size; retry = 0; again: /* * note, this has to work for d_size and s_size of zero, * since init() comes in that way. */ madd[0] = madd[1] = madd[2] = remap = NULL; for (found = 0, bp = mp->m_map; bp->m_size; ++bp) for (next = 0; next < 3; ++next) if (!madd[next] && sizes[next] <= bp->m_size) { madd[next] = bp; bp->m_size -= sizes[next]; if (!bp->m_size && !remap) remap = bp; if (++found == 3) goto resolve; } /* couldn't get it all; restore the old sizes, try again */ for (next = 0; next < 3; ++next) if (madd[next]) madd[next]->m_size += sizes[next]; if (!retry++) if (mp == swapmap) { printf("short of swap\n"); xumount(NODEV); goto again; } else if (mp == coremap) { xuncore(sizes[2]); /* smallest to largest; */ xuncore(sizes[1]); /* free up minimum space */ xuncore(sizes[0]); goto again; } return((memaddr)NULL); resolve: /* got it all, update the addresses. */ for (next = 0; next < 3; ++next) { bp = madd[next]; a[next] = bp->m_addr; bp->m_addr += sizes[next]; } #ifdef UCB_METER if (mp == coremap) freemem -= d_size + s_size + u_size; #endif /* remove any entries of size 0; addr of 0 terminates */ if (remap) for (bp = remap + 1;; ++bp) if (bp->m_size || !bp->m_addr) { *remap++ = *bp; if (!bp->m_addr) break; } return(a[2]); }