2 * Copyright (c) 2003-2005 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_OSREFERENCE_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
10 * License may not be used to create, or enable the creation or
11 * redistribution of, unlawful or unlicensed copies of an Apple operating
12 * system, or to circumvent, violate, or enable the circumvention or
13 * violation of, any terms of an Apple operating system software license
16 * Please obtain a copy of the License at
17 * http://www.opensource.apple.com/apsl/ and read it before using this
20 * The Original Code and all software distributed under the License are
21 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
22 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
23 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
25 * Please see the License for the specific language governing rights and
26 * limitations under the License.
28 * @APPLE_LICENSE_OSREFERENCE_HEADER_END@
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/fcntl.h>
34 #include <sys/kernel.h>
35 #include <sys/malloc.h>
37 #include <sys/vnode.h>
38 #include <sys/vnode_internal.h>
39 #include <sys/kauth.h>
42 #include <hfs/hfs_endian.h>
43 #include <hfs/hfs_format.h>
44 #include <hfs/hfs_mount.h>
45 #include <hfs/hfs_hotfiles.h>
47 #include "hfscommon/headers/BTreeScanner.h"
56 * Hot File List (runtime).
58 typedef struct hotfileinfo
{
60 u_int32_t hf_temperature
;
64 typedef struct hotfilelist
{
66 u_int32_t hfl_version
;
67 time_t hfl_duration
; /* duration of sample period */
68 int hfl_count
; /* count of hot files recorded */
69 int hfl_next
; /* next file to move */
70 int hfl_totalblocks
; /* total hot file blocks */
71 int hfl_reclaimblks
; /* blocks to reclaim in HFV */
72 u_int32_t hfl_spare
[2];
73 hotfileinfo_t hfl_hotfile
[1]; /* array of hot files */
78 * Hot File Entry (runtime).
80 typedef struct hotfile_entry
{
81 struct hotfile_entry
*left
;
82 struct hotfile_entry
*right
;
84 u_int32_t temperature
;
89 * Hot File Recording Data (runtime).
91 typedef struct hotfile_data
{
92 struct hfsmount
*hfsmp
;
94 int activefiles
; /* active number of hot files */
97 hotfile_entry_t
*rootentry
;
98 hotfile_entry_t
*freelist
;
99 hotfile_entry_t
*coldest
;
100 hotfile_entry_t entries
[1];
103 static int hfs_recording_start (struct hfsmount
*);
104 static int hfs_recording_stop (struct hfsmount
*);
108 * Hot File Data recording functions (in-memory binary tree).
110 static void hf_insert (hotfile_data_t
*, hotfile_entry_t
*);
111 static void hf_delete (hotfile_data_t
*, u_int32_t
, u_int32_t
);
112 static hotfile_entry_t
* hf_coldest (hotfile_data_t
*);
113 static hotfile_entry_t
* hf_getnewentry (hotfile_data_t
*);
114 static void hf_getsortedlist (hotfile_data_t
*, hotfilelist_t
*);
117 static hotfile_entry_t
* hf_lookup (hotfile_data_t
*, u_int32_t
, u_int32_t
);
118 static void hf_maxdepth(hotfile_entry_t
*, int, int *);
119 static void hf_printtree (hotfile_entry_t
*);
123 * Hot File misc support functions.
125 static int hotfiles_collect (struct hfsmount
*);
126 static int hotfiles_age (struct hfsmount
*);
127 static int hotfiles_adopt (struct hfsmount
*);
128 static int hotfiles_evict (struct hfsmount
*, struct proc
*);
129 static int hotfiles_refine (struct hfsmount
*);
130 static int hotextents(struct hfsmount
*, HFSPlusExtentDescriptor
*);
131 static int hfs_addhotfile_internal(struct vnode
*);
135 * Hot File Cluster B-tree (on disk) functions.
137 static int hfc_btree_create (struct hfsmount
*, int, int);
138 static int hfc_btree_open (struct hfsmount
*, struct vnode
**);
139 static int hfc_btree_close (struct hfsmount
*, struct vnode
*);
140 static int hfc_comparekeys (HotFileKey
*, HotFileKey
*);
143 char hfc_tag
[] = "CLUSTERED HOT FILES B-TREE ";
145 extern int UBCINFOEXISTS(struct vnode
* vp
);
146 extern int hfs_vnop_write(struct vnop_write_args
*ap
);
150 *========================================================================
151 * HOT FILE INTERFACE ROUTINES
152 *========================================================================
156 * Start recording the hotest files on a file system.
158 * Requires that the hfc_mutex be held.
161 hfs_recording_start(struct hfsmount
*hfsmp
)
163 hotfile_data_t
*hotdata
;
170 if ((hfsmp
->hfs_flags
& HFS_READ_ONLY
) ||
171 (hfsmp
->jnl
== NULL
) ||
172 (hfsmp
->hfs_flags
& HFS_METADATA_ZONE
) == 0) {
175 if (HFSTOVCB(hfsmp
)->freeBlocks
< (2 * (u_int32_t
)hfsmp
->hfs_hotfile_maxblks
)) {
178 if (hfsmp
->hfc_stage
!= HFC_IDLE
) {
181 hfsmp
->hfc_stage
= HFC_BUSY
;
184 * Dump previous recording data.
186 if (hfsmp
->hfc_recdata
) {
189 tmp
= hfsmp
->hfc_recdata
;
190 hfsmp
->hfc_recdata
= NULL
;
197 * On first startup check for suspended recording.
199 if (hfsmp
->hfc_timebase
== 0 &&
200 hfc_btree_open(hfsmp
, &hfsmp
->hfc_filevp
) == 0) {
201 HotFilesInfo hotfileinfo
;
203 if ((BTGetUserData(VTOF(hfsmp
->hfc_filevp
), &hotfileinfo
,
204 sizeof(hotfileinfo
)) == 0) &&
205 (SWAP_BE32 (hotfileinfo
.magic
) == HFC_MAGIC
) &&
206 (SWAP_BE32 (hotfileinfo
.timeleft
) > 0) &&
207 (SWAP_BE32 (hotfileinfo
.timebase
) > 0)) {
208 hfsmp
->hfc_maxfiles
= SWAP_BE32 (hotfileinfo
.maxfilecnt
);
209 hfsmp
->hfc_timeout
= SWAP_BE32 (hotfileinfo
.timeleft
) + tv
.tv_sec
;
210 hfsmp
->hfc_timebase
= SWAP_BE32 (hotfileinfo
.timebase
);
212 printf("Resume recording hot files on %s (%d secs left)\n",
213 hfsmp
->vcbVN
, SWAP_BE32 (hotfileinfo
.timeleft
));
216 hfsmp
->hfc_maxfiles
= HFC_DEFAULT_FILE_COUNT
;
217 hfsmp
->hfc_timebase
= tv
.tv_sec
+ 1;
218 hfsmp
->hfc_timeout
= hfsmp
->hfc_timebase
+ HFC_DEFAULT_DURATION
;
220 (void) hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
221 hfsmp
->hfc_filevp
= NULL
;
223 struct cat_attr cattr
;
227 * Make sure a btree file exists.
229 cnid
= GetFileInfo(HFSTOVCB(hfsmp
), kRootDirID
, HFC_FILENAME
, &cattr
, NULL
);
231 !S_ISREG(cattr
.ca_mode
) &&
232 (error
= hfc_btree_create(hfsmp
, HFSTOVCB(hfsmp
)->blockSize
, HFC_DEFAULT_FILE_COUNT
))) {
233 hfsmp
->hfc_stage
= HFC_IDLE
;
234 wakeup((caddr_t
)&hfsmp
->hfc_stage
);
238 printf("HFS: begin recording hot files on %s\n", hfsmp
->vcbVN
);
240 hfsmp
->hfc_maxfiles
= HFC_DEFAULT_FILE_COUNT
;
241 hfsmp
->hfc_timeout
= tv
.tv_sec
+ HFC_DEFAULT_DURATION
;
243 /* Reset time base. */
244 if (hfsmp
->hfc_timebase
== 0) {
245 hfsmp
->hfc_timebase
= tv
.tv_sec
+ 1;
247 time_t cumulativebase
;
249 cumulativebase
= hfsmp
->hfc_timeout
- (HFC_CUMULATIVE_CYCLES
* HFC_DEFAULT_DURATION
);
250 hfsmp
->hfc_timebase
= MAX(hfsmp
->hfc_timebase
, cumulativebase
);
254 if ((hfsmp
->hfc_maxfiles
== 0) ||
255 (hfsmp
->hfc_maxfiles
> HFC_MAXIMUM_FILE_COUNT
)) {
256 hfsmp
->hfc_maxfiles
= HFC_DEFAULT_FILE_COUNT
;
258 maxentries
= hfsmp
->hfc_maxfiles
;
260 size
= sizeof(hotfile_data_t
) + (maxentries
* sizeof(hotfile_entry_t
));
261 MALLOC(hotdata
, hotfile_data_t
*, size
, M_TEMP
, M_WAITOK
);
262 bzero(hotdata
, size
);
264 for (i
= 1; i
< maxentries
; i
++)
265 hotdata
->entries
[i
-1].right
= &hotdata
->entries
[i
];
267 hotdata
->freelist
= &hotdata
->entries
[0];
269 * Establish minimum temperature and maximum file size.
271 hotdata
->threshold
= HFC_MINIMUM_TEMPERATURE
;
272 hotdata
->maxblocks
= HFC_MAXIMUM_FILESIZE
/ HFSTOVCB(hfsmp
)->blockSize
;
273 hotdata
->hfsmp
= hfsmp
;
275 hfsmp
->hfc_recdata
= hotdata
;
276 hfsmp
->hfc_stage
= HFC_RECORDING
;
277 wakeup((caddr_t
)&hfsmp
->hfc_stage
);
282 * Stop recording the hotest files on a file system.
284 * Requires that the hfc_mutex be held.
287 hfs_recording_stop(struct hfsmount
*hfsmp
)
289 hotfile_data_t
*hotdata
;
290 hotfilelist_t
*listp
;
293 enum hfc_stage newstage
= HFC_IDLE
;
296 if (hfsmp
->hfc_stage
!= HFC_RECORDING
)
299 hotfiles_collect(hfsmp
);
301 if (hfsmp
->hfc_stage
!= HFC_RECORDING
)
304 hfsmp
->hfc_stage
= HFC_BUSY
;
307 * Convert hot file data into a simple file id list....
309 * then dump the sample data
312 printf("HFS: end of hot file recording on %s\n", hfsmp
->vcbVN
);
314 hotdata
= (hotfile_data_t
*)hfsmp
->hfc_recdata
;
317 hfsmp
->hfc_recdata
= NULL
;
318 hfsmp
->hfc_stage
= HFC_EVALUATION
;
319 wakeup((caddr_t
)&hfsmp
->hfc_stage
);
322 printf(" curentries: %d\n", hotdata
->activefiles
);
325 * If no hot files recorded then we're done.
327 if (hotdata
->rootentry
== NULL
) {
332 /* Open the B-tree file for writing... */
333 if (hfsmp
->hfc_filevp
)
334 panic("hfs_recording_stop: hfc_filevp exists (vp = 0x%08x)", hfsmp
->hfc_filevp
);
336 error
= hfc_btree_open(hfsmp
, &hfsmp
->hfc_filevp
);
342 * Age the previous set of clustered hot files.
344 error
= hotfiles_age(hfsmp
);
346 (void) hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
347 hfsmp
->hfc_filevp
= NULL
;
352 * Create a sorted list of hotest files.
354 size
= sizeof(hotfilelist_t
);
355 size
+= sizeof(hotfileinfo_t
) * (hotdata
->activefiles
- 1);
356 MALLOC(listp
, hotfilelist_t
*, size
, M_TEMP
, M_WAITOK
);
359 hf_getsortedlist(hotdata
, listp
); /* NOTE: destroys hot file tree! */
361 listp
->hfl_duration
= tv
.tv_sec
- hfsmp
->hfc_timebase
;
362 hfsmp
->hfc_recdata
= listp
;
365 * Account for duplicates.
367 error
= hotfiles_refine(hfsmp
);
369 (void) hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
370 hfsmp
->hfc_filevp
= NULL
;
375 * Compute the amount of space to reclaim...
377 if (listp
->hfl_totalblocks
> hfsmp
->hfs_hotfile_freeblks
) {
378 listp
->hfl_reclaimblks
=
379 MIN(listp
->hfl_totalblocks
, hfsmp
->hfs_hotfile_maxblks
) -
380 hfsmp
->hfs_hotfile_freeblks
;
382 printf("hfs_recording_stop: need to reclaim %d blocks\n", listp
->hfl_reclaimblks
);
384 if (listp
->hfl_reclaimblks
)
385 newstage
= HFC_EVICTION
;
387 newstage
= HFC_ADOPTION
;
389 newstage
= HFC_ADOPTION
;
392 if (newstage
== HFC_ADOPTION
&& listp
->hfl_totalblocks
== 0) {
393 (void) hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
394 hfsmp
->hfc_filevp
= NULL
;
399 if (newstage
== HFC_EVICTION
)
400 printf("HFS: evicting coldest files\n");
401 else if (newstage
== HFC_ADOPTION
)
402 printf("HFS: adopting hotest files\n");
404 FREE(hotdata
, M_TEMP
);
406 hfsmp
->hfc_stage
= newstage
;
407 wakeup((caddr_t
)&hfsmp
->hfc_stage
);
412 * Suspend recording the hotest files on a file system.
416 hfs_recording_suspend(struct hfsmount
*hfsmp
)
418 HotFilesInfo hotfileinfo
;
419 hotfile_data_t
*hotdata
= NULL
;
423 if (hfsmp
->hfc_stage
== HFC_DISABLED
)
426 lck_mtx_lock(&hfsmp
->hfc_mutex
);
430 * A suspend can occur during eval/evict/adopt stage.
431 * In that case we would need to write out info and
432 * flush our HFBT vnode. Currently we just bail.
435 hotdata
= (hotfile_data_t
*)hfsmp
->hfc_recdata
;
436 if (hotdata
== NULL
|| hfsmp
->hfc_stage
!= HFC_RECORDING
) {
440 hfsmp
->hfc_stage
= HFC_BUSY
;
443 printf("HFS: suspend hot file recording on %s\n", hfsmp
->vcbVN
);
445 error
= hfc_btree_open(hfsmp
, &hfsmp
->hfc_filevp
);
447 printf("hfs_recording_suspend: err %d opening btree\n", error
);
451 if (hfs_start_transaction(hfsmp
) != 0) {
455 if (hfs_lock(VTOC(hfsmp
->hfc_filevp
), HFS_EXCLUSIVE_LOCK
) != 0) {
461 hotfileinfo
.magic
= SWAP_BE32 (HFC_MAGIC
);
462 hotfileinfo
.version
= SWAP_BE32 (HFC_VERSION
);
463 hotfileinfo
.duration
= SWAP_BE32 (HFC_DEFAULT_DURATION
);
464 hotfileinfo
.timebase
= SWAP_BE32 (hfsmp
->hfc_timebase
);
465 hotfileinfo
.timeleft
= SWAP_BE32 (hfsmp
->hfc_timeout
- tv
.tv_sec
);
466 hotfileinfo
.threshold
= SWAP_BE32 (hotdata
->threshold
);
467 hotfileinfo
.maxfileblks
= SWAP_BE32 (hotdata
->maxblocks
);
468 hotfileinfo
.maxfilecnt
= SWAP_BE32 (HFC_DEFAULT_FILE_COUNT
);
469 strcpy(hotfileinfo
.tag
, hfc_tag
);
470 (void) BTSetUserData(VTOF(hfsmp
->hfc_filevp
), &hotfileinfo
, sizeof(hotfileinfo
));
472 hfs_unlock(VTOC(hfsmp
->hfc_filevp
));
473 hfs_end_transaction(hfsmp
);
475 if (hfsmp
->hfc_filevp
) {
476 (void) hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
477 hfsmp
->hfc_filevp
= NULL
;
480 FREE(hotdata
, M_TEMP
);
481 hfsmp
->hfc_recdata
= NULL
;
483 hfsmp
->hfc_stage
= HFC_DISABLED
;
484 wakeup((caddr_t
)&hfsmp
->hfc_stage
);
486 lck_mtx_unlock(&hfsmp
->hfc_mutex
);
496 hfs_recording_init(struct hfsmount
*hfsmp
)
499 CatalogRecord
* datap
;
501 HFSPlusCatalogFile
*filep
;
502 BTScanState scanstate
;
503 BTreeIterator
* iterator
;
504 FSBufferDescriptor record
;
506 filefork_t
* filefork
;
508 struct cat_attr cattr
;
512 int inserted
= 0; /* debug variables */
516 * For now, only the boot volume is supported.
518 if ((vfs_flags(HFSTOVFS(hfsmp
)) & MNT_ROOTFS
) == 0) {
519 hfsmp
->hfc_stage
= HFC_DISABLED
;
524 * If the Hot File btree exists then metadata zone is ready.
526 cnid
= GetFileInfo(HFSTOVCB(hfsmp
), kRootDirID
, HFC_FILENAME
, &cattr
, NULL
);
527 if (cnid
!= 0 && S_ISREG(cattr
.ca_mode
)) {
528 if (hfsmp
->hfc_stage
== HFC_DISABLED
)
529 hfsmp
->hfc_stage
= HFC_IDLE
;
532 error
= hfc_btree_create(hfsmp
, HFSTOVCB(hfsmp
)->blockSize
, HFC_DEFAULT_FILE_COUNT
);
535 printf("Error %d creating hot file b-tree on %s \n", error
, hfsmp
->vcbVN
);
540 * Open the Hot File B-tree file for writing.
542 if (hfsmp
->hfc_filevp
)
543 panic("hfs_recording_init: hfc_filevp exists (vp = 0x%08x)", hfsmp
->hfc_filevp
);
544 error
= hfc_btree_open(hfsmp
, &hfsmp
->hfc_filevp
);
547 printf("Error %d opening hot file b-tree on %s \n", error
, hfsmp
->vcbVN
);
551 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
552 bzero(iterator
, sizeof(*iterator
));
553 key
= (HotFileKey
*) &iterator
->key
;
554 key
->keyLength
= HFC_KEYLENGTH
;
556 record
.bufferAddress
= &data
;
557 record
.itemSize
= sizeof(u_int32_t
);
558 record
.itemCount
= 1;
560 printf("Evaluating space for \"%s\" metadata zone...\n", HFSTOVCB(hfsmp
)->vcbVN
);
563 * Get ready to scan the Catalog file.
565 error
= BTScanInitialize(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), 0, 0, 0,
566 kCatSearchBufferSize
, &scanstate
);
568 printf("hfs_recording_init: err %d BTScanInit\n", error
);
573 * The writes to Hot File B-tree file are journaled.
575 if (hfs_start_transaction(hfsmp
) != 0) {
579 if (hfs_lock(VTOC(hfsmp
->hfc_filevp
), HFS_EXCLUSIVE_LOCK
) != 0) {
583 filefork
= VTOF(hfsmp
->hfc_filevp
);
586 * Visit all the catalog btree leaf records.
589 error
= BTScanNextRecord(&scanstate
, 0, (void **)&keyp
, (void **)&datap
, &dataSize
);
591 if (error
== btNotFound
)
594 printf("hfs_recording_init: err %d BTScanNext\n", error
);
597 if ((datap
->recordType
!= kHFSPlusFileRecord
) ||
598 (dataSize
!= sizeof(HFSPlusCatalogFile
))) {
601 filep
= (HFSPlusCatalogFile
*)datap
;
603 if (filep
->dataFork
.totalBlocks
== 0) {
607 * Any file that has blocks inside the hot file
608 * space is recorded for later eviction.
610 * For now, resource forks are ignored.
612 if (!hotextents(hfsmp
, &filep
->dataFork
.extents
[0])) {
615 cnid
= filep
->fileID
;
617 /* Skip over journal files. */
618 if (cnid
== hfsmp
->hfs_jnlfileid
|| cnid
== hfsmp
->hfs_jnlinfoblkid
) {
622 * XXX - need to skip quota files as well.
625 /* Insert a hot file entry. */
626 key
->keyLength
= HFC_KEYLENGTH
;
627 key
->temperature
= HFC_MINIMUM_TEMPERATURE
;
631 error
= BTInsertRecord(filefork
, iterator
, &record
, record
.itemSize
);
633 printf("hfs_recording_init: BTInsertRecord failed %d (fileid %d)\n", error
, key
->fileID
);
634 error
= MacToVFSError(error
);
638 /* Insert the corresponding thread record. */
639 key
->keyLength
= HFC_KEYLENGTH
;
640 key
->temperature
= HFC_LOOKUPTAG
;
643 data
= HFC_MINIMUM_TEMPERATURE
;
644 error
= BTInsertRecord(filefork
, iterator
, &record
, record
.itemSize
);
646 printf("hfs_recording_init: BTInsertRecord failed %d (fileid %d)\n", error
, key
->fileID
);
647 error
= MacToVFSError(error
);
652 (void) BTFlushPath(filefork
);
653 hfs_unlock(VTOC(hfsmp
->hfc_filevp
));
655 hfs_end_transaction(hfsmp
);
657 printf("%d files identified out of %d\n", inserted
, filecount
);
661 (void) BTScanTerminate(&scanstate
, &data
, &data
, &data
);
663 FREE(iterator
, M_TEMP
);
664 if (hfsmp
->hfc_filevp
) {
665 (void) hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
666 hfsmp
->hfc_filevp
= NULL
;
669 hfsmp
->hfc_stage
= HFC_IDLE
;
675 * Use sync to perform ocassional background work.
679 hfs_hotfilesync(struct hfsmount
*hfsmp
, struct proc
*p
)
681 if (hfsmp
->hfc_stage
) {
684 lck_mtx_lock(&hfsmp
->hfc_mutex
);
686 switch (hfsmp
->hfc_stage
) {
688 (void) hfs_recording_start(hfsmp
);
693 if (tv
.tv_sec
> hfsmp
->hfc_timeout
)
694 (void) hfs_recording_stop(hfsmp
);
698 (void) hotfiles_evict(hfsmp
, p
);
702 (void) hotfiles_adopt(hfsmp
);
708 lck_mtx_unlock(&hfsmp
->hfc_mutex
);
714 * Add a hot file to the recording list.
716 * This can happen when a hot file gets reclaimed or at the
717 * end of the recording period for any active hot file.
719 * NOTE: Since both the data and resource fork can be hot,
720 * there can be two entries for the same file id.
722 * Note: the cnode is locked on entry.
726 hfs_addhotfile(struct vnode
*vp
)
732 if (hfsmp
->hfc_stage
!= HFC_RECORDING
)
735 lck_mtx_lock(&hfsmp
->hfc_mutex
);
736 error
= hfs_addhotfile_internal(vp
);
737 lck_mtx_unlock(&hfsmp
->hfc_mutex
);
742 hfs_addhotfile_internal(struct vnode
*vp
)
744 hotfile_data_t
*hotdata
;
745 hotfile_entry_t
*entry
;
749 u_int32_t temperature
;
752 if (hfsmp
->hfc_stage
!= HFC_RECORDING
)
755 if ((!vnode_isreg(vp
) && !vnode_islnk(vp
)) || vnode_issystem(vp
)) {
758 /* Skip resource forks for now. */
759 if (VNODE_IS_RSRC(vp
)) {
762 if ((hotdata
= (hotfile_data_t
*)hfsmp
->hfc_recdata
) == NULL
) {
768 if ((ffp
->ff_bytesread
== 0) ||
769 (ffp
->ff_blocks
== 0) ||
770 (ffp
->ff_blocks
> hotdata
->maxblocks
) ||
771 (cp
->c_flag
& (C_DELETED
| C_NOEXISTS
)) ||
772 (cp
->c_flags
& UF_NODUMP
) ||
773 (cp
->c_atime
< hfsmp
->hfc_timebase
)) {
777 temperature
= ffp
->ff_bytesread
/ ffp
->ff_size
;
778 if (temperature
< hotdata
->threshold
) {
782 * If there is room or this file is hotter than
783 * the coldest one then add it to the list.
786 if ((hotdata
->activefiles
< hfsmp
->hfc_maxfiles
) ||
787 (hotdata
->coldest
== NULL
) ||
788 (temperature
> hotdata
->coldest
->temperature
)) {
790 entry
= hf_getnewentry(hotdata
);
791 entry
->temperature
= temperature
;
792 entry
->fileid
= cp
->c_fileid
;
793 entry
->blocks
= ffp
->ff_blocks
;
794 hf_insert(hotdata
, entry
);
802 * Remove a hot file from the recording list.
804 * This can happen when a hot file becomes
805 * an active vnode (active hot files are
806 * not kept in the recording list until the
807 * end of the recording period).
809 * Note: the cnode is locked on entry.
813 hfs_removehotfile(struct vnode
*vp
)
815 hotfile_data_t
*hotdata
;
819 u_int32_t temperature
;
822 if (hfsmp
->hfc_stage
!= HFC_RECORDING
)
825 if ((!vnode_isreg(vp
) && !vnode_islnk(vp
)) || vnode_issystem(vp
)) {
832 if ((ffp
->ff_bytesread
== 0) || (ffp
->ff_blocks
== 0) ||
833 (cp
->c_atime
< hfsmp
->hfc_timebase
)) {
837 lck_mtx_lock(&hfsmp
->hfc_mutex
);
838 if (hfsmp
->hfc_stage
!= HFC_RECORDING
)
840 if ((hotdata
= (hotfile_data_t
*)hfsmp
->hfc_recdata
) == NULL
)
843 temperature
= ffp
->ff_bytesread
/ ffp
->ff_size
;
844 if (temperature
< hotdata
->threshold
)
847 if (hotdata
->coldest
&& (temperature
>= hotdata
->coldest
->temperature
)) {
849 hf_delete(hotdata
, VTOC(vp
)->c_fileid
, temperature
);
853 lck_mtx_unlock(&hfsmp
->hfc_mutex
);
859 *========================================================================
860 * HOT FILE MAINTENANCE ROUTINES
861 *========================================================================
865 hotfiles_collect_callback(struct vnode
*vp
, __unused
void *cargs
)
867 if ((vnode_isreg(vp
) || vnode_islnk(vp
)) && !vnode_issystem(vp
))
868 (void) hfs_addhotfile_internal(vp
);
870 return (VNODE_RETURNED
);
874 * Add all active hot files to the recording list.
877 hotfiles_collect(struct hfsmount
*hfsmp
)
879 struct mount
*mp
= HFSTOVFS(hfsmp
);
881 if (vfs_busy(mp
, LK_NOWAIT
))
885 * hotfiles_collect_callback will be called for each vnode
886 * hung off of this mount point
888 * properly referenced and unreferenced around the callback
890 vnode_iterate(mp
, 0, hotfiles_collect_callback
, (void *)NULL
);
899 * Update the data of a btree record
900 * This is called from within BTUpdateRecord.
903 update_callback(const HotFileKey
*key
, u_int32_t
*data
, u_int32_t
*state
)
905 if (key
->temperature
== HFC_LOOKUPTAG
)
911 * Identify files already in hot area.
914 hotfiles_refine(struct hfsmount
*hfsmp
)
916 BTreeIterator
* iterator
;
918 filefork_t
* filefork
;
919 hotfilelist_t
*listp
;
920 FSBufferDescriptor record
;
927 if ((listp
= (hotfilelist_t
*)hfsmp
->hfc_recdata
) == NULL
)
930 mp
= HFSTOVFS(hfsmp
);
932 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
933 bzero(iterator
, sizeof(*iterator
));
934 key
= (HotFileKey
*) &iterator
->key
;
936 record
.bufferAddress
= &data
;
937 record
.itemSize
= sizeof(u_int32_t
);
938 record
.itemCount
= 1;
940 if (hfs_start_transaction(hfsmp
) != 0) {
944 if (hfs_lock(VTOC(hfsmp
->hfc_filevp
), HFS_EXCLUSIVE_LOCK
) != 0) {
948 filefork
= VTOF(hfsmp
->hfc_filevp
);
950 for (i
= 0; i
< listp
->hfl_count
; ++i
) {
952 * Check if entry (thread) is already in hot area.
954 key
->keyLength
= HFC_KEYLENGTH
;
955 key
->temperature
= HFC_LOOKUPTAG
;
956 key
->fileID
= listp
->hfl_hotfile
[i
].hf_fileid
;
958 (void) BTInvalidateHint(iterator
);
959 if (BTSearchRecord(filefork
, iterator
, &record
, NULL
, iterator
) != 0) {
960 continue; /* not in hot area, so skip */
964 * Update thread entry with latest temperature.
966 error
= BTUpdateRecord(filefork
, iterator
,
967 (IterateCallBackProcPtr
)update_callback
,
968 &listp
->hfl_hotfile
[i
].hf_temperature
);
970 printf("hotfiles_refine: BTUpdateRecord failed %d (file %d)\n", error
, key
->fileID
);
971 error
= MacToVFSError(error
);
975 * Re-key entry with latest temperature.
977 key
->keyLength
= HFC_KEYLENGTH
;
978 key
->temperature
= data
;
979 key
->fileID
= listp
->hfl_hotfile
[i
].hf_fileid
;
981 /* Pick up record data. */
982 (void) BTInvalidateHint(iterator
);
983 (void) BTSearchRecord(filefork
, iterator
, &record
, NULL
, iterator
);
984 error
= BTDeleteRecord(filefork
, iterator
);
986 printf("hotfiles_refine: BTDeleteRecord failed %d (file %d)\n", error
, key
->fileID
);
987 error
= MacToVFSError(error
);
990 key
->keyLength
= HFC_KEYLENGTH
;
991 key
->temperature
= listp
->hfl_hotfile
[i
].hf_temperature
;
992 key
->fileID
= listp
->hfl_hotfile
[i
].hf_fileid
;
994 error
= BTInsertRecord(filefork
, iterator
, &record
, record
.itemSize
);
996 printf("hotfiles_refine: BTInsertRecord failed %d (file %d)\n", error
, key
->fileID
);
997 error
= MacToVFSError(error
);
1002 * Invalidate this entry in the list.
1004 listp
->hfl_hotfile
[i
].hf_temperature
= 0;
1005 listp
->hfl_totalblocks
-= listp
->hfl_hotfile
[i
].hf_blocks
;
1009 (void) BTFlushPath(filefork
);
1010 hfs_unlock(VTOC(hfsmp
->hfc_filevp
));
1012 hfs_end_transaction(hfsmp
);
1014 FREE(iterator
, M_TEMP
);
1019 * Move new hot files into hot area.
1021 * Requires that the hfc_mutex be held.
1024 hotfiles_adopt(struct hfsmount
*hfsmp
)
1026 BTreeIterator
* iterator
;
1028 filefork_t
* filefork
;
1029 hotfilelist_t
*listp
;
1030 FSBufferDescriptor record
;
1033 enum hfc_stage stage
;
1039 int startedtrans
= 0;
1041 if ((listp
= (hotfilelist_t
*)hfsmp
->hfc_recdata
) == NULL
)
1044 if (hfsmp
->hfc_stage
!= HFC_ADOPTION
) {
1047 if (hfs_lock(VTOC(hfsmp
->hfc_filevp
), HFS_EXCLUSIVE_LOCK
) != 0) {
1051 stage
= hfsmp
->hfc_stage
;
1052 hfsmp
->hfc_stage
= HFC_BUSY
;
1055 last
= listp
->hfl_next
+ HFC_FILESPERSYNC
;
1056 if (last
> listp
->hfl_count
)
1057 last
= listp
->hfl_count
;
1059 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
1060 bzero(iterator
, sizeof(*iterator
));
1061 key
= (HotFileKey
*) &iterator
->key
;
1062 key
->keyLength
= HFC_KEYLENGTH
;
1064 record
.bufferAddress
= &data
;
1065 record
.itemSize
= sizeof(u_int32_t
);
1066 record
.itemCount
= 1;
1068 filefork
= VTOF(hfsmp
->hfc_filevp
);
1070 for (i
= listp
->hfl_next
; (i
< last
) && (blksmoved
< HFC_BLKSPERSYNC
); ++i
) {
1072 * Skip invalid entries (already in hot area).
1074 if (listp
->hfl_hotfile
[i
].hf_temperature
== 0) {
1079 * Acquire a vnode for this file.
1081 error
= hfs_vget(hfsmp
, listp
->hfl_hotfile
[i
].hf_fileid
, &vp
, 0);
1083 if (error
== ENOENT
) {
1086 continue; /* stale entry, go to next */
1090 if (!vnode_isreg(vp
) && !vnode_islnk(vp
)) {
1091 printf("hotfiles_adopt: huh, not a file %d (%d)\n", listp
->hfl_hotfile
[i
].hf_fileid
, VTOC(vp
)->c_cnid
);
1092 hfs_unlock(VTOC(vp
));
1094 listp
->hfl_hotfile
[i
].hf_temperature
= 0;
1096 continue; /* stale entry, go to next */
1098 if (hotextents(hfsmp
, &VTOF(vp
)->ff_extents
[0])) {
1099 hfs_unlock(VTOC(vp
));
1101 listp
->hfl_hotfile
[i
].hf_temperature
= 0;
1103 listp
->hfl_totalblocks
-= listp
->hfl_hotfile
[i
].hf_blocks
;
1104 continue; /* stale entry, go to next */
1106 fileblocks
= VTOF(vp
)->ff_blocks
;
1107 if (fileblocks
> hfsmp
->hfs_hotfile_freeblks
) {
1108 hfs_unlock(VTOC(vp
));
1111 listp
->hfl_totalblocks
-= fileblocks
;
1112 continue; /* entry too big, go to next */
1115 if ((blksmoved
> 0) &&
1116 (blksmoved
+ fileblocks
) > HFC_BLKSPERSYNC
) {
1117 hfs_unlock(VTOC(vp
));
1119 break; /* adopt this entry the next time around */
1121 /* Start a new transaction. */
1122 if (hfs_start_transaction(hfsmp
) != 0) {
1124 hfs_unlock(VTOC(vp
));
1130 if (VTOC(vp
)->c_desc
.cd_nameptr
)
1131 data
= *(u_int32_t
*)(VTOC(vp
)->c_desc
.cd_nameptr
);
1135 error
= hfs_relocate(vp
, hfsmp
->hfs_hotfile_start
, kauth_cred_get(), current_proc());
1136 hfs_unlock(VTOC(vp
));
1141 /* Keep hot file free space current. */
1142 hfsmp
->hfs_hotfile_freeblks
-= fileblocks
;
1143 listp
->hfl_totalblocks
-= fileblocks
;
1145 /* Insert hot file entry */
1146 key
->keyLength
= HFC_KEYLENGTH
;
1147 key
->temperature
= listp
->hfl_hotfile
[i
].hf_temperature
;
1148 key
->fileID
= listp
->hfl_hotfile
[i
].hf_fileid
;
1151 error
= BTInsertRecord(filefork
, iterator
, &record
, record
.itemSize
);
1153 printf("hotfiles_adopt: BTInsertRecord failed %d (fileid %d)\n", error
, key
->fileID
);
1154 error
= MacToVFSError(error
);
1159 /* Insert thread record */
1160 key
->keyLength
= HFC_KEYLENGTH
;
1161 key
->temperature
= HFC_LOOKUPTAG
;
1162 key
->fileID
= listp
->hfl_hotfile
[i
].hf_fileid
;
1164 data
= listp
->hfl_hotfile
[i
].hf_temperature
;
1165 error
= BTInsertRecord(filefork
, iterator
, &record
, record
.itemSize
);
1167 printf("hotfiles_adopt: BTInsertRecord failed %d (fileid %d)\n", error
, key
->fileID
);
1168 error
= MacToVFSError(error
);
1172 (void) BTFlushPath(filefork
);
1174 /* Transaction complete. */
1176 hfs_end_transaction(hfsmp
);
1180 blksmoved
+= fileblocks
;
1182 if (listp
->hfl_next
>= listp
->hfl_count
) {
1185 if (hfsmp
->hfs_hotfile_freeblks
<= 0) {
1187 printf("hotfiles_adopt: free space exhausted (%d)\n", hfsmp
->hfs_hotfile_freeblks
);
1194 printf("hotfiles_adopt: [%d] adopted %d blocks (%d left)\n", listp
->hfl_next
, blksmoved
, listp
->hfl_totalblocks
);
1196 /* Finish any outstanding transactions. */
1198 (void) BTFlushPath(filefork
);
1199 hfs_end_transaction(hfsmp
);
1202 hfs_unlock(VTOC(hfsmp
->hfc_filevp
));
1204 if ((listp
->hfl_next
>= listp
->hfl_count
) || (hfsmp
->hfs_hotfile_freeblks
<= 0)) {
1206 printf("hotfiles_adopt: all done relocating %d files\n", listp
->hfl_count
);
1207 printf("hotfiles_adopt: %d blocks free in hot file band\n", hfsmp
->hfs_hotfile_freeblks
);
1211 FREE(iterator
, M_TEMP
);
1213 if (stage
!= HFC_ADOPTION
&& hfsmp
->hfc_filevp
) {
1214 (void) hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
1215 hfsmp
->hfc_filevp
= NULL
;
1217 hfsmp
->hfc_stage
= stage
;
1218 wakeup((caddr_t
)&hfsmp
->hfc_stage
);
1223 * Reclaim space by evicting the coldest files.
1225 * Requires that the hfc_mutex be held.
1228 hotfiles_evict(struct hfsmount
*hfsmp
, struct proc
*p
)
1230 BTreeIterator
* iterator
;
1233 filefork_t
* filefork
;
1234 hotfilelist_t
*listp
;
1235 enum hfc_stage stage
;
1236 u_int32_t savedtemp
;
1241 int startedtrans
= 0;
1244 if (hfsmp
->hfc_stage
!= HFC_EVICTION
) {
1248 if ((listp
= (hotfilelist_t
*)hfsmp
->hfc_recdata
) == NULL
)
1251 if (hfs_lock(VTOC(hfsmp
->hfc_filevp
), HFS_EXCLUSIVE_LOCK
) != 0) {
1255 stage
= hfsmp
->hfc_stage
;
1256 hfsmp
->hfc_stage
= HFC_BUSY
;
1258 filesmoved
= blksmoved
= 0;
1259 bt_op
= kBTreeFirstRecord
;
1261 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
1262 bzero(iterator
, sizeof(*iterator
));
1263 key
= (HotFileKey
*) &iterator
->key
;
1265 filefork
= VTOF(hfsmp
->hfc_filevp
);
1267 while (listp
->hfl_reclaimblks
> 0 &&
1268 blksmoved
< HFC_BLKSPERSYNC
&&
1269 filesmoved
< HFC_FILESPERSYNC
) {
1272 * Obtain the first record (ie the coldest one).
1274 if (BTIterateRecord(filefork
, bt_op
, iterator
, NULL
, NULL
) != 0) {
1276 printf("hotfiles_evict: no more records\n");
1279 stage
= HFC_ADOPTION
;
1282 if (key
->keyLength
!= HFC_KEYLENGTH
) {
1283 printf("hotfiles_evict: invalid key length %d\n", key
->keyLength
);
1287 if (key
->temperature
== HFC_LOOKUPTAG
) {
1289 printf("hotfiles_evict: ran into thread records\n");
1292 stage
= HFC_ADOPTION
;
1296 * Aquire the vnode for this file.
1298 error
= hfs_vget(hfsmp
, key
->fileID
, &vp
, 0);
1300 /* Start a new transaction. */
1301 if (hfs_start_transaction(hfsmp
) != 0) {
1303 hfs_unlock(VTOC(vp
));
1312 if (error
== ENOENT
) {
1313 goto delete; /* stale entry, go to next */
1315 printf("hotfiles_evict: err %d getting file %d\n",
1316 error
, key
->fileID
);
1320 if (!vnode_isreg(vp
) && !vnode_islnk(vp
)) {
1321 printf("hotfiles_evict: huh, not a file %d\n", key
->fileID
);
1322 hfs_unlock(VTOC(vp
));
1324 goto delete; /* invalid entry, go to next */
1326 fileblocks
= VTOF(vp
)->ff_blocks
;
1327 if ((blksmoved
> 0) &&
1328 (blksmoved
+ fileblocks
) > HFC_BLKSPERSYNC
) {
1329 hfs_unlock(VTOC(vp
));
1334 * Make sure file is in the hot area.
1336 if (!hotextents(hfsmp
, &VTOF(vp
)->ff_extents
[0])) {
1338 printf("hotfiles_evict: file %d isn't hot!\n", key
->fileID
);
1340 hfs_unlock(VTOC(vp
));
1342 goto delete; /* stale entry, go to next */
1346 * Relocate file out of hot area.
1348 error
= hfs_relocate(vp
, HFSTOVCB(hfsmp
)->nextAllocation
, proc_ucred(p
), p
);
1350 printf("hotfiles_evict: err %d relocating file %d\n", error
, key
->fileID
);
1351 hfs_unlock(VTOC(vp
));
1353 bt_op
= kBTreeNextRecord
;
1354 goto next
; /* go to next */
1358 // We do not believe that this call to hfs_fsync() is
1359 // necessary and it causes a journal transaction
1360 // deadlock so we are removing it.
1362 // (void) hfs_fsync(vp, MNT_WAIT, 0, p);
1364 hfs_unlock(VTOC(vp
));
1367 hfsmp
->hfs_hotfile_freeblks
+= fileblocks
;
1368 listp
->hfl_reclaimblks
-= fileblocks
;
1369 if (listp
->hfl_reclaimblks
< 0)
1370 listp
->hfl_reclaimblks
= 0;
1371 blksmoved
+= fileblocks
;
1374 error
= BTDeleteRecord(filefork
, iterator
);
1376 error
= MacToVFSError(error
);
1379 savedtemp
= key
->temperature
;
1380 key
->temperature
= HFC_LOOKUPTAG
;
1381 error
= BTDeleteRecord(filefork
, iterator
);
1383 error
= MacToVFSError(error
);
1386 key
->temperature
= savedtemp
;
1388 (void) BTFlushPath(filefork
);
1390 /* Transaction complete. */
1392 hfs_end_transaction(hfsmp
);
1399 printf("hotfiles_evict: moved %d files (%d blks, %d to go)\n", filesmoved
, blksmoved
, listp
->hfl_reclaimblks
);
1401 /* Finish any outstanding transactions. */
1403 (void) BTFlushPath(filefork
);
1404 hfs_end_transaction(hfsmp
);
1407 hfs_unlock(VTOC(hfsmp
->hfc_filevp
));
1410 * Move to next stage when finished.
1412 if (listp
->hfl_reclaimblks
<= 0) {
1413 stage
= HFC_ADOPTION
;
1415 printf("hotfiles_evict: %d blocks free in hot file band\n", hfsmp
->hfs_hotfile_freeblks
);
1418 FREE(iterator
, M_TEMP
);
1419 hfsmp
->hfc_stage
= stage
;
1420 wakeup((caddr_t
)&hfsmp
->hfc_stage
);
1425 * Age the existing records in the hot files b-tree.
1428 hotfiles_age(struct hfsmount
*hfsmp
)
1430 BTreeInfoRec btinfo
;
1431 BTreeIterator
* iterator
;
1432 BTreeIterator
* prev_iterator
;
1433 FSBufferDescriptor record
;
1434 FSBufferDescriptor prev_record
;
1436 HotFileKey
* prev_key
;
1437 filefork_t
* filefork
;
1439 u_int32_t prev_data
;
1448 MALLOC(iterator
, BTreeIterator
*, 2 * sizeof(*iterator
), M_TEMP
, M_WAITOK
);
1449 bzero(iterator
, 2 * sizeof(*iterator
));
1450 key
= (HotFileKey
*) &iterator
->key
;
1452 prev_iterator
= &iterator
[1];
1453 prev_key
= (HotFileKey
*) &prev_iterator
->key
;
1455 record
.bufferAddress
= &data
;
1456 record
.itemSize
= sizeof(data
);
1457 record
.itemCount
= 1;
1458 prev_record
.bufferAddress
= &prev_data
;
1459 prev_record
.itemSize
= sizeof(prev_data
);
1460 prev_record
.itemCount
= 1;
1463 * Capture b-tree changes inside a transaction
1465 if (hfs_start_transaction(hfsmp
) != 0) {
1469 if (hfs_lock(VTOC(hfsmp
->hfc_filevp
), HFS_EXCLUSIVE_LOCK
) != 0) {
1473 filefork
= VTOF(hfsmp
->hfc_filevp
);
1475 error
= BTGetInformation(filefork
, 0, &btinfo
);
1477 error
= MacToVFSError(error
);
1480 if (btinfo
.numRecords
< 2) {
1485 /* Only want 1st half of leaf records */
1486 numrecs
= (btinfo
.numRecords
/= 2) - 1;
1488 error
= BTIterateRecord(filefork
, kBTreeFirstRecord
, iterator
, &record
, &reclen
);
1490 printf("hfs_agehotfiles: BTIterateRecord: %d\n", error
);
1491 error
= MacToVFSError(error
);
1494 bcopy(iterator
, prev_iterator
, sizeof(BTreeIterator
));
1497 for (i
= 0; i
< numrecs
; ++i
) {
1498 error
= BTIterateRecord(filefork
, kBTreeNextRecord
, iterator
, &record
, &reclen
);
1500 if (key
->temperature
< prev_key
->temperature
) {
1501 printf("hfs_agehotfiles: out of order keys!\n");
1505 if (reclen
!= sizeof(data
)) {
1506 printf("hfs_agehotfiles: invalid record length %d\n", reclen
);
1510 if (key
->keyLength
!= HFC_KEYLENGTH
) {
1511 printf("hfs_agehotfiles: invalid key length %d\n", key
->keyLength
);
1515 } else if ((error
== fsBTEndOfIterationErr
|| error
== fsBTRecordNotFoundErr
) &&
1516 (i
== (numrecs
- 1))) {
1519 printf("hfs_agehotfiles: %d of %d BTIterateRecord: %d\n", i
, numrecs
, error
);
1520 error
= MacToVFSError(error
);
1523 if (prev_key
->temperature
== HFC_LOOKUPTAG
) {
1525 printf("hfs_agehotfiles: ran into thread record\n");
1530 error
= BTDeleteRecord(filefork
, prev_iterator
);
1532 printf("hfs_agehotfiles: BTDeleteRecord failed %d (file %d)\n", error
, prev_key
->fileID
);
1533 error
= MacToVFSError(error
);
1537 /* Age by halving the temperature (floor = 4) */
1538 newtemp
= MAX(prev_key
->temperature
>> 1, 4);
1539 prev_key
->temperature
= newtemp
;
1541 error
= BTInsertRecord(filefork
, prev_iterator
, &prev_record
, prev_record
.itemSize
);
1543 printf("hfs_agehotfiles: BTInsertRecord failed %d (file %d)\n", error
, prev_key
->fileID
);
1544 error
= MacToVFSError(error
);
1549 * Update thread entry with latest temperature.
1551 prev_key
->temperature
= HFC_LOOKUPTAG
;
1552 error
= BTUpdateRecord(filefork
, prev_iterator
,
1553 (IterateCallBackProcPtr
)update_callback
,
1556 printf("hfs_agehotfiles: %d of %d BTUpdateRecord failed %d (file %d, %d)\n",
1557 i
, numrecs
, error
, prev_key
->fileID
, newtemp
);
1558 error
= MacToVFSError(error
);
1562 bcopy(iterator
, prev_iterator
, sizeof(BTreeIterator
));
1569 printf("hfs_agehotfiles: aged %d records out of %d\n", aged
, btinfo
.numRecords
);
1571 (void) BTFlushPath(filefork
);
1573 hfs_unlock(VTOC(hfsmp
->hfc_filevp
));
1575 hfs_end_transaction(hfsmp
);
1577 FREE(iterator
, M_TEMP
);
1582 * Return true if any blocks (or all blocks if all is true)
1583 * are contained in the hot file region.
1586 hotextents(struct hfsmount
*hfsmp
, HFSPlusExtentDescriptor
* extents
)
1592 for (i
= 0; i
< kHFSPlusExtentDensity
; ++i
) {
1593 b1
= extents
[i
].startBlock
;
1596 b2
= b1
+ extents
[i
].blockCount
- 1;
1597 if ((b1
>= hfsmp
->hfs_hotfile_start
&&
1598 b2
<= hfsmp
->hfs_hotfile_end
) ||
1599 (b1
< hfsmp
->hfs_hotfile_end
&&
1600 b2
> hfsmp
->hfs_hotfile_end
)) {
1610 *========================================================================
1611 * HOT FILE B-TREE ROUTINES
1612 *========================================================================
1616 * Open the hot files b-tree for writing.
1618 * On successful exit the vnode has a reference but not an iocount.
1621 hfc_btree_open(struct hfsmount
*hfsmp
, struct vnode
**vpp
)
1625 struct cat_desc cdesc
;
1626 struct cat_attr cattr
;
1627 struct cat_fork cfork
;
1628 static char filename
[] = HFC_FILENAME
;
1636 bzero(&cdesc
, sizeof(cdesc
));
1637 cdesc
.cd_parentcnid
= kRootDirID
;
1638 cdesc
.cd_nameptr
= filename
;
1639 cdesc
.cd_namelen
= strlen(filename
);
1641 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_SHARED_LOCK
);
1643 error
= cat_lookup(hfsmp
, &cdesc
, 0, &cdesc
, &cattr
, &cfork
, NULL
);
1645 hfs_systemfile_unlock(hfsmp
, lockflags
);
1648 printf("hfc_btree_open: cat_lookup error %d\n", error
);
1652 cdesc
.cd_flags
|= CD_ISMETA
;
1653 error
= hfs_getnewvnode(hfsmp
, NULL
, NULL
, &cdesc
, 0, &cattr
, &cfork
, &vp
);
1655 printf("hfc_btree_open: hfs_getnewvnode error %d\n", error
);
1656 cat_releasedesc(&cdesc
);
1659 if (!vnode_issystem(vp
)) {
1661 printf("hfc_btree_open: file has UBC, try again\n");
1663 hfs_unlock(VTOC(vp
));
1672 /* Open the B-tree file for writing... */
1673 error
= BTOpenPath(VTOF(vp
), (KeyCompareProcPtr
) hfc_comparekeys
);
1675 printf("hfc_btree_open: BTOpenPath error %d\n", error
);
1676 error
= MacToVFSError(error
);
1679 hfs_unlock(VTOC(vp
));
1682 vnode_ref(vp
); /* keep a reference while its open */
1686 if (!vnode_issystem(vp
))
1687 panic("hfc_btree_open: not a system file (vp = 0x%08x)", vp
);
1689 if (UBCINFOEXISTS(vp
))
1690 panic("hfc_btree_open: has UBCInfo (vp = 0x%08x)", vp
);
1696 * Close the hot files b-tree.
1698 * On entry the vnode has a reference.
1701 hfc_btree_close(struct hfsmount
*hfsmp
, struct vnode
*vp
)
1703 struct proc
*p
= current_proc();
1708 journal_flush(hfsmp
->jnl
);
1711 if (vnode_get(vp
) == 0) {
1712 error
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
);
1714 (void) hfs_fsync(vp
, MNT_WAIT
, 0, p
);
1715 error
= BTClosePath(VTOF(vp
));
1716 hfs_unlock(VTOC(vp
));
1727 * Create a hot files btree file.
1731 hfc_btree_create(struct hfsmount
*hfsmp
, int nodesize
, int entries
)
1733 struct vnode
*dvp
= NULL
;
1734 struct vnode
*vp
= NULL
;
1735 struct cnode
*cp
= NULL
;
1736 struct vfs_context context
;
1737 struct vnode_attr va
;
1738 struct componentname cname
;
1739 static char filename
[] = HFC_FILENAME
;
1742 context
.vc_proc
= current_proc();
1743 context
.vc_ucred
= kauth_cred_get();
1745 if (hfsmp
->hfc_filevp
)
1746 panic("hfc_btree_create: hfc_filevp exists (vp = 0x%08x)", hfsmp
->hfc_filevp
);
1748 error
= VFS_ROOT(HFSTOVFS(hfsmp
), &dvp
, &context
);
1752 cname
.cn_nameiop
= CREATE
;
1753 cname
.cn_flags
= ISLASTCN
;
1754 cname
.cn_context
= &context
;
1755 cname
.cn_pnbuf
= filename
;
1756 cname
.cn_pnlen
= sizeof(filename
);
1757 cname
.cn_nameptr
= filename
;
1758 cname
.cn_namelen
= strlen(filename
);
1760 cname
.cn_consume
= 0;
1763 VATTR_SET(&va
, va_type
, VREG
);
1764 VATTR_SET(&va
, va_mode
, S_IFREG
| S_IRUSR
| S_IWUSR
);
1765 VATTR_SET(&va
, va_uid
, 0);
1766 VATTR_SET(&va
, va_gid
, 0);
1768 /* call ourselves directly, ignore the higher-level VFS file creation code */
1769 error
= VNOP_CREATE(dvp
, &vp
, &cname
, &va
, &context
);
1771 printf("HFS: error %d creating HFBT on %s\n", error
, HFSTOVCB(hfsmp
)->vcbVN
);
1778 if ((error
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
))) {
1783 /* Don't use non-regular files or files with links. */
1784 if (!vnode_isreg(vp
) || cp
->c_nlink
!= 1) {
1789 printf("HFS: created HFBT on %s\n", HFSTOVCB(hfsmp
)->vcbVN
);
1791 if (VTOF(vp
)->ff_size
< (u_int64_t
)nodesize
) {
1795 BTNodeDescriptor
*ndp
;
1797 HotFilesInfo
*hotfileinfo
;
1803 * Mark it invisible (truncate will pull these changes).
1805 ((FndrFileInfo
*)&cp
->c_finderinfo
[0])->fdFlags
|=
1806 SWAP_BE16 (kIsInvisible
+ kNameLocked
);
1808 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&buffer
, nodesize
)) {
1812 bzero(buffer
, nodesize
);
1813 index
= (int16_t *)buffer
;
1815 entirespernode
= (nodesize
- sizeof(BTNodeDescriptor
) - 2) /
1816 (sizeof(HotFileKey
) + 6);
1817 nodecnt
= 2 + howmany(entries
* 2, entirespernode
);
1818 nodecnt
= roundup(nodecnt
, 8);
1819 filesize
= nodecnt
* nodesize
;
1821 /* FILL IN THE NODE DESCRIPTOR: */
1822 ndp
= (BTNodeDescriptor
*)buffer
;
1823 ndp
->kind
= kBTHeaderNode
;
1824 ndp
->numRecords
= SWAP_BE16 (3);
1825 offset
= sizeof(BTNodeDescriptor
);
1826 index
[(nodesize
/ 2) - 1] = SWAP_BE16 (offset
);
1828 /* FILL IN THE HEADER RECORD: */
1829 bthp
= (BTHeaderRec
*)((UInt8
*)buffer
+ offset
);
1830 bthp
->nodeSize
= SWAP_BE16 (nodesize
);
1831 bthp
->totalNodes
= SWAP_BE32 (filesize
/ nodesize
);
1832 bthp
->freeNodes
= SWAP_BE32 (nodecnt
- 1);
1833 bthp
->clumpSize
= SWAP_BE32 (filesize
);
1834 bthp
->btreeType
= kUserBTreeType
; /* non-metadata */
1835 bthp
->attributes
|= SWAP_BE32 (kBTBigKeysMask
);
1836 bthp
->maxKeyLength
= SWAP_BE16 (HFC_KEYLENGTH
);
1837 offset
+= sizeof(BTHeaderRec
);
1838 index
[(nodesize
/ 2) - 2] = SWAP_BE16 (offset
);
1840 /* FILL IN THE USER RECORD: */
1841 hotfileinfo
= (HotFilesInfo
*)((UInt8
*)buffer
+ offset
);
1842 hotfileinfo
->magic
= SWAP_BE32 (HFC_MAGIC
);
1843 hotfileinfo
->version
= SWAP_BE32 (HFC_VERSION
);
1844 hotfileinfo
->duration
= SWAP_BE32 (HFC_DEFAULT_DURATION
);
1845 hotfileinfo
->timebase
= 0;
1846 hotfileinfo
->timeleft
= 0;
1847 hotfileinfo
->threshold
= SWAP_BE32 (HFC_MINIMUM_TEMPERATURE
);
1848 hotfileinfo
->maxfileblks
= SWAP_BE32 (HFC_MAXIMUM_FILESIZE
/ HFSTOVCB(hfsmp
)->blockSize
);
1849 hotfileinfo
->maxfilecnt
= SWAP_BE32 (HFC_DEFAULT_FILE_COUNT
);
1850 strcpy(hotfileinfo
->tag
, hfc_tag
);
1851 offset
+= kBTreeHeaderUserBytes
;
1852 index
[(nodesize
/ 2) - 3] = SWAP_BE16 (offset
);
1854 /* FILL IN THE MAP RECORD (only one node in use). */
1855 *((u_int8_t
*)buffer
+ offset
) = 0x80;
1856 offset
+= nodesize
- sizeof(BTNodeDescriptor
) - sizeof(BTHeaderRec
)
1857 - kBTreeHeaderUserBytes
- (4 * sizeof(int16_t));
1858 index
[(nodesize
/ 2) - 4] = SWAP_BE16 (offset
);
1860 vnode_setnoflush(vp
);
1861 error
= hfs_truncate(vp
, (off_t
)filesize
, IO_NDELAY
, 0, &context
);
1863 printf("HFS: error %d growing HFBT on %s\n", error
, HFSTOVCB(hfsmp
)->vcbVN
);
1866 cp
->c_flag
|= C_ZFWANTSYNC
;
1867 cp
->c_zftimeout
= 1;
1870 struct vnop_write_args args
;
1873 auio
= uio_create(1, 0, UIO_SYSSPACE32
, UIO_WRITE
);
1874 uio_addiov(auio
, (uintptr_t)buffer
, nodesize
);
1876 args
.a_desc
= &vnop_write_desc
;
1880 args
.a_context
= &context
;
1885 error
= hfs_vnop_write(&args
);
1887 printf("HFS: error %d writing HFBT on %s\n", error
, HFSTOVCB(hfsmp
)->vcbVN
);
1891 kmem_free(kernel_map
, (vm_offset_t
)buffer
, nodesize
);
1907 * Compare two hot file b-tree keys.
1909 * Result: +n search key > trial key
1910 * 0 search key = trial key
1911 * -n search key < trial key
1914 hfc_comparekeys(HotFileKey
*searchKey
, HotFileKey
*trialKey
)
1917 * Compared temperatures first.
1919 if (searchKey
->temperature
== trialKey
->temperature
) {
1921 * Temperatures are equal so compare file ids.
1923 if (searchKey
->fileID
== trialKey
->fileID
) {
1925 * File ids are equal so compare fork types.
1927 if (searchKey
->forkType
== trialKey
->forkType
) {
1929 } else if (searchKey
->forkType
> trialKey
->forkType
) {
1932 } else if (searchKey
->fileID
> trialKey
->fileID
) {
1935 } else if (searchKey
->temperature
> trialKey
->temperature
) {
1944 *========================================================================
1945 * HOT FILE DATA COLLECTING ROUTINES
1946 *========================================================================
1950 * Lookup a hot file entry in the tree.
1953 static hotfile_entry_t
*
1954 hf_lookup(hotfile_data_t
*hotdata
, u_int32_t fileid
, u_int32_t temperature
)
1956 hotfile_entry_t
*entry
= hotdata
->rootentry
;
1959 entry
->temperature
!= temperature
&&
1960 entry
->fileid
!= fileid
) {
1962 if (temperature
> entry
->temperature
)
1963 entry
= entry
->right
;
1964 else if (temperature
< entry
->temperature
)
1965 entry
= entry
->left
;
1966 else if (fileid
> entry
->fileid
)
1967 entry
= entry
->right
;
1969 entry
= entry
->left
;
1976 * Insert a hot file entry into the tree.
1979 hf_insert(hotfile_data_t
*hotdata
, hotfile_entry_t
*newentry
)
1981 hotfile_entry_t
*entry
= hotdata
->rootentry
;
1982 u_int32_t fileid
= newentry
->fileid
;
1983 u_int32_t temperature
= newentry
->temperature
;
1985 if (entry
== NULL
) {
1986 hotdata
->rootentry
= newentry
;
1987 hotdata
->coldest
= newentry
;
1988 hotdata
->activefiles
++;
1993 if (temperature
> entry
->temperature
) {
1995 entry
= entry
->right
;
1997 entry
->right
= newentry
;
2000 } else if (temperature
< entry
->temperature
) {
2002 entry
= entry
->left
;
2004 entry
->left
= newentry
;
2007 } else if (fileid
> entry
->fileid
) {
2009 entry
= entry
->right
;
2011 if (entry
->fileid
!= fileid
)
2012 entry
->right
= newentry
;
2017 entry
= entry
->left
;
2019 if (entry
->fileid
!= fileid
)
2020 entry
->left
= newentry
;
2026 hotdata
->activefiles
++;
2030 * Find the coldest entry in the tree.
2032 static hotfile_entry_t
*
2033 hf_coldest(hotfile_data_t
*hotdata
)
2035 hotfile_entry_t
*entry
= hotdata
->rootentry
;
2039 entry
= entry
->left
;
2045 * Find the hottest entry in the tree.
2047 static hotfile_entry_t
*
2048 hf_hottest(hotfile_data_t
*hotdata
)
2050 hotfile_entry_t
*entry
= hotdata
->rootentry
;
2053 while (entry
->right
)
2054 entry
= entry
->right
;
2060 * Delete a hot file entry from the tree.
2063 hf_delete(hotfile_data_t
*hotdata
, u_int32_t fileid
, u_int32_t temperature
)
2065 hotfile_entry_t
*entry
, *parent
, *next
;
2068 entry
= hotdata
->rootentry
;
2071 entry
->temperature
!= temperature
&&
2072 entry
->fileid
!= fileid
) {
2075 if (temperature
> entry
->temperature
)
2076 entry
= entry
->right
;
2077 else if (temperature
< entry
->temperature
)
2078 entry
= entry
->left
;
2079 else if (fileid
> entry
->fileid
)
2080 entry
= entry
->right
;
2082 entry
= entry
->left
;
2087 * Reorginize the sub-trees spanning from our entry.
2089 if ((next
= entry
->right
)) {
2090 hotfile_entry_t
*pnextl
, *psub
;
2092 * Tree pruning: take the left branch of the
2093 * current entry and place it at the lowest
2094 * left branch of the current right branch
2098 /* Walk the Right/Left sub tree from current entry */
2099 while ((pnextl
= psub
->left
))
2102 /* Plug the old left tree to the new ->Right leftmost entry */
2103 psub
->left
= entry
->left
;
2105 } else /* only left sub-tree, simple case */ {
2109 * Now, plug the current entry sub tree to
2110 * the good pointer of our parent entry.
2113 hotdata
->rootentry
= next
;
2114 else if (parent
->left
== entry
)
2115 parent
->left
= next
;
2117 parent
->right
= next
;
2119 /* Place entry back on the free-list */
2122 entry
->temperature
= 0;
2124 entry
->right
= hotdata
->freelist
;
2125 hotdata
->freelist
= entry
;
2126 hotdata
->activefiles
--;
2128 if (hotdata
->coldest
== entry
|| hotdata
->coldest
== NULL
) {
2129 hotdata
->coldest
= hf_coldest(hotdata
);
2136 * Get a free hot file entry.
2138 static hotfile_entry_t
*
2139 hf_getnewentry(hotfile_data_t
*hotdata
)
2141 hotfile_entry_t
* entry
;
2144 * When the free list is empty then steal the coldest one
2146 if (hotdata
->freelist
== NULL
) {
2147 entry
= hf_coldest(hotdata
);
2148 hf_delete(hotdata
, entry
->fileid
, entry
->temperature
);
2150 entry
= hotdata
->freelist
;
2151 hotdata
->freelist
= entry
->right
;
2159 * Generate a sorted list of hot files (hottest to coldest).
2161 * As a side effect, every node in the hot file tree will be
2162 * deleted (moved to the free list).
2165 hf_getsortedlist(hotfile_data_t
* hotdata
, hotfilelist_t
*sortedlist
)
2168 hotfile_entry_t
*entry
;
2170 while ((entry
= hf_hottest(hotdata
)) != NULL
) {
2171 sortedlist
->hfl_hotfile
[i
].hf_fileid
= entry
->fileid
;
2172 sortedlist
->hfl_hotfile
[i
].hf_temperature
= entry
->temperature
;
2173 sortedlist
->hfl_hotfile
[i
].hf_blocks
= entry
->blocks
;
2174 sortedlist
->hfl_totalblocks
+= entry
->blocks
;
2177 hf_delete(hotdata
, entry
->fileid
, entry
->temperature
);
2180 sortedlist
->hfl_count
= i
;
2183 printf("HFS: hf_getsortedlist returned %d entries\n", i
);
2190 hf_maxdepth(hotfile_entry_t
* root
, int depth
, int *maxdepth
)
2194 if (depth
> *maxdepth
)
2196 hf_maxdepth(root
->left
, depth
, maxdepth
);
2197 hf_maxdepth(root
->right
, depth
, maxdepth
);
2202 hf_printtree(hotfile_entry_t
* root
)
2205 hf_printtree(root
->left
);
2206 printf("temperature: % 8d, fileid %d\n", root
->temperature
, root
->fileid
);
2207 hf_printtree(root
->right
);