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