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