Add a new resource to limit the number of lwps per user, RLIMIT_NTHR. There

is a global sysctl kern.maxlwp to control this, which is by default 2048.
The first lwp of each process or kernel threads are not counted against the
limit. To show the current resource usage per user, I added a new sysctl
that dumps the uidinfo structure fields.
This commit is contained in:
christos 2012-06-09 02:31:14 +00:00
parent e7841c4a22
commit 0461089547
10 changed files with 219 additions and 17 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: param.c,v 1.63 2010/02/08 19:02:33 joerg Exp $ */
/* $NetBSD: param.c,v 1.64 2012/06/09 02:31:14 christos Exp $ */
/*
* Copyright (c) 1980, 1986, 1989 Regents of the University of California.
@ -37,7 +37,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: param.c,v 1.63 2010/02/08 19:02:33 joerg Exp $");
__KERNEL_RCSID(0, "$NetBSD: param.c,v 1.64 2012/06/09 02:31:14 christos Exp $");
#include "opt_hz.h"
#include "opt_rtc_offset.h"
@ -58,6 +58,7 @@ __KERNEL_RCSID(0, "$NetBSD: param.c,v 1.63 2010/02/08 19:02:33 joerg Exp $");
#include <sys/kernel.h>
#include <sys/utsname.h>
#include <sys/ksem.h>
#include <sys/lwp.h>
#ifdef SYSVSHM
#include <machine/vmparam.h>
#include <sys/shm.h>
@ -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' */

View File

