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() {