if (likely(cmp))
return (cmp);
+ if (d1->db_state == DB_MARKER) {
+ ASSERT3S(d2->db_state, !=, DB_MARKER);
+ return (TREE_PCMP(d1->db_parent, d2));
+ } else if (d2->db_state == DB_MARKER) {
+ ASSERT3S(d1->db_state, !=, DB_MARKER);
+ return (TREE_PCMP(d1, d2->db_parent));
+ }
+
if (d1->db_state == DB_SEARCH) {
ASSERT3S(d2->db_state, !=, DB_SEARCH);
return (-1);
return (B_TRUE);
}
-static void
+static uint_t
dnode_reclaim_slots(dnode_children_t *children, int idx, int slots)
{
+ uint_t reclaimed = 0;
+
ASSERT3S(idx + slots, <=, DNODES_PER_BLOCK);
for (int i = idx; i < idx + slots; i++) {
ASSERT3S(dnh->dnh_dnode->dn_type, ==, DMU_OT_NONE);
dnode_destroy(dnh->dnh_dnode);
dnh->dnh_dnode = DN_SLOT_FREE;
+ reclaimed++;
}
}
+
+ return (reclaimed);
}
void
} else {
dn = dnode_create(os, dn_block + idx, db,
object, dnh);
+ dmu_buf_add_user_size(&db->db,
+ sizeof (dnode_t));
}
}
* to be freed. Single slot dnodes can be safely
* re-purposed as a performance optimization.
*/
- if (slots > 1)
- dnode_reclaim_slots(dnc, idx + 1, slots - 1);
+ if (slots > 1) {
+ uint_t reclaimed =
+ dnode_reclaim_slots(dnc, idx + 1, slots - 1);
+ if (reclaimed > 0)
+ dmu_buf_sub_user_size(&db->db,
+ reclaimed * sizeof (dnode_t));
+ }
dnh = &dnc->dnc_children[idx];
if (DN_SLOT_IS_PTR(dnh->dnh_dnode)) {
} else {
dn = dnode_create(os, dn_block + idx, db,
object, dnh);
+ dmu_buf_add_user_size(&db->db, sizeof (dnode_t));
}
mutex_enter(&dn->dn_mtx);
}
/*
- * Checks if the dnode contains any uncommitted dirty records.
+ * Checks if the dnode itself is dirty, or is carrying any uncommitted records.
+ * It is important to check both conditions, as some operations (eg appending
+ * to a file) can dirty both as a single logical unit, but they are not synced
+ * out atomically, so checking one and not the other can result in an object
+ * appearing to be clean mid-way through a commit.
+ *
+ * Do not change this lightly! If you get it wrong, dmu_offset_next() can
+ * detect a hole where there is really data, leading to silent corruption.
*/
boolean_t
dnode_is_dirty(dnode_t *dn)
mutex_enter(&dn->dn_mtx);
for (int i = 0; i < TXG_SIZE; i++) {
- if (multilist_link_active(&dn->dn_dirty_link[i])) {
+ if (multilist_link_active(&dn->dn_dirty_link[i]) ||
+ !list_is_empty(&dn->dn_dirty_records[i])) {
mutex_exit(&dn->dn_mtx);
return (B_TRUE);
}
}
if (db != NULL && txg != 0 && (db->db_blkptr == NULL ||
- db->db_blkptr->blk_birth <= txg ||
+ BP_GET_LOGICAL_BIRTH(db->db_blkptr) <= txg ||
BP_IS_HOLE(db->db_blkptr))) {
/*
* This can only happen when we are searching up the tree
i >= 0 && i < epb; i += inc) {
if (BP_GET_FILL(&bp[i]) >= minfill &&
BP_GET_FILL(&bp[i]) <= maxfill &&
- (hole || bp[i].blk_birth > txg))
+ (hole || BP_GET_LOGICAL_BIRTH(&bp[i]) > txg))
break;
if (inc > 0 || *offset > 0)
*offset += inc;