staging: lustre: ldlm: handle ldlm lock cancel race when evicting client.
A ldlm lock could be canceled simutaneously by ldlm bl thread and cleanup_resource(). In this case, only one side will win the race and the other side should wait for the work to complete. Eviction on group lock is now well supported. Signed-off-by: Jinshan Xiong <jinshan.xiong@intel.com> Intel-bug-id: https://jira.hpdd.intel.com/browse/LU-6271 Reviewed-on: http://review.whamcloud.com/16456 Reviewed-by: Bobi Jam <bobijam@hotmail.com> Reviewed-by: John L. Hammond <john.hammond@intel.com> Reviewed-by: James Simmons <uja.ornl@yahoo.com> Reviewed-by: Oleg Drokin <oleg.drokin@intel.com> Signed-off-by: James Simmons <jsimmons@infradead.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
5fd8f8b8d5
commit
46ff82f956
|
@ -1639,10 +1639,15 @@ enum cl_enq_flags {
|
||||||
* enqueue a lock to test DLM lock existence.
|
* enqueue a lock to test DLM lock existence.
|
||||||
*/
|
*/
|
||||||
CEF_PEEK = 0x00000040,
|
CEF_PEEK = 0x00000040,
|
||||||
|
/**
|
||||||
|
* Lock match only. Used by group lock in I/O as group lock
|
||||||
|
* is known to exist.
|
||||||
|
*/
|
||||||
|
CEF_LOCK_MATCH = BIT(7),
|
||||||
/**
|
/**
|
||||||
* mask of enq_flags.
|
* mask of enq_flags.
|
||||||
*/
|
*/
|
||||||
CEF_MASK = 0x0000007f,
|
CEF_MASK = 0x000000ff,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -121,6 +121,9 @@
|
||||||
#define ldlm_set_test_lock(_l) LDLM_SET_FLAG((_l), 1ULL << 19)
|
#define ldlm_set_test_lock(_l) LDLM_SET_FLAG((_l), 1ULL << 19)
|
||||||
#define ldlm_clear_test_lock(_l) LDLM_CLEAR_FLAG((_l), 1ULL << 19)
|
#define ldlm_clear_test_lock(_l) LDLM_CLEAR_FLAG((_l), 1ULL << 19)
|
||||||
|
|
||||||
|
/** match lock only */
|
||||||
|
#define LDLM_FL_MATCH_LOCK 0x0000000000100000ULL /* bit 20 */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Immediately cancel such locks when they block some other locks. Send
|
* Immediately cancel such locks when they block some other locks. Send
|
||||||
* cancel notification to original lock holder, but expect no reply. This
|
* cancel notification to original lock holder, but expect no reply. This
|
||||||
|
|
|
@ -771,19 +771,11 @@ void ldlm_lock_decref_internal(struct ldlm_lock *lock, enum ldlm_mode mode)
|
||||||
|
|
||||||
ldlm_lock_decref_internal_nolock(lock, mode);
|
ldlm_lock_decref_internal_nolock(lock, mode);
|
||||||
|
|
||||||
if (ldlm_is_local(lock) &&
|
if ((ldlm_is_local(lock) || lock->l_req_mode == LCK_GROUP) &&
|
||||||
!lock->l_readers && !lock->l_writers) {
|
!lock->l_readers && !lock->l_writers) {
|
||||||
/* If this is a local lock on a server namespace and this was
|
/* If this is a local lock on a server namespace and this was
|
||||||
* the last reference, cancel the lock.
|
* the last reference, cancel the lock.
|
||||||
*/
|
*
|
||||||
CDEBUG(D_INFO, "forcing cancel of local lock\n");
|
|
||||||
ldlm_set_cbpending(lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!lock->l_readers && !lock->l_writers &&
|
|
||||||
(ldlm_is_cbpending(lock) || lock->l_req_mode == LCK_GROUP)) {
|
|
||||||
/* If we received a blocked AST and this was the last reference,
|
|
||||||
* run the callback.
|
|
||||||
* Group locks are special:
|
* Group locks are special:
|
||||||
* They must not go in LRU, but they are not called back
|
* They must not go in LRU, but they are not called back
|
||||||
* like non-group locks, instead they are manually released.
|
* like non-group locks, instead they are manually released.
|
||||||
|
@ -791,6 +783,13 @@ void ldlm_lock_decref_internal(struct ldlm_lock *lock, enum ldlm_mode mode)
|
||||||
* they are manually released, so we remove them when they have
|
* they are manually released, so we remove them when they have
|
||||||
* no more reader or writer references. - LU-6368
|
* no more reader or writer references. - LU-6368
|
||||||
*/
|
*/
|
||||||
|
ldlm_set_cbpending(lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!lock->l_readers && !lock->l_writers && ldlm_is_cbpending(lock)) {
|
||||||
|
/* If we received a blocked AST and this was the last reference,
|
||||||
|
* run the callback.
|
||||||
|
*/
|
||||||
LDLM_DEBUG(lock, "final decref done on cbpending lock");
|
LDLM_DEBUG(lock, "final decref done on cbpending lock");
|
||||||
|
|
||||||
LDLM_LOCK_GET(lock); /* dropped by bl thread */
|
LDLM_LOCK_GET(lock); /* dropped by bl thread */
|
||||||
|
@ -1882,6 +1881,19 @@ out:
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool is_bl_done(struct ldlm_lock *lock)
|
||||||
|
{
|
||||||
|
bool bl_done = true;
|
||||||
|
|
||||||
|
if (!ldlm_is_bl_done(lock)) {
|
||||||
|
lock_res_and_lock(lock);
|
||||||
|
bl_done = ldlm_is_bl_done(lock);
|
||||||
|
unlock_res_and_lock(lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
return bl_done;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper function to call blocking AST for LDLM lock \a lock in a
|
* Helper function to call blocking AST for LDLM lock \a lock in a
|
||||||
* "cancelling" mode.
|
* "cancelling" mode.
|
||||||
|
@ -1899,8 +1911,20 @@ void ldlm_cancel_callback(struct ldlm_lock *lock)
|
||||||
} else {
|
} else {
|
||||||
LDLM_DEBUG(lock, "no blocking ast");
|
LDLM_DEBUG(lock, "no blocking ast");
|
||||||
}
|
}
|
||||||
|
/* only canceller can set bl_done bit */
|
||||||
|
ldlm_set_bl_done(lock);
|
||||||
|
wake_up_all(&lock->l_waitq);
|
||||||
|
} else if (!ldlm_is_bl_done(lock)) {
|
||||||
|
struct l_wait_info lwi = { 0 };
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The lock is guaranteed to have been canceled once
|
||||||
|
* returning from this function.
|
||||||
|
*/
|
||||||
|
unlock_res_and_lock(lock);
|
||||||
|
l_wait_event(lock->l_waitq, is_bl_done(lock), &lwi);
|
||||||
|
lock_res_and_lock(lock);
|
||||||
}
|
}
|
||||||
ldlm_set_bl_done(lock);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1029,13 +1029,23 @@ int ldlm_cli_cancel(const struct lustre_handle *lockh,
|
||||||
struct ldlm_lock *lock;
|
struct ldlm_lock *lock;
|
||||||
LIST_HEAD(cancels);
|
LIST_HEAD(cancels);
|
||||||
|
|
||||||
/* concurrent cancels on the same handle can happen */
|
lock = ldlm_handle2lock_long(lockh, 0);
|
||||||
lock = ldlm_handle2lock_long(lockh, LDLM_FL_CANCELING);
|
|
||||||
if (!lock) {
|
if (!lock) {
|
||||||
LDLM_DEBUG_NOLOCK("lock is already being destroyed");
|
LDLM_DEBUG_NOLOCK("lock is already being destroyed");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lock_res_and_lock(lock);
|
||||||
|
/* Lock is being canceled and the caller doesn't want to wait */
|
||||||
|
if (ldlm_is_canceling(lock) && (cancel_flags & LCF_ASYNC)) {
|
||||||
|
unlock_res_and_lock(lock);
|
||||||
|
LDLM_LOCK_RELEASE(lock);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ldlm_set_canceling(lock);
|
||||||
|
unlock_res_and_lock(lock);
|
||||||
|
|
||||||
rc = ldlm_cli_cancel_local(lock);
|
rc = ldlm_cli_cancel_local(lock);
|
||||||
if (rc == LDLM_FL_LOCAL_ONLY || cancel_flags & LCF_LOCAL) {
|
if (rc == LDLM_FL_LOCAL_ONLY || cancel_flags & LCF_LOCAL) {
|
||||||
LDLM_LOCK_RELEASE(lock);
|
LDLM_LOCK_RELEASE(lock);
|
||||||
|
|
|
@ -806,7 +806,7 @@ static void cleanup_resource(struct ldlm_resource *res, struct list_head *q,
|
||||||
|
|
||||||
unlock_res(res);
|
unlock_res(res);
|
||||||
ldlm_lock2handle(lock, &lockh);
|
ldlm_lock2handle(lock, &lockh);
|
||||||
rc = ldlm_cli_cancel(&lockh, LCF_ASYNC);
|
rc = ldlm_cli_cancel(&lockh, LCF_LOCAL);
|
||||||
if (rc)
|
if (rc)
|
||||||
CERROR("ldlm_cli_cancel: %d\n", rc);
|
CERROR("ldlm_cli_cancel: %d\n", rc);
|
||||||
LDLM_LOCK_RELEASE(lock);
|
LDLM_LOCK_RELEASE(lock);
|
||||||
|
|
|
@ -219,6 +219,7 @@ static int vvp_io_one_lock_index(const struct lu_env *env, struct cl_io *io,
|
||||||
if (vio->vui_fd && (vio->vui_fd->fd_flags & LL_FILE_GROUP_LOCKED)) {
|
if (vio->vui_fd && (vio->vui_fd->fd_flags & LL_FILE_GROUP_LOCKED)) {
|
||||||
descr->cld_mode = CLM_GROUP;
|
descr->cld_mode = CLM_GROUP;
|
||||||
descr->cld_gid = vio->vui_fd->fd_grouplock.lg_gid;
|
descr->cld_gid = vio->vui_fd->fd_grouplock.lg_gid;
|
||||||
|
enqflags |= CEF_LOCK_MATCH;
|
||||||
} else {
|
} else {
|
||||||
descr->cld_mode = mode;
|
descr->cld_mode = mode;
|
||||||
}
|
}
|
||||||
|
|
|
@ -167,6 +167,8 @@ static __u64 osc_enq2ldlm_flags(__u32 enqflags)
|
||||||
result |= LDLM_FL_AST_DISCARD_DATA;
|
result |= LDLM_FL_AST_DISCARD_DATA;
|
||||||
if (enqflags & CEF_PEEK)
|
if (enqflags & CEF_PEEK)
|
||||||
result |= LDLM_FL_TEST_LOCK;
|
result |= LDLM_FL_TEST_LOCK;
|
||||||
|
if (enqflags & CEF_LOCK_MATCH)
|
||||||
|
result |= LDLM_FL_MATCH_LOCK;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2011,7 +2011,7 @@ int osc_enqueue_base(struct obd_export *exp, struct ldlm_res_id *res_id,
|
||||||
}
|
}
|
||||||
|
|
||||||
no_match:
|
no_match:
|
||||||
if (*flags & LDLM_FL_TEST_LOCK)
|
if (*flags & (LDLM_FL_TEST_LOCK | LDLM_FL_MATCH_LOCK))
|
||||||
return -ENOLCK;
|
return -ENOLCK;
|
||||||
if (intent) {
|
if (intent) {
|
||||||
req = ptlrpc_request_alloc(class_exp2cliimp(exp),
|
req = ptlrpc_request_alloc(class_exp2cliimp(exp),
|
||||||
|
@ -2495,7 +2495,13 @@ static int osc_ldlm_resource_invalidate(struct cfs_hash *hs,
|
||||||
osc = lock->l_ast_data;
|
osc = lock->l_ast_data;
|
||||||
cl_object_get(osc2cl(osc));
|
cl_object_get(osc2cl(osc));
|
||||||
}
|
}
|
||||||
lock->l_ast_data = NULL;
|
|
||||||
|
/*
|
||||||
|
* clear LDLM_FL_CLEANED flag to make sure it will be canceled
|
||||||
|
* by the 2nd round of ldlm_namespace_clean() call in
|
||||||
|
* osc_import_event().
|
||||||
|
*/
|
||||||
|
ldlm_clear_cleaned(lock);
|
||||||
}
|
}
|
||||||
unlock_res(res);
|
unlock_res(res);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue