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