1184588Sdfr/*-
2184588Sdfr * Copyright (c) 2008 Isilon Inc http://www.isilon.com/
3184588Sdfr *
4184588Sdfr * Redistribution and use in source and binary forms, with or without
5184588Sdfr * modification, are permitted provided that the following conditions
6184588Sdfr * are met:
7184588Sdfr * 1. Redistributions of source code must retain the above copyright
8184588Sdfr *    notice, this list of conditions and the following disclaimer.
9184588Sdfr * 2. Redistributions in binary form must reproduce the above copyright
10184588Sdfr *    notice, this list of conditions and the following disclaimer in the
11184588Sdfr *    documentation and/or other materials provided with the distribution.
12184588Sdfr *
13184588Sdfr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14184588Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15184588Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16184588Sdfr * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17184588Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18184588Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19184588Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20184588Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21184588Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22184588Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23184588Sdfr * SUCH DAMAGE.
24184588Sdfr */
25184588Sdfr
26184588Sdfr#include <sys/cdefs.h>
27184588Sdfr__FBSDID("$FreeBSD: stable/10/sys/nfs/nfs_fha.c 322138 2017-08-07 07:40:00Z mav $");
28184588Sdfr
29184588Sdfr#include <sys/param.h>
30184588Sdfr#include <sys/systm.h>
31184588Sdfr#include <sys/sysproto.h>
32184588Sdfr#include <sys/kernel.h>
33184588Sdfr#include <sys/sysctl.h>
34184588Sdfr#include <sys/vnode.h>
35184588Sdfr#include <sys/malloc.h>
36184588Sdfr#include <sys/mount.h>
37184588Sdfr#include <sys/mbuf.h>
38184588Sdfr#include <sys/sbuf.h>
39184588Sdfr
40184588Sdfr#include <rpc/rpc.h>
41249596Sken#include <nfs/nfs_fha.h>
42184588Sdfr
43184588Sdfrstatic MALLOC_DEFINE(M_NFS_FHA, "NFS FHA", "NFS FHA");
44184588Sdfr
45201899Smarius/*
46249592Sken * XXX need to commonize definitions between old and new NFS code.  Define
47249592Sken * this here so we don't include one nfsproto.h over the other.
48184588Sdfr */
49249592Sken#define	NFS_PROG		100003
50184588Sdfr
51249592Skenvoid
52249592Skenfha_init(struct fha_params *softc)
53184588Sdfr{
54261054Smav	int i;
55184588Sdfr
56261054Smav	for (i = 0; i < FHA_HASH_SIZE; i++)
57261054Smav		mtx_init(&softc->fha_hash[i].mtx, "fhalock", NULL, MTX_DEF);
58184588Sdfr
59184588Sdfr	/*
60249592Sken	 * Set the default tuning parameters.
61184588Sdfr	 */
62249592Sken	softc->ctls.enable = FHA_DEF_ENABLE;
63322138Smav	softc->ctls.read = FHA_DEF_READ;
64322138Smav	softc->ctls.write = FHA_DEF_WRITE;
65249592Sken	softc->ctls.bin_shift = FHA_DEF_BIN_SHIFT;
66249592Sken	softc->ctls.max_nfsds_per_fh = FHA_DEF_MAX_NFSDS_PER_FH;
67249592Sken	softc->ctls.max_reqs_per_nfsd = FHA_DEF_MAX_REQS_PER_NFSD;
68184588Sdfr
69249592Sken	/*
70322138Smav	 * Add sysctls so the user can change the tuning parameters.
71249592Sken	 */
72249592Sken	SYSCTL_ADD_UINT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree),
73322138Smav	    OID_AUTO, "enable", CTLFLAG_RWTUN,
74249592Sken	    &softc->ctls.enable, 0, "Enable NFS File Handle Affinity (FHA)");
75249592Sken
76249592Sken	SYSCTL_ADD_UINT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree),
77322138Smav	    OID_AUTO, "read", CTLFLAG_RWTUN,
78322138Smav	    &softc->ctls.read, 0, "Enable NFS FHA read locality");
79184588Sdfr
80249592Sken	SYSCTL_ADD_UINT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree),
81322138Smav	    OID_AUTO, "write", CTLFLAG_RWTUN,
82322138Smav	    &softc->ctls.write, 0, "Enable NFS FHA write locality");
83322138Smav
84322138Smav	SYSCTL_ADD_UINT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree),
85322138Smav	    OID_AUTO, "bin_shift", CTLFLAG_RWTUN,
86322138Smav	    &softc->ctls.bin_shift, 0, "Maximum locality distance 2^(bin_shift) bytes");
87322138Smav
88322138Smav	SYSCTL_ADD_UINT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree),
89322138Smav	    OID_AUTO, "max_nfsds_per_fh", CTLFLAG_RWTUN,
90249592Sken	    &softc->ctls.max_nfsds_per_fh, 0, "Maximum nfsd threads that "
91184588Sdfr	    "should be working on requests for the same file handle");
92184588Sdfr
93249592Sken	SYSCTL_ADD_UINT(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree),
94322138Smav	    OID_AUTO, "max_reqs_per_nfsd", CTLFLAG_RWTUN,
95249592Sken	    &softc->ctls.max_reqs_per_nfsd, 0, "Maximum requests that "
96184588Sdfr	    "single nfsd thread should be working on at any time");
97184588Sdfr
98249592Sken	SYSCTL_ADD_OID(&softc->sysctl_ctx, SYSCTL_CHILDREN(softc->sysctl_tree),
99184588Sdfr	    OID_AUTO, "fhe_stats", CTLTYPE_STRING | CTLFLAG_RD, 0, 0,
100249592Sken	    softc->callbacks.fhe_stats_sysctl, "A", "");
101249592Sken
102184588Sdfr}
103184588Sdfr
104249592Skenvoid
105249592Skenfha_uninit(struct fha_params *softc)
106184588Sdfr{
107261054Smav	int i;
108261054Smav
109249592Sken	sysctl_ctx_free(&softc->sysctl_ctx);
110261054Smav	for (i = 0; i < FHA_HASH_SIZE; i++)
111261054Smav		mtx_destroy(&softc->fha_hash[i].mtx);
112184588Sdfr}
113184588Sdfr
114201899Smarius/*
115184588Sdfr * This just specifies that offsets should obey affinity when within
116184588Sdfr * the same 1Mbyte (1<<20) chunk for the file (reads only for now).
117184588Sdfr */
118184588Sdfrstatic void
119249592Skenfha_extract_info(struct svc_req *req, struct fha_info *i,
120249592Sken    struct fha_callbacks *cb)
121184588Sdfr{
122201896Smarius	struct mbuf *md;
123201896Smarius	caddr_t dpos;
124184588Sdfr	static u_int64_t random_fh = 0;
125184588Sdfr	int error;
126184588Sdfr	int v3 = (req->rq_vers == 3);
127184588Sdfr	rpcproc_t procnum;
128184588Sdfr
129201899Smarius	/*
130201899Smarius	 * We start off with a random fh.  If we get a reasonable
131201899Smarius	 * procnum, we set the fh.  If there's a concept of offset
132184588Sdfr	 * that we're interested in, we set that.
133184588Sdfr	 */
134184588Sdfr	i->fh = ++random_fh;
135184588Sdfr	i->offset = 0;
136184588Sdfr	i->locktype = LK_EXCLUSIVE;
137322138Smav	i->read = i->write = 0;
138201899Smarius
139184588Sdfr	/*
140184744Sdfr	 * Extract the procnum and convert to v3 form if necessary,
141201899Smarius	 * taking care to deal with out-of-range procnums.  Caller will
142184744Sdfr	 * ensure that rq_vers is either 2 or 3.
143184588Sdfr	 */
144184588Sdfr	procnum = req->rq_proc;
145184744Sdfr	if (!v3) {
146249592Sken		rpcproc_t tmp_procnum;
147249592Sken
148249592Sken		tmp_procnum = cb->get_procnum(procnum);
149249592Sken		if (tmp_procnum == -1)
150184744Sdfr			goto out;
151249592Sken		procnum = tmp_procnum;
152184744Sdfr	}
153184588Sdfr
154201899Smarius	/*
155201899Smarius	 * We do affinity for most.  However, we divide a realm of affinity
156201899Smarius	 * by file offset so as to allow for concurrent random access.  We
157201899Smarius	 * only do this for reads today, but this may change when IFS supports
158184588Sdfr	 * efficient concurrent writes.
159184588Sdfr	 */
160249592Sken	if (cb->no_offset(procnum))
161184588Sdfr		goto out;
162201899Smarius
163322138Smav	i->read = cb->is_read(procnum);
164322138Smav	i->write = cb->is_write(procnum);
165322138Smav
166249592Sken	error = cb->realign(&req->rq_args, M_NOWAIT);
167203732Smarius	if (error)
168203732Smarius		goto out;
169201896Smarius	md = req->rq_args;
170201896Smarius	dpos = mtod(md, caddr_t);
171201896Smarius
172184588Sdfr	/* Grab the filehandle. */
173261049Smav	error = cb->get_fh(&i->fh, v3, &md, &dpos);
174184588Sdfr	if (error)
175184588Sdfr		goto out;
176184588Sdfr
177184588Sdfr	/* Content ourselves with zero offset for all but reads. */
178322138Smav	if (i->read || i->write)
179249592Sken		cb->get_offset(&md, &dpos, v3, i);
180184588Sdfr
181249592Skenout:
182249592Sken	cb->set_locktype(procnum, i);
183184588Sdfr}
184184588Sdfr
185184588Sdfrstatic struct fha_hash_entry *
186184588Sdfrfha_hash_entry_new(u_int64_t fh)
187184588Sdfr{
188184588Sdfr	struct fha_hash_entry *e;
189184588Sdfr
190184588Sdfr	e = malloc(sizeof(*e), M_NFS_FHA, M_WAITOK);
191184588Sdfr	e->fh = fh;
192249592Sken	e->num_rw = 0;
193249592Sken	e->num_exclusive = 0;
194184588Sdfr	e->num_threads = 0;
195184588Sdfr	LIST_INIT(&e->threads);
196201899Smarius
197201899Smarius	return (e);
198184588Sdfr}
199184588Sdfr
200184588Sdfrstatic void
201184588Sdfrfha_hash_entry_destroy(struct fha_hash_entry *e)
202184588Sdfr{
203184588Sdfr
204261054Smav	mtx_assert(e->mtx, MA_OWNED);
205261054Smav	KASSERT(e->num_rw == 0,
206261054Smav	    ("%d reqs on destroyed fhe %p", e->num_rw, e));
207261054Smav	KASSERT(e->num_exclusive == 0,
208261054Smav	    ("%d exclusive reqs on destroyed fhe %p", e->num_exclusive, e));
209261054Smav	KASSERT(e->num_threads == 0,
210261054Smav	    ("%d threads on destroyed fhe %p", e->num_threads, e));
211184588Sdfr	free(e, M_NFS_FHA);
212184588Sdfr}
213184588Sdfr
214184588Sdfrstatic void
215184588Sdfrfha_hash_entry_remove(struct fha_hash_entry *e)
216184588Sdfr{
217184588Sdfr
218261054Smav	mtx_assert(e->mtx, MA_OWNED);
219184588Sdfr	LIST_REMOVE(e, link);
220184588Sdfr	fha_hash_entry_destroy(e);
221184588Sdfr}
222184588Sdfr
223184588Sdfrstatic struct fha_hash_entry *
224249592Skenfha_hash_entry_lookup(struct fha_params *softc, u_int64_t fh)
225184588Sdfr{
226249592Sken	SVCPOOL *pool;
227261054Smav	struct fha_hash_slot *fhs;
228261054Smav	struct fha_hash_entry *fhe, *new_fhe;
229249592Sken
230249592Sken	pool = *softc->pool;
231261054Smav	fhs = &softc->fha_hash[fh % FHA_HASH_SIZE];
232261054Smav	new_fhe = fha_hash_entry_new(fh);
233261054Smav	new_fhe->mtx = &fhs->mtx;
234261054Smav	mtx_lock(&fhs->mtx);
235261054Smav	LIST_FOREACH(fhe, &fhs->list, link)
236184588Sdfr		if (fhe->fh == fh)
237184588Sdfr			break;
238184588Sdfr	if (!fhe) {
239261054Smav		fhe = new_fhe;
240261054Smav		LIST_INSERT_HEAD(&fhs->list, fhe, link);
241261054Smav	} else
242261054Smav		fha_hash_entry_destroy(new_fhe);
243201899Smarius	return (fhe);
244184588Sdfr}
245184588Sdfr
246184588Sdfrstatic void
247184588Sdfrfha_hash_entry_add_thread(struct fha_hash_entry *fhe, SVCTHREAD *thread)
248184588Sdfr{
249201899Smarius
250261054Smav	mtx_assert(fhe->mtx, MA_OWNED);
251261054Smav	thread->st_p2 = 0;
252184588Sdfr	LIST_INSERT_HEAD(&fhe->threads, thread, st_alink);
253184588Sdfr	fhe->num_threads++;
254184588Sdfr}
255184588Sdfr
256184588Sdfrstatic void
257184588Sdfrfha_hash_entry_remove_thread(struct fha_hash_entry *fhe, SVCTHREAD *thread)
258184588Sdfr{
259184588Sdfr
260261054Smav	mtx_assert(fhe->mtx, MA_OWNED);
261261054Smav	KASSERT(thread->st_p2 == 0,
262261054Smav	    ("%d reqs on removed thread %p", thread->st_p2, thread));
263184588Sdfr	LIST_REMOVE(thread, st_alink);
264184588Sdfr	fhe->num_threads--;
265184588Sdfr}
266184588Sdfr
267201899Smarius/*
268184588Sdfr * Account for an ongoing operation associated with this file.
269184588Sdfr */
270184588Sdfrstatic void
271184588Sdfrfha_hash_entry_add_op(struct fha_hash_entry *fhe, int locktype, int count)
272184588Sdfr{
273184588Sdfr
274261054Smav	mtx_assert(fhe->mtx, MA_OWNED);
275184588Sdfr	if (LK_EXCLUSIVE == locktype)
276249592Sken		fhe->num_exclusive += count;
277184588Sdfr	else
278249592Sken		fhe->num_rw += count;
279184588Sdfr}
280184588Sdfr
281201899Smarius/*
282184588Sdfr * Get the service thread currently associated with the fhe that is
283184588Sdfr * appropriate to handle this operation.
284184588Sdfr */
285267740Smavstatic SVCTHREAD *
286249592Skenfha_hash_entry_choose_thread(struct fha_params *softc,
287249592Sken    struct fha_hash_entry *fhe, struct fha_info *i, SVCTHREAD *this_thread)
288184588Sdfr{
289184588Sdfr	SVCTHREAD *thread, *min_thread = NULL;
290249592Sken	SVCPOOL *pool;
291184588Sdfr	int req_count, min_count = 0;
292184588Sdfr	off_t offset1, offset2;
293184588Sdfr
294249592Sken	pool = *softc->pool;
295249592Sken
296184588Sdfr	LIST_FOREACH(thread, &fhe->threads, st_alink) {
297261054Smav		req_count = thread->st_p2;
298184588Sdfr
299184588Sdfr		/* If there are any writes in progress, use the first thread. */
300249592Sken		if (fhe->num_exclusive) {
301184588Sdfr#if 0
302201899Smarius			ITRACE_CURPROC(ITRACE_NFS, ITRACE_INFO,
303184588Sdfr			    "fha: %p(%d)w", thread, req_count);
304184588Sdfr#endif
305184588Sdfr			return (thread);
306184588Sdfr		}
307184588Sdfr
308322138Smav		/* Check whether we should consider locality. */
309322138Smav		if ((i->read && !softc->ctls.read) ||
310322138Smav		    (i->write && !softc->ctls.write))
311322138Smav			goto noloc;
312322138Smav
313201899Smarius		/*
314322138Smav		 * Check for locality, making sure that we won't
315201899Smarius		 * exceed our per-thread load limit in the process.
316184588Sdfr		 */
317249592Sken		offset1 = i->offset;
318261054Smav		offset2 = thread->st_p3;
319249592Sken
320249592Sken		if (((offset1 >= offset2)
321249592Sken		  && ((offset1 - offset2) < (1 << softc->ctls.bin_shift)))
322249592Sken		 || ((offset2 > offset1)
323249592Sken		  && ((offset2 - offset1) < (1 << softc->ctls.bin_shift)))) {
324249592Sken			if ((softc->ctls.max_reqs_per_nfsd == 0) ||
325249592Sken			    (req_count < softc->ctls.max_reqs_per_nfsd)) {
326184588Sdfr#if 0
327201899Smarius				ITRACE_CURPROC(ITRACE_NFS, ITRACE_INFO,
328184588Sdfr				    "fha: %p(%d)r", thread, req_count);
329184588Sdfr#endif
330184588Sdfr				return (thread);
331184588Sdfr			}
332184588Sdfr		}
333184588Sdfr
334322138Smavnoloc:
335201899Smarius		/*
336184588Sdfr		 * We don't have a locality match, so skip this thread,
337201899Smarius		 * but keep track of the most attractive thread in case
338184588Sdfr		 * we need to come back to it later.
339184588Sdfr		 */
340184588Sdfr#if 0
341201899Smarius		ITRACE_CURPROC(ITRACE_NFS, ITRACE_INFO,
342201899Smarius		    "fha: %p(%d)s off1 %llu off2 %llu", thread,
343184588Sdfr		    req_count, offset1, offset2);
344184588Sdfr#endif
345184588Sdfr		if ((min_thread == NULL) || (req_count < min_count)) {
346184588Sdfr			min_count = req_count;
347184588Sdfr			min_thread = thread;
348184588Sdfr		}
349184588Sdfr	}
350184588Sdfr
351201899Smarius	/*
352201899Smarius	 * We didn't find a good match yet.  See if we can add
353184588Sdfr	 * a new thread to this file handle entry's thread list.
354184588Sdfr	 */
355249592Sken	if ((softc->ctls.max_nfsds_per_fh == 0) ||
356249592Sken	    (fhe->num_threads < softc->ctls.max_nfsds_per_fh)) {
357261054Smav		thread = this_thread;
358184588Sdfr#if 0
359261054Smav		ITRACE_CURPROC(ITRACE_NFS, ITRACE_INFO,
360261054Smav		    "fha: %p(%d)t", thread, thread->st_p2);
361184588Sdfr#endif
362184588Sdfr		fha_hash_entry_add_thread(fhe, thread);
363184588Sdfr	} else {
364201899Smarius		/*
365201899Smarius		 * We don't want to use any more threads for this file, so
366184588Sdfr		 * go back to the most attractive nfsd we're already using.
367184588Sdfr		 */
368184588Sdfr		thread = min_thread;
369184588Sdfr	}
370184588Sdfr
371184588Sdfr	return (thread);
372184588Sdfr}
373184588Sdfr
374201899Smarius/*
375201899Smarius * After getting a request, try to assign it to some thread.  Usually we
376184588Sdfr * handle it ourselves.
377184588Sdfr */
378184588SdfrSVCTHREAD *
379249592Skenfha_assign(SVCTHREAD *this_thread, struct svc_req *req,
380249592Sken    struct fha_params *softc)
381184588Sdfr{
382184588Sdfr	SVCTHREAD *thread;
383184588Sdfr	struct fha_info i;
384184588Sdfr	struct fha_hash_entry *fhe;
385249592Sken	struct fha_callbacks *cb;
386184588Sdfr
387249592Sken	cb = &softc->callbacks;
388249592Sken
389249592Sken	/* Check to see whether we're enabled. */
390249592Sken	if (softc->ctls.enable == 0)
391261054Smav		goto thist;
392249592Sken
393184588Sdfr	/*
394184588Sdfr	 * Only do placement if this is an NFS request.
395184588Sdfr	 */
396184588Sdfr	if (req->rq_prog != NFS_PROG)
397261054Smav		goto thist;
398184588Sdfr
399184588Sdfr	if (req->rq_vers != 2 && req->rq_vers != 3)
400261054Smav		goto thist;
401184588Sdfr
402249592Sken	fha_extract_info(req, &i, cb);
403184588Sdfr
404201899Smarius	/*
405201899Smarius	 * We save the offset associated with this request for later
406184588Sdfr	 * nfsd matching.
407184588Sdfr	 */
408249592Sken	fhe = fha_hash_entry_lookup(softc, i.fh);
409184588Sdfr	req->rq_p1 = fhe;
410184588Sdfr	req->rq_p2 = i.locktype;
411184588Sdfr	req->rq_p3 = i.offset;
412201899Smarius
413201899Smarius	/*
414184588Sdfr	 * Choose a thread, taking into consideration locality, thread load,
415184588Sdfr	 * and the number of threads already working on this file.
416184588Sdfr	 */
417249592Sken	thread = fha_hash_entry_choose_thread(softc, fhe, &i, this_thread);
418184588Sdfr	KASSERT(thread, ("fha_assign: NULL thread!"));
419184588Sdfr	fha_hash_entry_add_op(fhe, i.locktype, 1);
420261054Smav	thread->st_p2++;
421261054Smav	thread->st_p3 = i.offset;
422184588Sdfr
423261054Smav	/*
424261054Smav	 * Grab the pool lock here to not let chosen thread go away before
425261054Smav	 * the new request inserted to its queue while we drop fhe lock.
426261054Smav	 */
427267740Smav	mtx_lock(&thread->st_lock);
428261054Smav	mtx_unlock(fhe->mtx);
429261054Smav
430184588Sdfr	return (thread);
431261054Smavthist:
432261054Smav	req->rq_p1 = NULL;
433267740Smav	mtx_lock(&this_thread->st_lock);
434261054Smav	return (this_thread);
435184588Sdfr}
436184588Sdfr
437201899Smarius/*
438201899Smarius * Called when we're done with an operation.  The request has already
439184588Sdfr * been de-queued.
440184588Sdfr */
441184588Sdfrvoid
442184588Sdfrfha_nd_complete(SVCTHREAD *thread, struct svc_req *req)
443184588Sdfr{
444184588Sdfr	struct fha_hash_entry *fhe = req->rq_p1;
445261054Smav	struct mtx *mtx;
446184588Sdfr
447184588Sdfr	/*
448184588Sdfr	 * This may be called for reqs that didn't go through
449184588Sdfr	 * fha_assign (e.g. extra NULL ops used for RPCSEC_GSS.
450184588Sdfr	 */
451184588Sdfr	if (!fhe)
452184588Sdfr		return;
453184588Sdfr
454261054Smav	mtx = fhe->mtx;
455261054Smav	mtx_lock(mtx);
456184588Sdfr	fha_hash_entry_add_op(fhe, req->rq_p2, -1);
457261054Smav	thread->st_p2--;
458261054Smav	KASSERT(thread->st_p2 >= 0, ("Negative request count %d on %p",
459261054Smav	    thread->st_p2, thread));
460261054Smav	if (thread->st_p2 == 0) {
461184588Sdfr		fha_hash_entry_remove_thread(fhe, thread);
462249592Sken		if (0 == fhe->num_rw + fhe->num_exclusive)
463184588Sdfr			fha_hash_entry_remove(fhe);
464184588Sdfr	}
465261054Smav	mtx_unlock(mtx);
466184588Sdfr}
467184588Sdfr
468249592Skenint
469249592Skenfhe_stats_sysctl(SYSCTL_HANDLER_ARGS, struct fha_params *softc)
470184588Sdfr{
471267753Smav	int error, i;
472184588Sdfr	struct sbuf sb;
473184588Sdfr	struct fha_hash_entry *fhe;
474267753Smav	bool_t first, hfirst;
475184588Sdfr	SVCTHREAD *thread;
476249592Sken	SVCPOOL *pool;
477184588Sdfr
478267753Smav	sbuf_new(&sb, NULL, 65536, SBUF_FIXEDLEN);
479184588Sdfr
480249592Sken	pool = NULL;
481249592Sken
482249592Sken	if (!*softc->pool) {
483184588Sdfr		sbuf_printf(&sb, "NFSD not running\n");
484184588Sdfr		goto out;
485184588Sdfr	}
486249592Sken	pool = *softc->pool;
487184588Sdfr
488261054Smav	for (i = 0; i < FHA_HASH_SIZE; i++)
489261054Smav		if (!LIST_EMPTY(&softc->fha_hash[i].list))
490267753Smav			break;
491184588Sdfr
492267753Smav	if (i == FHA_HASH_SIZE) {
493184588Sdfr		sbuf_printf(&sb, "No file handle entries.\n");
494184588Sdfr		goto out;
495184588Sdfr	}
496184588Sdfr
497267753Smav	hfirst = TRUE;
498267753Smav	for (; i < FHA_HASH_SIZE; i++) {
499261054Smav		mtx_lock(&softc->fha_hash[i].mtx);
500267753Smav		if (LIST_EMPTY(&softc->fha_hash[i].list)) {
501267753Smav			mtx_unlock(&softc->fha_hash[i].mtx);
502267753Smav			continue;
503267753Smav		}
504267753Smav		sbuf_printf(&sb, "%shash %d: {\n", hfirst ? "" : ", ", i);
505267753Smav		first = TRUE;
506261054Smav		LIST_FOREACH(fhe, &softc->fha_hash[i].list, link) {
507267753Smav			sbuf_printf(&sb, "%sfhe %p: {\n", first ? "  " : ", ", fhe);
508184588Sdfr
509184588Sdfr			sbuf_printf(&sb, "    fh: %ju\n", (uintmax_t) fhe->fh);
510267753Smav			sbuf_printf(&sb, "    num_rw/exclusive: %d/%d\n",
511267753Smav			    fhe->num_rw, fhe->num_exclusive);
512184588Sdfr			sbuf_printf(&sb, "    num_threads: %d\n", fhe->num_threads);
513184588Sdfr
514184588Sdfr			LIST_FOREACH(thread, &fhe->threads, st_alink) {
515267753Smav				sbuf_printf(&sb, "      thread %p offset %ju "
516267753Smav				    "reqs %d\n", thread,
517261054Smav				    thread->st_p3, thread->st_p2);
518184588Sdfr			}
519184588Sdfr
520267753Smav			sbuf_printf(&sb, "  }");
521184588Sdfr			first = FALSE;
522184588Sdfr		}
523267753Smav		sbuf_printf(&sb, "\n}");
524261054Smav		mtx_unlock(&softc->fha_hash[i].mtx);
525267753Smav		hfirst = FALSE;
526184588Sdfr	}
527184588Sdfr
528184588Sdfr out:
529184588Sdfr	sbuf_trim(&sb);
530184588Sdfr	sbuf_finish(&sb);
531184588Sdfr	error = sysctl_handle_string(oidp, sbuf_data(&sb), sbuf_len(&sb), req);
532184588Sdfr	sbuf_delete(&sb);
533184588Sdfr	return (error);
534184588Sdfr}
535