Commit ac2370ac authored by NeilBrown's avatar NeilBrown Committed by Greg Kroah-Hartman

staging: lustre: ptlrpc: convert conn_hash to rhashtable

Linux has a resizeable hashtable implementation in lib,
so we should use that instead of having one in libcfs.

This patch converts the ptlrpc conn_hash to use rhashtable.
In the process we gain lockless lookup.

As connections are never deleted until the hash table is destroyed,
there is no need to count the reference in the hash table.  There
is also no need to enable automatic_shrinking.
Signed-off-by: default avatarNeilBrown <neilb@suse.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 0d1a9d0f
......@@ -67,6 +67,8 @@
#include <obd_support.h>
#include <uapi/linux/lustre/lustre_ver.h>
#include <linux/rhashtable.h>
/* MD flags we _always_ use */
#define PTLRPC_MD_OPTIONS 0
......@@ -286,7 +288,7 @@ struct ptlrpc_replay_async_args {
*/
struct ptlrpc_connection {
/** linkage for connections hash table */
struct hlist_node c_hash;
struct rhash_head c_hash;
/** Our own lnet nid for this connection */
lnet_nid_t c_self;
/** Remote side nid for this connection */
......
......@@ -67,9 +67,6 @@ extern char obd_jobid_var[];
#define HASH_UUID_BKT_BITS 5
#define HASH_UUID_CUR_BITS 7
#define HASH_UUID_MAX_BITS 12
#define HASH_CONN_BKT_BITS 5
#define HASH_CONN_CUR_BITS 5
#define HASH_CONN_MAX_BITS 15
/* Timeout definitions */
#define OBD_TIMEOUT_DEFAULT 100
/* Time to wait for all clients to reconnect during recovery (hard limit) */
......
......@@ -38,8 +38,41 @@
#include "ptlrpc_internal.h"
static struct cfs_hash *conn_hash;
static struct cfs_hash_ops 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 = hash_32(seed ^ lpi->pid, 32);
seed ^= 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(struct lnet_process_id peer, lnet_nid_t self,
......@@ -47,9 +80,11 @@ ptlrpc_connection_get(struct lnet_process_id peer, lnet_nid_t self,
{
struct ptlrpc_connection *conn, *conn2;
conn = cfs_hash_lookup(conn_hash, &peer);
if (conn)
conn = rhashtable_lookup_fast(&conn_hash, &peer, conn_hash_params);
if (conn) {
ptlrpc_connection_addref(conn);
goto out;
}
conn = kzalloc(sizeof(*conn), GFP_NOFS);
if (!conn)
......@@ -57,7 +92,6 @@ ptlrpc_connection_get(struct lnet_process_id peer, lnet_nid_t self,
conn->c_peer = peer;
conn->c_self = self;
INIT_HLIST_NODE(&conn->c_hash);
atomic_set(&conn->c_refcount, 1);
if (uuid)
obd_str2uuid(&conn->c_remote_uuid, uuid->uuid);
......@@ -65,17 +99,18 @@ ptlrpc_connection_get(struct lnet_process_id peer, lnet_nid_t self,
/*
* 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.
*/
/* In the function below, .hs_keycmp resolves to
* conn_keycmp()
* connection. The object which exists in the hash will be
* returned, otherwise NULL is returned on success.
*/
/* coverity[overrun-buffer-val] */
conn2 = cfs_hash_findadd_unique(conn_hash, &peer, &conn->c_hash);
if (conn != conn2) {
conn2 = rhashtable_lookup_get_insert_fast(&conn_hash, &conn->c_hash,
conn_hash_params);
if (conn2 != NULL) {
/* insertion failed */
kfree(conn);
if (IS_ERR(conn2))
return NULL;
conn = conn2;
ptlrpc_connection_addref(conn);
}
out:
CDEBUG(D_INFO, "conn=%p refcount %d to %s\n",
......@@ -91,7 +126,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
......@@ -109,7 +144,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",
......@@ -130,88 +165,11 @@ ptlrpc_connection_addref(struct ptlrpc_connection *conn)
return conn;
}
int ptlrpc_connection_init(void)
{
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;
}
void ptlrpc_connection_fini(void)
{
cfs_hash_putref(conn_hash);
}
/*
* Hash operations for net_peer<->connection
*/
static unsigned int
conn_hashfn(struct cfs_hash *hs, const void *key, unsigned int mask)
{
return cfs_hash_djb2_hash(key, sizeof(struct lnet_process_id), mask);
}
static int
conn_keycmp(const void *key, struct hlist_node *hnode)
{
struct ptlrpc_connection *conn;
const struct lnet_process_id *conn_key;
LASSERT(key);
conn_key = key;
conn = 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(struct hlist_node *hnode)
{
struct ptlrpc_connection *conn;
conn = hlist_entry(hnode, struct ptlrpc_connection, c_hash);
return &conn->c_peer;
}
static void *
conn_object(struct hlist_node *hnode)
{
return hlist_entry(hnode, struct ptlrpc_connection, c_hash);
}
static void
conn_get(struct cfs_hash *hs, struct hlist_node *hnode)
conn_exit(void *vconn, void *data)
{
struct ptlrpc_connection *conn;
struct ptlrpc_connection *conn = vconn;
conn = hlist_entry(hnode, struct ptlrpc_connection, c_hash);
atomic_inc(&conn->c_refcount);
}
static void
conn_put_locked(struct cfs_hash *hs, struct hlist_node *hnode)
{
struct ptlrpc_connection *conn;
conn = hlist_entry(hnode, struct ptlrpc_connection, c_hash);
atomic_dec(&conn->c_refcount);
}
static void
conn_exit(struct cfs_hash *hs, struct hlist_node *hnode)
{
struct ptlrpc_connection *conn;
conn = 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
......@@ -223,12 +181,12 @@ conn_exit(struct cfs_hash *hs, struct hlist_node *hnode)
kfree(conn);
}
static struct cfs_hash_ops 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);
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment