Whamcloud - gitweb
LU-5092 nodemap: convert member hash to a list 85/14885/12
authorKit Westneat <kit.westneat@gmail.com>
Wed, 1 Jul 2015 13:53:46 +0000 (09:53 -0400)
committerOleg Drokin <oleg.drokin@intel.com>
Fri, 10 Jul 2015 03:25:29 +0000 (03:25 +0000)
The members of a nodemap were stored in a hash, but they were only
iterated against, not accessed by key. This patch converts the hash
to a list in order to simplify things.

Signed-off-by: Kit Westneat <kit.westneat@gmail.com>
Change-Id: Ic1df4035d1c53a7c7966c4c528d20cefc7258d0e
Reviewed-on: http://review.whamcloud.com/14885
Tested-by: Jenkins
Reviewed-by: James Simmons <uja.ornl@yahoo.com>
Tested-by: Maloo <hpdd-maloo@intel.com>
Reviewed-by: John L. Hammond <john.hammond@intel.com>
Reviewed-by: Oleg Drokin <oleg.drokin@intel.com>
lustre/include/lustre_export.h
lustre/include/lustre_nodemap.h
lustre/mgs/mgs_handler.c
lustre/ptlrpc/nodemap_handler.c
lustre/ptlrpc/nodemap_internal.h
lustre/ptlrpc/nodemap_lproc.c
lustre/ptlrpc/nodemap_member.c

