4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 /*
27 * This file contains global data and code shared between master and slave parts
28 * of the pseudo-terminal driver.
29 *
30 * Pseudo terminals (or pt's for short) are allocated dynamically.
31 * pt's are put in the global ptms_slots array indexed by minor numbers.
32 *
33 * The slots array is initially small (of the size NPTY_MIN). When more pt's are
34 * needed than the slot array size, the larger slot array is allocated and all
35 * opened pt's move to the new one.
36 *
37 * Resource allocation:
38 *
39 * pt_ttys structures are allocated via pt_ttys_alloc, which uses
40 * kmem_cache_alloc().
41 * Minor number space is allocated via vmem_alloc() interface.
42 * ptms_slots arrays are allocated via kmem_alloc().
43 *
44 * Minors are started from 1 instead of 0 because vmem_alloc returns 0 in case
45 * of failure. Also, in anticipation of removing clone device interface to
46 * pseudo-terminal subsystem, minor 0 should not be used. (Potential future
47 * development).
48 *
49 * After the table slot size reaches pt_maxdelta, we stop 2^N extension
50 * algorithm and start extending the slot table size by pt_maxdelta.
51 *
52 * Device entries /dev/pts directory are created dynamically by the
53 * /dev filesystem. We no longer call ddi_create_minor_node() on
54 * behalf of the slave driver. The /dev filesystem creates /dev/pts
55 * nodes based on the pt_ttys array.
56 *
57 * Synchronization:
58 *
59 * All global data synchronization between ptm/pts is done via global
60 * ptms_lock mutex which is implicitly initialized by declaring it global.
61 *
62 * Individual fields of pt_ttys structure (except ptm_rdq, pts_rdq and
63 * pt_nullmsg) are protected by pt_ttys.pt_lock mutex.
64 *
65 * PT_ENTER_READ/PT_ENTER_WRITE are reference counter based read-write locks
66 * which allow reader locks to be reacquired by the same thread (usual
67 * reader/writer locks can't be used for that purpose since it is illegal for
68 * a thread to acquire a lock it already holds, even as a reader). The sole
69 * purpose of these macros is to guarantee that the peer queue will not
70 * disappear (due to closing peer) while it is used. It is safe to use
71 * PT_ENTER_READ/PT_EXIT_READ brackets across calls like putq/putnext (since
72 * they are not real locks but reference counts).
73 *
74 * PT_ENTER_WRITE/PT_EXIT_WRITE brackets are used ONLY in master/slave
75 * open/close paths to modify ptm_rdq and pts_rdq fields. These fields should
76 * be set to appropriate queues *after* qprocson() is called during open (to
77 * prevent peer from accessing the queue with incomplete plumbing) and set to
78 * NULL before qprocsoff() is called during close. Put and service procedures
79 * use PT_ENTER_READ/PT_EXIT_READ to prevent peer closes.
80 *
81 * The pt_nullmsg field is only used in open/close routines and is also
82 * protected by PT_ENTER_WRITE/PT_EXIT_WRITE brackets to avoid extra mutex
83 * holds.
84 *
85 * Lock Ordering:
86 *
87 * If both ptms_lock and per-pty lock should be held, ptms_lock should always
88 * be entered first, followed by per-pty lock.
89 *
90 * Global functions:
91 *
92 * void ptms_init(void);
93 *
94 * Called by pts/ptm _init entry points. It performes one-time
95 * initialization needed for both pts and ptm. This initialization is done
96 * here and not in ptms_initspace because all these data structures are not
97 * needed if pseudo-terminals are not used in the system.
98 *
99 * struct pt_ttys *pt_ttys_alloc(void);
100 *
101 * Allocate new minor number and pseudo-terminal entry. May sleep.
102 * New minor number is recorded in pt_minor field of the entry returned.
103 * This routine also initializes pt_minor and pt_state fields of the new
104 * pseudo-terminal and puts a pointer to it into ptms_slots array.
105 *
106 * struct pt_ttys *ptms_minor2ptty(minor_t minor)
107 *
108 * Find pt_ttys structure by minor number.
109 * Returns NULL when minor is out of range.
110 *
111 * int ptms_minor_valid(minor_t minor, uid_t *ruid, gid_t *rgid)
112 *
113 * Check if minor refers to an allocated pty in the current zone.
114 * Returns
115 * 0 if not allocated or not for this zone.
116 * 1 if an allocated pty in the current zone.
117 * Also returns owner of pty.
118 *
119 * int ptms_minor_exists(minor_t minor)
120 * Check if minor refers to an allocated pty (in any zone)
121 * Returns
122 * 0 if not an allocated pty
123 * 1 if an allocated pty
124 *
125 * void ptms_set_owner(minor_t minor, uid_t ruid, gid_t rgid)
126 *
127 * Sets the owner associated with a pty.
128 *
129 * void ptms_close(struct pt_ttys *pt, uint_t flags_to_clear);
130 *
131 * Clear flags_to_clear in pt and if no one owns it (PTMOPEN/PTSOPEN not
132 * set) free pt entry and corresponding slot.
133 *
134 * Tuneables and configuration:
135 *
136 * pt_cnt: minimum number of pseudo-terminals in the system. The system
137 * should provide at least this number of ptys (provided sufficient
138 * memory is available). It is different from the older semantics
139 * of pt_cnt meaning maximum number of ptys.
140 * Set to 0 by default.
141 *
142 * pt_max_pty: Maximum number of pseudo-terminals in the system. The system
143 * should not allocate more ptys than pt_max_pty (although, it may
144 * impose stricter maximum). Zero value means no user-defined
145 * maximum. This is intended to be used as "denial-of-service"
146 * protection.
147 * Set to 0 by default.
148 *
149 * Both pt_cnt and pt_max_pty may be modified during system lifetime
150 * with their semantics preserved.
151 *
152 * pt_init_cnt: Initial size of ptms_slots array. Set to NPTY_INITIAL.
153 *
154 * pt_ptyofmem: Approximate percentage of system memory that may be
155 * occupied by pty data structures. Initially set to NPTY_PERCENT.
156 * This variable is used once during initialization to estimate
157 * maximum number of ptys in the system. The actual maximum is
158 * determined as minimum of pt_max_pty and calculated value.
159 *
160 * pt_maxdelta: Maximum extension chunk of the slot table.
161 */
162
163
164
165 #include <sys/types.h>
166 #include <sys/param.h>
167 #include <sys/termios.h>
168 #include <sys/stream.h>
169 #include <sys/stropts.h>
170 #include <sys/kmem.h>
193 * Tuneable variables.
194 */
195 uint_t pt_cnt = 0; /* Minimum number of ptys */
196 size_t pt_max_pty = 0; /* Maximum number of ptys */
197 uint_t pt_init_cnt = NPTY_INITIAL; /* Initial number of ptms slots */
198 uint_t pt_pctofmem = NPTY_PERCENT; /* Percent of memory to use for ptys */
199 uint_t pt_maxdelta = PTY_MAXDELTA; /* Max increment for slot table size */
200
201 /* Other global variables */
202
203 kmutex_t ptms_lock; /* Global data access lock */
204
205 /*
206 * Slot array and its management variables
207 */
208 static struct pt_ttys **ptms_slots = NULL; /* Slots for actual pt structures */
209 static size_t ptms_nslots = 0; /* Size of slot array */
210 static size_t ptms_ptymax = 0; /* Maximum number of ptys */
211 static size_t ptms_inuse = 0; /* # of ptys currently allocated */
212
213 dev_info_t *pts_dip = NULL; /* set if slave is attached */
214
215 static struct kmem_cache *ptms_cache = NULL; /* pty cache */
216
217 static vmem_t *ptms_minor_arena = NULL; /* Arena for device minors */
218
219 static uint_t ptms_roundup(uint_t);
220 static int ptms_constructor(void *, void *, int);
221 static void ptms_destructor(void *, void *);
222 static minor_t ptms_grow(void);
223
224 /*
225 * Total size occupied by one pty. Each pty master/slave pair consumes one
226 * pointer for ptms_slots array, one pt_ttys structure and one empty message
227 * preallocated for pts close.
228 */
229
230 #define PTY_SIZE (sizeof (struct pt_ttys) + \
231 sizeof (struct pt_ttys *) + \
232 sizeof (dblk_t))
233
234 #ifdef DEBUG
235 int ptms_debug = 0;
236 #define PTMOD_ID 5
237 #endif
238
239 /*
240 * Clear all bits of x except the highest bit
241 */
242 #define truncate(x) ((x) <= 2 ? (x) : (1 << (highbit(x) - 1)))
243
244 /*
245 * Roundup the number to the nearest power of 2
246 */
247 static uint_t
278 /* Allocate integer space for minor numbers */
279 ptms_minor_arena = vmem_create("ptms_minor", (void *)1,
280 ptms_nslots, 1, NULL, NULL, NULL, 0,
281 VM_SLEEP | VMC_IDENTIFIER);
282
283 /*
284 * Calculate available number of ptys - how many ptys can we
285 * allocate in pt_pctofmem % of available memory. The value is
286 * rounded up to the nearest power of 2.
287 */
288 ptms_ptymax = ptms_roundup((pt_pctofmem * kmem_maxavail()) /
289 (100 * PTY_SIZE));
290 }
291 mutex_exit(&ptms_lock);
292 }
293
294 /*
295 * This routine attaches the pts dip.
296 */
297 int
298 ptms_attach_slave(void)
299 {
300 if (pts_dip == NULL && i_ddi_attach_pseudo_node("pts") == NULL)
301 return (-1);
302
303 ASSERT(pts_dip);
304 return (0);
305 }
306
307 /*
308 * Called from /dev fs. Checks if dip is attached,
309 * and if it is, returns its major number.
310 */
311 major_t
312 ptms_slave_attached(void)
313 {
314 major_t maj = DDI_MAJOR_T_NONE;
315
316 mutex_enter(&ptms_lock);
317 if (pts_dip)
318 maj = ddi_driver_major(pts_dip);
319 mutex_exit(&ptms_lock);
320
321 return (maj);
322 }
323
324 /*
325 * Allocate new minor number and pseudo-terminal entry. Returns the new entry or
326 * NULL if no memory or maximum number of entries reached.
327 */
328 struct pt_ttys *
329 pt_ttys_alloc(void)
330 {
331 minor_t dminor;
332 struct pt_ttys *pt = NULL;
|
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 * Copyright 2021 Oxide Computer Company
25 */
26
27 /*
28 * PSEUDO-TERMINAL COMMON DATA AND ROUTINES (PTM, PTS)
29 *
30 * This file contains global data and code shared between manager and
31 * subsidiary parts of the pseudo-terminal driver.
32 *
33 * Pseudo-terminals (or ptys for short) are allocated dynamically.
34 * ptys are put in the global ptms_slots array indexed by minor numbers.
35 *
36 * The slots array is initially small (of the size NPTY_MIN). When more ptys are
37 * needed than the slot array size, the larger slot array is allocated and all
38 * opened ptys move to the new one.
39 *
40 *
41 * RESOURCE ALLOCATION
42 *
43 * - pt_ttys structures are allocated via pt_ttys_alloc, which uses
44 * kmem_cache_alloc().
45 * - Minor number space is allocated via vmem_alloc() interface.
46 * - ptms_slots arrays are allocated via kmem_alloc().
47 *
48 * Minors start from 1 instead of 0, because vmem_alloc() returns 0 in case of
49 * failure. Also, in anticipation of removing the clone device interface to
50 * pseudo-terminal subsystem, minor 0 should not be used. (Potential future
51 * development).
52 *
53 * After the table slot size reaches pt_maxdelta, we stop 2^N extension
54 * algorithm and start extending the slot table size by pt_maxdelta.
55 *
56 * Device entries /dev/pts directory are created dynamically by the /dev
57 * filesystem. We no longer call ddi_create_minor_node() on behalf of the
58 * subsidiary driver. The /dev filesystem creates /dev/pts nodes based on the
59 * pt_ttys array.
60 *
61 *
62 * SYNCHRONIZATION
63 *
64 * All global data synchronization between ptm/pts is done via global ptms_lock
65 * mutex which is implicitly initialized by declaring it global.
66 *
67 * Individual fields of pt_ttys structure (except ptm_rdq, pts_rdq and
68 * pt_nullmsg) are protected by pt_ttys.pt_lock mutex.
69 *
70 * PT_ENTER_READ/PT_ENTER_WRITE are reference counter based read-write locks
71 * which allow reader locks to be reacquired by the same thread (usual
72 * reader/writer locks can't be used for that purpose since it is illegal for a
73 * thread to acquire a lock it already holds, even as a reader). The sole
74 * purpose of these macros is to guarantee that the peer queue will not
75 * disappear (due to closing peer) while it is used. It is safe to use
76 * PT_ENTER_READ/PT_EXIT_READ brackets across calls like putq/putnext (since
77 * they are not real locks but reference counts).
78 *
79 * PT_ENTER_WRITE/PT_EXIT_WRITE brackets are used ONLY in manager/subsidiary
80 * open/close paths to modify ptm_rdq and pts_rdq fields. These fields should
81 * be set to appropriate queues *after* qprocson() is called during open (to
82 * prevent peer from accessing the queue with incomplete plumbing) and set to
83 * NULL before qprocsoff() is called during close. Put and service procedures
84 * use PT_ENTER_READ/PT_EXIT_READ to prevent peer closes.
85 *
86 * The pt_nullmsg field is only used in open/close routines and is also
87 * protected by PT_ENTER_WRITE/PT_EXIT_WRITE brackets to avoid extra mutex
88 * holds.
89 *
90 *
91 * LOCK ORDERING
92 *
93 * If both ptms_lock and per-pty lock should be held, ptms_lock should always
94 * be entered first, followed by per-pty lock.
95 *
96 *
97 * GLOBAL FUNCTIONS
98 *
99 * void ptms_init(void);
100 *
101 * Called by pts/ptm _init entry points. It performes one-time
102 * initialization needed for both pts and ptm. This initialization is done
103 * here and not in ptms_initspace because all these data structures are not
104 * needed if pseudo-terminals are not used in the system.
105 *
106 * struct pt_ttys *pt_ttys_alloc(void);
107 *
108 * Allocate new minor number and pseudo-terminal entry. May sleep.
109 * New minor number is recorded in pt_minor field of the entry returned.
110 * This routine also initializes pt_minor and pt_state fields of the new
111 * pseudo-terminal and puts a pointer to it into ptms_slots array.
112 *
113 * struct pt_ttys *ptms_minor2ptty(minor_t minor)
114 *
115 * Find pt_ttys structure by minor number.
116 * Returns NULL when minor is out of range.
117 *
118 * int ptms_minor_valid(minor_t minor, uid_t *ruid, gid_t *rgid)
119 *
120 * Check if minor refers to an allocated pty in the current zone.
121 * Returns
122 * 0 if not allocated or not for this zone.
123 * 1 if an allocated pty in the current zone.
124 * Also returns owner of pty.
125 *
126 * int ptms_minor_exists(minor_t minor)
127 *
128 * Check if minor refers to an allocated pty (in any zone)
129 * Returns
130 * 0 if not an allocated pty
131 * 1 if an allocated pty
132 *
133 * void ptms_set_owner(minor_t minor, uid_t ruid, gid_t rgid)
134 *
135 * Sets the owner associated with a pty.
136 *
137 * void ptms_close(struct pt_ttys *pt, uint_t flags_to_clear);
138 *
139 * Clear flags_to_clear in pt and if no one owns it (PTMOPEN/PTSOPEN not
140 * set) free pt entry and corresponding slot.
141 *
142 *
143 * TUNEABLES AND CONFIGURATION
144 *
145 * pt_cnt: minimum number of pseudo-terminals in the system. The system
146 * should provide at least this number of ptys (provided sufficient
147 * memory is available). It is different from the older semantics
148 * of pt_cnt meaning maximum number of ptys.
149 * Set to 0 by default.
150 *
151 * pt_max_pty: Maximum number of pseudo-terminals in the system. The system
152 * should not allocate more ptys than pt_max_pty (although, it may
153 * impose stricter maximum). Zero value means no user-defined
154 * maximum. This is intended to be used as "denial-of-service"
155 * protection.
156 * Set to 0 by default.
157 *
158 * Both pt_cnt and pt_max_pty may be modified during system
159 * lifetime with their semantics preserved.
160 *
161 * pt_init_cnt: Initial size of ptms_slots array. Set to NPTY_INITIAL.
162 *
163 * pt_ptyofmem: Approximate percentage of system memory that may be
164 * occupied by pty data structures. Initially set to NPTY_PERCENT.
165 * This variable is used once during initialization to estimate
166 * maximum number of ptys in the system. The actual maximum is
167 * determined as minimum of pt_max_pty and calculated value.
168 *
169 * pt_maxdelta: Maximum extension chunk of the slot table.
170 */
171
172
173
174 #include <sys/types.h>
175 #include <sys/param.h>
176 #include <sys/termios.h>
177 #include <sys/stream.h>
178 #include <sys/stropts.h>
179 #include <sys/kmem.h>
202 * Tuneable variables.
203 */
204 uint_t pt_cnt = 0; /* Minimum number of ptys */
205 size_t pt_max_pty = 0; /* Maximum number of ptys */
206 uint_t pt_init_cnt = NPTY_INITIAL; /* Initial number of ptms slots */
207 uint_t pt_pctofmem = NPTY_PERCENT; /* Percent of memory to use for ptys */
208 uint_t pt_maxdelta = PTY_MAXDELTA; /* Max increment for slot table size */
209
210 /* Other global variables */
211
212 kmutex_t ptms_lock; /* Global data access lock */
213
214 /*
215 * Slot array and its management variables
216 */
217 static struct pt_ttys **ptms_slots = NULL; /* Slots for actual pt structures */
218 static size_t ptms_nslots = 0; /* Size of slot array */
219 static size_t ptms_ptymax = 0; /* Maximum number of ptys */
220 static size_t ptms_inuse = 0; /* # of ptys currently allocated */
221
222 dev_info_t *pts_dip = NULL; /* Set if subsidiary is attached */
223
224 static struct kmem_cache *ptms_cache = NULL; /* pty cache */
225
226 static vmem_t *ptms_minor_arena = NULL; /* Arena for device minors */
227
228 static uint_t ptms_roundup(uint_t);
229 static int ptms_constructor(void *, void *, int);
230 static void ptms_destructor(void *, void *);
231 static minor_t ptms_grow(void);
232
233 /*
234 * Total size occupied by one pty. Each pty manager/subsidiary pair consumes
235 * one pointer for ptms_slots array, one pt_ttys structure, and one empty
236 * message preallocated for pts close.
237 */
238
239 #define PTY_SIZE (sizeof (struct pt_ttys) + \
240 sizeof (struct pt_ttys *) + \
241 sizeof (dblk_t))
242
243 #ifdef DEBUG
244 int ptms_debug = 0;
245 #define PTMOD_ID 5
246 #endif
247
248 /*
249 * Clear all bits of x except the highest bit
250 */
251 #define truncate(x) ((x) <= 2 ? (x) : (1 << (highbit(x) - 1)))
252
253 /*
254 * Roundup the number to the nearest power of 2
255 */
256 static uint_t
287 /* Allocate integer space for minor numbers */
288 ptms_minor_arena = vmem_create("ptms_minor", (void *)1,
289 ptms_nslots, 1, NULL, NULL, NULL, 0,
290 VM_SLEEP | VMC_IDENTIFIER);
291
292 /*
293 * Calculate available number of ptys - how many ptys can we
294 * allocate in pt_pctofmem % of available memory. The value is
295 * rounded up to the nearest power of 2.
296 */
297 ptms_ptymax = ptms_roundup((pt_pctofmem * kmem_maxavail()) /
298 (100 * PTY_SIZE));
299 }
300 mutex_exit(&ptms_lock);
301 }
302
303 /*
304 * This routine attaches the pts dip.
305 */
306 int
307 ptms_attach_subsidiary(void)
308 {
309 if (pts_dip == NULL && i_ddi_attach_pseudo_node("pts") == NULL)
310 return (-1);
311
312 ASSERT(pts_dip);
313 return (0);
314 }
315
316 /*
317 * Called from /dev fs. Checks if dip is attached,
318 * and if it is, returns its major number.
319 */
320 major_t
321 ptms_subsidiary_attached(void)
322 {
323 major_t maj = DDI_MAJOR_T_NONE;
324
325 mutex_enter(&ptms_lock);
326 if (pts_dip)
327 maj = ddi_driver_major(pts_dip);
328 mutex_exit(&ptms_lock);
329
330 return (maj);
331 }
332
333 /*
334 * Allocate new minor number and pseudo-terminal entry. Returns the new entry or
335 * NULL if no memory or maximum number of entries reached.
336 */
337 struct pt_ttys *
338 pt_ttys_alloc(void)
339 {
340 minor_t dminor;
341 struct pt_ttys *pt = NULL;
|