xref: /titanic_50/usr/src/uts/common/fs/specfs/specsubr.c (revision a1e3386ed23bbb4894435257b2006a5f8edcc9ea)
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/vnode.h>
53 #include <sys/fs/snode.h>
54 #include <sys/fs/fifonode.h>
55 #include <sys/debug.h>
56 #include <sys/errno.h>
57 #include <sys/time.h>
58 #include <sys/file.h>
59 #include <sys/open.h>
60 #include <sys/user.h>
61 #include <sys/termios.h>
62 #include <sys/stream.h>
63 #include <sys/strsubr.h>
64 #include <sys/autoconf.h>
65 #include <sys/esunddi.h>
66 #include <sys/flock.h>
67 #include <sys/modctl.h>
68 
69 struct vfs spec_vfs;
70 static dev_t specdev;
71 struct kmem_cache *snode_cache;
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);	/* 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  * Associate the common snode with a devinfo node.  This is called from:
263  *
264  *   1) specvp_devfs to associate a specfs node with the dip attached
265  *	by devfs.
266  *
267  *   2) spec_open after path reconstruction and attach.
268  *
269  *   3) From dacf processing to associate a makespecvp node with
270  *	the dip that dacf postattach processing is being performed on.
271  *	This association is made prior to open to avoid recursion issues.
272  *
273  *   4) From ddi_assoc_queue_with_devi to change vnode association as part of
274  *	DL_ATTACH/DL_DETACH processing (SDIPSET already set).  The call
275  *	from ddi_assoc_queue_with_devi may specify a NULL dip.
276  *
277  * We put an extra hold on the devinfo node passed in as we establish it as
278  * the new s_dip pointer.  Any hold associated with the prior s_dip pointer
279  * is released. The new hold will stay active until another call to
280  * spec_assoc_vp_with_devi or until the common snode is destroyed by
281  * spec_inactive after the last VN_RELE of the common node. This devinfo hold
282  * transfers across a clone open except in the clone_dev case, where the clone
283  * driver is no longer required after open.
284  *
285  * When SDIPSET is set and s_dip is NULL, the vnode has an association with
286  * the driver even though there is currently no association with a specific
287  * hardware instance.
288  */
289 void
290 spec_assoc_vp_with_devi(struct vnode *vp, dev_info_t *dip)
291 {
292 	struct snode	*csp;
293 	dev_info_t	*olddip;
294 
295 	ASSERT(vp);
296 
297 	/*
298 	 * Don't establish a NULL association for a vnode associated with the
299 	 * clone driver.  The qassociate(, -1) call from a streams driver's
300 	 * open implementation to indicate support for qassociate has the
301 	 * side-effect of this type of spec_assoc_vp_with_devi call. This
302 	 * call should not change the the association of the pre-clone
303 	 * vnode associated with the clone driver, the post-clone newdev
304 	 * association will be established later by spec_clone().
305 	 */
306 	if ((dip == NULL) && (getmajor(vp->v_rdev) == clone_major))
307 		return;
308 
309 	/* hold the new */
310 	if (dip)
311 		e_ddi_hold_devi(dip);
312 
313 	csp = VTOS(VTOS(vp)->s_commonvp);
314 	mutex_enter(&csp->s_lock);
315 	olddip = csp->s_dip;
316 	csp->s_dip = dip;
317 	csp->s_flag |= SDIPSET;
318 
319 	/* If association changes then invalidate cached size */
320 	if (olddip != dip)
321 		csp->s_flag &= ~SSIZEVALID;
322 	mutex_exit(&csp->s_lock);
323 
324 	/* release the old */
325 	if (olddip)
326 		ddi_release_devi(olddip);
327 }
328 
329 /*
330  * Return the held dip associated with the specified snode.
331  */
332 dev_info_t *
333 spec_hold_devi_by_vp(struct vnode *vp)
334 {
335 	struct snode	*csp;
336 	dev_info_t	*dip;
337 
338 	ASSERT(vn_matchops(vp, spec_getvnodeops()));
339 
340 	csp = VTOS(VTOS(vp)->s_commonvp);
341 	dip = csp->s_dip;
342 	if (dip)
343 		e_ddi_hold_devi(dip);
344 	return (dip);
345 }
346 
347 /*
348  * Find a special vnode that refers to the given device
349  * of the given type.  Never return a "common" vnode.
350  * Return NULL if a special vnode does not exist.
351  * HOLD the vnode before returning it.
352  */
353 struct vnode *
354 specfind(dev_t dev, vtype_t type)
355 {
356 	struct snode *st;
357 	struct vnode *nvp;
358 
359 	mutex_enter(&stable_lock);
360 	st = stable[STABLEHASH(dev)];
361 	while (st != NULL) {
362 		if (st->s_dev == dev) {
363 			nvp = STOV(st);
364 			if (nvp->v_type == type && st->s_commonvp != nvp) {
365 				VN_HOLD(nvp);
366 				mutex_exit(&stable_lock);
367 				return (nvp);
368 			}
369 		}
370 		st = st->s_next;
371 	}
372 	mutex_exit(&stable_lock);
373 	return (NULL);
374 }
375 
376 /*
377  * Loop through the snode cache looking for snodes referencing dip.
378  *
379  * This function determines if a devinfo node is "BUSY" from the perspective
380  * of having an active vnode associated with the device, which represents a
381  * dependency on the device's services.  This function is needed because a
382  * devinfo node can have a non-zero devi_ref and still NOT be "BUSY" when,
383  * for instance, the framework is manipulating the node (has an open
384  * ndi_hold_devi).
385  *
386  * Returns:
387  *	DEVI_REFERENCED		- if dip is referenced
388  *	DEVI_NOT_REFERENCED	- if dip is not referenced
389  */
390 int
391 devi_stillreferenced(dev_info_t *dip)
392 {
393 	struct snode	*sp;
394 	int		i;
395 
396 	/* if no hold then there can't be an snode with s_dip == dip */
397 	if (e_ddi_devi_holdcnt(dip) == 0)
398 		return (DEVI_NOT_REFERENCED);
399 
400 	mutex_enter(&stable_lock);
401 	for (i = 0; i < STABLESIZE; i++) {
402 		for (sp = stable[i]; sp != NULL; sp = sp->s_next) {
403 			if (sp->s_dip == dip) {
404 				mutex_exit(&stable_lock);
405 				return (DEVI_REFERENCED);
406 			}
407 		}
408 	}
409 	mutex_exit(&stable_lock);
410 	return (DEVI_NOT_REFERENCED);
411 }
412 
413 /*
414  * Given an snode, returns the open count and the dip
415  * associated with that snode
416  * Assumes the caller holds the approriate locks
417  * to prevent snode and/or dip from going away.
418  * Returns:
419  *	-1	No associated dip
420  *	>= 0	Number of opens.
421  */
422 int
423 spec_devi_open_count(struct snode *sp, dev_info_t **dipp)
424 {
425 	dev_info_t *dip;
426 	uint_t count;
427 	struct vnode *vp;
428 
429 	ASSERT(sp);
430 	ASSERT(dipp);
431 
432 	vp = STOV(sp);
433 
434 	*dipp = NULL;
435 
436 	/*
437 	 * We are only interested in common snodes. Only common snodes
438 	 * get their s_count fields bumped up on opens.
439 	 */
440 	if (sp->s_commonvp != vp || (dip = sp->s_dip) == NULL)
441 		return (-1);
442 
443 	mutex_enter(&sp->s_lock);
444 	count = sp->s_count + sp->s_mapcnt;
445 	if (sp->s_flag & SLOCKED)
446 		count++;
447 	mutex_exit(&sp->s_lock);
448 
449 	*dipp = dip;
450 
451 	return (count);
452 }
453 
454 /*
455  * Given a device vnode, return the common
456  * vnode associated with it.
457  */
458 struct vnode *
459 common_specvp(struct vnode *vp)
460 {
461 	struct snode *sp;
462 
463 	if ((vp->v_type != VBLK) && (vp->v_type != VCHR) ||
464 	    !vn_matchops(vp, spec_getvnodeops()))
465 		return (vp);
466 	sp = VTOS(vp);
467 	return (sp->s_commonvp);
468 }
469 
470 /*
471  * Returns a special vnode for the given dev.  The vnode is the
472  * one which is "common" to all the snodes which represent the
473  * same device.
474  * Similar to commonvp() but doesn't acquire the stable_lock, and
475  * may use a pre-allocated snode provided by caller.
476  */
477 static struct vnode *
478 get_cvp(
479 	dev_t		dev,
480 	vtype_t		type,
481 	struct snode	*nsp,		/* pre-allocated snode */
482 	int		*used_nsp)	/* flag indicating if we use nsp */
483 {
484 	struct snode *sp;
485 	struct vnode *svp;
486 
487 	ASSERT(MUTEX_HELD(&stable_lock));
488 	if ((sp = sfind(dev, type, NULL)) == NULL) {
489 		sp = nsp;		/* Use pre-allocated snode */
490 		*used_nsp = 1;		/* return value */
491 		svp = STOV(sp);
492 
493 		sp->s_realvp	= NULL;
494 		sp->s_commonvp	= svp;		/* points to itself */
495 		sp->s_dev	= dev;
496 		sp->s_dip	= NULL;
497 		sp->s_nextr	= NULL;
498 		sp->s_list	= NULL;
499 		sp->s_plcy	= NULL;
500 		sp->s_size	= UNKNOWN_SIZE;
501 		sp->s_flag	= 0;
502 		sp->s_fsid	= specdev;
503 		sp->s_atime	= 0;
504 		sp->s_mtime	= 0;
505 		sp->s_ctime	= 0;
506 		sp->s_count	= 0;
507 		sp->s_mapcnt	= 0;
508 
509 		vn_reinit(svp);
510 		svp->v_vfsp	= &spec_vfs;
511 		svp->v_type	= type;
512 		svp->v_rdev	= dev;
513 		vn_exists(svp);
514 		sinsert(sp);
515 	} else
516 		*used_nsp = 0;
517 	return (STOV(sp));
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.  For use ONLY by SPECFS.
524  */
525 struct vnode *
526 commonvp(dev_t dev, vtype_t type)
527 {
528 	struct snode *sp, *nsp;
529 	struct vnode *svp;
530 
531 	/* Pre-allocate snode in case we might block */
532 	nsp = kmem_cache_alloc(snode_cache, KM_SLEEP);
533 
534 	mutex_enter(&stable_lock);
535 	if ((sp = sfind(dev, type, NULL)) == NULL) {
536 		sp = nsp;		/* Use pre-alloced snode */
537 		svp = STOV(sp);
538 
539 		sp->s_realvp	= NULL;
540 		sp->s_commonvp	= svp;		/* points to itself */
541 		sp->s_dev	= dev;
542 		sp->s_dip	= NULL;
543 		sp->s_nextr	= NULL;
544 		sp->s_list	= NULL;
545 		sp->s_plcy	= NULL;
546 		sp->s_size	= UNKNOWN_SIZE;
547 		sp->s_flag	= 0;
548 		sp->s_fsid	= specdev;
549 		sp->s_atime	= 0;
550 		sp->s_mtime	= 0;
551 		sp->s_ctime	= 0;
552 		sp->s_count	= 0;
553 		sp->s_mapcnt	= 0;
554 
555 		vn_reinit(svp);
556 		svp->v_vfsp	= &spec_vfs;
557 		svp->v_type	= type;
558 		svp->v_rdev	= dev;
559 		vn_exists(svp);
560 		sinsert(sp);
561 		mutex_exit(&stable_lock);
562 	} else {
563 		mutex_exit(&stable_lock);
564 		/* Didn't need the pre-allocated snode */
565 		kmem_cache_free(snode_cache, nsp);
566 	}
567 	return (STOV(sp));
568 }
569 
570 /*
571  * Snode lookup stuff.
572  * These routines maintain a table of snodes hashed by dev so
573  * that the snode for an dev can be found if it already exists.
574  */
575 struct snode *stable[STABLESIZE];
576 int		stablesz = STABLESIZE;
577 kmutex_t	stable_lock;
578 
579 /*
580  * Put a snode in the table.
581  */
582 static void
583 sinsert(struct snode *sp)
584 {
585 	ASSERT(MUTEX_HELD(&stable_lock));
586 	sp->s_next = stable[STABLEHASH(sp->s_dev)];
587 	stable[STABLEHASH(sp->s_dev)] = sp;
588 }
589 
590 /*
591  * Remove an snode from the hash table.
592  * The realvp is not released here because spec_inactive() still
593  * needs it to do a spec_fsync().
594  */
595 void
596 sdelete(struct snode *sp)
597 {
598 	struct snode *st;
599 	struct snode *stprev = NULL;
600 
601 	ASSERT(MUTEX_HELD(&stable_lock));
602 	st = stable[STABLEHASH(sp->s_dev)];
603 	while (st != NULL) {
604 		if (st == sp) {
605 			if (stprev == NULL)
606 				stable[STABLEHASH(sp->s_dev)] = st->s_next;
607 			else
608 				stprev->s_next = st->s_next;
609 			break;
610 		}
611 		stprev = st;
612 		st = st->s_next;
613 	}
614 }
615 
616 /*
617  * Lookup an snode by <dev, type, vp>.
618  * ONLY looks for snodes with non-NULL s_realvp members and
619  * common snodes (with s_commonvp pointing to its vnode).
620  *
621  * If vp is NULL, only return commonvp. Otherwise return
622  * shadow vp with both shadow and common vp's VN_HELD.
623  */
624 static struct snode *
625 sfind(
626 	dev_t	dev,
627 	vtype_t	type,
628 	struct vnode *vp)
629 {
630 	struct snode *st;
631 	struct vnode *svp;
632 
633 	ASSERT(MUTEX_HELD(&stable_lock));
634 	st = stable[STABLEHASH(dev)];
635 	while (st != NULL) {
636 		svp = STOV(st);
637 		if (st->s_dev == dev && svp->v_type == type &&
638 		    VN_CMP(st->s_realvp, vp) &&
639 		    (vp != NULL || st->s_commonvp == svp) &&
640 		    (vp == NULL || st->s_realvp->v_vfsp == vp->v_vfsp)) {
641 			VN_HOLD(svp);
642 			return (st);
643 		}
644 		st = st->s_next;
645 	}
646 	return (NULL);
647 }
648 
649 /*
650  * Mark the accessed, updated, or changed times in an snode
651  * with the current time.
652  */
653 void
654 smark(struct snode *sp, int flag)
655 {
656 	time_t	now = gethrestime_sec();
657 
658 	/* check for change to avoid unnecessary locking */
659 	ASSERT((flag & ~(SACC|SUPD|SCHG)) == 0);
660 	if (((flag & sp->s_flag) != flag) ||
661 	    ((flag & SACC) && (sp->s_atime != now)) ||
662 	    ((flag & SUPD) && (sp->s_mtime != now)) ||
663 	    ((flag & SCHG) && (sp->s_ctime != now))) {
664 		/* lock and update */
665 		mutex_enter(&sp->s_lock);
666 		sp->s_flag |= flag;
667 		if (flag & SACC)
668 			sp->s_atime = now;
669 		if (flag & SUPD)
670 			sp->s_mtime = now;
671 		if (flag & SCHG)
672 			sp->s_ctime = now;
673 		mutex_exit(&sp->s_lock);
674 	}
675 }
676 
677 /*
678  * Return the maximum file offset permitted for this device.
679  * -1 means unrestricted.  SLOFFSET is associated with D_64BIT.
680  *
681  * On a 32-bit kernel this will limit:
682  *   o	D_64BIT devices to SPEC_MAXOFFSET_T.
683  *   o	non-D_64BIT character drivers to a 32-bit offset (MAXOFF_T).
684  */
685 offset_t
686 spec_maxoffset(struct vnode *vp)
687 {
688 	struct snode *sp = VTOS(vp);
689 	struct snode *csp = VTOS(sp->s_commonvp);
690 
691 	if (STREAMSTAB(getmajor(sp->s_dev)))
692 		return ((offset_t)-1);
693 	else if (csp->s_flag & SANYOFFSET)	/* D_U64BIT */
694 		return ((offset_t)-1);
695 #ifdef _ILP32
696 	if (csp->s_flag & SLOFFSET)		/* D_64BIT */
697 		return (SPEC_MAXOFFSET_T);
698 #endif	/* _ILP32 */
699 	return (MAXOFF_T);
700 }
701 
702 /*ARGSUSED*/
703 static int
704 snode_constructor(void *buf, void *cdrarg, int kmflags)
705 {
706 	struct snode *sp = buf;
707 	struct vnode *vp;
708 
709 	vp = vn_alloc(KM_SLEEP);
710 
711 	sp->s_vnode = vp;
712 
713 	vn_setops(vp, spec_getvnodeops());
714 	vp->v_data = (caddr_t)sp;
715 
716 	mutex_init(&sp->s_lock, NULL, MUTEX_DEFAULT, NULL);
717 	cv_init(&sp->s_cv, NULL, CV_DEFAULT, NULL);
718 	return (0);
719 }
720 
721 /*ARGSUSED1*/
722 static void
723 snode_destructor(void *buf, void *cdrarg)
724 {
725 	struct snode *sp = buf;
726 	struct vnode *vp = STOV(sp);
727 
728 	mutex_destroy(&sp->s_lock);
729 	cv_destroy(&sp->s_cv);
730 
731 	vn_free(vp);
732 }
733 
734 
735 int
736 specinit(int fstype, char *name)
737 {
738 	static const fs_operation_def_t spec_vfsops_template[] = {
739 		VFSNAME_SYNC, (fs_generic_func_p) spec_sync,
740 		NULL, NULL
741 	};
742 	extern struct vnodeops *spec_vnodeops;
743 	extern const fs_operation_def_t spec_vnodeops_template[];
744 	struct vfsops *spec_vfsops;
745 	int error;
746 	dev_t dev;
747 
748 	/*
749 	 * Associate vfs and vnode operations.
750 	 */
751 	error = vfs_setfsops(fstype, spec_vfsops_template, &spec_vfsops);
752 	if (error != 0) {
753 		cmn_err(CE_WARN, "specinit: bad vfs ops template");
754 		return (error);
755 	}
756 
757 	error = vn_make_ops(name, spec_vnodeops_template, &spec_vnodeops);
758 	if (error != 0) {
759 		(void) vfs_freevfsops_by_type(fstype);
760 		cmn_err(CE_WARN, "specinit: bad vnode ops template");
761 		return (error);
762 	}
763 
764 	mutex_init(&stable_lock, NULL, MUTEX_DEFAULT, NULL);
765 	mutex_init(&spec_syncbusy, NULL, MUTEX_DEFAULT, NULL);
766 
767 	/*
768 	 * Create snode cache
769 	 */
770 	snode_cache = kmem_cache_create("snode_cache", sizeof (struct snode),
771 		0, snode_constructor, snode_destructor, NULL, NULL, NULL, 0);
772 
773 	/*
774 	 * Associate vfs operations with spec_vfs
775 	 */
776 	VFS_INIT(&spec_vfs, spec_vfsops, (caddr_t)NULL);
777 	if ((dev = getudev()) == -1)
778 		dev = 0;
779 	specdev = makedevice(dev, 0);
780 	return (0);
781 }
782 
783 int
784 device_close(struct vnode *vp, int flag, struct cred *cr)
785 {
786 	struct snode *sp = VTOS(vp);
787 	enum vtype type = vp->v_type;
788 	struct vnode *cvp;
789 	dev_t dev;
790 	int error;
791 
792 	dev = sp->s_dev;
793 	cvp = sp->s_commonvp;
794 
795 	switch (type) {
796 
797 	case VCHR:
798 		if (STREAMSTAB(getmajor(dev))) {
799 			if (cvp->v_stream != NULL)
800 				error = strclose(cvp, flag, cr);
801 			vp->v_stream = NULL;
802 		} else
803 			error = dev_close(dev, flag, OTYP_CHR, cr);
804 		break;
805 
806 	case VBLK:
807 		/*
808 		 * On last close a block device we must
809 		 * invalidate any in-core blocks so that we
810 		 * can, for example, change floppy disks.
811 		 */
812 		(void) spec_putpage(cvp, (offset_t)0,
813 		    (size_t)0, B_INVAL|B_FORCE, cr);
814 		bflush(dev);
815 		binval(dev);
816 		error = dev_close(dev, flag, OTYP_BLK, cr);
817 		break;
818 	default:
819 		panic("device_close: not a device");
820 		/*NOTREACHED*/
821 	}
822 
823 	return (error);
824 }
825 
826 struct vnode *
827 makectty(vnode_t *ovp)
828 {
829 	vnode_t *vp;
830 
831 	if (vp = makespecvp(ovp->v_rdev, VCHR)) {
832 		struct snode *sp;
833 		struct snode *csp;
834 		struct vnode *cvp;
835 
836 		sp = VTOS(vp);
837 		cvp = sp->s_commonvp;
838 		csp = VTOS(cvp);
839 		mutex_enter(&csp->s_lock);
840 		csp->s_count++;
841 		mutex_exit(&csp->s_lock);
842 	}
843 
844 	return (vp);
845 }
846 
847 void
848 spec_snode_walk(int (*callback)(struct snode *sp, void *arg), void *arg)
849 {
850 	struct snode	*sp;
851 	int		i;
852 
853 	ASSERT(callback);
854 
855 	mutex_enter(&stable_lock);
856 	for (i = 0; i < STABLESIZE; i++) {
857 		for (sp = stable[i]; sp; sp = sp->s_next) {
858 			if (callback(sp, arg) != DDI_WALK_CONTINUE)
859 				goto out;
860 		}
861 	}
862 out:
863 	mutex_exit(&stable_lock);
864 }
865 
866 int
867 spec_is_clone(vnode_t *vp)
868 {
869 	struct snode *sp;
870 
871 	if (vn_matchops(vp, spec_getvnodeops())) {
872 		sp = VTOS(vp);
873 		return ((sp->s_flag & SCLONE) ? 1 : 0);
874 	}
875 
876 	return (0);
877 }
878 
879 int
880 spec_is_selfclone(vnode_t *vp)
881 {
882 	struct snode *sp;
883 
884 	if (vn_matchops(vp, spec_getvnodeops())) {
885 		sp = VTOS(vp);
886 		return ((sp->s_flag & SSELFCLONE) ? 1 : 0);
887 	}
888 
889 	return (0);
890 }
891