ulfs_dirhash.c revision 1.6.2.2 1 1.6.2.2 tls /* $NetBSD: ulfs_dirhash.c,v 1.6.2.2 2013/06/23 06:18:39 tls Exp $ */
2 1.6.2.2 tls /* from NetBSD: ufs_dirhash.c,v 1.34 2009/10/05 23:48:08 rmind Exp */
3 1.6.2.2 tls
4 1.6.2.2 tls /*
5 1.6.2.2 tls * Copyright (c) 2001, 2002 Ian Dowse. All rights reserved.
6 1.6.2.2 tls *
7 1.6.2.2 tls * Redistribution and use in source and binary forms, with or without
8 1.6.2.2 tls * modification, are permitted provided that the following conditions
9 1.6.2.2 tls * are met:
10 1.6.2.2 tls * 1. Redistributions of source code must retain the above copyright
11 1.6.2.2 tls * notice, this list of conditions and the following disclaimer.
12 1.6.2.2 tls * 2. Redistributions in binary form must reproduce the above copyright
13 1.6.2.2 tls * notice, this list of conditions and the following disclaimer in the
14 1.6.2.2 tls * documentation and/or other materials provided with the distribution.
15 1.6.2.2 tls *
16 1.6.2.2 tls * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 1.6.2.2 tls * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 1.6.2.2 tls * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 1.6.2.2 tls * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 1.6.2.2 tls * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 1.6.2.2 tls * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 1.6.2.2 tls * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 1.6.2.2 tls * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 1.6.2.2 tls * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 1.6.2.2 tls * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 1.6.2.2 tls * SUCH DAMAGE.
27 1.6.2.2 tls *
28 1.6.2.2 tls * $FreeBSD: src/sys/ufs/ufs/ufs_dirhash.c,v 1.3.2.8 2004/12/08 11:54:13 dwmalone Exp $
29 1.6.2.2 tls */
30 1.6.2.2 tls
31 1.6.2.2 tls #include <sys/cdefs.h>
32 1.6.2.2 tls __KERNEL_RCSID(0, "$NetBSD: ulfs_dirhash.c,v 1.6.2.2 2013/06/23 06:18:39 tls Exp $");
33 1.6.2.2 tls
34 1.6.2.2 tls /*
35 1.6.2.2 tls * This implements a hash-based lookup scheme for ULFS directories.
36 1.6.2.2 tls */
37 1.6.2.2 tls
38 1.6.2.2 tls #include <sys/param.h>
39 1.6.2.2 tls #include <sys/systm.h>
40 1.6.2.2 tls #include <sys/kernel.h>
41 1.6.2.2 tls #include <sys/kmem.h>
42 1.6.2.2 tls #include <sys/types.h>
43 1.6.2.2 tls #include <sys/hash.h>
44 1.6.2.2 tls #include <sys/proc.h>
45 1.6.2.2 tls #include <sys/buf.h>
46 1.6.2.2 tls #include <sys/vnode.h>
47 1.6.2.2 tls #include <sys/mount.h>
48 1.6.2.2 tls #include <sys/pool.h>
49 1.6.2.2 tls #include <sys/sysctl.h>
50 1.6.2.2 tls #include <sys/atomic.h>
51 1.6.2.2 tls
52 1.6.2.2 tls #include <ufs/lfs/ulfs_inode.h>
53 1.6.2.2 tls #include <ufs/lfs/ulfs_dirhash.h>
54 1.6.2.2 tls #include <ufs/lfs/ulfsmount.h>
55 1.6.2.2 tls #include <ufs/lfs/ulfs_bswap.h>
56 1.6.2.2 tls #include <ufs/lfs/ulfs_extern.h>
57 1.6.2.2 tls
58 1.6.2.2 tls #define WRAPINCR(val, limit) (((val) + 1 == (limit)) ? 0 : ((val) + 1))
59 1.6.2.2 tls #define WRAPDECR(val, limit) (((val) == 0) ? ((limit) - 1) : ((val) - 1))
60 1.6.2.2 tls #define OFSFMT(ip) ((ip)->i_ump->um_maxsymlinklen <= 0)
61 1.6.2.2 tls #define BLKFREE2IDX(n) ((n) > DH_NFSTATS ? DH_NFSTATS : (n))
62 1.6.2.2 tls
63 1.6.2.2 tls static u_int ulfs_dirhashminblks = 5;
64 1.6.2.2 tls static u_int ulfs_dirhashmaxmem = 2 * 1024 * 1024;
65 1.6.2.2 tls static u_int ulfs_dirhashmem;
66 1.6.2.2 tls static u_int ulfs_dirhashcheck = 0;
67 1.6.2.2 tls
68 1.6.2.2 tls static int ulfsdirhash_hash(struct dirhash *dh, const char *name, int namelen);
69 1.6.2.2 tls static void ulfsdirhash_adjfree(struct dirhash *dh, doff_t offset, int diff,
70 1.6.2.2 tls int dirblksiz);
71 1.6.2.2 tls static void ulfsdirhash_delslot(struct dirhash *dh, int slot);
72 1.6.2.2 tls static int ulfsdirhash_findslot(struct dirhash *dh, const char *name,
73 1.6.2.2 tls int namelen, doff_t offset);
74 1.6.2.2 tls static doff_t ulfsdirhash_getprev(struct lfs_direct *dp, doff_t offset,
75 1.6.2.2 tls int dirblksiz);
76 1.6.2.2 tls static int ulfsdirhash_recycle(int wanted);
77 1.6.2.2 tls
78 1.6.2.2 tls static pool_cache_t ulfsdirhashblk_cache;
79 1.6.2.2 tls static pool_cache_t ulfsdirhash_cache;
80 1.6.2.2 tls
81 1.6.2.2 tls #define DIRHASHLIST_LOCK() mutex_enter(&ulfsdirhash_lock)
82 1.6.2.2 tls #define DIRHASHLIST_UNLOCK() mutex_exit(&ulfsdirhash_lock)
83 1.6.2.2 tls #define DIRHASH_LOCK(dh) mutex_enter(&(dh)->dh_lock)
84 1.6.2.2 tls #define DIRHASH_UNLOCK(dh) mutex_exit(&(dh)->dh_lock)
85 1.6.2.2 tls #define DIRHASH_BLKALLOC() \
86 1.6.2.2 tls pool_cache_get(ulfsdirhashblk_cache, PR_NOWAIT)
87 1.6.2.2 tls #define DIRHASH_BLKFREE(ptr) \
88 1.6.2.2 tls pool_cache_put(ulfsdirhashblk_cache, ptr)
89 1.6.2.2 tls
90 1.6.2.2 tls /* Dirhash list; recently-used entries are near the tail. */
91 1.6.2.2 tls static TAILQ_HEAD(, dirhash) ulfsdirhash_list;
92 1.6.2.2 tls
93 1.6.2.2 tls /* Protects: ulfsdirhash_list, `dh_list' field, ulfs_dirhashmem. */
94 1.6.2.2 tls static kmutex_t ulfsdirhash_lock;
95 1.6.2.2 tls
96 1.6.2.2 tls static struct sysctllog *ulfsdirhash_sysctl_log;
97 1.6.2.2 tls
98 1.6.2.2 tls /*
99 1.6.2.2 tls * Locking order:
100 1.6.2.2 tls * ulfsdirhash_lock
101 1.6.2.2 tls * dh_lock
102 1.6.2.2 tls *
103 1.6.2.2 tls * The dh_lock mutex should be acquired either via the inode lock, or via
104 1.6.2.2 tls * ulfsdirhash_lock. Only the owner of the inode may free the associated
105 1.6.2.2 tls * dirhash, but anything can steal its memory and set dh_hash to NULL.
106 1.6.2.2 tls */
107 1.6.2.2 tls
108 1.6.2.2 tls /*
109 1.6.2.2 tls * Attempt to build up a hash table for the directory contents in
110 1.6.2.2 tls * inode 'ip'. Returns 0 on success, or -1 of the operation failed.
111 1.6.2.2 tls */
112 1.6.2.2 tls int
113 1.6.2.2 tls ulfsdirhash_build(struct inode *ip)
114 1.6.2.2 tls {
115 1.6.2.2 tls struct dirhash *dh;
116 1.6.2.2 tls struct buf *bp = NULL;
117 1.6.2.2 tls struct lfs_direct *ep;
118 1.6.2.2 tls struct vnode *vp;
119 1.6.2.2 tls doff_t bmask, pos;
120 1.6.2.2 tls int dirblocks, i, j, memreqd, nblocks, narrays, nslots, slot;
121 1.6.2.2 tls const int needswap = ULFS_MPNEEDSWAP(ip->i_ump);
122 1.6.2.2 tls int dirblksiz = ip->i_ump->um_dirblksiz;
123 1.6.2.2 tls
124 1.6.2.2 tls /* Check if we can/should use dirhash. */
125 1.6.2.2 tls if (ip->i_dirhash == NULL) {
126 1.6.2.2 tls if (ip->i_size < (ulfs_dirhashminblks * dirblksiz) || OFSFMT(ip))
127 1.6.2.2 tls return (-1);
128 1.6.2.2 tls } else {
129 1.6.2.2 tls /* Hash exists, but sysctls could have changed. */
130 1.6.2.2 tls if (ip->i_size < (ulfs_dirhashminblks * dirblksiz) ||
131 1.6.2.2 tls ulfs_dirhashmem > ulfs_dirhashmaxmem) {
132 1.6.2.2 tls ulfsdirhash_free(ip);
133 1.6.2.2 tls return (-1);
134 1.6.2.2 tls }
135 1.6.2.2 tls /* Check if hash exists and is intact (note: unlocked read). */
136 1.6.2.2 tls if (ip->i_dirhash->dh_hash != NULL)
137 1.6.2.2 tls return (0);
138 1.6.2.2 tls /* Free the old, recycled hash and build a new one. */
139 1.6.2.2 tls ulfsdirhash_free(ip);
140 1.6.2.2 tls }
141 1.6.2.2 tls
142 1.6.2.2 tls /* Don't hash removed directories. */
143 1.6.2.2 tls if (ip->i_nlink == 0)
144 1.6.2.2 tls return (-1);
145 1.6.2.2 tls
146 1.6.2.2 tls vp = ip->i_vnode;
147 1.6.2.2 tls /* Allocate 50% more entries than this dir size could ever need. */
148 1.6.2.2 tls KASSERT(ip->i_size >= dirblksiz);
149 1.6.2.2 tls nslots = ip->i_size / LFS_DIRECTSIZ(1);
150 1.6.2.2 tls nslots = (nslots * 3 + 1) / 2;
151 1.6.2.2 tls narrays = howmany(nslots, DH_NBLKOFF);
152 1.6.2.2 tls nslots = narrays * DH_NBLKOFF;
153 1.6.2.2 tls dirblocks = howmany(ip->i_size, dirblksiz);
154 1.6.2.2 tls nblocks = (dirblocks * 3 + 1) / 2;
155 1.6.2.2 tls
156 1.6.2.2 tls memreqd = sizeof(*dh) + narrays * sizeof(*dh->dh_hash) +
157 1.6.2.2 tls narrays * DH_NBLKOFF * sizeof(**dh->dh_hash) +
158 1.6.2.2 tls nblocks * sizeof(*dh->dh_blkfree);
159 1.6.2.2 tls
160 1.6.2.2 tls while (atomic_add_int_nv(&ulfs_dirhashmem, memreqd) >
161 1.6.2.2 tls ulfs_dirhashmaxmem) {
162 1.6.2.2 tls atomic_add_int(&ulfs_dirhashmem, -memreqd);
163 1.6.2.2 tls if (memreqd > ulfs_dirhashmaxmem / 2)
164 1.6.2.2 tls return (-1);
165 1.6.2.2 tls /* Try to free some space. */
166 1.6.2.2 tls if (ulfsdirhash_recycle(memreqd) != 0)
167 1.6.2.2 tls return (-1);
168 1.6.2.2 tls else
169 1.6.2.2 tls DIRHASHLIST_UNLOCK();
170 1.6.2.2 tls }
171 1.6.2.2 tls
172 1.6.2.2 tls /*
173 1.6.2.2 tls * Use non-blocking mallocs so that we will revert to a linear
174 1.6.2.2 tls * lookup on failure rather than potentially blocking forever.
175 1.6.2.2 tls */
176 1.6.2.2 tls dh = pool_cache_get(ulfsdirhash_cache, PR_NOWAIT);
177 1.6.2.2 tls if (dh == NULL) {
178 1.6.2.2 tls atomic_add_int(&ulfs_dirhashmem, -memreqd);
179 1.6.2.2 tls return (-1);
180 1.6.2.2 tls }
181 1.6.2.2 tls memset(dh, 0, sizeof(*dh));
182 1.6.2.2 tls mutex_init(&dh->dh_lock, MUTEX_DEFAULT, IPL_NONE);
183 1.6.2.2 tls DIRHASH_LOCK(dh);
184 1.6.2.2 tls dh->dh_hashsz = narrays * sizeof(dh->dh_hash[0]);
185 1.6.2.2 tls dh->dh_hash = kmem_zalloc(dh->dh_hashsz, KM_NOSLEEP);
186 1.6.2.2 tls dh->dh_blkfreesz = nblocks * sizeof(dh->dh_blkfree[0]);
187 1.6.2.2 tls dh->dh_blkfree = kmem_zalloc(dh->dh_blkfreesz, KM_NOSLEEP);
188 1.6.2.2 tls if (dh->dh_hash == NULL || dh->dh_blkfree == NULL)
189 1.6.2.2 tls goto fail;
190 1.6.2.2 tls for (i = 0; i < narrays; i++) {
191 1.6.2.2 tls if ((dh->dh_hash[i] = DIRHASH_BLKALLOC()) == NULL)
192 1.6.2.2 tls goto fail;
193 1.6.2.2 tls for (j = 0; j < DH_NBLKOFF; j++)
194 1.6.2.2 tls dh->dh_hash[i][j] = DIRHASH_EMPTY;
195 1.6.2.2 tls }
196 1.6.2.2 tls
197 1.6.2.2 tls /* Initialise the hash table and block statistics. */
198 1.6.2.2 tls dh->dh_narrays = narrays;
199 1.6.2.2 tls dh->dh_hlen = nslots;
200 1.6.2.2 tls dh->dh_nblk = nblocks;
201 1.6.2.2 tls dh->dh_dirblks = dirblocks;
202 1.6.2.2 tls for (i = 0; i < dirblocks; i++)
203 1.6.2.2 tls dh->dh_blkfree[i] = dirblksiz / DIRALIGN;
204 1.6.2.2 tls for (i = 0; i < DH_NFSTATS; i++)
205 1.6.2.2 tls dh->dh_firstfree[i] = -1;
206 1.6.2.2 tls dh->dh_firstfree[DH_NFSTATS] = 0;
207 1.6.2.2 tls dh->dh_seqopt = 0;
208 1.6.2.2 tls dh->dh_seqoff = 0;
209 1.6.2.2 tls dh->dh_score = DH_SCOREINIT;
210 1.6.2.2 tls ip->i_dirhash = dh;
211 1.6.2.2 tls
212 1.6.2.2 tls bmask = VFSTOULFS(vp->v_mount)->um_mountp->mnt_stat.f_iosize - 1;
213 1.6.2.2 tls pos = 0;
214 1.6.2.2 tls while (pos < ip->i_size) {
215 1.6.2.2 tls if ((curcpu()->ci_schedstate.spc_flags & SPCF_SHOULDYIELD)
216 1.6.2.2 tls != 0) {
217 1.6.2.2 tls preempt();
218 1.6.2.2 tls }
219 1.6.2.2 tls /* If necessary, get the next directory block. */
220 1.6.2.2 tls if ((pos & bmask) == 0) {
221 1.6.2.2 tls if (bp != NULL)
222 1.6.2.2 tls brelse(bp, 0);
223 1.6.2.2 tls if (ulfs_blkatoff(vp, (off_t)pos, NULL, &bp, false) != 0)
224 1.6.2.2 tls goto fail;
225 1.6.2.2 tls }
226 1.6.2.2 tls
227 1.6.2.2 tls /* Add this entry to the hash. */
228 1.6.2.2 tls ep = (struct lfs_direct *)((char *)bp->b_data + (pos & bmask));
229 1.6.2.2 tls if (ep->d_reclen == 0 || ep->d_reclen >
230 1.6.2.2 tls dirblksiz - (pos & (dirblksiz - 1))) {
231 1.6.2.2 tls /* Corrupted directory. */
232 1.6.2.2 tls brelse(bp, 0);
233 1.6.2.2 tls goto fail;
234 1.6.2.2 tls }
235 1.6.2.2 tls if (ep->d_ino != 0) {
236 1.6.2.2 tls /* Add the entry (simplified ulfsdirhash_add). */
237 1.6.2.2 tls slot = ulfsdirhash_hash(dh, ep->d_name, ep->d_namlen);
238 1.6.2.2 tls while (DH_ENTRY(dh, slot) != DIRHASH_EMPTY)
239 1.6.2.2 tls slot = WRAPINCR(slot, dh->dh_hlen);
240 1.6.2.2 tls dh->dh_hused++;
241 1.6.2.2 tls DH_ENTRY(dh, slot) = pos;
242 1.6.2.2 tls ulfsdirhash_adjfree(dh, pos, -LFS_DIRSIZ(0, ep, needswap),
243 1.6.2.2 tls dirblksiz);
244 1.6.2.2 tls }
245 1.6.2.2 tls pos += ep->d_reclen;
246 1.6.2.2 tls }
247 1.6.2.2 tls
248 1.6.2.2 tls if (bp != NULL)
249 1.6.2.2 tls brelse(bp, 0);
250 1.6.2.2 tls DIRHASHLIST_LOCK();
251 1.6.2.2 tls TAILQ_INSERT_TAIL(&ulfsdirhash_list, dh, dh_list);
252 1.6.2.2 tls dh->dh_onlist = 1;
253 1.6.2.2 tls DIRHASH_UNLOCK(dh);
254 1.6.2.2 tls DIRHASHLIST_UNLOCK();
255 1.6.2.2 tls return (0);
256 1.6.2.2 tls
257 1.6.2.2 tls fail:
258 1.6.2.2 tls DIRHASH_UNLOCK(dh);
259 1.6.2.2 tls if (dh->dh_hash != NULL) {
260 1.6.2.2 tls for (i = 0; i < narrays; i++)
261 1.6.2.2 tls if (dh->dh_hash[i] != NULL)
262 1.6.2.2 tls DIRHASH_BLKFREE(dh->dh_hash[i]);
263 1.6.2.2 tls kmem_free(dh->dh_hash, dh->dh_hashsz);
264 1.6.2.2 tls }
265 1.6.2.2 tls if (dh->dh_blkfree != NULL)
266 1.6.2.2 tls kmem_free(dh->dh_blkfree, dh->dh_blkfreesz);
267 1.6.2.2 tls mutex_destroy(&dh->dh_lock);
268 1.6.2.2 tls pool_cache_put(ulfsdirhash_cache, dh);
269 1.6.2.2 tls ip->i_dirhash = NULL;
270 1.6.2.2 tls atomic_add_int(&ulfs_dirhashmem, -memreqd);
271 1.6.2.2 tls return (-1);
272 1.6.2.2 tls }
273 1.6.2.2 tls
274 1.6.2.2 tls /*
275 1.6.2.2 tls * Free any hash table associated with inode 'ip'.
276 1.6.2.2 tls */
277 1.6.2.2 tls void
278 1.6.2.2 tls ulfsdirhash_free(struct inode *ip)
279 1.6.2.2 tls {
280 1.6.2.2 tls struct dirhash *dh;
281 1.6.2.2 tls int i, mem;
282 1.6.2.2 tls
283 1.6.2.2 tls if ((dh = ip->i_dirhash) == NULL)
284 1.6.2.2 tls return;
285 1.6.2.2 tls
286 1.6.2.2 tls if (dh->dh_onlist) {
287 1.6.2.2 tls DIRHASHLIST_LOCK();
288 1.6.2.2 tls if (dh->dh_onlist)
289 1.6.2.2 tls TAILQ_REMOVE(&ulfsdirhash_list, dh, dh_list);
290 1.6.2.2 tls DIRHASHLIST_UNLOCK();
291 1.6.2.2 tls }
292 1.6.2.2 tls
293 1.6.2.2 tls /* The dirhash pointed to by 'dh' is exclusively ours now. */
294 1.6.2.2 tls mem = sizeof(*dh);
295 1.6.2.2 tls if (dh->dh_hash != NULL) {
296 1.6.2.2 tls for (i = 0; i < dh->dh_narrays; i++)
297 1.6.2.2 tls DIRHASH_BLKFREE(dh->dh_hash[i]);
298 1.6.2.2 tls kmem_free(dh->dh_hash, dh->dh_hashsz);
299 1.6.2.2 tls kmem_free(dh->dh_blkfree, dh->dh_blkfreesz);
300 1.6.2.2 tls mem += dh->dh_hashsz;
301 1.6.2.2 tls mem += dh->dh_narrays * DH_NBLKOFF * sizeof(**dh->dh_hash);
302 1.6.2.2 tls mem += dh->dh_nblk * sizeof(*dh->dh_blkfree);
303 1.6.2.2 tls }
304 1.6.2.2 tls mutex_destroy(&dh->dh_lock);
305 1.6.2.2 tls pool_cache_put(ulfsdirhash_cache, dh);
306 1.6.2.2 tls ip->i_dirhash = NULL;
307 1.6.2.2 tls
308 1.6.2.2 tls atomic_add_int(&ulfs_dirhashmem, -mem);
309 1.6.2.2 tls }
310 1.6.2.2 tls
311 1.6.2.2 tls /*
312 1.6.2.2 tls * Find the offset of the specified name within the given inode.
313 1.6.2.2 tls * Returns 0 on success, ENOENT if the entry does not exist, or
314 1.6.2.2 tls * EJUSTRETURN if the caller should revert to a linear search.
315 1.6.2.2 tls *
316 1.6.2.2 tls * If successful, the directory offset is stored in *offp, and a
317 1.6.2.2 tls * pointer to a struct buf containing the entry is stored in *bpp. If
318 1.6.2.2 tls * prevoffp is non-NULL, the offset of the previous entry within
319 1.6.2.2 tls * the DIRBLKSIZ-sized block is stored in *prevoffp (if the entry
320 1.6.2.2 tls * is the first in a block, the start of the block is used).
321 1.6.2.2 tls */
322 1.6.2.2 tls int
323 1.6.2.2 tls ulfsdirhash_lookup(struct inode *ip, const char *name, int namelen, doff_t *offp,
324 1.6.2.2 tls struct buf **bpp, doff_t *prevoffp)
325 1.6.2.2 tls {
326 1.6.2.2 tls struct dirhash *dh, *dh_next;
327 1.6.2.2 tls struct lfs_direct *dp;
328 1.6.2.2 tls struct vnode *vp;
329 1.6.2.2 tls struct buf *bp;
330 1.6.2.2 tls doff_t blkoff, bmask, offset, prevoff;
331 1.6.2.2 tls int i, slot;
332 1.6.2.2 tls const int needswap = ULFS_MPNEEDSWAP(ip->i_ump);
333 1.6.2.2 tls int dirblksiz = ip->i_ump->um_dirblksiz;
334 1.6.2.2 tls
335 1.6.2.2 tls if ((dh = ip->i_dirhash) == NULL)
336 1.6.2.2 tls return (EJUSTRETURN);
337 1.6.2.2 tls
338 1.6.2.2 tls /*
339 1.6.2.2 tls * Move this dirhash towards the end of the list if it has a
340 1.6.2.2 tls * score higher than the next entry, and acquire the dh_lock.
341 1.6.2.2 tls * Optimise the case where it's already the last by performing
342 1.6.2.2 tls * an unlocked read of the TAILQ_NEXT pointer.
343 1.6.2.2 tls *
344 1.6.2.2 tls * In both cases, end up holding just dh_lock.
345 1.6.2.2 tls */
346 1.6.2.2 tls if (TAILQ_NEXT(dh, dh_list) != NULL) {
347 1.6.2.2 tls DIRHASHLIST_LOCK();
348 1.6.2.2 tls DIRHASH_LOCK(dh);
349 1.6.2.2 tls /*
350 1.6.2.2 tls * If the new score will be greater than that of the next
351 1.6.2.2 tls * entry, then move this entry past it. With both mutexes
352 1.6.2.2 tls * held, dh_next won't go away, but its dh_score could
353 1.6.2.2 tls * change; that's not important since it is just a hint.
354 1.6.2.2 tls */
355 1.6.2.2 tls if (dh->dh_hash != NULL &&
356 1.6.2.2 tls (dh_next = TAILQ_NEXT(dh, dh_list)) != NULL &&
357 1.6.2.2 tls dh->dh_score >= dh_next->dh_score) {
358 1.6.2.2 tls KASSERT(dh->dh_onlist);
359 1.6.2.2 tls TAILQ_REMOVE(&ulfsdirhash_list, dh, dh_list);
360 1.6.2.2 tls TAILQ_INSERT_AFTER(&ulfsdirhash_list, dh_next, dh,
361 1.6.2.2 tls dh_list);
362 1.6.2.2 tls }
363 1.6.2.2 tls DIRHASHLIST_UNLOCK();
364 1.6.2.2 tls } else {
365 1.6.2.2 tls /* Already the last, though that could change as we wait. */
366 1.6.2.2 tls DIRHASH_LOCK(dh);
367 1.6.2.2 tls }
368 1.6.2.2 tls if (dh->dh_hash == NULL) {
369 1.6.2.2 tls DIRHASH_UNLOCK(dh);
370 1.6.2.2 tls ulfsdirhash_free(ip);
371 1.6.2.2 tls return (EJUSTRETURN);
372 1.6.2.2 tls }
373 1.6.2.2 tls
374 1.6.2.2 tls /* Update the score. */
375 1.6.2.2 tls if (dh->dh_score < DH_SCOREMAX)
376 1.6.2.2 tls dh->dh_score++;
377 1.6.2.2 tls
378 1.6.2.2 tls vp = ip->i_vnode;
379 1.6.2.2 tls bmask = VFSTOULFS(vp->v_mount)->um_mountp->mnt_stat.f_iosize - 1;
380 1.6.2.2 tls blkoff = -1;
381 1.6.2.2 tls bp = NULL;
382 1.6.2.2 tls restart:
383 1.6.2.2 tls slot = ulfsdirhash_hash(dh, name, namelen);
384 1.6.2.2 tls
385 1.6.2.2 tls if (dh->dh_seqopt) {
386 1.6.2.2 tls /*
387 1.6.2.2 tls * Sequential access optimisation. dh_seqoff contains the
388 1.6.2.2 tls * offset of the directory entry immediately following
389 1.6.2.2 tls * the last entry that was looked up. Check if this offset
390 1.6.2.2 tls * appears in the hash chain for the name we are looking for.
391 1.6.2.2 tls */
392 1.6.2.2 tls for (i = slot; (offset = DH_ENTRY(dh, i)) != DIRHASH_EMPTY;
393 1.6.2.2 tls i = WRAPINCR(i, dh->dh_hlen))
394 1.6.2.2 tls if (offset == dh->dh_seqoff)
395 1.6.2.2 tls break;
396 1.6.2.2 tls if (offset == dh->dh_seqoff) {
397 1.6.2.2 tls /*
398 1.6.2.2 tls * We found an entry with the expected offset. This
399 1.6.2.2 tls * is probably the entry we want, but if not, the
400 1.6.2.2 tls * code below will turn off seqoff and retry.
401 1.6.2.2 tls */
402 1.6.2.2 tls slot = i;
403 1.6.2.2 tls } else
404 1.6.2.2 tls dh->dh_seqopt = 0;
405 1.6.2.2 tls }
406 1.6.2.2 tls
407 1.6.2.2 tls for (; (offset = DH_ENTRY(dh, slot)) != DIRHASH_EMPTY;
408 1.6.2.2 tls slot = WRAPINCR(slot, dh->dh_hlen)) {
409 1.6.2.2 tls if (offset == DIRHASH_DEL)
410 1.6.2.2 tls continue;
411 1.6.2.2 tls
412 1.6.2.2 tls if (offset < 0 || offset >= ip->i_size)
413 1.6.2.2 tls panic("ulfsdirhash_lookup: bad offset in hash array");
414 1.6.2.2 tls if ((offset & ~bmask) != blkoff) {
415 1.6.2.2 tls if (bp != NULL)
416 1.6.2.2 tls brelse(bp, 0);
417 1.6.2.2 tls blkoff = offset & ~bmask;
418 1.6.2.2 tls if (ulfs_blkatoff(vp, (off_t)blkoff,
419 1.6.2.2 tls NULL, &bp, false) != 0) {
420 1.6.2.2 tls DIRHASH_UNLOCK(dh);
421 1.6.2.2 tls return (EJUSTRETURN);
422 1.6.2.2 tls }
423 1.6.2.2 tls }
424 1.6.2.2 tls dp = (struct lfs_direct *)((char *)bp->b_data + (offset & bmask));
425 1.6.2.2 tls if (dp->d_reclen == 0 || dp->d_reclen >
426 1.6.2.2 tls dirblksiz - (offset & (dirblksiz - 1))) {
427 1.6.2.2 tls /* Corrupted directory. */
428 1.6.2.2 tls DIRHASH_UNLOCK(dh);
429 1.6.2.2 tls brelse(bp, 0);
430 1.6.2.2 tls return (EJUSTRETURN);
431 1.6.2.2 tls }
432 1.6.2.2 tls if (dp->d_namlen == namelen &&
433 1.6.2.2 tls memcmp(dp->d_name, name, namelen) == 0) {
434 1.6.2.2 tls /* Found. Get the prev offset if needed. */
435 1.6.2.2 tls if (prevoffp != NULL) {
436 1.6.2.2 tls if (offset & (dirblksiz - 1)) {
437 1.6.2.2 tls prevoff = ulfsdirhash_getprev(dp,
438 1.6.2.2 tls offset, dirblksiz);
439 1.6.2.2 tls if (prevoff == -1) {
440 1.6.2.2 tls brelse(bp, 0);
441 1.6.2.2 tls return (EJUSTRETURN);
442 1.6.2.2 tls }
443 1.6.2.2 tls } else
444 1.6.2.2 tls prevoff = offset;
445 1.6.2.2 tls *prevoffp = prevoff;
446 1.6.2.2 tls }
447 1.6.2.2 tls
448 1.6.2.2 tls /* Check for sequential access, and update offset. */
449 1.6.2.2 tls if (dh->dh_seqopt == 0 && dh->dh_seqoff == offset)
450 1.6.2.2 tls dh->dh_seqopt = 1;
451 1.6.2.2 tls dh->dh_seqoff = offset + LFS_DIRSIZ(0, dp, needswap);
452 1.6.2.2 tls DIRHASH_UNLOCK(dh);
453 1.6.2.2 tls
454 1.6.2.2 tls *bpp = bp;
455 1.6.2.2 tls *offp = offset;
456 1.6.2.2 tls return (0);
457 1.6.2.2 tls }
458 1.6.2.2 tls
459 1.6.2.2 tls if (dh->dh_hash == NULL) {
460 1.6.2.2 tls DIRHASH_UNLOCK(dh);
461 1.6.2.2 tls if (bp != NULL)
462 1.6.2.2 tls brelse(bp, 0);
463 1.6.2.2 tls ulfsdirhash_free(ip);
464 1.6.2.2 tls return (EJUSTRETURN);
465 1.6.2.2 tls }
466 1.6.2.2 tls /*
467 1.6.2.2 tls * When the name doesn't match in the seqopt case, go back
468 1.6.2.2 tls * and search normally.
469 1.6.2.2 tls */
470 1.6.2.2 tls if (dh->dh_seqopt) {
471 1.6.2.2 tls dh->dh_seqopt = 0;
472 1.6.2.2 tls goto restart;
473 1.6.2.2 tls }
474 1.6.2.2 tls }
475 1.6.2.2 tls DIRHASH_UNLOCK(dh);
476 1.6.2.2 tls if (bp != NULL)
477 1.6.2.2 tls brelse(bp, 0);
478 1.6.2.2 tls return (ENOENT);
479 1.6.2.2 tls }
480 1.6.2.2 tls
481 1.6.2.2 tls /*
482 1.6.2.2 tls * Find a directory block with room for 'slotneeded' bytes. Returns
483 1.6.2.2 tls * the offset of the directory entry that begins the free space.
484 1.6.2.2 tls * This will either be the offset of an existing entry that has free
485 1.6.2.2 tls * space at the end, or the offset of an entry with d_ino == 0 at
486 1.6.2.2 tls * the start of a DIRBLKSIZ block.
487 1.6.2.2 tls *
488 1.6.2.2 tls * To use the space, the caller may need to compact existing entries in
489 1.6.2.2 tls * the directory. The total number of bytes in all of the entries involved
490 1.6.2.2 tls * in the compaction is stored in *slotsize. In other words, all of
491 1.6.2.2 tls * the entries that must be compacted are exactly contained in the
492 1.6.2.2 tls * region beginning at the returned offset and spanning *slotsize bytes.
493 1.6.2.2 tls *
494 1.6.2.2 tls * Returns -1 if no space was found, indicating that the directory
495 1.6.2.2 tls * must be extended.
496 1.6.2.2 tls */
497 1.6.2.2 tls doff_t
498 1.6.2.2 tls ulfsdirhash_findfree(struct inode *ip, int slotneeded, int *slotsize)
499 1.6.2.2 tls {
500 1.6.2.2 tls struct lfs_direct *dp;
501 1.6.2.2 tls struct dirhash *dh;
502 1.6.2.2 tls struct buf *bp;
503 1.6.2.2 tls doff_t pos, slotstart;
504 1.6.2.2 tls int dirblock, error, freebytes, i;
505 1.6.2.2 tls const int needswap = ULFS_MPNEEDSWAP(ip->i_ump);
506 1.6.2.2 tls int dirblksiz = ip->i_ump->um_dirblksiz;
507 1.6.2.2 tls
508 1.6.2.2 tls if ((dh = ip->i_dirhash) == NULL)
509 1.6.2.2 tls return (-1);
510 1.6.2.2 tls
511 1.6.2.2 tls DIRHASH_LOCK(dh);
512 1.6.2.2 tls if (dh->dh_hash == NULL) {
513 1.6.2.2 tls DIRHASH_UNLOCK(dh);
514 1.6.2.2 tls ulfsdirhash_free(ip);
515 1.6.2.2 tls return (-1);
516 1.6.2.2 tls }
517 1.6.2.2 tls
518 1.6.2.2 tls /* Find a directory block with the desired free space. */
519 1.6.2.2 tls dirblock = -1;
520 1.6.2.2 tls for (i = howmany(slotneeded, DIRALIGN); i <= DH_NFSTATS; i++)
521 1.6.2.2 tls if ((dirblock = dh->dh_firstfree[i]) != -1)
522 1.6.2.2 tls break;
523 1.6.2.2 tls if (dirblock == -1) {
524 1.6.2.2 tls DIRHASH_UNLOCK(dh);
525 1.6.2.2 tls return (-1);
526 1.6.2.2 tls }
527 1.6.2.2 tls
528 1.6.2.2 tls KASSERT(dirblock < dh->dh_nblk &&
529 1.6.2.2 tls dh->dh_blkfree[dirblock] >= howmany(slotneeded, DIRALIGN));
530 1.6.2.2 tls pos = dirblock * dirblksiz;
531 1.6.2.2 tls error = ulfs_blkatoff(ip->i_vnode, (off_t)pos, (void *)&dp, &bp, false);
532 1.6.2.2 tls if (error) {
533 1.6.2.2 tls DIRHASH_UNLOCK(dh);
534 1.6.2.2 tls return (-1);
535 1.6.2.2 tls }
536 1.6.2.2 tls /* Find the first entry with free space. */
537 1.6.2.2 tls for (i = 0; i < dirblksiz; ) {
538 1.6.2.2 tls if (dp->d_reclen == 0) {
539 1.6.2.2 tls DIRHASH_UNLOCK(dh);
540 1.6.2.2 tls brelse(bp, 0);
541 1.6.2.2 tls return (-1);
542 1.6.2.2 tls }
543 1.6.2.2 tls if (dp->d_ino == 0 || dp->d_reclen > LFS_DIRSIZ(0, dp, needswap))
544 1.6.2.2 tls break;
545 1.6.2.2 tls i += dp->d_reclen;
546 1.6.2.2 tls dp = (struct lfs_direct *)((char *)dp + dp->d_reclen);
547 1.6.2.2 tls }
548 1.6.2.2 tls if (i > dirblksiz) {
549 1.6.2.2 tls DIRHASH_UNLOCK(dh);
550 1.6.2.2 tls brelse(bp, 0);
551 1.6.2.2 tls return (-1);
552 1.6.2.2 tls }
553 1.6.2.2 tls slotstart = pos + i;
554 1.6.2.2 tls
555 1.6.2.2 tls /* Find the range of entries needed to get enough space */
556 1.6.2.2 tls freebytes = 0;
557 1.6.2.2 tls while (i < dirblksiz && freebytes < slotneeded) {
558 1.6.2.2 tls freebytes += dp->d_reclen;
559 1.6.2.2 tls if (dp->d_ino != 0)
560 1.6.2.2 tls freebytes -= LFS_DIRSIZ(0, dp, needswap);
561 1.6.2.2 tls if (dp->d_reclen == 0) {
562 1.6.2.2 tls DIRHASH_UNLOCK(dh);
563 1.6.2.2 tls brelse(bp, 0);
564 1.6.2.2 tls return (-1);
565 1.6.2.2 tls }
566 1.6.2.2 tls i += dp->d_reclen;
567 1.6.2.2 tls dp = (struct lfs_direct *)((char *)dp + dp->d_reclen);
568 1.6.2.2 tls }
569 1.6.2.2 tls if (i > dirblksiz) {
570 1.6.2.2 tls DIRHASH_UNLOCK(dh);
571 1.6.2.2 tls brelse(bp, 0);
572 1.6.2.2 tls return (-1);
573 1.6.2.2 tls }
574 1.6.2.2 tls if (freebytes < slotneeded)
575 1.6.2.2 tls panic("ulfsdirhash_findfree: free mismatch");
576 1.6.2.2 tls DIRHASH_UNLOCK(dh);
577 1.6.2.2 tls brelse(bp, 0);
578 1.6.2.2 tls *slotsize = pos + i - slotstart;
579 1.6.2.2 tls return (slotstart);
580 1.6.2.2 tls }
581 1.6.2.2 tls
582 1.6.2.2 tls /*
583 1.6.2.2 tls * Return the start of the unused space at the end of a directory, or
584 1.6.2.2 tls * -1 if there are no trailing unused blocks.
585 1.6.2.2 tls */
586 1.6.2.2 tls doff_t
587 1.6.2.2 tls ulfsdirhash_enduseful(struct inode *ip)
588 1.6.2.2 tls {
589 1.6.2.2 tls struct dirhash *dh;
590 1.6.2.2 tls int i;
591 1.6.2.2 tls int dirblksiz = ip->i_ump->um_dirblksiz;
592 1.6.2.2 tls
593 1.6.2.2 tls if ((dh = ip->i_dirhash) == NULL)
594 1.6.2.2 tls return (-1);
595 1.6.2.2 tls
596 1.6.2.2 tls DIRHASH_LOCK(dh);
597 1.6.2.2 tls if (dh->dh_hash == NULL) {
598 1.6.2.2 tls DIRHASH_UNLOCK(dh);
599 1.6.2.2 tls ulfsdirhash_free(ip);
600 1.6.2.2 tls return (-1);
601 1.6.2.2 tls }
602 1.6.2.2 tls
603 1.6.2.2 tls if (dh->dh_blkfree[dh->dh_dirblks - 1] != dirblksiz / DIRALIGN) {
604 1.6.2.2 tls DIRHASH_UNLOCK(dh);
605 1.6.2.2 tls return (-1);
606 1.6.2.2 tls }
607 1.6.2.2 tls
608 1.6.2.2 tls for (i = dh->dh_dirblks - 1; i >= 0; i--)
609 1.6.2.2 tls if (dh->dh_blkfree[i] != dirblksiz / DIRALIGN)
610 1.6.2.2 tls break;
611 1.6.2.2 tls DIRHASH_UNLOCK(dh);
612 1.6.2.2 tls return ((doff_t)(i + 1) * dirblksiz);
613 1.6.2.2 tls }
614 1.6.2.2 tls
615 1.6.2.2 tls /*
616 1.6.2.2 tls * Insert information into the hash about a new directory entry. dirp
617 1.6.2.2 tls * points to a struct lfs_direct containing the entry, and offset specifies
618 1.6.2.2 tls * the offset of this entry.
619 1.6.2.2 tls */
620 1.6.2.2 tls void
621 1.6.2.2 tls ulfsdirhash_add(struct inode *ip, struct lfs_direct *dirp, doff_t offset)
622 1.6.2.2 tls {
623 1.6.2.2 tls struct dirhash *dh;
624 1.6.2.2 tls int slot;
625 1.6.2.2 tls const int needswap = ULFS_MPNEEDSWAP(ip->i_ump);
626 1.6.2.2 tls int dirblksiz = ip->i_ump->um_dirblksiz;
627 1.6.2.2 tls
628 1.6.2.2 tls if ((dh = ip->i_dirhash) == NULL)
629 1.6.2.2 tls return;
630 1.6.2.2 tls
631 1.6.2.2 tls DIRHASH_LOCK(dh);
632 1.6.2.2 tls if (dh->dh_hash == NULL) {
633 1.6.2.2 tls DIRHASH_UNLOCK(dh);
634 1.6.2.2 tls ulfsdirhash_free(ip);
635 1.6.2.2 tls return;
636 1.6.2.2 tls }
637 1.6.2.2 tls
638 1.6.2.2 tls KASSERT(offset < dh->dh_dirblks * dirblksiz);
639 1.6.2.2 tls /*
640 1.6.2.2 tls * Normal hash usage is < 66%. If the usage gets too high then
641 1.6.2.2 tls * remove the hash entirely and let it be rebuilt later.
642 1.6.2.2 tls */
643 1.6.2.2 tls if (dh->dh_hused >= (dh->dh_hlen * 3) / 4) {
644 1.6.2.2 tls DIRHASH_UNLOCK(dh);
645 1.6.2.2 tls ulfsdirhash_free(ip);
646 1.6.2.2 tls return;
647 1.6.2.2 tls }
648 1.6.2.2 tls
649 1.6.2.2 tls /* Find a free hash slot (empty or deleted), and add the entry. */
650 1.6.2.2 tls slot = ulfsdirhash_hash(dh, dirp->d_name, dirp->d_namlen);
651 1.6.2.2 tls while (DH_ENTRY(dh, slot) >= 0)
652 1.6.2.2 tls slot = WRAPINCR(slot, dh->dh_hlen);
653 1.6.2.2 tls if (DH_ENTRY(dh, slot) == DIRHASH_EMPTY)
654 1.6.2.2 tls dh->dh_hused++;
655 1.6.2.2 tls DH_ENTRY(dh, slot) = offset;
656 1.6.2.2 tls
657 1.6.2.2 tls /* Update the per-block summary info. */
658 1.6.2.2 tls ulfsdirhash_adjfree(dh, offset, -LFS_DIRSIZ(0, dirp, needswap), dirblksiz);
659 1.6.2.2 tls DIRHASH_UNLOCK(dh);
660 1.6.2.2 tls }
661 1.6.2.2 tls
662 1.6.2.2 tls /*
663 1.6.2.2 tls * Remove the specified directory entry from the hash. The entry to remove
664 1.6.2.2 tls * is defined by the name in `dirp', which must exist at the specified
665 1.6.2.2 tls * `offset' within the directory.
666 1.6.2.2 tls */
667 1.6.2.2 tls void
668 1.6.2.2 tls ulfsdirhash_remove(struct inode *ip, struct lfs_direct *dirp, doff_t offset)
669 1.6.2.2 tls {
670 1.6.2.2 tls struct dirhash *dh;
671 1.6.2.2 tls int slot;
672 1.6.2.2 tls const int needswap = ULFS_MPNEEDSWAP(ip->i_ump);
673 1.6.2.2 tls int dirblksiz = ip->i_ump->um_dirblksiz;
674 1.6.2.2 tls
675 1.6.2.2 tls if ((dh = ip->i_dirhash) == NULL)
676 1.6.2.2 tls return;
677 1.6.2.2 tls
678 1.6.2.2 tls DIRHASH_LOCK(dh);
679 1.6.2.2 tls if (dh->dh_hash == NULL) {
680 1.6.2.2 tls DIRHASH_UNLOCK(dh);
681 1.6.2.2 tls ulfsdirhash_free(ip);
682 1.6.2.2 tls return;
683 1.6.2.2 tls }
684 1.6.2.2 tls
685 1.6.2.2 tls KASSERT(offset < dh->dh_dirblks * dirblksiz);
686 1.6.2.2 tls /* Find the entry */
687 1.6.2.2 tls slot = ulfsdirhash_findslot(dh, dirp->d_name, dirp->d_namlen, offset);
688 1.6.2.2 tls
689 1.6.2.2 tls /* Remove the hash entry. */
690 1.6.2.2 tls ulfsdirhash_delslot(dh, slot);
691 1.6.2.2 tls
692 1.6.2.2 tls /* Update the per-block summary info. */
693 1.6.2.2 tls ulfsdirhash_adjfree(dh, offset, LFS_DIRSIZ(0, dirp, needswap), dirblksiz);
694 1.6.2.2 tls DIRHASH_UNLOCK(dh);
695 1.6.2.2 tls }
696 1.6.2.2 tls
697 1.6.2.2 tls /*
698 1.6.2.2 tls * Change the offset associated with a directory entry in the hash. Used
699 1.6.2.2 tls * when compacting directory blocks.
700 1.6.2.2 tls */
701 1.6.2.2 tls void
702 1.6.2.2 tls ulfsdirhash_move(struct inode *ip, struct lfs_direct *dirp, doff_t oldoff,
703 1.6.2.2 tls doff_t newoff)
704 1.6.2.2 tls {
705 1.6.2.2 tls struct dirhash *dh;
706 1.6.2.2 tls int slot;
707 1.6.2.2 tls
708 1.6.2.2 tls if ((dh = ip->i_dirhash) == NULL)
709 1.6.2.2 tls return;
710 1.6.2.2 tls DIRHASH_LOCK(dh);
711 1.6.2.2 tls if (dh->dh_hash == NULL) {
712 1.6.2.2 tls DIRHASH_UNLOCK(dh);
713 1.6.2.2 tls ulfsdirhash_free(ip);
714 1.6.2.2 tls return;
715 1.6.2.2 tls }
716 1.6.2.2 tls
717 1.6.2.2 tls KASSERT(oldoff < dh->dh_dirblks * ip->i_ump->um_dirblksiz &&
718 1.6.2.2 tls newoff < dh->dh_dirblks * ip->i_ump->um_dirblksiz);
719 1.6.2.2 tls /* Find the entry, and update the offset. */
720 1.6.2.2 tls slot = ulfsdirhash_findslot(dh, dirp->d_name, dirp->d_namlen, oldoff);
721 1.6.2.2 tls DH_ENTRY(dh, slot) = newoff;
722 1.6.2.2 tls DIRHASH_UNLOCK(dh);
723 1.6.2.2 tls }
724 1.6.2.2 tls
725 1.6.2.2 tls /*
726 1.6.2.2 tls * Inform dirhash that the directory has grown by one block that
727 1.6.2.2 tls * begins at offset (i.e. the new length is offset + DIRBLKSIZ).
728 1.6.2.2 tls */
729 1.6.2.2 tls void
730 1.6.2.2 tls ulfsdirhash_newblk(struct inode *ip, doff_t offset)
731 1.6.2.2 tls {
732 1.6.2.2 tls struct dirhash *dh;
733 1.6.2.2 tls int block;
734 1.6.2.2 tls int dirblksiz = ip->i_ump->um_dirblksiz;
735 1.6.2.2 tls
736 1.6.2.2 tls if ((dh = ip->i_dirhash) == NULL)
737 1.6.2.2 tls return;
738 1.6.2.2 tls DIRHASH_LOCK(dh);
739 1.6.2.2 tls if (dh->dh_hash == NULL) {
740 1.6.2.2 tls DIRHASH_UNLOCK(dh);
741 1.6.2.2 tls ulfsdirhash_free(ip);
742 1.6.2.2 tls return;
743 1.6.2.2 tls }
744 1.6.2.2 tls
745 1.6.2.2 tls KASSERT(offset == dh->dh_dirblks * dirblksiz);
746 1.6.2.2 tls block = offset / dirblksiz;
747 1.6.2.2 tls if (block >= dh->dh_nblk) {
748 1.6.2.2 tls /* Out of space; must rebuild. */
749 1.6.2.2 tls DIRHASH_UNLOCK(dh);
750 1.6.2.2 tls ulfsdirhash_free(ip);
751 1.6.2.2 tls return;
752 1.6.2.2 tls }
753 1.6.2.2 tls dh->dh_dirblks = block + 1;
754 1.6.2.2 tls
755 1.6.2.2 tls /* Account for the new free block. */
756 1.6.2.2 tls dh->dh_blkfree[block] = dirblksiz / DIRALIGN;
757 1.6.2.2 tls if (dh->dh_firstfree[DH_NFSTATS] == -1)
758 1.6.2.2 tls dh->dh_firstfree[DH_NFSTATS] = block;
759 1.6.2.2 tls DIRHASH_UNLOCK(dh);
760 1.6.2.2 tls }
761 1.6.2.2 tls
762 1.6.2.2 tls /*
763 1.6.2.2 tls * Inform dirhash that the directory is being truncated.
764 1.6.2.2 tls */
765 1.6.2.2 tls void
766 1.6.2.2 tls ulfsdirhash_dirtrunc(struct inode *ip, doff_t offset)
767 1.6.2.2 tls {
768 1.6.2.2 tls struct dirhash *dh;
769 1.6.2.2 tls int block, i;
770 1.6.2.2 tls int dirblksiz = ip->i_ump->um_dirblksiz;
771 1.6.2.2 tls
772 1.6.2.2 tls if ((dh = ip->i_dirhash) == NULL)
773 1.6.2.2 tls return;
774 1.6.2.2 tls
775 1.6.2.2 tls DIRHASH_LOCK(dh);
776 1.6.2.2 tls if (dh->dh_hash == NULL) {
777 1.6.2.2 tls DIRHASH_UNLOCK(dh);
778 1.6.2.2 tls ulfsdirhash_free(ip);
779 1.6.2.2 tls return;
780 1.6.2.2 tls }
781 1.6.2.2 tls
782 1.6.2.2 tls KASSERT(offset <= dh->dh_dirblks * dirblksiz);
783 1.6.2.2 tls block = howmany(offset, dirblksiz);
784 1.6.2.2 tls /*
785 1.6.2.2 tls * If the directory shrinks to less than 1/8 of dh_nblk blocks
786 1.6.2.2 tls * (about 20% of its original size due to the 50% extra added in
787 1.6.2.2 tls * ulfsdirhash_build) then free it, and let the caller rebuild
788 1.6.2.2 tls * if necessary.
789 1.6.2.2 tls */
790 1.6.2.2 tls if (block < dh->dh_nblk / 8 && dh->dh_narrays > 1) {
791 1.6.2.2 tls DIRHASH_UNLOCK(dh);
792 1.6.2.2 tls ulfsdirhash_free(ip);
793 1.6.2.2 tls return;
794 1.6.2.2 tls }
795 1.6.2.2 tls
796 1.6.2.2 tls /*
797 1.6.2.2 tls * Remove any `first free' information pertaining to the
798 1.6.2.2 tls * truncated blocks. All blocks we're removing should be
799 1.6.2.2 tls * completely unused.
800 1.6.2.2 tls */
801 1.6.2.2 tls if (dh->dh_firstfree[DH_NFSTATS] >= block)
802 1.6.2.2 tls dh->dh_firstfree[DH_NFSTATS] = -1;
803 1.6.2.2 tls for (i = block; i < dh->dh_dirblks; i++)
804 1.6.2.2 tls if (dh->dh_blkfree[i] != dirblksiz / DIRALIGN)
805 1.6.2.2 tls panic("ulfsdirhash_dirtrunc: blocks in use");
806 1.6.2.2 tls for (i = 0; i < DH_NFSTATS; i++)
807 1.6.2.2 tls if (dh->dh_firstfree[i] >= block)
808 1.6.2.2 tls panic("ulfsdirhash_dirtrunc: first free corrupt");
809 1.6.2.2 tls dh->dh_dirblks = block;
810 1.6.2.2 tls DIRHASH_UNLOCK(dh);
811 1.6.2.2 tls }
812 1.6.2.2 tls
813 1.6.2.2 tls /*
814 1.6.2.2 tls * Debugging function to check that the dirhash information about
815 1.6.2.2 tls * a directory block matches its actual contents. Panics if a mismatch
816 1.6.2.2 tls * is detected.
817 1.6.2.2 tls *
818 1.6.2.2 tls * On entry, `sbuf' should point to the start of an in-core
819 1.6.2.2 tls * DIRBLKSIZ-sized directory block, and `offset' should contain the
820 1.6.2.2 tls * offset from the start of the directory of that block.
821 1.6.2.2 tls */
822 1.6.2.2 tls void
823 1.6.2.2 tls ulfsdirhash_checkblock(struct inode *ip, char *sbuf, doff_t offset)
824 1.6.2.2 tls {
825 1.6.2.2 tls struct dirhash *dh;
826 1.6.2.2 tls struct lfs_direct *dp;
827 1.6.2.2 tls int block, ffslot, i, nfree;
828 1.6.2.2 tls const int needswap = ULFS_MPNEEDSWAP(ip->i_ump);
829 1.6.2.2 tls int dirblksiz = ip->i_ump->um_dirblksiz;
830 1.6.2.2 tls
831 1.6.2.2 tls if (!ulfs_dirhashcheck)
832 1.6.2.2 tls return;
833 1.6.2.2 tls if ((dh = ip->i_dirhash) == NULL)
834 1.6.2.2 tls return;
835 1.6.2.2 tls
836 1.6.2.2 tls DIRHASH_LOCK(dh);
837 1.6.2.2 tls if (dh->dh_hash == NULL) {
838 1.6.2.2 tls DIRHASH_UNLOCK(dh);
839 1.6.2.2 tls ulfsdirhash_free(ip);
840 1.6.2.2 tls return;
841 1.6.2.2 tls }
842 1.6.2.2 tls
843 1.6.2.2 tls block = offset / dirblksiz;
844 1.6.2.2 tls if ((offset & (dirblksiz - 1)) != 0 || block >= dh->dh_dirblks)
845 1.6.2.2 tls panic("ulfsdirhash_checkblock: bad offset");
846 1.6.2.2 tls
847 1.6.2.2 tls nfree = 0;
848 1.6.2.2 tls for (i = 0; i < dirblksiz; i += dp->d_reclen) {
849 1.6.2.2 tls dp = (struct lfs_direct *)(sbuf + i);
850 1.6.2.2 tls if (dp->d_reclen == 0 || i + dp->d_reclen > dirblksiz)
851 1.6.2.2 tls panic("ulfsdirhash_checkblock: bad dir");
852 1.6.2.2 tls
853 1.6.2.2 tls if (dp->d_ino == 0) {
854 1.6.2.2 tls #if 0
855 1.6.2.2 tls /*
856 1.6.2.2 tls * XXX entries with d_ino == 0 should only occur
857 1.6.2.2 tls * at the start of a DIRBLKSIZ block. However the
858 1.6.2.2 tls * ulfs code is tolerant of such entries at other
859 1.6.2.2 tls * offsets, and fsck does not fix them.
860 1.6.2.2 tls */
861 1.6.2.2 tls if (i != 0)
862 1.6.2.2 tls panic("ulfsdirhash_checkblock: bad dir inode");
863 1.6.2.2 tls #endif
864 1.6.2.2 tls nfree += dp->d_reclen;
865 1.6.2.2 tls continue;
866 1.6.2.2 tls }
867 1.6.2.2 tls
868 1.6.2.2 tls /* Check that the entry exists (will panic if it doesn't). */
869 1.6.2.2 tls ulfsdirhash_findslot(dh, dp->d_name, dp->d_namlen, offset + i);
870 1.6.2.2 tls
871 1.6.2.2 tls nfree += dp->d_reclen - LFS_DIRSIZ(0, dp, needswap);
872 1.6.2.2 tls }
873 1.6.2.2 tls if (i != dirblksiz)
874 1.6.2.2 tls panic("ulfsdirhash_checkblock: bad dir end");
875 1.6.2.2 tls
876 1.6.2.2 tls if (dh->dh_blkfree[block] * DIRALIGN != nfree)
877 1.6.2.2 tls panic("ulfsdirhash_checkblock: bad free count");
878 1.6.2.2 tls
879 1.6.2.2 tls ffslot = BLKFREE2IDX(nfree / DIRALIGN);
880 1.6.2.2 tls for (i = 0; i <= DH_NFSTATS; i++)
881 1.6.2.2 tls if (dh->dh_firstfree[i] == block && i != ffslot)
882 1.6.2.2 tls panic("ulfsdirhash_checkblock: bad first-free");
883 1.6.2.2 tls if (dh->dh_firstfree[ffslot] == -1)
884 1.6.2.2 tls panic("ulfsdirhash_checkblock: missing first-free entry");
885 1.6.2.2 tls DIRHASH_UNLOCK(dh);
886 1.6.2.2 tls }
887 1.6.2.2 tls
888 1.6.2.2 tls /*
889 1.6.2.2 tls * Hash the specified filename into a dirhash slot.
890 1.6.2.2 tls */
891 1.6.2.2 tls static int
892 1.6.2.2 tls ulfsdirhash_hash(struct dirhash *dh, const char *name, int namelen)
893 1.6.2.2 tls {
894 1.6.2.2 tls u_int32_t hash;
895 1.6.2.2 tls
896 1.6.2.2 tls /*
897 1.6.2.2 tls * We hash the name and then some other bit of data that is
898 1.6.2.2 tls * invariant over the dirhash's lifetime. Otherwise names
899 1.6.2.2 tls * differing only in the last byte are placed close to one
900 1.6.2.2 tls * another in the table, which is bad for linear probing.
901 1.6.2.2 tls */
902 1.6.2.2 tls hash = hash32_buf(name, namelen, HASH32_BUF_INIT);
903 1.6.2.2 tls hash = hash32_buf(&dh, sizeof(dh), hash);
904 1.6.2.2 tls return (hash % dh->dh_hlen);
905 1.6.2.2 tls }
906 1.6.2.2 tls
907 1.6.2.2 tls /*
908 1.6.2.2 tls * Adjust the number of free bytes in the block containing `offset'
909 1.6.2.2 tls * by the value specified by `diff'.
910 1.6.2.2 tls *
911 1.6.2.2 tls * The caller must ensure we have exclusive access to `dh'; normally
912 1.6.2.2 tls * that means that dh_lock should be held, but this is also called
913 1.6.2.2 tls * from ulfsdirhash_build() where exclusive access can be assumed.
914 1.6.2.2 tls */
915 1.6.2.2 tls static void
916 1.6.2.2 tls ulfsdirhash_adjfree(struct dirhash *dh, doff_t offset, int diff, int dirblksiz)
917 1.6.2.2 tls {
918 1.6.2.2 tls int block, i, nfidx, ofidx;
919 1.6.2.2 tls
920 1.6.2.2 tls KASSERT(mutex_owned(&dh->dh_lock));
921 1.6.2.2 tls
922 1.6.2.2 tls /* Update the per-block summary info. */
923 1.6.2.2 tls block = offset / dirblksiz;
924 1.6.2.2 tls KASSERT(block < dh->dh_nblk && block < dh->dh_dirblks);
925 1.6.2.2 tls ofidx = BLKFREE2IDX(dh->dh_blkfree[block]);
926 1.6.2.2 tls dh->dh_blkfree[block] = (int)dh->dh_blkfree[block] + (diff / DIRALIGN);
927 1.6.2.2 tls nfidx = BLKFREE2IDX(dh->dh_blkfree[block]);
928 1.6.2.2 tls
929 1.6.2.2 tls /* Update the `first free' list if necessary. */
930 1.6.2.2 tls if (ofidx != nfidx) {
931 1.6.2.2 tls /* If removing, scan forward for the next block. */
932 1.6.2.2 tls if (dh->dh_firstfree[ofidx] == block) {
933 1.6.2.2 tls for (i = block + 1; i < dh->dh_dirblks; i++)
934 1.6.2.2 tls if (BLKFREE2IDX(dh->dh_blkfree[i]) == ofidx)
935 1.6.2.2 tls break;
936 1.6.2.2 tls dh->dh_firstfree[ofidx] = (i < dh->dh_dirblks) ? i : -1;
937 1.6.2.2 tls }
938 1.6.2.2 tls
939 1.6.2.2 tls /* Make this the new `first free' if necessary */
940 1.6.2.2 tls if (dh->dh_firstfree[nfidx] > block ||
941 1.6.2.2 tls dh->dh_firstfree[nfidx] == -1)
942 1.6.2.2 tls dh->dh_firstfree[nfidx] = block;
943 1.6.2.2 tls }
944 1.6.2.2 tls }
945 1.6.2.2 tls
946 1.6.2.2 tls /*
947 1.6.2.2 tls * Find the specified name which should have the specified offset.
948 1.6.2.2 tls * Returns a slot number, and panics on failure.
949 1.6.2.2 tls *
950 1.6.2.2 tls * `dh' must be locked on entry and remains so on return.
951 1.6.2.2 tls */
952 1.6.2.2 tls static int
953 1.6.2.2 tls ulfsdirhash_findslot(struct dirhash *dh, const char *name, int namelen,
954 1.6.2.2 tls doff_t offset)
955 1.6.2.2 tls {
956 1.6.2.2 tls int slot;
957 1.6.2.2 tls
958 1.6.2.2 tls KASSERT(mutex_owned(&dh->dh_lock));
959 1.6.2.2 tls
960 1.6.2.2 tls /* Find the entry. */
961 1.6.2.2 tls KASSERT(dh->dh_hused < dh->dh_hlen);
962 1.6.2.2 tls slot = ulfsdirhash_hash(dh, name, namelen);
963 1.6.2.2 tls while (DH_ENTRY(dh, slot) != offset &&
964 1.6.2.2 tls DH_ENTRY(dh, slot) != DIRHASH_EMPTY)
965 1.6.2.2 tls slot = WRAPINCR(slot, dh->dh_hlen);
966 1.6.2.2 tls if (DH_ENTRY(dh, slot) != offset)
967 1.6.2.2 tls panic("ulfsdirhash_findslot: '%.*s' not found", namelen, name);
968 1.6.2.2 tls
969 1.6.2.2 tls return (slot);
970 1.6.2.2 tls }
971 1.6.2.2 tls
972 1.6.2.2 tls /*
973 1.6.2.2 tls * Remove the entry corresponding to the specified slot from the hash array.
974 1.6.2.2 tls *
975 1.6.2.2 tls * `dh' must be locked on entry and remains so on return.
976 1.6.2.2 tls */
977 1.6.2.2 tls static void
978 1.6.2.2 tls ulfsdirhash_delslot(struct dirhash *dh, int slot)
979 1.6.2.2 tls {
980 1.6.2.2 tls int i;
981 1.6.2.2 tls
982 1.6.2.2 tls KASSERT(mutex_owned(&dh->dh_lock));
983 1.6.2.2 tls
984 1.6.2.2 tls /* Mark the entry as deleted. */
985 1.6.2.2 tls DH_ENTRY(dh, slot) = DIRHASH_DEL;
986 1.6.2.2 tls
987 1.6.2.2 tls /* If this is the end of a chain of DIRHASH_DEL slots, remove them. */
988 1.6.2.2 tls for (i = slot; DH_ENTRY(dh, i) == DIRHASH_DEL; )
989 1.6.2.2 tls i = WRAPINCR(i, dh->dh_hlen);
990 1.6.2.2 tls if (DH_ENTRY(dh, i) == DIRHASH_EMPTY) {
991 1.6.2.2 tls i = WRAPDECR(i, dh->dh_hlen);
992 1.6.2.2 tls while (DH_ENTRY(dh, i) == DIRHASH_DEL) {
993 1.6.2.2 tls DH_ENTRY(dh, i) = DIRHASH_EMPTY;
994 1.6.2.2 tls dh->dh_hused--;
995 1.6.2.2 tls i = WRAPDECR(i, dh->dh_hlen);
996 1.6.2.2 tls }
997 1.6.2.2 tls KASSERT(dh->dh_hused >= 0);
998 1.6.2.2 tls }
999 1.6.2.2 tls }
1000 1.6.2.2 tls
1001 1.6.2.2 tls /*
1002 1.6.2.2 tls * Given a directory entry and its offset, find the offset of the
1003 1.6.2.2 tls * previous entry in the same DIRBLKSIZ-sized block. Returns an
1004 1.6.2.2 tls * offset, or -1 if there is no previous entry in the block or some
1005 1.6.2.2 tls * other problem occurred.
1006 1.6.2.2 tls */
1007 1.6.2.2 tls static doff_t
1008 1.6.2.2 tls ulfsdirhash_getprev(struct lfs_direct *dirp, doff_t offset, int dirblksiz)
1009 1.6.2.2 tls {
1010 1.6.2.2 tls struct lfs_direct *dp;
1011 1.6.2.2 tls char *blkbuf;
1012 1.6.2.2 tls doff_t blkoff, prevoff;
1013 1.6.2.2 tls int entrypos, i;
1014 1.6.2.2 tls
1015 1.6.2.2 tls blkoff = offset & ~(dirblksiz - 1); /* offset of start of block */
1016 1.6.2.2 tls entrypos = offset & (dirblksiz - 1); /* entry relative to block */
1017 1.6.2.2 tls blkbuf = (char *)dirp - entrypos;
1018 1.6.2.2 tls prevoff = blkoff;
1019 1.6.2.2 tls
1020 1.6.2.2 tls /* If `offset' is the start of a block, there is no previous entry. */
1021 1.6.2.2 tls if (entrypos == 0)
1022 1.6.2.2 tls return (-1);
1023 1.6.2.2 tls
1024 1.6.2.2 tls /* Scan from the start of the block until we get to the entry. */
1025 1.6.2.2 tls for (i = 0; i < entrypos; i += dp->d_reclen) {
1026 1.6.2.2 tls dp = (struct lfs_direct *)(blkbuf + i);
1027 1.6.2.2 tls if (dp->d_reclen == 0 || i + dp->d_reclen > entrypos)
1028 1.6.2.2 tls return (-1); /* Corrupted directory. */
1029 1.6.2.2 tls prevoff = blkoff + i;
1030 1.6.2.2 tls }
1031 1.6.2.2 tls return (prevoff);
1032 1.6.2.2 tls }
1033 1.6.2.2 tls
1034 1.6.2.2 tls /*
1035 1.6.2.2 tls * Try to free up `wanted' bytes by stealing memory from existing
1036 1.6.2.2 tls * dirhashes. Returns zero with list locked if successful.
1037 1.6.2.2 tls */
1038 1.6.2.2 tls static int
1039 1.6.2.2 tls ulfsdirhash_recycle(int wanted)
1040 1.6.2.2 tls {
1041 1.6.2.2 tls struct dirhash *dh;
1042 1.6.2.2 tls doff_t **hash;
1043 1.6.2.2 tls u_int8_t *blkfree;
1044 1.6.2.2 tls int i, mem, narrays;
1045 1.6.2.2 tls size_t hashsz, blkfreesz;
1046 1.6.2.2 tls
1047 1.6.2.2 tls DIRHASHLIST_LOCK();
1048 1.6.2.2 tls while (wanted + ulfs_dirhashmem > ulfs_dirhashmaxmem) {
1049 1.6.2.2 tls /* Find a dirhash, and lock it. */
1050 1.6.2.2 tls if ((dh = TAILQ_FIRST(&ulfsdirhash_list)) == NULL) {
1051 1.6.2.2 tls DIRHASHLIST_UNLOCK();
1052 1.6.2.2 tls return (-1);
1053 1.6.2.2 tls }
1054 1.6.2.2 tls DIRHASH_LOCK(dh);
1055 1.6.2.2 tls KASSERT(dh->dh_hash != NULL);
1056 1.6.2.2 tls
1057 1.6.2.2 tls /* Decrement the score; only recycle if it becomes zero. */
1058 1.6.2.2 tls if (--dh->dh_score > 0) {
1059 1.6.2.2 tls DIRHASH_UNLOCK(dh);
1060 1.6.2.2 tls DIRHASHLIST_UNLOCK();
1061 1.6.2.2 tls return (-1);
1062 1.6.2.2 tls }
1063 1.6.2.2 tls
1064 1.6.2.2 tls /* Remove it from the list and detach its memory. */
1065 1.6.2.2 tls TAILQ_REMOVE(&ulfsdirhash_list, dh, dh_list);
1066 1.6.2.2 tls dh->dh_onlist = 0;
1067 1.6.2.2 tls hash = dh->dh_hash;
1068 1.6.2.2 tls hashsz = dh->dh_hashsz;
1069 1.6.2.2 tls dh->dh_hash = NULL;
1070 1.6.2.2 tls blkfree = dh->dh_blkfree;
1071 1.6.2.2 tls blkfreesz = dh->dh_blkfreesz;
1072 1.6.2.2 tls dh->dh_blkfree = NULL;
1073 1.6.2.2 tls narrays = dh->dh_narrays;
1074 1.6.2.2 tls mem = narrays * sizeof(*dh->dh_hash) +
1075 1.6.2.2 tls narrays * DH_NBLKOFF * sizeof(**dh->dh_hash) +
1076 1.6.2.2 tls dh->dh_nblk * sizeof(*dh->dh_blkfree);
1077 1.6.2.2 tls
1078 1.6.2.2 tls /* Unlock everything, free the detached memory. */
1079 1.6.2.2 tls DIRHASH_UNLOCK(dh);
1080 1.6.2.2 tls DIRHASHLIST_UNLOCK();
1081 1.6.2.2 tls
1082 1.6.2.2 tls for (i = 0; i < narrays; i++)
1083 1.6.2.2 tls DIRHASH_BLKFREE(hash[i]);
1084 1.6.2.2 tls kmem_free(hash, hashsz);
1085 1.6.2.2 tls kmem_free(blkfree, blkfreesz);
1086 1.6.2.2 tls
1087 1.6.2.2 tls /* Account for the returned memory, and repeat if necessary. */
1088 1.6.2.2 tls DIRHASHLIST_LOCK();
1089 1.6.2.2 tls atomic_add_int(&ulfs_dirhashmem, -mem);
1090 1.6.2.2 tls }
1091 1.6.2.2 tls /* Success. */
1092 1.6.2.2 tls return (0);
1093 1.6.2.2 tls }
1094 1.6.2.2 tls
1095 1.6.2.2 tls static void
1096 1.6.2.2 tls ulfsdirhash_sysctl_init(void)
1097 1.6.2.2 tls {
1098 1.6.2.2 tls const struct sysctlnode *rnode, *cnode;
1099 1.6.2.2 tls
1100 1.6.2.2 tls sysctl_createv(&ulfsdirhash_sysctl_log, 0, NULL, &rnode,
1101 1.6.2.2 tls CTLFLAG_PERMANENT,
1102 1.6.2.2 tls CTLTYPE_NODE, "vfs", NULL,
1103 1.6.2.2 tls NULL, 0, NULL, 0,
1104 1.6.2.2 tls CTL_VFS, CTL_EOL);
1105 1.6.2.2 tls
1106 1.6.2.2 tls sysctl_createv(&ulfsdirhash_sysctl_log, 0, &rnode, &rnode,
1107 1.6.2.2 tls CTLFLAG_PERMANENT,
1108 1.6.2.2 tls CTLTYPE_NODE, "ulfs",
1109 1.6.2.2 tls SYSCTL_DESCR("ulfs"),
1110 1.6.2.2 tls NULL, 0, NULL, 0,
1111 1.6.2.2 tls CTL_CREATE, CTL_EOL);
1112 1.6.2.2 tls
1113 1.6.2.2 tls sysctl_createv(&ulfsdirhash_sysctl_log, 0, &rnode, &rnode,
1114 1.6.2.2 tls CTLFLAG_PERMANENT,
1115 1.6.2.2 tls CTLTYPE_NODE, "dirhash",
1116 1.6.2.2 tls SYSCTL_DESCR("dirhash"),
1117 1.6.2.2 tls NULL, 0, NULL, 0,
1118 1.6.2.2 tls CTL_CREATE, CTL_EOL);
1119 1.6.2.2 tls
1120 1.6.2.2 tls sysctl_createv(&ulfsdirhash_sysctl_log, 0, &rnode, &cnode,
1121 1.6.2.2 tls CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
1122 1.6.2.2 tls CTLTYPE_INT, "minblocks",
1123 1.6.2.2 tls SYSCTL_DESCR("minimum hashed directory size in blocks"),
1124 1.6.2.2 tls NULL, 0, &ulfs_dirhashminblks, 0,
1125 1.6.2.2 tls CTL_CREATE, CTL_EOL);
1126 1.6.2.2 tls
1127 1.6.2.2 tls sysctl_createv(&ulfsdirhash_sysctl_log, 0, &rnode, &cnode,
1128 1.6.2.2 tls CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
1129 1.6.2.2 tls CTLTYPE_INT, "maxmem",
1130 1.6.2.2 tls SYSCTL_DESCR("maximum dirhash memory usage"),
1131 1.6.2.2 tls NULL, 0, &ulfs_dirhashmaxmem, 0,
1132 1.6.2.2 tls CTL_CREATE, CTL_EOL);
1133 1.6.2.2 tls
1134 1.6.2.2 tls sysctl_createv(&ulfsdirhash_sysctl_log, 0, &rnode, &cnode,
1135 1.6.2.2 tls CTLFLAG_PERMANENT|CTLFLAG_READONLY,
1136 1.6.2.2 tls CTLTYPE_INT, "memused",
1137 1.6.2.2 tls SYSCTL_DESCR("current dirhash memory usage"),
1138 1.6.2.2 tls NULL, 0, &ulfs_dirhashmem, 0,
1139 1.6.2.2 tls CTL_CREATE, CTL_EOL);
1140 1.6.2.2 tls
1141 1.6.2.2 tls sysctl_createv(&ulfsdirhash_sysctl_log, 0, &rnode, &cnode,
1142 1.6.2.2 tls CTLFLAG_PERMANENT|CTLFLAG_READWRITE,
1143 1.6.2.2 tls CTLTYPE_INT, "docheck",
1144 1.6.2.2 tls SYSCTL_DESCR("enable extra sanity checks"),
1145 1.6.2.2 tls NULL, 0, &ulfs_dirhashcheck, 0,
1146 1.6.2.2 tls CTL_CREATE, CTL_EOL);
1147 1.6.2.2 tls }
1148 1.6.2.2 tls
1149 1.6.2.2 tls void
1150 1.6.2.2 tls ulfsdirhash_init(void)
1151 1.6.2.2 tls {
1152 1.6.2.2 tls
1153 1.6.2.2 tls mutex_init(&ulfsdirhash_lock, MUTEX_DEFAULT, IPL_NONE);
1154 1.6.2.2 tls ulfsdirhashblk_cache = pool_cache_init(DH_NBLKOFF * sizeof(daddr_t), 0,
1155 1.6.2.2 tls 0, 0, "dirhashblk", NULL, IPL_NONE, NULL, NULL, NULL);
1156 1.6.2.2 tls ulfsdirhash_cache = pool_cache_init(sizeof(struct dirhash), 0,
1157 1.6.2.2 tls 0, 0, "dirhash", NULL, IPL_NONE, NULL, NULL, NULL);
1158 1.6.2.2 tls TAILQ_INIT(&ulfsdirhash_list);
1159 1.6.2.2 tls ulfsdirhash_sysctl_init();
1160 1.6.2.2 tls }
1161 1.6.2.2 tls
1162 1.6.2.2 tls void
1163 1.6.2.2 tls ulfsdirhash_done(void)
1164 1.6.2.2 tls {
1165 1.6.2.2 tls
1166 1.6.2.2 tls KASSERT(TAILQ_EMPTY(&ulfsdirhash_list));
1167 1.6.2.2 tls pool_cache_destroy(ulfsdirhashblk_cache);
1168 1.6.2.2 tls pool_cache_destroy(ulfsdirhash_cache);
1169 1.6.2.2 tls mutex_destroy(&ulfsdirhash_lock);
1170 1.6.2.2 tls sysctl_teardown(&ulfsdirhash_sysctl_log);
1171 1.6.2.2 tls }
1172