sabato 13 aprile 2013

FreeBSD kernel malloc

The FreeBSD kernel provides a set of malloc/free functions that are similar to the C-library ones for managing the memory inside the kernel space.

All the memory management is done using the malloc_type structure, that is defined as follows:

struct malloc_type {
   struct malloc_type *ks_next; /* Next in global chain. */
   u_long ks_magic; /* Detect programmer error. */
   const char *ks_shortdesc; /* Printable type name. */
   void *ks_handle; /* Priv. data, was lo_class. */
};

The first field is a pointer to another malloc_type structure used to chain the allocated memory descriptor in a list, in particular the kern_malloc.c contains a kmemstatistics struct that always points to the last allocated malloc_type structure (and hence to the head of the chain). The ks_magic field is used to check for wrong malloc'ed areas: if it does not contains the special value M_MAGIC the kernel will panic assuming the memory is corrupted. The ks_shortdesc contains a description of the memory area, as reported in the output of the command "vmstat -m". Finally, the ks_handle field contains another structure, malloc_type_internal that in turns contains private kernel data, such as DTrace probes, the zone from which the memory has been allocated and statistics about the memory area itself.

Therefore in order to handle a memory chunk in the kernel space a malloc_type struct must be hold. There are two main macros to deal with malloc_type, defined in the sys/malloc.h header file:
- MALLOC_DEFINE
- MALLOC_DECLARE

The former, MALLOC_DEFINE, is used to define an area of memory that will be allocated later using the malloc(9) call. The latter, MALLOC_DECLARE, is a shortcut for "importing" a memory definition into another source file.
The MALLOC_DEFINE macro is shown in the following:


#define MALLOC_DEFINE(type, shortdesc, longdesc) \
   struct malloc_type type[1] = { \
         { NULL, M_MAGIC, shortdesc, NULL } \
         }; \
   SYSINIT(type##_init, SI_SUB_KMEM, 
               SI_ORDER_SECOND, malloc_init, \
              type); \
   SYSUNINIT(type##_uninit, SI_SUB_KMEM, 
                    SI_ORDER_ANY, \
                    malloc_uninit, type)


The macro contains three instructions. The first is the declaration and initialization of a malloc_type structure as a single element array (this trick is done to have a pointer to the malloc type within the macro itself - the array name is the pointer to the malloc type). The other two instructions attach the malloc_init and malloc_uninit functions to the specified malloc type. In this way, the system will initialize (and uninitialize) the memory area with the above functions (from kern_malloc.c). The SI_SUB_KMEM subsystem is scanned and executed during the boot, and if the MALLOC_DEFINE happens to be in a module, the initialization and desctruction happens as soon as the module is loaded/unloaded. The "##" used in the macro is for macro token concatenation.

To better understand the above MALLOC_DEFINE macro, suppose the following is called in a module:

MALLOC_DEFINE( M_LUKE, "Luke Memory", 
               "Memory for the Luke subsystem" );

this will expand to:

struct malloc_type M_LUKE[1] = 
      { { NULL, M_MAGIC, "Luke Memory", NULL } };
SYSINIT(M_LUKE_init, 
          SI_SUB_KMEM, SI_ORDER_SECOND, 
          malloc_init, M_LUKE );
SYSUNINIT(M_LUKE_uninit, 
          SI_SUB_KMEM, SI_ORDER_ANY, 
          malloc_uninit, type);


The second macro for memory usage is MALLOC_DECLARE, that is defined in sys/malloc.h as follows:

#define MALLOC_DECLARE(type) \
extern struct malloc_type type[1]

and that is useful simply to inform that a piece of malloc-based memory metadata has been defined somewhere else in the source tree.

In order to conclude this brief bird's eye view on kernel memory allocation, the malloc_init and malloc_uninit functions defined in sys/kern_malloc.c perform calls to the uma_zalloc and uma_zfree_arg to respectively allocate and free a memory chunk. The zone used for the allocation is always the same, called mt_zone and created in the kmeminit function (executed of course early in the boot sequence):

static void
kmeminit(void *dummy)
{
...
mt_zone = uma_zcreate("mt_zone", 
           sizeof(struct malloc_type_internal),
           NULL, NULL, NULL, NULL
           UMA_ALIGN_PTR, UMA_ZONE_MALLOC);
...
}

The UMA related functions are defined in the vm/uma_core.c source file.

Nessun commento: