xref: /titanic_50/usr/src/uts/common/io/iwscons.c (revision 6d9a41ff63273acce102bfb2d51423ed920e6dcd)
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 2005 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 	vnode_t		*vp;
414 
415 	rw_enter(&iwscn_lock, RW_READER);
416 	wd = srilookup(getminor(dev));
417 	/*
418 	 * We don't need to hold iwscn_lock while waiting for the read to
419 	 * complete, but the vnode must not be destroyed.
420 	 */
421 	vp = wd->wd_list->wl_vp;
422 	VN_HOLD(vp);
423 	rw_exit(&iwscn_lock);
424 	error = strread(vp, uio, cred);
425 	VN_RELE(vp);
426 	return (error);
427 }
428 
429 static int
430 iwscnwrite(dev_t dev, uio_t *uio, cred_t *cred)
431 {
432 	wcd_data_t	*wd;
433 	int error;
434 
435 	rw_enter(&iwscn_lock, RW_READER);
436 	wd = srilookup(getminor(dev));
437 	error = strwrite(wd->wd_list->wl_vp, uio, cred);
438 	rw_exit(&iwscn_lock);
439 	return (error);
440 }
441 
442 static int
443 iwscnioctl(dev_t dev, int cmd, intptr_t arg, int flag,
444     cred_t *cred, int *rvalp)
445 {
446 	wcd_data_t *wd;
447 	int err = 0;
448 	file_t *f;
449 
450 	switch (cmd) {
451 	case SRIOCSREDIR: {
452 		wcrlist_t	*wlp;
453 		wcm_data_t	*mdp;
454 
455 		if (!rw_tryenter(&iwscn_lock, RW_WRITER)) {
456 			return (EBUSY);
457 		}
458 		wd = srilookup(getminor(dev));
459 		/*
460 		 * Find the vnode corresponding to the file descriptor
461 		 * argument and verify that it names a stream.
462 		 */
463 		if ((f = getf((int)arg)) == NULL) {
464 			err = EBADF;
465 			break;
466 		}
467 		if (f->f_vnode->v_stream == NULL) {
468 			err = ENOSTR;
469 			releasef((int)arg);
470 			break;
471 		}
472 		/*
473 		 * allocate the private data for redirmod, and pass it through
474 		 * a global to wcmopen().  This is all protected by iwscn_lock.
475 		 */
476 		mdp = kmem_alloc(sizeof (*mdp), KM_SLEEP);
477 		iwscn_wcm_data = mdp;
478 		iwscn_thread = curthread;
479 
480 		/*
481 		 * Push a new instance of the redirecting module onto the
482 		 * stream, so that its close routine can notify us when the
483 		 * overall stream is closed.  (In turn, we'll then remove it
484 		 * from the redirection list.)
485 		 */
486 		if ((err = VOP_IOCTL(f->f_vnode, I_PUSH, (intptr_t)"redirmod",
487 		    (FREAD | FKIOCTL), cred, rvalp)) != 0) {
488 			iwscn_thread = NULL;
489 			kmem_free(mdp, sizeof (*mdp));
490 			releasef((int)arg);
491 			break;
492 		}
493 		iwscn_thread = NULL;	/* clear authorization for wcmopen() */
494 
495 		/*
496 		 * Push it onto the redirection stack.
497 		 */
498 		wlp = srpush(&wd->wd_list, f->f_vnode);
499 		/*
500 		 * Fill in the redirecting module instance's private data with
501 		 * information to let it get to our redirection list when its
502 		 * close routine is called.  Cross-link it with the
503 		 * redirection list entry.
504 		 */
505 		mdp->wm_wd = wd;
506 		mdp->wm_entry = wlp;
507 		wlp->wl_data = mdp;
508 		releasef((int)arg);
509 
510 		break;
511 	    }
512 
513 	case SRIOCISREDIR:
514 		rw_enter(&iwscn_lock, RW_READER);
515 		wd = srilookup(getminor(dev));
516 		if ((f = getf((int)arg)) == NULL) {
517 			err = EBADF;
518 			break;
519 		}
520 		/*
521 		 * Return value is 1 if the argument descriptor is the current
522 		 * redirection target, and 0 otherwise.
523 		 */
524 		*rvalp = (f->f_vnode == wd->wd_list->wl_vp) ? 1 : 0;
525 		releasef((int)arg);
526 		break;
527 
528 	case I_POP: {
529 		/*
530 		 * XXX - This is a big kludge the handles a deadlock case
531 		 * when we are trying to pop off the redirection
532 		 * module.  Since this should only happen on a close
533 		 * of the device, and since it hangs the system, just
534 		 * do not allow a pop of the redirection module to happen.
535 		 * Popping other modules is allowed.
536 		 */
537 		struct stdata	*stp;
538 		char modname[FMNAMESZ + 1] = " ";
539 
540 		rw_enter(&iwscn_lock, RW_READER);
541 		wd = srilookup(getminor(dev));
542 		(void) strioctl(wd->wd_list->wl_vp, I_LOOK, (intptr_t)modname,
543 		    flag, K_TO_K, cred, rvalp);
544 		if (strcmp("redirmod", modname) == 0) {
545 			if ((f = getf((int)arg)) == NULL) {
546 				err = EBADF;
547 				break;
548 			}
549 			if ((stp = f->f_vnode->v_stream) == NULL) {
550 				err = ENOSTR;
551 				releasef((int)arg);
552 				break;
553 			}
554 			if (!(stp->sd_flag & STRCLOSE)) {
555 				releasef((int)arg);
556 				rw_exit(&iwscn_lock);
557 				cmn_err(CE_WARN, "Popping of redirection "
558 				    "module not allowed");
559 				return (EINVAL);
560 			}
561 
562 			releasef((int)arg);
563 		}
564 
565 		/* Process ioctl normally */
566 		err = strioctl(wd->wd_list->wl_vp, cmd, arg, flag, U_TO_K,
567 		    cred, rvalp);
568 		break;
569 	}
570 
571 	default:
572 		rw_enter(&iwscn_lock, RW_READER);
573 		wd = srilookup(getminor(dev));
574 		err = strioctl(wd->wd_list->wl_vp, cmd, arg, flag, U_TO_K,
575 			cred, rvalp);
576 		break;
577 	}
578 
579 	rw_exit(&iwscn_lock);
580 	return (err);
581 }
582 
583 static int
584 iwscnpoll(
585 	dev_t			dev,
586 	short			events,
587 	int			anyyet,
588 	short			*reventsp,
589 	struct pollhead		**phpp)
590 {
591 	wcd_data_t	*wd;
592 	int	error;
593 
594 	rw_enter(&iwscn_lock, RW_READER);
595 	wd = srilookup(getminor(dev));
596 	error = strpoll(wd->wd_list->wl_vp->v_stream, events, anyyet,
597 		    reventsp, phpp);
598 	rw_exit(&iwscn_lock);
599 	return (error);
600 }
601 
602 
603 /*
604  * Auxiliary routines.
605  */
606 
607 /*
608  * Additional public interfaces.
609  */
610 
611 /*
612  * Reset the current workstation console designee to the device denoted by the
613  * wl_vp field of the first entry in the redirection list.  Called from
614  * iwscnopen and from the SRIOCSREDIR case of iwscnioctl, in both cases after
615  * the target vp has been set to its new value.
616  */
617 static int
618 srreset(wcd_data_t *wd, int flag, cred_t *cred)
619 {
620 	wcrlist_t	*wlp;
621 	int		err = 0;
622 
623 	ASSERT(RW_WRITE_HELD(&iwscn_lock) || MUTEX_HELD(&iwscn_open_lock));
624 	wlp = wd->wd_list;		/* first entry */
625 
626 	/*
627 	 * If we're reverting back to the workstation console, make sure it's
628 	 * open.
629 	 */
630 	if (wlp != NULL && wlp->wl_vp == wd->wd_vp) {
631 		vnode_t	*vp = wd->wd_vp;	/* underlying device's vp */
632 
633 		err = VOP_OPEN(&vp, flag, cred);
634 		/*
635 		 * The underlying driver is not allowed to have cloned itself
636 		 * for this open.
637 		 */
638 		if (vp != wd->wd_vp) {
639 			panic("srreset: Illegal clone");
640 			/*NOTREACHED*/
641 		}
642 		if (!err)
643 			wd->wd_wsconsopen++;
644 	}
645 	return (err);
646 }
647 
648 /*
649  * Remove vp from the redirection list rooted at *rwlp, should it be there.
650  * If zap is nonzero, deallocate the entry and remove dangling references to
651  * the it from the corresponding redirecting module instance's wcm_data
652  * structure.
653  *
654  * If the entry doesn't exist upon completion, return NULL; otherwise return a
655  * pointer to it.
656  */
657 static wcrlist_t *
658 srrm(wcrlist_t **rwlp, vnode_t *vp, int zap)
659 {
660 	wcrlist_t	**delwlp;
661 	wcrlist_t	*wlp;
662 	wcm_data_t	*mdp;
663 
664 	ASSERT(RW_WRITE_HELD(&iwscn_lock) || MUTEX_HELD(&iwscn_open_lock));
665 	for (delwlp = rwlp; (wlp = *delwlp) != NULL; delwlp = &wlp->wl_next)
666 		if (wlp->wl_vp == vp)
667 			break;
668 	if (wlp == NULL)
669 		return (NULL);
670 	*delwlp = wlp->wl_next;
671 
672 	if (zap == 0)
673 		return (wlp);
674 
675 	if (wlp->wl_vp == vp)
676 		VN_RELE(vp);
677 	/*
678 	 * Make sure there are no dangling references leading to the entry
679 	 * from the corresponding redirecting module instance.
680 	 */
681 	if ((mdp = wlp->wl_data) != NULL) {
682 		mdp->wm_wd = NULL;
683 		mdp->wm_entry = NULL;
684 	}
685 
686 	kmem_free(wlp, sizeof (*wlp));
687 	return (NULL);
688 }
689 
690 /*
691  * srpop - remove redirection because the target stream is being closed.
692  * Called from wcmclose().
693  */
694 void
695 srpop(wcm_data_t *mdp, int flag, cred_t *cred)
696 {
697 	wcd_data_t	*ddp;
698 
699 	rw_enter(&iwscn_lock, RW_WRITER);
700 	if ((ddp = mdp->wm_wd) != NULL) {
701 		ASSERT(mdp->wm_entry != NULL);
702 		(void) srrm(&ddp->wd_list, mdp->wm_entry->wl_vp, 1);
703 		(void) srreset(ddp, flag, cred);
704 	}
705 	rw_exit(&iwscn_lock);
706 }
707 
708 /*
709  * Routines for allocating, deallocating, and finding wcd_data structures.
710  *
711  * For a given instantiation of the driver, its open instance structures are
712  * linked together into a list, on the assumption that there will never be
713  * enough open instances to make search efficiency a serious concern.
714  */
715 
716 /*
717  * Look up the instance structure denoted by unit.
718  */
719 static wcd_data_t *
720 srilookup(minor_t unit)
721 {
722 	wcd_data_t	*wd = wcddata;
723 
724 	ASSERT(RW_LOCK_HELD(&iwscn_lock));
725 	for (; wd != NULL && wd->wd_unit != unit; wd = wd->wd_next)
726 		continue;
727 
728 	return (wd);
729 }
730 
731 /*
732  * Allocate a wcd_data structure for the instance denoted by unit, link it in
733  * place, and return a pointer to it.  If it's already allocated, simply
734  * return a pointer to it.
735  */
736 static wcd_data_t *
737 srialloc(minor_t unit)
738 {
739 	wcd_data_t	*wdp;
740 	wcd_data_t	**wdpp;
741 
742 	ASSERT(MUTEX_HELD(&iwscn_open_lock));
743 	for (wdpp = &wcddata; (wdp = *wdpp) != NULL; wdpp = &wdp->wd_next) {
744 		if (unit < wdp->wd_unit)
745 			break;
746 		if (unit == wdp->wd_unit) {
747 			/* Already allocated and in place. */
748 			return (wdp);
749 		}
750 	}
751 	/*
752 	 * wdpp now points to the proper insertion point for unit's
753 	 * per-instance structure.
754 	 */
755 	wdp = kmem_zalloc(sizeof (*wdp), KM_SLEEP);
756 	wdp->wd_unit = unit;
757 	wdp->wd_next = *wdpp;
758 	*wdpp = wdp;
759 
760 	return (wdp);
761 }
762 
763 /*
764  * Deallocate the wcd_data structure denoted by wd and unlink it from the
765  * list of open instances.
766  */
767 static void
768 sridealloc(wcd_data_t *wd)
769 {
770 	wcd_data_t	*wdp;
771 	wcd_data_t	**wdpp;
772 
773 	ASSERT(RW_WRITE_HELD(&iwscn_lock) || MUTEX_HELD(&iwscn_open_lock));
774 	for (wdpp = &wcddata; (wdp = *wdpp) != NULL; wdpp = &wdp->wd_next)
775 		if (wd == wdp)
776 			break;
777 	if (wdp == NULL) {
778 		/*
779 		 * Not there.  This should probably be a panic.
780 		 */
781 		return;
782 	}
783 	*wdpp = wdp->wd_next;
784 	kmem_free(wdp, sizeof (*wdp));
785 }
786 
787 
788 /*
789  * Push vp onto the redirection list rooted at *wlpp.  If it's already there,
790  * move it to the front position.  Return a pointer to its list entry.
791  *
792  * N.B.: It is the caller's responsibility to initialize all fields in the
793  * entry other than the wl_next and wl_vp fields.
794  */
795 static wcrlist_t *
796 srpush(wcrlist_t **wlpp, vnode_t *vp)
797 {
798 	wcrlist_t	*nwlp;
799 
800 	ASSERT(RW_WRITE_HELD(&iwscn_lock) || MUTEX_HELD(&iwscn_open_lock));
801 	if ((nwlp = srrm(wlpp, vp, 0)) == NULL) {
802 		nwlp = kmem_zalloc(sizeof (*nwlp), KM_SLEEP);
803 		nwlp->wl_vp = vp;
804 		/*
805 		 * The hold will prevent underlying device from closing
806 		 * while this vnode is still on the redirection list.
807 		 */
808 		VN_HOLD(vp);
809 	}
810 	nwlp->wl_next = *wlpp;
811 	*wlpp = nwlp;
812 
813 	return (nwlp);
814 }
815