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
63 extern time_t time;
64
65 /* forward references */
66 int cachefs_rl_entry_get(cachefscache_t *, uint_t, rl_entry_t **);
67 void cachefs_garbage_collect_queue(cachefscache_t *cachep);
68 static time_t cachefs_gc_front_atime(cachefscache_t *cachep);
69 static void cachefs_garbage_collect(cachefscache_t *cachep);
70 static 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 */
80 void
cachefs_rlent_moveto(cachefscache_t * cachep,enum cachefs_rl_type type,uint_t entno,size_t blks)81 cachefs_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
90 void
cachefs_rlent_moveto_nolock(cachefscache_t * cachep,enum cachefs_rl_type type,uint_t entno,size_t blks)91 cachefs_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*/
175 void
cachefs_rlent_verify(cachefscache_t * cachep,enum cachefs_rl_type type,uint_t entno)176 cachefs_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 */
207 int
cachefs_rlent_data(cachefscache_t * cachep,rl_entry_t * valp,uint_t * entnop)208 cachefs_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 */
243 int
cachefs_rl_alloc(struct cachefscache * cachep,rl_entry_t * valp,uint_t * entnop)244 cachefs_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;
283 out:
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 */
293 void
cachefs_rl_changefileno(cachefscache_t * cachep,uint_t entno,ino64_t fileno)294 cachefs_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 */
313 void
cachefs_move_modified_to_mf(cachefscache_t * cachep,fscache_t * fscp)314 cachefs_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 */
359 void
cachefs_rl_cleanup(cachefscache_t * cachep)360 cachefs_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
417 int
cachefs_allocfile(cachefscache_t * cachep)418 cachefs_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
457 void
cachefs_freefile(cachefscache_t * cachep)458 cachefs_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*/
468 int
cachefs_allocblocks(cachefscache_t * cachep,size_t nblks,enum cachefs_rl_type type)469 cachefs_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
516 void
cachefs_freeblocks(cachefscache_t * cachep,size_t nblks,enum cachefs_rl_type type)517 cachefs_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
530 int
cachefs_victim(cachefscache_t * cachep)531 cachefs_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;
727 out:
728 if (fgp) {
729 filegrp_rele(fgp);
730 }
731 if (fscp) {
732 fscache_rele(fscp);
733 }
734 return (error);
735 }
736
737 static void
cachefs_garbage_collect(cachefscache_t * cachep)738 cachefs_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
816 static void
cachefs_packed_pending(cachefscache_t * cachep)817 cachefs_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 */
970 static time_t cachefs_ppend_time = 900;
971
972 /* main routine for the cachep worker thread */
973 void
cachefs_cachep_worker_thread(cachefscache_t * cachep)974 cachefs_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 */
1068 void
cachefs_garbage_collect_queue(cachefscache_t * cachep)1069 cachefs_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
1105 time_t cachefs_dbvalid = 123; /* default to non-zero junk */
1106 struct kmem_cache *cachefs_rl_debug_cache = NULL;
1107 static int cachefs_rl_debug_maxcount = CACHEFS_RLDB_DEF_MAXCOUNT;
1108 kmutex_t cachefs_rl_debug_mutex;
1109 static int cachefs_rl_debug_inuse = 0;
1110
1111 void
cachefs_rl_debug_reclaim(void * cdrarg)1112 cachefs_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
1138 void
cachefs_rl_debug_save(rl_entry_t * rlent)1139 cachefs_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
1189 void
cachefs_rl_debug_show(rl_entry_t * rlent)1190 cachefs_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
1279 void
cachefs_rl_debug_destroy(rl_entry_t * rlent)1280 cachefs_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
1302 int
cachefs_rl_entry_get(cachefscache_t * cachep,uint_t entno,rl_entry_t ** ent)1303 cachefs_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
1353 static time_t
cachefs_gc_front_atime(cachefscache_t * cachep)1354 cachefs_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
1431 out:
1432 return (rc);
1433 }
1434