Index: conf/param.c =================================================================== RCS file: /cvsroot/src/sys/conf/param.c,v retrieving revision 1.63 diff -u -p -u -r1.63 param.c --- conf/param.c 8 Feb 2010 19:02:33 -0000 1.63 +++ conf/param.c 9 Jun 2012 01:39:29 -0000 @@ -58,6 +58,7 @@ __KERNEL_RCSID(0, "$NetBSD: param.c,v 1. #include #include #include +#include #ifdef SYSVSHM #include #include @@ -117,6 +118,7 @@ int tick = 1000000 / HZ; int tickadj = (240000 / (60 * HZ)) ? (240000 / (60 * HZ)) : 1; int rtc_offset = RTC_OFFSET; int maxproc = NPROC; +int maxlwp = MAXLWP; int desiredvnodes = NVNODE; u_int maxfiles = MAXFILES; int fscale = FSCALE; /* kernel uses `FSCALE', user uses `fscale' */ Index: kern/kern_lwp.c =================================================================== RCS file: /cvsroot/src/sys/kern/kern_lwp.c,v retrieving revision 1.168 diff -u -p -u -r1.168 kern_lwp.c --- kern/kern_lwp.c 13 Apr 2012 15:32:43 -0000 1.168 +++ kern/kern_lwp.c 9 Jun 2012 01:39:31 -0000 @@ -239,6 +239,8 @@ __KERNEL_RCSID(0, "$NetBSD: kern_lwp.c,v #include #include #include +#include +#include #include #include @@ -286,6 +288,44 @@ struct lwp lwp0 __aligned(MIN_LWP_ALIGNM .l_fd = &filedesc0, }; +static int sysctl_kern_maxlwp(SYSCTLFN_PROTO); + +/* + * sysctl helper routine for kern.maxlwp. Ensures that the new + * values are not too low or too high. + */ +static int +sysctl_kern_maxlwp(SYSCTLFN_ARGS) +{ + int error, nmaxlwp; + struct sysctlnode node; + + nmaxlwp = maxlwp; + node = *rnode; + node.sysctl_data = &nmaxlwp; + error = sysctl_lookup(SYSCTLFN_CALL(&node)); + if (error || newp == NULL) + return error; + + if (nmaxlwp < 0 || nmaxlwp >= 65536) + return EINVAL; + if (nmaxlwp > cpu_maxlwp()) + return EINVAL; + maxlwp = nmaxlwp; + + return 0; +} + +SYSCTL_SETUP(sysctl_lwp_setup, "sysctl kern lwp subtree setup") +{ + sysctl_createv(clog, 0, NULL, NULL, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, + CTLTYPE_INT, "maxlwp", + SYSCTL_DESCR("Maximum number of simultaneous threads"), + sysctl_kern_maxlwp, 0, NULL, 0, + CTL_KERN, CTL_CREATE, CTL_EOL); +} + void lwpinit(void) { @@ -295,6 +335,8 @@ lwpinit(void) lwp_sys_init(); lwp_cache = pool_cache_init(sizeof(lwp_t), MIN_LWP_ALIGNMENT, 0, 0, "lwppl", NULL, IPL_NONE, NULL, lwp_dtor, NULL); + + maxlwp = cpu_maxlwp(); } void @@ -677,6 +719,28 @@ lwp_create(lwp_t *l1, proc_t *p2, vaddr_ KASSERT(l1 == curlwp || l1->l_proc == &proc0); /* + * Enforce limits, excluding the first lwp and kthreads. + */ + if (p2->p_nlwps != 0 && p2 != &proc0) { + uid_t uid = kauth_cred_getuid(l1->l_cred); + int count = chglwpcnt(uid, 1); + if (__predict_false(count > + p2->p_rlimit[RLIMIT_NTHR].rlim_cur)) { + if (kauth_authorize_process(l1->l_cred, + KAUTH_PROCESS_RLIMIT, p2, + KAUTH_ARG(KAUTH_REQ_PROCESS_RLIMIT_BYPASS), + &p2->p_rlimit[RLIMIT_NTHR], KAUTH_ARG(RLIMIT_NTHR)) + != 0) { + if ((count = chglwpcnt(uid, -1)) < 0) + printf("%s, %d: %s, %d, %d\n", __FILE__, + __LINE__, l1->l_proc->p_comm, + l1->l_proc->p_pid, count); + // return EAGAIN; + } + } + } + + /* * First off, reap any detached LWP waiting to be collected. * We can re-use its LWP structure and turnstile. */ @@ -1041,6 +1105,10 @@ lwp_free(struct lwp *l, bool recycle, bo KASSERT(l != curlwp); KASSERT(last || mutex_owned(p->p_lock)); + if (p != &proc0 && p->p_nlwps != 1) + if (chglwpcnt(kauth_cred_getuid(l->l_cred), -1) < 0) + printf("%s, %d: %d, %s\n", __FILE__, __LINE__, + p->p_pid, p->p_comm); /* * If this was not the last LWP in the process, then adjust * counters and unlock. Index: kern/kern_proc.c =================================================================== RCS file: /cvsroot/src/sys/kern/kern_proc.c,v retrieving revision 1.185 diff -u -p -u -r1.185 kern_proc.c --- kern/kern_proc.c 6 Jun 2012 11:20:21 -0000 1.185 +++ kern/kern_proc.c 9 Jun 2012 01:39:32 -0000 @@ -459,6 +459,9 @@ proc0_init(void) rlim[RLIMIT_MEMLOCK].rlim_max = lim; rlim[RLIMIT_MEMLOCK].rlim_cur = lim / 3; + rlim[RLIMIT_NTHR].rlim_max = maxlwp; + rlim[RLIMIT_NTHR].rlim_cur = maxlwp < maxuprc ? maxlwp : maxuprc; + /* Note that default core name has zero length. */ limit0.pl_corename = defcorename; limit0.pl_cnlen = 0; Index: kern/kern_prot.c =================================================================== RCS file: /cvsroot/src/sys/kern/kern_prot.c,v retrieving revision 1.114 diff -u -p -u -r1.114 kern_prot.c --- kern/kern_prot.c 19 Mar 2012 06:04:19 -0000 1.114 +++ kern/kern_prot.c 9 Jun 2012 01:39:32 -0000 @@ -346,6 +346,14 @@ do_setresuid(struct lwp *l, uid_t r, uid /* Update count of processes for this user */ (void)chgproccnt(kauth_cred_getuid(ncred), -1); (void)chgproccnt(r, 1); + + /* The first lwp of a process is not counted */ + int nlwps = p->p_nlwps - 1; + if (chglwpcnt(kauth_cred_getuid(ncred), -nlwps) < 0) + printf("%s, %d: %d, %s %d\n", __FILE__, __LINE__, + p->p_pid, p->p_comm, nlwps); + (void)chglwpcnt(r, nlwps); + kauth_cred_setuid(ncred, r); } if (sv != -1) Index: kern/kern_resource.c =================================================================== RCS file: /cvsroot/src/sys/kern/kern_resource.c,v retrieving revision 1.168 diff -u -p -u -r1.168 kern_resource.c --- kern/kern_resource.c 2 Dec 2011 12:33:12 -0000 1.168 +++ kern/kern_resource.c 9 Jun 2012 01:39:32 -0000 @@ -439,6 +439,13 @@ dosetrlimit(struct lwp *l, struct proc * if (limp->rlim_max > maxproc) limp->rlim_max = maxproc; break; + + case RLIMIT_NTHR: + if (limp->rlim_cur > maxlwp) + limp->rlim_cur = maxlwp; + if (limp->rlim_max > maxlwp) + limp->rlim_max = maxlwp; + break; } mutex_enter(&p->p_limit->pl_lock); @@ -1082,6 +1089,7 @@ sysctl_proc_setup(void) create_proc_plimit("descriptors", PROC_PID_LIMIT_NOFILE); create_proc_plimit("sbsize", PROC_PID_LIMIT_SBSIZE); create_proc_plimit("vmemoryuse", PROC_PID_LIMIT_AS); + create_proc_plimit("maxlwp", PROC_PID_LIMIT_NTHR); #undef create_proc_plimit Index: kern/kern_uidinfo.c =================================================================== RCS file: /cvsroot/src/sys/kern/kern_uidinfo.c,v retrieving revision 1.5 diff -u -p -u -r1.5 kern_uidinfo.c --- kern/kern_uidinfo.c 22 Mar 2009 00:49:13 -0000 1.5 +++ kern/kern_uidinfo.c 9 Jun 2012 01:39:32 -0000 @@ -43,6 +43,8 @@ __KERNEL_RCSID(0, "$NetBSD: kern_uidinfo #include #include #include +#include +#include #include static SLIST_HEAD(uihashhead, uidinfo) *uihashtbl; @@ -50,6 +52,79 @@ static u_long uihash; #define UIHASH(uid) (&uihashtbl[(uid) & uihash]) +static int +sysctl_kern_uidinfo_cnt(SYSCTLFN_ARGS) +{ + static const struct { + const char *name; + u_int value; + } nv[] = { +#define _MEM(n) { # n, offsetof(struct uidinfo, ui_ ## n) } + _MEM(proccnt), + _MEM(lwpcnt), + _MEM(lockcnt), + _MEM(sbsize), +#undef _MEM + }; + + for (size_t i = 0; i < __arraycount(nv); i++) + if (strcmp(nv[i].name, rnode->sysctl_name) == 0) { + uint64_t cnt; + struct sysctlnode node = *rnode; + struct uidinfo *uip; + + node.sysctl_data = &cnt; + uip = uid_find(kauth_cred_geteuid(l->l_cred)); + + *(uint64_t *)node.sysctl_data = + *(u_long *)((char *)uip + nv[i].value); + + return sysctl_lookup(SYSCTLFN_CALL(&node)); + } + + return EINVAL; +} + +static void +sysctl_kern_uidinfo_setup(void) +{ + const struct sysctlnode *rnode, *cnode; + struct sysctllog *kern_uidinfo_sysctllog; + + kern_uidinfo_sysctllog = NULL; + sysctl_createv(&kern_uidinfo_sysctllog, 0, NULL, &rnode, + CTLFLAG_PERMANENT, + CTLTYPE_NODE, "uidinfo", + SYSCTL_DESCR("Resource usage per uid"), + NULL, 0, NULL, 0, + CTL_KERN, CTL_CREATE, CTL_EOL); + + sysctl_createv(&kern_uidinfo_sysctllog, 0, &rnode, &cnode, + CTLFLAG_PERMANENT, + CTLTYPE_QUAD, "proccnt", + SYSCTL_DESCR("Number of processes for the current user"), + sysctl_kern_uidinfo_cnt, 0, NULL, 0, + CTL_CREATE, CTL_EOL); + sysctl_createv(&kern_uidinfo_sysctllog, 0, &rnode, &cnode, + CTLFLAG_PERMANENT, + CTLTYPE_QUAD, "lwpcnt", + SYSCTL_DESCR("Number of lwps for the current user"), + sysctl_kern_uidinfo_cnt, 0, NULL, 0, + CTL_CREATE, CTL_EOL); + sysctl_createv(&kern_uidinfo_sysctllog, 0, &rnode, &cnode, + CTLFLAG_PERMANENT, + CTLTYPE_QUAD, "lockcnt", + SYSCTL_DESCR("Number of locks for the current user"), + sysctl_kern_uidinfo_cnt, 0, NULL, 0, + CTL_CREATE, CTL_EOL); + sysctl_createv(&kern_uidinfo_sysctllog, 0, &rnode, &cnode, + CTLFLAG_PERMANENT, + CTLTYPE_QUAD, "sbsize", + SYSCTL_DESCR("Socket buffers used for the current user"), + sysctl_kern_uidinfo_cnt, 0, NULL, 0, + CTL_CREATE, CTL_EOL); +} + void uid_init(void) { @@ -68,6 +143,7 @@ uid_init(void) * sbreserve() expects it available from interrupt context. */ (void)uid_find(0); + sysctl_kern_uidinfo_setup(); } struct uidinfo * @@ -126,6 +202,27 @@ chgproccnt(uid_t uid, int diff) return proccnt; } +/* + * Change the count associated with number of lwps + * a given user is using. + */ +int +chglwpcnt(uid_t uid, int diff) +{ + struct uidinfo *uip; + long lwpcnt; + + uip = uid_find(uid); + lwpcnt = atomic_add_long_nv(&uip->ui_lwpcnt, diff); +#if 0 + KASSERT(lwpcnt >= 0); +#else + if (lwpcnt < 0) + printf("pid=%d lwpcnt=%ld\n", uid, lwpcnt); +#endif + return lwpcnt; +} + int chgsbsize(struct uidinfo *uip, u_long *hiwat, u_long to, rlim_t xmax) { Index: sys/lwp.h =================================================================== RCS file: /cvsroot/src/sys/sys/lwp.h,v retrieving revision 1.161 diff -u -p -u -r1.161 lwp.h --- sys/lwp.h 21 May 2012 14:15:19 -0000 1.161 +++ sys/lwp.h 9 Jun 2012 01:39:39 -0000 @@ -210,6 +210,13 @@ LIST_HEAD(lwplist, lwp); /* A list of L #ifdef _KERNEL extern struct lwplist alllwp; /* List of all LWPs. */ extern lwp_t lwp0; /* LWP for proc0. */ +extern int maxlwp __read_mostly; /* max number of lwps */ +#ifndef MAXLWP +#define MAXLWP 2048 +#endif +#ifndef __HAVE_CPU_MAXLWP +#define cpu_maxlwp() MAXLWP +#endif #endif /* These flags are kept in l_flag. */ Index: sys/param.h =================================================================== RCS file: /cvsroot/src/sys/sys/param.h,v retrieving revision 1.415 diff -u -p -u -r1.415 param.h --- sys/param.h 10 May 2012 07:48:07 -0000 1.415 +++ sys/param.h 9 Jun 2012 01:39:39 -0000 @@ -63,7 +63,7 @@ * 2.99.9 (299000900) */ -#define __NetBSD_Version__ 699000700 /* NetBSD 6.99.7 */ +#define __NetBSD_Version__ 699000800 /* NetBSD 6.99.8 */ #define __NetBSD_Prereq__(M,m,p) (((((M) * 100000000) + \ (m) * 1000000) + (p) * 100) <= __NetBSD_Version__) Index: sys/resource.h =================================================================== RCS file: /cvsroot/src/sys/sys/resource.h,v retrieving revision 1.32 diff -u -p -u -r1.32 resource.h --- sys/resource.h 14 May 2011 17:57:05 -0000 1.32 +++ sys/resource.h 9 Jun 2012 01:39:39 -0000 @@ -90,9 +90,10 @@ struct rusage { #define RLIMIT_SBSIZE 9 /* maximum size of all socket buffers */ #define RLIMIT_AS 10 /* virtual process size (inclusive of mmap) */ #define RLIMIT_VMEM RLIMIT_AS /* common alias */ +#define RLIMIT_NTHR 11 /* number of threads */ #if defined(_NETBSD_SOURCE) -#define RLIM_NLIMITS 11 /* number of resource limits */ +#define RLIM_NLIMITS 12 /* number of resource limits */ #endif #define RLIM_INFINITY (~((u_quad_t)1 << 63)) /* no limit */ Index: sys/sysctl.h =================================================================== RCS file: /cvsroot/src/sys/sys/sysctl.h,v retrieving revision 1.200 diff -u -p -u -r1.200 sysctl.h --- sys/sysctl.h 2 Jun 2012 21:36:48 -0000 1.200 +++ sys/sysctl.h 9 Jun 2012 01:39:43 -0000 @@ -982,6 +982,7 @@ struct evcnt_sysctl { #define PROC_PID_LIMIT_NOFILE (RLIMIT_NOFILE+1) #define PROC_PID_LIMIT_SBSIZE (RLIMIT_SBSIZE+1) #define PROC_PID_LIMIT_AS (RLIMIT_AS+1) +#define PROC_PID_LIMIT_NTHR (RLIMIT_NTHR+1) #define PROC_PID_LIMIT_MAXID (RLIM_NLIMITS+1) #define PROC_PID_LIMIT_NAMES { \ @@ -997,6 +998,7 @@ struct evcnt_sysctl { { "descriptors", CTLTYPE_NODE }, \ { "sbsize", CTLTYPE_NODE }, \ { "vmemoryuse", CTLTYPE_NODE }, \ + { "maxlwp", CTLTYPE_NODE }, \ } /* for each type, either hard or soft value */ #define PROC_PID_LIMIT_TYPE_SOFT 1 Index: sys/uidinfo.h =================================================================== RCS file: /cvsroot/src/sys/sys/uidinfo.h,v retrieving revision 1.2 diff -u -p -u -r1.2 uidinfo.h --- sys/uidinfo.h 14 Oct 2008 09:16:32 -0000 1.2 +++ sys/uidinfo.h 9 Jun 2012 01:39:43 -0000 @@ -43,11 +43,13 @@ struct uidinfo { SLIST_ENTRY(uidinfo) ui_hash; uid_t ui_uid; u_long ui_proccnt; /* Number of processes */ + u_long ui_lwpcnt; /* Number of lwps */ u_long ui_lockcnt; /* Number of locks */ u_long ui_sbsize; /* Socket buffer size */ }; int chgproccnt(uid_t, int); +int chglwpcnt(uid_t, int); int chgsbsize(struct uidinfo *, u_long *, u_long, rlim_t); struct uidinfo *uid_find(uid_t); void uid_init(void);