@ -1,4 +1,4 @@
/* $NetBSD: kern_lwp.c,v 1.168 2012/04/13 15:32:43 yamt Exp $ */
/* $NetBSD: kern_lwp.c,v 1.169 2012/06/09 02:31:14 christos Exp $ */
/*-
* Copyright (c) 2001, 2006, 2007, 2008, 2009 The NetBSD Foundation, Inc.
@ -211,7 +211,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: kern_lwp.c,v 1.168 2012/04/13 15:32:43 yamt Exp $");
__KERNEL_RCSID(0, "$NetBSD: kern_lwp.c,v 1.169 2012/06/09 02:31:14 christos Exp $");
#include "opt_ddb.h"
#include "opt_lockdebug.h"
@ -239,6 +239,8 @@ __KERNEL_RCSID(0, "$NetBSD: kern_lwp.c,v 1.168 2012/04/13 15:32:43 yamt Exp $");
#include <sys/dtrace_bsd.h>
#include <sys/sdt.h>
#include <sys/xcall.h>
#include <sys/uidinfo.h>
#include <sys/sysctl.h>
#include <uvm/uvm_extern.h>
#include <uvm/uvm_object.h>
@ -286,6 +288,47 @@ struct lwp lwp0 __aligned(MIN_LWP_ALIGNMENT) = {
.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;
}
static void
sysctl_kern_lwp_setup(void)
{
struct sysctllog *clog = NULL;
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 +338,9 @@ 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();
sysctl_kern_lwp_setup();
}
void
@ -676,6 +722,28 @@ lwp_create(lwp_t *l1, proc_t *p2, vaddr_t uaddr, int flags,
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 +1109,10 @@ lwp_free(struct lwp *l, bool recycle, bool last)
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.

View File

@ -1,4 +1,4 @@
/* $NetBSD: kern_proc.c,v 1.185 2012/06/06 11:20:21 martin Exp $ */
/* $NetBSD: kern_proc.c,v 1.186 2012/06/09 02:31:14 christos Exp $ */
/*-
* Copyright (c) 1999, 2006, 2007, 2008 The NetBSD Foundation, Inc.
@ -62,7 +62,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: kern_proc.c,v 1.185 2012/06/06 11:20:21 martin Exp $");
__KERNEL_RCSID(0, "$NetBSD: kern_proc.c,v 1.186 2012/06/09 02:31:14 christos Exp $");
#ifdef _KERNEL_OPT
#include "opt_kstack.h"
@ -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;

View File

@ -1,4 +1,4 @@
/* $NetBSD: kern_prot.c,v 1.114 2012/03/19 06:04:19 matt Exp $ */
/* $NetBSD: kern_prot.c,v 1.115 2012/06/09 02:31:15 christos Exp $ */
/*
* Copyright (c) 1982, 1986, 1989, 1990, 1991, 1993
@ -41,7 +41,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: kern_prot.c,v 1.114 2012/03/19 06:04:19 matt Exp $");
__KERNEL_RCSID(0, "$NetBSD: kern_prot.c,v 1.115 2012/06/09 02:31:15 christos Exp $");
#include "opt_compat_43.h"
@ -346,6 +346,14 @@ do_setresuid(struct lwp *l, uid_t r, uid_t e, uid_t sv, u_int flags)
/* 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)

View File

@ -1,4 +1,4 @@
/* $NetBSD: kern_resource.c,v 1.168 2011/12/02 12:33:12 yamt Exp $ */
/* $NetBSD: kern_resource.c,v 1.169 2012/06/09 02:31:15 christos Exp $ */
/*-
* Copyright (c) 1982, 1986, 1991, 1993
@ -37,7 +37,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: kern_resource.c,v 1.168 2011/12/02 12:33:12 yamt Exp $");
__KERNEL_RCSID(0, "$NetBSD: kern_resource.c,v 1.169 2012/06/09 02:31:15 christos Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@ -439,6 +439,13 @@ dosetrlimit(struct lwp *l, struct proc *p, int which, struct rlimit *limp)
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

View File

@ -1,4 +1,4 @@
/* $NetBSD: kern_uidinfo.c,v 1.5 2009/03/22 00:49:13 ad Exp $ */
/* $NetBSD: kern_uidinfo.c,v 1.6 2012/06/09 02:31:15 christos Exp $ */
/*-
* Copyright (c) 1982, 1986, 1991, 1993
@ -35,7 +35,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: kern_uidinfo.c,v 1.5 2009/03/22 00:49:13 ad Exp $");
__KERNEL_RCSID(0, "$NetBSD: kern_uidinfo.c,v 1.6 2012/06/09 02:31:15 christos Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@ -43,6 +43,8 @@ __KERNEL_RCSID(0, "$NetBSD: kern_uidinfo.c,v 1.5 2009/03/22 00:49:13 ad Exp $");
#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,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)
{

View File

@ -1,4 +1,4 @@
/* $NetBSD: lwp.h,v 1.161 2012/05/21 14:15:19 martin Exp $ */
/* $NetBSD: lwp.h,v 1.162 2012/06/09 02:31:15 christos Exp $ */
/*-
* Copyright (c) 2001, 2006, 2007, 2008, 2009, 2010
@ -210,6 +210,13 @@ LIST_HEAD(lwplist, lwp); /* A list of LWPs. */
#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. */

View File

@ -1,4 +1,4 @@
/* $NetBSD: resource.h,v 1.32 2011/05/14 17:57:05 rmind Exp $ */
/* $NetBSD: resource.h,v 1.33 2012/06/09 02:31:15 christos Exp $ */
/*
* Copyright (c) 1982, 1986, 1993
@ -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 */

View File

@ -1,4 +1,4 @@
/* $NetBSD: sysctl.h,v 1.200 2012/06/02 21:36:48 dsl Exp $ */
/* $NetBSD: sysctl.h,v 1.201 2012/06/09 02:31:15 christos Exp $ */
/*
* Copyright (c) 1989, 1993
@ -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

View File

@ -1,4 +1,4 @@
/* $NetBSD: uidinfo.h,v 1.2 2008/10/14 09:16:32 ad Exp $ */
/* $NetBSD: uidinfo.h,v 1.3 2012/06/09 02:31:15 christos Exp $ */
/*
* Copyright (c) 1991, 1993
@ -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);