@@ -127,6 +127,45 @@ int __ext4_journal_get_create_access(const char *where, unsigned int line,
return err;
}
+int __ext4_handle_release_buffer(const char *where, handle_t *handle,
+ struct buffer_head *bh)
+{
+ struct super_block *sb;
+ int err = 0;
+
+ if (!ext4_handle_valid(handle))
+ return 0;
+
+ sb = handle->h_transaction->t_journal->j_private;
+ if (!EXT4_SNAPSHOTS(sb) || IS_COWING(handle))
+ goto out;
+
+ /*
+ * Trying to cancel a previous call to get_write_access(), which may
+ * have resulted in a single COW operation. We don't need to add
+ * user credits, but if COW credits are too low we will try to
+ * extend the transaction to compensate for the buffer credits used
+ * by the extra COW operation.
+ */
+ err = ext4_journal_extend(handle, 0);
+ if (err > 0) {
+ /* well, we can't say we didn't try - now lets hope
+ * we have enough buffer credits to spare */
+ snapshot_debug(handle->h_buffer_credits < EXT4_MAX_TRANS_DATA
+ ? 1 : 2,
+ "%s: warning: couldn't extend transaction "
+ "from %s (credits=%d/%d)\n", __func__,
+ where, handle->h_buffer_credits,
+ handle->h_user_credits);
+ err = 0;
+ }
+ ext4_journal_trace(SNAP_WARN, where, handle, -1);
+out:
+ if (!err)
+ jbd2_journal_release_buffer(handle, bh);
+ return err;
+}
+
int __ext4_handle_dirty_metadata(const char *where, unsigned int line,
handle_t *handle, struct inode *inode,
struct buffer_head *bh)
@@ -264,12 +264,11 @@ static inline void ext4_handle_sync(handle_t *handle)
handle->h_sync = 1;
}
-static inline void ext4_handle_release_buffer(handle_t *handle,
- struct buffer_head *bh)
-{
- if (ext4_handle_valid(handle))
- jbd2_journal_release_buffer(handle, bh);
-}
+int __ext4_handle_release_buffer(const char *where, handle_t *handle,
+ struct buffer_head *bh);
+
+#define ext4_handle_release_buffer(handle, bh) \
+ __ext4_handle_release_buffer(__func__, (handle), (bh))
static inline int ext4_handle_is_aborted(handle_t *handle)
{
@@ -916,8 +916,14 @@ repeat_in_this_group:
goto got;
}
/* we lost it */
- ext4_handle_release_buffer(handle, inode_bitmap_bh);
- ext4_handle_release_buffer(handle, group_desc_bh);
+ err = ext4_handle_release_buffer(handle,
+ inode_bitmap_bh);
+ if (err)
+ goto fail;
+ err = ext4_handle_release_buffer(handle,
+ group_desc_bh);
+ if (err)
+ goto fail;
if (++ino < EXT4_INODES_PER_GROUP(sb))
goto repeat_in_this_group;
@@ -735,7 +735,9 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode,
int offset = (char *)s->here - bs->bh->b_data;
unlock_buffer(bs->bh);
- ext4_handle_release_buffer(handle, bs->bh);
+ error = ext4_handle_release_buffer(handle, bs->bh);
+ if (error)
+ goto cleanup;
if (ce) {
mb_cache_entry_release(ce);
ce = NULL;