1*0Sstevel@tonic-gate /* 2*0Sstevel@tonic-gate * CDDL HEADER START 3*0Sstevel@tonic-gate * 4*0Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*0Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 6*0Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 7*0Sstevel@tonic-gate * with the License. 8*0Sstevel@tonic-gate * 9*0Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*0Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 11*0Sstevel@tonic-gate * See the License for the specific language governing permissions 12*0Sstevel@tonic-gate * and limitations under the License. 13*0Sstevel@tonic-gate * 14*0Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 15*0Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*0Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 17*0Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 18*0Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 19*0Sstevel@tonic-gate * 20*0Sstevel@tonic-gate * CDDL HEADER END 21*0Sstevel@tonic-gate */ 22*0Sstevel@tonic-gate /* 23*0Sstevel@tonic-gate * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24*0Sstevel@tonic-gate * Use is subject to license terms. 25*0Sstevel@tonic-gate */ 26*0Sstevel@tonic-gate 27*0Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 28*0Sstevel@tonic-gate 29*0Sstevel@tonic-gate #include <sys/atomic.h> 30*0Sstevel@tonic-gate #include <sys/cmn_err.h> 31*0Sstevel@tonic-gate #include <sys/exacct.h> 32*0Sstevel@tonic-gate #include <sys/id_space.h> 33*0Sstevel@tonic-gate #include <sys/kmem.h> 34*0Sstevel@tonic-gate #include <sys/modhash.h> 35*0Sstevel@tonic-gate #include <sys/mutex.h> 36*0Sstevel@tonic-gate #include <sys/proc.h> 37*0Sstevel@tonic-gate #include <sys/project.h> 38*0Sstevel@tonic-gate #include <sys/rctl.h> 39*0Sstevel@tonic-gate #include <sys/systm.h> 40*0Sstevel@tonic-gate #include <sys/task.h> 41*0Sstevel@tonic-gate #include <sys/time.h> 42*0Sstevel@tonic-gate #include <sys/types.h> 43*0Sstevel@tonic-gate #include <sys/zone.h> 44*0Sstevel@tonic-gate #include <sys/cpuvar.h> 45*0Sstevel@tonic-gate #include <sys/fss.h> 46*0Sstevel@tonic-gate #include <sys/class.h> 47*0Sstevel@tonic-gate #include <sys/project.h> 48*0Sstevel@tonic-gate 49*0Sstevel@tonic-gate /* 50*0Sstevel@tonic-gate * Tasks 51*0Sstevel@tonic-gate * 52*0Sstevel@tonic-gate * A task is a collection of processes, associated with a common project ID 53*0Sstevel@tonic-gate * and related by a common initial parent. The task primarily represents a 54*0Sstevel@tonic-gate * natural process sequence with known resource usage, although it can also be 55*0Sstevel@tonic-gate * viewed as a convenient grouping of processes for signal delivery, processor 56*0Sstevel@tonic-gate * binding, and administrative operations. 57*0Sstevel@tonic-gate * 58*0Sstevel@tonic-gate * Membership and observership 59*0Sstevel@tonic-gate * We can conceive of situations where processes outside of the task may wish 60*0Sstevel@tonic-gate * to examine the resource usage of the task. Similarly, a number of the 61*0Sstevel@tonic-gate * administrative operations on a task can be performed by processes who are 62*0Sstevel@tonic-gate * not members of the task. Accordingly, we must design a locking strategy 63*0Sstevel@tonic-gate * where observers of the task, who wish to examine or operate on the task, 64*0Sstevel@tonic-gate * and members of task, who can perform the mentioned operations, as well as 65*0Sstevel@tonic-gate * leave the task, see a consistent and correct representation of the task at 66*0Sstevel@tonic-gate * all times. 67*0Sstevel@tonic-gate * 68*0Sstevel@tonic-gate * Locking 69*0Sstevel@tonic-gate * Because the task membership is a new relation between processes, its 70*0Sstevel@tonic-gate * locking becomes an additional responsibility of the pidlock/p_lock locking 71*0Sstevel@tonic-gate * sequence; however, tasks closely resemble sessions and the session locking 72*0Sstevel@tonic-gate * model is mostly appropriate for the interaction of tasks, processes, and 73*0Sstevel@tonic-gate * procfs. 74*0Sstevel@tonic-gate * 75*0Sstevel@tonic-gate * kmutex_t task_hash_lock 76*0Sstevel@tonic-gate * task_hash_lock is a global lock protecting the contents of the task 77*0Sstevel@tonic-gate * ID-to-task pointer hash. Holders of task_hash_lock must not attempt to 78*0Sstevel@tonic-gate * acquire pidlock or p_lock. 79*0Sstevel@tonic-gate * uint_t tk_hold_count 80*0Sstevel@tonic-gate * tk_hold_count, the number of members and observers of the current task, 81*0Sstevel@tonic-gate * must be manipulated atomically. 82*0Sstevel@tonic-gate * proc_t *tk_memb_list 83*0Sstevel@tonic-gate * proc_t *p_tasknext 84*0Sstevel@tonic-gate * proc_t *p_taskprev 85*0Sstevel@tonic-gate * The task's membership list is protected by pidlock, and is therefore 86*0Sstevel@tonic-gate * always acquired before any of its members' p_lock mutexes. The p_task 87*0Sstevel@tonic-gate * member of the proc structure is protected by pidlock or p_lock for 88*0Sstevel@tonic-gate * reading, and by both pidlock and p_lock for modification, as is done for 89*0Sstevel@tonic-gate * p_sessp. The key point is that only the process can modify its p_task, 90*0Sstevel@tonic-gate * and not any entity on the system. (/proc will use prlock() to prevent 91*0Sstevel@tonic-gate * the process from leaving, as opposed to pidlock.) 92*0Sstevel@tonic-gate * kmutex_t tk_usage_lock 93*0Sstevel@tonic-gate * tk_usage_lock is a per-task lock protecting the contents of the task 94*0Sstevel@tonic-gate * usage structure and tk_nlwps counter for the task.max-lwps resource 95*0Sstevel@tonic-gate * control. 96*0Sstevel@tonic-gate */ 97*0Sstevel@tonic-gate 98*0Sstevel@tonic-gate int task_hash_size = 256; 99*0Sstevel@tonic-gate static kmutex_t task_hash_lock; 100*0Sstevel@tonic-gate static mod_hash_t *task_hash; 101*0Sstevel@tonic-gate 102*0Sstevel@tonic-gate static id_space_t *taskid_space; /* global taskid space */ 103*0Sstevel@tonic-gate static kmem_cache_t *task_cache; /* kmem cache for task structures */ 104*0Sstevel@tonic-gate 105*0Sstevel@tonic-gate rctl_hndl_t rc_task_lwps; 106*0Sstevel@tonic-gate rctl_hndl_t rc_task_cpu_time; 107*0Sstevel@tonic-gate 108*0Sstevel@tonic-gate /* 109*0Sstevel@tonic-gate * static rctl_qty_t task_usage_lwps(void *taskp) 110*0Sstevel@tonic-gate * 111*0Sstevel@tonic-gate * Overview 112*0Sstevel@tonic-gate * task_usage_lwps() is the usage operation for the resource control 113*0Sstevel@tonic-gate * associated with the number of LWPs in a task. 114*0Sstevel@tonic-gate * 115*0Sstevel@tonic-gate * Return values 116*0Sstevel@tonic-gate * The number of LWPs in the given task is returned. 117*0Sstevel@tonic-gate * 118*0Sstevel@tonic-gate * Caller's context 119*0Sstevel@tonic-gate * The p->p_lock must be held across the call. 120*0Sstevel@tonic-gate */ 121*0Sstevel@tonic-gate /*ARGSUSED*/ 122*0Sstevel@tonic-gate static rctl_qty_t 123*0Sstevel@tonic-gate task_lwps_usage(rctl_t *r, proc_t *p) 124*0Sstevel@tonic-gate { 125*0Sstevel@tonic-gate task_t *t; 126*0Sstevel@tonic-gate rctl_qty_t nlwps; 127*0Sstevel@tonic-gate 128*0Sstevel@tonic-gate ASSERT(MUTEX_HELD(&p->p_lock)); 129*0Sstevel@tonic-gate 130*0Sstevel@tonic-gate t = p->p_task; 131*0Sstevel@tonic-gate mutex_enter(&p->p_zone->zone_nlwps_lock); 132*0Sstevel@tonic-gate nlwps = t->tk_nlwps; 133*0Sstevel@tonic-gate mutex_exit(&p->p_zone->zone_nlwps_lock); 134*0Sstevel@tonic-gate 135*0Sstevel@tonic-gate return (nlwps); 136*0Sstevel@tonic-gate } 137*0Sstevel@tonic-gate 138*0Sstevel@tonic-gate /* 139*0Sstevel@tonic-gate * static int task_test_lwps(void *taskp, rctl_val_t *, int64_t incr, 140*0Sstevel@tonic-gate * int flags) 141*0Sstevel@tonic-gate * 142*0Sstevel@tonic-gate * Overview 143*0Sstevel@tonic-gate * task_test_lwps() is the test-if-valid-increment for the resource control 144*0Sstevel@tonic-gate * for the number of processes in a task. 145*0Sstevel@tonic-gate * 146*0Sstevel@tonic-gate * Return values 147*0Sstevel@tonic-gate * 0 if the threshold limit was not passed, 1 if the limit was passed. 148*0Sstevel@tonic-gate * 149*0Sstevel@tonic-gate * Caller's context 150*0Sstevel@tonic-gate * p->p_lock must be held across the call. 151*0Sstevel@tonic-gate */ 152*0Sstevel@tonic-gate /*ARGSUSED*/ 153*0Sstevel@tonic-gate static int 154*0Sstevel@tonic-gate task_lwps_test(rctl_t *r, proc_t *p, rctl_entity_p_t *e, rctl_val_t *rcntl, 155*0Sstevel@tonic-gate rctl_qty_t incr, 156*0Sstevel@tonic-gate uint_t flags) 157*0Sstevel@tonic-gate { 158*0Sstevel@tonic-gate rctl_qty_t nlwps; 159*0Sstevel@tonic-gate 160*0Sstevel@tonic-gate ASSERT(MUTEX_HELD(&p->p_lock)); 161*0Sstevel@tonic-gate ASSERT(e->rcep_t == RCENTITY_TASK); 162*0Sstevel@tonic-gate if (e->rcep_p.task == NULL) 163*0Sstevel@tonic-gate return (0); 164*0Sstevel@tonic-gate 165*0Sstevel@tonic-gate ASSERT(MUTEX_HELD(&(e->rcep_p.task->tk_zone->zone_nlwps_lock))); 166*0Sstevel@tonic-gate nlwps = e->rcep_p.task->tk_nlwps; 167*0Sstevel@tonic-gate 168*0Sstevel@tonic-gate if (nlwps + incr > rcntl->rcv_value) 169*0Sstevel@tonic-gate return (1); 170*0Sstevel@tonic-gate 171*0Sstevel@tonic-gate return (0); 172*0Sstevel@tonic-gate } 173*0Sstevel@tonic-gate /*ARGSUSED*/ 174*0Sstevel@tonic-gate static int 175*0Sstevel@tonic-gate task_lwps_set(rctl_t *rctl, struct proc *p, rctl_entity_p_t *e, rctl_qty_t nv) { 176*0Sstevel@tonic-gate 177*0Sstevel@tonic-gate ASSERT(MUTEX_HELD(&p->p_lock)); 178*0Sstevel@tonic-gate ASSERT(e->rcep_t == RCENTITY_TASK); 179*0Sstevel@tonic-gate if (e->rcep_p.task == NULL) 180*0Sstevel@tonic-gate return (0); 181*0Sstevel@tonic-gate 182*0Sstevel@tonic-gate e->rcep_p.task->tk_nlwps_ctl = nv; 183*0Sstevel@tonic-gate return (0); 184*0Sstevel@tonic-gate } 185*0Sstevel@tonic-gate 186*0Sstevel@tonic-gate /* 187*0Sstevel@tonic-gate * static rctl_qty_t task_usage_cpu_secs(void *taskp) 188*0Sstevel@tonic-gate * 189*0Sstevel@tonic-gate * Overview 190*0Sstevel@tonic-gate * task_usage_cpu_secs() is the usage operation for the resource control 191*0Sstevel@tonic-gate * associated with the total accrued CPU seconds for a task. 192*0Sstevel@tonic-gate * 193*0Sstevel@tonic-gate * Return values 194*0Sstevel@tonic-gate * The number of CPU seconds consumed by the task is returned. 195*0Sstevel@tonic-gate * 196*0Sstevel@tonic-gate * Caller's context 197*0Sstevel@tonic-gate * The given task must be held across the call. 198*0Sstevel@tonic-gate */ 199*0Sstevel@tonic-gate /*ARGSUSED*/ 200*0Sstevel@tonic-gate static rctl_qty_t 201*0Sstevel@tonic-gate task_cpu_time_usage(rctl_t *r, proc_t *p) 202*0Sstevel@tonic-gate { 203*0Sstevel@tonic-gate task_t *t = p->p_task; 204*0Sstevel@tonic-gate 205*0Sstevel@tonic-gate ASSERT(MUTEX_HELD(&p->p_lock)); 206*0Sstevel@tonic-gate return (t->tk_cpu_time / hz); 207*0Sstevel@tonic-gate } 208*0Sstevel@tonic-gate 209*0Sstevel@tonic-gate /* 210*0Sstevel@tonic-gate * static int task_test_cpu_secs(void *taskp, rctl_val_t *, int64_t incr, 211*0Sstevel@tonic-gate * int flags) 212*0Sstevel@tonic-gate * 213*0Sstevel@tonic-gate * Overview 214*0Sstevel@tonic-gate * task_test_cpu_secs() is the test-if-valid-increment for the resource 215*0Sstevel@tonic-gate * control for the total accrued CPU seconds for a task. 216*0Sstevel@tonic-gate * 217*0Sstevel@tonic-gate * Return values 218*0Sstevel@tonic-gate * 0 if the threshold limit was not passed, 1 if the limit was passed. 219*0Sstevel@tonic-gate * 220*0Sstevel@tonic-gate * Caller's context 221*0Sstevel@tonic-gate * The given task must be held across the call. 222*0Sstevel@tonic-gate */ 223*0Sstevel@tonic-gate /*ARGSUSED*/ 224*0Sstevel@tonic-gate static int 225*0Sstevel@tonic-gate task_cpu_time_test(rctl_t *r, proc_t *p, rctl_entity_p_t *e, 226*0Sstevel@tonic-gate struct rctl_val *rcntl, rctl_qty_t incr, uint_t flags) 227*0Sstevel@tonic-gate { 228*0Sstevel@tonic-gate task_t *t; 229*0Sstevel@tonic-gate 230*0Sstevel@tonic-gate ASSERT(MUTEX_HELD(&p->p_lock)); 231*0Sstevel@tonic-gate ASSERT(e->rcep_t == RCENTITY_TASK); 232*0Sstevel@tonic-gate if (e->rcep_p.task == NULL) 233*0Sstevel@tonic-gate return (0); 234*0Sstevel@tonic-gate 235*0Sstevel@tonic-gate t = e->rcep_p.task; 236*0Sstevel@tonic-gate if ((t->tk_cpu_time + incr) / hz >= rcntl->rcv_value) 237*0Sstevel@tonic-gate return (1); 238*0Sstevel@tonic-gate 239*0Sstevel@tonic-gate return (0); 240*0Sstevel@tonic-gate } 241*0Sstevel@tonic-gate 242*0Sstevel@tonic-gate static task_t * 243*0Sstevel@tonic-gate task_find(taskid_t id, zoneid_t zoneid) 244*0Sstevel@tonic-gate { 245*0Sstevel@tonic-gate task_t *tk; 246*0Sstevel@tonic-gate 247*0Sstevel@tonic-gate ASSERT(MUTEX_HELD(&task_hash_lock)); 248*0Sstevel@tonic-gate 249*0Sstevel@tonic-gate if (mod_hash_find(task_hash, (mod_hash_key_t)(uintptr_t)id, 250*0Sstevel@tonic-gate (mod_hash_val_t *)&tk) == MH_ERR_NOTFOUND || 251*0Sstevel@tonic-gate (zoneid != ALL_ZONES && zoneid != tk->tk_zone->zone_id)) 252*0Sstevel@tonic-gate return (NULL); 253*0Sstevel@tonic-gate 254*0Sstevel@tonic-gate return (tk); 255*0Sstevel@tonic-gate } 256*0Sstevel@tonic-gate 257*0Sstevel@tonic-gate /* 258*0Sstevel@tonic-gate * task_hold_by_id(), task_hold_by_id_zone() 259*0Sstevel@tonic-gate * 260*0Sstevel@tonic-gate * Overview 261*0Sstevel@tonic-gate * task_hold_by_id() is used to take a reference on a task by its task id, 262*0Sstevel@tonic-gate * supporting the various system call interfaces for obtaining resource data, 263*0Sstevel@tonic-gate * delivering signals, and so forth. 264*0Sstevel@tonic-gate * 265*0Sstevel@tonic-gate * Return values 266*0Sstevel@tonic-gate * Returns a pointer to the task_t with taskid_t id. The task is returned 267*0Sstevel@tonic-gate * with its hold count incremented by one. Returns NULL if there 268*0Sstevel@tonic-gate * is no task with the requested id. 269*0Sstevel@tonic-gate * 270*0Sstevel@tonic-gate * Caller's context 271*0Sstevel@tonic-gate * Caller must not be holding task_hash_lock. No restrictions on context. 272*0Sstevel@tonic-gate */ 273*0Sstevel@tonic-gate task_t * 274*0Sstevel@tonic-gate task_hold_by_id_zone(taskid_t id, zoneid_t zoneid) 275*0Sstevel@tonic-gate { 276*0Sstevel@tonic-gate task_t *tk; 277*0Sstevel@tonic-gate 278*0Sstevel@tonic-gate mutex_enter(&task_hash_lock); 279*0Sstevel@tonic-gate if ((tk = task_find(id, zoneid)) != NULL) 280*0Sstevel@tonic-gate atomic_add_32(&tk->tk_hold_count, 1); 281*0Sstevel@tonic-gate mutex_exit(&task_hash_lock); 282*0Sstevel@tonic-gate 283*0Sstevel@tonic-gate return (tk); 284*0Sstevel@tonic-gate } 285*0Sstevel@tonic-gate 286*0Sstevel@tonic-gate task_t * 287*0Sstevel@tonic-gate task_hold_by_id(taskid_t id) 288*0Sstevel@tonic-gate { 289*0Sstevel@tonic-gate zoneid_t zoneid; 290*0Sstevel@tonic-gate 291*0Sstevel@tonic-gate if (INGLOBALZONE(curproc)) 292*0Sstevel@tonic-gate zoneid = ALL_ZONES; 293*0Sstevel@tonic-gate else 294*0Sstevel@tonic-gate zoneid = getzoneid(); 295*0Sstevel@tonic-gate return (task_hold_by_id_zone(id, zoneid)); 296*0Sstevel@tonic-gate } 297*0Sstevel@tonic-gate 298*0Sstevel@tonic-gate /* 299*0Sstevel@tonic-gate * void task_hold(task_t *) 300*0Sstevel@tonic-gate * 301*0Sstevel@tonic-gate * Overview 302*0Sstevel@tonic-gate * task_hold() is used to take an additional reference to the given task. 303*0Sstevel@tonic-gate * 304*0Sstevel@tonic-gate * Return values 305*0Sstevel@tonic-gate * None. 306*0Sstevel@tonic-gate * 307*0Sstevel@tonic-gate * Caller's context 308*0Sstevel@tonic-gate * No restriction on context. 309*0Sstevel@tonic-gate */ 310*0Sstevel@tonic-gate void 311*0Sstevel@tonic-gate task_hold(task_t *tk) 312*0Sstevel@tonic-gate { 313*0Sstevel@tonic-gate atomic_add_32(&tk->tk_hold_count, 1); 314*0Sstevel@tonic-gate } 315*0Sstevel@tonic-gate 316*0Sstevel@tonic-gate /* 317*0Sstevel@tonic-gate * void task_rele(task_t *) 318*0Sstevel@tonic-gate * 319*0Sstevel@tonic-gate * Overview 320*0Sstevel@tonic-gate * task_rele() relinquishes a reference on the given task, which was acquired 321*0Sstevel@tonic-gate * via task_hold() or task_hold_by_id(). If this is the last member or 322*0Sstevel@tonic-gate * observer of the task, dispatch it for commitment via the accounting 323*0Sstevel@tonic-gate * subsystem. 324*0Sstevel@tonic-gate * 325*0Sstevel@tonic-gate * Return values 326*0Sstevel@tonic-gate * None. 327*0Sstevel@tonic-gate * 328*0Sstevel@tonic-gate * Caller's context 329*0Sstevel@tonic-gate * Caller must not be holding the task_hash_lock. 330*0Sstevel@tonic-gate * Caller's context must be acceptable for KM_SLEEP allocations. 331*0Sstevel@tonic-gate */ 332*0Sstevel@tonic-gate void 333*0Sstevel@tonic-gate task_rele(task_t *tk) 334*0Sstevel@tonic-gate { 335*0Sstevel@tonic-gate mutex_enter(&task_hash_lock); 336*0Sstevel@tonic-gate if (atomic_add_32_nv(&tk->tk_hold_count, -1) > 0) { 337*0Sstevel@tonic-gate mutex_exit(&task_hash_lock); 338*0Sstevel@tonic-gate return; 339*0Sstevel@tonic-gate } 340*0Sstevel@tonic-gate 341*0Sstevel@tonic-gate mutex_enter(&tk->tk_zone->zone_nlwps_lock); 342*0Sstevel@tonic-gate tk->tk_proj->kpj_ntasks--; 343*0Sstevel@tonic-gate mutex_exit(&tk->tk_zone->zone_nlwps_lock); 344*0Sstevel@tonic-gate 345*0Sstevel@tonic-gate if (mod_hash_destroy(task_hash, 346*0Sstevel@tonic-gate (mod_hash_key_t)(uintptr_t)tk->tk_tkid) != 0) 347*0Sstevel@tonic-gate panic("unable to delete task %d", tk->tk_tkid); 348*0Sstevel@tonic-gate mutex_exit(&task_hash_lock); 349*0Sstevel@tonic-gate 350*0Sstevel@tonic-gate /* 351*0Sstevel@tonic-gate * At this point, there are no members or observers of the task, so we 352*0Sstevel@tonic-gate * can safely send it on for commitment to the accounting subsystem. 353*0Sstevel@tonic-gate * The task will be destroyed in task_end() subsequent to commitment. 354*0Sstevel@tonic-gate */ 355*0Sstevel@tonic-gate (void) taskq_dispatch(exacct_queue, exacct_commit_task, tk, KM_SLEEP); 356*0Sstevel@tonic-gate } 357*0Sstevel@tonic-gate 358*0Sstevel@tonic-gate /* 359*0Sstevel@tonic-gate * task_t *task_create(projid_t, zone *) 360*0Sstevel@tonic-gate * 361*0Sstevel@tonic-gate * Overview 362*0Sstevel@tonic-gate * A process constructing a new task calls task_create() to construct and 363*0Sstevel@tonic-gate * preinitialize the task for the appropriate destination project. Only one 364*0Sstevel@tonic-gate * task, the primordial task0, is not created with task_create(). 365*0Sstevel@tonic-gate * 366*0Sstevel@tonic-gate * Return values 367*0Sstevel@tonic-gate * None. 368*0Sstevel@tonic-gate * 369*0Sstevel@tonic-gate * Caller's context 370*0Sstevel@tonic-gate * Caller's context should be safe for KM_SLEEP allocations. 371*0Sstevel@tonic-gate * The caller should appropriately bump the kpj_ntasks counter on the 372*0Sstevel@tonic-gate * project that contains this task. 373*0Sstevel@tonic-gate */ 374*0Sstevel@tonic-gate task_t * 375*0Sstevel@tonic-gate task_create(projid_t projid, zone_t *zone) 376*0Sstevel@tonic-gate { 377*0Sstevel@tonic-gate task_t *tk = kmem_cache_alloc(task_cache, KM_SLEEP); 378*0Sstevel@tonic-gate task_t *ancestor_tk; 379*0Sstevel@tonic-gate taskid_t tkid; 380*0Sstevel@tonic-gate task_usage_t *tu = kmem_zalloc(sizeof (task_usage_t), KM_SLEEP); 381*0Sstevel@tonic-gate mod_hash_hndl_t hndl; 382*0Sstevel@tonic-gate rctl_set_t *set = rctl_set_create(); 383*0Sstevel@tonic-gate rctl_alloc_gp_t *gp; 384*0Sstevel@tonic-gate rctl_entity_p_t e; 385*0Sstevel@tonic-gate 386*0Sstevel@tonic-gate bzero(tk, sizeof (task_t)); 387*0Sstevel@tonic-gate 388*0Sstevel@tonic-gate tk->tk_tkid = tkid = id_alloc(taskid_space); 389*0Sstevel@tonic-gate tk->tk_nlwps = 0; 390*0Sstevel@tonic-gate tk->tk_nlwps_ctl = INT_MAX; 391*0Sstevel@tonic-gate tk->tk_usage = tu; 392*0Sstevel@tonic-gate tk->tk_proj = project_hold_by_id(projid, zone->zone_id, 393*0Sstevel@tonic-gate PROJECT_HOLD_INSERT); 394*0Sstevel@tonic-gate tk->tk_flags = TASK_NORMAL; 395*0Sstevel@tonic-gate 396*0Sstevel@tonic-gate /* 397*0Sstevel@tonic-gate * Copy ancestor task's resource controls. 398*0Sstevel@tonic-gate */ 399*0Sstevel@tonic-gate zone_task_hold(zone); 400*0Sstevel@tonic-gate mutex_enter(&curproc->p_lock); 401*0Sstevel@tonic-gate ancestor_tk = curproc->p_task; 402*0Sstevel@tonic-gate task_hold(ancestor_tk); 403*0Sstevel@tonic-gate tk->tk_zone = zone; 404*0Sstevel@tonic-gate mutex_exit(&curproc->p_lock); 405*0Sstevel@tonic-gate 406*0Sstevel@tonic-gate for (;;) { 407*0Sstevel@tonic-gate gp = rctl_set_dup_prealloc(ancestor_tk->tk_rctls); 408*0Sstevel@tonic-gate 409*0Sstevel@tonic-gate mutex_enter(&ancestor_tk->tk_rctls->rcs_lock); 410*0Sstevel@tonic-gate if (rctl_set_dup_ready(ancestor_tk->tk_rctls, gp)) 411*0Sstevel@tonic-gate break; 412*0Sstevel@tonic-gate 413*0Sstevel@tonic-gate mutex_exit(&ancestor_tk->tk_rctls->rcs_lock); 414*0Sstevel@tonic-gate 415*0Sstevel@tonic-gate rctl_prealloc_destroy(gp); 416*0Sstevel@tonic-gate } 417*0Sstevel@tonic-gate 418*0Sstevel@tonic-gate /* 419*0Sstevel@tonic-gate * At this point, curproc does not have the appropriate linkage 420*0Sstevel@tonic-gate * through the task to the project. So, rctl_set_dup should only 421*0Sstevel@tonic-gate * copy the rctls, and leave the callbacks for later. 422*0Sstevel@tonic-gate */ 423*0Sstevel@tonic-gate e.rcep_p.task = tk; 424*0Sstevel@tonic-gate e.rcep_t = RCENTITY_TASK; 425*0Sstevel@tonic-gate tk->tk_rctls = rctl_set_dup(ancestor_tk->tk_rctls, curproc, curproc, &e, 426*0Sstevel@tonic-gate set, gp, RCD_DUP); 427*0Sstevel@tonic-gate mutex_exit(&ancestor_tk->tk_rctls->rcs_lock); 428*0Sstevel@tonic-gate 429*0Sstevel@tonic-gate rctl_prealloc_destroy(gp); 430*0Sstevel@tonic-gate 431*0Sstevel@tonic-gate /* 432*0Sstevel@tonic-gate * Record the ancestor task's ID for use by extended accounting. 433*0Sstevel@tonic-gate */ 434*0Sstevel@tonic-gate tu->tu_anctaskid = ancestor_tk->tk_tkid; 435*0Sstevel@tonic-gate task_rele(ancestor_tk); 436*0Sstevel@tonic-gate 437*0Sstevel@tonic-gate /* 438*0Sstevel@tonic-gate * Put new task structure in the hash table. 439*0Sstevel@tonic-gate */ 440*0Sstevel@tonic-gate (void) mod_hash_reserve(task_hash, &hndl); 441*0Sstevel@tonic-gate mutex_enter(&task_hash_lock); 442*0Sstevel@tonic-gate ASSERT(task_find(tkid, getzoneid()) == NULL); 443*0Sstevel@tonic-gate if (mod_hash_insert_reserve(task_hash, (mod_hash_key_t)(uintptr_t)tkid, 444*0Sstevel@tonic-gate (mod_hash_val_t *)tk, hndl) != 0) { 445*0Sstevel@tonic-gate mod_hash_cancel(task_hash, &hndl); 446*0Sstevel@tonic-gate panic("unable to insert task %d(%p)", tkid, (void *)tk); 447*0Sstevel@tonic-gate } 448*0Sstevel@tonic-gate mutex_exit(&task_hash_lock); 449*0Sstevel@tonic-gate 450*0Sstevel@tonic-gate return (tk); 451*0Sstevel@tonic-gate } 452*0Sstevel@tonic-gate 453*0Sstevel@tonic-gate /* 454*0Sstevel@tonic-gate * void task_attach(task_t *, proc_t *) 455*0Sstevel@tonic-gate * 456*0Sstevel@tonic-gate * Overview 457*0Sstevel@tonic-gate * task_attach() is used to attach a process to a task; this operation is only 458*0Sstevel@tonic-gate * performed as a result of a fork() or settaskid() system call. The proc_t's 459*0Sstevel@tonic-gate * p_tasknext and p_taskprev fields will be set such that the proc_t is a 460*0Sstevel@tonic-gate * member of the doubly-linked list of proc_t's that make up the task. 461*0Sstevel@tonic-gate * 462*0Sstevel@tonic-gate * Return values 463*0Sstevel@tonic-gate * None. 464*0Sstevel@tonic-gate * 465*0Sstevel@tonic-gate * Caller's context 466*0Sstevel@tonic-gate * pidlock and p->p_lock must be held on entry. 467*0Sstevel@tonic-gate */ 468*0Sstevel@tonic-gate void 469*0Sstevel@tonic-gate task_attach(task_t *tk, proc_t *p) 470*0Sstevel@tonic-gate { 471*0Sstevel@tonic-gate proc_t *first, *prev; 472*0Sstevel@tonic-gate rctl_entity_p_t e; 473*0Sstevel@tonic-gate ASSERT(tk != NULL); 474*0Sstevel@tonic-gate ASSERT(p != NULL); 475*0Sstevel@tonic-gate ASSERT(MUTEX_HELD(&pidlock)); 476*0Sstevel@tonic-gate ASSERT(MUTEX_HELD(&p->p_lock)); 477*0Sstevel@tonic-gate 478*0Sstevel@tonic-gate if (tk->tk_memb_list == NULL) { 479*0Sstevel@tonic-gate p->p_tasknext = p; 480*0Sstevel@tonic-gate p->p_taskprev = p; 481*0Sstevel@tonic-gate } else { 482*0Sstevel@tonic-gate first = tk->tk_memb_list; 483*0Sstevel@tonic-gate prev = first->p_taskprev; 484*0Sstevel@tonic-gate first->p_taskprev = p; 485*0Sstevel@tonic-gate p->p_tasknext = first; 486*0Sstevel@tonic-gate p->p_taskprev = prev; 487*0Sstevel@tonic-gate prev->p_tasknext = p; 488*0Sstevel@tonic-gate } 489*0Sstevel@tonic-gate tk->tk_memb_list = p; 490*0Sstevel@tonic-gate task_hold(tk); 491*0Sstevel@tonic-gate p->p_task = tk; 492*0Sstevel@tonic-gate 493*0Sstevel@tonic-gate /* 494*0Sstevel@tonic-gate * Now that the linkage from process to task and project is 495*0Sstevel@tonic-gate * complete, do the required callbacks for the task and project 496*0Sstevel@tonic-gate * rctl sets. 497*0Sstevel@tonic-gate */ 498*0Sstevel@tonic-gate e.rcep_p.proj = tk->tk_proj; 499*0Sstevel@tonic-gate e.rcep_t = RCENTITY_PROJECT; 500*0Sstevel@tonic-gate (void) rctl_set_dup(NULL, NULL, p, &e, tk->tk_proj->kpj_rctls, NULL, 501*0Sstevel@tonic-gate RCD_CALLBACK); 502*0Sstevel@tonic-gate 503*0Sstevel@tonic-gate e.rcep_p.task = tk; 504*0Sstevel@tonic-gate e.rcep_t = RCENTITY_TASK; 505*0Sstevel@tonic-gate (void) rctl_set_dup(NULL, NULL, p, &e, tk->tk_rctls, NULL, 506*0Sstevel@tonic-gate RCD_CALLBACK); 507*0Sstevel@tonic-gate 508*0Sstevel@tonic-gate } 509*0Sstevel@tonic-gate 510*0Sstevel@tonic-gate /* 511*0Sstevel@tonic-gate * task_begin() 512*0Sstevel@tonic-gate * 513*0Sstevel@tonic-gate * Overview 514*0Sstevel@tonic-gate * A process constructing a new task calls task_begin() to initialize the 515*0Sstevel@tonic-gate * task, by attaching itself as a member. 516*0Sstevel@tonic-gate * 517*0Sstevel@tonic-gate * Return values 518*0Sstevel@tonic-gate * None. 519*0Sstevel@tonic-gate * 520*0Sstevel@tonic-gate * Caller's context 521*0Sstevel@tonic-gate * pidlock and p_lock must be held across the call to task_begin(). 522*0Sstevel@tonic-gate */ 523*0Sstevel@tonic-gate void 524*0Sstevel@tonic-gate task_begin(task_t *tk, proc_t *p) 525*0Sstevel@tonic-gate { 526*0Sstevel@tonic-gate timestruc_t ts; 527*0Sstevel@tonic-gate task_usage_t *tu; 528*0Sstevel@tonic-gate 529*0Sstevel@tonic-gate ASSERT(MUTEX_HELD(&pidlock)); 530*0Sstevel@tonic-gate ASSERT(MUTEX_HELD(&p->p_lock)); 531*0Sstevel@tonic-gate 532*0Sstevel@tonic-gate mutex_enter(&tk->tk_usage_lock); 533*0Sstevel@tonic-gate tu = tk->tk_usage; 534*0Sstevel@tonic-gate gethrestime(&ts); 535*0Sstevel@tonic-gate tu->tu_startsec = (uint64_t)ts.tv_sec; 536*0Sstevel@tonic-gate tu->tu_startnsec = (uint64_t)ts.tv_nsec; 537*0Sstevel@tonic-gate mutex_exit(&tk->tk_usage_lock); 538*0Sstevel@tonic-gate 539*0Sstevel@tonic-gate /* 540*0Sstevel@tonic-gate * Join process to the task as a member. 541*0Sstevel@tonic-gate */ 542*0Sstevel@tonic-gate task_attach(tk, p); 543*0Sstevel@tonic-gate } 544*0Sstevel@tonic-gate 545*0Sstevel@tonic-gate /* 546*0Sstevel@tonic-gate * void task_detach(proc_t *) 547*0Sstevel@tonic-gate * 548*0Sstevel@tonic-gate * Overview 549*0Sstevel@tonic-gate * task_detach() removes the specified process from its task. task_detach 550*0Sstevel@tonic-gate * sets the process's task membership to NULL, in anticipation of a final exit 551*0Sstevel@tonic-gate * or of joining a new task. Because task_rele() requires a context safe for 552*0Sstevel@tonic-gate * KM_SLEEP allocations, a task_detach() is followed by a subsequent 553*0Sstevel@tonic-gate * task_rele() once appropriate context is available. 554*0Sstevel@tonic-gate * 555*0Sstevel@tonic-gate * Because task_detach() involves relinquishing the process's membership in 556*0Sstevel@tonic-gate * the project, any observational rctls the process may have had on the task 557*0Sstevel@tonic-gate * or project are destroyed. 558*0Sstevel@tonic-gate * 559*0Sstevel@tonic-gate * Return values 560*0Sstevel@tonic-gate * None. 561*0Sstevel@tonic-gate * 562*0Sstevel@tonic-gate * Caller's context 563*0Sstevel@tonic-gate * pidlock and p_lock held across task_detach(). 564*0Sstevel@tonic-gate */ 565*0Sstevel@tonic-gate void 566*0Sstevel@tonic-gate task_detach(proc_t *p) 567*0Sstevel@tonic-gate { 568*0Sstevel@tonic-gate task_t *tk = p->p_task; 569*0Sstevel@tonic-gate 570*0Sstevel@tonic-gate ASSERT(MUTEX_HELD(&pidlock)); 571*0Sstevel@tonic-gate ASSERT(MUTEX_HELD(&p->p_lock)); 572*0Sstevel@tonic-gate ASSERT(p->p_task != NULL); 573*0Sstevel@tonic-gate ASSERT(tk->tk_memb_list != NULL); 574*0Sstevel@tonic-gate 575*0Sstevel@tonic-gate if (tk->tk_memb_list == p) 576*0Sstevel@tonic-gate tk->tk_memb_list = p->p_tasknext; 577*0Sstevel@tonic-gate if (tk->tk_memb_list == p) 578*0Sstevel@tonic-gate tk->tk_memb_list = NULL; 579*0Sstevel@tonic-gate p->p_taskprev->p_tasknext = p->p_tasknext; 580*0Sstevel@tonic-gate p->p_tasknext->p_taskprev = p->p_taskprev; 581*0Sstevel@tonic-gate 582*0Sstevel@tonic-gate rctl_set_tearoff(p->p_task->tk_rctls, p); 583*0Sstevel@tonic-gate rctl_set_tearoff(p->p_task->tk_proj->kpj_rctls, p); 584*0Sstevel@tonic-gate 585*0Sstevel@tonic-gate p->p_task = NULL; 586*0Sstevel@tonic-gate p->p_tasknext = p->p_taskprev = NULL; 587*0Sstevel@tonic-gate } 588*0Sstevel@tonic-gate 589*0Sstevel@tonic-gate /* 590*0Sstevel@tonic-gate * task_change(task_t *, proc_t *) 591*0Sstevel@tonic-gate * 592*0Sstevel@tonic-gate * Overview 593*0Sstevel@tonic-gate * task_change() removes the specified process from its current task. The 594*0Sstevel@tonic-gate * process is then attached to the specified task. This routine is called 595*0Sstevel@tonic-gate * from settaskid() when process is being moved to a new task. 596*0Sstevel@tonic-gate * 597*0Sstevel@tonic-gate * Return values 598*0Sstevel@tonic-gate * None. 599*0Sstevel@tonic-gate * 600*0Sstevel@tonic-gate * Caller's context 601*0Sstevel@tonic-gate * pidlock and p_lock held across task_change() 602*0Sstevel@tonic-gate */ 603*0Sstevel@tonic-gate void 604*0Sstevel@tonic-gate task_change(task_t *newtk, proc_t *p) 605*0Sstevel@tonic-gate { 606*0Sstevel@tonic-gate task_t *oldtk = p->p_task; 607*0Sstevel@tonic-gate 608*0Sstevel@tonic-gate ASSERT(MUTEX_HELD(&pidlock)); 609*0Sstevel@tonic-gate ASSERT(MUTEX_HELD(&p->p_lock)); 610*0Sstevel@tonic-gate ASSERT(oldtk != NULL); 611*0Sstevel@tonic-gate ASSERT(oldtk->tk_memb_list != NULL); 612*0Sstevel@tonic-gate 613*0Sstevel@tonic-gate mutex_enter(&p->p_zone->zone_nlwps_lock); 614*0Sstevel@tonic-gate oldtk->tk_nlwps -= p->p_lwpcnt; 615*0Sstevel@tonic-gate mutex_exit(&p->p_zone->zone_nlwps_lock); 616*0Sstevel@tonic-gate 617*0Sstevel@tonic-gate mutex_enter(&newtk->tk_zone->zone_nlwps_lock); 618*0Sstevel@tonic-gate newtk->tk_nlwps += p->p_lwpcnt; 619*0Sstevel@tonic-gate mutex_exit(&newtk->tk_zone->zone_nlwps_lock); 620*0Sstevel@tonic-gate 621*0Sstevel@tonic-gate task_detach(p); 622*0Sstevel@tonic-gate task_begin(newtk, p); 623*0Sstevel@tonic-gate } 624*0Sstevel@tonic-gate 625*0Sstevel@tonic-gate /* 626*0Sstevel@tonic-gate * task_end() 627*0Sstevel@tonic-gate * 628*0Sstevel@tonic-gate * Overview 629*0Sstevel@tonic-gate * task_end() contains the actions executed once the final member of 630*0Sstevel@tonic-gate * a task has released the task, and all actions connected with the task, such 631*0Sstevel@tonic-gate * as committing an accounting record to a file, are completed. It is called 632*0Sstevel@tonic-gate * by the known last consumer of the task information. Additionally, 633*0Sstevel@tonic-gate * task_end() must never refer to any process in the system. 634*0Sstevel@tonic-gate * 635*0Sstevel@tonic-gate * Return values 636*0Sstevel@tonic-gate * None. 637*0Sstevel@tonic-gate * 638*0Sstevel@tonic-gate * Caller's context 639*0Sstevel@tonic-gate * No restrictions on context, beyond that given above. 640*0Sstevel@tonic-gate */ 641*0Sstevel@tonic-gate void 642*0Sstevel@tonic-gate task_end(task_t *tk) 643*0Sstevel@tonic-gate { 644*0Sstevel@tonic-gate ASSERT(tk->tk_hold_count == 0); 645*0Sstevel@tonic-gate 646*0Sstevel@tonic-gate project_rele(tk->tk_proj); 647*0Sstevel@tonic-gate kmem_free(tk->tk_usage, sizeof (task_usage_t)); 648*0Sstevel@tonic-gate if (tk->tk_prevusage != NULL) 649*0Sstevel@tonic-gate kmem_free(tk->tk_prevusage, sizeof (task_usage_t)); 650*0Sstevel@tonic-gate if (tk->tk_zoneusage != NULL) 651*0Sstevel@tonic-gate kmem_free(tk->tk_zoneusage, sizeof (task_usage_t)); 652*0Sstevel@tonic-gate rctl_set_free(tk->tk_rctls); 653*0Sstevel@tonic-gate id_free(taskid_space, tk->tk_tkid); 654*0Sstevel@tonic-gate zone_task_rele(tk->tk_zone); 655*0Sstevel@tonic-gate kmem_cache_free(task_cache, tk); 656*0Sstevel@tonic-gate } 657*0Sstevel@tonic-gate 658*0Sstevel@tonic-gate static void 659*0Sstevel@tonic-gate changeproj(proc_t *p, kproject_t *kpj, zone_t *zone, void *projbuf, 660*0Sstevel@tonic-gate void *zonebuf) 661*0Sstevel@tonic-gate { 662*0Sstevel@tonic-gate kproject_t *oldkpj; 663*0Sstevel@tonic-gate kthread_t *t; 664*0Sstevel@tonic-gate 665*0Sstevel@tonic-gate ASSERT(MUTEX_HELD(&pidlock)); 666*0Sstevel@tonic-gate ASSERT(MUTEX_HELD(&p->p_lock)); 667*0Sstevel@tonic-gate 668*0Sstevel@tonic-gate if ((t = p->p_tlist) != NULL) { 669*0Sstevel@tonic-gate do { 670*0Sstevel@tonic-gate (void) project_hold(kpj); 671*0Sstevel@tonic-gate 672*0Sstevel@tonic-gate thread_lock(t); 673*0Sstevel@tonic-gate oldkpj = ttoproj(t); 674*0Sstevel@tonic-gate t->t_proj = kpj; 675*0Sstevel@tonic-gate t->t_pre_sys = 1; /* For cred update */ 676*0Sstevel@tonic-gate thread_unlock(t); 677*0Sstevel@tonic-gate fss_changeproj(t, kpj, zone, projbuf, zonebuf); 678*0Sstevel@tonic-gate 679*0Sstevel@tonic-gate project_rele(oldkpj); 680*0Sstevel@tonic-gate } while ((t = t->t_forw) != p->p_tlist); 681*0Sstevel@tonic-gate } 682*0Sstevel@tonic-gate } 683*0Sstevel@tonic-gate 684*0Sstevel@tonic-gate /* 685*0Sstevel@tonic-gate * task_join() 686*0Sstevel@tonic-gate * 687*0Sstevel@tonic-gate * Overview 688*0Sstevel@tonic-gate * task_join() contains the actions that must be executed when the first 689*0Sstevel@tonic-gate * member (curproc) of a newly created task joins it. It may never fail. 690*0Sstevel@tonic-gate * 691*0Sstevel@tonic-gate * The caller must make sure holdlwps() is called so that all other lwps are 692*0Sstevel@tonic-gate * stopped prior to calling this function. 693*0Sstevel@tonic-gate * 694*0Sstevel@tonic-gate * NB: It returns with curproc->p_lock held. 695*0Sstevel@tonic-gate * 696*0Sstevel@tonic-gate * Return values 697*0Sstevel@tonic-gate * Pointer to the old task. 698*0Sstevel@tonic-gate * 699*0Sstevel@tonic-gate * Caller's context 700*0Sstevel@tonic-gate * cpu_lock must be held entering the function. It will acquire pidlock, 701*0Sstevel@tonic-gate * p_crlock and p_lock during execution. 702*0Sstevel@tonic-gate */ 703*0Sstevel@tonic-gate task_t * 704*0Sstevel@tonic-gate task_join(task_t *tk, uint_t flags) 705*0Sstevel@tonic-gate { 706*0Sstevel@tonic-gate proc_t *p = ttoproc(curthread); 707*0Sstevel@tonic-gate task_t *prev_tk; 708*0Sstevel@tonic-gate void *projbuf, *zonebuf; 709*0Sstevel@tonic-gate zone_t *zone = tk->tk_zone; 710*0Sstevel@tonic-gate projid_t projid = tk->tk_proj->kpj_id; 711*0Sstevel@tonic-gate cred_t *oldcr; 712*0Sstevel@tonic-gate 713*0Sstevel@tonic-gate /* 714*0Sstevel@tonic-gate * We can't know for sure if holdlwps() was called, but we can check to 715*0Sstevel@tonic-gate * ensure we're single-threaded. 716*0Sstevel@tonic-gate */ 717*0Sstevel@tonic-gate ASSERT(curthread == p->p_agenttp || p->p_lwprcnt == 1); 718*0Sstevel@tonic-gate 719*0Sstevel@tonic-gate /* 720*0Sstevel@tonic-gate * Changing the credential is always hard because we cannot 721*0Sstevel@tonic-gate * allocate memory when holding locks but we don't know whether 722*0Sstevel@tonic-gate * we need to change it. We first get a reference to the current 723*0Sstevel@tonic-gate * cred if we need to change it. Then we create a credential 724*0Sstevel@tonic-gate * with an updated project id. Finally we install it, first 725*0Sstevel@tonic-gate * releasing the reference we had on the p_cred at the time we 726*0Sstevel@tonic-gate * acquired the lock the first time and later we release the 727*0Sstevel@tonic-gate * reference to p_cred at the time we acquired the lock the 728*0Sstevel@tonic-gate * second time. 729*0Sstevel@tonic-gate */ 730*0Sstevel@tonic-gate mutex_enter(&p->p_crlock); 731*0Sstevel@tonic-gate if (crgetprojid(p->p_cred) == projid) 732*0Sstevel@tonic-gate oldcr = NULL; 733*0Sstevel@tonic-gate else 734*0Sstevel@tonic-gate crhold(oldcr = p->p_cred); 735*0Sstevel@tonic-gate mutex_exit(&p->p_crlock); 736*0Sstevel@tonic-gate 737*0Sstevel@tonic-gate if (oldcr != NULL) { 738*0Sstevel@tonic-gate cred_t *newcr = crdup(oldcr); 739*0Sstevel@tonic-gate crsetprojid(newcr, projid); 740*0Sstevel@tonic-gate crfree(oldcr); 741*0Sstevel@tonic-gate 742*0Sstevel@tonic-gate mutex_enter(&p->p_crlock); 743*0Sstevel@tonic-gate oldcr = p->p_cred; 744*0Sstevel@tonic-gate p->p_cred = newcr; 745*0Sstevel@tonic-gate mutex_exit(&p->p_crlock); 746*0Sstevel@tonic-gate crfree(oldcr); 747*0Sstevel@tonic-gate } 748*0Sstevel@tonic-gate 749*0Sstevel@tonic-gate /* 750*0Sstevel@tonic-gate * Make sure that the number of processor sets is constant 751*0Sstevel@tonic-gate * across this operation. 752*0Sstevel@tonic-gate */ 753*0Sstevel@tonic-gate ASSERT(MUTEX_HELD(&cpu_lock)); 754*0Sstevel@tonic-gate 755*0Sstevel@tonic-gate projbuf = fss_allocbuf(FSS_NPSET_BUF, FSS_ALLOC_PROJ); 756*0Sstevel@tonic-gate zonebuf = fss_allocbuf(FSS_NPSET_BUF, FSS_ALLOC_ZONE); 757*0Sstevel@tonic-gate 758*0Sstevel@tonic-gate mutex_enter(&pidlock); 759*0Sstevel@tonic-gate mutex_enter(&p->p_lock); 760*0Sstevel@tonic-gate 761*0Sstevel@tonic-gate prev_tk = p->p_task; 762*0Sstevel@tonic-gate task_change(tk, p); 763*0Sstevel@tonic-gate 764*0Sstevel@tonic-gate /* 765*0Sstevel@tonic-gate * Now move threads one by one to their new project. 766*0Sstevel@tonic-gate */ 767*0Sstevel@tonic-gate changeproj(p, tk->tk_proj, zone, projbuf, zonebuf); 768*0Sstevel@tonic-gate if (flags & TASK_FINAL) 769*0Sstevel@tonic-gate p->p_task->tk_flags |= TASK_FINAL; 770*0Sstevel@tonic-gate 771*0Sstevel@tonic-gate mutex_exit(&pidlock); 772*0Sstevel@tonic-gate 773*0Sstevel@tonic-gate fss_freebuf(zonebuf, FSS_ALLOC_ZONE); 774*0Sstevel@tonic-gate fss_freebuf(projbuf, FSS_ALLOC_PROJ); 775*0Sstevel@tonic-gate return (prev_tk); 776*0Sstevel@tonic-gate } 777*0Sstevel@tonic-gate 778*0Sstevel@tonic-gate /* 779*0Sstevel@tonic-gate * rctl ops vectors 780*0Sstevel@tonic-gate */ 781*0Sstevel@tonic-gate static rctl_ops_t task_lwps_ops = { 782*0Sstevel@tonic-gate rcop_no_action, 783*0Sstevel@tonic-gate task_lwps_usage, 784*0Sstevel@tonic-gate task_lwps_set, 785*0Sstevel@tonic-gate task_lwps_test 786*0Sstevel@tonic-gate }; 787*0Sstevel@tonic-gate 788*0Sstevel@tonic-gate static rctl_ops_t task_cpu_time_ops = { 789*0Sstevel@tonic-gate rcop_no_action, 790*0Sstevel@tonic-gate task_cpu_time_usage, 791*0Sstevel@tonic-gate rcop_no_set, 792*0Sstevel@tonic-gate task_cpu_time_test 793*0Sstevel@tonic-gate }; 794*0Sstevel@tonic-gate 795*0Sstevel@tonic-gate /*ARGSUSED*/ 796*0Sstevel@tonic-gate /* 797*0Sstevel@tonic-gate * void task_init(void) 798*0Sstevel@tonic-gate * 799*0Sstevel@tonic-gate * Overview 800*0Sstevel@tonic-gate * task_init() initializes task-related hashes, caches, and the task id 801*0Sstevel@tonic-gate * space. Additionally, task_init() establishes p0 as a member of task0. 802*0Sstevel@tonic-gate * Called by main(). 803*0Sstevel@tonic-gate * 804*0Sstevel@tonic-gate * Return values 805*0Sstevel@tonic-gate * None. 806*0Sstevel@tonic-gate * 807*0Sstevel@tonic-gate * Caller's context 808*0Sstevel@tonic-gate * task_init() must be called prior to MP startup. 809*0Sstevel@tonic-gate */ 810*0Sstevel@tonic-gate void 811*0Sstevel@tonic-gate task_init(void) 812*0Sstevel@tonic-gate { 813*0Sstevel@tonic-gate proc_t *p = &p0; 814*0Sstevel@tonic-gate mod_hash_hndl_t hndl; 815*0Sstevel@tonic-gate rctl_set_t *set; 816*0Sstevel@tonic-gate rctl_alloc_gp_t *gp; 817*0Sstevel@tonic-gate rctl_entity_p_t e; 818*0Sstevel@tonic-gate /* 819*0Sstevel@tonic-gate * Initialize task_cache and taskid_space. 820*0Sstevel@tonic-gate */ 821*0Sstevel@tonic-gate task_cache = kmem_cache_create("task_cache", sizeof (task_t), 822*0Sstevel@tonic-gate 0, NULL, NULL, NULL, NULL, NULL, 0); 823*0Sstevel@tonic-gate taskid_space = id_space_create("taskid_space", 0, MAX_TASKID); 824*0Sstevel@tonic-gate 825*0Sstevel@tonic-gate /* 826*0Sstevel@tonic-gate * Initialize task hash table. 827*0Sstevel@tonic-gate */ 828*0Sstevel@tonic-gate task_hash = mod_hash_create_idhash("task_hash", task_hash_size, 829*0Sstevel@tonic-gate mod_hash_null_valdtor); 830*0Sstevel@tonic-gate 831*0Sstevel@tonic-gate /* 832*0Sstevel@tonic-gate * Initialize task-based rctls. 833*0Sstevel@tonic-gate */ 834*0Sstevel@tonic-gate rc_task_lwps = rctl_register("task.max-lwps", RCENTITY_TASK, 835*0Sstevel@tonic-gate RCTL_GLOBAL_NOACTION | RCTL_GLOBAL_COUNT, INT_MAX, INT_MAX, 836*0Sstevel@tonic-gate &task_lwps_ops); 837*0Sstevel@tonic-gate rc_task_cpu_time = rctl_register("task.max-cpu-time", RCENTITY_TASK, 838*0Sstevel@tonic-gate RCTL_GLOBAL_NOACTION | RCTL_GLOBAL_DENY_NEVER | 839*0Sstevel@tonic-gate RCTL_GLOBAL_CPU_TIME | RCTL_GLOBAL_INFINITE | 840*0Sstevel@tonic-gate RCTL_GLOBAL_UNOBSERVABLE | RCTL_GLOBAL_SECONDS, UINT64_MAX, 841*0Sstevel@tonic-gate UINT64_MAX, &task_cpu_time_ops); 842*0Sstevel@tonic-gate 843*0Sstevel@tonic-gate /* 844*0Sstevel@tonic-gate * Create task0 and place p0 in it as a member. 845*0Sstevel@tonic-gate */ 846*0Sstevel@tonic-gate task0p = kmem_cache_alloc(task_cache, KM_SLEEP); 847*0Sstevel@tonic-gate bzero(task0p, sizeof (task_t)); 848*0Sstevel@tonic-gate 849*0Sstevel@tonic-gate task0p->tk_tkid = id_alloc(taskid_space); 850*0Sstevel@tonic-gate task0p->tk_usage = kmem_zalloc(sizeof (task_usage_t), KM_SLEEP); 851*0Sstevel@tonic-gate task0p->tk_proj = project_hold_by_id(0, GLOBAL_ZONEID, 852*0Sstevel@tonic-gate PROJECT_HOLD_INSERT); 853*0Sstevel@tonic-gate task0p->tk_flags = TASK_NORMAL; 854*0Sstevel@tonic-gate task0p->tk_nlwps = p->p_lwpcnt; 855*0Sstevel@tonic-gate task0p->tk_zone = global_zone; 856*0Sstevel@tonic-gate 857*0Sstevel@tonic-gate set = rctl_set_create(); 858*0Sstevel@tonic-gate gp = rctl_set_init_prealloc(RCENTITY_TASK); 859*0Sstevel@tonic-gate mutex_enter(&curproc->p_lock); 860*0Sstevel@tonic-gate e.rcep_p.task = task0p; 861*0Sstevel@tonic-gate e.rcep_t = RCENTITY_TASK; 862*0Sstevel@tonic-gate task0p->tk_rctls = rctl_set_init(RCENTITY_TASK, curproc, &e, set, gp); 863*0Sstevel@tonic-gate mutex_exit(&curproc->p_lock); 864*0Sstevel@tonic-gate rctl_prealloc_destroy(gp); 865*0Sstevel@tonic-gate 866*0Sstevel@tonic-gate (void) mod_hash_reserve(task_hash, &hndl); 867*0Sstevel@tonic-gate mutex_enter(&task_hash_lock); 868*0Sstevel@tonic-gate ASSERT(task_find(task0p->tk_tkid, GLOBAL_ZONEID) == NULL); 869*0Sstevel@tonic-gate if (mod_hash_insert_reserve(task_hash, 870*0Sstevel@tonic-gate (mod_hash_key_t)(uintptr_t)task0p->tk_tkid, 871*0Sstevel@tonic-gate (mod_hash_val_t *)task0p, hndl) != 0) { 872*0Sstevel@tonic-gate mod_hash_cancel(task_hash, &hndl); 873*0Sstevel@tonic-gate panic("unable to insert task %d(%p)", task0p->tk_tkid, 874*0Sstevel@tonic-gate (void *)task0p); 875*0Sstevel@tonic-gate } 876*0Sstevel@tonic-gate mutex_exit(&task_hash_lock); 877*0Sstevel@tonic-gate 878*0Sstevel@tonic-gate task0p->tk_memb_list = p; 879*0Sstevel@tonic-gate 880*0Sstevel@tonic-gate /* 881*0Sstevel@tonic-gate * Initialize task pointers for p0, including doubly linked list of task 882*0Sstevel@tonic-gate * members. 883*0Sstevel@tonic-gate */ 884*0Sstevel@tonic-gate p->p_task = task0p; 885*0Sstevel@tonic-gate p->p_taskprev = p->p_tasknext = p; 886*0Sstevel@tonic-gate task_hold(task0p); 887*0Sstevel@tonic-gate } 888