Skip to content

Commit 5d6451b

Browse files
jtlaytonidryomov
authored andcommitted
ceph: shut down access to inode when async create fails
Add proper error handling for when an async create fails. The inode never existed, so any dirty caps or data are now toast. We already d_drop the dentry in that case, but the now-stale inode may still be around. We want to shut down access to these inodes, and ensure that they can't harbor any more dirty data, which can cause problems at umount time. When this occurs, flag such inodes as being SHUTDOWN, and trash any caps and cap flushes that may be in flight for them, and invalidate the pagecache for the inode. Add a new helper that can check whether an inode or an entire mount is now shut down, and call it instead of accessing the mount_state directly in places where we test that now. URL: https://tracker.ceph.com/issues/51279 Signed-off-by: Jeff Layton <[email protected]> Signed-off-by: Ilya Dryomov <[email protected]>
1 parent 36e6da9 commit 5d6451b

File tree

7 files changed

+85
-15
lines changed

7 files changed

+85
-15
lines changed

fs/ceph/addr.c

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -724,7 +724,7 @@ static int ceph_writepages_start(struct address_space *mapping,
724724
wbc->sync_mode == WB_SYNC_NONE ? "NONE" :
725725
(wbc->sync_mode == WB_SYNC_ALL ? "ALL" : "HOLD"));
726726

