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 * Copyright 2021 Oxide Computer Company 25 */ 26 27 /* 28 * PSEUDO-TERMINAL COMMON DATA AND ROUTINES (PTM, PTS) 29 * 30 * This file contains global data and code shared between manager and 31 * subsidiary parts of the pseudo-terminal driver. 32 * 33 * Pseudo-terminals (or ptys for short) are allocated dynamically. 34 * ptys are put in the global ptms_slots array indexed by minor numbers. 35 * 36 * The slots array is initially small (of the size NPTY_MIN). When more ptys are 37 * needed than the slot array size, the larger slot array is allocated and all 38 * opened ptys move to the new one. 39 * 40 * 41 * RESOURCE ALLOCATION 42 * 43 * - pt_ttys structures are allocated via pt_ttys_alloc, which uses 44 * kmem_cache_alloc(). 45 * - Minor number space is allocated via vmem_alloc() interface. 46 * - ptms_slots arrays are allocated via kmem_alloc(). 47 * 48 * Minors start from 1 instead of 0, because vmem_alloc() returns 0 in case of 49 * failure. Also, in anticipation of removing the clone device interface to 50 * pseudo-terminal subsystem, minor 0 should not be used. (Potential future 51 * development). 52 * 53 * After the table slot size reaches pt_maxdelta, we stop 2^N extension 54 * algorithm and start extending the slot table size by pt_maxdelta. 55 * 56 * Device entries /dev/pts directory are created dynamically by the /dev 57 * filesystem. We no longer call ddi_create_minor_node() on behalf of the 58 * subsidiary driver. The /dev filesystem creates /dev/pts nodes based on the 59 * pt_ttys array. 60 * 61 * 62 * SYNCHRONIZATION 63 * 64 * All global data synchronization between ptm/pts is done via global ptms_lock 65 * mutex which is implicitly initialized by declaring it global. 66 * 67 * Individual fields of pt_ttys structure (except ptm_rdq, pts_rdq and 68 * pt_nullmsg) are protected by pt_ttys.pt_lock mutex. 69 * 70 * PT_ENTER_READ/PT_ENTER_WRITE are reference counter based read-write locks 71 * which allow reader locks to be reacquired by the same thread (usual 72 * reader/writer locks can't be used for that purpose since it is illegal for a 73 * thread to acquire a lock it already holds, even as a reader). The sole 74 * purpose of these macros is to guarantee that the peer queue will not 75 * disappear (due to closing peer) while it is used. It is safe to use 76 * PT_ENTER_READ/PT_EXIT_READ brackets across calls like putq/putnext (since 77 * they are not real locks but reference counts). 78 * 79 * PT_ENTER_WRITE/PT_EXIT_WRITE brackets are used ONLY in manager/subsidiary 80 * open/close paths to modify ptm_rdq and pts_rdq fields. These fields should 81 * be set to appropriate queues *after* qprocson() is called during open (to 82 * prevent peer from accessing the queue with incomplete plumbing) and set to 83 * NULL before qprocsoff() is called during close. Put and service procedures 84 * use PT_ENTER_READ/PT_EXIT_READ to prevent peer closes. 85 * 86 * The pt_nullmsg field is only used in open/close routines and is also 87 * protected by PT_ENTER_WRITE/PT_EXIT_WRITE brackets to avoid extra mutex 88 * holds. 89 * 90 * 91 * LOCK ORDERING 92 * 93 * If both ptms_lock and per-pty lock should be held, ptms_lock should always 94 * be entered first, followed by per-pty lock. 95 * 96 * 97 * GLOBAL FUNCTIONS 98 * 99 * void ptms_init(void); 100 * 101 * Called by pts/ptm _init entry points. It performes one-time 102 * initialization needed for both pts and ptm. This initialization is done 103 * here and not in ptms_initspace because all these data structures are not 104 * needed if pseudo-terminals are not used in the system. 105 * 106 * struct pt_ttys *pt_ttys_alloc(void); 107 * 108 * Allocate new minor number and pseudo-terminal entry. May sleep. 109 * New minor number is recorded in pt_minor field of the entry returned. 110 * This routine also initializes pt_minor and pt_state fields of the new 111 * pseudo-terminal and puts a pointer to it into ptms_slots array. 112 * 113 * struct pt_ttys *ptms_minor2ptty(minor_t minor) 114 * 115 * Find pt_ttys structure by minor number. 116 * Returns NULL when minor is out of range. 117 * 118 * int ptms_minor_valid(minor_t minor, uid_t *ruid, gid_t *rgid) 119 * 120 * Check if minor refers to an allocated pty in the current zone. 121 * Returns 122 * 0 if not allocated or not for this zone. 123 * 1 if an allocated pty in the current zone. 124 * Also returns owner of pty. 125 * 126 * int ptms_minor_exists(minor_t minor) 127 * 128 * Check if minor refers to an allocated pty (in any zone) 129 * Returns 130 * 0 if not an allocated pty 131 * 1 if an allocated pty 132 * 133 * void ptms_set_owner(minor_t minor, uid_t ruid, gid_t rgid) 134 * 135 * Sets the owner associated with a pty. 136 * 137 * void ptms_close(struct pt_ttys *pt, uint_t flags_to_clear); 138 * 139 * Clear flags_to_clear in pt and if no one owns it (PTMOPEN/PTSOPEN not 140 * set) free pt entry and corresponding slot. 141 * 142 * 143 * TUNEABLES AND CONFIGURATION 144 * 145 * pt_cnt: minimum number of pseudo-terminals in the system. The system 146 * should provide at least this number of ptys (provided sufficient 147 * memory is available). It is different from the older semantics 148 * of pt_cnt meaning maximum number of ptys. 149 * Set to 0 by default. 150 * 151 * pt_max_pty: Maximum number of pseudo-terminals in the system. The system 152 * should not allocate more ptys than pt_max_pty (although, it may 153 * impose stricter maximum). Zero value means no user-defined 154 * maximum. This is intended to be used as "denial-of-service" 155 * protection. 156 * Set to 0 by default. 157 * 158 * Both pt_cnt and pt_max_pty may be modified during system 159 * lifetime with their semantics preserved. 160 * 161 * pt_init_cnt: Initial size of ptms_slots array. Set to NPTY_INITIAL. 162 * 163 * pt_ptyofmem: Approximate percentage of system memory that may be 164 * occupied by pty data structures. Initially set to NPTY_PERCENT. 165 * This variable is used once during initialization to estimate 166 * maximum number of ptys in the system. The actual maximum is 167 * determined as minimum of pt_max_pty and calculated value. 168 * 169 * pt_maxdelta: Maximum extension chunk of the slot table. 170 */ 171 172 173 174 #include <sys/types.h> 175 #include <sys/param.h> 176 #include <sys/termios.h> 177 #include <sys/stream.h> 178 #include <sys/stropts.h> 179 #include <sys/kmem.h> 180 #include <sys/ptms.h> 181 #include <sys/stat.h> 182 #include <sys/sunddi.h> 183 #include <sys/ddi.h> 184 #include <sys/bitmap.h> 185 #include <sys/sysmacros.h> 186 #include <sys/ddi_impldefs.h> 187 #include <sys/zone.h> 188 #ifdef DEBUG 189 #include <sys/strlog.h> 190 #endif 191 192 193 /* Initial number of ptms slots */ 194 #define NPTY_INITIAL 16 195 196 #define NPTY_PERCENT 5 197 198 /* Maximum increment of the slot table size */ 199 #define PTY_MAXDELTA 128 200 201 /* 202 * Tuneable variables. 203 */ 204 uint_t pt_cnt = 0; /* Minimum number of ptys */ 205 size_t pt_max_pty = 0; /* Maximum number of ptys */ 206 uint_t pt_init_cnt = NPTY_INITIAL; /* Initial number of ptms slots */ 207 uint_t pt_pctofmem = NPTY_PERCENT; /* Percent of memory to use for ptys */ 208 uint_t pt_maxdelta = PTY_MAXDELTA; /* Max increment for slot table size */ 209 210 /* Other global variables */ 211 212 kmutex_t ptms_lock; /* Global data access lock */ 213 214 /* 215 * Slot array and its management variables 216 */ 217 static struct pt_ttys **ptms_slots = NULL; /* Slots for actual pt structures */ 218 static size_t ptms_nslots = 0; /* Size of slot array */ 219 static size_t ptms_ptymax = 0; /* Maximum number of ptys */ 220 static size_t ptms_inuse = 0; /* # of ptys currently allocated */ 221 222 dev_info_t *pts_dip = NULL; /* Set if subsidiary is attached */ 223 224 static struct kmem_cache *ptms_cache = NULL; /* pty cache */ 225 226 static vmem_t *ptms_minor_arena = NULL; /* Arena for device minors */ 227 228 static uint_t ptms_roundup(uint_t); 229 static int ptms_constructor(void *, void *, int); 230 static void ptms_destructor(void *, void *); 231 static minor_t ptms_grow(void); 232 233 /* 234 * Total size occupied by one pty. Each pty manager/subsidiary pair consumes 235 * one pointer for ptms_slots array, one pt_ttys structure, and one empty 236 * message preallocated for pts close. 237 */ 238 239 #define PTY_SIZE (sizeof (struct pt_ttys) + \ 240 sizeof (struct pt_ttys *) + \ 241 sizeof (dblk_t)) 242 243 #ifdef DEBUG 244 int ptms_debug = 0; 245 #define PTMOD_ID 5 246 #endif 247 248 /* 249 * Clear all bits of x except the highest bit 250 */ 251 #define truncate(x) ((x) <= 2 ? (x) : (1 << (highbit(x) - 1))) 252 253 /* 254 * Roundup the number to the nearest power of 2 255 */ 256 static uint_t 257 ptms_roundup(uint_t x) 258 { 259 uint_t p = truncate(x); /* x with non-high bits stripped */ 260 261 /* 262 * If x is a power of 2, return x, otherwise roundup. 263 */ 264 return (p == x ? p : (p * 2)); 265 } 266 267 /* 268 * Allocate ptms_slots array and kmem cache for pt_ttys. This initialization is 269 * only called once during system lifetime. Called from ptm or pts _init 270 * routine. 271 */ 272 void 273 ptms_init(void) 274 { 275 mutex_enter(&ptms_lock); 276 277 if (ptms_slots == NULL) { 278 ptms_slots = kmem_zalloc(pt_init_cnt * 279 sizeof (struct pt_ttys *), KM_SLEEP); 280 281 ptms_cache = kmem_cache_create("pty_map", 282 sizeof (struct pt_ttys), 0, ptms_constructor, 283 ptms_destructor, NULL, NULL, NULL, 0); 284 285 ptms_nslots = pt_init_cnt; 286 287 /* Allocate integer space for minor numbers */ 288 ptms_minor_arena = vmem_create("ptms_minor", (void *)1, 289 ptms_nslots, 1, NULL, NULL, NULL, 0, 290 VM_SLEEP | VMC_IDENTIFIER); 291 292 /* 293 * Calculate available number of ptys - how many ptys can we 294 * allocate in pt_pctofmem % of available memory. The value is 295 * rounded up to the nearest power of 2. 296 */ 297 ptms_ptymax = ptms_roundup((pt_pctofmem * kmem_maxavail()) / 298 (100 * PTY_SIZE)); 299 } 300 mutex_exit(&ptms_lock); 301 } 302 303 /* 304 * This routine attaches the pts dip. 305 */ 306 int 307 ptms_attach_subsidiary(void) 308 { 309 if (pts_dip == NULL && i_ddi_attach_pseudo_node("pts") == NULL) 310 return (-1); 311 312 ASSERT(pts_dip); 313 return (0); 314 } 315 316 /* 317 * Called from /dev fs. Checks if dip is attached, 318 * and if it is, returns its major number. 319 */ 320 major_t 321 ptms_subsidiary_attached(void) 322 { 323 major_t maj = DDI_MAJOR_T_NONE; 324 325 mutex_enter(&ptms_lock); 326 if (pts_dip) 327 maj = ddi_driver_major(pts_dip); 328 mutex_exit(&ptms_lock); 329 330 return (maj); 331 } 332 333 /* 334 * Allocate new minor number and pseudo-terminal entry. Returns the new entry or 335 * NULL if no memory or maximum number of entries reached. 336 */ 337 struct pt_ttys * 338 pt_ttys_alloc(void) 339 { 340 minor_t dminor; 341 struct pt_ttys *pt = NULL; 342 343 mutex_enter(&ptms_lock); 344 345 /* 346 * Always try to allocate new pty when pt_cnt minimum limit is not 347 * achieved. If it is achieved, the maximum is determined by either 348 * user-specified value (if it is non-zero) or our memory estimations - 349 * whatever is less. 350 */ 351 if (ptms_inuse >= pt_cnt) { 352 /* 353 * When system achieved required minimum of ptys, check for the 354 * denial of service limits. 355 * 356 * Since pt_max_pty may be zero, the formula below is used to 357 * avoid conditional expression. It will equal to pt_max_pty if 358 * it is not zero and ptms_ptymax otherwise. 359 */ 360 size_t user_max = (pt_max_pty == 0 ? ptms_ptymax : pt_max_pty); 361 362 /* Do not try to allocate more than allowed */ 363 if (ptms_inuse >= min(ptms_ptymax, user_max)) { 364 mutex_exit(&ptms_lock); 365 return (NULL); 366 } 367 } 368 ptms_inuse++; 369 370 /* 371 * Allocate new minor number. If this fails, all slots are busy and 372 * we need to grow the hash. 373 */ 374 dminor = (minor_t)(uintptr_t) 375 vmem_alloc(ptms_minor_arena, 1, VM_NOSLEEP); 376 377 if (dminor == 0) { 378 /* Grow the cache and retry allocation */ 379 dminor = ptms_grow(); 380 } 381 382 if (dminor == 0) { 383 /* Not enough memory now */ 384 ptms_inuse--; 385 mutex_exit(&ptms_lock); 386 return (NULL); 387 } 388 389 pt = kmem_cache_alloc(ptms_cache, KM_NOSLEEP); 390 if (pt == NULL) { 391 /* Not enough memory - this entry can't be used now. */ 392 vmem_free(ptms_minor_arena, (void *)(uintptr_t)dminor, 1); 393 ptms_inuse--; 394 } else { 395 pt->pt_minor = dminor; 396 pt->pt_pid = curproc->p_pid; /* For debugging */ 397 pt->pt_state = (PTMOPEN | PTLOCK); 398 pt->pt_zoneid = getzoneid(); 399 pt->pt_ruid = 0; /* we don't know uid/gid yet. Report as root */ 400 pt->pt_rgid = 0; 401 ASSERT(ptms_slots[dminor - 1] == NULL); 402 ptms_slots[dminor - 1] = pt; 403 } 404 405 mutex_exit(&ptms_lock); 406 return (pt); 407 } 408 409 /* 410 * Get pt_ttys structure by minor number. 411 * Returns NULL when minor is out of range. 412 */ 413 struct pt_ttys * 414 ptms_minor2ptty(minor_t dminor) 415 { 416 struct pt_ttys *pt = NULL; 417 418 ASSERT(mutex_owned(&ptms_lock)); 419 if ((dminor >= 1) && (dminor <= ptms_nslots) && ptms_slots != NULL) 420 pt = ptms_slots[dminor - 1]; 421 422 return (pt); 423 } 424 425 /* 426 * Invoked in response to chown on /dev/pts nodes to change the 427 * permission on a pty 428 */ 429 void 430 ptms_set_owner(minor_t dminor, uid_t ruid, gid_t rgid) 431 { 432 struct pt_ttys *pt; 433 434 if (ruid > MAXUID || rgid > MAXUID) 435 return; 436 437 /* 438 * /dev/pts/0 is not used, but some applications may check it. There 439 * is no pty backing it - so we have nothing to do. 440 */ 441 if (dminor == 0) 442 return; 443 444 mutex_enter(&ptms_lock); 445 pt = ptms_minor2ptty(dminor); 446 if (pt != NULL && pt->pt_zoneid == getzoneid()) { 447 pt->pt_ruid = ruid; 448 pt->pt_rgid = rgid; 449 } 450 mutex_exit(&ptms_lock); 451 } 452 453 /* 454 * Given a ptm/pts minor number 455 * returns: 456 * 1 if the pty is allocated to the current zone. 457 * 0 otherwise 458 * 459 * If the pty is allocated to the current zone, it also returns the owner. 460 */ 461 int 462 ptms_minor_valid(minor_t dminor, uid_t *ruid, gid_t *rgid) 463 { 464 struct pt_ttys *pt; 465 int ret; 466 467 ASSERT(ruid); 468 ASSERT(rgid); 469 470 *ruid = (uid_t)-1; 471 *rgid = (gid_t)-1; 472 473 /* 474 * /dev/pts/0 is not used, but some applications may check it, so create 475 * it also. Report the owner as root. It belongs to all zones. 476 */ 477 if (dminor == 0) { 478 *ruid = 0; 479 *rgid = 0; 480 return (1); 481 } 482 483 ret = 0; 484 mutex_enter(&ptms_lock); 485 pt = ptms_minor2ptty(dminor); 486 if (pt != NULL) { 487 ASSERT(pt->pt_ruid <= MAXUID); 488 ASSERT(pt->pt_rgid <= MAXUID); 489 if (pt->pt_zoneid == getzoneid()) { 490 ret = 1; 491 *ruid = pt->pt_ruid; 492 *rgid = pt->pt_rgid; 493 } 494 } 495 mutex_exit(&ptms_lock); 496 497 return (ret); 498 } 499 500 /* 501 * Given a ptm/pts minor number 502 * returns: 503 * 0 if the pty is not allocated 504 * 1 if the pty is allocated 505 */ 506 int 507 ptms_minor_exists(minor_t dminor) 508 { 509 int ret; 510 511 mutex_enter(&ptms_lock); 512 ret = ptms_minor2ptty(dminor) ? 1 : 0; 513 mutex_exit(&ptms_lock); 514 515 return (ret); 516 } 517 518 /* 519 * Close the pt and clear flags_to_clear. 520 * If pt device is not opened by someone else, free it and clear its slot. 521 */ 522 void 523 ptms_close(struct pt_ttys *pt, uint_t flags_to_clear) 524 { 525 uint_t flags; 526 527 ASSERT(MUTEX_NOT_HELD(&ptms_lock)); 528 ASSERT(pt != NULL); 529 530 mutex_enter(&ptms_lock); 531 532 mutex_enter(&pt->pt_lock); 533 pt->pt_state &= ~flags_to_clear; 534 flags = pt->pt_state; 535 mutex_exit(&pt->pt_lock); 536 537 if (! (flags & (PTMOPEN | PTSOPEN))) { 538 /* No one owns the entry - free it */ 539 540 ASSERT(pt->ptm_rdq == NULL); 541 ASSERT(pt->pts_rdq == NULL); 542 ASSERT(pt->pt_nullmsg == NULL); 543 ASSERT(pt->pt_refcnt == 0); 544 ASSERT(pt->pt_minor <= ptms_nslots); 545 ASSERT(ptms_slots[pt->pt_minor - 1] == pt); 546 ASSERT(ptms_inuse > 0); 547 548 ptms_inuse--; 549 550 pt->pt_pid = 0; 551 552 ptms_slots[pt->pt_minor - 1] = NULL; 553 /* Return minor number to the pool of minors */ 554 vmem_free(ptms_minor_arena, (void *)(uintptr_t)pt->pt_minor, 1); 555 /* Return pt to the cache */ 556 kmem_cache_free(ptms_cache, pt); 557 } 558 mutex_exit(&ptms_lock); 559 } 560 561 /* 562 * Allocate another slot table twice as large as the original one (limited to 563 * global maximum). Migrate all pt to the new slot table and free the original 564 * one. Create more /devices entries for new devices. 565 */ 566 static minor_t 567 ptms_grow() 568 { 569 minor_t old_size = ptms_nslots; 570 minor_t delta = MIN(pt_maxdelta, old_size); 571 minor_t new_size = old_size + delta; 572 struct pt_ttys **ptms_old = ptms_slots; 573 struct pt_ttys **ptms_new; 574 void *vaddr; /* vmem_add return value */ 575 576 ASSERT(MUTEX_HELD(&ptms_lock)); 577 578 DDBG("ptmopen(%d): need to grow\n", (int)ptms_inuse); 579 580 /* Allocate new ptms array */ 581 ptms_new = kmem_zalloc(new_size * sizeof (struct pt_ttys *), 582 KM_NOSLEEP); 583 if (ptms_new == NULL) 584 return ((minor_t)0); 585 586 /* Increase clone index space */ 587 vaddr = vmem_add(ptms_minor_arena, (void *)(uintptr_t)(old_size + 1), 588 new_size - old_size, VM_NOSLEEP); 589 590 if (vaddr == NULL) { 591 kmem_free(ptms_new, new_size * sizeof (struct pt_ttys *)); 592 return ((minor_t)0); 593 } 594 595 /* Migrate pt entries to a new location */ 596 ptms_nslots = new_size; 597 bcopy(ptms_old, ptms_new, old_size * sizeof (struct pt_ttys *)); 598 ptms_slots = ptms_new; 599 kmem_free(ptms_old, old_size * sizeof (struct pt_ttys *)); 600 601 /* Allocate minor number and return it */ 602 return ((minor_t)(uintptr_t) 603 vmem_alloc(ptms_minor_arena, 1, VM_NOSLEEP)); 604 } 605 606 /*ARGSUSED*/ 607 static int 608 ptms_constructor(void *maddr, void *arg, int kmflags) 609 { 610 struct pt_ttys *pt = maddr; 611 612 pt->pts_rdq = NULL; 613 pt->ptm_rdq = NULL; 614 pt->pt_nullmsg = NULL; 615 pt->pt_pid = 0; 616 pt->pt_minor = 0; 617 pt->pt_refcnt = 0; 618 pt->pt_state = 0; 619 pt->pt_zoneid = GLOBAL_ZONEID; 620 621 cv_init(&pt->pt_cv, NULL, CV_DEFAULT, NULL); 622 mutex_init(&pt->pt_lock, NULL, MUTEX_DEFAULT, NULL); 623 return (0); 624 } 625 626 /*ARGSUSED*/ 627 static void 628 ptms_destructor(void *maddr, void *arg) 629 { 630 struct pt_ttys *pt = maddr; 631 632 ASSERT(pt->pt_refcnt == 0); 633 ASSERT(pt->pt_state == 0); 634 ASSERT(pt->ptm_rdq == NULL); 635 ASSERT(pt->pts_rdq == NULL); 636 637 mutex_destroy(&pt->pt_lock); 638 cv_destroy(&pt->pt_cv); 639 } 640 641 #ifdef DEBUG 642 void 643 ptms_log(char *str, uint_t arg) 644 { 645 if (ptms_debug) { 646 if (ptms_debug & 2) 647 cmn_err(CE_CONT, str, arg); 648 if (ptms_debug & 4) 649 (void) strlog(PTMOD_ID, -1, 0, SL_TRACE | SL_ERROR, 650 str, arg); 651 else 652 (void) strlog(PTMOD_ID, -1, 0, SL_TRACE, str, arg); 653 } 654 } 655 656 void 657 ptms_logp(char *str, uintptr_t arg) 658 { 659 if (ptms_debug) { 660 if (ptms_debug & 2) 661 cmn_err(CE_CONT, str, arg); 662 if (ptms_debug & 4) 663 (void) strlog(PTMOD_ID, -1, 0, SL_TRACE | SL_ERROR, 664 str, arg); 665 else 666 (void) strlog(PTMOD_ID, -1, 0, SL_TRACE, str, arg); 667 } 668 } 669 #endif 670