aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSrinivas Eeda <srinivas.eeda@oracle.com>2010-07-19 19:04:12 -0400
committerJoel Becker <joel.becker@oracle.com>2010-08-07 13:44:40 -0400
commit7beaf243787f85a2ef9213ccf13ab4a243283fde (patch)
tree689e5a447ee2b7b83fcaccf2959ca8dd5b0fb359
parent6d98c3ccb52f692f1a60339dde7c700686a5568b (diff)
ocfs2 fix o2dlm dlm run purgelist (rev 3)
This patch fixes two problems in dlm_run_purgelist 1. If a lockres is found to be in use, dlm_run_purgelist keeps trying to purge the same lockres instead of trying the next lockres. 2. When a lockres is found unused, dlm_run_purgelist releases lockres spinlock before setting DLM_LOCK_RES_DROPPING_REF and calls dlm_purge_lockres. spinlock is reacquired but in this window lockres can get reused. This leads to BUG. This patch modifies dlm_run_purgelist to skip lockres if it's in use and purge next lockres. It also sets DLM_LOCK_RES_DROPPING_REF before releasing the lockres spinlock protecting it from getting reused. Signed-off-by: Srinivas Eeda <srinivas.eeda@oracle.com> Acked-by: Sunil Mushran <sunil.mushran@oracle.com> Cc: stable@kernel.org Signed-off-by: Joel Becker <joel.becker@oracle.com>
-rw-r--r--fs/ocfs2/dlm/dlmthread.c80
1 files changed, 34 insertions, 46 deletions
diff --git a/fs/ocfs2/dlm/dlmthread.c b/fs/ocfs2/dlm/dlmthread.c
index d4f73ca68fe5..dd78ca3f360f 100644
--- a/fs/ocfs2/dlm/dlmthread.c
+++ b/fs/ocfs2/dlm/dlmthread.c
@@ -152,45 +152,25 @@ void dlm_lockres_calc_usage(struct dlm_ctxt *dlm,
152 spin_unlock(&dlm->spinlock); 152 spin_unlock(&dlm->spinlock);
153} 153}
154 154
155static int dlm_purge_lockres(struct dlm_ctxt *dlm, 155static void dlm_purge_lockres(struct dlm_ctxt *dlm,
156 struct dlm_lock_resource *res) 156 struct dlm_lock_resource *res)
157{ 157{
158 int master; 158 int master;
159 int ret = 0; 159 int ret = 0;
160 160
161 spin_lock(&res->spinlock); 161 assert_spin_locked(&dlm->spinlock);
162 if (!__dlm_lockres_unused(res)) { 162 assert_spin_locked(&res->spinlock);
163 mlog(0, "%s:%.*s: tried to purge but not unused\n",
164 dlm->name, res->lockname.len, res->lockname.name);
165 __dlm_print_one_lock_resource(res);
166 spin_unlock(&res->spinlock);
167 BUG();
168 }
169
170 if (res->state & DLM_LOCK_RES_MIGRATING) {
171 mlog(0, "%s:%.*s: Delay dropref as this lockres is "
172 "being remastered\n", dlm->name, res->lockname.len,
173 res->lockname.name);
174 /* Re-add the lockres to the end of the purge list */
175 if (!list_empty(&res->purge)) {
176 list_del_init(&res->purge);
177 list_add_tail(&res->purge, &dlm->purge_list);
178 }
179 spin_unlock(&res->spinlock);
180 return 0;
181 }
182 163
183 master = (res->owner == dlm->node_num); 164 master = (res->owner == dlm->node_num);
184 165
185 if (!master)
186 res->state |= DLM_LOCK_RES_DROPPING_REF;
187 spin_unlock(&res->spinlock);
188 166
189 mlog(0, "purging lockres %.*s, master = %d\n", res->lockname.len, 167 mlog(0, "purging lockres %.*s, master = %d\n", res->lockname.len,
190 res->lockname.name, master); 168 res->lockname.name, master);
191 169
192 if (!master) { 170 if (!master) {
171 res->state |= DLM_LOCK_RES_DROPPING_REF;
193 /* drop spinlock... retake below */ 172 /* drop spinlock... retake below */
173 spin_unlock(&res->spinlock);
194 spin_unlock(&dlm->spinlock); 174 spin_unlock(&dlm->spinlock);
195 175
196 spin_lock(&res->spinlock); 176 spin_lock(&res->spinlock);
@@ -208,31 +188,35 @@ static int dlm_purge_lockres(struct dlm_ctxt *dlm,
208 mlog(0, "%s:%.*s: dlm_deref_lockres returned %d\n", 188 mlog(0, "%s:%.*s: dlm_deref_lockres returned %d\n",
209 dlm->name, res->lockname.len, res->lockname.name, ret); 189 dlm->name, res->lockname.len, res->lockname.name, ret);
210 spin_lock(&dlm->spinlock); 190 spin_lock(&dlm->spinlock);
191 spin_lock(&res->spinlock);
211 } 192 }
212 193
213 spin_lock(&res->spinlock);
214 if (!list_empty(&res->purge)) { 194 if (!list_empty(&res->purge)) {
215 mlog(0, "removing lockres %.*s:%p from purgelist, " 195 mlog(0, "removing lockres %.*s:%p from purgelist, "
216 "master = %d\n", res->lockname.len, res->lockname.name, 196 "master = %d\n", res->lockname.len, res->lockname.name,
217 res, master); 197 res, master);
218 list_del_init(&res->purge); 198 list_del_init(&res->purge);
219 spin_unlock(&res->spinlock);
220 dlm_lockres_put(res); 199 dlm_lockres_put(res);
221 dlm->purge_count--; 200 dlm->purge_count--;
222 } else 201 }
223 spin_unlock(&res->spinlock); 202
203 if (!__dlm_lockres_unused(res)) {
204 mlog(ML_ERROR, "found lockres %s:%.*s: in use after deref\n",
205 dlm->name, res->lockname.len, res->lockname.name);
206 __dlm_print_one_lock_resource(res);
207 BUG();
208 }
224 209
225 __dlm_unhash_lockres(res); 210 __dlm_unhash_lockres(res);
226 211
227 /* lockres is not in the hash now. drop the flag and wake up 212 /* lockres is not in the hash now. drop the flag and wake up
228 * any processes waiting in dlm_get_lock_resource. */ 213 * any processes waiting in dlm_get_lock_resource. */
229 if (!master) { 214 if (!master) {
230 spin_lock(&res->spinlock);
231 res->state &= ~DLM_LOCK_RES_DROPPING_REF; 215 res->state &= ~DLM_LOCK_RES_DROPPING_REF;
232 spin_unlock(&res->spinlock); 216 spin_unlock(&res->spinlock);
233 wake_up(&res->wq); 217 wake_up(&res->wq);
234 } 218 } else
235 return 0; 219 spin_unlock(&res->spinlock);
236} 220}
237 221
238static void dlm_run_purge_list(struct dlm_ctxt *dlm, 222static void dlm_run_purge_list(struct dlm_ctxt *dlm,
@@ -251,17 +235,7 @@ static void dlm_run_purge_list(struct dlm_ctxt *dlm,
251 lockres = list_entry(dlm->purge_list.next, 235 lockres = list_entry(dlm->purge_list.next,
252 struct dlm_lock_resource, purge); 236 struct dlm_lock_resource, purge);
253 237
254 /* Status of the lockres *might* change so double
255 * check. If the lockres is unused, holding the dlm
256 * spinlock will prevent people from getting and more
257 * refs on it -- there's no need to keep the lockres
258 * spinlock. */
259 spin_lock(&lockres->spinlock); 238 spin_lock(&lockres->spinlock);
260 unused = __dlm_lockres_unused(lockres);
261 spin_unlock(&lockres->spinlock);
262
263 if (!unused)
264 continue;
265 239
266 purge_jiffies = lockres->last_used + 240 purge_jiffies = lockres->last_used +
267 msecs_to_jiffies(DLM_PURGE_INTERVAL_MS); 241 msecs_to_jiffies(DLM_PURGE_INTERVAL_MS);
@@ -273,15 +247,29 @@ static void dlm_run_purge_list(struct dlm_ctxt *dlm,
273 * in tail order, we can stop at the first 247 * in tail order, we can stop at the first
274 * unpurgable resource -- anyone added after 248 * unpurgable resource -- anyone added after
275 * him will have a greater last_used value */ 249 * him will have a greater last_used value */
250 spin_unlock(&lockres->spinlock);
276 break; 251 break;
277 } 252 }
278 253
254 /* Status of the lockres *might* change so double
255 * check. If the lockres is unused, holding the dlm
256 * spinlock will prevent people from getting and more
257 * refs on it. */
258 unused = __dlm_lockres_unused(lockres);
259 if (!unused ||
260 (lockres->state & DLM_LOCK_RES_MIGRATING)) {
261 mlog(0, "lockres %s:%.*s: is in use or "
262 "being remastered, used %d, state %d\n",
263 dlm->name, lockres->lockname.len,
264 lockres->lockname.name, !unused, lockres->state);
265 list_move_tail(&dlm->purge_list, &lockres->purge);
266 spin_unlock(&lockres->spinlock);
267 continue;
268 }
269
279 dlm_lockres_get(lockres); 270 dlm_lockres_get(lockres);
280 271
281 /* This may drop and reacquire the dlm spinlock if it 272 dlm_purge_lockres(dlm, lockres);
282 * has to do migration. */
283 if (dlm_purge_lockres(dlm, lockres))
284 BUG();
285 273
286 dlm_lockres_put(lockres); 274 dlm_lockres_put(lockres);
287 275