2 * Copyright (c) 2003-2005 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
20 * @APPLE_LICENSE_HEADER_END@
23 #include <sys/param.h>
24 #include <sys/systm.h>
25 #include <sys/fcntl.h>
26 #include <sys/kernel.h>
27 #include <sys/malloc.h>
29 #include <sys/vnode.h>
30 #include <sys/vnode_internal.h>
31 #include <sys/kauth.h>
34 #include <hfs/hfs_endian.h>
35 #include <hfs/hfs_format.h>
36 #include <hfs/hfs_mount.h>
37 #include <hfs/hfs_hotfiles.h>
39 #include "hfscommon/headers/BTreeScanner.h"
48 * Hot File List (runtime).
50 typedef struct hotfileinfo
{
52 u_int32_t hf_temperature
;
56 typedef struct hotfilelist
{
58 u_int32_t hfl_version
;
59 time_t hfl_duration
; /* duration of sample period */
60 int hfl_count
; /* count of hot files recorded */
61 int hfl_next
; /* next file to move */
62 int hfl_totalblocks
; /* total hot file blocks */
63 int hfl_reclaimblks
; /* blocks to reclaim in HFV */
64 u_int32_t hfl_spare
[2];
65 hotfileinfo_t hfl_hotfile
[1]; /* array of hot files */
70 * Hot File Entry (runtime).
72 typedef struct hotfile_entry
{
73 struct hotfile_entry
*left
;
74 struct hotfile_entry
*right
;
76 u_int32_t temperature
;
81 * Hot File Recording Data (runtime).
83 typedef struct hotfile_data
{
84 struct hfsmount
*hfsmp
;
86 int activefiles
; /* active number of hot files */
89 hotfile_entry_t
*rootentry
;
90 hotfile_entry_t
*freelist
;
91 hotfile_entry_t
*coldest
;
92 hotfile_entry_t entries
[1];
95 static int hfs_recording_start (struct hfsmount
*);
96 static int hfs_recording_stop (struct hfsmount
*);
100 * Hot File Data recording functions (in-memory binary tree).
102 static void hf_insert (hotfile_data_t
*, hotfile_entry_t
*);
103 static void hf_delete (hotfile_data_t
*, u_int32_t
, u_int32_t
);
104 static hotfile_entry_t
* hf_coldest (hotfile_data_t
*);
105 static hotfile_entry_t
* hf_getnewentry (hotfile_data_t
*);
106 static void hf_getsortedlist (hotfile_data_t
*, hotfilelist_t
*);
109 static hotfile_entry_t
* hf_lookup (hotfile_data_t
*, u_int32_t
, u_int32_t
);
110 static void hf_maxdepth(hotfile_entry_t
*, int, int *);
111 static void hf_printtree (hotfile_entry_t
*);
115 * Hot File misc support functions.
117 static int hotfiles_collect (struct hfsmount
*);
118 static int hotfiles_age (struct hfsmount
*);
119 static int hotfiles_adopt (struct hfsmount
*);
120 static int hotfiles_evict (struct hfsmount
*, struct proc
*);
121 static int hotfiles_refine (struct hfsmount
*);
122 static int hotextents(struct hfsmount
*, HFSPlusExtentDescriptor
*);
123 static int hfs_addhotfile_internal(struct vnode
*);
127 * Hot File Cluster B-tree (on disk) functions.
129 static int hfc_btree_create (struct hfsmount
*, int, int);
130 static int hfc_btree_open (struct hfsmount
*, struct vnode
**);
131 static int hfc_btree_close (struct hfsmount
*, struct vnode
*);
132 static int hfc_comparekeys (HotFileKey
*, HotFileKey
*);
135 char hfc_tag
[] = "CLUSTERED HOT FILES B-TREE ";
137 extern int UBCINFOEXISTS(struct vnode
* vp
);
138 extern int hfs_vnop_write(struct vnop_write_args
*ap
);
142 *========================================================================
143 * HOT FILE INTERFACE ROUTINES
144 *========================================================================
148 * Start recording the hotest files on a file system.
150 * Requires that the hfc_mutex be held.
153 hfs_recording_start(struct hfsmount
*hfsmp
)
155 hotfile_data_t
*hotdata
;
162 if ((hfsmp
->hfs_flags
& HFS_READ_ONLY
) ||
163 (hfsmp
->jnl
== NULL
) ||
164 (hfsmp
->hfs_flags
& HFS_METADATA_ZONE
) == 0) {
167 if (HFSTOVCB(hfsmp
)->freeBlocks
< (2 * (u_int32_t
)hfsmp
->hfs_hotfile_maxblks
)) {
170 if (hfsmp
->hfc_stage
!= HFC_IDLE
) {
173 hfsmp
->hfc_stage
= HFC_BUSY
;
176 * Dump previous recording data.
178 if (hfsmp
->hfc_recdata
) {
181 tmp
= hfsmp
->hfc_recdata
;
182 hfsmp
->hfc_recdata
= NULL
;
189 * On first startup check for suspended recording.
191 if (hfsmp
->hfc_timebase
== 0 &&
192 hfc_btree_open(hfsmp
, &hfsmp
->hfc_filevp
) == 0) {
193 HotFilesInfo hotfileinfo
;
195 if ((BTGetUserData(VTOF(hfsmp
->hfc_filevp
), &hotfileinfo
,
196 sizeof(hotfileinfo
)) == 0) &&
197 (SWAP_BE32 (hotfileinfo
.magic
) == HFC_MAGIC
) &&
198 (SWAP_BE32 (hotfileinfo
.timeleft
) > 0) &&
199 (SWAP_BE32 (hotfileinfo
.timebase
) > 0)) {
200 hfsmp
->hfc_maxfiles
= SWAP_BE32 (hotfileinfo
.maxfilecnt
);
201 hfsmp
->hfc_timeout
= SWAP_BE32 (hotfileinfo
.timeleft
) + tv
.tv_sec
;
202 hfsmp
->hfc_timebase
= SWAP_BE32 (hotfileinfo
.timebase
);
204 printf("Resume recording hot files on %s (%d secs left)\n",
205 hfsmp
->vcbVN
, SWAP_BE32 (hotfileinfo
.timeleft
));
208 hfsmp
->hfc_maxfiles
= HFC_DEFAULT_FILE_COUNT
;
209 hfsmp
->hfc_timebase
= tv
.tv_sec
+ 1;
210 hfsmp
->hfc_timeout
= hfsmp
->hfc_timebase
+ HFC_DEFAULT_DURATION
;
212 (void) hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
213 hfsmp
->hfc_filevp
= NULL
;
215 struct cat_attr cattr
;
219 * Make sure a btree file exists.
221 cnid
= GetFileInfo(HFSTOVCB(hfsmp
), kRootDirID
, HFC_FILENAME
, &cattr
, NULL
);
223 !S_ISREG(cattr
.ca_mode
) &&
224 (error
= hfc_btree_create(hfsmp
, HFSTOVCB(hfsmp
)->blockSize
, HFC_DEFAULT_FILE_COUNT
))) {
225 hfsmp
->hfc_stage
= HFC_IDLE
;
226 wakeup((caddr_t
)&hfsmp
->hfc_stage
);
230 printf("HFS: begin recording hot files on %s\n", hfsmp
->vcbVN
);
232 hfsmp
->hfc_maxfiles
= HFC_DEFAULT_FILE_COUNT
;
233 hfsmp
->hfc_timeout
= tv
.tv_sec
+ HFC_DEFAULT_DURATION
;
235 /* Reset time base. */
236 if (hfsmp
->hfc_timebase
== 0) {
237 hfsmp
->hfc_timebase
= tv
.tv_sec
+ 1;
239 time_t cumulativebase
;
241 cumulativebase
= hfsmp
->hfc_timeout
- (HFC_CUMULATIVE_CYCLES
* HFC_DEFAULT_DURATION
);
242 hfsmp
->hfc_timebase
= MAX(hfsmp
->hfc_timebase
, cumulativebase
);
246 if ((hfsmp
->hfc_maxfiles
== 0) ||
247 (hfsmp
->hfc_maxfiles
> HFC_MAXIMUM_FILE_COUNT
)) {
248 hfsmp
->hfc_maxfiles
= HFC_DEFAULT_FILE_COUNT
;
250 maxentries
= hfsmp
->hfc_maxfiles
;
252 size
= sizeof(hotfile_data_t
) + (maxentries
* sizeof(hotfile_entry_t
));
253 MALLOC(hotdata
, hotfile_data_t
*, size
, M_TEMP
, M_WAITOK
);
254 bzero(hotdata
, size
);
256 for (i
= 1; i
< maxentries
; i
++)
257 hotdata
->entries
[i
-1].right
= &hotdata
->entries
[i
];
259 hotdata
->freelist
= &hotdata
->entries
[0];
261 * Establish minimum temperature and maximum file size.
263 hotdata
->threshold
= HFC_MINIMUM_TEMPERATURE
;
264 hotdata
->maxblocks
= HFC_MAXIMUM_FILESIZE
/ HFSTOVCB(hfsmp
)->blockSize
;
265 hotdata
->hfsmp
= hfsmp
;
267 hfsmp
->hfc_recdata
= hotdata
;
268 hfsmp
->hfc_stage
= HFC_RECORDING
;
269 wakeup((caddr_t
)&hfsmp
->hfc_stage
);
274 * Stop recording the hotest files on a file system.
276 * Requires that the hfc_mutex be held.
279 hfs_recording_stop(struct hfsmount
*hfsmp
)
281 hotfile_data_t
*hotdata
;
282 hotfilelist_t
*listp
;
285 enum hfc_stage newstage
= HFC_IDLE
;
288 if (hfsmp
->hfc_stage
!= HFC_RECORDING
)
291 hfsmp
->hfc_stage
= HFC_BUSY
;
293 hotfiles_collect(hfsmp
);
296 * Convert hot file data into a simple file id list....
298 * then dump the sample data
301 printf("HFS: end of hot file recording on %s\n", hfsmp
->vcbVN
);
303 hotdata
= (hotfile_data_t
*)hfsmp
->hfc_recdata
;
306 hfsmp
->hfc_recdata
= NULL
;
307 hfsmp
->hfc_stage
= HFC_EVALUATION
;
308 wakeup((caddr_t
)&hfsmp
->hfc_stage
);
311 printf(" curentries: %d\n", hotdata
->activefiles
);
314 * If no hot files recorded then we're done.
316 if (hotdata
->rootentry
== NULL
) {
321 /* Open the B-tree file for writing... */
322 if (hfsmp
->hfc_filevp
)
323 panic("hfs_recording_stop: hfc_filevp exists (vp = 0x%08x)", hfsmp
->hfc_filevp
);
325 error
= hfc_btree_open(hfsmp
, &hfsmp
->hfc_filevp
);
331 * Age the previous set of clustered hot files.
333 error
= hotfiles_age(hfsmp
);
335 (void) hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
336 hfsmp
->hfc_filevp
= NULL
;
341 * Create a sorted list of hotest files.
343 size
= sizeof(hotfilelist_t
);
344 size
+= sizeof(hotfileinfo_t
) * (hotdata
->activefiles
- 1);
345 MALLOC(listp
, hotfilelist_t
*, size
, M_TEMP
, M_WAITOK
);
348 hf_getsortedlist(hotdata
, listp
); /* NOTE: destroys hot file tree! */
350 listp
->hfl_duration
= tv
.tv_sec
- hfsmp
->hfc_timebase
;
351 hfsmp
->hfc_recdata
= listp
;
354 * Account for duplicates.
356 error
= hotfiles_refine(hfsmp
);
358 (void) hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
359 hfsmp
->hfc_filevp
= NULL
;
364 * Compute the amount of space to reclaim...
366 if (listp
->hfl_totalblocks
> hfsmp
->hfs_hotfile_freeblks
) {
367 listp
->hfl_reclaimblks
=
368 MIN(listp
->hfl_totalblocks
, hfsmp
->hfs_hotfile_maxblks
) -
369 hfsmp
->hfs_hotfile_freeblks
;
371 printf("hfs_recording_stop: need to reclaim %d blocks\n", listp
->hfl_reclaimblks
);
373 if (listp
->hfl_reclaimblks
)
374 newstage
= HFC_EVICTION
;
376 newstage
= HFC_ADOPTION
;
378 newstage
= HFC_ADOPTION
;
381 if (newstage
== HFC_ADOPTION
&& listp
->hfl_totalblocks
== 0) {
382 (void) hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
383 hfsmp
->hfc_filevp
= NULL
;
388 if (newstage
== HFC_EVICTION
)
389 printf("HFS: evicting coldest files\n");
390 else if (newstage
== HFC_ADOPTION
)
391 printf("HFS: adopting hotest files\n");
393 FREE(hotdata
, M_TEMP
);
395 hfsmp
->hfc_stage
= newstage
;
396 wakeup((caddr_t
)&hfsmp
->hfc_stage
);
401 * Suspend recording the hotest files on a file system.
405 hfs_recording_suspend(struct hfsmount
*hfsmp
)
407 HotFilesInfo hotfileinfo
;
408 hotfile_data_t
*hotdata
= NULL
;
412 if (hfsmp
->hfc_stage
== HFC_DISABLED
)
415 lck_mtx_lock(&hfsmp
->hfc_mutex
);
419 * A suspend can occur during eval/evict/adopt stage.
420 * In that case we would need to write out info and
421 * flush our HFBT vnode. Currently we just bail.
424 hotdata
= (hotfile_data_t
*)hfsmp
->hfc_recdata
;
425 if (hotdata
== NULL
|| hfsmp
->hfc_stage
!= HFC_RECORDING
) {
429 hfsmp
->hfc_stage
= HFC_BUSY
;
432 printf("HFS: suspend hot file recording on %s\n", hfsmp
->vcbVN
);
434 error
= hfc_btree_open(hfsmp
, &hfsmp
->hfc_filevp
);
436 printf("hfs_recording_suspend: err %d opening btree\n", error
);
440 if (hfs_start_transaction(hfsmp
) != 0) {
444 if (hfs_lock(VTOC(hfsmp
->hfc_filevp
), HFS_EXCLUSIVE_LOCK
) != 0) {
450 hotfileinfo
.magic
= SWAP_BE32 (HFC_MAGIC
);
451 hotfileinfo
.version
= SWAP_BE32 (HFC_VERSION
);
452 hotfileinfo
.duration
= SWAP_BE32 (HFC_DEFAULT_DURATION
);
453 hotfileinfo
.timebase
= SWAP_BE32 (hfsmp
->hfc_timebase
);
454 hotfileinfo
.timeleft
= SWAP_BE32 (hfsmp
->hfc_timeout
- tv
.tv_sec
);
455 hotfileinfo
.threshold
= SWAP_BE32 (hotdata
->threshold
);
456 hotfileinfo
.maxfileblks
= SWAP_BE32 (hotdata
->maxblocks
);
457 hotfileinfo
.maxfilecnt
= SWAP_BE32 (HFC_DEFAULT_FILE_COUNT
);
458 strcpy(hotfileinfo
.tag
, hfc_tag
);
459 (void) BTSetUserData(VTOF(hfsmp
->hfc_filevp
), &hotfileinfo
, sizeof(hotfileinfo
));
461 hfs_unlock(VTOC(hfsmp
->hfc_filevp
));
462 hfs_end_transaction(hfsmp
);
464 if (hfsmp
->hfc_filevp
) {
465 (void) hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
466 hfsmp
->hfc_filevp
= NULL
;
469 FREE(hotdata
, M_TEMP
);
470 hfsmp
->hfc_recdata
= NULL
;
472 hfsmp
->hfc_stage
= HFC_DISABLED
;
473 wakeup((caddr_t
)&hfsmp
->hfc_stage
);
475 lck_mtx_unlock(&hfsmp
->hfc_mutex
);
485 hfs_recording_init(struct hfsmount
*hfsmp
)
488 CatalogRecord
* datap
;
490 HFSPlusCatalogFile
*filep
;
491 BTScanState scanstate
;
492 BTreeIterator
* iterator
;
493 FSBufferDescriptor record
;
495 filefork_t
* filefork
;
497 struct cat_attr cattr
;
501 int inserted
= 0; /* debug variables */
505 * For now, only the boot volume is supported.
507 if ((vfs_flags(HFSTOVFS(hfsmp
)) & MNT_ROOTFS
) == 0) {
508 hfsmp
->hfc_stage
= HFC_DISABLED
;
513 * If the Hot File btree exists then metadata zone is ready.
515 cnid
= GetFileInfo(HFSTOVCB(hfsmp
), kRootDirID
, HFC_FILENAME
, &cattr
, NULL
);
516 if (cnid
!= 0 && S_ISREG(cattr
.ca_mode
)) {
517 if (hfsmp
->hfc_stage
== HFC_DISABLED
)
518 hfsmp
->hfc_stage
= HFC_IDLE
;
521 error
= hfc_btree_create(hfsmp
, HFSTOVCB(hfsmp
)->blockSize
, HFC_DEFAULT_FILE_COUNT
);
524 printf("Error %d creating hot file b-tree on %s \n", error
, hfsmp
->vcbVN
);
529 * Open the Hot File B-tree file for writing.
531 if (hfsmp
->hfc_filevp
)
532 panic("hfs_recording_init: hfc_filevp exists (vp = 0x%08x)", hfsmp
->hfc_filevp
);
533 error
= hfc_btree_open(hfsmp
, &hfsmp
->hfc_filevp
);
536 printf("Error %d opening hot file b-tree on %s \n", error
, hfsmp
->vcbVN
);
540 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
541 bzero(iterator
, sizeof(*iterator
));
542 key
= (HotFileKey
*) &iterator
->key
;
543 key
->keyLength
= HFC_KEYLENGTH
;
545 record
.bufferAddress
= &data
;
546 record
.itemSize
= sizeof(u_int32_t
);
547 record
.itemCount
= 1;
549 printf("Evaluating space for \"%s\" metadata zone...\n", HFSTOVCB(hfsmp
)->vcbVN
);
552 * Get ready to scan the Catalog file.
554 error
= BTScanInitialize(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), 0, 0, 0,
555 kCatSearchBufferSize
, &scanstate
);
557 printf("hfs_recording_init: err %d BTScanInit\n", error
);
562 * The writes to Hot File B-tree file are journaled.
564 if (hfs_start_transaction(hfsmp
) != 0) {
568 if (hfs_lock(VTOC(hfsmp
->hfc_filevp
), HFS_EXCLUSIVE_LOCK
) != 0) {
572 filefork
= VTOF(hfsmp
->hfc_filevp
);
575 * Visit all the catalog btree leaf records.
578 error
= BTScanNextRecord(&scanstate
, 0, (void **)&keyp
, (void **)&datap
, &dataSize
);
580 if (error
== btNotFound
)
583 printf("hfs_recording_init: err %d BTScanNext\n", error
);
586 if ((datap
->recordType
!= kHFSPlusFileRecord
) ||
587 (dataSize
!= sizeof(HFSPlusCatalogFile
))) {
590 filep
= (HFSPlusCatalogFile
*)datap
;
592 if (filep
->dataFork
.totalBlocks
== 0) {
596 * Any file that has blocks inside the hot file
597 * space is recorded for later eviction.
599 * For now, resource forks are ignored.
601 if (!hotextents(hfsmp
, &filep
->dataFork
.extents
[0])) {
604 cnid
= filep
->fileID
;
606 /* Skip over journal files. */
607 if (cnid
== hfsmp
->hfs_jnlfileid
|| cnid
== hfsmp
->hfs_jnlinfoblkid
) {
611 * XXX - need to skip quota files as well.
614 /* Insert a hot file entry. */
615 key
->keyLength
= HFC_KEYLENGTH
;
616 key
->temperature
= HFC_MINIMUM_TEMPERATURE
;
620 error
= BTInsertRecord(filefork
, iterator
, &record
, record
.itemSize
);
622 printf("hfs_recording_init: BTInsertRecord failed %d (fileid %d)\n", error
, key
->fileID
);
623 error
= MacToVFSError(error
);
627 /* Insert the corresponding thread record. */
628 key
->keyLength
= HFC_KEYLENGTH
;
629 key
->temperature
= HFC_LOOKUPTAG
;
632 data
= HFC_MINIMUM_TEMPERATURE
;
633 error
= BTInsertRecord(filefork
, iterator
, &record
, record
.itemSize
);
635 printf("hfs_recording_init: BTInsertRecord failed %d (fileid %d)\n", error
, key
->fileID
);
636 error
= MacToVFSError(error
);
641 (void) BTFlushPath(filefork
);
642 hfs_unlock(VTOC(hfsmp
->hfc_filevp
));
644 hfs_end_transaction(hfsmp
);
646 printf("%d files identified out of %d\n", inserted
, filecount
);
650 (void) BTScanTerminate(&scanstate
, &data
, &data
, &data
);
652 FREE(iterator
, M_TEMP
);
653 if (hfsmp
->hfc_filevp
) {
654 (void) hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
655 hfsmp
->hfc_filevp
= NULL
;
658 hfsmp
->hfc_stage
= HFC_IDLE
;
664 * Use sync to perform ocassional background work.
668 hfs_hotfilesync(struct hfsmount
*hfsmp
, struct proc
*p
)
670 if (hfsmp
->hfc_stage
) {
673 lck_mtx_lock(&hfsmp
->hfc_mutex
);
675 switch (hfsmp
->hfc_stage
) {
677 (void) hfs_recording_start(hfsmp
);
682 if (tv
.tv_sec
> hfsmp
->hfc_timeout
)
683 (void) hfs_recording_stop(hfsmp
);
687 (void) hotfiles_evict(hfsmp
, p
);
691 (void) hotfiles_adopt(hfsmp
);
697 lck_mtx_unlock(&hfsmp
->hfc_mutex
);
703 * Add a hot file to the recording list.
705 * This can happen when a hot file gets reclaimed or at the
706 * end of the recording period for any active hot file.
708 * NOTE: Since both the data and resource fork can be hot,
709 * there can be two entries for the same file id.
711 * Note: the cnode is locked on entry.
715 hfs_addhotfile(struct vnode
*vp
)
721 if (hfsmp
->hfc_stage
!= HFC_RECORDING
)
724 lck_mtx_lock(&hfsmp
->hfc_mutex
);
725 error
= hfs_addhotfile_internal(vp
);
726 lck_mtx_unlock(&hfsmp
->hfc_mutex
);
731 hfs_addhotfile_internal(struct vnode
*vp
)
733 hotfile_data_t
*hotdata
;
734 hotfile_entry_t
*entry
;
738 u_int32_t temperature
;
741 if (hfsmp
->hfc_stage
!= HFC_RECORDING
)
744 if ((!vnode_isreg(vp
) && !vnode_islnk(vp
)) || vnode_issystem(vp
)) {
747 /* Skip resource forks for now. */
748 if (VNODE_IS_RSRC(vp
)) {
751 if ((hotdata
= (hotfile_data_t
*)hfsmp
->hfc_recdata
) == NULL
) {
757 if ((ffp
->ff_bytesread
== 0) ||
758 (ffp
->ff_blocks
== 0) ||
759 (ffp
->ff_size
== 0) ||
760 (ffp
->ff_blocks
> hotdata
->maxblocks
) ||
761 (cp
->c_flag
& (C_DELETED
| C_NOEXISTS
)) ||
762 (cp
->c_flags
& UF_NODUMP
) ||
763 (cp
->c_atime
< hfsmp
->hfc_timebase
)) {
767 temperature
= ffp
->ff_bytesread
/ ffp
->ff_size
;
768 if (temperature
< hotdata
->threshold
) {
772 * If there is room or this file is hotter than
773 * the coldest one then add it to the list.
776 if ((hotdata
->activefiles
< hfsmp
->hfc_maxfiles
) ||
777 (hotdata
->coldest
== NULL
) ||
778 (temperature
> hotdata
->coldest
->temperature
)) {
780 entry
= hf_getnewentry(hotdata
);
781 entry
->temperature
= temperature
;
782 entry
->fileid
= cp
->c_fileid
;
783 entry
->blocks
= ffp
->ff_blocks
;
784 hf_insert(hotdata
, entry
);
792 * Remove a hot file from the recording list.
794 * This can happen when a hot file becomes
795 * an active vnode (active hot files are
796 * not kept in the recording list until the
797 * end of the recording period).
799 * Note: the cnode is locked on entry.
803 hfs_removehotfile(struct vnode
*vp
)
805 hotfile_data_t
*hotdata
;
809 u_int32_t temperature
;
812 if (hfsmp
->hfc_stage
!= HFC_RECORDING
)
815 if ((!vnode_isreg(vp
) && !vnode_islnk(vp
)) || vnode_issystem(vp
)) {
822 if ((ffp
->ff_bytesread
== 0) || (ffp
->ff_blocks
== 0) ||
823 (ffp
->ff_size
== 0) || (cp
->c_atime
< hfsmp
->hfc_timebase
)) {
827 lck_mtx_lock(&hfsmp
->hfc_mutex
);
828 if (hfsmp
->hfc_stage
!= HFC_RECORDING
)
830 if ((hotdata
= (hotfile_data_t
*)hfsmp
->hfc_recdata
) == NULL
)
833 temperature
= ffp
->ff_bytesread
/ ffp
->ff_size
;
834 if (temperature
< hotdata
->threshold
)
837 if (hotdata
->coldest
&& (temperature
>= hotdata
->coldest
->temperature
)) {
839 hf_delete(hotdata
, VTOC(vp
)->c_fileid
, temperature
);
843 lck_mtx_unlock(&hfsmp
->hfc_mutex
);
849 *========================================================================
850 * HOT FILE MAINTENANCE ROUTINES
851 *========================================================================
855 hotfiles_collect_callback(struct vnode
*vp
, __unused
void *cargs
)
857 if ((vnode_isreg(vp
) || vnode_islnk(vp
)) && !vnode_issystem(vp
))
858 (void) hfs_addhotfile_internal(vp
);
860 return (VNODE_RETURNED
);
864 * Add all active hot files to the recording list.
867 hotfiles_collect(struct hfsmount
*hfsmp
)
869 struct mount
*mp
= HFSTOVFS(hfsmp
);
871 if (vfs_busy(mp
, LK_NOWAIT
))
875 * hotfiles_collect_callback will be called for each vnode
876 * hung off of this mount point
878 * properly referenced and unreferenced around the callback
880 vnode_iterate(mp
, 0, hotfiles_collect_callback
, (void *)NULL
);
889 * Update the data of a btree record
890 * This is called from within BTUpdateRecord.
893 update_callback(const HotFileKey
*key
, u_int32_t
*data
, u_int32_t
*state
)
895 if (key
->temperature
== HFC_LOOKUPTAG
)
901 * Identify files already in hot area.
904 hotfiles_refine(struct hfsmount
*hfsmp
)
906 BTreeIterator
* iterator
;
908 filefork_t
* filefork
;
909 hotfilelist_t
*listp
;
910 FSBufferDescriptor record
;
917 if ((listp
= (hotfilelist_t
*)hfsmp
->hfc_recdata
) == NULL
)
920 mp
= HFSTOVFS(hfsmp
);
922 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
923 bzero(iterator
, sizeof(*iterator
));
924 key
= (HotFileKey
*) &iterator
->key
;
926 record
.bufferAddress
= &data
;
927 record
.itemSize
= sizeof(u_int32_t
);
928 record
.itemCount
= 1;
930 if (hfs_start_transaction(hfsmp
) != 0) {
934 if (hfs_lock(VTOC(hfsmp
->hfc_filevp
), HFS_EXCLUSIVE_LOCK
) != 0) {
938 filefork
= VTOF(hfsmp
->hfc_filevp
);
940 for (i
= 0; i
< listp
->hfl_count
; ++i
) {
942 * Check if entry (thread) is already in hot area.
944 key
->keyLength
= HFC_KEYLENGTH
;
945 key
->temperature
= HFC_LOOKUPTAG
;
946 key
->fileID
= listp
->hfl_hotfile
[i
].hf_fileid
;
948 (void) BTInvalidateHint(iterator
);
949 if (BTSearchRecord(filefork
, iterator
, &record
, NULL
, iterator
) != 0) {
950 continue; /* not in hot area, so skip */
954 * Update thread entry with latest temperature.
956 error
= BTUpdateRecord(filefork
, iterator
,
957 (IterateCallBackProcPtr
)update_callback
,
958 &listp
->hfl_hotfile
[i
].hf_temperature
);
960 printf("hotfiles_refine: BTUpdateRecord failed %d (file %d)\n", error
, key
->fileID
);
961 error
= MacToVFSError(error
);
965 * Re-key entry with latest temperature.
967 key
->keyLength
= HFC_KEYLENGTH
;
968 key
->temperature
= data
;
969 key
->fileID
= listp
->hfl_hotfile
[i
].hf_fileid
;
971 /* Pick up record data. */
972 (void) BTInvalidateHint(iterator
);
973 (void) BTSearchRecord(filefork
, iterator
, &record
, NULL
, iterator
);
974 error
= BTDeleteRecord(filefork
, iterator
);
976 printf("hotfiles_refine: BTDeleteRecord failed %d (file %d)\n", error
, key
->fileID
);
977 error
= MacToVFSError(error
);
980 key
->keyLength
= HFC_KEYLENGTH
;
981 key
->temperature
= listp
->hfl_hotfile
[i
].hf_temperature
;
982 key
->fileID
= listp
->hfl_hotfile
[i
].hf_fileid
;
984 error
= BTInsertRecord(filefork
, iterator
, &record
, record
.itemSize
);
986 printf("hotfiles_refine: BTInsertRecord failed %d (file %d)\n", error
, key
->fileID
);
987 error
= MacToVFSError(error
);
992 * Invalidate this entry in the list.
994 listp
->hfl_hotfile
[i
].hf_temperature
= 0;
995 listp
->hfl_totalblocks
-= listp
->hfl_hotfile
[i
].hf_blocks
;
999 (void) BTFlushPath(filefork
);
1000 hfs_unlock(VTOC(hfsmp
->hfc_filevp
));
1002 hfs_end_transaction(hfsmp
);
1004 FREE(iterator
, M_TEMP
);
1009 * Move new hot files into hot area.
1011 * Requires that the hfc_mutex be held.
1014 hotfiles_adopt(struct hfsmount
*hfsmp
)
1016 BTreeIterator
* iterator
;
1018 filefork_t
* filefork
;
1019 hotfilelist_t
*listp
;
1020 FSBufferDescriptor record
;
1023 enum hfc_stage stage
;
1029 int startedtrans
= 0;
1031 if ((listp
= (hotfilelist_t
*)hfsmp
->hfc_recdata
) == NULL
)
1034 if (hfsmp
->hfc_stage
!= HFC_ADOPTION
) {
1037 if (hfs_lock(VTOC(hfsmp
->hfc_filevp
), HFS_EXCLUSIVE_LOCK
) != 0) {
1041 stage
= hfsmp
->hfc_stage
;
1042 hfsmp
->hfc_stage
= HFC_BUSY
;
1045 last
= listp
->hfl_next
+ HFC_FILESPERSYNC
;
1046 if (last
> listp
->hfl_count
)
1047 last
= listp
->hfl_count
;
1049 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
1050 bzero(iterator
, sizeof(*iterator
));
1051 key
= (HotFileKey
*) &iterator
->key
;
1052 key
->keyLength
= HFC_KEYLENGTH
;
1054 record
.bufferAddress
= &data
;
1055 record
.itemSize
= sizeof(u_int32_t
);
1056 record
.itemCount
= 1;
1058 filefork
= VTOF(hfsmp
->hfc_filevp
);
1060 for (i
= listp
->hfl_next
; (i
< last
) && (blksmoved
< HFC_BLKSPERSYNC
); ++i
) {
1062 * Skip invalid entries (already in hot area).
1064 if (listp
->hfl_hotfile
[i
].hf_temperature
== 0) {
1069 * Acquire a vnode for this file.
1071 error
= hfs_vget(hfsmp
, listp
->hfl_hotfile
[i
].hf_fileid
, &vp
, 0);
1073 if (error
== ENOENT
) {
1076 continue; /* stale entry, go to next */
1080 if (!vnode_isreg(vp
) && !vnode_islnk(vp
)) {
1081 printf("hotfiles_adopt: huh, not a file %d (%d)\n", listp
->hfl_hotfile
[i
].hf_fileid
, VTOC(vp
)->c_cnid
);
1082 hfs_unlock(VTOC(vp
));
1084 listp
->hfl_hotfile
[i
].hf_temperature
= 0;
1086 continue; /* stale entry, go to next */
1088 if (hotextents(hfsmp
, &VTOF(vp
)->ff_extents
[0])) {
1089 hfs_unlock(VTOC(vp
));
1091 listp
->hfl_hotfile
[i
].hf_temperature
= 0;
1093 listp
->hfl_totalblocks
-= listp
->hfl_hotfile
[i
].hf_blocks
;
1094 continue; /* stale entry, go to next */
1096 fileblocks
= VTOF(vp
)->ff_blocks
;
1097 if (fileblocks
> hfsmp
->hfs_hotfile_freeblks
) {
1098 hfs_unlock(VTOC(vp
));
1101 listp
->hfl_totalblocks
-= fileblocks
;
1102 continue; /* entry too big, go to next */
1105 if ((blksmoved
> 0) &&
1106 (blksmoved
+ fileblocks
) > HFC_BLKSPERSYNC
) {
1107 hfs_unlock(VTOC(vp
));
1109 break; /* adopt this entry the next time around */
1111 /* Start a new transaction. */
1112 if (hfs_start_transaction(hfsmp
) != 0) {
1114 hfs_unlock(VTOC(vp
));
1120 if (VTOC(vp
)->c_desc
.cd_nameptr
)
1121 data
= *(u_int32_t
*)(VTOC(vp
)->c_desc
.cd_nameptr
);
1125 error
= hfs_relocate(vp
, hfsmp
->hfs_hotfile_start
, kauth_cred_get(), current_proc());
1126 hfs_unlock(VTOC(vp
));
1131 /* Keep hot file free space current. */
1132 hfsmp
->hfs_hotfile_freeblks
-= fileblocks
;
1133 listp
->hfl_totalblocks
-= fileblocks
;
1135 /* Insert hot file entry */
1136 key
->keyLength
= HFC_KEYLENGTH
;
1137 key
->temperature
= listp
->hfl_hotfile
[i
].hf_temperature
;
1138 key
->fileID
= listp
->hfl_hotfile
[i
].hf_fileid
;
1141 error
= BTInsertRecord(filefork
, iterator
, &record
, record
.itemSize
);
1143 printf("hotfiles_adopt: BTInsertRecord failed %d (fileid %d)\n", error
, key
->fileID
);
1144 error
= MacToVFSError(error
);
1149 /* Insert thread record */
1150 key
->keyLength
= HFC_KEYLENGTH
;
1151 key
->temperature
= HFC_LOOKUPTAG
;
1152 key
->fileID
= listp
->hfl_hotfile
[i
].hf_fileid
;
1154 data
= listp
->hfl_hotfile
[i
].hf_temperature
;
1155 error
= BTInsertRecord(filefork
, iterator
, &record
, record
.itemSize
);
1157 printf("hotfiles_adopt: BTInsertRecord failed %d (fileid %d)\n", error
, key
->fileID
);
1158 error
= MacToVFSError(error
);
1162 (void) BTFlushPath(filefork
);
1164 /* Transaction complete. */
1166 hfs_end_transaction(hfsmp
);
1170 blksmoved
+= fileblocks
;
1172 if (listp
->hfl_next
>= listp
->hfl_count
) {
1175 if (hfsmp
->hfs_hotfile_freeblks
<= 0) {
1177 printf("hotfiles_adopt: free space exhausted (%d)\n", hfsmp
->hfs_hotfile_freeblks
);
1184 printf("hotfiles_adopt: [%d] adopted %d blocks (%d left)\n", listp
->hfl_next
, blksmoved
, listp
->hfl_totalblocks
);
1186 /* Finish any outstanding transactions. */
1188 (void) BTFlushPath(filefork
);
1189 hfs_end_transaction(hfsmp
);
1192 hfs_unlock(VTOC(hfsmp
->hfc_filevp
));
1194 if ((listp
->hfl_next
>= listp
->hfl_count
) || (hfsmp
->hfs_hotfile_freeblks
<= 0)) {
1196 printf("hotfiles_adopt: all done relocating %d files\n", listp
->hfl_count
);
1197 printf("hotfiles_adopt: %d blocks free in hot file band\n", hfsmp
->hfs_hotfile_freeblks
);
1201 FREE(iterator
, M_TEMP
);
1203 if (stage
!= HFC_ADOPTION
&& hfsmp
->hfc_filevp
) {
1204 (void) hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
1205 hfsmp
->hfc_filevp
= NULL
;
1207 hfsmp
->hfc_stage
= stage
;
1208 wakeup((caddr_t
)&hfsmp
->hfc_stage
);
1213 * Reclaim space by evicting the coldest files.
1215 * Requires that the hfc_mutex be held.
1218 hotfiles_evict(struct hfsmount
*hfsmp
, struct proc
*p
)
1220 BTreeIterator
* iterator
;
1223 filefork_t
* filefork
;
1224 hotfilelist_t
*listp
;
1225 enum hfc_stage stage
;
1226 u_int32_t savedtemp
;
1231 int startedtrans
= 0;
1234 if (hfsmp
->hfc_stage
!= HFC_EVICTION
) {
1238 if ((listp
= (hotfilelist_t
*)hfsmp
->hfc_recdata
) == NULL
)
1241 if (hfs_lock(VTOC(hfsmp
->hfc_filevp
), HFS_EXCLUSIVE_LOCK
) != 0) {
1245 stage
= hfsmp
->hfc_stage
;
1246 hfsmp
->hfc_stage
= HFC_BUSY
;
1248 filesmoved
= blksmoved
= 0;
1249 bt_op
= kBTreeFirstRecord
;
1251 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
1252 bzero(iterator
, sizeof(*iterator
));
1253 key
= (HotFileKey
*) &iterator
->key
;
1255 filefork
= VTOF(hfsmp
->hfc_filevp
);
1257 while (listp
->hfl_reclaimblks
> 0 &&
1258 blksmoved
< HFC_BLKSPERSYNC
&&
1259 filesmoved
< HFC_FILESPERSYNC
) {
1262 * Obtain the first record (ie the coldest one).
1264 if (BTIterateRecord(filefork
, bt_op
, iterator
, NULL
, NULL
) != 0) {
1266 printf("hotfiles_evict: no more records\n");
1269 stage
= HFC_ADOPTION
;
1272 if (key
->keyLength
!= HFC_KEYLENGTH
) {
1273 printf("hotfiles_evict: invalid key length %d\n", key
->keyLength
);
1277 if (key
->temperature
== HFC_LOOKUPTAG
) {
1279 printf("hotfiles_evict: ran into thread records\n");
1282 stage
= HFC_ADOPTION
;
1286 * Aquire the vnode for this file.
1288 error
= hfs_vget(hfsmp
, key
->fileID
, &vp
, 0);
1290 /* Start a new transaction. */
1291 if (hfs_start_transaction(hfsmp
) != 0) {
1293 hfs_unlock(VTOC(vp
));
1302 if (error
== ENOENT
) {
1303 goto delete; /* stale entry, go to next */
1305 printf("hotfiles_evict: err %d getting file %d\n",
1306 error
, key
->fileID
);
1310 if (!vnode_isreg(vp
) && !vnode_islnk(vp
)) {
1311 printf("hotfiles_evict: huh, not a file %d\n", key
->fileID
);
1312 hfs_unlock(VTOC(vp
));
1314 goto delete; /* invalid entry, go to next */
1316 fileblocks
= VTOF(vp
)->ff_blocks
;
1317 if ((blksmoved
> 0) &&
1318 (blksmoved
+ fileblocks
) > HFC_BLKSPERSYNC
) {
1319 hfs_unlock(VTOC(vp
));
1324 * Make sure file is in the hot area.
1326 if (!hotextents(hfsmp
, &VTOF(vp
)->ff_extents
[0])) {
1328 printf("hotfiles_evict: file %d isn't hot!\n", key
->fileID
);
1330 hfs_unlock(VTOC(vp
));
1332 goto delete; /* stale entry, go to next */
1336 * Relocate file out of hot area.
1338 error
= hfs_relocate(vp
, HFSTOVCB(hfsmp
)->nextAllocation
, proc_ucred(p
), p
);
1340 printf("hotfiles_evict: err %d relocating file %d\n", error
, key
->fileID
);
1341 hfs_unlock(VTOC(vp
));
1343 bt_op
= kBTreeNextRecord
;
1344 goto next
; /* go to next */
1348 // We do not believe that this call to hfs_fsync() is
1349 // necessary and it causes a journal transaction
1350 // deadlock so we are removing it.
1352 // (void) hfs_fsync(vp, MNT_WAIT, 0, p);
1354 hfs_unlock(VTOC(vp
));
1357 hfsmp
->hfs_hotfile_freeblks
+= fileblocks
;
1358 listp
->hfl_reclaimblks
-= fileblocks
;
1359 if (listp
->hfl_reclaimblks
< 0)
1360 listp
->hfl_reclaimblks
= 0;
1361 blksmoved
+= fileblocks
;
1364 error
= BTDeleteRecord(filefork
, iterator
);
1366 error
= MacToVFSError(error
);
1369 savedtemp
= key
->temperature
;
1370 key
->temperature
= HFC_LOOKUPTAG
;
1371 error
= BTDeleteRecord(filefork
, iterator
);
1373 error
= MacToVFSError(error
);
1376 key
->temperature
= savedtemp
;
1378 (void) BTFlushPath(filefork
);
1380 /* Transaction complete. */
1382 hfs_end_transaction(hfsmp
);
1389 printf("hotfiles_evict: moved %d files (%d blks, %d to go)\n", filesmoved
, blksmoved
, listp
->hfl_reclaimblks
);
1391 /* Finish any outstanding transactions. */
1393 (void) BTFlushPath(filefork
);
1394 hfs_end_transaction(hfsmp
);
1397 hfs_unlock(VTOC(hfsmp
->hfc_filevp
));
1400 * Move to next stage when finished.
1402 if (listp
->hfl_reclaimblks
<= 0) {
1403 stage
= HFC_ADOPTION
;
1405 printf("hotfiles_evict: %d blocks free in hot file band\n", hfsmp
->hfs_hotfile_freeblks
);
1408 FREE(iterator
, M_TEMP
);
1409 hfsmp
->hfc_stage
= stage
;
1410 wakeup((caddr_t
)&hfsmp
->hfc_stage
);
1415 * Age the existing records in the hot files b-tree.
1418 hotfiles_age(struct hfsmount
*hfsmp
)
1420 BTreeInfoRec btinfo
;
1421 BTreeIterator
* iterator
;
1422 BTreeIterator
* prev_iterator
;
1423 FSBufferDescriptor record
;
1424 FSBufferDescriptor prev_record
;
1426 HotFileKey
* prev_key
;
1427 filefork_t
* filefork
;
1429 u_int32_t prev_data
;
1438 MALLOC(iterator
, BTreeIterator
*, 2 * sizeof(*iterator
), M_TEMP
, M_WAITOK
);
1439 bzero(iterator
, 2 * sizeof(*iterator
));
1440 key
= (HotFileKey
*) &iterator
->key
;
1442 prev_iterator
= &iterator
[1];
1443 prev_key
= (HotFileKey
*) &prev_iterator
->key
;
1445 record
.bufferAddress
= &data
;
1446 record
.itemSize
= sizeof(data
);
1447 record
.itemCount
= 1;
1448 prev_record
.bufferAddress
= &prev_data
;
1449 prev_record
.itemSize
= sizeof(prev_data
);
1450 prev_record
.itemCount
= 1;
1453 * Capture b-tree changes inside a transaction
1455 if (hfs_start_transaction(hfsmp
) != 0) {
1459 if (hfs_lock(VTOC(hfsmp
->hfc_filevp
), HFS_EXCLUSIVE_LOCK
) != 0) {
1463 filefork
= VTOF(hfsmp
->hfc_filevp
);
1465 error
= BTGetInformation(filefork
, 0, &btinfo
);
1467 error
= MacToVFSError(error
);
1470 if (btinfo
.numRecords
< 2) {
1475 /* Only want 1st half of leaf records */
1476 numrecs
= (btinfo
.numRecords
/= 2) - 1;
1478 error
= BTIterateRecord(filefork
, kBTreeFirstRecord
, iterator
, &record
, &reclen
);
1480 printf("hfs_agehotfiles: BTIterateRecord: %d\n", error
);
1481 error
= MacToVFSError(error
);
1484 bcopy(iterator
, prev_iterator
, sizeof(BTreeIterator
));
1487 for (i
= 0; i
< numrecs
; ++i
) {
1488 error
= BTIterateRecord(filefork
, kBTreeNextRecord
, iterator
, &record
, &reclen
);
1490 if (key
->temperature
< prev_key
->temperature
) {
1491 printf("hfs_agehotfiles: out of order keys!\n");
1495 if (reclen
!= sizeof(data
)) {
1496 printf("hfs_agehotfiles: invalid record length %d\n", reclen
);
1500 if (key
->keyLength
!= HFC_KEYLENGTH
) {
1501 printf("hfs_agehotfiles: invalid key length %d\n", key
->keyLength
);
1505 } else if ((error
== fsBTEndOfIterationErr
|| error
== fsBTRecordNotFoundErr
) &&
1506 (i
== (numrecs
- 1))) {
1509 printf("hfs_agehotfiles: %d of %d BTIterateRecord: %d\n", i
, numrecs
, error
);
1510 error
= MacToVFSError(error
);
1513 if (prev_key
->temperature
== HFC_LOOKUPTAG
) {
1515 printf("hfs_agehotfiles: ran into thread record\n");
1520 error
= BTDeleteRecord(filefork
, prev_iterator
);
1522 printf("hfs_agehotfiles: BTDeleteRecord failed %d (file %d)\n", error
, prev_key
->fileID
);
1523 error
= MacToVFSError(error
);
1527 /* Age by halving the temperature (floor = 4) */
1528 newtemp
= MAX(prev_key
->temperature
>> 1, 4);
1529 prev_key
->temperature
= newtemp
;
1531 error
= BTInsertRecord(filefork
, prev_iterator
, &prev_record
, prev_record
.itemSize
);
1533 printf("hfs_agehotfiles: BTInsertRecord failed %d (file %d)\n", error
, prev_key
->fileID
);
1534 error
= MacToVFSError(error
);
1539 * Update thread entry with latest temperature.
1541 prev_key
->temperature
= HFC_LOOKUPTAG
;
1542 error
= BTUpdateRecord(filefork
, prev_iterator
,
1543 (IterateCallBackProcPtr
)update_callback
,
1546 printf("hfs_agehotfiles: %d of %d BTUpdateRecord failed %d (file %d, %d)\n",
1547 i
, numrecs
, error
, prev_key
->fileID
, newtemp
);
1548 error
= MacToVFSError(error
);
1552 bcopy(iterator
, prev_iterator
, sizeof(BTreeIterator
));
1559 printf("hfs_agehotfiles: aged %d records out of %d\n", aged
, btinfo
.numRecords
);
1561 (void) BTFlushPath(filefork
);
1563 hfs_unlock(VTOC(hfsmp
->hfc_filevp
));
1565 hfs_end_transaction(hfsmp
);
1567 FREE(iterator
, M_TEMP
);
1572 * Return true if any blocks (or all blocks if all is true)
1573 * are contained in the hot file region.
1576 hotextents(struct hfsmount
*hfsmp
, HFSPlusExtentDescriptor
* extents
)
1582 for (i
= 0; i
< kHFSPlusExtentDensity
; ++i
) {
1583 b1
= extents
[i
].startBlock
;
1586 b2
= b1
+ extents
[i
].blockCount
- 1;
1587 if ((b1
>= hfsmp
->hfs_hotfile_start
&&
1588 b2
<= hfsmp
->hfs_hotfile_end
) ||
1589 (b1
< hfsmp
->hfs_hotfile_end
&&
1590 b2
> hfsmp
->hfs_hotfile_end
)) {
1600 *========================================================================
1601 * HOT FILE B-TREE ROUTINES
1602 *========================================================================
1606 * Open the hot files b-tree for writing.
1608 * On successful exit the vnode has a reference but not an iocount.
1611 hfc_btree_open(struct hfsmount
*hfsmp
, struct vnode
**vpp
)
1615 struct cat_desc cdesc
;
1616 struct cat_attr cattr
;
1617 struct cat_fork cfork
;
1618 static char filename
[] = HFC_FILENAME
;
1626 bzero(&cdesc
, sizeof(cdesc
));
1627 cdesc
.cd_parentcnid
= kRootDirID
;
1628 cdesc
.cd_nameptr
= filename
;
1629 cdesc
.cd_namelen
= strlen(filename
);
1631 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_SHARED_LOCK
);
1633 error
= cat_lookup(hfsmp
, &cdesc
, 0, &cdesc
, &cattr
, &cfork
, NULL
);
1635 hfs_systemfile_unlock(hfsmp
, lockflags
);
1638 printf("hfc_btree_open: cat_lookup error %d\n", error
);
1642 cdesc
.cd_flags
|= CD_ISMETA
;
1643 error
= hfs_getnewvnode(hfsmp
, NULL
, NULL
, &cdesc
, 0, &cattr
, &cfork
, &vp
);
1645 printf("hfc_btree_open: hfs_getnewvnode error %d\n", error
);
1646 cat_releasedesc(&cdesc
);
1649 if (!vnode_issystem(vp
)) {
1651 printf("hfc_btree_open: file has UBC, try again\n");
1653 hfs_unlock(VTOC(vp
));
1662 /* Open the B-tree file for writing... */
1663 error
= BTOpenPath(VTOF(vp
), (KeyCompareProcPtr
) hfc_comparekeys
);
1665 printf("hfc_btree_open: BTOpenPath error %d\n", error
);
1666 error
= MacToVFSError(error
);
1669 hfs_unlock(VTOC(vp
));
1672 vnode_ref(vp
); /* keep a reference while its open */
1676 if (!vnode_issystem(vp
))
1677 panic("hfc_btree_open: not a system file (vp = 0x%08x)", vp
);
1679 if (UBCINFOEXISTS(vp
))
1680 panic("hfc_btree_open: has UBCInfo (vp = 0x%08x)", vp
);
1686 * Close the hot files b-tree.
1688 * On entry the vnode has a reference.
1691 hfc_btree_close(struct hfsmount
*hfsmp
, struct vnode
*vp
)
1693 struct proc
*p
= current_proc();
1698 journal_flush(hfsmp
->jnl
);
1701 if (vnode_get(vp
) == 0) {
1702 error
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
);
1704 (void) hfs_fsync(vp
, MNT_WAIT
, 0, p
);
1705 error
= BTClosePath(VTOF(vp
));
1706 hfs_unlock(VTOC(vp
));
1717 * Create a hot files btree file.
1721 hfc_btree_create(struct hfsmount
*hfsmp
, int nodesize
, int entries
)
1723 struct vnode
*dvp
= NULL
;
1724 struct vnode
*vp
= NULL
;
1725 struct cnode
*cp
= NULL
;
1726 struct vfs_context context
;
1727 struct vnode_attr va
;
1728 struct componentname cname
;
1729 static char filename
[] = HFC_FILENAME
;
1732 context
.vc_proc
= current_proc();
1733 context
.vc_ucred
= kauth_cred_get();
1735 if (hfsmp
->hfc_filevp
)
1736 panic("hfc_btree_create: hfc_filevp exists (vp = 0x%08x)", hfsmp
->hfc_filevp
);
1738 error
= VFS_ROOT(HFSTOVFS(hfsmp
), &dvp
, &context
);
1742 cname
.cn_nameiop
= CREATE
;
1743 cname
.cn_flags
= ISLASTCN
;
1744 cname
.cn_context
= &context
;
1745 cname
.cn_pnbuf
= filename
;
1746 cname
.cn_pnlen
= sizeof(filename
);
1747 cname
.cn_nameptr
= filename
;
1748 cname
.cn_namelen
= strlen(filename
);
1750 cname
.cn_consume
= 0;
1753 VATTR_SET(&va
, va_type
, VREG
);
1754 VATTR_SET(&va
, va_mode
, S_IFREG
| S_IRUSR
| S_IWUSR
);
1755 VATTR_SET(&va
, va_uid
, 0);
1756 VATTR_SET(&va
, va_gid
, 0);
1758 /* call ourselves directly, ignore the higher-level VFS file creation code */
1759 error
= VNOP_CREATE(dvp
, &vp
, &cname
, &va
, &context
);
1761 printf("HFS: error %d creating HFBT on %s\n", error
, HFSTOVCB(hfsmp
)->vcbVN
);
1768 if ((error
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
))) {
1773 /* Don't use non-regular files or files with links. */
1774 if (!vnode_isreg(vp
) || cp
->c_nlink
!= 1) {
1779 printf("HFS: created HFBT on %s\n", HFSTOVCB(hfsmp
)->vcbVN
);
1781 if (VTOF(vp
)->ff_size
< (u_int64_t
)nodesize
) {
1785 BTNodeDescriptor
*ndp
;
1787 HotFilesInfo
*hotfileinfo
;
1793 * Mark it invisible (truncate will pull these changes).
1795 ((FndrFileInfo
*)&cp
->c_finderinfo
[0])->fdFlags
|=
1796 SWAP_BE16 (kIsInvisible
+ kNameLocked
);
1798 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&buffer
, nodesize
)) {
1802 bzero(buffer
, nodesize
);
1803 index
= (int16_t *)buffer
;
1805 entirespernode
= (nodesize
- sizeof(BTNodeDescriptor
) - 2) /
1806 (sizeof(HotFileKey
) + 6);
1807 nodecnt
= 2 + howmany(entries
* 2, entirespernode
);
1808 nodecnt
= roundup(nodecnt
, 8);
1809 filesize
= nodecnt
* nodesize
;
1811 /* FILL IN THE NODE DESCRIPTOR: */
1812 ndp
= (BTNodeDescriptor
*)buffer
;
1813 ndp
->kind
= kBTHeaderNode
;
1814 ndp
->numRecords
= SWAP_BE16 (3);
1815 offset
= sizeof(BTNodeDescriptor
);
1816 index
[(nodesize
/ 2) - 1] = SWAP_BE16 (offset
);
1818 /* FILL IN THE HEADER RECORD: */
1819 bthp
= (BTHeaderRec
*)((UInt8
*)buffer
+ offset
);
1820 bthp
->nodeSize
= SWAP_BE16 (nodesize
);
1821 bthp
->totalNodes
= SWAP_BE32 (filesize
/ nodesize
);
1822 bthp
->freeNodes
= SWAP_BE32 (nodecnt
- 1);
1823 bthp
->clumpSize
= SWAP_BE32 (filesize
);
1824 bthp
->btreeType
= kUserBTreeType
; /* non-metadata */
1825 bthp
->attributes
|= SWAP_BE32 (kBTBigKeysMask
);
1826 bthp
->maxKeyLength
= SWAP_BE16 (HFC_KEYLENGTH
);
1827 offset
+= sizeof(BTHeaderRec
);
1828 index
[(nodesize
/ 2) - 2] = SWAP_BE16 (offset
);
1830 /* FILL IN THE USER RECORD: */
1831 hotfileinfo
= (HotFilesInfo
*)((UInt8
*)buffer
+ offset
);
1832 hotfileinfo
->magic
= SWAP_BE32 (HFC_MAGIC
);
1833 hotfileinfo
->version
= SWAP_BE32 (HFC_VERSION
);
1834 hotfileinfo
->duration
= SWAP_BE32 (HFC_DEFAULT_DURATION
);
1835 hotfileinfo
->timebase
= 0;
1836 hotfileinfo
->timeleft
= 0;
1837 hotfileinfo
->threshold
= SWAP_BE32 (HFC_MINIMUM_TEMPERATURE
);
1838 hotfileinfo
->maxfileblks
= SWAP_BE32 (HFC_MAXIMUM_FILESIZE
/ HFSTOVCB(hfsmp
)->blockSize
);
1839 hotfileinfo
->maxfilecnt
= SWAP_BE32 (HFC_DEFAULT_FILE_COUNT
);
1840 strcpy(hotfileinfo
->tag
, hfc_tag
);
1841 offset
+= kBTreeHeaderUserBytes
;
1842 index
[(nodesize
/ 2) - 3] = SWAP_BE16 (offset
);
1844 /* FILL IN THE MAP RECORD (only one node in use). */
1845 *((u_int8_t
*)buffer
+ offset
) = 0x80;
1846 offset
+= nodesize
- sizeof(BTNodeDescriptor
) - sizeof(BTHeaderRec
)
1847 - kBTreeHeaderUserBytes
- (4 * sizeof(int16_t));
1848 index
[(nodesize
/ 2) - 4] = SWAP_BE16 (offset
);
1850 vnode_setnoflush(vp
);
1851 error
= hfs_truncate(vp
, (off_t
)filesize
, IO_NDELAY
, 0, &context
);
1853 printf("HFS: error %d growing HFBT on %s\n", error
, HFSTOVCB(hfsmp
)->vcbVN
);
1856 cp
->c_flag
|= C_ZFWANTSYNC
;
1857 cp
->c_zftimeout
= 1;
1860 struct vnop_write_args args
;
1863 auio
= uio_create(1, 0, UIO_SYSSPACE32
, UIO_WRITE
);
1864 uio_addiov(auio
, (uintptr_t)buffer
, nodesize
);
1866 args
.a_desc
= &vnop_write_desc
;
1870 args
.a_context
= &context
;
1875 error
= hfs_vnop_write(&args
);
1877 printf("HFS: error %d writing HFBT on %s\n", error
, HFSTOVCB(hfsmp
)->vcbVN
);
1881 kmem_free(kernel_map
, (vm_offset_t
)buffer
, nodesize
);
1897 * Compare two hot file b-tree keys.
1899 * Result: +n search key > trial key
1900 * 0 search key = trial key
1901 * -n search key < trial key
1904 hfc_comparekeys(HotFileKey
*searchKey
, HotFileKey
*trialKey
)
1907 * Compared temperatures first.
1909 if (searchKey
->temperature
== trialKey
->temperature
) {
1911 * Temperatures are equal so compare file ids.
1913 if (searchKey
->fileID
== trialKey
->fileID
) {
1915 * File ids are equal so compare fork types.
1917 if (searchKey
->forkType
== trialKey
->forkType
) {
1919 } else if (searchKey
->forkType
> trialKey
->forkType
) {
1922 } else if (searchKey
->fileID
> trialKey
->fileID
) {
1925 } else if (searchKey
->temperature
> trialKey
->temperature
) {
1934 *========================================================================
1935 * HOT FILE DATA COLLECTING ROUTINES
1936 *========================================================================
1940 * Lookup a hot file entry in the tree.
1943 static hotfile_entry_t
*
1944 hf_lookup(hotfile_data_t
*hotdata
, u_int32_t fileid
, u_int32_t temperature
)
1946 hotfile_entry_t
*entry
= hotdata
->rootentry
;
1949 entry
->temperature
!= temperature
&&
1950 entry
->fileid
!= fileid
) {
1952 if (temperature
> entry
->temperature
)
1953 entry
= entry
->right
;
1954 else if (temperature
< entry
->temperature
)
1955 entry
= entry
->left
;
1956 else if (fileid
> entry
->fileid
)
1957 entry
= entry
->right
;
1959 entry
= entry
->left
;
1966 * Insert a hot file entry into the tree.
1969 hf_insert(hotfile_data_t
*hotdata
, hotfile_entry_t
*newentry
)
1971 hotfile_entry_t
*entry
= hotdata
->rootentry
;
1972 u_int32_t fileid
= newentry
->fileid
;
1973 u_int32_t temperature
= newentry
->temperature
;
1975 if (entry
== NULL
) {
1976 hotdata
->rootentry
= newentry
;
1977 hotdata
->coldest
= newentry
;
1978 hotdata
->activefiles
++;
1983 if (temperature
> entry
->temperature
) {
1985 entry
= entry
->right
;
1987 entry
->right
= newentry
;
1990 } else if (temperature
< entry
->temperature
) {
1992 entry
= entry
->left
;
1994 entry
->left
= newentry
;
1997 } else if (fileid
> entry
->fileid
) {
1999 entry
= entry
->right
;
2001 if (entry
->fileid
!= fileid
)
2002 entry
->right
= newentry
;
2007 entry
= entry
->left
;
2009 if (entry
->fileid
!= fileid
)
2010 entry
->left
= newentry
;
2016 hotdata
->activefiles
++;
2020 * Find the coldest entry in the tree.
2022 static hotfile_entry_t
*
2023 hf_coldest(hotfile_data_t
*hotdata
)
2025 hotfile_entry_t
*entry
= hotdata
->rootentry
;
2029 entry
= entry
->left
;
2035 * Find the hottest entry in the tree.
2037 static hotfile_entry_t
*
2038 hf_hottest(hotfile_data_t
*hotdata
)
2040 hotfile_entry_t
*entry
= hotdata
->rootentry
;
2043 while (entry
->right
)
2044 entry
= entry
->right
;
2050 * Delete a hot file entry from the tree.
2053 hf_delete(hotfile_data_t
*hotdata
, u_int32_t fileid
, u_int32_t temperature
)
2055 hotfile_entry_t
*entry
, *parent
, *next
;
2058 entry
= hotdata
->rootentry
;
2061 entry
->temperature
!= temperature
&&
2062 entry
->fileid
!= fileid
) {
2065 if (temperature
> entry
->temperature
)
2066 entry
= entry
->right
;
2067 else if (temperature
< entry
->temperature
)
2068 entry
= entry
->left
;
2069 else if (fileid
> entry
->fileid
)
2070 entry
= entry
->right
;
2072 entry
= entry
->left
;
2077 * Reorginize the sub-trees spanning from our entry.
2079 if ((next
= entry
->right
)) {
2080 hotfile_entry_t
*pnextl
, *psub
;
2082 * Tree pruning: take the left branch of the
2083 * current entry and place it at the lowest
2084 * left branch of the current right branch
2088 /* Walk the Right/Left sub tree from current entry */
2089 while ((pnextl
= psub
->left
))
2092 /* Plug the old left tree to the new ->Right leftmost entry */
2093 psub
->left
= entry
->left
;
2095 } else /* only left sub-tree, simple case */ {
2099 * Now, plug the current entry sub tree to
2100 * the good pointer of our parent entry.
2103 hotdata
->rootentry
= next
;
2104 else if (parent
->left
== entry
)
2105 parent
->left
= next
;
2107 parent
->right
= next
;
2109 /* Place entry back on the free-list */
2112 entry
->temperature
= 0;
2114 entry
->right
= hotdata
->freelist
;
2115 hotdata
->freelist
= entry
;
2116 hotdata
->activefiles
--;
2118 if (hotdata
->coldest
== entry
|| hotdata
->coldest
== NULL
) {
2119 hotdata
->coldest
= hf_coldest(hotdata
);
2126 * Get a free hot file entry.
2128 static hotfile_entry_t
*
2129 hf_getnewentry(hotfile_data_t
*hotdata
)
2131 hotfile_entry_t
* entry
;
2134 * When the free list is empty then steal the coldest one
2136 if (hotdata
->freelist
== NULL
) {
2137 entry
= hf_coldest(hotdata
);
2138 hf_delete(hotdata
, entry
->fileid
, entry
->temperature
);
2140 entry
= hotdata
->freelist
;
2141 hotdata
->freelist
= entry
->right
;
2149 * Generate a sorted list of hot files (hottest to coldest).
2151 * As a side effect, every node in the hot file tree will be
2152 * deleted (moved to the free list).
2155 hf_getsortedlist(hotfile_data_t
* hotdata
, hotfilelist_t
*sortedlist
)
2158 hotfile_entry_t
*entry
;
2160 while ((entry
= hf_hottest(hotdata
)) != NULL
) {
2161 sortedlist
->hfl_hotfile
[i
].hf_fileid
= entry
->fileid
;
2162 sortedlist
->hfl_hotfile
[i
].hf_temperature
= entry
->temperature
;
2163 sortedlist
->hfl_hotfile
[i
].hf_blocks
= entry
->blocks
;
2164 sortedlist
->hfl_totalblocks
+= entry
->blocks
;
2167 hf_delete(hotdata
, entry
->fileid
, entry
->temperature
);
2170 sortedlist
->hfl_count
= i
;
2173 printf("HFS: hf_getsortedlist returned %d entries\n", i
);
2180 hf_maxdepth(hotfile_entry_t
* root
, int depth
, int *maxdepth
)
2184 if (depth
> *maxdepth
)
2186 hf_maxdepth(root
->left
, depth
, maxdepth
);
2187 hf_maxdepth(root
->right
, depth
, maxdepth
);
2192 hf_printtree(hotfile_entry_t
* root
)
2195 hf_printtree(root
->left
);
2196 printf("temperature: % 8d, fileid %d\n", root
->temperature
, root
->fileid
);
2197 hf_printtree(root
->right
);