Whamcloud - gitweb
LU-6496 ptlrpc: Fix wrong code indentation in plain_authorize
[fs/lustre-release.git] / lustre / ptlrpc / nodemap_member.c
index 08b269b..9871f8d 100644 (file)
@@ -50,7 +50,7 @@ void nm_member_putref(struct obd_export *exp)
 static __u32 nm_member_hashfn(cfs_hash_t *hash_body,
                           const void *key, unsigned mask)
 {
-       return cfs_hash_djb2_hash(key, strlen(key), mask);
+       return hash_long((unsigned long)key, hash_body->hs_bkt_bits) & mask;
 }
 
 static void *nm_member_hs_key(struct hlist_node *hnode)
@@ -106,13 +106,14 @@ static void nm_member_hs_put_locked(cfs_hash_t *hs,
  */
 void nm_member_del(struct lu_nodemap *nodemap, struct obd_export *exp)
 {
+       struct obd_export *exp1;
 
-       exp->exp_target_data.ted_nodemap = NULL;
-       exp = cfs_hash_del_key(nodemap->nm_member_hash, exp);
-       if (exp == NULL)
-               return;
+       exp1 = cfs_hash_del_key(nodemap->nm_member_hash, exp);
+       if (exp1 != NULL)
+               class_export_put(exp1);
 
-       class_export_put(exp);
+       LASSERT(hlist_unhashed(&exp->exp_target_data.ted_nodemap_member));
+       exp->exp_target_data.ted_nodemap = NULL;
 }
 
 static cfs_hash_ops_t nm_member_hash_operations = {
@@ -219,7 +220,7 @@ int nm_member_add(struct lu_nodemap *nodemap, struct obd_export *exp)
        rc = cfs_hash_add_unique(nodemap->nm_member_hash, exp,
                                 &exp->exp_target_data.ted_nodemap_member);
 
-       if (rc == 0 )
+       if (rc == 0)
                class_export_get(exp);
        /* else -EALREADY - exp already in nodemap hash */
 
@@ -246,39 +247,61 @@ static int nm_member_reclassify_cb(cfs_hash_t *hs, cfs_hash_bd_t *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;
 
-       /* Call member add before del so exp->nodemap is never NULL. Must use
-        * bd_del_locked inside a cfs_hash callback. For those reasons, can't
-        * use member_del.
+       /* 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.
         */
-       nodemap_add_member(exp->exp_connection->c_peer.nid, exp);
-       cfs_hash_bd_del_locked(hs, bd, hnode);
-       class_export_put(exp);
+       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);
+
 /**
- * Reclassify the members of a nodemap after range changes or activation,
- * based on the member export's NID and the nodemap's new NID ranges. Exports
- * that are no longer classified as being part of this nodemap are moved to the
- * nodemap whose NID ranges contain the export's NID, and their locks are
- * revoked.
+ * Reclassify the members of a nodemap after range changes or activation.
+ * This function reclassifies the members of a nodemap based on the member
+ * export's NID and the nodemap's new NID ranges. Exports that are no longer
+ * classified as being part of this nodemap are moved to the nodemap whose
+ * NID ranges contain the export's NID, and their locks are revoked.
+ *
+ * Calls to this function are serialized due to a potential deadlock: Say there
+ * is a nodemap A and a nodemap B that both need to reclassify their members.
+ * If there is a member in nodemap A that should be in nodemap B, reclassify
+ * will attempt to add the member to nodemap B. If nodemap B is also
+ * reclassifying its members, then its hash is locked and nodemap A's attempt
+ * to add will block and wait for nodemap B's reclassify to finish. If
+ * nodemap B's reclassify then attempts to reclassify a member that should be
+ * in nodemap A, it will also try add the member to nodemap A's locked hash,
+ * causing a deadlock.
  *
  * \param      nodemap         nodemap with members to reclassify
  */
 void nm_member_reclassify_nodemap(struct lu_nodemap *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_unlock(&reclassify_nodemap_lock);
 }
 
 static int nm_member_revoke_locks_cb(cfs_hash_t *hs, cfs_hash_bd_t *bd,