mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-10-02 15:18:19 +00:00
gfs2: Never call gfs2_block_zero_range with an open transaction
[ Upstream commit70499cdfeb
] Before this patch, some functions started transactions then they called gfs2_block_zero_range. However, gfs2_block_zero_range, like writes, can start transactions, which results in a recursive transaction error. For example: do_shrink trunc_start gfs2_trans_begin <------------------------------------------------ gfs2_block_zero_range iomap_zero_range(inode, from, length, NULL, &gfs2_iomap_ops); iomap_apply ... iomap_zero_range_actor iomap_begin gfs2_iomap_begin gfs2_iomap_begin_write actor (iomap_zero_range_actor) iomap_zero iomap_write_begin gfs2_iomap_page_prepare gfs2_trans_begin <------------------------ This patch reorders the callers of gfs2_block_zero_range so that they only start their transactions after the call. It also adds a BUG_ON to ensure this doesn't happen again. Fixes:2257e468a6
("gfs2: implement gfs2_block_zero_range using iomap_zero_range") Cc: stable@vger.kernel.org # v5.5+ Signed-off-by: Bob Peterson <rpeterso@redhat.com> Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com> Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
parent
18a640d3b5
commit
888d9b829c
1 changed files with 39 additions and 30 deletions
|
@ -1350,9 +1350,15 @@ int gfs2_extent_map(struct inode *inode, u64 lblock, int *new, u64 *dblock, unsi
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* NOTE: Never call gfs2_block_zero_range with an open transaction because it
|
||||||
|
* uses iomap write to perform its actions, which begin their own transactions
|
||||||
|
* (iomap_begin, page_prepare, etc.)
|
||||||
|
*/
|
||||||
static int gfs2_block_zero_range(struct inode *inode, loff_t from,
|
static int gfs2_block_zero_range(struct inode *inode, loff_t from,
|
||||||
unsigned int length)
|
unsigned int length)
|
||||||
{
|
{
|
||||||
|
BUG_ON(current->journal_info);
|
||||||
return iomap_zero_range(inode, from, length, NULL, &gfs2_iomap_ops);
|
return iomap_zero_range(inode, from, length, NULL, &gfs2_iomap_ops);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1413,6 +1419,16 @@ static int trunc_start(struct inode *inode, u64 newsize)
|
||||||
u64 oldsize = inode->i_size;
|
u64 oldsize = inode->i_size;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
|
if (!gfs2_is_stuffed(ip)) {
|
||||||
|
unsigned int blocksize = i_blocksize(inode);
|
||||||
|
unsigned int offs = newsize & (blocksize - 1);
|
||||||
|
if (offs) {
|
||||||
|
error = gfs2_block_zero_range(inode, newsize,
|
||||||
|
blocksize - offs);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (journaled)
|
if (journaled)
|
||||||
error = gfs2_trans_begin(sdp, RES_DINODE + RES_JDATA, GFS2_JTRUNC_REVOKES);
|
error = gfs2_trans_begin(sdp, RES_DINODE + RES_JDATA, GFS2_JTRUNC_REVOKES);
|
||||||
else
|
else
|
||||||
|
@ -1426,19 +1442,10 @@ static int trunc_start(struct inode *inode, u64 newsize)
|
||||||
|
|
||||||
gfs2_trans_add_meta(ip->i_gl, dibh);
|
gfs2_trans_add_meta(ip->i_gl, dibh);
|
||||||
|
|
||||||
if (gfs2_is_stuffed(ip)) {
|
if (gfs2_is_stuffed(ip))
|
||||||
gfs2_buffer_clear_tail(dibh, sizeof(struct gfs2_dinode) + newsize);
|
gfs2_buffer_clear_tail(dibh, sizeof(struct gfs2_dinode) + newsize);
|
||||||
} else {
|
else
|
||||||
unsigned int blocksize = i_blocksize(inode);
|
|
||||||
unsigned int offs = newsize & (blocksize - 1);
|
|
||||||
if (offs) {
|
|
||||||
error = gfs2_block_zero_range(inode, newsize,
|
|
||||||
blocksize - offs);
|
|
||||||
if (error)
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
ip->i_diskflags |= GFS2_DIF_TRUNC_IN_PROG;
|
ip->i_diskflags |= GFS2_DIF_TRUNC_IN_PROG;
|
||||||
}
|
|
||||||
|
|
||||||
i_size_write(inode, newsize);
|
i_size_write(inode, newsize);
|
||||||
ip->i_inode.i_mtime = ip->i_inode.i_ctime = current_time(&ip->i_inode);
|
ip->i_inode.i_mtime = ip->i_inode.i_ctime = current_time(&ip->i_inode);
|
||||||
|
@ -2446,25 +2453,7 @@ int __gfs2_punch_hole(struct file *file, loff_t offset, loff_t length)
|
||||||
loff_t start, end;
|
loff_t start, end;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
start = round_down(offset, blocksize);
|
if (!gfs2_is_stuffed(ip)) {
|
||||||
end = round_up(offset + length, blocksize) - 1;
|
|
||||||
error = filemap_write_and_wait_range(inode->i_mapping, start, end);
|
|
||||||
if (error)
|
|
||||||
return error;
|
|
||||||
|
|
||||||
if (gfs2_is_jdata(ip))
|
|
||||||
error = gfs2_trans_begin(sdp, RES_DINODE + 2 * RES_JDATA,
|
|
||||||
GFS2_JTRUNC_REVOKES);
|
|
||||||
else
|
|
||||||
error = gfs2_trans_begin(sdp, RES_DINODE, 0);
|
|
||||||
if (error)
|
|
||||||
return error;
|
|
||||||
|
|
||||||
if (gfs2_is_stuffed(ip)) {
|
|
||||||
error = stuffed_zero_range(inode, offset, length);
|
|
||||||
if (error)
|
|
||||||
goto out;
|
|
||||||
} else {
|
|
||||||
unsigned int start_off, end_len;
|
unsigned int start_off, end_len;
|
||||||
|
|
||||||
start_off = offset & (blocksize - 1);
|
start_off = offset & (blocksize - 1);
|
||||||
|
@ -2487,6 +2476,26 @@ int __gfs2_punch_hole(struct file *file, loff_t offset, loff_t length)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
start = round_down(offset, blocksize);
|
||||||
|
end = round_up(offset + length, blocksize) - 1;
|
||||||
|
error = filemap_write_and_wait_range(inode->i_mapping, start, end);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
if (gfs2_is_jdata(ip))
|
||||||
|
error = gfs2_trans_begin(sdp, RES_DINODE + 2 * RES_JDATA,
|
||||||
|
GFS2_JTRUNC_REVOKES);
|
||||||
|
else
|
||||||
|
error = gfs2_trans_begin(sdp, RES_DINODE, 0);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
if (gfs2_is_stuffed(ip)) {
|
||||||
|
error = stuffed_zero_range(inode, offset, length);
|
||||||
|
if (error)
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
if (gfs2_is_jdata(ip)) {
|
if (gfs2_is_jdata(ip)) {
|
||||||
BUG_ON(!current->journal_info);
|
BUG_ON(!current->journal_info);
|
||||||
gfs2_journaled_truncate_range(inode, offset, length);
|
gfs2_journaled_truncate_range(inode, offset, length);
|
||||||
|
|
Loading…
Reference in a new issue