summaryrefslogtreecommitdiff
path: root/usr/src/uts/common
diff options
context:
space:
mode:
authorMenno Lageman <Menno.Lageman@Sun.COM>2010-06-29 02:03:28 -0700
committerMenno Lageman <Menno.Lageman@Sun.COM>2010-06-29 02:03:28 -0700
commitff19e029e81c950f4e0f40f1f1ee1f7d8f8d8041 (patch)
tree1dd3e8563f991d5f5525997320033ae6742031ac /usr/src/uts/common
parente2449ddf2ae27a6dd79f8605a10fbc4082aba5f7 (diff)
downloadillumos-joyent-ff19e029e81c950f4e0f40f1f1ee1f7d8f8d8041.tar.gz
PSARC 2009/042 max-processes rctl
6631612 non-global zone can overrun the process table of the system 6466380 Project resource set callbacks are needlessly called on every fork() 6516818 task resource callbacks are needlessly called on every fork()
Diffstat (limited to 'usr/src/uts/common')
-rw-r--r--usr/src/uts/common/os/exacct.c4
-rw-r--r--usr/src/uts/common/os/exit.c39
-rw-r--r--usr/src/uts/common/os/fork.c53
-rw-r--r--usr/src/uts/common/os/project.c136
-rw-r--r--usr/src/uts/common/os/rctl.c15
-rw-r--r--usr/src/uts/common/os/task.c239
-rw-r--r--usr/src/uts/common/os/zone.c167
-rw-r--r--usr/src/uts/common/sys/project.h12
-rw-r--r--usr/src/uts/common/sys/rctl.h3
-rw-r--r--usr/src/uts/common/sys/task.h18
-rw-r--r--usr/src/uts/common/sys/zone.h5
-rw-r--r--usr/src/uts/common/syscall/tasksys.c12
12 files changed, 597 insertions, 106 deletions
diff --git a/usr/src/uts/common/os/exacct.c b/usr/src/uts/common/os/exacct.c
index 4d9009abf2..992e401046 100644
--- a/usr/src/uts/common/os/exacct.c
+++ b/usr/src/uts/common/os/exacct.c
@@ -19,8 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
*/
#include <sys/exacct.h>
@@ -1735,6 +1734,7 @@ exacct_init()
exacct_queue = system_taskq;
exacct_object_cache = kmem_cache_create("exacct_object_cache",
sizeof (ea_object_t), 0, NULL, NULL, NULL, NULL, NULL, 0);
+ task_commit_thread_init();
}
/*
diff --git a/usr/src/uts/common/os/exit.c b/usr/src/uts/common/os/exit.c
index f86c422fd8..5b9d682ce1 100644
--- a/usr/src/uts/common/os/exit.c
+++ b/usr/src/uts/common/os/exit.c
@@ -20,8 +20,7 @@
*/
/*
- * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
@@ -766,11 +765,13 @@ proc_exit(int why, int what)
rctl_set_free(p->p_rctls);
/*
- * Give up task and project memberships. Decrement tk_nlwps counter
- * for our task.max-lwps resource control. An extended accounting
- * record, if that facility is active, is scheduled to be written.
- * Zombie processes are false members of task0 for the remainder of
- * their lifetime; no accounting information is recorded for them.
+ * Decrement tk_nlwps counter for our task.max-lwps resource control.
+ * An extended accounting record, if that facility is active, is
+ * scheduled to be written. We cannot give up task and project
+ * membership at this point because that would allow zombies to escape
+ * from the max-processes resource controls. Zombies stay in their
+ * current task and project until the process table slot is released
+ * in freeproc().
*/
tk = p->p_task;
@@ -779,8 +780,6 @@ proc_exit(int why, int what)
tk->tk_proj->kpj_nlwps--;
p->p_zone->zone_nlwps--;
mutex_exit(&p->p_zone->zone_nlwps_lock);
- task_detach(p);
- p->p_task = task0p;
/*
* Clear the lwp directory and the lwpid hash table
@@ -867,7 +866,6 @@ proc_exit(int why, int what)
* no longer point at zsched.
*/
t->t_procp = &p0;
- task_rele(tk);
kmem_free(lwpdir, lwpdir_sz * sizeof (lwpdir_t));
kmem_free(tidhash, tidhash_sz * sizeof (tidhash_t));
@@ -1205,6 +1203,8 @@ void
freeproc(proc_t *p)
{
proc_t *q;
+ task_t *tk;
+ zone_t *zone;
ASSERT(p->p_stat == SZOMB);
ASSERT(p->p_tlist == NULL);
@@ -1291,8 +1291,27 @@ freeproc(proc_t *p)
q->p_nextorph = p->p_nextorph;
}
+ /*
+ * The process table slot is being freed, so it is now safe to give up
+ * task and project membership.
+ */
+ zone = p->p_zone;
+ mutex_enter(&p->p_lock);
+ tk = p->p_task;
+ task_detach(p);
+ p->p_task = task0p;
+ mutex_exit(&p->p_lock);
+
proc_detach(p);
pid_exit(p); /* frees pid and proc structure */
+
+ mutex_enter(&zone->zone_nlwps_lock);
+ tk->tk_nprocs--;
+ tk->tk_proj->kpj_nprocs--;
+ zone->zone_nprocs--;
+ mutex_exit(&zone->zone_nlwps_lock);
+
+ task_rele(tk);
}
/*
diff --git a/usr/src/uts/common/os/fork.c b/usr/src/uts/common/os/fork.c
index b9f9dba96d..feb24dae28 100644
--- a/usr/src/uts/common/os/fork.c
+++ b/usr/src/uts/common/os/fork.c
@@ -20,8 +20,7 @@
*/
/*
- * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
*/
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
@@ -835,6 +834,7 @@ newproc(void (*pc)(), caddr_t arg, id_t cid, int pri, struct contract **ct,
tk = task_create(0, p->p_zone);
mutex_enter(&tk->tk_zone->zone_nlwps_lock);
tk->tk_proj->kpj_ntasks++;
+ tk->tk_nprocs++;
mutex_exit(&tk->tk_zone->zone_nlwps_lock);
default_gp = rctl_rlimit_set_prealloc(RLIM_NLIMITS);
@@ -849,6 +849,10 @@ newproc(void (*pc)(), caddr_t arg, id_t cid, int pri, struct contract **ct,
task_begin(tk, p);
mutex_exit(&pidlock);
+ mutex_enter(&tk_old->tk_zone->zone_nlwps_lock);
+ tk_old->tk_nprocs--;
+ mutex_exit(&tk_old->tk_zone->zone_nlwps_lock);
+
e.rcep_p.proc = p;
e.rcep_t = RCENTITY_PROCESS;
p->p_rctls = rctl_set_init(RCENTITY_PROCESS, p, &e, init_set,
@@ -912,6 +916,10 @@ getproc(proc_t **cpp, pid_t pid, uint_t flags)
struct cred *cr;
uid_t ruid;
zoneid_t zoneid;
+ task_t *task;
+ kproject_t *proj;
+ zone_t *zone;
+ int rctlfail = 0;
if (!page_mem_avail(tune.t_minarmem))
return (-1);
@@ -919,6 +927,40 @@ getproc(proc_t **cpp, pid_t pid, uint_t flags)
return (-1); /* no point in starting new processes */
pp = (flags & GETPROC_KERNEL) ? &p0 : curproc;
+ task = pp->p_task;
+ proj = task->tk_proj;
+ zone = pp->p_zone;
+
+ mutex_enter(&pp->p_lock);
+ mutex_enter(&zone->zone_nlwps_lock);
+ if (proj != proj0p) {
+ if (task->tk_nprocs >= task->tk_nprocs_ctl)
+ if (rctl_test(rc_task_nprocs, task->tk_rctls,
+ pp, 1, 0) & RCT_DENY)
+ rctlfail = 1;
+
+ if (proj->kpj_nprocs >= proj->kpj_nprocs_ctl)
+ if (rctl_test(rc_project_nprocs, proj->kpj_rctls,
+ pp, 1, 0) & RCT_DENY)
+ rctlfail = 1;
+
+ if (zone->zone_nprocs >= zone->zone_nprocs_ctl)
+ if (rctl_test(rc_zone_nprocs, zone->zone_rctls,
+ pp, 1, 0) & RCT_DENY)
+ rctlfail = 1;
+
+ if (rctlfail) {
+ mutex_exit(&zone->zone_nlwps_lock);
+ mutex_exit(&pp->p_lock);
+ goto punish;
+ }
+ }
+ task->tk_nprocs++;
+ proj->kpj_nprocs++;
+ zone->zone_nprocs++;
+ mutex_exit(&zone->zone_nlwps_lock);
+ mutex_exit(&pp->p_lock);
+
cp = kmem_cache_alloc(process_cache, KM_SLEEP);
bzero(cp, sizeof (proc_t));
@@ -1178,6 +1220,13 @@ bad:
}
kmem_cache_free(process_cache, cp);
+ mutex_enter(&zone->zone_nlwps_lock);
+ task->tk_nprocs--;
+ proj->kpj_nprocs--;
+ zone->zone_nprocs--;
+ mutex_exit(&zone->zone_nlwps_lock);
+
+punish:
/*
* We most likely got into this situation because some process is
* forking out of control. As punishment, put it to sleep for a
diff --git a/usr/src/uts/common/os/project.c b/usr/src/uts/common/os/project.c
index 98b5b93683..7bd3dd963f 100644
--- a/usr/src/uts/common/os/project.c
+++ b/usr/src/uts/common/os/project.c
@@ -50,6 +50,7 @@ static kproject_t *projects_list;
rctl_hndl_t rc_project_cpu_shares;
rctl_hndl_t rc_project_cpu_cap;
rctl_hndl_t rc_project_nlwps;
+rctl_hndl_t rc_project_nprocs;
rctl_hndl_t rc_project_ntasks;
rctl_hndl_t rc_project_msgmni;
rctl_hndl_t rc_project_semmni;
@@ -104,7 +105,7 @@ struct project_zone {
* acquired, the hash lock is to be acquired first.
*/
-static kstat_t *project_kstat_create(kproject_t *pj, zone_t *zone);
+static void project_kstat_create(kproject_t *pj, zone_t *zone);
static void project_kstat_delete(kproject_t *pj);
static void
@@ -123,6 +124,7 @@ project_data_init(kproject_data_t *data)
data->kpd_crypto_mem = 0;
data->kpd_crypto_mem_ctl = UINT64_MAX;
data->kpd_lockedmem_kstat = NULL;
+ data->kpd_nprocs_kstat = NULL;
}
/*ARGSUSED*/
@@ -218,7 +220,6 @@ project_hold_by_id(projid_t id, zone_t *zone, int flag)
rctl_entity_p_t e;
struct project_zone pz;
boolean_t create = B_FALSE;
- kstat_t *ksp;
pz.kpj_id = id;
pz.kpj_zoneid = zone->zone_id;
@@ -257,8 +258,10 @@ project_hold_by_id(projid_t id, zone_t *zone, int flag)
p->kpj_count = 0;
p->kpj_shares = 1;
p->kpj_nlwps = 0;
+ p->kpj_nprocs = 0;
p->kpj_ntasks = 0;
p->kpj_nlwps_ctl = INT_MAX;
+ p->kpj_nprocs_ctl = INT_MAX;
p->kpj_ntasks_ctl = INT_MAX;
project_data_init(&p->kpj_data);
e.rcep_p.proj = p;
@@ -313,11 +316,7 @@ project_hold_by_id(projid_t id, zone_t *zone, int flag)
/*
* Set up project kstats
*/
- ksp = project_kstat_create(p, zone);
- mutex_enter(&project_hash_lock);
- ASSERT(p->kpj_data.kpd_lockedmem_kstat == NULL);
- p->kpj_data.kpd_lockedmem_kstat = ksp;
- mutex_exit(&project_hash_lock);
+ project_kstat_create(p, zone);
}
return (p);
}
@@ -345,6 +344,8 @@ project_rele(kproject_t *p)
/*
* Remove project from global list.
*/
+ ASSERT(p->kpj_nprocs == 0);
+
mutex_enter(&projects_list_lock);
p->kpj_next->kpj_prev = p->kpj_prev;
p->kpj_prev->kpj_next = p->kpj_next;
@@ -548,6 +549,63 @@ static rctl_ops_t project_lwps_ops = {
/*ARGSUSED*/
static rctl_qty_t
+project_procs_usage(rctl_t *r, proc_t *p)
+{
+ kproject_t *pj;
+ rctl_qty_t nprocs;
+
+ ASSERT(MUTEX_HELD(&p->p_lock));
+ pj = p->p_task->tk_proj;
+ mutex_enter(&p->p_zone->zone_nlwps_lock);
+ nprocs = pj->kpj_nprocs;
+ mutex_exit(&p->p_zone->zone_nlwps_lock);
+
+ return (nprocs);
+}
+
+/*ARGSUSED*/
+static int
+project_procs_test(rctl_t *r, proc_t *p, rctl_entity_p_t *e, rctl_val_t *rcntl,
+ rctl_qty_t incr, uint_t flags)
+{
+ rctl_qty_t nprocs;
+
+ ASSERT(MUTEX_HELD(&p->p_lock));
+ ASSERT(MUTEX_HELD(&p->p_zone->zone_nlwps_lock));
+ ASSERT(e->rcep_t == RCENTITY_PROJECT);
+ if (e->rcep_p.proj == NULL)
+ return (0);
+
+ nprocs = e->rcep_p.proj->kpj_nprocs;
+ if (nprocs + incr > rcntl->rcv_value)
+ return (1);
+
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+project_procs_set(rctl_t *rctl, struct proc *p, rctl_entity_p_t *e,
+ rctl_qty_t nv) {
+
+ ASSERT(MUTEX_HELD(&p->p_lock));
+ ASSERT(e->rcep_t == RCENTITY_PROJECT);
+ if (e->rcep_p.proj == NULL)
+ return (0);
+
+ e->rcep_p.proj->kpj_nprocs_ctl = nv;
+ return (0);
+}
+
+static rctl_ops_t project_procs_ops = {
+ rcop_no_action,
+ project_procs_usage,
+ project_procs_set,
+ project_procs_test,
+};
+
+/*ARGSUSED*/
+static rctl_qty_t
project_ntasks_usage(rctl_t *r, proc_t *p)
{
kproject_t *pj;
@@ -865,6 +923,10 @@ project_init(void)
RCTL_GLOBAL_NOACTION | RCTL_GLOBAL_NOBASIC | RCTL_GLOBAL_COUNT,
INT_MAX, INT_MAX, &project_lwps_ops);
+ rc_project_nprocs = rctl_register("project.max-processes",
+ RCENTITY_PROJECT, RCTL_GLOBAL_NOACTION | RCTL_GLOBAL_NOBASIC |
+ RCTL_GLOBAL_COUNT, INT_MAX, INT_MAX, &project_procs_ops);
+
rc_project_ntasks = rctl_register("project.max-tasks", RCENTITY_PROJECT,
RCTL_GLOBAL_NOACTION | RCTL_GLOBAL_NOBASIC | RCTL_GLOBAL_COUNT,
INT_MAX, INT_MAX, &project_tasks_ops);
@@ -969,6 +1031,7 @@ project_init(void)
mutex_enter(&p0.p_lock);
proj0p->kpj_nlwps = p0.p_lwpcnt;
mutex_exit(&p0.p_lock);
+ proj0p->kpj_nprocs = 1;
proj0p->kpj_ntasks = 1;
}
@@ -986,14 +1049,28 @@ project_lockedmem_kstat_update(kstat_t *ksp, int rw)
return (0);
}
+static int
+project_nprocs_kstat_update(kstat_t *ksp, int rw)
+{
+ kproject_t *pj = ksp->ks_private;
+ kproject_kstat_t *kpk = ksp->ks_data;
+
+ if (rw == KSTAT_WRITE)
+ return (EACCES);
+
+ kpk->kpk_usage.value.ui64 = pj->kpj_nprocs;
+ kpk->kpk_value.value.ui64 = pj->kpj_nprocs_ctl;
+ return (0);
+}
+
static kstat_t *
-project_kstat_create(kproject_t *pj, zone_t *zone)
+project_kstat_create_common(kproject_t *pj, char *name, char *zonename,
+ int (*updatefunc) (kstat_t *, int))
{
kstat_t *ksp;
kproject_kstat_t *kpk;
- char *zonename = zone->zone_name;
- ksp = rctl_kstat_create_project(pj, "lockedmem", KSTAT_TYPE_NAMED,
+ ksp = rctl_kstat_create_project(pj, name, KSTAT_TYPE_NAMED,
sizeof (kproject_kstat_t) / sizeof (kstat_named_t),
KSTAT_FLAG_VIRTUAL);
@@ -1006,22 +1083,47 @@ project_kstat_create(kproject_t *pj, zone_t *zone)
kstat_named_setstr(&kpk->kpk_zonename, zonename);
kstat_named_init(&kpk->kpk_usage, "usage", KSTAT_DATA_UINT64);
kstat_named_init(&kpk->kpk_value, "value", KSTAT_DATA_UINT64);
- ksp->ks_update = project_lockedmem_kstat_update;
+ ksp->ks_update = updatefunc;
ksp->ks_private = pj;
kstat_install(ksp);
-
return (ksp);
}
static void
-project_kstat_delete(kproject_t *pj)
+project_kstat_create(kproject_t *pj, zone_t *zone)
+{
+ kstat_t *ksp_lockedmem;
+ kstat_t *ksp_nprocs;
+
+ ksp_lockedmem = project_kstat_create_common(pj, "lockedmem",
+ zone->zone_name, project_lockedmem_kstat_update);
+ ksp_nprocs = project_kstat_create_common(pj, "nprocs",
+ zone->zone_name, project_nprocs_kstat_update);
+
+ mutex_enter(&project_hash_lock);
+ ASSERT(pj->kpj_data.kpd_lockedmem_kstat == NULL);
+ pj->kpj_data.kpd_lockedmem_kstat = ksp_lockedmem;
+ ASSERT(pj->kpj_data.kpd_nprocs_kstat == NULL);
+ pj->kpj_data.kpd_nprocs_kstat = ksp_nprocs;
+ mutex_exit(&project_hash_lock);
+}
+
+static void
+project_kstat_delete_common(kstat_t **kstat)
{
void *data;
- if (pj->kpj_data.kpd_lockedmem_kstat != NULL) {
- data = pj->kpj_data.kpd_lockedmem_kstat->ks_data;
- kstat_delete(pj->kpj_data.kpd_lockedmem_kstat);
+ if (*kstat != NULL) {
+ data = (*kstat)->ks_data;
+ kstat_delete(*kstat);
kmem_free(data, sizeof (kproject_kstat_t));
+ *kstat = NULL;
}
- pj->kpj_data.kpd_lockedmem_kstat = NULL;
+}
+
+static void
+project_kstat_delete(kproject_t *pj)
+{
+ project_kstat_delete_common(&pj->kpj_data.kpd_lockedmem_kstat);
+ project_kstat_delete_common(&pj->kpj_data.kpd_nprocs_kstat);
}
diff --git a/usr/src/uts/common/os/rctl.c b/usr/src/uts/common/os/rctl.c
index fed4331f23..09b80323d5 100644
--- a/usr/src/uts/common/os/rctl.c
+++ b/usr/src/uts/common/os/rctl.c
@@ -3164,3 +3164,18 @@ rctl_kstat_create_project(kproject_t *kpj, char *ks_name, uchar_t ks_type,
return (rctl_kstat_create_common(name, kpj->kpj_id, "project_caps",
ks_type, ks_ndata, ks_flags, kpj->kpj_zoneid));
}
+
+/*
+ * Create task-specific resource kstat
+ */
+kstat_t *
+rctl_kstat_create_task(task_t *tk, char *ks_name, uchar_t ks_type,
+ uint_t ks_ndata, uchar_t ks_flags)
+{
+ char name[KSTAT_STRLEN];
+
+ (void) snprintf(name, KSTAT_STRLEN, "%s_task", ks_name);
+
+ return (rctl_kstat_create_common(name, tk->tk_tkid, "task_caps",
+ ks_type, ks_ndata, ks_flags, tk->tk_proj->kpj_zoneid));
+}
diff --git a/usr/src/uts/common/os/task.c b/usr/src/uts/common/os/task.c
index aa407dd97b..af578d9ce0 100644
--- a/usr/src/uts/common/os/task.c
+++ b/usr/src/uts/common/os/task.c
@@ -19,15 +19,16 @@
* CDDL HEADER END
*/
/*
- * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
*/
#include <sys/atomic.h>
+#include <sys/callb.h>
#include <sys/cmn_err.h>
#include <sys/exacct.h>
#include <sys/id_space.h>
#include <sys/kmem.h>
+#include <sys/kstat.h>
#include <sys/modhash.h>
#include <sys/mutex.h>
#include <sys/proc.h>
@@ -100,9 +101,25 @@ static id_space_t *taskid_space; /* global taskid space */
static kmem_cache_t *task_cache; /* kmem cache for task structures */
rctl_hndl_t rc_task_lwps;
+rctl_hndl_t rc_task_nprocs;
rctl_hndl_t rc_task_cpu_time;
/*
+ * Resource usage is committed using task queues; if taskq_dispatch() fails
+ * due to resource constraints, the task is placed on a list for background
+ * processing by the task_commit_thread() backup thread.
+ */
+static kmutex_t task_commit_lock; /* protects list pointers and cv */
+static kcondvar_t task_commit_cv; /* wakeup task_commit_thread */
+static task_t *task_commit_head = NULL;
+static task_t *task_commit_tail = NULL;
+kthread_t *task_commit_thread;
+
+static void task_commit();
+static kstat_t *task_kstat_create(task_t *, zone_t *);
+static void task_kstat_delete(task_t *);
+
+/*
* static rctl_qty_t task_usage_lwps(void *taskp)
*
* Overview
@@ -167,6 +184,7 @@ task_lwps_test(rctl_t *r, proc_t *p, rctl_entity_p_t *e, rctl_val_t *rcntl,
return (0);
}
+
/*ARGSUSED*/
static int
task_lwps_set(rctl_t *rctl, struct proc *p, rctl_entity_p_t *e, rctl_qty_t nv) {
@@ -180,6 +198,58 @@ task_lwps_set(rctl_t *rctl, struct proc *p, rctl_entity_p_t *e, rctl_qty_t nv) {
return (0);
}
+/*ARGSUSED*/
+static rctl_qty_t
+task_nprocs_usage(rctl_t *r, proc_t *p)
+{
+ task_t *t;
+ rctl_qty_t nprocs;
+
+ ASSERT(MUTEX_HELD(&p->p_lock));
+
+ t = p->p_task;
+ mutex_enter(&p->p_zone->zone_nlwps_lock);
+ nprocs = t->tk_nprocs;
+ mutex_exit(&p->p_zone->zone_nlwps_lock);
+
+ return (nprocs);
+}
+
+/*ARGSUSED*/
+static int
+task_nprocs_test(rctl_t *r, proc_t *p, rctl_entity_p_t *e, rctl_val_t *rcntl,
+ rctl_qty_t incr, uint_t flags)
+{
+ rctl_qty_t nprocs;
+
+ ASSERT(MUTEX_HELD(&p->p_lock));
+ ASSERT(e->rcep_t == RCENTITY_TASK);
+ if (e->rcep_p.task == NULL)
+ return (0);
+
+ ASSERT(MUTEX_HELD(&(e->rcep_p.task->tk_zone->zone_nlwps_lock)));
+ nprocs = e->rcep_p.task->tk_nprocs;
+
+ if (nprocs + incr > rcntl->rcv_value)
+ return (1);
+
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+task_nprocs_set(rctl_t *rctl, struct proc *p, rctl_entity_p_t *e,
+ rctl_qty_t nv) {
+
+ ASSERT(MUTEX_HELD(&p->p_lock));
+ ASSERT(e->rcep_t == RCENTITY_TASK);
+ if (e->rcep_p.task == NULL)
+ return (0);
+
+ e->rcep_p.task->tk_nprocs_ctl = nv;
+ return (0);
+}
+
/*
* static rctl_qty_t task_usage_cpu_secs(void *taskp)
*
@@ -353,7 +423,6 @@ task_hold(task_t *tk)
*
* Caller's context
* Caller must not be holding the task_hash_lock.
- * Caller's context must be acceptable for KM_SLEEP allocations.
*/
void
task_rele(task_t *tk)
@@ -364,10 +433,14 @@ task_rele(task_t *tk)
return;
}
+ ASSERT(tk->tk_nprocs == 0);
+
mutex_enter(&tk->tk_zone->zone_nlwps_lock);
tk->tk_proj->kpj_ntasks--;
mutex_exit(&tk->tk_zone->zone_nlwps_lock);
+ task_kstat_delete(tk);
+
if (mod_hash_destroy(task_hash,
(mod_hash_key_t)(uintptr_t)tk->tk_tkid) != 0)
panic("unable to delete task %d", tk->tk_tkid);
@@ -377,8 +450,22 @@ task_rele(task_t *tk)
* At this point, there are no members or observers of the task, so we
* can safely send it on for commitment to the accounting subsystem.
* The task will be destroyed in task_end() subsequent to commitment.
+ * Since we may be called with pidlock held, taskq_dispatch() cannot
+ * sleep. Commitment is handled by a backup thread in case dispatching
+ * the task fails.
*/
- (void) taskq_dispatch(exacct_queue, exacct_commit_task, tk, KM_SLEEP);
+ if (taskq_dispatch(exacct_queue, exacct_commit_task, tk,
+ TQ_NOSLEEP | TQ_NOQUEUE) == NULL) {
+ mutex_enter(&task_commit_lock);
+ if (task_commit_head == NULL) {
+ task_commit_head = task_commit_tail = tk;
+ } else {
+ task_commit_tail->tk_commit_next = tk;
+ task_commit_tail = tk;
+ }
+ cv_signal(&task_commit_cv);
+ mutex_exit(&task_commit_lock);
+ }
}
/*
@@ -414,10 +501,13 @@ task_create(projid_t projid, zone_t *zone)
tk->tk_tkid = tkid = id_alloc(taskid_space);
tk->tk_nlwps = 0;
tk->tk_nlwps_ctl = INT_MAX;
+ tk->tk_nprocs = 0;
+ tk->tk_nprocs_ctl = INT_MAX;
tk->tk_usage = tu;
tk->tk_inherited = kmem_zalloc(sizeof (task_usage_t), KM_SLEEP);
tk->tk_proj = project_hold_by_id(projid, zone, PROJECT_HOLD_INSERT);
tk->tk_flags = TASK_NORMAL;
+ tk->tk_commit_next = NULL;
/*
* Copy ancestor task's resource controls.
@@ -473,6 +563,7 @@ task_create(projid_t projid, zone_t *zone)
}
mutex_exit(&task_hash_lock);
+ tk->tk_nprocs_kstat = task_kstat_create(tk, zone);
return (tk);
}
@@ -495,7 +586,6 @@ void
task_attach(task_t *tk, proc_t *p)
{
proc_t *first, *prev;
- rctl_entity_p_t e;
ASSERT(tk != NULL);
ASSERT(p != NULL);
ASSERT(MUTEX_HELD(&pidlock));
@@ -515,22 +605,6 @@ task_attach(task_t *tk, proc_t *p)
tk->tk_memb_list = p;
task_hold(tk);
p->p_task = tk;
-
- /*
- * Now that the linkage from process to task and project is
- * complete, do the required callbacks for the task and project
- * rctl sets.
- */
- e.rcep_p.proj = tk->tk_proj;
- e.rcep_t = RCENTITY_PROJECT;
- (void) rctl_set_dup(NULL, NULL, p, &e, tk->tk_proj->kpj_rctls, NULL,
- RCD_CALLBACK);
-
- e.rcep_p.task = tk;
- e.rcep_t = RCENTITY_TASK;
- (void) rctl_set_dup(NULL, NULL, p, &e, tk->tk_rctls, NULL,
- RCD_CALLBACK);
-
}
/*
@@ -551,6 +625,7 @@ task_begin(task_t *tk, proc_t *p)
{
timestruc_t ts;
task_usage_t *tu;
+ rctl_entity_p_t e;
ASSERT(MUTEX_HELD(&pidlock));
ASSERT(MUTEX_HELD(&p->p_lock));
@@ -566,6 +641,15 @@ task_begin(task_t *tk, proc_t *p)
* Join process to the task as a member.
*/
task_attach(tk, p);
+
+ /*
+ * Now that the linkage from process to task is complete, do the
+ * required callback for the task rctl set.
+ */
+ e.rcep_p.task = tk;
+ e.rcep_t = RCENTITY_TASK;
+ (void) rctl_set_dup(NULL, NULL, p, &e, tk->tk_rctls, NULL,
+ RCD_CALLBACK);
}
/*
@@ -638,10 +722,12 @@ task_change(task_t *newtk, proc_t *p)
mutex_enter(&oldtk->tk_zone->zone_nlwps_lock);
oldtk->tk_nlwps -= p->p_lwpcnt;
+ oldtk->tk_nprocs--;
mutex_exit(&oldtk->tk_zone->zone_nlwps_lock);
mutex_enter(&newtk->tk_zone->zone_nlwps_lock);
newtk->tk_nlwps += p->p_lwpcnt;
+ newtk->tk_nprocs++;
mutex_exit(&newtk->tk_zone->zone_nlwps_lock);
task_detach(p);
@@ -828,6 +914,13 @@ static rctl_ops_t task_lwps_ops = {
task_lwps_test
};
+static rctl_ops_t task_procs_ops = {
+ rcop_no_action,
+ task_nprocs_usage,
+ task_nprocs_set,
+ task_nprocs_test
+};
+
static rctl_ops_t task_cpu_time_ops = {
rcop_no_action,
task_cpu_time_usage,
@@ -858,6 +951,7 @@ task_init(void)
rctl_set_t *set;
rctl_alloc_gp_t *gp;
rctl_entity_p_t e;
+
/*
* Initialize task_cache and taskid_space.
*/
@@ -877,6 +971,9 @@ task_init(void)
rc_task_lwps = rctl_register("task.max-lwps", RCENTITY_TASK,
RCTL_GLOBAL_NOACTION | RCTL_GLOBAL_COUNT, INT_MAX, INT_MAX,
&task_lwps_ops);
+ rc_task_nprocs = rctl_register("task.max-processes", RCENTITY_TASK,
+ RCTL_GLOBAL_NOACTION | RCTL_GLOBAL_COUNT, INT_MAX, INT_MAX,
+ &task_procs_ops);
rc_task_cpu_time = rctl_register("task.max-cpu-time", RCENTITY_TASK,
RCTL_GLOBAL_NOACTION | RCTL_GLOBAL_DENY_NEVER |
RCTL_GLOBAL_CPU_TIME | RCTL_GLOBAL_INFINITE |
@@ -896,7 +993,9 @@ task_init(void)
PROJECT_HOLD_INSERT);
task0p->tk_flags = TASK_NORMAL;
task0p->tk_nlwps = p->p_lwpcnt;
+ task0p->tk_nprocs = 1;
task0p->tk_zone = global_zone;
+ task0p->tk_commit_next = NULL;
set = rctl_set_create();
gp = rctl_set_init_prealloc(RCENTITY_TASK);
@@ -921,6 +1020,8 @@ task_init(void)
task0p->tk_memb_list = p;
+ task0p->tk_nprocs_kstat = task_kstat_create(task0p, task0p->tk_zone);
+
/*
* Initialize task pointers for p0, including doubly linked list of task
* members.
@@ -929,3 +1030,99 @@ task_init(void)
p->p_taskprev = p->p_tasknext = p;
task_hold(task0p);
}
+
+static int
+task_nprocs_kstat_update(kstat_t *ksp, int rw)
+{
+ task_t *tk = ksp->ks_private;
+ task_kstat_t *ktk = ksp->ks_data;
+
+ if (rw == KSTAT_WRITE)
+ return (EACCES);
+
+ ktk->ktk_usage.value.ui64 = tk->tk_nprocs;
+ ktk->ktk_value.value.ui64 = tk->tk_nprocs_ctl;
+ return (0);
+}
+
+static kstat_t *
+task_kstat_create(task_t *tk, zone_t *zone)
+{
+ kstat_t *ksp;
+ task_kstat_t *ktk;
+ char *zonename = zone->zone_name;
+
+ ksp = rctl_kstat_create_task(tk, "nprocs", KSTAT_TYPE_NAMED,
+ sizeof (task_kstat_t) / sizeof (kstat_named_t),
+ KSTAT_FLAG_VIRTUAL);
+
+ if (ksp == NULL)
+ return (NULL);
+
+ ktk = ksp->ks_data = kmem_alloc(sizeof (task_kstat_t), KM_SLEEP);
+ ksp->ks_data_size += strlen(zonename) + 1;
+ kstat_named_init(&ktk->ktk_zonename, "zonename", KSTAT_DATA_STRING);
+ kstat_named_setstr(&ktk->ktk_zonename, zonename);
+ kstat_named_init(&ktk->ktk_usage, "usage", KSTAT_DATA_UINT64);
+ kstat_named_init(&ktk->ktk_value, "value", KSTAT_DATA_UINT64);
+ ksp->ks_update = task_nprocs_kstat_update;
+ ksp->ks_private = tk;
+ kstat_install(ksp);
+
+ return (ksp);
+}
+
+static void
+task_kstat_delete(task_t *tk)
+{
+ void *data;
+
+ if (tk->tk_nprocs_kstat != NULL) {
+ data = tk->tk_nprocs_kstat->ks_data;
+ kstat_delete(tk->tk_nprocs_kstat);
+ kmem_free(data, sizeof (task_kstat_t));
+ tk->tk_nprocs_kstat = NULL;
+ }
+}
+
+void
+task_commit_thread_init()
+{
+ mutex_init(&task_commit_lock, NULL, MUTEX_DEFAULT, NULL);
+ cv_init(&task_commit_cv, NULL, CV_DEFAULT, NULL);
+ task_commit_thread = thread_create(NULL, 0, task_commit, NULL, 0,
+ &p0, TS_RUN, minclsyspri);
+}
+
+/*
+ * Backup thread to commit task resource usage when taskq_dispatch() fails.
+ */
+static void
+task_commit()
+{
+ callb_cpr_t cprinfo;
+
+ CALLB_CPR_INIT(&cprinfo, &task_commit_lock, callb_generic_cpr,
+ "task_commit_thread");
+
+ mutex_enter(&task_commit_lock);
+
+ for (;;) {
+ while (task_commit_head == NULL) {
+ CALLB_CPR_SAFE_BEGIN(&cprinfo);
+ cv_wait(&task_commit_cv, &task_commit_lock);
+ CALLB_CPR_SAFE_END(&cprinfo, &task_commit_lock);
+ }
+ while (task_commit_head != NULL) {
+ task_t *tk;
+
+ tk = task_commit_head;
+ task_commit_head = task_commit_head->tk_commit_next;
+ if (task_commit_head == NULL)
+ task_commit_tail = NULL;
+ mutex_exit(&task_commit_lock);
+ exacct_commit_task(tk);
+ mutex_enter(&task_commit_lock);
+ }
+ }
+}
diff --git a/usr/src/uts/common/os/zone.c b/usr/src/uts/common/os/zone.c
index d29f6b9986..caf10ee8df 100644
--- a/usr/src/uts/common/os/zone.c
+++ b/usr/src/uts/common/os/zone.c
@@ -171,7 +171,7 @@
*
* When taking zone_mem_lock or zone_nlwps_lock, the lock ordering is:
* zonehash_lock --> a_lock --> pidlock --> p_lock --> zone_mem_lock
- * zonehash_lock --> a_lock --> pidlock --> p_lock --> zone_mem_lock
+ * zonehash_lock --> a_lock --> pidlock --> p_lock --> zone_nlwps_lock
*
* Blocking memory allocations are permitted while holding any of the
* zone locks.
@@ -345,6 +345,7 @@ rctl_hndl_t rc_zone_max_swap;
rctl_hndl_t rc_zone_max_lofi;
rctl_hndl_t rc_zone_cpu_cap;
rctl_hndl_t rc_zone_nlwps;
+rctl_hndl_t rc_zone_nprocs;
rctl_hndl_t rc_zone_shmmax;
rctl_hndl_t rc_zone_shmmni;
rctl_hndl_t rc_zone_semmni;
@@ -1404,6 +1405,61 @@ static rctl_ops_t zone_lwps_ops = {
};
/*ARGSUSED*/
+static rctl_qty_t
+zone_procs_usage(rctl_t *r, proc_t *p)
+{
+ rctl_qty_t nprocs;
+ zone_t *zone = p->p_zone;
+
+ ASSERT(MUTEX_HELD(&p->p_lock));
+
+ mutex_enter(&zone->zone_nlwps_lock);
+ nprocs = zone->zone_nprocs;
+ mutex_exit(&zone->zone_nlwps_lock);
+
+ return (nprocs);
+}
+
+/*ARGSUSED*/
+static int
+zone_procs_test(rctl_t *r, proc_t *p, rctl_entity_p_t *e, rctl_val_t *rcntl,
+ rctl_qty_t incr, uint_t flags)
+{
+ rctl_qty_t nprocs;
+
+ ASSERT(MUTEX_HELD(&p->p_lock));
+ ASSERT(e->rcep_t == RCENTITY_ZONE);
+ if (e->rcep_p.zone == NULL)
+ return (0);
+ ASSERT(MUTEX_HELD(&(e->rcep_p.zone->zone_nlwps_lock)));
+ nprocs = e->rcep_p.zone->zone_nprocs;
+
+ if (nprocs + incr > rcntl->rcv_value)
+ return (1);
+
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+zone_procs_set(rctl_t *rctl, struct proc *p, rctl_entity_p_t *e, rctl_qty_t nv)
+{
+ ASSERT(MUTEX_HELD(&p->p_lock));
+ ASSERT(e->rcep_t == RCENTITY_ZONE);
+ if (e->rcep_p.zone == NULL)
+ return (0);
+ e->rcep_p.zone->zone_nprocs_ctl = nv;
+ return (0);
+}
+
+static rctl_ops_t zone_procs_ops = {
+ rcop_no_action,
+ zone_procs_usage,
+ zone_procs_set,
+ zone_procs_test,
+};
+
+/*ARGSUSED*/
static int
zone_shmmax_test(rctl_t *r, proc_t *p, rctl_entity_p_t *e, rctl_val_t *rval,
rctl_qty_t incr, uint_t flags)
@@ -1682,6 +1738,20 @@ zone_lockedmem_kstat_update(kstat_t *ksp, int rw)
}
static int
+zone_nprocs_kstat_update(kstat_t *ksp, int rw)
+{
+ zone_t *zone = ksp->ks_private;
+ zone_kstat_t *zk = ksp->ks_data;
+
+ if (rw == KSTAT_WRITE)
+ return (EACCES);
+
+ zk->zk_usage.value.ui64 = zone->zone_nprocs;
+ zk->zk_value.value.ui64 = zone->zone_nprocs_ctl;
+ return (0);
+}
+
+static int
zone_swapresv_kstat_update(kstat_t *ksp, int rw)
{
zone_t *zone = ksp->ks_private;
@@ -1695,37 +1765,19 @@ zone_swapresv_kstat_update(kstat_t *ksp, int rw)
return (0);
}
-static void
-zone_kstat_create(zone_t *zone)
+static kstat_t *
+zone_kstat_create_common(zone_t *zone, char *name,
+ int (*updatefunc) (kstat_t *, int))
{
kstat_t *ksp;
zone_kstat_t *zk;
- ksp = rctl_kstat_create_zone(zone, "lockedmem", KSTAT_TYPE_NAMED,
+ ksp = rctl_kstat_create_zone(zone, name, KSTAT_TYPE_NAMED,
sizeof (zone_kstat_t) / sizeof (kstat_named_t),
KSTAT_FLAG_VIRTUAL);
if (ksp == NULL)
- return;
-
- zk = ksp->ks_data = kmem_alloc(sizeof (zone_kstat_t), KM_SLEEP);
- ksp->ks_data_size += strlen(zone->zone_name) + 1;
- kstat_named_init(&zk->zk_zonename, "zonename", KSTAT_DATA_STRING);
- kstat_named_setstr(&zk->zk_zonename, zone->zone_name);
- kstat_named_init(&zk->zk_usage, "usage", KSTAT_DATA_UINT64);
- kstat_named_init(&zk->zk_value, "value", KSTAT_DATA_UINT64);
- ksp->ks_update = zone_lockedmem_kstat_update;
- ksp->ks_private = zone;
- kstat_install(ksp);
-
- zone->zone_lockedmem_kstat = ksp;
-
- ksp = rctl_kstat_create_zone(zone, "swapresv", KSTAT_TYPE_NAMED,
- sizeof (zone_kstat_t) / sizeof (kstat_named_t),
- KSTAT_FLAG_VIRTUAL);
-
- if (ksp == NULL)
- return;
+ return (NULL);
zk = ksp->ks_data = kmem_alloc(sizeof (zone_kstat_t), KM_SLEEP);
ksp->ks_data_size += strlen(zone->zone_name) + 1;
@@ -1733,30 +1785,44 @@ zone_kstat_create(zone_t *zone)
kstat_named_setstr(&zk->zk_zonename, zone->zone_name);
kstat_named_init(&zk->zk_usage, "usage", KSTAT_DATA_UINT64);
kstat_named_init(&zk->zk_value, "value", KSTAT_DATA_UINT64);
- ksp->ks_update = zone_swapresv_kstat_update;
+ ksp->ks_update = updatefunc;
ksp->ks_private = zone;
kstat_install(ksp);
+ return (ksp);
+}
- zone->zone_swapresv_kstat = ksp;
+static void
+zone_kstat_create(zone_t *zone)
+{
+ zone->zone_lockedmem_kstat = zone_kstat_create_common(zone,
+ "lockedmem", zone_lockedmem_kstat_update);
+ zone->zone_swapresv_kstat = zone_kstat_create_common(zone,
+ "swapresv", zone_swapresv_kstat_update);
+ zone->zone_nprocs_kstat = zone_kstat_create_common(zone,
+ "nprocs", zone_nprocs_kstat_update);
}
static void
-zone_kstat_delete(zone_t *zone)
+zone_kstat_delete_common(kstat_t **pkstat)
{
void *data;
- if (zone->zone_lockedmem_kstat != NULL) {
- data = zone->zone_lockedmem_kstat->ks_data;
- kstat_delete(zone->zone_lockedmem_kstat);
- kmem_free(data, sizeof (zone_kstat_t));
- }
- if (zone->zone_swapresv_kstat != NULL) {
- data = zone->zone_swapresv_kstat->ks_data;
- kstat_delete(zone->zone_swapresv_kstat);
+ if (*pkstat != NULL) {
+ data = (*pkstat)->ks_data;
+ kstat_delete(*pkstat);
kmem_free(data, sizeof (zone_kstat_t));
+ *pkstat = NULL;
}
}
+static void
+zone_kstat_delete(zone_t *zone)
+{
+ zone_kstat_delete_common(&zone->zone_lockedmem_kstat);
+ zone_kstat_delete_common(&zone->zone_swapresv_kstat);
+ zone_kstat_delete_common(&zone->zone_nprocs_kstat);
+}
+
/*
* Called very early on in boot to initialize the ZSD list so that
* zone_key_create() can be called before zone_init(). It also initializes
@@ -1782,6 +1848,8 @@ zone_zsd_init(void)
zone0.zone_shares = 1;
zone0.zone_nlwps = 0;
zone0.zone_nlwps_ctl = INT_MAX;
+ zone0.zone_nprocs = 0;
+ zone0.zone_nprocs_ctl = INT_MAX;
zone0.zone_locked_mem = 0;
zone0.zone_locked_mem_ctl = UINT64_MAX;
ASSERT(zone0.zone_max_swap == 0);
@@ -1809,6 +1877,7 @@ zone_zsd_init(void)
zone0.zone_initname = initname;
zone0.zone_lockedmem_kstat = NULL;
zone0.zone_swapresv_kstat = NULL;
+ zone0.zone_nprocs_kstat = NULL;
list_create(&zone0.zone_zsd, sizeof (struct zsd_entry),
offsetof(struct zsd_entry, zsd_linkage));
list_insert_head(&zone_active, &zone0);
@@ -1916,6 +1985,11 @@ zone_init(void)
rc_zone_nlwps = rctl_register("zone.max-lwps", RCENTITY_ZONE,
RCTL_GLOBAL_NOACTION | RCTL_GLOBAL_NOBASIC | RCTL_GLOBAL_COUNT,
INT_MAX, INT_MAX, &zone_lwps_ops);
+
+ rc_zone_nprocs = rctl_register("zone.max-processes", RCENTITY_ZONE,
+ RCTL_GLOBAL_NOACTION | RCTL_GLOBAL_NOBASIC | RCTL_GLOBAL_COUNT,
+ INT_MAX, INT_MAX, &zone_procs_ops);
+
/*
* System V IPC resource controls
*/
@@ -1976,6 +2050,7 @@ zone_init(void)
gp);
zone0.zone_nlwps = p0.p_lwpcnt;
+ zone0.zone_nprocs = 1;
zone0.zone_ntasks = 1;
mutex_exit(&p0.p_lock);
zone0.zone_restart_init = B_TRUE;
@@ -2061,6 +2136,7 @@ zone_free(zone_t *zone)
ASSERT(zone != global_zone);
ASSERT(zone->zone_ntasks == 0);
ASSERT(zone->zone_nlwps == 0);
+ ASSERT(zone->zone_nprocs == 0);
ASSERT(zone->zone_cred_ref == 0);
ASSERT(zone->zone_kcred == NULL);
ASSERT(zone_status_get(zone) == ZONE_IS_DEAD ||
@@ -3352,12 +3428,14 @@ zsched(void *arg)
mutex_exit(&pidlock);
/*
- * getting out of global zone, so decrement lwp counts
+ * getting out of global zone, so decrement lwp and process counts
*/
pj = pp->p_task->tk_proj;
mutex_enter(&global_zone->zone_nlwps_lock);
pj->kpj_nlwps -= pp->p_lwpcnt;
global_zone->zone_nlwps -= pp->p_lwpcnt;
+ pj->kpj_nprocs--;
+ global_zone->zone_nprocs--;
mutex_exit(&global_zone->zone_nlwps_lock);
/*
@@ -3388,14 +3466,16 @@ zsched(void *arg)
mutex_exit(&zone->zone_mem_lock);
/*
- * add lwp counts to zsched's zone, and increment project's task count
- * due to the task created in the above tasksys_settaskid
+ * add lwp and process counts to zsched's zone, and increment
+ * project's task and process count due to the task created in
+ * the above task_create.
*/
-
mutex_enter(&zone->zone_nlwps_lock);
pj->kpj_nlwps += pp->p_lwpcnt;
pj->kpj_ntasks += 1;
zone->zone_nlwps += pp->p_lwpcnt;
+ pj->kpj_nprocs++;
+ zone->zone_nprocs++;
mutex_exit(&zone->zone_nlwps_lock);
mutex_exit(&curproc->p_lock);
@@ -3946,6 +4026,8 @@ zone_create(const char *zone_name, const char *zone_root,
(void) strcpy(zone->zone_initname, zone_default_initname);
zone->zone_nlwps = 0;
zone->zone_nlwps_ctl = INT_MAX;
+ zone->zone_nprocs = 0;
+ zone->zone_nprocs_ctl = INT_MAX;
zone->zone_locked_mem = 0;
zone->zone_locked_mem_ctl = UINT64_MAX;
zone->zone_max_swap = 0;
@@ -5259,6 +5341,9 @@ zone_enter(zoneid_t zoneid)
zone->zone_nlwps += pp->p_lwpcnt;
/* add 1 task to zone's proj0 */
zone_proj0->kpj_ntasks += 1;
+
+ zone_proj0->kpj_nprocs++;
+ zone->zone_nprocs++;
mutex_exit(&zone->zone_nlwps_lock);
mutex_enter(&zone->zone_mem_lock);
@@ -5271,10 +5356,12 @@ zone_enter(zoneid_t zoneid)
zone_proj0->kpj_data.kpd_crypto_mem += pp->p_crypto_mem;
mutex_exit(&(zone_proj0->kpj_data.kpd_crypto_lock));
- /* remove lwps from proc's old zone and old project */
+ /* remove lwps and process from proc's old zone and old project */
mutex_enter(&pp->p_zone->zone_nlwps_lock);
pp->p_zone->zone_nlwps -= pp->p_lwpcnt;
pp->p_task->tk_proj->kpj_nlwps -= pp->p_lwpcnt;
+ pp->p_task->tk_proj->kpj_nprocs--;
+ pp->p_zone->zone_nprocs--;
mutex_exit(&pp->p_zone->zone_nlwps_lock);
mutex_enter(&pp->p_zone->zone_mem_lock);
diff --git a/usr/src/uts/common/sys/project.h b/usr/src/uts/common/sys/project.h
index a46a8082c6..308c05044e 100644
--- a/usr/src/uts/common/sys/project.h
+++ b/usr/src/uts/common/sys/project.h
@@ -19,16 +19,12 @@
* CDDL HEADER END
*/
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
*/
#ifndef _SYS_PROJECT_H
#define _SYS_PROJECT_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
-
#ifdef __cplusplus
extern "C" {
#endif
@@ -57,7 +53,7 @@ typedef struct kproject_data { /* Datum protected by: */
rctl_qty_t kpd_crypto_mem; /* kpd_crypto_lock above */
rctl_qty_t kpd_crypto_mem_ctl; /* kpj_rctls->rcs_lock */
kstat_t *kpd_lockedmem_kstat; /* locked memory kstat */
-
+ kstat_t *kpd_nprocs_kstat;
} kproject_data_t;
struct cpucap;
@@ -85,6 +81,9 @@ typedef struct kproject {
struct cpucap *kpj_cpucap; /* CPU cap data */
struct klpd_reg *kpj_klpd; /* our extended policy */
/* protected by klpd_mutex */
+ rctl_qty_t kpj_nprocs; /* protected by project's zone's */
+ /* zone_nlwps_lock */
+ rctl_qty_t kpj_nprocs_ctl; /* protected by kpj_rctls->rcs_lock */
} kproject_t;
#ifdef _KERNEL
@@ -104,6 +103,7 @@ projid_t curprojid(void);
extern kproject_t *proj0p;
extern rctl_hndl_t rc_project_nlwps;
+extern rctl_hndl_t rc_project_nprocs;
extern rctl_hndl_t rc_project_ntasks;
extern rctl_hndl_t rc_project_locked_mem;
extern rctl_hndl_t rc_project_crypto_mem;
diff --git a/usr/src/uts/common/sys/rctl.h b/usr/src/uts/common/sys/rctl.h
index c376ac2df7..cbcdb87a22 100644
--- a/usr/src/uts/common/sys/rctl.h
+++ b/usr/src/uts/common/sys/rctl.h
@@ -345,6 +345,9 @@ struct kstat *rctl_kstat_create_zone(struct zone *, char *, uchar_t, uint_t,
struct kstat *rctl_kstat_create_project(struct kproject *, char *, uchar_t,
uint_t, uchar_t);
+struct kstat *rctl_kstat_create_task(struct task *, char *, uchar_t,
+ uint_t, uchar_t);
+
#endif /* _KERNEL */
#ifdef __cplusplus
diff --git a/usr/src/uts/common/sys/task.h b/usr/src/uts/common/sys/task.h
index a22f64d970..a6051b79d1 100644
--- a/usr/src/uts/common/sys/task.h
+++ b/usr/src/uts/common/sys/task.h
@@ -19,15 +19,12 @@
* CDDL HEADER END
*/
/*
- * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
*/
#ifndef _SYS_TASK_H
#define _SYS_TASK_H
-#pragma ident "%Z%%M% %I% %E% SMI"
-
#ifdef __cplusplus
extern "C" {
#endif
@@ -75,10 +72,22 @@ typedef struct task {
/* member process */
rctl_qty_t tk_cpu_ticks; /* accumulated CPU ticks */
kmutex_t tk_cpu_time_lock; /* accumulated CPU seconds lock */
+ rctl_qty_t tk_nprocs; /* protected by */
+ /* tk_zone->zone_nlwps_lock */
+ rctl_qty_t tk_nprocs_ctl; /* protected by tk_rctls->rcs_lock */
+ kstat_t *tk_nprocs_kstat; /* max-processes rctl kstat */
+ struct task *tk_commit_next; /* next task on task commit list */
} task_t;
+typedef struct task_kstat {
+ kstat_named_t ktk_zonename;
+ kstat_named_t ktk_usage;
+ kstat_named_t ktk_value;
+} task_kstat_t;
+
extern task_t *task0p;
extern rctl_hndl_t rc_task_lwps;
+extern rctl_hndl_t rc_task_nprocs;
extern rctl_hndl_t rc_task_cpu_time;
extern void task_init(void);
@@ -94,6 +103,7 @@ extern void task_rele(task_t *);
extern void task_hold(task_t *);
extern void task_end(task_t *);
extern rctl_qty_t task_cpu_time_incr(task_t *, rctl_qty_t);
+extern void task_commit_thread_init(void);
#else /* _KERNEL */
diff --git a/usr/src/uts/common/sys/zone.h b/usr/src/uts/common/sys/zone.h
index 5c61a6833e..0a513f8cf7 100644
--- a/usr/src/uts/common/sys/zone.h
+++ b/usr/src/uts/common/sys/zone.h
@@ -453,6 +453,10 @@ typedef struct zone {
struct klpd_reg *zone_pfexecd;
char *zone_fs_allowed;
+ rctl_qty_t zone_nprocs; /* number of processes in the zone */
+ rctl_qty_t zone_nprocs_ctl; /* current limit protected by */
+ /* zone_rctls->rcs_lock */
+ kstat_t *zone_nprocs_kstat;
} zone_t;
/*
@@ -465,6 +469,7 @@ extern zone_t zone0;
extern zone_t *global_zone;
extern uint_t maxzones;
extern rctl_hndl_t rc_zone_nlwps;
+extern rctl_hndl_t rc_zone_nprocs;
extern long zone(int, void *, void *, void *, void *);
extern void zone_zsd_init(void);
diff --git a/usr/src/uts/common/syscall/tasksys.c b/usr/src/uts/common/syscall/tasksys.c
index 7038a19347..bbaaba9f5d 100644
--- a/usr/src/uts/common/syscall/tasksys.c
+++ b/usr/src/uts/common/syscall/tasksys.c
@@ -19,12 +19,9 @@
* CDDL HEADER END
*/
/*
- * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
*/
-#pragma ident "%Z%%M% %I% %E% SMI"
-
/*
* System calls for creating and inquiring about tasks and projects
@@ -124,6 +121,11 @@ tasksys_settaskid(projid_t projid, uint_t flags)
1, 0) & RCT_DENY)
rctlfail = 1;
+ if (kpj != proj0p && kpj->kpj_nprocs + 1 > kpj->kpj_nprocs_ctl)
+ if (rctl_test_entity(rc_project_nprocs, kpj->kpj_rctls, p, &e,
+ 1, 0) & RCT_DENY)
+ rctlfail = 1;
+
if (kpj->kpj_data.kpd_locked_mem + p->p_locked_mem >
kpj->kpj_data.kpd_locked_mem_ctl)
if (rctl_test_entity(rc_project_locked_mem, kpj->kpj_rctls, p,
@@ -151,12 +153,14 @@ tasksys_settaskid(projid_t projid, uint_t flags)
kpj->kpj_data.kpd_locked_mem += p->p_locked_mem;
kpj->kpj_nlwps += p->p_lwpcnt;
kpj->kpj_ntasks++;
+ kpj->kpj_nprocs++;
oldpj->kpj_data.kpd_locked_mem -= p->p_locked_mem;
mutex_enter(&(oldpj->kpj_data.kpd_crypto_lock));
oldpj->kpj_data.kpd_crypto_mem -= p->p_crypto_mem;
mutex_exit(&(oldpj->kpj_data.kpd_crypto_lock));
oldpj->kpj_nlwps -= p->p_lwpcnt;
+ oldpj->kpj_nprocs--;
mutex_exit(&zone->zone_mem_lock);
mutex_exit(&zone->zone_nlwps_lock);