xref: /titanic_51/usr/src/uts/common/io/zcons.c (revision c2580b931007758eab8cb5ae8726ebe1588e259b)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * Zone Console Driver.
31  *
32  * This driver, derived from the pts/ptm drivers, is the pseudo console driver
33  * for system zones.  Its implementation is straightforward.  Each instance
34  * of the driver represents a global-zone/local-zone pair (this maps in a
35  * straightforward way to the commonly used terminal notion of "master side"
36  * and "slave side", and we use that terminology throughout).
37  *
38  * Instances of zcons are onlined as children of /pseudo/zconsnex@1/
39  * by zoneadmd in userland, using the devctl framework; thus the driver
40  * does not need to maintain any sort of "admin" node.
41  *
42  * The driver shuttles I/O from master side to slave side and back.  In a break
43  * from the pts/ptm semantics, if one side is not open, I/O directed towards
44  * it will simply be discarded.  This is so that if zoneadmd is not holding
45  * the master side console open (i.e. it has died somehow), processes in
46  * the zone do not experience any errors and I/O to the console does not
47  * hang.
48  *
49  * TODO: we may want to revisit the other direction; i.e. we may want
50  * zoneadmd to be able to detect whether no zone processes are holding the
51  * console open, an unusual situation.
52  */
53 
54 #include <sys/types.h>
55 #include <sys/cmn_err.h>
56 #include <sys/conf.h>
57 #include <sys/cred.h>
58 #include <sys/ddi.h>
59 #include <sys/debug.h>
60 #include <sys/devops.h>
61 #include <sys/errno.h>
62 #include <sys/file.h>
63 #include <sys/modctl.h>
64 #include <sys/param.h>
65 #include <sys/stat.h>
66 #include <sys/stream.h>
67 #include <sys/stropts.h>
68 #include <sys/strsun.h>
69 #include <sys/sunddi.h>
70 #include <sys/sysmacros.h>
71 #include <sys/systm.h>
72 #include <sys/types.h>
73 #include <sys/zcons.h>
74 
75 static int zc_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
76 static int zc_attach(dev_info_t *, ddi_attach_cmd_t);
77 static int zc_detach(dev_info_t *, ddi_detach_cmd_t);
78 
79 static int zc_open(queue_t *, dev_t *, int, int, cred_t *);
80 static int zc_close(queue_t *, int, cred_t *);
81 static void zc_wput(queue_t *, mblk_t *);
82 static void zc_rsrv(queue_t *);
83 static void zc_wsrv(queue_t *);
84 
85 /*
86  * The instance number is encoded in the dev_t in the minor number; the lowest
87  * bit of the minor number is used to track the master vs. slave side of the
88  * virtual console.  The rest of the bits in the minor number are the instance.
89  */
90 #define	ZC_MASTER_MINOR	0
91 #define	ZC_SLAVE_MINOR	1
92 
93 #define	ZC_INSTANCE(x)	(getminor((x)) >> 1)
94 #define	ZC_NODE(x)	(getminor((x)) & 0x01)
95 
96 int zcons_debug = 0;
97 #define	DBG(a)   if (zcons_debug) cmn_err(CE_NOTE, a)
98 #define	DBG1(a, b)   if (zcons_debug) cmn_err(CE_NOTE, a, b)
99 
100 
101 /*
102  * Zone Console Pseudo Terminal Module: stream data structure definitions
103  */
104 static struct module_info zc_info = {
105 	31337,	/* c0z we r hAx0rs */
106 	"zcons",
107 	0,
108 	INFPSZ,
109 	2048,
110 	128
111 };
112 
113 static struct qinit zc_rinit = {
114 	NULL,
115 	(int (*)()) zc_rsrv,
116 	zc_open,
117 	zc_close,
118 	NULL,
119 	&zc_info,
120 	NULL
121 };
122 
123 static struct qinit zc_winit = {
124 	(int (*)()) zc_wput,
125 	(int (*)()) zc_wsrv,
126 	NULL,
127 	NULL,
128 	NULL,
129 	&zc_info,
130 	NULL
131 };
132 
133 static struct streamtab zc_tab_info = {
134 	&zc_rinit,
135 	&zc_winit,
136 	NULL,
137 	NULL
138 };
139 
140 #define	ZC_CONF_FLAG	(D_MP | D_MTQPAIR | D_MTOUTPERIM | D_MTOCEXCL)
141 
142 /*
143  * this will define (struct cb_ops cb_zc_ops) and (struct dev_ops zc_ops)
144  */
145 DDI_DEFINE_STREAM_OPS(zc_ops, nulldev, nulldev,	zc_attach, zc_detach, nodev, \
146 	zc_getinfo, ZC_CONF_FLAG, &zc_tab_info);
147 
148 /*
149  * Module linkage information for the kernel.
150  */
151 
152 static struct modldrv modldrv = {
153 	&mod_driverops, /* Type of module.  This one is a pseudo driver */
154 	"Zone console driver 'zcons' %I%",
155 	&zc_ops		/* driver ops */
156 };
157 
158 static struct modlinkage modlinkage = {
159 	MODREV_1,
160 	&modldrv,
161 	NULL
162 };
163 
164 typedef struct zc_state {
165 	dev_info_t *zc_devinfo;
166 	queue_t *zc_master_rdq;
167 	queue_t *zc_slave_rdq;
168 	int zc_state;
169 } zc_state_t;
170 
171 #define	ZC_STATE_MOPEN	0x01
172 #define	ZC_STATE_SOPEN	0x02
173 
174 static void *zc_soft_state;
175 
176 int
177 _init(void)
178 {
179 	int err;
180 
181 	if ((err = ddi_soft_state_init(&zc_soft_state,
182 	    sizeof (zc_state_t), 0)) != 0) {
183 		return (err);
184 	}
185 
186 	if ((err = mod_install(&modlinkage)) != 0)
187 		ddi_soft_state_fini(zc_soft_state);
188 
189 	return (err);
190 }
191 
192 
193 int
194 _fini(void)
195 {
196 	int err;
197 
198 	if ((err = mod_remove(&modlinkage)) != 0) {
199 		return (err);
200 	}
201 
202 	ddi_soft_state_fini(&zc_soft_state);
203 	return (0);
204 }
205 
206 int
207 _info(struct modinfo *modinfop)
208 {
209 	return (mod_info(&modlinkage, modinfop));
210 }
211 
212 static int
213 zc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
214 {
215 	zc_state_t *zcs;
216 	int instance;
217 
218 	if (cmd != DDI_ATTACH)
219 		return (DDI_FAILURE);
220 
221 	instance = ddi_get_instance(dip);
222 	if (ddi_soft_state_zalloc(zc_soft_state, instance) != DDI_SUCCESS)
223 		return (DDI_FAILURE);
224 
225 	if ((ddi_create_minor_node(dip, ZCONS_SLAVE_NAME, S_IFCHR,
226 	    instance << 1 | ZC_SLAVE_MINOR, DDI_PSEUDO, 0) == DDI_FAILURE) ||
227 	    (ddi_create_minor_node(dip, ZCONS_MASTER_NAME, S_IFCHR,
228 	    instance << 1 | ZC_MASTER_MINOR, DDI_PSEUDO, 0) == DDI_FAILURE)) {
229 		ddi_remove_minor_node(dip, NULL);
230 		ddi_soft_state_free(zc_soft_state, instance);
231 		return (DDI_FAILURE);
232 	}
233 
234 	if ((zcs = ddi_get_soft_state(zc_soft_state, instance)) == NULL) {
235 		ddi_remove_minor_node(dip, NULL);
236 		ddi_soft_state_free(zc_soft_state, instance);
237 		return (DDI_FAILURE);
238 	}
239 	zcs->zc_devinfo = dip;
240 
241 	return (DDI_SUCCESS);
242 }
243 
244 static int
245 zc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
246 {
247 	zc_state_t *zcs;
248 	int instance;
249 
250 	if (cmd != DDI_DETACH)
251 		return (DDI_FAILURE);
252 
253 	instance = ddi_get_instance(dip);
254 	if ((zcs = ddi_get_soft_state(zc_soft_state, instance)) == NULL)
255 		return (DDI_FAILURE);
256 
257 	if ((zcs->zc_state & ZC_STATE_MOPEN) ||
258 	    (zcs->zc_state & ZC_STATE_SOPEN)) {
259 		DBG1("zc_detach: device (dip=%p) still open\n", (void *)dip);
260 		return (DDI_FAILURE);
261 	}
262 
263 	ddi_remove_minor_node(dip, NULL);
264 	ddi_soft_state_free(zc_soft_state, instance);
265 
266 	return (DDI_SUCCESS);
267 }
268 
269 /*
270  * zc_getinfo()
271  *	getinfo(9e) entrypoint.
272  */
273 /*ARGSUSED*/
274 static int
275 zc_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
276 {
277 	zc_state_t *zcs;
278 	int instance = ZC_INSTANCE((dev_t)arg);
279 
280 	switch (infocmd) {
281 	case DDI_INFO_DEVT2DEVINFO:
282 		if ((zcs = ddi_get_soft_state(zc_soft_state, instance)) == NULL)
283 			return (DDI_FAILURE);
284 		*result = zcs->zc_devinfo;
285 		return (DDI_SUCCESS);
286 	case DDI_INFO_DEVT2INSTANCE:
287 		*result = (void *)(uintptr_t)instance;
288 		return (DDI_SUCCESS);
289 	}
290 	return (DDI_FAILURE);
291 }
292 
293 /*
294  * Return the equivalent queue from the other side of the relationship.
295  * e.g.: given the slave's write queue, return the master's write queue.
296  */
297 static queue_t *
298 zc_switch(queue_t *qp)
299 {
300 	zc_state_t *zcs = qp->q_ptr;
301 	ASSERT(zcs != NULL);
302 
303 	if (qp == zcs->zc_master_rdq)
304 		return (zcs->zc_slave_rdq);
305 	else if (OTHERQ(qp) == zcs->zc_master_rdq && zcs->zc_slave_rdq != NULL)
306 		return (OTHERQ(zcs->zc_slave_rdq));
307 	else if (qp == zcs->zc_slave_rdq)
308 		return (zcs->zc_master_rdq);
309 	else if (OTHERQ(qp) == zcs->zc_slave_rdq && zcs->zc_master_rdq != NULL)
310 		return (OTHERQ(zcs->zc_master_rdq));
311 	else
312 		return (NULL);
313 }
314 
315 /*
316  * For debugging and outputting messages.  Returns the name of the side of
317  * the relationship associated with this queue.
318  */
319 static const char *
320 zc_side(queue_t *qp)
321 {
322 	zc_state_t *zcs = qp->q_ptr;
323 	ASSERT(zcs != NULL);
324 
325 	if (qp == zcs->zc_master_rdq ||
326 	    OTHERQ(qp) == zcs->zc_master_rdq) {
327 		return ("master");
328 	}
329 	ASSERT(qp == zcs->zc_slave_rdq || OTHERQ(qp) == zcs->zc_slave_rdq);
330 	return ("slave");
331 }
332 
333 /*ARGSUSED*/
334 static int
335 zc_master_open(zc_state_t *zcs,
336     queue_t	*rqp,	/* pointer to the read side queue */
337     dev_t	*devp,	/* pointer to stream tail's dev */
338     int		oflag,	/* the user open(2) supplied flags */
339     int		sflag,	/* open state flag */
340     cred_t	*credp)	/* credentials */
341 {
342 	mblk_t *mop;
343 	struct stroptions *sop;
344 
345 	/*
346 	 * Enforce exclusivity on the master side; the only consumer should
347 	 * be the zoneadmd for the zone.
348 	 */
349 	if ((zcs->zc_state & ZC_STATE_MOPEN) != 0)
350 		return (EBUSY);
351 
352 	if ((mop = allocb(sizeof (struct stroptions), BPRI_MED)) == NULL) {
353 		DBG("zc_master_open(): mop allocation failed\n");
354 		return (ENOMEM);
355 	}
356 
357 	zcs->zc_state |= ZC_STATE_MOPEN;
358 
359 	/*
360 	 * q_ptr stores driver private data; stash the soft state data on both
361 	 * read and write sides of the queue.
362 	 */
363 	WR(rqp)->q_ptr = rqp->q_ptr = zcs;
364 	qprocson(rqp);
365 
366 	/*
367 	 * Following qprocson(), the master side is fully plumbed into the
368 	 * STREAM and may send/receive messages.  Setting zcs->zc_master_rdq
369 	 * will allow the slave to send messages to us (the master).
370 	 * This cannot occur before qprocson() because the master is not
371 	 * ready to process them until that point.
372 	 */
373 	zcs->zc_master_rdq = rqp;
374 
375 	/*
376 	 * set up hi/lo water marks on stream head read queue and add
377 	 * controlling tty as needed.
378 	 */
379 	mop->b_datap->db_type = M_SETOPTS;
380 	mop->b_wptr += sizeof (struct stroptions);
381 	sop = (struct stroptions *)mop->b_rptr;
382 	if (oflag & FNOCTTY)
383 		sop->so_flags = SO_HIWAT | SO_LOWAT;
384 	else
385 		sop->so_flags = SO_HIWAT | SO_LOWAT | SO_ISTTY;
386 	sop->so_hiwat = 512;
387 	sop->so_lowat = 256;
388 	putnext(rqp, mop);
389 
390 	return (0);
391 }
392 
393 /*ARGSUSED*/
394 static int
395 zc_slave_open(zc_state_t *zcs,
396     queue_t	*rqp,	/* pointer to the read side queue */
397     dev_t	*devp,	/* pointer to stream tail's dev */
398     int		oflag,	/* the user open(2) supplied flags */
399     int		sflag,	/* open state flag */
400     cred_t	*credp)	/* credentials */
401 {
402 	mblk_t *mop;
403 	struct stroptions *sop;
404 
405 	/*
406 	 * The slave side can be opened as many times as needed.
407 	 */
408 	if ((zcs->zc_state & ZC_STATE_SOPEN) != 0) {
409 		ASSERT((rqp != NULL) && (WR(rqp)->q_ptr == zcs));
410 		return (0);
411 	}
412 
413 	if ((mop = allocb(sizeof (struct stroptions), BPRI_MED)) == NULL) {
414 		DBG("zc_slave_open(): mop allocation failed\n");
415 		return (ENOMEM);
416 	}
417 
418 	zcs->zc_state |= ZC_STATE_SOPEN;
419 
420 	/*
421 	 * q_ptr stores driver private data; stash the soft state data on both
422 	 * read and write sides of the queue.
423 	 */
424 	WR(rqp)->q_ptr = rqp->q_ptr = zcs;
425 
426 	qprocson(rqp);
427 
428 	/*
429 	 * Must follow qprocson(), since we aren't ready to process until then.
430 	 */
431 	zcs->zc_slave_rdq = rqp;
432 
433 	/*
434 	 * set up hi/lo water marks on stream head read queue and add
435 	 * controlling tty as needed.
436 	 */
437 	mop->b_datap->db_type = M_SETOPTS;
438 	mop->b_wptr += sizeof (struct stroptions);
439 	sop = (struct stroptions *)mop->b_rptr;
440 	sop->so_flags = SO_HIWAT | SO_LOWAT | SO_ISTTY;
441 	sop->so_hiwat = 512;
442 	sop->so_lowat = 256;
443 	putnext(rqp, mop);
444 
445 	return (0);
446 }
447 
448 /*
449  * open(9e) entrypoint; checks sflag, and rejects anything unordinary.
450  */
451 static int
452 zc_open(queue_t *rqp,		/* pointer to the read side queue */
453 	dev_t   *devp,		/* pointer to stream tail's dev */
454 	int	oflag,		/* the user open(2) supplied flags */
455 	int	sflag,		/* open state flag */
456 	cred_t  *credp)		/* credentials */
457 {
458 	int instance = ZC_INSTANCE(*devp);
459 	int ret;
460 	zc_state_t *zcs;
461 
462 	if (sflag != 0)
463 		return (EINVAL);
464 
465 	if ((zcs = ddi_get_soft_state(zc_soft_state, instance)) == NULL)
466 		return (ENXIO);
467 
468 	switch (ZC_NODE(*devp)) {
469 	case ZC_MASTER_MINOR:
470 		ret = zc_master_open(zcs, rqp, devp, oflag, sflag, credp);
471 		break;
472 	case ZC_SLAVE_MINOR:
473 		ret = zc_slave_open(zcs, rqp, devp, oflag, sflag, credp);
474 		break;
475 	default:
476 		ret = ENXIO;
477 		break;
478 	}
479 
480 	return (ret);
481 }
482 
483 /*
484  * close(9e) entrypoint.
485  */
486 /*ARGSUSED1*/
487 static int
488 zc_close(queue_t *rqp, int flag, cred_t *credp)
489 {
490 	queue_t *wqp;
491 	mblk_t	*bp;
492 	zc_state_t *zcs;
493 
494 	zcs = (zc_state_t *)rqp->q_ptr;
495 
496 	if (rqp == zcs->zc_master_rdq) {
497 		DBG("Closing master side");
498 
499 		zcs->zc_master_rdq = NULL;
500 		zcs->zc_state &= ~ZC_STATE_MOPEN;
501 
502 		/*
503 		 * qenable slave side write queue so that it can flush
504 		 * its messages as master's read queue is going away
505 		 */
506 		if (zcs->zc_slave_rdq != NULL) {
507 			qenable(WR(zcs->zc_slave_rdq));
508 		}
509 
510 		qprocsoff(rqp);
511 		WR(rqp)->q_ptr = rqp->q_ptr = NULL;
512 
513 	} else if (rqp == zcs->zc_slave_rdq) {
514 
515 		DBG("Closing slave side");
516 		zcs->zc_state &= ~ZC_STATE_SOPEN;
517 		zcs->zc_slave_rdq = NULL;
518 
519 		wqp = WR(rqp);
520 		while ((bp = getq(wqp)) != NULL) {
521 			if (zcs->zc_master_rdq != NULL)
522 				putnext(zcs->zc_master_rdq, bp);
523 			else if (bp->b_datap->db_type == M_IOCTL)
524 				miocnak(wqp, bp, 0, 0);
525 			else
526 				freemsg(bp);
527 		}
528 
529 		/*
530 		 * Qenable master side write queue so that it can flush its
531 		 * messages as slaves's read queue is going away.
532 		 */
533 		if (zcs->zc_master_rdq != NULL)
534 			qenable(WR(zcs->zc_master_rdq));
535 
536 		qprocsoff(rqp);
537 		WR(rqp)->q_ptr = rqp->q_ptr = NULL;
538 	}
539 
540 	return (0);
541 }
542 
543 static void
544 handle_mflush(queue_t *qp, mblk_t *mp)
545 {
546 	mblk_t *nmp;
547 	DBG1("M_FLUSH on %s side", zc_side(qp));
548 
549 	if (*mp->b_rptr & FLUSHW) {
550 		DBG1("M_FLUSH, FLUSHW, %s side", zc_side(qp));
551 		flushq(qp, FLUSHDATA);
552 		*mp->b_rptr &= ~FLUSHW;
553 		if ((*mp->b_rptr & FLUSHR) == 0) {
554 			/*
555 			 * FLUSHW only. Change to FLUSHR and putnext other side,
556 			 * then we are done.
557 			 */
558 			*mp->b_rptr |= FLUSHR;
559 			if (zc_switch(RD(qp)) != NULL) {
560 				putnext(zc_switch(RD(qp)), mp);
561 				return;
562 			}
563 		} else if ((zc_switch(RD(qp)) != NULL) &&
564 		    (nmp = copyb(mp)) != NULL) {
565 			/*
566 			 * It is a FLUSHRW; we copy the mblk and send
567 			 * it to the other side, since we still need to use
568 			 * the mblk in FLUSHR processing, below.
569 			 */
570 			putnext(zc_switch(RD(qp)), nmp);
571 		}
572 	}
573 
574 	if (*mp->b_rptr & FLUSHR) {
575 		DBG("qreply(qp) turning FLUSHR around\n");
576 		qreply(qp, mp);
577 		return;
578 	}
579 	freemsg(mp);
580 }
581 
582 /*
583  * wput(9E) is symmetric for master and slave sides, so this handles both
584  * without splitting the codepath.
585  *
586  * zc_wput() looks at the other side; if there is no process holding that
587  * side open, it frees the message.  This prevents processes from hanging
588  * if no one is holding open the console.  Otherwise, it putnext's high
589  * priority messages, putnext's normal messages if possible, and otherwise
590  * enqueues the messages; in the case that something is enqueued, wsrv(9E)
591  * will take care of eventually shuttling I/O to the other side.
592  */
593 static void
594 zc_wput(queue_t *qp, mblk_t *mp)
595 {
596 	unsigned char type = mp->b_datap->db_type;
597 
598 	ASSERT(qp->q_ptr);
599 
600 	DBG1("entering zc_wput, %s side", zc_side(qp));
601 
602 	if (zc_switch(RD(qp)) == NULL) {
603 		DBG1("wput to %s side (no one listening)", zc_side(qp));
604 		switch (type) {
605 		case M_FLUSH:
606 			handle_mflush(qp, mp);
607 			break;
608 		case M_IOCTL:
609 			miocnak(qp, mp, 0, 0);
610 			break;
611 		default:
612 			freemsg(mp);
613 			break;
614 		}
615 		return;
616 	}
617 
618 	if (type >= QPCTL) {
619 		DBG1("(hipri) wput, %s side", zc_side(qp));
620 		switch (type) {
621 		case M_READ:		/* supposedly from ldterm? */
622 			DBG("zc_wput: tossing M_READ\n");
623 			freemsg(mp);
624 			break;
625 		case M_FLUSH:
626 			handle_mflush(qp, mp);
627 			break;
628 		default:
629 			/*
630 			 * Put this to the other side.
631 			 */
632 			ASSERT(zc_switch(RD(qp)) != NULL);
633 			putnext(zc_switch(RD(qp)), mp);
634 			break;
635 		}
636 		DBG1("done (hipri) wput, %s side", zc_side(qp));
637 		return;
638 	}
639 
640 	/*
641 	 * Only putnext if there isn't already something in the queue.
642 	 * otherwise things would wind up out of order.
643 	 */
644 	if (qp->q_first == NULL && bcanputnext(RD(zc_switch(qp)), mp->b_band)) {
645 		DBG("wput: putting message to other side\n");
646 		putnext(RD(zc_switch(qp)), mp);
647 	} else {
648 		DBG("wput: putting msg onto queue\n");
649 		(void) putq(qp, mp);
650 	}
651 	DBG1("done wput, %s side", zc_side(qp));
652 }
653 
654 /*
655  * rsrv(9E) is symmetric for master and slave, so zc_rsrv() handles both
656  * without splitting up the codepath.
657  *
658  * Enable the write side of the partner.  This triggers the partner to send
659  * messages queued on its write side to this queue's read side.
660  */
661 static void
662 zc_rsrv(queue_t *qp)
663 {
664 	zc_state_t *zcs;
665 	zcs = (zc_state_t *)qp->q_ptr;
666 
667 	/*
668 	 * Care must be taken here, as either of the master or slave side
669 	 * qptr could be NULL.
670 	 */
671 	ASSERT(qp == zcs->zc_master_rdq || qp == zcs->zc_slave_rdq);
672 	if (zc_switch(qp) == NULL) {
673 		DBG("zc_rsrv: other side isn't listening\n");
674 		return;
675 	}
676 	qenable(WR(zc_switch(qp)));
677 }
678 
679 /*
680  * This routine is symmetric for master and slave, so it handles both without
681  * splitting up the codepath.
682  *
683  * If there are messages on this queue that can be sent to the other, send
684  * them via putnext(). Else, if queued messages cannot be sent, leave them
685  * on this queue.
686  */
687 static void
688 zc_wsrv(queue_t *qp)
689 {
690 	mblk_t *mp;
691 
692 	DBG1("zc_wsrv master (%s) side", zc_side(qp));
693 
694 	/*
695 	 * Partner has no read queue, so take the data, and throw it away.
696 	 */
697 	if (zc_switch(RD(qp)) == NULL) {
698 		DBG("zc_wsrv: other side isn't listening");
699 		while ((mp = getq(qp)) != NULL) {
700 			if (mp->b_datap->db_type == M_IOCTL)
701 				miocnak(qp, mp, 0, 0);
702 			else
703 				freemsg(mp);
704 		}
705 		flushq(qp, FLUSHALL);
706 		return;
707 	}
708 
709 	/*
710 	 * while there are messages on this write queue...
711 	 */
712 	while ((mp = getq(qp)) != NULL) {
713 		/*
714 		 * Due to the way zc_wput is implemented, we should never
715 		 * see a control message here.
716 		 */
717 		ASSERT(mp->b_datap->db_type < QPCTL);
718 
719 		if (bcanputnext(RD(zc_switch(qp)), mp->b_band)) {
720 			DBG("wsrv: send message to other side\n");
721 			putnext(RD(zc_switch(qp)), mp);
722 		} else {
723 			DBG("wsrv: putting msg back on queue\n");
724 			(void) putbq(qp, mp);
725 			break;
726 		}
727 	}
728 }
729