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