Go to main content

Resource Management and Oracle® Solaris Zones Developer's Guide

Exit Print View

Updated: February 2021
 
 

Resource Control Code Examples

Master Observing Process for Resource Controls

The following example is the master observer process. Master Observing Process shows the resource controls for the master observing process. This master process observes any attempt to exceed the resource control limit by receiving signals generated by the attempt.


Note -  The line break is not valid in an /etc/project file. The line break is shown here only to allow the example to display on a printed or displayed page. Each entry in the /etc/project file must be on a separate line.

Figure 5  Master Observing Process

image:Diagram shows resource controls for the master observing process.

The key points for the example include the following:

  • Because the task's limit is privileged, the application cannot change the limit, or specify an action, such as a signal. A master process solves this problem by establishing the same resource control as a basic resource control on the task. The master process uses the same value or a little less on the resource, but with a different action, signal = XRES. The master process creates a thread to wait for this signal.

  • The rctlblk is opaque. The struct needs to be dynamically allocated.

  • Note the blocking of all signals before creating the thread, as required by sigwait.

  • The thread calls the sigwait system call to block for the signal. If sigwait returns the SIGXRES signal, the thread notifies the children of the master process, which adapt to reduce the number of LWPs being used. Each child should also be modelled similarly, with a thread in each child, waiting for this signal, and adapting the LWP usage of its processes appropriately.

When setting values for a new resource control block, rctlblk_set_value(), rctlblk_set_privilege(), rctlblk_set_local_action(), and rctlblk_set_local_flags() calls are all required. If not set, the corresponding properties of the resource control block have undefined values, causing an undefined result of any function reading the data of that resource control block. For resource control without local signal action, use 0 as the default value of signal in rctlblk_set_local_action(). For a resource control without a local flag, use 0 as the default value of local flag in rctlblk_set_local_flags().

rctlblk_t *mlwprcb;
sigset_t smask;

/* Omit return value checking/error processing to keep code sample short */
/* First, install a RCPRIV_BASIC, v=1000, signal=SIGXRES rctl */
mlwprcb = calloc(1, rctlblk_size());	 /* rctl blocks are opaque: */
       rctlblk_set_local_flags(mlwprcb, 0);
       rctlblk_set_value(mlwprcb, 1000);
       rctlblk_set_privilege(mlwprcb, RCPRIV_BASIC);
       rctlblk_set_local_action(mlwprcb, RCTL_LOCAL_SIGNAL, SIGXRES);
       if (setrctl("task.max-lwps", NULL, mlwprcb, RCTL_INSERT) == -1) {
           perror("setrctl");
           exit (1);
       }

/* Now, create the thread which waits for the signal */
        sigemptyset(&smask);
        sigaddset(&smask, SIGXRES);
        thr_sigsetmask(SIG_BLOCK, &smask, NULL);
thr_create(NULL, 0, sigthread, (void *)SIGXRES, THR_DETACHED, NULL));

/* Omit return value checking/error processing to keep code sample short */

void *sigthread(void *a)
{
        int sig = (int)a;
        int rsig;
        sigset_t sset;

        sigemptyset(&sset);
        sigaddset(&sset, sig);

        while (1) {
                 rsig = sigwait(&sset);
          if (rsig == SIGXRES) {
              notify_all_children();
              /* e.g. sigsend(P_PID, child_pid, SIGXRES); */
		     }
        }
}

List all the Value-Action Pairs for a Specific Resource Control

The following example lists all the value-action pairs for a specific resource control, task.max-lwps. The key point for the example is that getrctl takes two resource control blocks, and returns the resource control block for the RCTL_NEXT flag. To iterate through all resource control blocks, repeatedly swap the resource control block values, as shown here using the rcb_tmp rctl block.

rctlblk_t *rcb1, *rcb2, *rcb_tmp;

/* Omit return value checking/error processing to keep code sample short */
rcb1 = calloc(1, rctlblk_size()); /* rctl blocks are opaque: */
                               /* "rctlblk_t rcb" does not work */
rcb2 = calloc(1, rctlblk_size());
getrctl("task.max-lwps", NULL, rcb1, RCTL_FIRST);
while (1) {
     print_rctl(rcb1);
     rcb_tmp = rcb2;
     rcb2 = rcb1;
     rcb1 = rcb_tmp;        /* swap rcb1 with rcb2 */
     if (getrctl("task.max-lwps", rcb2,  rcb1, RCTL_NEXT) == -1) {
          if (errno == ENOENT) {
               break;
     } else {
          perror("getrctl");
          exit (1);
     }
     }
}

