Replace a single linked list with a hash table of lists.
mountd.c uses a single linked list of "struct exportlist" structures, where there is one of these for each exported file system on the NFS server. This list gets long if there are a large number of file systems exported and the list must be searched for each line in the exports file(s) when SIGHUP causes the exports file(s) to be reloaded. A simple benchmark that traverses SLIST() elements and compares two 32bit fields in the structure for equal (which is what the search is) appears to take a couple of nsec. So, for a server with 72000 exported file systems, this can take about 5sec during reload of the exports file(s). By replacing the single linked list with a hash table with a target of 10 elements per list, the time should be reduced to less than 1msec. Peter Errikson (who has a server with 72000+ exported file systems) ran a test program using 5 hashes to see how they worked. fnv_32_buf(fsid,..., 0) fnv_32_buf(fsid,..., FNV1_32_INIT) hash32_buf(fsid,..., 0) hash32_buf(fsid,..., HASHINIT) - plus simply using the low order bits of fsid.val[0]. The first three behaved about equally well, with the first one being slightly better than the others. It has an average variation of about 4.5% about the target list length and that is what this patch uses. Peter Errikson also tested this hash table version and found that the performance wasn't measurably improved by a larger hash table, so a load factor of 10 appears adequate. Tested by: pen@lysator.liu.se (with other patches) PR: 237860 MFC after: 1 month
This commit is contained in:
parent
26fd36b29d
commit
46a6b5c451
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=348452
@ -49,6 +49,7 @@ __FBSDID("$FreeBSD$");
|
|||||||
|
|
||||||
#include <sys/param.h>
|
#include <sys/param.h>
|
||||||
#include <sys/fcntl.h>
|
#include <sys/fcntl.h>
|
||||||
|
#include <sys/fnv_hash.h>
|
||||||
#include <sys/linker.h>
|
#include <sys/linker.h>
|
||||||
#include <sys/module.h>
|
#include <sys/module.h>
|
||||||
#include <sys/mount.h>
|
#include <sys/mount.h>
|
||||||
@ -234,7 +235,9 @@ static int xdr_fhs(XDR *, caddr_t);
|
|||||||
static int xdr_mlist(XDR *, caddr_t);
|
static int xdr_mlist(XDR *, caddr_t);
|
||||||
static void terminate(int);
|
static void terminate(int);
|
||||||
|
|
||||||
static struct exportlisthead exphead = SLIST_HEAD_INITIALIZER(&exphead);
|
#define EXPHASH(f) (fnv_32_buf((f), sizeof(fsid_t), 0) % exphashsize)
|
||||||
|
static struct exportlisthead *exphead = NULL;
|
||||||
|
static int exphashsize = 0;
|
||||||
static SLIST_HEAD(, mountlist) mlhead = SLIST_HEAD_INITIALIZER(&mlhead);
|
static SLIST_HEAD(, mountlist) mlhead = SLIST_HEAD_INITIALIZER(&mlhead);
|
||||||
static char *exnames_default[2] = { _PATH_EXPORTS, NULL };
|
static char *exnames_default[2] = { _PATH_EXPORTS, NULL };
|
||||||
static char **exnames;
|
static char **exnames;
|
||||||
@ -1092,7 +1095,7 @@ mntsrv(struct svc_req *rqstp, SVCXPRT *transp)
|
|||||||
if (bad)
|
if (bad)
|
||||||
ep = NULL;
|
ep = NULL;
|
||||||
else
|
else
|
||||||
ep = ex_search(&fsb.f_fsid, &exphead);
|
ep = ex_search(&fsb.f_fsid, exphead);
|
||||||
hostset = defset = 0;
|
hostset = defset = 0;
|
||||||
if (ep && (chk_host(ep->ex_defdir, saddr, &defset, &hostset,
|
if (ep && (chk_host(ep->ex_defdir, saddr, &defset, &hostset,
|
||||||
&numsecflavors, &secflavorsp) ||
|
&numsecflavors, &secflavorsp) ||
|
||||||
@ -1307,21 +1310,23 @@ xdr_explist_common(XDR *xdrsp, caddr_t cp __unused, int brief)
|
|||||||
int false = 0;
|
int false = 0;
|
||||||
int putdef;
|
int putdef;
|
||||||
sigset_t sighup_mask;
|
sigset_t sighup_mask;
|
||||||
|
int i;
|
||||||
|
|
||||||
sigemptyset(&sighup_mask);
|
sigemptyset(&sighup_mask);
|
||||||
sigaddset(&sighup_mask, SIGHUP);
|
sigaddset(&sighup_mask, SIGHUP);
|
||||||
sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
|
sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
|
||||||
|
|
||||||
SLIST_FOREACH(ep, &exphead, entries) {
|
for (i = 0; i < exphashsize; i++)
|
||||||
putdef = 0;
|
SLIST_FOREACH(ep, &exphead[i], entries) {
|
||||||
if (put_exlist(ep->ex_dirl, xdrsp, ep->ex_defdir,
|
putdef = 0;
|
||||||
&putdef, brief))
|
if (put_exlist(ep->ex_dirl, xdrsp, ep->ex_defdir,
|
||||||
goto errout;
|
&putdef, brief))
|
||||||
if (ep->ex_defdir && putdef == 0 &&
|
goto errout;
|
||||||
put_exlist(ep->ex_defdir, xdrsp, (struct dirlist *)NULL,
|
if (ep->ex_defdir && putdef == 0 &&
|
||||||
&putdef, brief))
|
put_exlist(ep->ex_defdir, xdrsp, NULL,
|
||||||
goto errout;
|
&putdef, brief))
|
||||||
}
|
goto errout;
|
||||||
|
}
|
||||||
sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
|
sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
|
||||||
if (!xdr_bool(xdrsp, &false))
|
if (!xdr_bool(xdrsp, &false))
|
||||||
return (0);
|
return (0);
|
||||||
@ -1545,7 +1550,7 @@ get_exportlist_one(void)
|
|||||||
* See if this directory is already
|
* See if this directory is already
|
||||||
* in the list.
|
* in the list.
|
||||||
*/
|
*/
|
||||||
ep = ex_search(&fsb.f_fsid, &exphead);
|
ep = ex_search(&fsb.f_fsid, exphead);
|
||||||
if (ep == (struct exportlist *)NULL) {
|
if (ep == (struct exportlist *)NULL) {
|
||||||
ep = get_exp();
|
ep = get_exp();
|
||||||
ep->ex_fs = fsb.f_fsid;
|
ep->ex_fs = fsb.f_fsid;
|
||||||
@ -1700,7 +1705,7 @@ get_exportlist_one(void)
|
|||||||
}
|
}
|
||||||
dirhead = (struct dirlist *)NULL;
|
dirhead = (struct dirlist *)NULL;
|
||||||
if ((ep->ex_flag & EX_LINKED) == 0) {
|
if ((ep->ex_flag & EX_LINKED) == 0) {
|
||||||
insert_exports(ep, &exphead);
|
insert_exports(ep, exphead);
|
||||||
|
|
||||||
ep->ex_flag |= EX_LINKED;
|
ep->ex_flag |= EX_LINKED;
|
||||||
}
|
}
|
||||||
@ -1739,7 +1744,8 @@ get_exportlist(void)
|
|||||||
/*
|
/*
|
||||||
* First, get rid of the old list
|
* First, get rid of the old list
|
||||||
*/
|
*/
|
||||||
free_exports(&exphead);
|
if (exphead != NULL)
|
||||||
|
free_exports(exphead);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* and the old V4 root dir.
|
* and the old V4 root dir.
|
||||||
@ -1762,6 +1768,21 @@ get_exportlist(void)
|
|||||||
*/
|
*/
|
||||||
num = getmntinfo(&mntbufp, MNT_NOWAIT);
|
num = getmntinfo(&mntbufp, MNT_NOWAIT);
|
||||||
|
|
||||||
|
/* Allocate hash tables, for first call. */
|
||||||
|
if (exphead == NULL) {
|
||||||
|
/* Target an average linked list length of 10. */
|
||||||
|
exphashsize = num / 10;
|
||||||
|
if (exphashsize < 1)
|
||||||
|
exphashsize = 1;
|
||||||
|
else if (exphashsize > 100000)
|
||||||
|
exphashsize = 100000;
|
||||||
|
exphead = malloc(exphashsize * sizeof(*exphead));
|
||||||
|
if (exphead == NULL)
|
||||||
|
errx(1, "Can't malloc hash table");
|
||||||
|
|
||||||
|
for (i = 0; i < exphashsize; i++)
|
||||||
|
SLIST_INIT(&exphead[i]);
|
||||||
|
}
|
||||||
if (num > 0) {
|
if (num > 0) {
|
||||||
build_iovec(&iov, &iovlen, "fstype", NULL, 0);
|
build_iovec(&iov, &iovlen, "fstype", NULL, 0);
|
||||||
build_iovec(&iov, &iovlen, "fspath", NULL, 0);
|
build_iovec(&iov, &iovlen, "fspath", NULL, 0);
|
||||||
@ -1806,8 +1827,10 @@ get_exportlist(void)
|
|||||||
static void
|
static void
|
||||||
insert_exports(struct exportlist *ep, struct exportlisthead *exhp)
|
insert_exports(struct exportlist *ep, struct exportlisthead *exhp)
|
||||||
{
|
{
|
||||||
|
uint32_t i;
|
||||||
|
|
||||||
SLIST_INSERT_HEAD(exhp, ep, entries);
|
i = EXPHASH(&ep->ex_fs);
|
||||||
|
SLIST_INSERT_HEAD(&exhp[i], ep, entries);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1817,12 +1840,15 @@ static void
|
|||||||
free_exports(struct exportlisthead *exhp)
|
free_exports(struct exportlisthead *exhp)
|
||||||
{
|
{
|
||||||
struct exportlist *ep, *ep2;
|
struct exportlist *ep, *ep2;
|
||||||
|
int i;
|
||||||
|
|
||||||
SLIST_FOREACH_SAFE(ep, exhp, entries, ep2) {
|
for (i = 0; i < exphashsize; i++) {
|
||||||
SLIST_REMOVE(exhp, ep, exportlist, entries);
|
SLIST_FOREACH_SAFE(ep, &exhp[i], entries, ep2) {
|
||||||
free_exp(ep);
|
SLIST_REMOVE(&exhp[i], ep, exportlist, entries);
|
||||||
|
free_exp(ep);
|
||||||
|
}
|
||||||
|
SLIST_INIT(&exhp[i]);
|
||||||
}
|
}
|
||||||
SLIST_INIT(exhp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1962,8 +1988,10 @@ static struct exportlist *
|
|||||||
ex_search(fsid_t *fsid, struct exportlisthead *exhp)
|
ex_search(fsid_t *fsid, struct exportlisthead *exhp)
|
||||||
{
|
{
|
||||||
struct exportlist *ep;
|
struct exportlist *ep;
|
||||||
|
uint32_t i;
|
||||||
|
|
||||||
SLIST_FOREACH(ep, exhp, entries) {
|
i = EXPHASH(fsid);
|
||||||
|
SLIST_FOREACH(ep, &exhp[i], entries) {
|
||||||
if (ep->ex_fs.val[0] == fsid->val[0] &&
|
if (ep->ex_fs.val[0] == fsid->val[0] &&
|
||||||
ep->ex_fs.val[1] == fsid->val[1])
|
ep->ex_fs.val[1] == fsid->val[1])
|
||||||
return (ep);
|
return (ep);
|
||||||
|
Loading…
Reference in New Issue
Block a user