Index: kern/init_main.c =================================================================== RCS file: /cvsroot/src/sys/kern/init_main.c,v retrieving revision 1.442 diff -u -p -u -r1.442 init_main.c --- kern/init_main.c 19 Feb 2012 21:06:47 -0000 1.442 +++ kern/init_main.c 23 May 2012 23:19:31 -0000 @@ -256,6 +256,7 @@ int cold = 1; /* still working on star struct timespec boottime; /* time at system startup - will only follow settime deltas */ int start_init_exec; /* semaphore for start_init() */ +int maxlwp; cprng_strong_t *kern_cprng; @@ -291,6 +292,12 @@ main(void) #endif l->l_pflag |= LP_RUNNING; +#ifdef __HAVE_CPU_MAXLWP + maxlwp = cpu_maxlwp(); +#else + maxlwp = 1024; +#endif + /* * Attempt to find console and initialize * in case of early panic or other messages. Index: kern/init_sysctl.c =================================================================== RCS file: /cvsroot/src/sys/kern/init_sysctl.c,v retrieving revision 1.189 diff -u -p -u -r1.189 init_sysctl.c --- kern/init_sysctl.c 7 Apr 2012 05:38:49 -0000 1.189 +++ kern/init_sysctl.c 23 May 2012 23:19:31 -0000 @@ -147,6 +147,7 @@ static int sysctl_kern_trigger_panic(SYS static int sysctl_kern_maxvnodes(SYSCTLFN_PROTO); static int sysctl_kern_rtc_offset(SYSCTLFN_PROTO); static int sysctl_kern_maxproc(SYSCTLFN_PROTO); +static int sysctl_kern_maxlwp(SYSCTLFN_PROTO); static int sysctl_kern_hostid(SYSCTLFN_PROTO); static int sysctl_setlen(SYSCTLFN_PROTO); static int sysctl_kern_clockrate(SYSCTLFN_PROTO); @@ -234,6 +235,12 @@ SYSCTL_SETUP(sysctl_kern_setup, "sysctl CTL_KERN, KERN_MAXPROC, CTL_EOL); 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, KERN_MAXLWP, CTL_EOL); + sysctl_createv(clog, 0, NULL, NULL, + CTLFLAG_PERMANENT|CTLFLAG_READWRITE, CTLTYPE_INT, "maxfiles", SYSCTL_DESCR("Maximum number of open files"), NULL, 0, &maxfiles, 0, @@ -1050,6 +1057,33 @@ sysctl_kern_maxproc(SYSCTLFN_ARGS) } /* + * 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; +#ifdef __HAVE_CPU_MAXLWP + if (nmaxlwp > cpu_maxlwp()) + return EINVAL; +#endif + maxlwp = nmaxlwp; + + return 0; +} +/* * sysctl helper function for kern.hostid. The hostid is a long, but * we export it as an int, so we need to give it a little help. */ Index: kern/kern_exec.c =================================================================== RCS file: /cvsroot/src/sys/kern/kern_exec.c,v retrieving revision 1.352 diff -u -p -u -r1.352 kern_exec.c --- kern/kern_exec.c 2 May 2012 23:33:11 -0000 1.352 +++ kern/kern_exec.c 23 May 2012 23:19:32 -0000 @@ -2295,7 +2295,7 @@ do_posix_spawn(struct lwp *l1, pid_t *pi /* create LWP */ lwp_create(l1, p2, uaddr, 0, NULL, 0, spawn_return, spawn_data, - &l2, l1->l_class); + &l2, l1->l_class, 0); l2->l_ctxlink = NULL; /* reset ucontext link */ /* Index: kern/kern_fork.c =================================================================== RCS file: /cvsroot/src/sys/kern/kern_fork.c,v retrieving revision 1.189 diff -u -p -u -r1.189 kern_fork.c --- kern/kern_fork.c 13 Mar 2012 18:40:52 -0000 1.189 +++ kern/kern_fork.c 23 May 2012 23:19:32 -0000 @@ -426,7 +426,7 @@ fork1(struct lwp *l1, int flags, int exi */ lwp_create(l1, p2, uaddr, (flags & FORK_PPWAIT) ? LWP_VFORK : 0, stack, stacksize, (func != NULL) ? func : child_return, arg, &l2, - l1->l_class); + l1->l_class, 0); /* * Inherit l_private from the parent. Index: kern/kern_kthread.c =================================================================== RCS file: /cvsroot/src/sys/kern/kern_kthread.c,v retrieving revision 1.38 diff -u -p -u -r1.38 kern_kthread.c --- kern/kern_kthread.c 1 Nov 2011 15:39:37 -0000 1.38 +++ kern/kern_kthread.c 23 May 2012 23:19:32 -0000 @@ -81,7 +81,7 @@ kthread_create(pri_t pri, int flag, stru } error = lwp_create(&lwp0, &proc0, uaddr, LWP_DETACHED, NULL, - 0, func, arg, &l, lc); + 0, func, arg, &l, lc, 0); if (error) { uvm_uarea_system_free(uaddr); return error; 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 23 May 2012 23:19:32 -0000 @@ -239,6 +239,7 @@ __KERNEL_RCSID(0, "$NetBSD: kern_lwp.c,v #include <sys/dtrace_bsd.h> #include <sys/sdt.h> #include <sys/xcall.h> +#include <sys/uidinfo.h> #include <uvm/uvm_extern.h> #include <uvm/uvm_object.h> @@ -668,15 +669,32 @@ lwp_wait1(struct lwp *l, lwpid_t lid, lw int lwp_create(lwp_t *l1, proc_t *p2, vaddr_t uaddr, int flags, void *stack, size_t stacksize, void (*func)(void *), void *arg, - lwp_t **rnewlwpp, int sclass) + lwp_t **rnewlwpp, int sclass, int enforce) { struct lwp *l2, *isfree; turnstile_t *ts; lwpid_t lid; + uid_t uid; + int count; KASSERT(l1 == curlwp || l1->l_proc == &proc0); /* + * Enforce limits. + */ + uid = kauth_cred_getuid(l1->l_cred); + count = chglwpcnt(uid, 1); + if (enforce && + __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) { + (void)chglwpcnt(uid, -1); + return EAGAIN; + } + } + + /* * First off, reap any detached LWP waiting to be collected. * We can re-use its LWP structure and turnstile. */ @@ -898,6 +916,7 @@ lwp_exit(struct lwp *l) SDT_PROBE(proc,,,lwp_exit, l, 0,0,0,0); + chglwpcnt(kauth_cred_getuid(l->l_cred), -1); /* * Verify that we hold no locks other than the kernel lock. */ Index: kern/kern_proc.c =================================================================== RCS file: /cvsroot/src/sys/kern/kern_proc.c,v retrieving revision 1.183 diff -u -p -u -r1.183 kern_proc.c --- kern/kern_proc.c 13 Apr 2012 15:32:15 -0000 1.183 +++ kern/kern_proc.c 23 May 2012 23:19:33 -0000 @@ -460,6 +460,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 23 May 2012 23:19:33 -0000 @@ -346,6 +346,11 @@ 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); + + int nlwps = p->p_nlwps; + (void)chglwpcnt(kauth_cred_getuid(ncred), -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 23 May 2012 23:19:33 -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_synch.c =================================================================== RCS file: /cvsroot/src/sys/kern/kern_synch.c,v retrieving revision 1.301 diff -u -p -u -r1.301 kern_synch.c --- kern/kern_synch.c 21 Apr 2012 22:38:25 -0000 1.301 +++ kern/kern_synch.c 23 May 2012 23:19:34 -0000 @@ -96,6 +96,8 @@ __KERNEL_RCSID(0, "$NetBSD: kern_synch.c #include <sys/lwpctl.h> #include <sys/atomic.h> #include <sys/simplelock.h> +#include <sys/uidinfo.h> +#include <sys/kauth.h> #include <sys/syslog.h> #include <uvm/uvm_extern.h> @@ -805,6 +807,7 @@ lwp_exit_switchaway(lwp_t *l) LOCKDEBUG_BARRIER(NULL, 0); kstack_check_magic(l); + chglwpcnt(kauth_cred_geteuid(l->l_cred), -1); /* Count time spent in current system call */ SYSCALL_TIME_SLEEP(l); 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 23 May 2012 23:19:34 -0000 @@ -43,6 +43,8 @@ __KERNEL_RCSID(0, "$NetBSD: kern_uidinfo #include <sys/proc.h> #include <sys/atomic.h> #include <sys/uidinfo.h> +#include <sys/sysctl.h> +#include <sys/kauth.h> #include <sys/cpu.h> 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,22 @@ 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); + KASSERT(lwpcnt >= 0); + return lwpcnt; +} + int chgsbsize(struct uidinfo *uip, u_long *hiwat, u_long to, rlim_t xmax) { Index: kern/sys_aio.c =================================================================== RCS file: /cvsroot/src/sys/kern/sys_aio.c,v retrieving revision 1.37 diff -u -p -u -r1.37 sys_aio.c --- kern/sys_aio.c 17 Feb 2011 19:02:50 -0000 1.37 +++ kern/sys_aio.c 23 May 2012 23:19:34 -0000 @@ -211,7 +211,7 @@ aio_procinit(struct proc *p) return EAGAIN; } error = lwp_create(curlwp, p, uaddr, 0, NULL, 0, aio_worker, - NULL, &l, curlwp->l_class); + NULL, &l, curlwp->l_class, 1); if (error != 0) { uvm_uarea_free(uaddr); aio_exit(p, aio); Index: kern/sys_lwp.c =================================================================== RCS file: /cvsroot/src/sys/kern/sys_lwp.c,v retrieving revision 1.53 diff -u -p -u -r1.53 sys_lwp.c --- kern/sys_lwp.c 19 Feb 2012 21:06:56 -0000 1.53 +++ kern/sys_lwp.c 23 May 2012 23:19:34 -0000 @@ -101,7 +101,7 @@ sys__lwp_create(struct lwp *l, const str } error = lwp_create(l, p, uaddr, SCARG(uap, flags) & LWP_DETACHED, - NULL, 0, p->p_emul->e_startlwp, newuc, &l2, l->l_class); + NULL, 0, p->p_emul->e_startlwp, newuc, &l2, l->l_class, 1); if (__predict_false(error)) { uvm_uarea_free(uaddr); kmem_free(newuc, sizeof(ucontext_t)); Index: sys/lwp.h =================================================================== RCS file: /cvsroot/src/sys/sys/lwp.h,v retrieving revision 1.160 diff -u -p -u -r1.160 lwp.h --- sys/lwp.h 19 Feb 2012 21:06:58 -0000 1.160 +++ sys/lwp.h 23 May 2012 23:19:35 -0000 @@ -210,6 +210,7 @@ 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; /* max number of lwps */ #endif /* These flags are kept in l_flag. */ @@ -409,7 +410,7 @@ lwp_eprio(lwp_t *l) } int lwp_create(lwp_t *, struct proc *, vaddr_t, int, - void *, size_t, void (*)(void *), void *, lwp_t **, int); + void *, size_t, void (*)(void *), void *, lwp_t **, int, int); /* * XXX _MODULE 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 23 May 2012 23:19:35 -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.199 diff -u -p -u -r1.199 sysctl.h --- sys/sysctl.h 27 Jan 2012 19:48:41 -0000 1.199 +++ sys/sysctl.h 23 May 2012 23:19:36 -0000 @@ -266,7 +266,8 @@ struct ctlname { #define KERN_SYSVIPC 82 /* node: SysV IPC parameters */ #define KERN_BOOTTIME 83 /* struct: time kernel was booted */ #define KERN_EVCNT 84 /* struct: evcnts */ -#define KERN_MAXID 85 /* number of valid kern ids */ +#define KERN_MAXLWP 85 /* int: maxlwp */ +#define KERN_MAXID 86 /* number of valid kern ids */ #define CTL_KERN_NAMES { \ @@ -355,6 +356,7 @@ struct ctlname { { "sysvipc", CTLTYPE_STRUCT }, \ { "boottime", CTLTYPE_STRUCT }, \ { "evcnt", CTLTYPE_STRUCT }, \ + { "maxlwp", CTLTYPE_INT }, \ } /* @@ -975,6 +977,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 { \ @@ -990,6 +993,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 23 May 2012 23:19:36 -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);