xref: /titanic_41/usr/src/uts/common/io/iwscons.c (revision 8eea8e29cc4374d1ee24c25a07f45af132db3499)
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  * Redirecting driver; used to handle workstation console redirection.
31  *
32  * Redirects all I/O through a given device instance to the device designated
33  * as the current target, as given by the vnode associated with the first
34  * entry in the list of redirections for the given device instance.  The
35  * implementation assumes that this vnode denotes a STREAMS device; this is
36  * perhaps a bug.
37  *
38  * Supports the SRIOCSREDIR ioctl for designating a new redirection target.
39  * The new target is added to the front of a list of potentially active
40  * designees.  Should the device at the front of this list be closed, the new
41  * front entry assumes active duty.  (Stated differently, redirection targets
42  * stack, except that it's possible for entries in the interior of the stack
43  * to go away.)
44  *
45  * Supports the SRIOCISREDIR ioctl for inquiring whether the descriptor given
46  * as argument is the current front of the redirection list associated with
47  * the descriptor on which the ioctl was issued.
48  *
49  * Every open instance of this driver corresponds to an instance of the
50  * underlying client driver.  If the redirection stack would otherwise become
51  * empty, this device (designated by the wd_vp field of the wcd_data
52  * structure) is implicitly opened and added to the front of the list.  Thus,
53  * there's always an active device for handling i/o through an open instance
54  * of this driver.
55  *
56  * XXX: Names -- many of the names in this driver and its companion STREAMS
57  *	module still reflect its origins as the workstation console
58  *	redirection driver.  Ultimately, they should be changed to reflect the
59  *	fact that this driver is potentially a general purpose redirection
60  *	driver.  In the meantime, the driver is still specialized to have a
61  *	single client -- the workstation console driver -- and its file name
62  *	remains iwscons.c to reflect that specialization.
63  *
64  *	Proposed change: "iwscn" becomes either "dr" (for "streams redirecting
65  *	driver") or "srm" (for "streams redirecting module"), as appropriate.
66  *
67  * XXX:	Add mechanism for notifying a redirectee that it's no longer the
68  *	current redirectee?  (This in contrast to the current facility for
69  *	letting it ask.)
70  */
71 
72 #include <sys/types.h>
73 #include <sys/sysmacros.h>
74 #include <sys/open.h>
75 #include <sys/param.h>
76 #include <sys/systm.h>
77 #include <sys/signal.h>
78 #include <sys/cred.h>
79 #include <sys/user.h>
80 #include <sys/proc.h>
81 #include <sys/vnode.h>
82 #include <sys/uio.h>
83 #include <sys/file.h>
84 #include <sys/kmem.h>
85 #include <sys/stat.h>
86 
87 #include <sys/stream.h>
88 #include <sys/stropts.h>
89 #include <sys/strsubr.h>
90 #include <sys/poll.h>
91 
92 #include <sys/debug.h>
93 
94 #include <sys/strredir.h>
95 
96 #include <sys/conf.h>
97 #include <sys/ddi.h>
98 #include <sys/sunddi.h>
99 
100 static int	iwscninfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
101 static int	iwscnattach(dev_info_t *, ddi_attach_cmd_t);
102 static int	iwscnopen(dev_t *, int, int, cred_t *);
103 static int	iwscnclose(dev_t, int, int, cred_t *);
104 static int	iwscnread(dev_t, struct uio *, cred_t *);
105 static int	iwscnwrite(dev_t, struct uio *, cred_t *);
106 static int	iwscnioctl(dev_t, int, intptr_t, int, cred_t *, int *);
107 static int	iwscnpoll(dev_t, short, int, short *, struct pollhead **);
108 
109 /*
110  * Private copy of devinfo pointer; iwscninfo uses it.
111  */
112 static dev_info_t	*iwscn_dip;
113 
114 struct cb_ops	iwscn_cb_ops = {
115 	iwscnopen,		/* open */
116 	iwscnclose,		/* close */
117 	nodev,			/* strategy */
118 	nodev,			/* print */
119 	nodev,			/* dump */
120 	iwscnread,		/* read */
121 	iwscnwrite,		/* write */
122 	iwscnioctl,		/* ioctl */
123 	nodev,			/* devmap */
124 	nodev,			/* mmap */
125 	nodev, 			/* segmap */
126 	iwscnpoll,		/* poll */
127 	ddi_prop_op,		/* cb_prop_op */
128 	0,			/* streamtab  */
129 	D_NEW|D_MP		/* Driver compatibility flag */
130 };
131 
132 struct dev_ops	iwscn_ops = {
133 	DEVO_REV,		/* devo_rev, */
134 	0,			/* refcnt  */
135 	iwscninfo,		/* info */
136 	nulldev,		/* identify */
137 	nulldev,		/* probe */
138 	iwscnattach,		/* attach */
139 	nodev,			/* detach */
140 	nodev,			/* reset */
141 	&iwscn_cb_ops,		/* driver operations */
142 	NULL			/* bus operations */
143 };
144 
145 static krwlock_t	iwscn_lock; /* lock proecting almost everything here */
146 
147 /*
148  * A read/write lock was used to serialize reads,writes/opens,closes.
149  * Sometime the open would hang due to a pending read.  The new lock
150  * iwscn_open_lock and the read lock are held in open to assure a single
151  * instance, while letting concurrent reads/writes to proceed.
152  */
153 static kmutex_t 	iwscn_open_lock; /* Serializes opens  */
154 
155 /*
156  * These next two fields, protected by iwscn_lock, pass the data to wcmopen()
157  * from the ioctl SRIOCSREDIR.  wcmopen() uses the data only if the thread
158  * matches.  This keeps other threads from interfering.
159  */
160 extern kthread_id_t	iwscn_thread;	/* thread that is allowed to */
161 					/* push redirm */
162 extern wcm_data_t	*iwscn_wcm_data;  /* allocated data for redirm */
163 
164 /*
165  * Forward declarations of private routines.
166  */
167 static int		srreset(wcd_data_t *, int, cred_t *);
168 static wcrlist_t	*srrm(wcrlist_t **, vnode_t *, int);
169 static wcd_data_t	*srilookup(minor_t);
170 static wcd_data_t	*srialloc(minor_t);
171 static void		sridealloc(wcd_data_t *);
172 static wcrlist_t	*srpush(wcrlist_t **, vnode_t *);
173 
174 /*
175  * The head of the list of open instances.
176  */
177 static wcd_data_t	*wcddata;
178 
179 /*
180  * Currently, the only client of this driver is the workstation console
181  * driver.  Thus, we can get away with hard-wiring a reference to it here.
182  *
183  * To handle multiple clients, the driver must be revised as follows.
184  * 1)	Add a registration routine that clients can call to announce
185  *	themselves to this driver.  The routine should take as arguments the
186  *	major device number of the corresponding instantiation of the
187  *	redirecting driver and a pointer to its dedvnops ops vector.
188  * 2)	Maintain a list (or perhaps hash array) or registered clients,
189  *	recording for each the srvnops ops vector and a pointer to the list
190  *	of open instances for that client.
191  * 3)	Modify the driver entry points to use their dev argument to look up
192  *	the proper instantiation, get the list of open instances, and then use
193  *	that as they currently use the open instance list.
194  * 4)	To allow clients to unload themselves, we probably need an unregister
195  *	routine.  This routine would have to cope with active open instances.
196  */
197 extern srvnops_t	wscons_srvnops;
198 
199 #include <sys/types.h>
200 #include <sys/conf.h>
201 #include <sys/param.h>
202 #include <sys/systm.h>
203 #include <sys/errno.h>
204 #include <sys/modctl.h>
205 
206 /*
207  * Module linkage information for the kernel.
208  */
209 
210 static struct modldrv modldrv = {
211 	&mod_driverops, /* Type of module.  This one is a pseudo driver */
212 	"Workstation Redirection driver 'iwscn' %I%",
213 	&iwscn_ops,	/* driver ops */
214 };
215 
216 static struct modlinkage modlinkage = {
217 	MODREV_1,
218 	&modldrv,
219 	NULL
220 };
221 
222 
223 int
224 _init(void)
225 {
226 	return (mod_install(&modlinkage));
227 }
228 
229 int
230 _fini(void)
231 {
232 	return (EBUSY);
233 }
234 
235 int
236 _info(struct modinfo *modinfop)
237 {
238 	return (mod_info(&modlinkage, modinfop));
239 }
240 
241 /*
242  * DDI glue routines.
243  */
244 
245 /*ARGSUSED*/
246 static int
247 iwscnattach(dev_info_t *devi, ddi_attach_cmd_t cmd)
248 {
249 	static char	been_here;
250 
251 	if (!been_here) {
252 		been_here = 1;
253 		rw_init(&iwscn_lock, NULL, RW_DEFAULT, NULL);
254 	}
255 	if (ddi_create_minor_node(devi, "iwscn", S_IFCHR,
256 	    0, DDI_PSEUDO, NULL) == DDI_FAILURE) {
257 		ddi_remove_minor_node(devi, NULL);
258 		return (-1);
259 	}
260 	iwscn_dip = devi;
261 	return (DDI_SUCCESS);
262 }
263 
264 /* ARGSUSED */
265 static int
266 iwscninfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
267 {
268 	int error;
269 
270 	switch (infocmd) {
271 	case DDI_INFO_DEVT2DEVINFO:
272 		if (iwscn_dip == NULL) {
273 			error = DDI_FAILURE;
274 		} else {
275 			*result = (void *)iwscn_dip;
276 			error = DDI_SUCCESS;
277 		}
278 		break;
279 	case DDI_INFO_DEVT2INSTANCE:
280 		*result = (void *)0;
281 		error = DDI_SUCCESS;
282 		break;
283 	default:
284 		error = DDI_FAILURE;
285 	}
286 	return (error);
287 }
288 
289 
290 /* ARGSUSED */
291 static int
292 iwscnopen(
293 	dev_t	*devp,
294 	int	flag,
295 	int	state,		/* should be OTYP_CHR */
296 	cred_t	*cred)
297 {
298 	minor_t		unit = getminor(*devp);
299 	wcd_data_t	*wd;
300 	int		err = 0;
301 	struct wcrlist	*wwd;
302 
303 	if (state != OTYP_CHR)
304 		return (ENXIO);
305 	rw_enter(&iwscn_lock, RW_READER);
306 	mutex_enter(&iwscn_open_lock);
307 	if ((wd = srilookup(unit)) == NULL) {
308 		vnode_t	*vp;
309 
310 		/*
311 		 * First open for this instance; get a state structure for it.
312 		 */
313 		wd = srialloc(unit);
314 
315 		/*
316 		 * Call the client driver to obtain a held vnode for the
317 		 * underlying "real" device instance.
318 		 *
319 		 * XXX:	There's wired in knowledge of the client driver here.
320 		 */
321 		err = wscons_srvnops.svn_get(unit, &vp);
322 		if (err != 0) {
323 			sridealloc(wd);
324 			mutex_exit(&iwscn_open_lock);
325 			rw_exit(&iwscn_lock);
326 			return (err);
327 		}
328 		wd->wd_vp = vp;
329 	}
330 
331 	/*
332 	 * Reinitalize the list if necessary.
333 	 *
334 	 * XXX:	Is it possible for the list to empty completely while this
335 	 *	instance is still open?  If not, this if should be coalesced
336 	 *	with the previous one.
337 	 */
338 	if (wd->wd_list == NULL) {
339 		wcrlist_t	*e = srpush(&wd->wd_list, wd->wd_vp);
340 
341 		/*
342 		 * There's no corresponding redirecting module instance for
343 		 * the underlying device.
344 		 */
345 		e->wl_data = NULL;
346 	}
347 
348 	err = srreset(wd, flag, cred);
349 	/*
350 	 *  XXX cleanup the sig list.  Hook for console driver.
351 	 */
352 	for (wwd = wd->wd_list; wwd != NULL; wwd = wwd->wl_next) {
353 		ASSERT(wwd->wl_vp->v_stream != NULL);
354 		str_cn_clean(wwd->wl_vp);
355 	}
356 	mutex_exit(&iwscn_open_lock);
357 	rw_exit(&iwscn_lock);
358 	return (err);
359 }
360 
361 /* ARGSUSED */
362 static int
363 iwscnclose(
364 	dev_t	dev,
365 	int	flag,
366 	int	state,		/* should be OTYP_CHR */
367 	cred_t	*cred)
368 {
369 	wcd_data_t	*wd;
370 	int		err = 0;
371 
372 	if (state != OTYP_CHR)
373 		return (ENXIO);
374 	rw_enter(&iwscn_lock, RW_WRITER);
375 	wd = srilookup(getminor(dev));
376 	/*
377 	 * Remove all outstanding redirections for this instance.
378 	 */
379 	while (wd->wd_list != NULL)
380 		(void) srrm(&wd->wd_list, wd->wd_list->wl_vp, 1);
381 
382 	/*
383 	 * Since this is the _last_ close, it's our last chance to close the
384 	 * underlying device.  (Note that if someone else has the underlying
385 	 * workstation console device open, we won't get here, since
386 	 * spec_close will see s_count > 1.)
387 	 */
388 	while ((wd->wd_wsconsopen != 0) && (!err)) {
389 		err = VOP_CLOSE(wd->wd_vp, flag, 1, (offset_t)0, cred);
390 		if (!err)
391 			wd->wd_wsconsopen--;
392 	}
393 	if (!err)
394 		wd->wd_vp->v_stream = NULL;
395 
396 	/*
397 	 * We don't need the vnode that the client driver gave us any more.
398 	 *
399 	 * XXX:	There's wired in knowledge of the client driver here.
400 	 */
401 	wscons_srvnops.svn_rele(wd->wd_unit, wd->wd_vp);
402 	sridealloc(wd);
403 
404 	rw_exit(&iwscn_lock);
405 	return (err);
406 }
407 
408 static int
409 iwscnread(dev_t dev, uio_t *uio, cred_t *cred)
410 {
411 	wcd_data_t	*wd;
412 	int error;
413 
414 	rw_enter(&iwscn_lock, RW_READER);
415 	wd = srilookup(getminor(dev));
416 	error = strread(wd->wd_list->wl_vp, uio, cred);
417 	rw_exit(&iwscn_lock);
418 	return (error);
419 }
420 
421 static int
422 iwscnwrite(dev_t dev, uio_t *uio, cred_t *cred)
423 {
424 	wcd_data_t	*wd;
425 	int error;
426 
427 	rw_enter(&iwscn_lock, RW_READER);
428 	wd = srilookup(getminor(dev));
429 	error = strwrite(wd->wd_list->wl_vp, uio, cred);
430 	rw_exit(&iwscn_lock);
431 	return (error);
432 }
433 
434 static int
435 iwscnioctl(dev_t dev, int cmd, intptr_t arg, int flag,
436     cred_t *cred, int *rvalp)
437 {
438 	wcd_data_t *wd;
439 	int err = 0;
440 	file_t *f;
441 
442 	switch (cmd) {
443 	case SRIOCSREDIR: {
444 		wcrlist_t	*wlp;
445 		wcm_data_t	*mdp;
446 
447 		if (!rw_tryenter(&iwscn_lock, RW_WRITER)) {
448 			return (EBUSY);
449 		}
450 		wd = srilookup(getminor(dev));
451 		/*
452 		 * Find the vnode corresponding to the file descriptor
453 		 * argument and verify that it names a stream.
454 		 */
455 		if ((f = getf((int)arg)) == NULL) {
456 			err = EBADF;
457 			break;
458 		}
459 		if (f->f_vnode->v_stream == NULL) {
460 			err = ENOSTR;
461 			releasef((int)arg);
462 			break;
463 		}
464 		/*
465 		 * allocate the private data for redirmod, and pass it through
466 		 * a global to wcmopen().  This is all protected by iwscn_lock.
467 		 */
468 		mdp = kmem_alloc(sizeof (*mdp), KM_SLEEP);
469 		iwscn_wcm_data = mdp;
470 		iwscn_thread = curthread;
471 
472 		/*
473 		 * Push a new instance of the redirecting module onto the
474 		 * stream, so that its close routine can notify us when the
475 		 * overall stream is closed.  (In turn, we'll then remove it
476 		 * from the redirection list.)
477 		 */
478 		if ((err = VOP_IOCTL(f->f_vnode, I_PUSH, (intptr_t)"redirmod",
479 		    (FREAD | FKIOCTL), cred, rvalp)) != 0) {
480 			iwscn_thread = NULL;
481 			kmem_free(mdp, sizeof (*mdp));
482 			releasef((int)arg);
483 			break;
484 		}
485 		iwscn_thread = NULL;	/* clear authorization for wcmopen() */
486 
487 		/*
488 		 * Push it onto the redirection stack.
489 		 */
490 		wlp = srpush(&wd->wd_list, f->f_vnode);
491 		/*
492 		 * Fill in the redirecting module instance's private data with
493 		 * information to let it get to our redirection list when its
494 		 * close routine is called.  Cross-link it with the
495 		 * redirection list entry.
496 		 */
497 		mdp->wm_wd = wd;
498 		mdp->wm_entry = wlp;
499 		wlp->wl_data = mdp;
500 		releasef((int)arg);
501 
502 		break;
503 	    }
504 
505 	case SRIOCISREDIR:
506 		rw_enter(&iwscn_lock, RW_READER);
507 		wd = srilookup(getminor(dev));
508 		if ((f = getf((int)arg)) == NULL) {
509 			err = EBADF;
510 			break;
511 		}
512 		/*
513 		 * Return value is 1 if the argument descriptor is the current
514 		 * redirection target, and 0 otherwise.
515 		 */
516 		*rvalp = (f->f_vnode == wd->wd_list->wl_vp) ? 1 : 0;
517 		releasef((int)arg);
518 		break;
519 
520 	case I_POP: {
521 		/*
522 		 * XXX - This is a big kludge the handles a deadlock case
523 		 * when we are trying to pop off the redirection
524 		 * module.  Since this should only happen on a close
525 		 * of the device, and since it hangs the system, just
526 		 * do not allow a pop of the redirection module to happen.
527 		 * Popping other modules is allowed.
528 		 */
529 		struct stdata	*stp;
530 		char modname[FMNAMESZ + 1] = " ";
531 
532 		rw_enter(&iwscn_lock, RW_READER);
533 		wd = srilookup(getminor(dev));
534 		(void) strioctl(wd->wd_list->wl_vp, I_LOOK, (intptr_t)modname,
535 		    flag, K_TO_K, cred, rvalp);
536 		if (strcmp("redirmod", modname) == 0) {
537 			if ((f = getf((int)arg)) == NULL) {
538 				err = EBADF;
539 				break;
540 			}
541 			if ((stp = f->f_vnode->v_stream) == NULL) {
542 				err = ENOSTR;
543 				releasef((int)arg);
544 				break;
545 			}
546 			if (!(stp->sd_flag & STRCLOSE)) {
547 				releasef((int)arg);
548 				rw_exit(&iwscn_lock);
549 				cmn_err(CE_WARN, "Popping of redirection "
550 				    "module not allowed");
551 				return (EINVAL);
552 			}
553 
554 			releasef((int)arg);
555 		}
556 
557 		/* Process ioctl normally */
558 		err = strioctl(wd->wd_list->wl_vp, cmd, arg, flag, U_TO_K,
559 		    cred, rvalp);
560 		break;
561 	}
562 
563 	default:
564 		rw_enter(&iwscn_lock, RW_READER);
565 		wd = srilookup(getminor(dev));
566 		err = strioctl(wd->wd_list->wl_vp, cmd, arg, flag, U_TO_K,
567 			cred, rvalp);
568 		break;
569 	}
570 
571 	rw_exit(&iwscn_lock);
572 	return (err);
573 }
574 
575 static int
576 iwscnpoll(
577 	dev_t			dev,
578 	short			events,
579 	int			anyyet,
580 	short			*reventsp,
581 	struct pollhead		**phpp)
582 {
583 	wcd_data_t	*wd;
584 	int	error;
585 
586 	rw_enter(&iwscn_lock, RW_READER);
587 	wd = srilookup(getminor(dev));
588 	error = strpoll(wd->wd_list->wl_vp->v_stream, events, anyyet,
589 		    reventsp, phpp);
590 	rw_exit(&iwscn_lock);
591 	return (error);
592 }
593 
594 
595 /*
596  * Auxiliary routines.
597  */
598 
599 /*
600  * Additional public interfaces.
601  */
602 
603 /*
604  * Reset the current workstation console designee to the device denoted by the
605  * wl_vp field of the first entry in the redirection list.  Called from
606  * iwscnopen and from the SRIOCSREDIR case of iwscnioctl, in both cases after
607  * the target vp has been set to its new value.
608  */
609 static int
610 srreset(wcd_data_t *wd, int flag, cred_t *cred)
611 {
612 	wcrlist_t	*wlp;
613 	int		err = 0;
614 
615 	ASSERT(RW_WRITE_HELD(&iwscn_lock) || MUTEX_HELD(&iwscn_open_lock));
616 	wlp = wd->wd_list;		/* first entry */
617 
618 	/*
619 	 * If we're reverting back to the workstation console, make sure it's
620 	 * open.
621 	 */
622 	if (wlp != NULL && wlp->wl_vp == wd->wd_vp) {
623 		vnode_t	*vp = wd->wd_vp;	/* underlying device's vp */
624 
625 		err = VOP_OPEN(&vp, flag, cred);
626 		/*
627 		 * The underlying driver is not allowed to have cloned itself
628 		 * for this open.
629 		 */
630 		if (vp != wd->wd_vp) {
631 			panic("srreset: Illegal clone");
632 			/*NOTREACHED*/
633 		}
634 		if (!err)
635 			wd->wd_wsconsopen++;
636 	}
637 	return (err);
638 }
639 
640 /*
641  * Remove vp from the redirection list rooted at *rwlp, should it be there.
642  * If zap is nonzero, deallocate the entry and remove dangling references to
643  * the it from the corresponding redirecting module instance's wcm_data
644  * structure.
645  *
646  * If the entry doesn't exist upon completion, return NULL; otherwise return a
647  * pointer to it.
648  */
649 static wcrlist_t *
650 srrm(wcrlist_t **rwlp, vnode_t *vp, int zap)
651 {
652 	wcrlist_t	**delwlp;
653 	wcrlist_t	*wlp;
654 	wcm_data_t	*mdp;
655 
656 	ASSERT(RW_WRITE_HELD(&iwscn_lock) || MUTEX_HELD(&iwscn_open_lock));
657 	for (delwlp = rwlp; (wlp = *delwlp) != NULL; delwlp = &wlp->wl_next)
658 		if (wlp->wl_vp == vp)
659 			break;
660 	if (wlp == NULL)
661 		return (NULL);
662 	*delwlp = wlp->wl_next;
663 
664 	if (zap == 0)
665 		return (wlp);
666 
667 	if (wlp->wl_vp == vp)
668 		VN_RELE(vp);
669 	/*
670 	 * Make sure there are no dangling references leading to the entry
671 	 * from the corresponding redirecting module instance.
672 	 */
673 	if ((mdp = wlp->wl_data) != NULL) {
674 		mdp->wm_wd = NULL;
675 		mdp->wm_entry = NULL;
676 	}
677 
678 	kmem_free(wlp, sizeof (*wlp));
679 	return (NULL);
680 }
681 
682 /*
683  * srpop - remove redirection because the target stream is being closed.
684  * Called from wcmclose().
685  */
686 void
687 srpop(wcm_data_t *mdp, int flag, cred_t *cred)
688 {
689 	wcd_data_t	*ddp;
690 
691 	rw_enter(&iwscn_lock, RW_WRITER);
692 	if ((ddp = mdp->wm_wd) != NULL) {
693 		ASSERT(mdp->wm_entry != NULL);
694 		(void) srrm(&ddp->wd_list, mdp->wm_entry->wl_vp, 1);
695 		(void) srreset(ddp, flag, cred);
696 	}
697 	rw_exit(&iwscn_lock);
698 }
699 
700 /*
701  * Routines for allocating, deallocating, and finding wcd_data structures.
702  *
703  * For a given instantiation of the driver, its open instance structures are
704  * linked together into a list, on the assumption that there will never be
705  * enough open instances to make search efficiency a serious concern.
706  */
707 
708 /*
709  * Look up the instance structure denoted by unit.
710  */
711 static wcd_data_t *
712 srilookup(minor_t unit)
713 {
714 	wcd_data_t	*wd = wcddata;
715 
716 	ASSERT(RW_LOCK_HELD(&iwscn_lock));
717 	for (; wd != NULL && wd->wd_unit != unit; wd = wd->wd_next)
718 		continue;
719 
720 	return (wd);
721 }
722 
723 /*
724  * Allocate a wcd_data structure for the instance denoted by unit, link it in
725  * place, and return a pointer to it.  If it's already allocated, simply
726  * return a pointer to it.
727  */
728 static wcd_data_t *
729 srialloc(minor_t unit)
730 {
731 	wcd_data_t	*wdp;
732 	wcd_data_t	**wdpp;
733 
734 	ASSERT(MUTEX_HELD(&iwscn_open_lock));
735 	for (wdpp = &wcddata; (wdp = *wdpp) != NULL; wdpp = &wdp->wd_next) {
736 		if (unit < wdp->wd_unit)
737 			break;
738 		if (unit == wdp->wd_unit) {
739 			/* Already allocated and in place. */
740 			return (wdp);
741 		}
742 	}
743 	/*
744 	 * wdpp now points to the proper insertion point for unit's
745 	 * per-instance structure.
746 	 */
747 	wdp = kmem_zalloc(sizeof (*wdp), KM_SLEEP);
748 	wdp->wd_unit = unit;
749 	wdp->wd_next = *wdpp;
750 	*wdpp = wdp;
751 
752 	return (wdp);
753 }
754 
755 /*
756  * Deallocate the wcd_data structure denoted by wd and unlink it from the
757  * list of open instances.
758  */
759 static void
760 sridealloc(wcd_data_t *wd)
761 {
762 	wcd_data_t	*wdp;
763 	wcd_data_t	**wdpp;
764 
765 	ASSERT(RW_WRITE_HELD(&iwscn_lock) || MUTEX_HELD(&iwscn_open_lock));
766 	for (wdpp = &wcddata; (wdp = *wdpp) != NULL; wdpp = &wdp->wd_next)
767 		if (wd == wdp)
768 			break;
769 	if (wdp == NULL) {
770 		/*
771 		 * Not there.  This should probably be a panic.
772 		 */
773 		return;
774 	}
775 	*wdpp = wdp->wd_next;
776 	kmem_free(wdp, sizeof (*wdp));
777 }
778 
779 
780 /*
781  * Push vp onto the redirection list rooted at *wlpp.  If it's already there,
782  * move it to the front position.  Return a pointer to its list entry.
783  *
784  * N.B.: It is the caller's responsibility to initialize all fields in the
785  * entry other than the wl_next and wl_vp fields.
786  */
787 static wcrlist_t *
788 srpush(wcrlist_t **wlpp, vnode_t *vp)
789 {
790 	wcrlist_t	*nwlp;
791 
792 	ASSERT(RW_WRITE_HELD(&iwscn_lock) || MUTEX_HELD(&iwscn_open_lock));
793 	if ((nwlp = srrm(wlpp, vp, 0)) == NULL) {
794 		nwlp = kmem_zalloc(sizeof (*nwlp), KM_SLEEP);
795 		nwlp->wl_vp = vp;
796 		/*
797 		 * The hold will prevent underlying device from closing
798 		 * while this vnode is still on the redirection list.
799 		 */
800 		VN_HOLD(vp);
801 	}
802 	nwlp->wl_next = *wlpp;
803 	*wlpp = nwlp;
804 
805 	return (nwlp);
806 }
807