Whamcloud - gitweb
LU-8130 ptlrpc: convert conn_hash to rhashtable
[fs/lustre-release.git] / lustre / ptlrpc / connection.c
index 54aaf0d..62db6aa 100644 (file)
  *
  * You should have received a copy of the GNU General Public License
  * version 2 along with this program; If not, see
- * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
- *
- * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
- * CA 95054 USA or visit www.sun.com if you need additional information or
- * have any questions.
+ * http://www.gnu.org/licenses/gpl-2.0.html
  *
  * GPL HEADER END
  */
  */
 
 #define DEBUG_SUBSYSTEM S_RPC
-#ifdef __KERNEL__
+
+#include <linux/delay.h>
+#include <libcfs/linux/linux-hash.h>
 #include <obd_support.h>
 #include <obd_class.h>
 #include <lustre_net.h>
-#else
-#include <liblustre.h>
-#endif
 
 #include "ptlrpc_internal.h"
 
-static cfs_hash_t *conn_hash = NULL;
-static cfs_hash_ops_t conn_hash_ops;
+static struct rhashtable conn_hash;
+
+/*
+ * struct lnet_process_id may contain unassigned bytes which might not
+ * be zero, so we cannot just hash and compare bytes.
+ */
+
+static u32 lnet_process_id_hash(const void *data, u32 len, u32 seed)
+{
+       const struct lnet_process_id *lpi = data;
+
+       seed = cfs_hash_32(seed ^ lpi->pid, 32);
+       seed ^= cfs_hash_64(lpi->nid, 32);
+       return seed;
+}
+
+static int lnet_process_id_cmp(struct rhashtable_compare_arg *arg,
+                              const void *obj)
+{
+       const struct lnet_process_id *lpi = arg->key;
+       const struct ptlrpc_connection *con = obj;
+
+       if (lpi->nid == con->c_peer.nid &&
+           lpi->pid == con->c_peer.pid)
+               return 0;
+       return -ESRCH;
+}
+
+static const struct rhashtable_params conn_hash_params = {
+       .key_len        = 1,    /* actually variable-length */
+       .key_offset     = offsetof(struct ptlrpc_connection, c_peer),
+       .head_offset    = offsetof(struct ptlrpc_connection, c_hash),
+       .hashfn         = lnet_process_id_hash,
+       .obj_cmpfn      = lnet_process_id_cmp,
+};
 
 struct ptlrpc_connection *
-ptlrpc_connection_get(lnet_process_id_t peer, lnet_nid_t self,
-                      struct obd_uuid *uuid)
+ptlrpc_connection_get(struct lnet_process_id peer, lnet_nid_t self,
+                     struct obd_uuid *uuid)
 {
-        struct ptlrpc_connection *conn, *conn2;
-        ENTRY;
+       struct ptlrpc_connection *conn, *conn2;
+       ENTRY;
 
-        conn = cfs_hash_lookup(conn_hash, &peer);
-        if (conn)
-                GOTO(out, conn);
+       peer.nid = LNetPrimaryNID(peer.nid);
+       conn = rhashtable_lookup_fast(&conn_hash, &peer, conn_hash_params);
+       if (conn) {
+               ptlrpc_connection_addref(conn);
+               GOTO(out, conn);
+       }
 
-        OBD_ALLOC_PTR(conn);
-        if (!conn)
-                RETURN(NULL);
+       OBD_ALLOC_PTR(conn);
+       if (!conn)
+               RETURN(NULL);
 
-        conn->c_peer = peer;
-        conn->c_self = self;
-        CFS_INIT_HLIST_NODE(&conn->c_hash);
+       conn->c_peer = peer;
+       conn->c_self = self;
        atomic_set(&conn->c_refcount, 1);
-        if (uuid)
-                obd_str2uuid(&conn->c_remote_uuid, uuid->uuid);
+       if (uuid)
+               obd_str2uuid(&conn->c_remote_uuid, uuid->uuid);
 
        /*
         * Add the newly created conn to the hash, on key collision we
         * lost a racing addition and must destroy our newly allocated
-        * connection.  The object which exists in the has will be
-        * returned and may be compared against out object.
+        * connection.  The object which exists in the hash will be
+        * returned,otherwise NULL is returned on success.
         */
-       /* In the function below, .hs_keycmp resolves to
-        * conn_keycmp() */
-       /* coverity[overrun-buffer-val] */
-       conn2 = cfs_hash_findadd_unique(conn_hash, &peer, &conn->c_hash);
-       if (conn != conn2) {
+try_again:
+       conn2 = rhashtable_lookup_get_insert_fast(&conn_hash, &conn->c_hash,
+                                                 conn_hash_params);
+       if (conn2) {
+               /* insertion failed */
                OBD_FREE_PTR(conn);
+               if (IS_ERR(conn2)) {
+                       /* hash table could be resizing. */
+                       if (PTR_ERR(conn2) == -ENOMEM ||
+                           PTR_ERR(conn2) == -EBUSY) {
+                               msleep(5);
+                               goto try_again;
+                       }
+                       return NULL;
+               }
                conn = conn2;
+               ptlrpc_connection_addref(conn);
        }
        EXIT;
 out:
@@ -91,7 +131,6 @@ out:
               libcfs_nid2str(conn->c_peer.nid));
        return conn;
 }
