xref: /dflybsd-src/sys/vfs/hammer2/hammer2_subr.c (revision ca86d83e7d8d6bfef814ef3683c37d99ad62f11c)
1 /*
2  * Copyright (c) 2011-2013 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@dragonflybsd.org>
6  * by Venkatesh Srinivas <vsrinivas@dragonflybsd.org>
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  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in
16  *    the documentation and/or other materials provided with the
17  *    distribution.
18  * 3. Neither the name of The DragonFly Project nor the names of its
19  *    contributors may be used to endorse or promote products derived
20  *    from this software without specific, prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
26  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
28  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
30  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 #include <sys/cdefs.h>
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/types.h>
39 #include <sys/lock.h>
40 #include <sys/uuid.h>
41 #include <sys/dirent.h>
42 
43 #include "hammer2.h"
44 
45 /*
46  * HAMMER2 inode locks
47  *
48  * HAMMER2 offers shared locks and exclusive locks on inodes.
49  *
50  * An inode's ip->chain pointer is resolved and stable while an inode is
51  * locked, and can be cleaned out at any time (become NULL) when an inode
52  * is not locked.
53  *
54  * The underlying chain is also locked and returned.
55  *
56  * NOTE: We don't combine the inode/chain lock because putting away an
57  *       inode would otherwise confuse multiple lock holders of the inode.
58  *
59  * WARNING! hammer2_inode_repoint() expects exactly one exclusive lock
60  *	    on ip->chain.
61  */
62 void
63 hammer2_inode_lock_ex(hammer2_inode_t *ip)
64 {
65 	hammer2_chain_t *chain;
66 
67 	hammer2_inode_ref(ip);
68 	ccms_thread_lock(&ip->topo_cst, CCMS_STATE_EXCLUSIVE);
69 
70 	chain = ip->chain;
71 	KKASSERT(chain != NULL);	/* for now */
72 	hammer2_chain_lock(chain, HAMMER2_RESOLVE_ALWAYS);
73 }
74 
75 void
76 hammer2_inode_unlock_ex(hammer2_inode_t *ip)
77 {
78 	hammer2_chain_t *chain;
79 
80 	/*
81 	 * XXX this will catch parent directories too which we don't
82 	 *     really want.
83 	 */
84 	chain = ip->chain;
85 	if (chain) {
86 		if (chain->flags & (HAMMER2_CHAIN_MODIFIED |
87 				    HAMMER2_CHAIN_SUBMODIFIED)) {
88 			atomic_set_int(&ip->flags, HAMMER2_INODE_MODIFIED);
89 		}
90 		hammer2_chain_unlock(chain);
91 	}
92 	ccms_thread_unlock(&ip->topo_cst);
93 	hammer2_inode_drop(ip);
94 }
95 
96 /*
97  * NOTE: We don't combine the inode/chain lock because putting away an
98  *       inode would otherwise confuse multiple lock holders of the inode.
99  *
100  *	 Shared locks are especially sensitive to having too many shared
101  *	 lock counts (from the same thread) on certain paths which might
102  *	 need to upgrade them.  Only one count of a shared lock can be
103  *	 upgraded.
104  */
105 void
106 hammer2_inode_lock_sh(hammer2_inode_t *ip)
107 {
108 	hammer2_chain_t *chain;
109 
110 	hammer2_inode_ref(ip);
111 	ccms_thread_lock(&ip->topo_cst, CCMS_STATE_SHARED);
112 
113 	chain = ip->chain;
114 	KKASSERT(chain != NULL);	/* for now */
115 	hammer2_chain_lock(chain, HAMMER2_RESOLVE_ALWAYS |
116 				  HAMMER2_RESOLVE_SHARED);
117 
118 }
119 
120 void
121 hammer2_inode_unlock_sh(hammer2_inode_t *ip)
122 {
123 	if (ip->chain)
124 		hammer2_chain_unlock(ip->chain);
125 	ccms_thread_unlock(&ip->topo_cst);
126 	hammer2_inode_drop(ip);
127 }
128 
129 ccms_state_t
130 hammer2_inode_lock_temp_release(hammer2_inode_t *ip)
131 {
132 	return(ccms_thread_lock_temp_release(&ip->topo_cst));
133 }
134 
135 ccms_state_t
136 hammer2_inode_lock_upgrade(hammer2_inode_t *ip)
137 {
138 	return(ccms_thread_lock_upgrade(&ip->topo_cst));
139 }
140 
141 void
142 hammer2_inode_lock_restore(hammer2_inode_t *ip, ccms_state_t ostate)
143 {
144 	ccms_thread_lock_restore(&ip->topo_cst, ostate);
145 }
146 
147 /*
148  * Mount-wide locks
149  */
150 
151 void
152 hammer2_mount_exlock(hammer2_mount_t *hmp)
153 {
154 	ccms_thread_lock(&hmp->vchain.core->cst, CCMS_STATE_EXCLUSIVE);
155 }
156 
157 void
158 hammer2_mount_shlock(hammer2_mount_t *hmp)
159 {
160 	ccms_thread_lock(&hmp->vchain.core->cst, CCMS_STATE_SHARED);
161 }
162 
163 void
164 hammer2_mount_unlock(hammer2_mount_t *hmp)
165 {
166 	ccms_thread_unlock(&hmp->vchain.core->cst);
167 }
168 
169 void
170 hammer2_voldata_lock(hammer2_mount_t *hmp)
171 {
172 	lockmgr(&hmp->voldatalk, LK_EXCLUSIVE);
173 }
174 
175 void
176 hammer2_voldata_unlock(hammer2_mount_t *hmp, int modify)
177 {
178 	if (modify &&
179 	    (hmp->vchain.flags & HAMMER2_CHAIN_MODIFIED) == 0) {
180 		atomic_set_int(&hmp->vchain.flags, HAMMER2_CHAIN_MODIFIED);
181 		hammer2_chain_ref(&hmp->vchain);
182 	}
183 	lockmgr(&hmp->voldatalk, LK_RELEASE);
184 }
185 
186 /*
187  * Return the directory entry type for an inode.
188  *
189  * ip must be locked sh/ex.
190  */
191 int
192 hammer2_get_dtype(hammer2_chain_t *chain)
193 {
194 	uint8_t type;
195 
196 	KKASSERT(chain->bref.type == HAMMER2_BREF_TYPE_INODE);
197 
198 	if ((type = chain->data->ipdata.type) == HAMMER2_OBJTYPE_HARDLINK)
199 		type = chain->data->ipdata.target_type;
200 
201 	switch(type) {
202 	case HAMMER2_OBJTYPE_UNKNOWN:
203 		return (DT_UNKNOWN);
204 	case HAMMER2_OBJTYPE_DIRECTORY:
205 		return (DT_DIR);
206 	case HAMMER2_OBJTYPE_REGFILE:
207 		return (DT_REG);
208 	case HAMMER2_OBJTYPE_FIFO:
209 		return (DT_FIFO);
210 	case HAMMER2_OBJTYPE_CDEV:	/* not supported */
211 		return (DT_CHR);
212 	case HAMMER2_OBJTYPE_BDEV:	/* not supported */
213 		return (DT_BLK);
214 	case HAMMER2_OBJTYPE_SOFTLINK:
215 		return (DT_LNK);
216 	case HAMMER2_OBJTYPE_HARDLINK:	/* (never directly associated w/vp) */
217 		return (DT_UNKNOWN);
218 	case HAMMER2_OBJTYPE_SOCKET:
219 		return (DT_SOCK);
220 	case HAMMER2_OBJTYPE_WHITEOUT:	/* not supported */
221 		return (DT_UNKNOWN);
222 	default:
223 		return (DT_UNKNOWN);
224 	}
225 	/* not reached */
226 }
227 
228 /*
229  * Return the directory entry type for an inode
230  */
231 int
232 hammer2_get_vtype(hammer2_chain_t *chain)
233 {
234 	KKASSERT(chain->bref.type == HAMMER2_BREF_TYPE_INODE);
235 
236 	switch(chain->data->ipdata.type) {
237 	case HAMMER2_OBJTYPE_UNKNOWN:
238 		return (VBAD);
239 	case HAMMER2_OBJTYPE_DIRECTORY:
240 		return (VDIR);
241 	case HAMMER2_OBJTYPE_REGFILE:
242 		return (VREG);
243 	case HAMMER2_OBJTYPE_FIFO:
244 		return (VFIFO);
245 	case HAMMER2_OBJTYPE_CDEV:	/* not supported */
246 		return (VCHR);
247 	case HAMMER2_OBJTYPE_BDEV:	/* not supported */
248 		return (VBLK);
249 	case HAMMER2_OBJTYPE_SOFTLINK:
250 		return (VLNK);
251 	case HAMMER2_OBJTYPE_HARDLINK:	/* XXX */
252 		return (VBAD);
253 	case HAMMER2_OBJTYPE_SOCKET:
254 		return (VSOCK);
255 	case HAMMER2_OBJTYPE_WHITEOUT:	/* not supported */
256 		return (DT_UNKNOWN);
257 	default:
258 		return (DT_UNKNOWN);
259 	}
260 	/* not reached */
261 }
262 
263 u_int8_t
264 hammer2_get_obj_type(enum vtype vtype)
265 {
266 	switch(vtype) {
267 	case VDIR:
268 		return(HAMMER2_OBJTYPE_DIRECTORY);
269 	case VREG:
270 		return(HAMMER2_OBJTYPE_REGFILE);
271 	case VFIFO:
272 		return(HAMMER2_OBJTYPE_FIFO);
273 	case VSOCK:
274 		return(HAMMER2_OBJTYPE_SOCKET);
275 	case VCHR:
276 		return(HAMMER2_OBJTYPE_CDEV);
277 	case VBLK:
278 		return(HAMMER2_OBJTYPE_BDEV);
279 	case VLNK:
280 		return(HAMMER2_OBJTYPE_SOFTLINK);
281 	default:
282 		return(HAMMER2_OBJTYPE_UNKNOWN);
283 	}
284 	/* not reached */
285 }
286 
287 /*
288  * Convert a hammer2 64-bit time to a timespec.
289  */
290 void
291 hammer2_time_to_timespec(u_int64_t xtime, struct timespec *ts)
292 {
293 	ts->tv_sec = (unsigned long)(xtime / 1000000);
294 	ts->tv_nsec = (unsigned int)(xtime % 1000000) * 1000L;
295 }
296 
297 u_int64_t
298 hammer2_timespec_to_time(struct timespec *ts)
299 {
300 	u_int64_t xtime;
301 
302 	xtime = (unsigned)(ts->tv_nsec / 1000) +
303 		(unsigned long)ts->tv_sec * 1000000ULL;
304 	return(xtime);
305 }
306 
307 /*
308  * Convert a uuid to a unix uid or gid
309  */
310 u_int32_t
311 hammer2_to_unix_xid(uuid_t *uuid)
312 {
313 	return(*(u_int32_t *)&uuid->node[2]);
314 }
315 
316 void
317 hammer2_guid_to_uuid(uuid_t *uuid, u_int32_t guid)
318 {
319 	bzero(uuid, sizeof(*uuid));
320 	*(u_int32_t *)&uuid->node[2] = guid;
321 }
322 
323 /*
324  * Borrow HAMMER1's directory hash algorithm #1 with a few modifications.
325  * The filename is split into fields which are hashed separately and then
326  * added together.
327  *
328  * Differences include: bit 63 must be set to 1 for HAMMER2 (HAMMER1 sets
329  * it to 0), this is because bit63=0 is used for hidden hardlinked inodes.
330  * (This means we do not need to do a 0-check/or-with-0x100000000 either).
331  *
332  * Also, the iscsi crc code is used instead of the old crc32 code.
333  */
334 hammer2_key_t
335 hammer2_dirhash(const unsigned char *name, size_t len)
336 {
337 	const unsigned char *aname = name;
338 	uint32_t crcx;
339 	uint64_t key;
340 	size_t i;
341 	size_t j;
342 
343 	key = 0;
344 
345 	/*
346 	 * m32
347 	 */
348 	crcx = 0;
349 	for (i = j = 0; i < len; ++i) {
350 		if (aname[i] == '.' ||
351 		    aname[i] == '-' ||
352 		    aname[i] == '_' ||
353 		    aname[i] == '~') {
354 			if (i != j)
355 				crcx += hammer2_icrc32(aname + j, i - j);
356 			j = i + 1;
357 		}
358 	}
359 	if (i != j)
360 		crcx += hammer2_icrc32(aname + j, i - j);
361 
362 	/*
363 	 * The directory hash utilizes the top 32 bits of the 64-bit key.
364 	 * Bit 63 must be set to 1.
365 	 */
366 	crcx |= 0x80000000U;
367 	key |= (uint64_t)crcx << 32;
368 
369 	/*
370 	 * l16 - crc of entire filename
371 	 *
372 	 * This crc reduces degenerate hash collision conditions
373 	 */
374 	crcx = hammer2_icrc32(aname, len);
375 	crcx = crcx ^ (crcx << 16);
376 	key |= crcx & 0xFFFF0000U;
377 
378 	/*
379 	 * Set bit 15.  This allows readdir to strip bit 63 so a positive
380 	 * 64-bit cookie/offset can always be returned, and still guarantee
381 	 * that the values 0x0000-0x7FFF are available for artificial entries.
382 	 * ('.' and '..').
383 	 */
384 	key |= 0x8000U;
385 
386 	return (key);
387 }
388 
389 /*
390  * Return the power-of-2 radix greater or equal to
391  * the specified number of bytes.
392  *
393  * Always returns at least the minimum media allocation
394  * size radix, HAMMER2_MIN_RADIX (10), which is 1KB.
395  */
396 int
397 hammer2_allocsize(size_t bytes)
398 {
399 	int radix;
400 
401 	if (bytes < HAMMER2_MIN_ALLOC)
402 		bytes = HAMMER2_MIN_ALLOC;
403 	if (bytes == HAMMER2_PBUFSIZE)
404 		radix = HAMMER2_PBUFRADIX;
405 	else if (bytes >= 16384)
406 		radix = 14;
407 	else if (bytes >= 1024)
408 		radix = 10;
409 	else
410 		radix = HAMMER2_MIN_RADIX;
411 
412 	while (((size_t)1 << radix) < bytes)
413 		++radix;
414 	return (radix);
415 }
416 
417 /*
418  * ip must be locked sh/ex
419  */
420 int
421 hammer2_calc_logical(hammer2_inode_t *ip, hammer2_off_t uoff,
422 		     hammer2_key_t *lbasep, hammer2_key_t *leofp)
423 {
424 	hammer2_inode_data_t *ipdata = &ip->chain->data->ipdata;
425 	int radix;
426 
427 	*lbasep = uoff & ~HAMMER2_PBUFMASK64;
428 	*leofp = ipdata->size & ~HAMMER2_PBUFMASK64;
429 	KKASSERT(*lbasep <= *leofp);
430 	if (*lbasep == *leofp /*&& *leofp < 1024 * 1024*/) {
431 		radix = hammer2_allocsize((size_t)(ipdata->size - *leofp));
432 		if (radix < HAMMER2_MINALLOCRADIX)
433 			radix = HAMMER2_MINALLOCRADIX;
434 		*leofp += 1U << radix;
435 		return (1U << radix);
436 	} else {
437 		return (HAMMER2_PBUFSIZE);
438 	}
439 }
440 
441 void
442 hammer2_update_time(uint64_t *timep)
443 {
444 	struct timeval tv;
445 
446 	getmicrotime(&tv);
447 	*timep = (unsigned long)tv.tv_sec * 1000000 + tv.tv_usec;
448 }
449