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 101 Lines • ▼ Show 20 Lines | 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 proc 'p' can allocate 'amount' of 'resource' in addition | * Check whether the credential 'cred' can allocate 'amount' of 'resource' in | ||||
* to what it keeps allocated now. Returns non-zero if the allocation should | * addition to what it keeps allocated now. Returns non-zero if the allocation | ||||
* be denied, 0 otherwise. | * should be denied, 0 otherwise. Does not enforce rules whose actions require | ||||
* a process, i.e., throttle and sig*. | |||||
*/ | */ | ||||
int | int | ||||
rctl_enforce(struct proc *p, int resource, uint64_t amount) | rctl_enforce_cred(struct ucred *cred, int resource, uint64_t amount) | ||||
{ | { | ||||
static struct timeval log_lasttime, devctl_lasttime; | int error = 0; | ||||
static int log_curtime = 0, devctl_curtime = 0; | error |= rctl_enforce_racct(cred->cr_ruidinfo->ui_racct, | ||||
struct rctl_rule *rule; | resource, amount, cred); | ||||
struct rctl_rule_link *link; | 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 void | |||||
rctl_log_handler(struct rctl_rule *rule, struct proc *p, struct ucred *cred) | |||||
markj: Style: the opening brace should be on its own line. | |||||
{ | |||||
static struct timeval log_lasttime; | |||||
static int log_curtime = 0; | |||||
struct sbuf sb; | struct sbuf sb; | ||||
char *buf; | char *buf; | ||||
int64_t available; | |||||
uint64_t sleep_ms, sleep_ratio; | |||||
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, &p->p_racct->r_rule_links, rrl_next) { | |||||
rule = link->rrl_rule; | |||||
if (rule->rr_resource != resource) | |||||
continue; | |||||
available = rctl_available_resource(p, 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 the process state is not fully initialized yet, | |||||
* we can't access most of the required fields, e.g. | |||||
* p->p_comm. This happens when called from fork1(). | |||||
* Ignore this rule for now; it will be processed just | |||||
* after fork, when called from racct_proc_fork_done(). | |||||
*/ | |||||
if (p->p_state != PRS_NORMAL) | |||||
continue; | |||||
if (!ppsratecheck(&log_lasttime, &log_curtime, | if (!ppsratecheck(&log_lasttime, &log_curtime, | ||||
rctl_log_rate_limit)) | rctl_log_rate_limit)) | ||||
continue; | return; | ||||
if (p) { | |||||
cred = p->p_ucred; | |||||
} | |||||
buf = malloc(RCTL_LOG_BUFSIZE, M_RCTL, M_NOWAIT); | buf = malloc(RCTL_LOG_BUFSIZE, M_RCTL, M_NOWAIT); | ||||
if (buf == NULL) { | if (buf == NULL) { | ||||
printf("rctl_enforce: out of memory\n"); | printf("rctl_enforce_racct: out of memory\n"); | ||||
continue; | return; | ||||
} | } | ||||
sbuf_new(&sb, buf, RCTL_LOG_BUFSIZE, SBUF_FIXEDLEN); | sbuf_new(&sb, buf, RCTL_LOG_BUFSIZE, SBUF_FIXEDLEN); | ||||
rctl_rule_to_sbuf(&sb, rule); | rctl_rule_to_sbuf(&sb, rule); | ||||
sbuf_finish(&sb); | sbuf_finish(&sb); | ||||
if (p) { | |||||
printf("rctl: rule \"%s\" matched by pid %d " | printf("rctl: rule \"%s\" matched by pid %d " | ||||
"(%s), uid %d, jail %s\n", sbuf_data(&sb), | "(%s), uid %d, jail %s\n", sbuf_data(&sb), | ||||
p->p_pid, p->p_comm, p->p_ucred->cr_uid, | p->p_pid, p->p_comm, p->p_ucred->cr_uid, | ||||
p->p_ucred->cr_prison->pr_prison_racct->prr_name); | p->p_ucred->cr_prison->pr_prison_racct->prr_name); | ||||
} | |||||
else { | |||||
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); | sbuf_delete(&sb); | ||||
free(buf, M_RCTL); | free(buf, M_RCTL); | ||||
link->rrl_exceeded = 1; | } | ||||
continue; | |||||
case RCTL_ACTION_DEVCTL: | |||||
if (link->rrl_exceeded != 0) | |||||
continue; | |||||
if (p->p_state != PRS_NORMAL) | static void | ||||
continue; | rctl_devctl_handler(struct rctl_rule *rule, struct proc *p, struct ucred *cred) | ||||
{ | |||||
static struct timeval devctl_lasttime; | |||||
static int devctl_curtime = 0; | |||||
struct sbuf sb; | |||||
char *buf; | |||||
if (!ppsratecheck(&devctl_lasttime, &devctl_curtime, | if (!ppsratecheck(&devctl_lasttime, &devctl_curtime, | ||||
rctl_devctl_rate_limit)) | rctl_devctl_rate_limit)) | ||||
continue; | return; | ||||
if (p) { | |||||
cred = p->p_ucred; | |||||
} | |||||
buf = malloc(RCTL_LOG_BUFSIZE, M_RCTL, M_NOWAIT); | buf = malloc(RCTL_LOG_BUFSIZE, M_RCTL, M_NOWAIT); | ||||
if (buf == NULL) { | if (buf == NULL) { | ||||
printf("rctl_enforce: out of memory\n"); | printf("rctl_enforce_racct: out of memory\n"); | ||||
continue; | return; | ||||
} | } | ||||
sbuf_new(&sb, buf, RCTL_LOG_BUFSIZE, SBUF_FIXEDLEN); | sbuf_new(&sb, buf, RCTL_LOG_BUFSIZE, SBUF_FIXEDLEN); | ||||
sbuf_printf(&sb, "rule="); | sbuf_printf(&sb, "rule="); | ||||
rctl_rule_to_sbuf(&sb, rule); | rctl_rule_to_sbuf(&sb, rule); | ||||
if (p) { | |||||
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… | |||||
sbuf_printf(&sb, " pid=%d ruid=%d jail=%s", | sbuf_printf(&sb, " pid=%d ruid=%d jail=%s", | ||||
p->p_pid, p->p_ucred->cr_ruid, | p->p_pid, p->p_ucred->cr_ruid, | ||||
p->p_ucred->cr_prison->pr_prison_racct->prr_name); | p->p_ucred->cr_prison->pr_prison_racct->prr_name); | ||||
} | |||||
else { | |||||
sbuf_printf(&sb, " ruid=%d jail=%s", cred->cr_ruid, | |||||
cred->cr_prison->pr_prison_racct->prr_name); | |||||
} | |||||
sbuf_finish(&sb); | sbuf_finish(&sb); | ||||
devctl_notify("RCTL", "rule", "matched", | devctl_notify("RCTL", "rule", "matched", | ||||
sbuf_data(&sb)); | sbuf_data(&sb)); | ||||
sbuf_delete(&sb); | sbuf_delete(&sb); | ||||
free(buf, M_RCTL); | free(buf, M_RCTL); | ||||
link->rrl_exceeded = 1; | } | ||||
continue; | |||||
case RCTL_ACTION_THROTTLE: | |||||
if (p->p_state != PRS_NORMAL) | |||||
continue; | |||||
static void | |||||
rctl_throttle_handler(struct rctl_rule *rule, int resource, struct proc * p) | |||||
{ | |||||
uint64_t sleep_ms, sleep_ratio; | |||||
int64_t available; | |||||
if (rule->rr_amount == 0) { | if (rule->rr_amount == 0) { | ||||
racct_proc_throttle(p, rctl_throttle_max); | racct_proc_throttle(p, rctl_throttle_max); | ||||
continue; | return; | ||||
} | } | ||||
/* | /* | ||||
* Make the process sleep for a fraction of second | * Make the process sleep for a fraction of second | ||||
* proportional to the ratio of process' resource | * proportional to the ratio of process' resource | ||||
* utilization compared to the limit. The point is | * utilization compared to the limit. The point is | ||||
* to penalize resource hogs: processes that consume | * to penalize resource hogs: processes that consume | ||||
* more of the available resources sleep for longer. | * more of the available resources sleep for longer. | ||||
* | * | ||||
* We're trying to defer division until the very end, | * We're trying to defer division until the very end, | ||||
* to minimize the rounding effects. The following | * to minimize the rounding effects. The following | ||||
* calculation could have been written in a clearer | * calculation could have been written in a clearer | ||||
* way like this: | * way like this: | ||||
* | * | ||||
* sleep_ms = hz * p->p_racct->r_resources[resource] / | * sleep_ms = hz * p->p_racct->r_resources[resource] / | ||||
* rule->rr_amount; | * rule->rr_amount; | ||||
* sleep_ms *= rctl_throttle_pct / 100; | * sleep_ms *= rctl_throttle_pct / 100; | ||||
* if (sleep_ms < rctl_throttle_min) | * if (sleep_ms < rctl_throttle_min) | ||||
* sleep_ms = rctl_throttle_min; | * sleep_ms = rctl_throttle_min; | ||||
* | * | ||||
*/ | */ | ||||
sleep_ms = xmul(hz, p->p_racct->r_resources[resource]); | sleep_ms = xmul(hz, p->p_racct->r_resources[resource]); | ||||
sleep_ms = xmul(sleep_ms, rctl_throttle_pct) / 100; | sleep_ms = xmul(sleep_ms, rctl_throttle_pct) / 100; | ||||
if (sleep_ms < rctl_throttle_min * rule->rr_amount) | if (sleep_ms < rctl_throttle_min * rule->rr_amount) | ||||
sleep_ms = rctl_throttle_min * rule->rr_amount; | sleep_ms = rctl_throttle_min * rule->rr_amount; | ||||
/* | /* | ||||
* Multiply that by the ratio of the resource | * Multiply that by the ratio of the resource | ||||
* consumption for the container compared to the limit, | * consumption for the container compared to the limit, | ||||
* squared. In other words, a process in a container | * squared. In other words, a process in a container | ||||
* that is two times over the limit will be throttled | * that is two times over the limit will be throttled | ||||
* four times as much for hitting the same rule. The | * four times as much for hitting the same rule. The | ||||
* point is to penalize processes more if the container | * point is to penalize processes more if the container | ||||
* itself (eg certain UID or jail) is above the limit. | * itself (eg certain UID or jail) is above the limit. | ||||
*/ | */ | ||||
available = rctl_available_resource(p, rule); | |||||
if (available < 0) | if (available < 0) | ||||
sleep_ratio = -available / rule->rr_amount; | sleep_ratio = -available / rule->rr_amount; | ||||
else | else | ||||
sleep_ratio = 0; | sleep_ratio = 0; | ||||
sleep_ratio = xmul(sleep_ratio, sleep_ratio); | sleep_ratio = xmul(sleep_ratio, sleep_ratio); | ||||
sleep_ratio = xmul(sleep_ratio, rctl_throttle_pct2) / 100; | sleep_ratio = xmul(sleep_ratio, rctl_throttle_pct2) / 100; | ||||
sleep_ms = xadd(sleep_ms, xmul(sleep_ms, sleep_ratio)); | sleep_ms = xadd(sleep_ms, xmul(sleep_ms, sleep_ratio)); | ||||
/* | /* | ||||
* Finally the division. | * Finally the division. | ||||
*/ | */ | ||||
sleep_ms /= rule->rr_amount; | sleep_ms /= rule->rr_amount; | ||||
if (sleep_ms > rctl_throttle_max) | if (sleep_ms > rctl_throttle_max) | ||||
sleep_ms = rctl_throttle_max; | sleep_ms = rctl_throttle_max; | ||||
#if 0 | #if 0 | ||||
printf("%s: pid %d (%s), %jd of %jd, will sleep for %ju ms (ratio %ju, available %jd)\n", | printf("%s: pid %d (%s), %jd of %jd, will sleep for %ju ms (ratio %ju, available %jd)\n", | ||||
__func__, p->p_pid, p->p_comm, | __func__, p->p_pid, p->p_comm, | ||||
p->p_racct->r_resources[resource], | p->p_racct->r_resources[resource], | ||||
rule->rr_amount, (uintmax_t)sleep_ms, | rule->rr_amount, (uintmax_t)sleep_ms, | ||||
(uintmax_t)sleep_ratio, (intmax_t)available); | (uintmax_t)sleep_ratio, (intmax_t)available); | ||||
#endif | #endif | ||||
KASSERT(sleep_ms >= rctl_throttle_min, ("%s: %ju < %d\n", | KASSERT(sleep_ms >= rctl_throttle_min, ("%s: %ju < %d\n", | ||||
__func__, (uintmax_t)sleep_ms, rctl_throttle_min)); | __func__, (uintmax_t)sleep_ms, rctl_throttle_min)); | ||||
racct_proc_throttle(p, sleep_ms); | racct_proc_throttle(p, sleep_ms); | ||||
continue; | } | ||||
default: | |||||
if (link->rrl_exceeded != 0) | |||||
continue; | |||||
if (p->p_state != PRS_NORMAL) | static void | ||||
continue; | rctl_sig_handler(struct rctl_rule *rule, struct proc *p) | ||||
{ | |||||
KASSERT(rule->rr_action > 0 && | KASSERT(rule->rr_action > 0 && | ||||
rule->rr_action <= RCTL_ACTION_SIGNAL_MAX, | rule->rr_action <= RCTL_ACTION_SIGNAL_MAX, | ||||
("rctl_enforce: unknown action %d", | ("rctl_sig: unknown action %d", | ||||
rule->rr_action)); | rule->rr_action)); | ||||
/* | /* | ||||
* We're using the fact that RCTL_ACTION_SIG* values | * We're using the fact that RCTL_ACTION_SIG* values | ||||
* are equal to their counterparts from sys/signal.h. | * are equal to their counterparts from sys/signal.h. | ||||
*/ | */ | ||||
kern_psignal(p, rule->rr_action); | kern_psignal(p, rule->rr_action); | ||||
} | |||||
static int | |||||
rctl_enforce_racct(struct racct *racct, int resource, uint64_t amount, struct ucred *cred) | |||||
{ | |||||
struct rctl_rule *rule; | |||||
struct rctl_rule_link *link; | |||||
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; | |||||
rctl_log_handler(rule, NULL, cred); | |||||
link->rrl_exceeded = 1; | |||||
continue; | |||||
case RCTL_ACTION_DEVCTL: | |||||
if (link->rrl_exceeded != 0) | |||||
continue; | |||||
rctl_devctl_handler(rule, NULL, cred); | |||||
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 | |||||
* to what it keeps allocated now. Returns non-zero if the allocation should | |||||
* be denied, 0 otherwise. | |||||
*/ | |||||
int | |||||
rctl_enforce(struct proc *p, int resource, uint64_t amount) | |||||
{ | |||||
struct rctl_rule *rule; | |||||
struct rctl_rule_link *link; | |||||
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, &p->p_racct->r_rule_links, rrl_next) { | |||||
rule = link->rrl_rule; | |||||
if (rule->rr_resource != resource) | |||||
continue; | |||||
available = rctl_available_resource(p, 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 the process state is not fully initialized yet, | |||||
* we can't access most of the required fields, e.g. | |||||
* p->p_comm. This happens when called from fork1(). | |||||
* Ignore this rule for now; it will be processed just | |||||
* after fork, when called from racct_proc_fork_done(). | |||||
*/ | |||||
if (p->p_state != PRS_NORMAL) | |||||
continue; | |||||
rctl_log_handler(rule, p, NULL); | |||||
link->rrl_exceeded = 1; | |||||
continue; | |||||
case RCTL_ACTION_DEVCTL: | |||||
if (link->rrl_exceeded != 0) | |||||
continue; | |||||
if (p->p_state != PRS_NORMAL) | |||||
continue; | |||||
rctl_devctl_handler(rule, p, NULL); | |||||
link->rrl_exceeded = 1; | |||||
continue; | |||||
case RCTL_ACTION_THROTTLE: | |||||
if (p->p_state != PRS_NORMAL) | |||||
continue; | |||||
rctl_throttle_handler(rule, resource, p); | |||||
continue; | |||||
default: | |||||
if (link->rrl_exceeded != 0) | |||||
continue; | |||||
if (p->p_state != PRS_NORMAL) | |||||
continue; | |||||
rctl_sig_handler(rule, p); | |||||
link->rrl_exceeded = 1; | link->rrl_exceeded = 1; | ||||
continue; | continue; | ||||
} | } | ||||
} | } | ||||
if (should_deny) { | if (should_deny) { | ||||
/* | /* | ||||
* Return fake error code; the caller should change it | * Return fake error code; the caller should change it | ||||
▲ Show 20 Lines • Show All 1,555 Lines • Show Last 20 Lines |
Style: the opening brace should be on its own line.