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