-EXPORT_SYMBOL(ptlrpc_connection_get);
 
 int ptlrpc_connection_put(struct ptlrpc_connection *conn)
 {
@@ -101,7 +140,7 @@ int ptlrpc_connection_put(struct ptlrpc_connection *conn)
        if (!conn)
                RETURN(rc);
 
-       LASSERT(atomic_read(&conn->c_refcount) > 1);
+       LASSERT(atomic_read(&conn->c_refcount) > 0);
 
        /*
         * We do not remove connection from hashtable and
@@ -119,7 +158,7 @@ int ptlrpc_connection_put(struct ptlrpc_connection *conn)
         * when ptlrpc_connection_fini()->lh_exit->conn_exit()
         * path is called.
         */
-       if (atomic_dec_return(&conn->c_refcount) == 1)
+       if (atomic_dec_return(&conn->c_refcount) == 0)
                rc = 1;
 
        CDEBUG(D_INFO, "PUT conn=%p refcount %d to %s\n",
@@ -128,7 +167,6 @@ int ptlrpc_connection_put(struct ptlrpc_connection *conn)
 
        RETURN(rc);
 }
-EXPORT_SYMBOL(ptlrpc_connection_put);
 
 struct ptlrpc_connection *
 ptlrpc_connection_addref(struct ptlrpc_connection *conn)
@@ -142,94 +180,12 @@ ptlrpc_connection_addref(struct ptlrpc_connection *conn)
 
        RETURN(conn);
 }
-EXPORT_SYMBOL(ptlrpc_connection_addref);
-
-int ptlrpc_connection_init(void)
-{
-        ENTRY;
-
-        conn_hash = cfs_hash_create("CONN_HASH",
-                                    HASH_CONN_CUR_BITS,
-                                    HASH_CONN_MAX_BITS,
-                                    HASH_CONN_BKT_BITS, 0,
-                                    CFS_HASH_MIN_THETA,
-                                    CFS_HASH_MAX_THETA,
-                                    &conn_hash_ops, CFS_HASH_DEFAULT);
-        if (!conn_hash)
-                RETURN(-ENOMEM);
-
-        RETURN(0);
-}
-EXPORT_SYMBOL(ptlrpc_connection_init);
-
-void ptlrpc_connection_fini(void) {
-        ENTRY;
-        cfs_hash_putref(conn_hash);
-        EXIT;
-}
-EXPORT_SYMBOL(ptlrpc_connection_fini);
-
-/*
- * Hash operations for net_peer<->connection
- */
-static unsigned
-conn_hashfn(cfs_hash_t *hs, const void *key, unsigned mask)
-{
-        return cfs_hash_djb2_hash(key, sizeof(lnet_process_id_t), mask);
-}
-
-static int
-conn_keycmp(const void *key, cfs_hlist_node_t *hnode)
-{
-        struct ptlrpc_connection *conn;
-        const lnet_process_id_t *conn_key;
-
-        LASSERT(key != NULL);
-        conn_key = (lnet_process_id_t*)key;
-        conn = cfs_hlist_entry(hnode, struct ptlrpc_connection, c_hash);
-
-        return conn_key->nid == conn->c_peer.nid &&
-               conn_key->pid == conn->c_peer.pid;
-}
-
-static void *
-conn_key(cfs_hlist_node_t *hnode)
-{
-        struct ptlrpc_connection *conn;
-        conn = cfs_hlist_entry(hnode, struct ptlrpc_connection, c_hash);
-        return &conn->c_peer;
-}
-
-static void *
-conn_object(cfs_hlist_node_t *hnode)
-{
-        return cfs_hlist_entry(hnode, struct ptlrpc_connection, c_hash);
-}
 
 static void
-conn_get(cfs_hash_t *hs, cfs_hlist_node_t *hnode)
+conn_exit(void *vconn, void *data)
 {
-        struct ptlrpc_connection *conn;
+       struct ptlrpc_connection *conn = vconn;
 
-        conn = cfs_hlist_entry(hnode, struct ptlrpc_connection, c_hash);
-       atomic_inc(&conn->c_refcount);
-}
-
-static void
-conn_put_locked(cfs_hash_t *hs, cfs_hlist_node_t *hnode)
-{
-        struct ptlrpc_connection *conn;
-
-        conn = cfs_hlist_entry(hnode, struct ptlrpc_connection, c_hash);
-       atomic_dec(&conn->c_refcount);
-}
-
-static void
-conn_exit(cfs_hash_t *hs, cfs_hlist_node_t *hnode)
-{
-       struct ptlrpc_connection *conn;
-
-       conn = cfs_hlist_entry(hnode, struct ptlrpc_connection, c_hash);
        /*
         * Nothing should be left. Connection user put it and
         * connection also was deleted from table by this time
@@ -241,12 +197,12 @@ conn_exit(cfs_hash_t *hs, cfs_hlist_node_t *hnode)
        OBD_FREE_PTR(conn);
 }
 
-static cfs_hash_ops_t conn_hash_ops = {
-        .hs_hash        = conn_hashfn,
-        .hs_keycmp      = conn_keycmp,
-        .hs_key         = conn_key,
-        .hs_object      = conn_object,
-        .hs_get         = conn_get,
-        .hs_put_locked  = conn_put_locked,
-        .hs_exit        = conn_exit,
-};
+int ptlrpc_connection_init(void)
+{
+       return rhashtable_init(&conn_hash, &conn_hash_params);
+}
+
+void ptlrpc_connection_fini(void)
+{
+       rhashtable_free_and_destroy(&conn_hash, conn_exit, NULL);
+}