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 2007 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 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_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 kmem_free(kp, sizeof (kproject_t)); 160 } 161 162 /* 163 * kproject_t *project_hold(kproject_t *) 164 * 165 * Overview 166 * Record that an additional reference on the indicated project has been 167 * taken. 168 * 169 * Return values 170 * A pointer to the indicated project. 171 * 172 * Caller's context 173 * project_hash_lock must not be held across the project_hold() call. 174 */ 175 kproject_t * 176 project_hold(kproject_t *p) 177 { 178 mutex_enter(&project_hash_lock); 179 ASSERT(p != NULL); 180 p->kpj_count++; 181 ASSERT(p->kpj_count != 0); 182 mutex_exit(&project_hash_lock); 183 return (p); 184 } 185 186 /* 187 * kproject_t *project_hold_by_id(projid_t, zone_t *, int) 188 * 189 * Overview 190 * project_hold_by_id() performs a look-up in the dictionary of projects 191 * active on the system by specified project ID + zone and puts a hold on 192 * it. The third argument defines the desired behavior in the case when 193 * project with given project ID cannot be found: 194 * 195 * PROJECT_HOLD_INSERT New entry is made in dictionary and the project 196 * is added to the global list. 197 * 198 * PROJECT_HOLD_FIND Return NULL. 199 * 200 * The project is returned with its reference count incremented by one. 201 * A new project derives its resource controls from those of project 0. 202 * 203 * Return values 204 * A pointer to the held project. 205 * 206 * Caller's context 207 * Caller must be in a context suitable for KM_SLEEP allocations. 208 */ 209 kproject_t * 210 project_hold_by_id(projid_t id, zone_t *zone, int flag) 211 { 212 kproject_t *spare_p; 213 kproject_t *p; 214 mod_hash_hndl_t hndl; 215 rctl_set_t *set; 216 rctl_alloc_gp_t *gp; 217 rctl_entity_p_t e; 218 struct project_zone pz; 219 boolean_t create = B_FALSE; 220 kstat_t *ksp; 221 222 pz.kpj_id = id; 223 pz.kpj_zoneid = zone->zone_id; 224 225 if (flag == PROJECT_HOLD_FIND) { 226 mutex_enter(&project_hash_lock); 227 228 if (mod_hash_find(projects_hash, (mod_hash_key_t)&pz, 229 (mod_hash_val_t)&p) == MH_ERR_NOTFOUND) 230 p = NULL; 231 else 232 p->kpj_count++; 233 234 mutex_exit(&project_hash_lock); 235 return (p); 236 } 237 238 ASSERT(flag == PROJECT_HOLD_INSERT); 239 240 spare_p = kmem_zalloc(sizeof (kproject_t), KM_SLEEP); 241 set = rctl_set_create(); 242 243 gp = rctl_set_init_prealloc(RCENTITY_PROJECT); 244 245 (void) mod_hash_reserve(projects_hash, &hndl); 246 247 mutex_enter(&curproc->p_lock); 248 mutex_enter(&project_hash_lock); 249 if (mod_hash_find(projects_hash, (mod_hash_key_t)&pz, 250 (mod_hash_val_t *)&p) == MH_ERR_NOTFOUND) { 251 252 p = spare_p; 253 p->kpj_id = id; 254 p->kpj_zoneid = zone->zone_id; 255 p->kpj_count = 0; 256 p->kpj_shares = 1; 257 p->kpj_nlwps = 0; 258 p->kpj_ntasks = 0; 259 p->kpj_nlwps_ctl = INT_MAX; 260 p->kpj_ntasks_ctl = INT_MAX; 261 project_data_init(&p->kpj_data); 262 e.rcep_p.proj = p; 263 e.rcep_t = RCENTITY_PROJECT; 264 p->kpj_rctls = rctl_set_init(RCENTITY_PROJECT, curproc, &e, 265 set, gp); 266 mutex_exit(&curproc->p_lock); 267 268 if (mod_hash_insert_reserve(projects_hash, (mod_hash_key_t)p, 269 (mod_hash_val_t)p, hndl)) 270 panic("unable to insert project %d(%p)", id, (void *)p); 271 272 /* 273 * Insert project into global project list. 274 */ 275 mutex_enter(&projects_list_lock); 276 if (id != 0 || zone != &zone0) { 277 p->kpj_next = projects_list; 278 p->kpj_prev = projects_list->kpj_prev; 279 p->kpj_prev->kpj_next = p; 280 projects_list->kpj_prev = p; 281 } else { 282 /* 283 * Special case: primordial hold on project 0. 284 */ 285 p->kpj_next = p; 286 p->kpj_prev = p; 287 projects_list = p; 288 } 289 mutex_exit(&projects_list_lock); 290 create = B_TRUE; 291 } else { 292 mutex_exit(&curproc->p_lock); 293 mod_hash_cancel(projects_hash, &hndl); 294 kmem_free(spare_p, sizeof (kproject_t)); 295 rctl_set_free(set); 296 } 297 298 rctl_prealloc_destroy(gp); 299 p->kpj_count++; 300 mutex_exit(&project_hash_lock); 301 302 /* 303 * The kstat stores the project's zone name, as zoneid's may change 304 * across reboots. 305 */ 306 if (create == B_TRUE) { 307 ksp = project_kstat_create(p, zone); 308 mutex_enter(&project_hash_lock); 309 ASSERT(p->kpj_data.kpd_lockedmem_kstat == NULL); 310 p->kpj_data.kpd_lockedmem_kstat = ksp; 311 mutex_exit(&project_hash_lock); 312 } 313 return (p); 314 } 315 316 /* 317 * void project_rele(kproject_t *) 318 * 319 * Overview 320 * Advertise that one external reference to this project is no longer needed. 321 * 322 * Return values 323 * None. 324 * 325 * Caller's context 326 * No restriction on context. 327 */ 328 void 329 project_rele(kproject_t *p) 330 { 331 mutex_enter(&project_hash_lock); 332 ASSERT(p->kpj_count != 0); 333 p->kpj_count--; 334 if (p->kpj_count == 0) { 335 336 /* 337 * Remove project from global list. 338 */ 339 mutex_enter(&projects_list_lock); 340 p->kpj_next->kpj_prev = p->kpj_prev; 341 p->kpj_prev->kpj_next = p->kpj_next; 342 if (projects_list == p) 343 projects_list = p->kpj_next; 344 mutex_exit(&projects_list_lock); 345 346 rctl_set_free(p->kpj_rctls); 347 project_kstat_delete(p); 348 349 if (mod_hash_destroy(projects_hash, (mod_hash_key_t)p)) 350 panic("unable to delete project %d zone %d", p->kpj_id, 351 p->kpj_zoneid); 352 353 } 354 mutex_exit(&project_hash_lock); 355 } 356 357 /* 358 * int project_walk_all(zoneid_t, int (*)(kproject_t *, void *), void *) 359 * 360 * Overview 361 * Walk the project list for the given zoneid with a callback. 362 * 363 * Return values 364 * -1 for an invalid walk, number of projects visited otherwise. 365 * 366 * Caller's context 367 * projects_list_lock must not be held, as it is acquired by 368 * project_walk_all(). Accordingly, callbacks may not perform KM_SLEEP 369 * allocations. 370 */ 371 int 372 project_walk_all(zoneid_t zoneid, int (*cb)(kproject_t *, void *), 373 void *walk_data) 374 { 375 int cnt = 0; 376 kproject_t *kp = proj0p; 377 378 mutex_enter(&projects_list_lock); 379 do { 380 if (zoneid != ALL_ZONES && kp->kpj_zoneid != zoneid) 381 continue; 382 if (cb(kp, walk_data) == -1) { 383 cnt = -1; 384 break; 385 } else { 386 cnt++; 387 } 388 } while ((kp = kp->kpj_next) != proj0p); 389 mutex_exit(&projects_list_lock); 390 return (cnt); 391 } 392 393 /* 394 * projid_t curprojid(void) 395 * 396 * Overview 397 * Return project ID of the current thread 398 * 399 * Caller's context 400 * No restrictions. 401 */ 402 projid_t 403 curprojid() 404 { 405 return (ttoproj(curthread)->kpj_id); 406 } 407 408 /* 409 * project.cpu-shares resource control support. 410 */ 411 /*ARGSUSED*/ 412 static rctl_qty_t 413 project_cpu_shares_usage(rctl_t *rctl, struct proc *p) 414 { 415 ASSERT(MUTEX_HELD(&p->p_lock)); 416 return (p->p_task->tk_proj->kpj_shares); 417 } 418 419 /*ARGSUSED*/ 420 static int 421 project_cpu_shares_set(rctl_t *rctl, struct proc *p, rctl_entity_p_t *e, 422 rctl_qty_t nv) 423 { 424 ASSERT(MUTEX_HELD(&p->p_lock)); 425 ASSERT(e->rcep_t == RCENTITY_PROJECT); 426 if (e->rcep_p.proj == NULL) 427 return (0); 428 429 e->rcep_p.proj->kpj_shares = nv; 430 431 return (0); 432 } 433 434 435 static rctl_ops_t project_cpu_shares_ops = { 436 rcop_no_action, 437 project_cpu_shares_usage, 438 project_cpu_shares_set, 439 rcop_no_test 440 }; 441 442 /*ARGSUSED*/ 443 static rctl_qty_t 444 project_lwps_usage(rctl_t *r, proc_t *p) 445 { 446 kproject_t *pj; 447 rctl_qty_t nlwps; 448 449 ASSERT(MUTEX_HELD(&p->p_lock)); 450 pj = p->p_task->tk_proj; 451 mutex_enter(&p->p_zone->zone_nlwps_lock); 452 nlwps = pj->kpj_nlwps; 453 mutex_exit(&p->p_zone->zone_nlwps_lock); 454 455 return (nlwps); 456 } 457 458 /*ARGSUSED*/ 459 static int 460 project_lwps_test(rctl_t *r, proc_t *p, rctl_entity_p_t *e, rctl_val_t *rcntl, 461 rctl_qty_t incr, uint_t flags) 462 { 463 rctl_qty_t nlwps; 464 465 ASSERT(MUTEX_HELD(&p->p_lock)); 466 ASSERT(MUTEX_HELD(&p->p_zone->zone_nlwps_lock)); 467 ASSERT(e->rcep_t == RCENTITY_PROJECT); 468 if (e->rcep_p.proj == NULL) 469 return (0); 470 471 nlwps = e->rcep_p.proj->kpj_nlwps; 472 if (nlwps + incr > rcntl->rcv_value) 473 return (1); 474 475 return (0); 476 } 477 478 /*ARGSUSED*/ 479 static int 480 project_lwps_set(rctl_t *rctl, struct proc *p, rctl_entity_p_t *e, 481 rctl_qty_t nv) { 482 483 ASSERT(MUTEX_HELD(&p->p_lock)); 484 ASSERT(e->rcep_t == RCENTITY_PROJECT); 485 if (e->rcep_p.proj == NULL) 486 return (0); 487 488 e->rcep_p.proj->kpj_nlwps_ctl = nv; 489 return (0); 490 } 491 492 static rctl_ops_t project_lwps_ops = { 493 rcop_no_action, 494 project_lwps_usage, 495 project_lwps_set, 496 project_lwps_test, 497 }; 498 499 /*ARGSUSED*/ 500 static rctl_qty_t 501 project_ntasks_usage(rctl_t *r, proc_t *p) 502 { 503 kproject_t *pj; 504 rctl_qty_t ntasks; 505 506 ASSERT(MUTEX_HELD(&p->p_lock)); 507 pj = p->p_task->tk_proj; 508 mutex_enter(&p->p_zone->zone_nlwps_lock); 509 ntasks = pj->kpj_ntasks; 510 mutex_exit(&p->p_zone->zone_nlwps_lock); 511 512 return (ntasks); 513 } 514 515 /*ARGSUSED*/ 516 static int 517 project_ntasks_test(rctl_t *r, proc_t *p, rctl_entity_p_t *e, rctl_val_t *rcntl, 518 rctl_qty_t incr, uint_t flags) 519 { 520 rctl_qty_t ntasks; 521 522 ASSERT(MUTEX_HELD(&p->p_lock)); 523 ASSERT(e->rcep_t == RCENTITY_PROJECT); 524 ntasks = e->rcep_p.proj->kpj_ntasks; 525 if (ntasks + incr > rcntl->rcv_value) 526 return (1); 527 528 return (0); 529 } 530 531 /*ARGSUSED*/ 532 static int 533 project_ntasks_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 e->rcep_p.proj->kpj_ntasks_ctl = nv; 539 return (0); 540 } 541 542 static rctl_ops_t project_tasks_ops = { 543 rcop_no_action, 544 project_ntasks_usage, 545 project_ntasks_set, 546 project_ntasks_test, 547 }; 548 549 /* 550 * project.max-shm-memory resource control support. 551 */ 552 553 /*ARGSUSED*/ 554 static int 555 project_shmmax_test(struct rctl *rctl, struct proc *p, rctl_entity_p_t *e, 556 rctl_val_t *rval, rctl_qty_t inc, uint_t flags) 557 { 558 rctl_qty_t v; 559 ASSERT(MUTEX_HELD(&p->p_lock)); 560 ASSERT(e->rcep_t == RCENTITY_PROJECT); 561 v = e->rcep_p.proj->kpj_data.kpd_shmmax + inc; 562 if (v > rval->rcv_value) 563 return (1); 564 565 return (0); 566 } 567 568 static rctl_ops_t project_shmmax_ops = { 569 rcop_no_action, 570 rcop_no_usage, 571 rcop_no_set, 572 project_shmmax_test 573 }; 574 575 /* 576 * project.max-shm-ids resource control support. 577 */ 578 579 /*ARGSUSED*/ 580 static int 581 project_shmmni_test(struct rctl *rctl, struct proc *p, rctl_entity_p_t *e, 582 rctl_val_t *rval, rctl_qty_t inc, uint_t flags) 583 { 584 rctl_qty_t v; 585 ASSERT(MUTEX_HELD(&p->p_lock)); 586 ASSERT(e->rcep_t == RCENTITY_PROJECT); 587 v = e->rcep_p.proj->kpj_data.kpd_ipc.ipcq_shmmni + inc; 588 if (v > rval->rcv_value) 589 return (1); 590 591 return (0); 592 } 593 594 static rctl_ops_t project_shmmni_ops = { 595 rcop_no_action, 596 rcop_no_usage, 597 rcop_no_set, 598 project_shmmni_test 599 }; 600 601 /* 602 * project.max-sem-ids resource control support. 603 */ 604 605 /*ARGSUSED*/ 606 static int 607 project_semmni_test(struct rctl *rctl, struct proc *p, rctl_entity_p_t *e, 608 rctl_val_t *rval, rctl_qty_t inc, uint_t flags) 609 { 610 rctl_qty_t v; 611 ASSERT(MUTEX_HELD(&p->p_lock)); 612 ASSERT(e->rcep_t == RCENTITY_PROJECT); 613 v = e->rcep_p.proj->kpj_data.kpd_ipc.ipcq_semmni + inc; 614 if (v > rval->rcv_value) 615 return (1); 616 617 return (0); 618 } 619 620 static rctl_ops_t project_semmni_ops = { 621 rcop_no_action, 622 rcop_no_usage, 623 rcop_no_set, 624 project_semmni_test 625 }; 626 627 /* 628 * project.max-msg-ids resource control support. 629 */ 630 631 /*ARGSUSED*/ 632 static int 633 project_msgmni_test(struct rctl *rctl, struct proc *p, rctl_entity_p_t *e, 634 rctl_val_t *rval, rctl_qty_t inc, uint_t flags) 635 { 636 rctl_qty_t v; 637 ASSERT(MUTEX_HELD(&p->p_lock)); 638 ASSERT(e->rcep_t == RCENTITY_PROJECT); 639 v = e->rcep_p.proj->kpj_data.kpd_ipc.ipcq_msgmni + inc; 640 if (v > rval->rcv_value) 641 return (1); 642 643 return (0); 644 } 645 646 static rctl_ops_t project_msgmni_ops = { 647 rcop_no_action, 648 rcop_no_usage, 649 rcop_no_set, 650 project_msgmni_test 651 }; 652 653 /*ARGSUSED*/ 654 static rctl_qty_t 655 project_locked_mem_usage(rctl_t *rctl, struct proc *p) 656 { 657 rctl_qty_t q; 658 ASSERT(MUTEX_HELD(&p->p_lock)); 659 mutex_enter(&p->p_zone->zone_mem_lock); 660 q = p->p_task->tk_proj->kpj_data.kpd_locked_mem; 661 mutex_exit(&p->p_zone->zone_mem_lock); 662 return (q); 663 } 664 665 /*ARGSUSED*/ 666 static int 667 project_locked_mem_test(struct rctl *rctl, struct proc *p, rctl_entity_p_t *e, 668 rctl_val_t *rval, rctl_qty_t inc, uint_t flags) 669 { 670 rctl_qty_t q; 671 ASSERT(MUTEX_HELD(&p->p_lock)); 672 ASSERT(MUTEX_HELD(&p->p_zone->zone_mem_lock)); 673 q = p->p_task->tk_proj->kpj_data.kpd_locked_mem; 674 if (q + inc > rval->rcv_value) 675 return (1); 676 return (0); 677 } 678 679 /*ARGSUSED*/ 680 static int 681 project_locked_mem_set(rctl_t *rctl, struct proc *p, rctl_entity_p_t *e, 682 rctl_qty_t nv) { 683 684 ASSERT(MUTEX_HELD(&p->p_lock)); 685 ASSERT(e->rcep_t == RCENTITY_PROJECT); 686 if (e->rcep_p.proj == NULL) 687 return (0); 688 689 e->rcep_p.proj->kpj_data.kpd_locked_mem_ctl = nv; 690 return (0); 691 } 692 693 static rctl_ops_t project_locked_mem_ops = { 694 rcop_no_action, 695 project_locked_mem_usage, 696 project_locked_mem_set, 697 project_locked_mem_test 698 }; 699 700 /* 701 * project.max-contracts resource control support. 702 */ 703 704 /*ARGSUSED*/ 705 static int 706 project_contract_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 711 ASSERT(MUTEX_HELD(&p->p_lock)); 712 ASSERT(e->rcep_t == RCENTITY_PROJECT); 713 714 v = e->rcep_p.proj->kpj_data.kpd_contract + inc; 715 716 if ((p->p_task != NULL) && (p->p_task->tk_proj) != NULL && 717 (v > rval->rcv_value)) 718 return (1); 719 720 return (0); 721 } 722 723 static rctl_ops_t project_contract_ops = { 724 rcop_no_action, 725 rcop_no_usage, 726 rcop_no_set, 727 project_contract_test 728 }; 729 730 /*ARGSUSED*/ 731 static rctl_qty_t 732 project_crypto_usage(rctl_t *r, proc_t *p) 733 { 734 ASSERT(MUTEX_HELD(&p->p_lock)); 735 return (p->p_task->tk_proj->kpj_data.kpd_crypto_mem); 736 } 737 738 /*ARGSUSED*/ 739 static int 740 project_crypto_set(rctl_t *r, proc_t *p, rctl_entity_p_t *e, 741 rctl_qty_t nv) 742 { 743 ASSERT(MUTEX_HELD(&p->p_lock)); 744 ASSERT(e->rcep_t == RCENTITY_PROJECT); 745 if (e->rcep_p.proj == NULL) 746 return (0); 747 748 e->rcep_p.proj->kpj_data.kpd_crypto_mem_ctl = nv; 749 return (0); 750 } 751 752 /*ARGSUSED*/ 753 static int 754 project_crypto_test(rctl_t *r, proc_t *p, rctl_entity_p_t *e, 755 rctl_val_t *rval, rctl_qty_t incr, uint_t flags) 756 { 757 rctl_qty_t v; 758 ASSERT(MUTEX_HELD(&p->p_lock)); 759 ASSERT(e->rcep_t == RCENTITY_PROJECT); 760 v = e->rcep_p.proj->kpj_data.kpd_crypto_mem + incr; 761 if (v > rval->rcv_value) 762 return (1); 763 return (0); 764 } 765 766 static rctl_ops_t project_crypto_mem_ops = { 767 rcop_no_action, 768 project_crypto_usage, 769 project_crypto_set, 770 project_crypto_test 771 }; 772 773 /* 774 * void project_init(void) 775 * 776 * Overview 777 * Initialize the project subsystem, including the primordial project 0 entry. 778 * Register generic project resource controls, if any. 779 * 780 * Return values 781 * None. 782 * 783 * Caller's context 784 * Safe for KM_SLEEP allocations. 785 */ 786 void 787 project_init(void) 788 { 789 rctl_qty_t shmmni, shmmax, qty; 790 boolean_t check; 791 792 projects_hash = mod_hash_create_extended("projects_hash", 793 project_hash_size, mod_hash_null_keydtor, project_hash_val_dtor, 794 project_hash_by_id, 795 (void *)(uintptr_t)mod_hash_iddata_gen(project_hash_size), 796 project_hash_key_cmp, KM_SLEEP); 797 798 rc_project_cpu_shares = rctl_register("project.cpu-shares", 799 RCENTITY_PROJECT, RCTL_GLOBAL_SIGNAL_NEVER | 800 RCTL_GLOBAL_DENY_NEVER | RCTL_GLOBAL_NOBASIC | 801 RCTL_GLOBAL_COUNT | RCTL_GLOBAL_SYSLOG_NEVER, 802 FSS_MAXSHARES, FSS_MAXSHARES, 803 &project_cpu_shares_ops); 804 rctl_add_default_limit("project.cpu-shares", 1, RCPRIV_PRIVILEGED, 805 RCTL_LOCAL_NOACTION); 806 807 rc_project_nlwps = rctl_register("project.max-lwps", RCENTITY_PROJECT, 808 RCTL_GLOBAL_NOACTION | RCTL_GLOBAL_NOBASIC | RCTL_GLOBAL_COUNT, 809 INT_MAX, INT_MAX, &project_lwps_ops); 810 811 rc_project_ntasks = rctl_register("project.max-tasks", RCENTITY_PROJECT, 812 RCTL_GLOBAL_NOACTION | RCTL_GLOBAL_NOBASIC | RCTL_GLOBAL_COUNT, 813 INT_MAX, INT_MAX, &project_tasks_ops); 814 815 /* 816 * This rctl handle is used by /dev/crypto. It is here rather than 817 * in misc/kcf or the drv/crypto module because resource controls 818 * currently don't allow modules to be unloaded, and the control 819 * must be registered before init starts. 820 */ 821 rc_project_crypto_mem = rctl_register("project.max-crypto-memory", 822 RCENTITY_PROJECT, RCTL_GLOBAL_DENY_ALWAYS | RCTL_GLOBAL_NOBASIC | 823 RCTL_GLOBAL_BYTES, UINT64_MAX, UINT64_MAX, 824 &project_crypto_mem_ops); 825 826 /* 827 * Default to a quarter of the machine's memory 828 */ 829 qty = availrmem_initial << (PAGESHIFT - 2); 830 rctl_add_default_limit("project.max-crypto-memory", qty, 831 RCPRIV_PRIVILEGED, RCTL_LOCAL_DENY); 832 833 /* 834 * System V IPC resource controls 835 */ 836 rc_project_semmni = rctl_register("project.max-sem-ids", 837 RCENTITY_PROJECT, RCTL_GLOBAL_DENY_ALWAYS | RCTL_GLOBAL_NOBASIC | 838 RCTL_GLOBAL_COUNT, IPC_IDS_MAX, IPC_IDS_MAX, &project_semmni_ops); 839 rctl_add_legacy_limit("project.max-sem-ids", "semsys", 840 "seminfo_semmni", 128, IPC_IDS_MAX); 841 842 rc_project_msgmni = rctl_register("project.max-msg-ids", 843 RCENTITY_PROJECT, RCTL_GLOBAL_DENY_ALWAYS | RCTL_GLOBAL_NOBASIC | 844 RCTL_GLOBAL_COUNT, IPC_IDS_MAX, IPC_IDS_MAX, &project_msgmni_ops); 845 rctl_add_legacy_limit("project.max-msg-ids", "msgsys", 846 "msginfo_msgmni", 128, IPC_IDS_MAX); 847 848 rc_project_shmmni = rctl_register("project.max-shm-ids", 849 RCENTITY_PROJECT, RCTL_GLOBAL_DENY_ALWAYS | RCTL_GLOBAL_NOBASIC | 850 RCTL_GLOBAL_COUNT, IPC_IDS_MAX, IPC_IDS_MAX, &project_shmmni_ops); 851 rctl_add_legacy_limit("project.max-shm-ids", "shmsys", 852 "shminfo_shmmni", 128, IPC_IDS_MAX); 853 854 rc_project_shmmax = rctl_register("project.max-shm-memory", 855 RCENTITY_PROJECT, RCTL_GLOBAL_DENY_ALWAYS | RCTL_GLOBAL_NOBASIC | 856 RCTL_GLOBAL_BYTES, UINT64_MAX, UINT64_MAX, &project_shmmax_ops); 857 858 check = B_FALSE; 859 if (!mod_sysvar("shmsys", "shminfo_shmmni", &shmmni)) 860 shmmni = 100; 861 else 862 check = B_TRUE; 863 if (!mod_sysvar("shmsys", "shminfo_shmmax", &shmmax)) 864 shmmax = 0x800000; 865 else 866 check = B_TRUE; 867 868 /* 869 * Default to a quarter of the machine's memory 870 */ 871 qty = availrmem_initial << (PAGESHIFT - 2); 872 if (check) { 873 if ((shmmax > 0) && (UINT64_MAX / shmmax <= shmmni)) 874 qty = UINT64_MAX; 875 else if (shmmni * shmmax > qty) 876 qty = shmmni * shmmax; 877 } 878 rctl_add_default_limit("project.max-shm-memory", qty, 879 RCPRIV_PRIVILEGED, RCTL_LOCAL_DENY); 880 881 /* 882 * Event Ports resource controls 883 */ 884 885 rc_project_portids = rctl_register("project.max-port-ids", 886 RCENTITY_PROJECT, RCTL_GLOBAL_DENY_ALWAYS | RCTL_GLOBAL_NOBASIC | 887 RCTL_GLOBAL_COUNT, PORT_MAX_PORTS, PORT_MAX_PORTS, 888 &rctl_absolute_ops); 889 rctl_add_default_limit("project.max-port-ids", PORT_DEFAULT_PORTS, 890 RCPRIV_PRIVILEGED, RCTL_LOCAL_DENY); 891 892 /* 893 * Resource control for locked memory 894 */ 895 rc_project_locked_mem = rctl_register( 896 "project.max-locked-memory", RCENTITY_PROJECT, 897 RCTL_GLOBAL_DENY_ALWAYS | RCTL_GLOBAL_NOBASIC | RCTL_GLOBAL_BYTES, 898 UINT64_MAX, UINT64_MAX, &project_locked_mem_ops); 899 900 /* Default value equals that of max-shm-memory. */ 901 rctl_add_default_limit("project.max-locked-memory", qty, 902 RCPRIV_PRIVILEGED, RCTL_LOCAL_DENY); 903 904 /* 905 * Per project limit on contracts. 906 */ 907 rc_project_contract = rctl_register("project.max-contracts", 908 RCENTITY_PROJECT, RCTL_GLOBAL_DENY_ALWAYS | RCTL_GLOBAL_COUNT, 909 INT_MAX, INT_MAX, &project_contract_ops); 910 rctl_add_default_limit("project.max-contracts", 10000, 911 RCPRIV_PRIVILEGED, RCTL_LOCAL_DENY); 912 913 t0.t_proj = proj0p = project_hold_by_id(0, &zone0, 914 PROJECT_HOLD_INSERT); 915 916 mutex_enter(&p0.p_lock); 917 proj0p->kpj_nlwps = p0.p_lwpcnt; 918 mutex_exit(&p0.p_lock); 919 proj0p->kpj_ntasks = 1; 920 } 921 922 static int 923 project_lockedmem_kstat_update(kstat_t *ksp, int rw) 924 { 925 kproject_t *pj = ksp->ks_private; 926 kproject_kstat_t *kpk = ksp->ks_data; 927 928 if (rw == KSTAT_WRITE) 929 return (EACCES); 930 931 kpk->kpk_usage.value.ui64 = pj->kpj_data.kpd_locked_mem; 932 kpk->kpk_value.value.ui64 = pj->kpj_data.kpd_locked_mem_ctl; 933 return (0); 934 } 935 936 static kstat_t * 937 project_kstat_create(kproject_t *pj, zone_t *zone) 938 { 939 kstat_t *ksp; 940 kproject_kstat_t *kpk; 941 char *zonename = zone->zone_name; 942 943 ksp = rctl_kstat_create_project(pj, "lockedmem", KSTAT_TYPE_NAMED, 944 sizeof (kproject_kstat_t) / sizeof (kstat_named_t), 945 KSTAT_FLAG_VIRTUAL); 946 947 if (ksp == NULL) 948 return (NULL); 949 950 kpk = ksp->ks_data = kmem_alloc(sizeof (kproject_kstat_t), KM_SLEEP); 951 ksp->ks_data_size += strlen(zonename) + 1; 952 kstat_named_init(&kpk->kpk_zonename, "zonename", KSTAT_DATA_STRING); 953 kstat_named_setstr(&kpk->kpk_zonename, zonename); 954 kstat_named_init(&kpk->kpk_usage, "usage", KSTAT_DATA_UINT64); 955 kstat_named_init(&kpk->kpk_value, "value", KSTAT_DATA_UINT64); 956 ksp->ks_update = project_lockedmem_kstat_update; 957 ksp->ks_private = pj; 958 kstat_install(ksp); 959 960 return (ksp); 961 } 962 963 static void 964 project_kstat_delete(kproject_t *pj) 965 { 966 void *data; 967 968 if (pj->kpj_data.kpd_lockedmem_kstat != NULL) { 969 data = pj->kpj_data.kpd_lockedmem_kstat->ks_data; 970 kstat_delete(pj->kpj_data.kpd_lockedmem_kstat); 971 kmem_free(data, sizeof (zone_kstat_t)); 972 } 973 pj->kpj_data.kpd_lockedmem_kstat = NULL; 974 } 975