Commit 890879cf authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4

* 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4:
  jbd2: Fix oops in jbd2_journal_remove_journal_head()
  jbd2: Remove obsolete parameters in the comments for some jbd2 functions
  ext4: fixed tracepoints cleanup
  ext4: use FIEMAP_EXTENT_LAST flag for last extent in fiemap
  ext4: Fix max file size and logical block counting of extent format file
  ext4: correct comments for ext4_free_blocks()
parents 56299378 de1b7941
...@@ -125,7 +125,7 @@ struct ext4_ext_path { ...@@ -125,7 +125,7 @@ struct ext4_ext_path {
* positive retcode - signal for ext4_ext_walk_space(), see below * positive retcode - signal for ext4_ext_walk_space(), see below
* callback must return valid extent (passed or newly created) * callback must return valid extent (passed or newly created)
*/ */
typedef int (*ext_prepare_callback)(struct inode *, struct ext4_ext_path *, typedef int (*ext_prepare_callback)(struct inode *, ext4_lblk_t,
struct ext4_ext_cache *, struct ext4_ext_cache *,
struct ext4_extent *, void *); struct ext4_extent *, void *);
...@@ -133,8 +133,11 @@ typedef int (*ext_prepare_callback)(struct inode *, struct ext4_ext_path *, ...@@ -133,8 +133,11 @@ typedef int (*ext_prepare_callback)(struct inode *, struct ext4_ext_path *,
#define EXT_BREAK 1 #define EXT_BREAK 1
#define EXT_REPEAT 2 #define EXT_REPEAT 2
/* Maximum logical block in a file; ext4_extent's ee_block is __le32 */ /*
#define EXT_MAX_BLOCK 0xffffffff * Maximum number of logical blocks in a file; ext4_extent's ee_block is
* __le32.
*/
#define EXT_MAX_BLOCKS 0xffffffff
/* /*
* EXT_INIT_MAX_LEN is the maximum number of blocks we can have in an * EXT_INIT_MAX_LEN is the maximum number of blocks we can have in an
......
...@@ -1408,7 +1408,7 @@ static int ext4_ext_search_right(struct inode *inode, ...@@ -1408,7 +1408,7 @@ static int ext4_ext_search_right(struct inode *inode,
/* /*
* ext4_ext_next_allocated_block: * ext4_ext_next_allocated_block:
* returns allocated block in subsequent extent or EXT_MAX_BLOCK. * returns allocated block in subsequent extent or EXT_MAX_BLOCKS.
* NOTE: it considers block number from index entry as * NOTE: it considers block number from index entry as
* allocated block. Thus, index entries have to be consistent * allocated block. Thus, index entries have to be consistent
* with leaves. * with leaves.
...@@ -1422,7 +1422,7 @@ ext4_ext_next_allocated_block(struct ext4_ext_path *path) ...@@ -1422,7 +1422,7 @@ ext4_ext_next_allocated_block(struct ext4_ext_path *path)
depth = path->p_depth; depth = path->p_depth;
if (depth == 0 && path->p_ext == NULL) if (depth == 0 && path->p_ext == NULL)
return EXT_MAX_BLOCK; return EXT_MAX_BLOCKS;
while (depth >= 0) { while (depth >= 0) {
if (depth == path->p_depth) { if (depth == path->p_depth) {
...@@ -1439,12 +1439,12 @@ ext4_ext_next_allocated_block(struct ext4_ext_path *path) ...@@ -1439,12 +1439,12 @@ ext4_ext_next_allocated_block(struct ext4_ext_path *path)
depth--; depth--;
} }
return EXT_MAX_BLOCK; return EXT_MAX_BLOCKS;
} }
/* /*
* ext4_ext_next_leaf_block: * ext4_ext_next_leaf_block:
* returns first allocated block from next leaf or EXT_MAX_BLOCK * returns first allocated block from next leaf or EXT_MAX_BLOCKS
*/ */
static ext4_lblk_t ext4_ext_next_leaf_block(struct inode *inode, static ext4_lblk_t ext4_ext_next_leaf_block(struct inode *inode,
struct ext4_ext_path *path) struct ext4_ext_path *path)
...@@ -1456,7 +1456,7 @@ static ext4_lblk_t ext4_ext_next_leaf_block(struct inode *inode, ...@@ -1456,7 +1456,7 @@ static ext4_lblk_t ext4_ext_next_leaf_block(struct inode *inode,
/* zero-tree has no leaf blocks at all */ /* zero-tree has no leaf blocks at all */
if (depth == 0) if (depth == 0)
return EXT_MAX_BLOCK; return EXT_MAX_BLOCKS;
/* go to index block */ /* go to index block */
depth--; depth--;
...@@ -1469,7 +1469,7 @@ static ext4_lblk_t ext4_ext_next_leaf_block(struct inode *inode, ...@@ -1469,7 +1469,7 @@ static ext4_lblk_t ext4_ext_next_leaf_block(struct inode *inode,
depth--; depth--;
} }
return EXT_MAX_BLOCK; return EXT_MAX_BLOCKS;
} }
/* /*
...@@ -1677,13 +1677,13 @@ static unsigned int ext4_ext_check_overlap(struct inode *inode, ...@@ -1677,13 +1677,13 @@ static unsigned int ext4_ext_check_overlap(struct inode *inode,
*/ */
if (b2 < b1) { if (b2 < b1) {
b2 = ext4_ext_next_allocated_block(path); b2 = ext4_ext_next_allocated_block(path);
if (b2 == EXT_MAX_BLOCK) if (b2 == EXT_MAX_BLOCKS)
goto out; goto out;
} }
/* check for wrap through zero on extent logical start block*/ /* check for wrap through zero on extent logical start block*/
if (b1 + len1 < b1) { if (b1 + len1 < b1) {
len1 = EXT_MAX_BLOCK - b1; len1 = EXT_MAX_BLOCKS - b1;
newext->ee_len = cpu_to_le16(len1); newext->ee_len = cpu_to_le16(len1);
ret = 1; ret = 1;
} }
...@@ -1767,7 +1767,7 @@ int ext4_ext_insert_extent(handle_t *handle, struct inode *inode, ...@@ -1767,7 +1767,7 @@ int ext4_ext_insert_extent(handle_t *handle, struct inode *inode,
fex = EXT_LAST_EXTENT(eh); fex = EXT_LAST_EXTENT(eh);
next = ext4_ext_next_leaf_block(inode, path); next = ext4_ext_next_leaf_block(inode, path);
if (le32_to_cpu(newext->ee_block) > le32_to_cpu(fex->ee_block) if (le32_to_cpu(newext->ee_block) > le32_to_cpu(fex->ee_block)
&& next != EXT_MAX_BLOCK) { && next != EXT_MAX_BLOCKS) {
ext_debug("next leaf block - %d\n", next); ext_debug("next leaf block - %d\n", next);
BUG_ON(npath != NULL); BUG_ON(npath != NULL);
npath = ext4_ext_find_extent(inode, next, NULL); npath = ext4_ext_find_extent(inode, next, NULL);
...@@ -1887,7 +1887,7 @@ static int ext4_ext_walk_space(struct inode *inode, ext4_lblk_t block, ...@@ -1887,7 +1887,7 @@ static int ext4_ext_walk_space(struct inode *inode, ext4_lblk_t block,
BUG_ON(func == NULL); BUG_ON(func == NULL);
BUG_ON(inode == NULL); BUG_ON(inode == NULL);
while (block < last && block != EXT_MAX_BLOCK) { while (block < last && block != EXT_MAX_BLOCKS) {
num = last - block; num = last - block;
/* find extent for this block */ /* find extent for this block */
down_read(&EXT4_I(inode)->i_data_sem); down_read(&EXT4_I(inode)->i_data_sem);
...@@ -1958,7 +1958,7 @@ static int ext4_ext_walk_space(struct inode *inode, ext4_lblk_t block, ...@@ -1958,7 +1958,7 @@ static int ext4_ext_walk_space(struct inode *inode, ext4_lblk_t block,
err = -EIO; err = -EIO;
break; break;
} }
err = func(inode, path, &cbex, ex, cbdata); err = func(inode, next, &cbex, ex, cbdata);
ext4_ext_drop_refs(path); ext4_ext_drop_refs(path);
if (err < 0) if (err < 0)
...@@ -2020,7 +2020,7 @@ ext4_ext_put_gap_in_cache(struct inode *inode, struct ext4_ext_path *path, ...@@ -2020,7 +2020,7 @@ ext4_ext_put_gap_in_cache(struct inode *inode, struct ext4_ext_path *path,
if (ex == NULL) { if (ex == NULL) {
/* there is no extent yet, so gap is [0;-] */ /* there is no extent yet, so gap is [0;-] */
lblock = 0; lblock = 0;
len = EXT_MAX_BLOCK; len = EXT_MAX_BLOCKS;
ext_debug("cache gap(whole file):"); ext_debug("cache gap(whole file):");
} else if (block < le32_to_cpu(ex->ee_block)) { } else if (block < le32_to_cpu(ex->ee_block)) {
lblock = block; lblock = block;
...@@ -2350,7 +2350,7 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode, ...@@ -2350,7 +2350,7 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode,
* never happen because at least one of the end points * never happen because at least one of the end points
* needs to be on the edge of the extent. * needs to be on the edge of the extent.
*/ */
if (end == EXT_MAX_BLOCK) { if (end == EXT_MAX_BLOCKS - 1) {
ext_debug(" bad truncate %u:%u\n", ext_debug(" bad truncate %u:%u\n",
start, end); start, end);
block = 0; block = 0;
...@@ -2398,7 +2398,7 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode, ...@@ -2398,7 +2398,7 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode,
* If this is a truncate, this condition * If this is a truncate, this condition
* should never happen * should never happen
*/ */
if (end == EXT_MAX_BLOCK) { if (end == EXT_MAX_BLOCKS - 1) {
ext_debug(" bad truncate %u:%u\n", ext_debug(" bad truncate %u:%u\n",
start, end); start, end);
err = -EIO; err = -EIO;
...@@ -2478,7 +2478,7 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode, ...@@ -2478,7 +2478,7 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode,
* we need to remove it from the leaf * we need to remove it from the leaf
*/ */
if (num == 0) { if (num == 0) {
if (end != EXT_MAX_BLOCK) { if (end != EXT_MAX_BLOCKS - 1) {
/* /*
* For hole punching, we need to scoot all the * For hole punching, we need to scoot all the
* extents up when an extent is removed so that * extents up when an extent is removed so that
...@@ -3699,7 +3699,7 @@ void ext4_ext_truncate(struct inode *inode) ...@@ -3699,7 +3699,7 @@ void ext4_ext_truncate(struct inode *inode)
last_block = (inode->i_size + sb->s_blocksize - 1) last_block = (inode->i_size + sb->s_blocksize - 1)
>> EXT4_BLOCK_SIZE_BITS(sb); >> EXT4_BLOCK_SIZE_BITS(sb);
err = ext4_ext_remove_space(inode, last_block, EXT_MAX_BLOCK); err = ext4_ext_remove_space(inode, last_block, EXT_MAX_BLOCKS - 1);
/* In a multi-transaction truncate, we only make the final /* In a multi-transaction truncate, we only make the final
* transaction synchronous. * transaction synchronous.
...@@ -3914,14 +3914,13 @@ int ext4_convert_unwritten_extents(struct inode *inode, loff_t offset, ...@@ -3914,14 +3914,13 @@ int ext4_convert_unwritten_extents(struct inode *inode, loff_t offset,
/* /*
* Callback function called for each extent to gather FIEMAP information. * Callback function called for each extent to gather FIEMAP information.
*/ */
static int ext4_ext_fiemap_cb(struct inode *inode, struct ext4_ext_path *path, static int ext4_ext_fiemap_cb(struct inode *inode, ext4_lblk_t next,
struct ext4_ext_cache *newex, struct ext4_extent *ex, struct ext4_ext_cache *newex, struct ext4_extent *ex,
void *data) void *data)
{ {
__u64 logical; __u64 logical;
__u64 physical; __u64 physical;
__u64 length; __u64 length;
loff_t size;
__u32 flags = 0; __u32 flags = 0;
int ret = 0; int ret = 0;
struct fiemap_extent_info *fieinfo = data; struct fiemap_extent_info *fieinfo = data;
...@@ -4103,8 +4102,7 @@ static int ext4_ext_fiemap_cb(struct inode *inode, struct ext4_ext_path *path, ...@@ -4103,8 +4102,7 @@ static int ext4_ext_fiemap_cb(struct inode *inode, struct ext4_ext_path *path,
if (ex && ext4_ext_is_uninitialized(ex)) if (ex && ext4_ext_is_uninitialized(ex))
flags |= FIEMAP_EXTENT_UNWRITTEN; flags |= FIEMAP_EXTENT_UNWRITTEN;
size = i_size_read(inode); if (next == EXT_MAX_BLOCKS)
if (logical + length >= size)
flags |= FIEMAP_EXTENT_LAST; flags |= FIEMAP_EXTENT_LAST;
ret = fiemap_fill_next_extent(fieinfo, logical, physical, ret = fiemap_fill_next_extent(fieinfo, logical, physical,
...@@ -4347,8 +4345,8 @@ int ext4_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, ...@@ -4347,8 +4345,8 @@ int ext4_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo,
start_blk = start >> inode->i_sb->s_blocksize_bits; start_blk = start >> inode->i_sb->s_blocksize_bits;
last_blk = (start + len - 1) >> inode->i_sb->s_blocksize_bits; last_blk = (start + len - 1) >> inode->i_sb->s_blocksize_bits;
if (last_blk >= EXT_MAX_BLOCK) if (last_blk >= EXT_MAX_BLOCKS)
last_blk = EXT_MAX_BLOCK-1; last_blk = EXT_MAX_BLOCKS-1;
len_blks = ((ext4_lblk_t) last_blk) - start_blk + 1; len_blks = ((ext4_lblk_t) last_blk) - start_blk + 1;
/* /*
......
...@@ -2634,7 +2634,7 @@ static int ext4_writepage(struct page *page, ...@@ -2634,7 +2634,7 @@ static int ext4_writepage(struct page *page,
struct buffer_head *page_bufs = NULL; struct buffer_head *page_bufs = NULL;
struct inode *inode = page->mapping->host; struct inode *inode = page->mapping->host;
trace_ext4_writepage(inode, page); trace_ext4_writepage(page);
size = i_size_read(inode); size = i_size_read(inode);
if (page->index == size >> PAGE_CACHE_SHIFT) if (page->index == size >> PAGE_CACHE_SHIFT)
len = size & ~PAGE_CACHE_MASK; len = size & ~PAGE_CACHE_MASK;
......
...@@ -3578,8 +3578,8 @@ ext4_mb_release_inode_pa(struct ext4_buddy *e4b, struct buffer_head *bitmap_bh, ...@@ -3578,8 +3578,8 @@ ext4_mb_release_inode_pa(struct ext4_buddy *e4b, struct buffer_head *bitmap_bh,
free += next - bit; free += next - bit;
trace_ext4_mballoc_discard(sb, NULL, group, bit, next - bit); trace_ext4_mballoc_discard(sb, NULL, group, bit, next - bit);
trace_ext4_mb_release_inode_pa(sb, pa->pa_inode, pa, trace_ext4_mb_release_inode_pa(pa, grp_blk_start + bit,
grp_blk_start + bit, next - bit); next - bit);
mb_free_blocks(pa->pa_inode, e4b, bit, next - bit); mb_free_blocks(pa->pa_inode, e4b, bit, next - bit);
bit = next + 1; bit = next + 1;
} }
...@@ -3608,7 +3608,7 @@ ext4_mb_release_group_pa(struct ext4_buddy *e4b, ...@@ -3608,7 +3608,7 @@ ext4_mb_release_group_pa(struct ext4_buddy *e4b,
ext4_group_t group; ext4_group_t group;
ext4_grpblk_t bit; ext4_grpblk_t bit;
trace_ext4_mb_release_group_pa(sb, pa); trace_ext4_mb_release_group_pa(pa);
BUG_ON(pa->pa_deleted == 0); BUG_ON(pa->pa_deleted == 0);
ext4_get_group_no_and_offset(sb, pa->pa_pstart, &group, &bit); ext4_get_group_no_and_offset(sb, pa->pa_pstart, &group, &bit);
BUG_ON(group != e4b->bd_group && pa->pa_len != 0); BUG_ON(group != e4b->bd_group && pa->pa_len != 0);
...@@ -4448,7 +4448,7 @@ ext4_mb_free_metadata(handle_t *handle, struct ext4_buddy *e4b, ...@@ -4448,7 +4448,7 @@ ext4_mb_free_metadata(handle_t *handle, struct ext4_buddy *e4b,
* @inode: inode * @inode: inode
* @block: start physical block to free * @block: start physical block to free
* @count: number of blocks to count * @count: number of blocks to count
* @metadata: Are these metadata blocks * @flags: flags used by ext4_free_blocks
*/ */
void ext4_free_blocks(handle_t *handle, struct inode *inode, void ext4_free_blocks(handle_t *handle, struct inode *inode,
struct buffer_head *bh, ext4_fsblk_t block, struct buffer_head *bh, ext4_fsblk_t block,
......
...@@ -1002,12 +1002,12 @@ mext_check_arguments(struct inode *orig_inode, ...@@ -1002,12 +1002,12 @@ mext_check_arguments(struct inode *orig_inode,
return -EINVAL; return -EINVAL;
} }
if ((orig_start > EXT_MAX_BLOCK) || if ((orig_start >= EXT_MAX_BLOCKS) ||
(donor_start > EXT_MAX_BLOCK) || (donor_start >= EXT_MAX_BLOCKS) ||
(*len > EXT_MAX_BLOCK) || (*len > EXT_MAX_BLOCKS) ||
(orig_start + *len > EXT_MAX_BLOCK)) { (orig_start + *len >= EXT_MAX_BLOCKS)) {
ext4_debug("ext4 move extent: Can't handle over [%u] blocks " ext4_debug("ext4 move extent: Can't handle over [%u] blocks "
"[ino:orig %lu, donor %lu]\n", EXT_MAX_BLOCK, "[ino:orig %lu, donor %lu]\n", EXT_MAX_BLOCKS,
orig_inode->i_ino, donor_inode->i_ino); orig_inode->i_ino, donor_inode->i_ino);
return -EINVAL; return -EINVAL;
} }
......
...@@ -2243,6 +2243,12 @@ static void ext4_orphan_cleanup(struct super_block *sb, ...@@ -2243,6 +2243,12 @@ static void ext4_orphan_cleanup(struct super_block *sb,
* in the vfs. ext4 inode has 48 bits of i_block in fsblock units, * in the vfs. ext4 inode has 48 bits of i_block in fsblock units,
* so that won't be a limiting factor. * so that won't be a limiting factor.
* *
* However there is other limiting factor. We do store extents in the form
* of starting block and length, hence the resulting length of the extent
* covering maximum file size must fit into on-disk format containers as
* well. Given that length is always by 1 unit bigger than max unit (because
* we count 0 as well) we have to lower the s_maxbytes by one fs block.
*
* Note, this does *not* consider any metadata overhead for vfs i_blocks. * Note, this does *not* consider any metadata overhead for vfs i_blocks.
*/ */
static loff_t ext4_max_size(int blkbits, int has_huge_files) static loff_t ext4_max_size(int blkbits, int has_huge_files)
...@@ -2264,10 +2270,13 @@ static loff_t ext4_max_size(int blkbits, int has_huge_files) ...@@ -2264,10 +2270,13 @@ static loff_t ext4_max_size(int blkbits, int has_huge_files)
upper_limit <<= blkbits; upper_limit <<= blkbits;
} }
/* 32-bit extent-start container, ee_block */ /*
res = 1LL << 32; * 32-bit extent-start container, ee_block. We lower the maxbytes
* by one fs block, so ee_len can cover the extent of maximum file
* size
*/
res = (1LL << 32) - 1;
res <<= blkbits; res <<= blkbits;
res -= 1;
/* Sanity check against vm- & vfs- imposed limits */ /* Sanity check against vm- & vfs- imposed limits */
if (res > upper_limit) if (res > upper_limit)
......
...@@ -97,10 +97,14 @@ static int __try_to_free_cp_buf(struct journal_head *jh) ...@@ -97,10 +97,14 @@ static int __try_to_free_cp_buf(struct journal_head *jh)
if (jh->b_jlist == BJ_None && !buffer_locked(bh) && if (jh->b_jlist == BJ_None && !buffer_locked(bh) &&
!buffer_dirty(bh) && !buffer_write_io_error(bh)) { !buffer_dirty(bh) && !buffer_write_io_error(bh)) {
/*
* Get our reference so that bh cannot be freed before
* we unlock it
*/
get_bh(bh);
JBUFFER_TRACE(jh, "remove from checkpoint list"); JBUFFER_TRACE(jh, "remove from checkpoint list");
ret = __jbd2_journal_remove_checkpoint(jh) + 1; ret = __jbd2_journal_remove_checkpoint(jh) + 1;
jbd_unlock_bh_state(bh); jbd_unlock_bh_state(bh);
jbd2_journal_remove_journal_head(bh);
BUFFER_TRACE(bh, "release"); BUFFER_TRACE(bh, "release");
__brelse(bh); __brelse(bh);
} else { } else {
...@@ -223,8 +227,8 @@ static int __wait_cp_io(journal_t *journal, transaction_t *transaction) ...@@ -223,8 +227,8 @@ static int __wait_cp_io(journal_t *journal, transaction_t *transaction)
spin_lock(&journal->j_list_lock); spin_lock(&journal->j_list_lock);
goto restart; goto restart;
} }
get_bh(bh);
if (buffer_locked(bh)) { if (buffer_locked(bh)) {
atomic_inc(&bh->b_count);
spin_unlock(&journal->j_list_lock); spin_unlock(&journal->j_list_lock);
jbd_unlock_bh_state(bh); jbd_unlock_bh_state(bh);
wait_on_buffer(bh); wait_on_buffer(bh);
...@@ -243,7 +247,6 @@ static int __wait_cp_io(journal_t *journal, transaction_t *transaction) ...@@ -243,7 +247,6 @@ static int __wait_cp_io(journal_t *journal, transaction_t *transaction)
*/ */
released = __jbd2_journal_remove_checkpoint(jh); released = __jbd2_journal_remove_checkpoint(jh);
jbd_unlock_bh_state(bh); jbd_unlock_bh_state(bh);
jbd2_journal_remove_journal_head(bh);
__brelse(bh); __brelse(bh);
} }
...@@ -284,7 +287,7 @@ static int __process_buffer(journal_t *journal, struct journal_head *jh, ...@@ -284,7 +287,7 @@ static int __process_buffer(journal_t *journal, struct journal_head *jh,
int ret = 0; int ret = 0;
if (buffer_locked(bh)) { if (buffer_locked(bh)) {
atomic_inc(&bh->b_count); get_bh(bh);
spin_unlock(&journal->j_list_lock); spin_unlock(&journal->j_list_lock);
jbd_unlock_bh_state(bh); jbd_unlock_bh_state(bh);
wait_on_buffer(bh); wait_on_buffer(bh);
...@@ -316,12 +319,12 @@ static int __process_buffer(journal_t *journal, struct journal_head *jh, ...@@ -316,12 +319,12 @@ static int __process_buffer(journal_t *journal, struct journal_head *jh,
ret = 1; ret = 1;
if (unlikely(buffer_write_io_error(bh))) if (unlikely(buffer_write_io_error(bh)))
ret = -EIO; ret = -EIO;
get_bh(bh);
J_ASSERT_JH(jh, !buffer_jbddirty(bh)); J_ASSERT_JH(jh, !buffer_jbddirty(bh));
BUFFER_TRACE(bh, "remove from checkpoint"); BUFFER_TRACE(bh, "remove from checkpoint");
__jbd2_journal_remove_checkpoint(jh); __jbd2_journal_remove_checkpoint(jh);
spin_unlock(&journal->j_list_lock); spin_unlock(&journal->j_list_lock);
jbd_unlock_bh_state(bh); jbd_unlock_bh_state(bh);
jbd2_journal_remove_journal_head(bh);
__brelse(bh); __brelse(bh);
} else { } else {
/* /*
...@@ -554,7 +557,8 @@ int jbd2_cleanup_journal_tail(journal_t *journal) ...@@ -554,7 +557,8 @@ int jbd2_cleanup_journal_tail(journal_t *journal)
/* /*
* journal_clean_one_cp_list * journal_clean_one_cp_list
* *
* Find all the written-back checkpoint buffers in the given list and release them. * Find all the written-back checkpoint buffers in the given list and
* release them.
* *
* Called with the journal locked. * Called with the journal locked.
* Called with j_list_lock held. * Called with j_list_lock held.
...@@ -663,8 +667,8 @@ int __jbd2_journal_clean_checkpoint_list(journal_t *journal) ...@@ -663,8 +667,8 @@ int __jbd2_journal_clean_checkpoint_list(journal_t *journal)
* checkpoint lists. * checkpoint lists.
* *
* The function returns 1 if it frees the transaction, 0 otherwise. * The function returns 1 if it frees the transaction, 0 otherwise.
* The function can free jh and bh.
* *
* This function is called with the journal locked.
* This function is called with j_list_lock held. * This function is called with j_list_lock held.
* This function is called with jbd_lock_bh_state(jh2bh(jh)) * This function is called with jbd_lock_bh_state(jh2bh(jh))
*/ */
...@@ -684,13 +688,14 @@ int __jbd2_journal_remove_checkpoint(struct journal_head *jh) ...@@ -684,13 +688,14 @@ int __jbd2_journal_remove_checkpoint(struct journal_head *jh)
} }
journal = transaction->t_journal; journal = transaction->t_journal;
JBUFFER_TRACE(jh, "removing from transaction");
__buffer_unlink(jh); __buffer_unlink(jh);
jh->b_cp_transaction = NULL; jh->b_cp_transaction = NULL;
jbd2_journal_put_journal_head(jh);
if (transaction->t_checkpoint_list != NULL || if (transaction->t_checkpoint_list != NULL ||
transaction->t_checkpoint_io_list != NULL) transaction->t_checkpoint_io_list != NULL)
goto out; goto out;
JBUFFER_TRACE(jh, "transaction has no more buffers");
/* /*
* There is one special case to worry about: if we have just pulled the * There is one special case to worry about: if we have just pulled the
...@@ -701,10 +706,8 @@ int __jbd2_journal_remove_checkpoint(struct journal_head *jh) ...@@ -701,10 +706,8 @@ int __jbd2_journal_remove_checkpoint(struct journal_head *jh)
* The locking here around t_state is a bit sleazy. * The locking here around t_state is a bit sleazy.
* See the comment at the end of jbd2_journal_commit_transaction(). * See the comment at the end of jbd2_journal_commit_transaction().
*/ */
if (transaction->t_state != T_FINISHED) { if (transaction->t_state != T_FINISHED)
JBUFFER_TRACE(jh, "belongs to running/committing transaction");
goto out; goto out;
}
/* OK, that was the last buffer for the transaction: we can now /* OK, that was the last buffer for the transaction: we can now
safely remove this transaction from the log */ safely remove this transaction from the log */
...@@ -723,7 +726,6 @@ int __jbd2_journal_remove_checkpoint(struct journal_head *jh) ...@@ -723,7 +726,6 @@ int __jbd2_journal_remove_checkpoint(struct journal_head *jh)
wake_up(&journal->j_wait_logspace); wake_up(&journal->j_wait_logspace);
ret = 1; ret = 1;
out: out:
JBUFFER_TRACE(jh, "exit");
return ret; return ret;
} }
...@@ -742,6 +744,8 @@ void __jbd2_journal_insert_checkpoint(struct journal_head *jh, ...@@ -742,6 +744,8 @@ void __jbd2_journal_insert_checkpoint(struct journal_head *jh,
J_ASSERT_JH(jh, buffer_dirty(jh2bh(jh)) || buffer_jbddirty(jh2bh(jh))); J_ASSERT_JH(jh, buffer_dirty(jh2bh(jh)) || buffer_jbddirty(jh2bh(jh)));
J_ASSERT_JH(jh, jh->b_cp_transaction == NULL); J_ASSERT_JH(jh, jh->b_cp_transaction == NULL);
/* Get reference for checkpointing transaction */
jbd2_journal_grab_journal_head(jh2bh(jh));
jh->b_cp_transaction = transaction; jh->b_cp_transaction = transaction;
if (!transaction->t_checkpoint_list) { if (!transaction->t_checkpoint_list) {
......
...@@ -848,10 +848,16 @@ void jbd2_journal_commit_transaction(journal_t *journal) ...@@ -848,10 +848,16 @@ void jbd2_journal_commit_transaction(journal_t *journal)
while (commit_transaction->t_forget) { while (commit_transaction->t_forget) {
transaction_t *cp_transaction; transaction_t *cp_transaction;
struct buffer_head *bh; struct buffer_head *bh;
int try_to_free = 0;
jh = commit_transaction->t_forget; jh = commit_transaction->t_forget;
spin_unlock(&journal->j_list_lock); spin_unlock(&journal->j_list_lock);
bh = jh2bh(jh); bh = jh2bh(jh);
/*
* Get a reference so that bh cannot be freed before we are
* done with it.
*/
get_bh(bh);
jbd_lock_bh_state(bh); jbd_lock_bh_state(bh);
J_ASSERT_JH(jh, jh->b_transaction == commit_transaction); J_ASSERT_JH(jh, jh->b_transaction == commit_transaction);
...@@ -914,28 +920,27 @@ void jbd2_journal_commit_transaction(journal_t *journal) ...@@ -914,28 +920,27 @@ void jbd2_journal_commit_transaction(journal_t *journal)
__jbd2_journal_insert_checkpoint(jh, commit_transaction); __jbd2_journal_insert_checkpoint(jh, commit_transaction);
if (is_journal_aborted(journal)) if (is_journal_aborted(journal))
clear_buffer_jbddirty(bh); clear_buffer_jbddirty(bh);
JBUFFER_TRACE(jh, "refile for checkpoint writeback");
__jbd2_journal_refile_buffer(jh);
jbd_unlock_bh_state(bh);
} else { } else {
J_ASSERT_BH(bh, !buffer_dirty(bh)); J_ASSERT_BH(bh, !buffer_dirty(bh));
/* The buffer on BJ_Forget list and not jbddirty means /*
* The buffer on BJ_Forget list and not jbddirty means
* it has been freed by this transaction and hence it * it has been freed by this transaction and hence it
* could not have been reallocated until this * could not have been reallocated until this
* transaction has committed. *BUT* it could be * transaction has committed. *BUT* it could be
* reallocated once we have written all the data to * reallocated once we have written all the data to
* disk and before we process the buffer on BJ_Forget * disk and before we process the buffer on BJ_Forget
* list. */ * list.
JBUFFER_TRACE(jh, "refile or unfile freed buffer"); */
if (!jh->b_next_transaction)
try_to_free = 1;
}
JBUFFER_TRACE(jh, "refile or unfile buffer");
__jbd2_journal_refile_buffer(jh); __jbd2_journal_refile_buffer(jh);
if (!jh->b_transaction) {
jbd_unlock_bh_state(bh);
/* needs a brelse */
jbd2_journal_remove_journal_head(bh);
release_buffer_page(bh);
} else
jbd_unlock_bh_state(bh); jbd_unlock_bh_state(bh);
} if (try_to_free)
release_buffer_page(bh); /* Drops bh reference */
else
__brelse(bh);
cond_resched_lock(&journal->j_list_lock); cond_resched_lock(&journal->j_list_lock);
} }
spin_unlock(&journal->j_list_lock); spin_unlock(&journal->j_list_lock);
......
...@@ -2078,10 +2078,9 @@ static void journal_free_journal_head(struct journal_head *jh) ...@@ -2078,10 +2078,9 @@ static void journal_free_journal_head(struct journal_head *jh)
* When a buffer has its BH_JBD bit set it is immune from being released by * When a buffer has its BH_JBD bit set it is immune from being released by
* core kernel code, mainly via ->b_count. * core kernel code, mainly via ->b_count.
* *
* A journal_head may be detached from its buffer_head when the journal_head's * A journal_head is detached from its buffer_head when the journal_head's
* b_transaction, b_cp_transaction and b_next_transaction pointers are NULL. * b_jcount reaches zero. Running transaction (b_transaction) and checkpoint
* Various places in JBD call jbd2_journal_remove_journal_head() to indicate that the * transaction (b_cp_transaction) hold their references to b_jcount.
* journal_head can be dropped if needed.
* *
* Various places in the kernel want to attach a journal_head to a buffer_head * Various places in the kernel want to attach a journal_head to a buffer_head
* _before_ attaching the journal_head to a transaction. To protect the * _before_ attaching the journal_head to a transaction. To protect the
...@@ -2094,17 +2093,16 @@ static void journal_free_journal_head(struct journal_head *jh) ...@@ -2094,17 +2093,16 @@ static void journal_free_journal_head(struct journal_head *jh)
* (Attach a journal_head if needed. Increments b_jcount) * (Attach a journal_head if needed. Increments b_jcount)
* struct journal_head *jh = jbd2_journal_add_journal_head(bh); * struct journal_head *jh = jbd2_journal_add_journal_head(bh);
* ... * ...
* (Get another reference for transaction)
* jbd2_journal_grab_journal_head(bh);
* jh->b_transaction = xxx; * jh->b_transaction = xxx;
* (Put original reference)
* jbd2_journal_put_journal_head(jh); * jbd2_journal_put_journal_head(jh);
*
* Now, the journal_head's b_jcount is zero, but it is safe from being released
* because it has a non-zero b_transaction.
*/ */
/* /*
* Give a buffer_head a journal_head. * Give a buffer_head a journal_head.
* *
* Doesn't need the journal lock.
* May sleep. * May sleep.
*/ */
struct journal_head *jbd2_journal_add_journal_head(struct buffer_head *bh) struct journal_head *jbd2_journal_add_journal_head(struct buffer_head *bh)
...@@ -2168,61 +2166,29 @@ static void __journal_remove_journal_head(struct buffer_head *bh) ...@@ -2168,61 +2166,29 @@ static void __journal_remove_journal_head(struct buffer_head *bh)
struct journal_head *jh = bh2jh(bh); struct journal_head *jh = bh2jh(bh);
J_ASSERT_JH(jh, jh->b_jcount >= 0); J_ASSERT_JH(jh, jh->b_jcount >= 0);
J_ASSERT_JH(jh, jh->b_transaction == NULL);
get_bh(bh); J_ASSERT_JH(jh, jh->b_next_transaction == NULL);
if (jh->b_jcount == 0) { J_ASSERT_JH(jh, jh->b_cp_transaction == NULL);
if (jh->b_transaction == NULL &&
jh->b_next_transaction == NULL &&
jh->b_cp_transaction == NULL) {
J_ASSERT_JH(jh, jh->b_jlist == BJ_None); J_ASSERT_JH(jh, jh->b_jlist == BJ_None);
J_ASSERT_BH(bh, buffer_jbd(bh)); J_ASSERT_BH(bh, buffer_jbd(bh));
J_ASSERT_BH(bh, jh2bh(jh) == bh); J_ASSERT_BH(bh, jh2bh(jh) == bh);
BUFFER_TRACE(bh, "remove journal_head"); BUFFER_TRACE(bh, "remove journal_head");
if (jh->b_frozen_data) { if (jh->b_frozen_data) {
printk(KERN_WARNING "%s: freeing " printk(KERN_WARNING "%s: freeing b_frozen_data\n", __func__);
"b_frozen_data\n",
__func__);
jbd2_free(jh->b_frozen_data, bh->b_size); jbd2_free(jh->b_frozen_data, bh->b_size);
} }
if (jh->b_committed_data) { if (jh->b_committed_data) {
printk(KERN_WARNING "%s: freeing " printk(KERN_WARNING "%s: freeing b_committed_data\n", __func__);
"b_committed_data\n",
__func__);
jbd2_free(jh->b_committed_data, bh->b_size); jbd2_free(jh->b_committed_data, bh->b_size);
} }
bh->b_private = NULL; bh->b_private = NULL;
jh->b_bh = NULL; /* debug, really */ jh->b_bh = NULL; /* debug, really */
clear_buffer_jbd(bh); clear_buffer_jbd(bh);
__brelse(bh);
journal_free_journal_head(jh); journal_free_journal_head(jh);
} else {
BUFFER_TRACE(bh, "journal_head was locked");
}
}
} }
/* /*
* jbd2_journal_remove_journal_head(): if the buffer isn't attached to a transaction * Drop a reference on the passed journal_head. If it fell to zero then
* and has a zero b_jcount then remove and release its journal_head. If we did
* see that the buffer is not used by any transaction we also "logically"
* decrement ->b_count.
*
* We in fact take an additional increment on ->b_count as a convenience,
* because the caller usually wants to do additional things with the bh
* after calling here.
* The caller of jbd2_journal_remove_journal_head() *must* run __brelse(bh) at some
* time. Once the caller has run __brelse(), the buffer is eligible for
* reaping by try_to_free_buffers().
*/
void jbd2_journal_remove_journal_head(struct buffer_head *bh)
{
jbd_lock_bh_journal_head(bh);
__journal_remove_journal_head(bh);
jbd_unlock_bh_journal_head(bh);
}
/*
* Drop a reference on the passed journal_head. If it fell to zero then try to
* release the journal_head from the buffer_head. * release the journal_head from the buffer_head.
*/ */
void jbd2_journal_put_journal_head(struct journal_head *jh) void jbd2_journal_put_journal_head(struct journal_head *jh)
...@@ -2232,10 +2198,11 @@ void jbd2_journal_put_journal_head(struct journal_head *jh) ...@@ -2232,10 +2198,11 @@ void jbd2_journal_put_journal_head(struct journal_head *jh)
jbd_lock_bh_journal_head(bh); jbd_lock_bh_journal_head(bh);
J_ASSERT_JH(jh, jh->b_jcount > 0); J_ASSERT_JH(jh, jh->b_jcount > 0);
--jh->b_jcount; --jh->b_jcount;
if (!jh->b_jcount && !jh->b_transaction) { if (!jh->b_jcount) {
__journal_remove_journal_head(bh); __journal_remove_journal_head(bh);
jbd_unlock_bh_journal_head(bh);
__brelse(bh); __brelse(bh);
} } else
jbd_unlock_bh_journal_head(bh); jbd_unlock_bh_journal_head(bh);
} }
......
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
#include <linux/module.h> #include <linux/module.h>
static void __jbd2_journal_temp_unlink_buffer(struct journal_head *jh); static void __jbd2_journal_temp_unlink_buffer(struct journal_head *jh);
static void __jbd2_journal_unfile_buffer(struct journal_head *jh);
/* /*
* jbd2_get_transaction: obtain a new transaction_t object. * jbd2_get_transaction: obtain a new transaction_t object.
...@@ -764,7 +765,6 @@ do_get_write_access(handle_t *handle, struct journal_head *jh, ...@@ -764,7 +765,6 @@ do_get_write_access(handle_t *handle, struct journal_head *jh,
if (!jh->b_transaction) { if (!jh->b_transaction) {
JBUFFER_TRACE(jh, "no transaction"); JBUFFER_TRACE(jh, "no transaction");
J_ASSERT_JH(jh, !jh->b_next_transaction); J_ASSERT_JH(jh, !jh->b_next_transaction);
jh->b_transaction = transaction;
JBUFFER_TRACE(jh, "file as BJ_Reserved"); JBUFFER_TRACE(jh, "file as BJ_Reserved");
spin_lock(&journal->j_list_lock); spin_lock(&journal->j_list_lock);
__jbd2_journal_file_buffer(jh, transaction, BJ_Reserved); __jbd2_journal_file_buffer(jh, transaction, BJ_Reserved);
...@@ -814,7 +814,6 @@ do_get_write_access(handle_t *handle, struct journal_head *jh, ...@@ -814,7 +814,6 @@ do_get_write_access(handle_t *handle, struct journal_head *jh,
* int jbd2_journal_get_write_access() - notify intent to modify a buffer for metadata (not data) update. * int jbd2_journal_get_write_access() - notify intent to modify a buffer for metadata (not data) update.
* @handle: transaction to add buffer modifications to * @handle: transaction to add buffer modifications to
* @bh: bh to be used for metadata writes * @bh: bh to be used for metadata writes
* @credits: variable that will receive credits for the buffer
* *
* Returns an error code or 0 on success. * Returns an error code or 0 on success.
* *
...@@ -896,8 +895,6 @@ int jbd2_journal_get_create_access(handle_t *handle, struct buffer_head *bh) ...@@ -896,8 +895,6 @@ int jbd2_journal_get_create_access(handle_t *handle, struct buffer_head *bh)
* committed and so it's safe to clear the dirty bit. * committed and so it's safe to clear the dirty bit.
*/ */
clear_buffer_dirty(jh2bh(jh)); clear_buffer_dirty(jh2bh(jh));
jh->b_transaction = transaction;
/* first access by this transaction */ /* first access by this transaction */
jh->b_modified = 0; jh->b_modified = 0;
...@@ -932,7 +929,6 @@ int jbd2_journal_get_create_access(handle_t *handle, struct buffer_head *bh) ...@@ -932,7 +929,6 @@ int jbd2_journal_get_create_access(handle_t *handle, struct buffer_head *bh)
* non-rewindable consequences * non-rewindable consequences
* @handle: transaction * @handle: transaction
* @bh: buffer to undo * @bh: buffer to undo
* @credits: store the number of taken credits here (if not NULL)
* *
* Sometimes there is a need to distinguish between metadata which has * Sometimes there is a need to distinguish between metadata which has
* been committed to disk and that which has not. The ext3fs code uses * been committed to disk and that which has not. The ext3fs code uses
...@@ -1232,8 +1228,6 @@ int jbd2_journal_forget (handle_t *handle, struct buffer_head *bh) ...@@ -1232,8 +1228,6 @@ int jbd2_journal_forget (handle_t *handle, struct buffer_head *bh)
__jbd2_journal_file_buffer(jh, transaction, BJ_Forget); __jbd2_journal_file_buffer(jh, transaction, BJ_Forget);
} else { } else {
__jbd2_journal_unfile_buffer(jh); __jbd2_journal_unfile_buffer(jh);
jbd2_journal_remove_journal_head(bh);
__brelse(bh);
if (!buffer_jbd(bh)) { if (!buffer_jbd(bh)) {
spin_unlock(&journal->j_list_lock); spin_unlock(&journal->j_list_lock);
jbd_unlock_bh_state(bh); jbd_unlock_bh_state(bh);
...@@ -1556,19 +1550,32 @@ void __jbd2_journal_temp_unlink_buffer(struct journal_head *jh) ...@@ -1556,19 +1550,32 @@ void __jbd2_journal_temp_unlink_buffer(struct journal_head *jh)
mark_buffer_dirty(bh); /* Expose it to the VM */ mark_buffer_dirty(bh); /* Expose it to the VM */
} }
void __jbd2_journal_unfile_buffer(struct journal_head *jh) /*
* Remove buffer from all transactions.
*
* Called with bh_state lock and j_list_lock
*
* jh and bh may be already freed when this function returns.
*/
static void __jbd2_journal_unfile_buffer(struct journal_head *jh)
{ {
__jbd2_journal_temp_unlink_buffer(jh); __jbd2_journal_temp_unlink_buffer(jh);
jh->b_transaction = NULL; jh->b_transaction = NULL;
jbd2_journal_put_journal_head(jh);
} }
void jbd2_journal_unfile_buffer(journal_t *journal, struct journal_head *jh) void jbd2_journal_unfile_buffer(journal_t *journal, struct journal_head *jh)
{ {
jbd_lock_bh_state(jh2bh(jh)); struct buffer_head *bh = jh2bh(jh);
/* Get reference so that buffer cannot be freed before we unlock it */
get_bh(bh);
jbd_lock_bh_state(bh);
spin_lock(&journal->j_list_lock); spin_lock(&journal->j_list_lock);
__jbd2_journal_unfile_buffer(jh); __jbd2_journal_unfile_buffer(jh);
spin_unlock(&journal->j_list_lock); spin_unlock(&journal->j_list_lock);
jbd_unlock_bh_state(jh2bh(jh)); jbd_unlock_bh_state(bh);
__brelse(bh);
} }
/* /*
...@@ -1595,8 +1602,6 @@ __journal_try_to_free_buffer(journal_t *journal, struct buffer_head *bh) ...@@ -1595,8 +1602,6 @@ __journal_try_to_free_buffer(journal_t *journal, struct buffer_head *bh)
if (jh->b_jlist == BJ_None) { if (jh->b_jlist == BJ_None) {
JBUFFER_TRACE(jh, "remove from checkpoint list"); JBUFFER_TRACE(jh, "remove from checkpoint list");
__jbd2_journal_remove_checkpoint(jh); __jbd2_journal_remove_checkpoint(jh);
jbd2_journal_remove_journal_head(bh);
__brelse(bh);
} }
} }
spin_unlock(&journal->j_list_lock); spin_unlock(&journal->j_list_lock);
...@@ -1659,7 +1664,6 @@ int jbd2_journal_try_to_free_buffers(journal_t *journal, ...@@ -1659,7 +1664,6 @@ int jbd2_journal_try_to_free_buffers(journal_t *journal,
/* /*
* We take our own ref against the journal_head here to avoid * We take our own ref against the journal_head here to avoid
* having to add tons of locking around each instance of * having to add tons of locking around each instance of
* jbd2_journal_remove_journal_head() and
* jbd2_journal_put_journal_head(). * jbd2_journal_put_journal_head().
*/ */
jh = jbd2_journal_grab_journal_head(bh); jh = jbd2_journal_grab_journal_head(bh);
...@@ -1697,10 +1701,9 @@ static int __dispose_buffer(struct journal_head *jh, transaction_t *transaction) ...@@ -1697,10 +1701,9 @@ static int __dispose_buffer(struct journal_head *jh, transaction_t *transaction)
int may_free = 1; int may_free = 1;
struct buffer_head *bh = jh2bh(jh); struct buffer_head *bh = jh2bh(jh);
__jbd2_journal_unfile_buffer(jh);
if (jh->b_cp_transaction) { if (jh->b_cp_transaction) {
JBUFFER_TRACE(jh, "on running+cp transaction"); JBUFFER_TRACE(jh, "on running+cp transaction");
__jbd2_journal_temp_unlink_buffer(jh);
/* /*
* We don't want to write the buffer anymore, clear the * We don't want to write the buffer anymore, clear the
* bit so that we don't confuse checks in * bit so that we don't confuse checks in
...@@ -1711,8 +1714,7 @@ static int __dispose_buffer(struct journal_head *jh, transaction_t *transaction) ...@@ -1711,8 +1714,7 @@ static int __dispose_buffer(struct journal_head *jh, transaction_t *transaction)
may_free = 0; may_free = 0;
} else { } else {
JBUFFER_TRACE(jh, "on running transaction"); JBUFFER_TRACE(jh, "on running transaction");
jbd2_journal_remove_journal_head(bh); __jbd2_journal_unfile_buffer(jh);
__brelse(bh);
} }
return may_free; return may_free;
} }
...@@ -1990,6 +1992,8 @@ void __jbd2_journal_file_buffer(struct journal_head *jh, ...@@ -1990,6 +1992,8 @@ void __jbd2_journal_file_buffer(struct journal_head *jh,
if (jh->b_transaction) if (jh->b_transaction)
__jbd2_journal_temp_unlink_buffer(jh); __jbd2_journal_temp_unlink_buffer(jh);
else
jbd2_journal_grab_journal_head(bh);
jh->b_transaction = transaction; jh->b_transaction = transaction;
switch (jlist) { switch (jlist) {
...@@ -2041,9 +2045,10 @@ void jbd2_journal_file_buffer(struct journal_head *jh, ...@@ -2041,9 +2045,10 @@ void jbd2_journal_file_buffer(struct journal_head *jh,
* already started to be used by a subsequent transaction, refile the * already started to be used by a subsequent transaction, refile the
* buffer on that transaction's metadata list. * buffer on that transaction's metadata list.
* *
* Called under journal->j_list_lock * Called under j_list_lock
*
* Called under jbd_lock_bh_state(jh2bh(jh)) * Called under jbd_lock_bh_state(jh2bh(jh))
*
* jh and bh may be already free when this function returns
*/ */
void __jbd2_journal_refile_buffer(struct journal_head *jh) void __jbd2_journal_refile_buffer(struct journal_head *jh)
{ {
...@@ -2067,6 +2072,11 @@ void __jbd2_journal_refile_buffer(struct journal_head *jh) ...@@ -2067,6 +2072,11 @@ void __jbd2_journal_refile_buffer(struct journal_head *jh)
was_dirty = test_clear_buffer_jbddirty(bh); was_dirty = test_clear_buffer_jbddirty(bh);
__jbd2_journal_temp_unlink_buffer(jh); __jbd2_journal_temp_unlink_buffer(jh);
/*
* We set b_transaction here because b_next_transaction will inherit
* our jh reference and thus __jbd2_journal_file_buffer() must not
* take a new one.
*/
jh->b_transaction = jh->b_next_transaction; jh->b_transaction = jh->b_next_transaction;
jh->b_next_transaction = NULL; jh->b_next_transaction = NULL;
if (buffer_freed(bh)) if (buffer_freed(bh))
...@@ -2083,30 +2093,21 @@ void __jbd2_journal_refile_buffer(struct journal_head *jh) ...@@ -2083,30 +2093,21 @@ void __jbd2_journal_refile_buffer(struct journal_head *jh)
} }
/* /*
* For the unlocked version of this call, also make sure that any * __jbd2_journal_refile_buffer() with necessary locking added. We take our
* hanging journal_head is cleaned up if necessary. * bh reference so that we can safely unlock bh.
* *
* __jbd2_journal_refile_buffer is usually called as part of a single locked * The jh and bh may be freed by this call.
* operation on a buffer_head, in which the caller is probably going to
* be hooking the journal_head onto other lists. In that case it is up
* to the caller to remove the journal_head if necessary. For the
* unlocked jbd2_journal_refile_buffer call, the caller isn't going to be
* doing anything else to the buffer so we need to do the cleanup
* ourselves to avoid a jh leak.
*
* *** The journal_head may be freed by this call! ***
*/ */
void jbd2_journal_refile_buffer(journal_t *journal, struct journal_head *jh) void jbd2_journal_refile_buffer(journal_t *journal, struct journal_head *jh)
{ {
struct buffer_head *bh = jh2bh(jh); struct buffer_head *bh = jh2bh(jh);
/* Get reference so that buffer cannot be freed before we unlock it */
get_bh(bh);
jbd_lock_bh_state(bh); jbd_lock_bh_state(bh);
spin_lock(&journal->j_list_lock); spin_lock(&journal->j_list_lock);
__jbd2_journal_refile_buffer(jh); __jbd2_journal_refile_buffer(jh);
jbd_unlock_bh_state(bh); jbd_unlock_bh_state(bh);
jbd2_journal_remove_journal_head(bh);
spin_unlock(&journal->j_list_lock); spin_unlock(&journal->j_list_lock);
__brelse(bh); __brelse(bh);
} }
......
...@@ -1024,7 +1024,6 @@ struct journal_s ...@@ -1024,7 +1024,6 @@ struct journal_s
/* Filing buffers */ /* Filing buffers */
extern void jbd2_journal_unfile_buffer(journal_t *, struct journal_head *); extern void jbd2_journal_unfile_buffer(journal_t *, struct journal_head *);
extern void __jbd2_journal_unfile_buffer(struct journal_head *);
extern void __jbd2_journal_refile_buffer(struct journal_head *); extern void __jbd2_journal_refile_buffer(struct journal_head *);
extern void jbd2_journal_refile_buffer(journal_t *, struct journal_head *); extern void jbd2_journal_refile_buffer(journal_t *, struct journal_head *);
extern void __jbd2_journal_file_buffer(struct journal_head *, transaction_t *, int); extern void __jbd2_journal_file_buffer(struct journal_head *, transaction_t *, int);
...@@ -1165,7 +1164,6 @@ extern void jbd2_journal_release_jbd_inode(journal_t *journal, struct jbd2_in ...@@ -1165,7 +1164,6 @@ extern void jbd2_journal_release_jbd_inode(journal_t *journal, struct jbd2_in
*/ */
struct journal_head *jbd2_journal_add_journal_head(struct buffer_head *bh); struct journal_head *jbd2_journal_add_journal_head(struct buffer_head *bh);
struct journal_head *jbd2_journal_grab_journal_head(struct buffer_head *bh); struct journal_head *jbd2_journal_grab_journal_head(struct buffer_head *bh);
void jbd2_journal_remove_journal_head(struct buffer_head *bh);
void jbd2_journal_put_journal_head(struct journal_head *jh); void jbd2_journal_put_journal_head(struct journal_head *jh);
/* /*
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment