Skip to content

Commit

Permalink
ceph: allow queueing cap/snap handling after putting cap references
Browse files Browse the repository at this point in the history
Testing with the fscache overhaul has triggered some lockdep warnings
about circular lock dependencies involving page_mkwrite and the
mmap_lock. It'd be better to do the "real work" without the mmap lock
being held.

Change the skip_checking_caps parameter in __ceph_put_cap_refs to an
enum, and use that to determine whether to queue check_caps, do it
synchronously or not at all. Change ceph_page_mkwrite to do a
ceph_put_cap_refs_async().

Signed-off-by: Jeff Layton <[email protected]>
Reviewed-by: Ilya Dryomov <[email protected]>
Signed-off-by: Ilya Dryomov <[email protected]>
  • Loading branch information
jtlayton authored and idryomov committed Feb 16, 2021
1 parent 64f28c6 commit a8810cd
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 8 deletions.
2 changes: 1 addition & 1 deletion fs/ceph/addr.c
Original file line number Diff line number Diff line change
Expand Up @@ -1662,7 +1662,7 @@ static vm_fault_t ceph_page_mkwrite(struct vm_fault *vmf)

dout("page_mkwrite %p %llu~%zd dropping cap refs on %s ret %x\n",
inode, off, len, ceph_cap_string(got), ret);
ceph_put_cap_refs(ci, got);
ceph_put_cap_refs_async(ci, got);
out_free:
ceph_restore_sigs(&oldset);
sb_end_pagefault(inode->i_sb);
Expand Down
29 changes: 25 additions & 4 deletions fs/ceph/caps.c
Original file line number Diff line number Diff line change
Expand Up @@ -3027,6 +3027,12 @@ static int ceph_try_drop_cap_snap(struct ceph_inode_info *ci,
return 0;
}

enum put_cap_refs_mode {
PUT_CAP_REFS_SYNC = 0,
PUT_CAP_REFS_NO_CHECK,
PUT_CAP_REFS_ASYNC,
};

/*
* Release cap refs.
*
Expand All @@ -3037,7 +3043,7 @@ static int ceph_try_drop_cap_snap(struct ceph_inode_info *ci,
* cap_snap, and wake up any waiters.
*/
static void __ceph_put_cap_refs(struct ceph_inode_info *ci, int had,
bool skip_checking_caps)
enum put_cap_refs_mode mode)
{
struct inode *inode = &ci->vfs_inode;
int last = 0, put = 0, flushsnaps = 0, wake = 0;
Expand Down Expand Up @@ -3093,11 +3099,21 @@ static void __ceph_put_cap_refs(struct ceph_inode_info *ci, int had,
dout("put_cap_refs %p had %s%s%s\n", inode, ceph_cap_string(had),
last ? " last" : "", put ? " put" : "");

if (!skip_checking_caps) {
switch (mode) {
case PUT_CAP_REFS_SYNC:
if (last)
ceph_check_caps(ci, 0, NULL);
else if (flushsnaps)
ceph_flush_snaps(ci, NULL);
break;
case PUT_CAP_REFS_ASYNC:
if (last)
ceph_queue_check_caps(inode);
else if (flushsnaps)
ceph_queue_flush_snaps(inode);
break;
default:
break;
}
if (wake)
wake_up_all(&ci->i_cap_wq);
Expand All @@ -3107,12 +3123,17 @@ static void __ceph_put_cap_refs(struct ceph_inode_info *ci, int had,

void ceph_put_cap_refs(struct ceph_inode_info *ci, int had)
{
__ceph_put_cap_refs(ci, had, false);
__ceph_put_cap_refs(ci, had, PUT_CAP_REFS_SYNC);
}

void ceph_put_cap_refs_async(struct ceph_inode_info *ci, int had)
{
__ceph_put_cap_refs(ci, had, PUT_CAP_REFS_ASYNC);
}

void ceph_put_cap_refs_no_check_caps(struct ceph_inode_info *ci, int had)
{
__ceph_put_cap_refs(ci, had, true);
__ceph_put_cap_refs(ci, had, PUT_CAP_REFS_NO_CHECK);
}

/*
Expand Down
6 changes: 6 additions & 0 deletions fs/ceph/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -1965,6 +1965,12 @@ static void ceph_inode_work(struct work_struct *work)
if (test_and_clear_bit(CEPH_I_WORK_VMTRUNCATE, &ci->i_work_mask))
__ceph_do_pending_vmtruncate(inode);

if (test_and_clear_bit(CEPH_I_WORK_CHECK_CAPS, &ci->i_work_mask))
ceph_check_caps(ci, 0, NULL);

if (test_and_clear_bit(CEPH_I_WORK_FLUSH_SNAPS, &ci->i_work_mask))
ceph_flush_snaps(ci, NULL);

iput(inode);
}

Expand Down
19 changes: 16 additions & 3 deletions fs/ceph/super.h
Original file line number Diff line number Diff line change
Expand Up @@ -562,9 +562,11 @@ static inline struct inode *ceph_find_inode(struct super_block *sb,
/*
* Masks of ceph inode work.
*/
#define CEPH_I_WORK_WRITEBACK 0 /* writeback */
#define CEPH_I_WORK_INVALIDATE_PAGES 1 /* invalidate pages */
#define CEPH_I_WORK_VMTRUNCATE 2 /* vmtruncate */
#define CEPH_I_WORK_WRITEBACK 0
#define CEPH_I_WORK_INVALIDATE_PAGES 1
#define CEPH_I_WORK_VMTRUNCATE 2
#define CEPH_I_WORK_CHECK_CAPS 3
#define CEPH_I_WORK_FLUSH_SNAPS 4

/*
* We set the ERROR_WRITE bit when we start seeing write errors on an inode
Expand Down Expand Up @@ -982,6 +984,16 @@ static inline void ceph_queue_writeback(struct inode *inode)
ceph_queue_inode_work(inode, CEPH_I_WORK_WRITEBACK);
}

static inline void ceph_queue_check_caps(struct inode *inode)
{
ceph_queue_inode_work(inode, CEPH_I_WORK_CHECK_CAPS);
}

static inline void ceph_queue_flush_snaps(struct inode *inode)
{
ceph_queue_inode_work(inode, CEPH_I_WORK_FLUSH_SNAPS);
}

extern int __ceph_do_getattr(struct inode *inode, struct page *locked_page,
int mask, bool force);
static inline int ceph_do_getattr(struct inode *inode, int mask, bool force)
Expand Down Expand Up @@ -1120,6 +1132,7 @@ extern void ceph_take_cap_refs(struct ceph_inode_info *ci, int caps,
bool snap_rwsem_locked);
extern void ceph_get_cap_refs(struct ceph_inode_info *ci, int caps);
extern void ceph_put_cap_refs(struct ceph_inode_info *ci, int had);
extern void ceph_put_cap_refs_async(struct ceph_inode_info *ci, int had);
extern void ceph_put_cap_refs_no_check_caps(struct ceph_inode_info *ci,
int had);
extern void ceph_put_wrbuffer_cap_refs(struct ceph_inode_info *ci, int nr,
Expand Down

0 comments on commit a8810cd

Please sign in to comment.