xref: /illumos-gate/usr/src/uts/common/fs/nfs/nfs_auth.c (revision 98cadd320d4521e7438bc624f89adef498589add)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2014 Nexenta Systems, Inc.  All rights reserved.
24  * Copyright (c) 1995, 2010, Oracle and/or its affiliates. All rights reserved.
25  */
26 
27 #include <sys/param.h>
28 #include <sys/errno.h>
29 #include <sys/vfs.h>
30 #include <sys/vnode.h>
31 #include <sys/cred.h>
32 #include <sys/cmn_err.h>
33 #include <sys/systm.h>
34 #include <sys/kmem.h>
35 #include <sys/pathname.h>
36 #include <sys/utsname.h>
37 #include <sys/debug.h>
38 #include <sys/door.h>
39 #include <sys/sdt.h>
40 #include <sys/thread.h>
41 
42 #include <rpc/types.h>
43 #include <rpc/auth.h>
44 #include <rpc/clnt.h>
45 
46 #include <nfs/nfs.h>
47 #include <nfs/export.h>
48 #include <nfs/nfs_clnt.h>
49 #include <nfs/auth.h>
50 
51 #define	EQADDR(a1, a2)  \
52 	(bcmp((char *)(a1)->buf, (char *)(a2)->buf, (a1)->len) == 0 && \
53 	(a1)->len == (a2)->len)
54 
55 static struct knetconfig auth_knconf;
56 static servinfo_t svp;
57 static clinfo_t ci;
58 
59 static struct kmem_cache *exi_cache_handle;
60 static void exi_cache_reclaim(void *);
61 static void exi_cache_trim(struct exportinfo *exi);
62 
63 extern pri_t minclsyspri;
64 
65 int nfsauth_cache_hit;
66 int nfsauth_cache_miss;
67 int nfsauth_cache_refresh;
68 int nfsauth_cache_reclaim;
69 
70 /*
71  * The lifetime of an auth cache entry:
72  * ------------------------------------
73  *
74  * An auth cache entry is created with both the auth_time
75  * and auth_freshness times set to the current time.
76  *
77  * Upon every client access which results in a hit, the
78  * auth_time will be updated.
79  *
80  * If a client access determines that the auth_freshness
81  * indicates that the entry is STALE, then it will be
82  * refreshed. Note that this will explicitly reset
83  * auth_time.
84  *
85  * When the REFRESH successfully occurs, then the
86  * auth_freshness is updated.
87  *
88  * There are two ways for an entry to leave the cache:
89  *
90  * 1) Purged by an action on the export (remove or changed)
91  * 2) Memory backpressure from the kernel (check against NFSAUTH_CACHE_TRIM)
92  *
93  * For 2) we check the timeout value against auth_time.
94  */
95 
96 /*
97  * Number of seconds until we mark for refresh an auth cache entry.
98  */
99 #define	NFSAUTH_CACHE_REFRESH 600
100 
101 /*
102  * Number of idle seconds until we yield to backpressure
103  * to trim a cache entry.
104  */
105 #define	NFSAUTH_CACHE_TRIM 3600
106 
107 /*
108  * While we could encapuslate the exi_list inside the
109  * exi structure, we can't do that for the auth_list.
110  * So, to keep things looking clean, we keep them both
111  * in these external lists.
112  */
113 typedef struct refreshq_exi_node {
114 	struct exportinfo	*ren_exi;
115 	list_t			ren_authlist;
116 	list_node_t		ren_node;
117 } refreshq_exi_node_t;
118 
119 typedef struct refreshq_auth_node {
120 	struct auth_cache	*ran_auth;
121 	list_node_t		ran_node;
122 } refreshq_auth_node_t;
123 
124 /*
125  * Used to manipulate things on the refreshq_queue.
126  * Note that the refresh thread will effectively
127  * pop a node off of the queue, at which point it
128  * will no longer need to hold the mutex.
129  */
130 static kmutex_t refreshq_lock;
131 static list_t refreshq_queue;
132 static kcondvar_t refreshq_cv;
133 
134 /*
135  * A list_t would be overkill. These are auth_cache
136  * entries which are no longer linked to an exi.
137  * It should be the case that all of their states
138  * are NFS_AUTH_INVALID.
139  *
140  * I.e., the only way to be put on this list is
141  * iff their state indicated that they had been placed
142  * on the refreshq_queue.
143  *
144  * Note that while there is no link from the exi or
145  * back to the exi, the exi can not go away until
146  * these entries are harvested.
147  */
148 static struct auth_cache	*refreshq_dead_entries;
149 
150 /*
151  * If there is ever a problem with loading the
152  * module, then nfsauth_fini() needs to be called
153  * to remove state. In that event, since the
154  * refreshq thread has been started, they need to
155  * work together to get rid of state.
156  */
157 typedef enum nfsauth_refreshq_thread_state {
158 	REFRESHQ_THREAD_RUNNING,
159 	REFRESHQ_THREAD_FINI_REQ,
160 	REFRESHQ_THREAD_HALTED
161 } nfsauth_refreshq_thread_state_t;
162 
163 nfsauth_refreshq_thread_state_t
164 refreshq_thread_state = REFRESHQ_THREAD_HALTED;
165 
166 static void nfsauth_free_node(struct auth_cache *);
167 static void nfsauth_remove_dead_entry(struct auth_cache *);
168 static void nfsauth_refresh_thread(void);
169 
170 /*
171  * mountd is a server-side only daemon. This will need to be
172  * revisited if the NFS server is ever made zones-aware.
173  */
174 kmutex_t	mountd_lock;
175 door_handle_t   mountd_dh;
176 
177 void
178 mountd_args(uint_t did)
179 {
180 	mutex_enter(&mountd_lock);
181 	if (mountd_dh != NULL)
182 		door_ki_rele(mountd_dh);
183 	mountd_dh = door_ki_lookup(did);
184 	mutex_exit(&mountd_lock);
185 }
186 
187 void
188 nfsauth_init(void)
189 {
190 	/*
191 	 * mountd can be restarted by smf(5). We need to make sure
192 	 * the updated door handle will safely make it to mountd_dh
193 	 */
194 	mutex_init(&mountd_lock, NULL, MUTEX_DEFAULT, NULL);
195 
196 	mutex_init(&refreshq_lock, NULL, MUTEX_DEFAULT, NULL);
197 	list_create(&refreshq_queue, sizeof (refreshq_exi_node_t),
198 	    offsetof(refreshq_exi_node_t, ren_node));
199 	refreshq_dead_entries = NULL;
200 
201 	cv_init(&refreshq_cv, NULL, CV_DEFAULT, NULL);
202 
203 	/*
204 	 * Allocate nfsauth cache handle
205 	 */
206 	exi_cache_handle = kmem_cache_create("exi_cache_handle",
207 	    sizeof (struct auth_cache), 0, NULL, NULL,
208 	    exi_cache_reclaim, NULL, NULL, 0);
209 
210 	refreshq_thread_state = REFRESHQ_THREAD_RUNNING;
211 	(void) zthread_create(NULL, 0, nfsauth_refresh_thread,
212 	    NULL, 0, minclsyspri);
213 }
214 
215 /*
216  * Finalization routine for nfsauth. It is important to call this routine
217  * before destroying the exported_lock.
218  */
219 void
220 nfsauth_fini(void)
221 {
222 	refreshq_exi_node_t	*ren;
223 	refreshq_auth_node_t	*ran;
224 	struct auth_cache	*p;
225 	struct auth_cache	*auth_next;
226 
227 	/*
228 	 * Prevent the refreshq_thread from getting new
229 	 * work.
230 	 */
231 	mutex_enter(&refreshq_lock);
232 	if (refreshq_thread_state != REFRESHQ_THREAD_HALTED) {
233 		refreshq_thread_state = REFRESHQ_THREAD_FINI_REQ;
234 		cv_broadcast(&refreshq_cv);
235 
236 		/*
237 		 * Also, wait for nfsauth_refresh_thread() to exit.
238 		 */
239 		while (refreshq_thread_state != REFRESHQ_THREAD_HALTED) {
240 			cv_wait(&refreshq_cv, &refreshq_lock);
241 		}
242 	}
243 
244 	/*
245 	 * Walk the exi_list and in turn, walk the
246 	 * auth_lists.
247 	 */
248 	while ((ren = list_remove_head(&refreshq_queue))) {
249 		while ((ran = list_remove_head(&ren->ren_authlist))) {
250 			kmem_free(ran, sizeof (refreshq_auth_node_t));
251 		}
252 
253 		list_destroy(&ren->ren_authlist);
254 		exi_rele(ren->ren_exi);
255 		kmem_free(ren, sizeof (refreshq_exi_node_t));
256 	}
257 
258 	/*
259 	 * Okay, now that the lists are deleted, we
260 	 * need to see if there are any dead entries
261 	 * to harvest.
262 	 */
263 	for (p = refreshq_dead_entries; p != NULL; p = auth_next) {
264 		auth_next = p->auth_next;
265 		nfsauth_free_node(p);
266 	}
267 
268 	mutex_exit(&refreshq_lock);
269 
270 	list_destroy(&refreshq_queue);
271 
272 	cv_destroy(&refreshq_cv);
273 	mutex_destroy(&refreshq_lock);
274 
275 	mutex_destroy(&mountd_lock);
276 
277 	/*
278 	 * Deallocate nfsauth cache handle
279 	 */
280 	kmem_cache_destroy(exi_cache_handle);
281 }
282 
283 /*
284  * Convert the address in a netbuf to
285  * a hash index for the auth_cache table.
286  */
287 static int
288 hash(struct netbuf *a)
289 {
290 	int i, h = 0;
291 
292 	for (i = 0; i < a->len; i++)
293 		h ^= a->buf[i];
294 
295 	return (h & (AUTH_TABLESIZE - 1));
296 }
297 
298 /*
299  * Mask out the components of an
300  * address that do not identify
301  * a host. For socket addresses the
302  * masking gets rid of the port number.
303  */
304 static void
305 addrmask(struct netbuf *addr, struct netbuf *mask)
306 {
307 	int i;
308 
309 	for (i = 0; i < addr->len; i++)
310 		addr->buf[i] &= mask->buf[i];
311 }
312 
313 /*
314  * nfsauth4_access is used for NFS V4 auth checking. Besides doing
315  * the common nfsauth_access(), it will check if the client can
316  * have a limited access to this vnode even if the security flavor
317  * used does not meet the policy.
318  */
319 int
320 nfsauth4_access(struct exportinfo *exi, vnode_t *vp, struct svc_req *req,
321     cred_t *cr, uid_t *uid, gid_t *gid, uint_t *ngids, gid_t **gids)
322 {
323 	int access;
324 
325 	access = nfsauth_access(exi, req, cr, uid, gid, ngids, gids);
326 
327 	/*
328 	 * There are cases that the server needs to allow the client
329 	 * to have a limited view.
330 	 *
331 	 * e.g.
332 	 * /export is shared as "sec=sys,rw=dfs-test-4,sec=krb5,rw"
333 	 * /export/home is shared as "sec=sys,rw"
334 	 *
335 	 * When the client mounts /export with sec=sys, the client
336 	 * would get a limited view with RO access on /export to see
337 	 * "home" only because the client is allowed to access
338 	 * /export/home with auth_sys.
339 	 */
340 	if (access & NFSAUTH_DENIED || access & NFSAUTH_WRONGSEC) {
341 		/*
342 		 * Allow ro permission with LIMITED view if there is a
343 		 * sub-dir exported under vp.
344 		 */
345 		if (has_visible(exi, vp))
346 			return (NFSAUTH_LIMITED);
347 	}
348 
349 	return (access);
350 }
351 
352 static void
353 sys_log(const char *msg)
354 {
355 	static time_t	tstamp = 0;
356 	time_t		now;
357 
358 	/*
359 	 * msg is shown (at most) once per minute
360 	 */
361 	now = gethrestime_sec();
362 	if ((tstamp + 60) < now) {
363 		tstamp = now;
364 		cmn_err(CE_WARN, msg);
365 	}
366 }
367 
368 /*
369  * Callup to the mountd to get access information in the kernel.
370  */
371 static bool_t
372 nfsauth_retrieve(struct exportinfo *exi, char *req_netid, int flavor,
373     struct netbuf *addr, int *access, uid_t clnt_uid, gid_t clnt_gid,
374     uint_t clnt_gids_cnt, const gid_t *clnt_gids, uid_t *srv_uid,
375     gid_t *srv_gid, uint_t *srv_gids_cnt, gid_t **srv_gids)
376 {
377 	varg_t			  varg = {0};
378 	nfsauth_res_t		  res = {0};
379 	XDR			  xdrs;
380 	size_t			  absz;
381 	caddr_t			  abuf;
382 	int			  last = 0;
383 	door_arg_t		  da;
384 	door_info_t		  di;
385 	door_handle_t		  dh;
386 	uint_t			  ntries = 0;
387 
388 	/*
389 	 * No entry in the cache for this client/flavor
390 	 * so we need to call the nfsauth service in the
391 	 * mount daemon.
392 	 */
393 
394 	varg.vers = V_PROTO;
395 	varg.arg_u.arg.cmd = NFSAUTH_ACCESS;
396 	varg.arg_u.arg.areq.req_client.n_len = addr->len;
397 	varg.arg_u.arg.areq.req_client.n_bytes = addr->buf;
398 	varg.arg_u.arg.areq.req_netid = req_netid;
399 	varg.arg_u.arg.areq.req_path = exi->exi_export.ex_path;
400 	varg.arg_u.arg.areq.req_flavor = flavor;
401 	varg.arg_u.arg.areq.req_clnt_uid = clnt_uid;
402 	varg.arg_u.arg.areq.req_clnt_gid = clnt_gid;
403 	varg.arg_u.arg.areq.req_clnt_gids.len = clnt_gids_cnt;
404 	varg.arg_u.arg.areq.req_clnt_gids.val = (gid_t *)clnt_gids;
405 
406 	DTRACE_PROBE1(nfsserv__func__nfsauth__varg, varg_t *, &varg);
407 
408 	/*
409 	 * Setup the XDR stream for encoding the arguments. Notice that
410 	 * in addition to the args having variable fields (req_netid and
411 	 * req_path), the argument data structure is itself versioned,
412 	 * so we need to make sure we can size the arguments buffer
413 	 * appropriately to encode all the args. If we can't get sizing
414 	 * info _or_ properly encode the arguments, there's really no
415 	 * point in continuting, so we fail the request.
416 	 */
417 	if ((absz = xdr_sizeof(xdr_varg, &varg)) == 0) {
418 		*access = NFSAUTH_DENIED;
419 		return (FALSE);
420 	}
421 
422 	abuf = (caddr_t)kmem_alloc(absz, KM_SLEEP);
423 	xdrmem_create(&xdrs, abuf, absz, XDR_ENCODE);
424 	if (!xdr_varg(&xdrs, &varg)) {
425 		XDR_DESTROY(&xdrs);
426 		goto fail;
427 	}
428 	XDR_DESTROY(&xdrs);
429 
430 	/*
431 	 * Prepare the door arguments
432 	 *
433 	 * We don't know the size of the message the daemon
434 	 * will pass back to us.  By setting rbuf to NULL,
435 	 * we force the door code to allocate a buf of the
436 	 * appropriate size.  We must set rsize > 0, however,
437 	 * else the door code acts as if no response was
438 	 * expected and doesn't pass the data to us.
439 	 */
440 	da.data_ptr = (char *)abuf;
441 	da.data_size = absz;
442 	da.desc_ptr = NULL;
443 	da.desc_num = 0;
444 	da.rbuf = NULL;
445 	da.rsize = 1;
446 
447 retry:
448 	mutex_enter(&mountd_lock);
449 	dh = mountd_dh;
450 	if (dh != NULL)
451 		door_ki_hold(dh);
452 	mutex_exit(&mountd_lock);
453 
454 	if (dh == NULL) {
455 		/*
456 		 * The rendezvous point has not been established yet!
457 		 * This could mean that either mountd(1m) has not yet
458 		 * been started or that _this_ routine nuked the door
459 		 * handle after receiving an EINTR for a REVOKED door.
460 		 *
461 		 * Returning NFSAUTH_DROP will cause the NFS client
462 		 * to retransmit the request, so let's try to be more
463 		 * rescillient and attempt for ntries before we bail.
464 		 */
465 		if (++ntries % NFSAUTH_DR_TRYCNT) {
466 			delay(hz);
467 			goto retry;
468 		}
469 
470 		kmem_free(abuf, absz);
471 
472 		sys_log("nfsauth: mountd has not established door");
473 		*access = NFSAUTH_DROP;
474 		return (FALSE);
475 	}
476 
477 	ntries = 0;
478 
479 	/*
480 	 * Now that we've got what we need, place the call.
481 	 */
482 	switch (door_ki_upcall_limited(dh, &da, NULL, SIZE_MAX, 0)) {
483 	case 0:				/* Success */
484 		door_ki_rele(dh);
485 
486 		if (da.data_ptr == NULL && da.data_size == 0) {
487 			/*
488 			 * The door_return that contained the data
489 			 * failed! We're here because of the 2nd
490 			 * door_return (w/o data) such that we can
491 			 * get control of the thread (and exit
492 			 * gracefully).
493 			 */
494 			DTRACE_PROBE1(nfsserv__func__nfsauth__door__nil,
495 			    door_arg_t *, &da);
496 			goto fail;
497 		}
498 
499 		break;
500 
501 	case EAGAIN:
502 		/*
503 		 * Server out of resources; back off for a bit
504 		 */
505 		door_ki_rele(dh);
506 		delay(hz);
507 		goto retry;
508 		/* NOTREACHED */
509 
510 	case EINTR:
511 		if (!door_ki_info(dh, &di)) {
512 			door_ki_rele(dh);
513 
514 			if (di.di_attributes & DOOR_REVOKED) {
515 				/*
516 				 * The server barfed and revoked
517 				 * the (existing) door on us; we
518 				 * want to wait to give smf(5) a
519 				 * chance to restart mountd(1m)
520 				 * and establish a new door handle.
521 				 */
522 				mutex_enter(&mountd_lock);
523 				if (dh == mountd_dh) {
524 					door_ki_rele(mountd_dh);
525 					mountd_dh = NULL;
526 				}
527 				mutex_exit(&mountd_lock);
528 				delay(hz);
529 				goto retry;
530 			}
531 			/*
532 			 * If the door was _not_ revoked on us,
533 			 * then more than likely we took an INTR,
534 			 * so we need to fail the operation.
535 			 */
536 			goto fail;
537 		}
538 		/*
539 		 * The only failure that can occur from getting
540 		 * the door info is EINVAL, so we let the code
541 		 * below handle it.
542 		 */
543 		/* FALLTHROUGH */
544 
545 	case EBADF:
546 	case EINVAL:
547 	default:
548 		/*
549 		 * If we have a stale door handle, give smf a last
550 		 * chance to start it by sleeping for a little bit.
551 		 * If we're still hosed, we'll fail the call.
552 		 *
553 		 * Since we're going to reacquire the door handle
554 		 * upon the retry, we opt to sleep for a bit and
555 		 * _not_ to clear mountd_dh. If mountd restarted
556 		 * and was able to set mountd_dh, we should see
557 		 * the new instance; if not, we won't get caught
558 		 * up in the retry/DELAY loop.
559 		 */
560 		door_ki_rele(dh);
561 		if (!last) {
562 			delay(hz);
563 			last++;
564 			goto retry;
565 		}
566 		sys_log("nfsauth: stale mountd door handle");
567 		goto fail;
568 	}
569 
570 	ASSERT(da.rbuf != NULL);
571 
572 	/*
573 	 * No door errors encountered; setup the XDR stream for decoding
574 	 * the results. If we fail to decode the results, we've got no
575 	 * other recourse than to fail the request.
576 	 */
577 	xdrmem_create(&xdrs, da.rbuf, da.rsize, XDR_DECODE);
578 	if (!xdr_nfsauth_res(&xdrs, &res)) {
579 		xdr_free(xdr_nfsauth_res, (char *)&res);
580 		XDR_DESTROY(&xdrs);
581 		kmem_free(da.rbuf, da.rsize);
582 		goto fail;
583 	}
584 	XDR_DESTROY(&xdrs);
585 	kmem_free(da.rbuf, da.rsize);
586 
587 	DTRACE_PROBE1(nfsserv__func__nfsauth__results, nfsauth_res_t *, &res);
588 	switch (res.stat) {
589 		case NFSAUTH_DR_OKAY:
590 			*access = res.ares.auth_perm;
591 			*srv_uid = res.ares.auth_srv_uid;
592 			*srv_gid = res.ares.auth_srv_gid;
593 			*srv_gids_cnt = res.ares.auth_srv_gids.len;
594 			*srv_gids = kmem_alloc(*srv_gids_cnt * sizeof (gid_t),
595 			    KM_SLEEP);
596 			bcopy(res.ares.auth_srv_gids.val, *srv_gids,
597 			    *srv_gids_cnt * sizeof (gid_t));
598 			break;
599 
600 		case NFSAUTH_DR_EFAIL:
601 		case NFSAUTH_DR_DECERR:
602 		case NFSAUTH_DR_BADCMD:
603 		default:
604 			xdr_free(xdr_nfsauth_res, (char *)&res);
605 fail:
606 			*access = NFSAUTH_DENIED;
607 			kmem_free(abuf, absz);
608 			return (FALSE);
609 			/* NOTREACHED */
610 	}
611 
612 	xdr_free(xdr_nfsauth_res, (char *)&res);
613 	kmem_free(abuf, absz);
614 
615 	return (TRUE);
616 }
617 
618 static void
619 nfsauth_refresh_thread(void)
620 {
621 	refreshq_exi_node_t	*ren;
622 	refreshq_auth_node_t	*ran;
623 
624 	struct exportinfo	*exi;
625 
626 	int			access;
627 	bool_t			retrieval;
628 
629 	callb_cpr_t		cprinfo;
630 
631 	CALLB_CPR_INIT(&cprinfo, &refreshq_lock, callb_generic_cpr,
632 	    "nfsauth_refresh");
633 
634 	for (;;) {
635 		mutex_enter(&refreshq_lock);
636 		if (refreshq_thread_state != REFRESHQ_THREAD_RUNNING) {
637 			/* Keep the hold on the lock! */
638 			break;
639 		}
640 
641 		ren = list_remove_head(&refreshq_queue);
642 		if (ren == NULL) {
643 			CALLB_CPR_SAFE_BEGIN(&cprinfo);
644 			cv_wait(&refreshq_cv, &refreshq_lock);
645 			CALLB_CPR_SAFE_END(&cprinfo, &refreshq_lock);
646 			mutex_exit(&refreshq_lock);
647 			continue;
648 		}
649 		mutex_exit(&refreshq_lock);
650 
651 		exi = ren->ren_exi;
652 		ASSERT(exi != NULL);
653 
654 		/*
655 		 * Since the ren was removed from the refreshq_queue above,
656 		 * this is the only thread aware about the ren existence, so we
657 		 * have the exclusive ownership of it and we do not need to
658 		 * protect it by any lock.
659 		 */
660 		while ((ran = list_remove_head(&ren->ren_authlist))) {
661 
662 			uint_t ngids;
663 			gid_t *gids;
664 			struct auth_cache *p = ran->ran_auth;
665 
666 			ASSERT(p != NULL);
667 			kmem_free(ran, sizeof (refreshq_auth_node_t));
668 
669 			/*
670 			 * We are shutting down. No need to refresh
671 			 * entries which are about to be nuked.
672 			 *
673 			 * So just throw them away until we are done
674 			 * with this exi node...
675 			 */
676 			if (refreshq_thread_state != REFRESHQ_THREAD_RUNNING)
677 				continue;
678 
679 			mutex_enter(&p->auth_lock);
680 
681 			/*
682 			 * Make sure the state is valid now that
683 			 * we have the lock. Note that once we
684 			 * change the state to NFS_AUTH_REFRESHING,
685 			 * no other thread will be able to work on
686 			 * this entry.
687 			 */
688 			if (p->auth_state != NFS_AUTH_STALE) {
689 				/*
690 				 * Once it goes INVALID, it can not
691 				 * change state.
692 				 */
693 				if (p->auth_state == NFS_AUTH_INVALID) {
694 					mutex_exit(&p->auth_lock);
695 					nfsauth_remove_dead_entry(p);
696 				} else
697 					mutex_exit(&p->auth_lock);
698 
699 				continue;
700 			}
701 
702 			p->auth_state = NFS_AUTH_REFRESHING;
703 			mutex_exit(&p->auth_lock);
704 
705 			DTRACE_PROBE2(nfsauth__debug__cache__refresh,
706 			    struct exportinfo *, exi,
707 			    struct auth_cache *, p);
708 
709 			/*
710 			 * The first caching of the access rights
711 			 * is done with the netid pulled out of the
712 			 * request from the client. All subsequent
713 			 * users of the cache may or may not have
714 			 * the same netid. It doesn't matter. So
715 			 * when we refresh, we simply use the netid
716 			 * of the request which triggered the
717 			 * refresh attempt.
718 			 */
719 			ASSERT(p->auth_netid != NULL);
720 
721 			retrieval = nfsauth_retrieve(exi, p->auth_netid,
722 			    p->auth_flavor, &p->auth_addr, &access,
723 			    p->auth_clnt_uid, p->auth_clnt_gid,
724 			    p->auth_clnt_ngids, p->auth_clnt_gids,
725 			    &p->auth_srv_uid, &p->auth_srv_gid, &ngids, &gids);
726 
727 			/*
728 			 * This can only be set in one other place
729 			 * and the state has to be NFS_AUTH_FRESH.
730 			 */
731 			kmem_free(p->auth_netid, strlen(p->auth_netid) + 1);
732 			p->auth_netid = NULL;
733 
734 			mutex_enter(&p->auth_lock);
735 			if (p->auth_state == NFS_AUTH_INVALID) {
736 				mutex_exit(&p->auth_lock);
737 				nfsauth_remove_dead_entry(p);
738 				if (retrieval == TRUE)
739 					kmem_free(gids, ngids * sizeof (gid_t));
740 			} else {
741 				/*
742 				 * If we got an error, do not reset the
743 				 * time. This will cause the next access
744 				 * check for the client to reschedule this
745 				 * node.
746 				 */
747 				if (retrieval == TRUE) {
748 					p->auth_access = access;
749 
750 					kmem_free(p->auth_srv_gids,
751 					    p->auth_srv_ngids * sizeof (gid_t));
752 					p->auth_srv_ngids = ngids;
753 					p->auth_srv_gids = gids;
754 
755 					p->auth_freshness = gethrestime_sec();
756 				}
757 				p->auth_state = NFS_AUTH_FRESH;
758 				mutex_exit(&p->auth_lock);
759 			}
760 		}
761 
762 		list_destroy(&ren->ren_authlist);
763 		exi_rele(ren->ren_exi);
764 		kmem_free(ren, sizeof (refreshq_exi_node_t));
765 	}
766 
767 	refreshq_thread_state = REFRESHQ_THREAD_HALTED;
768 	cv_broadcast(&refreshq_cv);
769 	CALLB_CPR_EXIT(&cprinfo);
770 	zthread_exit();
771 }
772 
773 /*
774  * Get the access information from the cache or callup to the mountd
775  * to get and cache the access information in the kernel.
776  */
777 static int
778 nfsauth_cache_get(struct exportinfo *exi, struct svc_req *req, int flavor,
779     cred_t *cr, uid_t *uid, gid_t *gid, uint_t *ngids, gid_t **gids)
780 {
781 	struct netbuf		*taddrmask;
782 	struct netbuf		addr;
783 	struct netbuf		*claddr;
784 	struct auth_cache	**head;
785 	struct auth_cache	*p;
786 	struct auth_cache	*prev;
787 	int			access;
788 	time_t			refresh;
789 
790 	refreshq_exi_node_t	*ren;
791 	refreshq_auth_node_t	*ran;
792 
793 	uid_t			tmpuid;
794 	gid_t			tmpgid;
795 	uint_t			tmpngids;
796 	gid_t			*tmpgids;
797 
798 	ASSERT(cr != NULL);
799 
800 	/*
801 	 * Now check whether this client already
802 	 * has an entry for this flavor in the cache
803 	 * for this export.
804 	 * Get the caller's address, mask off the
805 	 * parts of the address that do not identify
806 	 * the host (port number, etc), and then hash
807 	 * it to find the chain of cache entries.
808 	 */
809 
810 	claddr = svc_getrpccaller(req->rq_xprt);
811 	addr = *claddr;
812 	addr.buf = kmem_alloc(addr.len, KM_SLEEP);
813 	bcopy(claddr->buf, addr.buf, claddr->len);
814 	SVC_GETADDRMASK(req->rq_xprt, SVC_TATTR_ADDRMASK, (void **)&taddrmask);
815 	ASSERT(taddrmask != NULL);
816 	if (taddrmask)
817 		addrmask(&addr, taddrmask);
818 
819 	rw_enter(&exi->exi_cache_lock, RW_READER);
820 	head = &exi->exi_cache[hash(&addr)];
821 retry:
822 	prev = NULL;
823 	for (p = *head; p != NULL; p = p->auth_next) {
824 		if (EQADDR(&addr, &p->auth_addr) && flavor == p->auth_flavor &&
825 		    crgetuid(cr) == p->auth_clnt_uid &&
826 		    crgetgid(cr) == p->auth_clnt_gid)
827 			break;
828 		prev = p;
829 	}
830 
831 	if (p != NULL) {
832 		/*
833 		 * In a case the client's supplemental groups changed we need
834 		 * to discard the auth_cache entry and re-retrieve it.
835 		 */
836 		if (p->auth_clnt_ngids != crgetngroups(cr) ||
837 		    bcmp(p->auth_clnt_gids, crgetgroups(cr),
838 		    p->auth_clnt_ngids * sizeof (gid_t))) {
839 			struct auth_cache *next;
840 
841 			/*
842 			 * To remove the auth_cache entry from exi we need to
843 			 * hold the exi_cache_lock for write.  If we do not
844 			 * have it yet, we will try to upgrade, or re-lock as a
845 			 * last resort.  In a case of re-lock we will retry the
846 			 * search for the auth_cache entry because it might
847 			 * changed in the meantime.
848 			 */
849 			ASSERT(RW_LOCK_HELD(&exi->exi_cache_lock));
850 			if (rw_read_locked(&exi->exi_cache_lock) != 0) {
851 				if (rw_tryupgrade(&exi->exi_cache_lock) == 0) {
852 					rw_exit(&exi->exi_cache_lock);
853 					rw_enter(&exi->exi_cache_lock,
854 					    RW_WRITER);
855 
856 					goto retry;
857 				}
858 			}
859 
860 			/*
861 			 * Now, remove the entry from exi and free it, or place
862 			 * it at the dead list.
863 			 */
864 			next = p->auth_next;
865 			mutex_enter(&p->auth_lock);
866 			if (p->auth_state != NFS_AUTH_FRESH) {
867 				p->auth_state = NFS_AUTH_INVALID;
868 				mutex_exit(&p->auth_lock);
869 
870 				mutex_enter(&refreshq_lock);
871 				p->auth_next = refreshq_dead_entries;
872 				refreshq_dead_entries = p;
873 				mutex_exit(&refreshq_lock);
874 			} else {
875 				mutex_exit(&p->auth_lock);
876 				nfsauth_free_node(p);
877 			}
878 
879 			/*
880 			 * Finally, disconnect the entry from exi
881 			 */
882 			if (prev == NULL)
883 				*head = next;
884 			else
885 				prev->auth_next = next;
886 
887 			goto retrieve;
888 		}
889 
890 		/*
891 		 * If we hold the lock for write, downgrade
892 		 */
893 		if (rw_read_locked(&exi->exi_cache_lock) == 0)
894 			rw_downgrade(&exi->exi_cache_lock);
895 
896 		nfsauth_cache_hit++;
897 
898 		refresh = gethrestime_sec() - p->auth_freshness;
899 		DTRACE_PROBE2(nfsauth__debug__cache__hit,
900 		    int, nfsauth_cache_hit,
901 		    time_t, refresh);
902 
903 		mutex_enter(&p->auth_lock);
904 		if ((refresh > NFSAUTH_CACHE_REFRESH) &&
905 		    p->auth_state == NFS_AUTH_FRESH) {
906 			p->auth_state = NFS_AUTH_STALE;
907 			mutex_exit(&p->auth_lock);
908 
909 			ASSERT(p->auth_netid == NULL);
910 			p->auth_netid =
911 			    strdup(svc_getnetid(req->rq_xprt));
912 
913 			nfsauth_cache_refresh++;
914 
915 			DTRACE_PROBE3(nfsauth__debug__cache__stale,
916 			    struct exportinfo *, exi,
917 			    struct auth_cache *, p,
918 			    int, nfsauth_cache_refresh);
919 
920 			ran = kmem_alloc(sizeof (refreshq_auth_node_t),
921 			    KM_SLEEP);
922 			ran->ran_auth = p;
923 
924 			mutex_enter(&refreshq_lock);
925 			/*
926 			 * We should not add a work queue
927 			 * item if the thread is not
928 			 * accepting them.
929 			 */
930 			if (refreshq_thread_state == REFRESHQ_THREAD_RUNNING) {
931 				/*
932 				 * Is there an existing exi_list?
933 				 */
934 				for (ren = list_head(&refreshq_queue);
935 				    ren != NULL;
936 				    ren = list_next(&refreshq_queue, ren)) {
937 					if (ren->ren_exi == exi) {
938 						list_insert_tail(
939 						    &ren->ren_authlist, ran);
940 						break;
941 					}
942 				}
943 
944 				if (ren == NULL) {
945 					ren = kmem_alloc(
946 					    sizeof (refreshq_exi_node_t),
947 					    KM_SLEEP);
948 
949 					exi_hold(exi);
950 					ren->ren_exi = exi;
951 
952 					list_create(&ren->ren_authlist,
953 					    sizeof (refreshq_auth_node_t),
954 					    offsetof(refreshq_auth_node_t,
955 					    ran_node));
956 
957 					list_insert_tail(&ren->ren_authlist,
958 					    ran);
959 					list_insert_tail(&refreshq_queue, ren);
960 				}
961 
962 				cv_broadcast(&refreshq_cv);
963 			} else {
964 				kmem_free(ran, sizeof (refreshq_auth_node_t));
965 			}
966 
967 			mutex_exit(&refreshq_lock);
968 		} else {
969 			mutex_exit(&p->auth_lock);
970 		}
971 
972 		access = p->auth_access;
973 		if (uid != NULL)
974 			*uid = p->auth_srv_uid;
975 		if (gid != NULL)
976 			*gid = p->auth_srv_gid;
977 		if (ngids != NULL && gids != NULL) {
978 			*ngids = p->auth_srv_ngids;
979 			*gids = kmem_alloc(*ngids * sizeof (gid_t), KM_SLEEP);
980 			bcopy(p->auth_srv_gids, *gids, *ngids * sizeof (gid_t));
981 		}
982 
983 		p->auth_time = gethrestime_sec();
984 
985 		rw_exit(&exi->exi_cache_lock);
986 		kmem_free(addr.buf, addr.len);
987 
988 		return (access);
989 	}
990 
991 retrieve:
992 	rw_exit(&exi->exi_cache_lock);
993 
994 	nfsauth_cache_miss++;
995 
996 	if (!nfsauth_retrieve(exi, svc_getnetid(req->rq_xprt), flavor,
997 	    &addr, &access, crgetuid(cr), crgetgid(cr), crgetngroups(cr),
998 	    crgetgroups(cr), &tmpuid, &tmpgid, &tmpngids, &tmpgids)) {
999 		kmem_free(addr.buf, addr.len);
1000 		if (ngids != NULL && gids != NULL) {
1001 			*ngids = 0;
1002 			*gids = NULL;
1003 		}
1004 		return (access);
1005 	}
1006 
1007 	if (uid != NULL)
1008 		*uid = tmpuid;
1009 	if (gid != NULL)
1010 		*gid = tmpgid;
1011 	if (ngids != NULL && gids != NULL) {
1012 		*ngids = tmpngids;
1013 		*gids = tmpgids;
1014 
1015 		/*
1016 		 * We need a copy of gids for the auth_cache entry
1017 		 */
1018 		tmpgids = kmem_alloc(tmpngids * sizeof (gid_t), KM_NOSLEEP);
1019 		if (tmpgids != NULL)
1020 			bcopy(*gids, tmpgids, tmpngids * sizeof (gid_t));
1021 	}
1022 
1023 	/*
1024 	 * Now cache the result on the cache chain
1025 	 * for this export (if there's enough memory)
1026 	 */
1027 	p = kmem_cache_alloc(exi_cache_handle, KM_NOSLEEP);
1028 	if (p != NULL)
1029 		p->auth_clnt_gids = kmem_alloc(
1030 		    crgetngroups(cr) * sizeof (gid_t), KM_NOSLEEP);
1031 	if (p != NULL && (tmpngids == 0 || tmpgids != NULL) &&
1032 	    (crgetngroups(cr) == 0 || p->auth_clnt_gids != NULL)) {
1033 		p->auth_addr = addr;
1034 		p->auth_flavor = flavor;
1035 		p->auth_clnt_uid = crgetuid(cr);
1036 		p->auth_clnt_gid = crgetgid(cr);
1037 		p->auth_clnt_ngids = crgetngroups(cr);
1038 		bcopy(crgetgroups(cr), p->auth_clnt_gids,
1039 		    p->auth_clnt_ngids * sizeof (gid_t));
1040 		p->auth_srv_uid = tmpuid;
1041 		p->auth_srv_gid = tmpgid;
1042 		p->auth_srv_ngids = tmpngids;
1043 		p->auth_srv_gids = tmpgids;
1044 		p->auth_access = access;
1045 		p->auth_time = p->auth_freshness = gethrestime_sec();
1046 		p->auth_state = NFS_AUTH_FRESH;
1047 		p->auth_netid = NULL;
1048 		mutex_init(&p->auth_lock, NULL, MUTEX_DEFAULT, NULL);
1049 
1050 		rw_enter(&exi->exi_cache_lock, RW_WRITER);
1051 		p->auth_next = *head;
1052 		*head = p;
1053 		rw_exit(&exi->exi_cache_lock);
1054 	} else {
1055 		kmem_free(addr.buf, addr.len);
1056 		if (tmpgids != NULL)
1057 			kmem_free(tmpgids, tmpngids * sizeof (gid_t));
1058 		if (p != NULL) {
1059 			if (p->auth_clnt_gids != NULL)
1060 				kmem_free(p->auth_clnt_gids,
1061 				    crgetngroups(cr) * sizeof (gid_t));
1062 			kmem_cache_free(exi_cache_handle, p);
1063 		}
1064 	}
1065 
1066 	return (access);
1067 }
1068 
1069 /*
1070  * Check if the requesting client has access to the filesystem with
1071  * a given nfs flavor number which is an explicitly shared flavor.
1072  */
1073 int
1074 nfsauth4_secinfo_access(struct exportinfo *exi, struct svc_req *req,
1075 			int flavor, int perm, cred_t *cr)
1076 {
1077 	int access;
1078 
1079 	if (! (perm & M_4SEC_EXPORTED)) {
1080 		return (NFSAUTH_DENIED);
1081 	}
1082 
1083 	/*
1084 	 * Optimize if there are no lists
1085 	 */
1086 	if ((perm & (M_ROOT | M_NONE | M_MAP)) == 0) {
1087 		perm &= ~M_4SEC_EXPORTED;
1088 		if (perm == M_RO)
1089 			return (NFSAUTH_RO);
1090 		if (perm == M_RW)
1091 			return (NFSAUTH_RW);
1092 	}
1093 
1094 	access = nfsauth_cache_get(exi, req, flavor, cr, NULL, NULL, NULL,
1095 	    NULL);
1096 
1097 	return (access);
1098 }
1099 
1100 int
1101 nfsauth_access(struct exportinfo *exi, struct svc_req *req, cred_t *cr,
1102     uid_t *uid, gid_t *gid, uint_t *ngids, gid_t **gids)
1103 {
1104 	int access, mapaccess;
1105 	struct secinfo *sp;
1106 	int i, flavor, perm;
1107 	int authnone_entry = -1;
1108 
1109 	/*
1110 	 * By default root is mapped to anonymous user.
1111 	 * This might get overriden later in nfsauth_cache_get().
1112 	 */
1113 	if (crgetuid(cr) == 0) {
1114 		if (uid != NULL)
1115 			*uid = exi->exi_export.ex_anon;
1116 		if (gid != NULL)
1117 			*gid = exi->exi_export.ex_anon;
1118 	} else {
1119 		if (uid != NULL)
1120 			*uid = crgetuid(cr);
1121 		if (gid != NULL)
1122 			*gid = crgetgid(cr);
1123 	}
1124 
1125 	if (ngids != NULL)
1126 		*ngids = 0;
1127 	if (gids != NULL)
1128 		*gids = NULL;
1129 
1130 	/*
1131 	 *  Get the nfs flavor number from xprt.
1132 	 */
1133 	flavor = (int)(uintptr_t)req->rq_xprt->xp_cookie;
1134 
1135 	/*
1136 	 * First check the access restrictions on the filesystem.  If
1137 	 * there are no lists associated with this flavor then there's no
1138 	 * need to make an expensive call to the nfsauth service or to
1139 	 * cache anything.
1140 	 */
1141 
1142 	sp = exi->exi_export.ex_secinfo;
1143 	for (i = 0; i < exi->exi_export.ex_seccnt; i++) {
1144 		if (flavor != sp[i].s_secinfo.sc_nfsnum) {
1145 			if (sp[i].s_secinfo.sc_nfsnum == AUTH_NONE)
1146 				authnone_entry = i;
1147 			continue;
1148 		}
1149 		break;
1150 	}
1151 
1152 	mapaccess = 0;
1153 
1154 	if (i >= exi->exi_export.ex_seccnt) {
1155 		/*
1156 		 * Flavor not found, but use AUTH_NONE if it exists
1157 		 */
1158 		if (authnone_entry == -1)
1159 			return (NFSAUTH_DENIED);
1160 		flavor = AUTH_NONE;
1161 		mapaccess = NFSAUTH_MAPNONE;
1162 		i = authnone_entry;
1163 	}
1164 
1165 	/*
1166 	 * If the flavor is in the ex_secinfo list, but not an explicitly
1167 	 * shared flavor by the user, it is a result of the nfsv4 server
1168 	 * namespace setup. We will grant an RO permission similar for
1169 	 * a pseudo node except that this node is a shared one.
1170 	 *
1171 	 * e.g. flavor in (flavor) indicates that it is not explictly
1172 	 *	shared by the user:
1173 	 *
1174 	 *		/	(sys, krb5)
1175 	 *		|
1176 	 *		export  #share -o sec=sys (krb5)
1177 	 *		|
1178 	 *		secure  #share -o sec=krb5
1179 	 *
1180 	 *	In this case, when a krb5 request coming in to access
1181 	 *	/export, RO permission is granted.
1182 	 */
1183 	if (!(sp[i].s_flags & M_4SEC_EXPORTED))
1184 		return (mapaccess | NFSAUTH_RO);
1185 
1186 	/*
1187 	 * Optimize if there are no lists.
1188 	 * We cannot optimize for AUTH_SYS with NGRPS (16) supplemental groups.
1189 	 */
1190 	perm = sp[i].s_flags;
1191 	if ((perm & (M_ROOT | M_NONE | M_MAP)) == 0 && (ngroups_max <= NGRPS ||
1192 	    flavor != AUTH_SYS || crgetngroups(cr) < NGRPS)) {
1193 		perm &= ~M_4SEC_EXPORTED;
1194 		if (perm == M_RO)
1195 			return (mapaccess | NFSAUTH_RO);
1196 		if (perm == M_RW)
1197 			return (mapaccess | NFSAUTH_RW);
1198 	}
1199 
1200 	access = nfsauth_cache_get(exi, req, flavor, cr, uid, gid, ngids, gids);
1201 
1202 	/*
1203 	 * For both NFSAUTH_DENIED and NFSAUTH_WRONGSEC we do not care about
1204 	 * the supplemental groups.
1205 	 */
1206 	if (access & NFSAUTH_DENIED || access & NFSAUTH_WRONGSEC) {
1207 		if (ngids != NULL && gids != NULL) {
1208 			kmem_free(*gids, *ngids * sizeof (gid_t));
1209 			*ngids = 0;
1210 			*gids = NULL;
1211 		}
1212 	}
1213 
1214 	/*
1215 	 * Client's security flavor doesn't match with "ro" or
1216 	 * "rw" list. Try again using AUTH_NONE if present.
1217 	 */
1218 	if ((access & NFSAUTH_WRONGSEC) && (flavor != AUTH_NONE)) {
1219 		/*
1220 		 * Have we already encountered AUTH_NONE ?
1221 		 */
1222 		if (authnone_entry != -1) {
1223 			mapaccess = NFSAUTH_MAPNONE;
1224 			access = nfsauth_cache_get(exi, req, AUTH_NONE, cr,
1225 			    NULL, NULL, NULL, NULL);
1226 		} else {
1227 			/*
1228 			 * Check for AUTH_NONE presence.
1229 			 */
1230 			for (; i < exi->exi_export.ex_seccnt; i++) {
1231 				if (sp[i].s_secinfo.sc_nfsnum == AUTH_NONE) {
1232 					mapaccess = NFSAUTH_MAPNONE;
1233 					access = nfsauth_cache_get(exi, req,
1234 					    AUTH_NONE, cr, NULL, NULL, NULL,
1235 					    NULL);
1236 					break;
1237 				}
1238 			}
1239 		}
1240 	}
1241 
1242 	if (access & NFSAUTH_DENIED)
1243 		access = NFSAUTH_DENIED;
1244 
1245 	return (access | mapaccess);
1246 }
1247 
1248 static void
1249 nfsauth_free_node(struct auth_cache *p)
1250 {
1251 	if (p->auth_netid != NULL)
1252 		kmem_free(p->auth_netid, strlen(p->auth_netid) + 1);
1253 	kmem_free(p->auth_addr.buf, p->auth_addr.len);
1254 	kmem_free(p->auth_clnt_gids, p->auth_clnt_ngids * sizeof (gid_t));
1255 	kmem_free(p->auth_srv_gids, p->auth_srv_ngids * sizeof (gid_t));
1256 	mutex_destroy(&p->auth_lock);
1257 	kmem_cache_free(exi_cache_handle, p);
1258 }
1259 
1260 /*
1261  * Remove the dead entry from the refreshq_dead_entries
1262  * list.
1263  */
1264 static void
1265 nfsauth_remove_dead_entry(struct auth_cache *dead)
1266 {
1267 	struct auth_cache	*p;
1268 	struct auth_cache	*prev;
1269 	struct auth_cache	*next;
1270 
1271 	mutex_enter(&refreshq_lock);
1272 	prev = NULL;
1273 	for (p = refreshq_dead_entries; p != NULL; p = next) {
1274 		next = p->auth_next;
1275 
1276 		if (p == dead) {
1277 			if (prev == NULL)
1278 				refreshq_dead_entries = next;
1279 			else
1280 				prev->auth_next = next;
1281 
1282 			nfsauth_free_node(dead);
1283 			break;
1284 		}
1285 
1286 		prev = p;
1287 	}
1288 	mutex_exit(&refreshq_lock);
1289 }
1290 
1291 /*
1292  * Free the nfsauth cache for a given export
1293  */
1294 void
1295 nfsauth_cache_free(struct exportinfo *exi)
1296 {
1297 	int i;
1298 	struct auth_cache *p, *next;
1299 
1300 	for (i = 0; i < AUTH_TABLESIZE; i++) {
1301 		for (p = exi->exi_cache[i]; p; p = next) {
1302 			next = p->auth_next;
1303 
1304 			/*
1305 			 * The only way we got here
1306 			 * was with an exi_rele, which
1307 			 * means that no auth cache entry
1308 			 * is being refreshed.
1309 			 */
1310 			nfsauth_free_node(p);
1311 		}
1312 	}
1313 }
1314 
1315 /*
1316  * Called by the kernel memory allocator when
1317  * memory is low. Free unused cache entries.
1318  * If that's not enough, the VM system will
1319  * call again for some more.
1320  */
1321 /*ARGSUSED*/
1322 void
1323 exi_cache_reclaim(void *cdrarg)
1324 {
1325 	int i;
1326 	struct exportinfo *exi;
1327 
1328 	rw_enter(&exported_lock, RW_READER);
1329 
1330 	for (i = 0; i < EXPTABLESIZE; i++) {
1331 		for (exi = exptable[i]; exi; exi = exi->fid_hash.next) {
1332 			exi_cache_trim(exi);
1333 		}
1334 	}
1335 	nfsauth_cache_reclaim++;
1336 
1337 	rw_exit(&exported_lock);
1338 }
1339 
1340 void
1341 exi_cache_trim(struct exportinfo *exi)
1342 {
1343 	struct auth_cache *p;
1344 	struct auth_cache *prev, *next;
1345 	int i;
1346 	time_t stale_time;
1347 
1348 	stale_time = gethrestime_sec() - NFSAUTH_CACHE_TRIM;
1349 
1350 	rw_enter(&exi->exi_cache_lock, RW_WRITER);
1351 
1352 	for (i = 0; i < AUTH_TABLESIZE; i++) {
1353 
1354 		/*
1355 		 * Free entries that have not been
1356 		 * used for NFSAUTH_CACHE_TRIM seconds.
1357 		 */
1358 		prev = NULL;
1359 		for (p = exi->exi_cache[i]; p; p = next) {
1360 			next = p->auth_next;
1361 			if (p->auth_time > stale_time) {
1362 				prev = p;
1363 				continue;
1364 			}
1365 
1366 			mutex_enter(&p->auth_lock);
1367 			DTRACE_PROBE1(nfsauth__debug__trim__state,
1368 			    auth_state_t, p->auth_state);
1369 
1370 			if (p->auth_state != NFS_AUTH_FRESH) {
1371 				p->auth_state = NFS_AUTH_INVALID;
1372 				mutex_exit(&p->auth_lock);
1373 
1374 				mutex_enter(&refreshq_lock);
1375 				p->auth_next = refreshq_dead_entries;
1376 				refreshq_dead_entries = p;
1377 				mutex_exit(&refreshq_lock);
1378 			} else {
1379 				mutex_exit(&p->auth_lock);
1380 				nfsauth_free_node(p);
1381 			}
1382 
1383 			if (prev == NULL)
1384 				exi->exi_cache[i] = next;
1385 			else
1386 				prev->auth_next = next;
1387 		}
1388 	}
1389 
1390 	rw_exit(&exi->exi_cache_lock);
1391 }
1392