Add realloc() and reallocf(), and make free(NULL, ...) acceptable.

Reviewed by:	alfred
This commit is contained in:
Archie Cobbs 2002-03-13 01:42:33 +00:00
parent 9ed55d1061
commit 44a8ff315e
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=92194
4 changed files with 153 additions and 11 deletions

View File

@ -156,6 +156,7 @@ MLINKS+=make_dev.9 dev_depends.9
MLINKS+=make_dev.9 destroy_dev.9
MLINKS+=make_dev.9 make_dev_alias.9
MLINKS+=malloc.9 FREE.9 malloc.9 MALLOC.9 malloc.9 free.9
MLINKS+=malloc.9 realloc.9 malloc.9 reallocf.9
MLINKS+=mi_switch.9 cpu_switch.9 mi_switch.9 cpu_throw.9
MLINKS+=namei.9 NDINIT.9
MLINKS+=namei.9 NDFREE.9

View File

@ -54,18 +54,63 @@
.Ft void
.Fn free "void *addr" "struct malloc_type *type"
.Fn FREE "void *addr" "struct malloc_type *type"
.Ft void *
.Fn realloc "void *addr" "unsigned long size" "struct malloc_type *type" "int flags"
.Ft void *
.Fn reallocf "void *addr" "unsigned long size" "struct malloc_type *type" "int flags"
.Sh DESCRIPTION
The
.Fn malloc
function allocates uninitialized memory in kernel address space for an
object whose size is specified by
.Fa size .
.Pp
.Fn free
releases memory at address
.Fa addr
that was previously allocated by
.Fn malloc
for re-use. The memory is not zeroed.
for re-use.
The memory is not zeroed.
If
.Fa addr
is
.Dv NULL ,
then
.Fn free
does nothing.
.Pp
The
.Fn realloc
function changes the size of the previously allocated memory referenced by
.Fa addr
to
.Fa size
bytes.
The contents of the memory are unchanged up to the lesser of the new and
old sizes.
Note that the returned value may differ from
.Fa addr .
If the requested memory cannot be allocated,
.Dv NULL
is returned and the memory referenced by
.Fa addr
is valid and unchanged.
If
.Fa addr
is
.Dv NULL ,
the
.Fn realloc
function behaves identically to
.Fn malloc
for the specified size.
.Pp
The
.Fn reallocf
function call is identical to the realloc function call, except that it
will free the passed pointer when the requested memory cannot be allocated.
.Pp
The
.Fn MALLOC
macro variant is functionally equivalent to
@ -92,26 +137,35 @@ operational characteristics as follows:
Causes the allocated memory to be set to all zeros.
.It Dv M_NOWAIT
Causes
.Fn malloc
.Fn malloc ,
.Fn realloc ,
or
.Fn reallocf
to return
.Dv NULL
if the request cannot be immediately fulfilled due to resource shortage.
Otherwise,
.Fn malloc
may call sleep to wait for resources to be released by other processes.
Otherwise, the current process may be put to sleep to wait for
resources to be released by other processes.
If this flag is set,
.Fn malloc
will return
.Dv NULL
rather then block. Note that
rather then block.
Note that
.Dv M_WAITOK
is defined to be 0, meaning that blocking operation is the default.
Also note that
.Dv M_NOWAIT
is required when running in an interrupt context.
.It Dv M_WAITOK
Indicates that it is Ok to wait for resources. It is unconveniently
defined as 0 so care should be taken never to compare against this value
directly or try to AND it as a flag. The default operation is to block
until the memory allocation succeeds.
.Fn malloc
.Fn malloc ,
.Fn realloc ,
and
.Fn reallocf
can only return
.Dv NULL
if
@ -157,13 +211,22 @@ MALLOC(buf, struct foo_buf *, sizeof *buf, M_FOOBUF, M_NOWAIT);
.Ed
.Sh RETURN VALUES
.Fn malloc
returns a kernel virtual address that is suitably aligned for storage of
.Fn malloc ,
.Fn realloc ,
and
.Fn reallocf
return a kernel virtual address that is suitably aligned for storage of
any type of object, or
.Dv NULL
if the request could not be satisfied and
if the request could not be satisfied (implying that
.Dv M_NOWAIT
was set.
was set).
.Sh IMPLEMENTATION NOTES
The memory allocator allocates memory in chunks that have size a power
of two for requests up to the size of a page of memory.
For larger requests, one or more pages is allocated.
While it should not be relied upon, this information may be useful for
optimizing the efficiency of memory use.
.Sh SEE ALSO
.Xr vmstat 8
.Sh DIAGNOSTICS

