4.6. Updating Datatypes

Datatype mappings are maintained in a separate hash table per datatype. The table must be initialized before applying an update, and freed when reversing the update, as shown in Figure 4-6. Figure 4-7 shows a set of new fields that should be added in the existing datatype definition of struct task_struct, the process control block in Linux 2.4. All these fields are grouped in a new datatype definition called struct new_task_struct, instead of extending the existing definition.

Figure 4-6. Managing a datatype mapping table.

/* Mapping table for the updated datatype of struct task_struct. */
dynreplace_access_t access_new_task_struct;

void epckpt_init()
{
  dynreplace_access_init(&access_new_task_struct);
}

void epckpt_cleanup()
{
  dynreplace_access_cleanup(&access_new_task_struct);
}
         

Figure 4-7. Definition of struct new_task_struct.

struct new_task_struct {
	int collect_ckpt_data:1;
	struct mmap_list *mmap_list;
	struct shmem_list *shmem_list;
	struct sem_list *sem_list;
};
         
A datatype definition, like the one in Figure 4-7, may define the initial values fields will need to be initialized to. This initialization must be carried out manually. Shadow variables must be instantiated when the original datatype is instantiated, and a mapping between the original and its shadow must be established. Figure 4-8 shows an example creating a shadow variable instance on do_fork which will hold the new fields of struct new_task_struct as defined in Figure 4-7. A shadow instance is created, and a reference to the shadow is obtained. The reference is used to access the new fields.

Figure 4-8. Creating a shadow variable in do_fork.

int do_fork_v2(unsigned long clone_flags, unsigned long stack_start,
	       struct pt_regs *regs, unsigned long stack_size)
{
  ...
  struct task_struct *p;
  void *new_p;

  ...

  dynreplace_access_lock(&access_new_task_struct);

  /* Create a shadow instance for this task_struct */
  dynreplace_access_create(&access_new_task_struct,
                           (void *)p, sizeof(struct new_task_struct));

  /* Obtain a reference to the shadow instance */
  new_p = dynreplace_access_find(&access_new_task_struct, (void *)p);

  /* Initialize the instance */
  if (new_p != NULL) {
    /* Access the various fields */
    dynreplace_access_field(struct new_task_struct, new_p, collect_ckpt_data) = 1;
    dynreplace_access_field(struct new_task_struct, new_p, mmap_list) = NULL;
    dynreplace_access_field(struct new_task_struct, new_p, shmem_list) = NULL;
    dynreplace_access_field(struct new_task_struct, new_p, sem_list) = NULL;

    /* Use the new fields to change control flow as required by the update. */	
    if (dynreplace_access_field(struct new_task_struct, new_p, collect_ckpt_data)) {
      /* Perform process checkpointing bookkeeping */
      ...

    }
  }

  dynreplace_access_unlock(&access_new_task_struct);

 ...

}
         
Shadow variable instances must be freed when the original datatype is freed. Figure 4-9 shows an example freeing the shadow variable instance on do_exit.

Figure 4-9. Freeing a shadow variable in do_exit.

ATTRIB_NORET NORET_TYPE void do_exit_v2(long code) 
{
  struct task_struct *tsk = current;

 ...


  {
     void *new_current;

     dynreplace_access_lock(&access_new_task_struct);

     new_current = dynreplace_access_find(&access_new_task_struct, (void *)current);
     if (new_current != NULL)
       dynreplace_access_remove(&access_new_task_struct, (void *)current);

     dynreplace_access_unlock(&access_new_task_struct);
  }

 ...

}
         
On shadow variable instance creation and deletion the code that accesses the new fields and the code that frees the shadow variable is written in a manner that permits existing kernel code paths to execute without dereferencing a NULL pointer. For example, a process executing do_exit that has not had a shadow variable instance created (because when the process was created the update had not been applied) will still execute normally. Similarly, code in other functions that uses the new fields should not be executed if there is no shadow instance available. In essence, it is possible to have multiple versions of a function concurrently executing safely.