2 * Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
23 * @APPLE_LICENSE_HEADER_END@
26 #include <sys/param.h>
27 #include <sys/systm.h>
28 #include <sys/fcntl.h>
29 #include <sys/kernel.h>
30 #include <sys/malloc.h>
32 #include <sys/vnode.h>
35 #include <hfs/hfs_endian.h>
36 #include <hfs/hfs_format.h>
37 #include <hfs/hfs_mount.h>
38 #include <hfs/hfs_hotfiles.h>
40 #include "hfscommon/headers/BTreeScanner.h"
49 * Hot File List (runtime).
51 typedef struct hotfileinfo
{
53 u_int32_t hf_temperature
;
57 typedef struct hotfilelist
{
59 u_int32_t hfl_version
;
60 time_t hfl_duration
; /* duration of sample period */
61 int hfl_count
; /* count of hot files recorded */
62 int hfl_next
; /* next file to move */
63 int hfl_totalblocks
; /* total hot file blocks */
64 int hfl_reclaimblks
; /* blocks to reclaim in HFV */
65 u_int32_t hfl_spare
[2];
66 hotfileinfo_t hfl_hotfile
[1]; /* array of hot files */
71 * Hot File Entry (runtime).
73 typedef struct hotfile_entry
{
74 struct hotfile_entry
*left
;
75 struct hotfile_entry
*right
;
77 u_int32_t temperature
;
82 * Hot File Recording Data (runtime).
84 typedef struct hotfile_data
{
85 struct hfsmount
*hfsmp
;
87 int activefiles
; /* active number of hot files */
90 hotfile_entry_t
*rootentry
;
91 hotfile_entry_t
*freelist
;
92 hotfile_entry_t
*coldest
;
93 hotfile_entry_t entries
[1];
99 * Hot File Data recording functions (in-memory binary tree).
101 static void hf_insert (hotfile_data_t
*, hotfile_entry_t
*);
102 static void hf_delete (hotfile_data_t
*, u_int32_t
, u_int32_t
);
103 static hotfile_entry_t
* hf_lookup (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 int hf_getsortedlist (hotfile_data_t
*, hotfilelist_t
*);
107 static void hf_printtree (hotfile_entry_t
*);
110 * Hot File misc support functions.
112 static int hotfiles_collect (struct hfsmount
*, struct proc
*);
113 static int hotfiles_age (struct hfsmount
*, struct proc
*);
114 static int hotfiles_adopt (struct hfsmount
*, struct proc
*);
115 static int hotfiles_evict (struct hfsmount
*, struct proc
*);
116 static int hotfiles_refine (struct hfsmount
*, struct proc
*);
117 static int hotextents(struct hfsmount
*, HFSPlusExtentDescriptor
*);
120 * Hot File Cluster B-tree (on disk) functions.
122 static int hfc_btree_create (struct hfsmount
*, int, int);
123 static int hfc_btree_open (struct hfsmount
*, struct vnode
**);
124 static int hfc_btree_close (struct hfsmount
*, struct vnode
*);
125 static int hfc_comparekeys (HotFileKey
*, HotFileKey
*);
128 char hfc_tag
[] = "CLUSTERED HOT FILES B-TREE ";
132 *========================================================================
133 * HOT FILE INTERFACE ROUTINES
134 *========================================================================
138 * Start recording the hotest files on a file system.
143 hfs_recording_start(struct hfsmount
*hfsmp
, struct proc
*p
)
145 hotfile_data_t
*hotdata
;
151 if ((hfsmp
->hfs_flags
& HFS_READ_ONLY
) ||
152 (hfsmp
->jnl
== NULL
) ||
153 (hfsmp
->hfs_flags
& HFS_METADATA_ZONE
) == 0) {
156 if (HFSTOVCB(hfsmp
)->freeBlocks
< (2 * hfsmp
->hfs_hotfile_maxblks
)) {
159 if (hfsmp
->hfc_stage
!= HFC_IDLE
) {
162 hfsmp
->hfc_stage
= HFC_BUSY
;
165 * Dump previous recording data.
167 if (hfsmp
->hfc_recdata
) {
170 tmp
= hfsmp
->hfc_recdata
;
171 hfsmp
->hfc_recdata
= NULL
;
176 * On first startup check for suspended recording.
178 if (hfsmp
->hfc_timebase
== 0 &&
179 hfc_btree_open(hfsmp
, &hfsmp
->hfc_filevp
) == 0) {
180 HotFilesInfo hotfileinfo
;
182 if ((BTGetUserData(VTOF(hfsmp
->hfc_filevp
), &hotfileinfo
,
183 sizeof(hotfileinfo
)) == 0) &&
184 (SWAP_BE32 (hotfileinfo
.magic
) == HFC_MAGIC
) &&
185 (SWAP_BE32 (hotfileinfo
.timeleft
) > 0) &&
186 (SWAP_BE32 (hotfileinfo
.timebase
) > 0)) {
187 hfsmp
->hfc_maxfiles
= SWAP_BE32 (hotfileinfo
.maxfilecnt
);
188 hfsmp
->hfc_timeout
= SWAP_BE32 (hotfileinfo
.timeleft
) + time
.tv_sec
;
189 hfsmp
->hfc_timebase
= SWAP_BE32 (hotfileinfo
.timebase
);
191 printf("HFS: resume recording hot files (%d left)\n", SWAP_BE32 (hotfileinfo
.timeleft
));
194 hfsmp
->hfc_maxfiles
= HFC_DEFAULT_FILE_COUNT
;
195 hfsmp
->hfc_timebase
= time
.tv_sec
+ 1;
196 hfsmp
->hfc_timeout
= hfsmp
->hfc_timebase
+ HFC_DEFAULT_DURATION
;
198 (void) hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
199 hfsmp
->hfc_filevp
= NULL
;
201 struct cat_attr cattr
;
205 * Make sure a btree file exists.
207 cnid
= GetFileInfo(HFSTOVCB(hfsmp
), kRootDirID
, HFC_FILENAME
, &cattr
, NULL
);
209 !S_ISREG(cattr
.ca_mode
) &&
210 (error
= hfc_btree_create(hfsmp
, HFSTOVCB(hfsmp
)->blockSize
, HFC_DEFAULT_FILE_COUNT
))) {
211 hfsmp
->hfc_stage
= HFC_IDLE
;
212 wakeup((caddr_t
)&hfsmp
->hfc_stage
);
216 printf("HFS: begin recording hot files\n");
218 hfsmp
->hfc_maxfiles
= HFC_DEFAULT_FILE_COUNT
;
219 hfsmp
->hfc_timeout
= time
.tv_sec
+ HFC_DEFAULT_DURATION
;
221 /* Reset time base. */
222 if (hfsmp
->hfc_timebase
== 0) {
223 hfsmp
->hfc_timebase
= time
.tv_sec
+ 1;
225 u_int32_t cumulativebase
;
226 u_int32_t oldbase
= hfsmp
->hfc_timebase
;
228 cumulativebase
= hfsmp
->hfc_timeout
- (HFC_CUMULATIVE_CYCLES
* HFC_DEFAULT_DURATION
);
229 hfsmp
->hfc_timebase
= MAX(hfsmp
->hfc_timebase
, cumulativebase
);
233 if ((hfsmp
->hfc_maxfiles
== 0) ||
234 (hfsmp
->hfc_maxfiles
> HFC_MAXIMUM_FILE_COUNT
)) {
235 hfsmp
->hfc_maxfiles
= HFC_DEFAULT_FILE_COUNT
;
237 maxentries
= hfsmp
->hfc_maxfiles
;
239 size
= sizeof(hotfile_data_t
) + (maxentries
* sizeof(hotfile_entry_t
));
240 MALLOC(hotdata
, hotfile_data_t
*, size
, M_TEMP
, M_WAITOK
);
241 bzero(hotdata
, size
);
243 for (i
= 1; i
< maxentries
; i
++)
244 hotdata
->entries
[i
-1].right
= &hotdata
->entries
[i
];
246 hotdata
->freelist
= &hotdata
->entries
[0];
248 * Establish minimum temperature and maximum file size.
250 hotdata
->threshold
= HFC_MINIMUM_TEMPERATURE
;
251 hotdata
->maxblocks
= HFC_MAXIMUM_FILESIZE
/ HFSTOVCB(hfsmp
)->blockSize
;
252 hotdata
->hfsmp
= hfsmp
;
254 hfsmp
->hfc_recdata
= hotdata
;
256 hfsmp
->hfc_stage
= HFC_RECORDING
;
257 wakeup((caddr_t
)&hfsmp
->hfc_stage
);
262 * Stop recording the hotest files on a file system.
266 hfs_recording_stop(struct hfsmount
*hfsmp
, struct proc
*p
)
268 hotfile_data_t
*hotdata
;
269 hotfilelist_t
*listp
;
271 enum hfc_stage newstage
= HFC_IDLE
;
276 if (hfsmp
->hfc_stage
!= HFC_RECORDING
)
279 hotfiles_collect(hfsmp
, p
);
281 if (hfsmp
->hfc_stage
!= HFC_RECORDING
)
284 hfsmp
->hfc_stage
= HFC_BUSY
;
287 * Convert hot file data into a simple file id list....
289 * then dump the sample data
292 printf("HFS: end of hot file recording\n");
294 hotdata
= (hotfile_data_t
*)hfsmp
->hfc_recdata
;
297 hfsmp
->hfc_recdata
= NULL
;
298 hfsmp
->hfc_stage
= HFC_EVALUATION
;
299 wakeup((caddr_t
)&hfsmp
->hfc_stage
);
302 printf(" curentries: %d\n", hotdata
->activefiles
);
305 * If no hot files recorded then we're done.
307 if (hotdata
->rootentry
== NULL
) {
312 /* Open the B-tree file for writing... */
313 if (hfsmp
->hfc_filevp
)
314 panic("hfs_recording_stop: hfc_filevp exists (vp = 0x%08x)", hfsmp
->hfc_filevp
);
316 error
= hfc_btree_open(hfsmp
, &hfsmp
->hfc_filevp
);
322 * Age the previous set of clustered hot files.
324 error
= hotfiles_age(hfsmp
, p
);
326 (void) hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
327 hfsmp
->hfc_filevp
= NULL
;
332 * Create a sorted list of hotest files.
334 size
= sizeof(hotfilelist_t
);
335 size
+= sizeof(hotfileinfo_t
) * (hotdata
->activefiles
- 1);
336 MALLOC(listp
, hotfilelist_t
*, size
, M_TEMP
, M_WAITOK
);
339 hf_getsortedlist(hotdata
, listp
);
340 listp
->hfl_duration
= time
.tv_sec
- hfsmp
->hfc_timebase
;
341 hfsmp
->hfc_recdata
= listp
;
344 * Account for duplicates.
346 error
= hotfiles_refine(hfsmp
, p
);
348 (void) hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
349 hfsmp
->hfc_filevp
= NULL
;
354 * Compute the amount of space to reclaim...
356 if (listp
->hfl_totalblocks
> hfsmp
->hfs_hotfile_freeblks
) {
357 listp
->hfl_reclaimblks
=
358 MIN(listp
->hfl_totalblocks
, hfsmp
->hfs_hotfile_maxblks
) -
359 hfsmp
->hfs_hotfile_freeblks
;
361 printf("hfs_recording_stop: need to reclaim %d blocks\n", listp
->hfl_reclaimblks
);
363 if (listp
->hfl_reclaimblks
)
364 newstage
= HFC_EVICTION
;
366 newstage
= HFC_ADOPTION
;
368 newstage
= HFC_ADOPTION
;
371 if (newstage
== HFC_ADOPTION
&& listp
->hfl_totalblocks
== 0) {
372 (void) hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
373 hfsmp
->hfc_filevp
= NULL
;
378 if (newstage
== HFC_EVICTION
)
379 printf("HFS: evicting coldest files\n");
380 else if (newstage
== HFC_ADOPTION
)
381 printf("HFS: adopting hotest files\n");
383 FREE(hotdata
, M_TEMP
);
385 hfsmp
->hfc_stage
= newstage
;
386 wakeup((caddr_t
)&hfsmp
->hfc_stage
);
391 * Suspend recording the hotest files on a file system.
395 hfs_recording_suspend(struct hfsmount
*hfsmp
, struct proc
*p
)
397 HotFilesInfo hotfileinfo
;
398 hotfile_data_t
*hotdata
;
401 if (hfsmp
->hfc_stage
!= HFC_RECORDING
)
404 hotdata
= (hotfile_data_t
*)hfsmp
->hfc_recdata
;
405 if (hotdata
== NULL
) {
406 hfsmp
->hfc_stage
= HFC_DISABLED
;
409 hfsmp
->hfc_stage
= HFC_BUSY
;
412 printf("HFS: suspend hot file recording\n");
414 error
= hfc_btree_open(hfsmp
, &hfsmp
->hfc_filevp
);
416 printf("hfs_recording_suspend: err %d opening btree\n", error
);
420 hfs_global_shared_lock_acquire(hfsmp
);
422 if (journal_start_transaction(hfsmp
->jnl
) != 0) {
423 hfs_global_shared_lock_release(hfsmp
);
428 vn_lock(hfsmp
->hfc_filevp
, LK_EXCLUSIVE
| LK_RETRY
, p
);
430 hotfileinfo
.magic
= SWAP_BE32 (HFC_MAGIC
);
431 hotfileinfo
.version
= SWAP_BE32 (HFC_VERSION
);
432 hotfileinfo
.duration
= SWAP_BE32 (HFC_DEFAULT_DURATION
);
433 hotfileinfo
.timebase
= SWAP_BE32 (hfsmp
->hfc_timebase
);
434 hotfileinfo
.timeleft
= SWAP_BE32 (hfsmp
->hfc_timeout
- time
.tv_sec
);
435 hotfileinfo
.threshold
= SWAP_BE32 (hotdata
->threshold
);
436 hotfileinfo
.maxfileblks
= SWAP_BE32 (hotdata
->maxblocks
);
437 hotfileinfo
.maxfilecnt
= SWAP_BE32 (HFC_DEFAULT_FILE_COUNT
);
438 strcpy(hotfileinfo
.tag
, hfc_tag
);
439 (void) BTSetUserData(VTOF(hfsmp
->hfc_filevp
), &hotfileinfo
, sizeof(hotfileinfo
));
441 (void) VOP_UNLOCK(hfsmp
->hfc_filevp
, 0, p
);
443 journal_end_transaction(hfsmp
->jnl
);
445 hfs_global_shared_lock_release(hfsmp
);
447 (void) hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
448 hfsmp
->hfc_filevp
= NULL
;
450 FREE(hotdata
, M_TEMP
);
452 hfsmp
->hfc_stage
= HFC_DISABLED
;
453 wakeup((caddr_t
)&hfsmp
->hfc_stage
);
458 * Abort a hot file recording session.
462 hfs_recording_abort(struct hfsmount
*hfsmp
, struct proc
*p
)
466 if (hfsmp
->hfc_stage
== HFC_DISABLED
)
469 if (hfsmp
->hfc_stage
== HFC_BUSY
) {
470 (void) tsleep((caddr_t
)&hfsmp
->hfc_stage
, PINOD
, "hfs_recording_abort", 0);
472 hfsmp
->hfc_stage
= HFC_BUSY
;
474 printf("HFS: terminate hot file recording\n");
476 if (hfsmp
->hfc_recdata
) {
477 tmp
= hfsmp
->hfc_recdata
;
478 hfsmp
->hfc_recdata
= NULL
;
481 hfsmp
->hfc_stage
= HFC_DISABLED
;
482 wakeup((caddr_t
)&hfsmp
->hfc_stage
);
491 hfs_recording_init(struct hfsmount
*hfsmp
, struct proc
*p
)
494 CatalogRecord
* datap
;
496 HFSPlusCatalogFile
*filep
;
497 BTScanState scanstate
;
498 BTreeIterator
* iterator
;
499 FSBufferDescriptor record
;
501 filefork_t
* filefork
;
503 struct cat_attr cattr
;
507 int inserted
= 0; /* debug variables */
511 * If the Hot File btree exists then metadata zone is ready.
513 cnid
= GetFileInfo(HFSTOVCB(hfsmp
), kRootDirID
, HFC_FILENAME
, &cattr
, NULL
);
514 if (cnid
!= 0 && S_ISREG(cattr
.ca_mode
)) {
515 if (hfsmp
->hfc_stage
== HFC_DISABLED
)
516 hfsmp
->hfc_stage
= HFC_IDLE
;
520 * For now, only the boot volume is supported.
522 if ((HFSTOVFS(hfsmp
)->mnt_flag
& MNT_ROOTFS
) == 0) {
523 hfsmp
->hfs_flags
&= ~HFS_METADATA_ZONE
;
526 error
= hfc_btree_create(hfsmp
, HFSTOVCB(hfsmp
)->blockSize
, HFC_DEFAULT_FILE_COUNT
);
531 * Open the Hot File B-tree file for writing.
533 if (hfsmp
->hfc_filevp
)
534 panic("hfs_recording_init: hfc_filevp exists (vp = 0x%08x)", hfsmp
->hfc_filevp
);
535 error
= hfc_btree_open(hfsmp
, &hfsmp
->hfc_filevp
);
539 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
540 bzero(iterator
, sizeof(*iterator
));
541 key
= (HotFileKey
*) &iterator
->key
;
542 key
->keyLength
= HFC_KEYLENGTH
;
544 record
.bufferAddress
= &data
;
545 record
.itemSize
= sizeof(u_int32_t
);
546 record
.itemCount
= 1;
548 printf("Evaluating space for \"%s\" metadata zone...\n", HFSTOVCB(hfsmp
)->vcbVN
);
551 * Get ready to scan the Catalog file.
553 error
= BTScanInitialize(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), 0, 0, 0,
554 kCatSearchBufferSize
, &scanstate
);
556 printf("hfs_recording_init: err %d BTScanInit\n", error
);
561 * The writes to Hot File B-tree file are journaled.
563 hfs_global_shared_lock_acquire(hfsmp
);
565 if (journal_start_transaction(hfsmp
->jnl
) != 0) {
566 hfs_global_shared_lock_release(hfsmp
);
571 vn_lock(hfsmp
->hfc_filevp
, LK_EXCLUSIVE
| LK_RETRY
, p
);
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
, sizeof(data
));
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
, sizeof(data
));
635 printf("hfs_recording_init: BTInsertRecord failed %d (fileid %d)\n", error
, key
->fileID
);
636 error
= MacToVFSError(error
);
641 (void) BTFlushPath(filefork
);
642 (void) VOP_UNLOCK(hfsmp
->hfc_filevp
, 0, p
);
645 journal_end_transaction(hfsmp
->jnl
);
647 hfs_global_shared_lock_release(hfsmp
);
649 printf("%d files identified out of %d\n", inserted
, filecount
);
653 (void) BTScanTerminate(&scanstate
, &data
, &data
, &data
);
655 FREE(iterator
, M_TEMP
);
656 if (hfsmp
->hfc_filevp
) {
657 (void) hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
658 hfsmp
->hfc_filevp
= NULL
;
661 hfsmp
->hfc_stage
= HFC_IDLE
;
667 * Use sync to perform ocassional background work.
671 hfs_hotfilesync(struct hfsmount
*hfsmp
, struct proc
*p
)
673 if ((HFSTOVFS(hfsmp
)->mnt_kern_flag
& MNTK_UNMOUNT
) == 0 && hfsmp
->hfc_stage
) {
674 switch (hfsmp
->hfc_stage
) {
676 (void) hfs_recording_start(hfsmp
, p
);
680 if (time
.tv_sec
> hfsmp
->hfc_timeout
)
681 (void) hfs_recording_stop(hfsmp
, p
);
685 (void) hotfiles_evict(hfsmp
, p
);
689 (void) hotfiles_adopt(hfsmp
, p
);
697 * Add a hot file to the recording list.
699 * This can happen when a hot file gets reclaimed or at the
700 * end of the recording period for any active hot file.
702 * NOTE: Since both the data and resource fork can be hot,
703 * there can be two entries for the same file id.
708 hfs_addhotfile(struct vnode
*vp
)
710 hotfile_data_t
*hotdata
;
711 hotfile_entry_t
*entry
;
715 u_int32_t temperature
;
718 if (hfsmp
->hfc_stage
!= HFC_RECORDING
)
721 if (!(vp
->v_type
== VREG
|| vp
->v_type
== VLNK
) ||
722 (vp
->v_flag
& (VSYSTEM
| VSWAP
))) {
725 /* Skip resource forks for now. */
726 if (VNODE_IS_RSRC(vp
)) {
729 if ((hotdata
= (hotfile_data_t
*)hfsmp
->hfc_recdata
) == NULL
) {
735 if ((ffp
->ff_bytesread
== 0) ||
736 (ffp
->ff_blocks
== 0) ||
737 (ffp
->ff_blocks
> hotdata
->maxblocks
) ||
738 (cp
->c_flag
& (C_DELETED
| C_NOEXISTS
)) ||
739 (cp
->c_flags
& UF_NODUMP
) ||
740 (cp
->c_atime
< hfsmp
->hfc_timebase
)) {
744 temperature
= ffp
->ff_bytesread
/ ffp
->ff_size
;
745 if (temperature
< hotdata
->threshold
) {
749 * If there is room or this file is hotter than
750 * the coldest one then add it to the list.
753 if ((hotdata
->activefiles
< hfsmp
->hfc_maxfiles
) ||
754 (hotdata
->coldest
== NULL
) ||
755 (temperature
> hotdata
->coldest
->temperature
)) {
757 entry
= hf_getnewentry(hotdata
);
758 entry
->temperature
= temperature
;
759 entry
->fileid
= cp
->c_fileid
;
760 entry
->blocks
= ffp
->ff_blocks
;
761 hf_insert(hotdata
, entry
);
769 * Remove a hot file to the recording list.
771 * This can happen when a hot file becomes
772 * an active vnode (active hot files are
773 * not kept in the recording list until the
774 * end of the recording period).
779 hfs_removehotfile(struct vnode
*vp
)
781 hotfile_data_t
*hotdata
;
785 u_int32_t temperature
;
788 if (hfsmp
->hfc_stage
!= HFC_RECORDING
)
791 if (!(vp
->v_type
== VREG
|| vp
->v_type
== VLNK
) ||
792 (vp
->v_flag
& (VSYSTEM
| VSWAP
))) {
795 if ((hotdata
= (hotfile_data_t
*)hfsmp
->hfc_recdata
) == NULL
)
801 if ((ffp
->ff_bytesread
== 0) || (ffp
->ff_blocks
== 0) ||
802 (cp
->c_atime
< hfsmp
->hfc_timebase
)) {
806 temperature
= ffp
->ff_bytesread
/ ffp
->ff_size
;
807 if (temperature
< hotdata
->threshold
)
810 if (hotdata
->coldest
&& (temperature
>= hotdata
->coldest
->temperature
)) {
812 hf_delete(hotdata
, VTOC(vp
)->c_fileid
, temperature
);
821 *========================================================================
822 * HOT FILE MAINTENANCE ROUTINES
823 *========================================================================
827 * Add all active hot files to the recording list.
830 hotfiles_collect(struct hfsmount
*hfsmp
, struct proc
*p
)
832 struct mount
*mp
= HFSTOVFS(hfsmp
);
833 struct vnode
*nvp
, *vp
;
837 if (vfs_busy(mp
, LK_NOWAIT
, 0, p
))
840 simple_lock(&mntvnode_slock
);
841 for (vp
= mp
->mnt_vnodelist
.lh_first
; vp
!= NULL
; vp
= nvp
) {
842 if (vp
->v_mount
!= mp
) {
843 simple_unlock(&mntvnode_slock
);
846 simple_lock(&vp
->v_interlock
);
847 nvp
= vp
->v_mntvnodes
.le_next
;
849 if ((vp
->v_flag
& VSYSTEM
) ||
850 !(vp
->v_type
== VREG
|| vp
->v_type
== VLNK
)) {
851 simple_unlock(&vp
->v_interlock
);
856 if (cp
== NULL
|| vp
->v_flag
& (VXLOCK
|VORECLAIM
)) {
857 simple_unlock(&vp
->v_interlock
);
861 simple_unlock(&mntvnode_slock
);
862 error
= vget(vp
, LK_EXCLUSIVE
| LK_NOWAIT
| LK_INTERLOCK
, p
);
866 simple_lock(&mntvnode_slock
);
869 (void) hfs_addhotfile(vp
);
872 simple_lock(&mntvnode_slock
);
875 simple_unlock(&mntvnode_slock
);
884 * Update the data of a btree record
885 * This is called from within BTUpdateRecord.
888 update_callback(const HotFileKey
*key
, u_int32_t
*data
, u_int16_t datalen
, u_int32_t
*state
)
890 if (key
->temperature
== HFC_LOOKUPTAG
)
896 * Identify files already in hot area.
899 hotfiles_refine(struct hfsmount
*hfsmp
, struct proc
*p
)
901 BTreeIterator
* iterator
;
904 filefork_t
* filefork
;
905 hotfilelist_t
*listp
;
906 FSBufferDescriptor record
;
913 if ((listp
= (hotfilelist_t
*)hfsmp
->hfc_recdata
) == NULL
)
916 mp
= HFSTOVFS(hfsmp
);
918 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
919 bzero(iterator
, sizeof(*iterator
));
920 key
= (HotFileKey
*) &iterator
->key
;
922 record
.bufferAddress
= &data
;
923 record
.itemSize
= sizeof(u_int32_t
);
924 record
.itemCount
= 1;
926 hfs_global_shared_lock_acquire(hfsmp
);
928 if (journal_start_transaction(hfsmp
->jnl
) != 0) {
929 hfs_global_shared_lock_release(hfsmp
);
934 vn_lock(hfsmp
->hfc_filevp
, LK_EXCLUSIVE
| LK_RETRY
, p
);
935 filefork
= VTOF(hfsmp
->hfc_filevp
);
937 for (i
= 0; i
< listp
->hfl_count
; ++i
) {
939 * Check if entry (thread) is already in hot area.
941 key
->keyLength
= HFC_KEYLENGTH
;
942 key
->temperature
= HFC_LOOKUPTAG
;
943 key
->fileID
= listp
->hfl_hotfile
[i
].hf_fileid
;
945 (void) BTInvalidateHint(iterator
);
946 if (BTSearchRecord(filefork
, iterator
, &record
, NULL
, iterator
) != 0) {
947 continue; /* not in hot area, so skip */
951 * Update thread entry with latest temperature.
953 error
= BTUpdateRecord(filefork
, iterator
,
954 (IterateCallBackProcPtr
)update_callback
,
955 &listp
->hfl_hotfile
[i
].hf_temperature
);
957 printf("hotfiles_refine: BTUpdateRecord failed %d (file %d)\n", error
, key
->fileID
);
958 error
= MacToVFSError(error
);
962 * Re-key entry with latest temperature.
964 key
->keyLength
= HFC_KEYLENGTH
;
965 key
->temperature
= data
;
966 key
->fileID
= listp
->hfl_hotfile
[i
].hf_fileid
;
968 /* Pick up record data. */
969 (void) BTInvalidateHint(iterator
);
970 (void) BTSearchRecord(filefork
, iterator
, &record
, NULL
, iterator
);
971 error
= BTDeleteRecord(filefork
, iterator
);
973 printf("hotfiles_refine: BTDeleteRecord failed %d (file %d)\n", error
, key
->fileID
);
974 error
= MacToVFSError(error
);
977 key
->keyLength
= HFC_KEYLENGTH
;
978 key
->temperature
= listp
->hfl_hotfile
[i
].hf_temperature
;
979 key
->fileID
= listp
->hfl_hotfile
[i
].hf_fileid
;
981 error
= BTInsertRecord(filefork
, iterator
, &record
, sizeof(data
));
983 printf("hotfiles_refine: BTInsertRecord failed %d (file %d)\n", error
, key
->fileID
);
984 error
= MacToVFSError(error
);
989 * Invalidate this entry in the list.
991 listp
->hfl_hotfile
[i
].hf_temperature
= 0;
992 listp
->hfl_totalblocks
-= listp
->hfl_hotfile
[i
].hf_blocks
;
996 (void) BTFlushPath(filefork
);
997 (void) VOP_UNLOCK(hfsmp
->hfc_filevp
, 0, p
);
1000 journal_end_transaction(hfsmp
->jnl
);
1002 hfs_global_shared_lock_release(hfsmp
);
1004 FREE(iterator
, M_TEMP
);
1009 * Move new hot files into hot area.
1012 hotfiles_adopt(struct hfsmount
*hfsmp
, struct proc
*p
)
1014 BTreeIterator
* iterator
;
1017 filefork_t
* filefork
;
1018 hotfilelist_t
*listp
;
1019 FSBufferDescriptor record
;
1022 enum hfc_stage stage
;
1028 int startedtrans
= 0;
1029 int aquiredlock
= 0;
1031 if ((listp
= (hotfilelist_t
*)hfsmp
->hfc_recdata
) == NULL
)
1034 if (hfsmp
->hfc_stage
!= HFC_ADOPTION
) {
1037 stage
= hfsmp
->hfc_stage
;
1038 hfsmp
->hfc_stage
= HFC_BUSY
;
1040 mp
= HFSTOVFS(hfsmp
);
1042 last
= listp
->hfl_next
+ HFC_FILESPERSYNC
;
1043 if (last
> listp
->hfl_count
)
1044 last
= listp
->hfl_count
;
1046 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
1047 bzero(iterator
, sizeof(*iterator
));
1048 key
= (HotFileKey
*) &iterator
->key
;
1049 key
->keyLength
= HFC_KEYLENGTH
;
1051 record
.bufferAddress
= &data
;
1052 record
.itemSize
= sizeof(u_int32_t
);
1053 record
.itemCount
= 1;
1055 vn_lock(hfsmp
->hfc_filevp
, LK_EXCLUSIVE
| LK_RETRY
, p
);
1056 filefork
= VTOF(hfsmp
->hfc_filevp
);
1058 for (i
= listp
->hfl_next
; (i
< last
) && (blksmoved
< HFC_BLKSPERSYNC
); ++i
) {
1060 * Skip invalid entries (already in hot area).
1062 if (listp
->hfl_hotfile
[i
].hf_temperature
== 0) {
1067 * Acquire a vnode for this file.
1069 error
= VFS_VGET(mp
, &listp
->hfl_hotfile
[i
].hf_fileid
, &vp
);
1071 if (error
== ENOENT
) {
1074 continue; /* stale entry, go to next */
1078 if (vp
->v_type
!= VREG
&& vp
->v_type
!= VLNK
) {
1079 printf("hotfiles_adopt: huh, not a file %d (%d)\n", listp
->hfl_hotfile
[i
].hf_fileid
, VTOC(vp
)->c_cnid
);
1081 listp
->hfl_hotfile
[i
].hf_temperature
== 0;
1083 continue; /* stale entry, go to next */
1085 if (hotextents(hfsmp
, &VTOF(vp
)->ff_extents
[0])) {
1087 listp
->hfl_hotfile
[i
].hf_temperature
== 0;
1089 listp
->hfl_totalblocks
-= listp
->hfl_hotfile
[i
].hf_blocks
;
1090 continue; /* stale entry, go to next */
1092 fileblocks
= VTOF(vp
)->ff_blocks
;
1093 if (fileblocks
> hfsmp
->hfs_hotfile_freeblks
) {
1096 listp
->hfl_totalblocks
-= fileblocks
;
1097 continue; /* entry too big, go to next */
1100 if ((blksmoved
> 0) &&
1101 (blksmoved
+ fileblocks
) > HFC_BLKSPERSYNC
) {
1105 /* Start a new transaction. */
1106 hfs_global_shared_lock_acquire(hfsmp
);
1109 if (journal_start_transaction(hfsmp
->jnl
) != 0) {
1117 error
= hfs_relocate(vp
, hfsmp
->hfs_hotfile_start
, p
->p_ucred
, p
);
1122 /* Keep hot file free space current. */
1123 hfsmp
->hfs_hotfile_freeblks
-= fileblocks
;
1124 listp
->hfl_totalblocks
-= fileblocks
;
1126 /* Insert hot file entry */
1127 key
->keyLength
= HFC_KEYLENGTH
;
1128 key
->temperature
= listp
->hfl_hotfile
[i
].hf_temperature
;
1129 key
->fileID
= listp
->hfl_hotfile
[i
].hf_fileid
;
1131 if (VTOC(vp
)->c_desc
.cd_nameptr
)
1132 data
= *(u_int32_t
*)(VTOC(vp
)->c_desc
.cd_nameptr
);
1136 error
= BTInsertRecord(filefork
, iterator
, &record
, sizeof(data
));
1138 printf("hotfiles_adopt: BTInsertRecord failed %d (fileid %d)\n", error
, key
->fileID
);
1139 error
= MacToVFSError(error
);
1144 /* Insert thread record */
1145 key
->keyLength
= HFC_KEYLENGTH
;
1146 key
->temperature
= HFC_LOOKUPTAG
;
1147 key
->fileID
= listp
->hfl_hotfile
[i
].hf_fileid
;
1149 data
= listp
->hfl_hotfile
[i
].hf_temperature
;
1150 error
= BTInsertRecord(filefork
, iterator
, &record
, sizeof(data
));
1152 printf("hotfiles_adopt: BTInsertRecord failed %d (fileid %d)\n", error
, key
->fileID
);
1153 error
= MacToVFSError(error
);
1157 (void) BTFlushPath(filefork
);
1159 /* Transaction complete. */
1161 journal_end_transaction(hfsmp
->jnl
);
1164 hfs_global_shared_lock_release(hfsmp
);
1167 blksmoved
+= fileblocks
;
1169 if (listp
->hfl_next
>= listp
->hfl_count
) {
1172 if (hfsmp
->hfs_hotfile_freeblks
<= 0) {
1174 printf("hotfiles_adopt: free space exhausted (%d)\n", hfsmp
->hfs_hotfile_freeblks
);
1181 printf("hotfiles_adopt: [%d] adopted %d blocks (%d left)\n", listp
->hfl_next
, blksmoved
, listp
->hfl_totalblocks
);
1183 /* Finish any outstanding transactions. */
1185 (void) BTFlushPath(filefork
);
1186 journal_end_transaction(hfsmp
->jnl
);
1190 hfs_global_shared_lock_release(hfsmp
);
1193 (void) VOP_UNLOCK(hfsmp
->hfc_filevp
, 0, p
);
1195 if ((listp
->hfl_next
>= listp
->hfl_count
) || (hfsmp
->hfs_hotfile_freeblks
<= 0)) {
1197 printf("hotfiles_adopt: all done relocating %d files\n", listp
->hfl_count
);
1198 printf("hotfiles_adopt: %d blocks free in hot file band\n", hfsmp
->hfs_hotfile_freeblks
);
1202 FREE(iterator
, M_TEMP
);
1204 if (stage
!= HFC_ADOPTION
&& hfsmp
->hfc_filevp
) {
1205 (void) hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
1206 hfsmp
->hfc_filevp
= NULL
;
1208 hfsmp
->hfc_stage
= stage
;
1209 wakeup((caddr_t
)&hfsmp
->hfc_stage
);
1214 * Reclaim space by evicting the coldest files.
1217 hotfiles_evict(struct hfsmount
*hfsmp
, struct proc
*p
)
1219 BTreeIterator
* iterator
;
1223 filefork_t
* filefork
;
1224 hotfilelist_t
*listp
;
1225 enum hfc_stage stage
;
1230 int startedtrans
= 0;
1231 int aquiredlock
= 0;
1233 if (hfsmp
->hfc_stage
!= HFC_EVICTION
) {
1237 if ((listp
= (hotfilelist_t
*)hfsmp
->hfc_recdata
) == NULL
)
1240 stage
= hfsmp
->hfc_stage
;
1241 hfsmp
->hfc_stage
= HFC_BUSY
;
1243 mp
= HFSTOVFS(hfsmp
);
1244 filesmoved
= blksmoved
= 0;
1246 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
1247 bzero(iterator
, sizeof(*iterator
));
1248 key
= (HotFileKey
*) &iterator
->key
;
1250 vn_lock(hfsmp
->hfc_filevp
, LK_EXCLUSIVE
| LK_RETRY
, p
);
1251 filefork
= VTOF(hfsmp
->hfc_filevp
);
1253 while (listp
->hfl_reclaimblks
> 0 &&
1254 blksmoved
< HFC_BLKSPERSYNC
&&
1255 filesmoved
< HFC_FILESPERSYNC
) {
1258 * Obtain the first record (ie the coldest one).
1260 if (BTIterateRecord(filefork
, kBTreeFirstRecord
, iterator
, NULL
, NULL
) != 0) {
1262 printf("hotfiles_evict: no more records\n");
1265 stage
= HFC_ADOPTION
;
1268 if (key
->keyLength
!= HFC_KEYLENGTH
) {
1269 printf("hotfiles_evict: invalid key length %d\n", key
->keyLength
);
1273 if (key
->temperature
== HFC_LOOKUPTAG
) {
1275 printf("hotfiles_evict: ran into thread records\n");
1278 stage
= HFC_ADOPTION
;
1282 * Aquire the vnode for this file.
1284 error
= VFS_VGET(mp
, &key
->fileID
, &vp
);
1286 /* Start a new transaction. */
1287 hfs_global_shared_lock_acquire(hfsmp
);
1290 if (journal_start_transaction(hfsmp
->jnl
) != 0) {
1299 if (error
== ENOENT
) {
1300 (void) BTDeleteRecord(filefork
, iterator
);
1301 key
->temperature
= HFC_LOOKUPTAG
;
1302 (void) BTDeleteRecord(filefork
, iterator
);
1303 goto next
; /* stale entry, go to next */
1305 printf("hotfiles_evict: err %d getting file %d (%d)\n",
1306 error
, key
->fileID
);
1310 if (vp
->v_type
!= VREG
&& vp
->v_type
!= VLNK
) {
1311 printf("hotfiles_evict: huh, not a file %d\n", key
->fileID
);
1313 (void) BTDeleteRecord(filefork
, iterator
);
1314 key
->temperature
= HFC_LOOKUPTAG
;
1315 (void) BTDeleteRecord(filefork
, iterator
);
1316 goto next
; /* invalid entry, go to next */
1318 fileblocks
= VTOF(vp
)->ff_blocks
;
1319 if ((blksmoved
> 0) &&
1320 (blksmoved
+ fileblocks
) > HFC_BLKSPERSYNC
) {
1325 * Make sure file is in the hot area.
1327 if (!hotextents(hfsmp
, &VTOF(vp
)->ff_extents
[0])) {
1329 printf("hotfiles_evict: file %d isn't hot!\n", key
->fileID
);
1332 (void) BTDeleteRecord(filefork
, iterator
);
1333 key
->temperature
= HFC_LOOKUPTAG
;
1334 (void) BTDeleteRecord(filefork
, iterator
);
1335 goto next
; /* go to next */
1339 * Relocate file out of hot area.
1341 error
= hfs_relocate(vp
, HFSTOVCB(hfsmp
)->nextAllocation
, p
->p_ucred
, p
);
1343 /* XXX skip to next record here! */
1344 printf("hotfiles_evict: err % relocating file\n", error
, key
->fileID
);
1348 (void) VOP_FSYNC(vp
, p
->p_ucred
, MNT_WAIT
, p
);
1352 hfsmp
->hfs_hotfile_freeblks
+= fileblocks
;
1353 listp
->hfl_reclaimblks
-= fileblocks
;
1354 if (listp
->hfl_reclaimblks
< 0)
1355 listp
->hfl_reclaimblks
= 0;
1356 blksmoved
+= fileblocks
;
1359 error
= BTDeleteRecord(filefork
, iterator
);
1361 printf("hotfiles_evict: BTDeleteRecord failed %d (fileid %d)\n", error
, key
->fileID
);
1362 error
= MacToVFSError(error
);
1365 key
->temperature
= HFC_LOOKUPTAG
;
1366 error
= BTDeleteRecord(filefork
, iterator
);
1368 printf("hotfiles_evict: BTDeleteRecord thread failed %d (fileid %d)\n", error
, key
->fileID
);
1369 error
= MacToVFSError(error
);
1373 (void) BTFlushPath(filefork
);
1375 /* Transaction complete. */
1377 journal_end_transaction(hfsmp
->jnl
);
1380 hfs_global_shared_lock_release(hfsmp
);
1386 printf("hotfiles_evict: moved %d files (%d blks, %d to go)\n", filesmoved
, blksmoved
, listp
->hfl_reclaimblks
);
1388 /* Finish any outstanding transactions. */
1390 (void) BTFlushPath(filefork
);
1391 journal_end_transaction(hfsmp
->jnl
);
1395 hfs_global_shared_lock_release(hfsmp
);
1398 (void) VOP_UNLOCK(hfsmp
->hfc_filevp
, 0, p
);
1401 * Move to next stage when finished.
1403 if (listp
->hfl_reclaimblks
<= 0) {
1404 stage
= HFC_ADOPTION
;
1406 printf("hotfiles_evict: %d blocks free in hot file band\n", hfsmp
->hfs_hotfile_freeblks
);
1409 FREE(iterator
, M_TEMP
);
1410 hfsmp
->hfc_stage
= stage
;
1411 wakeup((caddr_t
)&hfsmp
->hfc_stage
);
1416 * Age the existing records in the hot files b-tree.
1419 hotfiles_age(struct hfsmount
*hfsmp
, struct proc
*p
)
1421 BTreeInfoRec btinfo
;
1422 BTreeIterator
* iterator
;
1423 BTreeIterator
* prev_iterator
;
1424 FSBufferDescriptor record
;
1425 FSBufferDescriptor prev_record
;
1427 HotFileKey
* prev_key
;
1428 filefork_t
* filefork
;
1430 u_int32_t prev_data
;
1439 MALLOC(iterator
, BTreeIterator
*, 2 * sizeof(*iterator
), M_TEMP
, M_WAITOK
);
1440 bzero(iterator
, 2 * sizeof(*iterator
));
1441 key
= (HotFileKey
*) &iterator
->key
;
1443 prev_iterator
= &iterator
[1];
1444 prev_key
= (HotFileKey
*) &prev_iterator
->key
;
1446 record
.bufferAddress
= &data
;
1447 record
.itemSize
= sizeof(data
);
1448 record
.itemCount
= 1;
1449 prev_record
.bufferAddress
= &prev_data
;
1450 prev_record
.itemSize
= sizeof(prev_data
);
1451 prev_record
.itemCount
= 1;
1454 * Capture b-tree changes inside a transaction
1456 hfs_global_shared_lock_acquire(hfsmp
);
1458 if (journal_start_transaction(hfsmp
->jnl
) != 0) {
1459 hfs_global_shared_lock_release(hfsmp
);
1464 vn_lock(hfsmp
->hfc_filevp
, LK_EXCLUSIVE
| LK_RETRY
, p
);
1465 filefork
= VTOF(hfsmp
->hfc_filevp
);
1467 error
= BTGetInformation(filefork
, 0, &btinfo
);
1469 error
= MacToVFSError(error
);
1472 if (btinfo
.numRecords
< 2) {
1477 /* Only want 1st half of leaf records */
1478 numrecs
= (btinfo
.numRecords
/= 2) - 1;
1480 error
= BTIterateRecord(filefork
, kBTreeFirstRecord
, iterator
, &record
, &reclen
);
1482 printf("hfs_agehotfiles: BTIterateRecord: %d\n", error
);
1483 error
= MacToVFSError(error
);
1486 bcopy(iterator
, prev_iterator
, sizeof(BTreeIterator
));
1489 for (i
= 0; i
< numrecs
; ++i
) {
1490 error
= BTIterateRecord(filefork
, kBTreeNextRecord
, iterator
, &record
, &reclen
);
1492 if (key
->temperature
< prev_key
->temperature
) {
1493 printf("hfs_agehotfiles: out of order keys!\n");
1497 if (reclen
!= sizeof(data
)) {
1498 printf("hfs_agehotfiles: invalid record length %d\n", reclen
);
1502 if (key
->keyLength
!= HFC_KEYLENGTH
) {
1503 printf("hfs_agehotfiles: invalid key length %d\n", key
->keyLength
);
1507 } else if ((error
== fsBTEndOfIterationErr
|| error
== fsBTRecordNotFoundErr
) &&
1508 (i
== (numrecs
- 1))) {
1511 printf("hfs_agehotfiles: %d of %d BTIterateRecord: %d\n", i
, numrecs
, error
);
1512 error
= MacToVFSError(error
);
1515 if (prev_key
->temperature
== HFC_LOOKUPTAG
) {
1517 printf("hfs_agehotfiles: ran into thread record\n");
1522 error
= BTDeleteRecord(filefork
, prev_iterator
);
1524 printf("hfs_agehotfiles: BTDeleteRecord failed %d (file %d)\n", error
, prev_key
->fileID
);
1525 error
= MacToVFSError(error
);
1529 /* Age by halving the temperature (floor = 4) */
1530 newtemp
= MAX(prev_key
->temperature
>> 1, 4);
1531 prev_key
->temperature
= newtemp
;
1533 error
= BTInsertRecord(filefork
, prev_iterator
, &prev_record
, sizeof(data
));
1535 printf("hfs_agehotfiles: BTInsertRecord failed %d (file %d)\n", error
, prev_key
->fileID
);
1536 error
= MacToVFSError(error
);
1541 * Update thread entry with latest temperature.
1543 prev_key
->temperature
= HFC_LOOKUPTAG
;
1544 error
= BTUpdateRecord(filefork
, prev_iterator
,
1545 (IterateCallBackProcPtr
)update_callback
,
1548 printf("hfs_agehotfiles: %d of %d BTUpdateRecord failed %d (file %d, %d)\n",
1549 i
, numrecs
, error
, prev_key
->fileID
, newtemp
);
1550 error
= MacToVFSError(error
);
1554 bcopy(iterator
, prev_iterator
, sizeof(BTreeIterator
));
1561 printf("hfs_agehotfiles: aged %d records out of %d\n", aged
, btinfo
.numRecords
);
1563 (void) BTFlushPath(filefork
);
1565 (void) VOP_UNLOCK(hfsmp
->hfc_filevp
, 0, p
);
1568 // hfs_flushvolumeheader(hfsmp, MNT_NOWAIT, 0);
1569 journal_end_transaction(hfsmp
->jnl
);
1571 hfs_global_shared_lock_release(hfsmp
);
1573 FREE(iterator
, M_TEMP
);
1578 * Return true if any blocks (or all blocks if all is true)
1579 * are contained in the hot file region.
1582 hotextents(struct hfsmount
*hfsmp
, HFSPlusExtentDescriptor
* extents
)
1588 for (i
= 0; i
< kHFSPlusExtentDensity
; ++i
) {
1589 b1
= extents
[i
].startBlock
;
1592 b2
= b1
+ extents
[i
].blockCount
- 1;
1593 if ((b1
>= hfsmp
->hfs_hotfile_start
&&
1594 b2
<= hfsmp
->hfs_hotfile_end
) ||
1595 (b1
< hfsmp
->hfs_hotfile_end
&&
1596 b2
> hfsmp
->hfs_hotfile_end
)) {
1606 *========================================================================
1607 * HOT FILE B-TREE ROUTINES
1608 *========================================================================
1612 * Open the hot files b-tree for writing.
1614 * On successful exit the vnode has a reference but is unlocked.
1617 hfc_btree_open(struct hfsmount
*hfsmp
, struct vnode
**vpp
)
1621 struct cat_desc cdesc
= {0};
1622 struct cat_attr cattr
;
1623 struct cat_fork cfork
;
1624 static char filename
[] = HFC_FILENAME
;
1631 cdesc
.cd_parentcnid
= kRootDirID
;
1632 cdesc
.cd_nameptr
= filename
;
1633 cdesc
.cd_namelen
= strlen(filename
);
1635 /* Lock catalog b-tree */
1636 error
= hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_SHARED
, p
);
1640 error
= cat_lookup(hfsmp
, &cdesc
, 0, &cdesc
, &cattr
, &cfork
);
1642 /* Unlock catalog b-tree */
1643 (void) hfs_metafilelocking(hfsmp
, kHFSCatalogFileID
, LK_RELEASE
, p
);
1646 printf("hfc_btree_open: cat_lookup error %d\n", error
);
1650 cdesc
.cd_flags
|= CD_ISMETA
;
1651 error
= hfs_getnewvnode(hfsmp
, NULL
, &cdesc
, 0, &cattr
, &cfork
, &vp
);
1653 printf("hfc_btree_open: hfs_getnewvnode error %d\n", error
);
1654 cat_releasedesc(&cdesc
);
1657 if ((vp
->v_flag
& VSYSTEM
) == 0) {
1659 printf("hfc_btree_open: file has UBC, try again\n");
1669 /* Open the B-tree file for writing... */
1670 error
= BTOpenPath(VTOF(vp
), (KeyCompareProcPtr
) hfc_comparekeys
);
1672 printf("hfc_btree_open: BTOpenPath error %d\n", error
);
1673 error
= MacToVFSError(error
);
1676 struct BTreeInfoRec btinfo
;
1678 if (BTGetInformation(VTOF(vp
), 0, &btinfo
) == 0) {
1679 printf("btinfo: nodeSize %d\n", btinfo
.nodeSize
);
1680 printf("btinfo: maxKeyLength %d\n", btinfo
.maxKeyLength
);
1681 printf("btinfo: treeDepth %d\n", btinfo
.treeDepth
);
1682 printf("btinfo: numRecords %d\n", btinfo
.numRecords
);
1683 printf("btinfo: numNodes %d\n", btinfo
.numNodes
);
1684 printf("btinfo: numFreeNodes %d\n", btinfo
.numFreeNodes
);
1689 VOP_UNLOCK(vp
, 0, p
); /* unlocked with a single reference */
1695 if ((vp
->v_flag
& VSYSTEM
) == 0)
1696 panic("hfc_btree_open: not a system file (vp = 0x%08x)", vp
);
1698 if (UBCINFOEXISTS(vp
))
1699 panic("hfc_btree_open: has UBCInfo (vp = 0x%08x)", vp
);
1705 * Close the hot files b-tree.
1707 * On entry the vnode is not locked but has a reference.
1710 hfc_btree_close(struct hfsmount
*hfsmp
, struct vnode
*vp
)
1712 struct proc
*p
= current_proc();
1717 journal_flush(hfsmp
->jnl
);
1720 if (vget(vp
, LK_EXCLUSIVE
, p
) == 0) {
1721 (void) VOP_FSYNC(vp
, NOCRED
, MNT_WAIT
, p
);
1722 error
= BTClosePath(VTOF(vp
));
1724 printf("hfc_btree_close: BTClosePath error %d\n", error
);
1735 * Create a hot files btree file.
1739 hfc_btree_create(struct hfsmount
*hfsmp
, int nodesize
, int entries
)
1742 struct nameidata nd
;
1748 if (hfsmp
->hfc_filevp
)
1749 panic("hfc_btree_create: hfc_filevp exists (vp = 0x%08x)", hfsmp
->hfc_filevp
);
1752 snprintf(path
, sizeof(path
), "%s/%s",
1753 hfsmp
->hfs_mp
->mnt_stat
.f_mntonname
, HFC_FILENAME
);
1754 NDINIT(&nd
, LOOKUP
, FOLLOW
, UIO_SYSSPACE
, path
, p
);
1755 if ((error
= vn_open(&nd
, O_CREAT
| FWRITE
, S_IRUSR
| S_IWUSR
)) != 0) {
1760 /* Don't use non-regular files or files with links. */
1761 if (vp
->v_type
!= VREG
|| VTOC(vp
)->c_nlink
!= 1) {
1766 printf("HFS: created HFBT on %s\n", HFSTOVCB(hfsmp
)->vcbVN
);
1768 if (VTOF(vp
)->ff_size
< nodesize
) {
1772 BTNodeDescriptor
*ndp
;
1774 HotFilesInfo
*hotfileinfo
;
1780 * Mark it invisible (truncate will pull these changes).
1782 ((FndrFileInfo
*)&VTOC(vp
)->c_finderinfo
[0])->fdFlags
|=
1783 SWAP_BE16 (kIsInvisible
+ kNameLocked
);
1785 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&buffer
, nodesize
)) {
1789 bzero(buffer
, nodesize
);
1790 index
= (int16_t *)buffer
;
1792 entirespernode
= (nodesize
- sizeof(BTNodeDescriptor
) - 2) /
1793 (sizeof(HotFileKey
) + 6);
1794 nodecnt
= 2 + howmany(entries
* 2, entirespernode
);
1795 nodecnt
= roundup(nodecnt
, 8);
1796 filesize
= nodecnt
* nodesize
;
1798 /* FILL IN THE NODE DESCRIPTOR: */
1799 ndp
= (BTNodeDescriptor
*)buffer
;
1800 ndp
->kind
= kBTHeaderNode
;
1801 ndp
->numRecords
= SWAP_BE16 (3);
1802 offset
= sizeof(BTNodeDescriptor
);
1803 index
[(nodesize
/ 2) - 1] = SWAP_BE16 (offset
);
1805 /* FILL IN THE HEADER RECORD: */
1806 bthp
= (BTHeaderRec
*)((UInt8
*)buffer
+ offset
);
1807 bthp
->nodeSize
= SWAP_BE16 (nodesize
);
1808 bthp
->totalNodes
= SWAP_BE32 (filesize
/ nodesize
);
1809 bthp
->freeNodes
= SWAP_BE32 (nodecnt
- 1);
1810 bthp
->clumpSize
= SWAP_BE32 (filesize
);
1811 bthp
->btreeType
= kUserBTreeType
; /* non-metadata */
1812 bthp
->attributes
|= SWAP_BE32 (kBTBigKeysMask
);
1813 bthp
->maxKeyLength
= SWAP_BE16 (HFC_KEYLENGTH
);
1814 offset
+= sizeof(BTHeaderRec
);
1815 index
[(nodesize
/ 2) - 2] = SWAP_BE16 (offset
);
1817 /* FILL IN THE USER RECORD: */
1818 hotfileinfo
= (HotFilesInfo
*)((UInt8
*)buffer
+ offset
);
1819 hotfileinfo
->magic
= SWAP_BE32 (HFC_MAGIC
);
1820 hotfileinfo
->version
= SWAP_BE32 (HFC_VERSION
);
1821 hotfileinfo
->duration
= SWAP_BE32 (HFC_DEFAULT_DURATION
);
1822 hotfileinfo
->timebase
= 0;
1823 hotfileinfo
->timeleft
= 0;
1824 hotfileinfo
->threshold
= SWAP_BE32 (HFC_MINIMUM_TEMPERATURE
);
1825 hotfileinfo
->maxfileblks
= SWAP_BE32 (HFC_MAXIMUM_FILESIZE
/ HFSTOVCB(hfsmp
)->blockSize
);
1826 hotfileinfo
->maxfilecnt
= SWAP_BE32 (HFC_DEFAULT_FILE_COUNT
);
1827 strcpy(hotfileinfo
->tag
, hfc_tag
);
1828 offset
+= kBTreeHeaderUserBytes
;
1829 index
[(nodesize
/ 2) - 3] = SWAP_BE16 (offset
);
1831 /* FILL IN THE MAP RECORD (only one node in use). */
1832 *((u_int8_t
*)buffer
+ offset
) = 0x80;
1833 offset
+= nodesize
- sizeof(BTNodeDescriptor
) - sizeof(BTHeaderRec
)
1834 - kBTreeHeaderUserBytes
- (4 * sizeof(int16_t));
1835 index
[(nodesize
/ 2) - 4] = SWAP_BE16 (offset
);
1837 vp
->v_flag
|= VNOFLUSH
;
1838 error
= VOP_TRUNCATE(vp
, (off_t
)filesize
, IO_NDELAY
, NOCRED
, p
);
1843 auio
.uio_iov
= &aiov
;
1844 auio
.uio_iovcnt
= 1;
1845 aiov
.iov_base
= buffer
;
1846 aiov
.iov_len
= filesize
;
1847 auio
.uio_resid
= nodesize
;
1848 auio
.uio_offset
= (off_t
)(0);
1849 auio
.uio_segflg
= UIO_SYSSPACE
;
1850 auio
.uio_rw
= UIO_WRITE
;
1851 auio
.uio_procp
= (struct proc
*)0;
1852 error
= VOP_WRITE(vp
, &auio
, 0, kernproc
->p_ucred
);
1854 kmem_free(kernel_map
, (vm_offset_t
)buffer
, nodesize
);
1857 (void) VOP_UNLOCK(vp
, 0, p
);
1858 (void) vn_close(vp
, FWRITE
, kernproc
->p_ucred
, p
);
1864 * Compare two hot file b-tree keys.
1866 * Result: +n search key > trial key
1867 * 0 search key = trial key
1868 * -n search key < trial key
1871 hfc_comparekeys(HotFileKey
*searchKey
, HotFileKey
*trialKey
)
1874 * Compared temperatures first.
1876 if (searchKey
->temperature
== trialKey
->temperature
) {
1878 * Temperatures are equal so compare file ids.
1880 if (searchKey
->fileID
== trialKey
->fileID
) {
1882 * File ids are equal so compare fork types.
1884 if (searchKey
->forkType
== trialKey
->forkType
) {
1886 } else if (searchKey
->forkType
> trialKey
->forkType
) {
1889 } else if (searchKey
->fileID
> trialKey
->fileID
) {
1892 } else if (searchKey
->temperature
> trialKey
->temperature
) {
1901 *========================================================================
1902 * HOT FILE DATA COLLECTING ROUTINES
1903 *========================================================================
1907 * Lookup a hot file entry in the tree.
1909 static hotfile_entry_t
*
1910 hf_lookup(hotfile_data_t
*hotdata
, u_int32_t fileid
, u_int32_t temperature
)
1912 hotfile_entry_t
*entry
= hotdata
->rootentry
;
1915 entry
->temperature
!= temperature
&&
1916 entry
->fileid
!= fileid
) {
1918 if (temperature
> entry
->temperature
)
1919 entry
= entry
->right
;
1920 else if (temperature
< entry
->temperature
)
1921 entry
= entry
->left
;
1922 else if (fileid
> entry
->fileid
)
1923 entry
= entry
->right
;
1925 entry
= entry
->left
;
1931 * Insert a hot file entry into the tree.
1934 hf_insert(hotfile_data_t
*hotdata
, hotfile_entry_t
*newentry
)
1936 hotfile_entry_t
*entry
= hotdata
->rootentry
;
1937 u_int32_t fileid
= newentry
->fileid
;
1938 u_int32_t temperature
= newentry
->temperature
;
1940 if (entry
== NULL
) {
1941 hotdata
->rootentry
= newentry
;
1942 hotdata
->coldest
= newentry
;
1943 hotdata
->activefiles
++;
1948 if (temperature
> entry
->temperature
) {
1950 entry
= entry
->right
;
1952 entry
->right
= newentry
;
1955 } else if (temperature
< entry
->temperature
) {
1957 entry
= entry
->left
;
1959 entry
->left
= newentry
;
1962 } else if (fileid
> entry
->fileid
) {
1964 entry
= entry
->right
;
1966 if (entry
->fileid
!= fileid
)
1967 entry
->right
= newentry
;
1972 entry
= entry
->left
;
1974 if (entry
->fileid
!= fileid
)
1975 entry
->left
= newentry
;
1981 hotdata
->activefiles
++;
1985 * Find the coldest entry in the tree.
1987 static hotfile_entry_t
*
1988 hf_coldest(hotfile_data_t
*hotdata
)
1990 hotfile_entry_t
*entry
= hotdata
->rootentry
;
1994 entry
= entry
->left
;
2000 * Delete a hot file entry from the tree.
2003 hf_delete(hotfile_data_t
*hotdata
, u_int32_t fileid
, u_int32_t temperature
)
2005 hotfile_entry_t
*entry
, *parent
, *next
;
2008 entry
= hotdata
->rootentry
;
2011 entry
->temperature
!= temperature
&&
2012 entry
->fileid
!= fileid
) {
2015 if (temperature
> entry
->temperature
)
2016 entry
= entry
->right
;
2017 else if (temperature
< entry
->temperature
)
2018 entry
= entry
->left
;
2019 else if (fileid
> entry
->fileid
)
2020 entry
= entry
->right
;
2022 entry
= entry
->left
;
2027 * Reorginize the sub-trees spanning from our entry.
2029 if ((next
= entry
->right
)) {
2030 hotfile_entry_t
*pnextl
, *psub
;
2032 * Tree pruning: take the left branch of the
2033 * current entry and place it at the lowest
2034 * left branch of the current right branch
2038 /* Walk the Right/Left sub tree from current entry */
2039 while ((pnextl
= psub
->left
))
2042 /* Plug the old left tree to the new ->Right leftmost entry */
2043 psub
->left
= entry
->left
;
2045 } else /* only left sub-tree, simple case */ {
2049 * Now, plug the current entry sub tree to
2050 * the good pointer of our parent entry.
2053 hotdata
->rootentry
= next
;
2054 else if (parent
->left
== entry
)
2055 parent
->left
= next
;
2057 parent
->right
= next
;
2059 /* Place entry back on the free-list */
2062 entry
->temperature
= 0;
2064 entry
->right
= hotdata
->freelist
;
2065 hotdata
->freelist
= entry
;
2066 hotdata
->activefiles
--;
2068 if (hotdata
->coldest
== entry
|| hotdata
->coldest
== NULL
) {
2069 hotdata
->coldest
= hf_coldest(hotdata
);
2076 * Get a free hot file entry.
2078 static hotfile_entry_t
*
2079 hf_getnewentry(hotfile_data_t
*hotdata
)
2081 hotfile_entry_t
* entry
;
2084 * When the free list is empty then steal the coldest one
2086 if (hotdata
->freelist
== NULL
) {
2087 entry
= hf_coldest(hotdata
);
2088 hf_delete(hotdata
, entry
->fileid
, entry
->temperature
);
2090 entry
= hotdata
->freelist
;
2091 hotdata
->freelist
= entry
->right
;
2099 * Visit the tree in desending order.
2102 hf_sortlist(hotfile_entry_t
* root
, int *index
, hotfilelist_t
*sortedlist
)
2107 hf_sortlist(root
->right
, index
, sortedlist
);
2110 sortedlist
->hfl_hotfile
[i
].hf_fileid
= root
->fileid
;
2111 sortedlist
->hfl_hotfile
[i
].hf_temperature
= root
->temperature
;
2112 sortedlist
->hfl_hotfile
[i
].hf_blocks
= root
->blocks
;
2113 sortedlist
->hfl_totalblocks
+= root
->blocks
;
2114 hf_sortlist(root
->left
, index
, sortedlist
);
2119 * Generate a sorted list of hot files.
2122 hf_getsortedlist(hotfile_data_t
* hotdata
, hotfilelist_t
*sortedlist
)
2126 hf_sortlist(hotdata
->rootentry
, &index
, sortedlist
);
2128 sortedlist
->hfl_count
= hotdata
->activefiles
;
2136 hf_maxdepth(hotfile_entry_t
* root
, int depth
, int *maxdepth
)
2140 if (depth
> *maxdepth
)
2142 hf_maxdepth(root
->left
, depth
, maxdepth
);
2143 hf_maxdepth(root
->right
, depth
, maxdepth
);
2148 hf_printtree(hotfile_entry_t
* root
)
2151 hf_printtree(root
->left
);
2152 printf("temperature: % 8d, fileid %d\n", root
->temperature
, root
->fileid
);
2153 hf_printtree(root
->right
);