BIRD Programmer's Documentation: Resources

Next Previous Contents

## 8. Resources

## 8.1 Introduction

Most large software projects implemented in classical procedural programming languages usually end up with lots of code taking care of resource allocation and deallocation. Bugs in such code are often very difficult to find, because they cause only `resource leakage', that is keeping a lot of memory and other resources which nobody references to.

We've tried to solve this problem by employing a resource tracking system which keeps track of all the resources allocated by all the modules of BIRD, deallocates everything automatically when a module shuts down and it is able to print out the list of resources and the corresponding modules they are allocated by.

Each allocated resource (from now we'll speak about allocated resources only) is represented by a structure starting with a standard header (struct resource) consisting of a list node (resources are often linked to various lists) and a pointer to resclass -- a resource class structure pointing to functions implementing generic resource operations (such as freeing of the resource) for the particular resource type.

There exist the following types of resources:

- Resource pools (pool)
- Memory blocks
- Linear memory pools (linpool)
- Slabs (slab)
- Events (event)
- Timers (timer)
- Sockets (socket)

## 8.2 Resource pools

Resource pools (pool) are just containers holding a list of other resources. Freeing a pool causes all the listed resources to be freed as well. Each existing resource is linked to some pool except for a root pool which isn't linked anywhere, so all the resources form a tree structure with internal nodes corresponding to pools and leaves being the other resources.

Example: Almost all modules of BIRD have their private pool which is freed upon shutdown of the module.

### Function

pool * rp_new (pool * p, struct domain_generic * dom, const char * name) -- create a resource pool

### Arguments

pool * p

  parent pool

struct domain_generic * dom

  -- undescribed --

const char * name

  pool name (to be included in debugging dumps)

### Description

rp_new() creates a new resource pool inside the specified parent pool.

### Function

void rmove (void * res, pool * p) -- move a resource

### Arguments

void * res

  resource

pool * p

  pool to move the resource to

### Description

rmove() moves a resource from one pool to another.

### Function

void rfree (void * res) -- free a resource

### Arguments

void * res

  resource

### Description

rfree() frees the given resource and all information associated with it. In case it's a resource pool, it also frees all the objects living inside the pool.

It works by calling a class-specific freeing function.

### Function

void rdump (struct dump_request * dreq, void * res) -- dump a resource

### Arguments

struct dump_request * dreq

  -- undescribed --

void * res

  resource

### Description

This function prints out all available information about the given resource to the debugging output.

It works by calling a class-specific dump function.

### Function

void * ralloc (pool * p, const struct resclass * c) -- create a resource

### Arguments

pool * p

  pool to create the resource in

const struct resclass * c

  class of the new resource

### Description

This function is called by the resource classes to create a new resource of the specified class and link it to the given pool. Allocated memory is zeroed. Size of the resource structure is taken from the size field of the resclass.

### Function

void rlookup (unsigned long a) -- look up a memory location

### Arguments

unsigned long a

  memory address

### Description

This function examines all existing resources to see whether the address a is inside any resource. It's used for debugging purposes only.

It works by calling a class-specific lookup function for each resource.

### Function

void resource_init (void) -- initialize the resource manager

### Description

This function is called during BIRD startup. It initializes all data structures of the resource manager and creates the root pool.

## 8.3 Memory blocks

Memory blocks are pieces of contiguous allocated memory. They are a bit non-standard since they are represented not by a pointer to resource, but by a void pointer to the start of data of the memory block. All memory block functions know how to locate the header given the data pointer.

Example: All "unique" data structures such as hash tables are allocated as memory blocks.

### Function

void * mb_alloc (pool * p, unsigned size) -- allocate a memory block

### Arguments

pool * p

  pool

unsigned size

  size of the block

### Description

mb_alloc() allocates memory of a given size and creates a memory block resource representing this memory chunk in the pool p.

Please note that mb_alloc() returns a pointer to the memory chunk, not to the resource, hence you have to free it using mb_free(), not rfree().

### Function

void * mb_allocz (pool * p, unsigned size) -- allocate and clear a memory block

### Arguments

pool * p

  pool

unsigned size

  size of the block

### Description

mb_allocz() allocates memory of a given size, initializes it to zeroes and creates a memory block resource representing this memory chunk in the pool p.

Please note that mb_allocz() returns a pointer to the memory chunk, not to the resource, hence you have to free it using mb_free(), not rfree().

### Function

void * mb_realloc (void * m, unsigned size) -- reallocate a memory block

### Arguments

void * m

  memory block

unsigned size

  new size of the block

### Description

mb_realloc() changes the size of the memory block m to a given size. The contents will be unchanged to the minimum of the old and new sizes; newly allocated memory will be uninitialized. Contrary to realloc() behavior, m must be non-NULL, because the resource pool is inherited from it.

Like mb_alloc(), mb_realloc() also returns a pointer to the memory chunk, not to the resource, hence you have to free it using mb_free(), not rfree().

### Function

void mb_free (void * m) -- free a memory block

### Arguments

void * m

  memory block

### Description

mb_free() frees all memory associated with the block m.

## 8.4 Linear memory pools

Linear memory pools are collections of memory blocks which support very fast allocation of new blocks, but are able to free only the whole collection at once (or in stack order).

Example: Each configuration is described by a complex system of structures, linked lists and function trees which are all allocated from a single linear pool, thus they can be freed at once when the configuration is no longer used.

### Function

linpool * lp_new (pool * p) -- create a new linear memory pool

### Arguments

pool * p

  pool

### Description

lp_new() creates a new linear memory pool resource inside the pool p. The linear pool consists of a list of memory chunks of page size.

### Function

void * lp_alloc (linpool * m, uint size) -- allocate memory from a linpool

### Arguments

linpool * m

  linear memory pool

uint size

  amount of memory

### Description

lp_alloc() allocates size bytes of memory from a linpool m and it returns a pointer to the allocated memory.

It works by trying to find free space in the last memory chunk associated with the linpool and creating a new chunk of the standard size (as specified during lp_new()) if the free space is too small to satisfy the allocation. If size is too large to fit in a standard size chunk, an "overflow" chunk is created for it instead.

### Function

void * lp_allocu (linpool * m, uint size) -- allocate unaligned memory from a linpool

### Arguments

linpool * m

  linear memory pool

uint size

  amount of memory

### Description

lp_allocu() allocates size bytes of memory from a linpool m and it returns a pointer to the allocated memory. It doesn't attempt to align the memory block, giving a very efficient way how to allocate strings without any space overhead.

### Function

void * lp_allocz (linpool * m, uint size) -- allocate cleared memory from a linpool

### Arguments

linpool * m

  linear memory pool

uint size

  amount of memory

### Description

This function is identical to lp_alloc() except that it clears the allocated memory block.

### Function

void lp_flush (linpool * m) -- flush a linear memory pool

### Arguments

linpool * m

  linear memory pool

### Description

This function frees the whole contents of the given linpool m, but leaves the pool itself.

### Function

struct lp_state * lp_save (linpool * m) -- save the state of a linear memory pool

### Arguments

linpool * m

  linear memory pool

### Description

This function saves the state of a linear memory pool. Saved state can be used later to restore the pool (to free memory allocated since).

### Function

void lp_restore (linpool * m, lp_state * p) -- restore the state of a linear memory pool

### Arguments

linpool * m

  linear memory pool

lp_state * p

  saved state

### Description

This function restores the state of a linear memory pool, freeing all memory allocated since the state was saved. Note that the function cannot un-free the memory, therefore the function also invalidates other states that were saved between (on the same pool).

## 8.5 Slabs

Slabs are collections of memory blocks of a fixed size. They support very fast allocation and freeing of such blocks, prevent memory fragmentation and optimize L2 cache usage. Slabs have been invented by Jeff Bonwick and published in USENIX proceedings as `The Slab Allocator: An Object-Caching Kernel Memory Allocator'. Our implementation follows this article except that we don't use constructors and destructors.

