xref: /titanic_44/usr/src/uts/common/fs/specfs/specsubr.c (revision edcc07547a39d6570197493a9836083bd6b2a197)
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 /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
27 /*	  All Rights Reserved  	*/
28 
29 /*
30  * University Copyright- Copyright (c) 1982, 1986, 1988
31  * The Regents of the University of California
32  * All Rights Reserved
33  *
34  * University Acknowledgment- Portions of this document are derived from
35  * software developed by the University of California, Berkeley, and its
36  * contributors.
37  */
38 
39 
40 #pragma ident	"%Z%%M%	%I%	%E% SMI"
41 
42 #include <sys/types.h>
43 #include <sys/t_lock.h>
44 #include <sys/param.h>
45 #include <sys/systm.h>
46 #include <sys/buf.h>
47 #include <sys/conf.h>
48 #include <sys/cred.h>
49 #include <sys/kmem.h>
50 #include <sys/sysmacros.h>
51 #include <sys/vfs.h>
52 #include <sys/vfs_opreg.h>
53 #include <sys/vnode.h>
54 #include <sys/fs/snode.h>
55 #include <sys/fs/fifonode.h>
56 #include <sys/debug.h>
57 #include <sys/errno.h>
58 #include <sys/time.h>
59 #include <sys/file.h>
60 #include <sys/open.h>
61 #include <sys/user.h>
62 #include <sys/termios.h>
63 #include <sys/stream.h>
64 #include <sys/strsubr.h>
65 #include <sys/autoconf.h>
66 #include <sys/esunddi.h>
67 #include <sys/flock.h>
68 #include <sys/modctl.h>
69 
70 struct vfs spec_vfs;
71 static dev_t specdev;
72 struct kmem_cache *snode_cache;
73 int spec_debug = 0;
74 
75 static struct snode *sfind(dev_t, vtype_t, struct vnode *);
76 static struct vnode *get_cvp(dev_t, vtype_t, struct snode *, int *);
77 static void sinsert(struct snode *);
78 
79 struct vnode *
80 specvp_devfs(
81 	struct vnode	*realvp,
82 	dev_t		dev,
83 	vtype_t		vtyp,
84 	struct cred	*cr,
85 	dev_info_t	*dip)
86 {
87 	struct vnode	*vp;
88 
89 	ASSERT(realvp && dip);
90 	vp = specvp(realvp, dev, vtyp, cr);
91 	ASSERT(vp);
92 
93 	/* associate a dip hold with the common snode's s_dip pointer */
94 	spec_assoc_vp_with_devi(vp, dip);
95 	return (vp);
96 }
97 
98 /*
99  * Return a shadow special vnode for the given dev.
100  * If no snode exists for this dev create one and put it
101  * in a table hashed by <dev, realvp>.  If the snode for
102  * this dev is already in the table return it (ref count is
103  * incremented by sfind).  The snode will be flushed from the
104  * table when spec_inactive calls sdelete.
105  *
106  * The fsid is inherited from the real vnode so that clones
107  * can be found.
108  *
109  */
110 struct vnode *
111 specvp(
112 	struct vnode	*vp,
113 	dev_t		dev,
114 	vtype_t		type,
115 	struct cred	*cr)
116 {
117 	struct snode *sp;
118 	struct snode *nsp;
119 	struct snode *csp;
120 	struct vnode *svp;
121 	struct vattr va;
122 	int	rc;
123 	int	used_csp = 0;		/* Did we use pre-allocated csp */
124 
125 	if (vp == NULL)
126 		return (NULL);
127 	if (vp->v_type == VFIFO)
128 		return (fifovp(vp, cr));
129 
130 	ASSERT(vp->v_type == type);
131 	ASSERT(vp->v_rdev == dev);
132 
133 	/*
134 	 * Pre-allocate snodes before holding any locks in case we block
135 	 */
136 	nsp = kmem_cache_alloc(snode_cache, KM_SLEEP);
137 	csp = kmem_cache_alloc(snode_cache, KM_SLEEP);
138 
139 	/*
140 	 * Get the time attributes outside of the stable lock since
141 	 * this operation may block. Unfortunately, it may not have
142 	 * been required if the snode is in the cache.
143 	 */
144 	va.va_mask = AT_FSID | AT_TIMES;
145 	rc = VOP_GETATTR(vp, &va, 0, cr, NULL);	/* XXX may block! */
146 
147 	mutex_enter(&stable_lock);
148 	if ((sp = sfind(dev, type, vp)) == NULL) {
149 		struct vnode *cvp;
150 
151 		sp = nsp;	/* Use pre-allocated snode */
152 		svp = STOV(sp);
153 
154 		sp->s_realvp	= vp;
155 		VN_HOLD(vp);
156 		sp->s_commonvp	= NULL;
157 		sp->s_dev	= dev;
158 		sp->s_dip	= NULL;
159 		sp->s_nextr	= NULL;
160 		sp->s_list	= NULL;
161 		sp->s_plcy	= NULL;
162 		sp->s_size	= 0;
163 		sp->s_flag	= 0;
164 		if (rc == 0) {
165 			/*
166 			 * Set times in snode to those in the vnode.
167 			 */
168 			sp->s_fsid = va.va_fsid;
169 			sp->s_atime = va.va_atime.tv_sec;
170 			sp->s_mtime = va.va_mtime.tv_sec;
171 			sp->s_ctime = va.va_ctime.tv_sec;
172 		} else {
173 			sp->s_fsid = specdev;
174 			sp->s_atime = 0;
175 			sp->s_mtime = 0;
176 			sp->s_ctime = 0;
177 		}
178 		sp->s_count	= 0;
179 		sp->s_mapcnt	= 0;
180 
181 		vn_reinit(svp);
182 		svp->v_flag	= (vp->v_flag & VROOT);
183 		svp->v_vfsp	= vp->v_vfsp;
184 		VFS_HOLD(svp->v_vfsp);
185 		svp->v_type	= type;
186 		svp->v_rdev	= dev;
187 		(void) vn_copypath(vp, svp);
188 		if (type == VBLK || type == VCHR) {
189 			cvp = get_cvp(dev, type, csp, &used_csp);
190 			svp->v_stream = cvp->v_stream;
191 
192 			sp->s_commonvp = cvp;
193 		}
194 		vn_exists(svp);
195 		sinsert(sp);
196 		mutex_exit(&stable_lock);
197 		if (used_csp == 0) {
198 			/* Didn't use pre-allocated snode so free it */
199 			kmem_cache_free(snode_cache, csp);
200 		}
201 	} else {
202 		mutex_exit(&stable_lock);
203 		/* free unused snode memory */
204 		kmem_cache_free(snode_cache, nsp);
205 		kmem_cache_free(snode_cache, csp);
206 	}
207 	return (STOV(sp));
208 }
209 
210 /*
211  * Return a special vnode for the given dev; no vnode is supplied
212  * for it to shadow.  Always create a new snode and put it in the
213  * table hashed by <dev, NULL>.  The snode will be flushed from the
214  * table when spec_inactive() calls sdelete().  The association of
215  * this node with a attached instance of hardware is not made until
216  * spec_open time.
217  *
218  * N.B. Assumes caller takes on responsibility of making sure no one
219  * else is creating a snode for (dev, type) at this time.
220  */
221 struct vnode *
222 makespecvp(dev_t dev, vtype_t type)
223 {
224 	struct snode *sp;
225 	struct vnode *svp, *cvp;
226 	time_t now;
227 
228 	sp = kmem_cache_alloc(snode_cache, KM_SLEEP);
229 	svp = STOV(sp);
230 	cvp = commonvp(dev, type);
231 	now = gethrestime_sec();
232 
233 	sp->s_realvp	= NULL;
234 	sp->s_commonvp	= cvp;
235 	sp->s_dev	= dev;
236 	sp->s_dip	= NULL;
237 	sp->s_nextr	= NULL;
238 	sp->s_list	= NULL;
239 	sp->s_plcy	= NULL;
240 	sp->s_size	= 0;
241 	sp->s_flag	= 0;
242 	sp->s_fsid	= specdev;
243 	sp->s_atime	= now;
244 	sp->s_mtime	= now;
245 	sp->s_ctime	= now;
246 	sp->s_count	= 0;
247 	sp->s_mapcnt	= 0;
248 
249 	vn_reinit(svp);
250 	svp->v_vfsp	= &spec_vfs;
251 	svp->v_stream	= cvp->v_stream;
252 	svp->v_type	= type;
253 	svp->v_rdev	= dev;
254 
255 	vn_exists(svp);
256 	mutex_enter(&stable_lock);
257 	sinsert(sp);
258 	mutex_exit(&stable_lock);
259 
260 	return (svp);
261 }
262 
263 
264 /*
265  * This function is called from spec_assoc_vp_with_devi(). That function
266  * associates a "new" dip with a common snode, releasing (any) old dip
267  * in the process. This function (spec_assoc_fence()) looks at the "new dip"
268  * and determines whether the snode should be fenced of or not. As the table
269  * below indicates, the value of old-dip is a don't care for all cases.
270  *
271  * old-dip	new-dip		common-snode
272  * =========================================
273  * Don't care	NULL		unfence
274  * Don't care	retired		fence
275  * Don't care	not-retired	unfence
276  *
277  * Since old-dip value is a "don't care", it is not passed into this function.
278  */
279 static void
280 spec_assoc_fence(dev_info_t *ndip, vnode_t *vp)
281 {
282 	int		fence;
283 	struct snode	*csp;
284 
285 	ASSERT(vp);
286 	ASSERT(vn_matchops(vp, spec_getvnodeops()));
287 
288 	fence = 0;
289 	if (ndip != NULL) {
290 		mutex_enter(&DEVI(ndip)->devi_lock);
291 		if (DEVI(ndip)->devi_flags & DEVI_RETIRED)
292 			fence = 1;
293 		mutex_exit(&DEVI(ndip)->devi_lock);
294 	}
295 
296 	csp = VTOCS(vp);
297 	ASSERT(csp);
298 
299 	/* SFENCED flag only set on common snode */
300 	mutex_enter(&csp->s_lock);
301 	if (fence)
302 		csp->s_flag |= SFENCED;
303 	else
304 		csp->s_flag &= ~SFENCED;
305 	mutex_exit(&csp->s_lock);
306 
307 	FENDBG((CE_NOTE, "%sfenced common snode (%p) for new dip=%p",
308 	    fence ? "" : "un", (void *)csp, (void *)ndip));
309 }
310 
311 /*
312  * Associate the common snode with a devinfo node.  This is called from:
313  *
314  *   1) specvp_devfs to associate a specfs node with the dip attached
315  *	by devfs.
316  *
317  *   2) spec_open after path reconstruction and attach.
318  *
319  *   3) From dacf processing to associate a makespecvp node with
320  *	the dip that dacf postattach processing is being performed on.
321  *	This association is made prior to open to avoid recursion issues.
322  *
323  *   4) From ddi_assoc_queue_with_devi to change vnode association as part of
324  *	DL_ATTACH/DL_DETACH processing (SDIPSET already set).  The call
325  *	from ddi_assoc_queue_with_devi may specify a NULL dip.
326  *
327  * We put an extra hold on the devinfo node passed in as we establish it as
328  * the new s_dip pointer.  Any hold associated with the prior s_dip pointer
329  * is released. The new hold will stay active until another call to
330  * spec_assoc_vp_with_devi or until the common snode is destroyed by
331  * spec_inactive after the last VN_RELE of the common node. This devinfo hold
332  * transfers across a clone open except in the clone_dev case, where the clone
333  * driver is no longer required after open.
334  *
335  * When SDIPSET is set and s_dip is NULL, the vnode has an association with
336  * the driver even though there is currently no association with a specific
337  * hardware instance.
338  */
339 void
340 spec_assoc_vp_with_devi(struct vnode *vp, dev_info_t *dip)
341 {
342 	struct snode	*csp;
343 	dev_info_t	*olddip;
344 
345 	ASSERT(vp);
346 
347 	/*
348 	 * Don't establish a NULL association for a vnode associated with the
349 	 * clone driver.  The qassociate(, -1) call from a streams driver's
350 	 * open implementation to indicate support for qassociate has the
351 	 * side-effect of this type of spec_assoc_vp_with_devi call. This
352 	 * call should not change the the association of the pre-clone
353 	 * vnode associated with the clone driver, the post-clone newdev
354 	 * association will be established later by spec_clone().
355 	 */
356 	if ((dip == NULL) && (getmajor(vp->v_rdev) == clone_major))
357 		return;
358 
359 	/* hold the new */
360 	if (dip)
361 		e_ddi_hold_devi(dip);
362 
363 	csp = VTOS(VTOS(vp)->s_commonvp);
364 	mutex_enter(&csp->s_lock);
365 	olddip = csp->s_dip;
366 	csp->s_dip = dip;
367 	csp->s_flag |= SDIPSET;
368 
369 	/* If association changes then invalidate cached size */
370 	if (olddip != dip)
371 		csp->s_flag &= ~SSIZEVALID;
372 	mutex_exit(&csp->s_lock);
373 
374 	spec_assoc_fence(dip, vp);
375 
376 	/* release the old */
377 	if (olddip)
378 		ddi_release_devi(olddip);
379 }
380 
381 /*
382  * Return the held dip associated with the specified snode.
383  */
384 dev_info_t *
385 spec_hold_devi_by_vp(struct vnode *vp)
386 {
387 	struct snode	*csp;
388 	dev_info_t	*dip;
389 
390 	ASSERT(vn_matchops(vp, spec_getvnodeops()));
391 
392 	csp = VTOS(VTOS(vp)->s_commonvp);
393 	dip = csp->s_dip;
394 	if (dip)
395 		e_ddi_hold_devi(dip);
396 	return (dip);
397 }
398 
399 /*
400  * Find a special vnode that refers to the given device
401  * of the given type.  Never return a "common" vnode.
402  * Return NULL if a special vnode does not exist.
403  * HOLD the vnode before returning it.
404  */
405 struct vnode *
406 specfind(dev_t dev, vtype_t type)
407 {
408 	struct snode *st;
409 	struct vnode *nvp;
410 
411 	mutex_enter(&stable_lock);
412 	st = stable[STABLEHASH(dev)];
413 	while (st != NULL) {
414 		if (st->s_dev == dev) {
415 			nvp = STOV(st);
416 			if (nvp->v_type == type && st->s_commonvp != nvp) {
417 				VN_HOLD(nvp);
418 				mutex_exit(&stable_lock);
419 				return (nvp);
420 			}
421 		}
422 		st = st->s_next;
423 	}
424 	mutex_exit(&stable_lock);
425 	return (NULL);
426 }
427 
428 /*
429  * Loop through the snode cache looking for snodes referencing dip.
430  *
431  * This function determines if a devinfo node is "BUSY" from the perspective
432  * of having an active vnode associated with the device, which represents a
433  * dependency on the device's services.  This function is needed because a
434  * devinfo node can have a non-zero devi_ref and still NOT be "BUSY" when,
435  * for instance, the framework is manipulating the node (has an open
436  * ndi_hold_devi).
437  *
438  * Returns:
439  *	DEVI_REFERENCED		- if dip is referenced
440  *	DEVI_NOT_REFERENCED	- if dip is not referenced
441  */
442 int
443 devi_stillreferenced(dev_info_t *dip)
444 {
445 	struct snode	*sp;
446 	int		i;
447 
448 	/* if no hold then there can't be an snode with s_dip == dip */
449 	if (e_ddi_devi_holdcnt(dip) == 0)
450 		return (DEVI_NOT_REFERENCED);
451 
452 	mutex_enter(&stable_lock);
453 	for (i = 0; i < STABLESIZE; i++) {
454 		for (sp = stable[i]; sp != NULL; sp = sp->s_next) {
455 			if (sp->s_dip == dip) {
456 				mutex_exit(&stable_lock);
457 				return (DEVI_REFERENCED);
458 			}
459 		}
460 	}
461 	mutex_exit(&stable_lock);
462 	return (DEVI_NOT_REFERENCED);
463 }
464 
465 /*
466  * Given an snode, returns the open count and the dip
467  * associated with that snode
468  * Assumes the caller holds the appropriate locks
469  * to prevent snode and/or dip from going away.
470  * Returns:
471  *	-1	No associated dip
472  *	>= 0	Number of opens.
473  */
474 int
475 spec_devi_open_count(struct snode *sp, dev_info_t **dipp)
476 {
477 	dev_info_t *dip;
478 	uint_t count;
479 	struct vnode *vp;
480 
481 	ASSERT(sp);
482 	ASSERT(dipp);
483 
484 	vp = STOV(sp);
485 
486 	*dipp = NULL;
487 
488 	/*
489 	 * We are only interested in common snodes. Only common snodes
490 	 * get their s_count fields bumped up on opens.
491 	 */
492 	if (sp->s_commonvp != vp || (dip = sp->s_dip) == NULL)
493 		return (-1);
494 
495 	mutex_enter(&sp->s_lock);
496 	count = sp->s_count + sp->s_mapcnt;
497 	if (sp->s_flag & SLOCKED)
498 		count++;
499 	mutex_exit(&sp->s_lock);
500 
501 	*dipp = dip;
502 
503 	return (count);
504 }
505 
506 /*
507  * Given a device vnode, return the common
508  * vnode associated with it.
509  */
510 struct vnode *
511 common_specvp(struct vnode *vp)
512 {
513 	struct snode *sp;
514 
515 	if ((vp->v_type != VBLK) && (vp->v_type != VCHR) ||
516 	    !vn_matchops(vp, spec_getvnodeops()))
517 		return (vp);
518 	sp = VTOS(vp);
519 	return (sp->s_commonvp);
520 }
521 
522 /*
523  * Returns a special vnode for the given dev.  The vnode is the
524  * one which is "common" to all the snodes which represent the
525  * same device.
526  * Similar to commonvp() but doesn't acquire the stable_lock, and
527  * may use a pre-allocated snode provided by caller.
528  */
529 static struct vnode *
530 get_cvp(
531 	dev_t		dev,
532 	vtype_t		type,
533 	struct snode	*nsp,		/* pre-allocated snode */
534 	int		*used_nsp)	/* flag indicating if we use nsp */
535 {
536 	struct snode *sp;
537 	struct vnode *svp;
538 
539 	ASSERT(MUTEX_HELD(&stable_lock));
540 	if ((sp = sfind(dev, type, NULL)) == NULL) {
541 		sp = nsp;		/* Use pre-allocated snode */
542 		*used_nsp = 1;		/* return value */
543 		svp = STOV(sp);
544 
545 		sp->s_realvp	= NULL;
546 		sp->s_commonvp	= svp;		/* points to itself */
547 		sp->s_dev	= dev;
548 		sp->s_dip	= NULL;
549 		sp->s_nextr	= NULL;
550 		sp->s_list	= NULL;
551 		sp->s_plcy	= NULL;
552 		sp->s_size	= UNKNOWN_SIZE;
553 		sp->s_flag	= 0;
554 		sp->s_fsid	= specdev;
555 		sp->s_atime	= 0;
556 		sp->s_mtime	= 0;
557 		sp->s_ctime	= 0;
558 		sp->s_count	= 0;
559 		sp->s_mapcnt	= 0;
560 
561 		vn_reinit(svp);
562 		svp->v_vfsp	= &spec_vfs;
563 		svp->v_type	= type;
564 		svp->v_rdev	= dev;
565 		vn_exists(svp);
566 		sinsert(sp);
567 	} else
568 		*used_nsp = 0;
569 	return (STOV(sp));
570 }
571 
572 /*
573  * Returns a special vnode for the given dev.  The vnode is the
574  * one which is "common" to all the snodes which represent the
575  * same device.  For use ONLY by SPECFS.
576  */
577 struct vnode *
578 commonvp(dev_t dev, vtype_t type)
579 {
580 	struct snode *sp, *nsp;
581 	struct vnode *svp;
582 
583 	/* Pre-allocate snode in case we might block */
584 	nsp = kmem_cache_alloc(snode_cache, KM_SLEEP);
585 
586 	mutex_enter(&stable_lock);
587 	if ((sp = sfind(dev, type, NULL)) == NULL) {
588 		sp = nsp;		/* Use pre-alloced snode */
589 		svp = STOV(sp);
590 
591 		sp->s_realvp	= NULL;
592 		sp->s_commonvp	= svp;		/* points to itself */
593 		sp->s_dev	= dev;
594 		sp->s_dip	= NULL;
595 		sp->s_nextr	= NULL;
596 		sp->s_list	= NULL;
597 		sp->s_plcy	= NULL;
598 		sp->s_size	= UNKNOWN_SIZE;
599 		sp->s_flag	= 0;
600 		sp->s_fsid	= specdev;
601 		sp->s_atime	= 0;
602 		sp->s_mtime	= 0;
603 		sp->s_ctime	= 0;
604 		sp->s_count	= 0;
605 		sp->s_mapcnt	= 0;
606 
607 		vn_reinit(svp);
608 		svp->v_vfsp	= &spec_vfs;
609 		svp->v_type	= type;
610 		svp->v_rdev	= dev;
611 		vn_exists(svp);
612 		sinsert(sp);
613 		mutex_exit(&stable_lock);
614 	} else {
615 		mutex_exit(&stable_lock);
616 		/* Didn't need the pre-allocated snode */
617 		kmem_cache_free(snode_cache, nsp);
618 	}
619 	return (STOV(sp));
620 }
621 
622 /*
623  * Snode lookup stuff.
624  * These routines maintain a table of snodes hashed by dev so
625  * that the snode for an dev can be found if it already exists.
626  */
627 struct snode *stable[STABLESIZE];
628 int		stablesz = STABLESIZE;
629 kmutex_t	stable_lock;
630 
631 /*
632  * Put a snode in the table.
633  */
634 static void
635 sinsert(struct snode *sp)
636 {
637 	ASSERT(MUTEX_HELD(&stable_lock));
638 	sp->s_next = stable[STABLEHASH(sp->s_dev)];
639 	stable[STABLEHASH(sp->s_dev)] = sp;
640 }
641 
642 /*
643  * Remove an snode from the hash table.
644  * The realvp is not released here because spec_inactive() still
645  * needs it to do a spec_fsync().
646  */
647 void
648 sdelete(struct snode *sp)
649 {
650 	struct snode *st;
651 	struct snode *stprev = NULL;
652 
653 	ASSERT(MUTEX_HELD(&stable_lock));
654 	st = stable[STABLEHASH(sp->s_dev)];
655 	while (st != NULL) {
656 		if (st == sp) {
657 			if (stprev == NULL)
658 				stable[STABLEHASH(sp->s_dev)] = st->s_next;
659 			else
660 				stprev->s_next = st->s_next;
661 			break;
662 		}
663 		stprev = st;
664 		st = st->s_next;
665 	}
666 }
667 
668 /*
669  * Lookup an snode by <dev, type, vp>.
670  * ONLY looks for snodes with non-NULL s_realvp members and
671  * common snodes (with s_commonvp pointing to its vnode).
672  *
673  * If vp is NULL, only return commonvp. Otherwise return
674  * shadow vp with both shadow and common vp's VN_HELD.
675  */
676 static struct snode *
677 sfind(
678 	dev_t	dev,
679 	vtype_t	type,
680 	struct vnode *vp)
681 {
682 	struct snode *st;
683 	struct vnode *svp;
684 
685 	ASSERT(MUTEX_HELD(&stable_lock));
686 	st = stable[STABLEHASH(dev)];
687 	while (st != NULL) {
688 		svp = STOV(st);
689 		if (st->s_dev == dev && svp->v_type == type &&
690 		    VN_CMP(st->s_realvp, vp) &&
691 		    (vp != NULL || st->s_commonvp == svp) &&
692 		    (vp == NULL || st->s_realvp->v_vfsp == vp->v_vfsp)) {
693 			VN_HOLD(svp);
694 			return (st);
695 		}
696 		st = st->s_next;
697 	}
698 	return (NULL);
699 }
700 
701 /*
702  * Mark the accessed, updated, or changed times in an snode
703  * with the current time.
704  */
705 void
706 smark(struct snode *sp, int flag)
707 {
708 	time_t	now = gethrestime_sec();
709 
710 	/* check for change to avoid unnecessary locking */
711 	ASSERT((flag & ~(SACC|SUPD|SCHG)) == 0);
712 	if (((flag & sp->s_flag) != flag) ||
713 	    ((flag & SACC) && (sp->s_atime != now)) ||
714 	    ((flag & SUPD) && (sp->s_mtime != now)) ||
715 	    ((flag & SCHG) && (sp->s_ctime != now))) {
716 		/* lock and update */
717 		mutex_enter(&sp->s_lock);
718 		sp->s_flag |= flag;
719 		if (flag & SACC)
720 			sp->s_atime = now;
721 		if (flag & SUPD)
722 			sp->s_mtime = now;
723 		if (flag & SCHG)
724 			sp->s_ctime = now;
725 		mutex_exit(&sp->s_lock);
726 	}
727 }
728 
729 /*
730  * Return the maximum file offset permitted for this device.
731  * -1 means unrestricted.  SLOFFSET is associated with D_64BIT.
732  *
733  * On a 32-bit kernel this will limit:
734  *   o	D_64BIT devices to SPEC_MAXOFFSET_T.
735  *   o	non-D_64BIT character drivers to a 32-bit offset (MAXOFF_T).
736  */
737 offset_t
738 spec_maxoffset(struct vnode *vp)
739 {
740 	struct snode *sp = VTOS(vp);
741 	struct snode *csp = VTOS(sp->s_commonvp);
742 
743 	if (STREAMSTAB(getmajor(sp->s_dev)))
744 		return ((offset_t)-1);
745 	else if (csp->s_flag & SANYOFFSET)	/* D_U64BIT */
746 		return ((offset_t)-1);
747 #ifdef _ILP32
748 	if (csp->s_flag & SLOFFSET)		/* D_64BIT */
749 		return (SPEC_MAXOFFSET_T);
750 #endif	/* _ILP32 */
751 	return (MAXOFF_T);
752 }
753 
754 /*ARGSUSED*/
755 static int
756 snode_constructor(void *buf, void *cdrarg, int kmflags)
757 {
758 	struct snode *sp = buf;
759 	struct vnode *vp;
760 
761 	vp = vn_alloc(KM_SLEEP);
762 
763 	sp->s_vnode = vp;
764 
765 	vn_setops(vp, spec_getvnodeops());
766 	vp->v_data = (caddr_t)sp;
767 
768 	mutex_init(&sp->s_lock, NULL, MUTEX_DEFAULT, NULL);
769 	cv_init(&sp->s_cv, NULL, CV_DEFAULT, NULL);
770 	return (0);
771 }
772 
773 /*ARGSUSED1*/
774 static void
775 snode_destructor(void *buf, void *cdrarg)
776 {
777 	struct snode *sp = buf;
778 	struct vnode *vp = STOV(sp);
779 
780 	mutex_destroy(&sp->s_lock);
781 	cv_destroy(&sp->s_cv);
782 
783 	vn_free(vp);
784 }
785 
786 
787 int
788 specinit(int fstype, char *name)
789 {
790 	static const fs_operation_def_t spec_vfsops_template[] = {
791 		VFSNAME_SYNC, { .vfs_sync = spec_sync },
792 		NULL, NULL
793 	};
794 	extern struct vnodeops *spec_vnodeops;
795 	extern const fs_operation_def_t spec_vnodeops_template[];
796 	struct vfsops *spec_vfsops;
797 	int error;
798 	dev_t dev;
799 
800 	/*
801 	 * Associate vfs and vnode operations.
802 	 */
803 	error = vfs_setfsops(fstype, spec_vfsops_template, &spec_vfsops);
804 	if (error != 0) {
805 		cmn_err(CE_WARN, "specinit: bad vfs ops template");
806 		return (error);
807 	}
808 
809 	error = vn_make_ops(name, spec_vnodeops_template, &spec_vnodeops);
810 	if (error != 0) {
811 		(void) vfs_freevfsops_by_type(fstype);
812 		cmn_err(CE_WARN, "specinit: bad vnode ops template");
813 		return (error);
814 	}
815 
816 	mutex_init(&stable_lock, NULL, MUTEX_DEFAULT, NULL);
817 	mutex_init(&spec_syncbusy, NULL, MUTEX_DEFAULT, NULL);
818 
819 	/*
820 	 * Create snode cache
821 	 */
822 	snode_cache = kmem_cache_create("snode_cache", sizeof (struct snode),
823 		0, snode_constructor, snode_destructor, NULL, NULL, NULL, 0);
824 
825 	/*
826 	 * Associate vfs operations with spec_vfs
827 	 */
828 	VFS_INIT(&spec_vfs, spec_vfsops, (caddr_t)NULL);
829 	if ((dev = getudev()) == -1)
830 		dev = 0;
831 	specdev = makedevice(dev, 0);
832 	return (0);
833 }
834 
835 int
836 device_close(struct vnode *vp, int flag, struct cred *cr)
837 {
838 	struct snode *sp = VTOS(vp);
839 	enum vtype type = vp->v_type;
840 	struct vnode *cvp;
841 	dev_t dev;
842 	int error;
843 
844 	dev = sp->s_dev;
845 	cvp = sp->s_commonvp;
846 
847 	switch (type) {
848 
849 	case VCHR:
850 		if (STREAMSTAB(getmajor(dev))) {
851 			if (cvp->v_stream != NULL)
852 				error = strclose(cvp, flag, cr);
853 			vp->v_stream = NULL;
854 		} else
855 			error = dev_close(dev, flag, OTYP_CHR, cr);
856 		break;
857 
858 	case VBLK:
859 		/*
860 		 * On last close a block device we must
861 		 * invalidate any in-core blocks so that we
862 		 * can, for example, change floppy disks.
863 		 */
864 		(void) spec_putpage(cvp, (offset_t)0,
865 		    (size_t)0, B_INVAL|B_FORCE, cr, NULL);
866 		bflush(dev);
867 		binval(dev);
868 		error = dev_close(dev, flag, OTYP_BLK, cr);
869 		break;
870 	default:
871 		panic("device_close: not a device");
872 		/*NOTREACHED*/
873 	}
874 
875 	return (error);
876 }
877 
878 struct vnode *
879 makectty(vnode_t *ovp)
880 {
881 	vnode_t *vp;
882 
883 	if (vp = makespecvp(ovp->v_rdev, VCHR)) {
884 		struct snode *sp;
885 		struct snode *csp;
886 		struct vnode *cvp;
887 
888 		sp = VTOS(vp);
889 		cvp = sp->s_commonvp;
890 		csp = VTOS(cvp);
891 		mutex_enter(&csp->s_lock);
892 		csp->s_count++;
893 		mutex_exit(&csp->s_lock);
894 	}
895 
896 	return (vp);
897 }
898 
899 void
900 spec_snode_walk(int (*callback)(struct snode *sp, void *arg), void *arg)
901 {
902 	struct snode	*sp;
903 	int		i;
904 
905 	ASSERT(callback);
906 
907 	mutex_enter(&stable_lock);
908 	for (i = 0; i < STABLESIZE; i++) {
909 		for (sp = stable[i]; sp; sp = sp->s_next) {
910 			if (callback(sp, arg) != DDI_WALK_CONTINUE)
911 				goto out;
912 		}
913 	}
914 out:
915 	mutex_exit(&stable_lock);
916 }
917 
918 int
919 spec_is_clone(vnode_t *vp)
920 {
921 	struct snode *sp;
922 
923 	if (vn_matchops(vp, spec_getvnodeops())) {
924 		sp = VTOS(vp);
925 		return ((sp->s_flag & SCLONE) ? 1 : 0);
926 	}
927 
928 	return (0);
929 }
930 
931 int
932 spec_is_selfclone(vnode_t *vp)
933 {
934 	struct snode *sp;
935 
936 	if (vn_matchops(vp, spec_getvnodeops())) {
937 		sp = VTOS(vp);
938 		return ((sp->s_flag & SSELFCLONE) ? 1 : 0);
939 	}
940 
941 	return (0);
942 }
943 
944 /*
945  * We may be invoked with a NULL vp in which case we fence off
946  * all snodes associated with dip
947  */
948 int
949 spec_fence_snode(dev_info_t *dip, struct vnode *vp)
950 {
951 	struct snode	*sp;
952 	struct snode	*csp;
953 	int		retired;
954 	int		i;
955 	char		*path;
956 	int		emitted;
957 
958 	ASSERT(dip);
959 
960 	retired = 0;
961 	mutex_enter(&DEVI(dip)->devi_lock);
962 	if (DEVI(dip)->devi_flags & DEVI_RETIRED)
963 		retired = 1;
964 	mutex_exit(&DEVI(dip)->devi_lock);
965 
966 	if (!retired)
967 		return (0);
968 
969 	path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
970 	(void) ddi_pathname(dip, path);
971 
972 
973 	if (vp != NULL) {
974 		ASSERT(vn_matchops(vp, spec_getvnodeops()));
975 		csp = VTOCS(vp);
976 		ASSERT(csp);
977 		mutex_enter(&csp->s_lock);
978 		csp->s_flag |= SFENCED;
979 		mutex_exit(&csp->s_lock);
980 		FENDBG((CE_NOTE, "fenced off snode(%p) for dip: %s",
981 		    (void *)csp, path));
982 		kmem_free(path, MAXPATHLEN);
983 		return (0);
984 	}
985 
986 	emitted = 0;
987 	mutex_enter(&stable_lock);
988 	for (i = 0; i < STABLESIZE; i++) {
989 		for (sp = stable[i]; sp != NULL; sp = sp->s_next) {
990 			ASSERT(sp->s_commonvp);
991 			csp = VTOS(sp->s_commonvp);
992 			if (csp->s_dip == dip) {
993 				/* fence off the common snode */
994 				mutex_enter(&csp->s_lock);
995 				csp->s_flag |= SFENCED;
996 				mutex_exit(&csp->s_lock);
997 				if (!emitted) {
998 					FENDBG((CE_NOTE, "fenced 1 of N"));
999 					emitted++;
1000 				}
1001 			}
1002 		}
1003 	}
1004 	mutex_exit(&stable_lock);
1005 
1006 	FENDBG((CE_NOTE, "fenced off all snodes for dip: %s", path));
1007 	kmem_free(path, MAXPATHLEN);
1008 
1009 	return (0);
1010 }
1011 
1012 
1013 int
1014 spec_unfence_snode(dev_info_t *dip)
1015 {
1016 	struct snode	*sp;
1017 	struct snode	*csp;
1018 	int		i;
1019 	char		*path;
1020 	int		emitted;
1021 
1022 	ASSERT(dip);
1023 
1024 	path = kmem_alloc(MAXPATHLEN, KM_SLEEP);
1025 	(void) ddi_pathname(dip, path);
1026 
1027 	emitted = 0;
1028 	mutex_enter(&stable_lock);
1029 	for (i = 0; i < STABLESIZE; i++) {
1030 		for (sp = stable[i]; sp != NULL; sp = sp->s_next) {
1031 			ASSERT(sp->s_commonvp);
1032 			csp = VTOS(sp->s_commonvp);
1033 			ASSERT(csp);
1034 			if (csp->s_dip == dip) {
1035 				/* unfence the common snode */
1036 				mutex_enter(&csp->s_lock);
1037 				csp->s_flag &= ~SFENCED;
1038 				mutex_exit(&csp->s_lock);
1039 				if (!emitted) {
1040 					FENDBG((CE_NOTE, "unfenced 1 of N"));
1041 					emitted++;
1042 				}
1043 			}
1044 		}
1045 	}
1046 	mutex_exit(&stable_lock);
1047 
1048 	FENDBG((CE_NOTE, "unfenced all snodes for dip: %s", path));
1049 	kmem_free(path, MAXPATHLEN);
1050 
1051 	return (0);
1052 }
1053