]>
git.saurik.com Git - apple/xnu.git/blob - bsd/hfs/hfs_vhash.c
b6ccc2445cf50e7168a5685fabbdd2d316202851
2 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
20 * @APPLE_LICENSE_HEADER_END@
23 /* Copyright (c) 1998 Apple Computer, Inc. All Rights Reserved */
25 * Copyright (c) 1982, 1986, 1989, 1991, 1993, 1995
26 * The Regents of the University of California. All rights reserved.
28 * Redistribution and use in source and binary forms, with or without
29 * modification, are permitted provided that the following conditions
31 * 1. Redistributions of source code must retain the above copyright
32 * notice, this list of conditions and the following disclaimer.
33 * 2. Redistributions in binary form must reproduce the above copyright
34 * notice, this list of conditions and the following disclaimer in the
35 * documentation and/or other materials provided with the distribution.
36 * 3. All advertising materials mentioning features or use of this software
37 * must display the following acknowledgement:
38 * This product includes software developed by the University of
39 * California, Berkeley and its contributors.
40 * 4. Neither the name of the University nor the names of its contributors
41 * may be used to endorse or promote products derived from this software
42 * without specific prior written permission.
44 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
45 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
46 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
47 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
48 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
49 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
50 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
51 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
52 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
53 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
57 * derived from @(#)ufs_ihash.c 8.7 (Berkeley) 5/17/95
60 #include <sys/param.h>
61 #include <sys/systm.h>
62 #include <sys/vnode.h>
63 #include <sys/malloc.h>
65 #include <sys/queue.h>
72 * Structures associated with hfsnode cacheing.
74 LIST_HEAD(vhashhead
, hfsnode
) *vhashtbl
;
75 u_long vhash
; /* size of hash table - 1 */
76 #define HFSNODEHASH(device, nodeID) (&vhashtbl[((device) + (nodeID)) & vhash])
77 struct slock hfs_vhash_slock
;
80 * Initialize hfsnode hash table.
86 vhashtbl
= hashinit(desiredvnodes
, M_HFSMNT
, &vhash
);
87 simple_lock_init(&hfs_vhash_slock
);
91 * Use the device/dirID/forkType tuple to find the incore hfsnode, and return a pointer
92 * to it. If it is in core, but locked, wait for it.
94 * Acceptable forkTypes are kData, kRsrcFork, kDirectory, or kDefault which translates to either
95 * kDataFork or kDirectory
97 * While traversing the hash, expext that a hfsnode is in the midst of being allocated, if so,
98 * then sleep and try again
101 hfs_vhashget(dev
, nodeID
, forkType
)
106 struct proc
*p
= current_proc(); /* XXX */
110 DBG_ASSERT(forkType
!=kUndefinedFork
);
112 * Go through the hash list
113 * If a vnode is in the process of being cleaned out or being
114 * allocated, wait for it to be finished and then try again
117 simple_lock(&hfs_vhash_slock
);
118 for (hp
= HFSNODEHASH(dev
, nodeID
)->lh_first
; hp
; hp
= hp
->h_hash
.le_next
) {
119 /* The vnode might be in an incomplete state, so sleep until its ready */
120 if (hp
->h_nodeflags
& IN_ALLOCATING
) {
121 simple_unlock(&hfs_vhash_slock
);
122 tsleep((caddr_t
)hp
, PINOD
, "hfs_vhashlookup", 0);
126 DBG_ASSERT(hp
->h_meta
!= NULL
);
127 if ((H_FILEID(hp
) == nodeID
) &&
128 (H_DEV(hp
) == dev
) &&
129 !(hp
->h_meta
->h_metaflags
& IN_NOEXISTS
)) {
130 /* SER XXX kDefault of meta data (ksysfile) is not assumed here */
131 if ( (forkType
== kAnyFork
) ||
132 (H_FORKTYPE(hp
) == forkType
) ||
133 ((forkType
== kDefault
) && ((H_FORKTYPE(hp
) == kDirectory
)
134 || (H_FORKTYPE(hp
) == kDataFork
)))) {
136 simple_lock(&vp
->v_interlock
);
137 simple_unlock(&hfs_vhash_slock
);
138 if (vget(vp
, LK_EXCLUSIVE
| LK_INTERLOCK
, p
))
144 simple_unlock(&hfs_vhash_slock
);
152 * Lock the hfsnode and insert the hfsnode into the hash table, and return it locked.
153 * Returns the sibling meta data if it exists, elses return NULL
156 hfs_vhashins_sibling(dev
, nodeID
, hp
, fm
)
160 struct hfsfilemeta
**fm
;
162 struct vhashhead
*ipp
;
164 struct hfsfilemeta
*tfm
;
166 DBG_ASSERT(fm
!= NULL
);
167 DBG_ASSERT(hp
!= NULL
);
168 DBG_ASSERT(hp
->h_meta
== NULL
);
169 DBG_ASSERT(H_FORKTYPE(hp
)==kDataFork
|| H_FORKTYPE(hp
)==kRsrcFork
);
172 lockmgr(&hp
->h_lock
, LK_EXCLUSIVE
, (struct slock
*)0, current_proc());
176 * Go through the hash list to see if a sibling exists
177 * If it does, store it to return
178 * If a vnode is in the process of being cleaned out or being
179 * allocated, wait for it to be finished and then try again
182 ipp
= HFSNODEHASH(dev
, nodeID
);
185 simple_lock(&hfs_vhash_slock
);
186 for (thp
= ipp
->lh_first
; thp
; thp
= thp
->h_hash
.le_next
) {
187 if (thp
->h_nodeflags
& IN_ALLOCATING
) { /* Its in the process of being allocated */
188 simple_unlock(&hfs_vhash_slock
);
189 tsleep((caddr_t
)thp
, PINOD
, "hfs_vhash_ins_meta", 0);
193 DBG_ASSERT(thp
->h_meta
!= NULL
);
194 if ((H_FILEID(thp
) == nodeID
) && (H_DEV(thp
) == dev
)) {
195 tfm
= hp
->h_meta
= thp
->h_meta
;
200 /* Add to sibling list..if it can have them */
201 if (tfm
&& (H_FORKTYPE(hp
)==kDataFork
|| H_FORKTYPE(hp
)==kRsrcFork
)) {
202 DBG_ASSERT(tfm
->h_siblinghead
.cqh_first
!= NULL
&& tfm
->h_siblinghead
.cqh_last
!= NULL
);
203 simple_lock(&tfm
->h_siblinglock
);
204 CIRCLEQ_INSERT_HEAD(&tfm
->h_siblinghead
, hp
, h_sibling
);
205 simple_unlock(&tfm
->h_siblinglock
);
208 LIST_INSERT_HEAD(ipp
, hp
, h_hash
);
209 simple_unlock(&hfs_vhash_slock
);
216 * Lock the hfsnode and insert the hfsnode into the hash table, and return it locked.
219 hfs_vhashins(dev
, nodeID
, hp
)
224 struct vhashhead
*ipp
;
226 DBG_ASSERT(hp
!= NULL
);
227 DBG_ASSERT(nodeID
!= 0);
229 lockmgr(&hp
->h_lock
, LK_EXCLUSIVE
, (struct slock
*)0, current_proc());
231 simple_lock(&hfs_vhash_slock
);
232 ipp
= HFSNODEHASH(dev
, nodeID
);
233 LIST_INSERT_HEAD(ipp
, hp
, h_hash
);
234 simple_unlock(&hfs_vhash_slock
);
239 * Remove the hfsnode from the hash table and then checks to see if another forks exists.
246 DBG_ASSERT(hp
!= NULL
);
247 DBG_ASSERT(hp
->h_meta
!= NULL
);
249 simple_lock(&hfs_vhash_slock
);
251 /* Test to see if there are siblings, should only apply to forks */
252 if (hp
->h_meta
->h_siblinghead
.cqh_first
!= NULL
) {
253 simple_lock(&hp
->h_meta
->h_siblinglock
);
254 CIRCLEQ_REMOVE(&hp
->h_meta
->h_siblinghead
, hp
, h_sibling
);
255 simple_unlock(&hp
->h_meta
->h_siblinglock
);
258 LIST_REMOVE(hp
, h_hash
);
261 hp
->h_hash
.le_next
= NULL
;
262 hp
->h_hash
.le_prev
= NULL
;
266 simple_unlock(&hfs_vhash_slock
);
273 * Moves the entries from one bucket to another
274 * nodeID is the old bucket id
277 hfs_vhashmove(hp
, oldNodeID
)
281 struct vhashhead
*oldHeadIndex
, *newHeadIndex
;
282 struct hfsnode
*thp
, *nextNode
;
285 DBG_ASSERT(hp
!= NULL
);
286 DBG_ASSERT(hp
->h_meta
!= NULL
);
288 newNodeID
= H_FILEID(hp
);
290 oldHeadIndex
= HFSNODEHASH(H_DEV(hp
), oldNodeID
);
291 newHeadIndex
= HFSNODEHASH(H_DEV(hp
), newNodeID
);
293 /* If it is moving to the same bucket...then we are done */
294 if (oldHeadIndex
== newHeadIndex
)
300 * Go through the old hash list
301 * If there is a nodeid mismatch, or the nodeid doesnt match the current bucket
302 * remove it and add it to the right bucket.
303 * If a vnode is in the process of being cleaned out or being
304 * allocated, wait for it to be finished and then try again
306 simple_lock(&hfs_vhash_slock
);
307 for (nextNode
= oldHeadIndex
->lh_first
; nextNode
; ) {
308 if (nextNode
->h_nodeflags
& IN_ALLOCATING
) { /* Its in the process of being allocated */
309 simple_unlock(&hfs_vhash_slock
);
310 tsleep((caddr_t
)nextNode
, PINOD
, "hfs_vhashmove", 0);
314 DBG_ASSERT(nextNode
->h_meta
!= NULL
);
316 nextNode
= nextNode
->h_hash
.le_next
;
317 if (newNodeID
== H_FILEID(thp
)) {
318 LIST_REMOVE(thp
, h_hash
);
319 thp
->h_hash
.le_next
= NULL
;
320 thp
->h_hash
.le_next
= NULL
;
321 LIST_INSERT_HEAD(newHeadIndex
, thp
, h_hash
);
325 simple_unlock(&hfs_vhash_slock
);
330 * This will test the hash entry for a given hfsnode
332 * 1. The uniqei existance of the node
333 * 2. All other nodes, proper membership to the hash
334 * 3. Proper termination of the hash
335 * 4. All members have a non-null h_meta
337 void hfs_vhash_dbg(hp
)
340 struct proc
*p
= current_proc(); /* XXX */
342 struct hfsnode
*thp
, *tthp
;
344 int wasFound
= false;
345 struct vhashhead
*ipp
, *jpp
;
346 dev_t dev
= H_DEV(hp
);
347 UInt32 nodeID
= H_FILEID(hp
);
348 UInt8 forkType
= H_FORKTYPE(hp
);
349 u_long forksfound
= 0;
351 if (forkType
==kDataFork
|| forkType
==kRsrcFork
)
355 DEBUG_BREAK_MSG(("hash_dgh: Null hfsnode"));
357 * Go through the hash list
358 * If a vnode is in the process of being cleaned out or being
359 * allocated, wait for it to be finished and then try again
361 ipp
= HFSNODEHASH(dev
, nodeID
);
364 simple_lock(&hfs_vhash_slock
);
365 for (thp
= ipp
->lh_first
; thp
; thp
= thp
->h_hash
.le_next
) {
366 if (thp
->h_nodeflags
& IN_ALLOCATING
) { /* Its in the process of being allocated */
367 simple_unlock(&hfs_vhash_slock
);
368 tsleep((caddr_t
)thp
, PINOD
, "hfs_vhash_ins_meta", 0);
372 if (thp
->h_meta
== NULL
)
373 DEBUG_BREAK_MSG(("hash_dgh: Null hfs_meta"));
374 jpp
= (HFSNODEHASH(H_DEV(thp
), H_FILEID(thp
)));
376 DEBUG_BREAK_MSG(("hash_dgh: Member on wrong hash"));
378 if ((H_FILEID(thp
) == nodeID
) && (H_DEV(thp
) == dev
)) {
381 DEBUG_BREAK_MSG(("hash_dgh: Too many siblings"));
382 if ((1<<H_FORKTYPE(thp
)) & forksfound
)
383 DEBUG_BREAK_MSG(("hash_dgh: Fork already found"));
384 forksfound
|= (1<<H_FORKTYPE(thp
));
386 if (H_FORKTYPE(thp
) == forkType
) {
387 if (wasFound
== true)
388 DEBUG_BREAK_MSG(("hash_dgh: Already found"));
393 simple_unlock(&hfs_vhash_slock
);
396 DEBUG_BREAK_MSG(("hash_dgh: Not found"));
400 #endif /* HFS_DIAGNOSTIC */