Skip to content

Commit a6d37cc

Browse files
lxbszidryomov
authored andcommitted
ceph: remove the capsnaps when removing caps
capsnaps will take inode references via ihold when queueing to flush. When force unmounting, the client will just close the sessions and may never get a flush reply, causing a leak and inode ref leak. Fix this by removing the capsnaps for an inode when removing the caps. URL: https://tracker.ceph.com/issues/52295 Signed-off-by: Xiubo Li <[email protected]> Reviewed-by: Jeff Layton <[email protected]> Signed-off-by: Ilya Dryomov <[email protected]>
1 parent b11ed50 commit a6d37cc

File tree

3 files changed

+87
-18
lines changed

3 files changed

+87
-18
lines changed

fs/ceph/caps.c

Lines changed: 51 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3159,7 +3159,16 @@ void ceph_put_wrbuffer_cap_refs(struct ceph_inode_info *ci, int nr,
31593159
break;
31603160
}
31613161
}
3162-
BUG_ON(!found);
3162+
3163+
if (!found) {
3164+
/*
3165+
* The capsnap should already be removed when removing
3166+
* auth cap in the case of a forced unmount.
3167+
*/
3168+
WARN_ON_ONCE(ci->i_auth_cap);
3169+
goto unlock;
3170+
}
3171+
31633172
capsnap->dirty_pages -= nr;
31643173
if (capsnap->dirty_pages == 0) {
31653174
complete_capsnap = true;
@@ -3181,6 +3190,7 @@ void ceph_put_wrbuffer_cap_refs(struct ceph_inode_info *ci, int nr,
31813190
complete_capsnap ? " (complete capsnap)" : "");
31823191
}
31833192

3193+
unlock:
31843194
spin_unlock(&ci->i_ceph_lock);
31853195

