diff options
| author | Srinivas Eeda <srinivas.eeda@oracle.com> | 2010-07-19 19:04:12 -0400 |
|---|---|---|
| committer | Joel Becker <joel.becker@oracle.com> | 2010-08-07 13:44:40 -0400 |
| commit | 7beaf243787f85a2ef9213ccf13ab4a243283fde (patch) | |
| tree | 689e5a447ee2b7b83fcaccf2959ca8dd5b0fb359 | |
| parent | 6d98c3ccb52f692f1a60339dde7c700686a5568b (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.c | 80 |
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 | ||
| 155 | static int dlm_purge_lockres(struct dlm_ctxt *dlm, | 155 | static 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 | ||
| 238 | static void dlm_run_purge_list(struct dlm_ctxt *dlm, | 222 | static 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 | ||
