From 9897e357de7df51e6cf9f140c4e8ffd6daae23a9 Mon Sep 17 00:00:00 2001 From: Rick Macklem Date: Tue, 14 Apr 2020 00:01:26 +0000 Subject: [PATCH] Re-organize the NFS file handle affinity code for the NFS server. The file handle affinity code was configured to be used by both the old and new NFS servers. This no longer makes sense, since there is only one NFS server. This patch copies a majority of the code in sys/nfs/nfs_fha.c and sys/nfs/nfs_fha.h into sys/fs/nfsserver/nfs_fha_new.c and sys/fs/nfsserver/nfs_fha_new.h, so that the files in sys/nfs can be deleted. The code is simplified by deleting the function callback pointers used to call functions in either the old or new NFS server and they were replaced by calls to the functions. As well as a cleanup, this re-organization simplifies the changes required for handling of external page mbufs, which is required for KERN_TLS. This patch should not result in a semantic change to file handle affinity. --- sys/conf/files | 1 - sys/fs/nfsserver/nfs_fha_new.c | 537 ++++++++++++++++++++++++++++---- sys/fs/nfsserver/nfs_fha_new.h | 68 ++++ sys/fs/nfsserver/nfs_nfsdkrpc.c | 3 +- sys/modules/nfsd/Makefile | 3 +- sys/nfs/nfs_fha.c | 527 ------------------------------- sys/nfs/nfs_fha.h | 122 -------- 7 files changed, 555 insertions(+), 706 deletions(-) delete mode 100644 sys/nfs/nfs_fha.c delete mode 100644 sys/nfs/nfs_fha.h diff --git a/sys/conf/files b/sys/conf/files index 1f11498138ef..14ffdfdf0221 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -4435,7 +4435,6 @@ netsmb/smb_usr.c optional netsmb nfs/bootp_subr.c optional bootp nfscl nfs/krpc_subr.c optional bootp nfscl nfs/nfs_diskless.c optional nfscl nfs_root -nfs/nfs_fha.c optional nfsd nfs/nfs_nfssvc.c optional nfscl | nfslockd | nfsd nlm/nlm_advlock.c optional nfslockd | nfsd nlm/nlm_prot_clnt.c optional nfslockd | nfsd diff --git a/sys/fs/nfsserver/nfs_fha_new.c b/sys/fs/nfsserver/nfs_fha_new.c index 86f81ad06818..3f5559cc8426 100644 --- a/sys/fs/nfsserver/nfs_fha_new.c +++ b/sys/fs/nfsserver/nfs_fha_new.c @@ -29,28 +29,32 @@ #include __FBSDID("$FreeBSD$"); -#include +#include +#include +#include -#include -#include -#include -#include -#include -#include +#include #include -static void fhanew_init(void *foo); -static void fhanew_uninit(void *foo); -rpcproc_t fhanew_get_procnum(rpcproc_t procnum); -int fhanew_realign(struct mbuf **mb, int malloc_flags); -int fhanew_get_fh(uint64_t *fh, int v3, struct mbuf **md, caddr_t *dpos); -int fhanew_is_read(rpcproc_t procnum); -int fhanew_is_write(rpcproc_t procnum); -int fhanew_get_offset(struct mbuf **md, caddr_t *dpos, int v3, - struct fha_info *info); -int fhanew_no_offset(rpcproc_t procnum); -void fhanew_set_locktype(rpcproc_t procnum, struct fha_info *info); -static int fhenew_stats_sysctl(SYSCTL_HANDLER_ARGS); +#include + +static MALLOC_DEFINE(M_NFS_FHA, "NFS FHA", "NFS FHA"); + +static void fhanew_init(void *foo); +static void fhanew_uninit(void *foo); +static rpcproc_t fhanew_get_procnum(rpcproc_t procnum); +static int fhanew_get_fh(uint64_t *fh, int v3, struct mbuf **md, + caddr_t *dpos); +static int fhanew_is_read(rpcproc_t procnum); +static int fhanew_is_write(rpcproc_t procnum); +static int fhanew_get_offset(struct mbuf **md, caddr_t *dpos, + int v3, struct fha_info *info); +static int fhanew_no_offset(rpcproc_t procnum); +static void fhanew_set_locktype(rpcproc_t procnum, + struct fha_info *info); +static int fhenew_stats_sysctl(SYSCTL_HANDLER_ARGS); +static void fha_extract_info(struct svc_req *req, + struct fha_info *i); static struct fha_params fhanew_softc; @@ -66,24 +70,12 @@ static void fhanew_init(void *foo) { struct fha_params *softc; + int i; softc = &fhanew_softc; bzero(softc, sizeof(*softc)); - /* - * Setup the callbacks for this FHA personality. - */ - softc->callbacks.get_procnum = fhanew_get_procnum; - softc->callbacks.realign = fhanew_realign; - softc->callbacks.get_fh = fhanew_get_fh; - softc->callbacks.is_read = fhanew_is_read; - softc->callbacks.is_write = fhanew_is_write; - softc->callbacks.get_offset = fhanew_get_offset; - softc->callbacks.no_offset = fhanew_no_offset; - softc->callbacks.set_locktype = fhanew_set_locktype; - softc->callbacks.fhe_stats_sysctl = fhenew_stats_sysctl; - snprintf(softc->server_name, sizeof(softc->server_name), FHANEW_SERVER_NAME); @@ -101,20 +93,68 @@ fhanew_init(void *foo) return; } - fha_init(softc); + for (i = 0; i < FHA_HASH_SIZE; i++) + mtx_init(&softc->fha_hash[i].mtx, "fhalock", NULL, MTX_DEF); + + /* + * Set the default tuning parameters. + */ + softc->ctls.enable = FHA_DEF_ENABLE; + softc->ctls.read = FHA_DEF_READ; + softc->ctls.write = FHA_DEF_WRITE; + softc->ctls.bin_shift = FHA_DEF_BIN_SHIFT; + softc->ctls.max_nfsds_per_fh = FHA_DEF_MAX_NFSDS_PER_FH; + softc->ctls.max_reqs_per_nfsd = FHA_DEF_MAX_REQS_PER_NFSD; + + /* + * Add sysctls so the user can change the tuning parameters. + */ + SYSCTL_ADD_UINT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), + OID_AUTO, "enable", CTLFLAG_RWTUN, + &softc->ctls.enable, 0, "Enable NFS File Handle Affinity (FHA)"); + + SYSCTL_ADD_UINT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), + OID_AUTO, "read", CTLFLAG_RWTUN, + &softc->ctls.read, 0, "Enable NFS FHA read locality"); + + SYSCTL_ADD_UINT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), + OID_AUTO, "write", CTLFLAG_RWTUN, + &softc->ctls.write, 0, "Enable NFS FHA write locality"); + + SYSCTL_ADD_UINT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), + OID_AUTO, "bin_shift", CTLFLAG_RWTUN, + &softc->ctls.bin_shift, 0, + "Maximum locality distance 2^(bin_shift) bytes"); + + SYSCTL_ADD_UINT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), + OID_AUTO, "max_nfsds_per_fh", CTLFLAG_RWTUN, + &softc->ctls.max_nfsds_per_fh, 0, "Maximum nfsd threads that " + "should be working on requests for the same file handle"); + + SYSCTL_ADD_UINT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), + OID_AUTO, "max_reqs_per_nfsd", CTLFLAG_RWTUN, + &softc->ctls.max_reqs_per_nfsd, 0, "Maximum requests that " + "single nfsd thread should be working on at any time"); + + SYSCTL_ADD_OID(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), + OID_AUTO, "fhe_stats", CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, + 0, 0, fhenew_stats_sysctl, "A", ""); } static void fhanew_uninit(void *foo) { struct fha_params *softc; + int i; softc = &fhanew_softc; - fha_uninit(softc); + sysctl_ctx_free(&softc->sysctl_ctx); + for (i = 0; i < FHA_HASH_SIZE; i++) + mtx_destroy(&softc->fha_hash[i].mtx); } -rpcproc_t +static rpcproc_t fhanew_get_procnum(rpcproc_t procnum) { if (procnum > NFSV2PROC_STATFS) @@ -123,13 +163,7 @@ fhanew_get_procnum(rpcproc_t procnum) return (newnfs_nfsv3_procid[procnum]); } -int -fhanew_realign(struct mbuf **mb, int malloc_flags) -{ - return (newnfs_realign(mb, malloc_flags)); -} - -int +static int fhanew_get_fh(uint64_t *fh, int v3, struct mbuf **md, caddr_t *dpos) { struct nfsrv_descript lnd, *nd; @@ -170,7 +204,7 @@ fhanew_get_fh(uint64_t *fh, int v3, struct mbuf **md, caddr_t *dpos) return (error); } -int +static int fhanew_is_read(rpcproc_t procnum) { if (procnum == NFSPROC_READ) @@ -179,7 +213,7 @@ fhanew_is_read(rpcproc_t procnum) return (0); } -int +static int fhanew_is_write(rpcproc_t procnum) { if (procnum == NFSPROC_WRITE) @@ -188,9 +222,9 @@ fhanew_is_write(rpcproc_t procnum) return (0); } -int +static int fhanew_get_offset(struct mbuf **md, caddr_t *dpos, int v3, - struct fha_info *info) + struct fha_info *info) { struct nfsrv_descript lnd, *nd; uint32_t *tl; @@ -217,7 +251,7 @@ fhanew_get_offset(struct mbuf **md, caddr_t *dpos, int v3, return (error); } -int +static int fhanew_no_offset(rpcproc_t procnum) { if (procnum == NFSPROC_FSSTAT || @@ -230,7 +264,7 @@ fhanew_no_offset(rpcproc_t procnum) return (0); } -void +static void fhanew_set_locktype(rpcproc_t procnum, struct fha_info *info) { switch (procnum) { @@ -264,15 +298,414 @@ fhanew_set_locktype(rpcproc_t procnum, struct fha_info *info) } } -static int -fhenew_stats_sysctl(SYSCTL_HANDLER_ARGS) + +/* + * This just specifies that offsets should obey affinity when within + * the same 1Mbyte (1<<20) chunk for the file (reads only for now). + */ +static void +fha_extract_info(struct svc_req *req, struct fha_info *i) { - return (fhe_stats_sysctl(oidp, arg1, arg2, req, &fhanew_softc)); + struct mbuf *md; + caddr_t dpos; + static u_int64_t random_fh = 0; + int error; + int v3 = (req->rq_vers == 3); + rpcproc_t procnum; + + /* + * We start off with a random fh. If we get a reasonable + * procnum, we set the fh. If there's a concept of offset + * that we're interested in, we set that. + */ + i->fh = ++random_fh; + i->offset = 0; + i->locktype = LK_EXCLUSIVE; + i->read = i->write = 0; + + /* + * Extract the procnum and convert to v3 form if necessary, + * taking care to deal with out-of-range procnums. Caller will + * ensure that rq_vers is either 2 or 3. + */ + procnum = req->rq_proc; + if (!v3) { + rpcproc_t tmp_procnum; + + tmp_procnum = fhanew_get_procnum(procnum); + if (tmp_procnum == -1) + goto out; + procnum = tmp_procnum; + } + + /* + * We do affinity for most. However, we divide a realm of affinity + * by file offset so as to allow for concurrent random access. We + * only do this for reads today, but this may change when IFS supports + * efficient concurrent writes. + */ + if (fhanew_no_offset(procnum)) + goto out; + + i->read = fhanew_is_read(procnum); + i->write = fhanew_is_write(procnum); + + error = newnfs_realign(&req->rq_args, M_NOWAIT); + if (error) + goto out; + md = req->rq_args; + dpos = mtod(md, caddr_t); + + /* Grab the filehandle. */ + error = fhanew_get_fh(&i->fh, v3, &md, &dpos); + if (error) + goto out; + + /* Content ourselves with zero offset for all but reads. */ + if (i->read || i->write) + fhanew_get_offset(&md, &dpos, v3, i); + +out: + fhanew_set_locktype(procnum, i); } +static struct fha_hash_entry * +fha_hash_entry_new(u_int64_t fh) +{ + struct fha_hash_entry *e; + e = malloc(sizeof(*e), M_NFS_FHA, M_WAITOK); + e->fh = fh; + e->num_rw = 0; + e->num_exclusive = 0; + e->num_threads = 0; + LIST_INIT(&e->threads); + + return (e); +} + +static void +fha_hash_entry_destroy(struct fha_hash_entry *e) +{ + + mtx_assert(e->mtx, MA_OWNED); + KASSERT(e->num_rw == 0, + ("%d reqs on destroyed fhe %p", e->num_rw, e)); + KASSERT(e->num_exclusive == 0, + ("%d exclusive reqs on destroyed fhe %p", e->num_exclusive, e)); + KASSERT(e->num_threads == 0, + ("%d threads on destroyed fhe %p", e->num_threads, e)); + free(e, M_NFS_FHA); +} + +static void +fha_hash_entry_remove(struct fha_hash_entry *e) +{ + + mtx_assert(e->mtx, MA_OWNED); + LIST_REMOVE(e, link); + fha_hash_entry_destroy(e); +} + +static struct fha_hash_entry * +fha_hash_entry_lookup(struct fha_params *softc, u_int64_t fh) +{ + struct fha_hash_slot *fhs; + struct fha_hash_entry *fhe, *new_fhe; + + fhs = &softc->fha_hash[fh % FHA_HASH_SIZE]; + new_fhe = fha_hash_entry_new(fh); + new_fhe->mtx = &fhs->mtx; + mtx_lock(&fhs->mtx); + LIST_FOREACH(fhe, &fhs->list, link) + if (fhe->fh == fh) + break; + if (!fhe) { + fhe = new_fhe; + LIST_INSERT_HEAD(&fhs->list, fhe, link); + } else + fha_hash_entry_destroy(new_fhe); + return (fhe); +} + +static void +fha_hash_entry_add_thread(struct fha_hash_entry *fhe, SVCTHREAD *thread) +{ + + mtx_assert(fhe->mtx, MA_OWNED); + thread->st_p2 = 0; + LIST_INSERT_HEAD(&fhe->threads, thread, st_alink); + fhe->num_threads++; +} + +static void +fha_hash_entry_remove_thread(struct fha_hash_entry *fhe, SVCTHREAD *thread) +{ + + mtx_assert(fhe->mtx, MA_OWNED); + KASSERT(thread->st_p2 == 0, + ("%d reqs on removed thread %p", thread->st_p2, thread)); + LIST_REMOVE(thread, st_alink); + fhe->num_threads--; +} + +/* + * Account for an ongoing operation associated with this file. + */ +static void +fha_hash_entry_add_op(struct fha_hash_entry *fhe, int locktype, int count) +{ + + mtx_assert(fhe->mtx, MA_OWNED); + if (LK_EXCLUSIVE == locktype) + fhe->num_exclusive += count; + else + fhe->num_rw += count; +} + +/* + * Get the service thread currently associated with the fhe that is + * appropriate to handle this operation. + */ +static SVCTHREAD * +fha_hash_entry_choose_thread(struct fha_params *softc, + struct fha_hash_entry *fhe, struct fha_info *i, SVCTHREAD *this_thread) +{ + SVCTHREAD *thread, *min_thread = NULL; + int req_count, min_count = 0; + off_t offset1, offset2; + + LIST_FOREACH(thread, &fhe->threads, st_alink) { + req_count = thread->st_p2; + + /* If there are any writes in progress, use the first thread. */ + if (fhe->num_exclusive) { +#if 0 + ITRACE_CURPROC(ITRACE_NFS, ITRACE_INFO, + "fha: %p(%d)w", thread, req_count); +#endif + return (thread); + } + + /* Check whether we should consider locality. */ + if ((i->read && !softc->ctls.read) || + (i->write && !softc->ctls.write)) + goto noloc; + + /* + * Check for locality, making sure that we won't + * exceed our per-thread load limit in the process. + */ + offset1 = i->offset; + offset2 = thread->st_p3; + + if (((offset1 >= offset2) + && ((offset1 - offset2) < (1 << softc->ctls.bin_shift))) + || ((offset2 > offset1) + && ((offset2 - offset1) < (1 << softc->ctls.bin_shift)))) { + if ((softc->ctls.max_reqs_per_nfsd == 0) || + (req_count < softc->ctls.max_reqs_per_nfsd)) { +#if 0 + ITRACE_CURPROC(ITRACE_NFS, ITRACE_INFO, + "fha: %p(%d)r", thread, req_count); +#endif + return (thread); + } + } + +noloc: + /* + * We don't have a locality match, so skip this thread, + * but keep track of the most attractive thread in case + * we need to come back to it later. + */ +#if 0 + ITRACE_CURPROC(ITRACE_NFS, ITRACE_INFO, + "fha: %p(%d)s off1 %llu off2 %llu", thread, + req_count, offset1, offset2); +#endif + if ((min_thread == NULL) || (req_count < min_count)) { + min_count = req_count; + min_thread = thread; + } + } + + /* + * We didn't find a good match yet. See if we can add + * a new thread to this file handle entry's thread list. + */ + if ((softc->ctls.max_nfsds_per_fh == 0) || + (fhe->num_threads < softc->ctls.max_nfsds_per_fh)) { + thread = this_thread; +#if 0 + ITRACE_CURPROC(ITRACE_NFS, ITRACE_INFO, + "fha: %p(%d)t", thread, thread->st_p2); +#endif + fha_hash_entry_add_thread(fhe, thread); + } else { + /* + * We don't want to use any more threads for this file, so + * go back to the most attractive nfsd we're already using. + */ + thread = min_thread; + } + + return (thread); +} + +/* + * After getting a request, try to assign it to some thread. Usually we + * handle it ourselves. + */ SVCTHREAD * fhanew_assign(SVCTHREAD *this_thread, struct svc_req *req) { - return (fha_assign(this_thread, req, &fhanew_softc)); + struct fha_params *softc = &fhanew_softc; + SVCTHREAD *thread; + struct fha_info i; + struct fha_hash_entry *fhe; + + /* Check to see whether we're enabled. */ + if (softc->ctls.enable == 0) + goto thist; + + /* + * Only do placement if this is an NFS request. + */ + if (req->rq_prog != NFS_PROG) + goto thist; + + if (req->rq_vers != 2 && req->rq_vers != 3) + goto thist; + + fha_extract_info(req, &i); + + /* + * We save the offset associated with this request for later + * nfsd matching. + */ + fhe = fha_hash_entry_lookup(softc, i.fh); + req->rq_p1 = fhe; + req->rq_p2 = i.locktype; + req->rq_p3 = i.offset; + + /* + * Choose a thread, taking into consideration locality, thread load, + * and the number of threads already working on this file. + */ + thread = fha_hash_entry_choose_thread(softc, fhe, &i, this_thread); + KASSERT(thread, ("fha_assign: NULL thread!")); + fha_hash_entry_add_op(fhe, i.locktype, 1); + thread->st_p2++; + thread->st_p3 = i.offset; + + /* + * Grab the pool lock here to not let chosen thread go away before + * the new request inserted to its queue while we drop fhe lock. + */ + mtx_lock(&thread->st_lock); + mtx_unlock(fhe->mtx); + + return (thread); +thist: + req->rq_p1 = NULL; + mtx_lock(&this_thread->st_lock); + return (this_thread); +} + +/* + * Called when we're done with an operation. The request has already + * been de-queued. + */ +void +fhanew_nd_complete(SVCTHREAD *thread, struct svc_req *req) +{ + struct fha_hash_entry *fhe = req->rq_p1; + struct mtx *mtx; + + /* + * This may be called for reqs that didn't go through + * fha_assign (e.g. extra NULL ops used for RPCSEC_GSS. + */ + if (!fhe) + return; + + mtx = fhe->mtx; + mtx_lock(mtx); + fha_hash_entry_add_op(fhe, req->rq_p2, -1); + thread->st_p2--; + KASSERT(thread->st_p2 >= 0, ("Negative request count %d on %p", + thread->st_p2, thread)); + if (thread->st_p2 == 0) { + fha_hash_entry_remove_thread(fhe, thread); + if (0 == fhe->num_rw + fhe->num_exclusive) + fha_hash_entry_remove(fhe); + } + mtx_unlock(mtx); +} + +static int +fhenew_stats_sysctl(SYSCTL_HANDLER_ARGS) +{ + struct fha_params *softc = &fhanew_softc; + int error, i; + struct sbuf sb; + struct fha_hash_entry *fhe; + bool_t first, hfirst; + SVCTHREAD *thread; + + sbuf_new(&sb, NULL, 65536, SBUF_FIXEDLEN); + + if (!*softc->pool) { + sbuf_printf(&sb, "NFSD not running\n"); + goto out; + } + + for (i = 0; i < FHA_HASH_SIZE; i++) + if (!LIST_EMPTY(&softc->fha_hash[i].list)) + break; + + if (i == FHA_HASH_SIZE) { + sbuf_printf(&sb, "No file handle entries.\n"); + goto out; + } + + hfirst = TRUE; + for (; i < FHA_HASH_SIZE; i++) { + mtx_lock(&softc->fha_hash[i].mtx); + if (LIST_EMPTY(&softc->fha_hash[i].list)) { + mtx_unlock(&softc->fha_hash[i].mtx); + continue; + } + sbuf_printf(&sb, "%shash %d: {\n", hfirst ? "" : ", ", i); + first = TRUE; + LIST_FOREACH(fhe, &softc->fha_hash[i].list, link) { + sbuf_printf(&sb, "%sfhe %p: {\n", first ? " " : ", ", + fhe); + sbuf_printf(&sb, " fh: %ju\n", (uintmax_t) fhe->fh); + sbuf_printf(&sb, " num_rw/exclusive: %d/%d\n", + fhe->num_rw, fhe->num_exclusive); + sbuf_printf(&sb, " num_threads: %d\n", + fhe->num_threads); + + LIST_FOREACH(thread, &fhe->threads, st_alink) { + sbuf_printf(&sb, " thread %p offset %ju " + "reqs %d\n", thread, + thread->st_p3, thread->st_p2); + } + + sbuf_printf(&sb, " }"); + first = FALSE; + } + sbuf_printf(&sb, "\n}"); + mtx_unlock(&softc->fha_hash[i].mtx); + hfirst = FALSE; + } + + out: + sbuf_trim(&sb); + sbuf_finish(&sb); + error = sysctl_handle_string(oidp, sbuf_data(&sb), sbuf_len(&sb), req); + sbuf_delete(&sb); + return (error); } diff --git a/sys/fs/nfsserver/nfs_fha_new.h b/sys/fs/nfsserver/nfs_fha_new.h index e360ffbb922e..7d437e7a0853 100644 --- a/sys/fs/nfsserver/nfs_fha_new.h +++ b/sys/fs/nfsserver/nfs_fha_new.h @@ -35,7 +35,75 @@ #define FHANEW_SERVER_NAME "nfsd" +/* Sysctl defaults. */ +#define FHA_DEF_ENABLE 1 +#define FHA_DEF_READ 1 +#define FHA_DEF_WRITE 1 +#define FHA_DEF_BIN_SHIFT 22 /* 4MB */ +#define FHA_DEF_MAX_NFSDS_PER_FH 8 +#define FHA_DEF_MAX_REQS_PER_NFSD 0 /* Unlimited */ + +#define FHA_HASH_SIZE 251 + +struct fha_ctls { + int enable; + int read; + int write; + uint32_t bin_shift; + uint32_t max_nfsds_per_fh; + uint32_t max_reqs_per_nfsd; +}; + +/* + * These are the entries in the filehandle hash. They talk about a specific + * file, requests against which are being handled by one or more nfsds. We + * keep a chain of nfsds against the file. We only have more than one if reads + * are ongoing, and then only if the reads affect disparate regions of the + * file. + * + * In general, we want to assign a new request to an existing nfsd if it is + * going to contend with work happening already on that nfsd, or if the + * operation is a read and the nfsd is already handling a proximate read. We + * do this to avoid jumping around in the read stream unnecessarily, and to + * avoid contention between threads over single files. + */ +struct fha_hash_entry { + struct mtx *mtx; + LIST_ENTRY(fha_hash_entry) link; + u_int64_t fh; + u_int32_t num_rw; + u_int32_t num_exclusive; + u_int8_t num_threads; + struct svcthread_list threads; +}; + +LIST_HEAD(fha_hash_entry_list, fha_hash_entry); + +struct fha_hash_slot { + struct fha_hash_entry_list list; + struct mtx mtx; +}; + +/* A structure used for passing around data internally. */ +struct fha_info { + u_int64_t fh; + off_t offset; + int locktype; + int read; + int write; +}; + +struct fha_params { + struct fha_hash_slot fha_hash[FHA_HASH_SIZE]; + struct sysctl_ctx_list sysctl_ctx; + struct sysctl_oid *sysctl_tree; + struct fha_ctls ctls; + char server_name[32]; + SVCPOOL **pool; +}; + SVCTHREAD *fhanew_assign(SVCTHREAD *this_thread, struct svc_req *req); +void fhanew_nd_complete(SVCTHREAD *, struct svc_req *); #endif /* _KERNEL */ #endif /* _NFS_FHA_NEW_H */ diff --git a/sys/fs/nfsserver/nfs_nfsdkrpc.c b/sys/fs/nfsserver/nfs_nfsdkrpc.c index 98a312f9e55b..d53d631d3cca 100644 --- a/sys/fs/nfsserver/nfs_nfsdkrpc.c +++ b/sys/fs/nfsserver/nfs_nfsdkrpc.c @@ -44,7 +44,6 @@ __FBSDID("$FreeBSD$"); #include #include -#include #include #include @@ -589,7 +588,7 @@ nfsrvd_init(int terminating) SYSCTL_STATIC_CHILDREN(_vfs_nfsd)); nfsrvd_pool->sp_rcache = NULL; nfsrvd_pool->sp_assign = fhanew_assign; - nfsrvd_pool->sp_done = fha_nd_complete; + nfsrvd_pool->sp_done = fhanew_nd_complete; NFSD_LOCK(); } } diff --git a/sys/modules/nfsd/Makefile b/sys/modules/nfsd/Makefile index 2ca9e88dde68..247db4a9bb54 100644 --- a/sys/modules/nfsd/Makefile +++ b/sys/modules/nfsd/Makefile @@ -1,9 +1,8 @@ # $FreeBSD$ -.PATH: ${SRCTOP}/sys/fs/nfsserver ${SRCTOP}/sys/nfs +.PATH: ${SRCTOP}/sys/fs/nfsserver KMOD= nfsd SRCS= vnode_if.h \ - nfs_fha.c \ nfs_fha_new.c \ nfs_nfsdserv.c \ nfs_nfsdcache.c \ diff --git a/sys/nfs/nfs_fha.c b/sys/nfs/nfs_fha.c deleted file mode 100644 index fa9af780c6df..000000000000 --- a/sys/nfs/nfs_fha.c +++ /dev/null @@ -1,527 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause-FreeBSD - * - * Copyright (c) 2008 Isilon Inc http://www.isilon.com/ - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include -__FBSDID("$FreeBSD$"); - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -static MALLOC_DEFINE(M_NFS_FHA, "NFS FHA", "NFS FHA"); - -/* - * XXX need to commonize definitions between old and new NFS code. Define - * this here so we don't include one nfsproto.h over the other. - */ -#define NFS_PROG 100003 - -void -fha_init(struct fha_params *softc) -{ - int i; - - for (i = 0; i < FHA_HASH_SIZE; i++) - mtx_init(&softc->fha_hash[i].mtx, "fhalock", NULL, MTX_DEF); - - /* - * Set the default tuning parameters. - */ - softc->ctls.enable = FHA_DEF_ENABLE; - softc->ctls.read = FHA_DEF_READ; - softc->ctls.write = FHA_DEF_WRITE; - softc->ctls.bin_shift = FHA_DEF_BIN_SHIFT; - softc->ctls.max_nfsds_per_fh = FHA_DEF_MAX_NFSDS_PER_FH; - softc->ctls.max_reqs_per_nfsd = FHA_DEF_MAX_REQS_PER_NFSD; - - /* - * Add sysctls so the user can change the tuning parameters. - */ - SYSCTL_ADD_UINT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), - OID_AUTO, "enable", CTLFLAG_RWTUN, - &softc->ctls.enable, 0, "Enable NFS File Handle Affinity (FHA)"); - - SYSCTL_ADD_UINT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), - OID_AUTO, "read", CTLFLAG_RWTUN, - &softc->ctls.read, 0, "Enable NFS FHA read locality"); - - SYSCTL_ADD_UINT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), - OID_AUTO, "write", CTLFLAG_RWTUN, - &softc->ctls.write, 0, "Enable NFS FHA write locality"); - - SYSCTL_ADD_UINT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), - OID_AUTO, "bin_shift", CTLFLAG_RWTUN, - &softc->ctls.bin_shift, 0, "Maximum locality distance 2^(bin_shift) bytes"); - - SYSCTL_ADD_UINT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), - OID_AUTO, "max_nfsds_per_fh", CTLFLAG_RWTUN, - &softc->ctls.max_nfsds_per_fh, 0, "Maximum nfsd threads that " - "should be working on requests for the same file handle"); - - SYSCTL_ADD_UINT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), - OID_AUTO, "max_reqs_per_nfsd", CTLFLAG_RWTUN, - &softc->ctls.max_reqs_per_nfsd, 0, "Maximum requests that " - "single nfsd thread should be working on at any time"); - - SYSCTL_ADD_OID(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree), - OID_AUTO, "fhe_stats", CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_MPSAFE, - 0, 0, softc->callbacks.fhe_stats_sysctl, "A", ""); - -} - -void -fha_uninit(struct fha_params *softc) -{ - int i; - - sysctl_ctx_free(&softc->sysctl_ctx); - for (i = 0; i < FHA_HASH_SIZE; i++) - mtx_destroy(&softc->fha_hash[i].mtx); -} - -/* - * This just specifies that offsets should obey affinity when within - * the same 1Mbyte (1<<20) chunk for the file (reads only for now). - */ -static void -fha_extract_info(struct svc_req *req, struct fha_info *i, - struct fha_callbacks *cb) -{ - struct mbuf *md; - caddr_t dpos; - static u_int64_t random_fh = 0; - int error; - int v3 = (req->rq_vers == 3); - rpcproc_t procnum; - - /* - * We start off with a random fh. If we get a reasonable - * procnum, we set the fh. If there's a concept of offset - * that we're interested in, we set that. - */ - i->fh = ++random_fh; - i->offset = 0; - i->locktype = LK_EXCLUSIVE; - i->read = i->write = 0; - - /* - * Extract the procnum and convert to v3 form if necessary, - * taking care to deal with out-of-range procnums. Caller will - * ensure that rq_vers is either 2 or 3. - */ - procnum = req->rq_proc; - if (!v3) { - rpcproc_t tmp_procnum; - - tmp_procnum = cb->get_procnum(procnum); - if (tmp_procnum == -1) - goto out; - procnum = tmp_procnum; - } - - /* - * We do affinity for most. However, we divide a realm of affinity - * by file offset so as to allow for concurrent random access. We - * only do this for reads today, but this may change when IFS supports - * efficient concurrent writes. - */ - if (cb->no_offset(procnum)) - goto out; - - i->read = cb->is_read(procnum); - i->write = cb->is_write(procnum); - - error = cb->realign(&req->rq_args, M_NOWAIT); - if (error) - goto out; - md = req->rq_args; - dpos = mtod(md, caddr_t); - - /* Grab the filehandle. */ - error = cb->get_fh(&i->fh, v3, &md, &dpos); - if (error) - goto out; - - /* Content ourselves with zero offset for all but reads. */ - if (i->read || i->write) - cb->get_offset(&md, &dpos, v3, i); - -out: - cb->set_locktype(procnum, i); -} - -static struct fha_hash_entry * -fha_hash_entry_new(u_int64_t fh) -{ - struct fha_hash_entry *e; - - e = malloc(sizeof(*e), M_NFS_FHA, M_WAITOK); - e->fh = fh; - e->num_rw = 0; - e->num_exclusive = 0; - e->num_threads = 0; - LIST_INIT(&e->threads); - - return (e); -} - -static void -fha_hash_entry_destroy(struct fha_hash_entry *e) -{ - - mtx_assert(e->mtx, MA_OWNED); - KASSERT(e->num_rw == 0, - ("%d reqs on destroyed fhe %p", e->num_rw, e)); - KASSERT(e->num_exclusive == 0, - ("%d exclusive reqs on destroyed fhe %p", e->num_exclusive, e)); - KASSERT(e->num_threads == 0, - ("%d threads on destroyed fhe %p", e->num_threads, e)); - free(e, M_NFS_FHA); -} - -static void -fha_hash_entry_remove(struct fha_hash_entry *e) -{ - - mtx_assert(e->mtx, MA_OWNED); - LIST_REMOVE(e, link); - fha_hash_entry_destroy(e); -} - -static struct fha_hash_entry * -fha_hash_entry_lookup(struct fha_params *softc, u_int64_t fh) -{ - struct fha_hash_slot *fhs; - struct fha_hash_entry *fhe, *new_fhe; - - fhs = &softc->fha_hash[fh % FHA_HASH_SIZE]; - new_fhe = fha_hash_entry_new(fh); - new_fhe->mtx = &fhs->mtx; - mtx_lock(&fhs->mtx); - LIST_FOREACH(fhe, &fhs->list, link) - if (fhe->fh == fh) - break; - if (!fhe) { - fhe = new_fhe; - LIST_INSERT_HEAD(&fhs->list, fhe, link); - } else - fha_hash_entry_destroy(new_fhe); - return (fhe); -} - -static void -fha_hash_entry_add_thread(struct fha_hash_entry *fhe, SVCTHREAD *thread) -{ - - mtx_assert(fhe->mtx, MA_OWNED); - thread->st_p2 = 0; - LIST_INSERT_HEAD(&fhe->threads, thread, st_alink); - fhe->num_threads++; -} - -static void -fha_hash_entry_remove_thread(struct fha_hash_entry *fhe, SVCTHREAD *thread) -{ - - mtx_assert(fhe->mtx, MA_OWNED); - KASSERT(thread->st_p2 == 0, - ("%d reqs on removed thread %p", thread->st_p2, thread)); - LIST_REMOVE(thread, st_alink); - fhe->num_threads--; -} - -/* - * Account for an ongoing operation associated with this file. - */ -static void -fha_hash_entry_add_op(struct fha_hash_entry *fhe, int locktype, int count) -{ - - mtx_assert(fhe->mtx, MA_OWNED); - if (LK_EXCLUSIVE == locktype) - fhe->num_exclusive += count; - else - fhe->num_rw += count; -} - -/* - * Get the service thread currently associated with the fhe that is - * appropriate to handle this operation. - */ -static SVCTHREAD * -fha_hash_entry_choose_thread(struct fha_params *softc, - struct fha_hash_entry *fhe, struct fha_info *i, SVCTHREAD *this_thread) -{ - SVCTHREAD *thread, *min_thread = NULL; - int req_count, min_count = 0; - off_t offset1, offset2; - - LIST_FOREACH(thread, &fhe->threads, st_alink) { - req_count = thread->st_p2; - - /* If there are any writes in progress, use the first thread. */ - if (fhe->num_exclusive) { -#if 0 - ITRACE_CURPROC(ITRACE_NFS, ITRACE_INFO, - "fha: %p(%d)w", thread, req_count); -#endif - return (thread); - } - - /* Check whether we should consider locality. */ - if ((i->read && !softc->ctls.read) || - (i->write && !softc->ctls.write)) - goto noloc; - - /* - * Check for locality, making sure that we won't - * exceed our per-thread load limit in the process. - */ - offset1 = i->offset; - offset2 = thread->st_p3; - - if (((offset1 >= offset2) - && ((offset1 - offset2) < (1 << softc->ctls.bin_shift))) - || ((offset2 > offset1) - && ((offset2 - offset1) < (1 << softc->ctls.bin_shift)))) { - if ((softc->ctls.max_reqs_per_nfsd == 0) || - (req_count < softc->ctls.max_reqs_per_nfsd)) { -#if 0 - ITRACE_CURPROC(ITRACE_NFS, ITRACE_INFO, - "fha: %p(%d)r", thread, req_count); -#endif - return (thread); - } - } - -noloc: - /* - * We don't have a locality match, so skip this thread, - * but keep track of the most attractive thread in case - * we need to come back to it later. - */ -#if 0 - ITRACE_CURPROC(ITRACE_NFS, ITRACE_INFO, - "fha: %p(%d)s off1 %llu off2 %llu", thread, - req_count, offset1, offset2); -#endif - if ((min_thread == NULL) || (req_count < min_count)) { - min_count = req_count; - min_thread = thread; - } - } - - /* - * We didn't find a good match yet. See if we can add - * a new thread to this file handle entry's thread list. - */ - if ((softc->ctls.max_nfsds_per_fh == 0) || - (fhe->num_threads < softc->ctls.max_nfsds_per_fh)) { - thread = this_thread; -#if 0 - ITRACE_CURPROC(ITRACE_NFS, ITRACE_INFO, - "fha: %p(%d)t", thread, thread->st_p2); -#endif - fha_hash_entry_add_thread(fhe, thread); - } else { - /* - * We don't want to use any more threads for this file, so - * go back to the most attractive nfsd we're already using. - */ - thread = min_thread; - } - - return (thread); -} - -/* - * After getting a request, try to assign it to some thread. Usually we - * handle it ourselves. - */ -SVCTHREAD * -fha_assign(SVCTHREAD *this_thread, struct svc_req *req, - struct fha_params *softc) -{ - SVCTHREAD *thread; - struct fha_info i; - struct fha_hash_entry *fhe; - struct fha_callbacks *cb; - - cb = &softc->callbacks; - - /* Check to see whether we're enabled. */ - if (softc->ctls.enable == 0) - goto thist; - - /* - * Only do placement if this is an NFS request. - */ - if (req->rq_prog != NFS_PROG) - goto thist; - - if (req->rq_vers != 2 && req->rq_vers != 3) - goto thist; - - fha_extract_info(req, &i, cb); - - /* - * We save the offset associated with this request for later - * nfsd matching. - */ - fhe = fha_hash_entry_lookup(softc, i.fh); - req->rq_p1 = fhe; - req->rq_p2 = i.locktype; - req->rq_p3 = i.offset; - - /* - * Choose a thread, taking into consideration locality, thread load, - * and the number of threads already working on this file. - */ - thread = fha_hash_entry_choose_thread(softc, fhe, &i, this_thread); - KASSERT(thread, ("fha_assign: NULL thread!")); - fha_hash_entry_add_op(fhe, i.locktype, 1); - thread->st_p2++; - thread->st_p3 = i.offset; - - /* - * Grab the pool lock here to not let chosen thread go away before - * the new request inserted to its queue while we drop fhe lock. - */ - mtx_lock(&thread->st_lock); - mtx_unlock(fhe->mtx); - - return (thread); -thist: - req->rq_p1 = NULL; - mtx_lock(&this_thread->st_lock); - return (this_thread); -} - -/* - * Called when we're done with an operation. The request has already - * been de-queued. - */ -void -fha_nd_complete(SVCTHREAD *thread, struct svc_req *req) -{ - struct fha_hash_entry *fhe = req->rq_p1; - struct mtx *mtx; - - /* - * This may be called for reqs that didn't go through - * fha_assign (e.g. extra NULL ops used for RPCSEC_GSS. - */ - if (!fhe) - return; - - mtx = fhe->mtx; - mtx_lock(mtx); - fha_hash_entry_add_op(fhe, req->rq_p2, -1); - thread->st_p2--; - KASSERT(thread->st_p2 >= 0, ("Negative request count %d on %p", - thread->st_p2, thread)); - if (thread->st_p2 == 0) { - fha_hash_entry_remove_thread(fhe, thread); - if (0 == fhe->num_rw + fhe->num_exclusive) - fha_hash_entry_remove(fhe); - } - mtx_unlock(mtx); -} - -int -fhe_stats_sysctl(SYSCTL_HANDLER_ARGS, struct fha_params *softc) -{ - int error, i; - struct sbuf sb; - struct fha_hash_entry *fhe; - bool_t first, hfirst; - SVCTHREAD *thread; - - sbuf_new(&sb, NULL, 65536, SBUF_FIXEDLEN); - - if (!*softc->pool) { - sbuf_printf(&sb, "NFSD not running\n"); - goto out; - } - - for (i = 0; i < FHA_HASH_SIZE; i++) - if (!LIST_EMPTY(&softc->fha_hash[i].list)) - break; - - if (i == FHA_HASH_SIZE) { - sbuf_printf(&sb, "No file handle entries.\n"); - goto out; - } - - hfirst = TRUE; - for (; i < FHA_HASH_SIZE; i++) { - mtx_lock(&softc->fha_hash[i].mtx); - if (LIST_EMPTY(&softc->fha_hash[i].list)) { - mtx_unlock(&softc->fha_hash[i].mtx); - continue; - } - sbuf_printf(&sb, "%shash %d: {\n", hfirst ? "" : ", ", i); - first = TRUE; - LIST_FOREACH(fhe, &softc->fha_hash[i].list, link) { - sbuf_printf(&sb, "%sfhe %p: {\n", first ? " " : ", ", fhe); - - sbuf_printf(&sb, " fh: %ju\n", (uintmax_t) fhe->fh); - sbuf_printf(&sb, " num_rw/exclusive: %d/%d\n", - fhe->num_rw, fhe->num_exclusive); - sbuf_printf(&sb, " num_threads: %d\n", fhe->num_threads); - - LIST_FOREACH(thread, &fhe->threads, st_alink) { - sbuf_printf(&sb, " thread %p offset %ju " - "reqs %d\n", thread, - thread->st_p3, thread->st_p2); - } - - sbuf_printf(&sb, " }"); - first = FALSE; - } - sbuf_printf(&sb, "\n}"); - mtx_unlock(&softc->fha_hash[i].mtx); - hfirst = FALSE; - } - - out: - sbuf_trim(&sb); - sbuf_finish(&sb); - error = sysctl_handle_string(oidp, sbuf_data(&sb), sbuf_len(&sb), req); - sbuf_delete(&sb); - return (error); -} diff --git a/sys/nfs/nfs_fha.h b/sys/nfs/nfs_fha.h deleted file mode 100644 index 230d13bf24c0..000000000000 --- a/sys/nfs/nfs_fha.h +++ /dev/null @@ -1,122 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause-FreeBSD - * - * Copyright (c) 2008 Isilon Inc http://www.isilon.com/ - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ -/* $FreeBSD$ */ - -#ifndef _NFS_FHA_H -#define _NFS_FHA_H 1 - -#ifdef _KERNEL - -/* Sysctl defaults. */ -#define FHA_DEF_ENABLE 1 -#define FHA_DEF_READ 1 -#define FHA_DEF_WRITE 1 -#define FHA_DEF_BIN_SHIFT 22 /* 4MB */ -#define FHA_DEF_MAX_NFSDS_PER_FH 8 -#define FHA_DEF_MAX_REQS_PER_NFSD 0 /* Unlimited */ - -#define FHA_HASH_SIZE 251 - -struct fha_ctls { - int enable; - int read; - int write; - uint32_t bin_shift; - uint32_t max_nfsds_per_fh; - uint32_t max_reqs_per_nfsd; -}; - -/* - * These are the entries in the filehandle hash. They talk about a specific - * file, requests against which are being handled by one or more nfsds. We - * keep a chain of nfsds against the file. We only have more than one if reads - * are ongoing, and then only if the reads affect disparate regions of the - * file. - * - * In general, we want to assign a new request to an existing nfsd if it is - * going to contend with work happening already on that nfsd, or if the - * operation is a read and the nfsd is already handling a proximate read. We - * do this to avoid jumping around in the read stream unnecessarily, and to - * avoid contention between threads over single files. - */ -struct fha_hash_entry { - struct mtx *mtx; - LIST_ENTRY(fha_hash_entry) link; - u_int64_t fh; - u_int32_t num_rw; - u_int32_t num_exclusive; - u_int8_t num_threads; - struct svcthread_list threads; -}; - -LIST_HEAD(fha_hash_entry_list, fha_hash_entry); - -struct fha_hash_slot { - struct fha_hash_entry_list list; - struct mtx mtx; -}; - -/* A structure used for passing around data internally. */ -struct fha_info { - u_int64_t fh; - off_t offset; - int locktype; - int read; - int write; -}; - -struct fha_callbacks { - rpcproc_t (*get_procnum)(rpcproc_t procnum); - int (*realign)(struct mbuf **mb, int malloc_flags); - int (*get_fh)(uint64_t *fh, int v3, struct mbuf **md, caddr_t *dpos); - int (*is_read)(rpcproc_t procnum); - int (*is_write)(rpcproc_t procnum); - int (*get_offset)(struct mbuf **md, caddr_t *dpos, int v3, struct - fha_info *info); - int (*no_offset)(rpcproc_t procnum); - void (*set_locktype)(rpcproc_t procnum, struct fha_info *info); - int (*fhe_stats_sysctl)(SYSCTL_HANDLER_ARGS); -}; - -struct fha_params { - struct fha_hash_slot fha_hash[FHA_HASH_SIZE]; - struct sysctl_ctx_list sysctl_ctx; - struct sysctl_oid *sysctl_tree; - struct fha_ctls ctls; - struct fha_callbacks callbacks; - char server_name[32]; - SVCPOOL **pool; -}; - -void fha_nd_complete(SVCTHREAD *, struct svc_req *); -SVCTHREAD *fha_assign(SVCTHREAD *, struct svc_req *, struct fha_params *); -void fha_init(struct fha_params *softc); -void fha_uninit(struct fha_params *softc); -int fhe_stats_sysctl(SYSCTL_HANDLER_ARGS, struct fha_params *softc); - -#endif /* _KERNEL */ -#endif /* _NFS_FHA_H_ */