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