diff --git a/lustre/include/interval_tree.h b/lustre/include/interval_tree.h
index a79b686fce06af0d2ae6e09f38df2343f404807d..df85d56282d42632f7b25b83ae5a567e36cb4705 100644
--- a/lustre/include/interval_tree.h
+++ b/lustre/include/interval_tree.h
@@ -49,8 +49,10 @@ struct interval_node {
         struct interval_node   *in_left;
         struct interval_node   *in_right;
         struct interval_node   *in_parent;
-        __u8                    in_color;
-        __u8                    res1[7];  /* tags, 8-bytes aligned */
+        unsigned                in_color:1,
+                                in_intree:1, /** set if the node is in tree */
+                                in_res1:30;
+        __u8                    in_res2[4];  /** tags, 8-bytes aligned */
         __u64                   in_max_high;
         struct interval_node_extent {
                 __u64 start;
@@ -63,6 +65,11 @@ enum interval_iter {
         INTERVAL_ITER_STOP = 2
 };
 
+static inline int interval_is_intree(struct interval_node *node)
+{
+        return node->in_intree == 1;
+}
+
 static inline __u64 interval_low(struct interval_node *node)
 {
         return node->in_extent.start;
diff --git a/lustre/include/obd_support.h b/lustre/include/obd_support.h
index b7817608afea3f436d3531db4fcb04cf1848e138..0a8f310e97ca1e85998957a7d6d68e797077b7d0 100644
--- a/lustre/include/obd_support.h
+++ b/lustre/include/obd_support.h
@@ -230,6 +230,7 @@ extern unsigned int obd_alloc_fail_rate;
 #define OBD_FAIL_LDLM_CLOSE_THREAD       0x313
 #define OBD_FAIL_LDLM_CANCEL_BL_CB_RACE  0x314
 #define OBD_FAIL_LDLM_CP_CB_WAIT         0x315
+#define OBD_FAIL_LDLM_OST_FAIL_RACE      0x316
 
 #define OBD_FAIL_OSC                     0x400
 #define OBD_FAIL_OSC_BRW_READ_BULK       0x401
diff --git a/lustre/ldlm/interval_tree.c b/lustre/ldlm/interval_tree.c
index 1ec969f7cc4f0174c68f9e89d9965ed210392339..21fa9e01959a1d86bd2228e60f2337015bd9fe84 100644
--- a/lustre/ldlm/interval_tree.c
+++ b/lustre/ldlm/interval_tree.c
@@ -390,6 +390,7 @@ struct interval_node *interval_insert(struct interval_node *node,
         struct interval_node **p, *parent = NULL;
         ENTRY;
 
+        LASSERT(!interval_is_intree(node));
         p = root;
         while (*p) {
                 parent = *p;
@@ -413,6 +414,7 @@ struct interval_node *interval_insert(struct interval_node *node,
         *p = node;
 
         interval_insert_color(node, root);
+        node->in_intree = 1;
 
         RETURN(NULL);
 }
@@ -528,6 +530,8 @@ void interval_erase(struct interval_node *node,
         int color;
         ENTRY;
 
+        LASSERT(interval_is_intree(node));
+        node->in_intree = 0;
         if (!node->in_left) {
                 child = node->in_right;
         } else if (!node->in_right) {
diff --git a/lustre/ldlm/ldlm_extent.c b/lustre/ldlm/ldlm_extent.c
index 922c4cd43ac3c713b752212269ba89818802c2f4..22c42c1ef17e98072a6ed87cda94a30a4c9b9db6 100644
--- a/lustre/ldlm/ldlm_extent.c
+++ b/lustre/ldlm/ldlm_extent.c
@@ -47,6 +47,7 @@
 #include <lustre_dlm.h>
 #include <obd_support.h>
 #include <obd.h>
+#include <obd_class.h>
 #include <lustre_lib.h>
 
 #include "ldlm_internal.h"
@@ -701,10 +702,25 @@ int ldlm_process_extent_lock(struct ldlm_lock *lock, int *flags, int first_enq,
                 if (list_empty(&lock->l_res_link))
                         ldlm_resource_add_lock(res, &res->lr_waiting, lock);
                 unlock_res(res);
+
                 rc = ldlm_run_bl_ast_work(&rpc_list);
-                lock_res(res);
 
+                if (OBD_FAIL_CHECK(OBD_FAIL_LDLM_OST_FAIL_RACE) &&
+                    !ns_is_client(res->lr_namespace))
+                        class_fail_export(lock->l_export);
+
+                lock_res(res);
                 if (rc == -ERESTART) {
+                        /* 15715: The lock was granted and destroyed after
+                         * resource lock was dropped. Interval node was freed
+                         * in ldlm_lock_destroy. Anyway, this always happens
+                         * when a client is being evicted. So it would be
+                         * ok to return an error. -jay */
+                        if (lock->l_destroyed) {
+                                *err = -EAGAIN;
+                                GOTO(out, rc = -EAGAIN);
+                        }
+
                         /* lock was granted while resource was unlocked. */
                         if (lock->l_granted_mode == lock->l_req_mode) {
                                 /* bug 11300: if the lock has been granted,
@@ -793,6 +809,7 @@ void ldlm_interval_free(struct ldlm_interval *node)
 {
         if (node) {
                 LASSERT(list_empty(&node->li_group));
+                LASSERT(!interval_is_intree(&node->li_node));
                 OBD_SLAB_FREE(node, ldlm_interval_slab, sizeof(*node));
         }
 }
@@ -845,6 +862,7 @@ void ldlm_extent_add_lock(struct ldlm_resource *res,
 
         node = lock->l_tree_node;
         LASSERT(node != NULL);
+        LASSERT(!interval_is_intree(&node->li_node));
 
         idx = lock_mode_to_index(lock->l_granted_mode);
         LASSERT(lock->l_granted_mode == 1 << idx);
@@ -872,14 +890,13 @@ void ldlm_extent_add_lock(struct ldlm_resource *res,
 void ldlm_extent_unlink_lock(struct ldlm_lock *lock)
 {
         struct ldlm_resource *res = lock->l_resource;
-        struct ldlm_interval *node;
+        struct ldlm_interval *node = lock->l_tree_node;
         struct ldlm_interval_tree *tree;
         int idx;
 
-        if (lock->l_granted_mode != lock->l_req_mode)
+        if (!node || !interval_is_intree(&node->li_node)) /* duplicate unlink */
                 return;
 
-        LASSERT(lock->l_tree_node != NULL);
         idx = lock_mode_to_index(lock->l_granted_mode);
         LASSERT(lock->l_granted_mode == 1 << idx);
         tree = &res->lr_itree[idx];
diff --git a/lustre/tests/sanityN.sh b/lustre/tests/sanityN.sh
index 0c3dc9f05078c663a75dcaa2c79f555e1c44c1bc..f8ee35487746965803fdafde9d24dd79b581e8ca 100644
--- a/lustre/tests/sanityN.sh
+++ b/lustre/tests/sanityN.sh
@@ -579,7 +579,7 @@ test_30() { #bug #11110
 
 run_test 30 "recreate file race ========="
 
-test_31() {
+test_31a() {
         mkdir -p $DIR1/$tdir || error "Creating dir $DIR1/$tdir"
         writes=`LANG=C dd if=/dev/zero of=$DIR/$tdir/$tfile count=1 2>&1 |
                 awk 'BEGIN { FS="+" } /out/ {print $1}'`
@@ -589,7 +589,24 @@ test_31() {
                awk 'BEGIN { FS="+" } /in/ {print $1}'`
         [ $reads -eq $writes ] || error "read" $reads "blocks, must be" $writes
 }
-run_test 31 "voluntary cancel / blocking ast race=============="
+run_test 31a "voluntary cancel / blocking ast race=============="
+
+test_31b() {
+        remote_ost || { skip "local OST" && return 0; }
+	remote_ost_nodsh && skip "remote OST w/o dsh" && return 0
+        mkdir -p $DIR1/$tdir || error "Creating dir $DIR1/$tdir"
+        lfs setstripe $DIR/$tdir/$tfile -i 0 -c 1
+        cp /etc/hosts $DIR/$tdir/$tfile
+        #define OBD_FAIL_LDLM_CANCEL_BL_CB_RACE   0x314
+        lctl set_param fail_loc=0x314
+        #define OBD_FAIL_LDLM_OST_FAIL_RACE      0x316
+        do_facet ost1 lctl set_param fail_loc=0x316
+        # Don't crash kernel
+        cat $DIR2/$tdir/$tfile > /dev/null 2>&1
+        lctl set_param fail_loc=0
+        do_facet ost1 lctl set_param fail_loc=0
+}
+run_test 31b "voluntary OST cancel / blocking ast race=============="
 
 # enable/disable lockless truncate feature, depending on the arg 0/1
 enable_lockless_truncate() {