ChorusOS 4.0 Hot Restart Programmer's Guide

Chapter 3 Programming With Persistent Memory

This chapter provides a detailed description of the API exported by the Persistent Memory Manager. In particular, it covers the following topics:

In this chapter, an example "hello world" program is used to illustrate different aspects of the Persistent Memory Manager interface. The code for this example is given in "3.2 A Simple Application".

To run the example, you will need to compile it and then copy it to a directory which is mounted by the target machine. See "B.1 Compiling and Running the Examples" for information about compiling and running the hot restart examples.


Note -

Before reading this chapter, make sure that you are familiar with the basic persistent memory architecture described in "2.1.2 Memory Requirements and Design Considerations".


3.1 Introduction to Persistent Memory Programming

Within a running ChorusOS system, access to persistent memory is provided by a ChorusOS actor known as the Persistent Memory Manager. The Persistent Memory Manager exports a specific API for allocating and freeing blocks of memory in the persistent memory bank. This API is distinct from the API used for allocating and de-allocating traditional ChorusOS memory regions (rgnAllocate(2K), rgnFree(2K), svPagesAllocate(2K), and svPagesFree(2K), for the following reasons:

The Persistent Memory Manager API is available to all ChorusOS 4.0 actors (not just restartable actors). The aim of this chapter is to describe in detail the use of this API.

3.2 A Simple Application

Before proceeding with a description of the different functions in the Persistent Memory Manager API, consider the following simple restartable application, an implementation of the familiar "hello world" example. When the actor is run for the first time, it displays the following message on the host console:

Hello world!

When the actor is restarted, it displays the following message on the target console:

Hello again! I have been restarted.

The basic flow of execution is as follows:


Example 3-1 The "Hello world" Restartable Actor

#include <stdio.h>
#include <pmm/chPmm.h>
#include <hr/hr.h>

#define HR_GROUP "HELLO_GROUP"

    int 
main()
{
	
	int res;
	int any = 1;
	int* counter_p;  /* It will be stored in persistent memory */
	long *p;
	PmmName name; 
	KnRgnDesc rgn;
	
            /* 
             * Initialize the name and medium fields 
             * to identify the persistent memory block in the system.
             */
	bzero(&name, sizeof(name));
	strcpy(name.medium,"RAM");
	strcpy(name.name,"PM1");
	
            /* 
             * Initialize the block fields
             */	
	bzero(&rgn, sizeof(rgn));
	rgn.options = K_ANYWHERE | K_RESERVED;
	rgn.size    = vmPageSize();
	res = rgnAllocate(K_MYACTOR, &rgn);
	if (res != K_OK) {
            printf("rgnAllocate() failed res=%d\n", res);
            HR_EXIT_HDL();
            exit(-1);
	}

	p = (long*) rgn.startAddr;

            /*
             * From now on p is a bad pointer, since 
             * VIRTUAL_ADDRESS_SPACE is true.
             */

            /* 
             * Allocate the persistent memory block that stores
             * counter_p.
             */
	res=pmmAllocate((VmAddr *)&counter_p,
                        &name,sizeof(int),
                        HR_GROUP,
                        sizeof(HR_GROUP));
	
	if (res != K_OK) {
            printf("Cannot allocate or map the persistent memory block called %s."
                   " Error = %d\n", name.name, res);
            HR_EXIT_HDL();
            exit(-1);
	}
	
            /* 
             * From the value of *counter_p the actor detects  
             * whether it has been hot restarted or not.
             */
	if ( *counter_p==0 ) {
                /*
                 * This is the first time the actor is run.
                 */
            printf("Hello world!\n");
		
                /* 
                 * Increment the counter
                 */
            (*counter_p)++;
		
                /*
                 * Normally the next instruction causes a core dump and
                 * a hot restart of the actor
                 */
            *p = 0xDeadBeef;

	} else {
                /* 
                 * The actor has been restarted
                 * NOTE: this message will appear on the console!
                 */
            printf("The actor has been restarted.\n");
		
                /*
                 * Free the persistent memory block before exiting
                 */
            res = pmmFree(&name);
            if (res != K_OK) {
                printf(" pmmFree failed, res=%d. Exit\n", res);
                HR_EXIT_HDL();
                exit(-1);
            }	
                /* 
                 * Terminate cleanly. 
                 */ 
            printf("Example finished. Exit.\n");
            HR_EXIT_HDL();
            exit(0);
	}
            /* Never reached */
}
	

The aspects of this program which are of interest for users of the Persistent Memory Manager API are discussed in the rest of this chapter.

3.3 Allocating and Retrieving a Persistent Memory Block

The "hello world" application uses a block of persistent memory to store a counter indicating whether it has been restarted. The value of the counter controls the program's flow of execution. This is a very common use of persistent memory. A counter or flag such as this is usually necessary as it is the only way an actor can know whether it has been restarted.

A block of persistent memory is described in the system by a structure of the following type:

#include <chPmm.h> 
typedef struct { PmmMedium   medium = "RAM";
                 PmmMemName  name; } 
PmmName;

Within the structure, medium is a character string which identifies the memory bank to be used. In the current implementation, it must always be set to RAM. name is a user-defined, null-terminated character string which uniquely identifies the block of memory in the memory bank. The lifetime of a block name is the same as the lifetime of the block itself in persistent memory. A system tunable parameter, pmm.maxBlocks, defines the number of distinct persistent memory blocks (and therefore names) which can be allocated at any one time. The default value is 30.


Caution - Caution -

Sharing persistent memory blocks between user actors, or between user and supervisor actors is not supported. Persistent memory blocks can only be shared between supervisor actors.


To allocate a block of persistent memory, or retrieve a block of memory which has already been allocated, use the pmmAllocate() function call, defined as follows:

#include <chPmm.h>
KnError pmmAllocate(  VmAddr     *addr,
                      PmmName    *name,
                      size_t      size,
                      PmmDelKey   delKey,
                      size_t      delKeySize);

If no memory block corresponding to the specified PmmName structure is present in persistent memory, pmmAllocate() allocates a block of size size in persistent memory, fills it with nulls, and returns the pointer *addr to the address of the block. The address is determined by the system and cannot be specified or changed.

If a block identified with the specified PmmName already exists in persistent memory, pmmAllocate() simply returns a pointer to the existing memory block as an address (*addr), and the size parameter is ignored. Persistent memory blocks are always mapped at the same address. In other words, the address returned by the first and subsequent calls to pmmAllocate() is always the same for a given block.

As a result of this dual functionality of the pmmAllocate() call, the difference between initially allocating and subsequently retrieving a persistent memory block is transparent at the programming level. The first time the code of the "hello world" example is executed, the call to pmmAllocate() allocates an integer-sized block of persistent memory which contains the initialized value of counter (0).

res=pmmAllocate((VmAddr *)&counter_p,
                        &name,sizeof(int),
                        HR_GROUP,
                        sizeof(HR_GROUP));

The second time the code is executed, the same function call returns a pointer to the value of counter in persistent memory.

The delKey and delKeySize parameters passed to pmmAllocate() are used to define the deletion key associated with the memory block. A deletion key is a user-defined binary array, used to mark a set of persistent memory blocks which can be freed simultaneously, using the pmmFreeAll() function, described in "3.4.2 Freeing a Persistent Memory Block Explicitly".

3.4 Freeing a Persistent Memory Block

This section describes the API calls used for freeing persistent memory blocks.

3.4.1 Responsibility

A persistent memory block can remain in memory beyond the lifetime of a run-time instance of the actor which allocates the block. This immediately raises the question of responsibility for freeing blocks of persistent memory. When a traditional ChorusOS 4.0 user actor terminates, any memory regions it allocated (using rgnAllocate(2K)) are automatically freed. Clearly, this simple rule makes little sense in the case of persistent memory blocks, which can survive beyond such a termination.

The hot restart feature provides two solutions to this problem:

In both cases, freeing a persistent memory block has the same effect: the block is immediately and permanently freed (cannot be retrieved), and the name which identified it can be re-used to identify a different memory block.

3.4.2 Freeing a Persistent Memory Block Explicitly

Use the pmmFree() or pmmFreeAll() function to explicitly free a persistent memory block. The explicit freeing of a given memory block can be performed by any actor, not necessarily the actor which originally allocated the block. It is the programmer's responsibility to ensure that the persistent memory block which will be freed is no longer in use.

Use pmmFree() to free a single memory block identified by a PmmName:

#include <chPmm.h>
int pmmFree( PmmName	*name )

Use pmmFreeAll() to free a group of persistent memory blocks which were allocated with the same deletion key. The deletion key for a persistent memory block is specified when the block is allocated with pmmAllocate().

#include <chPmm.h> 
int pmmFreeAll( PmmDelKey    delkey,
                size_t       delKeySize );

A typical use of a deletion key is to mark all persistent memory blocks used by an actor or a group of actors with the same key, and then have a separate, independent actor that frees all the blocks when a particular job is completed or a particular event occurs. For example, the "hello world" example uses pmmFree() to free the single memory block it allocates before it terminates. If the "hello world" actor did not free its own persistent memory block, the following call to pmmFreeAll() from another actor would free the block, along with any other blocks marked with the deletion key HR_GROUP.

pmmFreeAll( HR_GROUP, sizeof(HR_GROUP) );