#include "hammer.h"
static int btree_search(hammer_cursor_t cursor, int flags);
static int btree_split_internal(hammer_cursor_t cursor);
static int btree_split_leaf(hammer_cursor_t cursor);
static int btree_remove(hammer_cursor_t cursor, int *ndelete);
static __inline int btree_node_is_full(hammer_node_ondisk_t node);
static int hammer_btree_mirror_propagate(hammer_cursor_t cursor,
hammer_tid_t mirror_tid);
static void hammer_make_separator(hammer_base_elm_t key1,
hammer_base_elm_t key2, hammer_base_elm_t dest);
static void hammer_cursor_mirror_filter(hammer_cursor_t cursor);
static __inline void hammer_debug_btree_elm(hammer_cursor_t cursor,
hammer_btree_elm_t elm, const char *s, int res);
static __inline void hammer_debug_btree_parent(hammer_cursor_t cursor,
const char *s);
int
hammer_btree_iterate(hammer_cursor_t cursor)
{
hammer_node_ondisk_t node;
hammer_btree_elm_t elm;
hammer_mount_t hmp;
int error = 0;
int r;
int s;
hmp = cursor->trans->hmp;
node = cursor->node->ondisk;
if (node == NULL)
return(ENOENT);
if (cursor->index < node->count &&
(cursor->flags & HAMMER_CURSOR_ATEDISK)) {
++cursor->index;
}
if (++hmp->check_yield > hammer_yield_check) {
hmp->check_yield = 0;
lwkt_user_yield();
}
for (;;) {
++hammer_stats_btree_iterations;
hammer_flusher_clean_loose_ios(hmp);
if (cursor->index == node->count) {
if (hammer_debug_btree) {
hkprintf("BRACKETU %016jx[%d] -> %016jx[%d] td=%p\n",
(intmax_t)cursor->node->node_offset,
cursor->index,
(intmax_t)(cursor->parent ? cursor->parent->node_offset : -1),
cursor->parent_index,
curthread);
}
KKASSERT(cursor->parent == NULL ||
cursor->parent->ondisk->elms[cursor->parent_index].internal.subtree_offset == cursor->node->node_offset);
error = hammer_cursor_up(cursor);
if (error)
break;
node = cursor->node->ondisk;
KKASSERT(cursor->index != node->count);
if (cursor->flags & HAMMER_CURSOR_REBLOCKING) {
cursor->flags |= HAMMER_CURSOR_ATEDISK;
return(0);
}
++cursor->index;
continue;
}
if (node->type == HAMMER_BTREE_TYPE_INTERNAL) {
elm = &node->elms[cursor->index];
r = hammer_btree_cmp(&cursor->key_end, &elm[0].base);
s = hammer_btree_cmp(&cursor->key_beg, &elm[1].base);
if (hammer_debug_btree) {
hammer_debug_btree_elm(cursor, elm, "BRACKETL", r);
hammer_debug_btree_elm(cursor, elm + 1, "BRACKETR", s);
}
if (r < 0) {
error = ENOENT;
break;
}
if (r == 0 && (cursor->flags &
HAMMER_CURSOR_END_INCLUSIVE) == 0) {
error = ENOENT;
break;
}
KKASSERT(elm->internal.subtree_offset != 0);
if (s <= 0) {
if (cursor->flags &
HAMMER_CURSOR_MIRROR_FILTERED) {
if (elm->internal.mirror_tid <
cursor->cmirror->mirror_tid) {
hammer_cursor_mirror_filter(cursor);
return(0);
}
}
} else {
KKASSERT(cursor->flags &
HAMMER_CURSOR_ITERATE_CHECK);
hdkprintf("DEBUG: Caught parent seek "
"in internal iteration\n");
}
error = hammer_cursor_down(cursor);
if (error)
break;
KKASSERT(cursor->index == 0);
node = cursor->node->ondisk;
continue;
} else {
elm = &node->elms[cursor->index];
r = hammer_btree_cmp(&cursor->key_end, &elm->base);
if (hammer_debug_btree) {
hammer_debug_btree_elm(cursor, elm, "ELEMENT", r);
}
if (r < 0) {
error = ENOENT;
break;
}
if (r == 0 &&
(cursor->flags & HAMMER_CURSOR_END_INCLUSIVE) == 0) {
error = ENOENT;
break;
}
if (cursor->flags & HAMMER_CURSOR_ITERATE_CHECK) {
s = hammer_btree_cmp(&cursor->key_beg,
&elm->base);
if (s > 0) {
hdkprintf("DEBUG: Caught parent seek "
"in leaf iteration\n");
++cursor->index;
continue;
}
}
cursor->flags &= ~HAMMER_CURSOR_ITERATE_CHECK;
switch(elm->leaf.base.btype) {
case HAMMER_BTREE_TYPE_RECORD:
if ((cursor->flags & HAMMER_CURSOR_ASOF) &&
hammer_btree_chkts(cursor->asof, &elm->base)) {
++cursor->index;
continue;
}
error = 0;
break;
default:
error = EINVAL;
break;
}
if (error)
break;
}
if (hammer_debug_btree) {
elm = &cursor->node->ondisk->elms[cursor->index];
hammer_debug_btree_elm(cursor, elm, "ITERATE", 0xffff);
}
return(0);
}
return(error);
}
static void
hammer_cursor_mirror_filter(hammer_cursor_t cursor)
{
struct hammer_cmirror *cmirror;
hammer_node_ondisk_t ondisk;
hammer_btree_elm_t elm;
ondisk = cursor->node->ondisk;
cmirror = cursor->cmirror;
elm = &ondisk->elms[cursor->index];
if (cursor->index == 0)
cmirror->skip_beg = *cursor->left_bound;
else
cmirror->skip_beg = elm->internal.base;
while (cursor->index < ondisk->count) {
if (elm->internal.mirror_tid >= cmirror->mirror_tid)
break;
++cursor->index;
++elm;
}
if (cursor->index == ondisk->count)
cmirror->skip_end = *cursor->right_bound;
else
cmirror->skip_end = elm->internal.base;
if (hammer_btree_cmp(&cmirror->skip_beg, &cursor->key_beg) < 0)
cmirror->skip_beg = cursor->key_beg;
if (hammer_btree_cmp(&cmirror->skip_end, &cursor->key_end) > 0)
cmirror->skip_end = cursor->key_end;
}
int
hammer_btree_iterate_reverse(hammer_cursor_t cursor)
{
hammer_node_ondisk_t node;
hammer_btree_elm_t elm;
hammer_mount_t hmp;
int error = 0;
int r;
int s;
KKASSERT ((cursor->flags & HAMMER_CURSOR_MIRROR_FILTERED) == 0);
node = cursor->node->ondisk;
if (node == NULL)
return(ENOENT);
if (cursor->index != -1 &&
(cursor->flags & HAMMER_CURSOR_ATEDISK)) {
--cursor->index;
}
if (cursor->index == cursor->node->ondisk->count)
--cursor->index;
hmp = cursor->trans->hmp;
if (++hmp->check_yield > hammer_yield_check) {
hmp->check_yield = 0;
lwkt_user_yield();
}
for (;;) {
++hammer_stats_btree_iterations;
hammer_flusher_clean_loose_ios(hmp);
if (cursor->index == -1) {
error = hammer_cursor_up(cursor);
if (error) {
cursor->index = 0;
break;
}
node = cursor->node->ondisk;
KKASSERT(cursor->index != node->count);
--cursor->index;
continue;
}
KKASSERT(cursor->index != node->count);
if (node->type == HAMMER_BTREE_TYPE_INTERNAL) {
elm = &node->elms[cursor->index];
r = hammer_btree_cmp(&cursor->key_end, &elm[0].base);
s = hammer_btree_cmp(&cursor->key_beg, &elm[1].base);
if (hammer_debug_btree) {
hammer_debug_btree_elm(cursor, elm, "BRACKETL", r);
hammer_debug_btree_elm(cursor, elm + 1, "BRACKETR", s);
}
if (s >= 0) {
error = ENOENT;
break;
}
KKASSERT(r >= 0);
KKASSERT(elm->internal.subtree_offset != 0);
error = hammer_cursor_down(cursor);
if (error)
break;
KKASSERT(cursor->index == 0);
node = cursor->node->ondisk;
cursor->index = node->count - 1;
continue;
} else {
elm = &node->elms[cursor->index];
s = hammer_btree_cmp(&cursor->key_beg, &elm->base);
if (hammer_debug_btree) {
hammer_debug_btree_elm(cursor, elm, "ELEMENTR", s);
}
if (s > 0) {
error = ENOENT;
break;
}
cursor->flags &= ~HAMMER_CURSOR_ITERATE_CHECK;
switch(elm->leaf.base.btype) {
case HAMMER_BTREE_TYPE_RECORD:
if ((cursor->flags & HAMMER_CURSOR_ASOF) &&
hammer_btree_chkts(cursor->asof, &elm->base)) {
--cursor->index;
continue;
}
error = 0;
break;
default:
error = EINVAL;
break;
}
if (error)
break;
}
if (hammer_debug_btree) {
elm = &cursor->node->ondisk->elms[cursor->index];
hammer_debug_btree_elm(cursor, elm, "ITERATER", 0xffff);
}
return(0);
}
return(error);
}
int
hammer_btree_lookup(hammer_cursor_t cursor)
{
int error;
cursor->flags &= ~HAMMER_CURSOR_ITERATE_CHECK;
KKASSERT ((cursor->flags & HAMMER_CURSOR_INSERT) == 0 ||
cursor->trans->sync_lock_refs > 0);
++hammer_stats_btree_lookups;
if (cursor->flags & HAMMER_CURSOR_ASOF) {
KKASSERT((cursor->flags & HAMMER_CURSOR_INSERT) == 0);
cursor->key_beg.create_tid = cursor->asof;
for (;;) {
cursor->flags &= ~HAMMER_CURSOR_CREATE_CHECK;
error = btree_search(cursor, 0);
if (error != ENOENT ||
(cursor->flags & HAMMER_CURSOR_CREATE_CHECK) == 0) {
break;
}
if (hammer_debug_btree) {
hkprintf("CREATE_CHECK %016jx\n",
(intmax_t)cursor->create_check);
}
cursor->key_beg.create_tid = cursor->create_check;
}
} else {
error = btree_search(cursor, 0);
}
if (error == 0)
error = hammer_btree_extract(cursor, cursor->flags);
return(error);
}
int
hammer_btree_first(hammer_cursor_t cursor)
{
int error;
error = hammer_btree_lookup(cursor);
if (error == ENOENT) {
cursor->flags &= ~HAMMER_CURSOR_ATEDISK;
error = hammer_btree_iterate(cursor);
}
cursor->flags |= HAMMER_CURSOR_ATEDISK;
return(error);
}
int
hammer_btree_last(hammer_cursor_t cursor)
{
struct hammer_base_elm save;
int error;
save = cursor->key_beg;
cursor->key_beg = cursor->key_end;
error = hammer_btree_lookup(cursor);
cursor->key_beg = save;
if (error == ENOENT ||
(cursor->flags & HAMMER_CURSOR_END_INCLUSIVE) == 0) {
cursor->flags |= HAMMER_CURSOR_ATEDISK;
error = hammer_btree_iterate_reverse(cursor);
}
cursor->flags |= HAMMER_CURSOR_ATEDISK;
return(error);
}
int
hammer_btree_extract(hammer_cursor_t cursor, int flags)
{
hammer_node_ondisk_t node;
hammer_btree_elm_t elm;
hammer_off_t data_off;
hammer_mount_t hmp;
int32_t data_len;
int error;
if (cursor->node == NULL) {
hkprintf("NULL cursor->node, filesystem might "
"have gotten corrupted\n");
return (EINVAL);
}
node = cursor->node->ondisk;
elm = &node->elms[cursor->index];
cursor->data = NULL;
hmp = cursor->node->hmp;
if (node->type == HAMMER_BTREE_TYPE_INTERNAL)
return(EINVAL);
KKASSERT(node->type == HAMMER_BTREE_TYPE_LEAF);
cursor->leaf = &elm->leaf;
if ((flags & HAMMER_CURSOR_GET_DATA) == 0)
return(0);
if (elm->leaf.base.btype != HAMMER_BTREE_TYPE_RECORD)
return(EINVAL);
data_off = elm->leaf.data_offset;
data_len = elm->leaf.data_len;
if (data_off == 0)
return(0);
KKASSERT(data_len >= 0 && data_len <= HAMMER_XBUFSIZE);
cursor->data = hammer_bread_ext(hmp, data_off, data_len,
&error, &cursor->data_buffer);
if (error == 0) {
switch(elm->leaf.base.rec_type) {
case HAMMER_RECTYPE_DATA:
case HAMMER_RECTYPE_DB:
if ((data_off & HAMMER_ZONE_LARGE_DATA) == 0)
break;
if (hammer_double_buffer == 0 ||
(cursor->flags & HAMMER_CURSOR_NOSWAPCACHE)) {
hammer_io_notmeta(cursor->data_buffer);
}
break;
default:
break;
}
}
if (error == 0 &&
hammer_crc_test_leaf(hmp->version, cursor->data, &elm->leaf) == 0) {
hdkprintf("CRC DATA @ %016jx/%d FAILED\n",
(intmax_t)elm->leaf.data_offset, elm->leaf.data_len);
if (hammer_debug_critical)
Debugger("CRC FAILED: DATA");
if (cursor->trans->flags & HAMMER_TRANSF_CRCDOM)
error = EDOM;
else
error = EIO;
}
return(error);
}
int
hammer_btree_insert(hammer_cursor_t cursor, hammer_btree_leaf_elm_t elm,
int *doprop)
{
hammer_node_ondisk_t node;
int i;
int error;
*doprop = 0;
if ((error = hammer_cursor_upgrade_node(cursor)) != 0)
return(error);
++hammer_stats_btree_inserts;
hammer_modify_node_all(cursor->trans, cursor->node);
node = cursor->node->ondisk;
i = cursor->index;
KKASSERT(elm->base.btype != 0);
KKASSERT(node->type == HAMMER_BTREE_TYPE_LEAF);
KKASSERT(node->count < HAMMER_BTREE_LEAF_ELMS);
if (i != node->count) {
bcopy(&node->elms[i], &node->elms[i+1],
(node->count - i) * sizeof(*elm));
}
node->elms[i].leaf = *elm;
++node->count;
hammer_cursor_inserted_element(cursor->node, i);
if (node->mirror_tid < elm->base.delete_tid) {
node->mirror_tid = elm->base.delete_tid;
*doprop = 1;
}
if (node->mirror_tid < elm->base.create_tid) {
node->mirror_tid = elm->base.create_tid;
*doprop = 1;
}
hammer_modify_node_done(cursor->node);
KKASSERT(hammer_btree_cmp(cursor->left_bound, &elm->base) <= 0);
KKASSERT(hammer_btree_cmp(cursor->right_bound, &elm->base) > 0);
if (i) {
KKASSERT(hammer_btree_cmp(&node->elms[i-1].leaf.base, &elm->base) < 0);
}
if (i != node->count - 1)
KKASSERT(hammer_btree_cmp(&node->elms[i+1].leaf.base, &elm->base) > 0);
return(0);
}
int
hammer_btree_delete(hammer_cursor_t cursor, int *ndelete)
{
hammer_node_ondisk_t ondisk;
hammer_node_t node;
hammer_node_t parent __debugvar;
int error;
int i;
KKASSERT (cursor->trans->sync_lock_refs > 0);
if (ndelete)
*ndelete = 0;
if ((error = hammer_cursor_upgrade(cursor)) != 0)
return(error);
++hammer_stats_btree_deletes;
node = cursor->node;
ondisk = node->ondisk;
i = cursor->index;
KKASSERT(ondisk->type == HAMMER_BTREE_TYPE_LEAF);
KKASSERT(i >= 0 && i < ondisk->count);
hammer_modify_node_all(cursor->trans, node);
if (i + 1 != ondisk->count) {
bcopy(&ondisk->elms[i+1], &ondisk->elms[i],
(ondisk->count - i - 1) * sizeof(ondisk->elms[0]));
}
--ondisk->count;
hammer_modify_node_done(node);
hammer_cursor_deleted_element(node, i);
if (ondisk->parent) {
parent = cursor->parent;
KKASSERT(parent != NULL);
KKASSERT(parent->node_offset == ondisk->parent);
}
KKASSERT(cursor->index <= ondisk->count);
if (ondisk->count == 0) {
error = btree_remove(cursor, ndelete);
if (error == EDEADLK)
error = 0;
} else {
error = 0;
}
KKASSERT(cursor->parent == NULL ||
cursor->parent_index < cursor->parent->ondisk->count);
return(error);
}
static
int
btree_search(hammer_cursor_t cursor, int flags)
{
hammer_node_ondisk_t node;
hammer_btree_elm_t elm;
int error;
int enospc = 0;
int i;
int r;
int s;
flags |= cursor->flags;
++hammer_stats_btree_searches;
if (hammer_debug_btree) {
hammer_debug_btree_elm(cursor,
(hammer_btree_elm_t)&cursor->key_beg,
"SEARCH", 0xffff);
if (cursor->parent)
hammer_debug_btree_parent(cursor, "SEARCHP");
}
for (;;) {
r = hammer_btree_cmp(&cursor->key_beg, cursor->left_bound);
s = hammer_btree_cmp(&cursor->key_beg, cursor->right_bound);
if (r >= 0 && s < 0)
break;
KKASSERT(cursor->parent);
++hammer_stats_btree_iterations;
error = hammer_cursor_up(cursor);
if (error)
goto done;
}
if (r == 1) {
KKASSERT(cursor->left_bound->create_tid != 1);
cursor->create_check = cursor->left_bound->create_tid - 1;
cursor->flags |= HAMMER_CURSOR_CREATE_CHECK;
}
KKASSERT(cursor->node != NULL);
while (flags & HAMMER_CURSOR_INSERT) {
if (btree_node_is_full(cursor->node->ondisk) == 0)
break;
if (cursor->node->ondisk->parent == 0 ||
cursor->parent->ondisk->count != HAMMER_BTREE_INT_ELMS) {
break;
}
++hammer_stats_btree_iterations;
error = hammer_cursor_up(cursor);
if (error)
goto done;
}
node = cursor->node->ondisk;
while (node->type == HAMMER_BTREE_TYPE_INTERNAL) {
++hammer_stats_btree_iterations;
if (hammer_debug_btree) {
hkprintf("SEARCH-I %016jx count=%d\n",
(intmax_t)cursor->node->node_offset,
node->count);
}
i = hammer_btree_search_node(&cursor->key_beg, node);
while (i <= node->count) {
++hammer_stats_btree_elements;
elm = &node->elms[i];
r = hammer_btree_cmp(&cursor->key_beg, &elm->base);
if (hammer_debug_btree > 2) {
hkprintf(" IELM %p [%d] r=%d\n",
&node->elms[i], i, r);
}
if (r < 0)
break;
if (r == 1) {
KKASSERT(elm->base.create_tid != 1);
cursor->create_check = elm->base.create_tid - 1;
cursor->flags |= HAMMER_CURSOR_CREATE_CHECK;
}
++i;
}
if (hammer_debug_btree) {
hkprintf("SEARCH-I preI=%d/%d r=%d\n",
i, node->count, r);
}
if (i == 0) {
uint8_t save;
elm = &node->elms[0];
if ((flags & (HAMMER_CURSOR_INSERT |
HAMMER_CURSOR_PRUNING)) == 0) {
cursor->index = 0;
return(ENOENT);
}
if ((error = hammer_cursor_upgrade(cursor)) != 0)
return(error);
KKASSERT(cursor->flags & HAMMER_CURSOR_BACKEND);
hammer_modify_node_field(cursor->trans, cursor->node,
elms[0]);
save = node->elms[0].base.btype;
node->elms[0].base = *cursor->left_bound;
node->elms[0].base.btype = save;
hammer_modify_node_done(cursor->node);
} else if (i == node->count + 1) {
--i;
if ((flags & (HAMMER_CURSOR_INSERT |
HAMMER_CURSOR_PRUNING)) == 0) {
cursor->index = i;
return (ENOENT);
}
if ((error = hammer_cursor_upgrade(cursor)) != 0)
return(error);
elm = &node->elms[i];
KKASSERT(cursor->flags & HAMMER_CURSOR_BACKEND);
hammer_modify_node(cursor->trans, cursor->node,
&elm->base, sizeof(elm->base));
elm->base = *cursor->right_bound;
hammer_modify_node_done(cursor->node);
--i;
} else {
--i;
}
cursor->index = i;
elm = &node->elms[i];
if (hammer_debug_btree) {
hammer_debug_btree_elm(cursor, elm, "RESULT-I", 0xffff);
}
KKASSERT(elm->internal.subtree_offset != 0);
if ((flags & HAMMER_CURSOR_INSERT) && enospc == 0) {
if (btree_node_is_full(node)) {
error = btree_split_internal(cursor);
if (error) {
if (error != ENOSPC)
goto done;
enospc = 1;
}
i = cursor->index;
node = cursor->node->ondisk;
}
}
error = hammer_cursor_down(cursor);
if (error)
goto done;
node = cursor->node->ondisk;
}
++hammer_stats_btree_iterations;
KKASSERT (node->type == HAMMER_BTREE_TYPE_LEAF);
KKASSERT(node->count <= HAMMER_BTREE_LEAF_ELMS);
if (hammer_debug_btree) {
hkprintf("SEARCH-L %016jx count=%d\n",
(intmax_t)cursor->node->node_offset,
node->count);
}
i = hammer_btree_search_node(&cursor->key_beg, node);
while (i < node->count) {
++hammer_stats_btree_elements;
elm = &node->elms[i];
r = hammer_btree_cmp(&cursor->key_beg, &elm->leaf.base);
if (hammer_debug_btree > 1)
hkprintf(" LELM %p [%d] r=%d\n", &node->elms[i], i, r);
KKASSERT (elm->leaf.base.btype == HAMMER_BTREE_TYPE_RECORD);
if (r < 0)
goto failed;
if (r > 1) {
++i;
continue;
}
if (flags & HAMMER_CURSOR_ASOF) {
if (hammer_btree_chkts(cursor->asof,
&node->elms[i].base) != 0) {
++i;
continue;
}
} else {
if (r > 0) {
++i;
continue;
}
}
cursor->index = i;
error = 0;
if (hammer_debug_btree) {
hkprintf("RESULT-L %016jx[%d] (SUCCESS)\n",
(intmax_t)cursor->node->node_offset, i);
}
goto done;
}
failed:
if (hammer_debug_btree) {
hkprintf("RESULT-L %016jx[%d] (FAILED)\n",
(intmax_t)cursor->node->node_offset, i);
}
cursor->index = i;
if ((flags & HAMMER_CURSOR_INSERT) && enospc == 0 &&
btree_node_is_full(node)) {
error = btree_split_leaf(cursor);
if (error) {
if (error != ENOSPC)
goto done;
enospc = 1;
}
}
error = enospc ? ENOSPC : ENOENT;
done:
return(error);
}
int
hammer_btree_search_node(hammer_base_elm_t elm, hammer_node_ondisk_t node)
{
int b;
int s;
int i;
int r;
b = 0;
s = node->count;
while (s - b > 4) {
i = b + (s - b) / 2;
++hammer_stats_btree_elements;
r = hammer_btree_cmp(elm, &node->elms[i].leaf.base);
if (r <= 1) {
s = i;
} else {
b = i;
}
}
return(b);
}
static
int
btree_split_internal(hammer_cursor_t cursor)
{
hammer_node_ondisk_t ondisk;
hammer_node_t node;
hammer_node_t parent;
hammer_node_t new_node;
hammer_btree_elm_t elm;
hammer_btree_elm_t parent_elm;
struct hammer_node_lock lockroot;
hammer_mount_t hmp = cursor->trans->hmp;
int parent_index;
int made_root;
int split;
int error;
int i;
const int esize = sizeof(*elm);
hammer_node_lock_init(&lockroot, cursor->node);
error = hammer_btree_lock_children(cursor, 1, &lockroot, NULL);
if (error)
goto done;
if ((error = hammer_cursor_upgrade(cursor)) != 0)
goto done;
++hammer_stats_btree_splits;
node = cursor->node;
ondisk = node->ondisk;
KKASSERT(ondisk->count > 4);
if (cursor->index == ondisk->count &&
(node->flags & HAMMER_NODE_NONLINEAR) == 0) {
split = (ondisk->count + 1) * 3 / 4;
node->flags |= HAMMER_NODE_NONLINEAR;
} else {
split = (ondisk->count + 1) / 2;
if (cursor->index <= split)
--split;
}
if (ondisk->parent == 0) {
parent = hammer_alloc_btree(cursor->trans, 0, &error);
if (parent == NULL)
goto done;
hammer_lock_ex(&parent->lock);
hammer_modify_node_noundo(cursor->trans, parent);
ondisk = parent->ondisk;
ondisk->count = 1;
ondisk->parent = 0;
ondisk->mirror_tid = node->ondisk->mirror_tid;
ondisk->type = HAMMER_BTREE_TYPE_INTERNAL;
ondisk->elms[0].base = hmp->root_btree_beg;
ondisk->elms[0].base.btype = node->ondisk->type;
ondisk->elms[0].internal.subtree_offset = node->node_offset;
ondisk->elms[0].internal.mirror_tid = ondisk->mirror_tid;
ondisk->elms[1].base = hmp->root_btree_end;
hammer_modify_node_done(parent);
made_root = 1;
parent_index = 0;
} else {
made_root = 0;
parent = cursor->parent;
parent_index = cursor->parent_index;
}
new_node = hammer_alloc_btree(cursor->trans, 0, &error);
if (new_node == NULL) {
if (made_root) {
hammer_unlock(&parent->lock);
hammer_delete_node(cursor->trans, parent);
hammer_rel_node(parent);
}
goto done;
}
hammer_lock_ex(&new_node->lock);
hammer_modify_node_noundo(cursor->trans, new_node);
hammer_modify_node_all(cursor->trans, node);
ondisk = node->ondisk;
elm = &ondisk->elms[split];
bcopy(elm, &new_node->ondisk->elms[0],
(ondisk->count - split + 1) * esize);
new_node->ondisk->count = ondisk->count - split;
new_node->ondisk->parent = parent->node_offset;
new_node->ondisk->type = HAMMER_BTREE_TYPE_INTERNAL;
new_node->ondisk->mirror_tid = ondisk->mirror_tid;
KKASSERT(ondisk->type == new_node->ondisk->type);
hammer_cursor_split_node(node, new_node, split);
elm->base.btype = HAMMER_BTREE_TYPE_NONE;
elm->internal.subtree_offset = 0;
ondisk->count = split;
hammer_modify_node_all(cursor->trans, parent);
ondisk = parent->ondisk;
KKASSERT(ondisk->count != HAMMER_BTREE_INT_ELMS);
parent_elm = &ondisk->elms[parent_index+1];
bcopy(parent_elm, parent_elm + 1,
(ondisk->count - parent_index) * esize);
parent_elm->internal.base = elm->base;
parent_elm->internal.base.btype = new_node->ondisk->type;
parent_elm->internal.subtree_offset = new_node->node_offset;
parent_elm->internal.mirror_tid = new_node->ondisk->mirror_tid;
++ondisk->count;
hammer_modify_node_done(parent);
hammer_cursor_inserted_element(parent, parent_index + 1);
for (i = 0; i < new_node->ondisk->count; ++i) {
elm = &new_node->ondisk->elms[i];
error = btree_set_parent_of_child(cursor->trans, new_node, elm);
if (error) {
hpanic("btree-fixup problem");
}
}
hammer_modify_node_done(new_node);
if (made_root) {
hammer_volume_t volume;
volume = hammer_get_root_volume(hmp, &error);
KKASSERT(error == 0);
hammer_modify_volume_field(cursor->trans, volume,
vol0_btree_root);
volume->ondisk->vol0_btree_root = parent->node_offset;
hammer_modify_volume_done(volume);
node->ondisk->parent = parent->node_offset;
if (cursor->parent) {
hammer_unlock(&cursor->parent->lock);
hammer_rel_node(cursor->parent);
}
cursor->parent = parent;
hammer_rel_volume(volume, 0);
}
hammer_modify_node_done(node);
if (cursor->index >= split) {
cursor->parent_index = parent_index + 1;
cursor->index -= split;
hammer_unlock(&cursor->node->lock);
hammer_rel_node(cursor->node);
cursor->node = new_node;
} else {
cursor->parent_index = parent_index;
hammer_unlock(&new_node->lock);
hammer_rel_node(new_node);
}
parent_elm = &parent->ondisk->elms[cursor->parent_index];
cursor->left_bound = &parent_elm[0].internal.base;
cursor->right_bound = &parent_elm[1].internal.base;
KKASSERT(hammer_btree_cmp(cursor->left_bound,
&cursor->node->ondisk->elms[0].internal.base) <= 0);
KKASSERT(hammer_btree_cmp(cursor->right_bound,
&cursor->node->ondisk->elms[cursor->node->ondisk->count].internal.base) >= 0);
done:
hammer_btree_unlock_children(cursor->trans->hmp, &lockroot, NULL);
hammer_cursor_downgrade(cursor);
return (error);
}
static
int
btree_split_leaf(hammer_cursor_t cursor)
{
hammer_node_ondisk_t ondisk;
hammer_node_t parent;
hammer_node_t leaf;
hammer_mount_t hmp;
hammer_node_t new_leaf;
hammer_btree_elm_t elm;
hammer_btree_elm_t parent_elm;
hammer_base_elm_t mid_boundary;
int parent_index;
int made_root;
int split;
int error;
const size_t esize = sizeof(*elm);
if ((error = hammer_cursor_upgrade(cursor)) != 0)
return(error);
++hammer_stats_btree_splits;
KKASSERT(hammer_btree_cmp(cursor->left_bound,
&cursor->node->ondisk->elms[0].leaf.base) <= 0);
KKASSERT(hammer_btree_cmp(cursor->right_bound,
&cursor->node->ondisk->elms[cursor->node->ondisk->count-1].leaf.base) > 0);
leaf = cursor->node;
ondisk = leaf->ondisk;
KKASSERT(ondisk->count > 4);
if (cursor->index == ondisk->count &&
(leaf->flags & HAMMER_NODE_NONLINEAR) == 0) {
split = (ondisk->count + 1) * 3 / 4;
leaf->flags |= HAMMER_NODE_NONLINEAR;
} else {
split = (ondisk->count + 1) / 2;
}
#if 0
if (cursor->index == split)
--split;
#endif
KKASSERT(split > 0 && split < ondisk->count);
error = 0;
hmp = leaf->hmp;
elm = &ondisk->elms[split];
KKASSERT(hammer_btree_cmp(cursor->left_bound, &elm[-1].leaf.base) <= 0);
KKASSERT(hammer_btree_cmp(cursor->left_bound, &elm->leaf.base) <= 0);
KKASSERT(hammer_btree_cmp(cursor->right_bound, &elm->leaf.base) > 0);
KKASSERT(hammer_btree_cmp(cursor->right_bound, &elm[1].leaf.base) > 0);
if (ondisk->parent == 0) {
parent = hammer_alloc_btree(cursor->trans, 0, &error);
if (parent == NULL)
goto done;
hammer_lock_ex(&parent->lock);
hammer_modify_node_noundo(cursor->trans, parent);
ondisk = parent->ondisk;
ondisk->count = 1;
ondisk->parent = 0;
ondisk->mirror_tid = leaf->ondisk->mirror_tid;
ondisk->type = HAMMER_BTREE_TYPE_INTERNAL;
ondisk->elms[0].base = hmp->root_btree_beg;
ondisk->elms[0].base.btype = leaf->ondisk->type;
ondisk->elms[0].internal.subtree_offset = leaf->node_offset;
ondisk->elms[0].internal.mirror_tid = ondisk->mirror_tid;
ondisk->elms[1].base = hmp->root_btree_end;
hammer_modify_node_done(parent);
made_root = 1;
parent_index = 0;
} else {
made_root = 0;
parent = cursor->parent;
parent_index = cursor->parent_index;
}
new_leaf = hammer_alloc_btree(cursor->trans, 0, &error);
if (new_leaf == NULL) {
if (made_root) {
hammer_unlock(&parent->lock);
hammer_delete_node(cursor->trans, parent);
hammer_rel_node(parent);
}
goto done;
}
hammer_lock_ex(&new_leaf->lock);
hammer_modify_node_all(cursor->trans, leaf);
hammer_modify_node_noundo(cursor->trans, new_leaf);
ondisk = leaf->ondisk;
elm = &ondisk->elms[split];
bcopy(elm, &new_leaf->ondisk->elms[0], (ondisk->count - split) * esize);
new_leaf->ondisk->count = ondisk->count - split;
new_leaf->ondisk->parent = parent->node_offset;
new_leaf->ondisk->type = HAMMER_BTREE_TYPE_LEAF;
new_leaf->ondisk->mirror_tid = ondisk->mirror_tid;
KKASSERT(ondisk->type == new_leaf->ondisk->type);
hammer_modify_node_done(new_leaf);
hammer_cursor_split_node(leaf, new_leaf, split);
ondisk->count = split;
hammer_modify_node_all(cursor->trans, parent);
ondisk = parent->ondisk;
KKASSERT(split != 0);
KKASSERT(ondisk->count != HAMMER_BTREE_INT_ELMS);
parent_elm = &ondisk->elms[parent_index+1];
bcopy(parent_elm, parent_elm + 1,
(ondisk->count - parent_index) * esize);
hammer_make_separator(&elm[-1].base, &elm[0].base, &parent_elm->base);
parent_elm->internal.base.btype = new_leaf->ondisk->type;
parent_elm->internal.subtree_offset = new_leaf->node_offset;
parent_elm->internal.mirror_tid = new_leaf->ondisk->mirror_tid;
mid_boundary = &parent_elm->base;
++ondisk->count;
hammer_modify_node_done(parent);
hammer_cursor_inserted_element(parent, parent_index + 1);
if (made_root) {
hammer_volume_t volume;
volume = hammer_get_root_volume(hmp, &error);
KKASSERT(error == 0);
hammer_modify_volume_field(cursor->trans, volume,
vol0_btree_root);
volume->ondisk->vol0_btree_root = parent->node_offset;
hammer_modify_volume_done(volume);
leaf->ondisk->parent = parent->node_offset;
if (cursor->parent) {
hammer_unlock(&cursor->parent->lock);
hammer_rel_node(cursor->parent);
}
cursor->parent = parent;
hammer_rel_volume(volume, 0);
}
hammer_modify_node_done(leaf);
if (cursor->index > split ||
(cursor->index == split &&
hammer_btree_cmp(&cursor->key_beg, mid_boundary) >= 0)) {
cursor->parent_index = parent_index + 1;
cursor->index -= split;
hammer_unlock(&cursor->node->lock);
hammer_rel_node(cursor->node);
cursor->node = new_leaf;
} else {
cursor->parent_index = parent_index;
hammer_unlock(&new_leaf->lock);
hammer_rel_node(new_leaf);
}
parent_elm = &parent->ondisk->elms[cursor->parent_index];
cursor->left_bound = &parent_elm[0].internal.base;
cursor->right_bound = &parent_elm[1].internal.base;
KKASSERT(hammer_btree_cmp(cursor->left_bound,
&cursor->node->ondisk->elms[0].leaf.base) <= 0);
KKASSERT(hammer_btree_cmp(cursor->right_bound,
&cursor->node->ondisk->elms[cursor->node->ondisk->count-1].leaf.base) > 0);
KKASSERT(hammer_btree_cmp(cursor->left_bound, &cursor->key_beg) <= 0);
KKASSERT(hammer_btree_cmp(cursor->right_bound, &cursor->key_beg) > 0);
done:
hammer_cursor_downgrade(cursor);
return (error);
}
static int
btree_remove(hammer_cursor_t cursor, int *ndelete)
{
hammer_node_ondisk_t ondisk;
hammer_btree_elm_t elm;
hammer_node_t node;
hammer_node_t parent;
const int esize = sizeof(*elm);
int error;
node = cursor->node;
ondisk = node->ondisk;
if (ondisk->parent == 0) {
KKASSERT(cursor->parent == NULL);
hammer_modify_node_all(cursor->trans, node);
KKASSERT(ondisk == node->ondisk);
ondisk->type = HAMMER_BTREE_TYPE_LEAF;
ondisk->count = 0;
hammer_modify_node_done(node);
cursor->index = 0;
return(0);
}
parent = cursor->parent;
if (parent->ondisk->count == 1) {
error = hammer_cursor_up_locked(cursor);
parent = NULL;
if (error == 0) {
hammer_cursor_deleted_element(cursor->node, 0);
error = btree_remove(cursor, ndelete);
if (error == 0) {
KKASSERT(node != cursor->node);
hammer_cursor_removed_node(
node, cursor->node, cursor->index);
hammer_modify_node_all(cursor->trans, node);
ondisk = node->ondisk;
ondisk->type = HAMMER_BTREE_TYPE_DELETED;
ondisk->count = 0;
hammer_modify_node_done(node);
hammer_flush_node(node, 0);
hammer_delete_node(cursor->trans, node);
if (ndelete)
(*ndelete)++;
} else {
}
hammer_unlock(&node->lock);
hammer_rel_node(node);
} else {
}
} else {
KKASSERT(parent->ondisk->count > 1);
hammer_modify_node_all(cursor->trans, parent);
ondisk = parent->ondisk;
KKASSERT(ondisk->type == HAMMER_BTREE_TYPE_INTERNAL);
elm = &ondisk->elms[cursor->parent_index];
KKASSERT(elm->internal.subtree_offset == node->node_offset);
KKASSERT(ondisk->count > 0);
if (cursor->parent_index) {
if (elm[-1].internal.mirror_tid <
elm[0].internal.mirror_tid) {
elm[-1].internal.mirror_tid =
elm[0].internal.mirror_tid;
}
} else {
if (elm[1].internal.mirror_tid <
elm[0].internal.mirror_tid) {
elm[1].internal.mirror_tid =
elm[0].internal.mirror_tid;
}
}
bcopy(&elm[1], &elm[0],
(ondisk->count - cursor->parent_index) * esize);
--ondisk->count;
hammer_modify_node_done(parent);
hammer_cursor_removed_node(node, parent, cursor->parent_index);
hammer_cursor_deleted_element(parent, cursor->parent_index);
hammer_flush_node(node, 0);
hammer_delete_node(cursor->trans, node);
cursor->flags |= HAMMER_CURSOR_ITERATE_CHECK;
error = hammer_cursor_up(cursor);
if (ndelete)
(*ndelete)++;
}
return (error);
}
void
hammer_btree_do_propagation(hammer_cursor_t cursor,
hammer_btree_leaf_elm_t leaf)
{
hammer_cursor_t ncursor;
hammer_tid_t mirror_tid;
int error __debugvar;
if (cursor->trans->hmp->master_id < 0)
return;
mirror_tid = cursor->node->ondisk->mirror_tid;
KKASSERT(mirror_tid != 0);
ncursor = hammer_push_cursor(cursor);
error = hammer_btree_mirror_propagate(ncursor, mirror_tid);
KKASSERT(error == 0);
hammer_pop_cursor(cursor, ncursor);
}
static int
hammer_btree_mirror_propagate(hammer_cursor_t cursor, hammer_tid_t mirror_tid)
{
hammer_btree_internal_elm_t elm;
hammer_node_t node;
int error;
for (;;) {
error = hammer_cursor_up(cursor);
if (error == 0)
error = hammer_cursor_upgrade(cursor);
while (error == EDEADLK) {
hammer_recover_cursor(cursor);
error = hammer_cursor_upgrade(cursor);
}
if (error)
break;
node = cursor->node;
if (node->ondisk->type != HAMMER_BTREE_TYPE_INTERNAL)
continue;
elm = &node->ondisk->elms[cursor->index].internal;
if (elm->mirror_tid >= mirror_tid)
break;
hammer_modify_node(cursor->trans, node, &elm->mirror_tid,
sizeof(elm->mirror_tid));
elm->mirror_tid = mirror_tid;
hammer_modify_node_done(node);
if (hammer_debug_general & 0x0002) {
hdkprintf("propagate %016jx @%016jx:%d\n",
(intmax_t)mirror_tid,
(intmax_t)node->node_offset,
cursor->index);
}
if (node->ondisk->mirror_tid >= mirror_tid)
return(0);
hammer_modify_node_field(cursor->trans, node, mirror_tid);
node->ondisk->mirror_tid = mirror_tid;
hammer_modify_node_done(node);
if (hammer_debug_general & 0x0002) {
hdkprintf("propagate %016jx @%016jx\n",
(intmax_t)mirror_tid,
(intmax_t)node->node_offset);
}
}
if (error == ENOENT)
error = 0;
return(error);
}
hammer_node_t
hammer_btree_get_parent(hammer_transaction_t trans, hammer_node_t node,
int *parent_indexp, int *errorp, int try_exclusive)
{
hammer_node_t parent;
hammer_btree_elm_t elm;
int i;
parent = hammer_get_node(trans, node->ondisk->parent, 0, errorp);
if (*errorp) {
KKASSERT(parent == NULL);
return(NULL);
}
KKASSERT ((parent->flags & HAMMER_NODE_DELETED) == 0);
if (try_exclusive) {
if (hammer_lock_ex_try(&parent->lock)) {
hammer_rel_node(parent);
*errorp = EDEADLK;
return(NULL);
}
} else {
hammer_lock_sh(&parent->lock);
}
if (node->ondisk->count) {
i = hammer_btree_search_node(&node->ondisk->elms[0].base,
parent->ondisk);
} else {
i = 0;
}
while (i < parent->ondisk->count) {
elm = &parent->ondisk->elms[i];
if (elm->internal.subtree_offset == node->node_offset)
break;
++i;
}
if (i == parent->ondisk->count) {
hammer_unlock(&parent->lock);
hpanic("Bad B-Tree link: parent %p node %p", parent, node);
}
*parent_indexp = i;
KKASSERT(*errorp == 0);
return(parent);
}
int
btree_set_parent_of_child(hammer_transaction_t trans, hammer_node_t node,
hammer_btree_elm_t elm)
{
hammer_node_t child;
int error;
error = 0;
if (hammer_is_internal_node_elm(elm)) {
child = hammer_get_node(trans, elm->internal.subtree_offset,
0, &error);
if (error == 0) {
hammer_modify_node_field(trans, child, parent);
child->ondisk->parent = node->node_offset;
hammer_modify_node_done(child);
hammer_rel_node(child);
}
}
return(error);
}
void
hammer_node_lock_init(hammer_node_lock_t parent, hammer_node_t node)
{
TAILQ_INIT(&parent->list);
parent->parent = NULL;
parent->node = node;
parent->index = -1;
parent->count = node->ondisk->count;
parent->copy = NULL;
parent->flags = 0;
}
void
hammer_btree_lcache_init(hammer_mount_t hmp, hammer_node_lock_t lcache,
int depth)
{
hammer_node_lock_t item;
int count;
for (count = 1; depth; --depth)
count *= HAMMER_BTREE_LEAF_ELMS;
bzero(lcache, sizeof(*lcache));
TAILQ_INIT(&lcache->list);
while (count) {
item = kmalloc(sizeof(*item), hmp->m_misc, M_WAITOK|M_ZERO);
item->copy = kmalloc(sizeof(*item->copy),
hmp->m_misc, M_WAITOK);
TAILQ_INIT(&item->list);
TAILQ_INSERT_TAIL(&lcache->list, item, entry);
--count;
}
}
void
hammer_btree_lcache_free(hammer_mount_t hmp, hammer_node_lock_t lcache)
{
hammer_node_lock_t item;
while ((item = TAILQ_FIRST(&lcache->list)) != NULL) {
TAILQ_REMOVE(&lcache->list, item, entry);
KKASSERT(item->copy);
KKASSERT(TAILQ_EMPTY(&item->list));
kfree(item->copy, hmp->m_misc);
kfree(item, hmp->m_misc);
}
KKASSERT(lcache->copy == NULL);
}
int
hammer_btree_lock_children(hammer_cursor_t cursor, int depth,
hammer_node_lock_t parent,
hammer_node_lock_t lcache)
{
hammer_node_t node;
hammer_node_lock_t item;
hammer_node_ondisk_t ondisk;
hammer_btree_elm_t elm;
hammer_node_t child;
hammer_mount_t hmp;
int error;
int i;
node = parent->node;
ondisk = node->ondisk;
error = 0;
hmp = cursor->trans->hmp;
if (ondisk->type != HAMMER_BTREE_TYPE_INTERNAL)
return(0);
for (i = 0; i < ondisk->count; ++i) {
++hammer_stats_btree_elements;
elm = &ondisk->elms[i];
child = hammer_get_node(cursor->trans,
elm->internal.subtree_offset,
0, &error);
if (child)
hammer_rel_node(child);
}
for (i = 0; error == 0 && i < ondisk->count; ++i) {
++hammer_stats_btree_elements;
elm = &ondisk->elms[i];
KKASSERT(elm->internal.subtree_offset != 0);
child = hammer_get_node(cursor->trans,
elm->internal.subtree_offset,
0, &error);
if (child) {
if (hammer_lock_ex_try(&child->lock) != 0) {
if (cursor->deadlk_node == NULL) {
cursor->deadlk_node = child;
hammer_ref_node(cursor->deadlk_node);
}
error = EDEADLK;
hammer_rel_node(child);
} else {
if (lcache) {
item = TAILQ_FIRST(&lcache->list);
KKASSERT(item != NULL);
item->flags |= HAMMER_NODE_LOCK_LCACHE;
TAILQ_REMOVE(&lcache->list, item, entry);
} else {
item = kmalloc(sizeof(*item),
hmp->m_misc,
M_WAITOK|M_ZERO);
TAILQ_INIT(&item->list);
}
TAILQ_INSERT_TAIL(&parent->list, item, entry);
item->parent = parent;
item->node = child;
item->index = i;
item->count = child->ondisk->count;
if (depth > 1 && elm->base.btype == HAMMER_BTREE_TYPE_INTERNAL) {
error = hammer_btree_lock_children(
cursor,
depth - 1,
item,
lcache);
}
}
}
}
if (error)
hammer_btree_unlock_children(hmp, parent, lcache);
return(error);
}
void
hammer_btree_lock_copy(hammer_cursor_t cursor, hammer_node_lock_t parent)
{
hammer_mount_t hmp = cursor->trans->hmp;
hammer_node_lock_t item;
if (parent->copy == NULL) {
KKASSERT((parent->flags & HAMMER_NODE_LOCK_LCACHE) == 0);
parent->copy = kmalloc(sizeof(*parent->copy),
hmp->m_misc, M_WAITOK);
}
KKASSERT((parent->flags & HAMMER_NODE_LOCK_UPDATED) == 0);
*parent->copy = *parent->node->ondisk;
TAILQ_FOREACH(item, &parent->list, entry) {
hammer_btree_lock_copy(cursor, item);
}
}
int
hammer_btree_sync_copy(hammer_cursor_t cursor, hammer_node_lock_t parent)
{
hammer_node_lock_t item;
int count = 0;
if (parent->flags & HAMMER_NODE_LOCK_UPDATED) {
++count;
hammer_modify_node_all(cursor->trans, parent->node);
*parent->node->ondisk = *parent->copy;
hammer_modify_node_done(parent->node);
if (parent->copy->type == HAMMER_BTREE_TYPE_DELETED) {
hammer_flush_node(parent->node, 0);
hammer_delete_node(cursor->trans, parent->node);
}
}
TAILQ_FOREACH(item, &parent->list, entry) {
count += hammer_btree_sync_copy(cursor, item);
}
return(count);
}
void
hammer_btree_unlock_children(hammer_mount_t hmp, hammer_node_lock_t parent,
hammer_node_lock_t lcache)
{
hammer_node_lock_t item;
hammer_node_ondisk_t copy;
while ((item = TAILQ_FIRST(&parent->list)) != NULL) {
TAILQ_REMOVE(&parent->list, item, entry);
hammer_btree_unlock_children(hmp, item, lcache);
hammer_unlock(&item->node->lock);
hammer_rel_node(item->node);
if (lcache) {
KKASSERT(item->flags & HAMMER_NODE_LOCK_LCACHE);
KKASSERT(TAILQ_EMPTY(&item->list));
copy = item->copy;
bzero(item, sizeof(*item));
TAILQ_INIT(&item->list);
item->copy = copy;
if (copy)
bzero(copy, sizeof(*copy));
TAILQ_INSERT_TAIL(&lcache->list, item, entry);
} else {
kfree(item, hmp->m_misc);
}
}
if (parent->copy && (parent->flags & HAMMER_NODE_LOCK_LCACHE) == 0) {
kfree(parent->copy, hmp->m_misc);
parent->copy = NULL;
}
}
int
hammer_btree_cmp(hammer_base_elm_t key1, hammer_base_elm_t key2)
{
if (key1->localization < key2->localization)
return(-5);
if (key1->localization > key2->localization)
return(5);
if (key1->obj_id < key2->obj_id)
return(-4);
if (key1->obj_id > key2->obj_id)
return(4);
if (key1->rec_type < key2->rec_type)
return(-3);
if (key1->rec_type > key2->rec_type)
return(3);
if (key1->key < key2->key)
return(-2);
if (key1->key > key2->key)
return(2);
if (key1->create_tid == 0) {
if (key2->create_tid == 0)
return(0);
return(1);
}
if (key2->create_tid == 0)
return(-1);
if (key1->create_tid < key2->create_tid)
return(-1);
if (key1->create_tid > key2->create_tid)
return(1);
return(0);
}
int
hammer_btree_chkts(hammer_tid_t asof, hammer_base_elm_t base)
{
if (asof == 0) {
if (base->delete_tid)
return(1);
return(0);
}
if (asof < base->create_tid)
return(-1);
if (base->delete_tid && asof >= base->delete_tid)
return(1);
return(0);
}
#define MAKE_SEPARATOR(key1, key2, dest, field) \
dest->field = key1->field + ((key2->field - key1->field + 1) >> 1);
static void
hammer_make_separator(hammer_base_elm_t key1, hammer_base_elm_t key2,
hammer_base_elm_t dest)
{
bzero(dest, sizeof(*dest));
dest->rec_type = key2->rec_type;
dest->key = key2->key;
dest->obj_id = key2->obj_id;
dest->create_tid = key2->create_tid;
MAKE_SEPARATOR(key1, key2, dest, localization);
if (key1->localization == key2->localization) {
MAKE_SEPARATOR(key1, key2, dest, obj_id);
if (key1->obj_id == key2->obj_id) {
MAKE_SEPARATOR(key1, key2, dest, rec_type);
if (key1->rec_type == key2->rec_type) {
MAKE_SEPARATOR(key1, key2, dest, key);
}
}
}
}
#undef MAKE_SEPARATOR
static __inline
int
btree_node_is_full(hammer_node_ondisk_t node)
{
int n;
n = hammer_node_max_elements(node->type);
if (n == -1)
hpanic("bad type %d", node->type);
return(n == node->count);
}
void
hammer_print_btree_node(hammer_node_ondisk_t ondisk)
{
int i, n;
kprintf("node %p count=%d parent=%016jx type=%c\n",
ondisk, ondisk->count,
(intmax_t)ondisk->parent, ondisk->type);
switch (ondisk->type) {
case HAMMER_BTREE_TYPE_INTERNAL:
n = ondisk->count + 1;
break;
case HAMMER_BTREE_TYPE_LEAF:
n = ondisk->count;
break;
default:
return;
}
for (i = 0; i < n; ++i) {
kprintf(" %2d", i);
hammer_print_btree_elm(&ondisk->elms[i]);
}
}
void
hammer_print_btree_elm(hammer_btree_elm_t elm)
{
kprintf("\tobj_id = %016jx\n", (intmax_t)elm->base.obj_id);
kprintf("\tkey = %016jx\n", (intmax_t)elm->base.key);
kprintf("\tcreate_tid = %016jx\n", (intmax_t)elm->base.create_tid);
kprintf("\tdelete_tid = %016jx\n", (intmax_t)elm->base.delete_tid);
kprintf("\trec_type = %04x\n", elm->base.rec_type);
kprintf("\tobj_type = %02x\n", elm->base.obj_type);
kprintf("\tbtype = %02x (%c)\n", elm->base.btype,
hammer_elm_btype(elm));
kprintf("\tlocalization = %08x\n", elm->base.localization);
if (hammer_is_internal_node_elm(elm)) {
kprintf("\tsubtree_off = %016jx\n",
(intmax_t)elm->internal.subtree_offset);
} else if (hammer_is_leaf_node_elm(elm)) {
kprintf("\tdata_offset = %016jx\n",
(intmax_t)elm->leaf.data_offset);
kprintf("\tdata_len = %08x\n", elm->leaf.data_len);
kprintf("\tdata_crc = %08x\n", elm->leaf.data_crc);
}
}
static __inline
void
hammer_debug_btree_elm(hammer_cursor_t cursor, hammer_btree_elm_t elm,
const char *s, int res)
{
hkprintf("%-8s %016jx[%02d] %c "
"lo=%08x obj=%016jx rec=%02x key=%016jx tid=%016jx td=%p "
"r=%d\n",
s,
(intmax_t)cursor->node->node_offset,
cursor->index,
hammer_elm_btype(elm),
elm->base.localization,
(intmax_t)elm->base.obj_id,
elm->base.rec_type,
(intmax_t)elm->base.key,
(intmax_t)elm->base.create_tid,
curthread,
res);
}
static __inline
void
hammer_debug_btree_parent(hammer_cursor_t cursor, const char *s)
{
hammer_btree_elm_t elm =
&cursor->parent->ondisk->elms[cursor->parent_index];
hkprintf("%-8s %016jx[%d] %c "
"(%016jx/%016jx %016jx/%016jx) (%p/%p %p/%p)\n",
s,
(intmax_t)cursor->parent->node_offset,
cursor->parent_index,
hammer_elm_btype(elm),
(intmax_t)cursor->left_bound->obj_id,
(intmax_t)elm->internal.base.obj_id,
(intmax_t)cursor->right_bound->obj_id,
(intmax_t)(elm + 1)->internal.base.obj_id,
cursor->left_bound,
elm,
cursor->right_bound,
elm + 1);
}