ceph: wait unsafe sync writes for evicting inode
Otherwise ceph_sync_write_unsafe() may access/modify freed inode. Signed-off-by: Yan, Zheng <zyan@redhat.com>
This commit is contained in:
parent
fc8c3892f3
commit
9a5530c638
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user