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