+ * Helper iterator to convert nodemap hash to list.
+ *
+ * \param hs hash structure
+ * \param bd bucket descriptor
+ * \param hnode hash node
+ * \param nodemap_list_head list head for list of nodemaps in hash
+ */
+static int nodemap_cleanup_iter_cb(struct cfs_hash *hs, struct cfs_hash_bd *bd,
+ struct hlist_node *hnode,
+ void *nodemap_list_head)
+{
+ struct lu_nodemap *nodemap;
+
+ nodemap = hlist_entry(hnode, struct lu_nodemap, nm_hash);
+ list_add(&nodemap->nm_list, nodemap_list_head);
+
+ cfs_hash_bd_del_locked(hs, bd, hnode);
+
+ return 0;
+}
+
+struct nodemap_config *nodemap_config_alloc(void)
+{
+ struct nodemap_config *config;
+ int rc = 0;
+
+ OBD_ALLOC_PTR(config);
+ if (config == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ rc = nodemap_init_hash(config);
+ if (rc != 0) {
+ OBD_FREE_PTR(config);
+ return ERR_PTR(rc);
+ }
+
+ init_rwsem(&config->nmc_range_tree_lock);
+
+ return config;
+}
+EXPORT_SYMBOL(nodemap_config_alloc);
+
+/**
+ * Walk the nodemap_hash and remove all nodemaps.
+ */
+void nodemap_config_dealloc(struct nodemap_config *config)
+{
+ struct lu_nodemap *nodemap = NULL;
+ struct lu_nodemap *nodemap_temp;
+ struct lu_nid_range *range;
+ struct lu_nid_range *range_temp;
+ LIST_HEAD(nodemap_list_head);
+
+ cfs_hash_for_each_safe(config->nmc_nodemap_hash,
+ nodemap_cleanup_iter_cb, &nodemap_list_head);
+ cfs_hash_putref(config->nmc_nodemap_hash);
+
+ /* Because nodemap_destroy might sleep, we can't destroy them
+ * in cfs_hash_for_each, so we build a list there and destroy here
+ */
+ list_for_each_entry_safe(nodemap, nodemap_temp, &nodemap_list_head,
+ nm_list) {
+ mutex_lock(&active_config_lock);
+ down_write(&config->nmc_range_tree_lock);
+
+ /* move members to new config, requires ac lock */
+ nm_member_reclassify_nodemap(nodemap);
+ list_for_each_entry_safe(range, range_temp, &nodemap->nm_ranges,
+ rn_list)
+ range_delete(&config->nmc_range_tree, range);
+ up_write(&config->nmc_range_tree_lock);
+ mutex_unlock(&active_config_lock);
+
+ /* putref must be outside of ac lock if nm could be destroyed */
+ nodemap_putref(nodemap);
+ }
+ OBD_FREE_PTR(config);
+}
+EXPORT_SYMBOL(nodemap_config_dealloc);
+
+/*
+ * callback for cfs_hash_for_each_safe used to convert a nodemap hash to a
+ * nodemap list, generally for locking purposes as a hash cb can't sleep.
+ */
+int nm_hash_list_cb(struct cfs_hash *hs, struct cfs_hash_bd *bd,
+ struct hlist_node *hnode,
+ void *nodemap_list_head)
+{
+ struct lu_nodemap *nodemap;
+
+ nodemap = hlist_entry(hnode, struct lu_nodemap, nm_hash);
+ list_add(&nodemap->nm_list, nodemap_list_head);
+ return 0;
+}
+
+void nodemap_config_set_active(struct nodemap_config *config)
+{
+ struct nodemap_config *old_config = active_config;
+ struct lu_nodemap *nodemap;
+ struct lu_nodemap *tmp;
+ bool revoke_locks;
+ LIST_HEAD(nodemap_list_head);
+
+ ENTRY;
+
+ LASSERT(active_config != config);
+ LASSERT(config->nmc_default_nodemap);
+
+ mutex_lock(&active_config_lock);
+
+ /* move proc entries from already existing nms, create for new nms */
+ cfs_hash_for_each_safe(config->nmc_nodemap_hash,
+ nm_hash_list_cb, &nodemap_list_head);
+ list_for_each_entry_safe(nodemap, tmp, &nodemap_list_head, nm_list) {
+ struct lu_nodemap *old_nm = NULL;
+
+ if (active_config != NULL)
+ old_nm = cfs_hash_lookup(
+ active_config->nmc_nodemap_hash,
+ nodemap->nm_name);
+ if (old_nm != NULL) {
+ nodemap->nm_pde_data = old_nm->nm_pde_data;
+ old_nm->nm_pde_data = NULL;
+ nodemap_putref(old_nm);
+ } else {
+ bool is_def = (nodemap == config->nmc_default_nodemap);
+
+ lprocfs_nodemap_register(nodemap, is_def);
+ }
+ }
+
+ /*
+ * We only need to revoke locks if old nodemap was active, and new
+ * config is now nodemap inactive. nodemap_config_dealloc will
+ * reclassify exports, triggering a lock revoke if and only if new
+ * nodemap is active.
+ */
+ revoke_locks = !config->nmc_nodemap_is_active && nodemap_active;
+
+ /* if new config is inactive, deactivate live config before switching */
+ if (!config->nmc_nodemap_is_active)
+ nodemap_active = false;
+ active_config = config;
+ if (config->nmc_nodemap_is_active)
+ nodemap_active = true;
+
+ mutex_unlock(&active_config_lock);
+
+ if (old_config != NULL)
+ nodemap_config_dealloc(old_config);
+
+ if (revoke_locks)
+ nm_member_revoke_all();
+
+ EXIT;
+}
+
+/**