View File

@ -57,6 +57,16 @@
#include <machine/cpu.h>
#endif
/*
* When realloc() is called, if the new size is sufficiently smaller than
* the old size, realloc() will allocate a new, smaller block to avoid
* wasting memory. 'Sufficiently smaller' is defined as: newsize <=
* oldsize / 2^n, where REALLOC_FRACTION defines the value of 'n'.
*/
#ifndef REALLOC_FRACTION
#define REALLOC_FRACTION 1 /* new block if <= half the size */
#endif
MALLOC_DEFINE(M_CACHE, "cache", "Various Dynamically allocated caches");
MALLOC_DEFINE(M_DEVBUF, "devbuf", "device driver memory");
MALLOC_DEFINE(M_TEMP, "temp", "misc temporary data buffers");
@ -294,6 +304,10 @@ free(addr, type)
#endif
register struct malloc_type *ksp = type;
/* free(NULL, ...) does nothing */
if (addr == NULL)
return;
KASSERT(kmembase <= (char *)addr && (char *)addr < kmemlimit,
("free: address %p out of range", (void *)addr));
kup = btokup(addr);
@ -399,6 +413,66 @@ free(addr, type)
mtx_unlock(&malloc_mtx);
}
/*
* realloc: change the size of a memory block
*/
void *
realloc(addr, size, type, flags)
void *addr;
unsigned long size;
struct malloc_type *type;
int flags;
{
struct kmemusage *kup;
unsigned long alloc;
void *newaddr;
/* realloc(NULL, ...) is equivalent to malloc(...) */
if (addr == NULL)
return (malloc(size, type, flags));
/* Sanity check */
KASSERT(kmembase <= (char *)addr && (char *)addr < kmemlimit,
("realloc: address %p out of range", (void *)addr));
/* Get the size of the original block */
kup = btokup(addr);
alloc = 1 << kup->ku_indx;
if (alloc > MAXALLOCSAVE)
alloc = kup->ku_pagecnt << PAGE_SHIFT;
/* Reuse the original block if appropriate */
if (size <= alloc
&& (size > (alloc >> REALLOC_FRACTION) || alloc == MINALLOCSIZE))
return (addr);
/* Allocate a new, bigger (or smaller) block */
if ((newaddr = malloc(size, type, flags)) == NULL)
return (NULL);
/* Copy over original contents */
bcopy(addr, newaddr, min(size, alloc));
free(addr, type);
return (newaddr);
}
/*
* reallocf: same as realloc() but free memory on failure.
*/
void *
reallocf(addr, size, type, flags)
void *addr;
unsigned long size;
struct malloc_type *type;
int flags;
{
void *mem;
if ((mem = realloc(addr, size, type, flags)) == NULL)
free(addr, type);
return (mem);
}
/*
* Initialize the kernel memory allocator
*/

View File

@ -173,6 +173,10 @@ void free __P((void *addr, struct malloc_type *type));
void *malloc __P((unsigned long size, struct malloc_type *type, int flags));
void malloc_init __P((void *));
void malloc_uninit __P((void *));
void *realloc __P((void *addr, unsigned long size,
struct malloc_type *type, int flags));
void *reallocf __P((void *addr, unsigned long size,
struct malloc_type *type, int flags));
#endif /* _KERNEL */
#endif /* !_SYS_MALLOC_H_ */