diff options
author | Javen Wu <[email protected]> | 2012-08-24 19:24:48 -0700 |
---|---|---|
committer | Brian Behlendorf <[email protected]> | 2012-08-25 09:26:10 -0700 |
commit | a47587389e98820ea194fd16cd658491f8e4ee31 (patch) | |
tree | fb7a46a6502b4409ca1281c44e571369cdecc2f2 | |
parent | f828e63a0d408160cff9403f1c8991609f17011d (diff) |
Drop spill buffer reference
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
-rw-r--r-- | module/zfs/sa.c | 24 |
1 files changed, 22 insertions, 2 deletions
diff --git a/module/zfs/sa.c b/module/zfs/sa.c index 32b4c5b08..a26e5ebdd 100644 --- a/module/zfs/sa.c +++ b/module/zfs/sa.c @@ -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); @@ -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); } |