ocfs2/dlm: Migrate lockres with no locks if it has a reference

o2dlm was not migrating resources with zero locks because it assumed that that
resource would get purged by dlm_thread. However, some usage patterns involve
creating and dropping locks at a high rate leading to the migrate thread seeing
zero locks but the purge thread seeing an active reference. When this happens,
the dlm_thread cannot purge the resource and the migrate thread sees no reason
to migrate that resource. The spell is broken when the migrate thread catches
the resource with a lock.

The fix is to make the migrate thread also consider the reference map.

This usage pattern can be triggered by userspace on userdlm locks and flocks.

Signed-off-by: Sunil Mushran <sunil.mushran@oracle.com>
Signed-off-by: Joel Becker <joel.becker@oracle.com>

authored by Sunil Mushran and committed by Joel Becker 388c4bcb 771f8bc7

+27 -13
+27 -13
fs/ocfs2/dlm/dlmmaster.c
··· 2346 2346 */ 2347 2347 static int dlm_is_lockres_migrateable(struct dlm_ctxt *dlm, 2348 2348 struct dlm_lock_resource *res, 2349 - int *numlocks) 2349 + int *numlocks, 2350 + int *hasrefs) 2350 2351 { 2351 2352 int ret; 2352 2353 int i; ··· 2356 2355 struct dlm_lock *lock; 2357 2356 2358 2357 assert_spin_locked(&res->spinlock); 2358 + 2359 + *numlocks = 0; 2360 + *hasrefs = 0; 2359 2361 2360 2362 ret = -EINVAL; 2361 2363 if (res->owner == DLM_LOCK_RES_OWNER_UNKNOWN) { ··· 2390 2386 } 2391 2387 2392 2388 *numlocks = count; 2393 - mlog(0, "migrateable lockres having %d locks\n", *numlocks); 2389 + 2390 + count = find_next_bit(res->refmap, O2NM_MAX_NODES, 0); 2391 + if (count < O2NM_MAX_NODES) 2392 + *hasrefs = 1; 2393 + 2394 + mlog(0, "%s: res %.*s, Migrateable, locks %d, refs %d\n", dlm->name, 2395 + res->lockname.len, res->lockname.name, *numlocks, *hasrefs); 2394 2396 2395 2397 leave: 2396 2398 return ret; ··· 2418 2408 const char *name; 2419 2409 unsigned int namelen; 2420 2410 int mle_added = 0; 2421 - int numlocks; 2411 + int numlocks, hasrefs; 2422 2412 int wake = 0; 2423 2413 2424 2414 if (!dlm_grab(dlm)) ··· 2427 2417 name = res->lockname.name; 2428 2418 namelen = res->lockname.len; 2429 2419 2430 - mlog(0, "migrating %.*s to %u\n", namelen, name, target); 2420 + mlog(0, "%s: Migrating %.*s to %u\n", dlm->name, namelen, name, target); 2431 2421 2432 2422 /* 2433 2423 * ensure this lockres is a proper candidate for migration 2434 2424 */ 2435 2425 spin_lock(&res->spinlock); 2436 - ret = dlm_is_lockres_migrateable(dlm, res, &numlocks); 2426 + ret = dlm_is_lockres_migrateable(dlm, res, &numlocks, &hasrefs); 2437 2427 if (ret < 0) { 2438 2428 spin_unlock(&res->spinlock); 2439 2429 goto leave; ··· 2441 2431 spin_unlock(&res->spinlock); 2442 2432 2443 2433 /* no work to do */ 2444 - if (numlocks == 0) { 2445 - mlog(0, "no locks were found on this lockres! done!\n"); 2434 + if (numlocks == 0 && !hasrefs) 2446 2435 goto leave; 2447 - } 2448 2436 2449 2437 /* 2450 2438 * preallocate up front ··· 2467 2459 * find a node to migrate the lockres to 2468 2460 */ 2469 2461 2470 - mlog(0, "picking a migration node\n"); 2471 2462 spin_lock(&dlm->spinlock); 2472 2463 /* pick a new node */ 2473 2464 if (!test_bit(target, dlm->domain_map) || 2474 2465 target >= O2NM_MAX_NODES) { 2475 2466 target = dlm_pick_migration_target(dlm, res); 2476 2467 } 2477 - mlog(0, "node %u chosen for migration\n", target); 2468 + mlog(0, "%s: res %.*s, Node %u chosen for migration\n", dlm->name, 2469 + namelen, name, target); 2478 2470 2479 2471 if (target >= O2NM_MAX_NODES || 2480 2472 !test_bit(target, dlm->domain_map)) { ··· 2675 2667 { 2676 2668 int ret; 2677 2669 int lock_dropped = 0; 2678 - int numlocks; 2670 + int numlocks, hasrefs; 2679 2671 2680 2672 spin_lock(&res->spinlock); 2681 2673 if (res->owner != dlm->node_num) { ··· 2689 2681 } 2690 2682 2691 2683 /* No need to migrate a lockres having no locks */ 2692 - ret = dlm_is_lockres_migrateable(dlm, res, &numlocks); 2693 - if (ret >= 0 && numlocks == 0) { 2684 + ret = dlm_is_lockres_migrateable(dlm, res, &numlocks, &hasrefs); 2685 + if (ret >= 0 && numlocks == 0 && !hasrefs) { 2694 2686 spin_unlock(&res->spinlock); 2695 2687 goto leave; 2696 2688 } ··· 2922 2914 } 2923 2915 } 2924 2916 queue++; 2917 + } 2918 + 2919 + nodenum = find_next_bit(res->refmap, O2NM_MAX_NODES, 0); 2920 + if (nodenum < O2NM_MAX_NODES) { 2921 + spin_unlock(&res->spinlock); 2922 + return nodenum; 2925 2923 } 2926 2924 spin_unlock(&res->spinlock); 2927 2925 mlog(0, "have not found a suitable target yet! checking domain map\n");