Set project.cpu-shares and Add a New Value

    The key points of the example include the following:

  • Use the memcpy function instead of buffer swapping as in List all the Value-Action Pairs for a Specific Resource Control. The content of resource control blocks can be copied by memcpy().

  • To change the resource control value, call the setrctlx function with the RCTL_REPLACE flag. The new resource control block is identical to the old resource control block except for the new control value and local flags.

The example gets the first CPU share allocation, project.cpu-shares, and changes its value to nshares.

#include <assert.h>
#include <errno.h>
#include <rctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*
 * Change the value of first cpu-share of a project. Return 0 on success, -1 on
 * failure with errno set.
 */
int
replace_proj_first_cpu_share(projid_t projid, rctl_qty_t new_share)
{    
    int ret_internal;
    rctl_qty_t max_share;
    const char *rctl_name = "project.cpu-shares";
    const size_t blk_size = rctlblk_size();
    int ret = -1;
    rctlblk_t *blk1 = malloc(blk_size), *blk2 = malloc(blk_size);

    if (blk1 == NULL || blk2 == NULL) { 
        perror("malloc() failed");
        goto ret_block;
    }
     /*
      * First, we check that new_share is valid. To do that, we need to
      * compare new_share with the system limit value of
      * "project.cpu-shares". Since this value is same for all projects of
      * the system, we can get the system value for the caller's project.
      */ 
    ret_internal = getrctl(rctl_name, NULL, blk1, RCTL_SYSTEM); 
    /* getrctl() always succeeds for RCTL_SYSTEM given valid argument. */
    assert(ret_internal == 0); 

    max_share = rctlblk_get_value(blk1); 
    if (new_share > max_share) {
        (void) fprintf(stderr, "new_share too large: %llu\n",
            (unsigned long long)new_share);
        errno = EINVAL;
        goto ret_block;
     } 
     if (getrctlx(rctl_name, NULL, blk1, P_PROJID, projid, RCTL_FIRST) !=
         0) { 
        /* 
         * Given valid argument, only possible failure of getrctlx() is 
         * ESRCH, which means that the target entity (project, in this 
         * case) does not exist.
         */ 
        assert(errno == ESRCH); 
        perror("getrctlx() failed");
        goto ret_block; 
      } 
      (void) memcpy(blk2, blk1, blk_size); 

       /* 
        * Verify that cpu-share does not allow infinity, and set the
        * maximal flag if new_share is the system limit value. 
        */
       assert(!(rctlblk_get_global_flags(blk1) & RCTL_GLOBAL_INFINITE));
       rctlblk_set_local_flags(blk2, 
           (new_share == max_share) ? RCTL_LOCAL_MAXIMAL : 0); 
       rctlblk_set_value(blk2, new_share); 

       if (setrctlx(rctl_name, blk1, blk2, P_PROJID, projid, RCTL_REPLACE) !=
           0) { 
           perror("setrctlx() failed");
           goto ret_block; 
      } 

        ret = 0; 
    ret_block: 
        free(blk1); 
        free(blk2);
        return (ret); 
}

Set LWP Limit Using Resource Control Blocks

In the following example, an application has set a privileged limit of 3000 LWPs that may not be exceeded. In addition, the application has set a basic limit of 2000 LWPs. When this limit is exceeded, a SIGXRES is sent to the application. Upon receiving a SIGXRES, the application might send notification to its child processes that might in turn reduce the number of LWPs the processes use or need.

/* Omit return value and error checking */

#include <rctl.h>

rctlblk_t *rcb1, *rcb2;

/*
         * Resource control blocks are opaque
         * and must be explicitly allocated.
         */
rcb1 = calloc(rctlblk_size());	

rcb2 = calloc(rctlblk_size());	


/* Install an RCPRIV_PRIVILEGED, v=3000: do not allow more than 3000 LWPs */
rctlblk_set_local_flags(rcb1, 0)
rctlblk_set_value(rcb1, 3000);
rctlblk_set_privilege(rcb1, RCPRIV_PRIVILEGED);
rctlblk_set_local_action(rcb1, RCTL_LOCAL_DENY);
setrctl("task.max-lwps", NULL, rcb1, RCTL_INSERT);


/* Install an RCPRIV_BASIC, v=2000 to send SIGXRES when LWPs exceeds 2000 */
rctlblk_set_local_flags(rcb2, 0)
rctlblk_set_value(rcb2, 2000);
rctlblk_set_privilege(rcb2, RCPRIV_BASIC);
rctlblk_set_local_action(rcb2, RCTL_LOCAL_SIGNAL, SIGXRES);
setrctl("task.max-lwps", NULL, rcb2, RCTL_INSERT);