]>
git.saurik.com Git - apple/xnu.git/blob - bsd/miscfs/nullfs/null_subr.c
2 * Copyright (c) 2016 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
25 * Portions Copyright (c) 1992, 1993
26 * The Regents of the University of California. All rights reserved.
28 * This code is derived from software donated to Berkeley by
31 * Redistribution and use in source and binary forms, with or without
32 * modification, are permitted provided that the following conditions
34 * 1. Redistributions of source code must retain the above copyright
35 * notice, this list of conditions and the following disclaimer.
36 * 2. Redistributions in binary form must reproduce the above copyright
37 * notice, this list of conditions and the following disclaimer in the
38 * documentation and/or other materials provided with the distribution.
39 * 4. Neither the name of the University nor the names of its contributors
40 * may be used to endorse or promote products derived from this software
41 * without specific prior written permission.
43 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
44 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
45 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
46 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
47 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
48 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
49 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
50 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
51 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
52 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
55 * @(#)null_subr.c 8.7 (Berkeley) 5/14/95
59 #include <sys/param.h>
60 #include <sys/systm.h>
61 #include <sys/kernel.h>
63 #include <sys/malloc.h>
64 #include <sys/mount.h>
66 #include <sys/vnode.h>
72 * Each cache entry holds a reference to the lower vnode
73 * along with a pointer to the alias vnode. When an
74 * entry is added the lower vnode is VREF'd. When the
75 * alias is removed the lower vnode is vrele'd.
78 #define NULL_HASH_SIZE (desiredvnodes / 10)
80 /* osx doesn't really have the functionality freebsd uses here..gonna try this
82 #define NULL_NHASH(vp) (&null_node_hashtbl[((((uintptr_t)vp) >> vnsz2log) + (uintptr_t)vnode_mount(vp)) & null_hash_mask])
84 static LIST_HEAD(null_node_hashhead
, null_node
) * null_node_hashtbl
;
85 static lck_mtx_t null_hashmtx
;
86 static lck_attr_t
* null_hashlck_attr
;
87 static lck_grp_t
* null_hashlck_grp
;
88 static lck_grp_attr_t
* null_hashlck_grp_attr
;
89 static u_long null_hash_mask
;
91 /* os x doesn't have hashes built into vnode. gonna try doing what freebsd does
93 Don't want to create a dependency on vnode_internal.h and the real struct
95 9 is an eyeball of the log 2 size of vnode */
96 static int vnsz2log
= 9;
98 static int null_hashins(struct mount
*, struct null_node
*, struct vnode
**);
101 nullfs_init_lck(lck_mtx_t
* lck
)
104 if (lck
&& null_hashlck_grp
&& null_hashlck_attr
) {
105 lck_mtx_init(lck
, null_hashlck_grp
, null_hashlck_attr
);
112 nullfs_destroy_lck(lck_mtx_t
* lck
)
115 if (lck
&& null_hashlck_grp
) {
116 lck_mtx_destroy(lck
, null_hashlck_grp
);
123 * Initialise cache headers
126 nullfs_init(__unused
struct vfsconf
* vfsp
)
128 NULLFSDEBUG("%s\n", __FUNCTION__
);
130 /* assuming for now that this happens immediately and by default after fs
132 null_hashlck_grp_attr
= lck_grp_attr_alloc_init();
133 if (null_hashlck_grp_attr
== NULL
) {
136 null_hashlck_grp
= lck_grp_alloc_init("com.apple.filesystems.nullfs", null_hashlck_grp_attr
);
137 if (null_hashlck_grp
== NULL
) {
140 null_hashlck_attr
= lck_attr_alloc_init();
141 if (null_hashlck_attr
== NULL
) {
145 lck_mtx_init(&null_hashmtx
, null_hashlck_grp
, null_hashlck_attr
);
146 null_node_hashtbl
= hashinit(NULL_HASH_SIZE
, M_TEMP
, &null_hash_mask
);
147 NULLFSDEBUG("%s finished\n", __FUNCTION__
);
150 printf("NULLFS: failed to get lock element\n");
151 if (null_hashlck_grp_attr
) {
152 lck_grp_attr_free(null_hashlck_grp_attr
);
153 null_hashlck_grp_attr
= NULL
;
155 if (null_hashlck_grp
) {
156 lck_grp_free(null_hashlck_grp
);
157 null_hashlck_grp
= NULL
;
159 if (null_hashlck_attr
) {
160 lck_attr_free(null_hashlck_attr
);
161 null_hashlck_attr
= NULL
;
169 /* This gets called when the fs is uninstalled, there wasn't an exact
170 * equivalent in vfsops */
171 lck_mtx_destroy(&null_hashmtx
, null_hashlck_grp
);
172 FREE(null_node_hashtbl
, M_TEMP
);
173 if (null_hashlck_grp_attr
) {
174 lck_grp_attr_free(null_hashlck_grp_attr
);
175 null_hashlck_grp_attr
= NULL
;
177 if (null_hashlck_grp
) {
178 lck_grp_free(null_hashlck_grp
);
179 null_hashlck_grp
= NULL
;
181 if (null_hashlck_attr
) {
182 lck_attr_free(null_hashlck_attr
);
183 null_hashlck_attr
= NULL
;
189 * Find the nullfs vnode mapped to lowervp. Return it in *vpp with an iocount if found.
190 * Return 0 on success. On failure *vpp will be null and a non-zero error code will be returned.
193 null_hashget(struct mount
* mp
, struct vnode
* lowervp
, struct vnode
** vpp
)
195 struct null_node_hashhead
* hd
;
196 struct null_node
* a
;
201 * Find hash base, and then search the (two-way) linked
202 * list looking for a null_node structure which is referencing
203 * the lower vnode. We only give up our reference at reclaim so
204 * just check whether the lowervp has gotten pulled from under us
206 hd
= NULL_NHASH(lowervp
);
207 lck_mtx_lock(&null_hashmtx
);
208 LIST_FOREACH(a
, hd
, null_hash
)
210 if (a
->null_lowervp
== lowervp
&& vnode_mount(NULLTOV(a
)) == mp
) {
212 if (a
->null_lowervid
!= vnode_vid(lowervp
)) {
213 /*lowervp has reved */
216 /* if we found something then get an iocount on it */
217 error
= vnode_getwithvid(vp
, a
->null_myvid
);
225 lck_mtx_unlock(&null_hashmtx
);
230 * Act like null_hashget, but add passed null_node to hash if no existing
234 null_hashins(struct mount
* mp
, struct null_node
* xp
, struct vnode
** vpp
)
236 struct null_node_hashhead
* hd
;
237 struct null_node
* oxp
;
241 hd
= NULL_NHASH(xp
->null_lowervp
);
242 lck_mtx_lock(&null_hashmtx
);
243 LIST_FOREACH(oxp
, hd
, null_hash
)
245 if (oxp
->null_lowervp
== xp
->null_lowervp
&& vnode_mount(NULLTOV(oxp
)) == mp
) {
247 * See null_hashget for a description of this
251 if (oxp
->null_lowervid
!= vnode_vid(oxp
->null_lowervp
)) {
252 /*vp doesn't exist so return null (not sure we are actually gonna catch
254 This is an exceptional case right now, it suggests the vnode we are
255 trying to add has been recycled
260 /* if we found something in the hash map then grab an iocount */
261 error
= vnode_getwithvid(ovp
, oxp
->null_myvid
);
268 /* if it wasn't in the hash map then the vnode pointed to by xp already has a
269 * iocount so don't bother */
270 LIST_INSERT_HEAD(hd
, xp
, null_hash
);
271 xp
->null_flags
|= NULL_FLAG_HASHED
;
273 lck_mtx_unlock(&null_hashmtx
);
278 * Remove node from hash.
281 null_hashrem(struct null_node
* xp
)
283 lck_mtx_lock(&null_hashmtx
);
284 LIST_REMOVE(xp
, null_hash
);
285 lck_mtx_unlock(&null_hashmtx
);
288 static struct null_node
*
289 null_nodecreate(struct vnode
* lowervp
)
291 struct null_node
* xp
;
293 MALLOC(xp
, struct null_node
*, sizeof(struct null_node
), M_TEMP
, M_WAITOK
| M_ZERO
);
296 xp
->null_lowervp
= lowervp
;
297 xp
->null_lowervid
= vnode_vid(lowervp
);
303 /* assumption is that vnode has iocount on it after vnode create */
306 struct mount
* mp
, struct vnode
* lowervp
, struct vnode
* dvp
, struct vnode
** vpp
, struct componentname
* cnp
, int root
)
308 struct vnode_fsparam vnfs_param
;
310 enum vtype type
= VDIR
;
311 struct null_node
* xp
= null_nodecreate(lowervp
);
318 type
= vnode_vtype(lowervp
);
321 vnfs_param
.vnfs_mp
= mp
;
322 vnfs_param
.vnfs_vtype
= type
;
323 vnfs_param
.vnfs_str
= "nullfs";
324 vnfs_param
.vnfs_dvp
= dvp
;
325 vnfs_param
.vnfs_fsnode
= (void *)xp
;
326 vnfs_param
.vnfs_vops
= nullfs_vnodeop_p
;
327 vnfs_param
.vnfs_markroot
= root
;
328 vnfs_param
.vnfs_marksystem
= 0;
329 vnfs_param
.vnfs_rdev
= 0;
330 vnfs_param
.vnfs_filesize
= 0; // set this to 0 since we should only be shadowing non-regular files
331 vnfs_param
.vnfs_cnp
= cnp
;
332 vnfs_param
.vnfs_flags
= VNFS_ADDFSREF
;
334 error
= vnode_create(VNCREATE_FLAVOR
, VCREATESIZE
, &vnfs_param
, vpp
);
336 xp
->null_vnode
= *vpp
;
337 xp
->null_myvid
= vnode_vid(*vpp
);
338 vnode_settag(*vpp
, VT_NULL
);
346 * Make a new or get existing nullfs node.
347 * Vp is the alias vnode, lowervp is the lower vnode.
349 * lowervp is assumed to have an iocount on it from the caller
353 struct mount
* mp
, struct vnode
* lowervp
, struct vnode
* dvp
, struct vnode
** vpp
, struct componentname
* cnp
, int root
)
358 /* Lookup the hash firstly. */
359 error
= null_hashget(mp
, lowervp
, vpp
);
360 /* ENOENT means it wasn't found, EIO is a failure we should bail from, 0 is it
362 if (error
!= ENOENT
) {
363 /* null_hashget checked the vid, so if we got something here its legit to
364 * the best of our knowledge*/
365 /* if we found something then there is an iocount on vpp,
366 if we didn't find something then vpp shouldn't be used by the caller */
371 * We do not serialize vnode creation, instead we will check for
372 * duplicates later, when adding new vnode to hash.
374 error
= vnode_ref(lowervp
); // take a ref on lowervp so we let the system know we care about it
377 // Failed to get a reference on the lower vp so bail. Lowervp may be gone already.
381 error
= null_getnewvnode(mp
, lowervp
, dvp
, &vp
, cnp
, root
);
389 * Atomically insert our new node into the hash or vget existing
390 * if someone else has beaten us to it.
392 error
= null_hashins(mp
, VTONULL(vp
), vpp
);
393 if (error
|| *vpp
!= NULL
) {
394 /* recycle will call reclaim which will get rid of the internals */
397 /* if we found vpp, then null_hashins put an iocount on it */
401 /* vp has an iocount from null_getnewvnode */