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 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26#include <sys/param.h>
27#include <sys/types.h>
28#include <sys/systm.h>
29#include <sys/cred.h>
30#include <sys/proc.h>
31#include <sys/user.h>
32#include <sys/vfs.h>
33#include <sys/vnode.h>
34#include <sys/pathname.h>
35#include <sys/uio.h>
36#include <sys/tiuser.h>
37#include <sys/sysmacros.h>
38#include <sys/kmem.h>
39#include <sys/kobj.h>
40#include <sys/mount.h>
41#include <sys/ioctl.h>
42#include <sys/statvfs.h>
43#include <sys/errno.h>
44#include <sys/debug.h>
45#include <sys/cmn_err.h>
46#include <sys/utsname.h>
47#include <sys/modctl.h>
48#include <sys/file.h>
49#include <sys/stat.h>
50#include <sys/fcntl.h>
51#include <sys/callb.h>
52
53#include <vm/hat.h>
54#include <vm/as.h>
55#include <vm/page.h>
56#include <vm/pvn.h>
57#include <vm/seg.h>
58#include <vm/seg_map.h>
59#include <vm/seg_vn.h>
60#include <vm/rm.h>
61#include <sys/fs/cachefs_fs.h>
62
63extern time_t time;
64
65/* forward references */
66int cachefs_rl_entry_get(cachefscache_t *, uint_t, rl_entry_t **);
67void cachefs_garbage_collect_queue(cachefscache_t *cachep);
68static time_t cachefs_gc_front_atime(cachefscache_t *cachep);
69static void cachefs_garbage_collect(cachefscache_t *cachep);
70static void cachefs_packed_pending(cachefscache_t *cachep);
71
72
73#define	RL_HEAD(cachep, type) \
74	(&(cachep->c_rlinfo.rl_items[CACHEFS_RL_INDEX(type)]))
75
76/*
77 * This function moves an RL entry from wherever it currently is to
78 * the back of the requested list.
79 */
80void
81cachefs_rlent_moveto(cachefscache_t *cachep,
82    enum cachefs_rl_type type, uint_t entno, size_t blks)
83{
84	mutex_enter(&cachep->c_contentslock);
85	cachefs_cache_dirty(cachep, 0);
86	cachefs_rlent_moveto_nolock(cachep, type, entno, blks);
87	mutex_exit(&cachep->c_contentslock);
88}
89
90void
91cachefs_rlent_moveto_nolock(cachefscache_t *cachep,
92    enum cachefs_rl_type type, uint_t entno, size_t blks)
93{
94	rl_entry_t *rl_ent;
95	uint_t prev, next;
96	cachefs_rl_listhead_t *lhp;
97	enum cachefs_rl_type otype;
98	int error;
99
100	ASSERT(entno != 0);
101	ASSERT((cachep->c_flags & (CACHE_NOCACHE|CACHE_NOFILL)) == 0);
102	ASSERT(MUTEX_HELD(&cachep->c_contentslock));
103	ASSERT((CACHEFS_RL_START <= type) && (type <= CACHEFS_RL_END));
104
105	error = cachefs_rl_entry_get(cachep, entno, &rl_ent);
106	if (error)
107		return;
108	next = rl_ent->rl_fwd_idx;
109	prev = rl_ent->rl_bkwd_idx;
110	otype = rl_ent->rl_current;
111	ASSERT((CACHEFS_RL_START <= otype) && (otype <= CACHEFS_RL_END));
112	rl_ent->rl_current = CACHEFS_RL_NONE;
113
114	if (type == CACHEFS_RL_PACKED_PENDING) {
115		/* XXX sam: is this the right place to turn this on? */
116		cachep->c_flags |= CACHE_PACKED_PENDING;
117	}
118
119	/* remove entry from its previous list */
120
121	lhp = RL_HEAD(cachep, otype);
122	if ((lhp->rli_back == 0) || (lhp->rli_front == 0))
123		ASSERT((lhp->rli_back == 0) && (lhp->rli_front == 0));
124
125	if (lhp->rli_back == entno)
126		lhp->rli_back = next;
127	if (lhp->rli_front == entno)
128		lhp->rli_front = prev;
129	if (prev != 0) {
130		error = cachefs_rl_entry_get(cachep, prev, &rl_ent);
131		if (error)
132			return;
133		rl_ent->rl_fwd_idx = next;
134	}
135	if (next != 0) {
136		error = cachefs_rl_entry_get(cachep, next, &rl_ent);
137		if (error)
138			return;
139		rl_ent->rl_bkwd_idx = prev;
140	}
141	lhp->rli_blkcnt -= blks;
142	lhp->rli_itemcnt--;
143
144	/* add entry to its new list */
145
146	lhp = RL_HEAD(cachep, type);
147	error = cachefs_rl_entry_get(cachep, entno, &rl_ent);
148	if (error)
149		return;
150	rl_ent->rl_current = type;
151	rl_ent->rl_bkwd_idx = 0;
152	rl_ent->rl_fwd_idx = lhp->rli_back;
153
154	if (lhp->rli_back != 0) {
155		ASSERT(lhp->rli_front != 0);
156		error = cachefs_rl_entry_get(cachep, lhp->rli_back, &rl_ent);
157		if (error)
158			return;
159		rl_ent->rl_bkwd_idx = entno;
160	} else {
161		ASSERT(lhp->rli_front == 0);
162		lhp->rli_front = entno;
163	}
164	lhp->rli_back = entno;
165	lhp->rli_blkcnt += blks;
166	lhp->rli_itemcnt++;
167}
168
169/*
170 * This function verifies that an rl entry is of the `correct' type.
171 * it's used for debugging (only?).
172 */
173
174/*ARGSUSED*/
175void
176cachefs_rlent_verify(cachefscache_t *cachep,
177    enum cachefs_rl_type type, uint_t entno)
178{
179#ifdef CFSDEBUG
180	rl_entry_t *rl_ent;
181	int error;
182
183	ASSERT((CACHEFS_RL_START <= type) && (type <= CACHEFS_RL_END));
184
185	mutex_enter(&cachep->c_contentslock);
186
187	error = cachefs_rl_entry_get(cachep, entno, &rl_ent);
188	if (!error && rl_ent->rl_current != type) {
189#ifdef CFSRLDEBUG
190		printf("cachefs_rldebug: type should be %x\n", type);
191		cachefs_rl_debug_show(rl_ent);
192		debug_enter("cachefs_rlent_verify");
193#else /* CFSRLDEBUG */
194		cmn_err(CE_WARN, "rl entry %x type = %x should be %x\n",
195		    entno, rl_ent->rl_current, type);
196#endif /* CFSRLDEBUG */
197	}
198
199	mutex_exit(&cachep->c_contentslock);
200#endif /* CFSDEBUG */
201}
202
203/*
204 * Returns the rl data of the front of the specified resource list.
205 * Returns 0 for success, !0 if the list is empty.
206 */
207int
208cachefs_rlent_data(cachefscache_t *cachep, rl_entry_t *valp, uint_t *entnop)
209{
210	uint_t entno;
211	rl_entry_t *rl_ent;
212	int error = 0;
213	cachefs_rl_listhead_t *lhp;
214	enum cachefs_rl_type type;
215
216	ASSERT((cachep->c_flags & CACHE_NOCACHE) == 0);
217
218	if (entnop == NULL)
219		entnop = &entno;
220	*entnop = 0;
221
222	mutex_enter(&cachep->c_contentslock);
223
224	type = valp->rl_current;
225	ASSERT((CACHEFS_RL_START <= type) && (type <= CACHEFS_RL_END));
226	lhp = RL_HEAD(cachep, type);
227	entno = lhp->rli_front;
228
229	if (*entnop == 0) {
230		error = ENOENT;
231	} else {
232		error = cachefs_rl_entry_get(cachep, *entnop, &rl_ent);
233		if (!error)
234			*valp = *rl_ent;
235	}
236	mutex_exit(&cachep->c_contentslock);
237	return (error);
238}
239
240/*
241 * This function plucks a slot from the RL free list and creates an RL entry.
242 */
243int
244cachefs_rl_alloc(struct cachefscache *cachep, rl_entry_t *valp, uint_t *entnop)
245{
246	int error = 0;
247	uint_t entno;
248	rl_entry_t *rl_ent;
249	cachefs_rl_listhead_t *lhp;
250
251	ASSERT((cachep->c_flags & (CACHE_NOCACHE|CACHE_NOFILL)) == 0);
252	mutex_enter(&cachep->c_contentslock);
253
254	cachefs_cache_dirty(cachep, 0);
255	lhp = RL_HEAD(cachep, CACHEFS_RL_FREE);
256	entno = lhp->rli_front;
257	if (entno == 0) {
258		if (cachep->c_rlinfo.rl_entries >=
259		    cachep->c_label.cl_maxinodes) {
260			error = ENOMEM;
261			goto out;
262		}
263		entno = ++(cachep->c_rlinfo.rl_entries);
264		lhp->rli_itemcnt++;
265		error = cachefs_rl_entry_get(cachep, entno, &rl_ent);
266		if (error)
267			goto out;
268		rl_ent->rl_current = CACHEFS_RL_NONE;
269		rl_ent->rl_fwd_idx = 0;
270		rl_ent->rl_bkwd_idx = 0;
271	}
272
273	cachefs_rlent_moveto_nolock(cachep, CACHEFS_RL_NONE, entno, 0);
274
275	error = cachefs_rl_entry_get(cachep, entno, &rl_ent);
276	if (error)
277		goto out;
278	rl_ent->rl_fsid = valp->rl_fsid;
279	rl_ent->rl_fileno = valp->rl_fileno;
280	rl_ent->rl_local = valp->rl_local;
281	rl_ent->rl_attrc = valp->rl_attrc;
282	rl_ent->rl_fsck = 0;
283out:
284	mutex_exit(&cachep->c_contentslock);
285	if (error == 0)
286		*entnop = entno;
287	return (error);
288}
289
290/*
291 * Call to change a local fileno in an rl entry to a normal fileno.
292 */
293void
294cachefs_rl_changefileno(cachefscache_t *cachep, uint_t entno, ino64_t fileno)
295{
296	rl_entry_t *rl_ent;
297	int error;
298
299	mutex_enter(&cachep->c_contentslock);
300	error = cachefs_rl_entry_get(cachep, entno, &rl_ent);
301	if (!error) {
302		ASSERT(rl_ent->rl_local);
303		rl_ent->rl_local = 0;
304		rl_ent->rl_fileno = fileno;
305	}
306	mutex_exit(&cachep->c_contentslock);
307}
308
309/*
310 * Moves the files on the modified list for this file system to
311 * the modified fix list.
312 */
313void
314cachefs_move_modified_to_mf(cachefscache_t *cachep, fscache_t *fscp)
315{
316	rl_entry_t *list_ent;
317	uint_t curp, nextp;
318	cachefs_rl_listhead_t *lhp;
319	int error;
320
321	ASSERT(MUTEX_HELD(&cachep->c_mflock));
322
323	mutex_enter(&cachep->c_contentslock);
324
325	lhp = RL_HEAD(cachep, CACHEFS_RL_MF);
326	ASSERT(lhp->rli_front == 0);
327	ASSERT(lhp->rli_back == 0);
328	ASSERT(lhp->rli_itemcnt == 0);
329	lhp->rli_blkcnt = 0;
330
331	cachefs_cache_dirty(cachep, 0);
332
333	/* walk the modified list */
334	lhp = RL_HEAD(cachep, CACHEFS_RL_MODIFIED);
335	for (curp = lhp->rli_front; curp != 0; curp = nextp) {
336		/* get the next element */
337		error = cachefs_rl_entry_get(cachep, curp, &list_ent);
338		if (error) {
339			mutex_exit(&cachep->c_contentslock);
340			return;
341		}
342		nextp = list_ent->rl_bkwd_idx;
343
344		/* skip if element is not in this file system */
345		if (list_ent->rl_fsid != fscp->fs_cfsid)
346			continue;
347
348		/* move from modified list to mf list */
349		cachefs_rlent_moveto_nolock(cachep, CACHEFS_RL_MF, curp, 0);
350	}
351	mutex_exit(&cachep->c_contentslock);
352}
353
354/*
355 * Moves the contents of the active list to the rl list.
356 * Leave modified files on the active list, so they are not
357 * garbage collected.
358 */
359void
360cachefs_rl_cleanup(cachefscache_t *cachep)
361{
362	cachefs_rl_listhead_t *lhp;
363	rl_entry_t *rlp;
364	uint_t entno, next;
365	int error;
366
367	ASSERT(MUTEX_HELD(&cachep->c_contentslock));
368
369	/*
370	 * if fsck ran, then both of these lists should be empty.  the
371	 * only time this isn't the case is when we've done a cachefs
372	 * boot with a clean cache.  then, the cache may have been
373	 * clean, but files and attrfiles were left dangling.
374	 *
375	 * when this happens, we just fix the linked lists here.  this
376	 * means that the attrcache header and cnode metadata might
377	 * have incorrect information about which resource lists an
378	 * entity is currently on.  so, we set CACHE_CHECK_RLTYPE,
379	 * which says cache-wide to double-check and go with whatever
380	 * is in the resource list at the time such an object is
381	 * loaded into memory.
382	 */
383
384	lhp = RL_HEAD(cachep, CACHEFS_RL_ACTIVE);
385	if (lhp->rli_itemcnt > 0) {
386		cachep->c_flags |= CACHE_CHECK_RLTYPE;
387		cachefs_cache_dirty(cachep, 0);
388	}
389	for (entno = lhp->rli_front; entno != 0; entno = next) {
390		error = cachefs_rl_entry_get(cachep, entno, &rlp);
391		if (error)
392			return;
393		next = rlp->rl_bkwd_idx;
394
395		ASSERT(rlp->rl_current == CACHEFS_RL_ACTIVE);
396		cachefs_rlent_moveto_nolock(cachep, CACHEFS_RL_GC, entno, 0);
397	}
398
399#if 0
400	lhp = RL_HEAD(cachep, CACHEFS_RL_ATTRFILE);
401	if (lhp->rli_itemcnt > 0) {
402		cachep->c_flags |= CACHE_CHECK_RLTYPE;
403		cachefs_cache_dirty(cachep, 0);
404	}
405	for (entno = lhp->rli_front; entno != 0; entno = next) {
406		error = cachefs_rl_entry_get(cachep, entno, &rlp);
407		if (error)
408			return;
409		next = rlp->rl_bkwd_idx;
410
411		ASSERT(rlp->rl_current == CACHEFS_RL_ATTRFILE);
412		cachefs_rlent_moveto_nolock(cachep, CACHEFS_RL_GC, entno, 0);
413	}
414#endif
415}
416
417int
418cachefs_allocfile(cachefscache_t *cachep)
419{
420	int error = 0;
421	int collect = 0;
422	struct statvfs64 sb;
423	fsfilcnt64_t used;
424
425	(void) VFS_STATVFS(cachep->c_dirvp->v_vfsp, &sb);
426	used = sb.f_files - sb.f_ffree;
427
428	mutex_enter(&cachep->c_contentslock);
429
430	/* if there are no more available inodes */
431	if ((cachep->c_usage.cu_filesused >= cachep->c_label.cl_maxinodes) ||
432	    ((cachep->c_usage.cu_filesused > cachep->c_label.cl_filemin) &&
433	    (used > cachep->c_label.cl_filetresh))) {
434		error = ENOSPC;
435		if ((cachep->c_flags & CACHE_GARBAGE_COLLECT) == 0)
436			collect = 1;
437	}
438
439	/* else if there are more available inodes */
440	else {
441		cachefs_cache_dirty(cachep, 0);
442		cachep->c_usage.cu_filesused++;
443		if (((cachep->c_flags & CACHE_GARBAGE_COLLECT) == 0) &&
444		    (cachep->c_usage.cu_filesused >=
445		    cachep->c_label.cl_filehiwat))
446			collect = 1;
447	}
448
449	mutex_exit(&cachep->c_contentslock);
450
451	if (collect)
452		cachefs_garbage_collect_queue(cachep);
453
454	return (error);
455}
456
457void
458cachefs_freefile(cachefscache_t *cachep)
459{
460	mutex_enter(&cachep->c_contentslock);
461	ASSERT(cachep->c_usage.cu_filesused > 0);
462	cachefs_cache_dirty(cachep, 0);
463	cachep->c_usage.cu_filesused--;
464	mutex_exit(&cachep->c_contentslock);
465}
466
467/*ARGSUSED*/
468int
469cachefs_allocblocks(cachefscache_t *cachep, size_t nblks,
470    enum cachefs_rl_type type)
471{
472	int error = 0;
473	int collect = 0;
474	struct statvfs64 sb;
475	size_t used;
476	size_t blocks;
477
478	ASSERT(type != CACHEFS_RL_FREE);
479
480	(void) VFS_STATVFS(cachep->c_dirvp->v_vfsp, &sb);
481	used = ((sb.f_blocks - sb.f_bfree) * sb.f_frsize) / MAXBSIZE;
482
483	mutex_enter(&cachep->c_contentslock);
484
485	/* if there are no more available blocks */
486	blocks = cachep->c_usage.cu_blksused + nblks;
487	if ((blocks >= cachep->c_label.cl_maxblks) ||
488	    ((blocks > cachep->c_label.cl_blockmin) &&
489	    (used > cachep->c_label.cl_blocktresh))) {
490		error = ENOSPC;
491		if ((cachep->c_flags & CACHE_GARBAGE_COLLECT) == 0)
492			collect = 1;
493	}
494
495	/* else if there are more available blocks */
496	else {
497		cachefs_cache_dirty(cachep, 0);
498		cachep->c_usage.cu_blksused += (uint_t)nblks;
499		ASSERT((CACHEFS_RL_START <= type) && (type <= CACHEFS_RL_END));
500		RL_HEAD(cachep, type)->rli_blkcnt += nblks;
501
502		if (((cachep->c_flags & CACHE_GARBAGE_COLLECT) == 0) &&
503		    (cachep->c_usage.cu_blksused >=
504		    cachep->c_label.cl_blkhiwat))
505			collect = 1;
506	}
507
508	mutex_exit(&cachep->c_contentslock);
509
510	if (collect)
511		cachefs_garbage_collect_queue(cachep);
512
513	return (error);
514}
515
516void
517cachefs_freeblocks(cachefscache_t *cachep, size_t nblks,
518		enum cachefs_rl_type type)
519{
520	mutex_enter(&cachep->c_contentslock);
521	cachefs_cache_dirty(cachep, 0);
522	cachep->c_usage.cu_blksused -= (uint_t)nblks;
523	ASSERT(cachep->c_usage.cu_blksused >= 0);
524	ASSERT((CACHEFS_RL_START <= type) && (type <= CACHEFS_RL_END));
525	ASSERT(type != CACHEFS_RL_FREE);
526	RL_HEAD(cachep, type)->rli_blkcnt -= nblks;
527	mutex_exit(&cachep->c_contentslock);
528}
529
530int
531cachefs_victim(cachefscache_t *cachep)
532{
533	uint_t entno;
534	rl_entry_t *rl_ent;
535	int error = 0;
536	ino64_t fsid;
537	cfs_cid_t cid;
538	struct fscache *fscp;
539	struct filegrp *fgp;
540	struct cachefs_metadata md;
541	struct cnode *cp;
542	int isattrc;
543	cachefs_rl_listhead_t *lhp;
544
545	ASSERT((cachep->c_flags & (CACHE_NOCACHE|CACHE_NOFILL)) == 0);
546	fscp = NULL;
547	fgp = NULL;
548
549	/* get the file and fsid of the first item on the rl list */
550	/* XXX call rlent_data() instead */
551	mutex_enter(&cachep->c_contentslock);
552	lhp = RL_HEAD(cachep, CACHEFS_RL_GC);
553	entno = lhp->rli_front;
554	if (entno == 0) {
555		mutex_exit(&cachep->c_contentslock);
556		error = ENOSPC;
557		goto out;
558	}
559	error = cachefs_rl_entry_get(cachep, entno, &rl_ent);
560	if (error) {
561		mutex_exit(&cachep->c_contentslock);
562		goto out;
563	}
564	fsid = rl_ent->rl_fsid;
565	cid.cid_fileno = rl_ent->rl_fileno;
566	ASSERT(rl_ent->rl_local == 0);
567	cid.cid_flags = 0;
568	isattrc = rl_ent->rl_attrc;
569	mutex_exit(&cachep->c_contentslock);
570
571	/* get the file system cache object for this fsid */
572	mutex_enter(&cachep->c_fslistlock);
573	fscp = fscache_list_find(cachep, fsid);
574	if (fscp == NULL) {
575		fscp = fscache_create(cachep);
576		error = fscache_activate(fscp, fsid, NULL, NULL, 0);
577		if (error) {
578			cmn_err(CE_WARN,
579			    "cachefs: cache corruption, run fsck\n");
580			fscache_destroy(fscp);
581			fscp = NULL;
582			mutex_exit(&cachep->c_fslistlock);
583			error = 0;
584			goto out;
585		}
586		fscache_list_add(cachep, fscp);
587	}
588	fscache_hold(fscp);
589	mutex_exit(&cachep->c_fslistlock);
590
591	/* get the file group object for this file */
592	mutex_enter(&fscp->fs_fslock);
593	fgp = filegrp_list_find(fscp, &cid);
594	if (fgp == NULL) {
595		fgp = filegrp_create(fscp, &cid);
596		filegrp_list_add(fscp, fgp);
597	}
598	if (fgp->fg_flags & CFS_FG_ALLOC_ATTR) {
599		if (isattrc == 0) {
600			cmn_err(CE_WARN,
601			    "cachefs: cache corruption, run fsck\n");
602			delay(5*hz);
603		}
604		filegrp_list_remove(fscp, fgp);
605		filegrp_destroy(fgp);
606		error = 0;
607		fgp = NULL;
608		mutex_exit(&fscp->fs_fslock);
609		goto out;
610	}
611
612	/* if we are victimizing an attrcache file */
613	if (isattrc) {
614		mutex_enter(&fgp->fg_mutex);
615		/* if the filegrp is not writable */
616		if ((fgp->fg_flags & CFS_FG_WRITE) == 0) {
617			mutex_exit(&fgp->fg_mutex);
618			error = EROFS;
619			fgp = NULL;
620			mutex_exit(&fscp->fs_fslock);
621			goto out;
622		}
623
624		/* if the filegrp did not go active on us */
625		if ((fgp->fg_count == 0) && (fgp->fg_header->ach_nffs == 0)) {
626			mutex_exit(&fgp->fg_mutex);
627			filegrp_list_remove(fscp, fgp);
628			fgp->fg_header->ach_count = 0;
629			filegrp_destroy(fgp);
630		} else {
631#ifdef CFSDEBUG
632			CFS_DEBUG(CFSDEBUG_RESOURCE)
633				printf("c_victim: filegrp went active"
634				    " %p %llu %d %d %lld\n",
635				    (void *) fgp,
636				    (u_longlong_t)fgp->fg_id.cid_fileno,
637				    fgp->fg_header->ach_rlno,
638				    fgp->fg_count, fgp->fg_header->ach_nffs);
639#endif
640			ASSERT(fgp->fg_header->ach_rl_current !=
641			    CACHEFS_RL_GC);
642			mutex_exit(&fgp->fg_mutex);
643		}
644		fgp = NULL;
645		error = 0;
646		mutex_exit(&fscp->fs_fslock);
647		goto out;
648	}
649	ASSERT((fgp->fg_flags & CFS_FG_ALLOC_FILE) == 0);
650	filegrp_hold(fgp);
651	mutex_exit(&fscp->fs_fslock);
652
653	/* grab the cnode list lock */
654	mutex_enter(&fgp->fg_cnodelock);
655
656	/* see if a cnode exists for this file */
657	(void) cachefs_cnode_find(fgp, &cid, NULL, &cp, NULL, NULL);
658	if (cp) {
659		VN_HOLD(CTOV(cp));
660
661		/* move file from rl to active list */
662		cachefs_rlent_moveto(fscp->fs_cache,
663		    CACHEFS_RL_ACTIVE, cp->c_metadata.md_rlno,
664		    cp->c_metadata.md_frontblks);
665		cp->c_metadata.md_rltype = CACHEFS_RL_ACTIVE;
666		mutex_exit(&cp->c_statelock);
667		mutex_exit(&fgp->fg_cnodelock);
668		VN_RELE(CTOV(cp));
669		error = 0;
670		goto out;
671	}
672
673	/*
674	 * The cnode does not exist and since we hold the hashlock
675	 * it cannot be created until we are done.
676	 */
677
678	/* see if the item is no longer on the rl list, it could happen */
679	mutex_enter(&cachep->c_contentslock);
680	lhp = RL_HEAD(cachep, CACHEFS_RL_GC);
681	entno = lhp->rli_front;
682	if (entno == 0) {
683		mutex_exit(&cachep->c_contentslock);
684		mutex_exit(&fgp->fg_cnodelock);
685		error = ENOSPC;
686		goto out;
687	}
688	error = cachefs_rl_entry_get(cachep, entno, &rl_ent);
689	if (error) {
690		mutex_exit(&cachep->c_contentslock);
691		mutex_exit(&fgp->fg_cnodelock);
692		goto out;
693	}
694	if ((fsid != rl_ent->rl_fsid) ||
695	    (cid.cid_fileno != rl_ent->rl_fileno)) {
696		mutex_exit(&cachep->c_contentslock);
697		mutex_exit(&fgp->fg_cnodelock);
698		error = 0;
699		goto out;
700	}
701	mutex_exit(&cachep->c_contentslock);
702
703	/* Get the metadata from the attrcache file */
704	ASSERT((fgp->fg_flags & CFS_FG_ALLOC_ATTR) == 0);
705	error = filegrp_read_metadata(fgp, &cid, &md);
706	ASSERT(error == 0);
707
708	/* md.md_rltype may be incorrect, but we know file isn't active. */
709	if (error) {
710		/* XXX this should never happen, fix on panic */
711		mutex_exit(&fgp->fg_cnodelock);
712		error = 0;
713		goto out;
714	}
715
716	/* destroy the frontfile */
717	cachefs_removefrontfile(&md, &cid, fgp);
718
719	/* remove the victim from the gc list */
720	cachefs_rlent_moveto(fscp->fs_cache, CACHEFS_RL_FREE, entno, 0);
721
722	/* destroy the metadata */
723	(void) filegrp_destroy_metadata(fgp, &cid);
724
725	mutex_exit(&fgp->fg_cnodelock);
726	error = 0;
727out:
728	if (fgp) {
729		filegrp_rele(fgp);
730	}
731	if (fscp) {
732		fscache_rele(fscp);
733	}
734	return (error);
735}
736
737static void
738cachefs_garbage_collect(cachefscache_t *cachep)
739{
740	fsfilcnt64_t filelowat, filelowatmax, maxfiles, threshfiles;
741	fsblkcnt64_t blocklowat, blocklowatmax, maxblks, threshblks;
742	int error;
743	struct cache_usage *cup = &cachep->c_usage;
744
745	ASSERT((cachep->c_flags & (CACHE_NOCACHE|CACHE_NOFILL)) == 0);
746	mutex_enter(&cachep->c_contentslock);
747	ASSERT(cachep->c_flags & CACHE_GARBAGE_COLLECT);
748	filelowat = cachep->c_label.cl_filelowat;
749	blocklowat = cachep->c_label.cl_blklowat;
750	maxblks = cachep->c_label.cl_maxblks;
751	maxfiles = cachep->c_label.cl_maxinodes;
752	threshblks = cachep->c_label.cl_blocktresh;
753	threshfiles = cachep->c_label.cl_filetresh;
754	mutex_exit(&cachep->c_contentslock);
755
756	cachep->c_gc_count++;
757	cachep->c_gc_time = time;
758	cachep->c_gc_before = cachefs_gc_front_atime(cachep);
759
760	/*
761	 * since we're here, we're running out of blocks or files.
762	 * file and block lowat are what determine how low we garbage
763	 * collect.  in order to do any good, we should drop below
764	 * maxblocks, threshblocks, or the current blocks, whichever
765	 * is smaller (same goes for files).  however, we won't go
766	 * below an arbitrary (small) minimum for each.
767	 */
768
769	/* move down for maxfiles and maxblocks */
770	if ((filelowatmax = (maxfiles * 7) / 10) < filelowat)
771		filelowat = filelowatmax;
772	if ((blocklowatmax = (maxblks * 7) / 10) < blocklowat)
773		blocklowat = blocklowatmax;
774
775	/* move down for threshfiles and threshblocks */
776	if ((filelowatmax = (threshfiles * 7) / 10) < filelowat)
777		filelowat = filelowatmax;
778	if ((blocklowatmax = (threshblks * 7) / 10) < blocklowat)
779		blocklowat = blocklowatmax;
780
781	/* move down for current files and blocks */
782	if ((filelowatmax = ((fsfilcnt64_t)cup->cu_filesused * 7) / 10) <
783	    filelowat)
784		filelowat = filelowatmax;
785	if ((blocklowatmax = ((fsblkcnt64_t)cup->cu_blksused * 7) / 10) <
786	    blocklowat)
787		blocklowat = blocklowatmax;
788
789	/* move up for an arbitrary minimum */
790#define	MIN_BLKLO	640		/* 640*8192 == 5MB */
791#define	MIN_FILELO	1000
792	if (filelowat < MIN_FILELO)
793		filelowat = MIN_FILELO;
794	if (blocklowat < MIN_BLKLO)
795		blocklowat = MIN_BLKLO;
796
797	while (cup->cu_filesused > filelowat || cup->cu_blksused > blocklowat) {
798		/* if the thread is to terminate */
799		if (cachep->c_flags & CACHE_CACHEW_THREADEXIT)
800			break;
801
802		error = cachefs_victim(cachep);
803		if (error)
804			break;
805	}
806
807	cachep->c_gc_after = cachefs_gc_front_atime(cachep);
808	CACHEFS_TIME_TO_CFS_TIME_COPY(cachep->c_gc_after,
809	    cachep->c_rlinfo.rl_gctime, error);
810}
811
812/*
813 * traverse the packed pending list, repacking files when possible.
814 */
815
816static void
817cachefs_packed_pending(cachefscache_t *cachep)
818{
819	rl_entry_t rl;
820	int error = 0; /* not returned -- used as placeholder */
821	fscache_t *fscp = NULL;
822	cfs_cid_t cid;
823	cnode_t *cp;
824	uint_t entno;
825	int count = 0;
826	cachefs_rl_listhead_t *lhp;
827
828	ASSERT(MUTEX_HELD(&cachep->c_contentslock));
829
830	lhp = RL_HEAD(cachep, CACHEFS_RL_PACKED_PENDING);
831	count = lhp->rli_itemcnt;
832
833	mutex_exit(&cachep->c_contentslock);
834
835	rl.rl_current = CACHEFS_RL_PACKED_PENDING;
836	while (cachefs_rlent_data(cachep, &rl, &entno) == 0) {
837		if (count-- <= 0) {
838#ifdef CFSDEBUG
839			CFS_DEBUG(CFSDEBUG_RESOURCE)
840				printf("cachefs_ppending: count exceeded\n");
841#endif /* CFSDEBUG */
842			break;
843		}
844		if ((cachep->c_flags &
845		    (CACHE_PACKED_PENDING | CACHE_CACHEW_THREADEXIT)) !=
846		    CACHE_PACKED_PENDING) {
847#ifdef CFSDEBUG
848			CFS_DEBUG(CFSDEBUG_RESOURCE)
849				printf("cachefs_ppending: early exit\n");
850#endif /* CFSDEBUG */
851			break;
852		}
853		if (rl.rl_current != CACHEFS_RL_PACKED_PENDING) {
854#ifdef CFSDEBUG
855			CFS_DEBUG(CFSDEBUG_RESOURCE)
856				printf("cachefs_ppending: gone from list\n");
857#endif /* CFSDEBUG */
858			break;
859		}
860
861		/* if the fscp we have does not match */
862		if ((fscp == NULL) || (fscp->fs_cfsid != rl.rl_fsid)) {
863			if (fscp) {
864				cachefs_cd_release(fscp);
865				fscache_rele(fscp);
866				fscp = NULL;
867			}
868
869			/* get the file system cache object for this fsid */
870			mutex_enter(&cachep->c_fslistlock);
871			fscp = fscache_list_find(cachep, rl.rl_fsid);
872			if (fscp == NULL) {
873
874				/*
875				 * uh oh, the filesystem probably
876				 * isn't mounted.  we `move' this
877				 * entry onto the same list that it's
878				 * on, which really just moves it to
879				 * the back of the list.  we need not
880				 * worry about an infinite loop, due
881				 * to the counter.
882				 */
883
884				cachefs_rlent_moveto(cachep,
885				    CACHEFS_RL_PACKED_PENDING, entno, 0);
886#ifdef CFSDEBUG
887				CFS_DEBUG(CFSDEBUG_RESOURCE)
888					printf("cachefs_ppending: "
889					    "fscp find failed\n");
890#endif /* CFSDEBUG */
891				continue;
892			}
893			fscache_hold(fscp);
894			mutex_exit(&cachep->c_fslistlock);
895
896			/* get access to the file system */
897			error = cachefs_cd_access(fscp, 0, 0);
898			if ((error) ||
899			    (fscp->fs_cdconnected != CFS_CD_CONNECTED)) {
900#ifdef CFSDEBUG
901				CFS_DEBUG(CFSDEBUG_RESOURCE)
902					printf("cachefs: "
903					    "ppending: err %d con %d\n",
904					    error, fscp->fs_cdconnected);
905#endif /* CFSDEBUG */
906				fscache_rele(fscp);
907				fscp = NULL;
908				break;
909			}
910		}
911
912		/* get the cnode for the file */
913		cid.cid_fileno = rl.rl_fileno;
914		cid.cid_flags = rl.rl_local ? CFS_CID_LOCAL : 0;
915		error = cachefs_cnode_make(&cid, fscp,
916		    NULL, NULL, NULL, kcred, 0, &cp);
917		if (error) {
918#ifdef CFSDEBUG
919			CFS_DEBUG(CFSDEBUG_RESOURCE)
920				printf("cachefs: "
921				    "ppending: could not find %llu\n",
922				    (u_longlong_t)cid.cid_fileno);
923			delay(5*hz);
924#endif /* CFSDEBUG */
925			break;
926		}
927
928		mutex_enter(&cp->c_statelock);
929		if (cp->c_flags & CN_STALE) {
930			/* back file went away behind our back */
931			ASSERT(cp->c_metadata.md_rlno == 0);
932			mutex_exit(&cp->c_statelock);
933
934#ifdef CFSDEBUG
935			CFS_DEBUG(CFSDEBUG_RESOURCE)
936				printf("cachefs: ppending: stale\n");
937#endif /* CFSDEBUG */
938
939			VN_RELE(CTOV(cp));
940			continue;
941		}
942		mutex_exit(&cp->c_statelock);
943
944		error = cachefs_pack_common(CTOV(cp),
945		    (cp->c_cred) ? cp->c_cred : kcred);
946		VN_RELE(CTOV(cp));
947
948		if (error != 0) {
949#ifdef CFSDEBUG
950			CFS_DEBUG(CFSDEBUG_RESOURCE)
951				printf("cachefs: "
952				    "ppending: pack_common: error = %d\n",
953				    error);
954#endif /* CFSDEBUG */
955			break;
956		}
957	}
958
959	if (fscp != NULL) {
960		cachefs_cd_release(fscp);
961		fscache_rele(fscp);
962	}
963
964	mutex_enter(&cachep->c_contentslock);
965	if (lhp->rli_itemcnt == 0)
966		cachep->c_flags &= ~CACHE_PACKED_PENDING;
967}
968
969/* seconds; interval to do ppend list */
970static time_t cachefs_ppend_time = 900;
971
972/* main routine for the cachep worker thread */
973void
974cachefs_cachep_worker_thread(cachefscache_t *cachep)
975{
976	int error;
977	struct flock64 fl;
978	callb_cpr_t cprinfo;
979	kmutex_t cpr_lock;
980	clock_t wakeup;
981
982	/* lock the lock file for exclusive write access */
983	fl.l_type = F_WRLCK;
984	fl.l_whence = 0;
985	fl.l_start = (offset_t)0;
986	fl.l_len = (offset_t)1024;
987	fl.l_sysid = 0;
988	fl.l_pid = 0;
989	error = VOP_FRLOCK(cachep->c_lockvp, F_SETLK, &fl, FWRITE, (offset_t)0,
990	    NULL, kcred, NULL);
991	if (error) {
992		cmn_err(CE_WARN,
993		    "cachefs: Can't lock Cache Lock File(r); Error %d\n",
994		    error);
995	}
996
997	mutex_init(&cpr_lock, NULL, MUTEX_DEFAULT, NULL);
998	CALLB_CPR_INIT(&cprinfo, &cpr_lock, callb_generic_cpr, "cfs_gct");
999	mutex_enter(&cpr_lock);
1000	mutex_enter(&cachep->c_contentslock);
1001
1002	wakeup = (clock_t)(cachefs_ppend_time * hz);
1003
1004	/* loop while the thread is allowed to run */
1005	while ((cachep->c_flags & CACHE_CACHEW_THREADEXIT) == 0) {
1006		/* wait for a wakeup call */
1007		cachep->c_flags &= ~CACHE_GARBAGE_COLLECT;
1008		CALLB_CPR_SAFE_BEGIN(&cprinfo);
1009		mutex_exit(&cpr_lock);
1010		(void) cv_reltimedwait(&cachep->c_cwcv,
1011		    &cachep->c_contentslock, wakeup, TR_CLOCK_TICK);
1012		mutex_enter(&cpr_lock);
1013		CALLB_CPR_SAFE_END(&cprinfo, &cpr_lock);
1014
1015		/* if the thread is to terminate */
1016		if (cachep->c_flags & CACHE_CACHEW_THREADEXIT)
1017			break;
1018
1019		/* thread is running during nofill, but just to hold lock */
1020		if (cachep->c_flags & CACHE_NOFILL)
1021			continue;
1022
1023		/* if garbage collection is to run */
1024		if (cachep->c_flags & CACHE_GARBAGE_COLLECT) {
1025			mutex_exit(&cachep->c_contentslock);
1026			cachefs_garbage_collect(cachep);
1027
1028			/*
1029			 * Prevent garbage collection from running more
1030			 * than once every 30 seconds.  This addresses
1031			 * those cases which do not allow removing
1032			 * an item from the rl by keeping gc from
1033			 * being a spin loop.
1034			 */
1035			delay(30*hz); /* XXX sam: still do this? */
1036			mutex_enter(&cachep->c_contentslock);
1037		}
1038
1039		if (cachep->c_flags & CACHE_PACKED_PENDING)
1040			cachefs_packed_pending(cachep);
1041		ASSERT(MUTEX_HELD(&cachep->c_contentslock));
1042	}
1043
1044	cachep->c_flags &= ~CACHE_CACHEW_THREADRUN;
1045	cv_broadcast(&cachep->c_cwhaltcv);
1046	CALLB_CPR_EXIT(&cprinfo);
1047	mutex_exit(&cachep->c_contentslock);
1048	mutex_destroy(&cpr_lock);
1049
1050	/* unlock the lock file */
1051	fl.l_type = F_UNLCK;
1052	fl.l_whence = 0;
1053	fl.l_start = (offset_t)0;
1054	fl.l_len = (offset_t)1024;
1055	fl.l_sysid = 0;
1056	fl.l_pid = 0;
1057	error = VOP_FRLOCK(cachep->c_lockvp, F_SETLK, &fl, FWRITE, (offset_t)0,
1058	    NULL, kcred, NULL);
1059	if (error) {
1060		cmn_err(CE_WARN, "cachefs: Can't unlock lock file\n");
1061	}
1062
1063	thread_exit();
1064	/*NOTREACHED*/
1065}
1066
1067/* queues up a request to run the garbage collection */
1068void
1069cachefs_garbage_collect_queue(cachefscache_t *cachep)
1070{
1071	cachefs_rl_listhead_t *lhp;
1072
1073	ASSERT((cachep->c_flags & (CACHE_NOCACHE|CACHE_NOFILL)) == 0);
1074	mutex_enter(&cachep->c_contentslock);
1075
1076	/* quit if there is no garbage collection thread */
1077	if ((cachep->c_flags & CACHE_CACHEW_THREADRUN) == 0) {
1078		mutex_exit(&cachep->c_contentslock);
1079		return;
1080	}
1081
1082	/* quit if garbage collection is already in progress */
1083	if (cachep->c_flags & CACHE_GARBAGE_COLLECT) {
1084		mutex_exit(&cachep->c_contentslock);
1085		return;
1086	}
1087
1088	/* quit if there is no garbage to collect */
1089	lhp = RL_HEAD(cachep, CACHEFS_RL_GC);
1090	if (lhp->rli_front == 0) {
1091		mutex_exit(&cachep->c_contentslock);
1092		return;
1093	}
1094
1095	/* indicate garbage collecting is in progress */
1096	cachep->c_flags |= CACHE_GARBAGE_COLLECT;
1097
1098	/* wake up the garbage collection thread */
1099	cv_signal(&cachep->c_cwcv);
1100
1101	mutex_exit(&cachep->c_contentslock);
1102}
1103
1104#ifdef CFSRLDEBUG
1105time_t cachefs_dbvalid = 123; /* default to non-zero junk */
1106struct kmem_cache *cachefs_rl_debug_cache = NULL;
1107static int cachefs_rl_debug_maxcount = CACHEFS_RLDB_DEF_MAXCOUNT;
1108kmutex_t cachefs_rl_debug_mutex;
1109static int cachefs_rl_debug_inuse = 0;
1110
1111void
1112cachefs_rl_debug_reclaim(void *cdrarg)
1113{
1114	extern cachefscache_t *cachefs_cachelist;
1115	cachefscache_t *cachep;
1116	int index;
1117	int error;
1118
1119	for (cachep = cachefs_cachelist; cachep != NULL;
1120	    cachep = cachep->c_next) {
1121		mutex_enter(&cachep->c_contentslock);
1122
1123		for (index = 0;
1124		    index <= cachep->c_rlinfo.rl_entries;
1125		    index++) {
1126			rl_entry_t *rlent;
1127
1128			error = cachefs_rl_entry_get(cachep, index, &rlent);
1129			if (error)
1130				break;
1131			cachefs_rl_debug_destroy(rlent);
1132		}
1133
1134		mutex_exit(&cachep->c_contentslock);
1135	}
1136}
1137
1138void
1139cachefs_rl_debug_save(rl_entry_t *rlent)
1140{
1141	rl_debug_t *rldb, *prev, *next;
1142	int count = 0;
1143
1144	mutex_enter(&cachefs_rl_debug_mutex);
1145	if (cachefs_rl_debug_cache == NULL)
1146		cachefs_rl_debug_cache =
1147		    kmem_cache_create("cachefs_rl_debug",
1148		    sizeof (rl_debug_t), 0,
1149		    NULL, NULL, cachefs_rl_debug_reclaim, NULL, NULL, 0);
1150
1151	rldb = kmem_cache_alloc(cachefs_rl_debug_cache, KM_SLEEP);
1152	++cachefs_rl_debug_inuse;
1153
1154	rldb->db_hrtime = gethrtime();
1155
1156	rldb->db_attrc = rlent->rl_attrc;
1157	rldb->db_fsck = rlent->rl_fsck;
1158	rldb->db_fsid = rlent->rl_fsid;
1159	rldb->db_fileno = rlent->rl_fileno;
1160	rldb->db_current = rlent->rl_current;
1161
1162	rldb->db_stackheight = getpcstack(rldb->db_stack,
1163	    CACHEFS_RLDB_STACKSIZE);
1164
1165	if (rlent->rl_dbvalid == cachefs_dbvalid) {
1166		rldb->db_next = rlent->rl_debug;
1167	} else {
1168		rldb->db_next = NULL;
1169		rlent->rl_dbvalid = cachefs_dbvalid;
1170	}
1171	rlent->rl_debug = rldb;
1172
1173	prev = rldb;
1174	for (rldb = rldb->db_next; rldb != NULL; rldb = next) {
1175		next = rldb->db_next;
1176		if (++count >= cachefs_rl_debug_maxcount) {
1177			if (prev != NULL)
1178				prev->db_next = NULL;
1179			kmem_cache_free(cachefs_rl_debug_cache, rldb);
1180			--cachefs_rl_debug_inuse;
1181			prev = NULL;
1182		} else {
1183			prev = rldb;
1184		}
1185	}
1186	mutex_exit(&cachefs_rl_debug_mutex);
1187}
1188
1189void
1190cachefs_rl_debug_show(rl_entry_t *rlent)
1191{
1192	rl_debug_t *rldb;
1193	hrtime_t now, elapse;
1194	timestruc_t tv;
1195	char *cname = NULL;
1196	int i;
1197
1198	mutex_enter(&cachefs_rl_debug_mutex);
1199	if (rlent->rl_dbvalid != cachefs_dbvalid) {
1200		printf("cachefs_rldb: rl entry at %lx -- no info!\n",
1201		    (uintptr_t)rlent);
1202		mutex_exit(&cachefs_rl_debug_mutex);
1203		return;
1204	}
1205
1206	now = gethrtime();
1207	hrt2ts(now, &tv);
1208
1209	printf("===== cachefs_rldb start at %ld =====\n", tv.tv_sec);
1210	printf("-==== i am thread id %lx   ====-\n", (uintptr_t)curthread);
1211
1212	for (rldb = rlent->rl_debug;
1213	    rldb != NULL;
1214	    rldb = rldb->db_next) {
1215		printf("----- cachefs_rldb record start -----\n");
1216		elapse = now - rldb->db_hrtime;
1217		hrt2ts(elapse, &tv);
1218		printf("cachefs_rldb: ago = %lds %ldus\n",
1219		    tv.tv_sec, tv.tv_nsec / 1000);
1220
1221		printf("cachefs_rldb: rl_attrc = %d\n", rldb->db_attrc);
1222		printf("cachefs_rldb: rl_fsck = %d\n", rldb->db_fsck);
1223		printf("cachefs_rldb: rl_fsid = %u\n", rldb->db_fsid);
1224		printf("cachefs_rldb: rl_fileno = %lu\n", rldb->db_fileno);
1225
1226		switch (rldb->db_current) {
1227		case CACHEFS_RL_NONE:
1228			cname = "CACHEFS_RL_NONE";
1229			break;
1230		case CACHEFS_RL_FREE:
1231			cname = "CACHEFS_RL_FREE";
1232			break;
1233		case CACHEFS_RL_GC:
1234			cname = "CACHEFS_RL_GC";
1235			break;
1236		case CACHEFS_RL_ACTIVE:
1237			cname = "CACHEFS_RL_ACTIVE";
1238			break;
1239		case CACHEFS_RL_ATTRFILE:
1240			cname = "CACHEFS_RL_ATTRFILE";
1241			break;
1242		case CACHEFS_RL_MODIFIED:
1243			cname = "CACHEFS_RL_MODIFIED";
1244			break;
1245		case CACHEFS_RL_PACKED:
1246			cname = "CACHEFS_RL_PACKED";
1247			break;
1248		case CACHEFS_RL_PACKED_PENDING:
1249			cname = "CACHEFS_RL_PACKED_PENDING";
1250			break;
1251		case CACHEFS_RL_MF:
1252			cname = "CACHEFS_MF_GC";
1253			break;
1254		}
1255		if (cname != NULL) {
1256			printf("cachefs_rldb: state = %s\n", cname);
1257		} else {
1258			printf("cachefs_rldb: undefined state %x\n",
1259			    rldb->db_current);
1260		}
1261
1262		printf("cachefs_rldb: stack trace\n");
1263		for (i = 0; i < rldb->db_stackheight; i++) {
1264			char *sym;
1265			uint_t off;
1266
1267			sym = kobj_getsymname(rldb->db_stack[i], &off);
1268			printf("cachefs_rldb:    %s+%lx\n",
1269			    sym ? sym : "?", off);
1270			delay(hz/4);
1271		}
1272
1273		printf("----- cachefs_rldb record end -----\n");
1274	}
1275
1276	mutex_exit(&cachefs_rl_debug_mutex);
1277}
1278
1279void
1280cachefs_rl_debug_destroy(rl_entry_t *rlent)
1281{
1282	rl_debug_t *rldb, *next;
1283
1284	mutex_enter(&cachefs_rl_debug_mutex);
1285	if (rlent->rl_dbvalid != cachefs_dbvalid) {
1286		rlent->rl_debug = NULL;
1287		mutex_exit(&cachefs_rl_debug_mutex);
1288		return;
1289	}
1290
1291	for (rldb = rlent->rl_debug; rldb != NULL; rldb = next) {
1292		next = rldb->db_next;
1293		kmem_cache_free(cachefs_rl_debug_cache, rldb);
1294		--cachefs_rl_debug_inuse;
1295	}
1296
1297	rlent->rl_debug = NULL;
1298	mutex_exit(&cachefs_rl_debug_mutex);
1299}
1300#endif /* CFSRLDEBUG */
1301
1302int
1303cachefs_rl_entry_get(cachefscache_t *cachep, uint_t entno, rl_entry_t **ent)
1304{
1305	rl_entry_t *rl_ent;
1306	uint_t whichwindow, winoffset;
1307	int error = 0;
1308
1309	ASSERT(MUTEX_HELD(&cachep->c_contentslock));
1310	ASSERT(entno <= cachep->c_label.cl_maxinodes); /* strictly less? */
1311#if 0
1312	ASSERT((cachep->c_flags & CACHE_NOFILL) == 0);
1313#endif
1314
1315	whichwindow = entno / CACHEFS_RLPMBS;
1316	winoffset = entno % CACHEFS_RLPMBS;
1317
1318	if ((cachep->c_rl_entries == NULL) ||
1319	    (cachep->c_rl_window != whichwindow)) {
1320		if (cachep->c_rl_entries != NULL) {
1321			error = vn_rdwr(UIO_WRITE, cachep->c_resfilevp,
1322			    (caddr_t)cachep->c_rl_entries, MAXBSIZE,
1323			    (offset_t)((cachep->c_rl_window + 1) * MAXBSIZE),
1324			    UIO_SYSSPACE, 0, RLIM_INFINITY, kcred, NULL);
1325			if (error)
1326				return (error);
1327		}
1328		else
1329			cachep->c_rl_entries = (rl_entry_t *)
1330			    cachefs_kmem_alloc(MAXBSIZE, KM_SLEEP);
1331
1332		error = vn_rdwr(UIO_READ, cachep->c_resfilevp,
1333		    (caddr_t)cachep->c_rl_entries, MAXBSIZE,
1334		    (offset_t)((whichwindow + 1) * MAXBSIZE),
1335		    UIO_SYSSPACE, 0, RLIM_INFINITY, kcred, NULL);
1336		if (error) {
1337			cachefs_kmem_free(cachep->c_rl_entries, MAXBSIZE);
1338			cachep->c_rl_entries = NULL;
1339			return (error);
1340		}
1341		cachep->c_rl_window = whichwindow;
1342	}
1343	rl_ent = &cachep->c_rl_entries[winoffset];
1344
1345	*ent = rl_ent;
1346#ifdef CFSRLDEBUG
1347	cachefs_rl_debug_save(rl_ent);
1348#endif /* CFSRLDEBUG */
1349
1350	return (error);
1351}
1352
1353static time_t
1354cachefs_gc_front_atime(cachefscache_t *cachep)
1355{
1356	char namebuf[CFS_FRONTFILE_NAME_SIZE];
1357
1358	rl_entry_t rl, *rl_ent;
1359	uint_t entno, fgsize;
1360	cfs_cid_t dircid, cid;
1361	struct fscache *fscp;
1362	cachefs_rl_listhead_t *lhp;
1363	int error;
1364
1365	struct vnode *dirvp, *filevp;
1366	struct vattr va;
1367
1368	int reledir = 0;
1369	int gotfile = 0;
1370	time_t rc = (time_t)0;
1371
1372	mutex_enter(&cachep->c_contentslock);
1373	lhp = RL_HEAD(cachep, CACHEFS_RL_GC);
1374	entno = lhp->rli_front;
1375	if (entno == 0) {
1376		mutex_exit(&cachep->c_contentslock);
1377		goto out;
1378	}
1379
1380	error = cachefs_rl_entry_get(cachep, entno, &rl_ent);
1381	if (error) {
1382		mutex_exit(&cachep->c_contentslock);
1383		goto out;
1384	}
1385	rl = *rl_ent;
1386	mutex_exit(&cachep->c_contentslock);
1387	cid.cid_fileno = rl.rl_fileno;
1388	ASSERT(rl.rl_local == 0);
1389	cid.cid_flags = 0;
1390	dircid.cid_flags = 0;
1391	mutex_enter(&cachep->c_fslistlock);
1392	if ((fscp = fscache_list_find(cachep, rl.rl_fsid)) == NULL) {
1393		mutex_exit(&cachep->c_fslistlock);
1394		goto out;
1395	}
1396
1397	if (rl.rl_attrc) {
1398		make_ascii_name(&cid, namebuf);
1399		dirvp = fscp->fs_fsattrdir;
1400	} else {
1401		dirvp = NULL;
1402		fgsize = fscp->fs_info.fi_fgsize;
1403		dircid.cid_fileno = ((cid.cid_fileno / fgsize) * fgsize);
1404		make_ascii_name(&dircid, namebuf);
1405		if (VOP_LOOKUP(fscp->fs_fscdirvp, namebuf,
1406		    &dirvp, (struct pathname *)NULL, 0,
1407		    (vnode_t *)NULL, kcred, NULL, NULL, NULL) == 0) {
1408			make_ascii_name(&cid, namebuf);
1409			reledir++;
1410		} else {
1411			mutex_exit(&cachep->c_fslistlock);
1412			goto out;
1413		}
1414	}
1415	if (dirvp && VOP_LOOKUP(dirvp, namebuf, &filevp,
1416	    (struct pathname *)NULL, 0,
1417	    (vnode_t *)NULL, kcred, NULL, NULL, NULL) == 0) {
1418		gotfile = 1;
1419	}
1420	if (reledir)
1421		VN_RELE(dirvp);
1422	mutex_exit(&cachep->c_fslistlock);
1423
1424	if (gotfile) {
1425		va.va_mask = AT_ATIME;
1426		if (VOP_GETATTR(filevp, &va, 0, kcred, NULL) == 0)
1427			rc = va.va_atime.tv_sec;
1428		VN_RELE(filevp);
1429	}
1430
1431out:
1432	return (rc);
1433}
1434