When the `DEBUGGING` switch is turned on, we automatically fill all newly allocated and freed blocks with a special pattern to make detection of use of uninitialized or already freed memory easier.

Example: Nodes of a FIB are allocated from a per-FIB Slab.

### Function

uint sl_obj_count (const uint total_size, const uint fixed_overhead, const uint obj_size, const uint bits) -- calculate how many items fit into a memory block

### Arguments

const uint total_size

  total size of the available memory block, e.g. page size

const uint fixed_overhead

  fixed size of a header structure

const uint obj_size

  size of blocks to fit

const uint bits

  bits to allocate per fitted block

### Description

Returns the amount of items which fit into the memory block, when packed together with the header structure and bitfields.

### Function

slab * sl_new (pool * p, uint size) -- create a new Slab

### Arguments

pool * p

  resource pool

uint size

  block size

### Description

This function creates a new Slab resource from which objects of size size can be allocated.

### Function

void sl_delete (slab * s) -- destroy an existing Slab

### Arguments

slab * s

  slab

### Description

This function destroys the given Slab.

### Function

void * sl_alloc (slab * s) -- allocate an object from Slab

### Arguments

slab * s

  slab

### Description

sl_alloc() allocates space for a single object from the Slab and returns a pointer to the object.

### Function

void * sl_allocz (slab * s) -- allocate an object from Slab and zero it

