Home | History | Annotate | Download | only in os
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     27 
     28 #include <sys/project.h>
     29 #include <sys/modhash.h>
     30 #include <sys/modctl.h>
     31 #include <sys/kmem.h>
     32 #include <sys/kstat.h>
     33 #include <sys/atomic.h>
     34 #include <sys/cmn_err.h>
     35 #include <sys/proc.h>
     36 #include <sys/rctl.h>
     37 #include <sys/sunddi.h>
     38 #include <sys/fss.h>
     39 #include <sys/systm.h>
     40 #include <sys/ipc_impl.h>
     41 #include <sys/port_kernel.h>
     42 #include <sys/task.h>
     43 #include <sys/zone.h>
     44 #include <sys/cpucaps.h>
     45 #include <sys/klpd.h>
     46 
     47 int project_hash_size = 64;
     48 static kmutex_t project_hash_lock;
     49 static kmutex_t projects_list_lock;
     50 static mod_hash_t *projects_hash;
     51 static kproject_t *projects_list;
     52 
     53 rctl_hndl_t rc_project_cpu_shares;
     54 rctl_hndl_t rc_project_cpu_cap;
     55 rctl_hndl_t rc_project_nlwps;
     56 rctl_hndl_t rc_project_ntasks;
     57 rctl_hndl_t rc_project_msgmni;
     58 rctl_hndl_t rc_project_semmni;
     59 rctl_hndl_t rc_project_shmmax;
     60 rctl_hndl_t rc_project_shmmni;
     61 rctl_hndl_t rc_project_portids;
     62 rctl_hndl_t rc_project_locked_mem;
     63 rctl_hndl_t rc_project_contract;
     64 rctl_hndl_t rc_project_crypto_mem;
     65 
     66 /*
     67  * Dummy structure used when comparing projects.  This structure must be kept
     68  * identical to the first two fields of kproject_t.
     69  */
     70 struct project_zone {
     71 	projid_t	kpj_id;
     72 	zoneid_t	kpj_zoneid;
     73 };
     74 
     75 /*
     76  * Projects
     77  *
     78  *   A dictionary of all active projects is maintained by the kernel so that we
     79  *   may track project usage and limits.  (By an active project, we mean a
     80  *   project associated with one or more task, and therefore with one or more
     81  *   processes.) We build the dictionary on top of the mod_hash facility, since
     82  *   project additions and deletions are relatively rare events.  An
     83  *   integer-to-pointer mapping is maintained within the hash, representing the
     84  *   map from project id to project structure.  All projects, including the
     85  *   primordial "project 0", are allocated via the project_hold_by_id()
     86  *   interface.
     87  *
     88  *   Currently, the project contains a reference count; the project ID, which is
     89  *   examined by the extended accounting subsystem as well as /proc; a resource
     90  *   control set, which contains the allowable values (and actions on exceeding
     91  *   those values) for controlled project-level resources on the system; and a
     92  *   number of CPU shares, which is used by the fair share scheduling class
     93  *   (FSS) to support its proportion-based scheduling algorithm.
     94  *
     95  * Reference counting convention
     96  *   The dictionary entry does not itself count as a reference--only references
     97  *   outside of the subsystem are tallied.  At the drop of the final external
     98  *   reference, the project entry is removed.  The reference counter keeps
     99  *   track of the number of threads *and* tasks within a project.
    100  *
    101  * Locking
    102  *   Walking the doubly-linked project list must be done while holding
    103  *   projects_list_lock.  Thus, any dereference of kpj_next or kpj_prev must be
    104  *   under projects_list_lock.
    105  *
    106  *   If both the hash lock, project_hash_lock, and the list lock are to be
    107  *   acquired, the hash lock is to be acquired first.
    108  */
    109 
    110 static kstat_t *project_kstat_create(kproject_t *pj, zone_t *zone);
    111 static void project_kstat_delete(kproject_t *pj);
    112 
    113 static void
    114 project_data_init(kproject_data_t *data)
    115 {
    116 	/*
    117 	 * Initialize subsystem-specific data
    118 	 */
    119 	data->kpd_shmmax = 0;
    120 	data->kpd_ipc.ipcq_shmmni = 0;
    121 	data->kpd_ipc.ipcq_semmni = 0;
    122 	data->kpd_ipc.ipcq_msgmni = 0;
    123 	data->kpd_locked_mem = 0;
    124 	data->kpd_locked_mem_ctl = UINT64_MAX;
    125 	data->kpd_contract = 0;
    126 	data->kpd_crypto_mem = 0;
    127 	data->kpd_crypto_mem_ctl = UINT64_MAX;
    128 	data->kpd_lockedmem_kstat = NULL;
    129 }
    130 
    131 /*ARGSUSED*/
    132 static uint_t
    133 project_hash_by_id(void *hash_data, mod_hash_key_t key)
    134 {
    135 	struct project_zone *pz = key;
    136 	uint_t mykey;
    137 
    138 	/*
    139 	 * Merge the zoneid and projectid together to a 32-bit quantity, and
    140 	 * then pass that in to the existing idhash.
    141 	 */
    142 	mykey = (pz->kpj_zoneid << 16) | pz->kpj_id;
    143 	return (mod_hash_byid(hash_data, (mod_hash_key_t)(uintptr_t)mykey));
    144 }
    145 
    146 static int
    147 project_hash_key_cmp(mod_hash_key_t key1, mod_hash_key_t key2)
    148 {
    149 	struct project_zone *pz1 = key1, *pz2 = key2;
    150 	int retval;
    151 
    152 	return ((int)((retval = pz1->kpj_id - pz2->kpj_id) != 0 ? retval :
    153 	    pz1->kpj_zoneid - pz2->kpj_zoneid));
    154 }
    155 
    156 static void
    157 project_hash_val_dtor(mod_hash_val_t val)
    158 {
    159 	kproject_t *kp = (kproject_t *)val;
    160 
    161 	ASSERT(kp->kpj_count == 0);
    162 	ASSERT(kp->kpj_cpucap == NULL);
    163 	kmem_free(kp, sizeof (kproject_t));
    164 }
    165 
    166 /*
    167  * kproject_t *project_hold(kproject_t *)
    168  *
    169  * Overview
    170  *   Record that an additional reference on the indicated project has been
    171  *   taken.
    172  *
    173  * Return values
    174  *   A pointer to the indicated project.
    175  *
    176  * Caller's context
    177  *   project_hash_lock must not be held across the project_hold() call.
    178  */
    179 kproject_t *
    180 project_hold(kproject_t *p)
    181 {
    182 	mutex_enter(&project_hash_lock);
    183 	ASSERT(p != NULL);
    184 	p->kpj_count++;
    185 	ASSERT(p->kpj_count != 0);
    186 	mutex_exit(&project_hash_lock);
    187 	return (p);
    188 }
    189 
    190 /*
    191  * kproject_t *project_hold_by_id(projid_t, zone_t *, int)
    192  *
    193  * Overview
    194  *   project_hold_by_id() performs a look-up in the dictionary of projects
    195  *   active on the system by specified project ID + zone and puts a hold on
    196  *   it.  The third argument defines the desired behavior in the case when
    197  *   project with given project ID cannot be found:
    198  *
    199  *   PROJECT_HOLD_INSERT	New entry is made in dictionary and the project
    200  *   				is added to the global list.
    201  *
    202  *   PROJECT_HOLD_FIND		Return NULL.
    203  *
    204  *   The project is returned with its reference count incremented by one.
    205  *   A new project derives its resource controls from those of project 0.
    206  *
    207  * Return values
    208  *   A pointer to the held project.
    209  *
    210  * Caller's context
    211  *   Caller must be in a context suitable for KM_SLEEP allocations.
    212  */
    213 kproject_t *
    214 project_hold_by_id(projid_t id, zone_t *zone, int flag)
    215 {
    216 	kproject_t *spare_p;
    217 	kproject_t *p;
    218 	mod_hash_hndl_t hndl;
    219 	rctl_set_t *set;
    220 	rctl_alloc_gp_t *gp;
    221 	rctl_entity_p_t e;
    222 	struct project_zone pz;
    223 	boolean_t create = B_FALSE;
    224 	kstat_t *ksp;
    225 
    226 	pz.kpj_id = id;
    227 	pz.kpj_zoneid = zone->zone_id;
    228 
    229 	if (flag == PROJECT_HOLD_FIND) {
    230 		mutex_enter(&project_hash_lock);
    231 
    232 		if (mod_hash_find(projects_hash, (mod_hash_key_t)&pz,
    233 		    (mod_hash_val_t)&p) == MH_ERR_NOTFOUND)
    234 			p = NULL;
    235 		else
    236 			p->kpj_count++;
    237 
    238 		mutex_exit(&project_hash_lock);
    239 		return (p);
    240 	}
    241 
    242 	ASSERT(flag == PROJECT_HOLD_INSERT);
    243 
    244 	spare_p = kmem_zalloc(sizeof (kproject_t), KM_SLEEP);
    245 	set = rctl_set_create();
    246 
    247 	gp = rctl_set_init_prealloc(RCENTITY_PROJECT);
    248 
    249 	(void) mod_hash_reserve(projects_hash, &hndl);
    250 
    251 	mutex_enter(&curproc->p_lock);
    252 	mutex_enter(&project_hash_lock);
    253 	if (mod_hash_find(projects_hash, (mod_hash_key_t)&pz,
    254 	    (mod_hash_val_t *)&p) == MH_ERR_NOTFOUND) {
    255 
    256 		p = spare_p;
    257 		p->kpj_id = id;
    258 		p->kpj_zone = zone;
    259 		p->kpj_zoneid = zone->zone_id;
    260 		p->kpj_count = 0;
    261 		p->kpj_shares = 1;
    262 		p->kpj_nlwps = 0;
    263 		p->kpj_ntasks = 0;
    264 		p->kpj_nlwps_ctl = INT_MAX;
    265 		p->kpj_ntasks_ctl = INT_MAX;
    266 		project_data_init(&p->kpj_data);
    267 		e.rcep_p.proj = p;
    268 		e.rcep_t = RCENTITY_PROJECT;
    269 		p->kpj_rctls = rctl_set_init(RCENTITY_PROJECT, curproc, &e,
    270 		    set, gp);
    271 		mutex_exit(&curproc->p_lock);
    272 
    273 		if (mod_hash_insert_reserve(projects_hash, (mod_hash_key_t)p,
    274 		    (mod_hash_val_t)p, hndl))
    275 			panic("unable to insert project %d(%p)", id, (void *)p);
    276 
    277 		/*
    278 		 * Insert project into global project list.
    279 		 */
    280 		mutex_enter(&projects_list_lock);
    281 		if (id != 0 || zone != &zone0) {
    282 			p->kpj_next = projects_list;
    283 			p->kpj_prev = projects_list->kpj_prev;
    284 			p->kpj_prev->kpj_next = p;
    285 			projects_list->kpj_prev = p;
    286 		} else {
    287 			/*
    288 			 * Special case: primordial hold on project 0.
    289 			 */
    290 			p->kpj_next = p;
    291 			p->kpj_prev = p;
    292 			projects_list = p;
    293 		}
    294 		mutex_exit(&projects_list_lock);
    295 		create = B_TRUE;
    296 	} else {
    297 		mutex_exit(&curproc->p_lock);
    298 		mod_hash_cancel(projects_hash, &hndl);
    299 		kmem_free(spare_p, sizeof (kproject_t));
    300 		rctl_set_free(set);
    301 	}
    302 
    303 	rctl_prealloc_destroy(gp);
    304 	p->kpj_count++;
    305 	mutex_exit(&project_hash_lock);
    306 
    307 	/*
    308 	 * The kstat stores the project's zone name, as zoneid's may change
    309 	 * across reboots.
    310 	 */
    311 	if (create == B_TRUE) {
    312 		/*
    313 		 * Inform CPU caps framework of the new project
    314 		 */
    315 		cpucaps_project_add(p);
    316 		/*
    317 		 * Set up project kstats
    318 		 */
    319 		ksp = project_kstat_create(p, zone);
    320 		mutex_enter(&project_hash_lock);
    321 		ASSERT(p->kpj_data.kpd_lockedmem_kstat == NULL);
    322 		p->kpj_data.kpd_lockedmem_kstat = ksp;
    323 		mutex_exit(&project_hash_lock);
    324 	}
    325 	return (p);
    326 }
    327 
    328 /*
    329  * void project_rele(kproject_t *)
    330  *
    331  * Overview
    332  *   Advertise that one external reference to this project is no longer needed.
    333  *
    334  * Return values
    335  *   None.
    336  *
    337  * Caller's context
    338  *   No restriction on context.
    339  */
    340 void
    341 project_rele(kproject_t *p)
    342 {
    343 	mutex_enter(&project_hash_lock);
    344 	ASSERT(p->kpj_count != 0);
    345 	p->kpj_count--;
    346 	if (p->kpj_count == 0) {
    347 
    348 		/*
    349 		 * Remove project from global list.
    350 		 */
    351 		mutex_enter(&projects_list_lock);
    352 		p->kpj_next->kpj_prev = p->kpj_prev;
    353 		p->kpj_prev->kpj_next = p->kpj_next;
    354 		if (projects_list == p)
    355 			projects_list = p->kpj_next;
    356 		mutex_exit(&projects_list_lock);
    357 
    358 		cpucaps_project_remove(p);
    359 
    360 		rctl_set_free(p->kpj_rctls);
    361 		project_kstat_delete(p);
    362 
    363 		if (p->kpj_klpd != NULL)
    364 			klpd_remove(&p->kpj_klpd);
    365 
    366 		if (mod_hash_destroy(projects_hash, (mod_hash_key_t)p))
    367 			panic("unable to delete project %d zone %d", p->kpj_id,
    368 			    p->kpj_zoneid);
    369 
    370 	}
    371 	mutex_exit(&project_hash_lock);
    372 }
    373 
    374 /*
    375  * int project_walk_all(zoneid_t, int (*)(kproject_t *, void *), void *)
    376  *
    377  * Overview
    378  *   Walk the project list for the given zoneid with a callback.
    379  *
    380  * Return values
    381  *   -1 for an invalid walk, number of projects visited otherwise.
    382  *
    383  * Caller's context
    384  *   projects_list_lock must not be held, as it is acquired by
    385  *   project_walk_all().  Accordingly, callbacks may not perform KM_SLEEP
    386  *   allocations.
    387  */
    388 int
    389 project_walk_all(zoneid_t zoneid, int (*cb)(kproject_t *, void *),
    390     void *walk_data)
    391 {
    392 	int cnt = 0;
    393 	kproject_t *kp = proj0p;
    394 
    395 	mutex_enter(&projects_list_lock);
    396 	do {
    397 		if (zoneid != ALL_ZONES && kp->kpj_zoneid != zoneid)
    398 			continue;
    399 		if (cb(kp, walk_data) == -1) {
    400 			cnt = -1;
    401 			break;
    402 		} else {
    403 			cnt++;
    404 		}
    405 	} while ((kp = kp->kpj_next) != proj0p);
    406 	mutex_exit(&projects_list_lock);
    407 	return (cnt);
    408 }
    409 
    410 /*
    411  * projid_t curprojid(void)
    412  *
    413  * Overview
    414  *   Return project ID of the current thread
    415  *
    416  * Caller's context
    417  *   No restrictions.
    418  */
    419 projid_t
    420 curprojid()
    421 {
    422 	return (ttoproj(curthread)->kpj_id);
    423 }
    424 
    425 /*
    426  * project.cpu-shares resource control support.
    427  */
    428 /*ARGSUSED*/
    429 static rctl_qty_t
    430 project_cpu_shares_usage(rctl_t *rctl, struct proc *p)
    431 {
    432 	ASSERT(MUTEX_HELD(&p->p_lock));
    433 	return (p->p_task->tk_proj->kpj_shares);
    434 }
    435 
    436 /*ARGSUSED*/
    437 static int
    438 project_cpu_shares_set(rctl_t *rctl, struct proc *p, rctl_entity_p_t *e,
    439     rctl_qty_t nv)
    440 {
    441 	ASSERT(MUTEX_HELD(&p->p_lock));
    442 	ASSERT(e->rcep_t == RCENTITY_PROJECT);
    443 	if (e->rcep_p.proj == NULL)
    444 		return (0);
    445 
    446 	e->rcep_p.proj->kpj_shares = nv;
    447 
    448 	return (0);
    449 }
    450 
    451 static rctl_ops_t project_cpu_shares_ops = {
    452 	rcop_no_action,
    453 	project_cpu_shares_usage,
    454 	project_cpu_shares_set,
    455 	rcop_no_test
    456 };
    457 
    458 
    459 /*
    460  * project.cpu-cap resource control support.
    461  */
    462 /*ARGSUSED*/
    463 static rctl_qty_t
    464 project_cpu_cap_get(rctl_t *rctl, struct proc *p)
    465 {
    466 	ASSERT(MUTEX_HELD(&p->p_lock));
    467 	return (cpucaps_project_get(p->p_task->tk_proj));
    468 }
    469 
    470 /*ARGSUSED*/
    471 static int
    472 project_cpu_cap_set(rctl_t *rctl, struct proc *p, rctl_entity_p_t *e,
    473     rctl_qty_t nv)
    474 {
    475 	kproject_t *kpj = e->rcep_p.proj;
    476 
    477 	ASSERT(MUTEX_HELD(&p->p_lock));
    478 	ASSERT(e->rcep_t == RCENTITY_PROJECT);
    479 	if (kpj == NULL)
    480 		return (0);
    481 
    482 	/*
    483 	 * set cap to the new value.
    484 	 */
    485 	return (cpucaps_project_set(kpj,  nv));
    486 }
    487 
    488 static rctl_ops_t project_cpu_cap_ops = {
    489 	rcop_no_action,
    490 	project_cpu_cap_get,
    491 	project_cpu_cap_set,
    492 	rcop_no_test
    493 };
    494 
    495 /*ARGSUSED*/
    496 static rctl_qty_t
    497 project_lwps_usage(rctl_t *r, proc_t *p)
    498 {
    499 	kproject_t *pj;
    500 	rctl_qty_t nlwps;
    501 
    502 	ASSERT(MUTEX_HELD(&p->p_lock));
    503 	pj = p->p_task->tk_proj;
    504 	mutex_enter(&p->p_zone->zone_nlwps_lock);
    505 	nlwps = pj->kpj_nlwps;
    506 	mutex_exit(&p->p_zone->zone_nlwps_lock);
    507 
    508 	return (nlwps);
    509 }
    510 
    511 /*ARGSUSED*/
    512 static int
    513 project_lwps_test(rctl_t *r, proc_t *p, rctl_entity_p_t *e, rctl_val_t *rcntl,
    514     rctl_qty_t incr, uint_t flags)
    515 {
    516 	rctl_qty_t nlwps;
    517 
    518 	ASSERT(MUTEX_HELD(&p->p_lock));
    519 	ASSERT(MUTEX_HELD(&p->p_zone->zone_nlwps_lock));
    520 	ASSERT(e->rcep_t == RCENTITY_PROJECT);
    521 	if (e->rcep_p.proj == NULL)
    522 		return (0);
    523 
    524 	nlwps = e->rcep_p.proj->kpj_nlwps;
    525 	if (nlwps + incr > rcntl->rcv_value)
    526 		return (1);
    527 
    528 	return (0);
    529 }
    530 
    531 /*ARGSUSED*/
    532 static int
    533 project_lwps_set(rctl_t *rctl, struct proc *p, rctl_entity_p_t *e,
    534     rctl_qty_t nv) {
    535 
    536 	ASSERT(MUTEX_HELD(&p->p_lock));
    537 	ASSERT(e->rcep_t == RCENTITY_PROJECT);
    538 	if (e->rcep_p.proj == NULL)
    539 		return (0);
    540 
    541 	e->rcep_p.proj->kpj_nlwps_ctl = nv;
    542 	return (0);
    543 }
    544 
    545 static rctl_ops_t project_lwps_ops = {
    546 	rcop_no_action,
    547 	project_lwps_usage,
    548 	project_lwps_set,
    549 	project_lwps_test,
    550 };
    551 
    552 /*ARGSUSED*/
    553 static rctl_qty_t
    554 project_ntasks_usage(rctl_t *r, proc_t *p)
    555 {
    556 	kproject_t *pj;
    557 	rctl_qty_t ntasks;
    558 
    559 	ASSERT(MUTEX_HELD(&p->p_lock));
    560 	pj = p->p_task->tk_proj;
    561 	mutex_enter(&p->p_zone->zone_nlwps_lock);
    562 	ntasks = pj->kpj_ntasks;
    563 	mutex_exit(&p->p_zone->zone_nlwps_lock);
    564 
    565 	return (ntasks);
    566 }
    567 
    568 /*ARGSUSED*/
    569 static int
    570 project_ntasks_test(rctl_t *r, proc_t *p, rctl_entity_p_t *e, rctl_val_t *rcntl,
    571     rctl_qty_t incr, uint_t flags)
    572 {
    573 	rctl_qty_t ntasks;
    574 
    575 	ASSERT(MUTEX_HELD(&p->p_lock));
    576 	ASSERT(e->rcep_t == RCENTITY_PROJECT);
    577 	ntasks = e->rcep_p.proj->kpj_ntasks;
    578 	if (ntasks + incr > rcntl->rcv_value)
    579 		return (1);
    580 
    581 	return (0);
    582 }
    583 
    584 /*ARGSUSED*/
    585 static int
    586 project_ntasks_set(rctl_t *rctl, struct proc *p, rctl_entity_p_t *e,
    587     rctl_qty_t nv) {
    588 
    589 	ASSERT(MUTEX_HELD(&p->p_lock));
    590 	ASSERT(e->rcep_t == RCENTITY_PROJECT);
    591 	e->rcep_p.proj->kpj_ntasks_ctl = nv;
    592 	return (0);
    593 }
    594 
    595 static rctl_ops_t project_tasks_ops = {
    596 	rcop_no_action,
    597 	project_ntasks_usage,
    598 	project_ntasks_set,
    599 	project_ntasks_test,
    600 };
    601 
    602 /*
    603  * project.max-shm-memory resource control support.
    604  */
    605 
    606 /*ARGSUSED*/
    607 static int
    608 project_shmmax_test(struct rctl *rctl, struct proc *p, rctl_entity_p_t *e,
    609     rctl_val_t *rval, rctl_qty_t inc, uint_t flags)
    610 {
    611 	rctl_qty_t v;
    612 	ASSERT(MUTEX_HELD(&p->p_lock));
    613 	ASSERT(e->rcep_t == RCENTITY_PROJECT);
    614 	v = e->rcep_p.proj->kpj_data.kpd_shmmax + inc;
    615 	if (v > rval->rcv_value)
    616 		return (1);
    617 
    618 	return (0);
    619 }
    620 
    621 static rctl_ops_t project_shmmax_ops = {
    622 	rcop_no_action,
    623 	rcop_no_usage,
    624 	rcop_no_set,
    625 	project_shmmax_test
    626 };
    627 
    628 /*
    629  * project.max-shm-ids resource control support.
    630  */
    631 
    632 /*ARGSUSED*/
    633 static int
    634 project_shmmni_test(struct rctl *rctl, struct proc *p, rctl_entity_p_t *e,
    635     rctl_val_t *rval, rctl_qty_t inc, uint_t flags)
    636 {
    637 	rctl_qty_t v;
    638 	ASSERT(MUTEX_HELD(&p->p_lock));
    639 	ASSERT(e->rcep_t == RCENTITY_PROJECT);
    640 	v = e->rcep_p.proj->kpj_data.kpd_ipc.ipcq_shmmni + inc;
    641 	if (v > rval->rcv_value)
    642 		return (1);
    643 
    644 	return (0);
    645 }
    646 
    647 static rctl_ops_t project_shmmni_ops = {
    648 	rcop_no_action,
    649 	rcop_no_usage,
    650 	rcop_no_set,
    651 	project_shmmni_test
    652 };
    653 
    654 /*
    655  * project.max-sem-ids resource control support.
    656  */
    657 
    658 /*ARGSUSED*/
    659 static int
    660 project_semmni_test(struct rctl *rctl, struct proc *p, rctl_entity_p_t *e,
    661     rctl_val_t *rval, rctl_qty_t inc, uint_t flags)
    662 {
    663 	rctl_qty_t v;
    664 	ASSERT(MUTEX_HELD(&p->p_lock));
    665 	ASSERT(e->rcep_t == RCENTITY_PROJECT);
    666 	v = e->rcep_p.proj->kpj_data.kpd_ipc.ipcq_semmni + inc;
    667 	if (v > rval->rcv_value)
    668 		return (1);
    669 
    670 	return (0);
    671 }
    672 
    673 static rctl_ops_t project_semmni_ops = {
    674 	rcop_no_action,
    675 	rcop_no_usage,
    676 	rcop_no_set,
    677 	project_semmni_test
    678 };
    679 
    680 /*
    681  * project.max-msg-ids resource control support.
    682  */
    683 
    684 /*ARGSUSED*/
    685 static int
    686 project_msgmni_test(struct rctl *rctl, struct proc *p, rctl_entity_p_t *e,
    687     rctl_val_t *rval, rctl_qty_t inc, uint_t flags)
    688 {
    689 	rctl_qty_t v;
    690 	ASSERT(MUTEX_HELD(&p->p_lock));
    691 	ASSERT(e->rcep_t == RCENTITY_PROJECT);
    692 	v = e->rcep_p.proj->kpj_data.kpd_ipc.ipcq_msgmni + inc;
    693 	if (v > rval->rcv_value)
    694 		return (1);
    695 
    696 	return (0);
    697 }
    698 
    699 static rctl_ops_t project_msgmni_ops = {
    700 	rcop_no_action,
    701 	rcop_no_usage,
    702 	rcop_no_set,
    703 	project_msgmni_test
    704 };
    705 
    706 /*ARGSUSED*/
    707 static rctl_qty_t
    708 project_locked_mem_usage(rctl_t *rctl, struct proc *p)
    709 {
    710 	rctl_qty_t q;
    711 	ASSERT(MUTEX_HELD(&p->p_lock));
    712 	mutex_enter(&p->p_zone->zone_mem_lock);
    713 	q = p->p_task->tk_proj->kpj_data.kpd_locked_mem;
    714 	mutex_exit(&p->p_zone->zone_mem_lock);
    715 	return (q);
    716 }
    717 
    718 /*ARGSUSED*/
    719 static int
    720 project_locked_mem_test(struct rctl *rctl, struct proc *p, rctl_entity_p_t *e,
    721     rctl_val_t *rval, rctl_qty_t inc, uint_t flags)
    722 {
    723 	rctl_qty_t q;
    724 	ASSERT(MUTEX_HELD(&p->p_lock));
    725 	ASSERT(MUTEX_HELD(&p->p_zone->zone_mem_lock));
    726 	q = p->p_task->tk_proj->kpj_data.kpd_locked_mem;
    727 	if (q + inc > rval->rcv_value)
    728 		return (1);
    729 	return (0);
    730 }
    731 
    732 /*ARGSUSED*/
    733 static int
    734 project_locked_mem_set(rctl_t *rctl, struct proc *p, rctl_entity_p_t *e,
    735     rctl_qty_t nv) {
    736 
    737 	ASSERT(MUTEX_HELD(&p->p_lock));
    738 	ASSERT(e->rcep_t == RCENTITY_PROJECT);
    739 	if (e->rcep_p.proj == NULL)
    740 		return (0);
    741 
    742 	e->rcep_p.proj->kpj_data.kpd_locked_mem_ctl = nv;
    743 	return (0);
    744 }
    745 
    746 static rctl_ops_t project_locked_mem_ops = {
    747 	rcop_no_action,
    748 	project_locked_mem_usage,
    749 	project_locked_mem_set,
    750 	project_locked_mem_test
    751 };
    752 
    753 /*
    754  * project.max-contracts resource control support.
    755  */
    756 
    757 /*ARGSUSED*/
    758 static int
    759 project_contract_test(struct rctl *rctl, struct proc *p, rctl_entity_p_t *e,
    760     rctl_val_t *rval, rctl_qty_t inc, uint_t flags)
    761 {
    762 	rctl_qty_t v;
    763 
    764 	ASSERT(MUTEX_HELD(&p->p_lock));
    765 	ASSERT(e->rcep_t == RCENTITY_PROJECT);
    766 
    767 	v = e->rcep_p.proj->kpj_data.kpd_contract + inc;
    768 
    769 	if ((p->p_task != NULL) && (p->p_task->tk_proj) != NULL &&
    770 	    (v > rval->rcv_value))
    771 		return (1);
    772 
    773 	return (0);
    774 }
    775 
    776 static rctl_ops_t project_contract_ops = {
    777 	rcop_no_action,
    778 	rcop_no_usage,
    779 	rcop_no_set,
    780 	project_contract_test
    781 };
    782 
    783 /*ARGSUSED*/
    784 static rctl_qty_t
    785 project_crypto_usage(rctl_t *r, proc_t *p)
    786 {
    787 	ASSERT(MUTEX_HELD(&p->p_lock));
    788 	return (p->p_task->tk_proj->kpj_data.kpd_crypto_mem);
    789 }
    790 
    791 /*ARGSUSED*/
    792 static int
    793 project_crypto_set(rctl_t *r, proc_t *p, rctl_entity_p_t *e,
    794     rctl_qty_t nv)
    795 {
    796 	ASSERT(MUTEX_HELD(&p->p_lock));
    797 	ASSERT(e->rcep_t == RCENTITY_PROJECT);
    798 	if (e->rcep_p.proj == NULL)
    799 		return (0);
    800 
    801 	e->rcep_p.proj->kpj_data.kpd_crypto_mem_ctl = nv;
    802 	return (0);
    803 }
    804 
    805 /*ARGSUSED*/
    806 static int
    807 project_crypto_test(rctl_t *r, proc_t *p, rctl_entity_p_t *e,
    808     rctl_val_t *rval, rctl_qty_t incr, uint_t flags)
    809 {
    810 	rctl_qty_t v;
    811 	ASSERT(MUTEX_HELD(&p->p_lock));
    812 	ASSERT(e->rcep_t == RCENTITY_PROJECT);
    813 	v = e->rcep_p.proj->kpj_data.kpd_crypto_mem + incr;
    814 	if (v > rval->rcv_value)
    815 		return (1);
    816 	return (0);
    817 }
    818 
    819 static rctl_ops_t project_crypto_mem_ops = {
    820 	rcop_no_action,
    821 	project_crypto_usage,
    822 	project_crypto_set,
    823 	project_crypto_test
    824 };
    825 
    826 /*
    827  * void project_init(void)
    828  *
    829  * Overview
    830  *   Initialize the project subsystem, including the primordial project 0 entry.
    831  *   Register generic project resource controls, if any.
    832  *
    833  * Return values
    834  *   None.
    835  *
    836  * Caller's context
    837  *   Safe for KM_SLEEP allocations.
    838  */
    839 void
    840 project_init(void)
    841 {
    842 	rctl_qty_t shmmni, shmmax, qty;
    843 	boolean_t check;
    844 
    845 	projects_hash = mod_hash_create_extended("projects_hash",
    846 	    project_hash_size, mod_hash_null_keydtor, project_hash_val_dtor,
    847 	    project_hash_by_id,
    848 	    (void *)(uintptr_t)mod_hash_iddata_gen(project_hash_size),
    849 	    project_hash_key_cmp, KM_SLEEP);
    850 
    851 	rc_project_cpu_shares = rctl_register("project.cpu-shares",
    852 	    RCENTITY_PROJECT, RCTL_GLOBAL_SIGNAL_NEVER |
    853 	    RCTL_GLOBAL_DENY_NEVER | RCTL_GLOBAL_NOBASIC |
    854 	    RCTL_GLOBAL_COUNT | RCTL_GLOBAL_SYSLOG_NEVER,
    855 	    FSS_MAXSHARES, FSS_MAXSHARES,
    856 	    &project_cpu_shares_ops);
    857 	rctl_add_default_limit("project.cpu-shares", 1, RCPRIV_PRIVILEGED,
    858 	    RCTL_LOCAL_NOACTION);
    859 
    860 	rc_project_cpu_cap = rctl_register("project.cpu-cap",
    861 	    RCENTITY_PROJECT, RCTL_GLOBAL_SIGNAL_NEVER |
    862 	    RCTL_GLOBAL_DENY_ALWAYS | RCTL_GLOBAL_NOBASIC |
    863 	    RCTL_GLOBAL_COUNT | RCTL_GLOBAL_SYSLOG_NEVER |
    864 	    RCTL_GLOBAL_INFINITE,
    865 	    MAXCAP, MAXCAP, &project_cpu_cap_ops);
    866 
    867 	rc_project_nlwps = rctl_register("project.max-lwps", RCENTITY_PROJECT,
    868 	    RCTL_GLOBAL_NOACTION | RCTL_GLOBAL_NOBASIC | RCTL_GLOBAL_COUNT,
    869 	    INT_MAX, INT_MAX, &project_lwps_ops);
    870 
    871 	rc_project_ntasks = rctl_register("project.max-tasks", RCENTITY_PROJECT,
    872 	    RCTL_GLOBAL_NOACTION | RCTL_GLOBAL_NOBASIC | RCTL_GLOBAL_COUNT,
    873 	    INT_MAX, INT_MAX, &project_tasks_ops);
    874 
    875 	/*
    876 	 * This rctl handle is used by /dev/crypto. It is here rather than
    877 	 * in misc/kcf or the drv/crypto module because resource controls
    878 	 * currently don't allow modules to be unloaded, and the control
    879 	 * must be registered before init starts.
    880 	 */
    881 	rc_project_crypto_mem = rctl_register("project.max-crypto-memory",
    882 	    RCENTITY_PROJECT, RCTL_GLOBAL_DENY_ALWAYS | RCTL_GLOBAL_NOBASIC |
    883 	    RCTL_GLOBAL_BYTES, UINT64_MAX, UINT64_MAX,
    884 	    &project_crypto_mem_ops);
    885 
    886 	/*
    887 	 * Default to a quarter of the machine's memory
    888 	 */
    889 	qty = availrmem_initial << (PAGESHIFT - 2);
    890 	rctl_add_default_limit("project.max-crypto-memory", qty,
    891 	    RCPRIV_PRIVILEGED, RCTL_LOCAL_DENY);
    892 
    893 	/*
    894 	 * System V IPC resource controls
    895 	 */
    896 	rc_project_semmni = rctl_register("project.max-sem-ids",
    897 	    RCENTITY_PROJECT, RCTL_GLOBAL_DENY_ALWAYS | RCTL_GLOBAL_NOBASIC |
    898 	    RCTL_GLOBAL_COUNT, IPC_IDS_MAX, IPC_IDS_MAX, &project_semmni_ops);
    899 	rctl_add_legacy_limit("project.max-sem-ids", "semsys",
    900 	    "seminfo_semmni", 128, IPC_IDS_MAX);
    901 
    902 	rc_project_msgmni = rctl_register("project.max-msg-ids",
    903 	    RCENTITY_PROJECT, RCTL_GLOBAL_DENY_ALWAYS | RCTL_GLOBAL_NOBASIC |
    904 	    RCTL_GLOBAL_COUNT, IPC_IDS_MAX, IPC_IDS_MAX, &project_msgmni_ops);
    905 	rctl_add_legacy_limit("project.max-msg-ids", "msgsys",
    906 	    "msginfo_msgmni", 128, IPC_IDS_MAX);
    907 
    908 	rc_project_shmmni = rctl_register("project.max-shm-ids",
    909 	    RCENTITY_PROJECT, RCTL_GLOBAL_DENY_ALWAYS | RCTL_GLOBAL_NOBASIC |
    910 	    RCTL_GLOBAL_COUNT, IPC_IDS_MAX, IPC_IDS_MAX, &project_shmmni_ops);
    911 	rctl_add_legacy_limit("project.max-shm-ids", "shmsys",
    912 	    "shminfo_shmmni", 128, IPC_IDS_MAX);
    913 
    914 	rc_project_shmmax = rctl_register("project.max-shm-memory",
    915 	    RCENTITY_PROJECT, RCTL_GLOBAL_DENY_ALWAYS | RCTL_GLOBAL_NOBASIC |
    916 	    RCTL_GLOBAL_BYTES, UINT64_MAX, UINT64_MAX, &project_shmmax_ops);
    917 
    918 	check = B_FALSE;
    919 	if (!mod_sysvar("shmsys", "shminfo_shmmni", &shmmni))
    920 		shmmni = 100;
    921 	else
    922 		check = B_TRUE;
    923 	if (!mod_sysvar("shmsys", "shminfo_shmmax", &shmmax))
    924 		shmmax = 0x800000;
    925 	else
    926 		check = B_TRUE;
    927 
    928 	/*
    929 	 * Default to a quarter of the machine's memory
    930 	 */
    931 	qty = availrmem_initial << (PAGESHIFT - 2);
    932 	if (check) {
    933 		if ((shmmax > 0) && (UINT64_MAX / shmmax <= shmmni))
    934 			qty = UINT64_MAX;
    935 		else if (shmmni * shmmax > qty)
    936 			qty = shmmni * shmmax;
    937 	}
    938 	rctl_add_default_limit("project.max-shm-memory", qty,
    939 	    RCPRIV_PRIVILEGED, RCTL_LOCAL_DENY);
    940 
    941 	/*
    942 	 * Event Ports resource controls
    943 	 */
    944 
    945 	rc_project_portids = rctl_register("project.max-port-ids",
    946 	    RCENTITY_PROJECT, RCTL_GLOBAL_DENY_ALWAYS | RCTL_GLOBAL_NOBASIC |
    947 	    RCTL_GLOBAL_COUNT, PORT_MAX_PORTS, PORT_MAX_PORTS,
    948 	    &rctl_absolute_ops);
    949 	rctl_add_default_limit("project.max-port-ids", PORT_DEFAULT_PORTS,
    950 	    RCPRIV_PRIVILEGED, RCTL_LOCAL_DENY);
    951 
    952 	/*
    953 	 * Resource control for locked memory
    954 	 */
    955 	rc_project_locked_mem = rctl_register(
    956 	    "project.max-locked-memory", RCENTITY_PROJECT,
    957 	    RCTL_GLOBAL_DENY_ALWAYS | RCTL_GLOBAL_NOBASIC | RCTL_GLOBAL_BYTES,
    958 	    UINT64_MAX, UINT64_MAX, &project_locked_mem_ops);
    959 
    960 	/*
    961 	 * Per project limit on contracts.
    962 	 */
    963 	rc_project_contract = rctl_register("project.max-contracts",
    964 	    RCENTITY_PROJECT, RCTL_GLOBAL_DENY_ALWAYS | RCTL_GLOBAL_NOBASIC |
    965 	    RCTL_GLOBAL_COUNT, INT_MAX, INT_MAX, &project_contract_ops);
    966 	rctl_add_default_limit("project.max-contracts", 10000,
    967 	    RCPRIV_PRIVILEGED, RCTL_LOCAL_DENY);
    968 
    969 	t0.t_proj = proj0p = project_hold_by_id(0, &zone0,
    970 	    PROJECT_HOLD_INSERT);
    971 
    972 	mutex_enter(&p0.p_lock);
    973 	proj0p->kpj_nlwps = p0.p_lwpcnt;
    974 	mutex_exit(&p0.p_lock);
    975 	proj0p->kpj_ntasks = 1;
    976 }
    977 
    978 static int
    979 project_lockedmem_kstat_update(kstat_t *ksp, int rw)
    980 {
    981 	kproject_t *pj = ksp->ks_private;
    982 	kproject_kstat_t *kpk = ksp->ks_data;
    983 
    984 	if (rw == KSTAT_WRITE)
    985 		return (EACCES);
    986 
    987 	kpk->kpk_usage.value.ui64 = pj->kpj_data.kpd_locked_mem;
    988 	kpk->kpk_value.value.ui64 = pj->kpj_data.kpd_locked_mem_ctl;
    989 	return (0);
    990 }
    991 
    992 static kstat_t *
    993 project_kstat_create(kproject_t *pj, zone_t *zone)
    994 {
    995 	kstat_t *ksp;
    996 	kproject_kstat_t *kpk;
    997 	char *zonename = zone->zone_name;
    998 
    999 	ksp = rctl_kstat_create_project(pj, "lockedmem", KSTAT_TYPE_NAMED,
   1000 	    sizeof (kproject_kstat_t) / sizeof (kstat_named_t),
   1001 	    KSTAT_FLAG_VIRTUAL);
   1002 
   1003 	if (ksp == NULL)
   1004 		return (NULL);
   1005 
   1006 	kpk = ksp->ks_data = kmem_alloc(sizeof (kproject_kstat_t), KM_SLEEP);
   1007 	ksp->ks_data_size += strlen(zonename) + 1;
   1008 	kstat_named_init(&kpk->kpk_zonename, "zonename", KSTAT_DATA_STRING);
   1009 	kstat_named_setstr(&kpk->kpk_zonename, zonename);
   1010 	kstat_named_init(&kpk->kpk_usage, "usage", KSTAT_DATA_UINT64);
   1011 	kstat_named_init(&kpk->kpk_value, "value", KSTAT_DATA_UINT64);
   1012 	ksp->ks_update = project_lockedmem_kstat_update;
   1013 	ksp->ks_private = pj;
   1014 	kstat_install(ksp);
   1015 
   1016 	return (ksp);
   1017 }
   1018 
   1019 static void
   1020 project_kstat_delete(kproject_t *pj)
   1021 {
   1022 	void *data;
   1023 
   1024 	if (pj->kpj_data.kpd_lockedmem_kstat != NULL) {
   1025 		data = pj->kpj_data.kpd_lockedmem_kstat->ks_data;
   1026 		kstat_delete(pj->kpj_data.kpd_lockedmem_kstat);
   1027 		kmem_free(data, sizeof (kproject_kstat_t));
   1028 	}
   1029 	pj->kpj_data.kpd_lockedmem_kstat = NULL;
   1030 }
   1031