diff options
Diffstat (limited to 'usr/src/uts/common/os/rctl.c')
-rw-r--r-- | usr/src/uts/common/os/rctl.c | 291 |
1 files changed, 290 insertions, 1 deletions
diff --git a/usr/src/uts/common/os/rctl.c b/usr/src/uts/common/os/rctl.c index 026d6b13ed..3aa6e7b6cc 100644 --- a/usr/src/uts/common/os/rctl.c +++ b/usr/src/uts/common/os/rctl.c @@ -19,7 +19,7 @@ * CDDL HEADER END */ /* - * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ @@ -150,6 +150,41 @@ * * The locking subsequence of interest is: p_lock, rctl_dict_lock, * rctl_lists_lock, entity->rcs_lock. + * + * The projects(4) database and project entity resource controls + * A special case is made for RCENTITY_PROJECT values set through the + * setproject(3PROJECT) interface. setproject() makes use of a private + * interface, setprojrctl(), which passes through an array of resource control + * blocks that need to be set while holding the entity->rcs_lock. This + * ensures that the act of modifying a project's resource controls is + * "atomic" within the kernel. + * + * Within the rctl sub-system, we provide two interfaces that are only used by + * the setprojrctl() code path - rctl_local_insert_all() and + * rctl_local_replace_all(). rctl_local_insert_all() will ensure that the + * resource values specified in *new_values are applied. + * rctl_local_replace_all() will purge the current rctl->rc_projdb and + * rctl->rc_values entries, and apply the *new_values. + * + * These functions modify not only the linked list of active resource controls + * (rctl->rc_values), but also a "cached" linked list (rctl->rc_projdb) of + * values set through these interfaces. To clarify: + * + * rctl->rc_values - a linked list of rctl_val_t. These are the active + * resource values associated with this rctl, and may have been set by + * setrctl() - via prctl(1M), or by setprojrctl() - via + * setproject(3PROJECT). + * + * rctl->rc_projdb - a linked list of rctl_val_t. These reflect the + * resource values set by the setprojrctl() code path. rc_projdb is not + * referenced by any other component of the rctl sub-system. + * + * As various locks are held when calling these functions, we ensure that all + * the possible memory allocations are performed prior to calling the + * function. *alloc_values is a linked list of uninitialized rctl_val_t, + * which may be used to duplicate a new resource control value (passed in as + * one of the members of the *new_values linked list), in order to populate + * rctl->rc_values. */ id_t max_rctl_hndl = 32768; @@ -1081,6 +1116,7 @@ rctl_set_init(rctl_entity_t entity, struct proc *p, rctl_entity_p_t *e, rctl->rc_dict_entry = rde; rctl->rc_id = rde->rcd_id; + rctl->rc_projdb = NULL; rctl->rc_values = rctl_val_list_dup(rde->rcd_default_value, ragp, NULL, p); @@ -1688,6 +1724,259 @@ rctl_local_insert(rctl_hndl_t hndl, rctl_val_t *val, struct proc *p) return (rctl_local_op(hndl, NULL, val, rctl_local_insert_cb, p)); } +/* + * rctl_local_insert_all_cb() + * + * Overview + * Called for RCENTITY_PROJECT rctls only, via rctlsys_projset(). + * + * Inserts new values from the project database (new_values). alloc_values + * should be a linked list of pre-allocated rctl_val_t, which are used to + * populate (rc_projdb). + * + * Should the *new_values linked list match the contents of the rctl's + * rp_projdb then we do nothing. + * + * Return Values + * 0 is always returned. + */ +/*ARGSUSED*/ +static int +rctl_local_insert_all_cb(rctl_hndl_t hndl, struct proc *p, rctl_entity_p_t *e, + rctl_t *rctl, rctl_val_t *new_values, rctl_val_t *alloc_values) +{ + rctl_val_t *val; + rctl_val_t *tmp_val; + rctl_val_t *next; + int modified = 0; + + /* + * If this the first time we've set this project rctl, then we delete + * all the privilege values. These privilege values have been set by + * rctl_add_default_limit(). + * + * We save some cycles here by not calling rctl_val_list_delete(). + */ + if (rctl->rc_projdb == NULL) { + val = rctl->rc_values; + + while (val != NULL) { + if (val->rcv_privilege == RCPRIV_PRIVILEGED) { + if (val->rcv_prev != NULL) + val->rcv_prev->rcv_next = val->rcv_next; + else + rctl->rc_values = val->rcv_next; + + if (val->rcv_next != NULL) + val->rcv_next->rcv_prev = val->rcv_prev; + + tmp_val = val; + val = val->rcv_next; + kmem_cache_free(rctl_val_cache, tmp_val); + } else { + val = val->rcv_next; + } + } + modified = 1; + } + + /* + * Delete active values previously set through the project database. + */ + val = rctl->rc_projdb; + + while (val != NULL) { + + /* Is the old value found in the new values? */ + if (rctl_val_list_find(&new_values, val) == NULL) { + + /* + * Delete from the active values if it originated from + * the project database. + */ + if (((tmp_val = rctl_val_list_find(&rctl->rc_values, + val)) != NULL) && + (tmp_val->rcv_flagaction & RCTL_LOCAL_PROJDB)) { + (void) rctl_val_list_delete(&rctl->rc_values, + tmp_val); + } + + tmp_val = val->rcv_next; + (void) rctl_val_list_delete(&rctl->rc_projdb, val); + val = tmp_val; + modified = 1; + + } else + val = val->rcv_next; + } + + /* + * Insert new values from the project database. + */ + while (new_values != NULL) { + next = new_values->rcv_next; + + /* + * Insert this new value into the rc_projdb, and duplicate this + * entry to the active list. + */ + if (rctl_val_list_insert(&rctl->rc_projdb, new_values) == 0) { + + tmp_val = alloc_values->rcv_next; + bcopy(new_values, alloc_values, sizeof (rctl_val_t)); + alloc_values->rcv_next = tmp_val; + + if (rctl_val_list_insert(&rctl->rc_values, + alloc_values) == 0) { + /* inserted move alloc_values on */ + alloc_values = tmp_val; + modified = 1; + } + } else { + /* + * Unlike setrctl() we don't want to return an error on + * a duplicate entry; we are concerned solely with + * ensuring that all the values specified are set. + */ + kmem_cache_free(rctl_val_cache, new_values); + } + new_values = next; + } + + /* Teardown any unused rctl_val_t */ + while (alloc_values != NULL) { + tmp_val = alloc_values; + alloc_values = alloc_values->rcv_next; + kmem_cache_free(rctl_val_cache, tmp_val); + } + + /* Reset the cursor if rctl values have been modified */ + if (modified) { + rctl->rc_cursor = rctl->rc_values; + rctl_val_list_reset(rctl->rc_cursor); + RCTLOP_SET(rctl, p, e, rctl_model_value(rctl->rc_dict_entry, p, + rctl->rc_cursor->rcv_value)); + } + + return (0); +} + +int +rctl_local_insert_all(rctl_hndl_t hndl, rctl_val_t *new_values, + rctl_val_t *alloc_values, struct proc *p) +{ + return (rctl_local_op(hndl, new_values, alloc_values, + rctl_local_insert_all_cb, p)); +} + +/* + * rctl_local_replace_all_cb() + * + * Overview + * Called for RCENTITY_PROJECT rctls only, via rctlsys_projset(). + * + * Clears the active rctl values (rc_values), and stored values from the + * previous insertions from the project database (rc_projdb). + * + * Inserts new values from the project database (new_values). alloc_values + * should be a linked list of pre-allocated rctl_val_t, which are used to + * populate (rc_projdb). + * + * Return Values + * 0 is always returned. + */ +/*ARGSUSED*/ +static int +rctl_local_replace_all_cb(rctl_hndl_t hndl, struct proc *p, rctl_entity_p_t *e, + rctl_t *rctl, rctl_val_t *new_values, rctl_val_t *alloc_values) +{ + rctl_val_t *val; + rctl_val_t *next; + rctl_val_t *tmp_val; + + /* Delete all the privilege vaules */ + val = rctl->rc_values; + + while (val != NULL) { + if (val->rcv_privilege == RCPRIV_PRIVILEGED) { + if (val->rcv_prev != NULL) + val->rcv_prev->rcv_next = val->rcv_next; + else + rctl->rc_values = val->rcv_next; + + if (val->rcv_next != NULL) + val->rcv_next->rcv_prev = val->rcv_prev; + + tmp_val = val; + val = val->rcv_next; + kmem_cache_free(rctl_val_cache, tmp_val); + } else { + val = val->rcv_next; + } + } + + /* Delete the contents of rc_projdb */ + val = rctl->rc_projdb; + while (val != NULL) { + + tmp_val = val; + val = val->rcv_next; + kmem_cache_free(rctl_val_cache, tmp_val); + } + rctl->rc_projdb = NULL; + + /* + * Insert new values from the project database. + */ + while (new_values != NULL) { + next = new_values->rcv_next; + + if (rctl_val_list_insert(&rctl->rc_projdb, new_values) == 0) { + tmp_val = alloc_values->rcv_next; + bcopy(new_values, alloc_values, sizeof (rctl_val_t)); + alloc_values->rcv_next = tmp_val; + + if (rctl_val_list_insert(&rctl->rc_values, + alloc_values) == 0) { + /* inserted, so move alloc_values on */ + alloc_values = tmp_val; + } + } else { + /* + * Unlike setrctl() we don't want to return an error on + * a duplicate entry; we are concerned solely with + * ensuring that all the values specified are set. + */ + kmem_cache_free(rctl_val_cache, new_values); + } + + new_values = next; + } + + /* Teardown any unused rctl_val_t */ + while (alloc_values != NULL) { + tmp_val = alloc_values; + alloc_values = alloc_values->rcv_next; + kmem_cache_free(rctl_val_cache, tmp_val); + } + + /* Always reset the cursor */ + rctl->rc_cursor = rctl->rc_values; + rctl_val_list_reset(rctl->rc_cursor); + RCTLOP_SET(rctl, p, e, rctl_model_value(rctl->rc_dict_entry, p, + rctl->rc_cursor->rcv_value)); + + return (0); +} + +int +rctl_local_replace_all(rctl_hndl_t hndl, rctl_val_t *new_values, + rctl_val_t *alloc_values, struct proc *p) +{ + return (rctl_local_op(hndl, new_values, alloc_values, + rctl_local_replace_all_cb, p)); +} + static int rctl_local_replace_cb(rctl_hndl_t hndl, struct proc *p, rctl_entity_p_t *e, rctl_t *rctl, rctl_val_t *oval, rctl_val_t *nval) |