index d6e5c11..2431972 100644 (file)
@@ -71,7 +71,7 @@ struct tg_export_data {
 
        /** nodemap this export is a member of */
        struct lu_nodemap       *ted_nodemap;
-       struct hlist_node       ted_nodemap_member;
+       struct list_head        ted_nodemap_member;
 
        /* Every reply data fields below are
         * protected by ted_lcd_lock */
index 90555e3..221f8d7 100644 (file)
@@ -54,48 +54,48 @@ enum nodemap_tree_type {
 
 struct lu_nodemap {
        /* human readable ID */
-       char                    nm_name[LUSTRE_NODEMAP_NAME_LENGTH + 1];
+       char                     nm_name[LUSTRE_NODEMAP_NAME_LENGTH + 1];
        /* flags to govern nodemap behavior */
-       bool                    nmf_trust_client_ids:1,
-                               nmf_allow_root_access:1,
-                               nmf_block_lookups:1,
-                               nmf_hmac_required:1,
-                               nmf_encryption_required:1;
+       bool                     nmf_trust_client_ids:1,
+                                nmf_allow_root_access:1,
+                                nmf_block_lookups:1,
+                                nmf_hmac_required:1,
+                                nmf_encryption_required:1;
        /* unique ID set by MGS */
-       int                     nm_id;
+       unsigned int             nm_id;
        /* nodemap ref counter */
-       atomic_t                nm_refcount;
+       atomic_t                 nm_refcount;
        /* UID to squash unmapped UIDs */
-       uid_t                   nm_squash_uid;
+       uid_t                    nm_squash_uid;
        /* GID to squash unmapped GIDs */
-       gid_t                   nm_squash_gid;
+       gid_t                    nm_squash_gid;
        /* NID range list */
-       struct list_head        nm_ranges;
+       struct list_head         nm_ranges;
        /* lock for idmap red/black trees */
-       rwlock_t                nm_idmap_lock;
+       rwlock_t                 nm_idmap_lock;
        /* UID map keyed by local UID */
-       struct rb_root          nm_fs_to_client_uidmap;
+       struct rb_root           nm_fs_to_client_uidmap;
        /* UID map keyed by remote UID */
-       struct rb_root          nm_client_to_fs_uidmap;
+       struct rb_root           nm_client_to_fs_uidmap;
        /* GID map keyed by local UID */
-       struct rb_root          nm_fs_to_client_gidmap;
+       struct rb_root           nm_fs_to_client_gidmap;
        /* GID map keyed by remote UID */
-       struct rb_root          nm_client_to_fs_gidmap;
+       struct rb_root           nm_client_to_fs_gidmap;
        /* proc directory entry */
        struct proc_dir_entry   *nm_proc_entry;
        /* attached client members of this nodemap */
-       struct cfs_hash         *nm_member_hash;
+       struct mutex             nm_member_list_lock;
+       struct list_head         nm_member_list;
        /* access by nodemap name */
-       struct hlist_node       nm_hash;
+       struct hlist_node        nm_hash;
 
        /* used when unloading nodemaps */
-       struct list_head        nm_list;
+       struct list_head         nm_list;
 };
 
 void nodemap_activate(const bool value);
 int nodemap_add(const char *nodemap_name);
 int nodemap_del(const char *nodemap_name);
-struct lu_nodemap *nodemap_classify_nid(lnet_nid_t nid);
 int nodemap_add_member(lnet_nid_t nid, struct obd_export *exp);
 void nodemap_del_member(struct obd_export *exp);
 int nodemap_parse_range(const char *range_string, lnet_nid_t range[2]);
@@ -116,4 +116,7 @@ __u32 nodemap_map_id(struct lu_nodemap *nodemap,
                     enum nodemap_tree_type tree_type, __u32 id);
 ssize_t nodemap_map_acl(struct lu_nodemap *nodemap, void *buf, size_t size,
                        enum nodemap_tree_type tree_type);
+void nodemap_test_nid(lnet_nid_t nid, char *name_buf, size_t name_len);
+__u32 nodemap_test_id(lnet_nid_t nid, enum nodemap_id_type idtype,
+                     __u32 client_id);
 #endif /* _LUSTRE_NODEMAP_H */
index 2fc88bf..d3a0038 100644 (file)
@@ -631,7 +631,6 @@ static int mgs_iocontrol_nodemap(const struct lu_env *env,
                                 struct obd_ioctl_data *data)
 {
        struct lustre_cfg       *lcfg = NULL;
-       struct lu_nodemap       *nodemap;
        lnet_nid_t              nid;
        const char              *nodemap_name = NULL;
        const char              *nidstr = NULL;
@@ -639,8 +638,9 @@ static int mgs_iocontrol_nodemap(const struct lu_env *env,
        const char              *idtype_str = NULL;
        char                    *param = NULL;
        char                    fs_idstr[16];
+       char                    name_buf[LUSTRE_NODEMAP_NAME_LENGTH + 1];
        int                     rc = 0;
-       __u32                   client_id;
+       unsigned long           client_id;
        __u32                   fs_id;
        __u32                   cmd;
        int                     idtype;
@@ -687,10 +687,10 @@ static int mgs_iocontrol_nodemap(const struct lu_env *env,
                        GOTO(out_lcfg, rc = -EINVAL);
                nidstr = lustre_cfg_string(lcfg, 1);
                nid = libcfs_str2nid(nidstr);
-               nodemap = nodemap_classify_nid(nid);
-               memset(data->ioc_pbuf1, 0, data->ioc_plen1);
-               if (copy_to_user(data->ioc_pbuf1, nodemap->nm_name,
-                                strlen(nodemap->nm_name)) != 0)
+               nodemap_test_nid(nid, name_buf, sizeof(name_buf));
+               rc = copy_to_user(data->ioc_pbuf1, name_buf,
+                                 MIN(data->ioc_plen1, sizeof(name_buf)));
+               if (rc != 0)
                        GOTO(out_lcfg, rc = -EFAULT);
                break;
        case LCFG_NODEMAP_TEST_ID:
@@ -701,16 +701,16 @@ static int mgs_iocontrol_nodemap(const struct lu_env *env,
                client_idstr = lustre_cfg_string(lcfg, 3);
 
                nid = libcfs_str2nid(nidstr);
-               nodemap = nodemap_classify_nid(nid);
-               client_id = simple_strtoul(client_idstr, NULL, 10);
-
                if (strcmp(idtype_str, "uid") == 0)
                        idtype = NODEMAP_UID;
                else
                        idtype = NODEMAP_GID;
 
-               fs_id = nodemap_map_id(nodemap, idtype, NODEMAP_CLIENT_TO_FS,
-                                      client_id);
+               rc = kstrtoul(client_idstr, 10, &client_id);
+               if (rc != 0)
+                       GOTO(out_lcfg, rc = -EINVAL);
+
+               fs_id = nodemap_test_id(nid, idtype, client_id);
 
                if (data->ioc_plen1 < sizeof(fs_idstr))
                        GOTO(out_lcfg, rc = -EINVAL);
index 7445c94..984e45d 100644 (file)
@@ -89,10 +89,10 @@ static void nodemap_destroy(struct lu_nodemap *nodemap)
        idmap_delete_tree(nodemap);
        write_unlock(&nodemap->nm_idmap_lock);
        nm_member_reclassify_nodemap(nodemap);
-       if (!cfs_hash_is_empty(nodemap->nm_member_hash))
+       if (!list_empty(&nodemap->nm_member_list))
                CWARN("nodemap_destroy failed to reclassify all members\n");
 
-       nm_member_delete_hash(nodemap);
+       nm_member_delete_list(nodemap);
 
        OBD_FREE_PTR(nodemap);
 }
@@ -287,7 +287,8 @@ out:
 }
 
 /**
- * classify the nid into the proper nodemap
+ * Classify the nid into the proper nodemap. Caller must hold
+ * nm_range_tree_lock, and call nodemap_putref when done with nodemap.
  *
  * \param      nid                     nid to classify
  * \retval     nodemap                 nodemap containing the nid
@@ -296,14 +297,17 @@ out:
 struct lu_nodemap *nodemap_classify_nid(lnet_nid_t nid)
 {
        struct lu_nid_range     *range;
+       struct lu_nodemap       *nodemap;
 
        range = range_search(nid);
        if (range != NULL)
-               return range->rn_nodemap;
+               nodemap = range->rn_nodemap;
+       else
+               nodemap = default_nodemap;
+       nodemap_getref(nodemap);
 
-       return default_nodemap;
+       return nodemap;
 }
-EXPORT_SYMBOL(nodemap_classify_nid);
 
 /**
  * simple check for default nodemap
@@ -402,6 +406,7 @@ int nodemap_add_member(lnet_nid_t nid, struct obd_export *exp)
        nodemap = nodemap_classify_nid(nid);
        rc = nm_member_add(nodemap, exp);
        read_unlock(&nm_range_tree_lock);
+       nodemap_putref(nodemap);
        return rc;
 }
 EXPORT_SYMBOL(nodemap_add_member);
@@ -769,16 +774,11 @@ static int nodemap_create(const char *name, bool is_default)
                GOTO(out, rc = -EEXIST);
        }
 
-
-       rc = nm_member_init_hash(nodemap);
-       if (rc != 0) {
-               OBD_FREE_PTR(nodemap);
-               goto out;
-       }
-
        INIT_LIST_HEAD(&nodemap->nm_ranges);
        INIT_LIST_HEAD(&nodemap->nm_list);
+       INIT_LIST_HEAD(&nodemap->nm_member_list);
 
+       mutex_init(&nodemap->nm_member_list_lock);
        rwlock_init(&nodemap->nm_idmap_lock);
        nodemap->nm_fs_to_client_uidmap = RB_ROOT;
        nodemap->nm_client_to_fs_uidmap = RB_ROOT;
@@ -1052,3 +1052,55 @@ void nm_member_revoke_all()
        cfs_hash_for_each_safe(nodemap_hash, nm_member_revoke_all_cb, NULL);
 }
 
+/**
+ * Returns the nodemap classification for a given nid into an ioctl buffer.
+ * Useful for testing the nodemap configuration to make sure it is working as
+ * expected.
+ *
+ * \param      nid             nid to classify
+ * \param[out] name_buf        buffer to write the nodemap name to
+ * \param      name_len        length of buffer
+ */
+void nodemap_test_nid(lnet_nid_t nid, char *name_buf, size_t name_len)
+{
+       struct lu_nodemap       *nodemap;
+
+       read_lock(&nm_range_tree_lock);
+       nodemap = nodemap_classify_nid(nid);
+       read_unlock(&nm_range_tree_lock);
+
+       strncpy(name_buf, nodemap->nm_name, name_len);
+       if (name_len > 0)
+               name_buf[name_len - 1] = '\0';
+
+       nodemap_putref(nodemap);
+}
+EXPORT_SYMBOL(nodemap_test_nid);
+
+/**
+ * Returns the id mapping for a given nid/id pair. Useful for testing the
+ * nodemap configuration to make sure it is working as expected.
+ *
+ * \param      nid             nid to classify
+ * \param      idtype          uid or gid
+ * \param      client_id       id to map to fs
+ *
+ * \retval     the mapped fs_id of the given client_id
+ */
+__u32 nodemap_test_id(lnet_nid_t nid, enum nodemap_id_type idtype,
+                     __u32 client_id)
+{
+       struct lu_nodemap       *nodemap;
+       __u32                    fs_id;
+
+       read_lock(&nm_range_tree_lock);
+       nodemap = nodemap_classify_nid(nid);
+       read_unlock(&nm_range_tree_lock);
+
+       fs_id = nodemap_map_id(nodemap, idtype, NODEMAP_CLIENT_TO_FS,
+                              client_id);
+       nodemap_putref(nodemap);
+
+       return fs_id;
+}
+EXPORT_SYMBOL(nodemap_test_id);
index 01a812e..34c4c64 100644 (file)
@@ -95,16 +95,17 @@ struct lu_idmap *idmap_search(struct lu_nodemap *nodemap,
                              enum nodemap_id_type id_type,
                              __u32 id);
 int nodemap_cleanup_nodemaps(void);
-int nm_member_init_hash(struct lu_nodemap *nodemap);
 int nm_member_add(struct lu_nodemap *nodemap, struct obd_export *exp);
 void nm_member_del(struct lu_nodemap *nodemap, struct obd_export *exp);
-void nm_member_delete_hash(struct lu_nodemap *nodemap);
+void nm_member_delete_list(struct lu_nodemap *nodemap);
+struct lu_nodemap *nodemap_classify_nid(lnet_nid_t nid);
 void nm_member_reclassify_nodemap(struct lu_nodemap *nodemap);
 void nm_member_revoke_locks(struct lu_nodemap *nodemap);
 void nm_member_revoke_all(void);
 
 struct rb_node *nm_rb_next_postorder(const struct rb_node *node);
 struct rb_node *nm_rb_first_postorder(const struct rb_root *root);
+void nodemap_putref(struct lu_nodemap *nodemap);
 
 #define nm_rbtree_postorder_for_each_entry_safe(pos, n,                        \
                                                root, field)            \
index 458f375..123e7a7 100644 (file)
@@ -151,48 +151,31 @@ static int nodemap_ranges_open(struct inode *inode, struct file *file)
 }
 
 /**
- * Hash callback, reads and prints the exports attached to this nodemap.
+ * Reads and prints the exports attached to the given nodemap.
  *
- * \param      hs              nodemap member hash
- * \param      bd              unused
- * \param      hnode           current member in hash
- * \param      data            seq_file to print to
- * \retval     0               success
- */
-static int nodemap_exports_show_cb(struct cfs_hash *hs, struct cfs_hash_bd *bd,
-                                  struct hlist_node *hnode, void *data)
-{
-       struct seq_file         *m = data;
-       struct obd_export       *exp;
-       char                    nidstr[LNET_NIDSTR_SIZE] = "<unknown>";
-
-       exp = hlist_entry(hnode, struct obd_export,
-                         exp_target_data.ted_nodemap_member);
-       if (exp->exp_connection != NULL)
-               libcfs_nid2str_r(exp->exp_connection->c_peer.nid,
-                                nidstr, sizeof(nidstr));
-
-       seq_printf(m, " { nid: %s, uuid: %s },",
-                  nidstr, exp->exp_client_uuid.uuid);
-
-       return 0;
-}
-
-/**
- * Reads and prints the exports attached to the given nodemap via hash
- * foreach callback.
- *
- * \param      m               seq file in proc fs
+ * \param      m               seq file in proc fs, stores nodemap
  * \param      data            unused
  * \retval     0               success
  */
 static int nodemap_exports_show(struct seq_file *m, void *data)
 {
-       struct lu_nodemap       *nodemap = m->private;
+       struct lu_nodemap *nodemap = m->private;
+       struct obd_export *exp;
+       char nidstr[LNET_NIDSTR_SIZE] = "<unknown>";
 
        seq_printf(m, "[\n");
 
-       cfs_hash_for_each(nodemap->nm_member_hash, nodemap_exports_show_cb, m);
+       mutex_lock(&nodemap->nm_member_list_lock);
+       list_for_each_entry(exp, &nodemap->nm_member_list,
+                           exp_target_data.ted_nodemap_member) {
+               if (exp->exp_connection != NULL)
+                       libcfs_nid2str_r(exp->exp_connection->c_peer.nid,
+                                        nidstr, sizeof(nidstr));
+
+               seq_printf(m, " { nid: %s, uuid: %s },",
+                          nidstr, exp->exp_client_uuid.uuid);
+       }
+       mutex_unlock(&nodemap->nm_member_list_lock);
 
        seq_printf(m, "\n");
        seq_printf(m, "]\n");
index 1216fe7..81a0980 100644 (file)
 #define HASH_NODEMAP_MEMBER_MAX_BITS 7
 
 /**
- * member hash functions
+ * Delete a member from a member list
  *
- * The purpose of this hash is to maintain the list of
- * exports that are connected and associated with a
- * particular nodemap
- */
-static void nm_member_getref(struct obd_export *exp)
-{
-}
-
-void nm_member_putref(struct obd_export *exp)
-{
-}
-
-static __u32 nm_member_hashfn(struct cfs_hash *hash_body,
-                          const void *key, unsigned mask)
-{
-       return hash_long((unsigned long)key, hash_body->hs_bkt_bits) & mask;
-}
-
-static void *nm_member_hs_key(struct hlist_node *hnode)
-{
-       struct obd_export       *exp;
-
-       exp = hlist_entry(hnode, struct obd_export,
-                         exp_target_data.ted_nodemap_member);
-
-       return exp;
-}
-
-static int nm_member_hs_keycmp(const void *key, struct hlist_node *hnode)
-{
-       struct obd_export       *exp;
-
-       exp = hlist_entry(hnode, struct obd_export,
-                         exp_target_data.ted_nodemap_member);
-
-       return key == exp;
-}
-
-static void *nm_member_hs_hashobject(struct hlist_node *hnode)
-{
-       return hlist_entry(hnode, struct obd_export,
-                          exp_target_data.ted_nodemap_member);
-}
-
-static void nm_member_hs_get(struct cfs_hash *hs, struct hlist_node *hnode)
-{
-       struct obd_export       *exp;
-
-       exp = hlist_entry(hnode, struct obd_export,
-                         exp_target_data.ted_nodemap_member);
-       nm_member_getref(exp);
-}
-
-static void nm_member_hs_put_locked(struct cfs_hash *hs,
-                                struct hlist_node *hnode)
-{
-       struct obd_export       *exp;
-
-       exp = hlist_entry(hnode, struct obd_export,
-                         exp_target_data.ted_nodemap_member);
-       nm_member_putref(exp);
-}
-
-/**
- * Delete a member from a member hash
- *
- * \param      nodemap         nodemap containing hash
- * \paraa      nid             nid of member to delete
+ * \param      nodemap         nodemap containing list
+ * \param      exp             export member to delete
  */
 void nm_member_del(struct lu_nodemap *nodemap, struct obd_export *exp)
 {
-       struct obd_export *exp1;
+       mutex_lock(&nodemap->nm_member_list_lock);
+       list_del_init(&exp->exp_target_data.ted_nodemap_member);
+       mutex_unlock(&nodemap->nm_member_list_lock);
 
-       exp1 = cfs_hash_del_key(nodemap->nm_member_hash, exp);
-       if (exp1 != NULL)
-               class_export_put(exp1);
-
-       LASSERT(hlist_unhashed(&exp->exp_target_data.ted_nodemap_member));
        exp->exp_target_data.ted_nodemap = NULL;
-}
-
-static struct cfs_hash_ops nm_member_hash_operations = {
-       .hs_hash        = nm_member_hashfn,
-       .hs_key         = nm_member_hs_key,
-       .hs_keycmp      = nm_member_hs_keycmp,
-       .hs_object      = nm_member_hs_hashobject,
-       .hs_get         = nm_member_hs_get,
-       .hs_put_locked  = nm_member_hs_put_locked,
-};
-
-/**
- * Init a member hash of a nodemap
- *
- * \param      nodemap         nodemap containing the member hash
- */
-int nm_member_init_hash(struct lu_nodemap *nodemap)
-{
-       char nodemap_hashname[LUSTRE_NODEMAP_NAME_LENGTH + 3];
-
-
-       snprintf(nodemap_hashname, sizeof(nodemap_hashname),
-                "nm-%s", nodemap->nm_name);
-       nodemap->nm_member_hash = cfs_hash_create(nodemap_hashname,
-                                         HASH_NODEMAP_MEMBER_CUR_BITS,
-                                         HASH_NODEMAP_MEMBER_MAX_BITS,
-                                         HASH_NODEMAP_MEMBER_BKT_BITS, 0,
-                                         CFS_HASH_MIN_THETA,
-                                         CFS_HASH_MAX_THETA,
-                                         &nm_member_hash_operations,
-                                         CFS_HASH_DEFAULT);
-       if (nodemap->nm_member_hash == NULL)
-               return -ENOMEM;
-
-       return 0;
-}
-
-/**
- * Callback from deleting a hash member
- */
-static int nm_member_delete_hash_cb(struct cfs_hash *hs, struct cfs_hash_bd *bd,
-                                struct hlist_node *hnode, void *data)
-{
-       struct obd_export       *exp;
-
-       exp = hlist_entry(hnode, struct obd_export,
-                         exp_target_data.ted_nodemap_member);
-
-       exp->exp_target_data.ted_nodemap = NULL;
-       cfs_hash_bd_del_locked(hs, bd, hnode);
        class_export_put(exp);
-
-       return 0;
 }
 
 /**
- * Delete a member hash from a nodemap
+ * Delete a member list from a nodemap
  *
- * \param      nodemap         nodemap to remove the hash from
+ * \param      nodemap         nodemap to remove the list from
  */
-void nm_member_delete_hash(struct lu_nodemap *nodemap)
+void nm_member_delete_list(struct lu_nodemap *nodemap)
 {
-       cfs_hash_for_each_safe(nodemap->nm_member_hash,
-                              nm_member_delete_hash_cb,
-                              nodemap);
-       cfs_hash_putref(nodemap->nm_member_hash);
+       struct obd_export *exp;
+       struct obd_export *tmp;
+
+       mutex_lock(&nodemap->nm_member_list_lock);
+       list_for_each_entry_safe(exp, tmp, &nodemap->nm_member_list,
+                                exp_target_data.ted_nodemap_member) {
+               exp->exp_target_data.ted_nodemap = NULL;
+               list_del_init(&exp->exp_target_data.ted_nodemap_member);
+               class_export_put(exp);
+       }
+       mutex_unlock(&nodemap->nm_member_list_lock);
 }
 
 /**
  * Add a member export to a nodemap
  *
- * \param      nodemap         nodemap to search
- * \param      exp             obd_export to search
- * \retval     -EEXIST         export is already hashed to a different nodemap
+ * \param      nodemap         nodemap to add to
+ * \param      exp             obd_export to add
+ * \retval     -EEXIST         export is already part of a different nodemap
  * \retval     -EINVAL         export is NULL
  */
 int nm_member_add(struct lu_nodemap *nodemap, struct obd_export *exp)
 {
-       int     rc = 0;
-
        if (exp == NULL) {
                CWARN("attempted to add null export to nodemap %s\n",
                      nodemap->nm_name);
                return -EINVAL;
        }
 
-       if (hlist_unhashed(&exp->exp_target_data.ted_nodemap_member) == 0) {
+       if (exp->exp_target_data.ted_nodemap != NULL &&
+           !list_empty(&exp->exp_target_data.ted_nodemap_member)) {
                /* export is already member of nodemap */
                if (exp->exp_target_data.ted_nodemap == nodemap)
                        return 0;
@@ -215,16 +100,14 @@ int nm_member_add(struct lu_nodemap *nodemap, struct obd_export *exp)
                return -EEXIST;
        }
 
+       class_export_get(exp);
        exp->exp_target_data.ted_nodemap = nodemap;
+       mutex_lock(&nodemap->nm_member_list_lock);
+       list_add(&exp->exp_target_data.ted_nodemap_member,
+                &nodemap->nm_member_list);
+       mutex_unlock(&nodemap->nm_member_list_lock);
 
-       rc = cfs_hash_add_unique(nodemap->nm_member_hash, exp,
-                                &exp->exp_target_data.ted_nodemap_member);
-
-       if (rc == 0)
-               class_export_get(exp);
-       /* else -EALREADY - exp already in nodemap hash */
-
-       return rc;
+       return 0;
 }
 
 /**
@@ -243,35 +126,6 @@ static void nm_member_exp_revoke(struct obd_export *exp)
        ldlm_revoke_export_locks(exp);
 }
 
-static int nm_member_reclassify_cb(struct cfs_hash *hs, struct cfs_hash_bd *bd,
-                                  struct hlist_node *hnode, void *data)
-{
-       struct obd_export       *exp;
-       struct lu_nodemap       *nodemap;
-
-       exp = hlist_entry(hnode, struct obd_export,
-                         exp_target_data.ted_nodemap_member);
-       if (exp == NULL)
-               goto out;
-
-       /* Must use bd_del_locked inside a cfs_hash callback, and exp->nodemap
-        * should never be NULL. For those reasons, can't use member_del.
-        */
-       read_lock(&nm_range_tree_lock);
-       nodemap = nodemap_classify_nid(exp->exp_connection->c_peer.nid);
-       if (exp->exp_target_data.ted_nodemap != nodemap) {
-               cfs_hash_bd_del_locked(hs, bd, hnode);
-               exp->exp_target_data.ted_nodemap = nodemap;
-               cfs_hash_add_unique(nodemap->nm_member_hash, exp,
-                               &exp->exp_target_data.ted_nodemap_member);
-       }
-       read_unlock(&nm_range_tree_lock);
-
-       nm_member_exp_revoke(exp);
-out:
-       return 0;
-}
-
 /* Mutex used to serialize calls to reclassify_nodemap_lock */
 DEFINE_MUTEX(reclassify_nodemap_lock);
 
@@ -296,28 +150,41 @@ DEFINE_MUTEX(reclassify_nodemap_lock);
  */
 void nm_member_reclassify_nodemap(struct lu_nodemap *nodemap)
 {
+       struct obd_export *exp;
+       struct obd_export *tmp;
+       struct lu_nodemap *new_nodemap;
+
        /* reclassify only one nodemap at a time to avoid deadlock */
        mutex_lock(&reclassify_nodemap_lock);
-       cfs_hash_for_each_safe(nodemap->nm_member_hash,
-                              nm_member_reclassify_cb,
-                              NULL);
+       mutex_lock(&nodemap->nm_member_list_lock);
+       list_for_each_entry_safe(exp, tmp, &nodemap->nm_member_list,
+                                exp_target_data.ted_nodemap_member) {
+               lnet_nid_t nid = exp->exp_connection->c_peer.nid;
+
+               /* nodemap_classify_nid requires range tree lock */
+               read_lock(&nm_range_tree_lock);
+               new_nodemap = nodemap_classify_nid(nid);
+               read_unlock(&nm_range_tree_lock);
+               if (new_nodemap != nodemap) {
+                       /* don't use member_del because ted_nodemap
+                        * should never be null
+                        */
+                       list_del_init(&exp->exp_target_data.ted_nodemap_member);
+                       exp->exp_target_data.ted_nodemap = new_nodemap;
+
+                       /* could deadlock if new_nodemap also reclassifying */
+                       mutex_lock(&new_nodemap->nm_member_list_lock);
+                       list_add(&exp->exp_target_data.ted_nodemap_member,
+                                &new_nodemap->nm_member_list);
+                       mutex_unlock(&new_nodemap->nm_member_list_lock);
+                       nm_member_exp_revoke(exp);
+               }
+               nodemap_putref(new_nodemap);
+       }
+       mutex_unlock(&nodemap->nm_member_list_lock);
        mutex_unlock(&reclassify_nodemap_lock);
 }
 
-static int
-nm_member_revoke_locks_cb(struct cfs_hash *hs, struct cfs_hash_bd *bd,
-                         struct hlist_node *hnode, void *data)
-{
-       struct obd_export       *exp;
-       exp = hlist_entry(hnode, struct obd_export,
-                         exp_target_data.ted_nodemap_member);
-       if (exp == NULL)
-               return 0;
-
-       nm_member_exp_revoke(exp);
-       return 0;
-}
-
 /**
  * Revoke the locks for member exports. Changing the idmap is
  * akin to deleting the security context. If the locks are not
@@ -328,6 +195,12 @@ nm_member_revoke_locks_cb(struct cfs_hash *hs, struct cfs_hash_bd *bd,
  */
 void nm_member_revoke_locks(struct lu_nodemap *nodemap)
 {
-       cfs_hash_for_each(nodemap->nm_member_hash, nm_member_revoke_locks_cb,
-                         NULL);
+       struct obd_export *exp;
+       struct obd_export *tmp;
+
+       mutex_lock(&nodemap->nm_member_list_lock);
+       list_for_each_entry_safe(exp, tmp, &nodemap->nm_member_list,
+                           exp_target_data.ted_nodemap_member)
+               nm_member_exp_revoke(exp);
+       mutex_unlock(&nodemap->nm_member_list_lock);
 }