Provide a findhook method for ng_socket(4). The node stores a

hash with names of its hooks. It starts with size of 16, and
grows when number of hooks reaches twice the current size. A
failure to grow (memory is allocated with M_NOWAIT) isn't
fatal, however.

I used standard hash(9) function for the hash. With 25000
hooks named in the mpd (ports/net/mpd5) manner of "b%u", the
distributions is the following: 72.1% entries consist of one
element, 22.1% consist of two, 5.2% consist of three and
0.6% of four.

Speedup in a synthetic test that creates 25000 hooks and then
runs through a long cyclce dereferencing them in a random order
is over 25 times.
This commit is contained in:
Gleb Smirnoff 2012-01-23 16:43:13 +00:00
parent 93a1b4c4cf
commit 48a47609bc
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=230487

View File

@ -51,6 +51,7 @@
#include <sys/param.h>
#include <sys/domain.h>
#include <sys/hash.h>
#include <sys/kernel.h>
#include <sys/linker.h>
#include <sys/lock.h>
@ -112,6 +113,7 @@ static ng_rcvmsg_t ngs_rcvmsg;
static ng_shutdown_t ngs_shutdown;
static ng_newhook_t ngs_newhook;
static ng_connect_t ngs_connect;
static ng_findhook_t ngs_findhook;
static ng_rcvdata_t ngs_rcvdata;
static ng_disconnect_t ngs_disconnect;
@ -137,6 +139,7 @@ static struct ng_type typestruct = {
.shutdown = ngs_shutdown,
.newhook = ngs_newhook,
.connect = ngs_connect,
.findhook = ngs_findhook,
.rcvdata = ngs_rcvdata,
.disconnect = ngs_disconnect,
};
@ -162,11 +165,19 @@ static struct mtx ngsocketlist_mtx;
#define TRAP_ERROR
#endif
struct hookpriv {
LIST_ENTRY(hookpriv) next;
hook_p hook;
};
LIST_HEAD(ngshash, hookpriv);
/* Per-node private data */
struct ngsock {
struct ng_node *node; /* the associated netgraph node */
struct ngpcb *datasock; /* optional data socket */
struct ngpcb *ctlsock; /* optional control socket */
struct ngshash *hash; /* hash for hook names */
u_long hmask; /* hash mask */
int flags;
int refs;
struct mtx mtx; /* mtx to wait on */
@ -537,8 +548,14 @@ ng_attach_cntl(struct socket *so)
return (error);
}
/* Allocate node private info */
/*
* Allocate node private info and hash. We start
* with 16 hash entries, however we may grow later
* in ngs_newhook(). We can't predict how much hooks
* does this node plan to have.
*/
priv = malloc(sizeof(*priv), M_NETGRAPH_SOCK, M_WAITOK | M_ZERO);
priv->hash = hashinit(16, M_NETGRAPH_SOCK, &priv->hmask);
/* Initialize mutex. */
mtx_init(&priv->mtx, "ng_socket", NULL, MTX_DEF);
@ -643,6 +660,7 @@ ng_socket_free_priv(struct ngsock *priv)
if (priv->refs == 0) {
mtx_destroy(&priv->mtx);
hashdestroy(priv->hash, M_NETGRAPH_SOCK, priv->hmask);
free(priv, M_NETGRAPH_SOCK);
return;
}
@ -752,6 +770,35 @@ ngs_constructor(node_p nodep)
return (EINVAL);
}
static void
ngs_rehash(node_p node)
{
struct ngsock *priv = NG_NODE_PRIVATE(node);
struct ngshash *new;
struct hookpriv *hp;
hook_p hook;
uint32_t h;
u_long hmask;
new = hashinit_flags((priv->hmask + 1) * 2, M_NETGRAPH_SOCK, &hmask,
HASH_NOWAIT);
if (new == NULL)
return;
LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) {
hp = NG_HOOK_PRIVATE(hook);
#ifdef INVARIANTS
LIST_REMOVE(hp, next);
#endif
h = hash32_str(NG_HOOK_NAME(hook), HASHINIT) & hmask;
LIST_INSERT_HEAD(&new[h], hp, next);
}
hashdestroy(priv->hash, M_NETGRAPH_SOCK, priv->hmask);
priv->hash = new;
priv->hmask = hmask;
}
/*
* We allow any hook to be connected to the node.
* There is no per-hook private information though.
@ -759,7 +806,20 @@ ngs_constructor(node_p nodep)
static int
ngs_newhook(node_p node, hook_p hook, const char *name)
{
NG_HOOK_SET_PRIVATE(hook, NG_NODE_PRIVATE(node));
struct ngsock *const priv = NG_NODE_PRIVATE(node);
struct hookpriv *hp;
uint32_t h;
hp = malloc(sizeof(*hp), M_NETGRAPH_SOCK, M_NOWAIT);
if (hp == NULL)
return (ENOMEM);
if (node->nd_numhooks * 2 > priv->hmask)
ngs_rehash(node);
hp->hook = hook;
h = hash32_str(name, HASHINIT) & priv->hmask;
LIST_INSERT_HEAD(&priv->hash[h], hp, next);
NG_HOOK_SET_PRIVATE(hook, hp);
return (0);
}
@ -781,6 +841,41 @@ ngs_connect(hook_p hook)
return (0);
}
/* Look up hook by name */
static hook_p
ngs_findhook(node_p node, const char *name)
{
struct ngsock *priv = NG_NODE_PRIVATE(node);
struct hookpriv *hp;
uint32_t h;
/*
* Microoptimisations for a ng_socket with no
* hooks, or with a single hook, which is a
* common case.
*/
if (node->nd_numhooks == 0)
return (NULL);
if (node->nd_numhooks == 1) {
hook_p hook;
hook = LIST_FIRST(&node->nd_hooks);
if (strcmp(NG_HOOK_NAME(hook), name) == 0)
return (hook);
else
return (NULL);
}
h = hash32_str(name, HASHINIT) & priv->hmask;
LIST_FOREACH(hp, &priv->hash[h], next)
if (strcmp(NG_HOOK_NAME(hp->hook), name) == 0)
return (hp->hook);
return (NULL);
}
/*
* Incoming messages get passed up to the control socket.
* Unless they are for us specifically (socket_type)
@ -948,6 +1043,10 @@ ngs_disconnect(hook_p hook)
{
node_p node = NG_HOOK_NODE(hook);
struct ngsock *const priv = NG_NODE_PRIVATE(node);
struct hookpriv *hp = NG_HOOK_PRIVATE(hook);
LIST_REMOVE(hp, next);
free(hp, M_NETGRAPH_SOCK);
if ((priv->datasock) && (priv->datasock->ng_socket)) {
if (NG_NODE_NUMHOOKS(node) == 1)