xref: /dflybsd-src/sys/vfs/hammer2/hammer2_subr.c (revision 10cf3bfcde2ee9c50d77a153397b93d8026b03e1)
1 /*
2  * Copyright (c) 2011-2014 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  * Mount-wide locks
47  */
48 void
49 hammer2_dev_exlock(hammer2_dev_t *hmp)
50 {
51 	hammer2_mtx_ex(&hmp->vchain.core.lock);
52 }
53 
54 void
55 hammer2_dev_shlock(hammer2_dev_t *hmp)
56 {
57 	hammer2_mtx_sh(&hmp->vchain.core.lock);
58 }
59 
60 void
61 hammer2_dev_unlock(hammer2_dev_t *hmp)
62 {
63 	hammer2_mtx_unlock(&hmp->vchain.core.lock);
64 }
65 
66 /*
67  * Return the directory entry type for an inode.
68  *
69  * ip must be locked sh/ex.
70  */
71 int
72 hammer2_get_dtype(const hammer2_inode_data_t *ipdata)
73 {
74 	uint8_t type;
75 
76 	if ((type = ipdata->type) == HAMMER2_OBJTYPE_HARDLINK)
77 		type = ipdata->target_type;
78 
79 	switch(type) {
80 	case HAMMER2_OBJTYPE_UNKNOWN:
81 		return (DT_UNKNOWN);
82 	case HAMMER2_OBJTYPE_DIRECTORY:
83 		return (DT_DIR);
84 	case HAMMER2_OBJTYPE_REGFILE:
85 		return (DT_REG);
86 	case HAMMER2_OBJTYPE_FIFO:
87 		return (DT_FIFO);
88 	case HAMMER2_OBJTYPE_CDEV:	/* not supported */
89 		return (DT_CHR);
90 	case HAMMER2_OBJTYPE_BDEV:	/* not supported */
91 		return (DT_BLK);
92 	case HAMMER2_OBJTYPE_SOFTLINK:
93 		return (DT_LNK);
94 	case HAMMER2_OBJTYPE_HARDLINK:	/* (never directly associated w/vp) */
95 		return (DT_UNKNOWN);
96 	case HAMMER2_OBJTYPE_SOCKET:
97 		return (DT_SOCK);
98 	case HAMMER2_OBJTYPE_WHITEOUT:	/* not supported */
99 		return (DT_UNKNOWN);
100 	default:
101 		return (DT_UNKNOWN);
102 	}
103 	/* not reached */
104 }
105 
106 /*
107  * Return the directory entry type for an inode
108  */
109 int
110 hammer2_get_vtype(const hammer2_inode_data_t *ipdata)
111 {
112 	switch(ipdata->type) {
113 	case HAMMER2_OBJTYPE_UNKNOWN:
114 		return (VBAD);
115 	case HAMMER2_OBJTYPE_DIRECTORY:
116 		return (VDIR);
117 	case HAMMER2_OBJTYPE_REGFILE:
118 		return (VREG);
119 	case HAMMER2_OBJTYPE_FIFO:
120 		return (VFIFO);
121 	case HAMMER2_OBJTYPE_CDEV:	/* not supported */
122 		return (VCHR);
123 	case HAMMER2_OBJTYPE_BDEV:	/* not supported */
124 		return (VBLK);
125 	case HAMMER2_OBJTYPE_SOFTLINK:
126 		return (VLNK);
127 	case HAMMER2_OBJTYPE_HARDLINK:	/* XXX */
128 		return (VBAD);
129 	case HAMMER2_OBJTYPE_SOCKET:
130 		return (VSOCK);
131 	case HAMMER2_OBJTYPE_WHITEOUT:	/* not supported */
132 		return (DT_UNKNOWN);
133 	default:
134 		return (DT_UNKNOWN);
135 	}
136 	/* not reached */
137 }
138 
139 u_int8_t
140 hammer2_get_obj_type(enum vtype vtype)
141 {
142 	switch(vtype) {
143 	case VDIR:
144 		return(HAMMER2_OBJTYPE_DIRECTORY);
145 	case VREG:
146 		return(HAMMER2_OBJTYPE_REGFILE);
147 	case VFIFO:
148 		return(HAMMER2_OBJTYPE_FIFO);
149 	case VSOCK:
150 		return(HAMMER2_OBJTYPE_SOCKET);
151 	case VCHR:
152 		return(HAMMER2_OBJTYPE_CDEV);
153 	case VBLK:
154 		return(HAMMER2_OBJTYPE_BDEV);
155 	case VLNK:
156 		return(HAMMER2_OBJTYPE_SOFTLINK);
157 	default:
158 		return(HAMMER2_OBJTYPE_UNKNOWN);
159 	}
160 	/* not reached */
161 }
162 
163 /*
164  * Convert a hammer2 64-bit time to a timespec.
165  */
166 void
167 hammer2_time_to_timespec(u_int64_t xtime, struct timespec *ts)
168 {
169 	ts->tv_sec = (unsigned long)(xtime / 1000000);
170 	ts->tv_nsec = (unsigned int)(xtime % 1000000) * 1000L;
171 }
172 
173 u_int64_t
174 hammer2_timespec_to_time(const struct timespec *ts)
175 {
176 	u_int64_t xtime;
177 
178 	xtime = (unsigned)(ts->tv_nsec / 1000) +
179 		(unsigned long)ts->tv_sec * 1000000ULL;
180 	return(xtime);
181 }
182 
183 /*
184  * Convert a uuid to a unix uid or gid
185  */
186 u_int32_t
187 hammer2_to_unix_xid(const uuid_t *uuid)
188 {
189 	return(*(const u_int32_t *)&uuid->node[2]);
190 }
191 
192 void
193 hammer2_guid_to_uuid(uuid_t *uuid, u_int32_t guid)
194 {
195 	bzero(uuid, sizeof(*uuid));
196 	*(u_int32_t *)&uuid->node[2] = guid;
197 }
198 
199 /*
200  * Borrow HAMMER1's directory hash algorithm #1 with a few modifications.
201  * The filename is split into fields which are hashed separately and then
202  * added together.
203  *
204  * Differences include: bit 63 must be set to 1 for HAMMER2 (HAMMER1 sets
205  * it to 0), this is because bit63=0 is used for hidden hardlinked inodes.
206  * (This means we do not need to do a 0-check/or-with-0x100000000 either).
207  *
208  * Also, the iscsi crc code is used instead of the old crc32 code.
209  */
210 hammer2_key_t
211 hammer2_dirhash(const unsigned char *name, size_t len)
212 {
213 	const unsigned char *aname = name;
214 	uint32_t crcx;
215 	uint64_t key;
216 	size_t i;
217 	size_t j;
218 
219 	key = 0;
220 
221 	/*
222 	 * m32
223 	 */
224 	crcx = 0;
225 	for (i = j = 0; i < len; ++i) {
226 		if (aname[i] == '.' ||
227 		    aname[i] == '-' ||
228 		    aname[i] == '_' ||
229 		    aname[i] == '~') {
230 			if (i != j)
231 				crcx += hammer2_icrc32(aname + j, i - j);
232 			j = i + 1;
233 		}
234 	}
235 	if (i != j)
236 		crcx += hammer2_icrc32(aname + j, i - j);
237 
238 	/*
239 	 * The directory hash utilizes the top 32 bits of the 64-bit key.
240 	 * Bit 63 must be set to 1.
241 	 */
242 	crcx |= 0x80000000U;
243 	key |= (uint64_t)crcx << 32;
244 
245 	/*
246 	 * l16 - crc of entire filename
247 	 *
248 	 * This crc reduces degenerate hash collision conditions
249 	 */
250 	crcx = hammer2_icrc32(aname, len);
251 	crcx = crcx ^ (crcx << 16);
252 	key |= crcx & 0xFFFF0000U;
253 
254 	/*
255 	 * Set bit 15.  This allows readdir to strip bit 63 so a positive
256 	 * 64-bit cookie/offset can always be returned, and still guarantee
257 	 * that the values 0x0000-0x7FFF are available for artificial entries.
258 	 * ('.' and '..').
259 	 */
260 	key |= 0x8000U;
261 
262 	return (key);
263 }
264 
265 #if 0
266 /*
267  * Return the power-of-2 radix greater or equal to
268  * the specified number of bytes.
269  *
270  * Always returns at least the minimum media allocation
271  * size radix, HAMMER2_RADIX_MIN (10), which is 1KB.
272  */
273 int
274 hammer2_allocsize(size_t bytes)
275 {
276 	int radix;
277 
278 	if (bytes < HAMMER2_ALLOC_MIN)
279 		bytes = HAMMER2_ALLOC_MIN;
280 	if (bytes == HAMMER2_PBUFSIZE)
281 		radix = HAMMER2_PBUFRADIX;
282 	else if (bytes >= 16384)
283 		radix = 14;
284 	else if (bytes >= 1024)
285 		radix = 10;
286 	else
287 		radix = HAMMER2_RADIX_MIN;
288 
289 	while (((size_t)1 << radix) < bytes)
290 		++radix;
291 	return (radix);
292 }
293 
294 #endif
295 
296 /*
297  * Convert bytes to radix with no limitations
298  */
299 int
300 hammer2_getradix(size_t bytes)
301 {
302 	int radix;
303 
304 	if (bytes == HAMMER2_PBUFSIZE)
305 		radix = HAMMER2_PBUFRADIX;
306 	else if (bytes >= HAMMER2_LBUFSIZE)
307 		radix = HAMMER2_LBUFRADIX;
308 	else if (bytes >= HAMMER2_ALLOC_MIN)	/* clamp */
309 		radix = HAMMER2_RADIX_MIN;
310 	else
311 		radix = 0;
312 
313 	while (((size_t)1 << radix) < bytes)
314 		++radix;
315 	return (radix);
316 }
317 
318 /*
319  * ip must be locked sh/ex
320  *
321  * Use 16KB logical buffers for file blocks <= 1MB and 64KB logical buffers
322  * otherwise.  The write code may utilize smaller device buffers when
323  * compressing or handling the EOF case, but is not able to coalesce smaller
324  * logical buffers into larger device buffers.
325  *
326  * For now this means that even large files will have a bunch of 16KB blocks
327  * at the beginning of the file.  On the plus side this tends to cause small
328  * files to cluster together in the freemap.
329  */
330 int
331 hammer2_calc_logical(hammer2_inode_t *ip, hammer2_off_t uoff,
332 		     hammer2_key_t *lbasep, hammer2_key_t *leofp)
333 {
334 #if 0
335 	if (uoff < (hammer2_off_t)1024 * 1024) {
336 		if (lbasep)
337 			*lbasep = uoff & ~HAMMER2_LBUFMASK64;
338 		if (leofp) {
339 			if (ip->size > (hammer2_key_t)1024 * 1024)
340 				*leofp = (hammer2_key_t)1024 * 1024;
341 			else
342 				*leofp = (ip->size + HAMMER2_LBUFMASK64) &
343 					 ~HAMMER2_LBUFMASK64;
344 		}
345 		return (HAMMER2_LBUFSIZE);
346 	} else {
347 #endif
348 		if (lbasep)
349 			*lbasep = uoff & ~HAMMER2_PBUFMASK64;
350 		if (leofp) {
351 			*leofp = (ip->size + HAMMER2_PBUFMASK64) &
352 				 ~HAMMER2_PBUFMASK64;
353 		}
354 		return (HAMMER2_PBUFSIZE);
355 #if 0
356 	}
357 #endif
358 }
359 
360 /*
361  * Calculate the physical block size.  pblksize <= lblksize.  Primarily
362  * used to calculate a smaller physical block for the logical block
363  * containing the file EOF.
364  *
365  * Returns 0 if the requested base offset is beyond the file EOF.
366  */
367 int
368 hammer2_calc_physical(hammer2_inode_t *ip,
369 		      const hammer2_inode_data_t *ipdata,
370 		      hammer2_key_t lbase)
371 {
372 	int lblksize;
373 	int pblksize;
374 	int eofbytes;
375 
376 	lblksize = hammer2_calc_logical(ip, lbase, NULL, NULL);
377 	if (lbase + lblksize <= ipdata->size)
378 		return (lblksize);
379 	if (lbase >= ipdata->size)
380 		return (0);
381 	eofbytes = (int)(ipdata->size - lbase);
382 	pblksize = lblksize;
383 	while (pblksize >= eofbytes && pblksize >= HAMMER2_ALLOC_MIN)
384 		pblksize >>= 1;
385 	pblksize <<= 1;
386 
387 	return (pblksize);
388 }
389 
390 void
391 hammer2_update_time(uint64_t *timep)
392 {
393 	struct timeval tv;
394 
395 	getmicrotime(&tv);
396 	*timep = (unsigned long)tv.tv_sec * 1000000 + tv.tv_usec;
397 }
398 
399 void
400 hammer2_adjreadcounter(hammer2_blockref_t *bref, size_t bytes)
401 {
402 	long *counterp;
403 
404 	switch(bref->type) {
405 	case HAMMER2_BREF_TYPE_DATA:
406 		counterp = &hammer2_iod_file_read;
407 		break;
408 	case HAMMER2_BREF_TYPE_INODE:
409 		counterp = &hammer2_iod_meta_read;
410 		break;
411 	case HAMMER2_BREF_TYPE_INDIRECT:
412 		counterp = &hammer2_iod_indr_read;
413 		break;
414 	case HAMMER2_BREF_TYPE_FREEMAP_NODE:
415 	case HAMMER2_BREF_TYPE_FREEMAP_LEAF:
416 		counterp = &hammer2_iod_fmap_read;
417 		break;
418 	default:
419 		counterp = &hammer2_iod_volu_read;
420 		break;
421 	}
422 	*counterp += bytes;
423 }
424 
425 int
426 hammer2_signal_check(time_t *timep)
427 {
428 	int error = 0;
429 
430 	lwkt_user_yield();
431 	if (*timep != time_second) {
432 		*timep = time_second;
433 		if (CURSIG(curthread->td_lwp) != 0)
434 			error = EINTR;
435 	}
436 	return error;
437 }
438 
439 const char *
440 hammer2_error_str(int error)
441 {
442 	const char *str;
443 
444 	switch(error) {
445 	case HAMMER2_ERROR_NONE:
446 		str = "0";
447 		break;
448 	case HAMMER2_ERROR_IO:
449 		str = "I/O";
450 		break;
451 	case HAMMER2_ERROR_CHECK:
452 		str = "check/crc";
453 		break;
454 	case HAMMER2_ERROR_INCOMPLETE:
455 		str = "incomplete-node";
456 		break;
457 	default:
458 		str = "unknown";
459 		break;
460 	}
461 	return (str);
462 }
463