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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 * 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 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 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 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