Changeset View
Changeset View
Standalone View
Standalone View
sys/kern/kern_rctl.c
Show First 20 Lines • Show All 215 Lines • ▼ Show 20 Lines | |||||
static void rctl_init(void); | static void rctl_init(void); | ||||
SYSINIT(rctl, SI_SUB_RACCT, SI_ORDER_FIRST, rctl_init, NULL); | SYSINIT(rctl, SI_SUB_RACCT, SI_ORDER_FIRST, rctl_init, NULL); | ||||
static uma_zone_t rctl_rule_zone; | static uma_zone_t rctl_rule_zone; | ||||
static uma_zone_t rctl_rule_link_zone; | static uma_zone_t rctl_rule_link_zone; | ||||
static int rctl_rule_fully_specified(const struct rctl_rule *rule); | static int rctl_rule_fully_specified(const struct rctl_rule *rule); | ||||
static void rctl_rule_to_sbuf(struct sbuf *sb, const struct rctl_rule *rule); | static void rctl_rule_to_sbuf(struct sbuf *sb, const struct rctl_rule *rule); | ||||
static int rctl_enforce_racct(struct racct *racct, int resource, uint64_t amount, | |||||
struct ucred *cred); | |||||
static MALLOC_DEFINE(M_RCTL, "rctl", "Resource Limits"); | static MALLOC_DEFINE(M_RCTL, "rctl", "Resource Limits"); | ||||
static int rctl_throttle_min_sysctl(SYSCTL_HANDLER_ARGS) | static int rctl_throttle_min_sysctl(SYSCTL_HANDLER_ARGS) | ||||
{ | { | ||||
int error, val = rctl_throttle_min; | int error, val = rctl_throttle_min; | ||||
error = sysctl_handle_int(oidp, &val, 0, req); | error = sysctl_handle_int(oidp, &val, 0, req); | ||||
▲ Show 20 Lines • Show All 95 Lines • ▼ Show 20 Lines | for (i = 0; resourcenames[i].d_name != NULL; i++) { | ||||
if (resourcenames[i].d_value == resource) | if (resourcenames[i].d_value == resource) | ||||
return (resourcenames[i].d_name); | return (resourcenames[i].d_name); | ||||
} | } | ||||
panic("rctl_resource_name: unknown resource %d", resource); | panic("rctl_resource_name: unknown resource %d", resource); | ||||
} | } | ||||
static struct racct * | static struct racct * | ||||
rctl_proc_rule_to_racct(const struct proc *p, const struct rctl_rule *rule) | rctl_proc_rule_to_racct_cred(const struct ucred *cred, | ||||
const struct rctl_rule *rule) | |||||
{ | { | ||||
struct ucred *cred = p->p_ucred; | KASSERT(rule->rr_per != RCTL_SUBJECT_TYPE_PROCESS, | ||||
("rctl_proc_rule_to_racct_cred: cannot get process racct")); | |||||
ASSERT_RACCT_ENABLED(); | |||||
RACCT_LOCK_ASSERT(); | |||||
switch (rule->rr_per) { | switch (rule->rr_per) { | ||||
case RCTL_SUBJECT_TYPE_PROCESS: | |||||
return (p->p_racct); | |||||
case RCTL_SUBJECT_TYPE_USER: | case RCTL_SUBJECT_TYPE_USER: | ||||
return (cred->cr_ruidinfo->ui_racct); | return (cred->cr_ruidinfo->ui_racct); | ||||
case RCTL_SUBJECT_TYPE_LOGINCLASS: | case RCTL_SUBJECT_TYPE_LOGINCLASS: | ||||
return (cred->cr_loginclass->lc_racct); | return (cred->cr_loginclass->lc_racct); | ||||
case RCTL_SUBJECT_TYPE_JAIL: | case RCTL_SUBJECT_TYPE_JAIL: | ||||
return (cred->cr_prison->pr_prison_racct->prr_racct); | return (cred->cr_prison->pr_prison_racct->prr_racct); | ||||
default: | default: | ||||
panic("%s: unknown per %d", __func__, rule->rr_per); | panic("%s: unknown per %d", __func__, rule->rr_per); | ||||
} | } | ||||
} | } | ||||
static struct racct * | |||||
rctl_proc_rule_to_racct(const struct proc *p, const struct rctl_rule *rule) | |||||
{ | |||||
struct ucred *cred = p->p_ucred; | |||||
ASSERT_RACCT_ENABLED(); | |||||
RACCT_LOCK_ASSERT(); | |||||
switch (rule->rr_per) { | |||||
case RCTL_SUBJECT_TYPE_PROCESS: | |||||
return (p->p_racct); | |||||
default: | |||||
return (rctl_proc_rule_to_racct_cred(cred, rule)); | |||||
} | |||||
} | |||||
/* | /* | ||||
* Return the amount of resource that can be allocated by 'p' before | * Return the amount of resource that can be allocated by 'p' before | ||||
* hitting 'rule'. | * hitting 'rule'. | ||||
*/ | */ | ||||
static int64_t | static int64_t | ||||
rctl_available_resource(const struct proc *p, const struct rctl_rule *rule) | rctl_available_resource(const struct proc *p, const struct rctl_rule *rule) | ||||
{ | { | ||||
const struct racct *racct; | const struct racct *racct; | ||||
int64_t available; | int64_t available; | ||||
ASSERT_RACCT_ENABLED(); | ASSERT_RACCT_ENABLED(); | ||||
RACCT_LOCK_ASSERT(); | RACCT_LOCK_ASSERT(); | ||||
racct = rctl_proc_rule_to_racct(p, rule); | racct = rctl_proc_rule_to_racct(p, rule); | ||||
available = rule->rr_amount - racct->r_resources[rule->rr_resource]; | available = rule->rr_amount - racct->r_resources[rule->rr_resource]; | ||||
return (available); | return (available); | ||||
} | } | ||||
/* | /* | ||||
* Return the amount of resource that can be allocated by 'cred' before | |||||
* hitting 'rule'. | |||||
*/ | |||||
static int64_t | |||||
rctl_available_resource_cred(const struct ucred *cred, | |||||
const struct rctl_rule *rule) | |||||
{ | |||||
const struct racct *racct; | |||||
int64_t available; | |||||
ASSERT_RACCT_ENABLED(); | |||||
RACCT_LOCK_ASSERT(); | |||||
racct = rctl_proc_rule_to_racct_cred(cred, rule); | |||||
available = rule->rr_amount - racct->r_resources[rule->rr_resource]; | |||||
return (available); | |||||
} | |||||
/* | |||||
* Called every second for proc, uidinfo, loginclass, and jail containers. | * Called every second for proc, uidinfo, loginclass, and jail containers. | ||||
* If the limit isn't exceeded, it decreases the usage amount to zero. | * If the limit isn't exceeded, it decreases the usage amount to zero. | ||||
* Otherwise, it decreases it by the value of the limit. This way | * Otherwise, it decreases it by the value of the limit. This way | ||||
* resource consumption exceeding the limit "carries over" to the next | * resource consumption exceeding the limit "carries over" to the next | ||||
* period. | * period. | ||||
*/ | */ | ||||
void | void | ||||
rctl_throttle_decay(struct racct *racct, int resource) | rctl_throttle_decay(struct racct *racct, int resource) | ||||
▲ Show 20 Lines • Show All 98 Lines • ▼ Show 20 Lines | |||||
static uint64_t | static uint64_t | ||||
xmul(uint64_t a, uint64_t b) | xmul(uint64_t a, uint64_t b) | ||||
{ | { | ||||
if (b != 0 && a > UINT64_MAX / b) | if (b != 0 && a > UINT64_MAX / b) | ||||
return (UINT64_MAX); | return (UINT64_MAX); | ||||
return (a * b); | return (a * b); | ||||
} | |||||
/* | |||||
* Check whether the credential 'cred' can allocate 'amount' of 'resource' in | |||||
* addition to what it keeps allocated now. Returns non-zero if the allocation | |||||
* should be denied, 0 otherwise. Does not enforce rules whose actions require | |||||
* a process, i.e., throttle and sig*. | |||||
*/ | |||||
int | |||||
rctl_enforce_cred(struct ucred *cred, int resource, uint64_t amount) | |||||
{ | |||||
int error = 0; | |||||
error |= rctl_enforce_racct(cred->cr_ruidinfo->ui_racct, | |||||
resource, amount, cred); | |||||
error |= rctl_enforce_racct(cred->cr_loginclass->lc_racct, | |||||
resource, amount, cred); | |||||
error |= rctl_enforce_racct(cred->cr_prison->pr_prison_racct->prr_racct, | |||||
resource, amount, cred); | |||||
return (error); | |||||
} | |||||
static int | |||||
rctl_enforce_racct(struct racct *racct, int resource, uint64_t amount, struct ucred *cred) { | |||||
markj: Style: the opening brace should be on its own line. | |||||
static struct timeval log_lasttime, devctl_lasttime; | |||||
static int log_curtime = 0, devctl_curtime = 0; | |||||
struct rctl_rule *rule; | |||||
struct rctl_rule_link *link; | |||||
struct sbuf sb; | |||||
char *buf; | |||||
int64_t available; | |||||
int should_deny = 0; | |||||
ASSERT_RACCT_ENABLED(); | |||||
RACCT_LOCK_ASSERT(); | |||||
/* | |||||
* There may be more than one matching rule; go through all of them. | |||||
* Denial should be done last, after logging and sending signals. | |||||
*/ | |||||
LIST_FOREACH(link, &racct->r_rule_links, rrl_next) { | |||||
rule = link->rrl_rule; | |||||
if (rule->rr_resource != resource) | |||||
continue; | |||||
if (rule->rr_per == RCTL_SUBJECT_TYPE_PROCESS) | |||||
continue; | |||||
available = rctl_available_resource_cred(cred, rule); | |||||
if (available >= (int64_t)amount) { | |||||
link->rrl_exceeded = 0; | |||||
continue; | |||||
} | |||||
switch (rule->rr_action) { | |||||
case RCTL_ACTION_DENY: | |||||
should_deny = 1; | |||||
continue; | |||||
case RCTL_ACTION_LOG: | |||||
/* | |||||
* If rrl_exceeded != 0, it means we've already | |||||
* logged a warning for this process. | |||||
*/ | |||||
if (link->rrl_exceeded != 0) | |||||
continue; | |||||
if (!ppsratecheck(&log_lasttime, &log_curtime, | |||||
rctl_log_rate_limit)) | |||||
continue; | |||||
buf = malloc(RCTL_LOG_BUFSIZE, M_RCTL, M_NOWAIT); | |||||
if (buf == NULL) { | |||||
printf("rctl_enforce_racct: out of memory\n"); | |||||
continue; | |||||
} | |||||
sbuf_new(&sb, buf, RCTL_LOG_BUFSIZE, SBUF_FIXEDLEN); | |||||
rctl_rule_to_sbuf(&sb, rule); | |||||
sbuf_finish(&sb); | |||||
printf("rctl: rule \"%s\" matched by uid %d, jail %s\n", | |||||
sbuf_data(&sb), cred->cr_uid, | |||||
cred->cr_prison->pr_prison_racct->prr_name); | |||||
sbuf_delete(&sb); | |||||
free(buf, M_RCTL); | |||||
link->rrl_exceeded = 1; | |||||
continue; | |||||
markjUnsubmitted Not Done Inline ActionsCan we factor the individual handlers out? I see that for process-based rctl we have an extra check of p_state, but we can make that conditional, e.g., by specifying a NULL process pointer when enforcing limits by cred. markj: Can we factor the individual handlers out? I see that for process-based rctl we have an extra… | |||||
case RCTL_ACTION_DEVCTL: | |||||
if (link->rrl_exceeded != 0) | |||||
continue; | |||||
if (!ppsratecheck(&devctl_lasttime, &devctl_curtime, | |||||
rctl_devctl_rate_limit)) | |||||
continue; | |||||
buf = malloc(RCTL_LOG_BUFSIZE, M_RCTL, M_NOWAIT); | |||||
if (buf == NULL) { | |||||
printf("rctl_enforce_racct: out of memory\n"); | |||||
continue; | |||||
} | |||||
sbuf_new(&sb, buf, RCTL_LOG_BUFSIZE, SBUF_FIXEDLEN); | |||||
sbuf_printf(&sb, "rule="); | |||||
rctl_rule_to_sbuf(&sb, rule); | |||||
sbuf_printf(&sb, " ruid=%d jail=%s", cred->cr_ruid, | |||||
cred->cr_prison->pr_prison_racct->prr_name); | |||||
sbuf_finish(&sb); | |||||
devctl_notify("RCTL", "rule", "matched", | |||||
sbuf_data(&sb)); | |||||
sbuf_delete(&sb); | |||||
free(buf, M_RCTL); | |||||
link->rrl_exceeded = 1; | |||||
continue; | |||||
case RCTL_ACTION_THROTTLE: | |||||
continue; | |||||
default: | |||||
continue; | |||||
} | |||||
} | |||||
if (should_deny) { | |||||
/* | |||||
* Return fake error code; the caller should change it | |||||
* into one proper for the situation - EFSIZ, ENOMEM etc. | |||||
*/ | |||||
return (EDOOFUS); | |||||
} | |||||
return (0); | |||||
} | } | ||||
/* | /* | ||||
* Check whether the proc 'p' can allocate 'amount' of 'resource' in addition | * Check whether the proc 'p' can allocate 'amount' of 'resource' in addition | ||||
* to what it keeps allocated now. Returns non-zero if the allocation should | * to what it keeps allocated now. Returns non-zero if the allocation should | ||||
* be denied, 0 otherwise. | * be denied, 0 otherwise. | ||||
*/ | */ | ||||
int | int | ||||
▲ Show 20 Lines • Show All 1,750 Lines • Show Last 20 Lines |
Style: the opening brace should be on its own line.