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 (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
23 * Copyright 2016, Joyent, Inc.
24 */
25
26 #include <sys/project.h>
27 #include <sys/modhash.h>
28 #include <sys/modctl.h>
29 #include <sys/kmem.h>
30 #include <sys/kstat.h>
31 #include <sys/atomic.h>
32 #include <sys/cmn_err.h>
33 #include <sys/proc.h>
34 #include <sys/rctl.h>
35 #include <sys/sunddi.h>
36 #include <sys/fss.h>
37 #include <sys/systm.h>
38 #include <sys/ipc_impl.h>
39 #include <sys/port_kernel.h>
40 #include <sys/task.h>
41 #include <sys/zone.h>
42 #include <sys/cpucaps.h>
43 #include <sys/klpd.h>
44
45 int project_hash_size = 64;
46 static kmutex_t project_hash_lock;
47 static kmutex_t projects_list_lock;
48 static mod_hash_t *projects_hash;
49 static kproject_t *projects_list;
50
51 rctl_hndl_t rc_project_cpu_shares;
52 rctl_hndl_t rc_project_cpu_cap;
53 rctl_hndl_t rc_project_nlwps;
54 rctl_hndl_t rc_project_nprocs;
55 rctl_hndl_t rc_project_ntasks;
56 rctl_hndl_t rc_project_msgmni;
57 rctl_hndl_t rc_project_semmni;
58 rctl_hndl_t rc_project_shmmax;
59 rctl_hndl_t rc_project_shmmni;
60 rctl_hndl_t rc_project_portids;
61 rctl_hndl_t rc_project_locked_mem;
62 rctl_hndl_t rc_project_contract;
63 rctl_hndl_t rc_project_crypto_mem;
64
65 /*
66 * Dummy structure used when comparing projects. This structure must be kept
67 * identical to the first two fields of kproject_t.
68 */
69 struct project_zone {
70 projid_t kpj_id;
71 zoneid_t kpj_zoneid;
72 };
73
74 /*
75 * Projects
76 *
77 * A dictionary of all active projects is maintained by the kernel so that we
78 * may track project usage and limits. (By an active project, we mean a
79 * project associated with one or more task, and therefore with one or more
80 * processes.) We build the dictionary on top of the mod_hash facility, since
81 * project additions and deletions are relatively rare events. An
82 * integer-to-pointer mapping is maintained within the hash, representing the
83 * map from project id to project structure. All projects, including the
84 * primordial "project 0", are allocated via the project_hold_by_id()
85 * interface.
86 *
87 * Currently, the project contains a reference count; the project ID, which is
88 * examined by the extended accounting subsystem as well as /proc; a resource
89 * control set, which contains the allowable values (and actions on exceeding
90 * those values) for controlled project-level resources on the system; and a
91 * number of CPU shares, which is used by the fair share scheduling class
92 * (FSS) to support its proportion-based scheduling algorithm.
93 *
94 * Reference counting convention
95 * The dictionary entry does not itself count as a reference--only references
96 * outside of the subsystem are tallied. At the drop of the final external
97 * reference, the project entry is removed. The reference counter keeps
98 * track of the number of threads *and* tasks within a project.
99 *
100 * Locking
101 * Walking the doubly-linked project list must be done while holding
102 * projects_list_lock. Thus, any dereference of kpj_next or kpj_prev must be
103 * under projects_list_lock.
104 *
105 * If both the hash lock, project_hash_lock, and the list lock are to be
106 * acquired, the hash lock is to be acquired first.
107 */
108
109 static void project_kstat_create(kproject_t *pj, zone_t *zone);
110 static void project_kstat_delete(kproject_t *pj);
111
112 static void
project_data_init(kproject_data_t * data)113 project_data_init(kproject_data_t *data)
114 {
115 /*
116 * Initialize subsystem-specific data
117 */
118 data->kpd_shmmax = 0;
119 data->kpd_ipc.ipcq_shmmni = 0;
120 data->kpd_ipc.ipcq_semmni = 0;
121 data->kpd_ipc.ipcq_msgmni = 0;
122 data->kpd_locked_mem = 0;
123 data->kpd_locked_mem_ctl = UINT64_MAX;
124 data->kpd_contract = 0;
125 data->kpd_crypto_mem = 0;
126 data->kpd_crypto_mem_ctl = UINT64_MAX;
127 data->kpd_lockedmem_kstat = NULL;
128 data->kpd_nprocs_kstat = NULL;
129 }
130
131 /*ARGSUSED*/
132 static uint_t
project_hash_by_id(void * hash_data,mod_hash_key_t key)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
project_hash_key_cmp(mod_hash_key_t key1,mod_hash_key_t key2)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
project_hash_val_dtor(mod_hash_val_t val)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 *
project_hold(kproject_t * p)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 *
project_hold_by_id(projid_t id,zone_t * zone,int flag)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
225 pz.kpj_id = id;
226 pz.kpj_zoneid = zone->zone_id;
227
228 if (flag == PROJECT_HOLD_FIND) {
229 mutex_enter(&project_hash_lock);
230
231 if (mod_hash_find(projects_hash, (mod_hash_key_t)&pz,
232 (mod_hash_val_t)&p) == MH_ERR_NOTFOUND)
233 p = NULL;
234 else
235 p->kpj_count++;
236
237 mutex_exit(&project_hash_lock);
238 return (p);
239 }
240
241 ASSERT(flag == PROJECT_HOLD_INSERT);
242
243 spare_p = kmem_zalloc(sizeof (kproject_t), KM_SLEEP);
244 set = rctl_set_create();
245
246 gp = rctl_set_init_prealloc(RCENTITY_PROJECT);
247
248 (void) mod_hash_reserve(projects_hash, &hndl);
249
250 mutex_enter(&curproc->p_lock);
251 mutex_enter(&project_hash_lock);
252 if (mod_hash_find(projects_hash, (mod_hash_key_t)&pz,
253 (mod_hash_val_t *)&p) == MH_ERR_NOTFOUND) {
254
255 p = spare_p;
256 p->kpj_id = id;
257 p->kpj_zone = zone;
258 p->kpj_zoneid = zone->zone_id;
259 p->kpj_count = 0;
260 p->kpj_shares = 1;
261 p->kpj_nlwps = 0;
262 p->kpj_nprocs = 0;
263 p->kpj_ntasks = 0;
264 p->kpj_nlwps_ctl = INT_MAX;
265 p->kpj_nprocs_ctl = INT_MAX;
266 p->kpj_ntasks_ctl = INT_MAX;
267 project_data_init(&p->kpj_data);
268 e.rcep_p.proj = p;
269 e.rcep_t = RCENTITY_PROJECT;
270 p->kpj_rctls = rctl_set_init(RCENTITY_PROJECT, curproc, &e,
271 set, gp);
272 mutex_exit(&curproc->p_lock);
273
274 if (mod_hash_insert_reserve(projects_hash, (mod_hash_key_t)p,
275 (mod_hash_val_t)p, hndl))
276 panic("unable to insert project %d(%p)", id, (void *)p);
277
278 /*
279 * Insert project into global project list.
280 */
281 mutex_enter(&projects_list_lock);
282 if (id != 0 || zone != &zone0) {
283 p->kpj_next = projects_list;
284 p->kpj_prev = projects_list->kpj_prev;
285 p->kpj_prev->kpj_next = p;
286 projects_list->kpj_prev = p;
287 } else {
288 /*
289 * Special case: primordial hold on project 0.
290 */
291 p->kpj_next = p;
292 p->kpj_prev = p;
293 projects_list = p;
294 }
295 mutex_exit(&projects_list_lock);
296 create = B_TRUE;
297 } else {
298 mutex_exit(&curproc->p_lock);
299 mod_hash_cancel(projects_hash, &hndl);
300 kmem_free(spare_p, sizeof (kproject_t));
301 rctl_set_free(set);
302 }
303
304 rctl_prealloc_destroy(gp);
305 p->kpj_count++;
306 mutex_exit(&project_hash_lock);
307
308 /*
309 * The kstat stores the project's zone name, as zoneid's may change
310 * across reboots.
311 */
312 if (create == B_TRUE) {
313 /*
314 * Inform CPU caps framework of the new project
315 */
316 cpucaps_project_add(p);
317 /*
318 * Set up project kstats
319 */
320 project_kstat_create(p, zone);
321 }
322 return (p);
323 }
324
325 /*
326 * void project_rele(kproject_t *)
327 *
328 * Overview
329 * Advertise that one external reference to this project is no longer needed.
330 *
331 * Return values
332 * None.
333 *
334 * Caller's context
335 * No restriction on context.
336 */
337 void
project_rele(kproject_t * p)338 project_rele(kproject_t *p)
339 {
340 mutex_enter(&project_hash_lock);
341 ASSERT(p->kpj_count != 0);
342 p->kpj_count--;
343 if (p->kpj_count == 0) {
344
345 /*
346 * Remove project from global list.
347 */
348 ASSERT(p->kpj_nprocs == 0);
349
350 mutex_enter(&projects_list_lock);
351 p->kpj_next->kpj_prev = p->kpj_prev;
352 p->kpj_prev->kpj_next = p->kpj_next;
353 if (projects_list == p)
354 projects_list = p->kpj_next;
355 mutex_exit(&projects_list_lock);
356
357 cpucaps_project_remove(p);
358
359 rctl_set_free(p->kpj_rctls);
360 project_kstat_delete(p);
361
362 if (p->kpj_klpd != NULL)
363 klpd_freelist(&p->kpj_klpd);
364
365 if (mod_hash_destroy(projects_hash, (mod_hash_key_t)p))
366 panic("unable to delete project %d zone %d", p->kpj_id,
367 p->kpj_zoneid);
368
369 }
370 mutex_exit(&project_hash_lock);
371 }
372
373 /*
374 * int project_walk_all(zoneid_t, int (*)(kproject_t *, void *), void *)
375 *
376 * Overview
377 * Walk the project list for the given zoneid with a callback.
378 *
379 * Return values
380 * -1 for an invalid walk, number of projects visited otherwise.
381 *
382 * Caller's context
383 * projects_list_lock must not be held, as it is acquired by
384 * project_walk_all(). Accordingly, callbacks may not perform KM_SLEEP
385 * allocations.
386 */
387 int
project_walk_all(zoneid_t zoneid,int (* cb)(kproject_t *,void *),void * walk_data)388 project_walk_all(zoneid_t zoneid, int (*cb)(kproject_t *, void *),
389 void *walk_data)
390 {
391 int cnt = 0;
392 kproject_t *kp = proj0p;
393
394 mutex_enter(&projects_list_lock);
395 do {
396 if (zoneid != ALL_ZONES && kp->kpj_zoneid != zoneid)
397 continue;
398 if (cb(kp, walk_data) == -1) {
399 cnt = -1;
400 break;
401 } else {
402 cnt++;
403 }
404 } while ((kp = kp->kpj_next) != proj0p);
405 mutex_exit(&projects_list_lock);
406 return (cnt);
407 }
408
409 /*
410 * projid_t curprojid(void)
411 *
412 * Overview
413 * Return project ID of the current thread
414 *
415 * Caller's context
416 * No restrictions.
417 */
418 projid_t
curprojid()419 curprojid()
420 {
421 return (ttoproj(curthread)->kpj_id);
422 }
423
424 /*
425 * project.cpu-shares resource control support.
426 */
427 /*ARGSUSED*/
428 static rctl_qty_t
project_cpu_shares_usage(rctl_t * rctl,struct proc * p)429 project_cpu_shares_usage(rctl_t *rctl, struct proc *p)
430 {
431 ASSERT(MUTEX_HELD(&p->p_lock));
432 return (p->p_task->tk_proj->kpj_shares);
433 }
434
435 /*ARGSUSED*/
436 static int
project_cpu_shares_set(rctl_t * rctl,struct proc * p,rctl_entity_p_t * e,rctl_qty_t nv)437 project_cpu_shares_set(rctl_t *rctl, struct proc *p, rctl_entity_p_t *e,
438 rctl_qty_t nv)
439 {
440 ASSERT(MUTEX_HELD(&p->p_lock));
441 ASSERT(e->rcep_t == RCENTITY_PROJECT);
442 if (e->rcep_p.proj == NULL)
443 return (0);
444
445 e->rcep_p.proj->kpj_shares = nv;
446
447 return (0);
448 }
449
450 static rctl_ops_t project_cpu_shares_ops = {
451 rcop_no_action,
452 project_cpu_shares_usage,
453 project_cpu_shares_set,
454 rcop_no_test
455 };
456
457
458 /*
459 * project.cpu-cap resource control support.
460 */
461 /*ARGSUSED*/
462 static rctl_qty_t
project_cpu_cap_get(rctl_t * rctl,struct proc * p)463 project_cpu_cap_get(rctl_t *rctl, struct proc *p)
464 {
465 ASSERT(MUTEX_HELD(&p->p_lock));
466 return (cpucaps_project_get(p->p_task->tk_proj));
467 }
468
469 /*ARGSUSED*/
470 static int
project_cpu_cap_set(rctl_t * rctl,struct proc * p,rctl_entity_p_t * e,rctl_qty_t nv)471 project_cpu_cap_set(rctl_t *rctl, struct proc *p, rctl_entity_p_t *e,
472 rctl_qty_t nv)
473 {
474 kproject_t *kpj = e->rcep_p.proj;
475
476 ASSERT(MUTEX_HELD(&p->p_lock));
477 ASSERT(e->rcep_t == RCENTITY_PROJECT);
478 if (kpj == NULL)
479 return (0);
480
481 /*
482 * set cap to the new value.
483 */
484 return (cpucaps_project_set(kpj, nv));
485 }
486
487 static rctl_ops_t project_cpu_cap_ops = {
488 rcop_no_action,
489 project_cpu_cap_get,
490 project_cpu_cap_set,
491 rcop_no_test
492 };
493
494 /*ARGSUSED*/
495 static rctl_qty_t
project_lwps_usage(rctl_t * r,proc_t * p)496 project_lwps_usage(rctl_t *r, proc_t *p)
497 {
498 kproject_t *pj;
499 rctl_qty_t nlwps;
500
501 ASSERT(MUTEX_HELD(&p->p_lock));
502 pj = p->p_task->tk_proj;
503 mutex_enter(&p->p_zone->zone_nlwps_lock);
504 nlwps = pj->kpj_nlwps;
505 mutex_exit(&p->p_zone->zone_nlwps_lock);
506
507 return (nlwps);
508 }
509
510 /*ARGSUSED*/
511 static int
project_lwps_test(rctl_t * r,proc_t * p,rctl_entity_p_t * e,rctl_val_t * rcntl,rctl_qty_t incr,uint_t flags)512 project_lwps_test(rctl_t *r, proc_t *p, rctl_entity_p_t *e, rctl_val_t *rcntl,
513 rctl_qty_t incr, uint_t flags)
514 {
515 rctl_qty_t nlwps;
516
517 ASSERT(MUTEX_HELD(&p->p_lock));
518 ASSERT(MUTEX_HELD(&p->p_zone->zone_nlwps_lock));
519 ASSERT(e->rcep_t == RCENTITY_PROJECT);
520 if (e->rcep_p.proj == NULL)
521 return (0);
522
523 nlwps = e->rcep_p.proj->kpj_nlwps;
524 if (nlwps + incr > rcntl->rcv_value)
525 return (1);
526
527 return (0);
528 }
529
530 /*ARGSUSED*/
531 static int
project_lwps_set(rctl_t * rctl,struct proc * p,rctl_entity_p_t * e,rctl_qty_t nv)532 project_lwps_set(rctl_t *rctl, struct proc *p, rctl_entity_p_t *e,
533 rctl_qty_t nv) {
534
535 ASSERT(MUTEX_HELD(&p->p_lock));
536 ASSERT(e->rcep_t == RCENTITY_PROJECT);
537 if (e->rcep_p.proj == NULL)
538 return (0);
539
540 e->rcep_p.proj->kpj_nlwps_ctl = nv;
541 return (0);
542 }
543
544 static rctl_ops_t project_lwps_ops = {
545 rcop_no_action,
546 project_lwps_usage,
547 project_lwps_set,
548 project_lwps_test,
549 };
550
551 /*ARGSUSED*/
552 static rctl_qty_t
project_procs_usage(rctl_t * r,proc_t * p)553 project_procs_usage(rctl_t *r, proc_t *p)
554 {
555 kproject_t *pj;
556 rctl_qty_t nprocs;
557
558 ASSERT(MUTEX_HELD(&p->p_lock));
559 pj = p->p_task->tk_proj;
560 mutex_enter(&p->p_zone->zone_nlwps_lock);
561 nprocs = pj->kpj_nprocs;
562 mutex_exit(&p->p_zone->zone_nlwps_lock);
563
564 return (nprocs);
565 }
566
567 /*ARGSUSED*/
568 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)569 project_procs_test(rctl_t *r, proc_t *p, rctl_entity_p_t *e, rctl_val_t *rcntl,
570 rctl_qty_t incr, uint_t flags)
571 {
572 rctl_qty_t nprocs;
573
574 ASSERT(MUTEX_HELD(&p->p_lock));
575 ASSERT(MUTEX_HELD(&p->p_zone->zone_nlwps_lock));
576 ASSERT(e->rcep_t == RCENTITY_PROJECT);
577 if (e->rcep_p.proj == NULL)
578 return (0);
579
580 nprocs = e->rcep_p.proj->kpj_nprocs;
581 if (nprocs + incr > rcntl->rcv_value)
582 return (1);
583
584 return (0);
585 }
586
587 /*ARGSUSED*/
588 static int
project_procs_set(rctl_t * rctl,struct proc * p,rctl_entity_p_t * e,rctl_qty_t nv)589 project_procs_set(rctl_t *rctl, struct proc *p, rctl_entity_p_t *e,
590 rctl_qty_t nv) {
591
592 ASSERT(MUTEX_HELD(&p->p_lock));
593 ASSERT(e->rcep_t == RCENTITY_PROJECT);
594 if (e->rcep_p.proj == NULL)
595 return (0);
596
597 e->rcep_p.proj->kpj_nprocs_ctl = nv;
598 return (0);
599 }
600
601 static rctl_ops_t project_procs_ops = {
602 rcop_no_action,
603 project_procs_usage,
604 project_procs_set,
605 project_procs_test,
606 };
607
608 /*ARGSUSED*/
609 static rctl_qty_t
project_ntasks_usage(rctl_t * r,proc_t * p)610 project_ntasks_usage(rctl_t *r, proc_t *p)
611 {
612 kproject_t *pj;
613 rctl_qty_t ntasks;
614
615 ASSERT(MUTEX_HELD(&p->p_lock));
616 pj = p->p_task->tk_proj;
617 mutex_enter(&p->p_zone->zone_nlwps_lock);
618 ntasks = pj->kpj_ntasks;
619 mutex_exit(&p->p_zone->zone_nlwps_lock);
620
621 return (ntasks);
622 }
623
624 /*ARGSUSED*/
625 static int
project_ntasks_test(rctl_t * r,proc_t * p,rctl_entity_p_t * e,rctl_val_t * rcntl,rctl_qty_t incr,uint_t flags)626 project_ntasks_test(rctl_t *r, proc_t *p, rctl_entity_p_t *e, rctl_val_t *rcntl,
627 rctl_qty_t incr, uint_t flags)
628 {
629 rctl_qty_t ntasks;
630
631 ASSERT(MUTEX_HELD(&p->p_lock));
632 ASSERT(e->rcep_t == RCENTITY_PROJECT);
633 ntasks = e->rcep_p.proj->kpj_ntasks;
634 if (ntasks + incr > rcntl->rcv_value)
635 return (1);
636
637 return (0);
638 }
639
640 /*ARGSUSED*/
641 static int
project_ntasks_set(rctl_t * rctl,struct proc * p,rctl_entity_p_t * e,rctl_qty_t nv)642 project_ntasks_set(rctl_t *rctl, struct proc *p, rctl_entity_p_t *e,
643 rctl_qty_t nv) {
644
645 ASSERT(MUTEX_HELD(&p->p_lock));
646 ASSERT(e->rcep_t == RCENTITY_PROJECT);
647 e->rcep_p.proj->kpj_ntasks_ctl = nv;
648 return (0);
649 }
650
651 static rctl_ops_t project_tasks_ops = {
652 rcop_no_action,
653 project_ntasks_usage,
654 project_ntasks_set,
655 project_ntasks_test,
656 };
657
658 /*
659 * project.max-shm-memory resource control support.
660 */
661
662 /*ARGSUSED*/
663 static rctl_qty_t
project_shmmax_usage(rctl_t * rctl,struct proc * p)664 project_shmmax_usage(rctl_t *rctl, struct proc *p)
665 {
666 ASSERT(MUTEX_HELD(&p->p_lock));
667 return (p->p_task->tk_proj->kpj_data.kpd_shmmax);
668 }
669
670 /*ARGSUSED*/
671 static int
project_shmmax_test(struct rctl * rctl,struct proc * p,rctl_entity_p_t * e,rctl_val_t * rval,rctl_qty_t inc,uint_t flags)672 project_shmmax_test(struct rctl *rctl, struct proc *p, rctl_entity_p_t *e,
673 rctl_val_t *rval, rctl_qty_t inc, uint_t flags)
674 {
675 rctl_qty_t v;
676 ASSERT(MUTEX_HELD(&p->p_lock));
677 ASSERT(e->rcep_t == RCENTITY_PROJECT);
678 v = e->rcep_p.proj->kpj_data.kpd_shmmax + inc;
679 if (v > rval->rcv_value)
680 return (1);
681
682 return (0);
683 }
684
685 static rctl_ops_t project_shmmax_ops = {
686 rcop_no_action,
687 project_shmmax_usage,
688 rcop_no_set,
689 project_shmmax_test
690 };
691
692 /*
693 * project.max-shm-ids resource control support.
694 */
695
696 /*ARGSUSED*/
697 static rctl_qty_t
project_shmmni_usage(rctl_t * rctl,struct proc * p)698 project_shmmni_usage(rctl_t *rctl, struct proc *p)
699 {
700 ASSERT(MUTEX_HELD(&p->p_lock));
701 return (p->p_task->tk_proj->kpj_data.kpd_ipc.ipcq_shmmni);
702 }
703
704 /*ARGSUSED*/
705 static int
project_shmmni_test(struct rctl * rctl,struct proc * p,rctl_entity_p_t * e,rctl_val_t * rval,rctl_qty_t inc,uint_t flags)706 project_shmmni_test(struct rctl *rctl, struct proc *p, rctl_entity_p_t *e,
707 rctl_val_t *rval, rctl_qty_t inc, uint_t flags)
708 {
709 rctl_qty_t v;
710 ASSERT(MUTEX_HELD(&p->p_lock));
711 ASSERT(e->rcep_t == RCENTITY_PROJECT);
712 v = e->rcep_p.proj->kpj_data.kpd_ipc.ipcq_shmmni + inc;
713 if (v > rval->rcv_value)
714 return (1);
715
716 return (0);
717 }
718
719 static rctl_ops_t project_shmmni_ops = {
720 rcop_no_action,
721 project_shmmni_usage,
722 rcop_no_set,
723 project_shmmni_test
724 };
725
726 /*
727 * project.max-sem-ids resource control support.
728 */
729
730 /*ARGSUSED*/
731 static rctl_qty_t
project_semmni_usage(rctl_t * rctl,struct proc * p)732 project_semmni_usage(rctl_t *rctl, struct proc *p)
733 {
734 ASSERT(MUTEX_HELD(&p->p_lock));
735 return (p->p_task->tk_proj->kpj_data.kpd_ipc.ipcq_semmni);
736 }
737
738 /*ARGSUSED*/
739 static int
project_semmni_test(struct rctl * rctl,struct proc * p,rctl_entity_p_t * e,rctl_val_t * rval,rctl_qty_t inc,uint_t flags)740 project_semmni_test(struct rctl *rctl, struct proc *p, rctl_entity_p_t *e,
741 rctl_val_t *rval, rctl_qty_t inc, uint_t flags)
742 {
743 rctl_qty_t v;
744 ASSERT(MUTEX_HELD(&p->p_lock));
745 ASSERT(e->rcep_t == RCENTITY_PROJECT);
746 v = e->rcep_p.proj->kpj_data.kpd_ipc.ipcq_semmni + inc;
747 if (v > rval->rcv_value)
748 return (1);
749
750 return (0);
751 }
752
753 static rctl_ops_t project_semmni_ops = {
754 rcop_no_action,
755 project_semmni_usage,
756 rcop_no_set,
757 project_semmni_test
758 };
759
760 /*
761 * project.max-msg-ids resource control support.
762 */
763
764 /*ARGSUSED*/
765 static rctl_qty_t
project_msgmni_usage(rctl_t * rctl,struct proc * p)766 project_msgmni_usage(rctl_t *rctl, struct proc *p)
767 {
768 ASSERT(MUTEX_HELD(&p->p_lock));
769 return (p->p_task->tk_proj->kpj_data.kpd_ipc.ipcq_msgmni);
770 }
771
772 /*ARGSUSED*/
773 static int
project_msgmni_test(struct rctl * rctl,struct proc * p,rctl_entity_p_t * e,rctl_val_t * rval,rctl_qty_t inc,uint_t flags)774 project_msgmni_test(struct rctl *rctl, struct proc *p, rctl_entity_p_t *e,
775 rctl_val_t *rval, rctl_qty_t inc, uint_t flags)
776 {
777 rctl_qty_t v;
778 ASSERT(MUTEX_HELD(&p->p_lock));
779 ASSERT(e->rcep_t == RCENTITY_PROJECT);
780 v = e->rcep_p.proj->kpj_data.kpd_ipc.ipcq_msgmni + inc;
781 if (v > rval->rcv_value)
782 return (1);
783
784 return (0);
785 }
786
787 static rctl_ops_t project_msgmni_ops = {
788 rcop_no_action,
789 project_msgmni_usage,
790 rcop_no_set,
791 project_msgmni_test
792 };
793
794 /*ARGSUSED*/
795 static rctl_qty_t
project_locked_mem_usage(rctl_t * rctl,struct proc * p)796 project_locked_mem_usage(rctl_t *rctl, struct proc *p)
797 {
798 rctl_qty_t q;
799 ASSERT(MUTEX_HELD(&p->p_lock));
800 mutex_enter(&p->p_zone->zone_mem_lock);
801 q = p->p_task->tk_proj->kpj_data.kpd_locked_mem;
802 mutex_exit(&p->p_zone->zone_mem_lock);
803 return (q);
804 }
805
806 /*ARGSUSED*/
807 static int
project_locked_mem_test(struct rctl * rctl,struct proc * p,rctl_entity_p_t * e,rctl_val_t * rval,rctl_qty_t inc,uint_t flags)808 project_locked_mem_test(struct rctl *rctl, struct proc *p, rctl_entity_p_t *e,
809 rctl_val_t *rval, rctl_qty_t inc, uint_t flags)
810 {
811 rctl_qty_t q;
812 ASSERT(MUTEX_HELD(&p->p_lock));
813 ASSERT(MUTEX_HELD(&p->p_zone->zone_mem_lock));
814 q = p->p_task->tk_proj->kpj_data.kpd_locked_mem;
815 if (q + inc > rval->rcv_value)
816 return (1);
817 return (0);
818 }
819
820 /*ARGSUSED*/
821 static int
project_locked_mem_set(rctl_t * rctl,struct proc * p,rctl_entity_p_t * e,rctl_qty_t nv)822 project_locked_mem_set(rctl_t *rctl, struct proc *p, rctl_entity_p_t *e,
823 rctl_qty_t nv) {
824
825 ASSERT(MUTEX_HELD(&p->p_lock));
826 ASSERT(e->rcep_t == RCENTITY_PROJECT);
827 if (e->rcep_p.proj == NULL)
828 return (0);
829
830 e->rcep_p.proj->kpj_data.kpd_locked_mem_ctl = nv;
831 return (0);
832 }
833
834 static rctl_ops_t project_locked_mem_ops = {
835 rcop_no_action,
836 project_locked_mem_usage,
837 project_locked_mem_set,
838 project_locked_mem_test
839 };
840
841 /*
842 * project.max-contracts resource control support.
843 */
844
845 /*ARGSUSED*/
846 static int
project_contract_test(struct rctl * rctl,struct proc * p,rctl_entity_p_t * e,rctl_val_t * rval,rctl_qty_t inc,uint_t flags)847 project_contract_test(struct rctl *rctl, struct proc *p, rctl_entity_p_t *e,
848 rctl_val_t *rval, rctl_qty_t inc, uint_t flags)
849 {
850 rctl_qty_t v;
851
852 ASSERT(MUTEX_HELD(&p->p_lock));
853 ASSERT(e->rcep_t == RCENTITY_PROJECT);
854
855 v = e->rcep_p.proj->kpj_data.kpd_contract + inc;
856
857 if ((p->p_task != NULL) && (p->p_task->tk_proj) != NULL &&
858 (v > rval->rcv_value))
859 return (1);
860
861 return (0);
862 }
863
864 static rctl_ops_t project_contract_ops = {
865 rcop_no_action,
866 rcop_no_usage,
867 rcop_no_set,
868 project_contract_test
869 };
870
871 /*ARGSUSED*/
872 static rctl_qty_t
project_crypto_usage(rctl_t * r,proc_t * p)873 project_crypto_usage(rctl_t *r, proc_t *p)
874 {
875 ASSERT(MUTEX_HELD(&p->p_lock));
876 return (p->p_task->tk_proj->kpj_data.kpd_crypto_mem);
877 }
878
879 /*ARGSUSED*/
880 static int
project_crypto_set(rctl_t * r,proc_t * p,rctl_entity_p_t * e,rctl_qty_t nv)881 project_crypto_set(rctl_t *r, proc_t *p, rctl_entity_p_t *e,
882 rctl_qty_t nv)
883 {
884 ASSERT(MUTEX_HELD(&p->p_lock));
885 ASSERT(e->rcep_t == RCENTITY_PROJECT);
886 if (e->rcep_p.proj == NULL)
887 return (0);
888
889 e->rcep_p.proj->kpj_data.kpd_crypto_mem_ctl = nv;
890 return (0);
891 }
892
893 /*ARGSUSED*/
894 static int
project_crypto_test(rctl_t * r,proc_t * p,rctl_entity_p_t * e,rctl_val_t * rval,rctl_qty_t incr,uint_t flags)895 project_crypto_test(rctl_t *r, proc_t *p, rctl_entity_p_t *e,
896 rctl_val_t *rval, rctl_qty_t incr, uint_t flags)
897 {
898 rctl_qty_t v;
899 ASSERT(MUTEX_HELD(&p->p_lock));
900 ASSERT(e->rcep_t == RCENTITY_PROJECT);
901 v = e->rcep_p.proj->kpj_data.kpd_crypto_mem + incr;
902 if (v > rval->rcv_value)
903 return (1);
904 return (0);
905 }
906
907 static rctl_ops_t project_crypto_mem_ops = {
908 rcop_no_action,
909 project_crypto_usage,
910 project_crypto_set,
911 project_crypto_test
912 };
913
914 /*
915 * void project_init(void)
916 *
917 * Overview
918 * Initialize the project subsystem, including the primordial project 0 entry.
919 * Register generic project resource controls, if any.
920 *
921 * Return values
922 * None.
923 *
924 * Caller's context
925 * Safe for KM_SLEEP allocations.
926 */
927 void
project_init(void)928 project_init(void)
929 {
930 rctl_qty_t shmmni, shmmax, qty;
931 boolean_t check;
932
933 projects_hash = mod_hash_create_extended("projects_hash",
934 project_hash_size, mod_hash_null_keydtor, project_hash_val_dtor,
935 project_hash_by_id,
936 (void *)(uintptr_t)mod_hash_iddata_gen(project_hash_size),
937 project_hash_key_cmp, KM_SLEEP);
938
939 rc_project_cpu_shares = rctl_register("project.cpu-shares",
940 RCENTITY_PROJECT, RCTL_GLOBAL_SIGNAL_NEVER |
941 RCTL_GLOBAL_DENY_NEVER | RCTL_GLOBAL_NOBASIC |
942 RCTL_GLOBAL_COUNT | RCTL_GLOBAL_SYSLOG_NEVER,
943 FSS_MAXSHARES, FSS_MAXSHARES,
944 &project_cpu_shares_ops);
945 rctl_add_default_limit("project.cpu-shares", 1, RCPRIV_PRIVILEGED,
946 RCTL_LOCAL_NOACTION);
947
948 rc_project_cpu_cap = rctl_register("project.cpu-cap",
949 RCENTITY_PROJECT, RCTL_GLOBAL_SIGNAL_NEVER |
950 RCTL_GLOBAL_DENY_ALWAYS | RCTL_GLOBAL_NOBASIC |
951 RCTL_GLOBAL_COUNT | RCTL_GLOBAL_SYSLOG_NEVER |
952 RCTL_GLOBAL_INFINITE,
953 MAXCAP, MAXCAP, &project_cpu_cap_ops);
954
955 rc_project_nlwps = rctl_register("project.max-lwps", RCENTITY_PROJECT,
956 RCTL_GLOBAL_NOACTION | RCTL_GLOBAL_NOBASIC | RCTL_GLOBAL_COUNT,
957 INT_MAX, INT_MAX, &project_lwps_ops);
958
959 rc_project_nprocs = rctl_register("project.max-processes",
960 RCENTITY_PROJECT, RCTL_GLOBAL_NOACTION | RCTL_GLOBAL_NOBASIC |
961 RCTL_GLOBAL_COUNT, INT_MAX, INT_MAX, &project_procs_ops);
962
963 rc_project_ntasks = rctl_register("project.max-tasks", RCENTITY_PROJECT,
964 RCTL_GLOBAL_NOACTION | RCTL_GLOBAL_NOBASIC | RCTL_GLOBAL_COUNT,
965 INT_MAX, INT_MAX, &project_tasks_ops);
966
967 /*
968 * This rctl handle is used by /dev/crypto. It is here rather than
969 * in misc/kcf or the drv/crypto module because resource controls
970 * currently don't allow modules to be unloaded, and the control
971 * must be registered before init starts.
972 */
973 rc_project_crypto_mem = rctl_register("project.max-crypto-memory",
974 RCENTITY_PROJECT, RCTL_GLOBAL_DENY_ALWAYS | RCTL_GLOBAL_NOBASIC |
975 RCTL_GLOBAL_BYTES, UINT64_MAX, UINT64_MAX,
976 &project_crypto_mem_ops);
977
978 /*
979 * Default to a quarter of the machine's memory
980 */
981 qty = availrmem_initial << (PAGESHIFT - 2);
982 rctl_add_default_limit("project.max-crypto-memory", qty,
983 RCPRIV_PRIVILEGED, RCTL_LOCAL_DENY);
984
985 /*
986 * System V IPC resource controls
987 */
988 rc_project_semmni = rctl_register("project.max-sem-ids",
989 RCENTITY_PROJECT, RCTL_GLOBAL_DENY_ALWAYS | RCTL_GLOBAL_NOBASIC |
990 RCTL_GLOBAL_COUNT, IPC_IDS_MAX, IPC_IDS_MAX, &project_semmni_ops);
991 rctl_add_legacy_limit("project.max-sem-ids", "semsys",
992 "seminfo_semmni", 128, IPC_IDS_MAX);
993
994 rc_project_msgmni = rctl_register("project.max-msg-ids",
995 RCENTITY_PROJECT, RCTL_GLOBAL_DENY_ALWAYS | RCTL_GLOBAL_NOBASIC |
996 RCTL_GLOBAL_COUNT, IPC_IDS_MAX, IPC_IDS_MAX, &project_msgmni_ops);
997 rctl_add_legacy_limit("project.max-msg-ids", "msgsys",
998 "msginfo_msgmni", 128, IPC_IDS_MAX);
999
1000 rc_project_shmmni = rctl_register("project.max-shm-ids",
1001 RCENTITY_PROJECT, RCTL_GLOBAL_DENY_ALWAYS | RCTL_GLOBAL_NOBASIC |
1002 RCTL_GLOBAL_COUNT, IPC_IDS_MAX, IPC_IDS_MAX, &project_shmmni_ops);
1003 rctl_add_legacy_limit("project.max-shm-ids", "shmsys",
1004 "shminfo_shmmni", 128, IPC_IDS_MAX);
1005
1006 rc_project_shmmax = rctl_register("project.max-shm-memory",
1007 RCENTITY_PROJECT, RCTL_GLOBAL_DENY_ALWAYS | RCTL_GLOBAL_NOBASIC |
1008 RCTL_GLOBAL_BYTES, UINT64_MAX, UINT64_MAX, &project_shmmax_ops);
1009
1010 check = B_FALSE;
1011 if (!mod_sysvar("shmsys", "shminfo_shmmni", &shmmni))
1012 shmmni = 100;
1013 else
1014 check = B_TRUE;
1015 if (!mod_sysvar("shmsys", "shminfo_shmmax", &shmmax))
1016 shmmax = 0x800000;
1017 else
1018 check = B_TRUE;
1019
1020 /*
1021 * Default to a quarter of the machine's memory
1022 */
1023 qty = availrmem_initial << (PAGESHIFT - 2);
1024 if (check) {
1025 if ((shmmax > 0) && (UINT64_MAX / shmmax <= shmmni))
1026 qty = UINT64_MAX;
1027 else if (shmmni * shmmax > qty)
1028 qty = shmmni * shmmax;
1029 }
1030 rctl_add_default_limit("project.max-shm-memory", qty,
1031 RCPRIV_PRIVILEGED, RCTL_LOCAL_DENY);
1032
1033 /*
1034 * Event Ports resource controls
1035 */
1036
1037 rc_project_portids = rctl_register("project.max-port-ids",
1038 RCENTITY_PROJECT, RCTL_GLOBAL_DENY_ALWAYS | RCTL_GLOBAL_NOBASIC |
1039 RCTL_GLOBAL_COUNT, PORT_MAX_PORTS, PORT_MAX_PORTS,
1040 &rctl_absolute_ops);
1041 rctl_add_default_limit("project.max-port-ids", PORT_DEFAULT_PORTS,
1042 RCPRIV_PRIVILEGED, RCTL_LOCAL_DENY);
1043
1044 /*
1045 * Resource control for locked memory
1046 */
1047 rc_project_locked_mem = rctl_register(
1048 "project.max-locked-memory", RCENTITY_PROJECT,
1049 RCTL_GLOBAL_DENY_ALWAYS | RCTL_GLOBAL_NOBASIC | RCTL_GLOBAL_BYTES,
1050 UINT64_MAX, UINT64_MAX, &project_locked_mem_ops);
1051
1052 /*
1053 * Per project limit on contracts.
1054 */
1055 rc_project_contract = rctl_register("project.max-contracts",
1056 RCENTITY_PROJECT, RCTL_GLOBAL_DENY_ALWAYS | RCTL_GLOBAL_NOBASIC |
1057 RCTL_GLOBAL_COUNT, INT_MAX, INT_MAX, &project_contract_ops);
1058 rctl_add_default_limit("project.max-contracts", 10000,
1059 RCPRIV_PRIVILEGED, RCTL_LOCAL_DENY);
1060
1061 t0.t_proj = proj0p = project_hold_by_id(0, &zone0,
1062 PROJECT_HOLD_INSERT);
1063
1064 mutex_enter(&p0.p_lock);
1065 proj0p->kpj_nlwps = p0.p_lwpcnt;
1066 mutex_exit(&p0.p_lock);
1067 proj0p->kpj_nprocs = 1;
1068 proj0p->kpj_ntasks = 1;
1069 }
1070
1071 static int
project_lockedmem_kstat_update(kstat_t * ksp,int rw)1072 project_lockedmem_kstat_update(kstat_t *ksp, int rw)
1073 {
1074 kproject_t *pj = ksp->ks_private;
1075 kproject_kstat_t *kpk = ksp->ks_data;
1076
1077 if (rw == KSTAT_WRITE)
1078 return (EACCES);
1079
1080 kpk->kpk_usage.value.ui64 = pj->kpj_data.kpd_locked_mem;
1081 kpk->kpk_value.value.ui64 = pj->kpj_data.kpd_locked_mem_ctl;
1082 return (0);
1083 }
1084
1085 static int
project_nprocs_kstat_update(kstat_t * ksp,int rw)1086 project_nprocs_kstat_update(kstat_t *ksp, int rw)
1087 {
1088 kproject_t *pj = ksp->ks_private;
1089 kproject_kstat_t *kpk = ksp->ks_data;
1090
1091 if (rw == KSTAT_WRITE)
1092 return (EACCES);
1093
1094 kpk->kpk_usage.value.ui64 = pj->kpj_nprocs;
1095 kpk->kpk_value.value.ui64 = pj->kpj_nprocs_ctl;
1096 return (0);
1097 }
1098
1099 static kstat_t *
project_kstat_create_common(kproject_t * pj,char * name,char * zonename,int (* updatefunc)(kstat_t *,int))1100 project_kstat_create_common(kproject_t *pj, char *name, char *zonename,
1101 int (*updatefunc) (kstat_t *, int))
1102 {
1103 kstat_t *ksp;
1104 kproject_kstat_t *kpk;
1105
1106 ksp = rctl_kstat_create_project(pj, name, KSTAT_TYPE_NAMED,
1107 sizeof (kproject_kstat_t) / sizeof (kstat_named_t),
1108 KSTAT_FLAG_VIRTUAL);
1109
1110 if (ksp == NULL)
1111 return (NULL);
1112
1113 kpk = ksp->ks_data = kmem_alloc(sizeof (kproject_kstat_t), KM_SLEEP);
1114 ksp->ks_data_size += strlen(zonename) + 1;
1115 kstat_named_init(&kpk->kpk_zonename, "zonename", KSTAT_DATA_STRING);
1116 kstat_named_setstr(&kpk->kpk_zonename, zonename);
1117 kstat_named_init(&kpk->kpk_usage, "usage", KSTAT_DATA_UINT64);
1118 kstat_named_init(&kpk->kpk_value, "value", KSTAT_DATA_UINT64);
1119 ksp->ks_update = updatefunc;
1120 ksp->ks_private = pj;
1121 kstat_install(ksp);
1122 return (ksp);
1123 }
1124
1125 static void
project_kstat_create(kproject_t * pj,zone_t * zone)1126 project_kstat_create(kproject_t *pj, zone_t *zone)
1127 {
1128 kstat_t *ksp_lockedmem;
1129 kstat_t *ksp_nprocs;
1130
1131 ksp_lockedmem = project_kstat_create_common(pj, "lockedmem",
1132 zone->zone_name, project_lockedmem_kstat_update);
1133 ksp_nprocs = project_kstat_create_common(pj, "nprocs",
1134 zone->zone_name, project_nprocs_kstat_update);
1135
1136 mutex_enter(&project_hash_lock);
1137 ASSERT(pj->kpj_data.kpd_lockedmem_kstat == NULL);
1138 pj->kpj_data.kpd_lockedmem_kstat = ksp_lockedmem;
1139 ASSERT(pj->kpj_data.kpd_nprocs_kstat == NULL);
1140 pj->kpj_data.kpd_nprocs_kstat = ksp_nprocs;
1141 mutex_exit(&project_hash_lock);
1142 }
1143
1144 static void
project_kstat_delete_common(kstat_t ** kstat)1145 project_kstat_delete_common(kstat_t **kstat)
1146 {
1147 void *data;
1148
1149 if (*kstat != NULL) {
1150 data = (*kstat)->ks_data;
1151 kstat_delete(*kstat);
1152 kmem_free(data, sizeof (kproject_kstat_t));
1153 *kstat = NULL;
1154 }
1155 }
1156
1157 static void
project_kstat_delete(kproject_t * pj)1158 project_kstat_delete(kproject_t *pj)
1159 {
1160 project_kstat_delete_common(&pj->kpj_data.kpd_lockedmem_kstat);
1161 project_kstat_delete_common(&pj->kpj_data.kpd_nprocs_kstat);
1162 }
1163