xref: /openbsd-src/regress/usr.bin/diff/t8.1 (revision b725ae7711052a2233e31a66fefb8a752c388d7a)
1/*	$NetBSD: kern_malloc.c,v 1.11 1995/05/01 22:39:11 cgd Exp $	*/
2
3/*
4 * Copyright (c) 1987, 1991, 1993
5 *	The Regents of the University of California.  All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 *    must display the following acknowledgement:
17 *	This product includes software developed by the University of
18 *	California, Berkeley and its contributors.
19 * 4. Neither the name of the University nor the names of its contributors
20 *    may be used to endorse or promote products derived from this software
21 *    without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 *
35 *	@(#)kern_malloc.c	8.3 (Berkeley) 1/4/94
36 */
37
38#include <sys/param.h>
39#include <sys/proc.h>
40#include <sys/map.h>
41#include <sys/kernel.h>
42#include <sys/malloc.h>
43
44#include <vm/vm.h>
45#include <vm/vm_kern.h>
46
47struct kmembuckets bucket[MINBUCKET + 16];
48struct kmemstats kmemstats[M_LAST];
49struct kmemusage *kmemusage;
50char *kmembase, *kmemlimit;
51char *memname[] = INITKMEMNAMES;
52
53#ifdef DIAGNOSTIC
54/*
55 * This structure provides a set of masks to catch unaligned frees.
56 */
57long addrmask[] = { 0,
58	0x00000001, 0x00000003, 0x00000007, 0x0000000f,
59	0x0000001f, 0x0000003f, 0x0000007f, 0x000000ff,
60	0x000001ff, 0x000003ff, 0x000007ff, 0x00000fff,
61	0x00001fff, 0x00003fff, 0x00007fff, 0x0000ffff,
62};
63
64/*
65 * The WEIRD_ADDR is used as known text to copy into free objects so
66 * that modifications after frees can be detected.
67 */
68#define WEIRD_ADDR	0xdeadbeef
69#define MAX_COPY	32
70
71/*
72 * Normally the freelist structure is used only to hold the list pointer
73 * for free objects.  However, when running with diagnostics, the first
74 * 8 bytes of the structure is unused except for diagnostic information,
75 * and the free list pointer is at offst 8 in the structure.  Since the
76 * first 8 bytes is the portion of the structure most often modified, this
77 * helps to detect memory reuse problems and avoid free list corruption.
78 */
79struct freelist {
80	int32_t	spare0;
81	int16_t	type;
82	int16_t	spare1;
83	caddr_t	next;
84};
85#else /* !DIAGNOSTIC */
86struct freelist {
87	caddr_t	next;
88};
89#endif /* DIAGNOSTIC */
90
91/*
92 * Allocate a block of memory
93 */
94void *
95malloc(size, type, flags)
96	unsigned long size;
97	int type, flags;
98{
99	register struct kmembuckets *kbp;
100	register struct kmemusage *kup;
101	register struct freelist *freep;
102	long indx, npg, allocsize;
103	int s;
104	caddr_t va, cp, savedlist;
105#ifdef DIAGNOSTIC
106	int32_t *end, *lp;
107	int copysize;
108	char *savedtype;
109#endif
110#ifdef KMEMSTATS
111	register struct kmemstats *ksp = &kmemstats[type];
112
113	if (((unsigned long)type) > M_LAST)
114		panic("malloc - bogus type");
115#endif
116	indx = BUCKETINDX(size);
117	kbp = &bucket[indx];
118	s = splimp();
119#ifdef KMEMSTATS
120	while (ksp->ks_memuse >= ksp->ks_limit) {
121		if (flags & M_NOWAIT) {
122			splx(s);
123			return ((void *) NULL);
124		}
125		if (ksp->ks_limblocks < 65535)
126			ksp->ks_limblocks++;
127		tsleep((caddr_t)ksp, PSWP+2, memname[type], 0);
128	}
129	ksp->ks_size |= 1 << indx;
130#endif
131#ifdef DIAGNOSTIC
132	copysize = 1 << indx < MAX_COPY ? 1 << indx : MAX_COPY;
133#endif
134	if (kbp->kb_next == NULL) {
135		kbp->kb_last = NULL;
136		if (size > MAXALLOCSAVE)
137			allocsize = roundup(size, CLBYTES);
138		else
139			allocsize = 1 << indx;
140		npg = clrnd(btoc(allocsize));
141		va = (caddr_t) kmem_malloc(kmem_map, (vm_size_t)ctob(npg),
142					   !(flags & M_NOWAIT));
143		if (va == NULL) {
144			splx(s);
145			return ((void *) NULL);
146		}
147#ifdef KMEMSTATS
148		kbp->kb_total += kbp->kb_elmpercl;
149#endif
150		kup = btokup(va);
151		kup->ku_indx = indx;
152		if (allocsize > MAXALLOCSAVE) {
153			if (npg > 65535)
154				panic("malloc: allocation too large");
155			kup->ku_pagecnt = npg;
156#ifdef KMEMSTATS
157			ksp->ks_memuse += allocsize;
158#endif
159			goto out;
160		}
161#ifdef KMEMSTATS
162		kup->ku_freecnt = kbp->kb_elmpercl;
163		kbp->kb_totalfree += kbp->kb_elmpercl;
164#endif
165		/*
166		 * Just in case we blocked while allocating memory,
167		 * and someone else also allocated memory for this
168		 * bucket, don't assume the list is still empty.
169		 */
170		savedlist = kbp->kb_next;
171		kbp->kb_next = cp = va + (npg * NBPG) - allocsize;
172		for (;;) {
173			freep = (struct freelist *)cp;
174#ifdef DIAGNOSTIC
175			/*
176			 * Copy in known text to detect modification
177			 * after freeing.
178			 */
179			end = (int32_t *)&cp[copysize];
180			for (lp = (int32_t *)cp; lp < end; lp++)
181				*lp = WEIRD_ADDR;
182			freep->type = M_FREE;
183#endif /* DIAGNOSTIC */
184			if (cp <= va)
185				break;
186			cp -= allocsize;
187			freep->next = cp;
188		}
189		freep->next = savedlist;
190		if (kbp->kb_last == NULL)
191			kbp->kb_last = (caddr_t)freep;
192	}
193	va = kbp->kb_next;
194	kbp->kb_next = ((struct freelist *)va)->next;
195#ifdef DIAGNOSTIC
196	freep = (struct freelist *)va;
197	savedtype = (unsigned)freep->type < M_LAST ?
198		memname[freep->type] : "???";
199	if (kbp->kb_next &&
200	    !kernacc(kbp->kb_next, sizeof(struct freelist), 0)) {
201		printf("%s %d of object %p size %d %s %s (invalid addr %p)\n",
202			"Data modified on freelist: word",
203			(int32_t *)&kbp->kb_next - (int32_t *)kbp, va, size,
204			"previous type", savedtype, kbp->kb_next);
205		kbp->kb_next = NULL;
206	}
207
208	/* Fill the fields that we've used with WEIRD_ADDR */
209#if BYTE_ORDER == BIG_ENDIAN
210	freep->type = WEIRD_ADDR >> 16;
211#endif
212#if BYTE_ORDER == LITTLE_ENDIAN
213	freep->type = (short)WEIRD_ADDR;
214#endif
215	end = (int32_t *)&freep->next +
216	    (sizeof(freep->next) / sizeof(int32_t));
217	for (lp = (int32_t *)&freep->next; lp < end; lp++)
218		*lp = WEIRD_ADDR;
219
220	/* and check that the data hasn't been modified. */
221	end = (int32_t *)&va[copysize];
222	for (lp = (int32_t *)va; lp < end; lp++) {
223		if (*lp == WEIRD_ADDR)
224			continue;
225		printf("%s %d of object %p size %d %s %s (%p != %p)\n",
226			"Data modified on freelist: word", lp - (int32_t *)va,
227			va, size, "previous type", savedtype, *lp, WEIRD_ADDR);
228		break;
229	}
230
231	freep->spare0 = 0;
232#endif /* DIAGNOSTIC */
233#ifdef KMEMSTATS
234	kup = btokup(va);
235	if (kup->ku_indx != indx)
236		panic("malloc: wrong bucket");
237	if (kup->ku_freecnt == 0)
238		panic("malloc: lost data");
239	kup->ku_freecnt--;
240	kbp->kb_totalfree--;
241	ksp->ks_memuse += 1 << indx;
242out:
243	kbp->kb_calls++;
244	ksp->ks_inuse++;
245	ksp->ks_calls++;
246	if (ksp->ks_memuse > ksp->ks_maxused)
247		ksp->ks_maxused = ksp->ks_memuse;
248#else
249out:
250#endif
251	splx(s);
252	return ((void *) va);
253}
254
255/*
256 * Free a block of memory allocated by malloc.
257 */
258void
259free(addr, type)
260	void *addr;
261	int type;
262{
263	register struct kmembuckets *kbp;
264	register struct kmemusage *kup;
265	register struct freelist *freep;
266	long size;
267	int s;
268#ifdef DIAGNOSTIC
269	caddr_t cp;
270	int32_t *end, *lp;
271	long alloc, copysize;
272#endif
273#ifdef KMEMSTATS
274	register struct kmemstats *ksp = &kmemstats[type];
275#endif
276
277	kup = btokup(addr);
278	size = 1 << kup->ku_indx;
279	kbp = &bucket[kup->ku_indx];
280	s = splimp();
281#ifdef DIAGNOSTIC
282	/*
283	 * Check for returns of data that do not point to the
284	 * beginning of the allocation.
285	 */
286	if (size > NBPG * CLSIZE)
287		alloc = addrmask[BUCKETINDX(NBPG * CLSIZE)];
288	else
289		alloc = addrmask[kup->ku_indx];
290	if (((u_long)addr & alloc) != 0)
291		panic("free: unaligned addr 0x%x, size %d, type %s, mask %d\n",
292			addr, size, memname[type], alloc);
293#endif /* DIAGNOSTIC */
294	if (size > MAXALLOCSAVE) {
295		kmem_free(kmem_map, (vm_offset_t)addr, ctob(kup->ku_pagecnt));
296#ifdef KMEMSTATS
297		size = kup->ku_pagecnt << PGSHIFT;
298		ksp->ks_memuse -= size;
299		kup->ku_indx = 0;
300		kup->ku_pagecnt = 0;
301		if (ksp->ks_memuse + size >= ksp->ks_limit &&
302		    ksp->ks_memuse < ksp->ks_limit)
303			wakeup((caddr_t)ksp);
304		ksp->ks_inuse--;
305		kbp->kb_total -= 1;
306#endif
307		splx(s);
308		return;
309	}
310	freep = (struct freelist *)addr;
311#ifdef DIAGNOSTIC
312	/*
313	 * Check for multiple frees. Use a quick check to see if
314	 * it looks free before laboriously searching the freelist.
315	 */
316	if (freep->spare0 == WEIRD_ADDR) {
317		for (cp = kbp->kb_next; cp; cp = *(caddr_t *)cp) {
318			if (addr != cp)
319				continue;
320			printf("multiply freed item %p\n", addr);
321			panic("free: duplicated free");
322		}
323	}
324	/*
325	 * Copy in known text to detect modification after freeing
326	 * and to make it look free. Also, save the type being freed
327	 * so we can list likely culprit if modification is detected
328	 * when the object is reallocated.
329	 */
330	copysize = size < MAX_COPY ? size : MAX_COPY;
331	end = (int32_t *)&((caddr_t)addr)[copysize];
332	for (lp = (int32_t *)addr; lp < end; lp++)
333		*lp = WEIRD_ADDR;
334	freep->type = type;
335#endif /* DIAGNOSTIC */
336#ifdef KMEMSTATS
337	kup->ku_freecnt++;
338	if (kup->ku_freecnt >= kbp->kb_elmpercl)
339		if (kup->ku_freecnt > kbp->kb_elmpercl)
340			panic("free: multiple frees");
341		else if (kbp->kb_totalfree > kbp->kb_highwat)
342			kbp->kb_couldfree++;
343	kbp->kb_totalfree++;
344	ksp->ks_memuse -= size;
345	if (ksp->ks_memuse + size >= ksp->ks_limit &&
346	    ksp->ks_memuse < ksp->ks_limit)
347		wakeup((caddr_t)ksp);
348	ksp->ks_inuse--;
349#endif
350	if (kbp->kb_next == NULL)
351		kbp->kb_next = addr;
352	else
353		((struct freelist *)kbp->kb_last)->next = addr;
354	freep->next = NULL;
355	kbp->kb_last = addr;
356	splx(s);
357}
358
359/*
360 * Initialize the kernel memory allocator
361 */
362kmeminit()
363{
364	register long indx;
365	int npg;
366
367#if	((MAXALLOCSAVE & (MAXALLOCSAVE - 1)) != 0)
368		ERROR!_kmeminit:_MAXALLOCSAVE_not_power_of_2
369#endif
370#if	(MAXALLOCSAVE > MINALLOCSIZE * 32768)
371		ERROR!_kmeminit:_MAXALLOCSAVE_too_big
372#endif
373#if	(MAXALLOCSAVE < CLBYTES)
374		ERROR!_kmeminit:_MAXALLOCSAVE_too_small
375#endif
376
377	if (sizeof(struct freelist) > (1 << MINBUCKET))
378		panic("minbucket too small/struct freelist too big");
379
380	npg = VM_KMEM_SIZE/ NBPG;
381	kmemusage = (struct kmemusage *) kmem_alloc(kernel_map,
382		(vm_size_t)(npg * sizeof(struct kmemusage)));
383	kmem_map = kmem_suballoc(kernel_map, (vm_offset_t *)&kmembase,
384		(vm_offset_t *)&kmemlimit, (vm_size_t)(npg * NBPG), FALSE);
385#ifdef KMEMSTATS
386	for (indx = 0; indx < MINBUCKET + 16; indx++) {
387		if (1 << indx >= CLBYTES)
388			bucket[indx].kb_elmpercl = 1;
389		else
390			bucket[indx].kb_elmpercl = CLBYTES / (1 << indx);
391		bucket[indx].kb_highwat = 5 * bucket[indx].kb_elmpercl;
392	}
393	for (indx = 0; indx < M_LAST; indx++)
394		kmemstats[indx].ks_limit = npg * NBPG * 6 / 10;
395#endif
396}
397