Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 39c99f12 authored by Tristan Ye's avatar Tristan Ye Committed by Joel Becker
Browse files

Ocfs2: Teach 'coherency=full' O_DIRECT writes to correctly up_read i_alloc_sem.



Due to newly-introduced 'coherency=full' O_DIRECT writes also takes the EX
rw_lock like buffered writes did(rw_level == 1), it turns out messing the
usage of 'level' in ocfs2_dio_end_io() up, which caused i_alloc_sem being
failed to get up_read'd correctly.

This patch tries to teach ocfs2_dio_end_io to understand well on all locking
stuffs by explicitly introducing a new bit for i_alloc_sem in iocb's private
data, just like what we did for rw_lock.

Signed-off-by: default avatarTristan Ye <tristan.ye@oracle.com>
Signed-off-by: default avatarJoel Becker <joel.becker@oracle.com>
parent 388c4bcb
Loading
Loading
Loading
Loading
+5 −2
Original line number Diff line number Diff line
@@ -573,11 +573,14 @@ static void ocfs2_dio_end_io(struct kiocb *iocb,
	/* this io's submitter should not have unlocked this before we could */
	BUG_ON(!ocfs2_iocb_is_rw_locked(iocb));

	if (ocfs2_iocb_is_sem_locked(iocb)) {
		up_read(&inode->i_alloc_sem);
		ocfs2_iocb_clear_sem_locked(iocb);
	}

	ocfs2_iocb_clear_rw_locked(iocb);

	level = ocfs2_iocb_rw_locked_level(iocb);
	if (!level)
		up_read(&inode->i_alloc_sem);
	ocfs2_rw_unlock(inode, level);

	if (is_async)
+21 −2
Original line number Diff line number Diff line
@@ -68,8 +68,27 @@ static inline void ocfs2_iocb_set_rw_locked(struct kiocb *iocb, int level)
	else
		clear_bit(1, (unsigned long *)&iocb->private);
}

/*
 * Using a named enum representing lock types in terms of #N bit stored in
 * iocb->private, which is going to be used for communication bewteen
 * ocfs2_dio_end_io() and ocfs2_file_aio_write/read().
 */
enum ocfs2_iocb_lock_bits {
	OCFS2_IOCB_RW_LOCK = 0,
	OCFS2_IOCB_RW_LOCK_LEVEL,
	OCFS2_IOCB_SEM,
	OCFS2_IOCB_NUM_LOCKS
};

#define ocfs2_iocb_clear_rw_locked(iocb) \
	clear_bit(0, (unsigned long *)&iocb->private)
	clear_bit(OCFS2_IOCB_RW_LOCK, (unsigned long *)&iocb->private)
#define ocfs2_iocb_rw_locked_level(iocb) \
	test_bit(1, (unsigned long *)&iocb->private)
	test_bit(OCFS2_IOCB_RW_LOCK_LEVEL, (unsigned long *)&iocb->private)
#define ocfs2_iocb_set_sem_locked(iocb) \
	set_bit(OCFS2_IOCB_SEM, (unsigned long *)&iocb->private)
#define ocfs2_iocb_clear_sem_locked(iocb) \
	clear_bit(OCFS2_IOCB_SEM, (unsigned long *)&iocb->private)
#define ocfs2_iocb_is_sem_locked(iocb) \
	test_bit(OCFS2_IOCB_SEM, (unsigned long *)&iocb->private)
#endif /* OCFS2_FILE_H */
+13 −2
Original line number Diff line number Diff line
@@ -2241,11 +2241,15 @@ static ssize_t ocfs2_file_aio_write(struct kiocb *iocb,

	mutex_lock(&inode->i_mutex);

	ocfs2_iocb_clear_sem_locked(iocb);

relock:
	/* to match setattr's i_mutex -> i_alloc_sem -> rw_lock ordering */
	if (direct_io) {
		down_read(&inode->i_alloc_sem);
		have_alloc_sem = 1;
		/* communicate with ocfs2_dio_end_io */
		ocfs2_iocb_set_sem_locked(iocb);
	}

	/*
@@ -2382,8 +2386,10 @@ static ssize_t ocfs2_file_aio_write(struct kiocb *iocb,
		ocfs2_rw_unlock(inode, rw_level);

out_sems:
	if (have_alloc_sem)
	if (have_alloc_sem) {
		up_read(&inode->i_alloc_sem);
		ocfs2_iocb_clear_sem_locked(iocb);
	}

	mutex_unlock(&inode->i_mutex);

@@ -2527,6 +2533,8 @@ static ssize_t ocfs2_file_aio_read(struct kiocb *iocb,
		goto bail;
	}

	ocfs2_iocb_clear_sem_locked(iocb);

	/*
	 * buffered reads protect themselves in ->readpage().  O_DIRECT reads
	 * need locks to protect pending reads from racing with truncate.
@@ -2534,6 +2542,7 @@ static ssize_t ocfs2_file_aio_read(struct kiocb *iocb,
	if (filp->f_flags & O_DIRECT) {
		down_read(&inode->i_alloc_sem);
		have_alloc_sem = 1;
		ocfs2_iocb_set_sem_locked(iocb);

		ret = ocfs2_rw_lock(inode, 0);
		if (ret < 0) {
@@ -2575,8 +2584,10 @@ static ssize_t ocfs2_file_aio_read(struct kiocb *iocb,
	}

bail:
	if (have_alloc_sem)
	if (have_alloc_sem) {
		up_read(&inode->i_alloc_sem);
		ocfs2_iocb_clear_sem_locked(iocb);
	}
	if (rw_level != -1)
		ocfs2_rw_unlock(inode, rw_level);
	mlog_exit(ret);