- if (likely(!oti->oti_rollback && oti->oti_declare_ops[op] > 0)) {
- oti->oti_declare_ops[op]--;
- oti->oti_declare_ops_rb[op]++;
- } else {
- /* all future updates are considered rollback */
- oti->oti_rollback = true;
- rb = osd_trans_declare_op2rb[op];
- if (unlikely(rb >= OSD_OT_MAX)) {
- if (unlikely(ldiskfs_track_declares_assert))
- LASSERTF(rb < OSD_OT_MAX, "rb = %u\n", rb);
- else {
- CWARN("%s: Invalid rollback index %d\n",
- osd_name(oti->oti_dev), rb);
- libcfs_debug_dumpstack(NULL);
- return;
- }
- }
- if (unlikely(oti->oti_declare_ops_rb[rb] == 0)) {
- if (unlikely(ldiskfs_track_declares_assert))
- LASSERTF(oti->oti_declare_ops_rb[rb] > 0,
- "rb = %u\n", rb);
- else {
- CWARN("%s: Overflow in tracking declares for "
- "index, rb = %d\n",
- osd_name(oti->oti_dev), rb);
- libcfs_debug_dumpstack(NULL);
- return;
- }
- }
- oti->oti_declare_ops_rb[rb]--;
+ /* find rollback (or reverse) operation for the given one
+ * such an operation doesn't require additional credits
+ * as the same set of blocks are modified */
+ rb = osd_trans_declare_op2rb[op];
+
+ /* check whether credits for this operation were reserved at all */
+ if (unlikely(oti->oti_declare_ops_cred[op] == 0 &&
+ oti->oti_declare_ops_cred[rb] == 0)) {
+ /* the API is not perfect yet: CREATE does REF_ADD internally
+ * while DESTROY does not. To rollback CREATE the callers
+ * needs to call REF_DEL+DESTROY which is hard to detect using
+ * a simple table of rollback operations */
+ if (op == OSD_OT_REF_DEL &&
+ oti->oti_declare_ops_cred[OSD_OT_CREATE] > 0)
+ goto proceed;
+ if (op == OSD_OT_REF_ADD &&
+ oti->oti_declare_ops_cred[OSD_OT_DESTROY] > 0)
+ goto proceed;
+ CWARN("%s: opcode %u: credits = 0, rollback = %u\n",
+ osd_name(osd_dt_dev(oh->ot_super.th_dev)), op, rb);
+ osd_trans_dump_creds(env, th);
+ LASSERT(!ldiskfs_track_declares_assert);
+ }
+
+proceed:
+ /* remember how many credits we have unused before the operation */
+ oti->oti_credits_before = oh->ot_handle->h_buffer_credits;
+ left = oti->oti_declare_ops_cred[op] - oti->oti_declare_ops_used[op];
+ if (unlikely(oti->oti_credits_before < left)) {
+ CWARN("%s: opcode %u: before %u < left %u, rollback = %u\n",
+ osd_name(osd_dt_dev(oh->ot_super.th_dev)), op,
+ oti->oti_credits_before, left, rb);
+ osd_trans_dump_creds(env, th);
+ /* on a very small fs (testing?) it's possible that
+ * the transaction can't fit 1/4 of journal, so we
+ * just request less credits (see osd_trans_start()).
+ * ignore the same case here */
+ rb = osd_transaction_size(osd_dt_dev(th->th_dev));
+ if (unlikely(oh->ot_credits < rb))
+ LASSERT(!ldiskfs_track_declares_assert);