2 * Copyright (c) 2003-2015 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 #include <libkern/OSAtomic.h>
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/fcntl.h>
34 #include <sys/kernel.h>
35 #include <sys/malloc.h>
37 #include <sys/vnode.h>
38 #include <sys/kauth.h>
42 #include "hfs_endian.h"
43 #include "hfs_format.h"
44 #include "hfs_mount.h"
45 #include "hfs_hotfiles.h"
47 #include "BTreeScanner.h"
55 * Minimum post Tiger base time.
56 * Thu Mar 31 17:00:00 2005
58 #define HFC_MIN_BASE_TIME 0x424c8f00L
61 * Hot File List (runtime).
63 typedef struct hotfileinfo
{
65 u_int32_t hf_temperature
;
69 typedef struct hotfilelist
{
72 u_int32_t hfl_version
;
73 time_t hfl_duration
; /* duration of sample period */
74 int hfl_count
; /* count of hot files recorded */
75 int hfl_next
; /* next file to move */
76 int hfl_totalblocks
; /* total hot file blocks */
77 int hfl_reclaimblks
; /* blocks to reclaim in HFV */
78 u_int32_t hfl_spare
[2];
79 hotfileinfo_t hfl_hotfile
[1]; /* array of hot files */
84 * Hot File Entry (runtime).
86 typedef struct hotfile_entry
{
87 struct hotfile_entry
*left
;
88 struct hotfile_entry
*right
;
90 u_int32_t temperature
;
96 // We cap the max temperature for non-system files to "MAX_NORMAL_TEMP"
97 // so that they will always have a lower temperature than system (aka
98 // "auto-cached") files. System files have MAX_NORMAL_TEMP added to
99 // their temperature which produces two bands of files (all non-system
100 // files will have a temp less than MAX_NORMAL_TEMP and all system
101 // files will have a temp greatern than MAX_NORMAL_TEMP).
103 // This puts non-system files on the left side of the hotfile btree
104 // (and we start evicting from the left-side of the tree). The idea is
105 // that we will evict non-system files more aggressively since their
106 // working set changes much more dynamically than system files (which
107 // are for the most part, static).
109 // NOTE: these values have to fit into a 32-bit int. We use a
110 // value of 1-billion which gives a pretty broad range
111 // and yet should not run afoul of any sign issues.
113 #define MAX_NORMAL_TEMP 1000000000
114 #define HF_TEMP_RANGE MAX_NORMAL_TEMP
118 // These used to be defines of the hard coded values. But if
119 // we're on an cooperative fusion (CF) system we need to change
120 // the values (which happens in hfs_recording_init()
122 uint32_t hfc_default_file_count
= 1000;
123 uint32_t hfc_default_duration
= (3600 * 60);
124 uint32_t hfc_max_file_count
= 5000;
125 uint64_t hfc_max_file_size
= (10 * 1024 * 1024);
129 * Hot File Recording Data (runtime).
131 typedef struct hotfile_data
{
133 struct hfsmount
*hfsmp
;
135 u_int32_t activefiles
; /* active number of hot files */
138 hotfile_entry_t
*rootentry
;
139 hotfile_entry_t
*freelist
;
140 hotfile_entry_t
*coldest
;
141 hotfile_entry_t entries
[];
144 static int hfs_recording_start (struct hfsmount
*);
145 static int hfs_recording_stop (struct hfsmount
*);
147 /* Hotfiles pinning routines */
148 static int hfs_getvnode_and_pin (struct hfsmount
*hfsmp
, uint32_t fileid
, uint32_t *pinned
);
149 static int hfs_pin_extent_record (struct hfsmount
*hfsmp
, HFSPlusExtentRecord extents
, uint32_t *pinned
);
150 static int hfs_pin_catalog_rec (struct hfsmount
*hfsmp
, HFSPlusCatalogFile
*cfp
, int rsrc
);
153 * Hot File Data recording functions (in-memory binary tree).
155 static int hf_insert (hotfile_data_t
*, hotfile_entry_t
*);
156 static void hf_delete (hotfile_data_t
*, u_int32_t
, u_int32_t
);
157 static hotfile_entry_t
* hf_coldest (hotfile_data_t
*);
158 static hotfile_entry_t
* hf_getnewentry (hotfile_data_t
*);
159 static void hf_getsortedlist (hotfile_data_t
*, hotfilelist_t
*);
162 static hotfile_entry_t
* hf_lookup (hotfile_data_t
*, u_int32_t
, u_int32_t
);
163 static void hf_maxdepth(hotfile_entry_t
*, int, int *);
164 static void hf_printtree (hotfile_entry_t
*);
168 * Hot File misc support functions.
170 static int hotfiles_collect (struct hfsmount
*);
171 static int hotfiles_age (struct hfsmount
*);
172 static int hotfiles_adopt (struct hfsmount
*);
173 static int hotfiles_evict (struct hfsmount
*, vfs_context_t
);
174 static int hotfiles_refine (struct hfsmount
*);
175 static int hotextents(struct hfsmount
*, HFSPlusExtentDescriptor
*);
176 static int hfs_addhotfile_internal(struct vnode
*);
177 static int hfs_hotfile_cur_freeblks(hfsmount_t
*hfsmp
);
181 * Hot File Cluster B-tree (on disk) functions.
183 static int hfc_btree_create (struct hfsmount
*, unsigned int, unsigned int);
184 static int hfc_btree_open (struct hfsmount
*, struct vnode
**);
185 static int hfc_btree_open_ext(struct hfsmount
*hfsmp
, struct vnode
**vpp
, int ignore_btree_errs
);
186 static int hfc_btree_close (struct hfsmount
*, struct vnode
*);
187 static int hfc_btree_delete_record(struct hfsmount
*hfsmp
, BTreeIterator
*iterator
, HotFileKey
*key
);
188 static int hfc_btree_delete(struct hfsmount
*hfsmp
);
189 static int hfc_comparekeys (HotFileKey
*, HotFileKey
*);
192 char hfc_tag
[] = "CLUSTERED HOT FILES B-TREE ";
196 *========================================================================
197 * HOT FILE INTERFACE ROUTINES
198 *========================================================================
202 * Start recording the hottest files on a file system.
204 * Requires that the hfc_mutex be held.
207 hfs_recording_start(struct hfsmount
*hfsmp
)
209 hotfile_data_t
*hotdata
;
216 if ((hfsmp
->hfs_flags
& HFS_READ_ONLY
) ||
217 (hfsmp
->jnl
== NULL
) ||
218 (hfsmp
->hfs_flags
& HFS_METADATA_ZONE
) == 0) {
221 if (HFSTOVCB(hfsmp
)->freeBlocks
< (2 * (u_int32_t
)hfsmp
->hfs_hotfile_maxblks
)) {
224 if (hfsmp
->hfc_stage
!= HFC_IDLE
) {
227 hfsmp
->hfc_stage
= HFC_BUSY
;
229 if (hfsmp
->hfc_recdata
) {
230 hfs_free(hfsmp
->hfc_recdata
, hfsmp
->hfc_recdata
->size
);
231 hfsmp
->hfc_recdata
= NULL
;
233 if (hfsmp
->hfc_filelist
) {
234 hfs_free(hfsmp
->hfc_filelist
, hfsmp
->hfc_filelist
->hfl_size
);
235 hfsmp
->hfc_filelist
= NULL
;
238 microtime(&tv
); /* Times are base on GMT time. */
241 * On first startup check for suspended recording.
243 if (hfsmp
->hfc_timebase
== 0 &&
244 hfc_btree_open(hfsmp
, &hfsmp
->hfc_filevp
) == 0) {
245 HotFilesInfo hotfileinfo
;
247 if ((BTGetUserData(VTOF(hfsmp
->hfc_filevp
), &hotfileinfo
,
248 sizeof(hotfileinfo
)) == 0) &&
249 (SWAP_BE32 (hotfileinfo
.magic
) == HFC_MAGIC
) &&
250 (SWAP_BE32 (hotfileinfo
.timeleft
) > 0) &&
251 (SWAP_BE32 (hotfileinfo
.timebase
) > 0)) {
252 if (hfsmp
->hfs_flags
& HFS_CS_HOTFILE_PIN
) {
253 if (hfsmp
->hfs_hotfile_freeblks
== 0) {
254 hfsmp
->hfs_hotfile_freeblks
= hfsmp
->hfs_hotfile_maxblks
- SWAP_BE32 (hotfileinfo
.usedblocks
);
256 hfsmp
->hfc_maxfiles
= 0x7fffffff;
257 printf("hfs: %s: %s: hotfile freeblocks: %d, max: %d\n", hfsmp
->vcbVN
, __FUNCTION__
,
258 hfsmp
->hfs_hotfile_freeblks
, hfsmp
->hfs_hotfile_maxblks
);
260 hfsmp
->hfc_maxfiles
= SWAP_BE32 (hotfileinfo
.maxfilecnt
);
262 hfsmp
->hfc_timebase
= SWAP_BE32 (hotfileinfo
.timebase
);
263 int timeleft
= (int)SWAP_BE32(hotfileinfo
.timeleft
);
264 if (timeleft
< 0 || timeleft
> (int)(HFC_DEFAULT_DURATION
*2)) {
265 // in case this field got botched, don't let it screw things up
266 // printf("hfs: hotfiles: bogus looking timeleft: %d\n", timeleft);
267 timeleft
= HFC_DEFAULT_DURATION
;
269 hfsmp
->hfc_timeout
= timeleft
+ tv
.tv_sec
;
270 /* Fix up any bogus timebase values. */
271 if (hfsmp
->hfc_timebase
< HFC_MIN_BASE_TIME
) {
272 hfsmp
->hfc_timebase
= hfsmp
->hfc_timeout
- HFC_DEFAULT_DURATION
;
275 printf("hfs: Resume recording hot files on %s (%d secs left (%d); timeout %ld)\n",
276 hfsmp
->vcbVN
, SWAP_BE32 (hotfileinfo
.timeleft
), timeleft
, hfsmp
->hfc_timeout
- tv
.tv_sec
);
279 hfsmp
->hfc_maxfiles
= HFC_DEFAULT_FILE_COUNT
;
280 hfsmp
->hfc_timebase
= tv
.tv_sec
+ 1;
281 hfsmp
->hfc_timeout
= hfsmp
->hfc_timebase
+ HFC_DEFAULT_DURATION
;
283 (void) hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
284 hfsmp
->hfc_filevp
= NULL
;
286 struct cat_attr cattr
;
290 * Make sure a btree file exists.
292 cnid
= GetFileInfo(HFSTOVCB(hfsmp
), kRootDirID
, HFC_FILENAME
, &cattr
, NULL
);
294 !S_ISREG(cattr
.ca_mode
) &&
295 (error
= hfc_btree_create(hfsmp
, HFSTOVCB(hfsmp
)->blockSize
, HFC_DEFAULT_FILE_COUNT
))) {
296 hfsmp
->hfc_stage
= HFC_IDLE
;
297 wakeup((caddr_t
)&hfsmp
->hfc_stage
);
301 printf("hfs: begin recording hot files on %s (hotfile start/end block: %d - %d; max/free: %d/%d; maxfiles: %d)\n",
303 hfsmp
->hfs_hotfile_start
, hfsmp
->hfs_hotfile_end
,
304 hfsmp
->hfs_hotfile_maxblks
, hfsmp
->hfs_hotfile_freeblks
, hfsmp
->hfc_maxfiles
);
306 hfsmp
->hfc_maxfiles
= HFC_DEFAULT_FILE_COUNT
;
307 hfsmp
->hfc_timeout
= tv
.tv_sec
+ HFC_DEFAULT_DURATION
;
309 /* Reset time base. */
310 if (hfsmp
->hfc_timebase
== 0) {
311 hfsmp
->hfc_timebase
= tv
.tv_sec
+ 1;
313 time_t cumulativebase
;
315 cumulativebase
= hfsmp
->hfc_timeout
- (HFC_CUMULATIVE_CYCLES
* HFC_DEFAULT_DURATION
);
316 hfsmp
->hfc_timebase
= MAX(hfsmp
->hfc_timebase
, cumulativebase
);
320 if ((hfsmp
->hfc_maxfiles
== 0) ||
321 (hfsmp
->hfc_maxfiles
> HFC_MAXIMUM_FILE_COUNT
)) {
322 hfsmp
->hfc_maxfiles
= HFC_DEFAULT_FILE_COUNT
;
324 maxentries
= hfsmp
->hfc_maxfiles
;
326 size
= sizeof(hotfile_data_t
) + maxentries
* sizeof(hotfile_entry_t
);
327 hotdata
= hfs_mallocz(size
);
328 hotdata
->size
= size
;
330 for (i
= 1; i
< maxentries
; i
++)
331 hotdata
->entries
[i
-1].right
= &hotdata
->entries
[i
];
333 hotdata
->freelist
= &hotdata
->entries
[0];
335 * Establish minimum temperature and maximum file size.
337 hotdata
->threshold
= HFC_MINIMUM_TEMPERATURE
;
338 hotdata
->maxblocks
= HFC_MAXIMUM_FILESIZE
/ HFSTOVCB(hfsmp
)->blockSize
;
339 hotdata
->hfsmp
= hfsmp
;
341 hfsmp
->hfc_recdata
= hotdata
;
342 hfsmp
->hfc_stage
= HFC_RECORDING
;
343 wakeup((caddr_t
)&hfsmp
->hfc_stage
);
348 * Stop recording the hotest files on a file system.
350 * Requires that the hfc_mutex be held.
353 hfs_recording_stop(struct hfsmount
*hfsmp
)
355 hotfile_data_t
*hotdata
;
356 hotfilelist_t
*listp
;
359 enum hfc_stage newstage
= HFC_IDLE
;
362 if (hfsmp
->hfc_stage
!= HFC_RECORDING
)
365 hfsmp
->hfc_stage
= HFC_BUSY
;
367 hotfiles_collect(hfsmp
);
371 * Convert hot file data into a simple file id list....
373 * then dump the sample data
376 printf("hfs: end of hot file recording on %s\n", hfsmp
->vcbVN
);
378 hotdata
= hfsmp
->hfc_recdata
;
381 hfsmp
->hfc_recdata
= NULL
;
382 hfsmp
->hfc_stage
= HFC_EVALUATION
;
383 wakeup((caddr_t
)&hfsmp
->hfc_stage
);
386 printf("hfs: curentries: %d\n", hotdata
->activefiles
);
389 * If no hot files recorded then we're done.
391 if (hotdata
->rootentry
== NULL
) {
396 /* Open the B-tree file for writing... */
397 if (hfsmp
->hfc_filevp
)
398 panic("hfs_recording_stop: hfc_filevp exists (vp = %p)", hfsmp
->hfc_filevp
);
400 error
= hfc_btree_open(hfsmp
, &hfsmp
->hfc_filevp
);
406 * Age the previous set of clustered hot files.
408 error
= hotfiles_age(hfsmp
);
410 (void) hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
411 hfsmp
->hfc_filevp
= NULL
;
416 * Create a sorted list of hotest files.
418 size
= sizeof(hotfilelist_t
);
419 size
+= sizeof(hotfileinfo_t
) * (hotdata
->activefiles
- 1);
420 listp
= hfs_mallocz(size
);
421 listp
->hfl_size
= size
;
423 hf_getsortedlist(hotdata
, listp
); /* NOTE: destroys hot file tree! */
425 listp
->hfl_duration
= tv
.tv_sec
- hfsmp
->hfc_timebase
;
426 hfs_assert(!hfsmp
->hfc_filelist
);
427 hfsmp
->hfc_filelist
= listp
;
430 * Account for duplicates.
432 error
= hotfiles_refine(hfsmp
);
434 (void) hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
435 hfsmp
->hfc_filevp
= NULL
;
440 * Compute the amount of space to reclaim...
442 if (listp
->hfl_totalblocks
> hfs_hotfile_cur_freeblks(hfsmp
)) {
443 listp
->hfl_reclaimblks
=
444 MIN(listp
->hfl_totalblocks
, hfsmp
->hfs_hotfile_maxblks
) -
445 hfsmp
->hfs_hotfile_freeblks
;
447 printf("hfs_recording_stop: need to reclaim %d blocks\n", listp
->hfl_reclaimblks
);
449 if (listp
->hfl_reclaimblks
)
450 newstage
= HFC_EVICTION
;
452 newstage
= HFC_ADOPTION
;
454 newstage
= HFC_ADOPTION
;
457 if (newstage
== HFC_ADOPTION
&& listp
->hfl_totalblocks
== 0) {
458 (void) hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
459 hfsmp
->hfc_filevp
= NULL
;
464 if (newstage
== HFC_EVICTION
)
465 printf("hfs: evicting coldest files\n");
466 else if (newstage
== HFC_ADOPTION
)
467 printf("hfs: adopting hotest files\n");
469 hfs_free(hotdata
, hotdata
->size
);
471 hfsmp
->hfc_stage
= newstage
;
472 wakeup((caddr_t
)&hfsmp
->hfc_stage
);
477 save_btree_user_info(struct hfsmount
*hfsmp
)
479 HotFilesInfo hotfileinfo
;
483 hotfileinfo
.magic
= SWAP_BE32 (HFC_MAGIC
);
484 hotfileinfo
.version
= SWAP_BE32 (HFC_VERSION
);
485 hotfileinfo
.duration
= SWAP_BE32 (HFC_DEFAULT_DURATION
);
486 hotfileinfo
.timebase
= SWAP_BE32 (hfsmp
->hfc_timebase
);
487 hotfileinfo
.timeleft
= SWAP_BE32 (hfsmp
->hfc_timeout
- tv
.tv_sec
);
488 hotfileinfo
.threshold
= SWAP_BE32 (HFC_MINIMUM_TEMPERATURE
);
489 hotfileinfo
.maxfileblks
= SWAP_BE32 (HFC_MAXIMUM_FILESIZE
/ HFSTOVCB(hfsmp
)->blockSize
);
490 if (hfsmp
->hfs_flags
& HFS_CS_HOTFILE_PIN
) {
491 hotfileinfo
.usedblocks
= SWAP_BE32 (hfsmp
->hfs_hotfile_maxblks
- hfs_hotfile_cur_freeblks(hfsmp
));
493 printf("hfs: %s: saving usedblocks = %d (timeleft: %d; timeout %ld)\n", hfsmp
->vcbVN
, (hfsmp
->hfs_hotfile_maxblks
- hfsmp
->hfs_hotfile_freeblks
),
494 SWAP_BE32(hotfileinfo
.timeleft
), hfsmp
->hfc_timeout
);
497 hotfileinfo
.maxfilecnt
= SWAP_BE32 (HFC_DEFAULT_FILE_COUNT
);
499 strlcpy((char *)hotfileinfo
.tag
, hfc_tag
, sizeof hotfileinfo
.tag
);
500 (void) BTSetUserData(VTOF(hfsmp
->hfc_filevp
), &hotfileinfo
, sizeof(hotfileinfo
));
504 * Suspend recording the hotest files on a file system.
507 hfs_recording_suspend(struct hfsmount
*hfsmp
)
509 hotfile_data_t
*hotdata
= NULL
;
512 if (hfsmp
->hfc_stage
== HFC_DISABLED
)
515 lck_mtx_lock(&hfsmp
->hfc_mutex
);
519 * A suspend can occur during eval/evict/adopt stage.
520 * In that case we would need to write out info and
521 * flush our HFBT vnode. Currently we just bail.
524 hotdata
= hfsmp
->hfc_recdata
;
525 if (hotdata
== NULL
|| hfsmp
->hfc_stage
!= HFC_RECORDING
) {
529 hfsmp
->hfc_stage
= HFC_BUSY
;
532 printf("hfs: suspend hot file recording on %s\n", hfsmp
->vcbVN
);
534 error
= hfc_btree_open(hfsmp
, &hfsmp
->hfc_filevp
);
536 printf("hfs_recording_suspend: err %d opening btree\n", error
);
540 if (hfs_start_transaction(hfsmp
) != 0) {
543 if (hfs_lock(VTOC(hfsmp
->hfc_filevp
), HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
) != 0) {
544 goto end_transaction
;
547 save_btree_user_info(hfsmp
);
549 hfs_unlock(VTOC(hfsmp
->hfc_filevp
));
552 hfs_end_transaction(hfsmp
);
555 if (hfsmp
->hfc_filevp
) {
556 (void) hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
557 hfsmp
->hfc_filevp
= NULL
;
560 hfs_free(hotdata
, hotdata
->size
);
561 hfsmp
->hfc_recdata
= NULL
;
563 hfsmp
->hfc_stage
= HFC_DISABLED
;
564 wakeup((caddr_t
)&hfsmp
->hfc_stage
);
566 lck_mtx_unlock(&hfsmp
->hfc_mutex
);
572 reset_file_ids(struct hfsmount
*hfsmp
, uint32_t *fileid_table
, int num_ids
)
576 for(i
=0; i
< num_ids
; i
++) {
579 error
= hfs_vget(hfsmp
, fileid_table
[i
], &vp
, 0, 0);
581 if (error
== ENOENT
) {
583 continue; /* stale entry, go to next */
588 // hfs_vget returns a locked cnode so no need to lock here
590 if ((hfsmp
->hfs_flags
& HFS_CS_HOTFILE_PIN
) && (VTOC(vp
)->c_attr
.ca_recflags
& kHFSFastDevPinnedMask
)) {
591 error
= hfs_pin_vnode(hfsmp
, vp
, HFS_UNPIN_IT
, NULL
);
595 * The updates to the catalog must be journaled
597 hfs_start_transaction(hfsmp
);
600 // turn off _all_ the hotfile related bits since we're resetting state
602 if (VTOC(vp
)->c_attr
.ca_recflags
& kHFSFastDevCandidateMask
) {
603 vnode_clearfastdevicecandidate(vp
);
606 VTOC(vp
)->c_attr
.ca_recflags
&= ~(kHFSFastDevPinnedMask
|kHFSDoNotFastDevPinMask
|kHFSFastDevCandidateMask
|kHFSAutoCandidateMask
);
607 VTOC(vp
)->c_flag
|= C_MODIFIED
;
611 hfs_end_transaction(hfsmp
);
613 hfs_unlock(VTOC(vp
));
619 flag_hotfile(struct hfsmount
*hfsmp
, const char *filename
)
621 struct vnode
*dvp
= NULL
, *fvp
= NULL
;
622 vfs_context_t ctx
= vfs_context_kernel();
625 const char *orig_fname
= filename
;
627 if (filename
== NULL
) {
631 fname_len
= strlen(filename
); // do NOT include the trailing '\0' so that we break out of the loop below
633 error
= hfs_vfs_root(HFSTOVFS(hfsmp
), &dvp
, ctx
);
638 /* At this point, 'dvp' must be considered iocounted */
642 while (ptr
< (orig_fname
+ fname_len
- 1)) {
643 for(; ptr
< (orig_fname
+ fname_len
) && *ptr
&& *ptr
!= '/'; ptr
++) {
644 /* just keep advancing till we reach the end of the string or a slash */
647 struct componentname cname
= {
648 .cn_nameiop
= LOOKUP
,
649 .cn_flags
= ISLASTCN
,
650 .cn_pnbuf
= __DECONST(char *, orig_fname
),
651 .cn_nameptr
= __DECONST(char *, filename
),
652 .cn_pnlen
= fname_len
,
653 .cn_namelen
= ptr
- filename
656 struct vnop_lookup_args ap
= {
663 error
= hfs_vnop_lookup(&ap
);
666 * If 'dvp' is non-NULL, then it has an iocount. Make sure to release it
667 * before bailing out. VNOP_LOOKUP could legitimately return ENOENT
668 * if the item didn't exist or if we raced with a delete.
677 if (ptr
< orig_fname
+ fname_len
- 1) {
679 // we've got a multi-part pathname so drop the ref on the dir,
680 // make dvp become what we just looked up, and advance over
681 // the slash character in the pathname to get to the next part
688 filename
= ++ptr
; // skip the slash character
697 struct cnode
*cp
= VTOC(fvp
);
698 if ((error
= hfs_lock(cp
, HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
)) != 0) {
702 hfs_start_transaction(hfsmp
);
704 cp
->c_attr
.ca_recflags
|= (kHFSFastDevCandidateMask
|kHFSAutoCandidateMask
);
705 cp
->c_flag
|= C_MODIFIED
;
709 hfs_end_transaction(hfsmp
);
712 //printf("hfs: flagged /%s with the fast-dev-candidate|auto-candidate flags\n", filename);
731 hfs_setup_default_cf_hotfiles(struct hfsmount
*hfsmp
)
733 const char *system_default_hotfiles
[] = {
737 "private/var/db/dyld"
741 for(i
=0; i
< (int)(sizeof(system_default_hotfiles
)/sizeof(char *)); i
++) {
742 flag_hotfile(hfsmp
, system_default_hotfiles
[i
]);
747 #define NUM_FILE_RESET_IDS 4096 // so we allocate 16k to hold file-ids
750 hfs_hotfile_reset(struct hfsmount
*hfsmp
)
753 CatalogRecord
* datap
;
755 BTScanState scanstate
;
756 BTreeIterator
* iterator
= NULL
;
757 FSBufferDescriptor record
;
761 uint32_t *fileids
=NULL
;
762 int cur_id_index
= 0;
764 int cleared
= 0; /* debug variables */
769 printf("hfs: %s: %s\n", hfsmp
->vcbVN
, __FUNCTION__
);
772 iterator
= hfs_mallocz(sizeof(*iterator
));
774 fileids
= hfs_malloc(NUM_FILE_RESET_IDS
* sizeof(uint32_t));
776 record
.bufferAddress
= &data
;
777 record
.itemSize
= sizeof(u_int32_t
);
778 record
.itemCount
= 1;
781 * Get ready to scan the Catalog file.
783 error
= BTScanInitialize(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), 0, 0, 0,
784 kCatSearchBufferSize
, &scanstate
);
786 printf("hfs_hotfile_reset: err %d BTScanInit\n", error
);
791 * Visit all the catalog btree leaf records, clearing any that have the
792 * HotFileCached bit set.
795 error
= BTScanNextRecord(&scanstate
, 0, (void **)&keyp
, (void **)&datap
, &dataSize
);
797 if (error
== btNotFound
)
800 printf("hfs_hotfile_reset: err %d BTScanNext\n", error
);
804 if (datap
->recordType
== kHFSPlusFolderRecord
&& (dataSize
== sizeof(HFSPlusCatalogFolder
))) {
805 HFSPlusCatalogFolder
*dirp
= (HFSPlusCatalogFolder
*)datap
;
809 if ((dirp
->flags
& (kHFSFastDevPinnedMask
|kHFSDoNotFastDevPinMask
|kHFSFastDevCandidateMask
|kHFSAutoCandidateMask
)) == 0) {
813 cnid
= dirp
->folderID
;
814 } else if ((datap
->recordType
== kHFSPlusFileRecord
) && (dataSize
== sizeof(HFSPlusCatalogFile
))) {
815 HFSPlusCatalogFile
*filep
= (HFSPlusCatalogFile
*)datap
;
820 * If the file doesn't have any of the HotFileCached bits set, ignore it.
822 if ((filep
->flags
& (kHFSFastDevPinnedMask
|kHFSDoNotFastDevPinMask
|kHFSFastDevCandidateMask
|kHFSAutoCandidateMask
)) == 0) {
826 cnid
= filep
->fileID
;
831 /* Skip over journal files. */
832 if (cnid
== hfsmp
->hfs_jnlfileid
|| cnid
== hfsmp
->hfs_jnlinfoblkid
) {
837 // Just record the cnid of the file for now. We will modify it separately
838 // because we can't modify the catalog while we're scanning it.
840 fileids
[cur_id_index
++] = cnid
;
841 if (cur_id_index
>= NUM_FILE_RESET_IDS
) {
843 // We're over the limit of file-ids so we have to terminate this
844 // scan, go modify all the catalog records, then restart the scan.
845 // This is required because it's not permissible to modify the
846 // catalog while scanning it.
848 (void) BTScanTerminate(&scanstate
, &data
, &data
, &data
);
850 reset_file_ids(hfsmp
, fileids
, cur_id_index
);
851 cleared
+= cur_id_index
;
855 error
= BTScanInitialize(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), 0, 0, 0,
856 kCatSearchBufferSize
, &scanstate
);
858 printf("hfs_hotfile_reset: err %d BTScanInit\n", error
);
866 reset_file_ids(hfsmp
, fileids
, cur_id_index
);
867 cleared
+= cur_id_index
;
871 printf("hfs: cleared HotFileCache related bits on %d files out of %d (dircount %d)\n", cleared
, filecount
, dircount
);
873 (void) BTScanTerminate(&scanstate
, &data
, &data
, &data
);
876 hfs_free(fileids
, NUM_FILE_RESET_IDS
* sizeof(uint32_t));
877 hfs_free(iterator
, sizeof(*iterator
));
880 // If the hotfile btree exists, delete it. We need to open
881 // it to be able to delete it because we need the hfc_filevp
884 error
= hfc_btree_open_ext(hfsmp
, &hfsmp
->hfc_filevp
, 1);
886 printf("hfs: hotfile_reset: deleting existing hotfile btree\n");
887 hfc_btree_delete(hfsmp
);
890 if (hfsmp
->hfc_filevp
) {
891 (void) hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
892 hfsmp
->hfc_filevp
= NULL
;
895 hfsmp
->hfs_hotfile_blk_adjust
= 0;
896 hfsmp
->hfs_hotfile_freeblks
= hfsmp
->hfs_hotfile_maxblks
;
901 // This should ONLY be called by hfs_recording_init() and the special fsctl.
903 // We assume that the hotfile btree is already opened.
906 hfs_hotfile_repin_files(struct hfsmount
*hfsmp
)
908 BTreeIterator
* iterator
= NULL
;
910 filefork_t
* filefork
;
913 enum hfc_stage stage
;
914 uint32_t pinned_blocks
;
915 uint32_t num_files
=0, nrsrc
=0;
916 uint32_t total_pinned
=0;
918 if (!(hfsmp
->hfs_flags
& HFS_CS_HOTFILE_PIN
) || !hfsmp
->hfc_filevp
) {
920 // this is only meaningful if we're pinning hotfiles
921 // (as opposed to the regular form of hotfiles that
922 // get relocated to the hotfile zone)
928 printf("hfs: %s: %s\n", hfsmp
->vcbVN
, __FUNCTION__
);
931 if (hfs_lock(VTOC(hfsmp
->hfc_filevp
), HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
) != 0) {
936 iterator
= hfs_mallocz(sizeof(*iterator
));
938 stage
= hfsmp
->hfc_stage
;
939 hfsmp
->hfc_stage
= HFC_BUSY
;
941 bt_op
= kBTreeFirstRecord
;
943 key
= (HotFileKey
*) &iterator
->key
;
945 filefork
= VTOF(hfsmp
->hfc_filevp
);
952 * Obtain the first record (ie the coldest one).
954 if (BTIterateRecord(filefork
, bt_op
, iterator
, NULL
, NULL
) != 0) {
959 if (key
->keyLength
!= HFC_KEYLENGTH
) {
960 // printf("hfs: hotfiles_repin_files: invalid key length %d\n", key->keyLength);
964 if (key
->temperature
== HFC_LOOKUPTAG
) {
965 // ran into thread records in the hotfile btree
971 // Just lookup the records in the catalog and pin the direct
972 // mapped extents. Faster than instantiating full vnodes
973 // (and thereby thrashing the system vnode cache).
975 struct cat_desc fdesc
;
976 struct cat_attr attr
;
977 struct cat_fork fork
;
978 uint8_t forktype
= 0;
980 lockflags
= hfs_systemfile_lock(hfsmp
, (SFL_CATALOG
| SFL_EXTENTS
), HFS_SHARED_LOCK
);
982 * Snoop the cnode hash to find out if the item we want is in-core already.
984 * We largely expect this function to fail (the items we want are probably not in the hash).
985 * we use the special variant which bails out as soon as it finds a vnode (even if it is
986 * marked as open-unlinked or actually removed on-disk. If we find a vnode, then we
987 * release the systemfile locks and go through the pin-vnode path instead.
989 if (hfs_chash_snoop (hfsmp
, key
->fileID
, 1, NULL
, NULL
) == 0) {
992 /* unlock immediately and go through the in-core path */
993 hfs_systemfile_unlock(hfsmp
, lockflags
);
996 error
= hfs_getvnode_and_pin (hfsmp
, key
->fileID
, &pinned_blocks
);
998 /* if ENOENT, then it was deleted in the catalog. Remove from our hotfiles tracking */
999 if (error
== ENOENT
) {
1000 hfc_btree_delete_record(hfsmp
, iterator
, key
);
1002 /* other errors, just ignore and move on with life */
1005 total_pinned
+= pinned_blocks
;
1012 /* If we get here, we're still holding the systemfile locks */
1013 error
= cat_idlookup(hfsmp
, key
->fileID
, 1, 0, &fdesc
, &attr
, &fork
);
1016 // this file system could have been mounted while booted from a
1017 // different partition and thus the hotfile btree would not have
1018 // been maintained. thus a file that was hotfile cached could
1019 // have been deleted while booted from a different partition which
1020 // means we need to delete it from the hotfile btree.
1022 // block accounting is taken care of at the end: we re-assign
1023 // hfsmp->hfs_hotfile_freeblks based on how many blocks we actually
1026 hfc_btree_delete_record(hfsmp
, iterator
, key
);
1031 if (fork
.cf_size
== 0) {
1032 // hmmm, the data is probably in the resource fork (aka a compressed file)
1033 error
= cat_idlookup(hfsmp
, key
->fileID
, 1, 1, &fdesc
, &attr
, &fork
);
1035 hfc_btree_delete_record(hfsmp
, iterator
, key
);
1044 /* Can't release the catalog /extents lock yet, we may need to go find the overflow blocks */
1045 error
= hfs_pin_extent_record (hfsmp
, fork
.cf_extents
, &pinned_blocks
);
1047 goto next
; //skip to next
1049 /* add in the blocks from the inline 8 */
1050 total_pinned
+= pinned_blocks
;
1053 /* Could this file have overflow extents? */
1054 if (fork
.cf_extents
[kHFSPlusExtentDensity
-1].startBlock
) {
1055 /* better pin them, too */
1056 error
= hfs_pin_overflow_extents (hfsmp
, key
->fileID
, forktype
, &pinned_blocks
);
1058 /* If we fail to pin all of the overflow extents, then just skip to the next file */
1064 if (pinned_blocks
) {
1065 /* now add in any overflow also */
1066 total_pinned
+= pinned_blocks
;
1071 hfs_systemfile_unlock(hfsmp
, lockflags
);
1074 bt_op
= kBTreeNextRecord
;
1079 printf("hfs: hotfiles_repin_files: re-pinned %d files (nrsrc %d, total pinned %d blks; freeblock %d, maxblocks %d, calculated free: %d)\n",
1080 num_files
, nrsrc
, total_pinned
, hfsmp
->hfs_hotfile_freeblks
, hfsmp
->hfs_hotfile_maxblks
,
1081 hfsmp
->hfs_hotfile_maxblks
- total_pinned
);
1084 // make sure this is accurate based on how many blocks we actually pinned
1086 hfsmp
->hfs_hotfile_freeblks
= hfsmp
->hfs_hotfile_maxblks
- total_pinned
;
1088 hfs_unlock(VTOC(hfsmp
->hfc_filevp
));
1090 hfs_free(iterator
, sizeof(*iterator
));
1091 hfsmp
->hfc_stage
= stage
;
1092 wakeup((caddr_t
)&hfsmp
->hfc_stage
);
1097 hfs_repin_hotfiles(struct hfsmount
*hfsmp
)
1099 int error
, need_close
;
1101 lck_mtx_lock(&hfsmp
->hfc_mutex
);
1103 if (hfsmp
->hfc_filevp
== NULL
) {
1104 error
= hfc_btree_open(hfsmp
, &hfsmp
->hfc_filevp
);
1108 printf("hfs: failed to open the btree err=%d. Unable to re-pin hotfiles.\n", error
);
1109 lck_mtx_unlock(&hfsmp
->hfc_mutex
);
1116 hfs_pin_vnode(hfsmp
, hfsmp
->hfc_filevp
, HFS_PIN_IT
, NULL
);
1118 hfs_hotfile_repin_files(hfsmp
);
1121 (void) hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
1122 hfsmp
->hfc_filevp
= NULL
;
1125 lck_mtx_unlock(&hfsmp
->hfc_mutex
);
1129 * For a given file ID, find and pin all of its overflow extents to the underlying CS
1130 * device. Assumes that the extents overflow b-tree is locked for the duration of this call.
1132 * Emit the number of blocks pinned in output argument 'pinned'
1134 * Return success or failure (errno) in return value.
1137 int hfs_pin_overflow_extents (struct hfsmount
*hfsmp
, uint32_t fileid
,
1138 uint8_t forktype
, uint32_t *pinned
) {
1140 struct BTreeIterator
*ext_iter
= NULL
;
1141 ExtentKey
*ext_key_ptr
= NULL
;
1142 ExtentRecord ext_data
;
1143 FSBufferDescriptor btRecord
;
1144 uint16_t btRecordSize
;
1147 uint32_t pinned_blocks
= 0;
1150 ext_iter
= hfs_mallocz(sizeof (*ext_iter
));
1152 BTInvalidateHint (ext_iter
);
1153 ext_key_ptr
= (ExtentKey
*)&ext_iter
->key
;
1154 btRecord
.bufferAddress
= &ext_data
;
1155 btRecord
.itemCount
= 1;
1158 * This is like when you delete a file; we don't actually need most of the search machinery because
1159 * we are going to need all of the extent records that belong to this file (for a given fork type),
1160 * so we might as well use a straight-up iterator.
1162 * Position the B-Tree iterator at the first record with this file ID
1164 btRecord
.itemSize
= sizeof (HFSPlusExtentRecord
);
1165 ext_key_ptr
->hfsPlus
.keyLength
= kHFSPlusExtentKeyMaximumLength
;
1166 ext_key_ptr
->hfsPlus
.forkType
= forktype
;
1167 ext_key_ptr
->hfsPlus
.pad
= 0;
1168 ext_key_ptr
->hfsPlus
.fileID
= fileid
;
1169 ext_key_ptr
->hfsPlus
.startBlock
= 0;
1171 error
= BTSearchRecord (VTOF(hfsmp
->hfs_extents_vp
), ext_iter
, &btRecord
, &btRecordSize
, ext_iter
);
1172 if (error
== btNotFound
) {
1173 /* empty b-tree, so that's ok. we'll fall out during error check below. */
1178 uint32_t found_fileid
;
1181 error
= BTIterateRecord (VTOF(hfsmp
->hfs_extents_vp
), kBTreeNextRecord
, ext_iter
, &btRecord
, &btRecordSize
);
1183 /* swallow it if it's btNotFound, otherwise just bail out */
1184 if (error
== btNotFound
)
1189 found_fileid
= ext_key_ptr
->hfsPlus
.fileID
;
1191 * We only do one fork type at a time. So if either the fork-type doesn't
1192 * match what we are looking for (resource or data), OR the file id doesn't match
1193 * which indicates that there's nothing more with this file ID as the key, then bail out
1195 if ((found_fileid
!= fileid
) || (ext_key_ptr
->hfsPlus
.forkType
!= forktype
)) {
1200 /* Otherwise, we now have an extent record. Process and pin all of the file extents. */
1202 error
= hfs_pin_extent_record (hfsmp
, ext_data
.hfsPlus
, &pblocks
);
1207 pinned_blocks
+= pblocks
;
1209 /* if 8th extent is empty, then bail out */
1210 if (ext_data
.hfsPlus
[kHFSPlusExtentDensity
-1].startBlock
== 0) {
1215 } // end extent-getting loop
1217 /* dump the iterator */
1218 hfs_free(ext_iter
, sizeof(*ext_iter
));
1222 * In the event that the file has no overflow extents, pinned_blocks
1223 * will never be updated, so we'll properly export 0 pinned blocks to caller
1225 *pinned
= pinned_blocks
;
1234 hfs_getvnode_and_pin (struct hfsmount
*hfsmp
, uint32_t fileid
, uint32_t *pinned
) {
1241 * Acquire the vnode for this file. This returns a locked cnode on success
1243 error
= hfs_vget(hfsmp
, fileid
, &vp
, 0, 0);
1245 /* It's possible the file was open-unlinked. In this case, we'll get ENOENT back. */
1250 * Symlinks that may have been inserted into the hotfile zone during a previous OS are now stuck
1251 * here. We do not want to move them.
1253 if (!vnode_isreg(vp
)) {
1254 hfs_unlock(VTOC(vp
));
1259 if (!(VTOC(vp
)->c_attr
.ca_recflags
& kHFSFastDevPinnedMask
)) {
1260 hfs_unlock(VTOC(vp
));
1265 error
= hfs_pin_vnode(hfsmp
, vp
, HFS_PIN_IT
, &pblocks
);
1270 hfs_unlock(VTOC(vp
));
1278 * Pins an HFS Extent record to the underlying CoreStorage. Assumes that Catalog & Extents overflow
1279 * B-trees are held locked, as needed.
1281 * Returns the number of blocks pinned in the output argument 'pinned'
1283 * Returns error status (0 || errno) in return value.
1285 static int hfs_pin_extent_record (struct hfsmount
*hfsmp
, HFSPlusExtentRecord extents
, uint32_t *pinned
) {
1290 if (pinned
== NULL
) {
1297 /* iterate through the extents */
1298 for ( i
= 0; i
< kHFSPlusExtentDensity
; i
++) {
1299 if (extents
[i
].startBlock
== 0) {
1303 error
= hfs_pin_block_range (hfsmp
, HFS_PIN_IT
, extents
[i
].startBlock
,
1304 extents
[i
].blockCount
);
1309 pb
+= extents
[i
].blockCount
;
1318 * Consume an HFS Plus on-disk catalog record and pin its blocks
1319 * to the underlying CS devnode.
1321 * NOTE: This is an important distinction!
1322 * This function takes in an HFSPlusCatalogFile* which is the actual
1323 * 200-some-odd-byte on-disk representation in the Catalog B-Tree (not
1324 * one of the run-time structs that we normally use.
1326 * This assumes that the catalog and extents-overflow btrees
1327 * are locked, at least in shared mode
1329 static int hfs_pin_catalog_rec (struct hfsmount
*hfsmp
, HFSPlusCatalogFile
*cfp
, int rsrc
) {
1330 uint32_t pinned_blocks
= 0;
1331 HFSPlusForkData
*forkdata
;
1333 uint8_t forktype
= 0;
1336 forkdata
= &cfp
->resourceFork
;
1340 forkdata
= &cfp
->dataFork
;
1343 uint32_t pblocks
= 0;
1345 /* iterate through the inline extents */
1346 error
= hfs_pin_extent_record (hfsmp
, forkdata
->extents
, &pblocks
);
1351 pinned_blocks
+= pblocks
;
1354 /* it may have overflow extents */
1355 if (forkdata
->extents
[kHFSPlusExtentDensity
-1].startBlock
!= 0) {
1356 error
= hfs_pin_overflow_extents (hfsmp
, cfp
->fileID
, forktype
, &pblocks
);
1358 pinned_blocks
+= pblocks
;
1360 hfsmp
->hfs_hotfile_freeblks
-= pinned_blocks
;
1370 hfs_recording_init(struct hfsmount
*hfsmp
)
1373 CatalogRecord
* datap
;
1375 HFSPlusCatalogFile
*filep
;
1376 BTScanState scanstate
;
1377 BTreeIterator
* iterator
= NULL
;
1378 FSBufferDescriptor record
;
1380 filefork_t
* filefork
;
1382 struct cat_attr cattr
;
1388 int started_scan
= 0;
1390 int inserted
= 0; /* debug variables */
1392 int uncacheable
= 0;
1395 * For now, only the boot volume is supported.
1397 if ((vfs_flags(HFSTOVFS(hfsmp
)) & MNT_ROOTFS
) == 0) {
1398 hfsmp
->hfc_stage
= HFC_DISABLED
;
1402 /* We grab the HFC mutex even though we're not fully mounted yet, just for orderliness */
1403 lck_mtx_lock (&hfsmp
->hfc_mutex
);
1406 * Tracking of hot files requires up-to-date access times.
1407 * So if access time updates are disabled, then we disable
1410 if (vfs_flags(HFSTOVFS(hfsmp
)) & MNT_NOATIME
) {
1411 hfsmp
->hfc_stage
= HFC_DISABLED
;
1412 lck_mtx_unlock (&hfsmp
->hfc_mutex
);
1417 // Check if we've been asked to suspend operation
1419 cnid
= GetFileInfo(HFSTOVCB(hfsmp
), kRootDirID
, ".hotfile-suspend", &cattr
, NULL
);
1421 printf("hfs: %s: %s: hotfiles explicitly disabled! remove /.hotfiles-suspend to re-enable\n", hfsmp
->vcbVN
, __FUNCTION__
);
1422 hfsmp
->hfc_stage
= HFC_DISABLED
;
1423 lck_mtx_unlock (&hfsmp
->hfc_mutex
);
1428 // Check if we've been asked to reset our state.
1430 cnid
= GetFileInfo(HFSTOVCB(hfsmp
), kRootDirID
, ".hotfile-reset", &cattr
, NULL
);
1432 hfs_hotfile_reset(hfsmp
);
1435 if (hfsmp
->hfs_flags
& HFS_CS_HOTFILE_PIN
) {
1437 // Cooperative Fusion (CF) systems use different constants
1438 // than traditional hotfile systems. These were picked after a bit of
1439 // experimentation - we can cache many more files on the
1440 // ssd in an CF system and we can do so more rapidly
1441 // so bump the limits considerably (and turn down the
1442 // duration so that it doesn't take weeks to adopt all
1445 hfc_default_file_count
= 20000;
1446 hfc_default_duration
= 300; // 5min
1447 hfc_max_file_count
= 50000;
1448 hfc_max_file_size
= (512ULL * 1024ULL * 1024ULL);
1452 * If the Hot File btree exists then metadata zone is ready.
1454 cnid
= GetFileInfo(HFSTOVCB(hfsmp
), kRootDirID
, HFC_FILENAME
, &cattr
, NULL
);
1455 if (cnid
!= 0 && S_ISREG(cattr
.ca_mode
)) {
1458 if (hfsmp
->hfc_stage
== HFC_DISABLED
)
1459 hfsmp
->hfc_stage
= HFC_IDLE
;
1460 hfsmp
->hfs_hotfile_freeblks
= 0;
1462 if ((hfsmp
->hfs_flags
& HFS_CS_HOTFILE_PIN
) && cattr
.ca_blocks
> 0) {
1464 // make sure the hotfile btree is pinned
1466 error
= hfc_btree_open(hfsmp
, &hfsmp
->hfc_filevp
);
1468 /* XXX: must fix hfs_pin_vnode too */
1469 hfs_pin_vnode(hfsmp
, hfsmp
->hfc_filevp
, HFS_PIN_IT
, NULL
);
1472 printf("hfs: failed to open the btree err=%d. Recreating hotfile btree.\n", error
);
1476 hfs_hotfile_repin_files(hfsmp
);
1478 if (hfsmp
->hfc_filevp
) {
1479 (void) hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
1480 hfsmp
->hfc_filevp
= NULL
;
1483 } else if (hfsmp
->hfs_flags
& HFS_CS_HOTFILE_PIN
) {
1484 // hmmm, the hotfile btree is zero bytes long? how odd. let's recreate it.
1485 printf("hfs: hotfile btree is zero bytes long?! recreating it.\n");
1490 /* don't forget to unlock the mutex */
1491 lck_mtx_unlock (&hfsmp
->hfc_mutex
);
1495 // open the hotfile btree file ignoring errors because
1496 // we need the vnode pointer for hfc_btree_delete() to
1497 // be able to do its work
1499 error
= hfc_btree_open_ext(hfsmp
, &hfsmp
->hfc_filevp
, 1);
1502 error
= hfc_btree_delete(hfsmp
);
1503 (void) hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
1504 hfsmp
->hfc_filevp
= NULL
;
1509 printf("hfs: %s: %s: creating the hotfile btree\n", hfsmp
->vcbVN
, __FUNCTION__
);
1510 if (hfs_start_transaction(hfsmp
) != 0) {
1511 lck_mtx_unlock (&hfsmp
->hfc_mutex
);
1515 /* B-tree creation must be journaled */
1518 error
= hfc_btree_create(hfsmp
, HFSTOVCB(hfsmp
)->blockSize
, HFC_DEFAULT_FILE_COUNT
);
1521 printf("hfs: Error %d creating hot file b-tree on %s \n", error
, hfsmp
->vcbVN
);
1523 goto recording_init_out
;
1526 hfs_end_transaction (hfsmp
);
1529 * Do a journal flush + flush track cache. We have to ensure that the async I/Os have been issued to the media
1530 * before proceeding.
1532 hfs_flush (hfsmp
, HFS_FLUSH_FULL
);
1534 /* now re-start a new transaction */
1535 if (hfs_start_transaction (hfsmp
) != 0) {
1536 lck_mtx_unlock (&hfsmp
->hfc_mutex
);
1542 * Open the Hot File B-tree file for writing.
1544 if (hfsmp
->hfc_filevp
)
1545 panic("hfs_recording_init: hfc_filevp exists (vp = %p)", hfsmp
->hfc_filevp
);
1547 error
= hfc_btree_open(hfsmp
, &hfsmp
->hfc_filevp
);
1550 printf("hfs: Error %d opening hot file b-tree on %s \n", error
, hfsmp
->vcbVN
);
1552 goto recording_init_out
;
1556 * This function performs work similar to namei; we must NOT hold the catalog lock while
1557 * calling it. This will decorate catalog records as being pinning candidates. (no hotfiles work)
1559 hfs_setup_default_cf_hotfiles(hfsmp
);
1562 * now grab the hotfiles b-tree vnode/cnode lock first, as it is not classified as a systemfile.
1564 if (hfs_lock(VTOC(hfsmp
->hfc_filevp
), HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
) != 0) {
1566 (void) hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
1567 /* zero it out to avoid pinning later on */
1568 hfsmp
->hfc_filevp
= NULL
;
1569 goto recording_init_out
;
1572 iterator
= hfs_mallocz(sizeof(*iterator
));
1574 key
= (HotFileKey
*) &iterator
->key
;
1575 key
->keyLength
= HFC_KEYLENGTH
;
1577 record
.bufferAddress
= &data
;
1578 record
.itemSize
= sizeof(u_int32_t
);
1579 record
.itemCount
= 1;
1582 printf("hfs: Evaluating space for \"%s\" metadata zone... (freeblks %d)\n", HFSTOVCB(hfsmp
)->vcbVN
,
1583 hfsmp
->hfs_hotfile_freeblks
);
1587 * Get ready to scan the Catalog file. We explicitly do NOT grab the catalog lock because
1588 * we're fully single-threaded at the moment (by virtue of being called during mount()),
1589 * and if we have to grow the hotfile btree, then we would need to grab the catalog lock
1590 * and if we take a shared lock here, it would deadlock (see <rdar://problem/21486585>)
1592 * We already started a transaction so we should already be holding the journal lock at this point.
1593 * Note that we have to hold the journal lock / start a txn BEFORE the systemfile locks.
1596 error
= BTScanInitialize(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), 0, 0, 0,
1597 kCatSearchBufferSize
, &scanstate
);
1599 printf("hfs_recording_init: err %d BTScanInit\n", error
);
1601 /* drop the systemfile locks */
1602 hfs_unlock(VTOC(hfsmp
->hfc_filevp
));
1604 (void) hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
1606 /* zero it out to avoid pinning */
1607 hfsmp
->hfc_filevp
= NULL
;
1608 goto recording_init_out
;
1613 filefork
= VTOF(hfsmp
->hfc_filevp
);
1615 starting_temp
= random() % HF_TEMP_RANGE
;
1618 * Visit all the catalog btree leaf records. We have to hold the catalog lock to do this.
1620 * NOTE: The B-Tree scanner reads from the media itself. Under normal circumstances it would be
1621 * fine to simply use b-tree routines to read blocks that correspond to b-tree nodes, because the
1622 * block cache is going to ensure you always get the cached copy of a block (even if a journal
1623 * txn has modified one of those blocks). That is NOT true when
1624 * using the scanner. In particular, it will always read whatever is on-disk. So we have to ensure
1625 * that the journal has flushed and that the async I/Os to the metadata files have been issued.
1628 error
= BTScanNextRecord(&scanstate
, 0, (void **)&keyp
, (void **)&datap
, &dataSize
);
1630 if (error
== btNotFound
)
1633 printf("hfs_recording_init: err %d BTScanNext\n", error
);
1636 if ((datap
->recordType
!= kHFSPlusFileRecord
) ||
1637 (dataSize
!= sizeof(HFSPlusCatalogFile
))) {
1640 filep
= (HFSPlusCatalogFile
*)datap
;
1643 if (hfsmp
->hfs_flags
& HFS_CS_HOTFILE_PIN
) {
1644 if (filep
->flags
& kHFSDoNotFastDevPinMask
) {
1649 // If the file does not have the FastDevPinnedMask set, we
1650 // can ignore it and just go to the next record.
1652 if ((filep
->flags
& kHFSFastDevPinnedMask
) == 0) {
1655 } else if (filep
->dataFork
.totalBlocks
== 0) {
1660 * On a regular hdd, any file that has blocks inside
1661 * the hot file space is recorded for later eviction.
1663 * For now, resource forks are ignored.
1665 * We don't do this on CF systems as there is no real
1666 * hotfile area - we just pin/unpin blocks belonging to
1667 * interesting files.
1669 if (!(hfsmp
->hfs_flags
& HFS_CS_HOTFILE_PIN
) && !hotextents(hfsmp
, &filep
->dataFork
.extents
[0])) {
1672 cnid
= filep
->fileID
;
1674 /* Skip over journal files and the hotfiles B-Tree file. */
1675 if (cnid
== hfsmp
->hfs_jnlfileid
1676 || cnid
== hfsmp
->hfs_jnlinfoblkid
1677 || cnid
== VTOC(hfsmp
->hfc_filevp
)->c_fileid
) {
1681 * XXX - need to skip quota files as well.
1686 if (hfsmp
->hfs_flags
& HFS_CS_HOTFILE_PIN
) {
1689 temp
= (uint32_t)starting_temp
++;
1690 if (filep
->flags
& kHFSAutoCandidateMask
) {
1691 temp
+= MAX_NORMAL_TEMP
;
1694 /* use the data fork by default */
1695 if (filep
->dataFork
.totalBlocks
== 0) {
1697 * but if empty, switch to rsrc as its likely
1703 error
= hfs_pin_catalog_rec (hfsmp
, filep
, rsrc
);
1708 temp
= HFC_MINIMUM_TEMPERATURE
;
1711 /* Insert a hot file entry. */
1712 key
->keyLength
= HFC_KEYLENGTH
;
1713 key
->temperature
= temp
;
1717 error
= BTInsertRecord(filefork
, iterator
, &record
, record
.itemSize
);
1719 printf("hfs_recording_init: BTInsertRecord failed %d (fileid %d)\n", error
, key
->fileID
);
1720 error
= MacToVFSError(error
);
1724 /* Insert the corresponding thread record. */
1725 key
->keyLength
= HFC_KEYLENGTH
;
1726 key
->temperature
= HFC_LOOKUPTAG
;
1730 error
= BTInsertRecord(filefork
, iterator
, &record
, record
.itemSize
);
1732 printf("hfs_recording_init: BTInsertRecord failed %d (fileid %d)\n", error
, key
->fileID
);
1733 error
= MacToVFSError(error
);
1737 } // end catalog iteration loop
1739 save_btree_user_info(hfsmp
);
1740 (void) BTFlushPath(filefork
);
1744 /* Unlock first, then pin after releasing everything else */
1745 if (hfsmp
->hfc_filevp
) {
1746 hfs_unlock (VTOC(hfsmp
->hfc_filevp
));
1750 (void) BTScanTerminate (&scanstate
, &data
, &data
, &data
);
1754 hfs_end_transaction(hfsmp
);
1758 printf("hfs: %d files identified out of %d (freeblocks is now: %d)\n", inserted
, filecount
, hfsmp
->hfs_hotfile_freeblks
);
1760 printf("hfs: %d files were marked as uncacheable\n", uncacheable
);
1765 hfs_free(iterator
, sizeof(*iterator
));
1767 if (hfsmp
->hfc_filevp
) {
1768 if (hfsmp
->hfs_flags
& HFS_CS_HOTFILE_PIN
) {
1769 hfs_pin_vnode(hfsmp
, hfsmp
->hfc_filevp
, HFS_PIN_IT
, NULL
);
1771 (void) hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
1772 hfsmp
->hfc_filevp
= NULL
;
1776 hfsmp
->hfc_stage
= HFC_IDLE
;
1778 /* Finally, unlock the HFC mutex */
1779 lck_mtx_unlock (&hfsmp
->hfc_mutex
);
1785 * Use sync to perform ocassional background work.
1788 hfs_hotfilesync(struct hfsmount
*hfsmp
, vfs_context_t ctx
)
1790 if (hfsmp
->hfc_stage
) {
1793 lck_mtx_lock(&hfsmp
->hfc_mutex
);
1795 switch (hfsmp
->hfc_stage
) {
1797 (void) hfs_recording_start(hfsmp
);
1802 if (tv
.tv_sec
> hfsmp
->hfc_timeout
)
1803 (void) hfs_recording_stop(hfsmp
);
1807 (void) hotfiles_evict(hfsmp
, ctx
);
1811 (void) hotfiles_adopt(hfsmp
);
1817 lck_mtx_unlock(&hfsmp
->hfc_mutex
);
1823 * Add a hot file to the recording list.
1825 * This can happen when a hot file gets reclaimed or at the
1826 * end of the recording period for any active hot file.
1828 * NOTE: Since both the data and resource fork can be hot,
1829 * there can be two entries for the same file id.
1831 * Note: the cnode is locked on entry.
1834 hfs_addhotfile(struct vnode
*vp
)
1840 if (hfsmp
->hfc_stage
!= HFC_RECORDING
)
1843 lck_mtx_lock(&hfsmp
->hfc_mutex
);
1844 error
= hfs_addhotfile_internal(vp
);
1845 lck_mtx_unlock(&hfsmp
->hfc_mutex
);
1850 hf_ignore_process(const char *pname
, size_t maxlen
)
1852 if ( strncmp(pname
, "mds", maxlen
) == 0
1853 || strncmp(pname
, "mdworker", maxlen
) == 0
1854 || strncmp(pname
, "mds_stores", maxlen
) == 0
1855 || strncmp(pname
, "makewhatis", maxlen
) == 0) {
1864 hfs_addhotfile_internal(struct vnode
*vp
)
1866 hotfile_data_t
*hotdata
;
1867 hotfile_entry_t
*entry
;
1871 u_int32_t temperature
;
1874 if (hfsmp
->hfc_stage
!= HFC_RECORDING
)
1878 * Only regular files are eligible for hotfiles addition.
1880 * Symlinks were previously added to the list and may exist in
1881 * extant hotfiles regions, but no new ones will be added, and no
1882 * symlinks will now be relocated/evicted from the hotfiles region.
1884 if (!vnode_isreg(vp
) || vnode_issystem(vp
)) {
1888 /* Skip resource forks for now. */
1889 if (VNODE_IS_RSRC(vp
)) {
1892 if ((hotdata
= hfsmp
->hfc_recdata
) == NULL
) {
1898 if (cp
->c_attr
.ca_recflags
& (kHFSFastDevPinnedMask
|kHFSDoNotFastDevPinMask
)) {
1899 // it's already a hotfile or can't be a hotfile...
1903 if (vnode_isdir(vp
) || vnode_issystem(vp
) || (cp
->c_flag
& (C_DELETED
| C_NOEXISTS
))) {
1907 if ((hfsmp
->hfs_flags
& HFS_CS_HOTFILE_PIN
) && vnode_isfastdevicecandidate(vp
)) {
1909 // On cooperative fusion (CF) systems we have different criteria for whether something
1910 // can be pinned to the ssd.
1912 if (cp
->c_flag
& (C_DELETED
|C_NOEXISTS
)) {
1914 // dead files are definitely not worth caching
1917 } else if (ffp
->ff_blocks
== 0 && !(cp
->c_bsdflags
& UF_COMPRESSED
) && !(cp
->c_attr
.ca_recflags
& kHFSFastDevCandidateMask
)) {
1919 // empty files aren't worth caching but compressed ones might be, as are
1920 // newly created files that live in WorthCaching directories...
1927 proc_selfname(pname
, sizeof(pname
));
1928 if (hf_ignore_process(pname
, sizeof(pname
))) {
1929 // ignore i/o's from certain system daemons
1933 temperature
= cp
->c_fileid
; // in memory we just keep it sorted by file-id
1935 // the normal hard drive based hotfile checks
1936 if ((ffp
->ff_bytesread
== 0) ||
1937 (ffp
->ff_blocks
== 0) ||
1938 (ffp
->ff_size
== 0) ||
1939 (ffp
->ff_blocks
> hotdata
->maxblocks
) ||
1940 (cp
->c_bsdflags
& (UF_NODUMP
| UF_COMPRESSED
)) ||
1941 (cp
->c_atime
< hfsmp
->hfc_timebase
)) {
1945 temperature
= ffp
->ff_bytesread
/ ffp
->ff_size
;
1946 if (temperature
< hotdata
->threshold
) {
1952 * If there is room or this file is hotter than
1953 * the coldest one then add it to the list.
1956 if ((hotdata
->activefiles
< hfsmp
->hfc_maxfiles
) ||
1957 (hotdata
->coldest
== NULL
) ||
1958 (temperature
>= hotdata
->coldest
->temperature
)) {
1959 ++hotdata
->refcount
;
1960 entry
= hf_getnewentry(hotdata
);
1961 entry
->temperature
= temperature
;
1962 entry
->fileid
= cp
->c_fileid
;
1964 // if ffp->ff_blocks is zero, it might be compressed so make sure we record
1965 // that there's at least one block.
1967 entry
->blocks
= ffp
->ff_blocks
? ffp
->ff_blocks
: 1;
1968 if (hf_insert(hotdata
, entry
) == EEXIST
) {
1969 // entry is already present, don't need to add it again
1970 entry
->right
= hotdata
->freelist
;
1971 hotdata
->freelist
= entry
;
1973 --hotdata
->refcount
;
1980 * Remove a hot file from the recording list.
1982 * This can happen when a hot file becomes
1983 * an active vnode (active hot files are
1984 * not kept in the recording list until the
1985 * end of the recording period).
1987 * Note: the cnode is locked on entry.
1990 hfs_removehotfile(struct vnode
*vp
)
1992 hotfile_data_t
*hotdata
;
1996 u_int32_t temperature
;
1999 if (hfsmp
->hfc_stage
!= HFC_RECORDING
)
2002 if ((!vnode_isreg(vp
)) || vnode_issystem(vp
)) {
2009 if ((ffp
->ff_bytesread
== 0) || (ffp
->ff_blocks
== 0) ||
2010 (ffp
->ff_size
== 0) || (cp
->c_atime
< hfsmp
->hfc_timebase
)) {
2014 lck_mtx_lock(&hfsmp
->hfc_mutex
);
2015 if (hfsmp
->hfc_stage
!= HFC_RECORDING
)
2017 if ((hotdata
= hfsmp
->hfc_recdata
) == NULL
)
2020 temperature
= ffp
->ff_bytesread
/ ffp
->ff_size
;
2021 if (temperature
< hotdata
->threshold
)
2024 if (hotdata
->coldest
&& (temperature
>= hotdata
->coldest
->temperature
)) {
2025 ++hotdata
->refcount
;
2026 hf_delete(hotdata
, VTOC(vp
)->c_fileid
, temperature
);
2027 --hotdata
->refcount
;
2030 lck_mtx_unlock(&hfsmp
->hfc_mutex
);
2035 hfs_hotfile_deleted(__unused
struct vnode
*vp
)
2041 // XXXdbg - this code, while it would work, would introduce a huge inefficiency
2042 // to deleting files as the way it's written would require us to open
2043 // the hotfile btree on every open, delete two records in it and then
2044 // close the hotfile btree (which involves more writes).
2046 // We actually can be lazy about deleting hotfile records for files
2047 // that get deleted. When it's time to evict things, if we encounter
2048 // a record that references a dead file (i.e. a fileid which no
2049 // longer exists), the eviction code will remove the records. Likewise
2050 // the code that scans the HotFile B-Tree at boot time to re-pin files
2051 // will remove dead records.
2054 hotfile_data_t
*hotdata
;
2057 filefork_t
*filefork
;
2058 u_int32_t temperature
;
2059 BTreeIterator
* iterator
= NULL
;
2060 FSBufferDescriptor record
;
2066 if (cp
== NULL
|| !(cp
->c_attr
.ca_recflags
& kHFSFastDevPinnedMask
)) {
2071 if (!(hfsmp
->hfs_flags
& HFS_CS_HOTFILE_PIN
)) {
2075 if (hfc_btree_open(hfsmp
, &hfsmp
->hfc_filevp
) != 0 || hfsmp
->hfc_filevp
== NULL
) {
2076 // either there is no hotfile info or it's damaged
2080 filefork
= VTOF(hfsmp
->hfc_filevp
);
2081 if (filefork
== NULL
) {
2085 iterator
= hfs_mallocz(sizeof(*iterator
));
2087 key
= (HotFileKey
*) &iterator
->key
;
2089 record
.bufferAddress
= &data
;
2090 record
.itemSize
= sizeof(u_int32_t
);
2091 record
.itemCount
= 1;
2093 key
->keyLength
= HFC_KEYLENGTH
;
2094 key
->temperature
= HFC_LOOKUPTAG
;
2095 key
->fileID
= cp
->c_fileid
;
2098 lck_mtx_lock(&hfsmp
->hfc_mutex
);
2099 (void) BTInvalidateHint(iterator
);
2100 if (BTSearchRecord(filefork
, iterator
, &record
, NULL
, iterator
) == 0) {
2101 temperature
= key
->temperature
;
2102 hfc_btree_delete_record(hfsmp
, iterator
, key
);
2104 //printf("hfs: hotfile_deleted: did not find fileid %d\n", cp->c_fileid);
2108 if ((hotdata
= hfsmp
->hfc_recdata
) != NULL
) {
2109 // just in case, also make sure it's removed from the in-memory list as well
2110 ++hotdata
->refcount
;
2111 hf_delete(hotdata
, cp
->c_fileid
, cp
->c_fileid
);
2112 --hotdata
->refcount
;
2115 lck_mtx_unlock(&hfsmp
->hfc_mutex
);
2116 hfs_free(iterator
, sizeof(*iterator
));
2118 hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
2125 hfs_hotfile_adjust_blocks(struct vnode
*vp
, int64_t num_blocks
)
2135 if (!(hfsmp
->hfs_flags
& HFS_CS_HOTFILE_PIN
) || num_blocks
== 0 || vp
== NULL
) {
2140 // if file is not HotFileCached or it has the CanNotHotFile cache
2141 // bit set then there is nothing to do
2143 if (!(VTOC(vp
)->c_attr
.ca_recflags
& kHFSFastDevPinnedMask
) || (VTOC(vp
)->c_attr
.ca_recflags
& kHFSDoNotFastDevPinMask
)) {
2144 // it's not a hot file or can't be one so don't bother tracking
2148 OSAddAtomic(num_blocks
, &hfsmp
->hfs_hotfile_blk_adjust
);
2154 // Assumes hfsmp->hfc_mutex is LOCKED
2157 hfs_hotfile_cur_freeblks(hfsmount_t
*hfsmp
)
2159 if (hfsmp
->hfc_stage
< HFC_IDLE
) {
2163 int cur_blk_adjust
= hfsmp
->hfs_hotfile_blk_adjust
; // snap a copy of this value
2165 if (cur_blk_adjust
) {
2166 OSAddAtomic(-cur_blk_adjust
, &hfsmp
->hfs_hotfile_blk_adjust
);
2167 hfsmp
->hfs_hotfile_freeblks
+= cur_blk_adjust
;
2170 return hfsmp
->hfs_hotfile_freeblks
;
2175 *========================================================================
2176 * HOT FILE MAINTENANCE ROUTINES
2177 *========================================================================
2181 hotfiles_collect_callback(struct vnode
*vp
, __unused
void *cargs
)
2183 if ((vnode_isreg(vp
)) && !vnode_issystem(vp
))
2184 (void) hfs_addhotfile_internal(vp
);
2186 return (VNODE_RETURNED
);
2190 * Add all active hot files to the recording list.
2193 hotfiles_collect(struct hfsmount
*hfsmp
)
2195 struct mount
*mp
= HFSTOVFS(hfsmp
);
2197 if (vfs_busy(mp
, LK_NOWAIT
))
2201 * hotfiles_collect_callback will be called for each vnode
2202 * hung off of this mount point
2204 * properly referenced and unreferenced around the callback
2206 vnode_iterate(mp
, 0, hotfiles_collect_callback
, (void *)NULL
);
2215 * Update the data of a btree record
2216 * This is called from within BTUpdateRecord.
2219 update_callback(const HotFileKey
*key
, u_int32_t
*data
, u_int32_t
*state
)
2221 if (key
->temperature
== HFC_LOOKUPTAG
)
2227 * Identify files already in hot area.
2230 hotfiles_refine(struct hfsmount
*hfsmp
)
2232 BTreeIterator
* iterator
= NULL
;
2234 filefork_t
* filefork
;
2235 hotfilelist_t
*listp
;
2236 FSBufferDescriptor record
;
2242 if ((listp
= hfsmp
->hfc_filelist
) == NULL
)
2245 if (hfsmp
->hfs_flags
& HFS_CS_HOTFILE_PIN
) {
2246 // on ssd's we don't refine the temperature since the
2247 // replacement algorithm is simply random
2251 mp
= HFSTOVFS(hfsmp
);
2253 iterator
= hfs_mallocz(sizeof(*iterator
));
2255 key
= (HotFileKey
*) &iterator
->key
;
2257 record
.bufferAddress
= &data
;
2258 record
.itemSize
= sizeof(u_int32_t
);
2259 record
.itemCount
= 1;
2261 if (hfs_start_transaction(hfsmp
) != 0) {
2265 if (hfs_lock(VTOC(hfsmp
->hfc_filevp
), HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
) != 0) {
2269 filefork
= VTOF(hfsmp
->hfc_filevp
);
2271 for (i
= 0; i
< listp
->hfl_count
; ++i
) {
2273 * Check if entry (thread) is already in hot area.
2275 key
->keyLength
= HFC_KEYLENGTH
;
2276 key
->temperature
= HFC_LOOKUPTAG
;
2277 key
->fileID
= listp
->hfl_hotfile
[i
].hf_fileid
;
2279 (void) BTInvalidateHint(iterator
);
2280 if (BTSearchRecord(filefork
, iterator
, &record
, NULL
, iterator
) != 0) {
2281 continue; /* not in hot area, so skip */
2285 * Update thread entry with latest temperature.
2287 error
= BTUpdateRecord(filefork
, iterator
,
2288 (IterateCallBackProcPtr
)update_callback
,
2289 &listp
->hfl_hotfile
[i
].hf_temperature
);
2291 printf("hfs: hotfiles_refine: BTUpdateRecord failed %d (file %d)\n", error
, key
->fileID
);
2292 error
= MacToVFSError(error
);
2296 * Re-key entry with latest temperature.
2298 key
->keyLength
= HFC_KEYLENGTH
;
2299 key
->temperature
= data
;
2300 key
->fileID
= listp
->hfl_hotfile
[i
].hf_fileid
;
2302 /* Pick up record data. */
2303 (void) BTInvalidateHint(iterator
);
2304 (void) BTSearchRecord(filefork
, iterator
, &record
, NULL
, iterator
);
2305 error
= BTDeleteRecord(filefork
, iterator
);
2307 printf("hfs: hotfiles_refine: BTDeleteRecord failed %d (file %d)\n", error
, key
->fileID
);
2308 error
= MacToVFSError(error
);
2311 key
->keyLength
= HFC_KEYLENGTH
;
2312 key
->temperature
= listp
->hfl_hotfile
[i
].hf_temperature
;
2313 key
->fileID
= listp
->hfl_hotfile
[i
].hf_fileid
;
2315 error
= BTInsertRecord(filefork
, iterator
, &record
, record
.itemSize
);
2317 printf("hfs: hotfiles_refine: BTInsertRecord failed %d (file %d)\n", error
, key
->fileID
);
2318 error
= MacToVFSError(error
);
2322 * Invalidate this entry in the list.
2324 listp
->hfl_hotfile
[i
].hf_temperature
= 0;
2325 listp
->hfl_totalblocks
-= listp
->hfl_hotfile
[i
].hf_blocks
;
2329 (void) BTFlushPath(filefork
);
2330 hfs_unlock(VTOC(hfsmp
->hfc_filevp
));
2333 hfs_end_transaction(hfsmp
);
2336 hfs_free(iterator
, sizeof(*iterator
));
2341 * Move new hot files into hot area.
2343 * Requires that the hfc_mutex be held.
2346 hotfiles_adopt(struct hfsmount
*hfsmp
)
2348 BTreeIterator
* iterator
= NULL
;
2350 filefork_t
* filefork
;
2351 hotfilelist_t
*listp
;
2352 FSBufferDescriptor record
;
2355 enum hfc_stage stage
;
2361 int startedtrans
= 0;
2363 // all files in a given adoption phase have a temperature
2364 // that starts at a random value and then increases linearly.
2365 // the idea is that during eviction, files that were adopted
2366 // together will be evicted together
2368 long starting_temp
= random() % HF_TEMP_RANGE
;
2369 long temp_adjust
= 0;
2371 if ((listp
= hfsmp
->hfc_filelist
) == NULL
)
2374 if (hfsmp
->hfc_stage
!= HFC_ADOPTION
) {
2377 if (hfs_lock(VTOC(hfsmp
->hfc_filevp
), HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
) != 0) {
2381 iterator
= hfs_mallocz(sizeof(*iterator
));
2384 printf("hfs:%s: hotfiles_adopt: (hfl_next: %d, hotfile start/end block: %d - %d; max/free: %d/%d; maxfiles: %d)\n",
2387 hfsmp
->hfs_hotfile_start
, hfsmp
->hfs_hotfile_end
,
2388 hfsmp
->hfs_hotfile_maxblks
, hfsmp
->hfs_hotfile_freeblks
, hfsmp
->hfc_maxfiles
);
2391 stage
= hfsmp
->hfc_stage
;
2392 hfsmp
->hfc_stage
= HFC_BUSY
;
2395 last
= listp
->hfl_next
+ HFC_FILESPERSYNC
;
2396 if (last
> listp
->hfl_count
)
2397 last
= listp
->hfl_count
;
2399 key
= (HotFileKey
*) &iterator
->key
;
2400 key
->keyLength
= HFC_KEYLENGTH
;
2402 record
.bufferAddress
= &data
;
2403 record
.itemSize
= sizeof(u_int32_t
);
2404 record
.itemCount
= 1;
2406 filefork
= VTOF(hfsmp
->hfc_filevp
);
2408 for (i
= listp
->hfl_next
; (i
< last
) && (blksmoved
< HFC_BLKSPERSYNC
); ++i
) {
2410 * Skip entries that aren't going to work.
2412 if (listp
->hfl_hotfile
[i
].hf_temperature
== 0) {
2413 //printf("hfs: zero temp on file-id %d\n", listp->hfl_hotfile[i].hf_fileid);
2417 if (listp
->hfl_hotfile
[i
].hf_fileid
== VTOC(hfsmp
->hfc_filevp
)->c_fileid
) {
2418 //printf("hfs: cannot adopt the hotfile b-tree itself! (file-id %d)\n", listp->hfl_hotfile[i].hf_fileid);
2422 if (listp
->hfl_hotfile
[i
].hf_fileid
< kHFSFirstUserCatalogNodeID
) {
2423 //printf("hfs: cannot adopt system files (file-id %d)\n", listp->hfl_hotfile[i].hf_fileid);
2429 * Acquire a vnode for this file.
2431 error
= hfs_vget(hfsmp
, listp
->hfl_hotfile
[i
].hf_fileid
, &vp
, 0, 0);
2433 //printf("failed to get fileid %d (err %d)\n", listp->hfl_hotfile[i].hf_fileid, error);
2434 if (error
== ENOENT
) {
2437 continue; /* stale entry, go to next */
2442 //printf("hfs: examining hotfile entry w/fileid %d, temp %d, blocks %d (HotFileCached: %s)\n",
2443 // listp->hfl_hotfile[i].hf_fileid, listp->hfl_hotfile[i].hf_temperature,
2444 // listp->hfl_hotfile[i].hf_blocks,
2445 // (VTOC(vp)->c_attr.ca_recflags & kHFSFastDevPinnedMask) ? "YES" : "NO");
2447 if (!vnode_isreg(vp
)) {
2448 /* Symlinks are ineligible for adoption into the hotfile zone. */
2449 //printf("hfs: hotfiles_adopt: huh, not a file %d (%d)\n", listp->hfl_hotfile[i].hf_fileid, VTOC(vp)->c_cnid);
2450 hfs_unlock(VTOC(vp
));
2452 listp
->hfl_hotfile
[i
].hf_temperature
= 0;
2454 continue; /* stale entry, go to next */
2456 if ( (VTOC(vp
)->c_flag
& (C_DELETED
| C_NOEXISTS
))
2457 || (!(hfsmp
->hfs_flags
& HFS_CS_HOTFILE_PIN
) && hotextents(hfsmp
, &VTOF(vp
)->ff_extents
[0]))
2458 || (VTOC(vp
)->c_attr
.ca_recflags
& (kHFSFastDevPinnedMask
|kHFSDoNotFastDevPinMask
))) {
2459 hfs_unlock(VTOC(vp
));
2461 listp
->hfl_hotfile
[i
].hf_temperature
= 0;
2463 listp
->hfl_totalblocks
-= listp
->hfl_hotfile
[i
].hf_blocks
;
2464 continue; /* stale entry, go to next */
2467 fileblocks
= VTOF(vp
)->ff_blocks
;
2470 // for CF, if the file is empty (and not compressed) or it is too large,
2471 // do not try to pin it. (note: if fileblocks == 0 but the file is marked
2472 // as compressed, we may still be able to cache it).
2474 if ((hfsmp
->hfs_flags
& HFS_CS_HOTFILE_PIN
) &&
2475 ((fileblocks
== 0 && !(VTOC(vp
)->c_bsdflags
& UF_COMPRESSED
)) ||
2476 (unsigned int)fileblocks
> (HFC_MAXIMUM_FILESIZE
/ (uint64_t)HFSTOVCB(hfsmp
)->blockSize
))) {
2477 // don't try to cache something too large or that's zero-bytes
2479 vnode_clearfastdevicecandidate(vp
); // turn off the fast-dev-candidate flag so we don't keep trying to cache it.
2481 hfs_unlock(VTOC(vp
));
2483 listp
->hfl_hotfile
[i
].hf_temperature
= 0;
2485 listp
->hfl_totalblocks
-= listp
->hfl_hotfile
[i
].hf_blocks
;
2486 continue; /* entry is too big, just carry on with the next guy */
2490 // If a file is not an autocandidate (i.e. it's a user-tagged file desirous of
2491 // being hotfile cached) but it is already bigger than 4 megs, don't bother
2492 // hotfile caching it. Note that if a user tagged file starts small, gets
2493 // adopted and then grows over time we will allow it to grow bigger than 4 megs
2494 // which is intentional for things like the Mail or Photos database files which
2495 // grow slowly over time and benefit from being on the FastDevice.
2497 if ((hfsmp
->hfs_flags
& HFS_CS_HOTFILE_PIN
) &&
2498 !(VTOC(vp
)->c_attr
.ca_recflags
& kHFSAutoCandidateMask
) &&
2499 (VTOC(vp
)->c_attr
.ca_recflags
& kHFSFastDevCandidateMask
) &&
2500 (unsigned int)fileblocks
> ((4*1024*1024) / (uint64_t)HFSTOVCB(hfsmp
)->blockSize
)) {
2502 vnode_clearfastdevicecandidate(vp
); // turn off the fast-dev-candidate flag so we don't keep trying to cache it.
2504 hfs_unlock(VTOC(vp
));
2506 listp
->hfl_hotfile
[i
].hf_temperature
= 0;
2508 listp
->hfl_totalblocks
-= listp
->hfl_hotfile
[i
].hf_blocks
;
2509 continue; /* entry is too big, just carry on with the next guy */
2512 if (fileblocks
> hfs_hotfile_cur_freeblks(hfsmp
)) {
2514 // No room for this file. Although eviction should have made space
2515 // it's best that we check here as well since writes to existing
2516 // hotfiles may have eaten up space since we performed eviction
2518 hfs_unlock(VTOC(vp
));
2521 listp
->hfl_totalblocks
-= fileblocks
;
2522 continue; /* entry too big, go to next */
2525 if ((blksmoved
> 0) &&
2526 (blksmoved
+ fileblocks
) > HFC_BLKSPERSYNC
) {
2528 // we've done enough work, let's be nice to the system and
2529 // stop until the next iteration
2531 hfs_unlock(VTOC(vp
));
2533 break; /* adopt this entry the next time around */
2537 // The size of data for a hot file record is 4 bytes. The data
2538 // stored in hot file record is not really meaningful. However
2539 // to aid debugging, we store first four bytes of the file name
2540 // or the ASCII text "????"
2542 if (VTOC(vp
)->c_desc
.cd_nameptr
&& (VTOC(vp
)->c_desc
.cd_namelen
> 0)) {
2545 max_len
= sizeof(u_int32_t
);
2546 if (max_len
> (unsigned)VTOC(vp
)->c_desc
.cd_namelen
)
2547 max_len
= VTOC(vp
)->c_desc
.cd_namelen
;
2549 memcpy(&data
, VTOC(vp
)->c_desc
.cd_nameptr
, max_len
);
2554 if (hfsmp
->hfs_flags
& HFS_CS_HOTFILE_PIN
) {
2556 // For CF we pin the blocks belonging to the file
2557 // to the "fast" (aka ssd) media
2559 uint32_t pinned_blocks
;
2561 if (vnode_isautocandidate(vp
)) {
2562 VTOC(vp
)->c_attr
.ca_recflags
|= kHFSAutoCandidateMask
;
2564 if (VTOC(vp
)->c_attr
.ca_recflags
& kHFSAutoCandidateMask
) {
2566 // this moves auto-cached files to the higher tier
2567 // of "temperatures" which means they are less likely
2568 // to get evicted (user selected hotfiles will get
2569 // evicted first in the theory that they change more
2570 // frequently compared to system files)
2572 temp_adjust
= MAX_NORMAL_TEMP
;
2577 hfs_unlock(VTOC(vp
)); // don't need an exclusive lock for this
2578 hfs_lock(VTOC(vp
), HFS_SHARED_LOCK
, HFS_LOCK_ALLOW_NOEXISTS
);
2580 error
= hfs_pin_vnode(hfsmp
, vp
, HFS_PIN_IT
, &pinned_blocks
);
2582 fileblocks
= pinned_blocks
;
2584 // go back to an exclusive lock since we're going to modify the cnode again
2585 hfs_unlock(VTOC(vp
));
2586 hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
, HFS_LOCK_ALLOW_NOEXISTS
);
2589 // Old style hotfiles moves the data to the center (aka "hot")
2590 // region of the disk
2592 error
= hfs_relocate(vp
, hfsmp
->hfs_hotfile_start
, kauth_cred_get(), current_proc());
2596 VTOC(vp
)->c_attr
.ca_recflags
|= kHFSFastDevPinnedMask
;
2597 VTOC(vp
)->c_flag
|= C_MODIFIED
;
2598 } else if ((hfsmp
->hfs_flags
& HFS_CS_HOTFILE_PIN
) && error
== EALREADY
) {
2600 // If hfs_pin_vnode() returned EALREADY then this file is not
2601 // ever able to be hotfile cached the normal way. This can
2602 // happen with compressed files which have their data stored
2603 // in an extended attribute. We flag them so that we won't
2604 // bother to try and hotfile cache them again the next time
2607 VTOC(vp
)->c_attr
.ca_recflags
|= kHFSDoNotFastDevPinMask
;
2608 VTOC(vp
)->c_flag
|= C_MODIFIED
;
2611 hfs_unlock(VTOC(vp
));
2615 if (error
!= EALREADY
) {
2616 printf("hfs: hotfiles_adopt: could not relocate file %d (err %d)\n", listp
->hfl_hotfile
[i
].hf_fileid
, error
);
2620 if (last
< listp
->hfl_count
) {
2623 /* Move on to next item. */
2627 /* Keep hot file free space current. */
2628 hfsmp
->hfs_hotfile_freeblks
-= fileblocks
;
2629 listp
->hfl_totalblocks
-= fileblocks
;
2631 /* Insert hot file entry */
2632 key
->keyLength
= HFC_KEYLENGTH
;
2634 if (hfsmp
->hfs_flags
& HFS_CS_HOTFILE_PIN
) {
2636 // The "temperature" for a CF hotfile is simply a random
2637 // number that we sequentially increment for each file in
2638 // the set of files we're currently adopting. This has the
2639 // nice property that all of the files we pin to the ssd
2640 // in the current phase will sort together in the hotfile
2641 // btree. When eviction time comes we will evict them
2642 // together as well. This gives the eviction phase temporal
2643 // locality - things written together get evicted together
2644 // which is what ssd's like.
2646 listp
->hfl_hotfile
[i
].hf_temperature
= (uint32_t)temp_adjust
+ starting_temp
++;
2649 key
->temperature
= listp
->hfl_hotfile
[i
].hf_temperature
;
2650 key
->fileID
= listp
->hfl_hotfile
[i
].hf_fileid
;
2653 /* Start a new transaction before calling BTree code. */
2654 if (hfs_start_transaction(hfsmp
) != 0) {
2660 error
= BTInsertRecord(filefork
, iterator
, &record
, record
.itemSize
);
2662 int orig_error
= error
;
2663 error
= MacToVFSError(error
);
2664 printf("hfs: hotfiles_adopt:1: BTInsertRecord failed %d/%d (fileid %d)\n", error
, orig_error
, key
->fileID
);
2669 /* Insert thread record */
2670 key
->keyLength
= HFC_KEYLENGTH
;
2671 key
->temperature
= HFC_LOOKUPTAG
;
2672 key
->fileID
= listp
->hfl_hotfile
[i
].hf_fileid
;
2674 data
= listp
->hfl_hotfile
[i
].hf_temperature
;
2675 error
= BTInsertRecord(filefork
, iterator
, &record
, record
.itemSize
);
2677 int orig_error
= error
;
2678 error
= MacToVFSError(error
);
2679 printf("hfs: hotfiles_adopt:2: BTInsertRecord failed %d/%d (fileid %d)\n", error
, orig_error
, key
->fileID
);
2683 (void) BTFlushPath(filefork
);
2684 blksmoved
+= fileblocks
;
2688 if (listp
->hfl_next
>= listp
->hfl_count
) {
2692 /* Transaction complete. */
2694 hfs_end_transaction(hfsmp
);
2698 if (hfs_hotfile_cur_freeblks(hfsmp
) <= 0) {
2700 printf("hfs: hotfiles_adopt: free space exhausted (%d)\n", hfsmp
->hfs_hotfile_freeblks
);
2707 printf("hfs: hotfiles_adopt: [%d] adopted %d blocks (%d files left)\n", listp
->hfl_next
, blksmoved
, listp
->hfl_count
- i
);
2709 if (!startedtrans
) {
2710 // start a txn so we'll save the btree summary info
2711 if (hfs_start_transaction(hfsmp
) == 0) {
2716 /* Finish any outstanding transactions. */
2718 save_btree_user_info(hfsmp
);
2720 (void) BTFlushPath(filefork
);
2721 hfs_end_transaction(hfsmp
);
2724 hfs_unlock(VTOC(hfsmp
->hfc_filevp
));
2726 if ((listp
->hfl_next
>= listp
->hfl_count
) || (hfsmp
->hfs_hotfile_freeblks
<= 0)) {
2728 printf("hfs: hotfiles_adopt: all done relocating %d files\n", listp
->hfl_count
);
2729 printf("hfs: hotfiles_adopt: %d blocks free in hot file band\n", hfsmp
->hfs_hotfile_freeblks
);
2733 hfs_free(iterator
, sizeof(*iterator
));
2735 if (stage
!= HFC_ADOPTION
&& hfsmp
->hfc_filevp
) {
2736 (void) hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
2737 hfsmp
->hfc_filevp
= NULL
;
2739 hfsmp
->hfc_stage
= stage
;
2740 wakeup((caddr_t
)&hfsmp
->hfc_stage
);
2745 * Reclaim space by evicting the coldest files.
2747 * Requires that the hfc_mutex be held.
2750 hotfiles_evict(struct hfsmount
*hfsmp
, vfs_context_t ctx
)
2752 BTreeIterator
* iterator
= NULL
;
2755 filefork_t
* filefork
;
2756 hotfilelist_t
*listp
;
2757 enum hfc_stage stage
;
2758 u_int32_t savedtemp
;
2763 int startedtrans
= 0;
2766 if (hfsmp
->hfc_stage
!= HFC_EVICTION
) {
2770 if ((listp
= hfsmp
->hfc_filelist
) == NULL
)
2773 if (hfs_lock(VTOC(hfsmp
->hfc_filevp
), HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
) != 0) {
2778 printf("hfs:%s: hotfiles_evict (hotfile start/end block: %d - %d; max/free: %d/%d; maxfiles: %d)\n",
2780 hfsmp
->hfs_hotfile_start
, hfsmp
->hfs_hotfile_end
,
2781 hfsmp
->hfs_hotfile_maxblks
, hfsmp
->hfs_hotfile_freeblks
, hfsmp
->hfc_maxfiles
);
2784 iterator
= hfs_mallocz(sizeof(*iterator
));
2786 stage
= hfsmp
->hfc_stage
;
2787 hfsmp
->hfc_stage
= HFC_BUSY
;
2789 filesmoved
= blksmoved
= 0;
2790 bt_op
= kBTreeFirstRecord
;
2792 key
= (HotFileKey
*) &iterator
->key
;
2794 filefork
= VTOF(hfsmp
->hfc_filevp
);
2797 printf("hfs: hotfiles_evict: reclaim blks %d\n", listp
->hfl_reclaimblks
);
2800 while (listp
->hfl_reclaimblks
> 0 &&
2801 blksmoved
< HFC_BLKSPERSYNC
&&
2802 filesmoved
< HFC_FILESPERSYNC
) {
2805 * Obtain the first record (ie the coldest one).
2807 if (BTIterateRecord(filefork
, bt_op
, iterator
, NULL
, NULL
) != 0) {
2809 printf("hfs: hotfiles_evict: no more records\n");
2812 stage
= HFC_ADOPTION
;
2815 if (key
->keyLength
!= HFC_KEYLENGTH
) {
2816 printf("hfs: hotfiles_evict: invalid key length %d\n", key
->keyLength
);
2820 if (key
->temperature
== HFC_LOOKUPTAG
) {
2822 printf("hfs: hotfiles_evict: ran into thread records\n");
2825 stage
= HFC_ADOPTION
;
2829 // Jump straight to delete for some files...
2830 if (key
->fileID
== VTOC(hfsmp
->hfc_filevp
)->c_fileid
2831 || key
->fileID
== hfsmp
->hfs_jnlfileid
2832 || key
->fileID
== hfsmp
->hfs_jnlinfoblkid
2833 || key
->fileID
< kHFSFirstUserCatalogNodeID
) {
2838 * Aquire the vnode for this file.
2840 error
= hfs_vget(hfsmp
, key
->fileID
, &vp
, 0, 0);
2842 if (error
== ENOENT
) {
2843 goto delete; /* stale entry, go to next */
2845 printf("hfs: hotfiles_evict: err %d getting file %d\n",
2846 error
, key
->fileID
);
2852 * Symlinks that may have been inserted into the hotfile zone during a previous OS are now stuck
2853 * here. We do not want to move them.
2855 if (!vnode_isreg(vp
)) {
2856 //printf("hfs: hotfiles_evict: huh, not a file %d\n", key->fileID);
2857 hfs_unlock(VTOC(vp
));
2859 goto delete; /* invalid entry, go to next */
2862 fileblocks
= VTOF(vp
)->ff_blocks
;
2863 if ((blksmoved
> 0) &&
2864 (blksmoved
+ fileblocks
) > HFC_BLKSPERSYNC
) {
2865 hfs_unlock(VTOC(vp
));
2870 * Make sure file is in the hot area.
2872 if (!hotextents(hfsmp
, &VTOF(vp
)->ff_extents
[0]) && !(VTOC(vp
)->c_attr
.ca_recflags
& kHFSFastDevPinnedMask
)) {
2874 printf("hfs: hotfiles_evict: file %d isn't hot!\n", key
->fileID
);
2876 hfs_unlock(VTOC(vp
));
2878 goto delete; /* stale entry, go to next */
2882 * Relocate file out of hot area. On cooperative fusion (CF) that just
2883 * means un-pinning the data from the ssd. For traditional hotfiles that means moving
2884 * the file data out of the hot region of the disk.
2886 if (hfsmp
->hfs_flags
& HFS_CS_HOTFILE_PIN
) {
2887 uint32_t pinned_blocks
;
2889 hfs_unlock(VTOC(vp
)); // don't need an exclusive lock for this
2890 hfs_lock(VTOC(vp
), HFS_SHARED_LOCK
, HFS_LOCK_ALLOW_NOEXISTS
);
2892 error
= hfs_pin_vnode(hfsmp
, vp
, HFS_UNPIN_IT
, &pinned_blocks
);
2893 fileblocks
= pinned_blocks
;
2896 // go back to an exclusive lock since we're going to modify the cnode again
2897 hfs_unlock(VTOC(vp
));
2898 hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
, HFS_LOCK_ALLOW_NOEXISTS
);
2901 error
= hfs_relocate(vp
, HFSTOVCB(hfsmp
)->nextAllocation
, vfs_context_ucred(ctx
), vfs_context_proc(ctx
));
2905 printf("hfs: hotfiles_evict: err %d relocating file %d\n", error
, key
->fileID
);
2907 hfs_unlock(VTOC(vp
));
2909 bt_op
= kBTreeNextRecord
;
2910 goto next
; /* go to next */
2912 VTOC(vp
)->c_attr
.ca_recflags
&= ~kHFSFastDevPinnedMask
;
2913 VTOC(vp
)->c_flag
|= C_MODIFIED
;
2917 // We do not believe that this call to hfs_fsync() is
2918 // necessary and it causes a journal transaction
2919 // deadlock so we are removing it.
2921 // (void) hfs_fsync(vp, MNT_WAIT, 0, p);
2923 hfs_unlock(VTOC(vp
));
2926 hfsmp
->hfs_hotfile_freeblks
+= fileblocks
;
2927 listp
->hfl_reclaimblks
-= fileblocks
;
2928 if (listp
->hfl_reclaimblks
< 0)
2929 listp
->hfl_reclaimblks
= 0;
2930 blksmoved
+= fileblocks
;
2933 /* Start a new transaction before calling BTree code. */
2934 if (hfs_start_transaction(hfsmp
) != 0) {
2940 error
= BTDeleteRecord(filefork
, iterator
);
2942 error
= MacToVFSError(error
);
2945 savedtemp
= key
->temperature
;
2946 key
->temperature
= HFC_LOOKUPTAG
;
2947 error
= BTDeleteRecord(filefork
, iterator
);
2949 error
= MacToVFSError(error
);
2952 key
->temperature
= savedtemp
;
2954 (void) BTFlushPath(filefork
);
2956 /* Transaction complete. */
2958 hfs_end_transaction(hfsmp
);
2965 printf("hfs: hotfiles_evict: moved %d files (%d blks, %d to go)\n", filesmoved
, blksmoved
, listp
->hfl_reclaimblks
);
2967 /* Finish any outstanding transactions. */
2969 save_btree_user_info(hfsmp
);
2971 (void) BTFlushPath(filefork
);
2972 hfs_end_transaction(hfsmp
);
2975 hfs_unlock(VTOC(hfsmp
->hfc_filevp
));
2978 * Move to next stage when finished.
2980 if (listp
->hfl_reclaimblks
<= 0) {
2981 stage
= HFC_ADOPTION
;
2983 printf("hfs: hotfiles_evict: %d blocks free in hot file band\n", hfsmp
->hfs_hotfile_freeblks
);
2986 hfs_free(iterator
, sizeof(*iterator
));
2987 hfsmp
->hfc_stage
= stage
;
2988 wakeup((caddr_t
)&hfsmp
->hfc_stage
);
2993 * Age the existing records in the hot files b-tree.
2996 hotfiles_age(struct hfsmount
*hfsmp
)
2998 BTreeInfoRec btinfo
;
2999 BTreeIterator
* iterator
= NULL
;
3000 BTreeIterator
* prev_iterator
;
3001 FSBufferDescriptor record
;
3002 FSBufferDescriptor prev_record
;
3004 HotFileKey
* prev_key
;
3005 filefork_t
* filefork
;
3007 u_int32_t prev_data
;
3016 if (hfsmp
->hfs_flags
& HFS_CS_HOTFILE_PIN
) {
3018 // hotfiles don't age on CF
3023 iterator
= hfs_mallocz(2 * sizeof(*iterator
));
3025 key
= (HotFileKey
*) &iterator
->key
;
3027 prev_iterator
= &iterator
[1];
3028 prev_key
= (HotFileKey
*) &prev_iterator
->key
;
3030 record
.bufferAddress
= &data
;
3031 record
.itemSize
= sizeof(data
);
3032 record
.itemCount
= 1;
3033 prev_record
.bufferAddress
= &prev_data
;
3034 prev_record
.itemSize
= sizeof(prev_data
);
3035 prev_record
.itemCount
= 1;
3038 * Capture b-tree changes inside a transaction
3040 if (hfs_start_transaction(hfsmp
) != 0) {
3044 if (hfs_lock(VTOC(hfsmp
->hfc_filevp
), HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
) != 0) {
3048 filefork
= VTOF(hfsmp
->hfc_filevp
);
3050 error
= BTGetInformation(filefork
, 0, &btinfo
);
3052 error
= MacToVFSError(error
);
3055 if (btinfo
.numRecords
< 2) {
3060 /* Only want 1st half of leaf records */
3061 numrecs
= (btinfo
.numRecords
/= 2) - 1;
3063 error
= BTIterateRecord(filefork
, kBTreeFirstRecord
, iterator
, &record
, &reclen
);
3065 printf("hfs_agehotfiles: BTIterateRecord: %d\n", error
);
3066 error
= MacToVFSError(error
);
3069 bcopy(iterator
, prev_iterator
, sizeof(BTreeIterator
));
3072 for (i
= 0; i
< numrecs
; ++i
) {
3073 error
= BTIterateRecord(filefork
, kBTreeNextRecord
, iterator
, &record
, &reclen
);
3075 if (key
->temperature
< prev_key
->temperature
) {
3076 printf("hfs_agehotfiles: out of order keys!\n");
3080 if (reclen
!= sizeof(data
)) {
3081 printf("hfs_agehotfiles: invalid record length %d\n", reclen
);
3085 if (key
->keyLength
!= HFC_KEYLENGTH
) {
3086 printf("hfs_agehotfiles: invalid key length %d\n", key
->keyLength
);
3090 } else if ((error
== fsBTEndOfIterationErr
|| error
== fsBTRecordNotFoundErr
) &&
3091 (i
== (numrecs
- 1))) {
3094 printf("hfs_agehotfiles: %d of %d BTIterateRecord: %d\n", i
, numrecs
, error
);
3095 error
= MacToVFSError(error
);
3098 if (prev_key
->temperature
== HFC_LOOKUPTAG
) {
3100 printf("hfs_agehotfiles: ran into thread record\n");
3105 error
= BTDeleteRecord(filefork
, prev_iterator
);
3107 printf("hfs_agehotfiles: BTDeleteRecord failed %d (file %d)\n", error
, prev_key
->fileID
);
3108 error
= MacToVFSError(error
);
3112 /* Age by halving the temperature (floor = 4) */
3113 newtemp
= MAX(prev_key
->temperature
>> 1, 4);
3114 prev_key
->temperature
= newtemp
;
3116 error
= BTInsertRecord(filefork
, prev_iterator
, &prev_record
, prev_record
.itemSize
);
3118 printf("hfs_agehotfiles: BTInsertRecord failed %d (file %d)\n", error
, prev_key
->fileID
);
3119 error
= MacToVFSError(error
);
3124 * Update thread entry with latest temperature.
3126 prev_key
->temperature
= HFC_LOOKUPTAG
;
3127 error
= BTUpdateRecord(filefork
, prev_iterator
,
3128 (IterateCallBackProcPtr
)update_callback
,
3131 printf("hfs_agehotfiles: %d of %d BTUpdateRecord failed %d (file %d, %d)\n",
3132 i
, numrecs
, error
, prev_key
->fileID
, newtemp
);
3133 error
= MacToVFSError(error
);
3137 bcopy(iterator
, prev_iterator
, sizeof(BTreeIterator
));
3144 printf("hfs_agehotfiles: aged %d records out of %d\n", aged
, btinfo
.numRecords
);
3146 (void) BTFlushPath(filefork
);
3148 hfs_unlock(VTOC(hfsmp
->hfc_filevp
));
3150 hfs_end_transaction(hfsmp
);
3153 hfs_free(iterator
, 2 * sizeof(*iterator
));
3158 * Return true if any blocks (or all blocks if all is true)
3159 * are contained in the hot file region.
3162 hotextents(struct hfsmount
*hfsmp
, HFSPlusExtentDescriptor
* extents
)
3168 for (i
= 0; i
< kHFSPlusExtentDensity
; ++i
) {
3169 b1
= extents
[i
].startBlock
;
3172 b2
= b1
+ extents
[i
].blockCount
- 1;
3173 if ((b1
>= hfsmp
->hfs_hotfile_start
&&
3174 b2
<= hfsmp
->hfs_hotfile_end
) ||
3175 (b1
< hfsmp
->hfs_hotfile_end
&&
3176 b2
> hfsmp
->hfs_hotfile_end
)) {
3186 *========================================================================
3187 * HOT FILE B-TREE ROUTINES
3188 *========================================================================
3192 * Open the hot files b-tree for writing.
3194 * On successful exit the vnode has a reference but not an iocount.
3197 hfc_btree_open(struct hfsmount
*hfsmp
, struct vnode
**vpp
)
3199 return hfc_btree_open_ext(hfsmp
, vpp
, 0);
3203 hfc_btree_open_ext(struct hfsmount
*hfsmp
, struct vnode
**vpp
, int ignore_btree_errs
)
3207 struct cat_desc cdesc
;
3208 struct cat_attr cattr
;
3209 struct cat_fork cfork
;
3210 static char filename
[] = HFC_FILENAME
;
3214 int newvnode_flags
= 0;
3219 bzero(&cdesc
, sizeof(cdesc
));
3220 cdesc
.cd_parentcnid
= kRootDirID
;
3221 cdesc
.cd_nameptr
= (const u_int8_t
*)filename
;
3222 cdesc
.cd_namelen
= strlen(filename
);
3224 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_SHARED_LOCK
);
3226 error
= cat_lookup(hfsmp
, &cdesc
, 0, 0, &cdesc
, &cattr
, &cfork
, NULL
);
3228 hfs_systemfile_unlock(hfsmp
, lockflags
);
3231 printf("hfs: hfc_btree_open: cat_lookup error %d\n", error
);
3235 cdesc
.cd_flags
|= CD_ISMETA
;
3236 error
= hfs_getnewvnode(hfsmp
, NULL
, NULL
, &cdesc
, 0, &cattr
,
3237 &cfork
, &vp
, &newvnode_flags
);
3239 printf("hfs: hfc_btree_open: hfs_getnewvnode error %d\n", error
);
3240 cat_releasedesc(&cdesc
);
3243 if (!vnode_issystem(vp
)) {
3245 printf("hfs: hfc_btree_open: file has UBC, try again\n");
3247 hfs_unlock(VTOC(vp
));
3256 /* Open the B-tree file for writing... */
3257 error
= BTOpenPath(VTOF(vp
), (KeyCompareProcPtr
) hfc_comparekeys
);
3259 if (!ignore_btree_errs
) {
3260 printf("hfs: hfc_btree_open: BTOpenPath error %d; filesize %lld\n", error
, VTOF(vp
)->ff_size
);
3261 error
= MacToVFSError(error
);
3267 hfs_unlock(VTOC(vp
));
3270 vnode_ref(vp
); /* keep a reference while its open */
3274 if (!vnode_issystem(vp
))
3275 panic("hfs: hfc_btree_open: not a system file (vp = %p)", vp
);
3277 HotFilesInfo hotfileinfo
;
3279 if (error
== 0 && (hfsmp
->hfs_flags
& HFS_CS_HOTFILE_PIN
)) {
3280 if ((BTGetUserData(VTOF(vp
), &hotfileinfo
, sizeof(hotfileinfo
)) == 0) && (SWAP_BE32 (hotfileinfo
.magic
) == HFC_MAGIC
)) {
3281 if (hfsmp
->hfs_hotfile_freeblks
== 0) {
3282 hfsmp
->hfs_hotfile_freeblks
= hfsmp
->hfs_hotfile_maxblks
- SWAP_BE32 (hotfileinfo
.usedblocks
);
3285 hfs_hotfile_cur_freeblks(hfsmp
); // factors in any adjustments that happened at run-time
3293 * Close the hot files b-tree.
3295 * On entry the vnode has a reference.
3298 hfc_btree_close(struct hfsmount
*hfsmp
, struct vnode
*vp
)
3300 proc_t p
= current_proc();
3305 hfs_flush(hfsmp
, HFS_FLUSH_JOURNAL
);
3308 if (vnode_get(vp
) == 0) {
3309 error
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
);
3311 (void) hfs_fsync(vp
, MNT_WAIT
, 0, p
);
3312 error
= BTClosePath(VTOF(vp
));
3313 hfs_unlock(VTOC(vp
));
3324 // Assumes that hfsmp->hfc_filevp points to the hotfile btree vnode
3325 // (i.e. you called hfc_btree_open() ahead of time)
3328 hfc_btree_delete_record(struct hfsmount
*hfsmp
, BTreeIterator
*iterator
, HotFileKey
*key
)
3331 filefork_t
*filefork
=VTOF(hfsmp
->hfc_filevp
);
3333 /* Start a new transaction before calling BTree code. */
3334 if (hfs_start_transaction(hfsmp
) != 0) {
3338 error
= BTDeleteRecord(filefork
, iterator
);
3340 error
= MacToVFSError(error
);
3341 printf("hfs: failed to delete record for file-id %d : err %d\n", key
->fileID
, error
);
3346 savedtemp
= key
->temperature
;
3347 key
->temperature
= HFC_LOOKUPTAG
;
3348 error
= BTDeleteRecord(filefork
, iterator
);
3350 error
= MacToVFSError(error
);
3351 printf("hfs:2: failed to delete record for file-id %d : err %d\n", key
->fileID
, error
);
3353 key
->temperature
= savedtemp
;
3355 (void) BTFlushPath(filefork
);
3358 /* Transaction complete. */
3359 hfs_end_transaction(hfsmp
);
3365 // You have to have already opened the hotfile btree so
3366 // that hfsmp->hfc_filevp is filled in.
3369 hfc_btree_delete(struct hfsmount
*hfsmp
)
3371 struct vnode
*dvp
= NULL
;
3372 vfs_context_t ctx
= vfs_context_current();
3373 struct vnode_attr va
;
3374 static char filename
[] = HFC_FILENAME
;
3377 error
= hfs_vfs_root(HFSTOVFS(hfsmp
), &dvp
, ctx
);
3382 struct componentname cname
= {
3383 .cn_nameiop
= DELETE
,
3384 .cn_flags
= ISLASTCN
,
3385 .cn_pnbuf
= filename
,
3386 .cn_pnlen
= sizeof(filename
),
3387 .cn_nameptr
= filename
,
3388 .cn_namelen
= strlen(filename
),
3392 VATTR_SET(&va
, va_type
, VREG
);
3393 VATTR_SET(&va
, va_mode
, S_IFREG
| S_IRUSR
| S_IWUSR
);
3394 VATTR_SET(&va
, va_uid
, 0);
3395 VATTR_SET(&va
, va_gid
, 0);
3397 if (hfs_start_transaction(hfsmp
) != 0) {
3402 struct vnop_remove_args ap
= {
3404 .a_vp
= hfsmp
->hfc_filevp
,
3408 error
= hfs_vnop_remove(&ap
);
3410 printf("hfs: error %d removing HFBT on %s\n", error
, HFSTOVCB(hfsmp
)->vcbVN
);
3413 hfs_end_transaction(hfsmp
);
3428 * Create a hot files btree file.
3432 hfc_btree_create(struct hfsmount
*hfsmp
, unsigned int nodesize
, unsigned int entries
)
3434 struct vnode
*dvp
= NULL
;
3435 struct vnode
*vp
= NULL
;
3436 struct cnode
*cp
= NULL
;
3437 vfs_context_t ctx
= vfs_context_current();
3438 struct vnode_attr va
;
3439 static char filename
[] = HFC_FILENAME
;
3442 if (hfsmp
->hfc_filevp
)
3443 panic("hfs: hfc_btree_create: hfc_filevp exists (vp = %p)", hfsmp
->hfc_filevp
);
3445 error
= hfs_vfs_root(HFSTOVFS(hfsmp
), &dvp
, ctx
);
3450 struct componentname cname
= {
3451 .cn_nameiop
= CREATE
,
3452 .cn_flags
= ISLASTCN
,
3453 .cn_pnbuf
= filename
,
3454 .cn_pnlen
= sizeof(filename
),
3455 .cn_nameptr
= filename
,
3456 .cn_namelen
= strlen(filename
)
3460 VATTR_SET(&va
, va_type
, VREG
);
3461 VATTR_SET(&va
, va_mode
, S_IFREG
| S_IRUSR
| S_IWUSR
);
3462 VATTR_SET(&va
, va_uid
, 0);
3463 VATTR_SET(&va
, va_gid
, 0);
3465 if (hfs_start_transaction(hfsmp
) != 0) {
3470 /* call ourselves directly, ignore the higher-level VFS file creation code */
3472 struct vnop_create_args ap
= {
3479 error
= hfs_vnop_create(&ap
);
3481 printf("hfs: error %d creating HFBT on %s\n", error
, HFSTOVCB(hfsmp
)->vcbVN
);
3488 if ((error
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
))) {
3493 /* Don't use non-regular files or files with links. */
3494 if (!vnode_isreg(vp
) || cp
->c_linkcount
!= 1) {
3499 printf("hfs: created HFBT on %s\n", HFSTOVCB(hfsmp
)->vcbVN
);
3501 if (VTOF(vp
)->ff_size
< nodesize
) {
3505 BTNodeDescriptor
*ndp
;
3507 HotFilesInfo
*hotfileinfo
;
3513 * Mark it invisible (truncate will pull these changes).
3515 ((FndrFileInfo
*)&cp
->c_finderinfo
[0])->fdFlags
|=
3516 SWAP_BE16 (kIsInvisible
+ kNameLocked
);
3518 buffer
= hfs_mallocz(nodesize
);
3519 index
= (u_int16_t
*)buffer
;
3521 entirespernode
= (nodesize
- sizeof(BTNodeDescriptor
) - 2) /
3522 (sizeof(HotFileKey
) + 6);
3523 nodecnt
= 2 + howmany(entries
* 2, entirespernode
);
3524 nodecnt
= roundup(nodecnt
, 8);
3525 filesize
= nodecnt
* nodesize
;
3527 /* FILL IN THE NODE DESCRIPTOR: */
3528 ndp
= (BTNodeDescriptor
*)buffer
;
3529 ndp
->kind
= kBTHeaderNode
;
3530 ndp
->numRecords
= SWAP_BE16 (3);
3531 offset
= sizeof(BTNodeDescriptor
);
3532 index
[(nodesize
/ 2) - 1] = SWAP_BE16 (offset
);
3534 /* FILL IN THE HEADER RECORD: */
3535 bthp
= (BTHeaderRec
*)((u_int8_t
*)buffer
+ offset
);
3536 bthp
->nodeSize
= SWAP_BE16 (nodesize
);
3537 bthp
->totalNodes
= SWAP_BE32 (filesize
/ nodesize
);
3538 bthp
->freeNodes
= SWAP_BE32 (nodecnt
- 1);
3539 bthp
->clumpSize
= SWAP_BE32 (filesize
);
3540 bthp
->btreeType
= kUserBTreeType
; /* non-metadata */
3541 bthp
->attributes
|= SWAP_BE32 (kBTBigKeysMask
);
3542 bthp
->maxKeyLength
= SWAP_BE16 (HFC_KEYLENGTH
);
3543 offset
+= sizeof(BTHeaderRec
);
3544 index
[(nodesize
/ 2) - 2] = SWAP_BE16 (offset
);
3546 /* FILL IN THE USER RECORD: */
3547 hotfileinfo
= (HotFilesInfo
*)((u_int8_t
*)buffer
+ offset
);
3548 hotfileinfo
->magic
= SWAP_BE32 (HFC_MAGIC
);
3549 hotfileinfo
->version
= SWAP_BE32 (HFC_VERSION
);
3550 hotfileinfo
->duration
= SWAP_BE32 (HFC_DEFAULT_DURATION
);
3551 hotfileinfo
->timebase
= 0;
3552 hotfileinfo
->timeleft
= 0;
3553 hotfileinfo
->threshold
= SWAP_BE32 (HFC_MINIMUM_TEMPERATURE
);
3554 hotfileinfo
->maxfileblks
= SWAP_BE32 (HFC_MAXIMUM_FILESIZE
/ HFSTOVCB(hfsmp
)->blockSize
);
3555 if (hfsmp
->hfs_flags
& HFS_CS_HOTFILE_PIN
) {
3556 if (hfsmp
->hfs_hotfile_freeblks
== 0) {
3557 hfsmp
->hfs_hotfile_freeblks
= hfsmp
->hfs_hotfile_maxblks
;
3559 hotfileinfo
->usedblocks
= SWAP_BE32 (hfsmp
->hfs_hotfile_maxblks
- hfsmp
->hfs_hotfile_freeblks
);
3561 hotfileinfo
->maxfilecnt
= SWAP_BE32 (HFC_DEFAULT_FILE_COUNT
);
3563 strlcpy((char *)hotfileinfo
->tag
, hfc_tag
,
3564 sizeof hotfileinfo
->tag
);
3565 offset
+= kBTreeHeaderUserBytes
;
3566 index
[(nodesize
/ 2) - 3] = SWAP_BE16 (offset
);
3568 /* FILL IN THE MAP RECORD (only one node in use). */
3569 *((u_int8_t
*)buffer
+ offset
) = 0x80;
3570 offset
+= nodesize
- sizeof(BTNodeDescriptor
) - sizeof(BTHeaderRec
)
3571 - kBTreeHeaderUserBytes
- (4 * sizeof(int16_t));
3572 index
[(nodesize
/ 2) - 4] = SWAP_BE16 (offset
);
3574 vnode_setnoflush(vp
);
3575 error
= hfs_truncate(vp
, (off_t
)filesize
, IO_NDELAY
, 0, ctx
);
3577 printf("hfs: error %d growing HFBT on %s\n", error
, HFSTOVCB(hfsmp
)->vcbVN
);
3580 cp
->c_flag
|= C_ZFWANTSYNC
;
3581 cp
->c_zftimeout
= 1;
3584 struct vnop_write_args args
;
3587 auio
= uio_create(1, 0, UIO_SYSSPACE
, UIO_WRITE
);
3588 uio_addiov(auio
, (uintptr_t)buffer
, nodesize
);
3590 args
.a_desc
= &vnop_write_desc
;
3594 args
.a_context
= ctx
;
3599 error
= hfs_vnop_write(&args
);
3601 printf("hfs: error %d writing HFBT on %s\n", error
, HFSTOVCB(hfsmp
)->vcbVN
);
3605 hfs_free(buffer
, nodesize
);
3608 hfs_end_transaction(hfsmp
);
3622 * Compare two hot file b-tree keys.
3624 * Result: +n search key > trial key
3625 * 0 search key = trial key
3626 * -n search key < trial key
3629 hfc_comparekeys(HotFileKey
*searchKey
, HotFileKey
*trialKey
)
3632 * Compared temperatures first.
3634 if (searchKey
->temperature
== trialKey
->temperature
) {
3636 * Temperatures are equal so compare file ids.
3638 if (searchKey
->fileID
== trialKey
->fileID
) {
3640 * File ids are equal so compare fork types.
3642 if (searchKey
->forkType
== trialKey
->forkType
) {
3644 } else if (searchKey
->forkType
> trialKey
->forkType
) {
3647 } else if (searchKey
->fileID
> trialKey
->fileID
) {
3650 } else if (searchKey
->temperature
> trialKey
->temperature
) {
3659 *========================================================================
3660 * HOT FILE DATA COLLECTING ROUTINES
3661 *========================================================================
3665 * Lookup a hot file entry in the tree.
3668 static hotfile_entry_t
*
3669 hf_lookup(hotfile_data_t
*hotdata
, u_int32_t fileid
, u_int32_t temperature
)
3671 hotfile_entry_t
*entry
= hotdata
->rootentry
;
3674 entry
->temperature
!= temperature
&&
3675 entry
->fileid
!= fileid
) {
3677 if (temperature
> entry
->temperature
)
3678 entry
= entry
->right
;
3679 else if (temperature
< entry
->temperature
)
3680 entry
= entry
->left
;
3681 else if (fileid
> entry
->fileid
)
3682 entry
= entry
->right
;
3684 entry
= entry
->left
;
3691 * Insert a hot file entry into the tree.
3694 hf_insert(hotfile_data_t
*hotdata
, hotfile_entry_t
*newentry
)
3696 hotfile_entry_t
*entry
= hotdata
->rootentry
;
3697 u_int32_t fileid
= newentry
->fileid
;
3698 u_int32_t temperature
= newentry
->temperature
;
3700 if (entry
== NULL
) {
3701 hotdata
->rootentry
= newentry
;
3702 hotdata
->coldest
= newentry
;
3703 hotdata
->activefiles
++;
3708 if (temperature
> entry
->temperature
) {
3710 entry
= entry
->right
;
3712 entry
->right
= newentry
;
3715 } else if (temperature
< entry
->temperature
) {
3717 entry
= entry
->left
;
3719 entry
->left
= newentry
;
3722 } else if (fileid
> entry
->fileid
) {
3724 entry
= entry
->right
;
3726 if (entry
->fileid
!= fileid
)
3727 entry
->right
= newentry
;
3732 entry
= entry
->left
;
3734 if (entry
->fileid
!= fileid
) {
3735 entry
->left
= newentry
;
3744 hotdata
->activefiles
++;
3749 * Find the coldest entry in the tree.
3751 static hotfile_entry_t
*
3752 hf_coldest(hotfile_data_t
*hotdata
)
3754 hotfile_entry_t
*entry
= hotdata
->rootentry
;
3758 entry
= entry
->left
;
3764 * Find the hottest entry in the tree.
3766 static hotfile_entry_t
*
3767 hf_hottest(hotfile_data_t
*hotdata
)
3769 hotfile_entry_t
*entry
= hotdata
->rootentry
;
3772 while (entry
->right
)
3773 entry
= entry
->right
;
3779 * Delete a hot file entry from the tree.
3782 hf_delete(hotfile_data_t
*hotdata
, u_int32_t fileid
, u_int32_t temperature
)
3784 hotfile_entry_t
*entry
, *parent
, *next
;
3787 entry
= hotdata
->rootentry
;
3790 entry
->temperature
!= temperature
&&
3791 entry
->fileid
!= fileid
) {
3794 if (temperature
> entry
->temperature
)
3795 entry
= entry
->right
;
3796 else if (temperature
< entry
->temperature
)
3797 entry
= entry
->left
;
3798 else if (fileid
> entry
->fileid
)
3799 entry
= entry
->right
;
3801 entry
= entry
->left
;
3806 * Reorganize the sub-trees spanning from our entry.
3808 if ((next
= entry
->right
)) {
3809 hotfile_entry_t
*pnextl
, *psub
;
3811 * Tree pruning: take the left branch of the
3812 * current entry and place it at the lowest
3813 * left branch of the current right branch
3817 /* Walk the Right/Left sub tree from current entry */
3818 while ((pnextl
= psub
->left
))
3821 /* Plug the old left tree to the new ->Right leftmost entry */
3822 psub
->left
= entry
->left
;
3824 } else /* only left sub-tree, simple case */ {
3828 * Now, plug the current entry sub tree to
3829 * the good pointer of our parent entry.
3832 hotdata
->rootentry
= next
;
3833 else if (parent
->left
== entry
)
3834 parent
->left
= next
;
3836 parent
->right
= next
;
3838 /* Place entry back on the free-list */
3841 entry
->temperature
= 0;
3843 entry
->right
= hotdata
->freelist
;
3844 hotdata
->freelist
= entry
;
3845 hotdata
->activefiles
--;
3847 if (hotdata
->coldest
== entry
|| hotdata
->coldest
== NULL
) {
3848 hotdata
->coldest
= hf_coldest(hotdata
);
3855 * Get a free hot file entry.
3857 static hotfile_entry_t
*
3858 hf_getnewentry(hotfile_data_t
*hotdata
)
3860 hotfile_entry_t
* entry
;
3863 * When the free list is empty then steal the coldest one
3865 if (hotdata
->freelist
== NULL
) {
3866 entry
= hf_coldest(hotdata
);
3867 hf_delete(hotdata
, entry
->fileid
, entry
->temperature
);
3869 entry
= hotdata
->freelist
;
3870 hotdata
->freelist
= entry
->right
;
3878 * Generate a sorted list of hot files (hottest to coldest).
3880 * As a side effect, every node in the hot file tree will be
3881 * deleted (moved to the free list).
3884 hf_getsortedlist(hotfile_data_t
* hotdata
, hotfilelist_t
*sortedlist
)
3887 hotfile_entry_t
*entry
;
3889 while ((entry
= hf_hottest(hotdata
)) != NULL
) {
3890 sortedlist
->hfl_hotfile
[i
].hf_fileid
= entry
->fileid
;
3891 sortedlist
->hfl_hotfile
[i
].hf_temperature
= entry
->temperature
;
3892 sortedlist
->hfl_hotfile
[i
].hf_blocks
= entry
->blocks
;
3893 sortedlist
->hfl_totalblocks
+= entry
->blocks
;
3896 hf_delete(hotdata
, entry
->fileid
, entry
->temperature
);
3899 sortedlist
->hfl_count
= i
;
3902 printf("hfs: hf_getsortedlist returning %d entries w/%d total blocks\n", i
, sortedlist
->hfl_totalblocks
);
3909 hf_maxdepth(hotfile_entry_t
* root
, int depth
, int *maxdepth
)
3913 if (depth
> *maxdepth
)
3915 hf_maxdepth(root
->left
, depth
, maxdepth
);
3916 hf_maxdepth(root
->right
, depth
, maxdepth
);
3921 hf_printtree(hotfile_entry_t
* root
)
3924 hf_printtree(root
->left
);
3925 printf("hfs: temperature: % 8d, fileid %d\n", root
->temperature
, root
->fileid
);
3926 hf_printtree(root
->right
);