2 * Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
20 * @APPLE_LICENSE_HEADER_END@
23 #include <sys/param.h>
24 #include <sys/systm.h>
25 #include <sys/fcntl.h>
26 #include <sys/kernel.h>
27 #include <sys/malloc.h>
29 #include <sys/vnode.h>
32 #include <hfs/hfs_endian.h>
33 #include <hfs/hfs_format.h>
34 #include <hfs/hfs_mount.h>
35 #include <hfs/hfs_hotfiles.h>
37 #include "hfscommon/headers/BTreeScanner.h"
46 * Hot File List (runtime).
48 typedef struct hotfileinfo
{
50 u_int32_t hf_temperature
;
54 typedef struct hotfilelist
{
56 u_int32_t hfl_version
;
57 time_t hfl_duration
; /* duration of sample period */
58 int hfl_count
; /* count of hot files recorded */
59 int hfl_next
; /* next file to move */
60 int hfl_totalblocks
; /* total hot file blocks */
61 int hfl_reclaimblks
; /* blocks to reclaim in HFV */
62 u_int32_t hfl_spare
[2];
63 hotfileinfo_t hfl_hotfile
[1]; /* array of hot files */
68 * Hot File Entry (runtime).
70 typedef struct hotfile_entry
{
71 struct hotfile_entry
*left
;
72 struct hotfile_entry
*right
;
74 u_int32_t temperature
;
79 * Hot File Recording Data (runtime).
81 typedef struct hotfile_data
{
82 struct hfsmount
*hfsmp
;
84 int activefiles
; /* active number of hot files */
87 hotfile_entry_t
*rootentry
;
88 hotfile_entry_t
*freelist
;
89 hotfile_entry_t
*coldest
;
90 hotfile_entry_t entries
[1];
96 * Hot File Data recording functions (in-memory binary tree).
98 static void hf_insert (hotfile_data_t
*, hotfile_entry_t
*);
99 static void hf_delete (hotfile_data_t
*, u_int32_t
, u_int32_t
);
100 static hotfile_entry_t
* hf_lookup (hotfile_data_t
*, u_int32_t
, u_int32_t
);
101 static hotfile_entry_t
* hf_coldest (hotfile_data_t
*);
102 static hotfile_entry_t
* hf_getnewentry (hotfile_data_t
*);
103 static int hf_getsortedlist (hotfile_data_t
*, hotfilelist_t
*);
104 static void hf_printtree (hotfile_entry_t
*);
107 * Hot File misc support functions.
109 static int hotfiles_collect (struct hfsmount
*, struct proc
*);
110 static int hotfiles_age (struct hfsmount
*, struct proc
*);
111 static int hotfiles_adopt (struct hfsmount
*, struct proc
*);
112 static int hotfiles_evict (struct hfsmount
*, struct proc
*);
113 static int hotfiles_refine (struct hfsmount
*, struct proc
*);
114 static int hotextents(struct hfsmount
*, HFSPlusExtentDescriptor
*);
117 * Hot File Cluster B-tree (on disk) functions.
119 static int hfc_btree_create (struct hfsmount
*, int, int);
120 static int hfc_btree_open (struct hfsmount
*, struct vnode
**);
121 static int hfc_btree_close (struct hfsmount
*, struct vnode
*);
122 static int hfc_comparekeys (HotFileKey
*, HotFileKey
*);
125 char hfc_tag
[] = "CLUSTERED HOT FILES B-TREE ";
129 *========================================================================
130 * HOT FILE INTERFACE ROUTINES
131 *========================================================================
135 * Start recording the hotest files on a file system.
140 hfs_recording_start(struct hfsmount
*hfsmp
, struct proc
*p
)
142 hotfile_data_t
*hotdata
;
148 if ((hfsmp
->hfs_flags
& HFS_READ_ONLY
) ||
149 (hfsmp
->jnl
== NULL
) ||
150 (hfsmp
->hfs_flags
& HFS_METADATA_ZONE
) == 0) {
153 if (HFSTOVCB(hfsmp
)->freeBlocks
< (2 * hfsmp
->hfs_hotfile_maxblks
)) {
156 if (hfsmp
->hfc_stage
!= HFC_IDLE
) {
159 hfsmp
->hfc_stage
= HFC_BUSY
;
162 * Dump previous recording data.
164 if (hfsmp
->hfc_recdata
) {
167 tmp
= hfsmp
->hfc_recdata
;
168 hfsmp
->hfc_recdata
= NULL
;
173 * On first startup check for suspended recording.
175 if (hfsmp
->hfc_timebase
== 0 &&
176 hfc_btree_open(hfsmp
, &hfsmp
->hfc_filevp
) == 0) {
177 HotFilesInfo hotfileinfo
;
179 if ((BTGetUserData(VTOF(hfsmp
->hfc_filevp
), &hotfileinfo
,
180 sizeof(hotfileinfo
)) == 0) &&
181 (SWAP_BE32 (hotfileinfo
.magic
) == HFC_MAGIC
) &&
182 (SWAP_BE32 (hotfileinfo
.timeleft
) > 0) &&
183 (SWAP_BE32 (hotfileinfo
.timebase
) > 0)) {
184 hfsmp
->hfc_maxfiles
= SWAP_BE32 (hotfileinfo
.maxfilecnt
);
185 hfsmp
->hfc_timeout
= SWAP_BE32 (hotfileinfo
.timeleft
) + time
.tv_sec
;
186 hfsmp
->hfc_timebase
= SWAP_BE32 (hotfileinfo
.timebase
);
188 printf("HFS: resume recording hot files (%d left)\n", SWAP_BE32 (hotfileinfo
.timeleft
));
191 hfsmp
->hfc_maxfiles
= HFC_DEFAULT_FILE_COUNT
;
192 hfsmp
->hfc_timebase
= time
.tv_sec
+ 1;
193 hfsmp
->hfc_timeout
= hfsmp
->hfc_timebase
+ HFC_DEFAULT_DURATION
;
195 (void) hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
196 hfsmp
->hfc_filevp
= NULL
;
198 struct cat_attr cattr
;
202 * Make sure a btree file exists.
204 cnid
= GetFileInfo(HFSTOVCB(hfsmp
), kRootDirID
, HFC_FILENAME
, &cattr
, NULL
);
206 !S_ISREG(cattr
.ca_mode
) &&
207 (error
= hfc_btree_create(hfsmp
, HFSTOVCB(hfsmp
)->blockSize
, HFC_DEFAULT_FILE_COUNT
))) {
208 hfsmp
->hfc_stage
= HFC_IDLE
;
209 wakeup((caddr_t
)&hfsmp
->hfc_stage
);
213 printf("HFS: begin recording hot files\n");
215 hfsmp
->hfc_maxfiles
= HFC_DEFAULT_FILE_COUNT
;
216 hfsmp
->hfc_timeout
= time
.tv_sec
+ HFC_DEFAULT_DURATION
;
218 /* Reset time base. */
219 if (hfsmp
->hfc_timebase
== 0) {
220 hfsmp
->hfc_timebase
= time
.tv_sec
+ 1;
222 u_int32_t cumulativebase
;
223 u_int32_t oldbase
= hfsmp
->hfc_timebase
;
225 cumulativebase
= hfsmp
->hfc_timeout
- (HFC_CUMULATIVE_CYCLES
* HFC_DEFAULT_DURATION
);
226 hfsmp
->hfc_timebase
= MAX(hfsmp
->hfc_timebase
, cumulativebase
);
230 if ((hfsmp
->hfc_maxfiles
== 0) ||
231 (hfsmp
->hfc_maxfiles
> HFC_MAXIMUM_FILE_COUNT
)) {
232 hfsmp
->hfc_maxfiles
= HFC_DEFAULT_FILE_COUNT
;
234 maxentries
= hfsmp
->hfc_maxfiles
;
236 size
= sizeof(hotfile_data_t
) + (maxentries
* sizeof(hotfile_entry_t
));
237 MALLOC(hotdata
, hotfile_data_t
*, size
, M_TEMP
, M_WAITOK
);
238 bzero(hotdata
, size
);
240 for (i
= 1; i
< maxentries
; i
++)
241 hotdata
->entries
[i
-1].right
= &hotdata
->entries
[i
];
243 hotdata
->freelist
= &hotdata
->entries
[0];
245 * Establish minimum temperature and maximum file size.
247 hotdata
->threshold
= HFC_MINIMUM_TEMPERATURE
;
248 hotdata
->maxblocks
= HFC_MAXIMUM_FILESIZE
/ HFSTOVCB(hfsmp
)->blockSize
;
249 hotdata
->hfsmp
= hfsmp
;
251 hfsmp
->hfc_recdata
= hotdata
;
253 hfsmp
->hfc_stage
= HFC_RECORDING
;
254 wakeup((caddr_t
)&hfsmp
->hfc_stage
);
259 * Stop recording the hotest files on a file system.
263 hfs_recording_stop(struct hfsmount
*hfsmp
, struct proc
*p
)
265 hotfile_data_t
*hotdata
;
266 hotfilelist_t
*listp
;
268 enum hfc_stage newstage
= HFC_IDLE
;
273 if (hfsmp
->hfc_stage
!= HFC_RECORDING
)
276 hotfiles_collect(hfsmp
, p
);
278 if (hfsmp
->hfc_stage
!= HFC_RECORDING
)
281 hfsmp
->hfc_stage
= HFC_BUSY
;
284 * Convert hot file data into a simple file id list....
286 * then dump the sample data
289 printf("HFS: end of hot file recording\n");
291 hotdata
= (hotfile_data_t
*)hfsmp
->hfc_recdata
;
294 hfsmp
->hfc_recdata
= NULL
;
295 hfsmp
->hfc_stage
= HFC_EVALUATION
;
296 wakeup((caddr_t
)&hfsmp
->hfc_stage
);
299 printf(" curentries: %d\n", hotdata
->activefiles
);
302 * If no hot files recorded then we're done.
304 if (hotdata
->rootentry
== NULL
) {
309 /* Open the B-tree file for writing... */
310 if (hfsmp
->hfc_filevp
)
311 panic("hfs_recording_stop: hfc_filevp exists (vp = 0x%08x)", hfsmp
->hfc_filevp
);
313 error
= hfc_btree_open(hfsmp
, &hfsmp
->hfc_filevp
);
319 * Age the previous set of clustered hot files.
321 error
= hotfiles_age(hfsmp
, p
);
323 (void) hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
324 hfsmp
->hfc_filevp
= NULL
;
329 * Create a sorted list of hotest files.
331 size
= sizeof(hotfilelist_t
);
332 size
+= sizeof(hotfileinfo_t
) * (hotdata
->activefiles
- 1);
333 MALLOC(listp
, hotfilelist_t
*, size
, M_TEMP
, M_WAITOK
);
336 hf_getsortedlist(hotdata
, listp
);
337 listp
->hfl_duration
= time
.tv_sec
- hfsmp
->hfc_timebase
;
338 hfsmp
->hfc_recdata
= listp
;
341 * Account for duplicates.
343 error
= hotfiles_refine(hfsmp
, p
);
345 (void) hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
346 hfsmp
->hfc_filevp
= NULL
;
351 * Compute the amount of space to reclaim...
353 if (listp
->hfl_totalblocks
> hfsmp
->hfs_hotfile_freeblks
) {
354 listp
->hfl_reclaimblks
=
355 MIN(listp
->hfl_totalblocks
, hfsmp
->hfs_hotfile_maxblks
) -
356 hfsmp
->hfs_hotfile_freeblks
;
358 printf("hfs_recording_stop: need to reclaim %d blocks\n", listp
->hfl_reclaimblks
);
360 if (listp
->hfl_reclaimblks
)
361 newstage
= HFC_EVICTION
;
363 newstage
= HFC_ADOPTION
;
365 newstage
= HFC_ADOPTION
;
368 if (newstage
== HFC_ADOPTION
&& listp
->hfl_totalblocks
== 0) {
369 (void) hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
370 hfsmp
->hfc_filevp
= NULL
;
375 if (newstage
== HFC_EVICTION
)
376 printf("HFS: evicting coldest files\n");
377 else if (newstage
== HFC_ADOPTION
)
378 printf("HFS: adopting hotest files\n");
380 FREE(hotdata
, M_TEMP
);
382 hfsmp
->hfc_stage
= newstage
;
383 wakeup((caddr_t
)&hfsmp
->hfc_stage
);
388 * Suspend recording the hotest files on a file system.
392 hfs_recording_suspend(struct hfsmount
*hfsmp
, struct proc
*p
)
394 HotFilesInfo hotfileinfo
;
395 hotfile_data_t
*hotdata
;
398 if (hfsmp
->hfc_stage
!= HFC_RECORDING
)
401 hotdata
= (hotfile_data_t
*)hfsmp
->hfc_recdata
;
402 if (hotdata
== NULL
) {
403 hfsmp
->hfc_stage
= HFC_DISABLED
;
406 hfsmp
->hfc_stage
= HFC_BUSY
;
409 printf("HFS: suspend hot file recording\n");
411 error
= hfc_btree_open(hfsmp
, &hfsmp
->hfc_filevp
);
413 printf("hfs_recording_suspend: err %d opening btree\n", error
);
417 hfs_global_shared_lock_acquire(hfsmp
);
419 if (journal_start_transaction(hfsmp
->jnl
) != 0) {
420 hfs_global_shared_lock_release(hfsmp
);
425 vn_lock(hfsmp
->hfc_filevp
, LK_EXCLUSIVE
| LK_RETRY
, p
);
427 hotfileinfo
.magic
= SWAP_BE32 (HFC_MAGIC
);
428 hotfileinfo
.version
= SWAP_BE32 (HFC_VERSION
);
429 hotfileinfo
.duration
= SWAP_BE32 (HFC_DEFAULT_DURATION
);
430 hotfileinfo
.timebase
= SWAP_BE32 (hfsmp
->hfc_timebase
);
431 hotfileinfo
.timeleft
= SWAP_BE32 (hfsmp
->hfc_timeout
- time
.tv_sec
);
432 hotfileinfo
.threshold
= SWAP_BE32 (hotdata
->threshold
);
433 hotfileinfo
.maxfileblks
= SWAP_BE32 (hotdata
->maxblocks
);
434 hotfileinfo
.maxfilecnt
= SWAP_BE32 (HFC_DEFAULT_FILE_COUNT
);
435 strcpy(hotfileinfo
.tag
, hfc_tag
);
436 (void) BTSetUserData(VTOF(hfsmp
->hfc_filevp
), &hotfileinfo
, sizeof(hotfileinfo
));
438 (void) VOP_UNLOCK(hfsmp
->hfc_filevp
, 0, p
);
440 journal_end_transaction(hfsmp
->jnl
);
442 hfs_global_shared_lock_release(hfsmp
);
444 (void) hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
445 hfsmp
->hfc_filevp
= NULL
;
447 FREE(hotdata
, M_TEMP
);
449 hfsmp
->hfc_stage
= HFC_DISABLED
;
450 wakeup((caddr_t
)&hfsmp
->hfc_stage
);
455 * Abort a hot file recording session.
459 hfs_recording_abort(struct hfsmount
*hfsmp
, struct proc
*p
)
463 if (hfsmp
->hfc_stage
== HFC_DISABLED
)
466 if (hfsmp
->hfc_stage
== HFC_BUSY
) {
467 (void) tsleep((caddr_t
)&hfsmp
->hfc_stage
, PINOD
, "hfs_recording_abort", 0);
469 hfsmp
->hfc_stage
= HFC_BUSY
;
471 printf("HFS: terminate hot file recording\n");
473 if (hfsmp
->hfc_recdata
) {
474 tmp
= hfsmp
->hfc_recdata
;
475 hfsmp
->hfc_recdata
= NULL
;
478 hfsmp
->hfc_stage
= HFC_DISABLED
;
479 wakeup((caddr_t
)&hfsmp
->hfc_stage
);
488 hfs_recording_init(struct hfsmount
*hfsmp
, struct proc
*p
)
491 CatalogRecord
* datap
;
493 HFSPlusCatalogFile
*filep
;
494 BTScanState scanstate
;
495 BTreeIterator
* iterator
;
496 FSBufferDescriptor record
;
498 filefork_t
* filefork
;
500 struct cat_attr cattr
;
504 int inserted
= 0; /* debug variables */
508 * If the Hot File btree exists then metadata zone is ready.
510 cnid
= GetFileInfo(HFSTOVCB(hfsmp
), kRootDirID
, HFC_FILENAME
, &cattr
, NULL
);
511 if (cnid
!= 0 && S_ISREG(cattr
.ca_mode
)) {
512 if (hfsmp
->hfc_stage
== HFC_DISABLED
)
513 hfsmp
->hfc_stage
= HFC_IDLE
;
517 * For now, only the boot volume is supported.
519 if ((HFSTOVFS(hfsmp
)->mnt_flag
& MNT_ROOTFS
) == 0) {
520 hfsmp
->hfs_flags
&= ~HFS_METADATA_ZONE
;
523 error
= hfc_btree_create(hfsmp
, HFSTOVCB(hfsmp
)->blockSize
, HFC_DEFAULT_FILE_COUNT
);
528 * Open the Hot File B-tree file for writing.
530 if (hfsmp
->hfc_filevp
)
531 panic("hfs_recording_init: hfc_filevp exists (vp = 0x%08x)", hfsmp
->hfc_filevp
);
532 error
= hfc_btree_open(hfsmp
, &hfsmp
->hfc_filevp
);
536 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
537 bzero(iterator
, sizeof(*iterator
));
538 key
= (HotFileKey
*) &iterator
->key
;
539 key
->keyLength
= HFC_KEYLENGTH
;
541 record
.bufferAddress
= &data
;
542 record
.itemSize
= sizeof(u_int32_t
);
543 record
.itemCount
= 1;
545 printf("Evaluating space for \"%s\" metadata zone...\n", HFSTOVCB(hfsmp
)->vcbVN
);
548 * Get ready to scan the Catalog file.
550 error
= BTScanInitialize(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), 0, 0, 0,
551 kCatSearchBufferSize
, &scanstate
);
553 printf("hfs_recording_init: err %d BTScanInit\n", error
);
558 * The writes to Hot File B-tree file are journaled.
560 hfs_global_shared_lock_acquire(hfsmp
);
562 if (journal_start_transaction(hfsmp
->jnl
) != 0) {
563 hfs_global_shared_lock_release(hfsmp
);
568 vn_lock(hfsmp
->hfc_filevp
, LK_EXCLUSIVE
| LK_RETRY
, p
);
569 filefork
= VTOF(hfsmp
->hfc_filevp
);
572 * Visit all the catalog btree leaf records.
575 error
= BTScanNextRecord(&scanstate
, 0, (void **)&keyp
, (void **)&datap
, &dataSize
);
577 if (error
== btNotFound
)
580 printf("hfs_recording_init: err %d BTScanNext\n", error
);
583 if ((datap
->recordType
!= kHFSPlusFileRecord
) ||
584 (dataSize
!= sizeof(HFSPlusCatalogFile
))) {
587 filep
= (HFSPlusCatalogFile
*)datap
;
589 if (filep
->dataFork
.totalBlocks
== 0) {
593 * Any file that has blocks inside the hot file
594 * space is recorded for later eviction.
596 * For now, resource forks are ignored.
598 if (!hotextents(hfsmp
, &filep
->dataFork
.extents
[0])) {
601 cnid
= filep
->fileID
;
603 /* Skip over journal files. */
604 if (cnid
== hfsmp
->hfs_jnlfileid
|| cnid
== hfsmp
->hfs_jnlinfoblkid
) {
608 * XXX - need to skip quota files as well.
611 /* Insert a hot file entry. */
612 key
->keyLength
= HFC_KEYLENGTH
;
613 key
->temperature
= HFC_MINIMUM_TEMPERATURE
;
617 error
= BTInsertRecord(filefork
, iterator
, &record
, sizeof(data
));
619 printf("hfs_recording_init: BTInsertRecord failed %d (fileid %d)\n", error
, key
->fileID
);
620 error
= MacToVFSError(error
);
624 /* Insert the corresponding thread record. */
625 key
->keyLength
= HFC_KEYLENGTH
;
626 key
->temperature
= HFC_LOOKUPTAG
;
629 data
= HFC_MINIMUM_TEMPERATURE
;
630 error
= BTInsertRecord(filefork
, iterator
, &record
, sizeof(data
));
632 printf("hfs_recording_init: BTInsertRecord failed %d (fileid %d)\n", error
, key
->fileID
);
633 error
= MacToVFSError(error
);
638 (void) BTFlushPath(filefork
);
639 (void) VOP_UNLOCK(hfsmp
->hfc_filevp
, 0, p
);
642 journal_end_transaction(hfsmp
->jnl
);
644 hfs_global_shared_lock_release(hfsmp
);
646 printf("%d files identified out of %d\n", inserted
, filecount
);
650 (void) BTScanTerminate(&scanstate
, &data
, &data
, &data
);
652 FREE(iterator
, M_TEMP
);
653 if (hfsmp
->hfc_filevp
) {
654 (void) hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
655 hfsmp
->hfc_filevp
= NULL
;
658 hfsmp
->hfc_stage
= HFC_IDLE
;
664 * Use sync to perform ocassional background work.
668 hfs_hotfilesync(struct hfsmount
*hfsmp
, struct proc
*p
)
670 if ((HFSTOVFS(hfsmp
)->mnt_kern_flag
& MNTK_UNMOUNT
) == 0 && hfsmp
->hfc_stage
) {
671 switch (hfsmp
->hfc_stage
) {
673 (void) hfs_recording_start(hfsmp
, p
);
677 if (time
.tv_sec
> hfsmp
->hfc_timeout
)
678 (void) hfs_recording_stop(hfsmp
, p
);
682 (void) hotfiles_evict(hfsmp
, p
);
686 (void) hotfiles_adopt(hfsmp
, p
);
694 * Add a hot file to the recording list.
696 * This can happen when a hot file gets reclaimed or at the
697 * end of the recording period for any active hot file.
699 * NOTE: Since both the data and resource fork can be hot,
700 * there can be two entries for the same file id.
705 hfs_addhotfile(struct vnode
*vp
)
707 hotfile_data_t
*hotdata
;
708 hotfile_entry_t
*entry
;
712 u_int32_t temperature
;
715 if (hfsmp
->hfc_stage
!= HFC_RECORDING
)
718 if (!(vp
->v_type
== VREG
|| vp
->v_type
== VLNK
) ||
719 (vp
->v_flag
& (VSYSTEM
| VSWAP
))) {
722 /* Skip resource forks for now. */
723 if (VNODE_IS_RSRC(vp
)) {
726 if ((hotdata
= (hotfile_data_t
*)hfsmp
->hfc_recdata
) == NULL
) {
732 if ((ffp
->ff_bytesread
== 0) ||
733 (ffp
->ff_blocks
== 0) ||
734 (ffp
->ff_blocks
> hotdata
->maxblocks
) ||
735 (cp
->c_flag
& (C_DELETED
| C_NOEXISTS
)) ||
736 (cp
->c_flags
& UF_NODUMP
) ||
737 (cp
->c_atime
< hfsmp
->hfc_timebase
)) {
741 temperature
= ffp
->ff_bytesread
/ ffp
->ff_size
;
742 if (temperature
< hotdata
->threshold
) {
746 * If there is room or this file is hotter than
747 * the coldest one then add it to the list.
750 if ((hotdata
->activefiles
< hfsmp
->hfc_maxfiles
) ||
751 (hotdata
->coldest
== NULL
) ||
752 (temperature
> hotdata
->coldest
->temperature
)) {
754 entry
= hf_getnewentry(hotdata
);
755 entry
->temperature
= temperature
;
756 entry
->fileid
= cp
->c_fileid
;
757 entry
->blocks
= ffp
->ff_blocks
;
758 hf_insert(hotdata
, entry
);
766 * Remove a hot file to the recording list.
768 * This can happen when a hot file becomes
769 * an active vnode (active hot files are
770 * not kept in the recording list until the
771 * end of the recording period).
776 hfs_removehotfile(struct vnode
*vp
)
778 hotfile_data_t
*hotdata
;
782 u_int32_t temperature
;
785 if (hfsmp
->hfc_stage
!= HFC_RECORDING
)
788 if (!(vp
->v_type
== VREG
|| vp
->v_type
== VLNK
) ||
789 (vp
->v_flag
& (VSYSTEM
| VSWAP
))) {
792 if ((hotdata
= (hotfile_data_t
*)hfsmp
->hfc_recdata
) == NULL
)
798 if ((ffp
->ff_bytesread
== 0) || (ffp
->ff_blocks
== 0) ||
799 (cp
->c_atime
< hfsmp
->hfc_timebase
)) {
803 temperature
= ffp
->ff_bytesread
/ ffp
->ff_size
;
804 if (temperature
< hotdata
->threshold
)
807 if (hotdata
->coldest
&& (temperature
>= hotdata
->coldest
->temperature
)) {
809 hf_delete(hotdata
, VTOC(vp
)->c_fileid
, temperature
);
818 *========================================================================
819 * HOT FILE MAINTENANCE ROUTINES
820 *========================================================================
824 * Add all active hot files to the recording list.
827 hotfiles_collect(struct hfsmount
*hfsmp
, struct proc
*p
)
829 struct mount
*mp
= HFSTOVFS(hfsmp
);
830 struct vnode
*nvp
, *vp
;
834 if (vfs_busy(mp
, LK_NOWAIT
, 0, p
))
837 simple_lock(&mntvnode_slock
);
838 for (vp
= mp
->mnt_vnodelist
.lh_first
; vp
!= NULL
; vp
= nvp
) {
839 if (vp
->v_mount
!= mp
) {
840 simple_unlock(&mntvnode_slock
);
843 simple_lock(&vp
->v_interlock
);
844 nvp
= vp
->v_mntvnodes
.le_next
;
846 if ((vp
->v_flag
& VSYSTEM
) ||
847 !(vp
->v_type
== VREG
|| vp
->v_type
== VLNK
)) {
848 simple_unlock(&vp
->v_interlock
);
853 if (cp
== NULL
|| vp
->v_flag
& (VXLOCK
|VORECLAIM
)) {
854 simple_unlock(&vp
->v_interlock
);
858 simple_unlock(&mntvnode_slock
);
859 error
= vget(vp
, LK_EXCLUSIVE
| LK_NOWAIT
| LK_INTERLOCK
, p
);
863 simple_lock(&mntvnode_slock
);
866 (void) hfs_addhotfile(vp
);
869 simple_lock(&mntvnode_slock
);
872 simple_unlock(&mntvnode_slock
);
881 * Update the data of a btree record
882 * This is called from within BTUpdateRecord.
885 update_callback(const HotFileKey
*key
, u_int32_t
*data
, u_int16_t datalen
, u_int32_t
*state
)
887 if (key
->temperature
== HFC_LOOKUPTAG
)
893 * Identify files already in hot area.
896 hotfiles_refine(struct hfsmount
*hfsmp
, struct proc
*p
)
898 BTreeIterator
* iterator
;
901 filefork_t
* filefork
;
902 hotfilelist_t
*listp
;
903 FSBufferDescriptor record
;
910 if ((listp
= (hotfilelist_t
*)hfsmp
->hfc_recdata
) == NULL
)
913 mp
= HFSTOVFS(hfsmp
);
915 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
916 bzero(iterator
, sizeof(*iterator
));
917 key
= (HotFileKey
*) &iterator
->key
;
919 record
.bufferAddress
= &data
;
920 record
.itemSize
= sizeof(u_int32_t
);
921 record
.itemCount
= 1;
923 hfs_global_shared_lock_acquire(hfsmp
);
925 if (journal_start_transaction(hfsmp
->jnl
) != 0) {
926 hfs_global_shared_lock_release(hfsmp
);
931 vn_lock(hfsmp
->hfc_filevp
, LK_EXCLUSIVE
| LK_RETRY
, p
);
932 filefork
= VTOF(hfsmp
->hfc_filevp
);
934 for (i
= 0; i
< listp
->hfl_count
; ++i
) {
936 * Check if entry (thread) is already in hot area.
938 key
->keyLength
= HFC_KEYLENGTH
;
939 key
->temperature
= HFC_LOOKUPTAG
;
940 key
->fileID
= listp
->hfl_hotfile
[i
].hf_fileid
;
942 (void) BTInvalidateHint(iterator
);
943 if (BTSearchRecord(filefork
, iterator
, &record
, NULL
, iterator
) != 0) {
944 continue; /* not in hot area, so skip */
948 * Update thread entry with latest temperature.
950 error
= BTUpdateRecord(filefork
, iterator
,
951 (IterateCallBackProcPtr
)update_callback
,
952 &listp
->hfl_hotfile
[i
].hf_temperature
);
954 printf("hotfiles_refine: BTUpdateRecord failed %d (file %d)\n", error
, key
->fileID
);
955 error
= MacToVFSError(error
);
959 * Re-key entry with latest temperature.
961 key
->keyLength
= HFC_KEYLENGTH
;
962 key
->temperature
= data
;
963 key
->fileID
= listp
->hfl_hotfile
[i
].hf_fileid
;
965 /* Pick up record data. */
966 (void) BTInvalidateHint(iterator
);
967 (void) BTSearchRecord(filefork
, iterator
, &record
, NULL
, iterator
);
968 error
= BTDeleteRecord(filefork
, iterator
);
970 printf("hotfiles_refine: BTDeleteRecord failed %d (file %d)\n", error
, key
->fileID
);
971 error
= MacToVFSError(error
);
974 key
->keyLength
= HFC_KEYLENGTH
;
975 key
->temperature
= listp
->hfl_hotfile
[i
].hf_temperature
;
976 key
->fileID
= listp
->hfl_hotfile
[i
].hf_fileid
;
978 error
= BTInsertRecord(filefork
, iterator
, &record
, sizeof(data
));
980 printf("hotfiles_refine: BTInsertRecord failed %d (file %d)\n", error
, key
->fileID
);
981 error
= MacToVFSError(error
);
986 * Invalidate this entry in the list.
988 listp
->hfl_hotfile
[i
].hf_temperature
= 0;
989 listp
->hfl_totalblocks
-= listp
->hfl_hotfile
[i
].hf_blocks
;
993 (void) BTFlushPath(filefork
);
994 (void) VOP_UNLOCK(hfsmp
->hfc_filevp
, 0, p
);
997 journal_end_transaction(hfsmp
->jnl
);
999 hfs_global_shared_lock_release(hfsmp
);
1001 FREE(iterator
, M_TEMP
);
1006 * Move new hot files into hot area.
1009 hotfiles_adopt(struct hfsmount
*hfsmp
, struct proc
*p
)
1011 BTreeIterator
* iterator
;
1014 filefork_t
* filefork
;
1015 hotfilelist_t
*listp
;
1016 FSBufferDescriptor record
;
1019 enum hfc_stage stage
;
1025 int startedtrans
= 0;
1026 int aquiredlock
= 0;
1028 if ((listp
= (hotfilelist_t
*)hfsmp
->hfc_recdata
) == NULL
)
1031 if (hfsmp
->hfc_stage
!= HFC_ADOPTION
) {
1034 stage
= hfsmp
->hfc_stage
;
1035 hfsmp
->hfc_stage
= HFC_BUSY
;
1037 mp
= HFSTOVFS(hfsmp
);
1039 last
= listp
->hfl_next
+ HFC_FILESPERSYNC
;
1040 if (last
> listp
->hfl_count
)
1041 last
= listp
->hfl_count
;
1043 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
1044 bzero(iterator
, sizeof(*iterator
));
1045 key
= (HotFileKey
*) &iterator
->key
;
1046 key
->keyLength
= HFC_KEYLENGTH
;
1048 record
.bufferAddress
= &data
;
1049 record
.itemSize
= sizeof(u_int32_t
);
1050 record
.itemCount
= 1;
1052 vn_lock(hfsmp
->hfc_filevp
, LK_EXCLUSIVE
| LK_RETRY
, p
);
1053 filefork
= VTOF(hfsmp
->hfc_filevp
);
1055 for (i
= listp
->hfl_next
; (i
< last
) && (blksmoved
< HFC_BLKSPERSYNC
); ++i
) {
1057 * Skip invalid entries (already in hot area).
1059 if (listp
->hfl_hotfile
[i
].hf_temperature
== 0) {
1064 * Acquire a vnode for this file.
1066 error
= VFS_VGET(mp
, &listp
->hfl_hotfile
[i
].hf_fileid
, &vp
);
1068 if (error
== ENOENT
) {
1071 continue; /* stale entry, go to next */
1075 if (vp
->v_type
!= VREG
&& vp
->v_type
!= VLNK
) {
1076 printf("hotfiles_adopt: huh, not a file %d (%d)\n", listp
->hfl_hotfile
[i
].hf_fileid
, VTOC(vp
)->c_cnid
);
1078 listp
->hfl_hotfile
[i
].hf_temperature
== 0;
1080 continue; /* stale entry, go to next */
1082 if (hotextents(hfsmp
, &VTOF(vp
)->ff_extents
[0])) {
1084 listp
->hfl_hotfile
[i
].hf_temperature
== 0;
1086 listp
->hfl_totalblocks
-= listp
->hfl_hotfile
[i
].hf_blocks
;
1087 continue; /* stale entry, go to next */
1089 fileblocks
= VTOF(vp
)->ff_blocks
;
1090 if (fileblocks
> hfsmp
->hfs_hotfile_freeblks
) {
1093 listp
->hfl_totalblocks
-= fileblocks
;
1094 continue; /* entry too big, go to next */
1097 if ((blksmoved
> 0) &&
1098 (blksmoved
+ fileblocks
) > HFC_BLKSPERSYNC
) {
1102 /* Start a new transaction. */
1103 hfs_global_shared_lock_acquire(hfsmp
);
1106 if (journal_start_transaction(hfsmp
->jnl
) != 0) {
1114 error
= hfs_relocate(vp
, hfsmp
->hfs_hotfile_start
, p
->p_ucred
, p
);
1119 /* Keep hot file free space current. */
1120 hfsmp
->hfs_hotfile_freeblks
-= fileblocks
;
1121 listp
->hfl_totalblocks
-= fileblocks
;
1123 /* Insert hot file entry */
1124 key
->keyLength
= HFC_KEYLENGTH
;
1125 key
->temperature
= listp
->hfl_hotfile
[i
].hf_temperature
;
1126 key
->fileID
= listp
->hfl_hotfile
[i
].hf_fileid
;
1128 if (VTOC(vp
)->c_desc
.cd_nameptr
)
1129 data
= *(u_int32_t
*)(VTOC(vp
)->c_desc
.cd_nameptr
);
1133 error
= BTInsertRecord(filefork
, iterator
, &record
, sizeof(data
));
1135 printf("hotfiles_adopt: BTInsertRecord failed %d (fileid %d)\n", error
, key
->fileID
);
1136 error
= MacToVFSError(error
);
1141 /* Insert thread record */
1142 key
->keyLength
= HFC_KEYLENGTH
;
1143 key
->temperature
= HFC_LOOKUPTAG
;
1144 key
->fileID
= listp
->hfl_hotfile
[i
].hf_fileid
;
1146 data
= listp
->hfl_hotfile
[i
].hf_temperature
;
1147 error
= BTInsertRecord(filefork
, iterator
, &record
, sizeof(data
));
1149 printf("hotfiles_adopt: BTInsertRecord failed %d (fileid %d)\n", error
, key
->fileID
);
1150 error
= MacToVFSError(error
);
1154 (void) BTFlushPath(filefork
);
1156 /* Transaction complete. */
1158 journal_end_transaction(hfsmp
->jnl
);
1161 hfs_global_shared_lock_release(hfsmp
);
1164 blksmoved
+= fileblocks
;
1166 if (listp
->hfl_next
>= listp
->hfl_count
) {
1169 if (hfsmp
->hfs_hotfile_freeblks
<= 0) {
1171 printf("hotfiles_adopt: free space exhausted (%d)\n", hfsmp
->hfs_hotfile_freeblks
);
1178 printf("hotfiles_adopt: [%d] adopted %d blocks (%d left)\n", listp
->hfl_next
, blksmoved
, listp
->hfl_totalblocks
);
1180 /* Finish any outstanding transactions. */
1182 (void) BTFlushPath(filefork
);
1183 journal_end_transaction(hfsmp
->jnl
);
1187 hfs_global_shared_lock_release(hfsmp
);
1190 (void) VOP_UNLOCK(hfsmp
->hfc_filevp
, 0, p
);
1192 if ((listp
->hfl_next
>= listp
->hfl_count
) || (hfsmp
->hfs_hotfile_freeblks
<= 0)) {
1194 printf("hotfiles_adopt: all done relocating %d files\n", listp
->hfl_count
);
1195 printf("hotfiles_adopt: %d blocks free in hot file band\n", hfsmp
->hfs_hotfile_freeblks
);
1199 FREE(iterator
, M_TEMP
);
1201 if (stage
!= HFC_ADOPTION
&& hfsmp
->hfc_filevp
) {
1202 (void) hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
1203 hfsmp
->hfc_filevp
= NULL
;
1205 hfsmp
->hfc_stage
= stage
;
1206 wakeup((caddr_t
)&hfsmp
->hfc_stage
);
1211 * Reclaim space by evicting the coldest files.
1214 hotfiles_evict(struct hfsmount
*hfsmp
, struct proc
*p
)
1216 BTreeIterator
* iterator
;
1220 filefork_t
* filefork
;
1221 hotfilelist_t
*listp
;
1222 enum hfc_stage stage
;
1227 int startedtrans
= 0;
1228 int aquiredlock
= 0;
1230 if (hfsmp
->hfc_stage
!= HFC_EVICTION
) {
1234 if ((listp
= (hotfilelist_t
*)hfsmp
->hfc_recdata
) == NULL
)
1237 stage
= hfsmp
->hfc_stage
;
1238 hfsmp
->hfc_stage
= HFC_BUSY
;
1240 mp
= HFSTOVFS(hfsmp
);
1241 filesmoved
= blksmoved
= 0;
1243 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
1244 bzero(iterator
, sizeof(*iterator
));
1245 key
= (HotFileKey
*) &iterator
->key
;
1247 vn_lock(hfsmp
->hfc_filevp
, LK_EXCLUSIVE
| LK_RETRY
, p
);
1248 filefork
= VTOF(hfsmp
->hfc_filevp
);
1250 while (listp
->hfl_reclaimblks
> 0 &&
1251 blksmoved
< HFC_BLKSPERSYNC
&&
1252 filesmoved
< HFC_FILESPERSYNC
) {
1255 * Obtain the first record (ie the coldest one).
1257 if (BTIterateRecord(filefork
, kBTreeFirstRecord
, iterator
, NULL
, NULL
) != 0) {
1259 printf("hotfiles_evict: no more records\n");
1262 stage
= HFC_ADOPTION
;
1265 if (key
->keyLength
!= HFC_KEYLENGTH
) {
1266 printf("hotfiles_evict: invalid key length %d\n", key
->keyLength
);
1270 if (key
->temperature
== HFC_LOOKUPTAG
) {
1272 printf("hotfiles_evict: ran into thread records\n");
1275 stage
= HFC_ADOPTION
;
1279 * Aquire the vnode for this file.
1281 error
= VFS_VGET(mp
, &key
->fileID
, &vp
);
1283 /* Start a new transaction. */
1284 hfs_global_shared_lock_acquire(hfsmp
);
1287 if (journal_start_transaction(hfsmp
->jnl
) != 0) {
1296 if (error
== ENOENT
) {
1297 (void) BTDeleteRecord(filefork
, iterator
);
1298 key
->temperature
= HFC_LOOKUPTAG
;
1299 (void) BTDeleteRecord(filefork
, iterator
);
1300 goto next
; /* stale entry, go to next */
1302 printf("hotfiles_evict: err %d getting file %d (%d)\n",
1303 error
, key
->fileID
);
1307 if (vp
->v_type
!= VREG
&& vp
->v_type
!= VLNK
) {
1308 printf("hotfiles_evict: huh, not a file %d\n", key
->fileID
);
1310 (void) BTDeleteRecord(filefork
, iterator
);
1311 key
->temperature
= HFC_LOOKUPTAG
;
1312 (void) BTDeleteRecord(filefork
, iterator
);
1313 goto next
; /* invalid entry, go to next */
1315 fileblocks
= VTOF(vp
)->ff_blocks
;
1316 if ((blksmoved
> 0) &&
1317 (blksmoved
+ fileblocks
) > HFC_BLKSPERSYNC
) {
1322 * Make sure file is in the hot area.
1324 if (!hotextents(hfsmp
, &VTOF(vp
)->ff_extents
[0])) {
1326 printf("hotfiles_evict: file %d isn't hot!\n", key
->fileID
);
1329 (void) BTDeleteRecord(filefork
, iterator
);
1330 key
->temperature
= HFC_LOOKUPTAG
;
1331 (void) BTDeleteRecord(filefork
, iterator
);
1332 goto next
; /* go to next */
1336 * Relocate file out of hot area.
1338 error
= hfs_relocate(vp
, HFSTOVCB(hfsmp
)->nextAllocation
, p
->p_ucred
, p
);
1340 /* XXX skip to next record here! */
1341 printf("hotfiles_evict: err % relocating file\n", error
, key
->fileID
);
1345 (void) VOP_FSYNC(vp
, p
->p_ucred
, MNT_WAIT
, p
);
1349 hfsmp
->hfs_hotfile_freeblks
+= fileblocks
;
1350 listp
->hfl_reclaimblks
-= fileblocks
;
1351 if (listp
->hfl_reclaimblks
< 0)
1352 listp
->hfl_reclaimblks
= 0;
1353 blksmoved
+= fileblocks
;
1356 error
= BTDeleteRecord(filefork
, iterator
);
1358 printf("hotfiles_evict: BTDeleteRecord failed %d (fileid %d)\n", error
, key
->fileID
);
1359 error
= MacToVFSError(error
);
1362 key
->temperature
= HFC_LOOKUPTAG
;
1363 error
= BTDeleteRecord(filefork
, iterator
);
1365 printf("hotfiles_evict: BTDeleteRecord thread failed %d (fileid %d)\n", error
, key
->fileID
);
1366 error
= MacToVFSError(error
);
1370 (void) BTFlushPath(filefork
);
1372 /* Transaction complete. */
1374 journal_end_transaction(hfsmp
->jnl
);
1377 hfs_global_shared_lock_release(hfsmp
);
1383 printf("hotfiles_evict: moved %d files (%d blks, %d to go)\n", filesmoved
, blksmoved
, listp
->hfl_reclaimblks
);
1385 /* Finish any outstanding transactions. */
1387 (void) BTFlushPath(filefork
);
1388 journal_end_transaction(hfsmp
->jnl
);
1392 hfs_global_shared_lock_release(hfsmp
);
1395 (void) VOP_UNLOCK(hfsmp
->hfc_filevp
, 0, p
);
1398 * Move to next stage when finished.
1400 if (listp
->hfl_reclaimblks
<= 0) {
1401 stage
= HFC_ADOPTION
;
1403 printf("hotfiles_evict: %d blocks free in hot file band\n", hfsmp
->hfs_hotfile_freeblks
);
1406 FREE(iterator
, M_TEMP
);
1407 hfsmp
->hfc_stage
= stage
;
1408 wakeup((caddr_t
)&hfsmp
->hfc_stage
);
1413 * Age the existing records in the hot files b-tree.
1416 hotfiles_age(struct hfsmount
*hfsmp
, struct proc
*p
)
1418 BTreeInfoRec btinfo
;
1419 BTreeIterator
* iterator
;
1420 BTreeIterator
* prev_iterator
;
1421 FSBufferDescriptor record
;
1422 FSBufferDescriptor prev_record
;
1424 HotFileKey
* prev_key
;
1425 filefork_t
* filefork
;
1427 u_int32_t prev_data
;
1436 MALLOC(iterator
, BTreeIterator
*, 2 * sizeof(*iterator
), M_TEMP
, M_WAITOK
);
1437 bzero(iterator
, 2 * sizeof(*iterator
));
1438 key
= (HotFileKey
*) &iterator
->key
;
1440 prev_iterator
= &iterator
[1];
1441 prev_key
= (HotFileKey
*) &prev_iterator
->key
;
1443 record
.bufferAddress
= &data
;
1444 record
.itemSize
= sizeof(data
);
1445 record
.itemCount
= 1;
1446 prev_record
.bufferAddress
= &prev_data
;
1447 prev_record
.itemSize
= sizeof(prev_data
);
1448 prev_record
.itemCount
= 1;
1451 * Capture b-tree changes inside a transaction
1453 hfs_global_shared_lock_acquire(hfsmp
);
1455 if (journal_start_transaction(hfsmp
->jnl
) != 0) {
1456 hfs_global_shared_lock_release(hfsmp
);
1461 vn_lock(hfsmp
->hfc_filevp
, LK_EXCLUSIVE
| LK_RETRY
, p
);
1462 filefork
= VTOF(hfsmp
->hfc_filevp
);
1464 error
= BTGetInformation(filefork
, 0, &btinfo
);
1466 error
= MacToVFSError(error
);
1469 if (btinfo
.numRecords
< 2) {
1474 /* Only want 1st half of leaf records */
1475 numrecs
= (btinfo
.numRecords
/= 2) - 1;
1477 error
= BTIterateRecord(filefork
, kBTreeFirstRecord
, iterator
, &record
, &reclen
);
1479 printf("hfs_agehotfiles: BTIterateRecord: %d\n", error
);
1480 error
= MacToVFSError(error
);
1483 bcopy(iterator
, prev_iterator
, sizeof(BTreeIterator
));
1486 for (i
= 0; i
< numrecs
; ++i
) {
1487 error
= BTIterateRecord(filefork
, kBTreeNextRecord
, iterator
, &record
, &reclen
);
1489 if (key
->temperature
< prev_key
->temperature
) {
1490 printf("hfs_agehotfiles: out of order keys!\n");
1494 if (reclen
!= sizeof(data
)) {
1495 printf("hfs_agehotfiles: invalid record length %d\n", reclen
);
1499 if (key
->keyLength
!= HFC_KEYLENGTH
) {
1500 printf("hfs_agehotfiles: invalid key length %d\n", key
->keyLength
);
1504 } else if ((error
== fsBTEndOfIterationErr
|| error
== fsBTRecordNotFoundErr
) &&
1505 (i
== (numrecs
- 1))) {
1508 printf("hfs_agehotfiles: %d of %d BTIterateRecord: %d\n", i
, numrecs
, error
);
1509 error
= MacToVFSError(error
);
1512 if (prev_key
->temperature
== HFC_LOOKUPTAG
) {
1514 printf("hfs_agehotfiles: ran into thread record\n");
1519 error
= BTDeleteRecord(filefork
, prev_iterator
);
1521 printf("hfs_agehotfiles: BTDeleteRecord failed %d (file %d)\n", error
, prev_key
->fileID
);
1522 error
= MacToVFSError(error
);
1526 /* Age by halving the temperature (floor = 4) */
1527 newtemp
= MAX(prev_key
->temperature
>> 1, 4);
1528 prev_key
->temperature
= newtemp
;
1530 error
= BTInsertRecord(filefork
, prev_iterator
, &prev_record
, sizeof(data
));
1532 printf("hfs_agehotfiles: BTInsertRecord failed %d (file %d)\n", error
, prev_key
->fileID
);
1533 error
= MacToVFSError(error
);
1538 * Update thread entry with latest temperature.
1540 prev_key
->temperature
= HFC_LOOKUPTAG
;
1541 error
= BTUpdateRecord(filefork
, prev_iterator
,
1542 (IterateCallBackProcPtr
)update_callback
,
1545 printf("hfs_agehotfiles: %d of %d BTUpdateRecord failed %d (file %d, %d)\n",
1546 i
, numrecs
, error
, prev_key
->fileID
, newtemp
);
1547 error
= MacToVFSError(error
);
1551 bcopy(iterator
, prev_iterator
, sizeof(BTreeIterator
));
1558 printf("hfs_agehotfiles: aged %d records out of %d\n", aged
, btinfo
.numRecords
);
1560 (void) BTFlushPath(filefork
);
1562 (void) VOP_UNLOCK(hfsmp
->hfc_filevp
, 0, p
);
1565 // hfs_flushvolumeheader(hfsmp, MNT_NOWAIT, 0);
1566 journal_end_transaction(hfsmp
->jnl
);
1568 hfs_global_shared_lock_release(hfsmp
);
1570 FREE(iterator
, M_TEMP
);
1575 * Return true if any blocks (or all blocks if all is true)
1576 * are contained in the hot file region.
1579 hotextents(struct hfsmount
*hfsmp
, HFSPlusExtentDescriptor
* extents
)
1585 for (i
= 0; i
< kHFSPlusExtentDensity
; ++i
) {
1586 b1
= extents
[i
].startBlock
;
1589 b2
= b1
+ extents
[i
].blockCount
- 1;
1590 if ((b1
>= hfsmp
->hfs_hotfile_start
&&
1591 b2
<= hfsmp
->hfs_hotfile_end
) ||
1592 (b1
< hfsmp
->hfs_hotfile_end
&&
1593 b2
> hfsmp
->hfs_hotfile_end
)) {
1603 *========================================================================
1604 * HOT FILE B-TREE ROUTINES
1605 *========================================================================
1609 * Open the hot files b-tree for writing.
1611 * On successful exit the vnode has a reference but is unlocked.
1614 hfc_btree_open(struct hfsmount
*hfsmp
, struct vnode
**vpp
)
1618 struct cat_desc cdesc
= {0};
1619 struct cat_attr cattr
;
1620 struct cat_fork cfork
;
1621 static char filename
[] = HFC_FILENAME
;
1628 cdesc
.cd_parentcnid
= kRootDirID
;
1629 cdesc
.cd_nameptr
= filename
;
1630 cdesc
.cd_namelen
= strlen(filename
);
1632 /* Lock catalog b-tree */
1633 error
= hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_SHARED
, p
);
1637 error
= cat_lookup(hfsmp
, &cdesc
, 0, &cdesc
, &cattr
, &cfork
);
1639 /* Unlock catalog b-tree */
1640 (void) hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_RELEASE
, p
);
1643 printf("hfc_btree_open: cat_lookup error %d\n", error
);
1647 cdesc
.cd_flags
|= CD_ISMETA
;
1648 error
= hfs_getnewvnode(hfsmp
, NULL
, &cdesc
, 0, &cattr
, &cfork
, &vp
);
1650 printf("hfc_btree_open: hfs_getnewvnode error %d\n", error
);
1651 cat_releasedesc(&cdesc
);
1654 if ((vp
->v_flag
& VSYSTEM
) == 0) {
1656 printf("hfc_btree_open: file has UBC, try again\n");
1666 /* Open the B-tree file for writing... */
1667 error
= BTOpenPath(VTOF(vp
), (KeyCompareProcPtr
) hfc_comparekeys
);
1669 printf("hfc_btree_open: BTOpenPath error %d\n", error
);
1670 error
= MacToVFSError(error
);
1673 struct BTreeInfoRec btinfo
;
1675 if (BTGetInformation(VTOF(vp
), 0, &btinfo
) == 0) {
1676 printf("btinfo: nodeSize %d\n", btinfo
.nodeSize
);
1677 printf("btinfo: maxKeyLength %d\n", btinfo
.maxKeyLength
);
1678 printf("btinfo: treeDepth %d\n", btinfo
.treeDepth
);
1679 printf("btinfo: numRecords %d\n", btinfo
.numRecords
);
1680 printf("btinfo: numNodes %d\n", btinfo
.numNodes
);
1681 printf("btinfo: numFreeNodes %d\n", btinfo
.numFreeNodes
);
1686 VOP_UNLOCK(vp
, 0, p
); /* unlocked with a single reference */
1692 if ((vp
->v_flag
& VSYSTEM
) == 0)
1693 panic("hfc_btree_open: not a system file (vp = 0x%08x)", vp
);
1695 if (UBCINFOEXISTS(vp
))
1696 panic("hfc_btree_open: has UBCInfo (vp = 0x%08x)", vp
);
1702 * Close the hot files b-tree.
1704 * On entry the vnode is not locked but has a reference.
1707 hfc_btree_close(struct hfsmount
*hfsmp
, struct vnode
*vp
)
1709 struct proc
*p
= current_proc();
1714 journal_flush(hfsmp
->jnl
);
1717 if (vget(vp
, LK_EXCLUSIVE
, p
) == 0) {
1718 (void) VOP_FSYNC(vp
, NOCRED
, MNT_WAIT
, p
);
1719 error
= BTClosePath(VTOF(vp
));
1721 printf("hfc_btree_close: BTClosePath error %d\n", error
);
1732 * Create a hot files btree file.
1736 hfc_btree_create(struct hfsmount
*hfsmp
, int nodesize
, int entries
)
1739 struct nameidata nd
;
1745 if (hfsmp
->hfc_filevp
)
1746 panic("hfc_btree_create: hfc_filevp exists (vp = 0x%08x)", hfsmp
->hfc_filevp
);
1749 snprintf(path
, sizeof(path
), "%s/%s",
1750 hfsmp
->hfs_mp
->mnt_stat
.f_mntonname
, HFC_FILENAME
);
1751 NDINIT(&nd
, LOOKUP
, FOLLOW
, UIO_SYSSPACE
, path
, p
);
1752 if ((error
= vn_open(&nd
, O_CREAT
| FWRITE
, S_IRUSR
| S_IWUSR
)) != 0) {
1757 /* Don't use non-regular files or files with links. */
1758 if (vp
->v_type
!= VREG
|| VTOC(vp
)->c_nlink
!= 1) {
1763 printf("HFS: created HFBT on %s\n", HFSTOVCB(hfsmp
)->vcbVN
);
1765 if (VTOF(vp
)->ff_size
< nodesize
) {
1769 BTNodeDescriptor
*ndp
;
1771 HotFilesInfo
*hotfileinfo
;
1777 * Mark it invisible (truncate will pull these changes).
1779 ((FndrFileInfo
*)&VTOC(vp
)->c_finderinfo
[0])->fdFlags
|=
1780 SWAP_BE16 (kIsInvisible
+ kNameLocked
);
1782 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&buffer
, nodesize
)) {
1786 bzero(buffer
, nodesize
);
1787 index
= (int16_t *)buffer
;
1789 entirespernode
= (nodesize
- sizeof(BTNodeDescriptor
) - 2) /
1790 (sizeof(HotFileKey
) + 6);
1791 nodecnt
= 2 + howmany(entries
* 2, entirespernode
);
1792 nodecnt
= roundup(nodecnt
, 8);
1793 filesize
= nodecnt
* nodesize
;
1795 /* FILL IN THE NODE DESCRIPTOR: */
1796 ndp
= (BTNodeDescriptor
*)buffer
;
1797 ndp
->kind
= kBTHeaderNode
;
1798 ndp
->numRecords
= SWAP_BE16 (3);
1799 offset
= sizeof(BTNodeDescriptor
);
1800 index
[(nodesize
/ 2) - 1] = SWAP_BE16 (offset
);
1802 /* FILL IN THE HEADER RECORD: */
1803 bthp
= (BTHeaderRec
*)((UInt8
*)buffer
+ offset
);
1804 bthp
->nodeSize
= SWAP_BE16 (nodesize
);
1805 bthp
->totalNodes
= SWAP_BE32 (filesize
/ nodesize
);
1806 bthp
->freeNodes
= SWAP_BE32 (nodecnt
- 1);
1807 bthp
->clumpSize
= SWAP_BE32 (filesize
);
1808 bthp
->btreeType
= kUserBTreeType
; /* non-metadata */
1809 bthp
->attributes
|= SWAP_BE32 (kBTBigKeysMask
);
1810 bthp
->maxKeyLength
= SWAP_BE16 (HFC_KEYLENGTH
);
1811 offset
+= sizeof(BTHeaderRec
);
1812 index
[(nodesize
/ 2) - 2] = SWAP_BE16 (offset
);
1814 /* FILL IN THE USER RECORD: */
1815 hotfileinfo
= (HotFilesInfo
*)((UInt8
*)buffer
+ offset
);
1816 hotfileinfo
->magic
= SWAP_BE32 (HFC_MAGIC
);
1817 hotfileinfo
->version
= SWAP_BE32 (HFC_VERSION
);
1818 hotfileinfo
->duration
= SWAP_BE32 (HFC_DEFAULT_DURATION
);
1819 hotfileinfo
->timebase
= 0;
1820 hotfileinfo
->timeleft
= 0;
1821 hotfileinfo
->threshold
= SWAP_BE32 (HFC_MINIMUM_TEMPERATURE
);
1822 hotfileinfo
->maxfileblks
= SWAP_BE32 (HFC_MAXIMUM_FILESIZE
/ HFSTOVCB(hfsmp
)->blockSize
);
1823 hotfileinfo
->maxfilecnt
= SWAP_BE32 (HFC_DEFAULT_FILE_COUNT
);
1824 strcpy(hotfileinfo
->tag
, hfc_tag
);
1825 offset
+= kBTreeHeaderUserBytes
;
1826 index
[(nodesize
/ 2) - 3] = SWAP_BE16 (offset
);
1828 /* FILL IN THE MAP RECORD (only one node in use). */
1829 *((u_int8_t
*)buffer
+ offset
) = 0x80;
1830 offset
+= nodesize
- sizeof(BTNodeDescriptor
) - sizeof(BTHeaderRec
)
1831 - kBTreeHeaderUserBytes
- (4 * sizeof(int16_t));
1832 index
[(nodesize
/ 2) - 4] = SWAP_BE16 (offset
);
1834 vp
->v_flag
|= VNOFLUSH
;
1835 error
= VOP_TRUNCATE(vp
, (off_t
)filesize
, IO_NDELAY
, NOCRED
, p
);
1840 auio
.uio_iov
= &aiov
;
1841 auio
.uio_iovcnt
= 1;
1842 aiov
.iov_base
= buffer
;
1843 aiov
.iov_len
= filesize
;
1844 auio
.uio_resid
= nodesize
;
1845 auio
.uio_offset
= (off_t
)(0);
1846 auio
.uio_segflg
= UIO_SYSSPACE
;
1847 auio
.uio_rw
= UIO_WRITE
;
1848 auio
.uio_procp
= (struct proc
*)0;
1849 error
= VOP_WRITE(vp
, &auio
, 0, kernproc
->p_ucred
);
1851 kmem_free(kernel_map
, (vm_offset_t
)buffer
, nodesize
);
1854 (void) VOP_UNLOCK(vp
, 0, p
);
1855 (void) vn_close(vp
, FWRITE
, kernproc
->p_ucred
, p
);
1861 * Compare two hot file b-tree keys.
1863 * Result: +n search key > trial key
1864 * 0 search key = trial key
1865 * -n search key < trial key
1868 hfc_comparekeys(HotFileKey
*searchKey
, HotFileKey
*trialKey
)
1871 * Compared temperatures first.
1873 if (searchKey
->temperature
== trialKey
->temperature
) {
1875 * Temperatures are equal so compare file ids.
1877 if (searchKey
->fileID
== trialKey
->fileID
) {
1879 * File ids are equal so compare fork types.
1881 if (searchKey
->forkType
== trialKey
->forkType
) {
1883 } else if (searchKey
->forkType
> trialKey
->forkType
) {
1886 } else if (searchKey
->fileID
> trialKey
->fileID
) {
1889 } else if (searchKey
->temperature
> trialKey
->temperature
) {
1898 *========================================================================
1899 * HOT FILE DATA COLLECTING ROUTINES
1900 *========================================================================
1904 * Lookup a hot file entry in the tree.
1906 static hotfile_entry_t
*
1907 hf_lookup(hotfile_data_t
*hotdata
, u_int32_t fileid
, u_int32_t temperature
)
1909 hotfile_entry_t
*entry
= hotdata
->rootentry
;
1912 entry
->temperature
!= temperature
&&
1913 entry
->fileid
!= fileid
) {
1915 if (temperature
> entry
->temperature
)
1916 entry
= entry
->right
;
1917 else if (temperature
< entry
->temperature
)
1918 entry
= entry
->left
;
1919 else if (fileid
> entry
->fileid
)
1920 entry
= entry
->right
;
1922 entry
= entry
->left
;
1928 * Insert a hot file entry into the tree.
1931 hf_insert(hotfile_data_t
*hotdata
, hotfile_entry_t
*newentry
)
1933 hotfile_entry_t
*entry
= hotdata
->rootentry
;
1934 u_int32_t fileid
= newentry
->fileid
;
1935 u_int32_t temperature
= newentry
->temperature
;
1937 if (entry
== NULL
) {
1938 hotdata
->rootentry
= newentry
;
1939 hotdata
->coldest
= newentry
;
1940 hotdata
->activefiles
++;
1945 if (temperature
> entry
->temperature
) {
1947 entry
= entry
->right
;
1949 entry
->right
= newentry
;
1952 } else if (temperature
< entry
->temperature
) {
1954 entry
= entry
->left
;
1956 entry
->left
= newentry
;
1959 } else if (fileid
> entry
->fileid
) {
1961 entry
= entry
->right
;
1963 if (entry
->fileid
!= fileid
)
1964 entry
->right
= newentry
;
1969 entry
= entry
->left
;
1971 if (entry
->fileid
!= fileid
)
1972 entry
->left
= newentry
;
1978 hotdata
->activefiles
++;
1982 * Find the coldest entry in the tree.
1984 static hotfile_entry_t
*
1985 hf_coldest(hotfile_data_t
*hotdata
)
1987 hotfile_entry_t
*entry
= hotdata
->rootentry
;
1991 entry
= entry
->left
;
1997 * Delete a hot file entry from the tree.
2000 hf_delete(hotfile_data_t
*hotdata
, u_int32_t fileid
, u_int32_t temperature
)
2002 hotfile_entry_t
*entry
, *parent
, *next
;
2005 entry
= hotdata
->rootentry
;
2008 entry
->temperature
!= temperature
&&
2009 entry
->fileid
!= fileid
) {
2012 if (temperature
> entry
->temperature
)
2013 entry
= entry
->right
;
2014 else if (temperature
< entry
->temperature
)
2015 entry
= entry
->left
;
2016 else if (fileid
> entry
->fileid
)
2017 entry
= entry
->right
;
2019 entry
= entry
->left
;
2024 * Reorginize the sub-trees spanning from our entry.
2026 if ((next
= entry
->right
)) {
2027 hotfile_entry_t
*pnextl
, *psub
;
2029 * Tree pruning: take the left branch of the
2030 * current entry and place it at the lowest
2031 * left branch of the current right branch
2035 /* Walk the Right/Left sub tree from current entry */
2036 while ((pnextl
= psub
->left
))
2039 /* Plug the old left tree to the new ->Right leftmost entry */
2040 psub
->left
= entry
->left
;
2042 } else /* only left sub-tree, simple case */ {
2046 * Now, plug the current entry sub tree to
2047 * the good pointer of our parent entry.
2050 hotdata
->rootentry
= next
;
2051 else if (parent
->left
== entry
)
2052 parent
->left
= next
;
2054 parent
->right
= next
;
2056 /* Place entry back on the free-list */
2059 entry
->temperature
= 0;
2061 entry
->right
= hotdata
->freelist
;
2062 hotdata
->freelist
= entry
;
2063 hotdata
->activefiles
--;
2065 if (hotdata
->coldest
== entry
|| hotdata
->coldest
== NULL
) {
2066 hotdata
->coldest
= hf_coldest(hotdata
);
2073 * Get a free hot file entry.
2075 static hotfile_entry_t
*
2076 hf_getnewentry(hotfile_data_t
*hotdata
)
2078 hotfile_entry_t
* entry
;
2081 * When the free list is empty then steal the coldest one
2083 if (hotdata
->freelist
== NULL
) {
2084 entry
= hf_coldest(hotdata
);
2085 hf_delete(hotdata
, entry
->fileid
, entry
->temperature
);
2087 entry
= hotdata
->freelist
;
2088 hotdata
->freelist
= entry
->right
;
2096 * Visit the tree in desending order.
2099 hf_sortlist(hotfile_entry_t
* root
, int *index
, hotfilelist_t
*sortedlist
)
2104 hf_sortlist(root
->right
, index
, sortedlist
);
2107 sortedlist
->hfl_hotfile
[i
].hf_fileid
= root
->fileid
;
2108 sortedlist
->hfl_hotfile
[i
].hf_temperature
= root
->temperature
;
2109 sortedlist
->hfl_hotfile
[i
].hf_blocks
= root
->blocks
;
2110 sortedlist
->hfl_totalblocks
+= root
->blocks
;
2111 hf_sortlist(root
->left
, index
, sortedlist
);
2116 * Generate a sorted list of hot files.
2119 hf_getsortedlist(hotfile_data_t
* hotdata
, hotfilelist_t
*sortedlist
)
2123 hf_sortlist(hotdata
->rootentry
, &index
, sortedlist
);
2125 sortedlist
->hfl_count
= hotdata
->activefiles
;
2133 hf_maxdepth(hotfile_entry_t
* root
, int depth
, int *maxdepth
)
2137 if (depth
> *maxdepth
)
2139 hf_maxdepth(root
->left
, depth
, maxdepth
);
2140 hf_maxdepth(root
->right
, depth
, maxdepth
);
2145 hf_printtree(hotfile_entry_t
* root
)
2148 hf_printtree(root
->left
);
2149 printf("temperature: % 8d, fileid %d\n", root
->temperature
, root
->fileid
);
2150 hf_printtree(root
->right
);