Skip to content

Commit

Permalink
ceph: track and report error of async metadata operation
Browse files Browse the repository at this point in the history
Use errseq_t to track and report errors of async metadata operations,
similar to how kernel handles errors during writeback.

If any dirty caps or any unsafe request gets dropped during session
eviction, record -EIO in corresponding inode's i_meta_err. The error
will be reported by subsequent fsync,

Signed-off-by: "Yan, Zheng" <[email protected]>
Reviewed-by: Jeff Layton <[email protected]>
Signed-off-by: Ilya Dryomov <[email protected]>
  • Loading branch information
ukernel authored and idryomov committed Sep 16, 2019
1 parent 7e6906c commit f4b9786
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 22 deletions.
24 changes: 17 additions & 7 deletions fs/ceph/caps.c
Original file line number Diff line number Diff line change
Expand Up @@ -2261,35 +2261,45 @@ static int unsafe_request_wait(struct inode *inode)

int ceph_fsync(struct file *file, loff_t start, loff_t end, int datasync)
{
struct ceph_file_info *fi = file->private_data;
struct inode *inode = file->f_mapping->host;
struct ceph_inode_info *ci = ceph_inode(inode);
u64 flush_tid;
int ret;
int ret, err;
int dirty;

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

ret = file_write_and_wait_range(file, start, end);
if (ret < 0)
goto out;

if (datasync)
goto out;

dirty = try_flush_caps(inode, &flush_tid);
dout("fsync dirty caps are %s\n", ceph_cap_string(dirty));

ret = unsafe_request_wait(inode);
err = unsafe_request_wait(inode);

/*
* only wait on non-file metadata writeback (the mds
* can recover size and mtime, so we don't need to
* wait for that)
*/
if (!ret && (dirty & ~CEPH_CAP_ANY_FILE_WR)) {
ret = wait_event_interruptible(ci->i_cap_wq,
if (!err && (dirty & ~CEPH_CAP_ANY_FILE_WR)) {
err = wait_event_interruptible(ci->i_cap_wq,
caps_are_flushed(inode, flush_tid));
}

if (err < 0)
ret = err;

if (errseq_check(&ci->i_meta_err, READ_ONCE(fi->meta_err))) {
spin_lock(&file->f_lock);
err = errseq_check_and_advance(&ci->i_meta_err,
&fi->meta_err);
spin_unlock(&file->f_lock);
if (err < 0)
ret = err;
}
out:
dout("fsync %p%s result=%d\n", inode, datasync ? " datasync" : "", ret);
return ret;
Expand Down
6 changes: 4 additions & 2 deletions fs/ceph/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ prepare_open_request(struct super_block *sb, int flags, int create_mode)
static int ceph_init_file_info(struct inode *inode, struct file *file,
int fmode, bool isdir)
{
struct ceph_inode_info *ci = ceph_inode(inode);
struct ceph_file_info *fi;

dout("%s %p %p 0%o (%s)\n", __func__, inode, file,
Expand All @@ -211,7 +212,7 @@ static int ceph_init_file_info(struct inode *inode, struct file *file,
struct ceph_dir_file_info *dfi =
kmem_cache_zalloc(ceph_dir_file_cachep, GFP_KERNEL);
if (!dfi) {
ceph_put_fmode(ceph_inode(inode), fmode); /* clean up */
ceph_put_fmode(ci, fmode); /* clean up */
return -ENOMEM;
}

Expand All @@ -222,7 +223,7 @@ static int ceph_init_file_info(struct inode *inode, struct file *file,
} else {
fi = kmem_cache_zalloc(ceph_file_cachep, GFP_KERNEL);
if (!fi) {
ceph_put_fmode(ceph_inode(inode), fmode); /* clean up */
ceph_put_fmode(ci, fmode); /* clean up */
return -ENOMEM;
}

Expand All @@ -232,6 +233,7 @@ static int ceph_init_file_info(struct inode *inode, struct file *file,
fi->fmode = fmode;
spin_lock_init(&fi->rw_contexts_lock);
INIT_LIST_HEAD(&fi->rw_contexts);
fi->meta_err = errseq_sample(&ci->i_meta_err);

return 0;
}
Expand Down
2 changes: 2 additions & 0 deletions fs/ceph/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,8 @@ struct inode *ceph_alloc_inode(struct super_block *sb)

ceph_fscache_inode_init(ci);

ci->i_meta_err = 0;

return &ci->vfs_inode;
}

Expand Down
40 changes: 27 additions & 13 deletions fs/ceph/mds_client.c
Original file line number Diff line number Diff line change
Expand Up @@ -1270,6 +1270,7 @@ static void cleanup_session_requests(struct ceph_mds_client *mdsc,
{
struct ceph_mds_request *req;
struct rb_node *p;
struct ceph_inode_info *ci;

dout("cleanup_session_requests mds%d\n", session->s_mds);
mutex_lock(&mdsc->mutex);
Expand All @@ -1278,6 +1279,16 @@ static void cleanup_session_requests(struct ceph_mds_client *mdsc,
struct ceph_mds_request, r_unsafe_item);
pr_warn_ratelimited(" dropping unsafe request %llu\n",
req->r_tid);
if (req->r_target_inode) {
/* dropping unsafe change of inode's attributes */
ci = ceph_inode(req->r_target_inode);
errseq_set(&ci->i_meta_err, -EIO);
}
if (req->r_unsafe_dir) {
/* dropping unsafe directory operation */
ci = ceph_inode(req->r_unsafe_dir);
errseq_set(&ci->i_meta_err, -EIO);
}
__unregister_request(mdsc, req);
}
/* zero r_attempts, so kick_requests() will re-send requests */
Expand Down Expand Up @@ -1370,7 +1381,7 @@ static int remove_session_caps_cb(struct inode *inode, struct ceph_cap *cap,
struct ceph_fs_client *fsc = (struct ceph_fs_client *)arg;
struct ceph_inode_info *ci = ceph_inode(inode);
LIST_HEAD(to_remove);
bool drop = false;
bool dirty_dropped = false;
bool invalidate = false;

dout("removing cap %p, ci is %p, inode is %p\n",
Expand Down Expand Up @@ -1405,7 +1416,7 @@ static int remove_session_caps_cb(struct inode *inode, struct ceph_cap *cap,
inode, ceph_ino(inode));
ci->i_dirty_caps = 0;
list_del_init(&ci->i_dirty_item);
drop = true;
dirty_dropped = true;
}
if (!list_empty(&ci->i_flushing_item)) {
pr_warn_ratelimited(
Expand All @@ -1415,10 +1426,22 @@ static int remove_session_caps_cb(struct inode *inode, struct ceph_cap *cap,
ci->i_flushing_caps = 0;
list_del_init(&ci->i_flushing_item);
mdsc->num_cap_flushing--;
drop = true;
dirty_dropped = true;
}
spin_unlock(&mdsc->cap_dirty_lock);

if (dirty_dropped) {
errseq_set(&ci->i_meta_err, -EIO);

if (ci->i_wrbuffer_ref_head == 0 &&
ci->i_wr_ref == 0 &&
ci->i_dirty_caps == 0 &&
ci->i_flushing_caps == 0) {
ceph_put_snap_context(ci->i_head_snapc);
ci->i_head_snapc = NULL;
}
}

if (atomic_read(&ci->i_filelock_ref) > 0) {
/* make further file lock syscall return -EIO */
ci->i_ceph_flags |= CEPH_I_ERROR_FILELOCK;
Expand All @@ -1430,15 +1453,6 @@ static int remove_session_caps_cb(struct inode *inode, struct ceph_cap *cap,
list_add(&ci->i_prealloc_cap_flush->i_list, &to_remove);
ci->i_prealloc_cap_flush = NULL;
}

if (drop &&
ci->i_wrbuffer_ref_head == 0 &&
ci->i_wr_ref == 0 &&
ci->i_dirty_caps == 0 &&
ci->i_flushing_caps == 0) {
ceph_put_snap_context(ci->i_head_snapc);
ci->i_head_snapc = NULL;
}
}
spin_unlock(&ci->i_ceph_lock);
while (!list_empty(&to_remove)) {
Expand All @@ -1452,7 +1466,7 @@ static int remove_session_caps_cb(struct inode *inode, struct ceph_cap *cap,
wake_up_all(&ci->i_cap_wq);
if (invalidate)
ceph_queue_invalidate(inode);
if (drop)
if (dirty_dropped)
iput(inode);
return 0;
}
Expand Down
4 changes: 4 additions & 0 deletions fs/ceph/super.h
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,8 @@ struct ceph_inode_info {
struct fscache_cookie *fscache;
u32 i_fscache_gen;
#endif
errseq_t i_meta_err;

struct inode vfs_inode; /* at end */
};

Expand Down Expand Up @@ -703,6 +705,8 @@ struct ceph_file_info {

spinlock_t rw_contexts_lock;
struct list_head rw_contexts;

errseq_t meta_err;
};

struct ceph_dir_file_info {
Expand Down

0 comments on commit f4b9786

Please sign in to comment.