Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ceph: allow queueing cap/snap handling after putting cap references

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 <jlayton@kernel.org>
Reviewed-by: Ilya Dryomov <idryomov@gmail.com>
Signed-off-by: Ilya Dryomov <idryomov@gmail.com>

authored by

Jeff Layton and committed by
Ilya Dryomov
a8810cdc 64f28c62

+48 -8
+1 -1
fs/ceph/addr.c
··· 1662 1662 1663 1663 dout("page_mkwrite %p %llu~%zd dropping cap refs on %s ret %x\n", 1664 1664 inode, off, len, ceph_cap_string(got), ret); 1665 - ceph_put_cap_refs(ci, got); 1665 + ceph_put_cap_refs_async(ci, got); 1666 1666 out_free: 1667 1667 ceph_restore_sigs(&oldset); 1668 1668 sb_end_pagefault(inode->i_sb);
+25 -4
fs/ceph/caps.c
··· 3027 3027 return 0; 3028 3028 } 3029 3029 3030 + enum put_cap_refs_mode { 3031 + PUT_CAP_REFS_SYNC = 0, 3032 + PUT_CAP_REFS_NO_CHECK, 3033 + PUT_CAP_REFS_ASYNC, 3034 + }; 3035 + 3030 3036 /* 3031 3037 * Release cap refs. 3032 3038 * ··· 3043 3037 * cap_snap, and wake up any waiters. 3044 3038 */ 3045 3039 static void __ceph_put_cap_refs(struct ceph_inode_info *ci, int had, 3046 - bool skip_checking_caps) 3040 + enum put_cap_refs_mode mode) 3047 3041 { 3048 3042 struct inode *inode = &ci->vfs_inode; 3049 3043 int last = 0, put = 0, flushsnaps = 0, wake = 0; ··· 3099 3093 dout("put_cap_refs %p had %s%s%s\n", inode, ceph_cap_string(had), 3100 3094 last ? " last" : "", put ? " put" : ""); 3101 3095 3102 - if (!skip_checking_caps) { 3096 + switch (mode) { 3097 + case PUT_CAP_REFS_SYNC: 3103 3098 if (last) 3104 3099 ceph_check_caps(ci, 0, NULL); 3105 3100 else if (flushsnaps) 3106 3101 ceph_flush_snaps(ci, NULL); 3102 + break; 3103 + case PUT_CAP_REFS_ASYNC: 3104 + if (last) 3105 + ceph_queue_check_caps(inode); 3106 + else if (flushsnaps) 3107 + ceph_queue_flush_snaps(inode); 3108 + break; 3109 + default: 3110 + break; 3107 3111 } 3108 3112 if (wake) 3109 3113 wake_up_all(&ci->i_cap_wq); ··· 3123 3107 3124 3108 void ceph_put_cap_refs(struct ceph_inode_info *ci, int had) 3125 3109 { 3126 - __ceph_put_cap_refs(ci, had, false); 3110 + __ceph_put_cap_refs(ci, had, PUT_CAP_REFS_SYNC); 3111 + } 3112 + 3113 + void ceph_put_cap_refs_async(struct ceph_inode_info *ci, int had) 3114 + { 3115 + __ceph_put_cap_refs(ci, had, PUT_CAP_REFS_ASYNC); 3127 3116 } 3128 3117 3129 3118 void ceph_put_cap_refs_no_check_caps(struct ceph_inode_info *ci, int had) 3130 3119 { 3131 - __ceph_put_cap_refs(ci, had, true); 3120 + __ceph_put_cap_refs(ci, had, PUT_CAP_REFS_NO_CHECK); 3132 3121 } 3133 3122 3134 3123 /*
+6
fs/ceph/inode.c
··· 1965 1965 if (test_and_clear_bit(CEPH_I_WORK_VMTRUNCATE, &ci->i_work_mask)) 1966 1966 __ceph_do_pending_vmtruncate(inode); 1967 1967 1968 + if (test_and_clear_bit(CEPH_I_WORK_CHECK_CAPS, &ci->i_work_mask)) 1969 + ceph_check_caps(ci, 0, NULL); 1970 + 1971 + if (test_and_clear_bit(CEPH_I_WORK_FLUSH_SNAPS, &ci->i_work_mask)) 1972 + ceph_flush_snaps(ci, NULL); 1973 + 1968 1974 iput(inode); 1969 1975 } 1970 1976
+16 -3
fs/ceph/super.h
··· 562 562 /* 563 563 * Masks of ceph inode work. 564 564 */ 565 - #define CEPH_I_WORK_WRITEBACK 0 /* writeback */ 566 - #define CEPH_I_WORK_INVALIDATE_PAGES 1 /* invalidate pages */ 567 - #define CEPH_I_WORK_VMTRUNCATE 2 /* vmtruncate */ 565 + #define CEPH_I_WORK_WRITEBACK 0 566 + #define CEPH_I_WORK_INVALIDATE_PAGES 1 567 + #define CEPH_I_WORK_VMTRUNCATE 2 568 + #define CEPH_I_WORK_CHECK_CAPS 3 569 + #define CEPH_I_WORK_FLUSH_SNAPS 4 568 570 569 571 /* 570 572 * We set the ERROR_WRITE bit when we start seeing write errors on an inode ··· 984 982 ceph_queue_inode_work(inode, CEPH_I_WORK_WRITEBACK); 985 983 } 986 984 985 + static inline void ceph_queue_check_caps(struct inode *inode) 986 + { 987 + ceph_queue_inode_work(inode, CEPH_I_WORK_CHECK_CAPS); 988 + } 989 + 990 + static inline void ceph_queue_flush_snaps(struct inode *inode) 991 + { 992 + ceph_queue_inode_work(inode, CEPH_I_WORK_FLUSH_SNAPS); 993 + } 994 + 987 995 extern int __ceph_do_getattr(struct inode *inode, struct page *locked_page, 988 996 int mask, bool force); 989 997 static inline int ceph_do_getattr(struct inode *inode, int mask, bool force) ··· 1132 1120 bool snap_rwsem_locked); 1133 1121 extern void ceph_get_cap_refs(struct ceph_inode_info *ci, int caps); 1134 1122 extern void ceph_put_cap_refs(struct ceph_inode_info *ci, int had); 1123 + extern void ceph_put_cap_refs_async(struct ceph_inode_info *ci, int had); 1135 1124 extern void ceph_put_cap_refs_no_check_caps(struct ceph_inode_info *ci, 1136 1125 int had); 1137 1126 extern void ceph_put_wrbuffer_cap_refs(struct ceph_inode_info *ci, int nr,