|
| 1 | +gfs2: Revise glock reference counting model |
| 2 | + |
| 3 | +jira LE-3201 |
| 4 | +Rebuild_History Non-Buildable kernel-rt-4.18.0-553.32.1.rt7.373.el8_10 |
| 5 | +commit-author Andreas Gruenbacher < [email protected]> |
| 6 | +commit 767fd5a0160774178a597b7a7b6e07915fe00efa |
| 7 | +Empty-Commit: Cherry-Pick Conflicts during history rebuild. |
| 8 | +Will be included in final tarball splat. Ref for failed cherry-pick at: |
| 9 | +ciq/ciq_backports/kernel-rt-4.18.0-553.32.1.rt7.373.el8_10/767fd5a0.failed |
| 10 | + |
| 11 | +In the current glock reference counting model, a bias of one is added to |
| 12 | +a glock's refcount when it is locked (gl->gl_state != LM_ST_UNLOCKED). |
| 13 | +A glock is removed from the lru_list when it is enqueued, and added back |
| 14 | +when it is dequeued. This isn't a very appropriate model because most |
| 15 | +glocks are held for long periods of time (for example, the inode "owns" |
| 16 | +references to its inode and iopen glocks as long as the inode is cached |
| 17 | +even when the glock state changes to LM_ST_UNLOCKED), and they can only |
| 18 | +be freed when they are no longer referenced, anyway. |
| 19 | + |
| 20 | +Fix this by getting rid of the refcount bias for locked glocks. That |
| 21 | +way, we can use lockref_put_or_lock() to efficiently drop all but the |
| 22 | +last glock reference, and put the glock onto the lru_list when the last |
| 23 | +reference is dropped. When find_insert_glock() returns a reference to a |
| 24 | +cached glock, it removes the glock from the lru_list. |
| 25 | + |
| 26 | +Dumping the "glocks" and "glstats" debugfs files also takes glock |
| 27 | +references, but instead of removing the glocks from the lru_list in that |
| 28 | +case as well, we leave them on the list. This ensures that dumping |
| 29 | +those files won't perturb the order of the glocks on the lru_list. |
| 30 | + |
| 31 | +In addition, when the last reference to an *unlocked* glock is dropped, |
| 32 | +we immediately free it; this preserves the preexisting behavior. If it |
| 33 | +later turns out that caching unlocked glocks is useful in some |
| 34 | +situations, we can change the caching strategy. |
| 35 | + |
| 36 | +It is currently unclear if a glock that has no active references can |
| 37 | +have the GLF_LFLUSH flag set. To make sure that such a glock won't |
| 38 | +accidentally be evicted due to memory pressure, we add a GLF_LFLUSH |
| 39 | +check to gfs2_dispose_glock_lru(). |
| 40 | + |
| 41 | + Signed-off-by: Andreas Gruenbacher < [email protected]> |
| 42 | +(cherry picked from commit 767fd5a0160774178a597b7a7b6e07915fe00efa) |
| 43 | + Signed-off-by: Jonathan Maple < [email protected]> |
| 44 | + |
| 45 | +# Conflicts: |
| 46 | +# fs/gfs2/glock.c |
| 47 | +# fs/gfs2/glock.h |
| 48 | +diff --cc fs/gfs2/glock.c |
| 49 | +index 54811d4dae86,e2a72c21194a..000000000000 |
| 50 | +--- a/fs/gfs2/glock.c |
| 51 | ++++ b/fs/gfs2/glock.c |
| 52 | +@@@ -310,12 -305,18 +310,27 @@@ static void __gfs2_glock_put(struct gfs |
| 53 | + sdp->sd_lockstruct.ls_ops->lm_put_lock(gl); |
| 54 | + } |
| 55 | + |
| 56 | +++<<<<<<< HEAD |
| 57 | + +/* |
| 58 | + + * Cause the glock to be put in work queue context. |
| 59 | + + */ |
| 60 | + +void gfs2_glock_queue_put(struct gfs2_glock *gl) |
| 61 | + +{ |
| 62 | + + gfs2_glock_queue_work(gl, 0); |
| 63 | +++======= |
| 64 | ++ static bool __gfs2_glock_put_or_lock(struct gfs2_glock *gl) |
| 65 | ++ { |
| 66 | ++ if (lockref_put_or_lock(&gl->gl_lockref)) |
| 67 | ++ return true; |
| 68 | ++ GLOCK_BUG_ON(gl, gl->gl_lockref.count != 1); |
| 69 | ++ if (gl->gl_state != LM_ST_UNLOCKED) { |
| 70 | ++ gl->gl_lockref.count--; |
| 71 | ++ gfs2_glock_add_to_lru(gl); |
| 72 | ++ spin_unlock(&gl->gl_lockref.lock); |
| 73 | ++ return true; |
| 74 | ++ } |
| 75 | ++ return false; |
| 76 | +++>>>>>>> 767fd5a01607 (gfs2: Revise glock reference counting model) |
| 77 | + } |
| 78 | + |
| 79 | + /** |
| 80 | +@@@ -332,6 -333,22 +347,25 @@@ void gfs2_glock_put(struct gfs2_glock * |
| 81 | + __gfs2_glock_put(gl); |
| 82 | + } |
| 83 | + |
| 84 | +++<<<<<<< HEAD |
| 85 | +++======= |
| 86 | ++ /* |
| 87 | ++ * gfs2_glock_put_async - Decrement reference count without sleeping |
| 88 | ++ * @gl: The glock to put |
| 89 | ++ * |
| 90 | ++ * Decrement the reference count on glock immediately unless it is the last |
| 91 | ++ * reference. Defer putting the last reference to work queue context. |
| 92 | ++ */ |
| 93 | ++ void gfs2_glock_put_async(struct gfs2_glock *gl) |
| 94 | ++ { |
| 95 | ++ if (__gfs2_glock_put_or_lock(gl)) |
| 96 | ++ return; |
| 97 | ++ |
| 98 | ++ gfs2_glock_queue_work(gl, 0); |
| 99 | ++ spin_unlock(&gl->gl_lockref.lock); |
| 100 | ++ } |
| 101 | ++ |
| 102 | +++>>>>>>> 767fd5a01607 (gfs2: Revise glock reference counting model) |
| 103 | + /** |
| 104 | + * may_grant - check if it's ok to grant a new lock |
| 105 | + * @gl: The glock |
| 106 | +@@@ -1057,18 -1136,18 +1079,26 @@@ static void glock_work_func(struct work |
| 107 | + drop_refs--; |
| 108 | + if (gl->gl_name.ln_type != LM_TYPE_INODE) |
| 109 | + delay = 0; |
| 110 | + - gfs2_glock_queue_work(gl, delay); |
| 111 | + + __gfs2_glock_queue_work(gl, delay); |
| 112 | + } |
| 113 | + |
| 114 | +++<<<<<<< HEAD |
| 115 | + + /* |
| 116 | + + * Drop the remaining glock references manually here. (Mind that |
| 117 | + + * __gfs2_glock_queue_work depends on the lockref spinlock begin held |
| 118 | + + * here as well.) |
| 119 | + + */ |
| 120 | +++======= |
| 121 | ++ /* Drop the remaining glock references manually. */ |
| 122 | ++ GLOCK_BUG_ON(gl, gl->gl_lockref.count < drop_refs); |
| 123 | +++>>>>>>> 767fd5a01607 (gfs2: Revise glock reference counting model) |
| 124 | + gl->gl_lockref.count -= drop_refs; |
| 125 | + if (!gl->gl_lockref.count) { |
| 126 | +- __gfs2_glock_put(gl); |
| 127 | +- return; |
| 128 | ++ if (gl->gl_state == LM_ST_UNLOCKED) { |
| 129 | ++ __gfs2_glock_put(gl); |
| 130 | ++ return; |
| 131 | ++ } |
| 132 | ++ gfs2_glock_add_to_lru(gl); |
| 133 | + } |
| 134 | + spin_unlock(&gl->gl_lockref.lock); |
| 135 | + } |
| 136 | +@@@ -1535,8 -1614,24 +1567,29 @@@ int gfs2_glock_nq(struct gfs2_holder *g |
| 137 | + if (glock_blocked_by_withdraw(gl) && !(gh->gh_flags & LM_FLAG_NOEXP)) |
| 138 | + return -EIO; |
| 139 | + |
| 140 | +++<<<<<<< HEAD |
| 141 | + + if (test_bit(GLF_LRU, &gl->gl_flags)) |
| 142 | + + gfs2_glock_remove_from_lru(gl); |
| 143 | +++======= |
| 144 | ++ if (gh->gh_flags & GL_NOBLOCK) { |
| 145 | ++ struct gfs2_holder *current_gh; |
| 146 | ++ |
| 147 | ++ error = -ECHILD; |
| 148 | ++ spin_lock(&gl->gl_lockref.lock); |
| 149 | ++ if (find_last_waiter(gl)) |
| 150 | ++ goto unlock; |
| 151 | ++ current_gh = find_first_holder(gl); |
| 152 | ++ if (!may_grant(gl, current_gh, gh)) |
| 153 | ++ goto unlock; |
| 154 | ++ set_bit(HIF_HOLDER, &gh->gh_iflags); |
| 155 | ++ list_add_tail(&gh->gh_list, &gl->gl_holders); |
| 156 | ++ trace_gfs2_promote(gh); |
| 157 | ++ error = 0; |
| 158 | ++ unlock: |
| 159 | ++ spin_unlock(&gl->gl_lockref.lock); |
| 160 | ++ return error; |
| 161 | ++ } |
| 162 | +++>>>>>>> 767fd5a01607 (gfs2: Revise glock reference counting model) |
| 163 | + |
| 164 | + gh->gh_error = 0; |
| 165 | + spin_lock(&gl->gl_lockref.lock); |
| 166 | +@@@ -2085,9 -2180,10 +2137,10 @@@ static void thaw_glock(struct gfs2_gloc |
| 167 | + if (!lockref_get_not_dead(&gl->gl_lockref)) |
| 168 | + return; |
| 169 | + |
| 170 | ++ gfs2_glock_remove_from_lru(gl); |
| 171 | + spin_lock(&gl->gl_lockref.lock); |
| 172 | + - set_bit(GLF_HAVE_REPLY, &gl->gl_flags); |
| 173 | + - gfs2_glock_queue_work(gl, 0); |
| 174 | + + set_bit(GLF_REPLY_PENDING, &gl->gl_flags); |
| 175 | + + __gfs2_glock_queue_work(gl, 0); |
| 176 | + spin_unlock(&gl->gl_lockref.lock); |
| 177 | + } |
| 178 | + |
| 179 | +diff --cc fs/gfs2/glock.h |
| 180 | +index be4bc9edf46d,adf0091cc98f..000000000000 |
| 181 | +--- a/fs/gfs2/glock.h |
| 182 | ++++ b/fs/gfs2/glock.h |
| 183 | +@@@ -252,29 -242,27 +252,42 @@@ static inline int gfs2_glock_nq_init(st |
| 184 | + return error; |
| 185 | + } |
| 186 | + |
| 187 | +++<<<<<<< HEAD |
| 188 | + +extern void gfs2_glock_cb(struct gfs2_glock *gl, unsigned int state); |
| 189 | + +extern void gfs2_glock_complete(struct gfs2_glock *gl, int ret); |
| 190 | + +extern bool gfs2_queue_try_to_evict(struct gfs2_glock *gl); |
| 191 | + +extern bool gfs2_queue_verify_delete(struct gfs2_glock *gl); |
| 192 | + +extern void gfs2_cancel_delete_work(struct gfs2_glock *gl); |
| 193 | + +extern void gfs2_flush_delete_work(struct gfs2_sbd *sdp); |
| 194 | + +extern void gfs2_gl_hash_clear(struct gfs2_sbd *sdp); |
| 195 | + +extern void gfs2_gl_dq_holders(struct gfs2_sbd *sdp); |
| 196 | + +extern void gfs2_glock_thaw(struct gfs2_sbd *sdp); |
| 197 | + +extern void gfs2_glock_add_to_lru(struct gfs2_glock *gl); |
| 198 | + +extern void gfs2_glock_free(struct gfs2_glock *gl); |
| 199 | + +extern void gfs2_glock_free_later(struct gfs2_glock *gl); |
| 200 | +++======= |
| 201 | ++ void gfs2_glock_cb(struct gfs2_glock *gl, unsigned int state); |
| 202 | ++ void gfs2_glock_complete(struct gfs2_glock *gl, int ret); |
| 203 | ++ bool gfs2_queue_try_to_evict(struct gfs2_glock *gl); |
| 204 | ++ void gfs2_cancel_delete_work(struct gfs2_glock *gl); |
| 205 | ++ void gfs2_flush_delete_work(struct gfs2_sbd *sdp); |
| 206 | ++ void gfs2_gl_hash_clear(struct gfs2_sbd *sdp); |
| 207 | ++ void gfs2_gl_dq_holders(struct gfs2_sbd *sdp); |
| 208 | ++ void gfs2_glock_thaw(struct gfs2_sbd *sdp); |
| 209 | ++ void gfs2_glock_free(struct gfs2_glock *gl); |
| 210 | ++ void gfs2_glock_free_later(struct gfs2_glock *gl); |
| 211 | +++>>>>>>> 767fd5a01607 (gfs2: Revise glock reference counting model) |
| 212 | + |
| 213 | + -int __init gfs2_glock_init(void); |
| 214 | + -void gfs2_glock_exit(void); |
| 215 | + +extern int __init gfs2_glock_init(void); |
| 216 | + +extern void gfs2_glock_exit(void); |
| 217 | + |
| 218 | + -void gfs2_create_debugfs_file(struct gfs2_sbd *sdp); |
| 219 | + -void gfs2_delete_debugfs_file(struct gfs2_sbd *sdp); |
| 220 | + -void gfs2_register_debugfs(void); |
| 221 | + -void gfs2_unregister_debugfs(void); |
| 222 | + +extern void gfs2_create_debugfs_file(struct gfs2_sbd *sdp); |
| 223 | + +extern void gfs2_delete_debugfs_file(struct gfs2_sbd *sdp); |
| 224 | + +extern void gfs2_register_debugfs(void); |
| 225 | + +extern void gfs2_unregister_debugfs(void); |
| 226 | + |
| 227 | + -void glock_set_object(struct gfs2_glock *gl, void *object); |
| 228 | + -void glock_clear_object(struct gfs2_glock *gl, void *object); |
| 229 | + +extern void glock_set_object(struct gfs2_glock *gl, void *object); |
| 230 | + +extern void glock_clear_object(struct gfs2_glock *gl, void *object); |
| 231 | + |
| 232 | + extern const struct lm_lockops gfs2_dlm_ops; |
| 233 | + |
| 234 | +* Unmerged path fs/gfs2/glock.c |
| 235 | +* Unmerged path fs/gfs2/glock.h |
| 236 | +diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c |
| 237 | +index a0ec4e506f04..9438aeb6ca90 100644 |
| 238 | +--- a/fs/gfs2/super.c |
| 239 | ++++ b/fs/gfs2/super.c |
| 240 | +@@ -1574,7 +1574,6 @@ static void gfs2_evict_inode(struct inode *inode) |
| 241 | + if (ip->i_gl) { |
| 242 | + glock_clear_object(ip->i_gl, ip); |
| 243 | + wait_on_bit_io(&ip->i_flags, GIF_GLOP_PENDING, TASK_UNINTERRUPTIBLE); |
| 244 | +- gfs2_glock_add_to_lru(ip->i_gl); |
| 245 | + gfs2_glock_put_eventually(ip->i_gl); |
| 246 | + ip->i_gl = NULL; |
| 247 | + } |
0 commit comments