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