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