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

Commit 9a5530c6 authored by Yan, Zheng's avatar Yan, Zheng Committed by Ilya Dryomov
Browse files

ceph: wait unsafe sync writes for evicting inode



Otherwise ceph_sync_write_unsafe() may access/modify freed inode.

Signed-off-by: default avatarYan, Zheng <zyan@redhat.com>
parent fc8c3892
Loading
Loading
Loading
Loading
+2 −48
Original line number Diff line number Diff line
@@ -1927,53 +1927,6 @@ static int caps_are_flushed(struct inode *inode, u64 flush_tid)
	return ret;
}

/*
 * Wait on any unsafe replies for the given inode.  First wait on the
 * newest request, and make that the upper bound.  Then, if there are
 * more requests, keep waiting on the oldest as long as it is still older
 * than the original request.
 */
static void sync_write_wait(struct inode *inode)
{
	struct ceph_inode_info *ci = ceph_inode(inode);
	struct list_head *head = &ci->i_unsafe_writes;
	struct ceph_osd_request *req;
	u64 last_tid;

	if (!S_ISREG(inode->i_mode))
		return;

	spin_lock(&ci->i_unsafe_lock);
	if (list_empty(head))
		goto out;

	/* set upper bound as _last_ entry in chain */
	req = list_last_entry(head, struct ceph_osd_request,
			      r_unsafe_item);
	last_tid = req->r_tid;

	do {
		ceph_osdc_get_request(req);
		spin_unlock(&ci->i_unsafe_lock);
		dout("sync_write_wait on tid %llu (until %llu)\n",
		     req->r_tid, last_tid);
		wait_for_completion(&req->r_safe_completion);
		spin_lock(&ci->i_unsafe_lock);
		ceph_osdc_put_request(req);

		/*
		 * from here on look at first entry in chain, since we
		 * only want to wait for anything older than last_tid
		 */
		if (list_empty(head))
			break;
		req = list_first_entry(head, struct ceph_osd_request,
				       r_unsafe_item);
	} while (req->r_tid < last_tid);
out:
	spin_unlock(&ci->i_unsafe_lock);
}

/*
 * wait for any unsafe requests to complete.
 */
@@ -2026,7 +1979,8 @@ int ceph_fsync(struct file *file, loff_t start, loff_t end, int datasync)
	int dirty;

	dout("fsync %p%s\n", inode, datasync ? " datasync" : "");
	sync_write_wait(inode);

	ceph_sync_write_wait(inode);

	ret = filemap_write_and_wait_range(inode->i_mapping, start, end);
	if (ret < 0)
+48 −0
Original line number Diff line number Diff line
@@ -821,6 +821,54 @@ static void ceph_sync_write_unsafe(struct ceph_osd_request *req, bool unsafe)
	}
}

/*
 * Wait on any unsafe replies for the given inode.  First wait on the
 * newest request, and make that the upper bound.  Then, if there are
 * more requests, keep waiting on the oldest as long as it is still older
 * than the original request.
 */
void ceph_sync_write_wait(struct inode *inode)
{
	struct ceph_inode_info *ci = ceph_inode(inode);
	struct list_head *head = &ci->i_unsafe_writes;
	struct ceph_osd_request *req;
	u64 last_tid;

	if (!S_ISREG(inode->i_mode))
		return;

	spin_lock(&ci->i_unsafe_lock);
	if (list_empty(head))
		goto out;

	/* set upper bound as _last_ entry in chain */

	req = list_last_entry(head, struct ceph_osd_request,
			      r_unsafe_item);
	last_tid = req->r_tid;

	do {
		ceph_osdc_get_request(req);
		spin_unlock(&ci->i_unsafe_lock);

		dout("sync_write_wait on tid %llu (until %llu)\n",
		     req->r_tid, last_tid);
		wait_for_completion(&req->r_safe_completion);
		ceph_osdc_put_request(req);

		spin_lock(&ci->i_unsafe_lock);
		/*
		 * from here on look at first entry in chain, since we
		 * only want to wait for anything older than last_tid
		 */
		if (list_empty(head))
			break;
		req = list_first_entry(head, struct ceph_osd_request,
				       r_unsafe_item);
	} while (req->r_tid < last_tid);
out:
	spin_unlock(&ci->i_unsafe_lock);
}

static ssize_t
ceph_direct_read_write(struct kiocb *iocb, struct iov_iter *iter,
+8 −0
Original line number Diff line number Diff line
@@ -585,6 +585,14 @@ int ceph_drop_inode(struct inode *inode)
	return 1;
}

void ceph_evict_inode(struct inode *inode)
{
	/* wait unsafe sync writes */
	ceph_sync_write_wait(inode);
	truncate_inode_pages_final(&inode->i_data);
	clear_inode(inode);
}

static inline blkcnt_t calc_inode_blocks(u64 size)
{
	return (size + (1<<9) - 1) >> 9;
+1 −0
Original line number Diff line number Diff line
@@ -731,6 +731,7 @@ static const struct super_operations ceph_super_ops = {
	.destroy_inode	= ceph_destroy_inode,
	.write_inode    = ceph_write_inode,
	.drop_inode	= ceph_drop_inode,
	.evict_inode	= ceph_evict_inode,
	.sync_fs        = ceph_sync_fs,
	.put_super	= ceph_put_super,
	.show_options   = ceph_show_options,
+2 −0
Original line number Diff line number Diff line
@@ -749,6 +749,7 @@ extern const struct inode_operations ceph_file_iops;
extern struct inode *ceph_alloc_inode(struct super_block *sb);
extern void ceph_destroy_inode(struct inode *inode);
extern int ceph_drop_inode(struct inode *inode);
extern void ceph_evict_inode(struct inode *inode);

extern struct inode *ceph_get_inode(struct super_block *sb,
				    struct ceph_vino vino);
@@ -927,6 +928,7 @@ extern int ceph_atomic_open(struct inode *dir, struct dentry *dentry,
extern int ceph_release(struct inode *inode, struct file *filp);
extern void ceph_fill_inline_data(struct inode *inode, struct page *locked_page,
				  char *data, size_t len);
extern void ceph_sync_write_wait(struct inode *inode);
/* dir.c */
extern const struct file_operations ceph_dir_fops;
extern const struct file_operations ceph_snapdir_fops;