Skip to content

Commit

Permalink
Drop spill buffer reference
Browse files Browse the repository at this point in the history
When calling sa_update() and friends it is possible that a spill
buffer will be needed to accomidate the update.  When this happens
a hold is taken on the new dbuf and that hold must be released
before calling dmu_tx_commit().  Failing to release the hold will
cause a copy of the dbuf to be made in dbuf_sync_leaf().  This is
done to ensure further updates to the dbuf never sneak in to the
syncing txg.

This could be left to the sa_update() caller.  But then the caller
would need to be aware of this internal SA implementation detail.
It is therefore preferable to handle this all internally in the
SA implementation.

Signed-off-by: Brian Behlendorf <[email protected]>
Closes #503
Closes #513
  • Loading branch information
javenwu authored and behlendorf committed Aug 25, 2012
1 parent f828e63 commit a475873
Showing 1 changed file with 22 additions and 2 deletions.
24 changes: 22 additions & 2 deletions module/zfs/sa.c
Original file line number Diff line number Diff line change
Expand Up @@ -1773,12 +1773,14 @@ sa_bulk_update_impl(sa_handle_t *hdl, sa_bulk_attr_t *bulk, int count,
int error;
sa_os_t *sa = hdl->sa_os->os_sa;
dmu_object_type_t bonustype;

bonustype = SA_BONUSTYPE_FROM_DB(SA_GET_DB(hdl, SA_BONUS));
dmu_buf_t *saved_spill;

ASSERT(hdl);
ASSERT(MUTEX_HELD(&hdl->sa_lock));

bonustype = SA_BONUSTYPE_FROM_DB(SA_GET_DB(hdl, SA_BONUS));
saved_spill = hdl->sa_spill;

/* sync out registration table if necessary */
if (sa->sa_need_attr_registration)
sa_attr_register_sync(hdl, tx);
Expand All @@ -1787,6 +1789,24 @@ sa_bulk_update_impl(sa_handle_t *hdl, sa_bulk_attr_t *bulk, int count,
if (error == 0 && !IS_SA_BONUSTYPE(bonustype) && sa->sa_update_cb)
sa->sa_update_cb(hdl, tx);

/*
* If saved_spill is NULL and current sa_spill is not NULL that
* means we increased the refcount of the spill buffer through
* sa_get_spill() or dmu_spill_hold_by_dnode(). Therefore we
* must release the hold before calling dmu_tx_commit() to avoid
* making a copy of this buffer in dbuf_sync_leaf() due to the
* reference count now being greater than 1.
*/
if (!saved_spill && hdl->sa_spill) {
if (hdl->sa_spill_tab) {
sa_idx_tab_rele(hdl->sa_os, hdl->sa_spill_tab);
hdl->sa_spill_tab = NULL;
}

dmu_buf_rele((dmu_buf_t *)hdl->sa_spill, NULL);
hdl->sa_spill = NULL;
}

return (error);
}

Expand Down

0 comments on commit a475873

Please sign in to comment.