xref: /illumos-gate/usr/src/uts/common/io/pts.c (revision 1fa2a664)
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 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
26 /*	  All Rights Reserved	*/
27 
28 /*
29  * Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
30  * Copyright 2021 Oxide Computer Company
31  */
32 
33 /*
34  * PSEUDO-TERMINAL SUBSIDIARY DRIVER (PTS)
35  *
36  * The pseudo-terminal subsystem simulates a terminal connection, where the
37  * manager side represents the terminal and the subsidiary represents the user
38  * process's special device end point.  The manager device is set up as a
39  * cloned device where its major device number is the major for the clone
40  * device and its minor device number is the major for the ptm driver.  There
41  * are no nodes in the file system for manager devices.  The manager pseudo
42  * driver is opened using the open(2) system call with /dev/ptmx as the device
43  * parameter.  The clone open finds the next available minor device for the ptm
44  * major device.
45  *
46  * A manager device is available only if it and its corresponding subsidiary
47  * device are not already open.  When the manager device is opened, the
48  * corresponding subsidiary device is automatically locked out.  Only one open
49  * is allowed on a manager device.  Multiple opens are allowed on the
50  * subsidiary device.  After both the manager and subsidiary have been opened,
51  * the user has two file descriptors which are the end points of a full duplex
52  * connection composed of two streams which are automatically connected at the
53  * manager and subsidiary drivers.  The user may then push modules onto either
54  * side of the stream pair.
55  *
56  * The manager and subsidiary drivers pass all messages to their adjacent
57  * queues.  Only the M_FLUSH needs some processing.  Because the read queue of
58  * one side is connected to the write queue of the other, the FLUSHR flag is
59  * changed to the FLUSHW flag and vice versa.  When the manager device is
60  * closed an M_HANGUP message is sent to the subsidiary device which will
61  * render the device unusable.  The process on the subsidiary side gets the EIO
62  * when attempting to write on that stream but it will be able to read any data
63  * remaining on the stream head read queue.  When all the data has been read,
64  * read() returns 0 indicating that the stream can no longer be used.  On the
65  * last close of the subsidiary device, a 0-length message is sent to the
66  * manager device.  When the application on the manager side issues a read() or
67  * getmsg() and 0 is returned, the user of the manager device decides whether
68  * to issue a close() that dismantles the pseudo-terminal subsystem.  If the
69  * manager device is not closed, the pseudo-tty subsystem will be available to
70  * another user to open the subsidiary device.
71  *
72  *
73  * SYNCHRONIZATION
74  *
75  * All global data synchronization between ptm/pts is done via global ptms_lock
76  * mutex which is initialized at system boot time from ptms_initspace (called
77  * from space.c).
78  *
79  * Individual fields of pt_ttys structure (except ptm_rdq, pts_rdq and
80  * pt_nullmsg) are protected by pt_ttys.pt_lock mutex.
81  *
82  * PT_ENTER_READ/PT_ENTER_WRITE are reference counter based read-write locks
83  * which allow reader locks to be reacquired by the same thread (usual
84  * reader/writer locks can't be used for that purpose since it is illegal for a
85  * thread to acquire a lock it already holds, even as a reader).  The sole
86  * purpose of these macros is to guarantee that the peer queue will not
87  * disappear (due to closing peer) while it is used.  It is safe to use
88  * PT_ENTER_READ/PT_EXIT_READ brackets across calls like putq/putnext (since
89  * they are not real locks but reference counts).
90  *
91  * PT_ENTER_WRITE/PT_EXIT_WRITE brackets are used ONLY in manager/subsidiary
92  * open/close paths to modify ptm_rdq and pts_rdq fields.  These fields should
93  * be set to appropriate queues *after* qprocson() is called during open (to
94  * prevent peer from accessing the queue with incomplete plumbing) and set to
95  * NULL before qprocsoff() is called during close.
96  *
97  * The pt_nullmsg field is only used in open/close routines and it is also
98  * protected by PT_ENTER_WRITE/PT_EXIT_WRITE brackets to avoid extra mutex
99  * holds.
100  *
101  *
102  * LOCK ORDERING
103  *
104  * If both ptms_lock and per-pty lock should be held, ptms_lock should always
105  * be entered first, followed by per-pty lock.
106  *
107  * See ptms.h, ptm.c and ptms_conf.c fore more information.
108  */
109 
110 #include <sys/types.h>
111 #include <sys/param.h>
112 #include <sys/sysmacros.h>
113 #include <sys/stream.h>
114 #include <sys/stropts.h>
115 #include <sys/strsubr.h>
116 #include <sys/stat.h>
117 #include <sys/errno.h>
118 #include <sys/debug.h>
119 #include <sys/cmn_err.h>
120 #include <sys/ptms.h>
121 #include <sys/systm.h>
122 #include <sys/modctl.h>
123 #include <sys/conf.h>
124 #include <sys/ddi.h>
125 #include <sys/sunddi.h>
126 #include <sys/cred.h>
127 #include <sys/zone.h>
128 
129 #ifdef DEBUG
130 int pts_debug = 0;
131 #define	DBG(a)	 if (pts_debug) cmn_err(CE_NOTE, a)
132 #else
133 #define	DBG(a)
134 #endif
135 
136 static int ptsopen(queue_t *, dev_t *, int, int, cred_t *);
137 static int ptsclose(queue_t *, int, cred_t *);
138 static int ptswput(queue_t *, mblk_t *);
139 static int ptsrsrv(queue_t *);
140 static int ptswsrv(queue_t *);
141 
142 static struct module_info pts_info = {
143 	0xface,
144 	"pts",
145 	0,
146 	_TTY_BUFSIZ,
147 	_TTY_BUFSIZ,
148 	128
149 };
150 
151 static struct qinit ptsrint = {
152 	NULL,
153 	ptsrsrv,
154 	ptsopen,
155 	ptsclose,
156 	NULL,
157 	&pts_info,
158 	NULL
159 };
160 
161 static struct qinit ptswint = {
162 	ptswput,
163 	ptswsrv,
164 	NULL,
165 	NULL,
166 	NULL,
167 	&pts_info,
168 	NULL
169 };
170 
171 static struct streamtab ptsinfo = {
172 	&ptsrint,
173 	&ptswint,
174 	NULL,
175 	NULL
176 };
177 
178 static int pts_devinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
179 static int pts_attach(dev_info_t *, ddi_attach_cmd_t);
180 static int pts_detach(dev_info_t *, ddi_detach_cmd_t);
181 
182 #define	PTS_CONF_FLAG	(D_NEW | D_MP)
183 
184 /*
185  * this will define (struct cb_ops cb_pts_ops) and (struct dev_ops pts_ops)
186  */
187 DDI_DEFINE_STREAM_OPS(pts_ops, nulldev, nulldev,	\
188     pts_attach, pts_detach, nodev,			\
189     pts_devinfo, PTS_CONF_FLAG, &ptsinfo, ddi_quiesce_not_supported);
190 
191 /*
192  * Module linkage information for the kernel.
193  */
194 
195 static struct modldrv modldrv = {
196 	&mod_driverops,
197 	"Pseudo-Terminal Subsidiary Driver",
198 	&pts_ops,
199 };
200 
201 static struct modlinkage modlinkage = {
202 	MODREV_1,
203 	&modldrv,
204 	NULL
205 };
206 
207 int
_init(void)208 _init(void)
209 {
210 	int rc;
211 
212 	if ((rc = mod_install(&modlinkage)) == 0)
213 		ptms_init();
214 	return (rc);
215 }
216 
217 
218 int
_fini(void)219 _fini(void)
220 {
221 	return (mod_remove(&modlinkage));
222 }
223 
224 int
_info(struct modinfo * modinfop)225 _info(struct modinfo *modinfop)
226 {
227 	return (mod_info(&modlinkage, modinfop));
228 }
229 
230 static int
pts_attach(dev_info_t * devi,ddi_attach_cmd_t cmd)231 pts_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
232 {
233 	if (cmd != DDI_ATTACH)
234 		return (DDI_FAILURE);
235 
236 	mutex_enter(&ptms_lock);
237 	pts_dip = devi;
238 	mutex_exit(&ptms_lock);
239 
240 	return (DDI_SUCCESS);
241 }
242 
243 static int
pts_detach(dev_info_t * devi,ddi_detach_cmd_t cmd)244 pts_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
245 {
246 	if (cmd != DDI_DETACH)
247 		return (DDI_FAILURE);
248 
249 	/*
250 	 * For now, pts cannot be detached.
251 	 */
252 	return (DDI_FAILURE);
253 }
254 
255 static int
pts_devinfo(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)256 pts_devinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
257     void **result)
258 {
259 	int error;
260 
261 	switch (infocmd) {
262 	case DDI_INFO_DEVT2DEVINFO:
263 		if (pts_dip == NULL) {
264 			error = DDI_FAILURE;
265 		} else {
266 			*result = (void *)pts_dip;
267 			error = DDI_SUCCESS;
268 		}
269 		break;
270 	case DDI_INFO_DEVT2INSTANCE:
271 		*result = (void *)0;
272 		error = DDI_SUCCESS;
273 		break;
274 	default:
275 		error = DDI_FAILURE;
276 	}
277 	return (error);
278 }
279 
280 /* ARGSUSED */
281 /*
282  * Open the subsidiary device. Reject a clone open and do not allow the
283  * driver to be pushed. If the subsidiary/manager pair is locked or if
284  * the manager is not open, return EACCESS.
285  * Upon success, store the write queue pointer in private data and
286  * set the PTSOPEN bit in the pt_state field.
287  */
288 static int
ptsopen(queue_t * rqp,dev_t * devp,int oflag,int sflag,cred_t * credp)289 ptsopen(
290 	queue_t *rqp,		/* pointer to the read side queue */
291 	dev_t   *devp,		/* pointer to stream tail's dev */
292 	int	oflag,		/* the user open(2) supplied flags */
293 	int	sflag,		/* open state flag */
294 	cred_t  *credp)		/* credentials */
295 {
296 	struct pt_ttys	*ptsp;
297 	mblk_t		*mp;
298 	mblk_t		*mop;	/* ptr to a setopts message block */
299 	minor_t		dminor = getminor(*devp);
300 	struct stroptions *sop;
301 
302 	DDBG("entering ptsopen(%d)", dminor);
303 
304 	if (sflag != 0) {
305 		return (EINVAL);
306 	}
307 
308 	mutex_enter(&ptms_lock);
309 	ptsp = ptms_minor2ptty(dminor);
310 
311 	if (ptsp == NULL) {
312 		mutex_exit(&ptms_lock);
313 		return (ENXIO);
314 	}
315 	mutex_enter(&ptsp->pt_lock);
316 
317 	/*
318 	 * Prevent opens from zones other than the one blessed by ptm.  We
319 	 * can't even allow the global zone to open all pts's, as it would
320 	 * otherwise inproperly be able to claim pts's already opened by zones.
321 	 */
322 	if (ptsp->pt_zoneid != getzoneid()) {
323 		mutex_exit(&ptsp->pt_lock);
324 		mutex_exit(&ptms_lock);
325 		return (EPERM);
326 	}
327 
328 	/*
329 	 * Allow reopen of this device.
330 	 */
331 	if (rqp->q_ptr != NULL) {
332 		ASSERT(rqp->q_ptr == ptsp);
333 		ASSERT(ptsp->pts_rdq == rqp);
334 		mutex_exit(&ptsp->pt_lock);
335 		mutex_exit(&ptms_lock);
336 		return (0);
337 	}
338 
339 	DDBGP("ptsopen: p = %p\n", (uintptr_t)ptsp);
340 	DDBG("ptsopen: state = %x\n", ptsp->pt_state);
341 
342 	ASSERT(ptsp->pt_minor == dminor);
343 
344 	if ((ptsp->pt_state & PTLOCK) || !(ptsp->pt_state & PTMOPEN)) {
345 		mutex_exit(&ptsp->pt_lock);
346 		mutex_exit(&ptms_lock);
347 		return (EAGAIN);
348 	}
349 
350 	/*
351 	 * if already open, simply return...
352 	 */
353 	if (ptsp->pt_state & PTSOPEN) {
354 		ASSERT(rqp->q_ptr == ptsp);
355 		ASSERT(ptsp->pts_rdq == rqp);
356 		mutex_exit(&ptsp->pt_lock);
357 		mutex_exit(&ptms_lock);
358 		return (0);
359 	}
360 
361 	/*
362 	 * Allocate message block for setting stream head options.
363 	 */
364 	if ((mop = allocb(sizeof (struct stroptions), BPRI_MED)) == NULL) {
365 		mutex_exit(&ptsp->pt_lock);
366 		mutex_exit(&ptms_lock);
367 		return (ENOMEM);
368 	}
369 
370 	/*
371 	 * Subsidiary should send zero-length message to a manager when it is
372 	 * closing.  If memory is low at that time, manager will not detect
373 	 * subsidiary closes, this pty will not be deallocated.  So,
374 	 * preallocate this zero-length message block early.
375 	 */
376 	if ((mp = allocb(0, BPRI_MED)) == NULL) {
377 		mutex_exit(&ptsp->pt_lock);
378 		mutex_exit(&ptms_lock);
379 		freemsg(mop);
380 		return (ENOMEM);
381 	}
382 
383 	ptsp->pt_state |= PTSOPEN;
384 
385 	WR(rqp)->q_ptr = rqp->q_ptr = ptsp;
386 
387 	mutex_exit(&ptsp->pt_lock);
388 	mutex_exit(&ptms_lock);
389 
390 	if (ptsp->pt_state & PTSTTY)
391 		STREAM(rqp)->sd_flag |= STRXPG4TTY;
392 
393 	qprocson(rqp);
394 
395 	/*
396 	 * After qprocson pts driver is fully plumbed into the stream and can
397 	 * send/receive messages.  Setting pts_rdq will allow manager side to
398 	 * send messages to the subsidiary.  This setting can't occur before
399 	 * qprocson() is finished because subsidiary is not ready to process
400 	 * them.
401 	 */
402 	PT_ENTER_WRITE(ptsp);
403 	ptsp->pts_rdq = rqp;
404 	ASSERT(ptsp->pt_nullmsg == NULL);
405 	ptsp->pt_nullmsg = mp;
406 	PT_EXIT_WRITE(ptsp);
407 
408 	/*
409 	 * set up hi/lo water marks on stream head read queue
410 	 * and add controlling tty if not set
411 	 */
412 
413 	mop->b_datap->db_type = M_SETOPTS;
414 	mop->b_wptr += sizeof (struct stroptions);
415 	sop = (struct stroptions *)mop->b_rptr;
416 	sop->so_flags = SO_HIWAT | SO_LOWAT | SO_ISTTY;
417 	sop->so_hiwat = _TTY_BUFSIZ;
418 	sop->so_lowat = 256;
419 	putnext(rqp, mop);
420 
421 	return (0);
422 }
423 
424 /*
425  * Find the address to private data identifying the subsidiary's write queue.
426  * Send a 0-length msg up the subsidiary's read queue to designate the manager
427  * is closing.  Uattach the manager from the subsidiary by nulling out
428  * manager's write queue field in private data.
429  */
430 static int
ptsclose(queue_t * rqp,int flag,cred_t * credp)431 ptsclose(queue_t *rqp, int flag, cred_t *credp)
432 {
433 	struct pt_ttys	*ptsp;
434 	queue_t *wqp;
435 	mblk_t	*mp;
436 	mblk_t	*bp;
437 
438 	/*
439 	 * q_ptr should never be NULL in the close routine and it is checked in
440 	 * DEBUG kernel by ASSERT. For non-DEBUG kernel the attempt is made to
441 	 * behave gracefully.
442 	 */
443 	ASSERT(rqp->q_ptr != NULL);
444 	if (rqp->q_ptr == NULL) {
445 		qprocsoff(rqp);
446 		return (0);
447 	}
448 
449 	ptsp = (struct pt_ttys *)rqp->q_ptr;
450 
451 	/*
452 	 * Subsidiary is going to close and doesn't want any new messages
453 	 * coming from the manager side, so set pts_rdq to NULL.  This should
454 	 * be done before call to qprocsoff() since subsidiary can't process
455 	 * additional messages from the manager after qprocsoff is called.
456 	 */
457 	PT_ENTER_WRITE(ptsp);
458 	mp = ptsp->pt_nullmsg;
459 	ptsp->pt_nullmsg = NULL;
460 	ptsp->pts_rdq = NULL;
461 	PT_EXIT_WRITE(ptsp);
462 
463 	/*
464 	 * Drain the ouput
465 	 */
466 	wqp = WR(rqp);
467 	PT_ENTER_READ(ptsp);
468 	while ((bp = getq(wqp)) != NULL) {
469 		if (ptsp->ptm_rdq) {
470 			putnext(ptsp->ptm_rdq, bp);
471 		} else if (bp->b_datap->db_type == M_IOCTL) {
472 			bp->b_datap->db_type = M_IOCNAK;
473 			freemsg(bp->b_cont);
474 			bp->b_cont = NULL;
475 			qreply(wqp, bp);
476 		} else {
477 			freemsg(bp);
478 		}
479 	}
480 	/*
481 	 * qenable manager side write queue so that it can flush its messages
482 	 * as subsidiarys's read queue is going away:
483 	 */
484 	if (ptsp->ptm_rdq) {
485 		if (mp)
486 			putnext(ptsp->ptm_rdq, mp);
487 		else
488 			qenable(WR(ptsp->ptm_rdq));
489 	} else
490 		freemsg(mp);
491 	PT_EXIT_READ(ptsp);
492 
493 	qprocsoff(rqp);
494 
495 	rqp->q_ptr = NULL;
496 	WR(rqp)->q_ptr = NULL;
497 
498 	ptms_close(ptsp, PTSOPEN | PTSTTY);
499 
500 	return (0);
501 }
502 
503 
504 /*
505  * The wput procedure will only handle flush messages.  All other messages are
506  * queued and the write side service procedure sends them off to the manager
507  * side.
508  */
509 static int
ptswput(queue_t * qp,mblk_t * mp)510 ptswput(queue_t *qp, mblk_t *mp)
511 {
512 	struct pt_ttys *ptsp;
513 	struct iocblk  *iocp;
514 	unsigned char type = mp->b_datap->db_type;
515 
516 	DBG(("entering ptswput\n"));
517 	ASSERT(qp->q_ptr);
518 
519 	ptsp = (struct pt_ttys *)qp->q_ptr;
520 	PT_ENTER_READ(ptsp);
521 	if (ptsp->ptm_rdq == NULL) {
522 		DBG(("in write put proc but no manager\n"));
523 		/*
524 		 * NAK ioctl as subsidiary side read queue is gone.
525 		 * Or else free the message.
526 		 */
527 		if (mp->b_datap->db_type == M_IOCTL) {
528 			mp->b_datap->db_type = M_IOCNAK;
529 			freemsg(mp->b_cont);
530 			mp->b_cont = NULL;
531 			qreply(qp, mp);
532 		} else
533 			freemsg(mp);
534 		PT_EXIT_READ(ptsp);
535 		return (0);
536 	}
537 
538 	if (type >= QPCTL) {
539 		switch (type) {
540 
541 		/*
542 		 * if write queue request, flush subsidiary's write
543 		 * queue and send FLUSHR to ptm. If read queue
544 		 * request, send FLUSHR to ptm.
545 		 */
546 		case M_FLUSH:
547 		DBG(("pts got flush request\n"));
548 		if (*mp->b_rptr & FLUSHW) {
549 
550 			DBG(("got FLUSHW, flush pts write Q\n"));
551 			if (*mp->b_rptr & FLUSHBAND)
552 				/*
553 				 * if it is a FLUSHBAND, do flushband.
554 				 */
555 				flushband(qp, *(mp->b_rptr + 1), FLUSHDATA);
556 			else
557 				flushq(qp, FLUSHDATA);
558 
559 			*mp->b_rptr &= ~FLUSHW;
560 			if ((*mp->b_rptr & FLUSHR) == 0) {
561 				/*
562 				 * FLUSHW only. Change to FLUSHR and putnext
563 				 * to ptm, then we are done.
564 				 */
565 				*mp->b_rptr |= FLUSHR;
566 				if (ptsp->ptm_rdq)
567 					putnext(ptsp->ptm_rdq, mp);
568 				break;
569 			} else {
570 				mblk_t *nmp;
571 
572 				/* It is a FLUSHRW. Duplicate the mblk */
573 				nmp = copyb(mp);
574 				if (nmp) {
575 					/*
576 					 * Change FLUSHW to FLUSHR before
577 					 * putnext to ptm.
578 					 */
579 					DBG(("putnext nmp(FLUSHR) to ptm\n"));
580 					*nmp->b_rptr |= FLUSHR;
581 					if (ptsp->ptm_rdq)
582 						putnext(ptsp->ptm_rdq, nmp);
583 				}
584 			}
585 		}
586 		/*
587 		 * Since the packet module will toss any M_FLUSHES sent to the
588 		 * manager's stream head read queue, we simply turn it around
589 		 * here.
590 		 */
591 		if (*mp->b_rptr & FLUSHR) {
592 			ASSERT(RD(qp)->q_first == NULL);
593 			DBG(("qreply(qp) turning FLUSHR around\n"));
594 			qreply(qp, mp);
595 		} else {
596 			freemsg(mp);
597 		}
598 		break;
599 
600 		case M_READ:
601 		/* Caused by ldterm - can not pass to manager */
602 		freemsg(mp);
603 		break;
604 
605 		default:
606 		if (ptsp->ptm_rdq)
607 			putnext(ptsp->ptm_rdq, mp);
608 		break;
609 		}
610 		PT_EXIT_READ(ptsp);
611 		return (0);
612 	}
613 
614 	switch (type) {
615 
616 	case M_IOCTL:
617 		/*
618 		 * For case PTSSTTY set the flag PTSTTY and ACK
619 		 * the ioctl so that the user program can push
620 		 * the associated modules to get tty semantics.
621 		 * See bugid 4025044
622 		 */
623 		iocp = (struct iocblk *)mp->b_rptr;
624 		switch (iocp->ioc_cmd) {
625 		default:
626 			break;
627 
628 		case PTSSTTY:
629 			if (ptsp->pt_state & PTSTTY) {
630 				mp->b_datap->db_type = M_IOCNAK;
631 				iocp->ioc_error = EEXIST;
632 			} else {
633 				mp->b_datap->db_type = M_IOCACK;
634 				mutex_enter(&ptsp->pt_lock);
635 				ptsp->pt_state |= PTSTTY;
636 				mutex_exit(&ptsp->pt_lock);
637 				iocp->ioc_error = 0;
638 			}
639 			iocp->ioc_count = 0;
640 			qreply(qp, mp);
641 			PT_EXIT_READ(ptsp);
642 			return (0);
643 		}
644 		/* FALLTHROUGH */
645 	default:
646 		/*
647 		 * send other messages to the manager
648 		 */
649 		DBG(("put msg on subsidiary's write queue\n"));
650 		(void) putq(qp, mp);
651 		break;
652 	}
653 
654 	PT_EXIT_READ(ptsp);
655 	DBG(("return from ptswput()\n"));
656 	return (0);
657 }
658 
659 
660 /*
661  * Enable the write side of the manager.  This triggers the manager to send any
662  * messages queued on its write side to the read side of this subsidiary.
663  */
664 static int
ptsrsrv(queue_t * qp)665 ptsrsrv(queue_t *qp)
666 {
667 	struct pt_ttys *ptsp;
668 
669 	DBG(("entering ptsrsrv\n"));
670 	ASSERT(qp->q_ptr);
671 
672 	ptsp = (struct pt_ttys *)qp->q_ptr;
673 	PT_ENTER_READ(ptsp);
674 	if (ptsp->ptm_rdq == NULL) {
675 		DBG(("in read srv proc but no manager\n"));
676 		PT_EXIT_READ(ptsp);
677 		return (0);
678 	}
679 	qenable(WR(ptsp->ptm_rdq));
680 	PT_EXIT_READ(ptsp);
681 	DBG(("leaving ptsrsrv\n"));
682 	return (0);
683 }
684 
685 /*
686  * If there are messages on this queue that can be sent to manager, send them
687  * via putnext().  Otherwise, if queued messages cannot be sent, leave them on
688  * this queue.  If priority messages on this queue, send them to manager no
689  * matter what.
690  */
691 static int
ptswsrv(queue_t * qp)692 ptswsrv(queue_t *qp)
693 {
694 	struct pt_ttys *ptsp;
695 	queue_t *ptm_rdq;
696 	mblk_t *mp;
697 
698 	DBG(("entering ptswsrv\n"));
699 	ASSERT(qp->q_ptr);
700 
701 	ptsp = (struct pt_ttys *)qp->q_ptr;
702 	PT_ENTER_READ(ptsp);
703 	if (ptsp->ptm_rdq == NULL) {
704 		DBG(("in write srv proc but no manager\n"));
705 		/*
706 		 * Free messages on the write queue and send NAK for any
707 		 * M_IOCTL type messages to wakeup the user process waiting for
708 		 * ACK/NAK from the ioctl invocation
709 		 */
710 		while ((mp = getq(qp)) != NULL) {
711 			if (mp->b_datap->db_type == M_IOCTL) {
712 				mp->b_datap->db_type = M_IOCNAK;
713 				freemsg(mp->b_cont);
714 				mp->b_cont = NULL;
715 				qreply(qp, mp);
716 			} else
717 				freemsg(mp);
718 		}
719 		PT_EXIT_READ(ptsp);
720 		return (0);
721 	} else {
722 		ptm_rdq = ptsp->ptm_rdq;
723 	}
724 
725 	/*
726 	 * While there are messages on this write queue...
727 	 */
728 	while ((mp = getq(qp)) != NULL) {
729 		/*
730 		 * If this is not a control message and we cannot put messages
731 		 * on the manager's read queue, put it back on this queue.
732 		 */
733 		if (mp->b_datap->db_type <= QPCTL &&
734 		    !bcanputnext(ptm_rdq, mp->b_band)) {
735 			DBG(("put msg. back on Q\n"));
736 			(void) putbq(qp, mp);
737 			break;
738 		}
739 		/*
740 		 * Otherwise, send the message up manager's stream:
741 		 */
742 		DBG(("send message to manager\n"));
743 		putnext(ptm_rdq, mp);
744 	}
745 	DBG(("leaving ptswsrv\n"));
746 	PT_EXIT_READ(ptsp);
747 	return (0);
748 }
749