2 * Copyright (c) 2003-2013 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 #include <sys/param.h>
30 #include <sys/systm.h>
31 #include <sys/fcntl.h>
32 #include <sys/kernel.h>
33 #include <sys/malloc.h>
35 #include <sys/ubc_internal.h>
36 #include <sys/vnode.h>
37 #include <sys/vnode_internal.h>
38 #include <sys/kauth.h>
41 #include <hfs/hfs_endian.h>
42 #include <hfs/hfs_format.h>
43 #include <hfs/hfs_mount.h>
44 #include <hfs/hfs_hotfiles.h>
46 #include "hfscommon/headers/BTreeScanner.h"
54 * Minimum post Tiger base time.
55 * Thu Mar 31 17:00:00 2005
57 #define HFC_MIN_BASE_TIME 0x424c8f00L
60 * Hot File List (runtime).
62 typedef struct hotfileinfo
{
64 u_int32_t hf_temperature
;
68 typedef struct hotfilelist
{
70 u_int32_t hfl_version
;
71 time_t hfl_duration
; /* duration of sample period */
72 int hfl_count
; /* count of hot files recorded */
73 int hfl_next
; /* next file to move */
74 int hfl_totalblocks
; /* total hot file blocks */
75 int hfl_reclaimblks
; /* blocks to reclaim in HFV */
76 u_int32_t hfl_spare
[2];
77 hotfileinfo_t hfl_hotfile
[1]; /* array of hot files */
82 * Hot File Entry (runtime).
84 typedef struct hotfile_entry
{
85 struct hotfile_entry
*left
;
86 struct hotfile_entry
*right
;
88 u_int32_t temperature
;
93 * Hot File Recording Data (runtime).
95 typedef struct hotfile_data
{
96 struct hfsmount
*hfsmp
;
98 int activefiles
; /* active number of hot files */
101 hotfile_entry_t
*rootentry
;
102 hotfile_entry_t
*freelist
;
103 hotfile_entry_t
*coldest
;
104 hotfile_entry_t entries
[1];
107 static int hfs_recording_start (struct hfsmount
*);
108 static int hfs_recording_stop (struct hfsmount
*);
112 * Hot File Data recording functions (in-memory binary tree).
114 static void hf_insert (hotfile_data_t
*, hotfile_entry_t
*);
115 static void hf_delete (hotfile_data_t
*, u_int32_t
, u_int32_t
);
116 static hotfile_entry_t
* hf_coldest (hotfile_data_t
*);
117 static hotfile_entry_t
* hf_getnewentry (hotfile_data_t
*);
118 static void hf_getsortedlist (hotfile_data_t
*, hotfilelist_t
*);
121 static hotfile_entry_t
* hf_lookup (hotfile_data_t
*, u_int32_t
, u_int32_t
);
122 static void hf_maxdepth(hotfile_entry_t
*, int, int *);
123 static void hf_printtree (hotfile_entry_t
*);
127 * Hot File misc support functions.
129 static int hotfiles_collect (struct hfsmount
*);
130 static int hotfiles_age (struct hfsmount
*);
131 static int hotfiles_adopt (struct hfsmount
*);
132 static int hotfiles_evict (struct hfsmount
*, vfs_context_t
);
133 static int hotfiles_refine (struct hfsmount
*);
134 static int hotextents(struct hfsmount
*, HFSPlusExtentDescriptor
*);
135 static int hfs_addhotfile_internal(struct vnode
*);
139 * Hot File Cluster B-tree (on disk) functions.
141 static int hfc_btree_create (struct hfsmount
*, unsigned int, unsigned int);
142 static int hfc_btree_open (struct hfsmount
*, struct vnode
**);
143 static int hfc_btree_close (struct hfsmount
*, struct vnode
*);
144 static int hfc_comparekeys (HotFileKey
*, HotFileKey
*);
147 char hfc_tag
[] = "CLUSTERED HOT FILES B-TREE ";
151 *========================================================================
152 * HOT FILE INTERFACE ROUTINES
153 *========================================================================
157 * Start recording the hotest files on a file system.
159 * Requires that the hfc_mutex be held.
162 hfs_recording_start(struct hfsmount
*hfsmp
)
164 hotfile_data_t
*hotdata
;
171 if ((hfsmp
->hfs_flags
& HFS_READ_ONLY
) ||
172 (hfsmp
->jnl
== NULL
) ||
173 (hfsmp
->hfs_flags
& HFS_METADATA_ZONE
) == 0) {
176 if (HFSTOVCB(hfsmp
)->freeBlocks
< (2 * (u_int32_t
)hfsmp
->hfs_hotfile_maxblks
)) {
179 if (hfsmp
->hfc_stage
!= HFC_IDLE
) {
182 hfsmp
->hfc_stage
= HFC_BUSY
;
185 * Dump previous recording data.
187 if (hfsmp
->hfc_recdata
) {
190 tmp
= hfsmp
->hfc_recdata
;
191 hfsmp
->hfc_recdata
= NULL
;
195 microtime(&tv
); /* Times are base on GMT time. */
198 * On first startup check for suspended recording.
200 if (hfsmp
->hfc_timebase
== 0 &&
201 hfc_btree_open(hfsmp
, &hfsmp
->hfc_filevp
) == 0) {
202 HotFilesInfo hotfileinfo
;
204 if ((BTGetUserData(VTOF(hfsmp
->hfc_filevp
), &hotfileinfo
,
205 sizeof(hotfileinfo
)) == 0) &&
206 (SWAP_BE32 (hotfileinfo
.magic
) == HFC_MAGIC
) &&
207 (SWAP_BE32 (hotfileinfo
.timeleft
) > 0) &&
208 (SWAP_BE32 (hotfileinfo
.timebase
) > 0)) {
209 hfsmp
->hfc_maxfiles
= SWAP_BE32 (hotfileinfo
.maxfilecnt
);
210 hfsmp
->hfc_timebase
= SWAP_BE32 (hotfileinfo
.timebase
);
211 hfsmp
->hfc_timeout
= SWAP_BE32 (hotfileinfo
.timeleft
) + tv
.tv_sec
;
212 /* Fix up any bogus timebase values. */
213 if (hfsmp
->hfc_timebase
< HFC_MIN_BASE_TIME
) {
214 hfsmp
->hfc_timebase
= hfsmp
->hfc_timeout
- HFC_DEFAULT_DURATION
;
217 printf("hfs: Resume recording hot files on %s (%d secs left)\n",
218 hfsmp
->vcbVN
, SWAP_BE32 (hotfileinfo
.timeleft
));
221 hfsmp
->hfc_maxfiles
= HFC_DEFAULT_FILE_COUNT
;
222 hfsmp
->hfc_timebase
= tv
.tv_sec
+ 1;
223 hfsmp
->hfc_timeout
= hfsmp
->hfc_timebase
+ HFC_DEFAULT_DURATION
;
225 (void) hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
226 hfsmp
->hfc_filevp
= NULL
;
228 struct cat_attr cattr
;
232 * Make sure a btree file exists.
234 cnid
= GetFileInfo(HFSTOVCB(hfsmp
), kRootDirID
, HFC_FILENAME
, &cattr
, NULL
);
236 !S_ISREG(cattr
.ca_mode
) &&
237 (error
= hfc_btree_create(hfsmp
, HFSTOVCB(hfsmp
)->blockSize
, HFC_DEFAULT_FILE_COUNT
))) {
238 hfsmp
->hfc_stage
= HFC_IDLE
;
239 wakeup((caddr_t
)&hfsmp
->hfc_stage
);
243 printf("hfs: begin recording hot files on %s\n", hfsmp
->vcbVN
);
245 hfsmp
->hfc_maxfiles
= HFC_DEFAULT_FILE_COUNT
;
246 hfsmp
->hfc_timeout
= tv
.tv_sec
+ HFC_DEFAULT_DURATION
;
248 /* Reset time base. */
249 if (hfsmp
->hfc_timebase
== 0) {
250 hfsmp
->hfc_timebase
= tv
.tv_sec
+ 1;
252 time_t cumulativebase
;
254 cumulativebase
= hfsmp
->hfc_timeout
- (HFC_CUMULATIVE_CYCLES
* HFC_DEFAULT_DURATION
);
255 hfsmp
->hfc_timebase
= MAX(hfsmp
->hfc_timebase
, cumulativebase
);
259 if ((hfsmp
->hfc_maxfiles
== 0) ||
260 (hfsmp
->hfc_maxfiles
> HFC_MAXIMUM_FILE_COUNT
)) {
261 hfsmp
->hfc_maxfiles
= HFC_DEFAULT_FILE_COUNT
;
263 maxentries
= hfsmp
->hfc_maxfiles
;
265 size
= sizeof(hotfile_data_t
) + (maxentries
* sizeof(hotfile_entry_t
));
266 MALLOC(hotdata
, hotfile_data_t
*, size
, M_TEMP
, M_WAITOK
);
267 if (hotdata
== NULL
) {
268 hfsmp
->hfc_recdata
= NULL
;
269 hfsmp
->hfc_stage
= HFC_IDLE
;
270 wakeup((caddr_t
)&hfsmp
->hfc_stage
);
274 bzero(hotdata
, size
);
276 for (i
= 1; i
< maxentries
; i
++)
277 hotdata
->entries
[i
-1].right
= &hotdata
->entries
[i
];
279 hotdata
->freelist
= &hotdata
->entries
[0];
281 * Establish minimum temperature and maximum file size.
283 hotdata
->threshold
= HFC_MINIMUM_TEMPERATURE
;
284 hotdata
->maxblocks
= HFC_MAXIMUM_FILESIZE
/ HFSTOVCB(hfsmp
)->blockSize
;
285 hotdata
->hfsmp
= hfsmp
;
287 hfsmp
->hfc_recdata
= hotdata
;
288 hfsmp
->hfc_stage
= HFC_RECORDING
;
289 wakeup((caddr_t
)&hfsmp
->hfc_stage
);
294 * Stop recording the hotest files on a file system.
296 * Requires that the hfc_mutex be held.
299 hfs_recording_stop(struct hfsmount
*hfsmp
)
301 hotfile_data_t
*hotdata
;
302 hotfilelist_t
*listp
;
305 enum hfc_stage newstage
= HFC_IDLE
;
308 if (hfsmp
->hfc_stage
!= HFC_RECORDING
)
311 hfsmp
->hfc_stage
= HFC_BUSY
;
313 hotfiles_collect(hfsmp
);
317 * Convert hot file data into a simple file id list....
319 * then dump the sample data
322 printf("hfs: end of hot file recording on %s\n", hfsmp
->vcbVN
);
324 hotdata
= (hotfile_data_t
*)hfsmp
->hfc_recdata
;
327 hfsmp
->hfc_recdata
= NULL
;
328 hfsmp
->hfc_stage
= HFC_EVALUATION
;
329 wakeup((caddr_t
)&hfsmp
->hfc_stage
);
332 printf("hfs: curentries: %d\n", hotdata
->activefiles
);
335 * If no hot files recorded then we're done.
337 if (hotdata
->rootentry
== NULL
) {
342 /* Open the B-tree file for writing... */
343 if (hfsmp
->hfc_filevp
)
344 panic("hfs_recording_stop: hfc_filevp exists (vp = %p)", hfsmp
->hfc_filevp
);
346 error
= hfc_btree_open(hfsmp
, &hfsmp
->hfc_filevp
);
352 * Age the previous set of clustered hot files.
354 error
= hotfiles_age(hfsmp
);
356 (void) hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
357 hfsmp
->hfc_filevp
= NULL
;
362 * Create a sorted list of hotest files.
364 size
= sizeof(hotfilelist_t
);
365 size
+= sizeof(hotfileinfo_t
) * (hotdata
->activefiles
- 1);
366 MALLOC(listp
, hotfilelist_t
*, size
, M_TEMP
, M_WAITOK
);
369 (void) hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
370 hfsmp
->hfc_filevp
= NULL
;
376 hf_getsortedlist(hotdata
, listp
); /* NOTE: destroys hot file tree! */
378 listp
->hfl_duration
= tv
.tv_sec
- hfsmp
->hfc_timebase
;
379 hfsmp
->hfc_recdata
= listp
;
382 * Account for duplicates.
384 error
= hotfiles_refine(hfsmp
);
386 (void) hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
387 hfsmp
->hfc_filevp
= NULL
;
392 * Compute the amount of space to reclaim...
394 if (listp
->hfl_totalblocks
> hfsmp
->hfs_hotfile_freeblks
) {
395 listp
->hfl_reclaimblks
=
396 MIN(listp
->hfl_totalblocks
, hfsmp
->hfs_hotfile_maxblks
) -
397 hfsmp
->hfs_hotfile_freeblks
;
399 printf("hfs_recording_stop: need to reclaim %d blocks\n", listp
->hfl_reclaimblks
);
401 if (listp
->hfl_reclaimblks
)
402 newstage
= HFC_EVICTION
;
404 newstage
= HFC_ADOPTION
;
406 newstage
= HFC_ADOPTION
;
409 if (newstage
== HFC_ADOPTION
&& listp
->hfl_totalblocks
== 0) {
410 (void) hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
411 hfsmp
->hfc_filevp
= NULL
;
416 if (newstage
== HFC_EVICTION
)
417 printf("hfs: evicting coldest files\n");
418 else if (newstage
== HFC_ADOPTION
)
419 printf("hfs: adopting hotest files\n");
421 FREE(hotdata
, M_TEMP
);
423 hfsmp
->hfc_stage
= newstage
;
424 wakeup((caddr_t
)&hfsmp
->hfc_stage
);
429 * Suspend recording the hotest files on a file system.
432 hfs_recording_suspend(struct hfsmount
*hfsmp
)
434 HotFilesInfo hotfileinfo
;
435 hotfile_data_t
*hotdata
= NULL
;
439 if (hfsmp
->hfc_stage
== HFC_DISABLED
)
442 lck_mtx_lock(&hfsmp
->hfc_mutex
);
446 * A suspend can occur during eval/evict/adopt stage.
447 * In that case we would need to write out info and
448 * flush our HFBT vnode. Currently we just bail.
451 hotdata
= (hotfile_data_t
*)hfsmp
->hfc_recdata
;
452 if (hotdata
== NULL
|| hfsmp
->hfc_stage
!= HFC_RECORDING
) {
456 hfsmp
->hfc_stage
= HFC_BUSY
;
459 printf("hfs: suspend hot file recording on %s\n", hfsmp
->vcbVN
);
461 error
= hfc_btree_open(hfsmp
, &hfsmp
->hfc_filevp
);
463 printf("hfs_recording_suspend: err %d opening btree\n", error
);
467 if (hfs_start_transaction(hfsmp
) != 0) {
471 if (hfs_lock(VTOC(hfsmp
->hfc_filevp
), HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
) != 0) {
473 goto end_transaction
;
477 hotfileinfo
.magic
= SWAP_BE32 (HFC_MAGIC
);
478 hotfileinfo
.version
= SWAP_BE32 (HFC_VERSION
);
479 hotfileinfo
.duration
= SWAP_BE32 (HFC_DEFAULT_DURATION
);
480 hotfileinfo
.timebase
= SWAP_BE32 (hfsmp
->hfc_timebase
);
481 hotfileinfo
.timeleft
= SWAP_BE32 (hfsmp
->hfc_timeout
- tv
.tv_sec
);
482 hotfileinfo
.threshold
= SWAP_BE32 (hotdata
->threshold
);
483 hotfileinfo
.maxfileblks
= SWAP_BE32 (hotdata
->maxblocks
);
484 hotfileinfo
.maxfilecnt
= SWAP_BE32 (HFC_DEFAULT_FILE_COUNT
);
485 strlcpy((char *)hotfileinfo
.tag
, hfc_tag
, sizeof hotfileinfo
.tag
);
486 (void) BTSetUserData(VTOF(hfsmp
->hfc_filevp
), &hotfileinfo
, sizeof(hotfileinfo
));
488 hfs_unlock(VTOC(hfsmp
->hfc_filevp
));
491 hfs_end_transaction(hfsmp
);
494 if (hfsmp
->hfc_filevp
) {
495 (void) hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
496 hfsmp
->hfc_filevp
= NULL
;
499 FREE(hotdata
, M_TEMP
);
500 hfsmp
->hfc_recdata
= NULL
;
502 hfsmp
->hfc_stage
= HFC_DISABLED
;
503 wakeup((caddr_t
)&hfsmp
->hfc_stage
);
505 lck_mtx_unlock(&hfsmp
->hfc_mutex
);
514 hfs_recording_init(struct hfsmount
*hfsmp
)
517 CatalogRecord
* datap
;
519 HFSPlusCatalogFile
*filep
;
520 BTScanState scanstate
;
521 BTreeIterator
* iterator
= NULL
;
522 FSBufferDescriptor record
;
524 filefork_t
* filefork
;
526 struct cat_attr cattr
;
530 int inserted
= 0; /* debug variables */
534 * For now, only the boot volume is supported.
536 if ((vfs_flags(HFSTOVFS(hfsmp
)) & MNT_ROOTFS
) == 0) {
537 hfsmp
->hfc_stage
= HFC_DISABLED
;
542 * Tracking of hot files requires up-to-date access times.
543 * So if access time updates are disabled, then we disable
546 if (vfs_flags(HFSTOVFS(hfsmp
)) & MNT_NOATIME
) {
547 hfsmp
->hfc_stage
= HFC_DISABLED
;
552 * If the Hot File btree exists then metadata zone is ready.
554 cnid
= GetFileInfo(HFSTOVCB(hfsmp
), kRootDirID
, HFC_FILENAME
, &cattr
, NULL
);
555 if (cnid
!= 0 && S_ISREG(cattr
.ca_mode
)) {
556 if (hfsmp
->hfc_stage
== HFC_DISABLED
)
557 hfsmp
->hfc_stage
= HFC_IDLE
;
561 if (hfs_start_transaction(hfsmp
) != 0) {
565 error
= hfc_btree_create(hfsmp
, HFSTOVCB(hfsmp
)->blockSize
, HFC_DEFAULT_FILE_COUNT
);
568 printf("hfs: Error %d creating hot file b-tree on %s \n", error
, hfsmp
->vcbVN
);
573 * Open the Hot File B-tree file for writing.
575 if (hfsmp
->hfc_filevp
)
576 panic("hfs_recording_init: hfc_filevp exists (vp = %p)", hfsmp
->hfc_filevp
);
577 error
= hfc_btree_open(hfsmp
, &hfsmp
->hfc_filevp
);
580 printf("hfs: Error %d opening hot file b-tree on %s \n", error
, hfsmp
->vcbVN
);
584 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
585 if (iterator
== NULL
) {
587 (void) hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
588 hfsmp
->hfc_filevp
= NULL
;
591 bzero(iterator
, sizeof(*iterator
));
592 key
= (HotFileKey
*) &iterator
->key
;
593 key
->keyLength
= HFC_KEYLENGTH
;
595 record
.bufferAddress
= &data
;
596 record
.itemSize
= sizeof(u_int32_t
);
597 record
.itemCount
= 1;
599 printf("hfs: Evaluating space for \"%s\" metadata zone...\n", HFSTOVCB(hfsmp
)->vcbVN
);
602 * Get ready to scan the Catalog file.
604 error
= BTScanInitialize(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), 0, 0, 0,
605 kCatSearchBufferSize
, &scanstate
);
607 printf("hfs_recording_init: err %d BTScanInit\n", error
);
612 * The writes to Hot File B-tree file are journaled.
614 if (hfs_start_transaction(hfsmp
) != 0) {
618 if (hfs_lock(VTOC(hfsmp
->hfc_filevp
), HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
) != 0) {
622 filefork
= VTOF(hfsmp
->hfc_filevp
);
625 * Visit all the catalog btree leaf records.
628 error
= BTScanNextRecord(&scanstate
, 0, (void **)&keyp
, (void **)&datap
, &dataSize
);
630 if (error
== btNotFound
)
633 printf("hfs_recording_init: err %d BTScanNext\n", error
);
636 if ((datap
->recordType
!= kHFSPlusFileRecord
) ||
637 (dataSize
!= sizeof(HFSPlusCatalogFile
))) {
640 filep
= (HFSPlusCatalogFile
*)datap
;
642 if (filep
->dataFork
.totalBlocks
== 0) {
646 * Any file that has blocks inside the hot file
647 * space is recorded for later eviction.
649 * For now, resource forks are ignored.
651 if (!hotextents(hfsmp
, &filep
->dataFork
.extents
[0])) {
654 cnid
= filep
->fileID
;
656 /* Skip over journal files. */
657 if (cnid
== hfsmp
->hfs_jnlfileid
|| cnid
== hfsmp
->hfs_jnlinfoblkid
) {
661 * XXX - need to skip quota files as well.
664 /* Insert a hot file entry. */
665 key
->keyLength
= HFC_KEYLENGTH
;
666 key
->temperature
= HFC_MINIMUM_TEMPERATURE
;
670 error
= BTInsertRecord(filefork
, iterator
, &record
, record
.itemSize
);
672 printf("hfs_recording_init: BTInsertRecord failed %d (fileid %d)\n", error
, key
->fileID
);
673 error
= MacToVFSError(error
);
677 /* Insert the corresponding thread record. */
678 key
->keyLength
= HFC_KEYLENGTH
;
679 key
->temperature
= HFC_LOOKUPTAG
;
682 data
= HFC_MINIMUM_TEMPERATURE
;
683 error
= BTInsertRecord(filefork
, iterator
, &record
, record
.itemSize
);
685 printf("hfs_recording_init: BTInsertRecord failed %d (fileid %d)\n", error
, key
->fileID
);
686 error
= MacToVFSError(error
);
691 (void) BTFlushPath(filefork
);
692 hfs_unlock(VTOC(hfsmp
->hfc_filevp
));
695 hfs_end_transaction(hfsmp
);
697 printf("hfs: %d files identified out of %d\n", inserted
, filecount
);
701 (void) BTScanTerminate(&scanstate
, &data
, &data
, &data
);
703 hfs_end_transaction(hfsmp
);
705 FREE(iterator
, M_TEMP
);
706 if (hfsmp
->hfc_filevp
) {
707 (void) hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
708 hfsmp
->hfc_filevp
= NULL
;
711 hfsmp
->hfc_stage
= HFC_IDLE
;
717 * Use sync to perform ocassional background work.
720 hfs_hotfilesync(struct hfsmount
*hfsmp
, vfs_context_t ctx
)
722 if (hfsmp
->hfc_stage
) {
725 lck_mtx_lock(&hfsmp
->hfc_mutex
);
727 switch (hfsmp
->hfc_stage
) {
729 (void) hfs_recording_start(hfsmp
);
734 if (tv
.tv_sec
> hfsmp
->hfc_timeout
)
735 (void) hfs_recording_stop(hfsmp
);
739 (void) hotfiles_evict(hfsmp
, ctx
);
743 (void) hotfiles_adopt(hfsmp
);
749 lck_mtx_unlock(&hfsmp
->hfc_mutex
);
755 * Add a hot file to the recording list.
757 * This can happen when a hot file gets reclaimed or at the
758 * end of the recording period for any active hot file.
760 * NOTE: Since both the data and resource fork can be hot,
761 * there can be two entries for the same file id.
763 * Note: the cnode is locked on entry.
766 hfs_addhotfile(struct vnode
*vp
)
772 if (hfsmp
->hfc_stage
!= HFC_RECORDING
)
775 lck_mtx_lock(&hfsmp
->hfc_mutex
);
776 error
= hfs_addhotfile_internal(vp
);
777 lck_mtx_unlock(&hfsmp
->hfc_mutex
);
782 hfs_addhotfile_internal(struct vnode
*vp
)
784 hotfile_data_t
*hotdata
;
785 hotfile_entry_t
*entry
;
789 u_int32_t temperature
;
792 if (hfsmp
->hfc_stage
!= HFC_RECORDING
)
796 * Only regular files are eligible for hotfiles addition.
798 * Symlinks were previously added to the list and may exist in
799 * extant hotfiles regions, but no new ones will be added, and no
800 * symlinks will now be relocated/evicted from the hotfiles region.
802 if (!vnode_isreg(vp
) || vnode_issystem(vp
)) {
806 /* Skip resource forks for now. */
807 if (VNODE_IS_RSRC(vp
)) {
810 if ((hotdata
= (hotfile_data_t
*)hfsmp
->hfc_recdata
) == NULL
) {
816 if ((ffp
->ff_bytesread
== 0) ||
817 (ffp
->ff_blocks
== 0) ||
818 (ffp
->ff_size
== 0) ||
819 (ffp
->ff_blocks
> hotdata
->maxblocks
) ||
820 (cp
->c_flag
& (C_DELETED
| C_NOEXISTS
)) ||
821 (cp
->c_bsdflags
& UF_NODUMP
) ||
822 (cp
->c_atime
< hfsmp
->hfc_timebase
)) {
826 temperature
= ffp
->ff_bytesread
/ ffp
->ff_size
;
827 if (temperature
< hotdata
->threshold
) {
831 * If there is room or this file is hotter than
832 * the coldest one then add it to the list.
835 if ((hotdata
->activefiles
< hfsmp
->hfc_maxfiles
) ||
836 (hotdata
->coldest
== NULL
) ||
837 (temperature
> hotdata
->coldest
->temperature
)) {
839 entry
= hf_getnewentry(hotdata
);
840 entry
->temperature
= temperature
;
841 entry
->fileid
= cp
->c_fileid
;
842 entry
->blocks
= ffp
->ff_blocks
;
843 hf_insert(hotdata
, entry
);
851 * Remove a hot file from the recording list.
853 * This can happen when a hot file becomes
854 * an active vnode (active hot files are
855 * not kept in the recording list until the
856 * end of the recording period).
858 * Note: the cnode is locked on entry.
861 hfs_removehotfile(struct vnode
*vp
)
863 hotfile_data_t
*hotdata
;
867 u_int32_t temperature
;
870 if (hfsmp
->hfc_stage
!= HFC_RECORDING
)
873 if ((!vnode_isreg(vp
)) || vnode_issystem(vp
)) {
880 if ((ffp
->ff_bytesread
== 0) || (ffp
->ff_blocks
== 0) ||
881 (ffp
->ff_size
== 0) || (cp
->c_atime
< hfsmp
->hfc_timebase
)) {
885 lck_mtx_lock(&hfsmp
->hfc_mutex
);
886 if (hfsmp
->hfc_stage
!= HFC_RECORDING
)
888 if ((hotdata
= (hotfile_data_t
*)hfsmp
->hfc_recdata
) == NULL
)
891 temperature
= ffp
->ff_bytesread
/ ffp
->ff_size
;
892 if (temperature
< hotdata
->threshold
)
895 if (hotdata
->coldest
&& (temperature
>= hotdata
->coldest
->temperature
)) {
897 hf_delete(hotdata
, VTOC(vp
)->c_fileid
, temperature
);
901 lck_mtx_unlock(&hfsmp
->hfc_mutex
);
907 *========================================================================
908 * HOT FILE MAINTENANCE ROUTINES
909 *========================================================================
913 hotfiles_collect_callback(struct vnode
*vp
, __unused
void *cargs
)
915 if ((vnode_isreg(vp
)) && !vnode_issystem(vp
))
916 (void) hfs_addhotfile_internal(vp
);
918 return (VNODE_RETURNED
);
922 * Add all active hot files to the recording list.
925 hotfiles_collect(struct hfsmount
*hfsmp
)
927 struct mount
*mp
= HFSTOVFS(hfsmp
);
929 if (vfs_busy(mp
, LK_NOWAIT
))
933 * hotfiles_collect_callback will be called for each vnode
934 * hung off of this mount point
936 * properly referenced and unreferenced around the callback
938 vnode_iterate(mp
, 0, hotfiles_collect_callback
, (void *)NULL
);
947 * Update the data of a btree record
948 * This is called from within BTUpdateRecord.
951 update_callback(const HotFileKey
*key
, u_int32_t
*data
, u_int32_t
*state
)
953 if (key
->temperature
== HFC_LOOKUPTAG
)
959 * Identify files already in hot area.
962 hotfiles_refine(struct hfsmount
*hfsmp
)
964 BTreeIterator
* iterator
= NULL
;
966 filefork_t
* filefork
;
967 hotfilelist_t
*listp
;
968 FSBufferDescriptor record
;
975 if ((listp
= (hotfilelist_t
*)hfsmp
->hfc_recdata
) == NULL
)
978 mp
= HFSTOVFS(hfsmp
);
980 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
981 if (iterator
== NULL
) {
985 bzero(iterator
, sizeof(*iterator
));
986 key
= (HotFileKey
*) &iterator
->key
;
988 record
.bufferAddress
= &data
;
989 record
.itemSize
= sizeof(u_int32_t
);
990 record
.itemCount
= 1;
992 if (hfs_start_transaction(hfsmp
) != 0) {
996 if (hfs_lock(VTOC(hfsmp
->hfc_filevp
), HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
) != 0) {
1000 filefork
= VTOF(hfsmp
->hfc_filevp
);
1002 for (i
= 0; i
< listp
->hfl_count
; ++i
) {
1004 * Check if entry (thread) is already in hot area.
1006 key
->keyLength
= HFC_KEYLENGTH
;
1007 key
->temperature
= HFC_LOOKUPTAG
;
1008 key
->fileID
= listp
->hfl_hotfile
[i
].hf_fileid
;
1010 (void) BTInvalidateHint(iterator
);
1011 if (BTSearchRecord(filefork
, iterator
, &record
, NULL
, iterator
) != 0) {
1012 continue; /* not in hot area, so skip */
1016 * Update thread entry with latest temperature.
1018 error
= BTUpdateRecord(filefork
, iterator
,
1019 (IterateCallBackProcPtr
)update_callback
,
1020 &listp
->hfl_hotfile
[i
].hf_temperature
);
1022 printf("hfs: hotfiles_refine: BTUpdateRecord failed %d (file %d)\n", error
, key
->fileID
);
1023 error
= MacToVFSError(error
);
1027 * Re-key entry with latest temperature.
1029 key
->keyLength
= HFC_KEYLENGTH
;
1030 key
->temperature
= data
;
1031 key
->fileID
= listp
->hfl_hotfile
[i
].hf_fileid
;
1033 /* Pick up record data. */
1034 (void) BTInvalidateHint(iterator
);
1035 (void) BTSearchRecord(filefork
, iterator
, &record
, NULL
, iterator
);
1036 error
= BTDeleteRecord(filefork
, iterator
);
1038 printf("hfs: hotfiles_refine: BTDeleteRecord failed %d (file %d)\n", error
, key
->fileID
);
1039 error
= MacToVFSError(error
);
1042 key
->keyLength
= HFC_KEYLENGTH
;
1043 key
->temperature
= listp
->hfl_hotfile
[i
].hf_temperature
;
1044 key
->fileID
= listp
->hfl_hotfile
[i
].hf_fileid
;
1046 error
= BTInsertRecord(filefork
, iterator
, &record
, record
.itemSize
);
1048 printf("hfs: hotfiles_refine: BTInsertRecord failed %d (file %d)\n", error
, key
->fileID
);
1049 error
= MacToVFSError(error
);
1054 * Invalidate this entry in the list.
1056 listp
->hfl_hotfile
[i
].hf_temperature
= 0;
1057 listp
->hfl_totalblocks
-= listp
->hfl_hotfile
[i
].hf_blocks
;
1061 (void) BTFlushPath(filefork
);
1062 hfs_unlock(VTOC(hfsmp
->hfc_filevp
));
1065 hfs_end_transaction(hfsmp
);
1068 FREE(iterator
, M_TEMP
);
1073 * Move new hot files into hot area.
1075 * Requires that the hfc_mutex be held.
1078 hotfiles_adopt(struct hfsmount
*hfsmp
)
1080 BTreeIterator
* iterator
= NULL
;
1082 filefork_t
* filefork
;
1083 hotfilelist_t
*listp
;
1084 FSBufferDescriptor record
;
1087 enum hfc_stage stage
;
1093 int startedtrans
= 0;
1095 if ((listp
= (hotfilelist_t
*)hfsmp
->hfc_recdata
) == NULL
)
1098 if (hfsmp
->hfc_stage
!= HFC_ADOPTION
) {
1101 if (hfs_lock(VTOC(hfsmp
->hfc_filevp
), HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
) != 0) {
1105 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
1106 if (iterator
== NULL
) {
1107 hfs_unlock(VTOC(hfsmp
->hfc_filevp
));
1111 stage
= hfsmp
->hfc_stage
;
1112 hfsmp
->hfc_stage
= HFC_BUSY
;
1115 last
= listp
->hfl_next
+ HFC_FILESPERSYNC
;
1116 if (last
> listp
->hfl_count
)
1117 last
= listp
->hfl_count
;
1119 bzero(iterator
, sizeof(*iterator
));
1120 key
= (HotFileKey
*) &iterator
->key
;
1121 key
->keyLength
= HFC_KEYLENGTH
;
1123 record
.bufferAddress
= &data
;
1124 record
.itemSize
= sizeof(u_int32_t
);
1125 record
.itemCount
= 1;
1127 filefork
= VTOF(hfsmp
->hfc_filevp
);
1129 for (i
= listp
->hfl_next
; (i
< last
) && (blksmoved
< HFC_BLKSPERSYNC
); ++i
) {
1131 * Skip invalid entries (already in hot area).
1133 if (listp
->hfl_hotfile
[i
].hf_temperature
== 0) {
1138 * Acquire a vnode for this file.
1140 error
= hfs_vget(hfsmp
, listp
->hfl_hotfile
[i
].hf_fileid
, &vp
, 0, 0);
1142 if (error
== ENOENT
) {
1145 continue; /* stale entry, go to next */
1149 if (!vnode_isreg(vp
)) {
1150 /* Symlinks are ineligible for adoption into the hotfile zone. */
1151 printf("hfs: hotfiles_adopt: huh, not a file %d (%d)\n", listp
->hfl_hotfile
[i
].hf_fileid
, VTOC(vp
)->c_cnid
);
1152 hfs_unlock(VTOC(vp
));
1154 listp
->hfl_hotfile
[i
].hf_temperature
= 0;
1156 continue; /* stale entry, go to next */
1158 if (hotextents(hfsmp
, &VTOF(vp
)->ff_extents
[0])) {
1159 hfs_unlock(VTOC(vp
));
1161 listp
->hfl_hotfile
[i
].hf_temperature
= 0;
1163 listp
->hfl_totalblocks
-= listp
->hfl_hotfile
[i
].hf_blocks
;
1164 continue; /* stale entry, go to next */
1166 fileblocks
= VTOF(vp
)->ff_blocks
;
1167 if (fileblocks
> hfsmp
->hfs_hotfile_freeblks
) {
1168 hfs_unlock(VTOC(vp
));
1171 listp
->hfl_totalblocks
-= fileblocks
;
1172 continue; /* entry too big, go to next */
1175 if ((blksmoved
> 0) &&
1176 (blksmoved
+ fileblocks
) > HFC_BLKSPERSYNC
) {
1177 hfs_unlock(VTOC(vp
));
1179 break; /* adopt this entry the next time around */
1181 if (VTOC(vp
)->c_desc
.cd_nameptr
)
1182 data
= *(const u_int32_t
*)(VTOC(vp
)->c_desc
.cd_nameptr
);
1186 error
= hfs_relocate(vp
, hfsmp
->hfs_hotfile_start
, kauth_cred_get(), current_proc());
1187 hfs_unlock(VTOC(vp
));
1190 /* Move on to next item. */
1194 /* Keep hot file free space current. */
1195 hfsmp
->hfs_hotfile_freeblks
-= fileblocks
;
1196 listp
->hfl_totalblocks
-= fileblocks
;
1198 /* Insert hot file entry */
1199 key
->keyLength
= HFC_KEYLENGTH
;
1200 key
->temperature
= listp
->hfl_hotfile
[i
].hf_temperature
;
1201 key
->fileID
= listp
->hfl_hotfile
[i
].hf_fileid
;
1204 /* Start a new transaction before calling BTree code. */
1205 if (hfs_start_transaction(hfsmp
) != 0) {
1211 error
= BTInsertRecord(filefork
, iterator
, &record
, record
.itemSize
);
1213 printf("hfs: hotfiles_adopt: BTInsertRecord failed %d (fileid %d)\n", error
, key
->fileID
);
1214 error
= MacToVFSError(error
);
1219 /* Insert thread record */
1220 key
->keyLength
= HFC_KEYLENGTH
;
1221 key
->temperature
= HFC_LOOKUPTAG
;
1222 key
->fileID
= listp
->hfl_hotfile
[i
].hf_fileid
;
1224 data
= listp
->hfl_hotfile
[i
].hf_temperature
;
1225 error
= BTInsertRecord(filefork
, iterator
, &record
, record
.itemSize
);
1227 printf("hfs: hotfiles_adopt: BTInsertRecord failed %d (fileid %d)\n", error
, key
->fileID
);
1228 error
= MacToVFSError(error
);
1232 (void) BTFlushPath(filefork
);
1234 /* Transaction complete. */
1236 hfs_end_transaction(hfsmp
);
1240 blksmoved
+= fileblocks
;
1242 if (listp
->hfl_next
>= listp
->hfl_count
) {
1245 if (hfsmp
->hfs_hotfile_freeblks
<= 0) {
1247 printf("hfs: hotfiles_adopt: free space exhausted (%d)\n", hfsmp
->hfs_hotfile_freeblks
);
1254 printf("hfs: hotfiles_adopt: [%d] adopted %d blocks (%d left)\n", listp
->hfl_next
, blksmoved
, listp
->hfl_totalblocks
);
1256 /* Finish any outstanding transactions. */
1258 (void) BTFlushPath(filefork
);
1259 hfs_end_transaction(hfsmp
);
1262 hfs_unlock(VTOC(hfsmp
->hfc_filevp
));
1264 if ((listp
->hfl_next
>= listp
->hfl_count
) || (hfsmp
->hfs_hotfile_freeblks
<= 0)) {
1266 printf("hfs: hotfiles_adopt: all done relocating %d files\n", listp
->hfl_count
);
1267 printf("hfs: hotfiles_adopt: %d blocks free in hot file band\n", hfsmp
->hfs_hotfile_freeblks
);
1271 FREE(iterator
, M_TEMP
);
1273 if (stage
!= HFC_ADOPTION
&& hfsmp
->hfc_filevp
) {
1274 (void) hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
1275 hfsmp
->hfc_filevp
= NULL
;
1277 hfsmp
->hfc_stage
= stage
;
1278 wakeup((caddr_t
)&hfsmp
->hfc_stage
);
1283 * Reclaim space by evicting the coldest files.
1285 * Requires that the hfc_mutex be held.
1288 hotfiles_evict(struct hfsmount
*hfsmp
, vfs_context_t ctx
)
1290 BTreeIterator
* iterator
= NULL
;
1293 filefork_t
* filefork
;
1294 hotfilelist_t
*listp
;
1295 enum hfc_stage stage
;
1296 u_int32_t savedtemp
;
1301 int startedtrans
= 0;
1304 if (hfsmp
->hfc_stage
!= HFC_EVICTION
) {
1308 if ((listp
= (hotfilelist_t
*)hfsmp
->hfc_recdata
) == NULL
)
1311 if (hfs_lock(VTOC(hfsmp
->hfc_filevp
), HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
) != 0) {
1315 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
1316 if (iterator
== NULL
) {
1317 hfs_unlock(VTOC(hfsmp
->hfc_filevp
));
1321 stage
= hfsmp
->hfc_stage
;
1322 hfsmp
->hfc_stage
= HFC_BUSY
;
1324 filesmoved
= blksmoved
= 0;
1325 bt_op
= kBTreeFirstRecord
;
1327 bzero(iterator
, sizeof(*iterator
));
1328 key
= (HotFileKey
*) &iterator
->key
;
1330 filefork
= VTOF(hfsmp
->hfc_filevp
);
1332 while (listp
->hfl_reclaimblks
> 0 &&
1333 blksmoved
< HFC_BLKSPERSYNC
&&
1334 filesmoved
< HFC_FILESPERSYNC
) {
1337 * Obtain the first record (ie the coldest one).
1339 if (BTIterateRecord(filefork
, bt_op
, iterator
, NULL
, NULL
) != 0) {
1341 printf("hfs: hotfiles_evict: no more records\n");
1344 stage
= HFC_ADOPTION
;
1347 if (key
->keyLength
!= HFC_KEYLENGTH
) {
1348 printf("hfs: hotfiles_evict: invalid key length %d\n", key
->keyLength
);
1352 if (key
->temperature
== HFC_LOOKUPTAG
) {
1354 printf("hfs: hotfiles_evict: ran into thread records\n");
1357 stage
= HFC_ADOPTION
;
1361 * Aquire the vnode for this file.
1363 error
= hfs_vget(hfsmp
, key
->fileID
, &vp
, 0, 0);
1365 if (error
== ENOENT
) {
1366 goto delete; /* stale entry, go to next */
1368 printf("hfs: hotfiles_evict: err %d getting file %d\n",
1369 error
, key
->fileID
);
1375 * Symlinks that may have been inserted into the hotfile zone during a previous OS are now stuck
1376 * here. We do not want to move them.
1378 if (!vnode_isreg(vp
)) {
1379 printf("hfs: hotfiles_evict: huh, not a file %d\n", key
->fileID
);
1380 hfs_unlock(VTOC(vp
));
1382 goto delete; /* invalid entry, go to next */
1385 fileblocks
= VTOF(vp
)->ff_blocks
;
1386 if ((blksmoved
> 0) &&
1387 (blksmoved
+ fileblocks
) > HFC_BLKSPERSYNC
) {
1388 hfs_unlock(VTOC(vp
));
1393 * Make sure file is in the hot area.
1395 if (!hotextents(hfsmp
, &VTOF(vp
)->ff_extents
[0])) {
1397 printf("hfs: hotfiles_evict: file %d isn't hot!\n", key
->fileID
);
1399 hfs_unlock(VTOC(vp
));
1401 goto delete; /* stale entry, go to next */
1405 * Relocate file out of hot area.
1407 error
= hfs_relocate(vp
, HFSTOVCB(hfsmp
)->nextAllocation
, vfs_context_ucred(ctx
), vfs_context_proc(ctx
));
1409 printf("hfs: hotfiles_evict: err %d relocating file %d\n", error
, key
->fileID
);
1410 hfs_unlock(VTOC(vp
));
1412 bt_op
= kBTreeNextRecord
;
1413 goto next
; /* go to next */
1417 // We do not believe that this call to hfs_fsync() is
1418 // necessary and it causes a journal transaction
1419 // deadlock so we are removing it.
1421 // (void) hfs_fsync(vp, MNT_WAIT, 0, p);
1423 hfs_unlock(VTOC(vp
));
1426 hfsmp
->hfs_hotfile_freeblks
+= fileblocks
;
1427 listp
->hfl_reclaimblks
-= fileblocks
;
1428 if (listp
->hfl_reclaimblks
< 0)
1429 listp
->hfl_reclaimblks
= 0;
1430 blksmoved
+= fileblocks
;
1433 /* Start a new transaction before calling BTree code. */
1434 if (hfs_start_transaction(hfsmp
) != 0) {
1440 error
= BTDeleteRecord(filefork
, iterator
);
1442 error
= MacToVFSError(error
);
1445 savedtemp
= key
->temperature
;
1446 key
->temperature
= HFC_LOOKUPTAG
;
1447 error
= BTDeleteRecord(filefork
, iterator
);
1449 error
= MacToVFSError(error
);
1452 key
->temperature
= savedtemp
;
1454 (void) BTFlushPath(filefork
);
1456 /* Transaction complete. */
1458 hfs_end_transaction(hfsmp
);
1465 printf("hfs: hotfiles_evict: moved %d files (%d blks, %d to go)\n", filesmoved
, blksmoved
, listp
->hfl_reclaimblks
);
1467 /* Finish any outstanding transactions. */
1469 (void) BTFlushPath(filefork
);
1470 hfs_end_transaction(hfsmp
);
1473 hfs_unlock(VTOC(hfsmp
->hfc_filevp
));
1476 * Move to next stage when finished.
1478 if (listp
->hfl_reclaimblks
<= 0) {
1479 stage
= HFC_ADOPTION
;
1481 printf("hfs: hotfiles_evict: %d blocks free in hot file band\n", hfsmp
->hfs_hotfile_freeblks
);
1484 FREE(iterator
, M_TEMP
);
1485 hfsmp
->hfc_stage
= stage
;
1486 wakeup((caddr_t
)&hfsmp
->hfc_stage
);
1491 * Age the existing records in the hot files b-tree.
1494 hotfiles_age(struct hfsmount
*hfsmp
)
1496 BTreeInfoRec btinfo
;
1497 BTreeIterator
* iterator
= NULL
;
1498 BTreeIterator
* prev_iterator
;
1499 FSBufferDescriptor record
;
1500 FSBufferDescriptor prev_record
;
1502 HotFileKey
* prev_key
;
1503 filefork_t
* filefork
;
1505 u_int32_t prev_data
;
1514 MALLOC(iterator
, BTreeIterator
*, 2 * sizeof(*iterator
), M_TEMP
, M_WAITOK
);
1515 if (iterator
== NULL
) {
1519 bzero(iterator
, 2 * sizeof(*iterator
));
1520 key
= (HotFileKey
*) &iterator
->key
;
1522 prev_iterator
= &iterator
[1];
1523 prev_key
= (HotFileKey
*) &prev_iterator
->key
;
1525 record
.bufferAddress
= &data
;
1526 record
.itemSize
= sizeof(data
);
1527 record
.itemCount
= 1;
1528 prev_record
.bufferAddress
= &prev_data
;
1529 prev_record
.itemSize
= sizeof(prev_data
);
1530 prev_record
.itemCount
= 1;
1533 * Capture b-tree changes inside a transaction
1535 if (hfs_start_transaction(hfsmp
) != 0) {
1539 if (hfs_lock(VTOC(hfsmp
->hfc_filevp
), HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
) != 0) {
1543 filefork
= VTOF(hfsmp
->hfc_filevp
);
1545 error
= BTGetInformation(filefork
, 0, &btinfo
);
1547 error
= MacToVFSError(error
);
1550 if (btinfo
.numRecords
< 2) {
1555 /* Only want 1st half of leaf records */
1556 numrecs
= (btinfo
.numRecords
/= 2) - 1;
1558 error
= BTIterateRecord(filefork
, kBTreeFirstRecord
, iterator
, &record
, &reclen
);
1560 printf("hfs_agehotfiles: BTIterateRecord: %d\n", error
);
1561 error
= MacToVFSError(error
);
1564 bcopy(iterator
, prev_iterator
, sizeof(BTreeIterator
));
1567 for (i
= 0; i
< numrecs
; ++i
) {
1568 error
= BTIterateRecord(filefork
, kBTreeNextRecord
, iterator
, &record
, &reclen
);
1570 if (key
->temperature
< prev_key
->temperature
) {
1571 printf("hfs_agehotfiles: out of order keys!\n");
1575 if (reclen
!= sizeof(data
)) {
1576 printf("hfs_agehotfiles: invalid record length %d\n", reclen
);
1580 if (key
->keyLength
!= HFC_KEYLENGTH
) {
1581 printf("hfs_agehotfiles: invalid key length %d\n", key
->keyLength
);
1585 } else if ((error
== fsBTEndOfIterationErr
|| error
== fsBTRecordNotFoundErr
) &&
1586 (i
== (numrecs
- 1))) {
1589 printf("hfs_agehotfiles: %d of %d BTIterateRecord: %d\n", i
, numrecs
, error
);
1590 error
= MacToVFSError(error
);
1593 if (prev_key
->temperature
== HFC_LOOKUPTAG
) {
1595 printf("hfs_agehotfiles: ran into thread record\n");
1600 error
= BTDeleteRecord(filefork
, prev_iterator
);
1602 printf("hfs_agehotfiles: BTDeleteRecord failed %d (file %d)\n", error
, prev_key
->fileID
);
1603 error
= MacToVFSError(error
);
1607 /* Age by halving the temperature (floor = 4) */
1608 newtemp
= MAX(prev_key
->temperature
>> 1, 4);
1609 prev_key
->temperature
= newtemp
;
1611 error
= BTInsertRecord(filefork
, prev_iterator
, &prev_record
, prev_record
.itemSize
);
1613 printf("hfs_agehotfiles: BTInsertRecord failed %d (file %d)\n", error
, prev_key
->fileID
);
1614 error
= MacToVFSError(error
);
1619 * Update thread entry with latest temperature.
1621 prev_key
->temperature
= HFC_LOOKUPTAG
;
1622 error
= BTUpdateRecord(filefork
, prev_iterator
,
1623 (IterateCallBackProcPtr
)update_callback
,
1626 printf("hfs_agehotfiles: %d of %d BTUpdateRecord failed %d (file %d, %d)\n",
1627 i
, numrecs
, error
, prev_key
->fileID
, newtemp
);
1628 error
= MacToVFSError(error
);
1632 bcopy(iterator
, prev_iterator
, sizeof(BTreeIterator
));
1639 printf("hfs_agehotfiles: aged %d records out of %d\n", aged
, btinfo
.numRecords
);
1641 (void) BTFlushPath(filefork
);
1643 hfs_unlock(VTOC(hfsmp
->hfc_filevp
));
1645 hfs_end_transaction(hfsmp
);
1648 FREE(iterator
, M_TEMP
);
1653 * Return true if any blocks (or all blocks if all is true)
1654 * are contained in the hot file region.
1657 hotextents(struct hfsmount
*hfsmp
, HFSPlusExtentDescriptor
* extents
)
1663 for (i
= 0; i
< kHFSPlusExtentDensity
; ++i
) {
1664 b1
= extents
[i
].startBlock
;
1667 b2
= b1
+ extents
[i
].blockCount
- 1;
1668 if ((b1
>= hfsmp
->hfs_hotfile_start
&&
1669 b2
<= hfsmp
->hfs_hotfile_end
) ||
1670 (b1
< hfsmp
->hfs_hotfile_end
&&
1671 b2
> hfsmp
->hfs_hotfile_end
)) {
1681 *========================================================================
1682 * HOT FILE B-TREE ROUTINES
1683 *========================================================================
1687 * Open the hot files b-tree for writing.
1689 * On successful exit the vnode has a reference but not an iocount.
1692 hfc_btree_open(struct hfsmount
*hfsmp
, struct vnode
**vpp
)
1696 struct cat_desc cdesc
;
1697 struct cat_attr cattr
;
1698 struct cat_fork cfork
;
1699 static char filename
[] = HFC_FILENAME
;
1703 int newvnode_flags
= 0;
1708 bzero(&cdesc
, sizeof(cdesc
));
1709 cdesc
.cd_parentcnid
= kRootDirID
;
1710 cdesc
.cd_nameptr
= (const u_int8_t
*)filename
;
1711 cdesc
.cd_namelen
= strlen(filename
);
1713 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_SHARED_LOCK
);
1715 error
= cat_lookup(hfsmp
, &cdesc
, 0, 0, &cdesc
, &cattr
, &cfork
, NULL
);
1717 hfs_systemfile_unlock(hfsmp
, lockflags
);
1720 printf("hfs: hfc_btree_open: cat_lookup error %d\n", error
);
1724 cdesc
.cd_flags
|= CD_ISMETA
;
1725 error
= hfs_getnewvnode(hfsmp
, NULL
, NULL
, &cdesc
, 0, &cattr
,
1726 &cfork
, &vp
, &newvnode_flags
);
1728 printf("hfs: hfc_btree_open: hfs_getnewvnode error %d\n", error
);
1729 cat_releasedesc(&cdesc
);
1732 if (!vnode_issystem(vp
)) {
1734 printf("hfs: hfc_btree_open: file has UBC, try again\n");
1736 hfs_unlock(VTOC(vp
));
1745 /* Open the B-tree file for writing... */
1746 error
= BTOpenPath(VTOF(vp
), (KeyCompareProcPtr
) hfc_comparekeys
);
1748 printf("hfs: hfc_btree_open: BTOpenPath error %d\n", error
);
1749 error
= MacToVFSError(error
);
1752 hfs_unlock(VTOC(vp
));
1755 vnode_ref(vp
); /* keep a reference while its open */
1759 if (!vnode_issystem(vp
))
1760 panic("hfs: hfc_btree_open: not a system file (vp = %p)", vp
);
1766 * Close the hot files b-tree.
1768 * On entry the vnode has a reference.
1771 hfc_btree_close(struct hfsmount
*hfsmp
, struct vnode
*vp
)
1773 proc_t p
= current_proc();
1778 hfs_journal_flush(hfsmp
, FALSE
);
1781 if (vnode_get(vp
) == 0) {
1782 error
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
);
1784 (void) hfs_fsync(vp
, MNT_WAIT
, 0, p
);
1785 error
= BTClosePath(VTOF(vp
));
1786 hfs_unlock(VTOC(vp
));
1797 * Create a hot files btree file.
1801 hfc_btree_create(struct hfsmount
*hfsmp
, unsigned int nodesize
, unsigned int entries
)
1803 struct vnode
*dvp
= NULL
;
1804 struct vnode
*vp
= NULL
;
1805 struct cnode
*cp
= NULL
;
1806 vfs_context_t ctx
= vfs_context_current();
1807 struct vnode_attr va
;
1808 struct componentname cname
;
1809 static char filename
[] = HFC_FILENAME
;
1812 if (hfsmp
->hfc_filevp
)
1813 panic("hfs: hfc_btree_create: hfc_filevp exists (vp = %p)", hfsmp
->hfc_filevp
);
1815 error
= VFS_ROOT(HFSTOVFS(hfsmp
), &dvp
, ctx
);
1819 cname
.cn_nameiop
= CREATE
;
1820 cname
.cn_flags
= ISLASTCN
;
1821 cname
.cn_context
= ctx
;
1822 cname
.cn_pnbuf
= filename
;
1823 cname
.cn_pnlen
= sizeof(filename
);
1824 cname
.cn_nameptr
= filename
;
1825 cname
.cn_namelen
= strlen(filename
);
1827 cname
.cn_consume
= 0;
1830 VATTR_SET(&va
, va_type
, VREG
);
1831 VATTR_SET(&va
, va_mode
, S_IFREG
| S_IRUSR
| S_IWUSR
);
1832 VATTR_SET(&va
, va_uid
, 0);
1833 VATTR_SET(&va
, va_gid
, 0);
1835 if (hfs_start_transaction(hfsmp
) != 0) {
1840 /* call ourselves directly, ignore the higher-level VFS file creation code */
1841 error
= VNOP_CREATE(dvp
, &vp
, &cname
, &va
, ctx
);
1843 printf("hfs: error %d creating HFBT on %s\n", error
, HFSTOVCB(hfsmp
)->vcbVN
);
1850 if ((error
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
))) {
1855 /* Don't use non-regular files or files with links. */
1856 if (!vnode_isreg(vp
) || cp
->c_linkcount
!= 1) {
1861 printf("hfs: created HFBT on %s\n", HFSTOVCB(hfsmp
)->vcbVN
);
1863 if (VTOF(vp
)->ff_size
< nodesize
) {
1867 BTNodeDescriptor
*ndp
;
1869 HotFilesInfo
*hotfileinfo
;
1875 * Mark it invisible (truncate will pull these changes).
1877 ((FndrFileInfo
*)&cp
->c_finderinfo
[0])->fdFlags
|=
1878 SWAP_BE16 (kIsInvisible
+ kNameLocked
);
1880 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&buffer
, nodesize
)) {
1884 bzero(buffer
, nodesize
);
1885 index
= (u_int16_t
*)buffer
;
1887 entirespernode
= (nodesize
- sizeof(BTNodeDescriptor
) - 2) /
1888 (sizeof(HotFileKey
) + 6);
1889 nodecnt
= 2 + howmany(entries
* 2, entirespernode
);
1890 nodecnt
= roundup(nodecnt
, 8);
1891 filesize
= nodecnt
* nodesize
;
1893 /* FILL IN THE NODE DESCRIPTOR: */
1894 ndp
= (BTNodeDescriptor
*)buffer
;
1895 ndp
->kind
= kBTHeaderNode
;
1896 ndp
->numRecords
= SWAP_BE16 (3);
1897 offset
= sizeof(BTNodeDescriptor
);
1898 index
[(nodesize
/ 2) - 1] = SWAP_BE16 (offset
);
1900 /* FILL IN THE HEADER RECORD: */
1901 bthp
= (BTHeaderRec
*)((u_int8_t
*)buffer
+ offset
);
1902 bthp
->nodeSize
= SWAP_BE16 (nodesize
);
1903 bthp
->totalNodes
= SWAP_BE32 (filesize
/ nodesize
);
1904 bthp
->freeNodes
= SWAP_BE32 (nodecnt
- 1);
1905 bthp
->clumpSize
= SWAP_BE32 (filesize
);
1906 bthp
->btreeType
= kUserBTreeType
; /* non-metadata */
1907 bthp
->attributes
|= SWAP_BE32 (kBTBigKeysMask
);
1908 bthp
->maxKeyLength
= SWAP_BE16 (HFC_KEYLENGTH
);
1909 offset
+= sizeof(BTHeaderRec
);
1910 index
[(nodesize
/ 2) - 2] = SWAP_BE16 (offset
);
1912 /* FILL IN THE USER RECORD: */
1913 hotfileinfo
= (HotFilesInfo
*)((u_int8_t
*)buffer
+ offset
);
1914 hotfileinfo
->magic
= SWAP_BE32 (HFC_MAGIC
);
1915 hotfileinfo
->version
= SWAP_BE32 (HFC_VERSION
);
1916 hotfileinfo
->duration
= SWAP_BE32 (HFC_DEFAULT_DURATION
);
1917 hotfileinfo
->timebase
= 0;
1918 hotfileinfo
->timeleft
= 0;
1919 hotfileinfo
->threshold
= SWAP_BE32 (HFC_MINIMUM_TEMPERATURE
);
1920 hotfileinfo
->maxfileblks
= SWAP_BE32 (HFC_MAXIMUM_FILESIZE
/ HFSTOVCB(hfsmp
)->blockSize
);
1921 hotfileinfo
->maxfilecnt
= SWAP_BE32 (HFC_DEFAULT_FILE_COUNT
);
1922 strlcpy((char *)hotfileinfo
->tag
, hfc_tag
,
1923 sizeof hotfileinfo
->tag
);
1924 offset
+= kBTreeHeaderUserBytes
;
1925 index
[(nodesize
/ 2) - 3] = SWAP_BE16 (offset
);
1927 /* FILL IN THE MAP RECORD (only one node in use). */
1928 *((u_int8_t
*)buffer
+ offset
) = 0x80;
1929 offset
+= nodesize
- sizeof(BTNodeDescriptor
) - sizeof(BTHeaderRec
)
1930 - kBTreeHeaderUserBytes
- (4 * sizeof(int16_t));
1931 index
[(nodesize
/ 2) - 4] = SWAP_BE16 (offset
);
1933 vnode_setnoflush(vp
);
1934 error
= hfs_truncate(vp
, (off_t
)filesize
, IO_NDELAY
, 0, 0, ctx
);
1936 printf("hfs: error %d growing HFBT on %s\n", error
, HFSTOVCB(hfsmp
)->vcbVN
);
1939 cp
->c_flag
|= C_ZFWANTSYNC
;
1940 cp
->c_zftimeout
= 1;
1943 struct vnop_write_args args
;
1946 auio
= uio_create(1, 0, UIO_SYSSPACE
, UIO_WRITE
);
1947 uio_addiov(auio
, (uintptr_t)buffer
, nodesize
);
1949 args
.a_desc
= &vnop_write_desc
;
1953 args
.a_context
= ctx
;
1958 error
= hfs_vnop_write(&args
);
1960 printf("hfs: error %d writing HFBT on %s\n", error
, HFSTOVCB(hfsmp
)->vcbVN
);
1964 kmem_free(kernel_map
, (vm_offset_t
)buffer
, nodesize
);
1967 hfs_end_transaction(hfsmp
);
1981 * Compare two hot file b-tree keys.
1983 * Result: +n search key > trial key
1984 * 0 search key = trial key
1985 * -n search key < trial key
1988 hfc_comparekeys(HotFileKey
*searchKey
, HotFileKey
*trialKey
)
1991 * Compared temperatures first.
1993 if (searchKey
->temperature
== trialKey
->temperature
) {
1995 * Temperatures are equal so compare file ids.
1997 if (searchKey
->fileID
== trialKey
->fileID
) {
1999 * File ids are equal so compare fork types.
2001 if (searchKey
->forkType
== trialKey
->forkType
) {
2003 } else if (searchKey
->forkType
> trialKey
->forkType
) {
2006 } else if (searchKey
->fileID
> trialKey
->fileID
) {
2009 } else if (searchKey
->temperature
> trialKey
->temperature
) {
2018 *========================================================================
2019 * HOT FILE DATA COLLECTING ROUTINES
2020 *========================================================================
2024 * Lookup a hot file entry in the tree.
2027 static hotfile_entry_t
*
2028 hf_lookup(hotfile_data_t
*hotdata
, u_int32_t fileid
, u_int32_t temperature
)
2030 hotfile_entry_t
*entry
= hotdata
->rootentry
;
2033 entry
->temperature
!= temperature
&&
2034 entry
->fileid
!= fileid
) {
2036 if (temperature
> entry
->temperature
)
2037 entry
= entry
->right
;
2038 else if (temperature
< entry
->temperature
)
2039 entry
= entry
->left
;
2040 else if (fileid
> entry
->fileid
)
2041 entry
= entry
->right
;
2043 entry
= entry
->left
;
2050 * Insert a hot file entry into the tree.
2053 hf_insert(hotfile_data_t
*hotdata
, hotfile_entry_t
*newentry
)
2055 hotfile_entry_t
*entry
= hotdata
->rootentry
;
2056 u_int32_t fileid
= newentry
->fileid
;
2057 u_int32_t temperature
= newentry
->temperature
;
2059 if (entry
== NULL
) {
2060 hotdata
->rootentry
= newentry
;
2061 hotdata
->coldest
= newentry
;
2062 hotdata
->activefiles
++;
2067 if (temperature
> entry
->temperature
) {
2069 entry
= entry
->right
;
2071 entry
->right
= newentry
;
2074 } else if (temperature
< entry
->temperature
) {
2076 entry
= entry
->left
;
2078 entry
->left
= newentry
;
2081 } else if (fileid
> entry
->fileid
) {
2083 entry
= entry
->right
;
2085 if (entry
->fileid
!= fileid
)
2086 entry
->right
= newentry
;
2091 entry
= entry
->left
;
2093 if (entry
->fileid
!= fileid
)
2094 entry
->left
= newentry
;
2100 hotdata
->activefiles
++;
2104 * Find the coldest entry in the tree.
2106 static hotfile_entry_t
*
2107 hf_coldest(hotfile_data_t
*hotdata
)
2109 hotfile_entry_t
*entry
= hotdata
->rootentry
;
2113 entry
= entry
->left
;
2119 * Find the hottest entry in the tree.
2121 static hotfile_entry_t
*
2122 hf_hottest(hotfile_data_t
*hotdata
)
2124 hotfile_entry_t
*entry
= hotdata
->rootentry
;
2127 while (entry
->right
)
2128 entry
= entry
->right
;
2134 * Delete a hot file entry from the tree.
2137 hf_delete(hotfile_data_t
*hotdata
, u_int32_t fileid
, u_int32_t temperature
)
2139 hotfile_entry_t
*entry
, *parent
, *next
;
2142 entry
= hotdata
->rootentry
;
2145 entry
->temperature
!= temperature
&&
2146 entry
->fileid
!= fileid
) {
2149 if (temperature
> entry
->temperature
)
2150 entry
= entry
->right
;
2151 else if (temperature
< entry
->temperature
)
2152 entry
= entry
->left
;
2153 else if (fileid
> entry
->fileid
)
2154 entry
= entry
->right
;
2156 entry
= entry
->left
;
2161 * Reorginize the sub-trees spanning from our entry.
2163 if ((next
= entry
->right
)) {
2164 hotfile_entry_t
*pnextl
, *psub
;
2166 * Tree pruning: take the left branch of the
2167 * current entry and place it at the lowest
2168 * left branch of the current right branch
2172 /* Walk the Right/Left sub tree from current entry */
2173 while ((pnextl
= psub
->left
))
2176 /* Plug the old left tree to the new ->Right leftmost entry */
2177 psub
->left
= entry
->left
;
2179 } else /* only left sub-tree, simple case */ {
2183 * Now, plug the current entry sub tree to
2184 * the good pointer of our parent entry.
2187 hotdata
->rootentry
= next
;
2188 else if (parent
->left
== entry
)
2189 parent
->left
= next
;
2191 parent
->right
= next
;
2193 /* Place entry back on the free-list */
2196 entry
->temperature
= 0;
2198 entry
->right
= hotdata
->freelist
;
2199 hotdata
->freelist
= entry
;
2200 hotdata
->activefiles
--;
2202 if (hotdata
->coldest
== entry
|| hotdata
->coldest
== NULL
) {
2203 hotdata
->coldest
= hf_coldest(hotdata
);
2210 * Get a free hot file entry.
2212 static hotfile_entry_t
*
2213 hf_getnewentry(hotfile_data_t
*hotdata
)
2215 hotfile_entry_t
* entry
;
2218 * When the free list is empty then steal the coldest one
2220 if (hotdata
->freelist
== NULL
) {
2221 entry
= hf_coldest(hotdata
);
2222 hf_delete(hotdata
, entry
->fileid
, entry
->temperature
);
2224 entry
= hotdata
->freelist
;
2225 hotdata
->freelist
= entry
->right
;
2233 * Generate a sorted list of hot files (hottest to coldest).
2235 * As a side effect, every node in the hot file tree will be
2236 * deleted (moved to the free list).
2239 hf_getsortedlist(hotfile_data_t
* hotdata
, hotfilelist_t
*sortedlist
)
2242 hotfile_entry_t
*entry
;
2244 while ((entry
= hf_hottest(hotdata
)) != NULL
) {
2245 sortedlist
->hfl_hotfile
[i
].hf_fileid
= entry
->fileid
;
2246 sortedlist
->hfl_hotfile
[i
].hf_temperature
= entry
->temperature
;
2247 sortedlist
->hfl_hotfile
[i
].hf_blocks
= entry
->blocks
;
2248 sortedlist
->hfl_totalblocks
+= entry
->blocks
;
2251 hf_delete(hotdata
, entry
->fileid
, entry
->temperature
);
2254 sortedlist
->hfl_count
= i
;
2257 printf("hfs: hf_getsortedlist returned %d entries\n", i
);
2264 hf_maxdepth(hotfile_entry_t
* root
, int depth
, int *maxdepth
)
2268 if (depth
> *maxdepth
)
2270 hf_maxdepth(root
->left
, depth
, maxdepth
);
2271 hf_maxdepth(root
->right
, depth
, maxdepth
);
2276 hf_printtree(hotfile_entry_t
* root
)
2279 hf_printtree(root
->left
);
2280 printf("hfs: temperature: % 8d, fileid %d\n", root
->temperature
, root
->fileid
);
2281 hf_printtree(root
->right
);