null_subr.c revision 245408
1/*-
2 * Copyright (c) 1992, 1993
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * This code is derived from software donated to Berkeley by
6 * Jan-Simon Pendry.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 4. Neither the name of the University nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 *
32 *	@(#)null_subr.c	8.7 (Berkeley) 5/14/95
33 *
34 * $FreeBSD: head/sys/fs/nullfs/null_subr.c 245408 2013-01-14 05:44:47Z kib $
35 */
36
37#include <sys/param.h>
38#include <sys/systm.h>
39#include <sys/kernel.h>
40#include <sys/lock.h>
41#include <sys/mutex.h>
42#include <sys/malloc.h>
43#include <sys/mount.h>
44#include <sys/proc.h>
45#include <sys/vnode.h>
46
47#include <fs/nullfs/null.h>
48
49/*
50 * Null layer cache:
51 * Each cache entry holds a reference to the lower vnode
52 * along with a pointer to the alias vnode.  When an
53 * entry is added the lower vnode is VREF'd.  When the
54 * alias is removed the lower vnode is vrele'd.
55 */
56
57#define	NULL_NHASH(vp) (&null_node_hashtbl[vfs_hash_index(vp) & null_hash_mask])
58
59static LIST_HEAD(null_node_hashhead, null_node) *null_node_hashtbl;
60static struct mtx null_hashmtx;
61static u_long null_hash_mask;
62
63static MALLOC_DEFINE(M_NULLFSHASH, "nullfs_hash", "NULLFS hash table");
64MALLOC_DEFINE(M_NULLFSNODE, "nullfs_node", "NULLFS vnode private part");
65
66static struct vnode * null_hashins(struct mount *, struct null_node *);
67
68/*
69 * Initialise cache headers
70 */
71int
72nullfs_init(vfsp)
73	struct vfsconf *vfsp;
74{
75
76	null_node_hashtbl = hashinit(desiredvnodes, M_NULLFSHASH,
77	    &null_hash_mask);
78	mtx_init(&null_hashmtx, "nullhs", NULL, MTX_DEF);
79	return (0);
80}
81
82int
83nullfs_uninit(vfsp)
84	struct vfsconf *vfsp;
85{
86
87	mtx_destroy(&null_hashmtx);
88	hashdestroy(null_node_hashtbl, M_NULLFSHASH, null_hash_mask);
89	return (0);
90}
91
92/*
93 * Return a VREF'ed alias for lower vnode if already exists, else 0.
94 * Lower vnode should be locked on entry and will be left locked on exit.
95 */
96struct vnode *
97null_hashget(mp, lowervp)
98	struct mount *mp;
99	struct vnode *lowervp;
100{
101	struct null_node_hashhead *hd;
102	struct null_node *a;
103	struct vnode *vp;
104
105	ASSERT_VOP_LOCKED(lowervp, "null_hashget");
106
107	/*
108	 * Find hash base, and then search the (two-way) linked
109	 * list looking for a null_node structure which is referencing
110	 * the lower vnode.  If found, the increment the null_node
111	 * reference count (but NOT the lower vnode's VREF counter).
112	 */
113	hd = NULL_NHASH(lowervp);
114	mtx_lock(&null_hashmtx);
115	LIST_FOREACH(a, hd, null_hash) {
116		if (a->null_lowervp == lowervp && NULLTOV(a)->v_mount == mp) {
117			/*
118			 * Since we have the lower node locked the nullfs
119			 * node can not be in the process of recycling.  If
120			 * it had been recycled before we grabed the lower
121			 * lock it would not have been found on the hash.
122			 */
123			vp = NULLTOV(a);
124			vref(vp);
125			mtx_unlock(&null_hashmtx);
126			return (vp);
127		}
128	}
129	mtx_unlock(&null_hashmtx);
130	return (NULLVP);
131}
132
133/*
134 * Act like null_hashget, but add passed null_node to hash if no existing
135 * node found.
136 */
137static struct vnode *
138null_hashins(mp, xp)
139	struct mount *mp;
140	struct null_node *xp;
141{
142	struct null_node_hashhead *hd;
143	struct null_node *oxp;
144	struct vnode *ovp;
145
146	hd = NULL_NHASH(xp->null_lowervp);
147	mtx_lock(&null_hashmtx);
148	LIST_FOREACH(oxp, hd, null_hash) {
149		if (oxp->null_lowervp == xp->null_lowervp &&
150		    NULLTOV(oxp)->v_mount == mp) {
151			/*
152			 * See null_hashget for a description of this
153			 * operation.
154			 */
155			ovp = NULLTOV(oxp);
156			vref(ovp);
157			mtx_unlock(&null_hashmtx);
158			return (ovp);
159		}
160	}
161	LIST_INSERT_HEAD(hd, xp, null_hash);
162	mtx_unlock(&null_hashmtx);
163	return (NULLVP);
164}
165
166static void
167null_destroy_proto(struct vnode *vp, void *xp)
168{
169
170	lockmgr(&vp->v_lock, LK_EXCLUSIVE, NULL);
171	VI_LOCK(vp);
172	vp->v_data = NULL;
173	vp->v_vnlock = &vp->v_lock;
174	vp->v_op = &dead_vnodeops;
175	VI_UNLOCK(vp);
176	vgone(vp);
177	vput(vp);
178	free(xp, M_NULLFSNODE);
179}
180
181static void
182null_insmntque_dtr(struct vnode *vp, void *xp)
183{
184
185	vput(((struct null_node *)xp)->null_lowervp);
186	null_destroy_proto(vp, xp);
187}
188
189/*
190 * Make a new or get existing nullfs node.
191 * Vp is the alias vnode, lowervp is the lower vnode.
192 *
193 * The lowervp assumed to be locked and having "spare" reference. This routine
194 * vrele lowervp if nullfs node was taken from hash. Otherwise it "transfers"
195 * the caller's "spare" reference to created nullfs vnode.
196 */
197int
198null_nodeget(mp, lowervp, vpp)
199	struct mount *mp;
200	struct vnode *lowervp;
201	struct vnode **vpp;
202{
203	struct null_node *xp;
204	struct vnode *vp;
205	int error;
206
207	ASSERT_VOP_LOCKED(lowervp, "lowervp");
208	KASSERT(lowervp->v_usecount >= 1, ("Unreferenced vnode %p", lowervp));
209
210	/* Lookup the hash firstly. */
211	*vpp = null_hashget(mp, lowervp);
212	if (*vpp != NULL) {
213		vrele(lowervp);
214		return (0);
215	}
216
217	/*
218	 * The insmntque1() call below requires the exclusive lock on
219	 * the nullfs vnode.  Upgrade the lock now if hash failed to
220	 * provide ready to use vnode.
221	 */
222	if (VOP_ISLOCKED(lowervp) != LK_EXCLUSIVE) {
223		KASSERT((MOUNTTONULLMOUNT(mp)->nullm_flags & NULLM_CACHE) != 0,
224		    ("lowervp %p is not excl locked and cache is disabled",
225		    lowervp));
226		vn_lock(lowervp, LK_UPGRADE | LK_RETRY);
227		if ((lowervp->v_iflag & VI_DOOMED) != 0) {
228			vput(lowervp);
229			return (ENOENT);
230		}
231	}
232
233	/*
234	 * We do not serialize vnode creation, instead we will check for
235	 * duplicates later, when adding new vnode to hash.
236	 * Note that duplicate can only appear in hash if the lowervp is
237	 * locked LK_SHARED.
238	 */
239	xp = malloc(sizeof(struct null_node), M_NULLFSNODE, M_WAITOK);
240
241	error = getnewvnode("null", mp, &null_vnodeops, &vp);
242	if (error) {
243		vput(lowervp);
244		free(xp, M_NULLFSNODE);
245		return (error);
246	}
247
248	xp->null_vnode = vp;
249	xp->null_lowervp = lowervp;
250	vp->v_type = lowervp->v_type;
251	vp->v_data = xp;
252	vp->v_vnlock = lowervp->v_vnlock;
253	error = insmntque1(vp, mp, null_insmntque_dtr, xp);
254	if (error != 0)
255		return (error);
256	/*
257	 * Atomically insert our new node into the hash or vget existing
258	 * if someone else has beaten us to it.
259	 */
260	*vpp = null_hashins(mp, xp);
261	if (*vpp != NULL) {
262		vrele(lowervp);
263		null_destroy_proto(vp, xp);
264		return (0);
265	}
266	*vpp = vp;
267
268	return (0);
269}
270
271/*
272 * Remove node from hash.
273 */
274void
275null_hashrem(xp)
276	struct null_node *xp;
277{
278
279	mtx_lock(&null_hashmtx);
280	LIST_REMOVE(xp, null_hash);
281	mtx_unlock(&null_hashmtx);
282}
283
284#ifdef DIAGNOSTIC
285
286struct vnode *
287null_checkvp(vp, fil, lno)
288	struct vnode *vp;
289	char *fil;
290	int lno;
291{
292	struct null_node *a = VTONULL(vp);
293
294#ifdef notyet
295	/*
296	 * Can't do this check because vop_reclaim runs
297	 * with a funny vop vector.
298	 */
299	if (vp->v_op != null_vnodeop_p) {
300		printf ("null_checkvp: on non-null-node\n");
301		panic("null_checkvp");
302	}
303#endif
304	if (a->null_lowervp == NULLVP) {
305		/* Should never happen */
306		panic("null_checkvp %p", vp);
307	}
308	VI_LOCK_FLAGS(a->null_lowervp, MTX_DUPOK);
309	if (a->null_lowervp->v_usecount < 1)
310		panic ("null with unref'ed lowervp, vp %p lvp %p",
311		    vp, a->null_lowervp);
312	VI_UNLOCK(a->null_lowervp);
313#ifdef notyet
314	printf("null %x/%d -> %x/%d [%s, %d]\n",
315	        NULLTOV(a), vrefcnt(NULLTOV(a)),
316		a->null_lowervp, vrefcnt(a->null_lowervp),
317		fil, lno);
318#endif
319	return (a->null_lowervp);
320}
321#endif
322