xref: /onnv-gate/usr/src/uts/common/io/ptms_conf.c (revision 0:68f95e015346)
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