Commit c2131f7e authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'gfs2-v5.13-rc2-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/gfs2/linux-gfs2

Pull gfs2 fixes from Andreas Gruenbacher:
 "Various gfs2 fixes"

* tag 'gfs2-v5.13-rc2-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/gfs2/linux-gfs2:
  gfs2: Fix use-after-free in gfs2_glock_shrink_scan
  gfs2: Fix mmap locking for write faults
  gfs2: Clean up revokes on normal withdraws
  gfs2: fix a deadlock on withdraw-during-mount
  gfs2: fix scheduling while atomic bug in glocks
  gfs2: Fix I_NEW check in gfs2_dinode_in
  gfs2: Prevent direct-I/O write fallback errors from getting lost
parents 36c79551 1ab19c5d
...@@ -540,9 +540,11 @@ static vm_fault_t gfs2_fault(struct vm_fault *vmf) ...@@ -540,9 +540,11 @@ static vm_fault_t gfs2_fault(struct vm_fault *vmf)
struct gfs2_inode *ip = GFS2_I(inode); struct gfs2_inode *ip = GFS2_I(inode);
struct gfs2_holder gh; struct gfs2_holder gh;
vm_fault_t ret; vm_fault_t ret;
u16 state;
int err; int err;
gfs2_holder_init(ip->i_gl, LM_ST_SHARED, 0, &gh); state = (vmf->flags & FAULT_FLAG_WRITE) ? LM_ST_EXCLUSIVE : LM_ST_SHARED;
gfs2_holder_init(ip->i_gl, state, 0, &gh);
err = gfs2_glock_nq(&gh); err = gfs2_glock_nq(&gh);
if (err) { if (err) {
ret = block_page_mkwrite_return(err); ret = block_page_mkwrite_return(err);
...@@ -911,8 +913,11 @@ static ssize_t gfs2_file_write_iter(struct kiocb *iocb, struct iov_iter *from) ...@@ -911,8 +913,11 @@ static ssize_t gfs2_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
current->backing_dev_info = inode_to_bdi(inode); current->backing_dev_info = inode_to_bdi(inode);
buffered = iomap_file_buffered_write(iocb, from, &gfs2_iomap_ops); buffered = iomap_file_buffered_write(iocb, from, &gfs2_iomap_ops);
current->backing_dev_info = NULL; current->backing_dev_info = NULL;
if (unlikely(buffered <= 0)) if (unlikely(buffered <= 0)) {
if (!ret)
ret = buffered;
goto out_unlock; goto out_unlock;
}
/* /*
* We need to ensure that the page cache pages are written to * We need to ensure that the page cache pages are written to
......
...@@ -582,6 +582,16 @@ static void finish_xmote(struct gfs2_glock *gl, unsigned int ret) ...@@ -582,6 +582,16 @@ static void finish_xmote(struct gfs2_glock *gl, unsigned int ret)
spin_unlock(&gl->gl_lockref.lock); spin_unlock(&gl->gl_lockref.lock);
} }
static bool is_system_glock(struct gfs2_glock *gl)
{
struct gfs2_sbd *sdp = gl->gl_name.ln_sbd;
struct gfs2_inode *m_ip = GFS2_I(sdp->sd_statfs_inode);
if (gl == m_ip->i_gl)
return true;
return false;
}
/** /**
* do_xmote - Calls the DLM to change the state of a lock * do_xmote - Calls the DLM to change the state of a lock
* @gl: The lock state * @gl: The lock state
...@@ -671,17 +681,25 @@ __acquires(&gl->gl_lockref.lock) ...@@ -671,17 +681,25 @@ __acquires(&gl->gl_lockref.lock)
* to see sd_log_error and withdraw, and in the meantime, requeue the * to see sd_log_error and withdraw, and in the meantime, requeue the
* work for later. * work for later.
* *
* We make a special exception for some system glocks, such as the
* system statfs inode glock, which needs to be granted before the
* gfs2_quotad daemon can exit, and that exit needs to finish before
* we can unmount the withdrawn file system.
*
* However, if we're just unlocking the lock (say, for unmount, when * However, if we're just unlocking the lock (say, for unmount, when
* gfs2_gl_hash_clear calls clear_glock) and recovery is complete * gfs2_gl_hash_clear calls clear_glock) and recovery is complete
* then it's okay to tell dlm to unlock it. * then it's okay to tell dlm to unlock it.
*/ */
if (unlikely(sdp->sd_log_error && !gfs2_withdrawn(sdp))) if (unlikely(sdp->sd_log_error && !gfs2_withdrawn(sdp)))
gfs2_withdraw_delayed(sdp); gfs2_withdraw_delayed(sdp);
if (glock_blocked_by_withdraw(gl)) { if (glock_blocked_by_withdraw(gl) &&
if (target != LM_ST_UNLOCKED || (target != LM_ST_UNLOCKED ||
test_bit(SDF_WITHDRAW_RECOVERY, &sdp->sd_flags)) { test_bit(SDF_WITHDRAW_RECOVERY, &sdp->sd_flags))) {
if (!is_system_glock(gl)) {
gfs2_glock_queue_work(gl, GL_GLOCK_DFT_HOLD); gfs2_glock_queue_work(gl, GL_GLOCK_DFT_HOLD);
goto out; goto out;
} else {
clear_bit(GLF_INVALIDATE_IN_PROGRESS, &gl->gl_flags);
} }
} }
...@@ -1466,9 +1484,11 @@ void gfs2_glock_dq(struct gfs2_holder *gh) ...@@ -1466,9 +1484,11 @@ void gfs2_glock_dq(struct gfs2_holder *gh)
glock_blocked_by_withdraw(gl) && glock_blocked_by_withdraw(gl) &&
gh->gh_gl != sdp->sd_jinode_gl) { gh->gh_gl != sdp->sd_jinode_gl) {
sdp->sd_glock_dqs_held++; sdp->sd_glock_dqs_held++;
spin_unlock(&gl->gl_lockref.lock);
might_sleep(); might_sleep();
wait_on_bit(&sdp->sd_flags, SDF_WITHDRAW_RECOVERY, wait_on_bit(&sdp->sd_flags, SDF_WITHDRAW_RECOVERY,
TASK_UNINTERRUPTIBLE); TASK_UNINTERRUPTIBLE);
spin_lock(&gl->gl_lockref.lock);
} }
if (gh->gh_flags & GL_NOCACHE) if (gh->gh_flags & GL_NOCACHE)
handle_callback(gl, LM_ST_UNLOCKED, 0, false); handle_callback(gl, LM_ST_UNLOCKED, 0, false);
...@@ -1775,6 +1795,7 @@ __acquires(&lru_lock) ...@@ -1775,6 +1795,7 @@ __acquires(&lru_lock)
while(!list_empty(list)) { while(!list_empty(list)) {
gl = list_first_entry(list, struct gfs2_glock, gl_lru); gl = list_first_entry(list, struct gfs2_glock, gl_lru);
list_del_init(&gl->gl_lru); list_del_init(&gl->gl_lru);
clear_bit(GLF_LRU, &gl->gl_flags);
if (!spin_trylock(&gl->gl_lockref.lock)) { if (!spin_trylock(&gl->gl_lockref.lock)) {
add_back_to_lru: add_back_to_lru:
list_add(&gl->gl_lru, &lru_list); list_add(&gl->gl_lru, &lru_list);
...@@ -1820,7 +1841,6 @@ static long gfs2_scan_glock_lru(int nr) ...@@ -1820,7 +1841,6 @@ static long gfs2_scan_glock_lru(int nr)
if (!test_bit(GLF_LOCK, &gl->gl_flags)) { if (!test_bit(GLF_LOCK, &gl->gl_flags)) {
list_move(&gl->gl_lru, &dispose); list_move(&gl->gl_lru, &dispose);
atomic_dec(&lru_count); atomic_dec(&lru_count);
clear_bit(GLF_LRU, &gl->gl_flags);
freed++; freed++;
continue; continue;
} }
......
...@@ -396,7 +396,7 @@ static int gfs2_dinode_in(struct gfs2_inode *ip, const void *buf) ...@@ -396,7 +396,7 @@ static int gfs2_dinode_in(struct gfs2_inode *ip, const void *buf)
struct timespec64 atime; struct timespec64 atime;
u16 height, depth; u16 height, depth;
umode_t mode = be32_to_cpu(str->di_mode); umode_t mode = be32_to_cpu(str->di_mode);
bool is_new = ip->i_inode.i_flags & I_NEW; bool is_new = ip->i_inode.i_state & I_NEW;
if (unlikely(ip->i_no_addr != be64_to_cpu(str->di_num.no_addr))) if (unlikely(ip->i_no_addr != be64_to_cpu(str->di_num.no_addr)))
goto corrupt; goto corrupt;
......
...@@ -926,10 +926,10 @@ static void log_write_header(struct gfs2_sbd *sdp, u32 flags) ...@@ -926,10 +926,10 @@ static void log_write_header(struct gfs2_sbd *sdp, u32 flags)
} }
/** /**
* ail_drain - drain the ail lists after a withdraw * gfs2_ail_drain - drain the ail lists after a withdraw
* @sdp: Pointer to GFS2 superblock * @sdp: Pointer to GFS2 superblock
*/ */
static void ail_drain(struct gfs2_sbd *sdp) void gfs2_ail_drain(struct gfs2_sbd *sdp)
{ {
struct gfs2_trans *tr; struct gfs2_trans *tr;
...@@ -956,6 +956,7 @@ static void ail_drain(struct gfs2_sbd *sdp) ...@@ -956,6 +956,7 @@ static void ail_drain(struct gfs2_sbd *sdp)
list_del(&tr->tr_list); list_del(&tr->tr_list);
gfs2_trans_free(sdp, tr); gfs2_trans_free(sdp, tr);
} }
gfs2_drain_revokes(sdp);
spin_unlock(&sdp->sd_ail_lock); spin_unlock(&sdp->sd_ail_lock);
} }
...@@ -1162,7 +1163,6 @@ void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl, u32 flags) ...@@ -1162,7 +1163,6 @@ void gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl, u32 flags)
if (tr && list_empty(&tr->tr_list)) if (tr && list_empty(&tr->tr_list))
list_add(&tr->tr_list, &sdp->sd_ail1_list); list_add(&tr->tr_list, &sdp->sd_ail1_list);
spin_unlock(&sdp->sd_ail_lock); spin_unlock(&sdp->sd_ail_lock);
ail_drain(sdp); /* frees all transactions */
tr = NULL; tr = NULL;
goto out_end; goto out_end;
} }
......
...@@ -93,5 +93,6 @@ extern int gfs2_logd(void *data); ...@@ -93,5 +93,6 @@ extern int gfs2_logd(void *data);
extern void gfs2_add_revoke(struct gfs2_sbd *sdp, struct gfs2_bufdata *bd); extern void gfs2_add_revoke(struct gfs2_sbd *sdp, struct gfs2_bufdata *bd);
extern void gfs2_glock_remove_revoke(struct gfs2_glock *gl); extern void gfs2_glock_remove_revoke(struct gfs2_glock *gl);
extern void gfs2_flush_revokes(struct gfs2_sbd *sdp); extern void gfs2_flush_revokes(struct gfs2_sbd *sdp);
extern void gfs2_ail_drain(struct gfs2_sbd *sdp);
#endif /* __LOG_DOT_H__ */ #endif /* __LOG_DOT_H__ */
...@@ -885,7 +885,7 @@ static void revoke_lo_before_commit(struct gfs2_sbd *sdp, struct gfs2_trans *tr) ...@@ -885,7 +885,7 @@ static void revoke_lo_before_commit(struct gfs2_sbd *sdp, struct gfs2_trans *tr)
gfs2_log_write_page(sdp, page); gfs2_log_write_page(sdp, page);
} }
static void revoke_lo_after_commit(struct gfs2_sbd *sdp, struct gfs2_trans *tr) void gfs2_drain_revokes(struct gfs2_sbd *sdp)
{ {
struct list_head *head = &sdp->sd_log_revokes; struct list_head *head = &sdp->sd_log_revokes;
struct gfs2_bufdata *bd; struct gfs2_bufdata *bd;
...@@ -900,6 +900,11 @@ static void revoke_lo_after_commit(struct gfs2_sbd *sdp, struct gfs2_trans *tr) ...@@ -900,6 +900,11 @@ static void revoke_lo_after_commit(struct gfs2_sbd *sdp, struct gfs2_trans *tr)
} }
} }
static void revoke_lo_after_commit(struct gfs2_sbd *sdp, struct gfs2_trans *tr)
{
gfs2_drain_revokes(sdp);
}
static void revoke_lo_before_scan(struct gfs2_jdesc *jd, static void revoke_lo_before_scan(struct gfs2_jdesc *jd,
struct gfs2_log_header_host *head, int pass) struct gfs2_log_header_host *head, int pass)
{ {
......
...@@ -20,6 +20,7 @@ extern void gfs2_log_submit_bio(struct bio **biop, int opf); ...@@ -20,6 +20,7 @@ extern void gfs2_log_submit_bio(struct bio **biop, int opf);
extern void gfs2_pin(struct gfs2_sbd *sdp, struct buffer_head *bh); extern void gfs2_pin(struct gfs2_sbd *sdp, struct buffer_head *bh);
extern int gfs2_find_jhead(struct gfs2_jdesc *jd, extern int gfs2_find_jhead(struct gfs2_jdesc *jd,
struct gfs2_log_header_host *head, bool keep_cache); struct gfs2_log_header_host *head, bool keep_cache);
extern void gfs2_drain_revokes(struct gfs2_sbd *sdp);
static inline unsigned int buf_limit(struct gfs2_sbd *sdp) static inline unsigned int buf_limit(struct gfs2_sbd *sdp)
{ {
return sdp->sd_ldptrs; return sdp->sd_ldptrs;
......
...@@ -131,6 +131,7 @@ static void signal_our_withdraw(struct gfs2_sbd *sdp) ...@@ -131,6 +131,7 @@ static void signal_our_withdraw(struct gfs2_sbd *sdp)
if (test_bit(SDF_NORECOVERY, &sdp->sd_flags) || !sdp->sd_jdesc) if (test_bit(SDF_NORECOVERY, &sdp->sd_flags) || !sdp->sd_jdesc)
return; return;
gfs2_ail_drain(sdp); /* frees all transactions */
inode = sdp->sd_jdesc->jd_inode; inode = sdp->sd_jdesc->jd_inode;
ip = GFS2_I(inode); ip = GFS2_I(inode);
i_gl = ip->i_gl; i_gl = ip->i_gl;
......
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