.\" Id: memcluster.mdoc,v 1.3 2004/03/09 06:30:08 marka Exp
.\"
.\" Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
.\" Copyright (c) 1995-1999 by Internet Software Consortium
.\"
.\" Permission to use, copy, modify, and distribute this software for any
.\" purpose with or without fee is hereby granted, provided that the above
.\" copyright notice and this permission notice appear in all copies.
.\"
.\" THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
.\" OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
.\" The following six UNCOMMENTED lines are required.
.Dd Month day, year
.\"Os OPERATING_SYSTEM [version/release]
.Os BSD 4
.\"Dt DOCUMENT_TITLE [section number] [volume]
.Dt MEMCLUSTER 3
.Sh NAME
.Nm meminit ,
.Nm memget ,
.Nm memput ,
.Nm memstats
.Nd memory allocation/deallocation system
.Sh SYNOPSIS
.Fd #include \&<isc/memcluster.h\&>
.Ft void *
.Fn memget "size_t size"
.Ft void
.Fn memput "void *mem" "size_t size"
.Ft void
.Fn memstats "FILE *out"
.Sh DESCRIPTION
These functions access a memory management system which allows callers to not
fragment memory to the extent which can ordinarily occur through many random
calls to
.Xr malloc 3 .
Instead,
.Fn memget
gets a large contiguous chunk of blocks of the requested
.Fa size
and parcels out these blocks as requested. The symmetric call is
.Fn memput ,
which callers use to return a piece of memory obtained from
.Fn memget .
Statistics about memory usage are returned by
.Fn memstats ,
which prints a report on the stream
.Fa out .
.Ss INTERNALS
Internally, linked lists of free memory blocks are stored in an array.
The size of this array is determined by the value
.Dv MEM_FREECOUNT ,
currently set to 1100. In general, for any requested blocksize
.Dq Fa size ,
any free blocks will be stored on the linked list at that index.
No free lists are managed for blocks greater than or equal to
.Dv MEM_FREECOUNT
bytes; instead, calls to
.Xr malloc 3
or
.Xr free 3
are made, directly.
.Pp
Since the blocks are actually stored as linked lists, they must at least
be large enough to hold a pointer to the next block. This size, which is
.Dv SMALL_SIZE_LIMIT ,
is currently defined as
.Bd -literal -offset indent
#define SMALL_SIZE_LIMIT sizeof(struct { void *next; })
.Ed
.Pp
Both
.Fn memget
and
.Fn memput
enforce this limit; for example, any call to
.Fn memget
requesting a block smaller than
.Dv SMALL_SIZE_LIMIT
bytes will actually be considered to be of size
.Dv SMALL_SIZE_LIMIT
internally. (Such a caller request will be logged for
.Fn memstats
purposes using the caller-requested
.Fa size ;
see the discussion of
.Fn memstats ,
below, for more information.)
.Pp
Additionally, the requested
.Fa size
will be adjusted so that when a large
.Xr malloc 3 Ns No -d
chunk of memory is broken up into a linked list, the blocks will all fall on
the correct memory alignment boundaries. Thus, one can conceptualize a call
which mentions
.Fa size
as resulting in a
.Fa new_size
which is used internally.
.Pp
In order to more efficiently allocate memory, there is a
.Dq target
size for calls to
.Xr malloc 3 .
It is given by the pre-defined value
.Dv MEM_TARGET ,
which is currently 4096 bytes.
For any requested block
.Fa size ,
enough memory is
.Xr malloc 3 Ns No -d
in order to fill up a block of about
.Dv MEM_TARGET
bytes.
.No [ Ns Sy NOTE :
For allocations larger than
.Dv MEM_TARGET Ns No /2
bytes, there is a
.Dq fudge factor
introduced which boosts the target size by 25% of
.Dv MEM_TARGET .
This means that enough memory for two blocks
will actually be allocated for any
.Fa size
such that
.Pq Dv MEM_TARGET Ns No / 3
.No < Fa size No <
.Pq Dv MEM_TARGET Ns No *5/8 ,
provided that the value of
.Dv MEM_FREECOUNT
is at least as large as the upper limit shown above.]
.Pp
.Ss FUNCTION DESCRIPTIONS
.Pp
The function
.Fn memget
returns a pointer to a block of memory of at least the requested
.Fa size .
After adjusting
.Fa size
to the value
.Va new_size
as mentioned above in the
.Sx INTERNALS
subsection, the internal array of free lists is checked.
If there is no block of the needed
.Va new_size ,
then
.Fn memget
will
.Xr malloc 3
a chunk of memory which is as many times as
.Va new_size
will fit into the target size. This memory is then turned into a linked list
of
.Va new_size Ns No -sized
blocks which are given out as requested; the last such block is the first one
returned by
.Fn memget .
If the requested
.Fa size
is zero or negative, then
.Dv NULL
is returned and
.Va errno
is set to
.Dv EINVAL ;
if
.Fa size
is larger than or equal to the pre-defined maximum size
.Dv MEM_FREECOUNT ,
then only a single block of exactly
.Fa size
will be
.Xr malloc 3 Ns No -d
and returned.
.Pp
The
.Fn memput
call is used to return memory once the caller is finished with it.
After adjusting
.Fa size
the the value
.Va new_size
as mentioned in the
.Sx INTERNALS
subsection, above, the block is placed at the head of the free list of
.Va new_size Ns -sized
blocks.
If the given
.Fa size
is zero or negative, then
.Va errno
is set to
.Dv EINVAL ,
as for
.Fn memget .
If
.Fa size
is larger than or equal to the pre-defined maximum size
.Dv MEM_FREECOUNT ,
then the block is immediately
.Xr free 3 Ns No -d .
.Pp
.Sy NOTE :
It is important that callers give
.Fn memput
.Em only
blocks of memory which were previously obtained from
.Fn memget
if the block is
.Em actually
less than
.Dv SMALL_SIZE_LIMIT
bytes in size. Since all blocks will be added to a free list, any block
which is not at least
.Dv SMALL_SIZE_LIMIT
bytes long will not be able to hold a pointer to the next block in the
free list.
.Pp
The
.Fn memstats
function will summarize the number of calls to
.Fn memget
and
.Fn memput
for any block size from 1 byte up to
.Pq Dv MEM_FREECOUNT No - 1
bytes, followed by a single line for any calls using a
.Fa size
greater than or equal to
.Dv MEM_FREECOUNT ;
a brief header with shell-style comment lines prefaces the report and
explains the information. The
.Dv FILE
pointer
.Fa out
identifies the stream which is used for this report. Currently,
.Fn memstat
reports the number of calls to
.Fn memget
and
.Fn memput
using the caller-supplied value
.Fa size ;
the percentage of outstanding blocks of a given size (i.e., the percentage
by which calls to
.Fn memget
exceed
.Fn memput )
are also reported on the line for blocks of the given
.Fa size .
However, the percent of blocks used is computed using the number of
blocks allocated according to the internal parameter
.Va new_size ;
it is the percentage of blocks used to those available at a given
.Va new_size ,
and is computed using the
.Em total
number of caller
.Dq gets
for any caller
.Fa size Ns No -s
which map to the internally-computed
.Va new_size .
Keep in mind that
.Va new_size
is generally
.Em not
equal to
.Fa size ,
which has these implications:
.Bl -enum -offset indent
.It
For
.Fa size
smaller than
.Dv SMALL_SIZE_LIMIT ,
.Fn memstat
.Em will
show statistics for caller requests under
.Fa size ,
but "percent used" information about such blocks will be reported under
.Dv SMALL_SIZE_LIMIT Ns No -sized
blocks.
.It
As a general case of point 1, internal statistics are reported on the the
line corresponding to
.Va new_size ,
so that, for a given caller-supplied
.Fa size ,
the associated internal information will appear on that line or on the next
line which shows "percent used" information.
.El
.Pp
.Sy NOTE :
If the caller returns blocks of a given
.Fa size
and requests others of
.Fa size Ns No -s
which map to the same internal
.Va new_size ,
it is possible for
.Fn memstats
to report usage of greater than 100% for blocks of size
.Va new_size .
This should be viewed as A Good Thing.
.Sh RETURN VALUES
The function
.Fn memget
returns a
.No non- Ns Dv NULL
pointer to a block of memory of the requested
.Fa size .
It returns
.Dv NULL
if either the
.Fa size
is invalid (less than or equal to zero) or a
.Xr malloc 3
of a new block of memory fails. In the former case,
.Va errno
is set to
.Dv EINVAL ;
in the latter, it is set to
.Dv ENOMEM .
.Pp
Neither
.Fn memput
nor
.Fn memstats
return a value.
.\" This next request is for sections 1, 6, 7 & 8 only
.\" .Sh ENVIRONMENT
.\" .Sh FILES
.\" .Sh EXAMPLES
.\" This next request is for sections 1, 6, 7 & 8 only
.\" (command return values (to shell) and
.\" fprintf/stderr type diagnostics)
.\" .Sh DIAGNOSTICS
.\" The next request is for sections 2 and 3 error
.\" and signal handling only.
.Sh ERRORS
.Va errno
is set as follows:
.Bl -tag -width "ENOMEM " -offset indent
.It Dv EINVAL
set by both
.Fn memget
and
.Fn memput
if the
.Fa size
is zero or negative
.It Dv ENOMEM
set by
.Fn memget
if a call to
.Xr malloc 3
fails
.El
.Sh SEE ALSO
.Xr free 3 ,
.Xr malloc 3 .
.\" .Sh STANDARDS
.\" .Sh HISTORY
.Sh AUTHORS
Steven J. Richardson and Paul Vixie, Vixie Enterprises.
.\" .Sh BUGS