mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-04 04:04:19 +00:00
ocfs2: fix a tiny race when truncate dio orohaned entry
Once dio crashed it will leave an entry in orphan dir. And orphan scan will take care of the clean up. There is a tiny race case that the same entry will be truncated twice and then trigger the BUG in ocfs2_del_inode_from_orphan. Signed-off-by: Joseph Qi <joseph.qi@huawei.com> Cc: Mark Fasheh <mfasheh@suse.com> Cc: Joel Becker <jlbec@evilplan.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
parent
e327284abb
commit
cf1776a9e8
@ -925,13 +925,23 @@ static ssize_t ocfs2_direct_IO_write(struct kiocb *iocb,
|
|||||||
int update_isize = written > 0 ? 1 : 0;
|
int update_isize = written > 0 ? 1 : 0;
|
||||||
loff_t end = update_isize ? offset + written : 0;
|
loff_t end = update_isize ? offset + written : 0;
|
||||||
|
|
||||||
tmp_ret = ocfs2_del_inode_from_orphan(osb, inode,
|
tmp_ret = ocfs2_inode_lock(inode, &di_bh, 1);
|
||||||
|
if (tmp_ret < 0) {
|
||||||
|
ret = tmp_ret;
|
||||||
|
mlog_errno(ret);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
tmp_ret = ocfs2_del_inode_from_orphan(osb, inode, di_bh,
|
||||||
update_isize, end);
|
update_isize, end);
|
||||||
if (tmp_ret < 0) {
|
if (tmp_ret < 0) {
|
||||||
ret = tmp_ret;
|
ret = tmp_ret;
|
||||||
|
mlog_errno(ret);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ocfs2_inode_unlock(inode, 1);
|
||||||
|
|
||||||
tmp_ret = jbd2_journal_force_commit(journal);
|
tmp_ret = jbd2_journal_force_commit(journal);
|
||||||
if (tmp_ret < 0) {
|
if (tmp_ret < 0) {
|
||||||
ret = tmp_ret;
|
ret = tmp_ret;
|
||||||
|
@ -2137,6 +2137,8 @@ static int ocfs2_recover_orphans(struct ocfs2_super *osb,
|
|||||||
struct inode *inode = NULL;
|
struct inode *inode = NULL;
|
||||||
struct inode *iter;
|
struct inode *iter;
|
||||||
struct ocfs2_inode_info *oi;
|
struct ocfs2_inode_info *oi;
|
||||||
|
struct buffer_head *di_bh = NULL;
|
||||||
|
struct ocfs2_dinode *di = NULL;
|
||||||
|
|
||||||
trace_ocfs2_recover_orphans(slot);
|
trace_ocfs2_recover_orphans(slot);
|
||||||
|
|
||||||
@ -2157,16 +2159,22 @@ static int ocfs2_recover_orphans(struct ocfs2_super *osb,
|
|||||||
iter = oi->ip_next_orphan;
|
iter = oi->ip_next_orphan;
|
||||||
oi->ip_next_orphan = NULL;
|
oi->ip_next_orphan = NULL;
|
||||||
|
|
||||||
|
ret = ocfs2_rw_lock(inode, 1);
|
||||||
|
if (ret < 0) {
|
||||||
|
mlog_errno(ret);
|
||||||
|
goto next;
|
||||||
|
}
|
||||||
/*
|
/*
|
||||||
* We need to take and drop the inode lock to
|
* We need to take and drop the inode lock to
|
||||||
* force read inode from disk.
|
* force read inode from disk.
|
||||||
*/
|
*/
|
||||||
ret = ocfs2_inode_lock(inode, NULL, 0);
|
ret = ocfs2_inode_lock(inode, &di_bh, 1);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
mlog_errno(ret);
|
mlog_errno(ret);
|
||||||
goto next;
|
goto unlock_rw;
|
||||||
}
|
}
|
||||||
ocfs2_inode_unlock(inode, 0);
|
|
||||||
|
di = (struct ocfs2_dinode *)di_bh->b_data;
|
||||||
|
|
||||||
if (inode->i_nlink == 0) {
|
if (inode->i_nlink == 0) {
|
||||||
spin_lock(&oi->ip_lock);
|
spin_lock(&oi->ip_lock);
|
||||||
@ -2174,43 +2182,30 @@ static int ocfs2_recover_orphans(struct ocfs2_super *osb,
|
|||||||
* ocfs2_delete_inode. */
|
* ocfs2_delete_inode. */
|
||||||
oi->ip_flags |= OCFS2_INODE_MAYBE_ORPHANED;
|
oi->ip_flags |= OCFS2_INODE_MAYBE_ORPHANED;
|
||||||
spin_unlock(&oi->ip_lock);
|
spin_unlock(&oi->ip_lock);
|
||||||
} else if (orphan_reco_type == ORPHAN_NEED_TRUNCATE) {
|
} else if ((orphan_reco_type == ORPHAN_NEED_TRUNCATE) &&
|
||||||
struct buffer_head *di_bh = NULL;
|
(di->i_flags & cpu_to_le32(OCFS2_DIO_ORPHANED_FL))) {
|
||||||
|
|
||||||
ret = ocfs2_rw_lock(inode, 1);
|
|
||||||
if (ret) {
|
|
||||||
mlog_errno(ret);
|
|
||||||
goto next;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = ocfs2_inode_lock(inode, &di_bh, 1);
|
|
||||||
if (ret < 0) {
|
|
||||||
ocfs2_rw_unlock(inode, 1);
|
|
||||||
mlog_errno(ret);
|
|
||||||
goto next;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = ocfs2_truncate_file(inode, di_bh,
|
ret = ocfs2_truncate_file(inode, di_bh,
|
||||||
i_size_read(inode));
|
i_size_read(inode));
|
||||||
ocfs2_inode_unlock(inode, 1);
|
|
||||||
ocfs2_rw_unlock(inode, 1);
|
|
||||||
brelse(di_bh);
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
if (ret != -ENOSPC)
|
if (ret != -ENOSPC)
|
||||||
mlog_errno(ret);
|
mlog_errno(ret);
|
||||||
goto next;
|
goto unlock_inode;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = ocfs2_del_inode_from_orphan(osb, inode, 0, 0);
|
ret = ocfs2_del_inode_from_orphan(osb, inode, di_bh, 0, 0);
|
||||||
if (ret)
|
if (ret)
|
||||||
mlog_errno(ret);
|
mlog_errno(ret);
|
||||||
|
|
||||||
wake_up(&OCFS2_I(inode)->append_dio_wq);
|
wake_up(&OCFS2_I(inode)->append_dio_wq);
|
||||||
} /* else if ORPHAN_NO_NEED_TRUNCATE, do nothing */
|
} /* else if ORPHAN_NO_NEED_TRUNCATE, do nothing */
|
||||||
|
unlock_inode:
|
||||||
|
ocfs2_inode_unlock(inode, 1);
|
||||||
|
unlock_rw:
|
||||||
|
ocfs2_rw_unlock(inode, 1);
|
||||||
next:
|
next:
|
||||||
iput(inode);
|
iput(inode);
|
||||||
|
brelse(di_bh);
|
||||||
|
di_bh = NULL;
|
||||||
inode = iter;
|
inode = iter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2670,30 +2670,22 @@ int ocfs2_add_inode_to_orphan(struct ocfs2_super *osb,
|
|||||||
}
|
}
|
||||||
|
|
||||||
int ocfs2_del_inode_from_orphan(struct ocfs2_super *osb,
|
int ocfs2_del_inode_from_orphan(struct ocfs2_super *osb,
|
||||||
struct inode *inode, int update_isize,
|
struct inode *inode, struct buffer_head *di_bh,
|
||||||
loff_t end)
|
int update_isize, loff_t end)
|
||||||
{
|
{
|
||||||
struct inode *orphan_dir_inode = NULL;
|
struct inode *orphan_dir_inode = NULL;
|
||||||
struct buffer_head *orphan_dir_bh = NULL;
|
struct buffer_head *orphan_dir_bh = NULL;
|
||||||
struct buffer_head *di_bh = NULL;
|
struct ocfs2_dinode *di = (struct ocfs2_dinode *)di_bh->b_data;
|
||||||
struct ocfs2_dinode *di = NULL;
|
|
||||||
handle_t *handle = NULL;
|
handle_t *handle = NULL;
|
||||||
int status = 0;
|
int status = 0;
|
||||||
|
|
||||||
status = ocfs2_inode_lock(inode, &di_bh, 1);
|
|
||||||
if (status < 0) {
|
|
||||||
mlog_errno(status);
|
|
||||||
goto bail;
|
|
||||||
}
|
|
||||||
di = (struct ocfs2_dinode *) di_bh->b_data;
|
|
||||||
|
|
||||||
orphan_dir_inode = ocfs2_get_system_file_inode(osb,
|
orphan_dir_inode = ocfs2_get_system_file_inode(osb,
|
||||||
ORPHAN_DIR_SYSTEM_INODE,
|
ORPHAN_DIR_SYSTEM_INODE,
|
||||||
le16_to_cpu(di->i_dio_orphaned_slot));
|
le16_to_cpu(di->i_dio_orphaned_slot));
|
||||||
if (!orphan_dir_inode) {
|
if (!orphan_dir_inode) {
|
||||||
status = -ENOENT;
|
status = -ENOENT;
|
||||||
mlog_errno(status);
|
mlog_errno(status);
|
||||||
goto bail_unlock_inode;
|
goto bail;
|
||||||
}
|
}
|
||||||
|
|
||||||
mutex_lock(&orphan_dir_inode->i_mutex);
|
mutex_lock(&orphan_dir_inode->i_mutex);
|
||||||
@ -2702,7 +2694,7 @@ int ocfs2_del_inode_from_orphan(struct ocfs2_super *osb,
|
|||||||
mutex_unlock(&orphan_dir_inode->i_mutex);
|
mutex_unlock(&orphan_dir_inode->i_mutex);
|
||||||
iput(orphan_dir_inode);
|
iput(orphan_dir_inode);
|
||||||
mlog_errno(status);
|
mlog_errno(status);
|
||||||
goto bail_unlock_inode;
|
goto bail;
|
||||||
}
|
}
|
||||||
|
|
||||||
handle = ocfs2_start_trans(osb,
|
handle = ocfs2_start_trans(osb,
|
||||||
@ -2749,10 +2741,6 @@ int ocfs2_del_inode_from_orphan(struct ocfs2_super *osb,
|
|||||||
brelse(orphan_dir_bh);
|
brelse(orphan_dir_bh);
|
||||||
iput(orphan_dir_inode);
|
iput(orphan_dir_inode);
|
||||||
|
|
||||||
bail_unlock_inode:
|
|
||||||
ocfs2_inode_unlock(inode, 1);
|
|
||||||
brelse(di_bh);
|
|
||||||
|
|
||||||
bail:
|
bail:
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
@ -42,8 +42,8 @@ int ocfs2_create_inode_in_orphan(struct inode *dir,
|
|||||||
int ocfs2_add_inode_to_orphan(struct ocfs2_super *osb,
|
int ocfs2_add_inode_to_orphan(struct ocfs2_super *osb,
|
||||||
struct inode *inode);
|
struct inode *inode);
|
||||||
int ocfs2_del_inode_from_orphan(struct ocfs2_super *osb,
|
int ocfs2_del_inode_from_orphan(struct ocfs2_super *osb,
|
||||||
struct inode *inode, int update_isize,
|
struct inode *inode, struct buffer_head *di_bh,
|
||||||
loff_t end);
|
int update_isize, loff_t end);
|
||||||
int ocfs2_mv_orphaned_inode_to_new(struct inode *dir,
|
int ocfs2_mv_orphaned_inode_to_new(struct inode *dir,
|
||||||
struct inode *new_inode,
|
struct inode *new_inode,
|
||||||
struct dentry *new_dentry);
|
struct dentry *new_dentry);
|
||||||
|
Loading…
Reference in New Issue
Block a user