10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 52621Sllai1 * Common Development and Distribution License (the "License"). 62621Sllai1 * You may not use this file except in compliance with the License. 70Sstevel@tonic-gate * 80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 100Sstevel@tonic-gate * See the License for the specific language governing permissions 110Sstevel@tonic-gate * and limitations under the License. 120Sstevel@tonic-gate * 130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * CDDL HEADER END 200Sstevel@tonic-gate */ 210Sstevel@tonic-gate /* 22*4321Scasper * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 230Sstevel@tonic-gate * Use is subject to license terms. 240Sstevel@tonic-gate */ 250Sstevel@tonic-gate 260Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 270Sstevel@tonic-gate 280Sstevel@tonic-gate /* 290Sstevel@tonic-gate * This file contains global data and code shared between master and slave parts 300Sstevel@tonic-gate * of the pseudo-terminal driver. 310Sstevel@tonic-gate * 320Sstevel@tonic-gate * Pseudo terminals (or pt's for short) are allocated dynamically. 330Sstevel@tonic-gate * pt's are put in the global ptms_slots array indexed by minor numbers. 340Sstevel@tonic-gate * 350Sstevel@tonic-gate * The slots array is initially small (of the size NPTY_MIN). When more pt's are 360Sstevel@tonic-gate * needed than the slot array size, the larger slot array is allocated and all 370Sstevel@tonic-gate * opened pt's move to the new one. 380Sstevel@tonic-gate * 390Sstevel@tonic-gate * Resource allocation: 400Sstevel@tonic-gate * 410Sstevel@tonic-gate * pt_ttys structures are allocated via pt_ttys_alloc, which uses 420Sstevel@tonic-gate * kmem_cache_alloc(). 430Sstevel@tonic-gate * Minor number space is allocated via vmem_alloc() interface. 440Sstevel@tonic-gate * ptms_slots arrays are allocated via kmem_alloc(). 450Sstevel@tonic-gate * 460Sstevel@tonic-gate * Minors are started from 1 instead of 0 because vmem_alloc returns 0 in case 470Sstevel@tonic-gate * of failure. Also, in anticipation of removing clone device interface to 480Sstevel@tonic-gate * pseudo-terminal subsystem, minor 0 should not be used. (Potential future 490Sstevel@tonic-gate * development). 500Sstevel@tonic-gate * 510Sstevel@tonic-gate * After the table slot size reaches pt_maxdelta, we stop 2^N extension 520Sstevel@tonic-gate * algorithm and start extending the slot table size by pt_maxdelta. 530Sstevel@tonic-gate * 542621Sllai1 * Device entries /dev/pts directory are created dynamically by the 552621Sllai1 * /dev filesystem. We no longer call ddi_create_minor_node() on 562621Sllai1 * behalf of the slave driver. The /dev filesystem creates /dev/pts 572621Sllai1 * nodes based on the pt_ttys array. 582621Sllai1 * 590Sstevel@tonic-gate * Synchronization: 600Sstevel@tonic-gate * 610Sstevel@tonic-gate * All global data synchronization between ptm/pts is done via global 620Sstevel@tonic-gate * ptms_lock mutex which is implicitly initialized by declaring it global. 630Sstevel@tonic-gate * 640Sstevel@tonic-gate * Individual fields of pt_ttys structure (except ptm_rdq, pts_rdq and 650Sstevel@tonic-gate * pt_nullmsg) are protected by pt_ttys.pt_lock mutex. 660Sstevel@tonic-gate * 670Sstevel@tonic-gate * PT_ENTER_READ/PT_ENTER_WRITE are reference counter based read-write locks 680Sstevel@tonic-gate * which allow reader locks to be reacquired by the same thread (usual 690Sstevel@tonic-gate * reader/writer locks can't be used for that purpose since it is illegal for 700Sstevel@tonic-gate * a thread to acquire a lock it already holds, even as a reader). The sole 710Sstevel@tonic-gate * purpose of these macros is to guarantee that the peer queue will not 720Sstevel@tonic-gate * disappear (due to closing peer) while it is used. It is safe to use 730Sstevel@tonic-gate * PT_ENTER_READ/PT_EXIT_READ brackets across calls like putq/putnext (since 740Sstevel@tonic-gate * they are not real locks but reference counts). 750Sstevel@tonic-gate * 760Sstevel@tonic-gate * PT_ENTER_WRITE/PT_EXIT_WRITE brackets are used ONLY in master/slave 770Sstevel@tonic-gate * open/close paths to modify ptm_rdq and pts_rdq fields. These fields should 780Sstevel@tonic-gate * be set to appropriate queues *after* qprocson() is called during open (to 790Sstevel@tonic-gate * prevent peer from accessing the queue with incomplete plumbing) and set to 800Sstevel@tonic-gate * NULL before qprocsoff() is called during close. Put and service procedures 810Sstevel@tonic-gate * use PT_ENTER_READ/PT_EXIT_READ to prevent peer closes. 820Sstevel@tonic-gate * 830Sstevel@tonic-gate * The pt_nullmsg field is only used in open/close routines and is also 840Sstevel@tonic-gate * protected by PT_ENTER_WRITE/PT_EXIT_WRITE brackets to avoid extra mutex 850Sstevel@tonic-gate * holds. 860Sstevel@tonic-gate * 870Sstevel@tonic-gate * Lock Ordering: 880Sstevel@tonic-gate * 890Sstevel@tonic-gate * If both ptms_lock and per-pty lock should be held, ptms_lock should always 900Sstevel@tonic-gate * be entered first, followed by per-pty lock. 910Sstevel@tonic-gate * 920Sstevel@tonic-gate * Global functions: 930Sstevel@tonic-gate * 940Sstevel@tonic-gate * void ptms_init(void); 950Sstevel@tonic-gate * 960Sstevel@tonic-gate * Called by pts/ptm _init entry points. It performes one-time 970Sstevel@tonic-gate * initialization needed for both pts and ptm. This initialization is done 980Sstevel@tonic-gate * here and not in ptms_initspace because all these data structures are not 990Sstevel@tonic-gate * needed if pseudo-terminals are not used in the system. 1000Sstevel@tonic-gate * 1010Sstevel@tonic-gate * struct pt_ttys *pt_ttys_alloc(void); 1020Sstevel@tonic-gate * 1030Sstevel@tonic-gate * Allocate new minor number and pseudo-terminal entry. May sleep. 1040Sstevel@tonic-gate * New minor number is recorded in pt_minor field of the entry returned. 1050Sstevel@tonic-gate * This routine also initializes pt_minor and pt_state fields of the new 1060Sstevel@tonic-gate * pseudo-terminal and puts a pointer to it into ptms_slots array. 1070Sstevel@tonic-gate * 1080Sstevel@tonic-gate * struct pt_ttys *ptms_minor2ptty(minor_t minor) 1090Sstevel@tonic-gate * 1100Sstevel@tonic-gate * Find pt_ttys structure by minor number. 1110Sstevel@tonic-gate * Returns NULL when minor is out of range. 1120Sstevel@tonic-gate * 1132621Sllai1 * int ptms_minor_valid(minor_t minor, uid_t *ruid, gid_t *rgid) 1142621Sllai1 * 1152621Sllai1 * Check if minor refers to an allocated pty in the current zone. 1162621Sllai1 * Returns 1172621Sllai1 * 0 if not allocated or not for this zone. 1182621Sllai1 * 1 if an allocated pty in the current zone. 1192621Sllai1 * Also returns owner of pty. 1202621Sllai1 * 1212621Sllai1 * int ptms_minor_exists(minor_t minor) 1222621Sllai1 * Check if minor refers to an allocated pty (in any zone) 1232621Sllai1 * Returns 1242621Sllai1 * 0 if not an allocated pty 1252621Sllai1 * 1 if an allocated pty 1262621Sllai1 * 1272621Sllai1 * void ptms_set_owner(minor_t minor, uid_t ruid, gid_t rgid) 1282621Sllai1 * 1292621Sllai1 * Sets the owner associated with a pty. 1302621Sllai1 * 1310Sstevel@tonic-gate * void ptms_close(struct pt_ttys *pt, uint_t flags_to_clear); 1320Sstevel@tonic-gate * 1330Sstevel@tonic-gate * Clear flags_to_clear in pt and if no one owns it (PTMOPEN/PTSOPEN not 1340Sstevel@tonic-gate * set) free pt entry and corresponding slot. 1350Sstevel@tonic-gate * 1360Sstevel@tonic-gate * Tuneables and configuration: 1370Sstevel@tonic-gate * 1380Sstevel@tonic-gate * pt_cnt: minimum number of pseudo-terminals in the system. The system 1390Sstevel@tonic-gate * should provide at least this number of ptys (provided sufficient 1400Sstevel@tonic-gate * memory is available). It is different from the older semantics 1410Sstevel@tonic-gate * of pt_cnt meaning maximum number of ptys. 1420Sstevel@tonic-gate * Set to 0 by default. 1430Sstevel@tonic-gate * 1440Sstevel@tonic-gate * pt_max_pty: Maximum number of pseudo-terminals in the system. The system 1450Sstevel@tonic-gate * should not allocate more ptys than pt_max_pty (although, it may 1460Sstevel@tonic-gate * impose stricter maximum). Zero value means no user-defined 1470Sstevel@tonic-gate * maximum. This is intended to be used as "denial-of-service" 1480Sstevel@tonic-gate * protection. 1490Sstevel@tonic-gate * Set to 0 by default. 1500Sstevel@tonic-gate * 1510Sstevel@tonic-gate * Both pt_cnt and pt_max_pty may be modified during system lifetime 1520Sstevel@tonic-gate * with their semantics preserved. 1530Sstevel@tonic-gate * 1540Sstevel@tonic-gate * pt_init_cnt: Initial size of ptms_slots array. Set to NPTY_INITIAL. 1550Sstevel@tonic-gate * 1560Sstevel@tonic-gate * pt_ptyofmem: Approximate percentage of system memory that may be 1570Sstevel@tonic-gate * occupied by pty data structures. Initially set to NPTY_PERCENT. 1580Sstevel@tonic-gate * This variable is used once during initialization to estimate 1590Sstevel@tonic-gate * maximum number of ptys in the system. The actual maximum is 1600Sstevel@tonic-gate * determined as minimum of pt_max_pty and calculated value. 1610Sstevel@tonic-gate * 1620Sstevel@tonic-gate * pt_maxdelta: Maximum extension chunk of the slot table. 1630Sstevel@tonic-gate */ 1640Sstevel@tonic-gate 1650Sstevel@tonic-gate 1660Sstevel@tonic-gate 1670Sstevel@tonic-gate #include <sys/types.h> 1680Sstevel@tonic-gate #include <sys/param.h> 1690Sstevel@tonic-gate #include <sys/termios.h> 1700Sstevel@tonic-gate #include <sys/stream.h> 1710Sstevel@tonic-gate #include <sys/stropts.h> 1720Sstevel@tonic-gate #include <sys/kmem.h> 1730Sstevel@tonic-gate #include <sys/ptms.h> 1740Sstevel@tonic-gate #include <sys/stat.h> 1750Sstevel@tonic-gate #include <sys/sunddi.h> 1760Sstevel@tonic-gate #include <sys/ddi.h> 1770Sstevel@tonic-gate #include <sys/bitmap.h> 1780Sstevel@tonic-gate #include <sys/sysmacros.h> 1790Sstevel@tonic-gate #include <sys/ddi_impldefs.h> 1800Sstevel@tonic-gate #include <sys/zone.h> 1810Sstevel@tonic-gate #ifdef DEBUG 1820Sstevel@tonic-gate #include <sys/strlog.h> 1830Sstevel@tonic-gate #endif 1840Sstevel@tonic-gate 1850Sstevel@tonic-gate 1860Sstevel@tonic-gate /* Initial number of ptms slots */ 1870Sstevel@tonic-gate #define NPTY_INITIAL 16 1880Sstevel@tonic-gate 1890Sstevel@tonic-gate #define NPTY_PERCENT 5 1900Sstevel@tonic-gate 1910Sstevel@tonic-gate /* Maximum increment of the slot table size */ 1920Sstevel@tonic-gate #define PTY_MAXDELTA 128 1930Sstevel@tonic-gate 1940Sstevel@tonic-gate /* 1950Sstevel@tonic-gate * Tuneable variables. 1960Sstevel@tonic-gate */ 1970Sstevel@tonic-gate uint_t pt_cnt = 0; /* Minimum number of ptys */ 1980Sstevel@tonic-gate size_t pt_max_pty = 0; /* Maximum number of ptys */ 1990Sstevel@tonic-gate uint_t pt_init_cnt = NPTY_INITIAL; /* Initial number of ptms slots */ 2000Sstevel@tonic-gate uint_t pt_pctofmem = NPTY_PERCENT; /* Percent of memory to use for ptys */ 2010Sstevel@tonic-gate uint_t pt_maxdelta = PTY_MAXDELTA; /* Max increment for slot table size */ 2020Sstevel@tonic-gate 2030Sstevel@tonic-gate /* Other global variables */ 2040Sstevel@tonic-gate 2050Sstevel@tonic-gate kmutex_t ptms_lock; /* Global data access lock */ 2060Sstevel@tonic-gate 2070Sstevel@tonic-gate /* 2080Sstevel@tonic-gate * Slot array and its management variables 2090Sstevel@tonic-gate */ 2100Sstevel@tonic-gate static struct pt_ttys **ptms_slots = NULL; /* Slots for actual pt structures */ 2110Sstevel@tonic-gate static size_t ptms_nslots = 0; /* Size of slot array */ 2120Sstevel@tonic-gate static size_t ptms_ptymax = 0; /* Maximum number of ptys */ 2130Sstevel@tonic-gate static size_t ptms_inuse = 0; /* # of ptys currently allocated */ 2140Sstevel@tonic-gate 2152621Sllai1 dev_info_t *pts_dip = NULL; /* set if slave is attached */ 2160Sstevel@tonic-gate 2170Sstevel@tonic-gate static struct kmem_cache *ptms_cache = NULL; /* pty cache */ 2180Sstevel@tonic-gate 2190Sstevel@tonic-gate static vmem_t *ptms_minor_arena = NULL; /* Arena for device minors */ 2200Sstevel@tonic-gate 2210Sstevel@tonic-gate static uint_t ptms_roundup(uint_t); 2220Sstevel@tonic-gate static int ptms_constructor(void *, void *, int); 2230Sstevel@tonic-gate static void ptms_destructor(void *, void *); 2240Sstevel@tonic-gate static minor_t ptms_grow(void); 2250Sstevel@tonic-gate 2260Sstevel@tonic-gate /* 2270Sstevel@tonic-gate * Total size occupied by one pty. Each pty master/slave pair consumes one 2280Sstevel@tonic-gate * pointer for ptms_slots array, one pt_ttys structure and one empty message 2290Sstevel@tonic-gate * preallocated for pts close. 2300Sstevel@tonic-gate */ 2310Sstevel@tonic-gate 2320Sstevel@tonic-gate #define PTY_SIZE (sizeof (struct pt_ttys) + \ 2330Sstevel@tonic-gate sizeof (struct pt_ttys *) + \ 2340Sstevel@tonic-gate sizeof (dblk_t)) 2350Sstevel@tonic-gate 2360Sstevel@tonic-gate #ifdef DEBUG 2370Sstevel@tonic-gate int ptms_debug = 0; 2380Sstevel@tonic-gate #define PTMOD_ID 5 2390Sstevel@tonic-gate #endif 2400Sstevel@tonic-gate 2410Sstevel@tonic-gate /* 2420Sstevel@tonic-gate * Clear all bits of x except the highest bit 2430Sstevel@tonic-gate */ 2440Sstevel@tonic-gate #define truncate(x) ((x) <= 2 ? (x) : (1 << (highbit(x) - 1))) 2450Sstevel@tonic-gate 2460Sstevel@tonic-gate /* 2470Sstevel@tonic-gate * Roundup the number to the nearest power of 2 2480Sstevel@tonic-gate */ 2490Sstevel@tonic-gate static uint_t 2500Sstevel@tonic-gate ptms_roundup(uint_t x) 2510Sstevel@tonic-gate { 2520Sstevel@tonic-gate uint_t p = truncate(x); /* x with non-high bits stripped */ 2530Sstevel@tonic-gate 2540Sstevel@tonic-gate /* 2550Sstevel@tonic-gate * If x is a power of 2, return x, otherwise roundup. 2560Sstevel@tonic-gate */ 2570Sstevel@tonic-gate return (p == x ? p : (p * 2)); 2580Sstevel@tonic-gate } 2590Sstevel@tonic-gate 2600Sstevel@tonic-gate /* 2610Sstevel@tonic-gate * Allocate ptms_slots array and kmem cache for pt_ttys. This initialization is 2620Sstevel@tonic-gate * only called once during system lifetime. Called from ptm or pts _init 2630Sstevel@tonic-gate * routine. 2640Sstevel@tonic-gate */ 2650Sstevel@tonic-gate void 2660Sstevel@tonic-gate ptms_init(void) 2670Sstevel@tonic-gate { 2680Sstevel@tonic-gate mutex_enter(&ptms_lock); 2690Sstevel@tonic-gate 2700Sstevel@tonic-gate if (ptms_slots == NULL) { 2710Sstevel@tonic-gate ptms_slots = kmem_zalloc(pt_init_cnt * 2720Sstevel@tonic-gate sizeof (struct pt_ttys *), KM_SLEEP); 2730Sstevel@tonic-gate 2740Sstevel@tonic-gate ptms_cache = kmem_cache_create("pty_map", 2750Sstevel@tonic-gate sizeof (struct pt_ttys), 0, ptms_constructor, 2760Sstevel@tonic-gate ptms_destructor, NULL, NULL, NULL, 0); 2770Sstevel@tonic-gate 2780Sstevel@tonic-gate ptms_nslots = pt_init_cnt; 2790Sstevel@tonic-gate 2800Sstevel@tonic-gate /* Allocate integer space for minor numbers */ 2810Sstevel@tonic-gate ptms_minor_arena = vmem_create("ptms_minor", (void *)1, 2820Sstevel@tonic-gate ptms_nslots, 1, NULL, NULL, NULL, 0, 2830Sstevel@tonic-gate VM_SLEEP | VMC_IDENTIFIER); 2840Sstevel@tonic-gate 2850Sstevel@tonic-gate /* 2860Sstevel@tonic-gate * Calculate available number of ptys - how many ptys can we 2870Sstevel@tonic-gate * allocate in pt_pctofmem % of available memory. The value is 2880Sstevel@tonic-gate * rounded up to the nearest power of 2. 2890Sstevel@tonic-gate */ 2900Sstevel@tonic-gate ptms_ptymax = ptms_roundup((pt_pctofmem * kmem_maxavail()) / 2910Sstevel@tonic-gate (100 * PTY_SIZE)); 2920Sstevel@tonic-gate } 2930Sstevel@tonic-gate mutex_exit(&ptms_lock); 2940Sstevel@tonic-gate } 2950Sstevel@tonic-gate 2962621Sllai1 /* 2972621Sllai1 * This routine attaches the pts dip. 2982621Sllai1 */ 2992621Sllai1 int 3002621Sllai1 ptms_attach_slave(void) 3010Sstevel@tonic-gate { 3022621Sllai1 if (pts_dip == NULL && i_ddi_attach_pseudo_node("pts") == NULL) 3032621Sllai1 return (-1); 3040Sstevel@tonic-gate 3052621Sllai1 ASSERT(pts_dip); 3062621Sllai1 return (0); 3070Sstevel@tonic-gate } 3080Sstevel@tonic-gate 3090Sstevel@tonic-gate /* 3102621Sllai1 * Called from /dev fs. Checks if dip is attached, 3112621Sllai1 * and if it is, returns its major number. 3120Sstevel@tonic-gate */ 3132621Sllai1 major_t 3142621Sllai1 ptms_slave_attached(void) 3150Sstevel@tonic-gate { 3162621Sllai1 major_t maj = (major_t)-1; 3170Sstevel@tonic-gate 3180Sstevel@tonic-gate mutex_enter(&ptms_lock); 3192621Sllai1 if (pts_dip) 3202621Sllai1 maj = ddi_driver_major(pts_dip); 3210Sstevel@tonic-gate mutex_exit(&ptms_lock); 3220Sstevel@tonic-gate 3232621Sllai1 return (maj); 3240Sstevel@tonic-gate } 3250Sstevel@tonic-gate 3260Sstevel@tonic-gate /* 3270Sstevel@tonic-gate * Allocate new minor number and pseudo-terminal entry. Returns the new entry or 3280Sstevel@tonic-gate * NULL if no memory or maximum number of entries reached. 3290Sstevel@tonic-gate */ 3300Sstevel@tonic-gate struct pt_ttys * 3310Sstevel@tonic-gate pt_ttys_alloc(void) 3320Sstevel@tonic-gate { 3330Sstevel@tonic-gate minor_t dminor; 3340Sstevel@tonic-gate struct pt_ttys *pt = NULL; 3350Sstevel@tonic-gate 3360Sstevel@tonic-gate mutex_enter(&ptms_lock); 3370Sstevel@tonic-gate 3380Sstevel@tonic-gate /* 3390Sstevel@tonic-gate * Always try to allocate new pty when pt_cnt minimum limit is not 3400Sstevel@tonic-gate * achieved. If it is achieved, the maximum is determined by either 3410Sstevel@tonic-gate * user-specified value (if it is non-zero) or our memory estimations - 3420Sstevel@tonic-gate * whatever is less. 3430Sstevel@tonic-gate */ 3440Sstevel@tonic-gate if (ptms_inuse >= pt_cnt) { 3450Sstevel@tonic-gate /* 3460Sstevel@tonic-gate * When system achieved required minimum of ptys, check for the 3470Sstevel@tonic-gate * denial of service limits. 3480Sstevel@tonic-gate * 3490Sstevel@tonic-gate * Since pt_max_pty may be zero, the formula below is used to 3500Sstevel@tonic-gate * avoid conditional expression. It will equal to pt_max_pty if 3510Sstevel@tonic-gate * it is not zero and ptms_ptymax otherwise. 3520Sstevel@tonic-gate */ 3530Sstevel@tonic-gate size_t user_max = (pt_max_pty == 0 ? ptms_ptymax : pt_max_pty); 3540Sstevel@tonic-gate 3550Sstevel@tonic-gate /* Do not try to allocate more than allowed */ 3560Sstevel@tonic-gate if (ptms_inuse >= min(ptms_ptymax, user_max)) { 3570Sstevel@tonic-gate mutex_exit(&ptms_lock); 3580Sstevel@tonic-gate return (NULL); 3590Sstevel@tonic-gate } 3600Sstevel@tonic-gate } 3610Sstevel@tonic-gate ptms_inuse++; 3620Sstevel@tonic-gate 3630Sstevel@tonic-gate /* 3640Sstevel@tonic-gate * Allocate new minor number. If this fails, all slots are busy and 3650Sstevel@tonic-gate * we need to grow the hash. 3660Sstevel@tonic-gate */ 3670Sstevel@tonic-gate dminor = (minor_t)(uintptr_t) 3680Sstevel@tonic-gate vmem_alloc(ptms_minor_arena, 1, VM_NOSLEEP); 3690Sstevel@tonic-gate 3700Sstevel@tonic-gate if (dminor == 0) { 3710Sstevel@tonic-gate /* Grow the cache and retry allocation */ 3720Sstevel@tonic-gate dminor = ptms_grow(); 3730Sstevel@tonic-gate } 3740Sstevel@tonic-gate 3750Sstevel@tonic-gate if (dminor == 0) { 3760Sstevel@tonic-gate /* Not enough memory now */ 3770Sstevel@tonic-gate ptms_inuse--; 3780Sstevel@tonic-gate mutex_exit(&ptms_lock); 3790Sstevel@tonic-gate return (NULL); 3800Sstevel@tonic-gate } 3810Sstevel@tonic-gate 3820Sstevel@tonic-gate pt = kmem_cache_alloc(ptms_cache, KM_NOSLEEP); 3830Sstevel@tonic-gate if (pt == NULL) { 3840Sstevel@tonic-gate /* Not enough memory - this entry can't be used now. */ 3850Sstevel@tonic-gate vmem_free(ptms_minor_arena, (void *)(uintptr_t)dminor, 1); 3860Sstevel@tonic-gate ptms_inuse--; 3870Sstevel@tonic-gate } else { 3880Sstevel@tonic-gate pt->pt_minor = dminor; 3890Sstevel@tonic-gate pt->pt_pid = curproc->p_pid; /* For debugging */ 3900Sstevel@tonic-gate pt->pt_state = (PTMOPEN | PTLOCK); 3910Sstevel@tonic-gate pt->pt_zoneid = getzoneid(); 3922621Sllai1 pt->pt_ruid = 0; /* we don't know uid/gid yet. Report as root */ 3932621Sllai1 pt->pt_rgid = 0; 3940Sstevel@tonic-gate ASSERT(ptms_slots[dminor - 1] == NULL); 3950Sstevel@tonic-gate ptms_slots[dminor - 1] = pt; 3960Sstevel@tonic-gate } 3970Sstevel@tonic-gate 3980Sstevel@tonic-gate mutex_exit(&ptms_lock); 3990Sstevel@tonic-gate return (pt); 4000Sstevel@tonic-gate } 4010Sstevel@tonic-gate 4020Sstevel@tonic-gate /* 4030Sstevel@tonic-gate * Get pt_ttys structure by minor number. 4040Sstevel@tonic-gate * Returns NULL when minor is out of range. 4050Sstevel@tonic-gate */ 4060Sstevel@tonic-gate struct pt_ttys * 4070Sstevel@tonic-gate ptms_minor2ptty(minor_t dminor) 4080Sstevel@tonic-gate { 4090Sstevel@tonic-gate struct pt_ttys *pt = NULL; 4100Sstevel@tonic-gate 4110Sstevel@tonic-gate ASSERT(mutex_owned(&ptms_lock)); 4120Sstevel@tonic-gate if ((dminor >= 1) && (dminor <= ptms_nslots) && ptms_slots != NULL) 4130Sstevel@tonic-gate pt = ptms_slots[dminor - 1]; 4140Sstevel@tonic-gate 4150Sstevel@tonic-gate return (pt); 4160Sstevel@tonic-gate } 4170Sstevel@tonic-gate 4180Sstevel@tonic-gate /* 4192621Sllai1 * Invoked in response to chown on /dev/pts nodes to change the 4202621Sllai1 * permission on a pty 4212621Sllai1 */ 4222621Sllai1 void 4232621Sllai1 ptms_set_owner(minor_t dminor, uid_t ruid, gid_t rgid) 4242621Sllai1 { 4252621Sllai1 struct pt_ttys *pt; 4262621Sllai1 4272621Sllai1 ASSERT(ruid >= 0); 4282621Sllai1 ASSERT(rgid >= 0); 4292621Sllai1 4302621Sllai1 if (ruid < 0 || rgid < 0) 4312621Sllai1 return; 4322621Sllai1 4332621Sllai1 /* 4342621Sllai1 * /dev/pts/0 is not used, but some applications may check it. There 4352621Sllai1 * is no pty backing it - so we have nothing to do. 4362621Sllai1 */ 4372621Sllai1 if (dminor == 0) 4382621Sllai1 return; 4392621Sllai1 4402621Sllai1 mutex_enter(&ptms_lock); 4412621Sllai1 pt = ptms_minor2ptty(dminor); 4422621Sllai1 if (pt != NULL && pt->pt_zoneid == getzoneid()) { 4432621Sllai1 pt->pt_ruid = ruid; 4442621Sllai1 pt->pt_rgid = rgid; 4452621Sllai1 } 4462621Sllai1 mutex_exit(&ptms_lock); 4472621Sllai1 } 4482621Sllai1 4492621Sllai1 /* 4502621Sllai1 * Given a ptm/pts minor number 4512621Sllai1 * returns: 4522621Sllai1 * 1 if the pty is allocated to the current zone. 4532621Sllai1 * 0 otherwise 4542621Sllai1 * 4552621Sllai1 * If the pty is allocated to the current zone, it also returns the owner. 4562621Sllai1 */ 4572621Sllai1 int 4582621Sllai1 ptms_minor_valid(minor_t dminor, uid_t *ruid, gid_t *rgid) 4592621Sllai1 { 4602621Sllai1 struct pt_ttys *pt; 4612621Sllai1 int ret; 4622621Sllai1 4632621Sllai1 ASSERT(ruid); 4642621Sllai1 ASSERT(rgid); 4652621Sllai1 466*4321Scasper *ruid = (uid_t)-1; 467*4321Scasper *rgid = (gid_t)-1; 4682621Sllai1 4692621Sllai1 /* 4702621Sllai1 * /dev/pts/0 is not used, but some applications may check it, so create 4712621Sllai1 * it also. Report the owner as root. It belongs to all zones. 4722621Sllai1 */ 4732621Sllai1 if (dminor == 0) { 4742621Sllai1 *ruid = 0; 4752621Sllai1 *rgid = 0; 4762621Sllai1 return (1); 4772621Sllai1 } 4782621Sllai1 4792621Sllai1 ret = 0; 4802621Sllai1 mutex_enter(&ptms_lock); 4812621Sllai1 pt = ptms_minor2ptty(dminor); 4822621Sllai1 if (pt != NULL) { 4832621Sllai1 ASSERT(pt->pt_ruid >= 0); 4842621Sllai1 ASSERT(pt->pt_rgid >= 0); 4852621Sllai1 if (pt->pt_zoneid == getzoneid()) { 4862621Sllai1 ret = 1; 4872621Sllai1 *ruid = pt->pt_ruid; 4882621Sllai1 *rgid = pt->pt_rgid; 4892621Sllai1 } 4902621Sllai1 } 4912621Sllai1 mutex_exit(&ptms_lock); 4922621Sllai1 4932621Sllai1 return (ret); 4942621Sllai1 } 4952621Sllai1 4962621Sllai1 /* 4972621Sllai1 * Given a ptm/pts minor number 4982621Sllai1 * returns: 4992621Sllai1 * 0 if the pty is not allocated 5002621Sllai1 * 1 if the pty is allocated 5012621Sllai1 */ 5022621Sllai1 int 5032621Sllai1 ptms_minor_exists(minor_t dminor) 5042621Sllai1 { 5052621Sllai1 int ret; 5062621Sllai1 5072621Sllai1 mutex_enter(&ptms_lock); 5082621Sllai1 ret = ptms_minor2ptty(dminor) ? 1 : 0; 5092621Sllai1 mutex_exit(&ptms_lock); 5102621Sllai1 5112621Sllai1 return (ret); 5122621Sllai1 } 5132621Sllai1 5142621Sllai1 /* 5150Sstevel@tonic-gate * Close the pt and clear flags_to_clear. 5160Sstevel@tonic-gate * If pt device is not opened by someone else, free it and clear its slot. 5170Sstevel@tonic-gate */ 5180Sstevel@tonic-gate void 5190Sstevel@tonic-gate ptms_close(struct pt_ttys *pt, uint_t flags_to_clear) 5200Sstevel@tonic-gate { 5210Sstevel@tonic-gate uint_t flags; 5220Sstevel@tonic-gate 5230Sstevel@tonic-gate ASSERT(MUTEX_NOT_HELD(&ptms_lock)); 5240Sstevel@tonic-gate ASSERT(pt != NULL); 5250Sstevel@tonic-gate 5260Sstevel@tonic-gate mutex_enter(&ptms_lock); 5270Sstevel@tonic-gate 5280Sstevel@tonic-gate mutex_enter(&pt->pt_lock); 5290Sstevel@tonic-gate pt->pt_state &= ~flags_to_clear; 5300Sstevel@tonic-gate flags = pt->pt_state; 5310Sstevel@tonic-gate mutex_exit(&pt->pt_lock); 5320Sstevel@tonic-gate 5330Sstevel@tonic-gate if (! (flags & (PTMOPEN | PTSOPEN))) { 5340Sstevel@tonic-gate /* No one owns the entry - free it */ 5350Sstevel@tonic-gate 5360Sstevel@tonic-gate ASSERT(pt->ptm_rdq == NULL); 5370Sstevel@tonic-gate ASSERT(pt->pts_rdq == NULL); 5380Sstevel@tonic-gate ASSERT(pt->pt_nullmsg == NULL); 5390Sstevel@tonic-gate ASSERT(pt->pt_refcnt == 0); 5400Sstevel@tonic-gate ASSERT(pt->pt_minor <= ptms_nslots); 5410Sstevel@tonic-gate ASSERT(ptms_slots[pt->pt_minor - 1] == pt); 5420Sstevel@tonic-gate ASSERT(ptms_inuse > 0); 5430Sstevel@tonic-gate 5440Sstevel@tonic-gate ptms_inuse--; 5450Sstevel@tonic-gate 5460Sstevel@tonic-gate pt->pt_pid = 0; 5470Sstevel@tonic-gate 5480Sstevel@tonic-gate ptms_slots[pt->pt_minor - 1] = NULL; 5490Sstevel@tonic-gate /* Return minor number to the pool of minors */ 5500Sstevel@tonic-gate vmem_free(ptms_minor_arena, (void *)(uintptr_t)pt->pt_minor, 1); 5510Sstevel@tonic-gate /* Return pt to the cache */ 5520Sstevel@tonic-gate kmem_cache_free(ptms_cache, pt); 5530Sstevel@tonic-gate } 5540Sstevel@tonic-gate mutex_exit(&ptms_lock); 5550Sstevel@tonic-gate } 5560Sstevel@tonic-gate 5570Sstevel@tonic-gate /* 5580Sstevel@tonic-gate * Allocate another slot table twice as large as the original one (limited to 5590Sstevel@tonic-gate * global maximum). Migrate all pt to the new slot table and free the original 5600Sstevel@tonic-gate * one. Create more /devices entries for new devices. 5610Sstevel@tonic-gate */ 5620Sstevel@tonic-gate static minor_t 5630Sstevel@tonic-gate ptms_grow() 5640Sstevel@tonic-gate { 5650Sstevel@tonic-gate minor_t old_size = ptms_nslots; 5660Sstevel@tonic-gate minor_t delta = MIN(pt_maxdelta, old_size); 5670Sstevel@tonic-gate minor_t new_size = old_size + delta; 5680Sstevel@tonic-gate struct pt_ttys **ptms_old = ptms_slots; 5690Sstevel@tonic-gate struct pt_ttys **ptms_new; 5700Sstevel@tonic-gate void *vaddr; /* vmem_add return value */ 5710Sstevel@tonic-gate 5720Sstevel@tonic-gate ASSERT(MUTEX_HELD(&ptms_lock)); 5730Sstevel@tonic-gate 5740Sstevel@tonic-gate DDBG("ptmopen(%d): need to grow\n", (int)ptms_inuse); 5750Sstevel@tonic-gate 5760Sstevel@tonic-gate /* Allocate new ptms array */ 5770Sstevel@tonic-gate ptms_new = kmem_zalloc(new_size * sizeof (struct pt_ttys *), 5780Sstevel@tonic-gate KM_NOSLEEP); 5790Sstevel@tonic-gate if (ptms_new == NULL) 5800Sstevel@tonic-gate return ((minor_t)0); 5810Sstevel@tonic-gate 5820Sstevel@tonic-gate /* Increase clone index space */ 5830Sstevel@tonic-gate vaddr = vmem_add(ptms_minor_arena, (void *)(uintptr_t)(old_size + 1), 5840Sstevel@tonic-gate new_size - old_size, VM_NOSLEEP); 5850Sstevel@tonic-gate 5860Sstevel@tonic-gate if (vaddr == NULL) { 5870Sstevel@tonic-gate kmem_free(ptms_new, new_size * sizeof (struct pt_ttys *)); 5880Sstevel@tonic-gate return ((minor_t)0); 5890Sstevel@tonic-gate } 5900Sstevel@tonic-gate 5910Sstevel@tonic-gate /* Migrate pt entries to a new location */ 5920Sstevel@tonic-gate ptms_nslots = new_size; 5930Sstevel@tonic-gate bcopy(ptms_old, ptms_new, old_size * sizeof (struct pt_ttys *)); 5940Sstevel@tonic-gate ptms_slots = ptms_new; 5950Sstevel@tonic-gate kmem_free(ptms_old, old_size * sizeof (struct pt_ttys *)); 5960Sstevel@tonic-gate 5970Sstevel@tonic-gate /* Allocate minor number and return it */ 5980Sstevel@tonic-gate return ((minor_t)(uintptr_t) 5990Sstevel@tonic-gate vmem_alloc(ptms_minor_arena, 1, VM_NOSLEEP)); 6000Sstevel@tonic-gate } 6010Sstevel@tonic-gate 6020Sstevel@tonic-gate /*ARGSUSED*/ 6030Sstevel@tonic-gate static int 6040Sstevel@tonic-gate ptms_constructor(void *maddr, void *arg, int kmflags) 6050Sstevel@tonic-gate { 6060Sstevel@tonic-gate struct pt_ttys *pt = maddr; 6070Sstevel@tonic-gate 6080Sstevel@tonic-gate pt->pts_rdq = NULL; 6090Sstevel@tonic-gate pt->ptm_rdq = NULL; 6100Sstevel@tonic-gate pt->pt_nullmsg = NULL; 6110Sstevel@tonic-gate pt->pt_pid = NULL; 6120Sstevel@tonic-gate pt->pt_minor = NULL; 6130Sstevel@tonic-gate pt->pt_refcnt = 0; 6140Sstevel@tonic-gate pt->pt_state = 0; 6150Sstevel@tonic-gate pt->pt_zoneid = GLOBAL_ZONEID; 6160Sstevel@tonic-gate 6170Sstevel@tonic-gate cv_init(&pt->pt_cv, NULL, CV_DEFAULT, NULL); 6180Sstevel@tonic-gate mutex_init(&pt->pt_lock, NULL, MUTEX_DEFAULT, NULL); 6190Sstevel@tonic-gate return (0); 6200Sstevel@tonic-gate } 6210Sstevel@tonic-gate 6220Sstevel@tonic-gate /*ARGSUSED*/ 6230Sstevel@tonic-gate static void 6240Sstevel@tonic-gate ptms_destructor(void *maddr, void *arg) 6250Sstevel@tonic-gate { 6260Sstevel@tonic-gate struct pt_ttys *pt = maddr; 6270Sstevel@tonic-gate 6280Sstevel@tonic-gate ASSERT(pt->pt_refcnt == 0); 6290Sstevel@tonic-gate ASSERT(pt->pt_state == 0); 6300Sstevel@tonic-gate ASSERT(pt->ptm_rdq == NULL); 6310Sstevel@tonic-gate ASSERT(pt->pts_rdq == NULL); 6320Sstevel@tonic-gate 6330Sstevel@tonic-gate mutex_destroy(&pt->pt_lock); 6340Sstevel@tonic-gate cv_destroy(&pt->pt_cv); 6350Sstevel@tonic-gate } 6360Sstevel@tonic-gate 6370Sstevel@tonic-gate #ifdef DEBUG 6380Sstevel@tonic-gate void 6390Sstevel@tonic-gate ptms_log(char *str, uint_t arg) 6400Sstevel@tonic-gate { 6410Sstevel@tonic-gate if (ptms_debug) { 6420Sstevel@tonic-gate if (ptms_debug & 2) 6430Sstevel@tonic-gate cmn_err(CE_CONT, str, arg); 6440Sstevel@tonic-gate if (ptms_debug & 4) 6450Sstevel@tonic-gate (void) strlog(PTMOD_ID, -1, 0, SL_TRACE | SL_ERROR, 6460Sstevel@tonic-gate str, arg); 6470Sstevel@tonic-gate else 6480Sstevel@tonic-gate (void) strlog(PTMOD_ID, -1, 0, SL_TRACE, str, arg); 6490Sstevel@tonic-gate } 6500Sstevel@tonic-gate } 6510Sstevel@tonic-gate 6520Sstevel@tonic-gate void 6530Sstevel@tonic-gate ptms_logp(char *str, uintptr_t arg) 6540Sstevel@tonic-gate { 6550Sstevel@tonic-gate if (ptms_debug) { 6560Sstevel@tonic-gate if (ptms_debug & 2) 6570Sstevel@tonic-gate cmn_err(CE_CONT, str, arg); 6580Sstevel@tonic-gate if (ptms_debug & 4) 6590Sstevel@tonic-gate (void) strlog(PTMOD_ID, -1, 0, SL_TRACE | SL_ERROR, 6600Sstevel@tonic-gate str, arg); 6610Sstevel@tonic-gate else 6620Sstevel@tonic-gate (void) strlog(PTMOD_ID, -1, 0, SL_TRACE, str, arg); 6630Sstevel@tonic-gate } 6640Sstevel@tonic-gate } 6650Sstevel@tonic-gate #endif 666