727-
if (READ_ONCE(fsc->mount_state) >= CEPH_MOUNT_SHUTDOWN) {
727+
if (ceph_inode_is_shutdown(inode)) {
728728
if (ci->i_wrbuffer_ref > 0) {
729729
pr_warn_ratelimited(
730730
"writepage_start %p %lld forced umount\n",
@@ -1145,12 +1145,12 @@ static struct ceph_snap_context *
11451145
ceph_find_incompatible(struct page *page)
11461146
{
11471147
struct inode *inode = page->mapping->host;
1148-
struct ceph_fs_client *fsc = ceph_inode_to_client(inode);
11491148
struct ceph_inode_info *ci = ceph_inode(inode);
11501149

1151-
if (READ_ONCE(fsc->mount_state) >= CEPH_MOUNT_SHUTDOWN) {
1152-
dout(" page %p forced umount\n", page);
1153-
return ERR_PTR(-EIO);
1150+
if (ceph_inode_is_shutdown(inode)) {
1151+
dout(" page %p %llx:%llx is shutdown\n", page,
1152+
ceph_vinop(inode));
1153+
return ERR_PTR(-ESTALE);
11541154
}
11551155

11561156
for (;;) {
@@ -1345,6 +1345,9 @@ static vm_fault_t ceph_filemap_fault(struct vm_fault *vmf)
13451345
sigset_t oldset;
13461346
vm_fault_t ret = VM_FAULT_SIGBUS;
13471347

1348+
if (ceph_inode_is_shutdown(inode))
1349+
return ret;
1350+
13481351
ceph_block_sigs(&oldset);
13491352

13501353
dout("filemap_fault %p %llx.%llx %llu trying to get caps\n",
@@ -1436,6 +1439,9 @@ static vm_fault_t ceph_page_mkwrite(struct vm_fault *vmf)
14361439
sigset_t oldset;
14371440
vm_fault_t ret = VM_FAULT_SIGBUS;
14381441

1442+
if (ceph_inode_is_shutdown(inode))
1443+
return ret;
1444+
14391445
prealloc_cf = ceph_alloc_cap_flush();
14401446
if (!prealloc_cf)
14411447
return VM_FAULT_OOM;

fs/ceph/caps.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1188,11 +1188,11 @@ void ceph_remove_cap(struct ceph_cap *cap, bool queue_release)
11881188

11891189
lockdep_assert_held(&ci->i_ceph_lock);
11901190

1191-
fsc = ceph_sb_to_client(ci->vfs_inode.i_sb);
1191+
fsc = ceph_inode_to_client(&ci->vfs_inode);
11921192
WARN_ON_ONCE(ci->i_auth_cap == cap &&
11931193
!list_empty(&ci->i_dirty_item) &&
11941194
!fsc->blocklisted &&
1195-
READ_ONCE(fsc->mount_state) != CEPH_MOUNT_SHUTDOWN);
1195+
!ceph_inode_is_shutdown(&ci->vfs_inode));
11961196

11971197
__ceph_remove_cap(cap, queue_release);
11981198
}
@@ -2750,9 +2750,9 @@ static int try_get_cap_refs(struct inode *inode, int need, int want,
27502750
goto out_unlock;
27512751
}
27522752

2753-
if (READ_ONCE(mdsc->fsc->mount_state) >= CEPH_MOUNT_SHUTDOWN) {
2754-
dout("get_cap_refs %p forced umount\n", inode);
2755-
ret = -EIO;
2753+
if (ceph_inode_is_shutdown(inode)) {
2754+
dout("get_cap_refs %p inode is shutdown\n", inode);
2755+
ret = -ESTALE;
27562756
goto out_unlock;
27572757
}
27582758
mds_wanted = __ceph_caps_mds_wanted(ci, false);
@@ -4604,7 +4604,7 @@ int ceph_purge_inode_cap(struct inode *inode, struct ceph_cap *cap, bool *invali
46044604
if (is_auth) {
46054605
struct ceph_cap_flush *cf;
46064606

4607-
if (READ_ONCE(fsc->mount_state) >= CEPH_MOUNT_SHUTDOWN) {
4607+
if (ceph_inode_is_shutdown(inode)) {
46084608
if (inode->i_data.nrpages > 0)
46094609
*invalidate = true;
46104610
if (ci->i_wrbuffer_ref > 0)

fs/ceph/export.c

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,11 @@ static struct inode *__lookup_inode(struct super_block *sb, u64 ino)
157157
ceph_mdsc_put_request(req);
158158
if (!inode)
159159
return err < 0 ? ERR_PTR(err) : ERR_PTR(-ESTALE);
160+
} else {
161+
if (ceph_inode_is_shutdown(inode)) {
162+
iput(inode);
163+
return ERR_PTR(-ESTALE);
164+
}
160165
}
161166
return inode;
162167
}
@@ -223,8 +228,13 @@ static struct dentry *__snapfh_to_dentry(struct super_block *sb,
223228
return ERR_PTR(-ESTALE);
224229

225230
inode = ceph_find_inode(sb, vino);
226-
if (inode)
231+
if (inode) {
232+
if (ceph_inode_is_shutdown(inode)) {
233+
iput(inode);
234+
return ERR_PTR(-ESTALE);
235+
}
227236
return d_obtain_alias(inode);
237+
}
228238

229239
req = ceph_mdsc_create_request(mdsc, CEPH_MDS_OP_LOOKUPINO,
230240
USE_ANY_MDS);

fs/ceph/file.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -525,6 +525,7 @@ static void ceph_async_create_cb(struct ceph_mds_client *mdsc,
525525

526526
if (result) {
527527
struct dentry *dentry = req->r_dentry;
528+
struct inode *inode = d_inode(dentry);
528529
int pathlen = 0;
529530
u64 base = 0;
530531
char *path = ceph_mdsc_build_path(req->r_dentry, &pathlen,
@@ -534,7 +535,8 @@ static void ceph_async_create_cb(struct ceph_mds_client *mdsc,
534535
if (!d_unhashed(dentry))
535536
d_drop(dentry);
536537

537-
/* FIXME: start returning I/O errors on all accesses? */
538+
ceph_inode_shutdown(inode);
539+
538540
pr_warn("ceph: async create failure path=(%llx)%s result=%d!\n",
539541
base, IS_ERR(path) ? "<<bad>>" : path, result);
540542
ceph_mdsc_free_path(path, pathlen);
@@ -1526,6 +1528,9 @@ static ssize_t ceph_read_iter(struct kiocb *iocb, struct iov_iter *to)
15261528
dout("aio_read %p %llx.%llx %llu~%u trying to get caps on %p\n",
15271529
inode, ceph_vinop(inode), iocb->ki_pos, (unsigned)len, inode);
15281530

1531+
if (ceph_inode_is_shutdown(inode))
1532+
return -ESTALE;
1533+
15291534
if (direct_lock)
15301535
ceph_start_io_direct(inode);
15311536
else
@@ -1678,6 +1683,9 @@ static ssize_t ceph_write_iter(struct kiocb *iocb, struct iov_iter *from)
16781683
loff_t pos;
16791684
loff_t limit = max(i_size_read(inode), fsc->max_file_size);
16801685

1686+
if (ceph_inode_is_shutdown(inode))
1687+
return -ESTALE;
1688+
16811689
if (ceph_snap(inode) != CEPH_NOSNAP)
16821690
return -EROFS;
16831691

fs/ceph/inode.c

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1841,13 +1841,12 @@ void ceph_queue_inode_work(struct inode *inode, int work_bit)
18411841
static void ceph_do_invalidate_pages(struct inode *inode)
18421842
{
18431843
struct ceph_inode_info *ci = ceph_inode(inode);
1844-
struct ceph_fs_client *fsc = ceph_inode_to_client(inode);
18451844
u32 orig_gen;
18461845
int check = 0;
18471846

18481847
mutex_lock(&ci->i_truncate_mutex);
18491848

1850-
if (READ_ONCE(fsc->mount_state) >= CEPH_MOUNT_SHUTDOWN) {
1849+
if (ceph_inode_is_shutdown(inode)) {
18511850
pr_warn_ratelimited("%s: inode %llx.%llx is shut down\n",
18521851
__func__, ceph_vinop(inode));
18531852
mapping_set_error(inode->i_mapping, -EIO);
@@ -2218,6 +2217,9 @@ int ceph_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
22182217
if (ceph_snap(inode) != CEPH_NOSNAP)
22192218
return -EROFS;
22202219

2220+
if (ceph_inode_is_shutdown(inode))
2221+
return -ESTALE;
2222+
22212223
err = setattr_prepare(&init_user_ns, dentry, attr);
22222224
if (err != 0)
22232225
return err;
@@ -2348,6 +2350,9 @@ int ceph_getattr(struct user_namespace *mnt_userns, const struct path *path,
23482350
u32 valid_mask = STATX_BASIC_STATS;
23492351
int err = 0;
23502352

2353+
if (ceph_inode_is_shutdown(inode))
2354+
return -ESTALE;
2355+
23512356
/* Skip the getattr altogether if we're asked not to sync */
23522357
if (!(flags & AT_STATX_DONT_SYNC)) {
23532358
err = ceph_do_getattr(inode,
@@ -2395,3 +2400,27 @@ int ceph_getattr(struct user_namespace *mnt_userns, const struct path *path,
23952400
stat->result_mask = request_mask & valid_mask;
23962401
return err;
23972402
}
2403+
2404+
void ceph_inode_shutdown(struct inode *inode)
2405+
{
2406+
struct ceph_inode_info *ci = ceph_inode(inode);
2407+
struct rb_node *p;
2408+
int iputs = 0;
2409+
bool invalidate = false;
2410+
2411+
spin_lock(&ci->i_ceph_lock);
2412+
ci->i_ceph_flags |= CEPH_I_SHUTDOWN;
2413+
p = rb_first(&ci->i_caps);
2414+
while (p) {
2415+
struct ceph_cap *cap = rb_entry(p, struct ceph_cap, ci_node);
2416+
2417+
p = rb_next(p);
2418+
iputs += ceph_purge_inode_cap(inode, cap, &invalidate);
2419+
}
2420+
spin_unlock(&ci->i_ceph_lock);
2421+
2422+
if (invalidate)
2423+
ceph_queue_invalidate(inode);
2424+
while (iputs--)
2425+
iput(inode);
2426+
}

fs/ceph/locks.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,9 @@ int ceph_lock(struct file *file, int cmd, struct file_lock *fl)
241241
if (!(fl->fl_flags & FL_POSIX))
242242
return -ENOLCK;
243243

244+
if (ceph_inode_is_shutdown(inode))
245+
return -ESTALE;
246+
244247
dout("ceph_lock, fl_owner: %p\n", fl->fl_owner);
245248

246249
/* set wait bit as appropriate, then make command as Ceph expects it*/
@@ -306,6 +309,9 @@ int ceph_flock(struct file *file, int cmd, struct file_lock *fl)
306309
if (fl->fl_type & LOCK_MAND)
307310
return -EOPNOTSUPP;
308311

312+
if (ceph_inode_is_shutdown(inode))
313+
return -ESTALE;
314+
309315
dout("ceph_flock, fl_file: %p\n", fl->fl_file);
310316

311317
spin_lock(&ci->i_ceph_lock);

fs/ceph/super.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -581,6 +581,7 @@ static inline struct inode *ceph_find_inode(struct super_block *sb,
581581
#define CEPH_I_ODIRECT (1 << 11) /* inode in direct I/O mode */
582582
#define CEPH_ASYNC_CREATE_BIT (12) /* async create in flight for this */
583583
#define CEPH_I_ASYNC_CREATE (1 << CEPH_ASYNC_CREATE_BIT)
584+
#define CEPH_I_SHUTDOWN (1 << 13) /* inode is no longer usable */
584585

585586
/*
586587
* Masks of ceph inode work.
@@ -1028,6 +1029,16 @@ extern int ceph_setattr(struct user_namespace *mnt_userns,
10281029
extern int ceph_getattr(struct user_namespace *mnt_userns,
10291030
const struct path *path, struct kstat *stat,
10301031
u32 request_mask, unsigned int flags);
1032+
void ceph_inode_shutdown(struct inode *inode);
1033+
1034+
static inline bool ceph_inode_is_shutdown(struct inode *inode)
1035+
{
1036+
unsigned long flags = READ_ONCE(ceph_inode(inode)->i_ceph_flags);
1037+
struct ceph_fs_client *fsc = ceph_inode_to_client(inode);
1038+
int state = READ_ONCE(fsc->mount_state);
1039+
1040+
return (flags & CEPH_I_SHUTDOWN) || state >= CEPH_MOUNT_SHUTDOWN;
1041+
}
10311042

10321043
/* xattr.c */
10331044
int __ceph_setxattr(struct inode *, const char *, const void *, size_t, int);

0 commit comments

Comments
 (0)