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