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