|
@@ -90,6 +90,7 @@ static void __receive_convert_reply(struct dlm_rsb *r, struct dlm_lkb *lkb,
|
|
|
static int receive_extralen(struct dlm_message *ms);
|
|
|
static void do_purge(struct dlm_ls *ls, int nodeid, int pid);
|
|
|
static void del_timeout(struct dlm_lkb *lkb);
|
|
|
+static void toss_rsb(struct kref *kref);
|
|
|
|
|
|
/*
|
|
|
* Lock compatibilty matrix - thanks Steve
|
|
@@ -170,9 +171,11 @@ void dlm_print_lkb(struct dlm_lkb *lkb)
|
|
|
|
|
|
static void dlm_print_rsb(struct dlm_rsb *r)
|
|
|
{
|
|
|
- printk(KERN_ERR "rsb: nodeid %d flags %lx first %x rlc %d name %s\n",
|
|
|
- r->res_nodeid, r->res_flags, r->res_first_lkid,
|
|
|
- r->res_recover_locks_count, r->res_name);
|
|
|
+ printk(KERN_ERR "rsb: nodeid %d master %d dir %d flags %lx first %x "
|
|
|
+ "rlc %d name %s\n",
|
|
|
+ r->res_nodeid, r->res_master_nodeid, r->res_dir_nodeid,
|
|
|
+ r->res_flags, r->res_first_lkid, r->res_recover_locks_count,
|
|
|
+ r->res_name);
|
|
|
}
|
|
|
|
|
|
void dlm_dump_rsb(struct dlm_rsb *r)
|
|
@@ -327,6 +330,37 @@ static void queue_bast(struct dlm_rsb *r, struct dlm_lkb *lkb, int rqmode)
|
|
|
* Basic operations on rsb's and lkb's
|
|
|
*/
|
|
|
|
|
|
+/* This is only called to add a reference when the code already holds
|
|
|
+ a valid reference to the rsb, so there's no need for locking. */
|
|
|
+
|
|
|
+static inline void hold_rsb(struct dlm_rsb *r)
|
|
|
+{
|
|
|
+ kref_get(&r->res_ref);
|
|
|
+}
|
|
|
+
|
|
|
+void dlm_hold_rsb(struct dlm_rsb *r)
|
|
|
+{
|
|
|
+ hold_rsb(r);
|
|
|
+}
|
|
|
+
|
|
|
+/* When all references to the rsb are gone it's transferred to
|
|
|
+ the tossed list for later disposal. */
|
|
|
+
|
|
|
+static void put_rsb(struct dlm_rsb *r)
|
|
|
+{
|
|
|
+ struct dlm_ls *ls = r->res_ls;
|
|
|
+ uint32_t bucket = r->res_bucket;
|
|
|
+
|
|
|
+ spin_lock(&ls->ls_rsbtbl[bucket].lock);
|
|
|
+ kref_put(&r->res_ref, toss_rsb);
|
|
|
+ spin_unlock(&ls->ls_rsbtbl[bucket].lock);
|
|
|
+}
|
|
|
+
|
|
|
+void dlm_put_rsb(struct dlm_rsb *r)
|
|
|
+{
|
|
|
+ put_rsb(r);
|
|
|
+}
|
|
|
+
|
|
|
static int pre_rsb_struct(struct dlm_ls *ls)
|
|
|
{
|
|
|
struct dlm_rsb *r1, *r2;
|
|
@@ -411,11 +445,10 @@ static int rsb_cmp(struct dlm_rsb *r, const char *name, int nlen)
|
|
|
}
|
|
|
|
|
|
int dlm_search_rsb_tree(struct rb_root *tree, char *name, int len,
|
|
|
- unsigned int flags, struct dlm_rsb **r_ret)
|
|
|
+ struct dlm_rsb **r_ret)
|
|
|
{
|
|
|
struct rb_node *node = tree->rb_node;
|
|
|
struct dlm_rsb *r;
|
|
|
- int error = 0;
|
|
|
int rc;
|
|
|
|
|
|
while (node) {
|
|
@@ -432,10 +465,8 @@ int dlm_search_rsb_tree(struct rb_root *tree, char *name, int len,
|
|
|
return -EBADR;
|
|
|
|
|
|
found:
|
|
|
- if (r->res_nodeid && (flags & R_MASTER))
|
|
|
- error = -ENOTBLK;
|
|
|
*r_ret = r;
|
|
|
- return error;
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
static int rsb_insert(struct dlm_rsb *rsb, struct rb_root *tree)
|
|
@@ -467,124 +498,587 @@ static int rsb_insert(struct dlm_rsb *rsb, struct rb_root *tree)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static int _search_rsb(struct dlm_ls *ls, char *name, int len, int b,
|
|
|
- unsigned int flags, struct dlm_rsb **r_ret)
|
|
|
+/*
|
|
|
+ * Find rsb in rsbtbl and potentially create/add one
|
|
|
+ *
|
|
|
+ * Delaying the release of rsb's has a similar benefit to applications keeping
|
|
|
+ * NL locks on an rsb, but without the guarantee that the cached master value
|
|
|
+ * will still be valid when the rsb is reused. Apps aren't always smart enough
|
|
|
+ * to keep NL locks on an rsb that they may lock again shortly; this can lead
|
|
|
+ * to excessive master lookups and removals if we don't delay the release.
|
|
|
+ *
|
|
|
+ * Searching for an rsb means looking through both the normal list and toss
|
|
|
+ * list. When found on the toss list the rsb is moved to the normal list with
|
|
|
+ * ref count of 1; when found on normal list the ref count is incremented.
|
|
|
+ *
|
|
|
+ * rsb's on the keep list are being used locally and refcounted.
|
|
|
+ * rsb's on the toss list are not being used locally, and are not refcounted.
|
|
|
+ *
|
|
|
+ * The toss list rsb's were either
|
|
|
+ * - previously used locally but not any more (were on keep list, then
|
|
|
+ * moved to toss list when last refcount dropped)
|
|
|
+ * - created and put on toss list as a directory record for a lookup
|
|
|
+ * (we are the dir node for the res, but are not using the res right now,
|
|
|
+ * but some other node is)
|
|
|
+ *
|
|
|
+ * The purpose of find_rsb() is to return a refcounted rsb for local use.
|
|
|
+ * So, if the given rsb is on the toss list, it is moved to the keep list
|
|
|
+ * before being returned.
|
|
|
+ *
|
|
|
+ * toss_rsb() happens when all local usage of the rsb is done, i.e. no
|
|
|
+ * more refcounts exist, so the rsb is moved from the keep list to the
|
|
|
+ * toss list.
|
|
|
+ *
|
|
|
+ * rsb's on both keep and toss lists are used for doing a name to master
|
|
|
+ * lookups. rsb's that are in use locally (and being refcounted) are on
|
|
|
+ * the keep list, rsb's that are not in use locally (not refcounted) and
|
|
|
+ * only exist for name/master lookups are on the toss list.
|
|
|
+ *
|
|
|
+ * rsb's on the toss list who's dir_nodeid is not local can have stale
|
|
|
+ * name/master mappings. So, remote requests on such rsb's can potentially
|
|
|
+ * return with an error, which means the mapping is stale and needs to
|
|
|
+ * be updated with a new lookup. (The idea behind MASTER UNCERTAIN and
|
|
|
+ * first_lkid is to keep only a single outstanding request on an rsb
|
|
|
+ * while that rsb has a potentially stale master.)
|
|
|
+ */
|
|
|
+
|
|
|
+static int find_rsb_dir(struct dlm_ls *ls, char *name, int len,
|
|
|
+ uint32_t hash, uint32_t b,
|
|
|
+ int dir_nodeid, int from_nodeid,
|
|
|
+ unsigned int flags, struct dlm_rsb **r_ret)
|
|
|
{
|
|
|
- struct dlm_rsb *r;
|
|
|
+ struct dlm_rsb *r = NULL;
|
|
|
+ int our_nodeid = dlm_our_nodeid();
|
|
|
+ int from_local = 0;
|
|
|
+ int from_other = 0;
|
|
|
+ int from_dir = 0;
|
|
|
+ int create = 0;
|
|
|
int error;
|
|
|
|
|
|
- error = dlm_search_rsb_tree(&ls->ls_rsbtbl[b].keep, name, len, flags, &r);
|
|
|
- if (!error) {
|
|
|
- kref_get(&r->res_ref);
|
|
|
- goto out;
|
|
|
+ if (flags & R_RECEIVE_REQUEST) {
|
|
|
+ if (from_nodeid == dir_nodeid)
|
|
|
+ from_dir = 1;
|
|
|
+ else
|
|
|
+ from_other = 1;
|
|
|
+ } else if (flags & R_REQUEST) {
|
|
|
+ from_local = 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * flags & R_RECEIVE_RECOVER is from dlm_recover_master_copy, so
|
|
|
+ * from_nodeid has sent us a lock in dlm_recover_locks, believing
|
|
|
+ * we're the new master. Our local recovery may not have set
|
|
|
+ * res_master_nodeid to our_nodeid yet, so allow either. Don't
|
|
|
+ * create the rsb; dlm_recover_process_copy() will handle EBADR
|
|
|
+ * by resending.
|
|
|
+ *
|
|
|
+ * If someone sends us a request, we are the dir node, and we do
|
|
|
+ * not find the rsb anywhere, then recreate it. This happens if
|
|
|
+ * someone sends us a request after we have removed/freed an rsb
|
|
|
+ * from our toss list. (They sent a request instead of lookup
|
|
|
+ * because they are using an rsb from their toss list.)
|
|
|
+ */
|
|
|
+
|
|
|
+ if (from_local || from_dir ||
|
|
|
+ (from_other && (dir_nodeid == our_nodeid))) {
|
|
|
+ create = 1;
|
|
|
}
|
|
|
- if (error == -ENOTBLK)
|
|
|
- goto out;
|
|
|
|
|
|
- error = dlm_search_rsb_tree(&ls->ls_rsbtbl[b].toss, name, len, flags, &r);
|
|
|
+ retry:
|
|
|
+ if (create) {
|
|
|
+ error = pre_rsb_struct(ls);
|
|
|
+ if (error < 0)
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ spin_lock(&ls->ls_rsbtbl[b].lock);
|
|
|
+
|
|
|
+ error = dlm_search_rsb_tree(&ls->ls_rsbtbl[b].keep, name, len, &r);
|
|
|
if (error)
|
|
|
- goto out;
|
|
|
+ goto do_toss;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * rsb is active, so we can't check master_nodeid without lock_rsb.
|
|
|
+ */
|
|
|
|
|
|
- rb_erase(&r->res_hashnode, &ls->ls_rsbtbl[b].toss);
|
|
|
- error = rsb_insert(r, &ls->ls_rsbtbl[b].keep);
|
|
|
+ kref_get(&r->res_ref);
|
|
|
+ error = 0;
|
|
|
+ goto out_unlock;
|
|
|
+
|
|
|
+
|
|
|
+ do_toss:
|
|
|
+ error = dlm_search_rsb_tree(&ls->ls_rsbtbl[b].toss, name, len, &r);
|
|
|
if (error)
|
|
|
- return error;
|
|
|
+ goto do_new;
|
|
|
|
|
|
- if (dlm_no_directory(ls))
|
|
|
- goto out;
|
|
|
+ /*
|
|
|
+ * rsb found inactive (master_nodeid may be out of date unless
|
|
|
+ * we are the dir_nodeid or were the master) No other thread
|
|
|
+ * is using this rsb because it's on the toss list, so we can
|
|
|
+ * look at or update res_master_nodeid without lock_rsb.
|
|
|
+ */
|
|
|
|
|
|
- if (r->res_nodeid == -1) {
|
|
|
+ if ((r->res_master_nodeid != our_nodeid) && from_other) {
|
|
|
+ /* our rsb was not master, and another node (not the dir node)
|
|
|
+ has sent us a request */
|
|
|
+ log_debug(ls, "find_rsb toss from_other %d master %d dir %d %s",
|
|
|
+ from_nodeid, r->res_master_nodeid, dir_nodeid,
|
|
|
+ r->res_name);
|
|
|
+ error = -ENOTBLK;
|
|
|
+ goto out_unlock;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ((r->res_master_nodeid != our_nodeid) && from_dir) {
|
|
|
+ /* don't think this should ever happen */
|
|
|
+ log_error(ls, "find_rsb toss from_dir %d master %d",
|
|
|
+ from_nodeid, r->res_master_nodeid);
|
|
|
+ dlm_print_rsb(r);
|
|
|
+ /* fix it and go on */
|
|
|
+ r->res_master_nodeid = our_nodeid;
|
|
|
+ r->res_nodeid = 0;
|
|
|
rsb_clear_flag(r, RSB_MASTER_UNCERTAIN);
|
|
|
r->res_first_lkid = 0;
|
|
|
- } else if (r->res_nodeid > 0) {
|
|
|
+ }
|
|
|
+
|
|
|
+ if (from_local && (r->res_master_nodeid != our_nodeid)) {
|
|
|
+ /* Because we have held no locks on this rsb,
|
|
|
+ res_master_nodeid could have become stale. */
|
|
|
rsb_set_flag(r, RSB_MASTER_UNCERTAIN);
|
|
|
r->res_first_lkid = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ rb_erase(&r->res_hashnode, &ls->ls_rsbtbl[b].toss);
|
|
|
+ error = rsb_insert(r, &ls->ls_rsbtbl[b].keep);
|
|
|
+ goto out_unlock;
|
|
|
+
|
|
|
+
|
|
|
+ do_new:
|
|
|
+ /*
|
|
|
+ * rsb not found
|
|
|
+ */
|
|
|
+
|
|
|
+ if (error == -EBADR && !create)
|
|
|
+ goto out_unlock;
|
|
|
+
|
|
|
+ error = get_rsb_struct(ls, name, len, &r);
|
|
|
+ if (error == -EAGAIN) {
|
|
|
+ spin_unlock(&ls->ls_rsbtbl[b].lock);
|
|
|
+ goto retry;
|
|
|
+ }
|
|
|
+ if (error)
|
|
|
+ goto out_unlock;
|
|
|
+
|
|
|
+ r->res_hash = hash;
|
|
|
+ r->res_bucket = b;
|
|
|
+ r->res_dir_nodeid = dir_nodeid;
|
|
|
+ kref_init(&r->res_ref);
|
|
|
+
|
|
|
+ if (from_dir) {
|
|
|
+ /* want to see how often this happens */
|
|
|
+ log_debug(ls, "find_rsb new from_dir %d recreate %s",
|
|
|
+ from_nodeid, r->res_name);
|
|
|
+ r->res_master_nodeid = our_nodeid;
|
|
|
+ r->res_nodeid = 0;
|
|
|
+ goto out_add;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (from_other && (dir_nodeid != our_nodeid)) {
|
|
|
+ /* should never happen */
|
|
|
+ log_error(ls, "find_rsb new from_other %d dir %d our %d %s",
|
|
|
+ from_nodeid, dir_nodeid, our_nodeid, r->res_name);
|
|
|
+ dlm_free_rsb(r);
|
|
|
+ error = -ENOTBLK;
|
|
|
+ goto out_unlock;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (from_other) {
|
|
|
+ log_debug(ls, "find_rsb new from_other %d dir %d %s",
|
|
|
+ from_nodeid, dir_nodeid, r->res_name);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (dir_nodeid == our_nodeid) {
|
|
|
+ /* When we are the dir nodeid, we can set the master
|
|
|
+ node immediately */
|
|
|
+ r->res_master_nodeid = our_nodeid;
|
|
|
+ r->res_nodeid = 0;
|
|
|
} else {
|
|
|
- DLM_ASSERT(r->res_nodeid == 0, dlm_print_rsb(r););
|
|
|
- DLM_ASSERT(!rsb_flag(r, RSB_MASTER_UNCERTAIN),);
|
|
|
+ /* set_master will send_lookup to dir_nodeid */
|
|
|
+ r->res_master_nodeid = 0;
|
|
|
+ r->res_nodeid = -1;
|
|
|
}
|
|
|
+
|
|
|
+ out_add:
|
|
|
+ error = rsb_insert(r, &ls->ls_rsbtbl[b].keep);
|
|
|
+ out_unlock:
|
|
|
+ spin_unlock(&ls->ls_rsbtbl[b].lock);
|
|
|
out:
|
|
|
*r_ret = r;
|
|
|
return error;
|
|
|
}
|
|
|
|
|
|
+/* During recovery, other nodes can send us new MSTCPY locks (from
|
|
|
+ dlm_recover_locks) before we've made ourself master (in
|
|
|
+ dlm_recover_masters). */
|
|
|
+
|
|
|
+static int find_rsb_nodir(struct dlm_ls *ls, char *name, int len,
|
|
|
+ uint32_t hash, uint32_t b,
|
|
|
+ int dir_nodeid, int from_nodeid,
|
|
|
+ unsigned int flags, struct dlm_rsb **r_ret)
|
|
|
+{
|
|
|
+ struct dlm_rsb *r = NULL;
|
|
|
+ int our_nodeid = dlm_our_nodeid();
|
|
|
+ int recover = (flags & R_RECEIVE_RECOVER);
|
|
|
+ int error;
|
|
|
+
|
|
|
+ retry:
|
|
|
+ error = pre_rsb_struct(ls);
|
|
|
+ if (error < 0)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ spin_lock(&ls->ls_rsbtbl[b].lock);
|
|
|
+
|
|
|
+ error = dlm_search_rsb_tree(&ls->ls_rsbtbl[b].keep, name, len, &r);
|
|
|
+ if (error)
|
|
|
+ goto do_toss;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * rsb is active, so we can't check master_nodeid without lock_rsb.
|
|
|
+ */
|
|
|
+
|
|
|
+ kref_get(&r->res_ref);
|
|
|
+ goto out_unlock;
|
|
|
+
|
|
|
+
|
|
|
+ do_toss:
|
|
|
+ error = dlm_search_rsb_tree(&ls->ls_rsbtbl[b].toss, name, len, &r);
|
|
|
+ if (error)
|
|
|
+ goto do_new;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * rsb found inactive. No other thread is using this rsb because
|
|
|
+ * it's on the toss list, so we can look at or update
|
|
|
+ * res_master_nodeid without lock_rsb.
|
|
|
+ */
|
|
|
+
|
|
|
+ if (!recover && (r->res_master_nodeid != our_nodeid) && from_nodeid) {
|
|
|
+ /* our rsb is not master, and another node has sent us a
|
|
|
+ request; this should never happen */
|
|
|
+ log_error(ls, "find_rsb toss from_nodeid %d master %d dir %d",
|
|
|
+ from_nodeid, r->res_master_nodeid, dir_nodeid);
|
|
|
+ dlm_print_rsb(r);
|
|
|
+ error = -ENOTBLK;
|
|
|
+ goto out_unlock;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!recover && (r->res_master_nodeid != our_nodeid) &&
|
|
|
+ (dir_nodeid == our_nodeid)) {
|
|
|
+ /* our rsb is not master, and we are dir; may as well fix it;
|
|
|
+ this should never happen */
|
|
|
+ log_error(ls, "find_rsb toss our %d master %d dir %d",
|
|
|
+ our_nodeid, r->res_master_nodeid, dir_nodeid);
|
|
|
+ dlm_print_rsb(r);
|
|
|
+ r->res_master_nodeid = our_nodeid;
|
|
|
+ r->res_nodeid = 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ rb_erase(&r->res_hashnode, &ls->ls_rsbtbl[b].toss);
|
|
|
+ error = rsb_insert(r, &ls->ls_rsbtbl[b].keep);
|
|
|
+ goto out_unlock;
|
|
|
+
|
|
|
+
|
|
|
+ do_new:
|
|
|
+ /*
|
|
|
+ * rsb not found
|
|
|
+ */
|
|
|
+
|
|
|
+ error = get_rsb_struct(ls, name, len, &r);
|
|
|
+ if (error == -EAGAIN) {
|
|
|
+ spin_unlock(&ls->ls_rsbtbl[b].lock);
|
|
|
+ goto retry;
|
|
|
+ }
|
|
|
+ if (error)
|
|
|
+ goto out_unlock;
|
|
|
+
|
|
|
+ r->res_hash = hash;
|
|
|
+ r->res_bucket = b;
|
|
|
+ r->res_dir_nodeid = dir_nodeid;
|
|
|
+ r->res_master_nodeid = dir_nodeid;
|
|
|
+ r->res_nodeid = (dir_nodeid == our_nodeid) ? 0 : dir_nodeid;
|
|
|
+ kref_init(&r->res_ref);
|
|
|
+
|
|
|
+ error = rsb_insert(r, &ls->ls_rsbtbl[b].keep);
|
|
|
+ out_unlock:
|
|
|
+ spin_unlock(&ls->ls_rsbtbl[b].lock);
|
|
|
+ out:
|
|
|
+ *r_ret = r;
|
|
|
+ return error;
|
|
|
+}
|
|
|
+
|
|
|
+static int find_rsb(struct dlm_ls *ls, char *name, int len, int from_nodeid,
|
|
|
+ unsigned int flags, struct dlm_rsb **r_ret)
|
|
|
+{
|
|
|
+ uint32_t hash, b;
|
|
|
+ int dir_nodeid;
|
|
|
+
|
|
|
+ if (len > DLM_RESNAME_MAXLEN)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ hash = jhash(name, len, 0);
|
|
|
+ b = hash & (ls->ls_rsbtbl_size - 1);
|
|
|
+
|
|
|
+ dir_nodeid = dlm_hash2nodeid(ls, hash);
|
|
|
+
|
|
|
+ if (dlm_no_directory(ls))
|
|
|
+ return find_rsb_nodir(ls, name, len, hash, b, dir_nodeid,
|
|
|
+ from_nodeid, flags, r_ret);
|
|
|
+ else
|
|
|
+ return find_rsb_dir(ls, name, len, hash, b, dir_nodeid,
|
|
|
+ from_nodeid, flags, r_ret);
|
|
|
+}
|
|
|
+
|
|
|
+/* we have received a request and found that res_master_nodeid != our_nodeid,
|
|
|
+ so we need to return an error or make ourself the master */
|
|
|
+
|
|
|
+static int validate_master_nodeid(struct dlm_ls *ls, struct dlm_rsb *r,
|
|
|
+ int from_nodeid)
|
|
|
+{
|
|
|
+ if (dlm_no_directory(ls)) {
|
|
|
+ log_error(ls, "find_rsb keep from_nodeid %d master %d dir %d",
|
|
|
+ from_nodeid, r->res_master_nodeid,
|
|
|
+ r->res_dir_nodeid);
|
|
|
+ dlm_print_rsb(r);
|
|
|
+ return -ENOTBLK;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (from_nodeid != r->res_dir_nodeid) {
|
|
|
+ /* our rsb is not master, and another node (not the dir node)
|
|
|
+ has sent us a request. this is much more common when our
|
|
|
+ master_nodeid is zero, so limit debug to non-zero. */
|
|
|
+
|
|
|
+ if (r->res_master_nodeid) {
|
|
|
+ log_debug(ls, "validate master from_other %d master %d "
|
|
|
+ "dir %d first %x %s", from_nodeid,
|
|
|
+ r->res_master_nodeid, r->res_dir_nodeid,
|
|
|
+ r->res_first_lkid, r->res_name);
|
|
|
+ }
|
|
|
+ return -ENOTBLK;
|
|
|
+ } else {
|
|
|
+ /* our rsb is not master, but the dir nodeid has sent us a
|
|
|
+ request; this could happen with master 0 / res_nodeid -1 */
|
|
|
+
|
|
|
+ if (r->res_master_nodeid) {
|
|
|
+ log_error(ls, "validate master from_dir %d master %d "
|
|
|
+ "first %x %s",
|
|
|
+ from_nodeid, r->res_master_nodeid,
|
|
|
+ r->res_first_lkid, r->res_name);
|
|
|
+ }
|
|
|
+
|
|
|
+ r->res_master_nodeid = dlm_our_nodeid();
|
|
|
+ r->res_nodeid = 0;
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
- * Find rsb in rsbtbl and potentially create/add one
|
|
|
+ * We're the dir node for this res and another node wants to know the
|
|
|
+ * master nodeid. During normal operation (non recovery) this is only
|
|
|
+ * called from receive_lookup(); master lookups when the local node is
|
|
|
+ * the dir node are done by find_rsb().
|
|
|
*
|
|
|
- * Delaying the release of rsb's has a similar benefit to applications keeping
|
|
|
- * NL locks on an rsb, but without the guarantee that the cached master value
|
|
|
- * will still be valid when the rsb is reused. Apps aren't always smart enough
|
|
|
- * to keep NL locks on an rsb that they may lock again shortly; this can lead
|
|
|
- * to excessive master lookups and removals if we don't delay the release.
|
|
|
+ * normal operation, we are the dir node for a resource
|
|
|
+ * . _request_lock
|
|
|
+ * . set_master
|
|
|
+ * . send_lookup
|
|
|
+ * . receive_lookup
|
|
|
+ * . dlm_master_lookup flags 0
|
|
|
*
|
|
|
- * Searching for an rsb means looking through both the normal list and toss
|
|
|
- * list. When found on the toss list the rsb is moved to the normal list with
|
|
|
- * ref count of 1; when found on normal list the ref count is incremented.
|
|
|
+ * recover directory, we are rebuilding dir for all resources
|
|
|
+ * . dlm_recover_directory
|
|
|
+ * . dlm_rcom_names
|
|
|
+ * remote node sends back the rsb names it is master of and we are dir of
|
|
|
+ * . dlm_master_lookup RECOVER_DIR (fix_master 0, from_master 1)
|
|
|
+ * we either create new rsb setting remote node as master, or find existing
|
|
|
+ * rsb and set master to be the remote node.
|
|
|
+ *
|
|
|
+ * recover masters, we are finding the new master for resources
|
|
|
+ * . dlm_recover_masters
|
|
|
+ * . recover_master
|
|
|
+ * . dlm_send_rcom_lookup
|
|
|
+ * . receive_rcom_lookup
|
|
|
+ * . dlm_master_lookup RECOVER_MASTER (fix_master 1, from_master 0)
|
|
|
*/
|
|
|
|
|
|
-static int find_rsb(struct dlm_ls *ls, char *name, int namelen,
|
|
|
- unsigned int flags, struct dlm_rsb **r_ret)
|
|
|
+int dlm_master_lookup(struct dlm_ls *ls, int from_nodeid, char *name, int len,
|
|
|
+ unsigned int flags, int *r_nodeid, int *result)
|
|
|
{
|
|
|
struct dlm_rsb *r = NULL;
|
|
|
- uint32_t hash, bucket;
|
|
|
- int error;
|
|
|
+ uint32_t hash, b;
|
|
|
+ int from_master = (flags & DLM_LU_RECOVER_DIR);
|
|
|
+ int fix_master = (flags & DLM_LU_RECOVER_MASTER);
|
|
|
+ int our_nodeid = dlm_our_nodeid();
|
|
|
+ int dir_nodeid, error, toss_list = 0;
|
|
|
|
|
|
- if (namelen > DLM_RESNAME_MAXLEN) {
|
|
|
- error = -EINVAL;
|
|
|
- goto out;
|
|
|
+ if (len > DLM_RESNAME_MAXLEN)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ if (from_nodeid == our_nodeid) {
|
|
|
+ log_error(ls, "dlm_master_lookup from our_nodeid %d flags %x",
|
|
|
+ our_nodeid, flags);
|
|
|
+ return -EINVAL;
|
|
|
}
|
|
|
|
|
|
- if (dlm_no_directory(ls))
|
|
|
- flags |= R_CREATE;
|
|
|
+ hash = jhash(name, len, 0);
|
|
|
+ b = hash & (ls->ls_rsbtbl_size - 1);
|
|
|
|
|
|
- hash = jhash(name, namelen, 0);
|
|
|
- bucket = hash & (ls->ls_rsbtbl_size - 1);
|
|
|
+ dir_nodeid = dlm_hash2nodeid(ls, hash);
|
|
|
+ if (dir_nodeid != our_nodeid) {
|
|
|
+ log_error(ls, "dlm_master_lookup from %d dir %d our %d h %x %d",
|
|
|
+ from_nodeid, dir_nodeid, our_nodeid, hash,
|
|
|
+ ls->ls_num_nodes);
|
|
|
+ *r_nodeid = -1;
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
|
|
|
retry:
|
|
|
- if (flags & R_CREATE) {
|
|
|
- error = pre_rsb_struct(ls);
|
|
|
- if (error < 0)
|
|
|
- goto out;
|
|
|
+ error = pre_rsb_struct(ls);
|
|
|
+ if (error < 0)
|
|
|
+ return error;
|
|
|
+
|
|
|
+ spin_lock(&ls->ls_rsbtbl[b].lock);
|
|
|
+ error = dlm_search_rsb_tree(&ls->ls_rsbtbl[b].keep, name, len, &r);
|
|
|
+ if (!error) {
|
|
|
+ /* because the rsb is active, we need to lock_rsb before
|
|
|
+ checking/changing re_master_nodeid */
|
|
|
+
|
|
|
+ hold_rsb(r);
|
|
|
+ spin_unlock(&ls->ls_rsbtbl[b].lock);
|
|
|
+ lock_rsb(r);
|
|
|
+ goto found;
|
|
|
}
|
|
|
|
|
|
- spin_lock(&ls->ls_rsbtbl[bucket].lock);
|
|
|
+ error = dlm_search_rsb_tree(&ls->ls_rsbtbl[b].toss, name, len, &r);
|
|
|
+ if (error)
|
|
|
+ goto not_found;
|
|
|
|
|
|
- error = _search_rsb(ls, name, namelen, bucket, flags, &r);
|
|
|
- if (!error)
|
|
|
- goto out_unlock;
|
|
|
+ /* because the rsb is inactive (on toss list), it's not refcounted
|
|
|
+ and lock_rsb is not used, but is protected by the rsbtbl lock */
|
|
|
|
|
|
- if (error == -EBADR && !(flags & R_CREATE))
|
|
|
- goto out_unlock;
|
|
|
+ toss_list = 1;
|
|
|
+ found:
|
|
|
+ if (r->res_dir_nodeid != our_nodeid) {
|
|
|
+ /* should not happen, but may as well fix it and carry on */
|
|
|
+ log_error(ls, "dlm_master_lookup res_dir %d our %d %s",
|
|
|
+ r->res_dir_nodeid, our_nodeid, r->res_name);
|
|
|
+ r->res_dir_nodeid = our_nodeid;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (fix_master && dlm_is_removed(ls, r->res_master_nodeid)) {
|
|
|
+ /* Recovery uses this function to set a new master when
|
|
|
+ the previous master failed. Setting NEW_MASTER will
|
|
|
+ force dlm_recover_masters to call recover_master on this
|
|
|
+ rsb even though the res_nodeid is no longer removed. */
|
|
|
+
|
|
|
+ r->res_master_nodeid = from_nodeid;
|
|
|
+ r->res_nodeid = from_nodeid;
|
|
|
+ rsb_set_flag(r, RSB_NEW_MASTER);
|
|
|
+
|
|
|
+ if (toss_list) {
|
|
|
+ /* I don't think we should ever find it on toss list. */
|
|
|
+ log_error(ls, "dlm_master_lookup fix_master on toss");
|
|
|
+ dlm_dump_rsb(r);
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- /* the rsb was found but wasn't a master copy */
|
|
|
- if (error == -ENOTBLK)
|
|
|
- goto out_unlock;
|
|
|
+ if (from_master && (r->res_master_nodeid != from_nodeid)) {
|
|
|
+ /* this will happen if from_nodeid became master during
|
|
|
+ a previous recovery cycle, and we aborted the previous
|
|
|
+ cycle before recovering this master value */
|
|
|
|
|
|
- error = get_rsb_struct(ls, name, namelen, &r);
|
|
|
+ log_limit(ls, "dlm_master_lookup from_master %d "
|
|
|
+ "master_nodeid %d res_nodeid %d first %x %s",
|
|
|
+ from_nodeid, r->res_master_nodeid, r->res_nodeid,
|
|
|
+ r->res_first_lkid, r->res_name);
|
|
|
+
|
|
|
+ if (r->res_master_nodeid == our_nodeid) {
|
|
|
+ log_error(ls, "from_master %d our_master", from_nodeid);
|
|
|
+ dlm_dump_rsb(r);
|
|
|
+ dlm_send_rcom_lookup_dump(r, from_nodeid);
|
|
|
+ goto out_found;
|
|
|
+ }
|
|
|
+
|
|
|
+ r->res_master_nodeid = from_nodeid;
|
|
|
+ r->res_nodeid = from_nodeid;
|
|
|
+ rsb_set_flag(r, RSB_NEW_MASTER);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!r->res_master_nodeid) {
|
|
|
+ /* this will happen if recovery happens while we're looking
|
|
|
+ up the master for this rsb */
|
|
|
+
|
|
|
+ log_debug(ls, "dlm_master_lookup master 0 to %d first %x %s",
|
|
|
+ from_nodeid, r->res_first_lkid, r->res_name);
|
|
|
+ r->res_master_nodeid = from_nodeid;
|
|
|
+ r->res_nodeid = from_nodeid;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!from_master && !fix_master &&
|
|
|
+ (r->res_master_nodeid == from_nodeid)) {
|
|
|
+ /* this can happen when the master sends remove, the dir node
|
|
|
+ finds the rsb on the keep list and ignores the remove,
|
|
|
+ and the former master sends a lookup */
|
|
|
+
|
|
|
+ log_limit(ls, "dlm_master_lookup from master %d flags %x "
|
|
|
+ "first %x %s", from_nodeid, flags,
|
|
|
+ r->res_first_lkid, r->res_name);
|
|
|
+ }
|
|
|
+
|
|
|
+ out_found:
|
|
|
+ *r_nodeid = r->res_master_nodeid;
|
|
|
+ if (result)
|
|
|
+ *result = DLM_LU_MATCH;
|
|
|
+
|
|
|
+ if (toss_list) {
|
|
|
+ r->res_toss_time = jiffies;
|
|
|
+ /* the rsb was inactive (on toss list) */
|
|
|
+ spin_unlock(&ls->ls_rsbtbl[b].lock);
|
|
|
+ } else {
|
|
|
+ /* the rsb was active */
|
|
|
+ unlock_rsb(r);
|
|
|
+ put_rsb(r);
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ not_found:
|
|
|
+ error = get_rsb_struct(ls, name, len, &r);
|
|
|
if (error == -EAGAIN) {
|
|
|
- spin_unlock(&ls->ls_rsbtbl[bucket].lock);
|
|
|
+ spin_unlock(&ls->ls_rsbtbl[b].lock);
|
|
|
goto retry;
|
|
|
}
|
|
|
if (error)
|
|
|
goto out_unlock;
|
|
|
|
|
|
r->res_hash = hash;
|
|
|
- r->res_bucket = bucket;
|
|
|
- r->res_nodeid = -1;
|
|
|
+ r->res_bucket = b;
|
|
|
+ r->res_dir_nodeid = our_nodeid;
|
|
|
+ r->res_master_nodeid = from_nodeid;
|
|
|
+ r->res_nodeid = from_nodeid;
|
|
|
kref_init(&r->res_ref);
|
|
|
+ r->res_toss_time = jiffies;
|
|
|
|
|
|
- /* With no directory, the master can be set immediately */
|
|
|
- if (dlm_no_directory(ls)) {
|
|
|
- int nodeid = dlm_dir_nodeid(r);
|
|
|
- if (nodeid == dlm_our_nodeid())
|
|
|
- nodeid = 0;
|
|
|
- r->res_nodeid = nodeid;
|
|
|
+ error = rsb_insert(r, &ls->ls_rsbtbl[b].toss);
|
|
|
+ if (error) {
|
|
|
+ /* should never happen */
|
|
|
+ dlm_free_rsb(r);
|
|
|
+ spin_unlock(&ls->ls_rsbtbl[b].lock);
|
|
|
+ goto retry;
|
|
|
}
|
|
|
- error = rsb_insert(r, &ls->ls_rsbtbl[bucket].keep);
|
|
|
+
|
|
|
+ if (result)
|
|
|
+ *result = DLM_LU_ADD;
|
|
|
+ *r_nodeid = from_nodeid;
|
|
|
+ error = 0;
|
|
|
out_unlock:
|
|
|
- spin_unlock(&ls->ls_rsbtbl[bucket].lock);
|
|
|
- out:
|
|
|
- *r_ret = r;
|
|
|
+ spin_unlock(&ls->ls_rsbtbl[b].lock);
|
|
|
return error;
|
|
|
}
|
|
|
|
|
@@ -605,17 +1099,27 @@ static void dlm_dump_rsb_hash(struct dlm_ls *ls, uint32_t hash)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-/* This is only called to add a reference when the code already holds
|
|
|
- a valid reference to the rsb, so there's no need for locking. */
|
|
|
-
|
|
|
-static inline void hold_rsb(struct dlm_rsb *r)
|
|
|
+void dlm_dump_rsb_name(struct dlm_ls *ls, char *name, int len)
|
|
|
{
|
|
|
- kref_get(&r->res_ref);
|
|
|
-}
|
|
|
+ struct dlm_rsb *r = NULL;
|
|
|
+ uint32_t hash, b;
|
|
|
+ int error;
|
|
|
|
|
|
-void dlm_hold_rsb(struct dlm_rsb *r)
|
|
|
-{
|
|
|
- hold_rsb(r);
|
|
|
+ hash = jhash(name, len, 0);
|
|
|
+ b = hash & (ls->ls_rsbtbl_size - 1);
|
|
|
+
|
|
|
+ spin_lock(&ls->ls_rsbtbl[b].lock);
|
|
|
+ error = dlm_search_rsb_tree(&ls->ls_rsbtbl[b].keep, name, len, &r);
|
|
|
+ if (!error)
|
|
|
+ goto out_dump;
|
|
|
+
|
|
|
+ error = dlm_search_rsb_tree(&ls->ls_rsbtbl[b].toss, name, len, &r);
|
|
|
+ if (error)
|
|
|
+ goto out;
|
|
|
+ out_dump:
|
|
|
+ dlm_dump_rsb(r);
|
|
|
+ out:
|
|
|
+ spin_unlock(&ls->ls_rsbtbl[b].lock);
|
|
|
}
|
|
|
|
|
|
static void toss_rsb(struct kref *kref)
|
|
@@ -634,24 +1138,6 @@ static void toss_rsb(struct kref *kref)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-/* When all references to the rsb are gone it's transferred to
|
|
|
- the tossed list for later disposal. */
|
|
|
-
|
|
|
-static void put_rsb(struct dlm_rsb *r)
|
|
|
-{
|
|
|
- struct dlm_ls *ls = r->res_ls;
|
|
|
- uint32_t bucket = r->res_bucket;
|
|
|
-
|
|
|
- spin_lock(&ls->ls_rsbtbl[bucket].lock);
|
|
|
- kref_put(&r->res_ref, toss_rsb);
|
|
|
- spin_unlock(&ls->ls_rsbtbl[bucket].lock);
|
|
|
-}
|
|
|
-
|
|
|
-void dlm_put_rsb(struct dlm_rsb *r)
|
|
|
-{
|
|
|
- put_rsb(r);
|
|
|
-}
|
|
|
-
|
|
|
/* See comment for unhold_lkb */
|
|
|
|
|
|
static void unhold_rsb(struct dlm_rsb *r)
|
|
@@ -1138,27 +1624,13 @@ static int remove_from_waiters_ms(struct dlm_lkb *lkb, struct dlm_message *ms)
|
|
|
return error;
|
|
|
}
|
|
|
|
|
|
-static void dir_remove(struct dlm_rsb *r)
|
|
|
-{
|
|
|
- int to_nodeid;
|
|
|
-
|
|
|
- if (dlm_no_directory(r->res_ls))
|
|
|
- return;
|
|
|
-
|
|
|
- to_nodeid = dlm_dir_nodeid(r);
|
|
|
- if (to_nodeid != dlm_our_nodeid())
|
|
|
- send_remove(r);
|
|
|
- else
|
|
|
- dlm_dir_remove_entry(r->res_ls, to_nodeid,
|
|
|
- r->res_name, r->res_length);
|
|
|
-}
|
|
|
-
|
|
|
/* FIXME: make this more efficient */
|
|
|
|
|
|
static int shrink_bucket(struct dlm_ls *ls, int b)
|
|
|
{
|
|
|
struct rb_node *n;
|
|
|
struct dlm_rsb *r;
|
|
|
+ int our_nodeid = dlm_our_nodeid();
|
|
|
int count = 0, found;
|
|
|
|
|
|
for (;;) {
|
|
@@ -1166,6 +1638,17 @@ static int shrink_bucket(struct dlm_ls *ls, int b)
|
|
|
spin_lock(&ls->ls_rsbtbl[b].lock);
|
|
|
for (n = rb_first(&ls->ls_rsbtbl[b].toss); n; n = rb_next(n)) {
|
|
|
r = rb_entry(n, struct dlm_rsb, res_hashnode);
|
|
|
+
|
|
|
+ /* If we're the directory record for this rsb, and
|
|
|
+ we're not the master of it, then we need to wait
|
|
|
+ for the master node to send us a dir remove for
|
|
|
+ before removing the dir record. */
|
|
|
+
|
|
|
+ if (!dlm_no_directory(ls) && !is_master(r) &&
|
|
|
+ (dlm_dir_nodeid(r) == our_nodeid)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
if (!time_after_eq(jiffies, r->res_toss_time +
|
|
|
dlm_config.ci_toss_secs * HZ))
|
|
|
continue;
|
|
@@ -1182,8 +1665,15 @@ static int shrink_bucket(struct dlm_ls *ls, int b)
|
|
|
rb_erase(&r->res_hashnode, &ls->ls_rsbtbl[b].toss);
|
|
|
spin_unlock(&ls->ls_rsbtbl[b].lock);
|
|
|
|
|
|
- if (is_master(r))
|
|
|
- dir_remove(r);
|
|
|
+ /* We're the master of this rsb but we're not
|
|
|
+ the directory record, so we need to tell the
|
|
|
+ dir node to remove the dir record. */
|
|
|
+
|
|
|
+ if (!dlm_no_directory(ls) && is_master(r) &&
|
|
|
+ (dlm_dir_nodeid(r) != our_nodeid)) {
|
|
|
+ send_remove(r);
|
|
|
+ }
|
|
|
+
|
|
|
dlm_free_rsb(r);
|
|
|
count++;
|
|
|
} else {
|
|
@@ -2078,8 +2568,7 @@ static void send_blocking_asts_all(struct dlm_rsb *r, struct dlm_lkb *lkb)
|
|
|
|
|
|
static int set_master(struct dlm_rsb *r, struct dlm_lkb *lkb)
|
|
|
{
|
|
|
- struct dlm_ls *ls = r->res_ls;
|
|
|
- int i, error, dir_nodeid, ret_nodeid, our_nodeid = dlm_our_nodeid();
|
|
|
+ int our_nodeid = dlm_our_nodeid();
|
|
|
|
|
|
if (rsb_flag(r, RSB_MASTER_UNCERTAIN)) {
|
|
|
rsb_clear_flag(r, RSB_MASTER_UNCERTAIN);
|
|
@@ -2093,53 +2582,35 @@ static int set_master(struct dlm_rsb *r, struct dlm_lkb *lkb)
|
|
|
return 1;
|
|
|
}
|
|
|
|
|
|
- if (r->res_nodeid == 0) {
|
|
|
+ if (r->res_master_nodeid == our_nodeid) {
|
|
|
lkb->lkb_nodeid = 0;
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
- if (r->res_nodeid > 0) {
|
|
|
- lkb->lkb_nodeid = r->res_nodeid;
|
|
|
+ if (r->res_master_nodeid) {
|
|
|
+ lkb->lkb_nodeid = r->res_master_nodeid;
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
- DLM_ASSERT(r->res_nodeid == -1, dlm_dump_rsb(r););
|
|
|
-
|
|
|
- dir_nodeid = dlm_dir_nodeid(r);
|
|
|
-
|
|
|
- if (dir_nodeid != our_nodeid) {
|
|
|
- r->res_first_lkid = lkb->lkb_id;
|
|
|
- send_lookup(r, lkb);
|
|
|
- return 1;
|
|
|
- }
|
|
|
-
|
|
|
- for (i = 0; i < 2; i++) {
|
|
|
- /* It's possible for dlm_scand to remove an old rsb for
|
|
|
- this same resource from the toss list, us to create
|
|
|
- a new one, look up the master locally, and find it
|
|
|
- already exists just before dlm_scand does the
|
|
|
- dir_remove() on the previous rsb. */
|
|
|
-
|
|
|
- error = dlm_dir_lookup(ls, our_nodeid, r->res_name,
|
|
|
- r->res_length, &ret_nodeid);
|
|
|
- if (!error)
|
|
|
- break;
|
|
|
- log_debug(ls, "dir_lookup error %d %s", error, r->res_name);
|
|
|
- schedule();
|
|
|
- }
|
|
|
- if (error && error != -EEXIST)
|
|
|
- return error;
|
|
|
-
|
|
|
- if (ret_nodeid == our_nodeid) {
|
|
|
- r->res_first_lkid = 0;
|
|
|
+ if (dlm_dir_nodeid(r) == our_nodeid) {
|
|
|
+ /* This is a somewhat unusual case; find_rsb will usually
|
|
|
+ have set res_master_nodeid when dir nodeid is local, but
|
|
|
+ there are cases where we become the dir node after we've
|
|
|
+ past find_rsb and go through _request_lock again.
|
|
|
+ confirm_master() or process_lookup_list() needs to be
|
|
|
+ called after this. */
|
|
|
+ log_debug(r->res_ls, "set_master %x self master %d dir %d %s",
|
|
|
+ lkb->lkb_id, r->res_master_nodeid, r->res_dir_nodeid,
|
|
|
+ r->res_name);
|
|
|
+ r->res_master_nodeid = our_nodeid;
|
|
|
r->res_nodeid = 0;
|
|
|
lkb->lkb_nodeid = 0;
|
|
|
- } else {
|
|
|
- r->res_first_lkid = lkb->lkb_id;
|
|
|
- r->res_nodeid = ret_nodeid;
|
|
|
- lkb->lkb_nodeid = ret_nodeid;
|
|
|
+ return 0;
|
|
|
}
|
|
|
- return 0;
|
|
|
+
|
|
|
+ r->res_first_lkid = lkb->lkb_id;
|
|
|
+ send_lookup(r, lkb);
|
|
|
+ return 1;
|
|
|
}
|
|
|
|
|
|
static void process_lookup_list(struct dlm_rsb *r)
|
|
@@ -2584,7 +3055,7 @@ static void do_unlock_effects(struct dlm_rsb *r, struct dlm_lkb *lkb,
|
|
|
}
|
|
|
|
|
|
/* returns: 0 did nothing, -DLM_ECANCEL canceled lock */
|
|
|
-
|
|
|
+
|
|
|
static int do_cancel(struct dlm_rsb *r, struct dlm_lkb *lkb)
|
|
|
{
|
|
|
int error;
|
|
@@ -2708,11 +3179,11 @@ static int request_lock(struct dlm_ls *ls, struct dlm_lkb *lkb, char *name,
|
|
|
|
|
|
error = validate_lock_args(ls, lkb, args);
|
|
|
if (error)
|
|
|
- goto out;
|
|
|
+ return error;
|
|
|
|
|
|
- error = find_rsb(ls, name, len, R_CREATE, &r);
|
|
|
+ error = find_rsb(ls, name, len, 0, R_REQUEST, &r);
|
|
|
if (error)
|
|
|
- goto out;
|
|
|
+ return error;
|
|
|
|
|
|
lock_rsb(r);
|
|
|
|
|
@@ -2723,8 +3194,6 @@ static int request_lock(struct dlm_ls *ls, struct dlm_lkb *lkb, char *name,
|
|
|
|
|
|
unlock_rsb(r);
|
|
|
put_rsb(r);
|
|
|
-
|
|
|
- out:
|
|
|
return error;
|
|
|
}
|
|
|
|
|
@@ -3406,8 +3875,11 @@ static int receive_request(struct dlm_ls *ls, struct dlm_message *ms)
|
|
|
{
|
|
|
struct dlm_lkb *lkb;
|
|
|
struct dlm_rsb *r;
|
|
|
+ int from_nodeid;
|
|
|
int error, namelen;
|
|
|
|
|
|
+ from_nodeid = ms->m_header.h_nodeid;
|
|
|
+
|
|
|
error = create_lkb(ls, &lkb);
|
|
|
if (error)
|
|
|
goto fail;
|
|
@@ -3420,9 +3892,16 @@ static int receive_request(struct dlm_ls *ls, struct dlm_message *ms)
|
|
|
goto fail;
|
|
|
}
|
|
|
|
|
|
+ /* The dir node is the authority on whether we are the master
|
|
|
+ for this rsb or not, so if the master sends us a request, we should
|
|
|
+ recreate the rsb if we've destroyed it. This race happens when we
|
|
|
+ send a remove message to the dir node at the same time that the dir
|
|
|
+ node sends us a request for the rsb. */
|
|
|
+
|
|
|
namelen = receive_extralen(ms);
|
|
|
|
|
|
- error = find_rsb(ls, ms->m_extra, namelen, R_MASTER, &r);
|
|
|
+ error = find_rsb(ls, ms->m_extra, namelen, from_nodeid,
|
|
|
+ R_RECEIVE_REQUEST, &r);
|
|
|
if (error) {
|
|
|
__put_lkb(ls, lkb);
|
|
|
goto fail;
|
|
@@ -3430,6 +3909,16 @@ static int receive_request(struct dlm_ls *ls, struct dlm_message *ms)
|
|
|
|
|
|
lock_rsb(r);
|
|
|
|
|
|
+ if (r->res_master_nodeid != dlm_our_nodeid()) {
|
|
|
+ error = validate_master_nodeid(ls, r, from_nodeid);
|
|
|
+ if (error) {
|
|
|
+ unlock_rsb(r);
|
|
|
+ put_rsb(r);
|
|
|
+ __put_lkb(ls, lkb);
|
|
|
+ goto fail;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
attach_lkb(r, lkb);
|
|
|
error = do_request(r, lkb);
|
|
|
send_request_reply(r, lkb, error);
|
|
@@ -3445,6 +3934,23 @@ static int receive_request(struct dlm_ls *ls, struct dlm_message *ms)
|
|
|
return 0;
|
|
|
|
|
|
fail:
|
|
|
+ /* TODO: instead of returning ENOTBLK, add the lkb to res_lookup
|
|
|
+ and do this receive_request again from process_lookup_list once
|
|
|
+ we get the lookup reply. This would avoid a many repeated
|
|
|
+ ENOTBLK request failures when the lookup reply designating us
|
|
|
+ as master is delayed. */
|
|
|
+
|
|
|
+ /* We could repeatedly return -EBADR here if our send_remove() is
|
|
|
+ delayed in being sent/arriving/being processed on the dir node.
|
|
|
+ Another node would repeatedly lookup up the master, and the dir
|
|
|
+ node would continue returning our nodeid until our send_remove
|
|
|
+ took effect. */
|
|
|
+
|
|
|
+ if (error != -ENOTBLK) {
|
|
|
+ log_limit(ls, "receive_request %x from %d %d",
|
|
|
+ ms->m_lkid, from_nodeid, error);
|
|
|
+ }
|
|
|
+
|
|
|
setup_stub_lkb(ls, ms);
|
|
|
send_request_reply(&ls->ls_stub_rsb, &ls->ls_stub_lkb, error);
|
|
|
return error;
|
|
@@ -3651,49 +4157,110 @@ static int receive_bast(struct dlm_ls *ls, struct dlm_message *ms)
|
|
|
|
|
|
static void receive_lookup(struct dlm_ls *ls, struct dlm_message *ms)
|
|
|
{
|
|
|
- int len, error, ret_nodeid, dir_nodeid, from_nodeid, our_nodeid;
|
|
|
+ int len, error, ret_nodeid, from_nodeid, our_nodeid;
|
|
|
|
|
|
from_nodeid = ms->m_header.h_nodeid;
|
|
|
our_nodeid = dlm_our_nodeid();
|
|
|
|
|
|
len = receive_extralen(ms);
|
|
|
|
|
|
- dir_nodeid = dlm_hash2nodeid(ls, ms->m_hash);
|
|
|
- if (dir_nodeid != our_nodeid) {
|
|
|
- log_error(ls, "lookup dir_nodeid %d from %d",
|
|
|
- dir_nodeid, from_nodeid);
|
|
|
- error = -EINVAL;
|
|
|
- ret_nodeid = -1;
|
|
|
- goto out;
|
|
|
- }
|
|
|
-
|
|
|
- error = dlm_dir_lookup(ls, from_nodeid, ms->m_extra, len, &ret_nodeid);
|
|
|
+ error = dlm_master_lookup(ls, from_nodeid, ms->m_extra, len, 0,
|
|
|
+ &ret_nodeid, NULL);
|
|
|
|
|
|
/* Optimization: we're master so treat lookup as a request */
|
|
|
if (!error && ret_nodeid == our_nodeid) {
|
|
|
receive_request(ls, ms);
|
|
|
return;
|
|
|
}
|
|
|
- out:
|
|
|
send_lookup_reply(ls, ms, ret_nodeid, error);
|
|
|
}
|
|
|
|
|
|
static void receive_remove(struct dlm_ls *ls, struct dlm_message *ms)
|
|
|
{
|
|
|
- int len, dir_nodeid, from_nodeid;
|
|
|
+ char name[DLM_RESNAME_MAXLEN+1];
|
|
|
+ struct dlm_rsb *r;
|
|
|
+ uint32_t hash, b;
|
|
|
+ int rv, len, dir_nodeid, from_nodeid;
|
|
|
|
|
|
from_nodeid = ms->m_header.h_nodeid;
|
|
|
|
|
|
len = receive_extralen(ms);
|
|
|
|
|
|
+ if (len > DLM_RESNAME_MAXLEN) {
|
|
|
+ log_error(ls, "receive_remove from %d bad len %d",
|
|
|
+ from_nodeid, len);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
dir_nodeid = dlm_hash2nodeid(ls, ms->m_hash);
|
|
|
if (dir_nodeid != dlm_our_nodeid()) {
|
|
|
- log_error(ls, "remove dir entry dir_nodeid %d from %d",
|
|
|
- dir_nodeid, from_nodeid);
|
|
|
+ log_error(ls, "receive_remove from %d bad nodeid %d",
|
|
|
+ from_nodeid, dir_nodeid);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- dlm_dir_remove_entry(ls, from_nodeid, ms->m_extra, len);
|
|
|
+ /* Look for name on rsbtbl.toss, if it's there, kill it.
|
|
|
+ If it's on rsbtbl.keep, it's being used, and we should ignore this
|
|
|
+ message. This is an expected race between the dir node sending a
|
|
|
+ request to the master node at the same time as the master node sends
|
|
|
+ a remove to the dir node. The resolution to that race is for the
|
|
|
+ dir node to ignore the remove message, and the master node to
|
|
|
+ recreate the master rsb when it gets a request from the dir node for
|
|
|
+ an rsb it doesn't have. */
|
|
|
+
|
|
|
+ memset(name, 0, sizeof(name));
|
|
|
+ memcpy(name, ms->m_extra, len);
|
|
|
+
|
|
|
+ hash = jhash(name, len, 0);
|
|
|
+ b = hash & (ls->ls_rsbtbl_size - 1);
|
|
|
+
|
|
|
+ spin_lock(&ls->ls_rsbtbl[b].lock);
|
|
|
+
|
|
|
+ rv = dlm_search_rsb_tree(&ls->ls_rsbtbl[b].toss, name, len, &r);
|
|
|
+ if (rv) {
|
|
|
+ /* verify the rsb is on keep list per comment above */
|
|
|
+ rv = dlm_search_rsb_tree(&ls->ls_rsbtbl[b].keep, name, len, &r);
|
|
|
+ if (rv) {
|
|
|
+ /* should not happen */
|
|
|
+ log_error(ls, "receive_remove from %d not found %s",
|
|
|
+ from_nodeid, name);
|
|
|
+ spin_unlock(&ls->ls_rsbtbl[b].lock);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (r->res_master_nodeid != from_nodeid) {
|
|
|
+ /* should not happen */
|
|
|
+ log_error(ls, "receive_remove keep from %d master %d",
|
|
|
+ from_nodeid, r->res_master_nodeid);
|
|
|
+ dlm_print_rsb(r);
|
|
|
+ spin_unlock(&ls->ls_rsbtbl[b].lock);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ log_debug(ls, "receive_remove from %d master %d first %x %s",
|
|
|
+ from_nodeid, r->res_master_nodeid, r->res_first_lkid,
|
|
|
+ name);
|
|
|
+ spin_unlock(&ls->ls_rsbtbl[b].lock);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (r->res_master_nodeid != from_nodeid) {
|
|
|
+ log_error(ls, "receive_remove toss from %d master %d",
|
|
|
+ from_nodeid, r->res_master_nodeid);
|
|
|
+ dlm_print_rsb(r);
|
|
|
+ spin_unlock(&ls->ls_rsbtbl[b].lock);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (kref_put(&r->res_ref, kill_rsb)) {
|
|
|
+ rb_erase(&r->res_hashnode, &ls->ls_rsbtbl[b].toss);
|
|
|
+ spin_unlock(&ls->ls_rsbtbl[b].lock);
|
|
|
+ dlm_free_rsb(r);
|
|
|
+ } else {
|
|
|
+ log_error(ls, "receive_remove from %d rsb ref error",
|
|
|
+ from_nodeid);
|
|
|
+ dlm_print_rsb(r);
|
|
|
+ spin_unlock(&ls->ls_rsbtbl[b].lock);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
static void receive_purge(struct dlm_ls *ls, struct dlm_message *ms)
|
|
@@ -3706,6 +4273,7 @@ static int receive_request_reply(struct dlm_ls *ls, struct dlm_message *ms)
|
|
|
struct dlm_lkb *lkb;
|
|
|
struct dlm_rsb *r;
|
|
|
int error, mstype, result;
|
|
|
+ int from_nodeid = ms->m_header.h_nodeid;
|
|
|
|
|
|
error = find_lkb(ls, ms->m_remid, &lkb);
|
|
|
if (error)
|
|
@@ -3723,8 +4291,7 @@ static int receive_request_reply(struct dlm_ls *ls, struct dlm_message *ms)
|
|
|
error = remove_from_waiters(lkb, DLM_MSG_REQUEST_REPLY);
|
|
|
if (error) {
|
|
|
log_error(ls, "receive_request_reply %x remote %d %x result %d",
|
|
|
- lkb->lkb_id, ms->m_header.h_nodeid, ms->m_lkid,
|
|
|
- ms->m_result);
|
|
|
+ lkb->lkb_id, from_nodeid, ms->m_lkid, ms->m_result);
|
|
|
dlm_dump_rsb(r);
|
|
|
goto out;
|
|
|
}
|
|
@@ -3732,8 +4299,9 @@ static int receive_request_reply(struct dlm_ls *ls, struct dlm_message *ms)
|
|
|
/* Optimization: the dir node was also the master, so it took our
|
|
|
lookup as a request and sent request reply instead of lookup reply */
|
|
|
if (mstype == DLM_MSG_LOOKUP) {
|
|
|
- r->res_nodeid = ms->m_header.h_nodeid;
|
|
|
- lkb->lkb_nodeid = r->res_nodeid;
|
|
|
+ r->res_master_nodeid = from_nodeid;
|
|
|
+ r->res_nodeid = from_nodeid;
|
|
|
+ lkb->lkb_nodeid = from_nodeid;
|
|
|
}
|
|
|
|
|
|
/* this is the value returned from do_request() on the master */
|
|
@@ -3767,18 +4335,30 @@ static int receive_request_reply(struct dlm_ls *ls, struct dlm_message *ms)
|
|
|
case -EBADR:
|
|
|
case -ENOTBLK:
|
|
|
/* find_rsb failed to find rsb or rsb wasn't master */
|
|
|
- log_debug(ls, "receive_request_reply %x %x master diff %d %d",
|
|
|
- lkb->lkb_id, lkb->lkb_flags, r->res_nodeid, result);
|
|
|
- r->res_nodeid = -1;
|
|
|
- lkb->lkb_nodeid = -1;
|
|
|
+ log_limit(ls, "receive_request_reply %x from %d %d "
|
|
|
+ "master %d dir %d first %x %s", lkb->lkb_id,
|
|
|
+ from_nodeid, result, r->res_master_nodeid,
|
|
|
+ r->res_dir_nodeid, r->res_first_lkid, r->res_name);
|
|
|
+
|
|
|
+ if (r->res_dir_nodeid != dlm_our_nodeid() &&
|
|
|
+ r->res_master_nodeid != dlm_our_nodeid()) {
|
|
|
+ /* cause _request_lock->set_master->send_lookup */
|
|
|
+ r->res_master_nodeid = 0;
|
|
|
+ r->res_nodeid = -1;
|
|
|
+ lkb->lkb_nodeid = -1;
|
|
|
+ }
|
|
|
|
|
|
if (is_overlap(lkb)) {
|
|
|
/* we'll ignore error in cancel/unlock reply */
|
|
|
queue_cast_overlap(r, lkb);
|
|
|
confirm_master(r, result);
|
|
|
unhold_lkb(lkb); /* undoes create_lkb() */
|
|
|
- } else
|
|
|
+ } else {
|
|
|
_request_lock(r, lkb);
|
|
|
+
|
|
|
+ if (r->res_master_nodeid == dlm_our_nodeid())
|
|
|
+ confirm_master(r, 0);
|
|
|
+ }
|
|
|
break;
|
|
|
|
|
|
default:
|
|
@@ -3994,6 +4574,7 @@ static void receive_lookup_reply(struct dlm_ls *ls, struct dlm_message *ms)
|
|
|
struct dlm_lkb *lkb;
|
|
|
struct dlm_rsb *r;
|
|
|
int error, ret_nodeid;
|
|
|
+ int do_lookup_list = 0;
|
|
|
|
|
|
error = find_lkb(ls, ms->m_lkid, &lkb);
|
|
|
if (error) {
|
|
@@ -4001,7 +4582,7 @@ static void receive_lookup_reply(struct dlm_ls *ls, struct dlm_message *ms)
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- /* ms->m_result is the value returned by dlm_dir_lookup on dir node
|
|
|
+ /* ms->m_result is the value returned by dlm_master_lookup on dir node
|
|
|
FIXME: will a non-zero error ever be returned? */
|
|
|
|
|
|
r = lkb->lkb_resource;
|
|
@@ -4013,12 +4594,37 @@ static void receive_lookup_reply(struct dlm_ls *ls, struct dlm_message *ms)
|
|
|
goto out;
|
|
|
|
|
|
ret_nodeid = ms->m_nodeid;
|
|
|
+
|
|
|
+ /* We sometimes receive a request from the dir node for this
|
|
|
+ rsb before we've received the dir node's loookup_reply for it.
|
|
|
+ The request from the dir node implies we're the master, so we set
|
|
|
+ ourself as master in receive_request_reply, and verify here that
|
|
|
+ we are indeed the master. */
|
|
|
+
|
|
|
+ if (r->res_master_nodeid && (r->res_master_nodeid != ret_nodeid)) {
|
|
|
+ /* This should never happen */
|
|
|
+ log_error(ls, "receive_lookup_reply %x from %d ret %d "
|
|
|
+ "master %d dir %d our %d first %x %s",
|
|
|
+ lkb->lkb_id, ms->m_header.h_nodeid, ret_nodeid,
|
|
|
+ r->res_master_nodeid, r->res_dir_nodeid,
|
|
|
+ dlm_our_nodeid(), r->res_first_lkid, r->res_name);
|
|
|
+ }
|
|
|
+
|
|
|
if (ret_nodeid == dlm_our_nodeid()) {
|
|
|
+ r->res_master_nodeid = ret_nodeid;
|
|
|
r->res_nodeid = 0;
|
|
|
- ret_nodeid = 0;
|
|
|
+ do_lookup_list = 1;
|
|
|
r->res_first_lkid = 0;
|
|
|
+ } else if (ret_nodeid == -1) {
|
|
|
+ /* the remote node doesn't believe it's the dir node */
|
|
|
+ log_error(ls, "receive_lookup_reply %x from %d bad ret_nodeid",
|
|
|
+ lkb->lkb_id, ms->m_header.h_nodeid);
|
|
|
+ r->res_master_nodeid = 0;
|
|
|
+ r->res_nodeid = -1;
|
|
|
+ lkb->lkb_nodeid = -1;
|
|
|
} else {
|
|
|
- /* set_master() will copy res_nodeid to lkb_nodeid */
|
|
|
+ /* set_master() will set lkb_nodeid from r */
|
|
|
+ r->res_master_nodeid = ret_nodeid;
|
|
|
r->res_nodeid = ret_nodeid;
|
|
|
}
|
|
|
|
|
@@ -4033,7 +4639,7 @@ static void receive_lookup_reply(struct dlm_ls *ls, struct dlm_message *ms)
|
|
|
_request_lock(r, lkb);
|
|
|
|
|
|
out_list:
|
|
|
- if (!ret_nodeid)
|
|
|
+ if (do_lookup_list)
|
|
|
process_lookup_list(r);
|
|
|
out:
|
|
|
unlock_rsb(r);
|
|
@@ -4047,7 +4653,7 @@ static void _receive_message(struct dlm_ls *ls, struct dlm_message *ms,
|
|
|
int error = 0, noent = 0;
|
|
|
|
|
|
if (!dlm_is_member(ls, ms->m_header.h_nodeid)) {
|
|
|
- log_debug(ls, "ignore non-member message %d from %d %x %x %d",
|
|
|
+ log_limit(ls, "receive %d from non-member %d %x %x %d",
|
|
|
ms->m_type, ms->m_header.h_nodeid, ms->m_lkid,
|
|
|
ms->m_remid, ms->m_result);
|
|
|
return;
|
|
@@ -4174,6 +4780,15 @@ static void dlm_receive_message(struct dlm_ls *ls, struct dlm_message *ms,
|
|
|
int nodeid)
|
|
|
{
|
|
|
if (dlm_locking_stopped(ls)) {
|
|
|
+ /* If we were a member of this lockspace, left, and rejoined,
|
|
|
+ other nodes may still be sending us messages from the
|
|
|
+ lockspace generation before we left. */
|
|
|
+ if (!ls->ls_generation) {
|
|
|
+ log_limit(ls, "receive %d from %d ignore old gen",
|
|
|
+ ms->m_type, nodeid);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
dlm_add_requestqueue(ls, nodeid, ms);
|
|
|
} else {
|
|
|
dlm_wait_requestqueue(ls);
|
|
@@ -4798,6 +5413,7 @@ int dlm_recover_master_copy(struct dlm_ls *ls, struct dlm_rcom *rc)
|
|
|
struct dlm_rsb *r;
|
|
|
struct dlm_lkb *lkb;
|
|
|
uint32_t remid = 0;
|
|
|
+ int from_nodeid = rc->rc_header.h_nodeid;
|
|
|
int error;
|
|
|
|
|
|
if (rl->rl_parent_lkid) {
|
|
@@ -4815,21 +5431,21 @@ int dlm_recover_master_copy(struct dlm_ls *ls, struct dlm_rcom *rc)
|
|
|
we make ourselves master, dlm_recover_masters() won't touch the
|
|
|
MSTCPY locks we've received early. */
|
|
|
|
|
|
- error = find_rsb(ls, rl->rl_name, le16_to_cpu(rl->rl_namelen), 0, &r);
|
|
|
+ error = find_rsb(ls, rl->rl_name, le16_to_cpu(rl->rl_namelen),
|
|
|
+ from_nodeid, R_RECEIVE_RECOVER, &r);
|
|
|
if (error)
|
|
|
goto out;
|
|
|
|
|
|
+ lock_rsb(r);
|
|
|
+
|
|
|
if (dlm_no_directory(ls) && (dlm_dir_nodeid(r) != dlm_our_nodeid())) {
|
|
|
log_error(ls, "dlm_recover_master_copy remote %d %x not dir",
|
|
|
- rc->rc_header.h_nodeid, remid);
|
|
|
+ from_nodeid, remid);
|
|
|
error = -EBADR;
|
|
|
- put_rsb(r);
|
|
|
- goto out;
|
|
|
+ goto out_unlock;
|
|
|
}
|
|
|
|
|
|
- lock_rsb(r);
|
|
|
-
|
|
|
- lkb = search_remid(r, rc->rc_header.h_nodeid, remid);
|
|
|
+ lkb = search_remid(r, from_nodeid, remid);
|
|
|
if (lkb) {
|
|
|
error = -EEXIST;
|
|
|
goto out_remid;
|
|
@@ -4866,7 +5482,7 @@ int dlm_recover_master_copy(struct dlm_ls *ls, struct dlm_rcom *rc)
|
|
|
out:
|
|
|
if (error && error != -EEXIST)
|
|
|
log_debug(ls, "dlm_recover_master_copy remote %d %x error %d",
|
|
|
- rc->rc_header.h_nodeid, remid, error);
|
|
|
+ from_nodeid, remid, error);
|
|
|
rl->rl_result = cpu_to_le32(error);
|
|
|
return error;
|
|
|
}
|