31863196
if (last) {
@@ -3651,6 +3661,43 @@ static void handle_cap_flush_ack(struct inode *inode, u64 flush_tid,
36513661
iput(inode);
36523662
}
36533663

3664+
void __ceph_remove_capsnap(struct inode *inode, struct ceph_cap_snap *capsnap,
3665+
bool *wake_ci, bool *wake_mdsc)
3666+
{
3667+
struct ceph_inode_info *ci = ceph_inode(inode);
3668+
struct ceph_mds_client *mdsc = ceph_sb_to_client(inode->i_sb)->mdsc;
3669+
bool ret;
3670+
3671+
lockdep_assert_held(&ci->i_ceph_lock);
3672+
3673+
dout("removing capsnap %p, inode %p ci %p\n", capsnap, inode, ci);
3674+
3675+
list_del_init(&capsnap->ci_item);
3676+
ret = __detach_cap_flush_from_ci(ci, &capsnap->cap_flush);
3677+
if (wake_ci)
3678+
*wake_ci = ret;
3679+
3680+
spin_lock(&mdsc->cap_dirty_lock);
3681+
if (list_empty(&ci->i_cap_flush_list))
3682+
list_del_init(&ci->i_flushing_item);
3683+
3684+
ret = __detach_cap_flush_from_mdsc(mdsc, &capsnap->cap_flush);
3685+
if (wake_mdsc)
3686+
*wake_mdsc = ret;
3687+
spin_unlock(&mdsc->cap_dirty_lock);
3688+
}
3689+
3690+
void ceph_remove_capsnap(struct inode *inode, struct ceph_cap_snap *capsnap,
3691+
bool *wake_ci, bool *wake_mdsc)
3692+
{
3693+
struct ceph_inode_info *ci = ceph_inode(inode);
3694+
3695+
lockdep_assert_held(&ci->i_ceph_lock);
3696+
3697+
WARN_ON_ONCE(capsnap->dirty_pages || capsnap->writing);
3698+
__ceph_remove_capsnap(inode, capsnap, wake_ci, wake_mdsc);
3699+
}
3700+
36543701
/*
36553702
* Handle FLUSHSNAP_ACK. MDS has flushed snap data to disk and we can
36563703
* throw away our cap_snap.
@@ -3688,23 +3735,10 @@ static void handle_cap_flushsnap_ack(struct inode *inode, u64 flush_tid,
36883735
capsnap, capsnap->follows);
36893736
}
36903737
}
3691-
if (flushed) {
3692-
WARN_ON(capsnap->dirty_pages || capsnap->writing);
3693-
dout(" removing %p cap_snap %p follows %lld\n",
3694-
inode, capsnap, follows);
3695-
list_del(&capsnap->ci_item);
3696-
wake_ci |= __detach_cap_flush_from_ci(ci, &capsnap->cap_flush);
3697-
3698-
spin_lock(&mdsc->cap_dirty_lock);
3699-
3700-
if (list_empty(&ci->i_cap_flush_list))
3701-
list_del_init(&ci->i_flushing_item);
3702-
3703-
wake_mdsc |= __detach_cap_flush_from_mdsc(mdsc,
3704-
&capsnap->cap_flush);
3705-
spin_unlock(&mdsc->cap_dirty_lock);
3706-
}
3738+
if (flushed)
3739+
ceph_remove_capsnap(inode, capsnap, &wake_ci, &wake_mdsc);
37073740
spin_unlock(&ci->i_ceph_lock);
3741+
37083742
if (flushed) {
37093743
ceph_put_snap_context(capsnap->context);
37103744
ceph_put_cap_snap(capsnap);

fs/ceph/mds_client.c

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1604,22 +1604,46 @@ int ceph_iterate_session_caps(struct ceph_mds_session *session,
16041604
return ret;
16051605
}
16061606

1607+
static int remove_capsnaps(struct ceph_mds_client *mdsc, struct inode *inode)
1608+
{
1609+
struct ceph_inode_info *ci = ceph_inode(inode);
1610+
struct ceph_cap_snap *capsnap;
1611+
int capsnap_release = 0;
1612+
1613+
lockdep_assert_held(&ci->i_ceph_lock);
1614+
1615+
dout("removing capsnaps, ci is %p, inode is %p\n", ci, inode);
1616+
1617+
while (!list_empty(&ci->i_cap_snaps)) {
1618+
capsnap = list_first_entry(&ci->i_cap_snaps,
1619+
struct ceph_cap_snap, ci_item);
1620+
__ceph_remove_capsnap(inode, capsnap, NULL, NULL);
1621+
ceph_put_snap_context(capsnap->context);
1622+
ceph_put_cap_snap(capsnap);
1623+
capsnap_release++;
1624+
}
1625+
wake_up_all(&ci->i_cap_wq);
1626+
wake_up_all(&mdsc->cap_flushing_wq);
1627+
return capsnap_release;
1628+
}
1629+
16071630
static int remove_session_caps_cb(struct inode *inode, struct ceph_cap *cap,
16081631
void *arg)
16091632
{
16101633
struct ceph_fs_client *fsc = (struct ceph_fs_client *)arg;
1634+
struct ceph_mds_client *mdsc = fsc->mdsc;
16111635
struct ceph_inode_info *ci = ceph_inode(inode);
16121636
LIST_HEAD(to_remove);
16131637
bool dirty_dropped = false;
16141638
bool invalidate = false;
1639+
int capsnap_release = 0;
16151640

16161641
dout("removing cap %p, ci is %p, inode is %p\n",
16171642
cap, ci, &ci->vfs_inode);
16181643
spin_lock(&ci->i_ceph_lock);
16191644
__ceph_remove_cap(cap, false);
16201645
if (!ci->i_auth_cap) {
16211646
struct ceph_cap_flush *cf;
1622-
struct ceph_mds_client *mdsc = fsc->mdsc;
16231647

16241648
if (READ_ONCE(fsc->mount_state) >= CEPH_MOUNT_SHUTDOWN) {
16251649
if (inode->i_data.nrpages > 0)
@@ -1683,6 +1707,9 @@ static int remove_session_caps_cb(struct inode *inode, struct ceph_cap *cap,
16831707
list_add(&ci->i_prealloc_cap_flush->i_list, &to_remove);
16841708
ci->i_prealloc_cap_flush = NULL;
16851709
}
1710+
1711+
if (!list_empty(&ci->i_cap_snaps))
1712+
capsnap_release = remove_capsnaps(mdsc, inode);
16861713
}
16871714
spin_unlock(&ci->i_ceph_lock);
16881715
while (!list_empty(&to_remove)) {
@@ -1699,6 +1726,8 @@ static int remove_session_caps_cb(struct inode *inode, struct ceph_cap *cap,
16991726
ceph_queue_invalidate(inode);
17001727
if (dirty_dropped)
17011728
iput(inode);
1729+
while (capsnap_release--)
1730+
iput(inode);
17021731
return 0;
17031732
}
17041733

fs/ceph/super.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1163,6 +1163,12 @@ extern void ceph_put_cap_refs_no_check_caps(struct ceph_inode_info *ci,
11631163
int had);
11641164
extern void ceph_put_wrbuffer_cap_refs(struct ceph_inode_info *ci, int nr,
11651165
struct ceph_snap_context *snapc);
1166+
extern void __ceph_remove_capsnap(struct inode *inode,
1167+
struct ceph_cap_snap *capsnap,
1168+
bool *wake_ci, bool *wake_mdsc);
1169+
extern void ceph_remove_capsnap(struct inode *inode,
1170+
struct ceph_cap_snap *capsnap,
1171+
bool *wake_ci, bool *wake_mdsc);
11661172
extern void ceph_flush_snaps(struct ceph_inode_info *ci,
11671173
struct ceph_mds_session **psession);
11681174
extern bool __ceph_should_report_size(struct ceph_inode_info *ci);

0 commit comments

Comments
 (0)