#include "hammer.h"
static int hammer_load_cursor_parent(hammer_cursor_t cursor, int try_exclusive);
int
hammer_init_cursor(hammer_transaction_t trans, hammer_cursor_t cursor,
hammer_node_cache_t cache, hammer_inode_t ip)
{
hammer_volume_t volume;
hammer_node_t node;
hammer_mount_t hmp;
u_int tticks;
int error;
bzero(cursor, sizeof(*cursor));
cursor->trans = trans;
hmp = trans->hmp;
tticks = hammer_tdmux_ticks;
if (trans->type != HAMMER_TRANS_FLS && tticks &&
hmp->count_reclaims > hammer_limit_reclaims / tticks &&
hmp->count_reclaims > hammer_autoflush * 2 &&
hammer_flusher_running(hmp)) {
u_int rticks;
u_int xticks;
u_int dummy;
rticks = (u_int)ticks % tticks;
xticks = hmp->count_reclaims * tticks / hammer_limit_reclaims;
cpu_ccfence();
if (rticks < xticks) {
if (hammer_debug_general & 0x0004)
hdkprintf("rt %3u, xt %3u, tt %3u\n",
rticks, xticks, tticks);
tsleep(&dummy, 0, "htdmux", xticks - rticks);
}
}
if ((cursor->ip = ip) != NULL) {
++ip->cursor_ip_refs;
if (trans->type == HAMMER_TRANS_FLS) {
hammer_lock_ex(&ip->lock);
} else {
#if 0
if (ip->cursor_exclreq_count) {
tsleep(&ip->cursor_exclreq_count, 0,
"hstag1", hammer_tdmux_ticks);
}
#endif
hammer_lock_sh(&ip->lock);
}
}
if (cache && cache->node) {
node = hammer_ref_node_safe(trans, cache, &error);
if (error == 0) {
hammer_lock_sh(&node->lock);
if (node->flags & HAMMER_NODE_DELETED) {
hammer_unlock(&node->lock);
hammer_rel_node(node);
node = NULL;
}
}
if (node == NULL)
++hammer_stats_btree_root_iterations;
} else {
node = NULL;
++hammer_stats_btree_root_iterations;
}
while (node == NULL) {
volume = hammer_get_root_volume(hmp, &error);
if (error)
break;
node = hammer_get_node(trans, volume->ondisk->vol0_btree_root,
0, &error);
hammer_rel_volume(volume, 0);
if (error)
break;
#if 0
if (node->cursor_exclreq_count &&
cursor->trans->type != HAMMER_TRANS_FLS) {
tsleep(&node->cursor_exclreq_count, 0,
"hstag3", hammer_tdmux_ticks);
}
#endif
hammer_lock_sh(&node->lock);
if (node->flags & HAMMER_NODE_DELETED) {
hammer_unlock(&node->lock);
hammer_rel_node(node);
node = NULL;
continue;
}
if (volume->ondisk->vol0_btree_root != node->node_offset) {
hammer_unlock(&node->lock);
hammer_rel_node(node);
node = NULL;
continue;
}
}
cursor->node = node;
if (error == 0)
error = hammer_load_cursor_parent(cursor, 0);
KKASSERT(error == 0);
return(error);
}
void
hammer_normalize_cursor(hammer_cursor_t cursor)
{
if (cursor->node == NULL) {
KKASSERT(cursor->parent != NULL);
hammer_cursor_up(cursor);
}
}
void
hammer_done_cursor(hammer_cursor_t cursor)
{
hammer_inode_t ip;
KKASSERT((cursor->flags & HAMMER_CURSOR_TRACKED) == 0);
if (cursor->parent) {
hammer_unlock(&cursor->parent->lock);
hammer_rel_node(cursor->parent);
cursor->parent = NULL;
}
if (cursor->node) {
hammer_unlock(&cursor->node->lock);
hammer_rel_node(cursor->node);
cursor->node = NULL;
}
if (cursor->data_buffer) {
hammer_rel_buffer(cursor->data_buffer, 0);
cursor->data_buffer = NULL;
}
if ((ip = cursor->ip) != NULL) {
KKASSERT(ip->cursor_ip_refs > 0);
--ip->cursor_ip_refs;
hammer_unlock(&ip->lock);
cursor->ip = NULL;
}
if (cursor->iprec) {
hammer_rel_mem_record(cursor->iprec);
cursor->iprec = NULL;
}
if (cursor->deadlk_node) {
#if 0
if (ip && cursor->trans->type == HAMMER_TRANS_FLS)
++ip->cursor_exclreq_count;
++cursor->deadlk_node->cursor_exclreq_count;
#endif
hammer_lock_ex_ident(&cursor->deadlk_node->lock, "hmrdlk");
hammer_unlock(&cursor->deadlk_node->lock);
#if 0
if (--cursor->deadlk_node->cursor_exclreq_count == 0)
wakeup(&cursor->deadlk_node->cursor_exclreq_count);
if (ip && cursor->trans->type == HAMMER_TRANS_FLS) {
if (--ip->cursor_exclreq_count == 0)
wakeup(&ip->cursor_exclreq_count);
}
#endif
hammer_rel_node(cursor->deadlk_node);
cursor->deadlk_node = NULL;
}
if (cursor->deadlk_rec) {
hammer_wait_mem_record_ident(cursor->deadlk_rec, "hmmdlr");
hammer_rel_mem_record(cursor->deadlk_rec);
cursor->deadlk_rec = NULL;
}
cursor->data = NULL;
cursor->leaf = NULL;
cursor->left_bound = NULL;
cursor->right_bound = NULL;
cursor->trans = NULL;
}
int
hammer_cursor_upgrade(hammer_cursor_t cursor)
{
int error;
if (cursor->parent) {
error = hammer_lock_upgrade(&cursor->parent->lock, 1);
if (error && cursor->deadlk_node == NULL) {
cursor->deadlk_node = cursor->parent;
hammer_ref_node(cursor->deadlk_node);
}
} else {
error = 0;
}
if (error == 0) {
error = hammer_lock_upgrade(&cursor->node->lock, 1);
if (error && cursor->deadlk_node == NULL) {
cursor->deadlk_node = cursor->node;
hammer_ref_node(cursor->deadlk_node);
}
}
#if 0
error = hammer_lock_upgrade(&cursor->node->lock, 1);
if (error && cursor->deadlk_node == NULL) {
cursor->deadlk_node = cursor->node;
hammer_ref_node(cursor->deadlk_node);
} else if (error == 0 && cursor->parent) {
error = hammer_lock_upgrade(&cursor->parent->lock, 1);
if (error && cursor->deadlk_node == NULL) {
cursor->deadlk_node = cursor->parent;
hammer_ref_node(cursor->deadlk_node);
}
}
#endif
return(error);
}
int
hammer_cursor_upgrade_node(hammer_cursor_t cursor)
{
int error;
error = hammer_lock_upgrade(&cursor->node->lock, 1);
if (error && cursor->deadlk_node == NULL) {
cursor->deadlk_node = cursor->node;
hammer_ref_node(cursor->deadlk_node);
}
return(error);
}
void
hammer_cursor_downgrade(hammer_cursor_t cursor)
{
if (hammer_lock_excl_owned(&cursor->node->lock, curthread))
hammer_lock_downgrade(&cursor->node->lock, 1);
if (cursor->parent &&
hammer_lock_excl_owned(&cursor->parent->lock, curthread)) {
hammer_lock_downgrade(&cursor->parent->lock, 1);
}
}
static __noinline
int
collect_node(hammer_node_t *array, int *counts, int n, hammer_node_t node)
{
int i;
for (i = 0; i < n; ++i) {
if (array[i] == node)
break;
}
if (i == n) {
array[i] = node;
counts[i] = 1;
++i;
} else {
++counts[i];
}
return(i);
}
int
hammer_cursor_upgrade2(hammer_cursor_t cursor1, hammer_cursor_t cursor2)
{
hammer_node_t nodes[4];
int counts[4];
int error;
int i;
int n;
n = collect_node(nodes, counts, 0, cursor1->node);
if (cursor1->parent)
n = collect_node(nodes, counts, n, cursor1->parent);
n = collect_node(nodes, counts, n, cursor2->node);
if (cursor2->parent)
n = collect_node(nodes, counts, n, cursor2->parent);
error = 0;
for (i = 0; i < n; ++i) {
error = hammer_lock_upgrade(&nodes[i]->lock, counts[i]);
if (error)
break;
}
if (error) {
while (--i >= 0)
hammer_lock_downgrade(&nodes[i]->lock, counts[i]);
}
return (error);
}
void
hammer_cursor_downgrade2(hammer_cursor_t cursor1, hammer_cursor_t cursor2)
{
hammer_node_t nodes[4];
int counts[4];
int i;
int n;
n = collect_node(nodes, counts, 0, cursor1->node);
if (cursor1->parent)
n = collect_node(nodes, counts, n, cursor1->parent);
n = collect_node(nodes, counts, n, cursor2->node);
if (cursor2->parent)
n = collect_node(nodes, counts, n, cursor2->parent);
for (i = 0; i < n; ++i)
hammer_lock_downgrade(&nodes[i]->lock, counts[i]);
}
int
hammer_cursor_seek(hammer_cursor_t cursor, hammer_node_t node, int index)
{
int error;
hammer_cursor_downgrade(cursor);
error = 0;
if (cursor->node != node) {
hammer_unlock(&cursor->node->lock);
hammer_rel_node(cursor->node);
cursor->node = node;
hammer_ref_node(node);
hammer_lock_sh(&node->lock);
KKASSERT ((node->flags & HAMMER_NODE_DELETED) == 0);
if (cursor->parent) {
hammer_unlock(&cursor->parent->lock);
hammer_rel_node(cursor->parent);
cursor->parent = NULL;
cursor->parent_index = 0;
}
error = hammer_load_cursor_parent(cursor, 0);
}
cursor->index = index;
return (error);
}
static
int
hammer_load_cursor_parent(hammer_cursor_t cursor, int try_exclusive)
{
hammer_mount_t hmp;
hammer_node_t parent;
hammer_node_t node;
hammer_btree_elm_t elm;
int error;
int parent_index;
hmp = cursor->trans->hmp;
if (cursor->node->ondisk->parent) {
node = cursor->node;
parent = hammer_btree_get_parent(cursor->trans, node,
&parent_index,
&error, try_exclusive);
if (error == 0) {
elm = &parent->ondisk->elms[parent_index];
cursor->parent = parent;
cursor->parent_index = parent_index;
cursor->left_bound = &elm[0].internal.base;
cursor->right_bound = &elm[1].internal.base;
}
} else {
cursor->parent = NULL;
cursor->parent_index = 0;
cursor->left_bound = &hmp->root_btree_beg;
cursor->right_bound = &hmp->root_btree_end;
error = 0;
}
return(error);
}
int
hammer_cursor_up(hammer_cursor_t cursor)
{
int error;
hammer_cursor_downgrade(cursor);
if (cursor->parent == NULL)
return (ENOENT);
hammer_unlock(&cursor->node->lock);
hammer_rel_node(cursor->node);
cursor->node = cursor->parent;
cursor->index = cursor->parent_index;
cursor->parent = NULL;
cursor->parent_index = 0;
error = hammer_load_cursor_parent(cursor, 0);
return(error);
}
int
hammer_cursor_up_locked(hammer_cursor_t cursor)
{
hammer_node_t save;
int error;
int save_index;
if (cursor->parent == NULL)
return (ENOENT);
save = cursor->node;
save_index = cursor->index;
cursor->node = cursor->parent;
cursor->index = cursor->parent_index;
cursor->parent = NULL;
cursor->parent_index = 0;
error = hammer_load_cursor_parent(cursor, 1);
if (error) {
cursor->parent = cursor->node;
cursor->parent_index = cursor->index;
cursor->node = save;
cursor->index = save_index;
}
return(error);
}
int
hammer_cursor_down(hammer_cursor_t cursor)
{
hammer_node_t node;
hammer_btree_elm_t elm;
int error;
hammer_cursor_downgrade(cursor);
node = cursor->node;
KKASSERT(cursor->index >= 0 && cursor->index < node->ondisk->count);
if (cursor->parent) {
hammer_unlock(&cursor->parent->lock);
hammer_rel_node(cursor->parent);
}
cursor->parent = node;
cursor->parent_index = cursor->index;
cursor->node = NULL;
cursor->index = 0;
elm = &node->ondisk->elms[cursor->parent_index];
KKASSERT(node->ondisk->type == HAMMER_BTREE_TYPE_INTERNAL);
KKASSERT(elm->internal.subtree_offset != 0);
cursor->left_bound = &elm[0].internal.base;
cursor->right_bound = &elm[1].internal.base;
node = hammer_get_node(cursor->trans,
elm->internal.subtree_offset, 0, &error);
if (error == 0) {
KASSERT(elm->base.btype == node->ondisk->type,
("BTYPE MISMATCH %c %c NODE %p",
elm->base.btype, node->ondisk->type, node));
if (node->ondisk->parent != cursor->parent->node_offset)
hpanic("node %p %016jx vs %016jx",
node,
(intmax_t)node->ondisk->parent,
(intmax_t)cursor->parent->node_offset);
KKASSERT(node->ondisk->parent == cursor->parent->node_offset);
}
if (error == 0) {
#if 0
if (node->cursor_exclreq_count &&
cursor->trans->type != HAMMER_TRANS_FLS) {
tsleep(&node->cursor_exclreq_count, 0,
"hstag2", hammer_tdmux_ticks);
}
#endif
hammer_lock_sh(&node->lock);
KKASSERT ((node->flags & HAMMER_NODE_DELETED) == 0);
cursor->node = node;
cursor->index = 0;
}
return(error);
}
void
hammer_unlock_cursor(hammer_cursor_t cursor)
{
hammer_node_t node;
KKASSERT((cursor->flags & HAMMER_CURSOR_TRACKED) == 0);
KKASSERT(cursor->node);
if (cursor->parent) {
hammer_unlock(&cursor->parent->lock);
hammer_rel_node(cursor->parent);
cursor->parent = NULL;
}
node = cursor->node;
cursor->flags |= HAMMER_CURSOR_TRACKED;
TAILQ_INSERT_TAIL(&node->cursor_list, cursor, deadlk_entry);
hammer_unlock(&node->lock);
}
int
hammer_lock_cursor(hammer_cursor_t cursor)
{
hammer_node_t node;
int error;
KKASSERT(cursor->flags & HAMMER_CURSOR_TRACKED);
for (;;) {
node = cursor->node;
hammer_ref_node(node);
hammer_lock_sh(&node->lock);
if (cursor->node == node) {
hammer_rel_node(node);
break;
}
hammer_unlock(&node->lock);
hammer_rel_node(node);
}
TAILQ_REMOVE(&node->cursor_list, cursor, deadlk_entry);
cursor->flags &= ~HAMMER_CURSOR_TRACKED;
if (cursor->flags & HAMMER_CURSOR_TRACKED_RIPOUT) {
cursor->flags &= ~HAMMER_CURSOR_TRACKED_RIPOUT;
cursor->flags &= ~HAMMER_CURSOR_ATEDISK;
cursor->flags |= HAMMER_CURSOR_RETEST;
}
error = hammer_load_cursor_parent(cursor, 0);
return(error);
}
int
hammer_recover_cursor(hammer_cursor_t cursor)
{
hammer_transaction_t trans __debugvar;
#if 0
hammer_inode_t ip;
#endif
int error;
hammer_unlock_cursor(cursor);
#if 0
ip = cursor->ip;
#endif
trans = cursor->trans;
KKASSERT(trans->sync_lock_refs > 0);
if (cursor->deadlk_node) {
#if 0
if (ip && trans->type == HAMMER_TRANS_FLS)
++ip->cursor_exclreq_count;
++cursor->deadlk_node->cursor_exclreq_count;
#endif
hammer_lock_ex_ident(&cursor->deadlk_node->lock, "hmrdlk");
hammer_unlock(&cursor->deadlk_node->lock);
#if 0
if (--cursor->deadlk_node->cursor_exclreq_count == 0)
wakeup(&cursor->deadlk_node->cursor_exclreq_count);
if (ip && trans->type == HAMMER_TRANS_FLS) {
if (--ip->cursor_exclreq_count == 0)
wakeup(&ip->cursor_exclreq_count);
}
#endif
hammer_rel_node(cursor->deadlk_node);
cursor->deadlk_node = NULL;
}
if (cursor->deadlk_rec) {
hammer_wait_mem_record_ident(cursor->deadlk_rec, "hmmdlr");
hammer_rel_mem_record(cursor->deadlk_rec);
cursor->deadlk_rec = NULL;
}
error = hammer_lock_cursor(cursor);
return(error);
}
hammer_cursor_t
hammer_push_cursor(hammer_cursor_t ocursor)
{
hammer_cursor_t ncursor;
hammer_inode_t ip;
hammer_node_t node;
hammer_mount_t hmp;
hmp = ocursor->trans->hmp;
ncursor = kmalloc(sizeof(*ncursor), hmp->m_misc, M_WAITOK | M_ZERO);
bcopy(ocursor, ncursor, sizeof(*ocursor));
node = ocursor->node;
hammer_ref_node(node);
if ((ocursor->flags & HAMMER_CURSOR_TRACKED) == 0) {
ocursor->flags |= HAMMER_CURSOR_TRACKED;
TAILQ_INSERT_TAIL(&node->cursor_list, ocursor, deadlk_entry);
}
if (ncursor->parent)
ocursor->parent = NULL;
ocursor->data_buffer = NULL;
ocursor->leaf = NULL;
ocursor->data = NULL;
if (ncursor->flags & HAMMER_CURSOR_TRACKED)
TAILQ_INSERT_TAIL(&node->cursor_list, ncursor, deadlk_entry);
if ((ip = ncursor->ip) != NULL) {
++ip->cursor_ip_refs;
}
if (ncursor->iprec)
hammer_ref(&ncursor->iprec->lock);
return(ncursor);
}
void
hammer_pop_cursor(hammer_cursor_t ocursor, hammer_cursor_t ncursor)
{
hammer_mount_t hmp;
hammer_inode_t ip;
hmp = ncursor->trans->hmp;
ip = ncursor->ip;
ncursor->ip = NULL;
if (ip)
--ip->cursor_ip_refs;
hammer_done_cursor(ncursor);
kfree(ncursor, hmp->m_misc);
KKASSERT(ocursor->ip == ip);
hammer_lock_cursor(ocursor);
}
void
hammer_cursor_replaced_node(hammer_node_t onode, hammer_node_t nnode)
{
hammer_cursor_t cursor;
hammer_node_ondisk_t ondisk;
hammer_node_ondisk_t nndisk;
ondisk = onode->ondisk;
nndisk = nnode->ondisk;
while ((cursor = TAILQ_FIRST(&onode->cursor_list)) != NULL) {
TAILQ_REMOVE(&onode->cursor_list, cursor, deadlk_entry);
TAILQ_INSERT_TAIL(&nnode->cursor_list, cursor, deadlk_entry);
KKASSERT(cursor->node == onode);
if (cursor->leaf == &ondisk->elms[cursor->index].leaf)
cursor->leaf = &nndisk->elms[cursor->index].leaf;
cursor->node = nnode;
hammer_ref_node(nnode);
hammer_rel_node(onode);
}
}
void
hammer_cursor_removed_node(hammer_node_t node, hammer_node_t parent, int index)
{
hammer_cursor_t cursor;
hammer_node_ondisk_t ondisk;
KKASSERT(parent != NULL);
ondisk = node->ondisk;
while ((cursor = TAILQ_FIRST(&node->cursor_list)) != NULL) {
KKASSERT(cursor->node == node);
KKASSERT(cursor->index == 0);
TAILQ_REMOVE(&node->cursor_list, cursor, deadlk_entry);
TAILQ_INSERT_TAIL(&parent->cursor_list, cursor, deadlk_entry);
if (cursor->leaf == &ondisk->elms[cursor->index].leaf)
cursor->leaf = NULL;
cursor->flags |= HAMMER_CURSOR_TRACKED_RIPOUT;
cursor->flags |= HAMMER_CURSOR_ITERATE_CHECK;
cursor->node = parent;
cursor->index = index;
hammer_ref_node(parent);
hammer_rel_node(node);
}
}
void
hammer_cursor_split_node(hammer_node_t onode, hammer_node_t nnode, int index)
{
hammer_cursor_t cursor;
hammer_node_ondisk_t ondisk;
hammer_node_ondisk_t nndisk;
ondisk = onode->ondisk;
nndisk = nnode->ondisk;
again:
TAILQ_FOREACH(cursor, &onode->cursor_list, deadlk_entry) {
KKASSERT(cursor->node == onode);
if (cursor->index < index)
continue;
TAILQ_REMOVE(&onode->cursor_list, cursor, deadlk_entry);
TAILQ_INSERT_TAIL(&nnode->cursor_list, cursor, deadlk_entry);
if (cursor->leaf == &ondisk->elms[cursor->index].leaf)
cursor->leaf = &nndisk->elms[cursor->index - index].leaf;
cursor->node = nnode;
cursor->index -= index;
hammer_ref_node(nnode);
hammer_rel_node(onode);
goto again;
}
}
void
hammer_cursor_moved_element(hammer_node_t oparent, int pindex,
hammer_node_t onode, int oindex,
hammer_node_t nnode, int nindex)
{
hammer_cursor_t cursor;
hammer_node_ondisk_t ondisk;
hammer_node_ondisk_t nndisk;
ondisk = onode->ondisk;
nndisk = nnode->ondisk;
again1:
TAILQ_FOREACH(cursor, &onode->cursor_list, deadlk_entry) {
KKASSERT(cursor->node == onode);
if (cursor->index != oindex)
continue;
TAILQ_REMOVE(&onode->cursor_list, cursor, deadlk_entry);
TAILQ_INSERT_TAIL(&nnode->cursor_list, cursor, deadlk_entry);
if (cursor->leaf == &ondisk->elms[oindex].leaf)
cursor->leaf = &nndisk->elms[nindex].leaf;
cursor->node = nnode;
cursor->index = nindex;
hammer_ref_node(nnode);
hammer_rel_node(onode);
goto again1;
}
if (onode == nnode || oindex)
return;
ondisk = oparent->ondisk;
again2:
TAILQ_FOREACH(cursor, &oparent->cursor_list, deadlk_entry) {
KKASSERT(cursor->node == oparent);
if (cursor->index != pindex)
continue;
hkprintf("debug: shifted cursor pointing at parent\n"
"parent %016jx:%d onode %016jx:%d nnode %016jx:%d\n",
(intmax_t)oparent->node_offset, pindex,
(intmax_t)onode->node_offset, oindex,
(intmax_t)nnode->node_offset, nindex);
TAILQ_REMOVE(&oparent->cursor_list, cursor, deadlk_entry);
TAILQ_INSERT_TAIL(&nnode->cursor_list, cursor, deadlk_entry);
if (cursor->leaf == &ondisk->elms[oindex].leaf)
cursor->leaf = &nndisk->elms[nindex].leaf;
cursor->node = nnode;
cursor->index = nindex;
cursor->flags &= ~HAMMER_CURSOR_ATEDISK;
hammer_ref_node(nnode);
hammer_rel_node(oparent);
goto again2;
}
}
void
hammer_cursor_parent_changed(hammer_node_t node, hammer_node_t oparent,
hammer_node_t nparent, int nindex)
{
hammer_cursor_t cursor;
again:
TAILQ_FOREACH(cursor, &node->cursor_list, deadlk_entry) {
KKASSERT(cursor->node == node);
if (cursor->parent == oparent) {
cursor->parent = nparent;
cursor->parent_index = nindex;
hammer_ref_node(nparent);
hammer_rel_node(oparent);
goto again;
}
}
}
void
hammer_cursor_deleted_element(hammer_node_t node, int index)
{
hammer_cursor_t cursor;
hammer_node_ondisk_t ondisk;
ondisk = node->ondisk;
TAILQ_FOREACH(cursor, &node->cursor_list, deadlk_entry) {
KKASSERT(cursor->node == node);
if (cursor->index == index) {
cursor->flags |= HAMMER_CURSOR_TRACKED_RIPOUT;
if (cursor->leaf == &ondisk->elms[cursor->index].leaf)
cursor->leaf = NULL;
} else if (cursor->index > index) {
if (cursor->leaf == &ondisk->elms[cursor->index].leaf)
cursor->leaf = &ondisk->elms[cursor->index - 1].leaf;
--cursor->index;
}
}
}
void
hammer_cursor_inserted_element(hammer_node_t node, int index)
{
hammer_cursor_t cursor;
hammer_node_ondisk_t ondisk;
ondisk = node->ondisk;
TAILQ_FOREACH(cursor, &node->cursor_list, deadlk_entry) {
KKASSERT(cursor->node == node);
if (cursor->index >= index) {
if (cursor->leaf == &ondisk->elms[cursor->index].leaf)
cursor->leaf = &ondisk->elms[cursor->index + 1].leaf;
++cursor->index;
}
}
}
void
hammer_cursor_invalidate_cache(hammer_cursor_t cursor)
{
if (cursor->data_buffer) {
hammer_rel_buffer(cursor->data_buffer, 0);
cursor->data_buffer = NULL;
cursor->data = NULL;
}
}