From owner-p4-projects@FreeBSD.ORG Wed Jul 8 15:29:57 2009 Return-Path: Delivered-To: p4-projects@freebsd.org Received: by hub.freebsd.org (Postfix, from userid 32767) id 18A391065673; Wed, 8 Jul 2009 15:29:57 +0000 (UTC) Delivered-To: perforce@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id CC4271065672 for ; Wed, 8 Jul 2009 15:29:56 +0000 (UTC) (envelope-from trasz@freebsd.org) Received: from repoman.freebsd.org (repoman.freebsd.org [IPv6:2001:4f8:fff6::29]) by mx1.freebsd.org (Postfix) with ESMTP id AE8D88FC1C for ; Wed, 8 Jul 2009 15:29:56 +0000 (UTC) (envelope-from trasz@freebsd.org) Received: from repoman.freebsd.org (localhost [127.0.0.1]) by repoman.freebsd.org (8.14.3/8.14.3) with ESMTP id n68FTu8b057555 for ; Wed, 8 Jul 2009 15:29:56 GMT (envelope-from trasz@freebsd.org) Received: (from perforce@localhost) by repoman.freebsd.org (8.14.3/8.14.3/Submit) id n68FTuBH057553 for perforce@freebsd.org; Wed, 8 Jul 2009 15:29:56 GMT (envelope-from trasz@freebsd.org) Date: Wed, 8 Jul 2009 15:29:56 GMT Message-Id: <200907081529.n68FTuBH057553@repoman.freebsd.org> X-Authentication-Warning: repoman.freebsd.org: perforce set sender to trasz@freebsd.org using -f From: Edward Tomasz Napierala To: Perforce Change Reviews Cc: Subject: PERFORCE change 165824 for review X-BeenThere: p4-projects@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: p4 projects tree changes List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 08 Jul 2009 15:29:57 -0000 http://perforce.freebsd.org/chv.cgi?CH=165824 Change 165824 by trasz@trasz_victim on 2009/07/08 15:29:43 Add a mechanism to get from the given process to the applicable resource limits. Affected files ... .. //depot/projects/soc2009/trasz_limits/sys/kern/kern_hrl.c#27 edit .. //depot/projects/soc2009/trasz_limits/sys/sys/hrl.h#20 edit .. //depot/projects/soc2009/trasz_limits/sys/sys/jail.h#7 edit .. //depot/projects/soc2009/trasz_limits/sys/sys/proc.h#7 edit .. //depot/projects/soc2009/trasz_limits/sys/sys/resourcevar.h#8 edit Differences ... ==== //depot/projects/soc2009/trasz_limits/sys/kern/kern_hrl.c#27 (text+ko) ==== @@ -148,9 +148,13 @@ static void hrl_init(void); SYSINIT(hrl, SI_SUB_CPU, SI_ORDER_FIRST, hrl_init, NULL); -static uma_zone_t hrl_zone; +static uma_zone_t hrl_node_zone; +static uma_zone_t hrl_limit_zone; static struct mtx hrl_lock; +static void hrl_rule_remove_limits(struct hrl_rule *rule); +static void hrl_rule_add_limits(struct hrl_rule *rule); + MALLOC_DEFINE(M_HRL, "hrl", "Hierarchical Resource Limits"); #if 0 @@ -267,10 +271,18 @@ int i; mtx_lock(&hrl_lock); + /* + * Inherit resources from the parent. + */ for (i = 0; i < HRL_RESOURCE_MAX; i++) { if (parent->p_usage.hu_resources[i] != 0 && hrl_resource_inheritable(i)) hrl_allocated_proc(child, i, parent->p_usage.hu_resources[i]); } + + /* + * Assign rules appropriate for the process. + */ + LIST_INIT(&child->p_limits); mtx_unlock(&hrl_lock); } @@ -485,15 +497,17 @@ KASSERT(node != NULL, ("hrl_adjust: node removal failed")); } mtx_unlock(&hrl_lock); - if (node != NULL) - uma_zfree(hrl_zone, node); + if (node != NULL) { + hrl_rule_remove_limits(&node->hn_rule); + uma_zfree(hrl_node_zone, node); + } return; } /* * Adding a new limit or changing existing one. */ - node = uma_zalloc(hrl_zone, M_WAITOK); + node = uma_zalloc(hrl_node_zone, M_WAITOK); *node = searched; mtx_lock(&hrl_lock); existing = RB_INSERT(hrl_tree, &hrls, node); @@ -503,7 +517,9 @@ node->hn_rule.hr_amount = amount; mtx_unlock(&hrl_lock); if (existing != NULL) - uma_zfree(hrl_zone, node); + uma_zfree(hrl_node_zone, node); + else + hrl_rule_add_limits(&node->hn_rule); } void @@ -629,12 +645,185 @@ return (0); } +/* + * Add a new limit for a rule, if it's not already there. + */ +static void +hrl_limit_add_if_needed(struct hrl_limits_head *limits_head, struct hrl_rule *rule) +{ + struct hrl_limit *limit, *new_limit; + + new_limit = uma_zalloc(hrl_limit_zone, M_WAITOK); + mtx_lock(&hrl_lock); + LIST_FOREACH(limit, limits_head, hl_next) { + if (limit->hl_rule == rule) { + mtx_unlock(&hrl_lock); + uma_zfree(hrl_limit_zone, new_limit); + return; + } + } + new_limit->hl_rule = rule; + LIST_INSERT_HEAD(limits_head, new_limit, hl_next); + mtx_unlock(&hrl_lock); +} + +/* + * Remove a limit for a rule. + */ +static void +hrl_limit_remove(struct hrl_limits_head *limits_head, struct hrl_rule *rule) +{ + struct hrl_limit *limit, *limittmp; + + mtx_lock(&hrl_lock); + LIST_FOREACH_SAFE(limit, limits_head, hl_next, limittmp) { + if (limit->hl_rule == rule) { + LIST_REMOVE(limit, hl_next); + mtx_unlock(&hrl_lock); + uma_zfree(hrl_limit_zone, limit); + return; + } + } + mtx_unlock(&hrl_lock); +} + +/* + * Bind a rule to subjects to which it applies. + */ +static void +hrl_rule_add_limits(struct hrl_rule *rule) +{ + struct proc *p; + struct uidinfo *uip; + struct gidinfo *gip; + struct prison *pr; + + switch (rule->hr_subject) { + case HRL_SUBJECT_PROCESS: + /* + * The sx lock is to keep the process from going away. + */ + sx_slock(&proctree_lock); + p = pfind(rule->hr_subject_id); + if (p == NULL) { + sx_sunlock(&proctree_lock); + return; + } + + PROC_UNLOCK(p); + hrl_limit_add_if_needed(&p->p_limits, rule); + sx_sunlock(&proctree_lock); + return; + + case HRL_SUBJECT_USER: + uip = uifind_existing(rule->hr_subject_id); + if (uip == NULL) + return; + hrl_limit_add_if_needed(&uip->ui_limits, rule); + uifree(uip); + return; + + case HRL_SUBJECT_GROUP: + gip = gifind_existing(rule->hr_subject_id); + if (gip == NULL) + return; + hrl_limit_add_if_needed(&gip->gi_limits, rule); + gifree(gip); + return; + + case HRL_SUBJECT_LOGINCLASS: + panic("hrl_rule_add_limits: HRL_SUBJECT_LOGINCLASS unimplemented"); + return; + + case HRL_SUBJECT_JAIL: + sx_xlock(&allprison_lock); + pr = prison_find(rule->hr_subject_id); + if (pr == NULL) { + sx_xunlock(&allprison_lock); + return; + } + hrl_limit_add_if_needed(&pr->pr_limits, rule); + prison_free(pr); + sx_xunlock(&allprison_lock); + return; + + default: + panic("hrl_rule_add_limits: unknown subject %d", rule->hr_subject); + } +} + +static void +hrl_rule_remove_limits(struct hrl_rule *rule) +{ + struct proc *p; + struct uidinfo *uip; + struct gidinfo *gip; + struct prison *pr; + + switch (rule->hr_subject) { + case HRL_SUBJECT_PROCESS: + /* + * The sx lock is to keep the process from going away. + */ + sx_slock(&proctree_lock); + p = pfind(rule->hr_subject_id); + if (p == NULL) { + sx_sunlock(&proctree_lock); + return; + } + + PROC_UNLOCK(p); + hrl_limit_remove(&p->p_limits, rule); + sx_sunlock(&proctree_lock); + return; + + case HRL_SUBJECT_USER: + uip = uifind_existing(rule->hr_subject_id); + if (uip == NULL) + return; + hrl_limit_remove(&uip->ui_limits, rule); + uifree(uip); + return; + + case HRL_SUBJECT_GROUP: + gip = gifind_existing(rule->hr_subject_id); + if (gip == NULL) + return; + hrl_limit_remove(&gip->gi_limits, rule); + gifree(gip); + return; + + case HRL_SUBJECT_LOGINCLASS: + panic("hrl_rule_remove: HRL_SUBJECT_LOGINCLASS unimplemented"); + return; + + case HRL_SUBJECT_JAIL: + sx_xlock(&allprison_lock); + pr = prison_find(rule->hr_subject_id); + if (pr == NULL) { + sx_xunlock(&allprison_lock); + return; + } + hrl_limit_remove(&pr->pr_limits, rule); + prison_free(pr); + sx_xunlock(&allprison_lock); + return; + + default: + panic("hrl_rule_remove: unknown subject %d", rule->hr_subject); + } +} + +/* + * Add a rule to the ruleset or change the 'hr_amount' of existing + * rule. + */ static int hrl_rule_add(struct hrl_rule *rule) { struct hrl_node *node, *existing; - node = uma_zalloc(hrl_zone, M_WAITOK); + node = uma_zalloc(hrl_node_zone, M_WAITOK); node->hn_rule = *rule; mtx_lock(&hrl_lock); @@ -644,7 +833,9 @@ mtx_unlock(&hrl_lock); if (existing != NULL) - uma_zfree(hrl_zone, node); + uma_zfree(hrl_node_zone, node); + else + hrl_rule_add_limits(rule); return (0); } @@ -816,6 +1007,9 @@ return (error); } +/* + * Remove a rule from the ruleset. + */ static int hrl_rule_remove(struct hrl_rule *rule) { @@ -830,10 +1024,11 @@ continue; found = 1; node = RB_REMOVE(hrl_tree, &hrls, node); - KASSERT(node != NULL, ("hrl_proc_exit: node removal failed")); + KASSERT(node != NULL, ("hrl_rule_remove: node removal failed")); mtx_unlock(&hrl_lock); - uma_zfree(hrl_zone, node); + hrl_rule_remove_limits(&node->hn_rule); + uma_zfree(hrl_node_zone, node); goto restart; } mtx_unlock(&hrl_lock); @@ -1029,6 +1224,7 @@ { int i; struct hrl_node *node, *next; + struct hrl_limit *limit; struct uidinfo *uip; uip = p->p_ucred->cr_ruidinfo; @@ -1057,17 +1253,33 @@ KASSERT(node != NULL, ("hrl_proc_exit: node removal failed")); mtx_unlock(&hrl_lock); - uma_zfree(hrl_zone, node); + uma_zfree(hrl_node_zone, node); goto restart; } mtx_unlock(&hrl_lock); + + /* + * Free the hrl_limit structures. + */ +restart2: + mtx_lock(&hrl_lock); + while (!LIST_EMPTY(&p->p_limits)) { + limit = LIST_FIRST(&p->p_limits); + LIST_REMOVE(limit, hl_next); + mtx_unlock(&hrl_lock); + uma_zfree(hrl_limit_zone, limit); + goto restart2; + } + mtx_unlock(&hrl_lock); } static void hrl_init(void) { - hrl_zone = uma_zcreate("hrl", sizeof(struct hrl_node), NULL, NULL, + hrl_node_zone = uma_zcreate("hrl", sizeof(struct hrl_node), NULL, NULL, + NULL, NULL, UMA_ALIGN_PTR, UMA_ZONE_NOFREE); + hrl_limit_zone = uma_zcreate("hrl", sizeof(struct hrl_limit), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, UMA_ZONE_NOFREE); mtx_init(&hrl_lock, "hrl lock", NULL, MTX_RECURSE); /* XXX: Make it non-recurseable later. */ EVENTHANDLER_REGISTER(process_exit, hrl_proc_exit, NULL, EVENTHANDLER_PRI_ANY); ==== //depot/projects/soc2009/trasz_limits/sys/sys/hrl.h#20 (text+ko) ==== @@ -30,6 +30,7 @@ #define _HRL_H_ #include +#include #include /* @@ -112,6 +113,18 @@ int64_t hu_resources[HRL_RESOURCE_MAX + 1]; }; +/* + * 'hrl_limit' is used to link a subject with rules that apply + * to it. This way we don't have to search the whole HRL rules + * tree to enforce the limits. + */ +struct hrl_limit { + LIST_ENTRY(hrl_limit) hl_next; + struct hrl_rule *hl_rule; +}; + +LIST_HEAD(hrl_limits_head, hrl_limit); + #ifdef _KERNEL struct proc; ==== //depot/projects/soc2009/trasz_limits/sys/sys/jail.h#7 (text+ko) ==== @@ -178,6 +178,7 @@ char pr_domainname[MAXHOSTNAMELEN]; /* (p) jail domainname */ char pr_hostuuid[HOSTUUIDLEN]; /* (p) jail hostuuid */ struct hrl_usage pr_usage; /* (*) HRL resource accounting */ + struct hrl_limits_head pr_limits; /* (*) HRL rules applicable to the prison */ }; #endif /* _KERNEL || _WANT_PRISON */ ==== //depot/projects/soc2009/trasz_limits/sys/sys/proc.h#7 (text+ko) ==== @@ -515,6 +515,7 @@ int p_pendingcnt; /* how many signals are pending */ struct itimers *p_itimers; /* (c) POSIX interval timers. */ struct hrl_usage p_usage; /* (*) HRL resource accounting */ + struct hrl_limits_head p_limits;/* (*) HRL rules applicable to the proccess */ /* End area that is zeroed on creation. */ #define p_endzero p_magic ==== //depot/projects/soc2009/trasz_limits/sys/sys/resourcevar.h#8 (text+ko) ==== @@ -99,6 +99,7 @@ uid_t ui_uid; /* (a) uid */ u_int ui_ref; /* (b) reference count */ struct hrl_usage ui_usage; /* (*) HRL resource accounting */ + struct hrl_limits_head ui_limits;/* (*) HRL rules applicable to the uid */ }; #define UIDINFO_VMSIZE_LOCK(ui) mtx_lock(&((ui)->ui_vmsize_mtx)) @@ -117,6 +118,7 @@ gid_t gi_gid; /* (a) gid */ u_int gi_ref; /* (b) reference count */ struct hrl_usage gi_usage; /* (*) HRL resource accounting */ + struct hrl_limits_head gi_limits;/* (*) HRL rules applicable to the gid */ }; struct proc;