*/
leaf = cursor->node;
ondisk = leaf->ondisk;
+ KKASSERT(ondisk->count > 2);
split = (ondisk->count + 1) / 2;
if (cursor->index <= split)
--split;
* The passed inode has no relationship to the cursor position other
* then being in the same pseudofs as the insertion or deletion we
* are propagating the mirror_tid for.
+ *
+ * WARNING! Because we push and pop the passed cursor, it may be
+ * modified by other B-Tree operations while it is unlocked
+ * and things like the node & leaf pointers, and indexes might
+ * change.
*/
void
hammer_btree_do_propagation(hammer_cursor_t cursor,
error = hammer_btree_mirror_propagate(ncursor, mirror_tid);
KKASSERT(error == 0);
hammer_pop_cursor(cursor, ncursor);
+ /* WARNING: cursor's leaf pointer may change after pop */
}
* used for the mirror propagation and physical node removal cases but
* ultimately the intention is to use them for all deadlock recovery
* operations.
+ *
+ * WARNING! The contents of the cursor may be modified while unlocked.
+ * passive modifications including adjusting the node, parent,
+ * indexes, and leaf pointer.
+ *
+ * An outright removal of the element the cursor was pointing at
+ * will cause the HAMMER_CURSOR_TRACKED_RIPOUT flag to be set,
+ * which chains to causing the HAMMER_CURSOR_RETEST to be set
+ * when the cursor is locked again.
*/
void
hammer_unlock_cursor(hammer_cursor_t cursor)
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);
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->node = parent;
cursor->index = index;
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) {
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);
int oindex, int nindex)
{
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) {
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_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;
}
}
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->index >= index) {
+ if (cursor->leaf == &ondisk->elms[cursor->index].leaf)
+ cursor->leaf = &ondisk->elms[cursor->index + 1].leaf;
++cursor->index;
+ }
}
}
*
* We must release our cursor lock to avoid a 3-way deadlock
* due to the exclusive sync lock the finalizer must get.
+ *
+ * WARNING: See warnings in hammer_unlock_cursor() function.
*/
if (hammer_flusher_meta_limit(hmp)) {
hammer_unlock_cursor(cursor);
/*
* Don't blow out the buffer cache. Leave room for frontend
* cache as well.
+ *
+ * WARNING: See warnings in hammer_unlock_cursor() function.
*/
while (hammer_flusher_meta_halflimit(trans->hmp) ||
hammer_flusher_undo_exhausted(trans, 2)) {
hammer_modify_volume_done(trans->rootvol);
}
+ /*
+ * WARNING! cursor's leaf pointer may have changed after
+ * do_propagation returns.
+ */
if (error == 0 && doprop)
hammer_btree_do_propagation(cursor, NULL, &mrec->leaf);
/*
* Basic record comparison code similar to hammer_btree_cmp().
+ *
+ * obj_id is not compared and may not yet be assigned in the record.
*/
static int
hammer_rec_cmp(hammer_base_elm_t elm, hammer_record_t rec)
/*
* If the record is on-disk we have to queue the deletion by
* the record's key. This also causes lookups to skip the
- * record.
+ * record (lookups for the purposes of finding an unused
+ * directory key do not skip the record).
*/
KKASSERT(dip->flags &
(HAMMER_INODE_ONDISK | HAMMER_INODE_DONDISK));
record = hammer_alloc_mem_record(dip, 0);
record->type = HAMMER_MEM_RECORD_DEL;
record->leaf.base = cursor->leaf->base;
+ KKASSERT(dip->obj_id == record->leaf.base.obj_id);
/*
* ip may be NULL, indicating the deletion of a directory
* sync a valid directory entry to disk due to dependancies,
* we must convert the record to a covering delete so the
* frontend does not have visibility on the synced entry.
+ *
+ * WARNING: cursor's leaf pointer may have changed after do_propagation
+ * returns!
*/
if (error == 0) {
if (doprop) {
KKASSERT(record->type == HAMMER_MEM_RECORD_ADD);
record->flags &= ~HAMMER_RECF_DELETED_FE;
record->type = HAMMER_MEM_RECORD_DEL;
+ KKASSERT(record->ip->obj_id == record->leaf.base.obj_id);
KKASSERT(record->flush_state == HAMMER_FST_FLUSH);
record->flags &= ~HAMMER_RECF_CONVERT_DELETE;
KKASSERT((record->flags & (HAMMER_RECF_COMMITTED |
* cursor->ip is NULL when called from the pruning, mirroring,
* and pfs code. If non-NULL propagation will be conditionalized
* on whether the PFS is in no-history mode or not.
+ *
+ * WARNING: cursor's leaf pointer may have changed after do_propagation
+ * returns!
*/
if (doprop) {
if (cursor->ip)
* We only care about leafs. Internal nodes can be returned
* in mirror-filtered mode (they are used to generate SKIP
* mrecords), but we don't need them for this code.
+ *
+ * WARNING: See warnings in hammer_unlock_cursor() function.
*/
cursor.flags |= HAMMER_CURSOR_ATEDISK;
if (cursor.node->ondisk->type == HAMMER_BTREE_TYPE_LEAF) {
}
++prune->stat_scanrecords;
+ /*
+ * WARNING: See warnings in hammer_unlock_cursor() function.
+ */
while (hammer_flusher_meta_halflimit(trans->hmp) ||
hammer_flusher_undo_exhausted(trans, 2)) {
hammer_unlock_cursor(&cursor);
/*
* Update returned scan position and do a flush if
* necessary.
+ *
+ * WARNING: See warnings in hammer_unlock_cursor()
+ * function.
*/
elm = &cursor.node->ondisk->elms[cursor.index].leaf;
rebal->key_cur = elm->base;
/*
* If there is insufficient free space it may be due to
* reserved bigblocks, which flushing might fix.
+ *
+ * WARNING: See warnings in hammer_unlock_cursor() function.
*/
if (hammer_checkspace(trans->hmp, slop)) {
if (++checkspace_count == 10) {
* crossing a synchronization boundary.
*
* NOTE: cursor.node may have changed on return.
+ *
+ * WARNING: See warnings in hammer_unlock_cursor() function.
*/
hammer_sync_lock_sh(trans);
error = hammer_reblock_helper(reblock, &cursor, elm);
* them. But if we allocate too many we can still deadlock
* the buffer cache.
*
- * (The cursor's node and element may change!)
+ * WARNING: See warnings in hammer_unlock_cursor() function.
+ * (The cursor's node and element may change!)
*/
if (bd_heatup()) {
hammer_unlock_cursor(&cursor);
* This is nasty, the uncache code may have to get
* vnode locks and because of that we can't hold
* the cursor locked.
+ *
+ * WARNING: See warnings in hammer_unlock_cursor()
+ * function.
*/
leaf = elm->leaf;
hammer_unlock_cursor(cursor);
int hammer_debug_tid;
int hammer_debug_recover; /* -1 will disable, +1 will force */
int hammer_debug_recover_faults;
+int hammer_error_panic; /* panic on error levels */
int hammer_cluster_enable = 1; /* enable read clustering by default */
int hammer_count_fsyncs;
int hammer_count_inodes;
&hammer_debug_recover, 0, "");
SYSCTL_INT(_vfs_hammer, OID_AUTO, debug_recover_faults, CTLFLAG_RW,
&hammer_debug_recover_faults, 0, "");
+SYSCTL_INT(_vfs_hammer, OID_AUTO, error_panic, CTLFLAG_RW,
+ &hammer_error_panic, 0, "");
SYSCTL_INT(_vfs_hammer, OID_AUTO, cluster_enable, CTLFLAG_RW,
&hammer_cluster_enable, 0, "");
int error, const char *msg)
{
hmp->flags |= HAMMER_MOUNT_CRITICAL_ERROR;
+
krateprintf(&hmp->krate,
- "HAMMER(%s): Critical error inode=%lld %s\n",
- hmp->mp->mnt_stat.f_mntfromname,
- (long long)(ip ? ip->obj_id : -1), msg);
+ "HAMMER(%s): Critical error inode=%jd error=%d %s\n",
+ hmp->mp->mnt_stat.f_mntfromname,
+ (intmax_t)(ip ? ip->obj_id : -1),
+ error, msg);
+
if (hmp->ronly == 0) {
hmp->ronly = 2; /* special errored read-only mode */
hmp->mp->mnt_flag |= MNT_RDONLY;
hmp->mp->mnt_stat.f_mntfromname);
}
hmp->error = error;
+ if (hammer_error_panic > 2)
+ Debugger("Entering debugger");
}
*
* If any changes whatsoever have been made to the cursor
* set EDEADLK and retry.
+ *
+ * WARNING: See warnings in hammer_unlock_cursor()
+ * function.
*/
if (error == 0 && ip && ip->ino_data.obj_type ==
HAMMER_OBJTYPE_DIRECTORY) {