xref: /dragonfly/sys/vfs/hammer/hammer_subs.c (revision bc3d4063)
1 /*
2  * Copyright (c) 2007-2008 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@backplane.com>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  *
34  * $DragonFly: src/sys/vfs/hammer/hammer_subs.c,v 1.34 2008/07/11 01:22:29 dillon Exp $
35  */
36 /*
37  * HAMMER structural locking
38  */
39 
40 #include "hammer.h"
41 #include <sys/dirent.h>
42 
43 void
44 hammer_lock_ex_ident(struct hammer_lock *lock, const char *ident)
45 {
46 	thread_t td = curthread;
47 
48 	KKASSERT(lock->refs > 0);
49 	crit_enter();
50 	if (lock->locktd != td) {
51 		while (lock->locktd != NULL || lock->lockcount) {
52 			++lock->exwanted;
53 			lock->wanted = 1;
54 			if (hammer_debug_locks) {
55 				kprintf("hammer_lock_ex: held by %p\n",
56 					lock->locktd);
57 			}
58 			++hammer_contention_count;
59 			tsleep(lock, 0, ident, 0);
60 			if (hammer_debug_locks)
61 				kprintf("hammer_lock_ex: try again\n");
62 			--lock->exwanted;
63 		}
64 		lock->locktd = td;
65 	}
66 	KKASSERT(lock->lockcount >= 0);
67 	++lock->lockcount;
68 	crit_exit();
69 }
70 
71 /*
72  * Try to obtain an exclusive lock
73  */
74 int
75 hammer_lock_ex_try(struct hammer_lock *lock)
76 {
77 	thread_t td = curthread;
78 
79 	KKASSERT(lock->refs > 0);
80 	crit_enter();
81 	if (lock->locktd != td) {
82 		if (lock->locktd != NULL || lock->lockcount) {
83 			crit_exit();
84 			return(EAGAIN);
85 		}
86 		lock->locktd = td;
87 	}
88 	KKASSERT(lock->lockcount >= 0);
89 	++lock->lockcount;
90 	crit_exit();
91 	return(0);
92 }
93 
94 /*
95  * Obtain a shared lock
96  *
97  * We do not give pending exclusive locks priority over shared locks as
98  * doing so could lead to a deadlock.
99  */
100 void
101 hammer_lock_sh(struct hammer_lock *lock)
102 {
103 	KKASSERT(lock->refs > 0);
104 	crit_enter();
105 	while (lock->locktd != NULL) {
106 		if (lock->locktd == curthread) {
107 			Debugger("hammer_lock_sh: lock_sh on exclusive");
108 			++lock->lockcount;
109 			crit_exit();
110 			return;
111 		}
112 		lock->wanted = 1;
113 		tsleep(lock, 0, "hmrlck", 0);
114 	}
115 	KKASSERT(lock->lockcount <= 0);
116 	--lock->lockcount;
117 	crit_exit();
118 }
119 
120 int
121 hammer_lock_sh_try(struct hammer_lock *lock)
122 {
123 	KKASSERT(lock->refs > 0);
124 	crit_enter();
125 	if (lock->locktd) {
126 		crit_exit();
127 		return(EAGAIN);
128 	}
129 	KKASSERT(lock->lockcount <= 0);
130 	--lock->lockcount;
131 	crit_exit();
132 	return(0);
133 }
134 
135 /*
136  * Upgrade a shared lock to an exclusively held lock.  This function will
137  * return EDEADLK If there is more then one shared holder.
138  *
139  * No error occurs and no action is taken if the lock is already exclusively
140  * held by the caller.  If the lock is not held at all or held exclusively
141  * by someone else, this function will panic.
142  */
143 int
144 hammer_lock_upgrade(struct hammer_lock *lock)
145 {
146 	int error;
147 
148 	crit_enter();
149 	if (lock->lockcount > 0) {
150 		if (lock->locktd != curthread)
151 			panic("hammer_lock_upgrade: illegal lock state");
152 		error = 0;
153 	} else if (lock->lockcount == -1) {
154 		lock->lockcount = 1;
155 		lock->locktd = curthread;
156 		error = 0;
157 	} else if (lock->lockcount != 0) {
158 		error = EDEADLK;
159 	} else {
160 		panic("hammer_lock_upgrade: lock is not held");
161 		/* NOT REACHED */
162 		error = 0;
163 	}
164 	crit_exit();
165 	return(error);
166 }
167 
168 /*
169  * Downgrade an exclusively held lock to a shared lock.
170  */
171 void
172 hammer_lock_downgrade(struct hammer_lock *lock)
173 {
174 	KKASSERT(lock->lockcount == 1 && lock->locktd == curthread);
175 	crit_enter();
176 	lock->lockcount = -1;
177 	lock->locktd = NULL;
178 	if (lock->wanted) {
179 		lock->wanted = 0;
180 		wakeup(lock);
181 	}
182 	crit_exit();
183 	/* XXX memory barrier */
184 }
185 
186 void
187 hammer_unlock(struct hammer_lock *lock)
188 {
189 	crit_enter();
190 	KKASSERT(lock->lockcount != 0);
191 	if (lock->lockcount < 0) {
192 		if (++lock->lockcount == 0 && lock->wanted) {
193 			lock->wanted = 0;
194 			wakeup(lock);
195 		}
196 	} else {
197 		KKASSERT(lock->locktd == curthread);
198 		if (--lock->lockcount == 0) {
199 			lock->locktd = NULL;
200 			if (lock->wanted) {
201 				lock->wanted = 0;
202 				wakeup(lock);
203 			}
204 		}
205 
206 	}
207 	crit_exit();
208 }
209 
210 /*
211  * The calling thread must be holding a shared or exclusive lock.
212  * Returns < 0 if lock is held shared, and > 0 if held exlusively.
213  */
214 int
215 hammer_lock_status(struct hammer_lock *lock)
216 {
217 	if (lock->lockcount < 0)
218 		return(-1);
219 	if (lock->lockcount > 0)
220 		return(1);
221 	panic("hammer_lock_status: lock must be held: %p", lock);
222 }
223 
224 void
225 hammer_ref(struct hammer_lock *lock)
226 {
227 	KKASSERT(lock->refs >= 0);
228 	crit_enter();
229 	++lock->refs;
230 	crit_exit();
231 }
232 
233 void
234 hammer_unref(struct hammer_lock *lock)
235 {
236 	KKASSERT(lock->refs > 0);
237 	crit_enter();
238 	--lock->refs;
239 	crit_exit();
240 }
241 
242 /*
243  * The sync_lock must be held when doing any modifying operations on
244  * meta-data.  It does not have to be held when modifying non-meta-data buffers
245  * (backend or frontend).
246  *
247  * The flusher holds the lock exclusively while all other consumers hold it
248  * shared.  All modifying operations made while holding the lock are atomic
249  * in that they will be made part of the same flush group.
250  *
251  * Due to the atomicy requirement deadlock recovery code CANNOT release the
252  * sync lock, nor can we give pending exclusive sync locks priority over
253  * a shared sync lock as this could lead to a 3-way deadlock.
254  */
255 void
256 hammer_sync_lock_ex(hammer_transaction_t trans)
257 {
258 	++trans->sync_lock_refs;
259 	hammer_lock_ex(&trans->hmp->sync_lock);
260 }
261 
262 void
263 hammer_sync_lock_sh(hammer_transaction_t trans)
264 {
265 	++trans->sync_lock_refs;
266 	hammer_lock_sh(&trans->hmp->sync_lock);
267 }
268 
269 int
270 hammer_sync_lock_sh_try(hammer_transaction_t trans)
271 {
272 	int error;
273 
274 	++trans->sync_lock_refs;
275 	if ((error = hammer_lock_sh_try(&trans->hmp->sync_lock)) != 0)
276 		--trans->sync_lock_refs;
277 	return (error);
278 }
279 
280 void
281 hammer_sync_unlock(hammer_transaction_t trans)
282 {
283 	--trans->sync_lock_refs;
284 	hammer_unlock(&trans->hmp->sync_lock);
285 }
286 
287 /*
288  * Misc
289  */
290 u_int32_t
291 hammer_to_unix_xid(uuid_t *uuid)
292 {
293 	return(*(u_int32_t *)&uuid->node[2]);
294 }
295 
296 void
297 hammer_guid_to_uuid(uuid_t *uuid, u_int32_t guid)
298 {
299 	bzero(uuid, sizeof(*uuid));
300 	*(u_int32_t *)&uuid->node[2] = guid;
301 }
302 
303 void
304 hammer_time_to_timespec(u_int64_t xtime, struct timespec *ts)
305 {
306 	ts->tv_sec = (unsigned long)(xtime / 1000000);
307 	ts->tv_nsec = (unsigned int)(xtime % 1000000) * 1000L;
308 }
309 
310 u_int64_t
311 hammer_timespec_to_time(struct timespec *ts)
312 {
313 	u_int64_t xtime;
314 
315 	xtime = (unsigned)(ts->tv_nsec / 1000) +
316 		(unsigned long)ts->tv_sec * 1000000ULL;
317 	return(xtime);
318 }
319 
320 
321 /*
322  * Convert a HAMMER filesystem object type to a vnode type
323  */
324 enum vtype
325 hammer_get_vnode_type(u_int8_t obj_type)
326 {
327 	switch(obj_type) {
328 	case HAMMER_OBJTYPE_DIRECTORY:
329 		return(VDIR);
330 	case HAMMER_OBJTYPE_REGFILE:
331 		return(VREG);
332 	case HAMMER_OBJTYPE_DBFILE:
333 		return(VDATABASE);
334 	case HAMMER_OBJTYPE_FIFO:
335 		return(VFIFO);
336 	case HAMMER_OBJTYPE_SOCKET:
337 		return(VSOCK);
338 	case HAMMER_OBJTYPE_CDEV:
339 		return(VCHR);
340 	case HAMMER_OBJTYPE_BDEV:
341 		return(VBLK);
342 	case HAMMER_OBJTYPE_SOFTLINK:
343 		return(VLNK);
344 	default:
345 		return(VBAD);
346 	}
347 	/* not reached */
348 }
349 
350 int
351 hammer_get_dtype(u_int8_t obj_type)
352 {
353 	switch(obj_type) {
354 	case HAMMER_OBJTYPE_DIRECTORY:
355 		return(DT_DIR);
356 	case HAMMER_OBJTYPE_REGFILE:
357 		return(DT_REG);
358 	case HAMMER_OBJTYPE_DBFILE:
359 		return(DT_DBF);
360 	case HAMMER_OBJTYPE_FIFO:
361 		return(DT_FIFO);
362 	case HAMMER_OBJTYPE_SOCKET:
363 		return(DT_SOCK);
364 	case HAMMER_OBJTYPE_CDEV:
365 		return(DT_CHR);
366 	case HAMMER_OBJTYPE_BDEV:
367 		return(DT_BLK);
368 	case HAMMER_OBJTYPE_SOFTLINK:
369 		return(DT_LNK);
370 	default:
371 		return(DT_UNKNOWN);
372 	}
373 	/* not reached */
374 }
375 
376 u_int8_t
377 hammer_get_obj_type(enum vtype vtype)
378 {
379 	switch(vtype) {
380 	case VDIR:
381 		return(HAMMER_OBJTYPE_DIRECTORY);
382 	case VREG:
383 		return(HAMMER_OBJTYPE_REGFILE);
384 	case VDATABASE:
385 		return(HAMMER_OBJTYPE_DBFILE);
386 	case VFIFO:
387 		return(HAMMER_OBJTYPE_FIFO);
388 	case VSOCK:
389 		return(HAMMER_OBJTYPE_SOCKET);
390 	case VCHR:
391 		return(HAMMER_OBJTYPE_CDEV);
392 	case VBLK:
393 		return(HAMMER_OBJTYPE_BDEV);
394 	case VLNK:
395 		return(HAMMER_OBJTYPE_SOFTLINK);
396 	default:
397 		return(HAMMER_OBJTYPE_UNKNOWN);
398 	}
399 	/* not reached */
400 }
401 
402 /*
403  * Return flags for hammer_delete_at_cursor()
404  */
405 int
406 hammer_nohistory(hammer_inode_t ip)
407 {
408 	if (ip->hmp->hflags & HMNT_NOHISTORY)
409 		return(HAMMER_DELETE_DESTROY);
410 	if (ip->ino_data.uflags & (SF_NOHISTORY|UF_NOHISTORY))
411 		return(HAMMER_DELETE_DESTROY);
412 	return(0);
413 }
414 
415 /*
416  * Return a namekey hash.   The 64 bit namekey hash consists of a 32 bit
417  * crc in the MSB and 0 in the LSB.  The caller will use the low bits to
418  * generate a unique key and will scan all entries with the same upper
419  * 32 bits when issuing a lookup.
420  *
421  * We strip bit 63 in order to provide a positive key, this way a seek
422  * offset of 0 will represent the base of the directory.
423  *
424  * This function can never return 0.  We use the MSB-0 space to synthesize
425  * artificial directory entries such as "." and "..".
426  */
427 int64_t
428 hammer_directory_namekey(const void *name, int len)
429 {
430 	int64_t key;
431 
432 	key = (int64_t)(crc32(name, len) & 0x7FFFFFFF) << 32;
433 	if (key == 0)
434 		key |= 0x100000000LL;
435 	return(key);
436 }
437 
438 hammer_tid_t
439 hammer_str_to_tid(const char *str, int *ispfs, u_int32_t *localizationp)
440 
441 {
442 	hammer_tid_t tid;
443 	char *ptr;
444 
445 	tid = strtouq(str, &ptr, 0);
446 	if (*ptr == ':') {
447 		*ispfs = 1;
448 		*localizationp = strtoul(ptr + 1, NULL, 10) << 16;
449 	} else {
450 		*ispfs = 0;
451 	}
452 	return(tid);
453 }
454 
455 void
456 hammer_crc_set_blockmap(hammer_blockmap_t blockmap)
457 {
458 	blockmap->entry_crc = crc32(blockmap, HAMMER_BLOCKMAP_CRCSIZE);
459 }
460 
461 void
462 hammer_crc_set_volume(hammer_volume_ondisk_t ondisk)
463 {
464 	ondisk->vol_crc = crc32(ondisk, HAMMER_VOL_CRCSIZE1) ^
465 			  crc32(&ondisk->vol_crc + 1, HAMMER_VOL_CRCSIZE2);
466 }
467 
468 int
469 hammer_crc_test_blockmap(hammer_blockmap_t blockmap)
470 {
471 	hammer_crc_t crc;
472 
473 	crc = crc32(blockmap, HAMMER_BLOCKMAP_CRCSIZE);
474 	return (blockmap->entry_crc == crc);
475 }
476 
477 int
478 hammer_crc_test_volume(hammer_volume_ondisk_t ondisk)
479 {
480 	hammer_crc_t crc;
481 
482 	crc = crc32(ondisk, HAMMER_VOL_CRCSIZE1) ^
483 	      crc32(&ondisk->vol_crc + 1, HAMMER_VOL_CRCSIZE2);
484 	return (ondisk->vol_crc == crc);
485 }
486 
487 int
488 hammer_crc_test_btree(hammer_node_ondisk_t ondisk)
489 {
490 	hammer_crc_t crc;
491 
492 	crc = crc32(&ondisk->crc + 1, HAMMER_BTREE_CRCSIZE);
493 	return (ondisk->crc == crc);
494 }
495 
496 /*
497  * Test or set the leaf->data_crc field.  Deal with any special cases given
498  * a generic B-Tree leaf element and its data.
499  *
500  * NOTE: Inode-data: the atime and mtime fields are not CRCd, allowing them
501  *       to be updated in-place.
502  */
503 int
504 hammer_crc_test_leaf(void *data, hammer_btree_leaf_elm_t leaf)
505 {
506 	hammer_crc_t crc;
507 
508 	if (leaf->data_len == 0) {
509 		crc = 0;
510 	} else {
511 		switch(leaf->base.rec_type) {
512 		case HAMMER_RECTYPE_INODE:
513 			if (leaf->data_len != sizeof(struct hammer_inode_data))
514 				return(0);
515 			crc = crc32(data, HAMMER_INODE_CRCSIZE);
516 			break;
517 		default:
518 			crc = crc32(data, leaf->data_len);
519 			break;
520 		}
521 	}
522 	return (leaf->data_crc == crc);
523 }
524 
525 void
526 hammer_crc_set_leaf(void *data, hammer_btree_leaf_elm_t leaf)
527 {
528 	if (leaf->data_len == 0) {
529 		leaf->data_crc = 0;
530 	} else {
531 		switch(leaf->base.rec_type) {
532 		case HAMMER_RECTYPE_INODE:
533 			KKASSERT(leaf->data_len ==
534 				  sizeof(struct hammer_inode_data));
535 			leaf->data_crc = crc32(data, HAMMER_INODE_CRCSIZE);
536 			break;
537 		default:
538 			leaf->data_crc = crc32(data, leaf->data_len);
539 			break;
540 		}
541 	}
542 }
543 
544 void
545 hkprintf(const char *ctl, ...)
546 {
547 	__va_list va;
548 
549 	if (hammer_debug_debug) {
550 		__va_start(va, ctl);
551 		kvprintf(ctl, va);
552 		__va_end(va);
553 	}
554 }
555 
556 /*
557  * Return the block size at the specified file offset.
558  */
559 int
560 hammer_blocksize(int64_t file_offset)
561 {
562 	if (file_offset < HAMMER_XDEMARC)
563 		return(HAMMER_BUFSIZE);
564 	else
565 		return(HAMMER_XBUFSIZE);
566 }
567 
568 /*
569  * Return the demarkation point between the two offsets where
570  * the block size changes.
571  */
572 int64_t
573 hammer_blockdemarc(int64_t file_offset1, int64_t file_offset2)
574 {
575 	if (file_offset1 < HAMMER_XDEMARC) {
576 		if (file_offset2 <= HAMMER_XDEMARC)
577 			return(file_offset2);
578 		return(HAMMER_XDEMARC);
579 	}
580 	panic("hammer_blockdemarc: illegal range %lld %lld\n",
581 	      file_offset1, file_offset2);
582 }
583 
584 udev_t
585 hammer_fsid_to_udev(uuid_t *uuid)
586 {
587 	u_int32_t crc;
588 
589 	crc = crc32(uuid, sizeof(*uuid));
590 	return((udev_t)crc);
591 }
592 
593