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.32 2008/07/09 10:29:20 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 void 98 hammer_lock_sh(struct hammer_lock *lock) 99 { 100 KKASSERT(lock->refs > 0); 101 crit_enter(); 102 while (lock->locktd != NULL) { 103 if (lock->locktd == curthread) { 104 Debugger("hammer_lock_sh: lock_sh on exclusive"); 105 ++lock->lockcount; 106 crit_exit(); 107 return; 108 } 109 lock->wanted = 1; 110 tsleep(lock, 0, "hmrlck", 0); 111 } 112 KKASSERT(lock->lockcount <= 0); 113 --lock->lockcount; 114 crit_exit(); 115 } 116 117 /* 118 * Obtain a shared lock at a lower priority then thread waiting for an 119 * exclusive lock. To avoid a deadlock this may only be done if no other 120 * shared locks are being held by the caller. 121 */ 122 void 123 hammer_lock_sh_lowpri(struct hammer_lock *lock) 124 { 125 KKASSERT(lock->refs > 0); 126 crit_enter(); 127 while (lock->locktd != NULL || lock->exwanted) { 128 if (lock->locktd == curthread) { 129 Debugger("hammer_lock_sh: lock_sh on exclusive"); 130 ++lock->lockcount; 131 crit_exit(); 132 return; 133 } 134 lock->wanted = 1; 135 tsleep(lock, 0, "hmrlck", 0); 136 } 137 KKASSERT(lock->lockcount <= 0); 138 --lock->lockcount; 139 crit_exit(); 140 } 141 142 int 143 hammer_lock_sh_try(struct hammer_lock *lock) 144 { 145 KKASSERT(lock->refs > 0); 146 crit_enter(); 147 if (lock->locktd) { 148 crit_exit(); 149 return(EAGAIN); 150 } 151 KKASSERT(lock->lockcount <= 0); 152 --lock->lockcount; 153 crit_exit(); 154 return(0); 155 } 156 157 /* 158 * Upgrade a shared lock to an exclusively held lock. This function will 159 * return EDEADLK If there is more then one shared holder. 160 * 161 * No error occurs and no action is taken if the lock is already exclusively 162 * held by the caller. If the lock is not held at all or held exclusively 163 * by someone else, this function will panic. 164 */ 165 int 166 hammer_lock_upgrade(struct hammer_lock *lock) 167 { 168 int error; 169 170 crit_enter(); 171 if (lock->lockcount > 0) { 172 if (lock->locktd != curthread) 173 panic("hammer_lock_upgrade: illegal lock state"); 174 error = 0; 175 } else if (lock->lockcount == -1) { 176 lock->lockcount = 1; 177 lock->locktd = curthread; 178 error = 0; 179 } else if (lock->lockcount != 0) { 180 error = EDEADLK; 181 } else { 182 panic("hammer_lock_upgrade: lock is not held"); 183 /* NOT REACHED */ 184 error = 0; 185 } 186 crit_exit(); 187 return(error); 188 } 189 190 /* 191 * Downgrade an exclusively held lock to a shared lock. 192 */ 193 void 194 hammer_lock_downgrade(struct hammer_lock *lock) 195 { 196 KKASSERT(lock->lockcount == 1 && lock->locktd == curthread); 197 crit_enter(); 198 lock->lockcount = -1; 199 lock->locktd = NULL; 200 if (lock->wanted) { 201 lock->wanted = 0; 202 wakeup(lock); 203 } 204 crit_exit(); 205 /* XXX memory barrier */ 206 } 207 208 void 209 hammer_unlock(struct hammer_lock *lock) 210 { 211 crit_enter(); 212 KKASSERT(lock->lockcount != 0); 213 if (lock->lockcount < 0) { 214 if (++lock->lockcount == 0 && lock->wanted) { 215 lock->wanted = 0; 216 wakeup(lock); 217 } 218 } else { 219 KKASSERT(lock->locktd == curthread); 220 if (--lock->lockcount == 0) { 221 lock->locktd = NULL; 222 if (lock->wanted) { 223 lock->wanted = 0; 224 wakeup(lock); 225 } 226 } 227 228 } 229 crit_exit(); 230 } 231 232 /* 233 * The calling thread must be holding a shared or exclusive lock. 234 * Returns < 0 if lock is held shared, and > 0 if held exlusively. 235 */ 236 int 237 hammer_lock_status(struct hammer_lock *lock) 238 { 239 if (lock->lockcount < 0) 240 return(-1); 241 if (lock->lockcount > 0) 242 return(1); 243 panic("hammer_lock_status: lock must be held: %p", lock); 244 } 245 246 void 247 hammer_ref(struct hammer_lock *lock) 248 { 249 KKASSERT(lock->refs >= 0); 250 crit_enter(); 251 ++lock->refs; 252 crit_exit(); 253 } 254 255 void 256 hammer_unref(struct hammer_lock *lock) 257 { 258 KKASSERT(lock->refs > 0); 259 crit_enter(); 260 --lock->refs; 261 crit_exit(); 262 } 263 264 /* 265 * The sync_lock must be held when doing any modifying operations on 266 * meta-data. The flusher holds the lock exclusively while the reblocker 267 * and pruner use a shared lock. 268 * 269 * Modifying operations can run in parallel until the flusher needs to 270 * sync the disk media. 271 */ 272 void 273 hammer_sync_lock_ex(hammer_transaction_t trans) 274 { 275 ++trans->sync_lock_refs; 276 hammer_lock_ex(&trans->hmp->sync_lock); 277 } 278 279 void 280 hammer_sync_lock_sh(hammer_transaction_t trans) 281 { 282 ++trans->sync_lock_refs; 283 hammer_lock_sh(&trans->hmp->sync_lock); 284 } 285 286 int 287 hammer_sync_lock_sh_try(hammer_transaction_t trans) 288 { 289 int error; 290 291 ++trans->sync_lock_refs; 292 if ((error = hammer_lock_sh_try(&trans->hmp->sync_lock)) != 0) 293 --trans->sync_lock_refs; 294 return (error); 295 } 296 297 void 298 hammer_sync_unlock(hammer_transaction_t trans) 299 { 300 --trans->sync_lock_refs; 301 hammer_unlock(&trans->hmp->sync_lock); 302 } 303 304 /* 305 * Misc 306 */ 307 u_int32_t 308 hammer_to_unix_xid(uuid_t *uuid) 309 { 310 return(*(u_int32_t *)&uuid->node[2]); 311 } 312 313 void 314 hammer_guid_to_uuid(uuid_t *uuid, u_int32_t guid) 315 { 316 bzero(uuid, sizeof(*uuid)); 317 *(u_int32_t *)&uuid->node[2] = guid; 318 } 319 320 void 321 hammer_time_to_timespec(u_int64_t xtime, struct timespec *ts) 322 { 323 ts->tv_sec = (unsigned long)(xtime / 1000000); 324 ts->tv_nsec = (unsigned int)(xtime % 1000000) * 1000L; 325 } 326 327 u_int64_t 328 hammer_timespec_to_time(struct timespec *ts) 329 { 330 u_int64_t xtime; 331 332 xtime = (unsigned)(ts->tv_nsec / 1000) + 333 (unsigned long)ts->tv_sec * 1000000ULL; 334 return(xtime); 335 } 336 337 338 /* 339 * Convert a HAMMER filesystem object type to a vnode type 340 */ 341 enum vtype 342 hammer_get_vnode_type(u_int8_t obj_type) 343 { 344 switch(obj_type) { 345 case HAMMER_OBJTYPE_DIRECTORY: 346 return(VDIR); 347 case HAMMER_OBJTYPE_REGFILE: 348 return(VREG); 349 case HAMMER_OBJTYPE_DBFILE: 350 return(VDATABASE); 351 case HAMMER_OBJTYPE_FIFO: 352 return(VFIFO); 353 case HAMMER_OBJTYPE_SOCKET: 354 return(VSOCK); 355 case HAMMER_OBJTYPE_CDEV: 356 return(VCHR); 357 case HAMMER_OBJTYPE_BDEV: 358 return(VBLK); 359 case HAMMER_OBJTYPE_SOFTLINK: 360 return(VLNK); 361 default: 362 return(VBAD); 363 } 364 /* not reached */ 365 } 366 367 int 368 hammer_get_dtype(u_int8_t obj_type) 369 { 370 switch(obj_type) { 371 case HAMMER_OBJTYPE_DIRECTORY: 372 return(DT_DIR); 373 case HAMMER_OBJTYPE_REGFILE: 374 return(DT_REG); 375 case HAMMER_OBJTYPE_DBFILE: 376 return(DT_DBF); 377 case HAMMER_OBJTYPE_FIFO: 378 return(DT_FIFO); 379 case HAMMER_OBJTYPE_SOCKET: 380 return(DT_SOCK); 381 case HAMMER_OBJTYPE_CDEV: 382 return(DT_CHR); 383 case HAMMER_OBJTYPE_BDEV: 384 return(DT_BLK); 385 case HAMMER_OBJTYPE_SOFTLINK: 386 return(DT_LNK); 387 default: 388 return(DT_UNKNOWN); 389 } 390 /* not reached */ 391 } 392 393 u_int8_t 394 hammer_get_obj_type(enum vtype vtype) 395 { 396 switch(vtype) { 397 case VDIR: 398 return(HAMMER_OBJTYPE_DIRECTORY); 399 case VREG: 400 return(HAMMER_OBJTYPE_REGFILE); 401 case VDATABASE: 402 return(HAMMER_OBJTYPE_DBFILE); 403 case VFIFO: 404 return(HAMMER_OBJTYPE_FIFO); 405 case VSOCK: 406 return(HAMMER_OBJTYPE_SOCKET); 407 case VCHR: 408 return(HAMMER_OBJTYPE_CDEV); 409 case VBLK: 410 return(HAMMER_OBJTYPE_BDEV); 411 case VLNK: 412 return(HAMMER_OBJTYPE_SOFTLINK); 413 default: 414 return(HAMMER_OBJTYPE_UNKNOWN); 415 } 416 /* not reached */ 417 } 418 419 /* 420 * Return flags for hammer_delete_at_cursor() 421 */ 422 int 423 hammer_nohistory(hammer_inode_t ip) 424 { 425 if (ip->hmp->hflags & HMNT_NOHISTORY) 426 return(HAMMER_DELETE_DESTROY); 427 if (ip->ino_data.uflags & (SF_NOHISTORY|UF_NOHISTORY)) 428 return(HAMMER_DELETE_DESTROY); 429 return(0); 430 } 431 432 /* 433 * Return a namekey hash. The 64 bit namekey hash consists of a 32 bit 434 * crc in the MSB and 0 in the LSB. The caller will use the low bits to 435 * generate a unique key and will scan all entries with the same upper 436 * 32 bits when issuing a lookup. 437 * 438 * We strip bit 63 in order to provide a positive key, this way a seek 439 * offset of 0 will represent the base of the directory. 440 * 441 * This function can never return 0. We use the MSB-0 space to synthesize 442 * artificial directory entries such as "." and "..". 443 */ 444 int64_t 445 hammer_directory_namekey(const void *name, int len) 446 { 447 int64_t key; 448 449 key = (int64_t)(crc32(name, len) & 0x7FFFFFFF) << 32; 450 if (key == 0) 451 key |= 0x100000000LL; 452 return(key); 453 } 454 455 hammer_tid_t 456 hammer_str_to_tid(const char *str, int *ispfs, u_int32_t *localizationp) 457 458 { 459 hammer_tid_t tid; 460 char *ptr; 461 462 tid = strtouq(str, &ptr, 0); 463 if (*ptr == ':') { 464 *ispfs = 1; 465 *localizationp = strtoul(ptr + 1, NULL, 0) << 16; 466 } else { 467 *ispfs = 0; 468 } 469 return(tid); 470 } 471 472 void 473 hammer_crc_set_blockmap(hammer_blockmap_t blockmap) 474 { 475 blockmap->entry_crc = crc32(blockmap, HAMMER_BLOCKMAP_CRCSIZE); 476 } 477 478 void 479 hammer_crc_set_volume(hammer_volume_ondisk_t ondisk) 480 { 481 ondisk->vol_crc = crc32(ondisk, HAMMER_VOL_CRCSIZE1) ^ 482 crc32(&ondisk->vol_crc + 1, HAMMER_VOL_CRCSIZE2); 483 } 484 485 int 486 hammer_crc_test_blockmap(hammer_blockmap_t blockmap) 487 { 488 hammer_crc_t crc; 489 490 crc = crc32(blockmap, HAMMER_BLOCKMAP_CRCSIZE); 491 return (blockmap->entry_crc == crc); 492 } 493 494 int 495 hammer_crc_test_volume(hammer_volume_ondisk_t ondisk) 496 { 497 hammer_crc_t crc; 498 499 crc = crc32(ondisk, HAMMER_VOL_CRCSIZE1) ^ 500 crc32(&ondisk->vol_crc + 1, HAMMER_VOL_CRCSIZE2); 501 return (ondisk->vol_crc == crc); 502 } 503 504 int 505 hammer_crc_test_btree(hammer_node_ondisk_t ondisk) 506 { 507 hammer_crc_t crc; 508 509 crc = crc32(&ondisk->crc + 1, HAMMER_BTREE_CRCSIZE); 510 return (ondisk->crc == crc); 511 } 512 513 /* 514 * Test or set the leaf->data_crc field. Deal with any special cases given 515 * a generic B-Tree leaf element and its data. 516 * 517 * NOTE: Inode-data: the atime and mtime fields are not CRCd, allowing them 518 * to be updated in-place. 519 */ 520 int 521 hammer_crc_test_leaf(void *data, hammer_btree_leaf_elm_t leaf) 522 { 523 hammer_crc_t crc; 524 525 if (leaf->data_len == 0) { 526 crc = 0; 527 } else { 528 switch(leaf->base.rec_type) { 529 case HAMMER_RECTYPE_INODE: 530 if (leaf->data_len != sizeof(struct hammer_inode_data)) 531 return(0); 532 crc = crc32(data, HAMMER_INODE_CRCSIZE); 533 break; 534 default: 535 crc = crc32(data, leaf->data_len); 536 break; 537 } 538 } 539 return (leaf->data_crc == crc); 540 } 541 542 void 543 hammer_crc_set_leaf(void *data, hammer_btree_leaf_elm_t leaf) 544 { 545 if (leaf->data_len == 0) { 546 leaf->data_crc = 0; 547 } else { 548 switch(leaf->base.rec_type) { 549 case HAMMER_RECTYPE_INODE: 550 KKASSERT(leaf->data_len == 551 sizeof(struct hammer_inode_data)); 552 leaf->data_crc = crc32(data, HAMMER_INODE_CRCSIZE); 553 break; 554 default: 555 leaf->data_crc = crc32(data, leaf->data_len); 556 break; 557 } 558 } 559 } 560 561 void 562 hkprintf(const char *ctl, ...) 563 { 564 __va_list va; 565 566 if (hammer_debug_debug) { 567 __va_start(va, ctl); 568 kvprintf(ctl, va); 569 __va_end(va); 570 } 571 } 572 573 /* 574 * Return the block size at the specified file offset. 575 */ 576 int 577 hammer_blocksize(int64_t file_offset) 578 { 579 if (file_offset < HAMMER_XDEMARC) 580 return(HAMMER_BUFSIZE); 581 else 582 return(HAMMER_XBUFSIZE); 583 } 584 585 /* 586 * Return the demarkation point between the two offsets where 587 * the block size changes. 588 */ 589 int64_t 590 hammer_blockdemarc(int64_t file_offset1, int64_t file_offset2) 591 { 592 if (file_offset1 < HAMMER_XDEMARC) { 593 if (file_offset2 <= HAMMER_XDEMARC) 594 return(file_offset2); 595 return(HAMMER_XDEMARC); 596 } 597 panic("hammer_blockdemarc: illegal range %lld %lld\n", 598 file_offset1, file_offset2); 599 } 600 601 udev_t 602 hammer_fsid_to_udev(uuid_t *uuid) 603 { 604 u_int32_t crc; 605 606 crc = crc32(uuid, sizeof(*uuid)); 607 return((udev_t)crc); 608 } 609 610