#include "hammer.h"
void
hammer_lock_ex_ident(struct hammer_lock *lock, const char *ident)
{
thread_t td = curthread;
u_int lv;
u_int nlv;
KKASSERT(lock->refs);
for (;;) {
lv = lock->lockval;
if (lv == 0) {
nlv = 1 | HAMMER_LOCKF_EXCLUSIVE;
if (atomic_cmpset_int(&lock->lockval, lv, nlv)) {
lock->lowner = td;
break;
}
} else if ((lv & HAMMER_LOCKF_EXCLUSIVE) &&
lock->lowner == td) {
nlv = (lv + 1);
if (atomic_cmpset_int(&lock->lockval, lv, nlv))
break;
} else {
if (hammer_debug_locks) {
hdkprintf("held by %p\n", lock->lowner);
}
nlv = lv | HAMMER_LOCKF_WANTED;
++hammer_contention_count;
tsleep_interlock(&lock->lockval, 0);
if (atomic_cmpset_int(&lock->lockval, lv, nlv)) {
tsleep(&lock->lockval, PINTERLOCKED, ident, 0);
if (hammer_debug_locks)
hdkprintf("try again\n");
}
}
}
}
int
hammer_lock_ex_try(struct hammer_lock *lock)
{
thread_t td = curthread;
int error;
u_int lv;
u_int nlv;
KKASSERT(lock->refs);
for (;;) {
lv = lock->lockval;
if (lv == 0) {
nlv = 1 | HAMMER_LOCKF_EXCLUSIVE;
if (atomic_cmpset_int(&lock->lockval, lv, nlv)) {
lock->lowner = td;
error = 0;
break;
}
} else if ((lv & HAMMER_LOCKF_EXCLUSIVE) &&
lock->lowner == td) {
nlv = (lv + 1);
if (atomic_cmpset_int(&lock->lockval, lv, nlv)) {
error = 0;
break;
}
} else {
error = EAGAIN;
break;
}
}
return (error);
}
void
hammer_lock_sh(struct hammer_lock *lock)
{
thread_t td = curthread;
u_int lv;
u_int nlv;
const char *ident = "hmrlck";
KKASSERT(lock->refs);
for (;;) {
lv = lock->lockval;
if ((lv & HAMMER_LOCKF_EXCLUSIVE) == 0) {
nlv = (lv + 1);
if (atomic_cmpset_int(&lock->lockval, lv, nlv))
break;
} else if (lock->lowner == td) {
nlv = (lv + 1);
if (atomic_cmpset_int(&lock->lockval, lv, nlv)) {
if (hammer_debug_critical)
Debugger("hammer_lock_sh: holding ex");
break;
}
} else {
nlv = lv | HAMMER_LOCKF_WANTED;
++hammer_contention_count;
tsleep_interlock(&lock->lockval, 0);
if (atomic_cmpset_int(&lock->lockval, lv, nlv))
tsleep(&lock->lockval, PINTERLOCKED, ident, 0);
}
}
}
int
hammer_lock_sh_try(struct hammer_lock *lock)
{
thread_t td = curthread;
u_int lv;
u_int nlv;
int error;
KKASSERT(lock->refs);
for (;;) {
lv = lock->lockval;
if ((lv & HAMMER_LOCKF_EXCLUSIVE) == 0) {
nlv = (lv + 1);
if (atomic_cmpset_int(&lock->lockval, lv, nlv)) {
error = 0;
break;
}
} else if (lock->lowner == td) {
nlv = (lv + 1);
if (atomic_cmpset_int(&lock->lockval, lv, nlv)) {
if (hammer_debug_critical)
Debugger("hammer_lock_sh: holding ex");
error = 0;
break;
}
} else {
error = EAGAIN;
break;
}
}
return (error);
}
int
hammer_lock_upgrade(struct hammer_lock *lock, int shcount)
{
thread_t td = curthread;
u_int lv;
u_int nlv;
int error;
for (;;) {
lv = lock->lockval;
if ((lv & ~HAMMER_LOCKF_WANTED) == shcount) {
nlv = lv | HAMMER_LOCKF_EXCLUSIVE;
if (atomic_cmpset_int(&lock->lockval, lv, nlv)) {
lock->lowner = td;
error = 0;
break;
}
} else if (lv & HAMMER_LOCKF_EXCLUSIVE) {
if (lock->lowner != curthread)
hpanic("illegal state");
error = 0;
break;
} else if ((lv & ~HAMMER_LOCKF_WANTED) == 0) {
hpanic("lock is not held");
error = EDEADLK;
break;
} else {
error = EDEADLK;
break;
}
}
return (error);
}
void
hammer_lock_downgrade(struct hammer_lock *lock, int shcount)
{
thread_t td __debugvar = curthread;
u_int lv;
u_int nlv;
KKASSERT((lock->lockval & ~HAMMER_LOCKF_WANTED) ==
(HAMMER_LOCKF_EXCLUSIVE | shcount));
KKASSERT(lock->lowner == td);
lock->lowner = NULL;
for (;;) {
lv = lock->lockval;
nlv = lv & ~(HAMMER_LOCKF_EXCLUSIVE | HAMMER_LOCKF_WANTED);
if (atomic_cmpset_int(&lock->lockval, lv, nlv)) {
if (lv & HAMMER_LOCKF_WANTED)
wakeup(&lock->lockval);
break;
}
}
}
void
hammer_unlock(struct hammer_lock *lock)
{
thread_t td __debugvar = curthread;
u_int lv;
u_int nlv;
lv = lock->lockval;
KKASSERT(lv != 0);
if (lv & HAMMER_LOCKF_EXCLUSIVE)
KKASSERT(lock->lowner == td);
for (;;) {
lv = lock->lockval;
nlv = lv & ~(HAMMER_LOCKF_EXCLUSIVE | HAMMER_LOCKF_WANTED);
if (nlv > 1) {
nlv = lv - 1;
if (atomic_cmpset_int(&lock->lockval, lv, nlv))
break;
} else if (nlv == 1) {
nlv = 0;
if (lv & HAMMER_LOCKF_EXCLUSIVE)
lock->lowner = NULL;
if (atomic_cmpset_int(&lock->lockval, lv, nlv)) {
if (lv & HAMMER_LOCKF_WANTED)
wakeup(&lock->lockval);
break;
}
} else {
hpanic("lock %p is not held", lock);
}
}
}
int
hammer_lock_status(struct hammer_lock *lock)
{
u_int lv = lock->lockval;
if (lv & HAMMER_LOCKF_EXCLUSIVE)
return(1);
else if (lv)
return(-1);
hpanic("lock must be held: %p", lock);
}
void
hammer_ref(struct hammer_lock *lock)
{
u_int lv;
u_int nlv;
for (;;) {
lv = lock->refs;
if ((lv & ~HAMMER_REFS_FLAGS) == 0) {
nlv = (lv + 1) | HAMMER_REFS_CHECK;
if (atomic_cmpset_int(&lock->refs, lv, nlv))
return;
} else {
nlv = (lv + 1);
KKASSERT((int)nlv > 0);
if (atomic_cmpset_int(&lock->refs, lv, nlv))
return;
}
}
}
void
hammer_rel(struct hammer_lock *lock)
{
u_int lv;
u_int nlv;
for (;;) {
lv = lock->refs;
if ((lv & ~HAMMER_REFS_FLAGS) == 1) {
nlv = (lv - 1) & ~HAMMER_REFS_CHECK;
if (atomic_cmpset_int(&lock->refs, lv, nlv))
return;
} else {
KKASSERT((int)lv > 0);
nlv = (lv - 1);
if (atomic_cmpset_int(&lock->refs, lv, nlv))
return;
}
}
}
int
hammer_ref_interlock(struct hammer_lock *lock)
{
u_int lv;
u_int nlv;
for (;;) {
lv = lock->refs;
if (lv == 0) {
nlv = 1 | HAMMER_REFS_LOCKED | HAMMER_REFS_CHECK;
if (atomic_cmpset_int(&lock->refs, lv, nlv)) {
lock->rowner = curthread;
return(1);
}
} else {
nlv = (lv + 1);
if ((lv & ~HAMMER_REFS_FLAGS) == 0)
nlv |= HAMMER_REFS_CHECK;
if ((nlv & HAMMER_REFS_CHECK) == 0) {
if (atomic_cmpset_int(&lock->refs, lv, nlv))
return(0);
} else if (lv & HAMMER_REFS_LOCKED) {
if (atomic_cmpset_int(&lock->refs, lv, nlv))
break;
} else {
nlv |= HAMMER_REFS_LOCKED;
if (atomic_cmpset_int(&lock->refs, lv, nlv)) {
lock->rowner = curthread;
return(1);
}
}
}
}
for (;;) {
lv = lock->refs;
if ((lv & HAMMER_REFS_CHECK) == 0)
return(0);
if (lv & HAMMER_REFS_LOCKED) {
tsleep_interlock(&lock->refs, 0);
nlv = (lv | HAMMER_REFS_WANTED);
if (atomic_cmpset_int(&lock->refs, lv, nlv))
tsleep(&lock->refs, PINTERLOCKED, "h1lk", 0);
} else {
nlv = lv | HAMMER_REFS_LOCKED;
if (atomic_cmpset_int(&lock->refs, lv, nlv)) {
lock->rowner = curthread;
return(1);
}
}
}
}
int
hammer_ref_interlock_true(struct hammer_lock *lock)
{
u_int lv;
u_int nlv;
for (;;) {
lv = lock->refs;
if (lv) {
hpanic("bad lock %p %08x", lock, lock->refs);
}
nlv = 1 | HAMMER_REFS_LOCKED | HAMMER_REFS_CHECK;
if (atomic_cmpset_int(&lock->refs, lv, nlv)) {
lock->rowner = curthread;
return (1);
}
}
}
void
hammer_ref_interlock_done(struct hammer_lock *lock)
{
u_int lv;
u_int nlv;
for (;;) {
lv = lock->refs;
nlv = lv & ~HAMMER_REFS_FLAGS;
if (atomic_cmpset_int(&lock->refs, lv, nlv)) {
if (lv & HAMMER_REFS_WANTED)
wakeup(&lock->refs);
break;
}
}
}
int
hammer_rel_interlock(struct hammer_lock *lock, int locked)
{
u_int lv;
u_int nlv;
if (locked) {
hammer_rel(lock);
return(1);
}
for (;;) {
lv = lock->refs;
if (lv == 1) {
nlv = 0 | HAMMER_REFS_LOCKED;
if (atomic_cmpset_int(&lock->refs, lv, nlv)) {
lock->rowner = curthread;
return(1);
}
} else if ((lv & ~HAMMER_REFS_FLAGS) == 1) {
if ((lv & HAMMER_REFS_LOCKED) == 0) {
nlv = (lv - 1) | HAMMER_REFS_LOCKED;
if (atomic_cmpset_int(&lock->refs, lv, nlv)) {
lock->rowner = curthread;
return(1);
}
} else {
nlv = lv | HAMMER_REFS_WANTED;
tsleep_interlock(&lock->refs, 0);
if (atomic_cmpset_int(&lock->refs, lv, nlv)) {
tsleep(&lock->refs, PINTERLOCKED,
"h0lk", 0);
}
}
} else {
nlv = (lv - 1);
KKASSERT((int)nlv >= 0);
if (atomic_cmpset_int(&lock->refs, lv, nlv))
return(0);
}
}
}
void
hammer_rel_interlock_done(struct hammer_lock *lock, int orig_locked __unused)
{
u_int lv;
u_int nlv;
for (;;) {
lv = lock->refs;
nlv = lv & ~(HAMMER_REFS_LOCKED | HAMMER_REFS_WANTED);
if ((lv & ~HAMMER_REFS_FLAGS) == 0)
nlv &= ~HAMMER_REFS_CHECK;
if (atomic_cmpset_int(&lock->refs, lv, nlv)) {
if (lv & HAMMER_REFS_WANTED)
wakeup(&lock->refs);
break;
}
}
}
int
hammer_get_interlock(struct hammer_lock *lock)
{
u_int lv;
u_int nlv;
for (;;) {
lv = lock->refs;
if (lv & HAMMER_REFS_LOCKED) {
nlv = lv | HAMMER_REFS_WANTED;
tsleep_interlock(&lock->refs, 0);
if (atomic_cmpset_int(&lock->refs, lv, nlv))
tsleep(&lock->refs, PINTERLOCKED, "hilk", 0);
} else {
nlv = (lv | HAMMER_REFS_LOCKED);
if (atomic_cmpset_int(&lock->refs, lv, nlv)) {
lock->rowner = curthread;
return((lv & HAMMER_REFS_CHECK) ? 1 : 0);
}
}
}
}
int
hammer_try_interlock_norefs(struct hammer_lock *lock)
{
u_int lv;
u_int nlv;
for (;;) {
lv = lock->refs;
if (lv == 0) {
nlv = lv | HAMMER_REFS_LOCKED;
if (atomic_cmpset_int(&lock->refs, lv, nlv)) {
lock->rowner = curthread;
return(1);
}
} else {
return(0);
}
}
}
void
hammer_put_interlock(struct hammer_lock *lock, int error)
{
u_int lv;
u_int nlv;
for (;;) {
lv = lock->refs;
KKASSERT(lv & HAMMER_REFS_LOCKED);
nlv = lv & ~(HAMMER_REFS_LOCKED | HAMMER_REFS_WANTED);
if ((nlv & ~HAMMER_REFS_FLAGS) == 0 || error == 0)
nlv &= ~HAMMER_REFS_CHECK;
else
nlv |= HAMMER_REFS_CHECK;
if (atomic_cmpset_int(&lock->refs, lv, nlv)) {
if (lv & HAMMER_REFS_WANTED)
wakeup(&lock->refs);
return;
}
}
}
void
hammer_sync_lock_ex(hammer_transaction_t trans)
{
++trans->sync_lock_refs;
hammer_lock_ex(&trans->hmp->sync_lock);
}
void
hammer_sync_lock_sh(hammer_transaction_t trans)
{
++trans->sync_lock_refs;
hammer_lock_sh(&trans->hmp->sync_lock);
}
int
hammer_sync_lock_sh_try(hammer_transaction_t trans)
{
int error;
++trans->sync_lock_refs;
if ((error = hammer_lock_sh_try(&trans->hmp->sync_lock)) != 0)
--trans->sync_lock_refs;
return (error);
}
void
hammer_sync_unlock(hammer_transaction_t trans)
{
--trans->sync_lock_refs;
hammer_unlock(&trans->hmp->sync_lock);
}
uint32_t
hammer_to_unix_xid(hammer_uuid_t *uuid)
{
return(*(uint32_t *)&uuid->node[2]);
}
void
hammer_guid_to_uuid(hammer_uuid_t *uuid, uint32_t guid)
{
bzero(uuid, sizeof(*uuid));
*(uint32_t *)&uuid->node[2] = guid;
}
void
hammer_time_to_timespec(uint64_t xtime, struct timespec *ts)
{
ts->tv_sec = (unsigned long)(xtime / 1000000);
ts->tv_nsec = (unsigned int)(xtime % 1000000) * 1000L;
}
uint64_t
hammer_timespec_to_time(struct timespec *ts)
{
uint64_t xtime;
xtime = (unsigned)(ts->tv_nsec / 1000) +
(unsigned long)ts->tv_sec * 1000000ULL;
return(xtime);
}
enum vtype
hammer_get_vnode_type(uint8_t obj_type)
{
switch(obj_type) {
case HAMMER_OBJTYPE_DIRECTORY:
return(VDIR);
case HAMMER_OBJTYPE_REGFILE:
return(VREG);
case HAMMER_OBJTYPE_DBFILE:
return(VDATABASE);
case HAMMER_OBJTYPE_FIFO:
return(VFIFO);
case HAMMER_OBJTYPE_SOCKET:
return(VSOCK);
case HAMMER_OBJTYPE_CDEV:
return(VCHR);
case HAMMER_OBJTYPE_BDEV:
return(VBLK);
case HAMMER_OBJTYPE_SOFTLINK:
return(VLNK);
default:
return(VBAD);
}
}
int
hammer_get_dtype(uint8_t obj_type)
{
switch(obj_type) {
case HAMMER_OBJTYPE_DIRECTORY:
return(DT_DIR);
case HAMMER_OBJTYPE_REGFILE:
return(DT_REG);
case HAMMER_OBJTYPE_DBFILE:
return(DT_DBF);
case HAMMER_OBJTYPE_FIFO:
return(DT_FIFO);
case HAMMER_OBJTYPE_SOCKET:
return(DT_SOCK);
case HAMMER_OBJTYPE_CDEV:
return(DT_CHR);
case HAMMER_OBJTYPE_BDEV:
return(DT_BLK);
case HAMMER_OBJTYPE_SOFTLINK:
return(DT_LNK);
default:
return(DT_UNKNOWN);
}
}
uint8_t
hammer_get_obj_type(enum vtype vtype)
{
switch(vtype) {
case VDIR:
return(HAMMER_OBJTYPE_DIRECTORY);
case VREG:
return(HAMMER_OBJTYPE_REGFILE);
case VDATABASE:
return(HAMMER_OBJTYPE_DBFILE);
case VFIFO:
return(HAMMER_OBJTYPE_FIFO);
case VSOCK:
return(HAMMER_OBJTYPE_SOCKET);
case VCHR:
return(HAMMER_OBJTYPE_CDEV);
case VBLK:
return(HAMMER_OBJTYPE_BDEV);
case VLNK:
return(HAMMER_OBJTYPE_SOFTLINK);
default:
return(HAMMER_OBJTYPE_UNKNOWN);
}
}
int
hammer_nohistory(hammer_inode_t ip)
{
if (ip->hmp->hflags & HMNT_NOHISTORY)
return(HAMMER_DELETE_DESTROY);
if (ip->ino_data.uflags & (SF_NOHISTORY|UF_NOHISTORY))
return(HAMMER_DELETE_DESTROY);
return(0);
}
int64_t
hammer_direntry_namekey(hammer_inode_t dip, const void *name, int len,
uint32_t *max_iterationsp)
{
const char *aname = name;
int32_t crcx;
int64_t key;
int i;
int j;
switch (dip->ino_data.cap_flags & HAMMER_INODE_CAP_DIRHASH_MASK) {
case HAMMER_INODE_CAP_DIRHASH_ALG0:
key = (int64_t)(crc32(aname, len) & 0x7FFFFFFF) << 32;
if (key == 0)
key |= 0x100000000LL;
*max_iterationsp = 0xFFFFFFFFU;
break;
case HAMMER_INODE_CAP_DIRHASH_ALG1:
key = 0;
crcx = 0;
for (i = j = 0; i < len; ++i) {
if (aname[i] == '.' ||
aname[i] == '-' ||
aname[i] == '_' ||
aname[i] == '~') {
if (i != j)
crcx += crc32(aname + j, i - j);
j = i + 1;
}
}
if (i != j)
crcx += crc32(aname + j, i - j);
#if 0
crcx ^= (uint32_t)crcx >> (32 - 5);
crcx = (crcx & 0x07FFFFFF) | ((aname[0] & 0x0F) << (32 - 5));
#endif
crcx &= 0x7FFFFFFFU;
key |= (uint64_t)crcx << 32;
crcx = crc32(aname, len);
crcx = crcx ^ (crcx << 16);
key |= crcx & 0xFFFF0000U;
if ((key & 0xFFFFFFFF00000000LL) == 0)
key |= 0x100000000LL;
if (hammer_debug_general & 0x0400) {
hdkprintf("0x%016jx %*.*s\n",
(intmax_t)key, len, len, aname);
}
*max_iterationsp = 0x00FFFFFF;
break;
case HAMMER_INODE_CAP_DIRHASH_ALG2:
case HAMMER_INODE_CAP_DIRHASH_ALG3:
default:
key = 0;
*max_iterationsp = 1;
hpanic("bad algorithm %p", dip);
break;
}
return(key);
}
int
hammer_str_to_tid(const char *str, int *ispfsp,
hammer_tid_t *tidp, uint32_t *localizationp)
{
hammer_tid_t tid;
uint32_t localization;
char *ptr;
int ispfs;
int n;
tid = strtouq(str, &ptr, 0);
n = ptr - str;
if (n == 2 && str[0] == '-' && str[1] == '1') {
} else if (n == 18 && str[0] == '0' && (str[1] | 0x20) == 'x') {
} else {
return(EINVAL);
}
str = ptr;
if (*str == ':') {
localization = pfs_to_lo(strtoul(str + 1, &ptr, 10));
if (ptr - str != 6)
return(EINVAL);
str = ptr;
ispfs = 1;
} else {
localization = *localizationp;
ispfs = 0;
}
if (*str)
return(EINVAL);
*tidp = tid;
*localizationp = localization;
*ispfsp = ispfs;
return(0);
}
int
hammer_blocksize(int64_t file_offset)
{
if (file_offset < HAMMER_XDEMARC)
return(HAMMER_BUFSIZE);
else
return(HAMMER_XBUFSIZE);
}
int
hammer_blockoff(int64_t file_offset)
{
if (file_offset < HAMMER_XDEMARC)
return((int)file_offset & HAMMER_BUFMASK);
else
return((int)file_offset & HAMMER_XBUFMASK);
}
int64_t
hammer_blockdemarc(int64_t file_offset1, int64_t file_offset2)
{
if (file_offset1 < HAMMER_XDEMARC) {
if (file_offset2 <= HAMMER_XDEMARC)
return(file_offset2);
return(HAMMER_XDEMARC);
}
hpanic("illegal range %jd %jd",
(intmax_t)file_offset1, (intmax_t)file_offset2);
}
dev_t
hammer_fsid_to_udev(hammer_uuid_t *uuid)
{
uint32_t crc;
crc = crc32(uuid, sizeof(*uuid));
return((dev_t)crc);
}