/* returns a referenced lock or NULL */
static struct ldlm_lock *search_queue(struct list_head *queue, ldlm_mode_t mode,
- struct ldlm_extent *extent)
+ struct ldlm_extent *extent,
+ struct ldlm_lock *old_lock)
{
struct ldlm_lock *lock;
struct list_head *tmp;
list_for_each(tmp, queue) {
lock = list_entry(tmp, struct ldlm_lock, l_res_link);
+ if (lock == old_lock)
+ continue;
+
if (lock->l_flags & (LDLM_FL_CBPENDING | LDLM_FL_DESTROYED))
continue;
return NULL;
}
-/* Must be called with no resource or lock locks held.
+/* Can be called in two ways:
+ *
+ * If 'ns' is NULL, then lockh describes an existing lock that we want to look
+ * for a duplicate of.
+ *
+ * Otherwise, all of the fields must be filled in, to match against.
*
* Returns 1 if it finds an already-existing lock that is compatible; in this
* case, lockh is filled in with a addref()ed lock
-*/
-int ldlm_lock_match(struct ldlm_namespace *ns, __u64 * res_id, __u32 type,
+ */
+int ldlm_lock_match(struct ldlm_namespace *ns, __u64 *res_id, __u32 type,
void *cookie, int cookielen, ldlm_mode_t mode,
struct lustre_handle *lockh)
{
struct ldlm_resource *res;
- struct ldlm_lock *lock;
+ struct ldlm_lock *lock, *old_lock = NULL;
int rc = 0;
ENTRY;
+ if (ns == NULL) {
+ old_lock = ldlm_handle2lock(lockh);
+ LASSERT(old_lock);
+
+ ns = old_lock->l_resource->lr_namespace;
+ res_id = old_lock->l_resource->lr_name;
+ type = old_lock->l_resource->lr_type;
+ mode = old_lock->l_req_mode;
+ }
+
res = ldlm_resource_get(ns, NULL, res_id, type, 0);
- if (res == NULL)
+ if (res == NULL) {
+ LASSERT(old_lock == NULL);
RETURN(0);
+ }
ns = res->lr_namespace;
l_lock(&ns->ns_lock);
- if ((lock = search_queue(&res->lr_granted, mode, cookie)))
+ if ((lock = search_queue(&res->lr_granted, mode, cookie, old_lock)))
GOTO(out, rc = 1);
- if ((lock = search_queue(&res->lr_converting, mode, cookie)))
+ if ((lock = search_queue(&res->lr_converting, mode, cookie, old_lock)))
GOTO(out, rc = 1);
- if ((lock = search_queue(&res->lr_waiting, mode, cookie)))
+ if ((lock = search_queue(&res->lr_waiting, mode, cookie, old_lock)))
GOTO(out, rc = 1);
EXIT;
- out:
+ out:
ldlm_resource_put(res);
l_unlock(&ns->ns_lock);
LDLM_DEBUG(lock, "matched");
else
LDLM_DEBUG_NOLOCK("not matched");
+
+ if (old_lock)
+ LDLM_LOCK_PUT(old_lock);
+
return rc;
}
LBUG();
RETURN(-EINVAL);
}
-#warning FIXME: the data here needs to be different if a lock was granted for a different inode
+
rc = ldlm_cli_enqueue(conn, req, obddev->obd_namespace, NULL, res_id,
lock_type, NULL, 0, lock_mode, &flags,
ldlm_completion_ast, mdc_blocking_ast, data,
} else if (rc != 0) {
CERROR("ldlm_cli_enqueue: %d\n", rc);
RETURN(rc);
+ } else {
+ /* The server almost certainly gave us a lock other than the one
+ * that we asked for. If we already have a matching lock, then
+ * cancel this one--we don't need two. */
+ struct ldlm_lock *lock = ldlm_handle2lock(lockh);
+ struct lustre_handle lockh2;
+ LASSERT(lock);
+
+ LDLM_DEBUG(lock, "matching against this");
+
+ memcpy(&lockh2, lockh, sizeof(lockh2));
+ if (ldlm_lock_match(NULL, NULL, LDLM_PLAIN, NULL, 0, LCK_NL,
+ &lockh2)) {
+ /* We already have a lock; cancel the old one */
+ ldlm_lock_decref(lockh, lock_mode);
+ ldlm_cli_cancel(lockh);
+ memcpy(lockh, &lockh2, sizeof(lockh2));
+ }
+ LDLM_LOCK_PUT(lock);
}
/* On replay, we don't want the lock granted. */