### Arguments

slab * s

  slab

### Description

sl_allocz() allocates space for a single object from the Slab and returns a pointer to the object after zeroing out the object memory.

### Function

void sl_free (void * oo) -- return a free object back to a Slab

### Arguments

void * oo

  object returned by sl_alloc()

### Description

This function frees memory associated with the object oo and returns it back to the Slab s.

## 8.6 Events

Events are there to keep track of deferred execution. Since BIRD is single-threaded, it requires long lasting tasks to be split to smaller parts, so that no module can monopolize the CPU. To split such a task, just create an event resource, point it to the function you want to have called and call ev_schedule() to ask the core to run the event when nothing more important requires attention.

You can also define your own event lists (the event_list structure), enqueue your events in them and explicitly ask to run them.

### Function

event * ev_new (pool * p) -- create a new event

### Arguments

pool * p

  resource pool

### Description

This function creates a new event resource. To use it, you need to fill the structure fields and call ev_schedule().

### Function

void ev_run (event * e) -- run an event

### Arguments

event * e

  an event

### Description

This function explicitly runs the event e (calls its hook function) and removes it from an event list if it's linked to any.

From the hook function, you can call ev_enqueue() or ev_schedule() to re-add the event.

### Function

void ev_send (event_list * l, event * e) -- enqueue an event

### Arguments

event_list * l

  an event list

event * e

  an event

### Description

ev_enqueue() stores the event e to the specified event list l which can be run by calling ev_run_list().

### Function

int ev_run_list_limited (event_list * l, uint limit) -- run an event list

### Arguments

event_list * l

  an event list

uint limit

  -- undescribed --

### Description

This function calls ev_run() for all events enqueued in the list l.

## 8.7 Publish/Subscribe Queues

BIRD implements a publish/subscribe messaging system with dynamic topic management and resource tracking. The system allows multiple publishers to send messages to named topics, which are then distributed to all subscribers of those topics.

The system is built around four main components: queues, topics, publishers, and subscribers. A ps_queue serves as the central coordination point, maintaining list of topics. Topics are created dynamically when first referenced and can have multiple publishers and subscribers attached.

Publishers and subscribers are implemented as managed resources. Each publisher or subscriber can be attached to only one topic. When publishers or subscribers are destroyed, they automatically detach from their associated topics.

The ps_init_queue() function initializes a new message queue with a given name and memory pool. Topics are created on-demand through ps_get_topic(). Publishers attach to topics using ps_attach() and can send messages via ps_publish(), which sends notification to all subscribers. Subscribers use ps_subscribe() to register for topic updates. When a subscriber joins a topic with attached publishers, these publishers are notified of the new subscription through their subscribe hooks.

## 8.8 Sockets

Socket resources represent network connections. Their data structure (socket) contains a lot of fields defining the exact type of the socket, the local and remote addresses and ports, pointers to socket buffers and finally pointers to hook functions to be called when new data have arrived to the receive buffer (rx_hook), when the contents of the transmit buffer have been transmitted (tx_hook) and when an error or connection close occurs (err_hook).

Freeing of sockets from inside socket hooks is perfectly safe.

### Function

int sk_setup_multicast (sock * s) -- enable multicast for given socket

### Arguments

sock * s

  socket

### Description

Prepare transmission of multicast packets for given datagram socket. The socket must have defined iface.

### Result

0 for success, -1 for an error.

### Function

int sk_join_group (sock * s, ip_addr maddr) -- join multicast group for given socket

### Arguments

sock * s

  socket

ip_addr maddr

  multicast address

### Description

Join multicast group for given datagram socket and associated interface. The socket must have defined iface.

### Result

0 for success, -1 for an error.

### Function

int sk_leave_group (sock * s, ip_addr maddr) -- leave multicast group for given socket

### Arguments

sock * s

  socket

ip_addr maddr

  multicast address

### Description

Leave multicast group for given datagram socket and associated interface. The socket must have defined iface.

### Result

0 for success, -1 for an error.

### Function

int sk_setup_broadcast (sock * s) -- enable broadcast for given socket

### Arguments

sock * s

  socket

### Description

Allow reception and transmission of broadcast packets for given datagram socket. The socket must have defined iface. For transmission, packets should be send to brd address of iface.

### Result

0 for success, -1 for an error.

### Function

int sk_set_ttl (sock * s, int ttl) -- set transmit TTL for given socket

### Arguments

sock * s

  socket

int ttl

  TTL value

### Description

Set TTL for already opened connections when TTL was not set before. Useful for accepted connections when different ones should have different TTL.

### Result

0 for success, -1 for an error.

### Function

int sk_set_min_ttl (sock * s, int ttl) -- set minimal accepted TTL for given socket

### Arguments

sock * s

  socket

int ttl

  TTL value

### Description

Set minimal accepted TTL for given socket. Can be used for TTL security. implementations.

### Result

0 for success, -1 for an error.

### Function

int sk_set_md5_auth (sock * s, ip_addr local, ip_addr remote, struct iface * ifa, char * passwd, int setkey) -- add / remove MD5 security association for given socket

### Arguments

sock * s

  socket

ip_addr local

  IP address of local side

ip_addr remote

  IP address of remote side

struct iface * ifa

  Interface for link-local IP address

char * passwd

  Password used for MD5 authentication

int setkey

  Update also system SA/SP database

### Description

In TCP MD5 handling code in kernel, there is a set of security associations used for choosing password and other authentication parameters according to the local and remote address. This function is useful for listening socket, for active sockets it may be enough to set s->password field.

When called with passwd != NULL, the new pair is added, When called with passwd == NULL, the existing pair is removed.

Note that while in Linux, the MD5 SAs are specific to socket, in BSD they are stored in global SA/SP database (but the behavior also must be enabled on per-socket basis). In case of multiple sockets to the same neighbor, the socket-specific state must be configured for each socket while global state just once per src-dst pair. The setkey argument controls whether the global state (SA/SP database) is also updated.

### Result

0 for success, -1 for an error.

### Function

int sk_set_ipv6_checksum (sock * s, int offset) -- specify IPv6 checksum offset for given socket

### Arguments

sock * s

  socket

int offset

  offset

### Description

Specify IPv6 checksum field offset for given raw IPv6 socket. After that, the kernel will automatically fill it for outgoing packets and check it for incoming packets. Should not be used on ICMPv6 sockets, where the position is known to the kernel.

### Result

0 for success, -1 for an error.

### Function

sock * sock_new (pool * p) -- create a socket

### Arguments

pool * p

  pool

### Description

This function creates a new socket resource. If you want to use it, you need to fill in all the required fields of the structure and call sk_open() to do the actual opening of the socket.

The real function name is sock_new(), sk_new() is a macro wrapper to avoid collision with OpenSSL.

### Function

int sk_open (sock * s, struct birdloop * loop) -- open a socket

### Arguments

sock * s

  socket

struct birdloop * loop

  loop

### Description

This function takes a socket resource created by sk_new() and initialized by the user and binds a corresponding network connection to it.

### Result

0 for success, -1 for an error.

### Function

int sk_send (sock * s, unsigned len) -- send data to a socket

### Arguments

sock * s

  socket

unsigned len

  number of bytes to send

### Description

This function sends len bytes of data prepared in the transmit buffer of the socket s to the network connection. If the packet can be sent immediately, it does so and returns 1, else it queues the packet for later processing, returns 0 and calls the tx_hook of the socket when the tranmission takes place.

### Function

int sk_send_to (sock * s, unsigned len, ip_addr addr, unsigned port) -- send data to a specific destination

### Arguments

sock * s

  socket

unsigned len

  number of bytes to send

ip_addr addr

  IP address to send the packet to

unsigned port

  port to send the packet to

### Description

This is a sk_send() replacement for connection-less packet sockets which allows destination of the packet to be chosen dynamically. Raw IP sockets should use 0 for port.

### Function

void io_log_event (void * hook, void * data, uint flag) -- mark approaching event into event log

### Arguments

void * hook

  event hook address

void * data

  event data address

uint flag

  -- undescribed --

### Description

Store info (hook, data, timestamp) about the following internal event into a circular event log (event_log). When latency tracking is enabled, the log entry is kept open (in event_open) so the duration can be filled later.

Next Previous Contents