2 * Copyright (c) 2003-2013 Apple Inc. All rights reserved.
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
29 #include <sys/param.h>
30 #include <sys/systm.h>
31 #include <sys/fcntl.h>
32 #include <sys/kernel.h>
33 #include <sys/malloc.h>
35 #include <sys/ubc_internal.h>
36 #include <sys/vnode.h>
37 #include <sys/vnode_internal.h>
38 #include <sys/kauth.h>
41 #include <hfs/hfs_endian.h>
42 #include <hfs/hfs_format.h>
43 #include <hfs/hfs_mount.h>
44 #include <hfs/hfs_hotfiles.h>
46 #include "hfscommon/headers/BTreeScanner.h"
54 * Minimum post Tiger base time.
55 * Thu Mar 31 17:00:00 2005
57 #define HFC_MIN_BASE_TIME 0x424c8f00L
60 * Hot File List (runtime).
62 typedef struct hotfileinfo
{
64 u_int32_t hf_temperature
;
68 typedef struct hotfilelist
{
70 u_int32_t hfl_version
;
71 time_t hfl_duration
; /* duration of sample period */
72 int hfl_count
; /* count of hot files recorded */
73 int hfl_next
; /* next file to move */
74 int hfl_totalblocks
; /* total hot file blocks */
75 int hfl_reclaimblks
; /* blocks to reclaim in HFV */
76 u_int32_t hfl_spare
[2];
77 hotfileinfo_t hfl_hotfile
[1]; /* array of hot files */
82 * Hot File Entry (runtime).
84 typedef struct hotfile_entry
{
85 struct hotfile_entry
*left
;
86 struct hotfile_entry
*right
;
88 u_int32_t temperature
;
94 // We cap the max temperature for non-system files to "MAX_NORMAL_TEMP"
95 // so that they will always have a lower temperature than system (aka
96 // "auto-cached") files. System files have MAX_NORMAL_TEMP added to
97 // their temperature which produces two bands of files (all non-system
98 // files will have a temp less than MAX_NORMAL_TEMP and all system
99 // files will have a temp greatern than MAX_NORMAL_TEMP).
101 // This puts non-system files on the left side of the hotfile btree
102 // (and we start evicting from the left-side of the tree). The idea is
103 // that we will evict non-system files more aggressively since their
104 // working set changes much more dynamically than system files (which
105 // are for the most part, static).
107 // NOTE: these values have to fit into a 32-bit int. We use a
108 // value of 1-billion which gives a pretty broad range
109 // and yet should not run afoul of any sign issues.
111 #define MAX_NORMAL_TEMP 1000000000
112 #define HF_TEMP_RANGE MAX_NORMAL_TEMP
116 // These used to be defines of the hard coded values. But if
117 // we're on an cooperative fusion (CF) system we need to change
118 // the values (which happens in hfs_recording_init()
120 uint32_t hfc_default_file_count
= 1000;
121 uint32_t hfc_default_duration
= (3600 * 60);
122 uint32_t hfc_max_file_count
= 5000;
123 uint64_t hfc_max_file_size
= (10 * 1024 * 1024);
127 * Hot File Recording Data (runtime).
129 typedef struct hotfile_data
{
130 struct hfsmount
*hfsmp
;
132 u_int32_t activefiles
; /* active number of hot files */
135 hotfile_entry_t
*rootentry
;
136 hotfile_entry_t
*freelist
;
137 hotfile_entry_t
*coldest
;
138 hotfile_entry_t entries
[1];
141 static int hfs_recording_start (struct hfsmount
*);
142 static int hfs_recording_stop (struct hfsmount
*);
144 /* Hotfiles pinning routines */
145 static int hfs_getvnode_and_pin (struct hfsmount
*hfsmp
, uint32_t fileid
, uint32_t *pinned
);
146 static int hfs_pin_extent_record (struct hfsmount
*hfsmp
, HFSPlusExtentRecord extents
, uint32_t *pinned
);
147 static int hfs_pin_catalog_rec (struct hfsmount
*hfsmp
, HFSPlusCatalogFile
*cfp
, int rsrc
);
150 * Hot File Data recording functions (in-memory binary tree).
152 static int hf_insert (hotfile_data_t
*, hotfile_entry_t
*);
153 static void hf_delete (hotfile_data_t
*, u_int32_t
, u_int32_t
);
154 static hotfile_entry_t
* hf_coldest (hotfile_data_t
*);
155 static hotfile_entry_t
* hf_getnewentry (hotfile_data_t
*);
156 static void hf_getsortedlist (hotfile_data_t
*, hotfilelist_t
*);
159 static hotfile_entry_t
* hf_lookup (hotfile_data_t
*, u_int32_t
, u_int32_t
);
160 static void hf_maxdepth(hotfile_entry_t
*, int, int *);
161 static void hf_printtree (hotfile_entry_t
*);
165 * Hot File misc support functions.
167 static int hotfiles_collect (struct hfsmount
*);
168 static int hotfiles_age (struct hfsmount
*);
169 static int hotfiles_adopt (struct hfsmount
*, vfs_context_t
);
170 static int hotfiles_evict (struct hfsmount
*, vfs_context_t
);
171 static int hotfiles_refine (struct hfsmount
*);
172 static int hotextents(struct hfsmount
*, HFSPlusExtentDescriptor
*);
173 static int hfs_addhotfile_internal(struct vnode
*);
174 static int hfs_hotfile_cur_freeblks(hfsmount_t
*hfsmp
);
178 * Hot File Cluster B-tree (on disk) functions.
180 static int hfc_btree_create (struct hfsmount
*, unsigned int, unsigned int);
181 static int hfc_btree_open (struct hfsmount
*, struct vnode
**);
182 static int hfc_btree_open_ext(struct hfsmount
*hfsmp
, struct vnode
**vpp
, int ignore_btree_errs
);
183 static int hfc_btree_close (struct hfsmount
*, struct vnode
*);
184 static int hfc_btree_delete_record(struct hfsmount
*hfsmp
, BTreeIterator
*iterator
, HotFileKey
*key
);
185 static int hfc_btree_delete(struct hfsmount
*hfsmp
);
186 static int hfc_comparekeys (HotFileKey
*, HotFileKey
*);
189 char hfc_tag
[] = "CLUSTERED HOT FILES B-TREE ";
193 *========================================================================
194 * HOT FILE INTERFACE ROUTINES
195 *========================================================================
199 * Start recording the hottest files on a file system.
201 * Requires that the hfc_mutex be held.
204 hfs_recording_start(struct hfsmount
*hfsmp
)
206 hotfile_data_t
*hotdata
;
213 if ((hfsmp
->hfs_flags
& HFS_READ_ONLY
) ||
214 (hfsmp
->jnl
== NULL
) ||
215 (hfsmp
->hfs_flags
& HFS_METADATA_ZONE
) == 0) {
218 if (HFSTOVCB(hfsmp
)->freeBlocks
< (2 * (u_int32_t
)hfsmp
->hfs_hotfile_maxblks
)) {
221 if (hfsmp
->hfc_stage
!= HFC_IDLE
) {
224 hfsmp
->hfc_stage
= HFC_BUSY
;
227 * Dump previous recording data.
229 if (hfsmp
->hfc_recdata
) {
232 tmp
= hfsmp
->hfc_recdata
;
233 hfsmp
->hfc_recdata
= NULL
;
237 microtime(&tv
); /* Times are base on GMT time. */
240 * On first startup check for suspended recording.
242 if (hfsmp
->hfc_timebase
== 0 &&
243 hfc_btree_open(hfsmp
, &hfsmp
->hfc_filevp
) == 0) {
244 HotFilesInfo hotfileinfo
;
246 if ((BTGetUserData(VTOF(hfsmp
->hfc_filevp
), &hotfileinfo
,
247 sizeof(hotfileinfo
)) == 0) &&
248 (SWAP_BE32 (hotfileinfo
.magic
) == HFC_MAGIC
) &&
249 (SWAP_BE32 (hotfileinfo
.timeleft
) > 0) &&
250 (SWAP_BE32 (hotfileinfo
.timebase
) > 0)) {
251 if (hfsmp
->hfs_flags
& HFS_CS_HOTFILE_PIN
) {
252 if (hfsmp
->hfs_hotfile_freeblks
== 0) {
253 hfsmp
->hfs_hotfile_freeblks
= hfsmp
->hfs_hotfile_maxblks
- SWAP_BE32 (hotfileinfo
.usedblocks
);
255 hfsmp
->hfc_maxfiles
= 0x7fffffff;
256 printf("hfs: %s: %s: hotfile freeblocks: %d, max: %d\n", hfsmp
->vcbVN
, __FUNCTION__
,
257 hfsmp
->hfs_hotfile_freeblks
, hfsmp
->hfs_hotfile_maxblks
);
259 hfsmp
->hfc_maxfiles
= SWAP_BE32 (hotfileinfo
.maxfilecnt
);
261 hfsmp
->hfc_timebase
= SWAP_BE32 (hotfileinfo
.timebase
);
262 int timeleft
= (int)SWAP_BE32(hotfileinfo
.timeleft
);
263 if (timeleft
< 0 || timeleft
> (int)(HFC_DEFAULT_DURATION
*2)) {
264 // in case this field got botched, don't let it screw things up
265 // printf("hfs: hotfiles: bogus looking timeleft: %d\n", timeleft);
266 timeleft
= HFC_DEFAULT_DURATION
;
268 hfsmp
->hfc_timeout
= timeleft
+ tv
.tv_sec
;
269 /* Fix up any bogus timebase values. */
270 if (hfsmp
->hfc_timebase
< HFC_MIN_BASE_TIME
) {
271 hfsmp
->hfc_timebase
= hfsmp
->hfc_timeout
- HFC_DEFAULT_DURATION
;
274 printf("hfs: Resume recording hot files on %s (%d secs left (%d); timeout %ld)\n",
275 hfsmp
->vcbVN
, SWAP_BE32 (hotfileinfo
.timeleft
), timeleft
, hfsmp
->hfc_timeout
- tv
.tv_sec
);
278 hfsmp
->hfc_maxfiles
= HFC_DEFAULT_FILE_COUNT
;
279 hfsmp
->hfc_timebase
= tv
.tv_sec
+ 1;
280 hfsmp
->hfc_timeout
= hfsmp
->hfc_timebase
+ HFC_DEFAULT_DURATION
;
282 (void) hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
283 hfsmp
->hfc_filevp
= NULL
;
285 struct cat_attr cattr
;
289 * Make sure a btree file exists.
291 cnid
= GetFileInfo(HFSTOVCB(hfsmp
), kRootDirID
, HFC_FILENAME
, &cattr
, NULL
);
293 !S_ISREG(cattr
.ca_mode
) &&
294 (error
= hfc_btree_create(hfsmp
, HFSTOVCB(hfsmp
)->blockSize
, HFC_DEFAULT_FILE_COUNT
))) {
295 hfsmp
->hfc_stage
= HFC_IDLE
;
296 wakeup((caddr_t
)&hfsmp
->hfc_stage
);
300 printf("hfs: begin recording hot files on %s (hotfile start/end block: %d - %d; max/free: %d/%d; maxfiles: %d)\n",
302 hfsmp
->hfs_hotfile_start
, hfsmp
->hfs_hotfile_end
,
303 hfsmp
->hfs_hotfile_maxblks
, hfsmp
->hfs_hotfile_freeblks
, hfsmp
->hfc_maxfiles
);
305 hfsmp
->hfc_maxfiles
= HFC_DEFAULT_FILE_COUNT
;
306 hfsmp
->hfc_timeout
= tv
.tv_sec
+ HFC_DEFAULT_DURATION
;
308 /* Reset time base. */
309 if (hfsmp
->hfc_timebase
== 0) {
310 hfsmp
->hfc_timebase
= tv
.tv_sec
+ 1;
312 time_t cumulativebase
;
314 cumulativebase
= hfsmp
->hfc_timeout
- (HFC_CUMULATIVE_CYCLES
* HFC_DEFAULT_DURATION
);
315 hfsmp
->hfc_timebase
= MAX(hfsmp
->hfc_timebase
, cumulativebase
);
319 if ((hfsmp
->hfc_maxfiles
== 0) ||
320 (hfsmp
->hfc_maxfiles
> HFC_MAXIMUM_FILE_COUNT
)) {
321 hfsmp
->hfc_maxfiles
= HFC_DEFAULT_FILE_COUNT
;
323 maxentries
= hfsmp
->hfc_maxfiles
;
325 size
= sizeof(hotfile_data_t
) + (maxentries
* sizeof(hotfile_entry_t
));
326 MALLOC(hotdata
, hotfile_data_t
*, size
, M_TEMP
, M_WAITOK
);
327 if (hotdata
== NULL
) {
328 hfsmp
->hfc_recdata
= NULL
;
329 hfsmp
->hfc_stage
= HFC_IDLE
;
330 wakeup((caddr_t
)&hfsmp
->hfc_stage
);
334 bzero(hotdata
, size
);
336 for (i
= 1; i
< maxentries
; i
++)
337 hotdata
->entries
[i
-1].right
= &hotdata
->entries
[i
];
339 hotdata
->freelist
= &hotdata
->entries
[0];
341 * Establish minimum temperature and maximum file size.
343 hotdata
->threshold
= HFC_MINIMUM_TEMPERATURE
;
344 hotdata
->maxblocks
= HFC_MAXIMUM_FILESIZE
/ HFSTOVCB(hfsmp
)->blockSize
;
345 hotdata
->hfsmp
= hfsmp
;
347 hfsmp
->hfc_recdata
= hotdata
;
348 hfsmp
->hfc_stage
= HFC_RECORDING
;
349 wakeup((caddr_t
)&hfsmp
->hfc_stage
);
354 * Stop recording the hotest files on a file system.
356 * Requires that the hfc_mutex be held.
359 hfs_recording_stop(struct hfsmount
*hfsmp
)
361 hotfile_data_t
*hotdata
;
362 hotfilelist_t
*listp
;
365 enum hfc_stage newstage
= HFC_IDLE
;
368 if (hfsmp
->hfc_stage
!= HFC_RECORDING
)
371 hfsmp
->hfc_stage
= HFC_BUSY
;
373 hotfiles_collect(hfsmp
);
377 * Convert hot file data into a simple file id list....
379 * then dump the sample data
382 printf("hfs: end of hot file recording on %s\n", hfsmp
->vcbVN
);
384 hotdata
= (hotfile_data_t
*)hfsmp
->hfc_recdata
;
387 hfsmp
->hfc_recdata
= NULL
;
388 hfsmp
->hfc_stage
= HFC_EVALUATION
;
389 wakeup((caddr_t
)&hfsmp
->hfc_stage
);
392 printf("hfs: curentries: %d\n", hotdata
->activefiles
);
395 * If no hot files recorded then we're done.
397 if (hotdata
->rootentry
== NULL
) {
402 /* Open the B-tree file for writing... */
403 if (hfsmp
->hfc_filevp
)
404 panic("hfs_recording_stop: hfc_filevp exists (vp = %p)", hfsmp
->hfc_filevp
);
406 error
= hfc_btree_open(hfsmp
, &hfsmp
->hfc_filevp
);
412 * Age the previous set of clustered hot files.
414 error
= hotfiles_age(hfsmp
);
416 (void) hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
417 hfsmp
->hfc_filevp
= NULL
;
422 * Create a sorted list of hotest files.
424 size
= sizeof(hotfilelist_t
);
425 size
+= sizeof(hotfileinfo_t
) * (hotdata
->activefiles
- 1);
426 MALLOC(listp
, hotfilelist_t
*, size
, M_TEMP
, M_WAITOK
);
429 (void) hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
430 hfsmp
->hfc_filevp
= NULL
;
436 hf_getsortedlist(hotdata
, listp
); /* NOTE: destroys hot file tree! */
438 listp
->hfl_duration
= tv
.tv_sec
- hfsmp
->hfc_timebase
;
439 hfsmp
->hfc_recdata
= listp
;
442 * Account for duplicates.
444 error
= hotfiles_refine(hfsmp
);
446 (void) hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
447 hfsmp
->hfc_filevp
= NULL
;
452 * Compute the amount of space to reclaim...
454 if (listp
->hfl_totalblocks
> hfs_hotfile_cur_freeblks(hfsmp
)) {
455 listp
->hfl_reclaimblks
=
456 MIN(listp
->hfl_totalblocks
, hfsmp
->hfs_hotfile_maxblks
) -
457 hfsmp
->hfs_hotfile_freeblks
;
459 printf("hfs_recording_stop: need to reclaim %d blocks\n", listp
->hfl_reclaimblks
);
461 if (listp
->hfl_reclaimblks
)
462 newstage
= HFC_EVICTION
;
464 newstage
= HFC_ADOPTION
;
466 newstage
= HFC_ADOPTION
;
469 if (newstage
== HFC_ADOPTION
&& listp
->hfl_totalblocks
== 0) {
470 (void) hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
471 hfsmp
->hfc_filevp
= NULL
;
476 if (newstage
== HFC_EVICTION
)
477 printf("hfs: evicting coldest files\n");
478 else if (newstage
== HFC_ADOPTION
)
479 printf("hfs: adopting hotest files\n");
481 FREE(hotdata
, M_TEMP
);
483 hfsmp
->hfc_stage
= newstage
;
484 wakeup((caddr_t
)&hfsmp
->hfc_stage
);
489 save_btree_user_info(struct hfsmount
*hfsmp
)
491 HotFilesInfo hotfileinfo
;
495 hotfileinfo
.magic
= SWAP_BE32 (HFC_MAGIC
);
496 hotfileinfo
.version
= SWAP_BE32 (HFC_VERSION
);
497 hotfileinfo
.duration
= SWAP_BE32 (HFC_DEFAULT_DURATION
);
498 hotfileinfo
.timebase
= SWAP_BE32 (hfsmp
->hfc_timebase
);
499 hotfileinfo
.timeleft
= SWAP_BE32 (hfsmp
->hfc_timeout
- tv
.tv_sec
);
500 hotfileinfo
.threshold
= SWAP_BE32 (HFC_MINIMUM_TEMPERATURE
);
501 hotfileinfo
.maxfileblks
= SWAP_BE32 (HFC_MAXIMUM_FILESIZE
/ HFSTOVCB(hfsmp
)->blockSize
);
502 if (hfsmp
->hfs_flags
& HFS_CS_HOTFILE_PIN
) {
503 hotfileinfo
.usedblocks
= SWAP_BE32 (hfsmp
->hfs_hotfile_maxblks
- hfs_hotfile_cur_freeblks(hfsmp
));
505 printf("hfs: %s: saving usedblocks = %d (timeleft: %d; timeout %ld)\n", hfsmp
->vcbVN
, (hfsmp
->hfs_hotfile_maxblks
- hfsmp
->hfs_hotfile_freeblks
),
506 SWAP_BE32(hotfileinfo
.timeleft
), hfsmp
->hfc_timeout
);
509 hotfileinfo
.maxfilecnt
= SWAP_BE32 (HFC_DEFAULT_FILE_COUNT
);
511 strlcpy((char *)hotfileinfo
.tag
, hfc_tag
, sizeof hotfileinfo
.tag
);
512 (void) BTSetUserData(VTOF(hfsmp
->hfc_filevp
), &hotfileinfo
, sizeof(hotfileinfo
));
516 * Suspend recording the hotest files on a file system.
519 hfs_recording_suspend(struct hfsmount
*hfsmp
)
521 hotfile_data_t
*hotdata
= NULL
;
524 if (hfsmp
->hfc_stage
== HFC_DISABLED
)
527 lck_mtx_lock(&hfsmp
->hfc_mutex
);
531 * A suspend can occur during eval/evict/adopt stage.
532 * In that case we would need to write out info and
533 * flush our HFBT vnode. Currently we just bail.
536 hotdata
= (hotfile_data_t
*)hfsmp
->hfc_recdata
;
537 if (hotdata
== NULL
|| hfsmp
->hfc_stage
!= HFC_RECORDING
) {
541 hfsmp
->hfc_stage
= HFC_BUSY
;
544 printf("hfs: suspend hot file recording on %s\n", hfsmp
->vcbVN
);
546 error
= hfc_btree_open(hfsmp
, &hfsmp
->hfc_filevp
);
548 printf("hfs_recording_suspend: err %d opening btree\n", error
);
552 if (hfs_start_transaction(hfsmp
) != 0) {
555 if (hfs_lock(VTOC(hfsmp
->hfc_filevp
), HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
) != 0) {
556 goto end_transaction
;
559 save_btree_user_info(hfsmp
);
561 hfs_unlock(VTOC(hfsmp
->hfc_filevp
));
564 hfs_end_transaction(hfsmp
);
567 if (hfsmp
->hfc_filevp
) {
568 (void) hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
569 hfsmp
->hfc_filevp
= NULL
;
572 FREE(hotdata
, M_TEMP
);
573 hfsmp
->hfc_recdata
= NULL
;
575 hfsmp
->hfc_stage
= HFC_DISABLED
;
576 wakeup((caddr_t
)&hfsmp
->hfc_stage
);
578 lck_mtx_unlock(&hfsmp
->hfc_mutex
);
584 reset_file_ids(struct hfsmount
*hfsmp
, uint32_t *fileid_table
, int num_ids
)
588 for(i
=0; i
< num_ids
; i
++) {
591 error
= hfs_vget(hfsmp
, fileid_table
[i
], &vp
, 0, 0);
593 if (error
== ENOENT
) {
595 continue; /* stale entry, go to next */
600 // hfs_vget returns a locked cnode so no need to lock here
602 if ((hfsmp
->hfs_flags
& HFS_CS_HOTFILE_PIN
) && (VTOC(vp
)->c_attr
.ca_recflags
& kHFSFastDevPinnedMask
)) {
603 error
= hfs_pin_vnode(hfsmp
, vp
, HFS_UNPIN_IT
, NULL
, vfs_context_kernel());
607 * The updates to the catalog must be journaled
609 hfs_start_transaction(hfsmp
);
612 // turn off _all_ the hotfile related bits since we're resetting state
614 if (VTOC(vp
)->c_attr
.ca_recflags
& kHFSFastDevCandidateMask
) {
615 vnode_clearfastdevicecandidate(vp
);
618 VTOC(vp
)->c_attr
.ca_recflags
&= ~(kHFSFastDevPinnedMask
|kHFSDoNotFastDevPinMask
|kHFSFastDevCandidateMask
|kHFSAutoCandidateMask
);
619 VTOC(vp
)->c_flag
|= C_MODIFIED
;
623 hfs_end_transaction(hfsmp
);
625 hfs_unlock(VTOC(vp
));
631 flag_hotfile(struct hfsmount
*hfsmp
, const char *filename
)
633 struct vnode
*dvp
= NULL
, *fvp
= NULL
;
634 vfs_context_t ctx
= vfs_context_kernel();
635 struct componentname cname
;
638 const char *orig_fname
= filename
;
640 if (filename
== NULL
) {
644 fname_len
= strlen(filename
); // do NOT include the trailing '\0' so that we break out of the loop below
646 error
= VFS_ROOT(HFSTOVFS(hfsmp
), &dvp
, ctx
);
651 /* At this point, 'dvp' must be considered iocounted */
655 while (ptr
< (orig_fname
+ fname_len
- 1)) {
656 for(; ptr
< (orig_fname
+ fname_len
) && *ptr
&& *ptr
!= '/'; ptr
++) {
657 /* just keep advancing till we reach the end of the string or a slash */
660 cname
.cn_nameiop
= LOOKUP
;
661 cname
.cn_flags
= ISLASTCN
;
662 cname
.cn_context
= ctx
;
664 cname
.cn_pnbuf
= __DECONST(char *, orig_fname
);
665 cname
.cn_nameptr
= __DECONST(char *, filename
);
666 cname
.cn_pnlen
= fname_len
;
667 cname
.cn_namelen
= ptr
- filename
;
669 cname
.cn_consume
= 0;
671 error
= VNOP_LOOKUP(dvp
, &fvp
, &cname
, ctx
);
674 * If 'dvp' is non-NULL, then it has an iocount. Make sure to release it
675 * before bailing out. VNOP_LOOKUP could legitimately return ENOENT
676 * if the item didn't exist or if we raced with a delete.
685 if (ptr
< orig_fname
+ fname_len
- 1) {
687 // we've got a multi-part pathname so drop the ref on the dir,
688 // make dvp become what we just looked up, and advance over
689 // the slash character in the pathname to get to the next part
696 filename
= ++ptr
; // skip the slash character
705 struct cnode
*cp
= VTOC(fvp
);
706 if ((error
= hfs_lock(cp
, HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
)) != 0) {
710 hfs_start_transaction(hfsmp
);
712 cp
->c_attr
.ca_recflags
|= (kHFSFastDevCandidateMask
|kHFSAutoCandidateMask
);
713 cp
->c_flag
|= C_MODIFIED
;
717 hfs_end_transaction(hfsmp
);
720 //printf("hfs: flagged /%s with the fast-dev-candidate|auto-candidate flags\n", filename);
739 hfs_setup_default_cf_hotfiles(struct hfsmount
*hfsmp
)
741 const char *system_default_hotfiles
[] = {
745 "private/var/db/dyld"
749 for(i
=0; i
< (int)(sizeof(system_default_hotfiles
)/sizeof(char *)); i
++) {
750 flag_hotfile(hfsmp
, system_default_hotfiles
[i
]);
755 #define NUM_FILE_RESET_IDS 4096 // so we allocate 16k to hold file-ids
758 hfs_hotfile_reset(struct hfsmount
*hfsmp
)
761 CatalogRecord
* datap
;
763 BTScanState scanstate
;
764 BTreeIterator
* iterator
= NULL
;
765 FSBufferDescriptor record
;
769 uint32_t *fileids
=NULL
;
770 int cur_id_index
= 0;
772 int cleared
= 0; /* debug variables */
777 printf("hfs: %s: %s\n", hfsmp
->vcbVN
, __FUNCTION__
);
780 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
781 if (iterator
== NULL
) {
785 bzero(iterator
, sizeof(*iterator
));
787 MALLOC(fileids
, uint32_t *, NUM_FILE_RESET_IDS
* sizeof(uint32_t), M_TEMP
, M_WAITOK
);
788 if (fileids
== NULL
) {
793 record
.bufferAddress
= &data
;
794 record
.itemSize
= sizeof(u_int32_t
);
795 record
.itemCount
= 1;
798 * Get ready to scan the Catalog file.
800 error
= BTScanInitialize(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), 0, 0, 0,
801 kCatSearchBufferSize
, &scanstate
);
803 printf("hfs_hotfile_reset: err %d BTScanInit\n", error
);
808 * Visit all the catalog btree leaf records, clearing any that have the
809 * HotFileCached bit set.
812 error
= BTScanNextRecord(&scanstate
, 0, (void **)&keyp
, (void **)&datap
, &dataSize
);
814 if (error
== btNotFound
)
817 printf("hfs_hotfile_reset: err %d BTScanNext\n", error
);
821 if (datap
->recordType
== kHFSPlusFolderRecord
&& (dataSize
== sizeof(HFSPlusCatalogFolder
))) {
822 HFSPlusCatalogFolder
*dirp
= (HFSPlusCatalogFolder
*)datap
;
826 if ((dirp
->flags
& (kHFSFastDevPinnedMask
|kHFSDoNotFastDevPinMask
|kHFSFastDevCandidateMask
|kHFSAutoCandidateMask
)) == 0) {
830 cnid
= dirp
->folderID
;
831 } else if ((datap
->recordType
== kHFSPlusFileRecord
) && (dataSize
== sizeof(HFSPlusCatalogFile
))) {
832 HFSPlusCatalogFile
*filep
= (HFSPlusCatalogFile
*)datap
;
837 * If the file doesn't have any of the HotFileCached bits set, ignore it.
839 if ((filep
->flags
& (kHFSFastDevPinnedMask
|kHFSDoNotFastDevPinMask
|kHFSFastDevCandidateMask
|kHFSAutoCandidateMask
)) == 0) {
843 cnid
= filep
->fileID
;
848 /* Skip over journal files. */
849 if (cnid
== hfsmp
->hfs_jnlfileid
|| cnid
== hfsmp
->hfs_jnlinfoblkid
) {
854 // Just record the cnid of the file for now. We will modify it separately
855 // because we can't modify the catalog while we're scanning it.
857 fileids
[cur_id_index
++] = cnid
;
858 if (cur_id_index
>= NUM_FILE_RESET_IDS
) {
860 // We're over the limit of file-ids so we have to terminate this
861 // scan, go modify all the catalog records, then restart the scan.
862 // This is required because it's not permissible to modify the
863 // catalog while scanning it.
865 (void) BTScanTerminate(&scanstate
, &data
, &data
, &data
);
867 reset_file_ids(hfsmp
, fileids
, cur_id_index
);
868 cleared
+= cur_id_index
;
872 error
= BTScanInitialize(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), 0, 0, 0,
873 kCatSearchBufferSize
, &scanstate
);
875 printf("hfs_hotfile_reset: err %d BTScanInit\n", error
);
883 reset_file_ids(hfsmp
, fileids
, cur_id_index
);
884 cleared
+= cur_id_index
;
888 printf("hfs: cleared HotFileCache related bits on %d files out of %d (dircount %d)\n", cleared
, filecount
, dircount
);
890 (void) BTScanTerminate(&scanstate
, &data
, &data
, &data
);
894 FREE(fileids
, M_TEMP
);
897 FREE(iterator
, M_TEMP
);
900 // If the hotfile btree exists, delete it. We need to open
901 // it to be able to delete it because we need the hfc_filevp
904 error
= hfc_btree_open_ext(hfsmp
, &hfsmp
->hfc_filevp
, 1);
906 printf("hfs: hotfile_reset: deleting existing hotfile btree\n");
907 hfc_btree_delete(hfsmp
);
910 if (hfsmp
->hfc_filevp
) {
911 (void) hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
912 hfsmp
->hfc_filevp
= NULL
;
915 hfsmp
->hfs_hotfile_blk_adjust
= 0;
916 hfsmp
->hfs_hotfile_freeblks
= hfsmp
->hfs_hotfile_maxblks
;
921 // This should ONLY be called by hfs_recording_init() and the special fsctl.
923 // We assume that the hotfile btree is already opened.
926 hfs_hotfile_repin_files(struct hfsmount
*hfsmp
)
928 BTreeIterator
* iterator
= NULL
;
930 filefork_t
* filefork
;
933 enum hfc_stage stage
;
934 uint32_t pinned_blocks
;
935 uint32_t num_files
=0, nrsrc
=0;
936 uint32_t total_pinned
=0;
938 if (!(hfsmp
->hfs_flags
& HFS_CS_HOTFILE_PIN
) || !hfsmp
->hfc_filevp
) {
940 // this is only meaningful if we're pinning hotfiles
941 // (as opposed to the regular form of hotfiles that
942 // get relocated to the hotfile zone)
948 printf("hfs: %s: %s\n", hfsmp
->vcbVN
, __FUNCTION__
);
951 if (hfs_lock(VTOC(hfsmp
->hfc_filevp
), HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
) != 0) {
956 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
957 if (iterator
== NULL
) {
958 hfs_unlock(VTOC(hfsmp
->hfc_filevp
));
962 stage
= hfsmp
->hfc_stage
;
963 hfsmp
->hfc_stage
= HFC_BUSY
;
965 bt_op
= kBTreeFirstRecord
;
967 bzero(iterator
, sizeof(*iterator
));
968 key
= (HotFileKey
*) &iterator
->key
;
970 filefork
= VTOF(hfsmp
->hfc_filevp
);
977 * Obtain the first record (ie the coldest one).
979 if (BTIterateRecord(filefork
, bt_op
, iterator
, NULL
, NULL
) != 0) {
984 if (key
->keyLength
!= HFC_KEYLENGTH
) {
985 // printf("hfs: hotfiles_repin_files: invalid key length %d\n", key->keyLength);
989 if (key
->temperature
== HFC_LOOKUPTAG
) {
990 // ran into thread records in the hotfile btree
996 // Just lookup the records in the catalog and pin the direct
997 // mapped extents. Faster than instantiating full vnodes
998 // (and thereby thrashing the system vnode cache).
1000 struct cat_desc fdesc
;
1001 struct cat_attr attr
;
1002 struct cat_fork fork
;
1003 uint8_t forktype
= 0;
1005 lockflags
= hfs_systemfile_lock(hfsmp
, (SFL_CATALOG
| SFL_EXTENTS
), HFS_SHARED_LOCK
);
1007 * Snoop the cnode hash to find out if the item we want is in-core already.
1009 * We largely expect this function to fail (the items we want are probably not in the hash).
1010 * we use the special variant which bails out as soon as it finds a vnode (even if it is
1011 * marked as open-unlinked or actually removed on-disk. If we find a vnode, then we
1012 * release the systemfile locks and go through the pin-vnode path instead.
1014 if (hfs_chash_snoop (hfsmp
, key
->fileID
, 1, NULL
, NULL
) == 0) {
1017 /* unlock immediately and go through the in-core path */
1018 hfs_systemfile_unlock(hfsmp
, lockflags
);
1021 error
= hfs_getvnode_and_pin (hfsmp
, key
->fileID
, &pinned_blocks
);
1023 /* if ENOENT, then it was deleted in the catalog. Remove from our hotfiles tracking */
1024 if (error
== ENOENT
) {
1025 hfc_btree_delete_record(hfsmp
, iterator
, key
);
1027 /* other errors, just ignore and move on with life */
1030 total_pinned
+= pinned_blocks
;
1037 /* If we get here, we're still holding the systemfile locks */
1038 error
= cat_idlookup(hfsmp
, key
->fileID
, 1, 0, &fdesc
, &attr
, &fork
);
1041 // this file system could have been mounted while booted from a
1042 // different partition and thus the hotfile btree would not have
1043 // been maintained. thus a file that was hotfile cached could
1044 // have been deleted while booted from a different partition which
1045 // means we need to delete it from the hotfile btree.
1047 // block accounting is taken care of at the end: we re-assign
1048 // hfsmp->hfs_hotfile_freeblks based on how many blocks we actually
1051 hfc_btree_delete_record(hfsmp
, iterator
, key
);
1056 if (fork
.cf_size
== 0) {
1057 // hmmm, the data is probably in the resource fork (aka a compressed file)
1058 error
= cat_idlookup(hfsmp
, key
->fileID
, 1, 1, &fdesc
, &attr
, &fork
);
1060 hfc_btree_delete_record(hfsmp
, iterator
, key
);
1069 /* Can't release the catalog /extents lock yet, we may need to go find the overflow blocks */
1070 error
= hfs_pin_extent_record (hfsmp
, fork
.cf_extents
, &pinned_blocks
);
1072 goto next
; //skip to next
1074 /* add in the blocks from the inline 8 */
1075 total_pinned
+= pinned_blocks
;
1078 /* Could this file have overflow extents? */
1079 if (fork
.cf_extents
[kHFSPlusExtentDensity
-1].startBlock
) {
1080 /* better pin them, too */
1081 error
= hfs_pin_overflow_extents (hfsmp
, key
->fileID
, forktype
, &pinned_blocks
);
1083 /* If we fail to pin all of the overflow extents, then just skip to the next file */
1089 if (pinned_blocks
) {
1090 /* now add in any overflow also */
1091 total_pinned
+= pinned_blocks
;
1096 hfs_systemfile_unlock(hfsmp
, lockflags
);
1099 bt_op
= kBTreeNextRecord
;
1104 printf("hfs: hotfiles_repin_files: re-pinned %d files (nrsrc %d, total pinned %d blks; freeblock %d, maxblocks %d, calculated free: %d)\n",
1105 num_files
, nrsrc
, total_pinned
, hfsmp
->hfs_hotfile_freeblks
, hfsmp
->hfs_hotfile_maxblks
,
1106 hfsmp
->hfs_hotfile_maxblks
- total_pinned
);
1109 // make sure this is accurate based on how many blocks we actually pinned
1111 hfsmp
->hfs_hotfile_freeblks
= hfsmp
->hfs_hotfile_maxblks
- total_pinned
;
1113 hfs_unlock(VTOC(hfsmp
->hfc_filevp
));
1115 FREE(iterator
, M_TEMP
);
1116 hfsmp
->hfc_stage
= stage
;
1117 wakeup((caddr_t
)&hfsmp
->hfc_stage
);
1122 hfs_repin_hotfiles(struct hfsmount
*hfsmp
)
1124 int error
, need_close
;
1126 lck_mtx_lock(&hfsmp
->hfc_mutex
);
1128 if (hfsmp
->hfc_filevp
== NULL
) {
1129 error
= hfc_btree_open(hfsmp
, &hfsmp
->hfc_filevp
);
1133 printf("hfs: failed to open the btree err=%d. Unable to re-pin hotfiles.\n", error
);
1134 lck_mtx_unlock(&hfsmp
->hfc_mutex
);
1141 hfs_pin_vnode(hfsmp
, hfsmp
->hfc_filevp
, HFS_PIN_IT
, NULL
, vfs_context_kernel());
1143 hfs_hotfile_repin_files(hfsmp
);
1146 (void) hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
1147 hfsmp
->hfc_filevp
= NULL
;
1150 lck_mtx_unlock(&hfsmp
->hfc_mutex
);
1154 * For a given file ID, find and pin all of its overflow extents to the underlying CS
1155 * device. Assumes that the extents overflow b-tree is locked for the duration of this call.
1157 * Emit the number of blocks pinned in output argument 'pinned'
1159 * Return success or failure (errno) in return value.
1162 int hfs_pin_overflow_extents (struct hfsmount
*hfsmp
, uint32_t fileid
,
1163 uint8_t forktype
, uint32_t *pinned
) {
1165 struct BTreeIterator
*ext_iter
= NULL
;
1166 ExtentKey
*ext_key_ptr
= NULL
;
1167 ExtentRecord ext_data
;
1168 FSBufferDescriptor btRecord
;
1169 uint16_t btRecordSize
;
1172 uint32_t pinned_blocks
= 0;
1175 MALLOC (ext_iter
, struct BTreeIterator
*, sizeof (struct BTreeIterator
), M_TEMP
, M_WAITOK
);
1176 if (ext_iter
== NULL
) {
1179 bzero (ext_iter
, sizeof(*ext_iter
));
1181 BTInvalidateHint (ext_iter
);
1182 ext_key_ptr
= (ExtentKey
*)&ext_iter
->key
;
1183 btRecord
.bufferAddress
= &ext_data
;
1184 btRecord
.itemCount
= 1;
1187 * This is like when you delete a file; we don't actually need most of the search machinery because
1188 * we are going to need all of the extent records that belong to this file (for a given fork type),
1189 * so we might as well use a straight-up iterator.
1191 * Position the B-Tree iterator at the first record with this file ID
1193 btRecord
.itemSize
= sizeof (HFSPlusExtentRecord
);
1194 ext_key_ptr
->hfsPlus
.keyLength
= kHFSPlusExtentKeyMaximumLength
;
1195 ext_key_ptr
->hfsPlus
.forkType
= forktype
;
1196 ext_key_ptr
->hfsPlus
.pad
= 0;
1197 ext_key_ptr
->hfsPlus
.fileID
= fileid
;
1198 ext_key_ptr
->hfsPlus
.startBlock
= 0;
1200 error
= BTSearchRecord (VTOF(hfsmp
->hfs_extents_vp
), ext_iter
, &btRecord
, &btRecordSize
, ext_iter
);
1201 if (error
== btNotFound
) {
1202 /* empty b-tree, so that's ok. we'll fall out during error check below. */
1207 uint32_t found_fileid
;
1210 error
= BTIterateRecord (VTOF(hfsmp
->hfs_extents_vp
), kBTreeNextRecord
, ext_iter
, &btRecord
, &btRecordSize
);
1212 /* swallow it if it's btNotFound, otherwise just bail out */
1213 if (error
== btNotFound
)
1218 found_fileid
= ext_key_ptr
->hfsPlus
.fileID
;
1220 * We only do one fork type at a time. So if either the fork-type doesn't
1221 * match what we are looking for (resource or data), OR the file id doesn't match
1222 * which indicates that there's nothing more with this file ID as the key, then bail out
1224 if ((found_fileid
!= fileid
) || (ext_key_ptr
->hfsPlus
.forkType
!= forktype
)) {
1229 /* Otherwise, we now have an extent record. Process and pin all of the file extents. */
1231 error
= hfs_pin_extent_record (hfsmp
, ext_data
.hfsPlus
, &pblocks
);
1236 pinned_blocks
+= pblocks
;
1238 /* if 8th extent is empty, then bail out */
1239 if (ext_data
.hfsPlus
[kHFSPlusExtentDensity
-1].startBlock
== 0) {
1244 } // end extent-getting loop
1246 /* dump the iterator */
1247 FREE (ext_iter
, M_TEMP
);
1251 * In the event that the file has no overflow extents, pinned_blocks
1252 * will never be updated, so we'll properly export 0 pinned blocks to caller
1254 *pinned
= pinned_blocks
;
1263 hfs_getvnode_and_pin (struct hfsmount
*hfsmp
, uint32_t fileid
, uint32_t *pinned
) {
1270 * Acquire the vnode for this file. This returns a locked cnode on success
1272 error
= hfs_vget(hfsmp
, fileid
, &vp
, 0, 0);
1274 /* It's possible the file was open-unlinked. In this case, we'll get ENOENT back. */
1279 * Symlinks that may have been inserted into the hotfile zone during a previous OS are now stuck
1280 * here. We do not want to move them.
1282 if (!vnode_isreg(vp
)) {
1283 hfs_unlock(VTOC(vp
));
1288 if (!(VTOC(vp
)->c_attr
.ca_recflags
& kHFSFastDevPinnedMask
)) {
1289 hfs_unlock(VTOC(vp
));
1294 error
= hfs_pin_vnode(hfsmp
, vp
, HFS_PIN_IT
, &pblocks
, vfs_context_kernel());
1299 hfs_unlock(VTOC(vp
));
1307 * Pins an HFS Extent record to the underlying CoreStorage. Assumes that Catalog & Extents overflow
1308 * B-trees are held locked, as needed.
1310 * Returns the number of blocks pinned in the output argument 'pinned'
1312 * Returns error status (0 || errno) in return value.
1314 static int hfs_pin_extent_record (struct hfsmount
*hfsmp
, HFSPlusExtentRecord extents
, uint32_t *pinned
) {
1319 if (pinned
== NULL
) {
1326 /* iterate through the extents */
1327 for ( i
= 0; i
< kHFSPlusExtentDensity
; i
++) {
1328 if (extents
[i
].startBlock
== 0) {
1332 error
= hfs_pin_block_range (hfsmp
, HFS_PIN_IT
, extents
[i
].startBlock
,
1333 extents
[i
].blockCount
, vfs_context_kernel());
1338 pb
+= extents
[i
].blockCount
;
1347 * Consume an HFS Plus on-disk catalog record and pin its blocks
1348 * to the underlying CS devnode.
1350 * NOTE: This is an important distinction!
1351 * This function takes in an HFSPlusCatalogFile* which is the actual
1352 * 200-some-odd-byte on-disk representation in the Catalog B-Tree (not
1353 * one of the run-time structs that we normally use.
1355 * This assumes that the catalog and extents-overflow btrees
1356 * are locked, at least in shared mode
1358 static int hfs_pin_catalog_rec (struct hfsmount
*hfsmp
, HFSPlusCatalogFile
*cfp
, int rsrc
) {
1359 uint32_t pinned_blocks
= 0;
1360 HFSPlusForkData
*forkdata
;
1362 uint8_t forktype
= 0;
1365 forkdata
= &cfp
->resourceFork
;
1369 forkdata
= &cfp
->dataFork
;
1372 uint32_t pblocks
= 0;
1374 /* iterate through the inline extents */
1375 error
= hfs_pin_extent_record (hfsmp
, forkdata
->extents
, &pblocks
);
1380 pinned_blocks
+= pblocks
;
1383 /* it may have overflow extents */
1384 if (forkdata
->extents
[kHFSPlusExtentDensity
-1].startBlock
!= 0) {
1385 error
= hfs_pin_overflow_extents (hfsmp
, cfp
->fileID
, forktype
, &pblocks
);
1387 pinned_blocks
+= pblocks
;
1389 hfsmp
->hfs_hotfile_freeblks
-= pinned_blocks
;
1399 hfs_recording_init(struct hfsmount
*hfsmp
)
1402 CatalogRecord
* datap
;
1404 HFSPlusCatalogFile
*filep
;
1405 BTScanState scanstate
;
1406 BTreeIterator
* iterator
= NULL
;
1407 FSBufferDescriptor record
;
1409 filefork_t
* filefork
;
1411 struct cat_attr cattr
;
1417 int started_scan
= 0;
1419 int inserted
= 0; /* debug variables */
1421 int uncacheable
= 0;
1424 * For now, only the boot volume is supported.
1426 if ((vfs_flags(HFSTOVFS(hfsmp
)) & MNT_ROOTFS
) == 0) {
1427 hfsmp
->hfc_stage
= HFC_DISABLED
;
1431 /* We grab the HFC mutex even though we're not fully mounted yet, just for orderliness */
1432 lck_mtx_lock (&hfsmp
->hfc_mutex
);
1435 * Tracking of hot files requires up-to-date access times.
1436 * So if access time updates are disabled, then we disable
1439 if (vfs_flags(HFSTOVFS(hfsmp
)) & MNT_NOATIME
) {
1440 hfsmp
->hfc_stage
= HFC_DISABLED
;
1441 lck_mtx_unlock (&hfsmp
->hfc_mutex
);
1446 // Check if we've been asked to suspend operation
1448 cnid
= GetFileInfo(HFSTOVCB(hfsmp
), kRootDirID
, ".hotfile-suspend", &cattr
, NULL
);
1450 printf("hfs: %s: %s: hotfiles explicitly disabled! remove /.hotfiles-suspend to re-enable\n", hfsmp
->vcbVN
, __FUNCTION__
);
1451 hfsmp
->hfc_stage
= HFC_DISABLED
;
1452 lck_mtx_unlock (&hfsmp
->hfc_mutex
);
1457 // Check if we've been asked to reset our state.
1459 cnid
= GetFileInfo(HFSTOVCB(hfsmp
), kRootDirID
, ".hotfile-reset", &cattr
, NULL
);
1461 hfs_hotfile_reset(hfsmp
);
1464 if (hfsmp
->hfs_flags
& HFS_CS_HOTFILE_PIN
) {
1466 // Cooperative Fusion (CF) systems use different constants
1467 // than traditional hotfile systems. These were picked after a bit of
1468 // experimentation - we can cache many more files on the
1469 // ssd in an CF system and we can do so more rapidly
1470 // so bump the limits considerably (and turn down the
1471 // duration so that it doesn't take weeks to adopt all
1474 hfc_default_file_count
= 20000;
1475 hfc_default_duration
= 300; // 5min
1476 hfc_max_file_count
= 50000;
1477 hfc_max_file_size
= (512ULL * 1024ULL * 1024ULL);
1481 * If the Hot File btree exists then metadata zone is ready.
1483 cnid
= GetFileInfo(HFSTOVCB(hfsmp
), kRootDirID
, HFC_FILENAME
, &cattr
, NULL
);
1484 if (cnid
!= 0 && S_ISREG(cattr
.ca_mode
)) {
1487 if (hfsmp
->hfc_stage
== HFC_DISABLED
)
1488 hfsmp
->hfc_stage
= HFC_IDLE
;
1489 hfsmp
->hfs_hotfile_freeblks
= 0;
1491 if ((hfsmp
->hfs_flags
& HFS_CS_HOTFILE_PIN
) && cattr
.ca_blocks
> 0) {
1493 // make sure the hotfile btree is pinned
1495 error
= hfc_btree_open(hfsmp
, &hfsmp
->hfc_filevp
);
1497 /* XXX: must fix hfs_pin_vnode too */
1498 hfs_pin_vnode(hfsmp
, hfsmp
->hfc_filevp
, HFS_PIN_IT
, NULL
, vfs_context_kernel());
1501 printf("hfs: failed to open the btree err=%d. Recreating hotfile btree.\n", error
);
1505 hfs_hotfile_repin_files(hfsmp
);
1507 if (hfsmp
->hfc_filevp
) {
1508 (void) hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
1509 hfsmp
->hfc_filevp
= NULL
;
1512 } else if (hfsmp
->hfs_flags
& HFS_CS_HOTFILE_PIN
) {
1513 // hmmm, the hotfile btree is zero bytes long? how odd. let's recreate it.
1514 printf("hfs: hotfile btree is zero bytes long?! recreating it.\n");
1519 /* don't forget to unlock the mutex */
1520 lck_mtx_unlock (&hfsmp
->hfc_mutex
);
1524 // open the hotfile btree file ignoring errors because
1525 // we need the vnode pointer for hfc_btree_delete() to
1526 // be able to do its work
1528 error
= hfc_btree_open_ext(hfsmp
, &hfsmp
->hfc_filevp
, 1);
1531 error
= hfc_btree_delete(hfsmp
);
1532 (void) hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
1533 hfsmp
->hfc_filevp
= NULL
;
1538 printf("hfs: %s: %s: creating the hotfile btree\n", hfsmp
->vcbVN
, __FUNCTION__
);
1539 if (hfs_start_transaction(hfsmp
) != 0) {
1540 lck_mtx_unlock (&hfsmp
->hfc_mutex
);
1544 /* B-tree creation must be journaled */
1547 error
= hfc_btree_create(hfsmp
, HFSTOVCB(hfsmp
)->blockSize
, HFC_DEFAULT_FILE_COUNT
);
1550 printf("hfs: Error %d creating hot file b-tree on %s \n", error
, hfsmp
->vcbVN
);
1552 goto recording_init_out
;
1555 hfs_end_transaction (hfsmp
);
1558 * Do a journal flush + flush track cache. We have to ensure that the async I/Os have been issued to the media
1559 * before proceeding.
1561 hfs_flush (hfsmp
, HFS_FLUSH_FULL
);
1563 /* now re-start a new transaction */
1564 if (hfs_start_transaction (hfsmp
) != 0) {
1565 lck_mtx_unlock (&hfsmp
->hfc_mutex
);
1571 * Open the Hot File B-tree file for writing.
1573 if (hfsmp
->hfc_filevp
)
1574 panic("hfs_recording_init: hfc_filevp exists (vp = %p)", hfsmp
->hfc_filevp
);
1576 error
= hfc_btree_open(hfsmp
, &hfsmp
->hfc_filevp
);
1579 printf("hfs: Error %d opening hot file b-tree on %s \n", error
, hfsmp
->vcbVN
);
1581 goto recording_init_out
;
1585 * This function performs work similar to namei; we must NOT hold the catalog lock while
1586 * calling it. This will decorate catalog records as being pinning candidates. (no hotfiles work)
1588 hfs_setup_default_cf_hotfiles(hfsmp
);
1591 * now grab the hotfiles b-tree vnode/cnode lock first, as it is not classified as a systemfile.
1593 if (hfs_lock(VTOC(hfsmp
->hfc_filevp
), HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
) != 0) {
1595 (void) hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
1596 /* zero it out to avoid pinning later on */
1597 hfsmp
->hfc_filevp
= NULL
;
1598 goto recording_init_out
;
1601 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
1602 if (iterator
== NULL
) {
1604 hfs_unlock (VTOC(hfsmp
->hfc_filevp
));
1605 (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
;
1611 bzero(iterator
, sizeof(*iterator
));
1612 key
= (HotFileKey
*) &iterator
->key
;
1613 key
->keyLength
= HFC_KEYLENGTH
;
1615 record
.bufferAddress
= &data
;
1616 record
.itemSize
= sizeof(u_int32_t
);
1617 record
.itemCount
= 1;
1620 printf("hfs: Evaluating space for \"%s\" metadata zone... (freeblks %d)\n", HFSTOVCB(hfsmp
)->vcbVN
,
1621 hfsmp
->hfs_hotfile_freeblks
);
1625 * Get ready to scan the Catalog file. We explicitly do NOT grab the catalog lock because
1626 * we're fully single-threaded at the moment (by virtue of being called during mount()),
1627 * and if we have to grow the hotfile btree, then we would need to grab the catalog lock
1628 * and if we take a shared lock here, it would deadlock (see <rdar://problem/21486585>)
1630 * We already started a transaction so we should already be holding the journal lock at this point.
1631 * Note that we have to hold the journal lock / start a txn BEFORE the systemfile locks.
1634 error
= BTScanInitialize(VTOF(HFSTOVCB(hfsmp
)->catalogRefNum
), 0, 0, 0,
1635 kCatSearchBufferSize
, &scanstate
);
1637 printf("hfs_recording_init: err %d BTScanInit\n", error
);
1639 /* drop the systemfile locks */
1640 hfs_unlock(VTOC(hfsmp
->hfc_filevp
));
1642 (void) hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
1644 /* zero it out to avoid pinning */
1645 hfsmp
->hfc_filevp
= NULL
;
1646 goto recording_init_out
;
1651 filefork
= VTOF(hfsmp
->hfc_filevp
);
1653 starting_temp
= random() % HF_TEMP_RANGE
;
1656 * Visit all the catalog btree leaf records. We have to hold the catalog lock to do this.
1658 * NOTE: The B-Tree scanner reads from the media itself. Under normal circumstances it would be
1659 * fine to simply use b-tree routines to read blocks that correspond to b-tree nodes, because the
1660 * block cache is going to ensure you always get the cached copy of a block (even if a journal
1661 * txn has modified one of those blocks). That is NOT true when
1662 * using the scanner. In particular, it will always read whatever is on-disk. So we have to ensure
1663 * that the journal has flushed and that the async I/Os to the metadata files have been issued.
1666 error
= BTScanNextRecord(&scanstate
, 0, (void **)&keyp
, (void **)&datap
, &dataSize
);
1668 if (error
== btNotFound
)
1671 printf("hfs_recording_init: err %d BTScanNext\n", error
);
1674 if ((datap
->recordType
!= kHFSPlusFileRecord
) ||
1675 (dataSize
!= sizeof(HFSPlusCatalogFile
))) {
1678 filep
= (HFSPlusCatalogFile
*)datap
;
1681 if (hfsmp
->hfs_flags
& HFS_CS_HOTFILE_PIN
) {
1682 if (filep
->flags
& kHFSDoNotFastDevPinMask
) {
1687 // If the file does not have the FastDevPinnedMask set, we
1688 // can ignore it and just go to the next record.
1690 if ((filep
->flags
& kHFSFastDevPinnedMask
) == 0) {
1693 } else if (filep
->dataFork
.totalBlocks
== 0) {
1698 * On a regular hdd, any file that has blocks inside
1699 * the hot file space is recorded for later eviction.
1701 * For now, resource forks are ignored.
1703 * We don't do this on CF systems as there is no real
1704 * hotfile area - we just pin/unpin blocks belonging to
1705 * interesting files.
1707 if (!(hfsmp
->hfs_flags
& HFS_CS_HOTFILE_PIN
) && !hotextents(hfsmp
, &filep
->dataFork
.extents
[0])) {
1710 cnid
= filep
->fileID
;
1712 /* Skip over journal files and the hotfiles B-Tree file. */
1713 if (cnid
== hfsmp
->hfs_jnlfileid
1714 || cnid
== hfsmp
->hfs_jnlinfoblkid
1715 || cnid
== VTOC(hfsmp
->hfc_filevp
)->c_fileid
) {
1719 * XXX - need to skip quota files as well.
1724 if (hfsmp
->hfs_flags
& HFS_CS_HOTFILE_PIN
) {
1727 temp
= (uint32_t)starting_temp
++;
1728 if (filep
->flags
& kHFSAutoCandidateMask
) {
1729 temp
+= MAX_NORMAL_TEMP
;
1732 /* use the data fork by default */
1733 if (filep
->dataFork
.totalBlocks
== 0) {
1735 * but if empty, switch to rsrc as its likely
1741 error
= hfs_pin_catalog_rec (hfsmp
, filep
, rsrc
);
1746 temp
= HFC_MINIMUM_TEMPERATURE
;
1749 /* Insert a hot file entry. */
1750 key
->keyLength
= HFC_KEYLENGTH
;
1751 key
->temperature
= temp
;
1755 error
= BTInsertRecord(filefork
, iterator
, &record
, record
.itemSize
);
1757 printf("hfs_recording_init: BTInsertRecord failed %d (fileid %d)\n", error
, key
->fileID
);
1758 error
= MacToVFSError(error
);
1762 /* Insert the corresponding thread record. */
1763 key
->keyLength
= HFC_KEYLENGTH
;
1764 key
->temperature
= HFC_LOOKUPTAG
;
1768 error
= BTInsertRecord(filefork
, iterator
, &record
, record
.itemSize
);
1770 printf("hfs_recording_init: BTInsertRecord failed %d (fileid %d)\n", error
, key
->fileID
);
1771 error
= MacToVFSError(error
);
1775 } // end catalog iteration loop
1777 save_btree_user_info(hfsmp
);
1778 (void) BTFlushPath(filefork
);
1782 /* Unlock first, then pin after releasing everything else */
1783 if (hfsmp
->hfc_filevp
) {
1784 hfs_unlock (VTOC(hfsmp
->hfc_filevp
));
1788 (void) BTScanTerminate (&scanstate
, &data
, &data
, &data
);
1792 hfs_end_transaction(hfsmp
);
1796 printf("hfs: %d files identified out of %d (freeblocks is now: %d)\n", inserted
, filecount
, hfsmp
->hfs_hotfile_freeblks
);
1798 printf("hfs: %d files were marked as uncacheable\n", uncacheable
);
1803 FREE(iterator
, M_TEMP
);
1805 if (hfsmp
->hfc_filevp
) {
1806 if (hfsmp
->hfs_flags
& HFS_CS_HOTFILE_PIN
) {
1807 hfs_pin_vnode(hfsmp
, hfsmp
->hfc_filevp
, HFS_PIN_IT
, NULL
, vfs_context_kernel());
1809 (void) hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
1810 hfsmp
->hfc_filevp
= NULL
;
1814 hfsmp
->hfc_stage
= HFC_IDLE
;
1816 /* Finally, unlock the HFC mutex */
1817 lck_mtx_unlock (&hfsmp
->hfc_mutex
);
1823 * Use sync to perform ocassional background work.
1826 hfs_hotfilesync(struct hfsmount
*hfsmp
, vfs_context_t ctx
)
1828 if (hfsmp
->hfc_stage
) {
1831 lck_mtx_lock(&hfsmp
->hfc_mutex
);
1833 switch (hfsmp
->hfc_stage
) {
1835 (void) hfs_recording_start(hfsmp
);
1840 if (tv
.tv_sec
> hfsmp
->hfc_timeout
)
1841 (void) hfs_recording_stop(hfsmp
);
1845 (void) hotfiles_evict(hfsmp
, ctx
);
1849 (void) hotfiles_adopt(hfsmp
, ctx
);
1855 lck_mtx_unlock(&hfsmp
->hfc_mutex
);
1861 * Add a hot file to the recording list.
1863 * This can happen when a hot file gets reclaimed or at the
1864 * end of the recording period for any active hot file.
1866 * NOTE: Since both the data and resource fork can be hot,
1867 * there can be two entries for the same file id.
1869 * Note: the cnode is locked on entry.
1872 hfs_addhotfile(struct vnode
*vp
)
1878 if (hfsmp
->hfc_stage
!= HFC_RECORDING
)
1881 lck_mtx_lock(&hfsmp
->hfc_mutex
);
1882 error
= hfs_addhotfile_internal(vp
);
1883 lck_mtx_unlock(&hfsmp
->hfc_mutex
);
1888 hf_ignore_process(const char *pname
, size_t maxlen
)
1890 if ( strncmp(pname
, "mds", maxlen
) == 0
1891 || strncmp(pname
, "mdworker", maxlen
) == 0
1892 || strncmp(pname
, "mds_stores", maxlen
) == 0
1893 || strncmp(pname
, "makewhatis", maxlen
) == 0) {
1902 hfs_addhotfile_internal(struct vnode
*vp
)
1904 hotfile_data_t
*hotdata
;
1905 hotfile_entry_t
*entry
;
1909 u_int32_t temperature
;
1912 if (hfsmp
->hfc_stage
!= HFC_RECORDING
)
1916 * Only regular files are eligible for hotfiles addition.
1918 * Symlinks were previously added to the list and may exist in
1919 * extant hotfiles regions, but no new ones will be added, and no
1920 * symlinks will now be relocated/evicted from the hotfiles region.
1922 if (!vnode_isreg(vp
) || vnode_issystem(vp
)) {
1926 /* Skip resource forks for now. */
1927 if (VNODE_IS_RSRC(vp
)) {
1930 if ((hotdata
= (hotfile_data_t
*)hfsmp
->hfc_recdata
) == NULL
) {
1936 if (cp
->c_attr
.ca_recflags
& (kHFSFastDevPinnedMask
|kHFSDoNotFastDevPinMask
)) {
1937 // it's already a hotfile or can't be a hotfile...
1941 if (vnode_isdir(vp
) || vnode_issystem(vp
) || (cp
->c_flag
& (C_DELETED
| C_NOEXISTS
))) {
1945 if ((hfsmp
->hfs_flags
& HFS_CS_HOTFILE_PIN
) && vnode_isfastdevicecandidate(vp
)) {
1947 // On cooperative fusion (CF) systems we have different criteria for whether something
1948 // can be pinned to the ssd.
1950 if (cp
->c_flag
& (C_DELETED
|C_NOEXISTS
)) {
1952 // dead files are definitely not worth caching
1955 } else if (ffp
->ff_blocks
== 0 && !(cp
->c_bsdflags
& UF_COMPRESSED
) && !(cp
->c_attr
.ca_recflags
& kHFSFastDevCandidateMask
)) {
1957 // empty files aren't worth caching but compressed ones might be, as are
1958 // newly created files that live in WorthCaching directories...
1965 proc_selfname(pname
, sizeof(pname
));
1966 if (hf_ignore_process(pname
, sizeof(pname
))) {
1967 // ignore i/o's from certain system daemons
1971 temperature
= cp
->c_fileid
; // in memory we just keep it sorted by file-id
1973 // the normal hard drive based hotfile checks
1974 if ((ffp
->ff_bytesread
== 0) ||
1975 (ffp
->ff_blocks
== 0) ||
1976 (ffp
->ff_size
== 0) ||
1977 (ffp
->ff_blocks
> hotdata
->maxblocks
) ||
1978 (cp
->c_bsdflags
& (UF_NODUMP
| UF_COMPRESSED
)) ||
1979 (cp
->c_atime
< hfsmp
->hfc_timebase
)) {
1983 temperature
= ffp
->ff_bytesread
/ ffp
->ff_size
;
1984 if (temperature
< hotdata
->threshold
) {
1990 * If there is room or this file is hotter than
1991 * the coldest one then add it to the list.
1994 if ((hotdata
->activefiles
< hfsmp
->hfc_maxfiles
) ||
1995 (hotdata
->coldest
== NULL
) ||
1996 (temperature
>= hotdata
->coldest
->temperature
)) {
1997 ++hotdata
->refcount
;
1998 entry
= hf_getnewentry(hotdata
);
1999 entry
->temperature
= temperature
;
2000 entry
->fileid
= cp
->c_fileid
;
2002 // if ffp->ff_blocks is zero, it might be compressed so make sure we record
2003 // that there's at least one block.
2005 entry
->blocks
= ffp
->ff_blocks
? ffp
->ff_blocks
: 1;
2006 if (hf_insert(hotdata
, entry
) == EEXIST
) {
2007 // entry is already present, don't need to add it again
2008 entry
->right
= hotdata
->freelist
;
2009 hotdata
->freelist
= entry
;
2011 --hotdata
->refcount
;
2018 * Remove a hot file from the recording list.
2020 * This can happen when a hot file becomes
2021 * an active vnode (active hot files are
2022 * not kept in the recording list until the
2023 * end of the recording period).
2025 * Note: the cnode is locked on entry.
2028 hfs_removehotfile(struct vnode
*vp
)
2030 hotfile_data_t
*hotdata
;
2034 u_int32_t temperature
;
2037 if (hfsmp
->hfc_stage
!= HFC_RECORDING
)
2040 if ((!vnode_isreg(vp
)) || vnode_issystem(vp
)) {
2047 if ((ffp
->ff_bytesread
== 0) || (ffp
->ff_blocks
== 0) ||
2048 (ffp
->ff_size
== 0) || (cp
->c_atime
< hfsmp
->hfc_timebase
)) {
2052 lck_mtx_lock(&hfsmp
->hfc_mutex
);
2053 if (hfsmp
->hfc_stage
!= HFC_RECORDING
)
2055 if ((hotdata
= (hotfile_data_t
*)hfsmp
->hfc_recdata
) == NULL
)
2058 temperature
= ffp
->ff_bytesread
/ ffp
->ff_size
;
2059 if (temperature
< hotdata
->threshold
)
2062 if (hotdata
->coldest
&& (temperature
>= hotdata
->coldest
->temperature
)) {
2063 ++hotdata
->refcount
;
2064 hf_delete(hotdata
, VTOC(vp
)->c_fileid
, temperature
);
2065 --hotdata
->refcount
;
2068 lck_mtx_unlock(&hfsmp
->hfc_mutex
);
2073 hfs_hotfile_deleted(__unused
struct vnode
*vp
)
2079 // XXXdbg - this code, while it would work, would introduce a huge inefficiency
2080 // to deleting files as the way it's written would require us to open
2081 // the hotfile btree on every open, delete two records in it and then
2082 // close the hotfile btree (which involves more writes).
2084 // We actually can be lazy about deleting hotfile records for files
2085 // that get deleted. When it's time to evict things, if we encounter
2086 // a record that references a dead file (i.e. a fileid which no
2087 // longer exists), the eviction code will remove the records. Likewise
2088 // the code that scans the HotFile B-Tree at boot time to re-pin files
2089 // will remove dead records.
2092 hotfile_data_t
*hotdata
;
2095 filefork_t
*filefork
;
2096 u_int32_t temperature
;
2097 BTreeIterator
* iterator
= NULL
;
2098 FSBufferDescriptor record
;
2104 if (cp
== NULL
|| !(cp
->c_attr
.ca_recflags
& kHFSFastDevPinnedMask
)) {
2109 if (!(hfsmp
->hfs_flags
& HFS_CS_HOTFILE_PIN
)) {
2113 if (hfc_btree_open(hfsmp
, &hfsmp
->hfc_filevp
) != 0 || hfsmp
->hfc_filevp
== NULL
) {
2114 // either there is no hotfile info or it's damaged
2118 filefork
= VTOF(hfsmp
->hfc_filevp
);
2119 if (filefork
== NULL
) {
2123 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
2124 if (iterator
== NULL
) {
2127 bzero(iterator
, sizeof(*iterator
));
2128 key
= (HotFileKey
*) &iterator
->key
;
2130 record
.bufferAddress
= &data
;
2131 record
.itemSize
= sizeof(u_int32_t
);
2132 record
.itemCount
= 1;
2134 key
->keyLength
= HFC_KEYLENGTH
;
2135 key
->temperature
= HFC_LOOKUPTAG
;
2136 key
->fileID
= cp
->c_fileid
;
2139 lck_mtx_lock(&hfsmp
->hfc_mutex
);
2140 (void) BTInvalidateHint(iterator
);
2141 if (BTSearchRecord(filefork
, iterator
, &record
, NULL
, iterator
) == 0) {
2142 temperature
= key
->temperature
;
2143 hfc_btree_delete_record(hfsmp
, iterator
, key
);
2145 //printf("hfs: hotfile_deleted: did not find fileid %d\n", cp->c_fileid);
2149 if ((hotdata
= (hotfile_data_t
*)hfsmp
->hfc_recdata
) != NULL
) {
2150 // just in case, also make sure it's removed from the in-memory list as well
2151 ++hotdata
->refcount
;
2152 hf_delete(hotdata
, cp
->c_fileid
, cp
->c_fileid
);
2153 --hotdata
->refcount
;
2156 lck_mtx_unlock(&hfsmp
->hfc_mutex
);
2157 FREE(iterator
, M_TEMP
);
2159 hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
2166 hfs_hotfile_adjust_blocks(struct vnode
*vp
, int64_t num_blocks
)
2176 if (!(hfsmp
->hfs_flags
& HFS_CS_HOTFILE_PIN
) || num_blocks
== 0 || vp
== NULL
) {
2181 // if file is not HotFileCached or it has the CanNotHotFile cache
2182 // bit set then there is nothing to do
2184 if (!(VTOC(vp
)->c_attr
.ca_recflags
& kHFSFastDevPinnedMask
) || (VTOC(vp
)->c_attr
.ca_recflags
& kHFSDoNotFastDevPinMask
)) {
2185 // it's not a hot file or can't be one so don't bother tracking
2189 OSAddAtomic(num_blocks
, &hfsmp
->hfs_hotfile_blk_adjust
);
2195 // Assumes hfsmp->hfc_mutex is LOCKED
2198 hfs_hotfile_cur_freeblks(hfsmount_t
*hfsmp
)
2200 if (hfsmp
->hfc_stage
< HFC_IDLE
) {
2204 int cur_blk_adjust
= hfsmp
->hfs_hotfile_blk_adjust
; // snap a copy of this value
2206 if (cur_blk_adjust
) {
2207 OSAddAtomic(-cur_blk_adjust
, &hfsmp
->hfs_hotfile_blk_adjust
);
2208 hfsmp
->hfs_hotfile_freeblks
+= cur_blk_adjust
;
2211 return hfsmp
->hfs_hotfile_freeblks
;
2216 *========================================================================
2217 * HOT FILE MAINTENANCE ROUTINES
2218 *========================================================================
2222 hotfiles_collect_callback(struct vnode
*vp
, __unused
void *cargs
)
2224 if ((vnode_isreg(vp
)) && !vnode_issystem(vp
))
2225 (void) hfs_addhotfile_internal(vp
);
2227 return (VNODE_RETURNED
);
2231 * Add all active hot files to the recording list.
2234 hotfiles_collect(struct hfsmount
*hfsmp
)
2236 struct mount
*mp
= HFSTOVFS(hfsmp
);
2238 if (vfs_busy(mp
, LK_NOWAIT
))
2242 * hotfiles_collect_callback will be called for each vnode
2243 * hung off of this mount point
2245 * properly referenced and unreferenced around the callback
2247 vnode_iterate(mp
, 0, hotfiles_collect_callback
, (void *)NULL
);
2256 * Update the data of a btree record
2257 * This is called from within BTUpdateRecord.
2260 update_callback(const HotFileKey
*key
, u_int32_t
*data
, u_int32_t
*state
)
2262 if (key
->temperature
== HFC_LOOKUPTAG
)
2268 * Identify files already in hot area.
2271 hotfiles_refine(struct hfsmount
*hfsmp
)
2273 BTreeIterator
* iterator
= NULL
;
2275 filefork_t
* filefork
;
2276 hotfilelist_t
*listp
;
2277 FSBufferDescriptor record
;
2283 if ((listp
= (hotfilelist_t
*)hfsmp
->hfc_recdata
) == NULL
)
2286 if (hfsmp
->hfs_flags
& HFS_CS_HOTFILE_PIN
) {
2287 // on ssd's we don't refine the temperature since the
2288 // replacement algorithm is simply random
2292 mp
= HFSTOVFS(hfsmp
);
2294 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
2295 if (iterator
== NULL
) {
2299 bzero(iterator
, sizeof(*iterator
));
2300 key
= (HotFileKey
*) &iterator
->key
;
2302 record
.bufferAddress
= &data
;
2303 record
.itemSize
= sizeof(u_int32_t
);
2304 record
.itemCount
= 1;
2306 if (hfs_start_transaction(hfsmp
) != 0) {
2310 if (hfs_lock(VTOC(hfsmp
->hfc_filevp
), HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
) != 0) {
2314 filefork
= VTOF(hfsmp
->hfc_filevp
);
2316 for (i
= 0; i
< listp
->hfl_count
; ++i
) {
2318 * Check if entry (thread) is already in hot area.
2320 key
->keyLength
= HFC_KEYLENGTH
;
2321 key
->temperature
= HFC_LOOKUPTAG
;
2322 key
->fileID
= listp
->hfl_hotfile
[i
].hf_fileid
;
2324 (void) BTInvalidateHint(iterator
);
2325 if (BTSearchRecord(filefork
, iterator
, &record
, NULL
, iterator
) != 0) {
2326 continue; /* not in hot area, so skip */
2330 * Update thread entry with latest temperature.
2332 error
= BTUpdateRecord(filefork
, iterator
,
2333 (IterateCallBackProcPtr
)update_callback
,
2334 &listp
->hfl_hotfile
[i
].hf_temperature
);
2336 printf("hfs: hotfiles_refine: BTUpdateRecord failed %d (file %d)\n", error
, key
->fileID
);
2337 error
= MacToVFSError(error
);
2341 * Re-key entry with latest temperature.
2343 key
->keyLength
= HFC_KEYLENGTH
;
2344 key
->temperature
= data
;
2345 key
->fileID
= listp
->hfl_hotfile
[i
].hf_fileid
;
2347 /* Pick up record data. */
2348 (void) BTInvalidateHint(iterator
);
2349 (void) BTSearchRecord(filefork
, iterator
, &record
, NULL
, iterator
);
2350 error
= BTDeleteRecord(filefork
, iterator
);
2352 printf("hfs: hotfiles_refine: BTDeleteRecord failed %d (file %d)\n", error
, key
->fileID
);
2353 error
= MacToVFSError(error
);
2356 key
->keyLength
= HFC_KEYLENGTH
;
2357 key
->temperature
= listp
->hfl_hotfile
[i
].hf_temperature
;
2358 key
->fileID
= listp
->hfl_hotfile
[i
].hf_fileid
;
2360 error
= BTInsertRecord(filefork
, iterator
, &record
, record
.itemSize
);
2362 printf("hfs: hotfiles_refine: BTInsertRecord failed %d (file %d)\n", error
, key
->fileID
);
2363 error
= MacToVFSError(error
);
2367 * Invalidate this entry in the list.
2369 listp
->hfl_hotfile
[i
].hf_temperature
= 0;
2370 listp
->hfl_totalblocks
-= listp
->hfl_hotfile
[i
].hf_blocks
;
2374 (void) BTFlushPath(filefork
);
2375 hfs_unlock(VTOC(hfsmp
->hfc_filevp
));
2378 hfs_end_transaction(hfsmp
);
2381 FREE(iterator
, M_TEMP
);
2386 * Move new hot files into hot area.
2388 * Requires that the hfc_mutex be held.
2391 hotfiles_adopt(struct hfsmount
*hfsmp
, vfs_context_t ctx
)
2393 BTreeIterator
* iterator
= NULL
;
2395 filefork_t
* filefork
;
2396 hotfilelist_t
*listp
;
2397 FSBufferDescriptor record
;
2400 enum hfc_stage stage
;
2406 int startedtrans
= 0;
2408 // all files in a given adoption phase have a temperature
2409 // that starts at a random value and then increases linearly.
2410 // the idea is that during eviction, files that were adopted
2411 // together will be evicted together
2413 long starting_temp
= random() % HF_TEMP_RANGE
;
2414 long temp_adjust
= 0;
2416 if ((listp
= (hotfilelist_t
*)hfsmp
->hfc_recdata
) == NULL
)
2419 if (hfsmp
->hfc_stage
!= HFC_ADOPTION
) {
2422 if (hfs_lock(VTOC(hfsmp
->hfc_filevp
), HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
) != 0) {
2426 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
2427 if (iterator
== NULL
) {
2428 hfs_unlock(VTOC(hfsmp
->hfc_filevp
));
2433 printf("hfs:%s: hotfiles_adopt: (hfl_next: %d, hotfile start/end block: %d - %d; max/free: %d/%d; maxfiles: %d)\n",
2436 hfsmp
->hfs_hotfile_start
, hfsmp
->hfs_hotfile_end
,
2437 hfsmp
->hfs_hotfile_maxblks
, hfsmp
->hfs_hotfile_freeblks
, hfsmp
->hfc_maxfiles
);
2440 stage
= hfsmp
->hfc_stage
;
2441 hfsmp
->hfc_stage
= HFC_BUSY
;
2444 last
= listp
->hfl_next
+ HFC_FILESPERSYNC
;
2445 if (last
> listp
->hfl_count
)
2446 last
= listp
->hfl_count
;
2448 bzero(iterator
, sizeof(*iterator
));
2449 key
= (HotFileKey
*) &iterator
->key
;
2450 key
->keyLength
= HFC_KEYLENGTH
;
2452 record
.bufferAddress
= &data
;
2453 record
.itemSize
= sizeof(u_int32_t
);
2454 record
.itemCount
= 1;
2456 filefork
= VTOF(hfsmp
->hfc_filevp
);
2458 for (i
= listp
->hfl_next
; (i
< last
) && (blksmoved
< HFC_BLKSPERSYNC
); ++i
) {
2460 * Skip entries that aren't going to work.
2462 if (listp
->hfl_hotfile
[i
].hf_temperature
== 0) {
2463 //printf("hfs: zero temp on file-id %d\n", listp->hfl_hotfile[i].hf_fileid);
2467 if (listp
->hfl_hotfile
[i
].hf_fileid
== VTOC(hfsmp
->hfc_filevp
)->c_fileid
) {
2468 //printf("hfs: cannot adopt the hotfile b-tree itself! (file-id %d)\n", listp->hfl_hotfile[i].hf_fileid);
2472 if (listp
->hfl_hotfile
[i
].hf_fileid
< kHFSFirstUserCatalogNodeID
) {
2473 //printf("hfs: cannot adopt system files (file-id %d)\n", listp->hfl_hotfile[i].hf_fileid);
2479 * Acquire a vnode for this file.
2481 error
= hfs_vget(hfsmp
, listp
->hfl_hotfile
[i
].hf_fileid
, &vp
, 0, 0);
2483 //printf("failed to get fileid %d (err %d)\n", listp->hfl_hotfile[i].hf_fileid, error);
2484 if (error
== ENOENT
) {
2487 continue; /* stale entry, go to next */
2492 //printf("hfs: examining hotfile entry w/fileid %d, temp %d, blocks %d (HotFileCached: %s)\n",
2493 // listp->hfl_hotfile[i].hf_fileid, listp->hfl_hotfile[i].hf_temperature,
2494 // listp->hfl_hotfile[i].hf_blocks,
2495 // (VTOC(vp)->c_attr.ca_recflags & kHFSFastDevPinnedMask) ? "YES" : "NO");
2497 if (!vnode_isreg(vp
)) {
2498 /* Symlinks are ineligible for adoption into the hotfile zone. */
2499 //printf("hfs: hotfiles_adopt: huh, not a file %d (%d)\n", listp->hfl_hotfile[i].hf_fileid, VTOC(vp)->c_cnid);
2500 hfs_unlock(VTOC(vp
));
2502 listp
->hfl_hotfile
[i
].hf_temperature
= 0;
2504 continue; /* stale entry, go to next */
2506 if ( (VTOC(vp
)->c_flag
& (C_DELETED
| C_NOEXISTS
))
2507 || (!(hfsmp
->hfs_flags
& HFS_CS_HOTFILE_PIN
) && hotextents(hfsmp
, &VTOF(vp
)->ff_extents
[0]))
2508 || (VTOC(vp
)->c_attr
.ca_recflags
& (kHFSFastDevPinnedMask
|kHFSDoNotFastDevPinMask
))) {
2509 hfs_unlock(VTOC(vp
));
2511 listp
->hfl_hotfile
[i
].hf_temperature
= 0;
2513 listp
->hfl_totalblocks
-= listp
->hfl_hotfile
[i
].hf_blocks
;
2514 continue; /* stale entry, go to next */
2517 fileblocks
= VTOF(vp
)->ff_blocks
;
2520 // for CF, if the file is empty (and not compressed) or it is too large,
2521 // do not try to pin it. (note: if fileblocks == 0 but the file is marked
2522 // as compressed, we may still be able to cache it).
2524 if ((hfsmp
->hfs_flags
& HFS_CS_HOTFILE_PIN
) &&
2525 ((fileblocks
== 0 && !(VTOC(vp
)->c_bsdflags
& UF_COMPRESSED
)) ||
2526 (unsigned int)fileblocks
> (HFC_MAXIMUM_FILESIZE
/ (uint64_t)HFSTOVCB(hfsmp
)->blockSize
))) {
2527 // don't try to cache something too large or that's zero-bytes
2529 vnode_clearfastdevicecandidate(vp
); // turn off the fast-dev-candidate flag so we don't keep trying to cache it.
2531 hfs_unlock(VTOC(vp
));
2533 listp
->hfl_hotfile
[i
].hf_temperature
= 0;
2535 listp
->hfl_totalblocks
-= listp
->hfl_hotfile
[i
].hf_blocks
;
2536 continue; /* entry is too big, just carry on with the next guy */
2540 // If a file is not an autocandidate (i.e. it's a user-tagged file desirous of
2541 // being hotfile cached) but it is already bigger than 4 megs, don't bother
2542 // hotfile caching it. Note that if a user tagged file starts small, gets
2543 // adopted and then grows over time we will allow it to grow bigger than 4 megs
2544 // which is intentional for things like the Mail or Photos database files which
2545 // grow slowly over time and benefit from being on the FastDevice.
2547 if ((hfsmp
->hfs_flags
& HFS_CS_HOTFILE_PIN
) &&
2548 !(VTOC(vp
)->c_attr
.ca_recflags
& kHFSAutoCandidateMask
) &&
2549 (VTOC(vp
)->c_attr
.ca_recflags
& kHFSFastDevCandidateMask
) &&
2550 (unsigned int)fileblocks
> ((4*1024*1024) / (uint64_t)HFSTOVCB(hfsmp
)->blockSize
)) {
2552 vnode_clearfastdevicecandidate(vp
); // turn off the fast-dev-candidate flag so we don't keep trying to cache it.
2554 hfs_unlock(VTOC(vp
));
2556 listp
->hfl_hotfile
[i
].hf_temperature
= 0;
2558 listp
->hfl_totalblocks
-= listp
->hfl_hotfile
[i
].hf_blocks
;
2559 continue; /* entry is too big, just carry on with the next guy */
2562 if (fileblocks
> hfs_hotfile_cur_freeblks(hfsmp
)) {
2564 // No room for this file. Although eviction should have made space
2565 // it's best that we check here as well since writes to existing
2566 // hotfiles may have eaten up space since we performed eviction
2568 hfs_unlock(VTOC(vp
));
2571 listp
->hfl_totalblocks
-= fileblocks
;
2572 continue; /* entry too big, go to next */
2575 if ((blksmoved
> 0) &&
2576 (blksmoved
+ fileblocks
) > HFC_BLKSPERSYNC
) {
2578 // we've done enough work, let's be nice to the system and
2579 // stop until the next iteration
2581 hfs_unlock(VTOC(vp
));
2583 break; /* adopt this entry the next time around */
2585 if (VTOC(vp
)->c_desc
.cd_nameptr
)
2586 data
= *(const u_int32_t
*)(VTOC(vp
)->c_desc
.cd_nameptr
);
2591 if (hfsmp
->hfs_flags
& HFS_CS_HOTFILE_PIN
) {
2593 // For CF we pin the blocks belonging to the file
2594 // to the "fast" (aka ssd) media
2596 uint32_t pinned_blocks
;
2598 if (vnode_isautocandidate(vp
)) {
2599 VTOC(vp
)->c_attr
.ca_recflags
|= kHFSAutoCandidateMask
;
2601 if (VTOC(vp
)->c_attr
.ca_recflags
& kHFSAutoCandidateMask
) {
2603 // this moves auto-cached files to the higher tier
2604 // of "temperatures" which means they are less likely
2605 // to get evicted (user selected hotfiles will get
2606 // evicted first in the theory that they change more
2607 // frequently compared to system files)
2609 temp_adjust
= MAX_NORMAL_TEMP
;
2614 hfs_unlock(VTOC(vp
)); // don't need an exclusive lock for this
2615 hfs_lock(VTOC(vp
), HFS_SHARED_LOCK
, HFS_LOCK_ALLOW_NOEXISTS
);
2617 error
= hfs_pin_vnode(hfsmp
, vp
, HFS_PIN_IT
, &pinned_blocks
, ctx
);
2619 fileblocks
= pinned_blocks
;
2621 // go back to an exclusive lock since we're going to modify the cnode again
2622 hfs_unlock(VTOC(vp
));
2623 hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
, HFS_LOCK_ALLOW_NOEXISTS
);
2626 // Old style hotfiles moves the data to the center (aka "hot")
2627 // region of the disk
2629 error
= hfs_relocate(vp
, hfsmp
->hfs_hotfile_start
, kauth_cred_get(), current_proc());
2633 VTOC(vp
)->c_attr
.ca_recflags
|= kHFSFastDevPinnedMask
;
2634 VTOC(vp
)->c_flag
|= C_MODIFIED
;
2635 } else if ((hfsmp
->hfs_flags
& HFS_CS_HOTFILE_PIN
) && error
== EALREADY
) {
2637 // If hfs_pin_vnode() returned EALREADY then this file is not
2638 // ever able to be hotfile cached the normal way. This can
2639 // happen with compressed files which have their data stored
2640 // in an extended attribute. We flag them so that we won't
2641 // bother to try and hotfile cache them again the next time
2644 VTOC(vp
)->c_attr
.ca_recflags
|= kHFSDoNotFastDevPinMask
;
2645 VTOC(vp
)->c_flag
|= C_MODIFIED
;
2648 hfs_unlock(VTOC(vp
));
2652 if (error
!= EALREADY
) {
2653 printf("hfs: hotfiles_adopt: could not relocate file %d (err %d)\n", listp
->hfl_hotfile
[i
].hf_fileid
, error
);
2657 if (last
< listp
->hfl_count
) {
2660 /* Move on to next item. */
2664 /* Keep hot file free space current. */
2665 hfsmp
->hfs_hotfile_freeblks
-= fileblocks
;
2666 listp
->hfl_totalblocks
-= fileblocks
;
2668 /* Insert hot file entry */
2669 key
->keyLength
= HFC_KEYLENGTH
;
2671 if (hfsmp
->hfs_flags
& HFS_CS_HOTFILE_PIN
) {
2673 // The "temperature" for a CF hotfile is simply a random
2674 // number that we sequentially increment for each file in
2675 // the set of files we're currently adopting. This has the
2676 // nice property that all of the files we pin to the ssd
2677 // in the current phase will sort together in the hotfile
2678 // btree. When eviction time comes we will evict them
2679 // together as well. This gives the eviction phase temporal
2680 // locality - things written together get evicted together
2681 // which is what ssd's like.
2683 listp
->hfl_hotfile
[i
].hf_temperature
= (uint32_t)temp_adjust
+ starting_temp
++;
2686 key
->temperature
= listp
->hfl_hotfile
[i
].hf_temperature
;
2687 key
->fileID
= listp
->hfl_hotfile
[i
].hf_fileid
;
2690 /* Start a new transaction before calling BTree code. */
2691 if (hfs_start_transaction(hfsmp
) != 0) {
2697 error
= BTInsertRecord(filefork
, iterator
, &record
, record
.itemSize
);
2699 int orig_error
= error
;
2700 error
= MacToVFSError(error
);
2701 printf("hfs: hotfiles_adopt:1: BTInsertRecord failed %d/%d (fileid %d)\n", error
, orig_error
, key
->fileID
);
2706 /* Insert thread record */
2707 key
->keyLength
= HFC_KEYLENGTH
;
2708 key
->temperature
= HFC_LOOKUPTAG
;
2709 key
->fileID
= listp
->hfl_hotfile
[i
].hf_fileid
;
2711 data
= listp
->hfl_hotfile
[i
].hf_temperature
;
2712 error
= BTInsertRecord(filefork
, iterator
, &record
, record
.itemSize
);
2714 int orig_error
= error
;
2715 error
= MacToVFSError(error
);
2716 printf("hfs: hotfiles_adopt:2: BTInsertRecord failed %d/%d (fileid %d)\n", error
, orig_error
, key
->fileID
);
2720 (void) BTFlushPath(filefork
);
2721 blksmoved
+= fileblocks
;
2725 if (listp
->hfl_next
>= listp
->hfl_count
) {
2729 /* Transaction complete. */
2731 hfs_end_transaction(hfsmp
);
2735 if (hfs_hotfile_cur_freeblks(hfsmp
) <= 0) {
2737 printf("hfs: hotfiles_adopt: free space exhausted (%d)\n", hfsmp
->hfs_hotfile_freeblks
);
2744 printf("hfs: hotfiles_adopt: [%d] adopted %d blocks (%d files left)\n", listp
->hfl_next
, blksmoved
, listp
->hfl_count
- i
);
2746 if (!startedtrans
) {
2747 // start a txn so we'll save the btree summary info
2748 if (hfs_start_transaction(hfsmp
) == 0) {
2753 /* Finish any outstanding transactions. */
2755 save_btree_user_info(hfsmp
);
2757 (void) BTFlushPath(filefork
);
2758 hfs_end_transaction(hfsmp
);
2761 hfs_unlock(VTOC(hfsmp
->hfc_filevp
));
2763 if ((listp
->hfl_next
>= listp
->hfl_count
) || (hfsmp
->hfs_hotfile_freeblks
<= 0)) {
2765 printf("hfs: hotfiles_adopt: all done relocating %d files\n", listp
->hfl_count
);
2766 printf("hfs: hotfiles_adopt: %d blocks free in hot file band\n", hfsmp
->hfs_hotfile_freeblks
);
2770 FREE(iterator
, M_TEMP
);
2772 if (stage
!= HFC_ADOPTION
&& hfsmp
->hfc_filevp
) {
2773 (void) hfc_btree_close(hfsmp
, hfsmp
->hfc_filevp
);
2774 hfsmp
->hfc_filevp
= NULL
;
2776 hfsmp
->hfc_stage
= stage
;
2777 wakeup((caddr_t
)&hfsmp
->hfc_stage
);
2782 * Reclaim space by evicting the coldest files.
2784 * Requires that the hfc_mutex be held.
2787 hotfiles_evict(struct hfsmount
*hfsmp
, vfs_context_t ctx
)
2789 BTreeIterator
* iterator
= NULL
;
2792 filefork_t
* filefork
;
2793 hotfilelist_t
*listp
;
2794 enum hfc_stage stage
;
2795 u_int32_t savedtemp
;
2800 int startedtrans
= 0;
2803 if (hfsmp
->hfc_stage
!= HFC_EVICTION
) {
2807 if ((listp
= (hotfilelist_t
*)hfsmp
->hfc_recdata
) == NULL
)
2810 if (hfs_lock(VTOC(hfsmp
->hfc_filevp
), HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
) != 0) {
2815 printf("hfs:%s: hotfiles_evict (hotfile start/end block: %d - %d; max/free: %d/%d; maxfiles: %d)\n",
2817 hfsmp
->hfs_hotfile_start
, hfsmp
->hfs_hotfile_end
,
2818 hfsmp
->hfs_hotfile_maxblks
, hfsmp
->hfs_hotfile_freeblks
, hfsmp
->hfc_maxfiles
);
2821 MALLOC(iterator
, BTreeIterator
*, sizeof(*iterator
), M_TEMP
, M_WAITOK
);
2822 if (iterator
== NULL
) {
2823 hfs_unlock(VTOC(hfsmp
->hfc_filevp
));
2827 stage
= hfsmp
->hfc_stage
;
2828 hfsmp
->hfc_stage
= HFC_BUSY
;
2830 filesmoved
= blksmoved
= 0;
2831 bt_op
= kBTreeFirstRecord
;
2833 bzero(iterator
, sizeof(*iterator
));
2834 key
= (HotFileKey
*) &iterator
->key
;
2836 filefork
= VTOF(hfsmp
->hfc_filevp
);
2839 printf("hfs: hotfiles_evict: reclaim blks %d\n", listp
->hfl_reclaimblks
);
2842 while (listp
->hfl_reclaimblks
> 0 &&
2843 blksmoved
< HFC_BLKSPERSYNC
&&
2844 filesmoved
< HFC_FILESPERSYNC
) {
2847 * Obtain the first record (ie the coldest one).
2849 if (BTIterateRecord(filefork
, bt_op
, iterator
, NULL
, NULL
) != 0) {
2851 printf("hfs: hotfiles_evict: no more records\n");
2854 stage
= HFC_ADOPTION
;
2857 if (key
->keyLength
!= HFC_KEYLENGTH
) {
2858 printf("hfs: hotfiles_evict: invalid key length %d\n", key
->keyLength
);
2862 if (key
->temperature
== HFC_LOOKUPTAG
) {
2864 printf("hfs: hotfiles_evict: ran into thread records\n");
2867 stage
= HFC_ADOPTION
;
2871 // Jump straight to delete for some files...
2872 if (key
->fileID
== VTOC(hfsmp
->hfc_filevp
)->c_fileid
2873 || key
->fileID
== hfsmp
->hfs_jnlfileid
2874 || key
->fileID
== hfsmp
->hfs_jnlinfoblkid
2875 || key
->fileID
< kHFSFirstUserCatalogNodeID
) {
2880 * Aquire the vnode for this file.
2882 error
= hfs_vget(hfsmp
, key
->fileID
, &vp
, 0, 0);
2884 if (error
== ENOENT
) {
2885 goto delete; /* stale entry, go to next */
2887 printf("hfs: hotfiles_evict: err %d getting file %d\n",
2888 error
, key
->fileID
);
2894 * Symlinks that may have been inserted into the hotfile zone during a previous OS are now stuck
2895 * here. We do not want to move them.
2897 if (!vnode_isreg(vp
)) {
2898 //printf("hfs: hotfiles_evict: huh, not a file %d\n", key->fileID);
2899 hfs_unlock(VTOC(vp
));
2901 goto delete; /* invalid entry, go to next */
2904 fileblocks
= VTOF(vp
)->ff_blocks
;
2905 if ((blksmoved
> 0) &&
2906 (blksmoved
+ fileblocks
) > HFC_BLKSPERSYNC
) {
2907 hfs_unlock(VTOC(vp
));
2912 * Make sure file is in the hot area.
2914 if (!hotextents(hfsmp
, &VTOF(vp
)->ff_extents
[0]) && !(VTOC(vp
)->c_attr
.ca_recflags
& kHFSFastDevPinnedMask
)) {
2916 printf("hfs: hotfiles_evict: file %d isn't hot!\n", key
->fileID
);
2918 hfs_unlock(VTOC(vp
));
2920 goto delete; /* stale entry, go to next */
2924 * Relocate file out of hot area. On cooperative fusion (CF) that just
2925 * means un-pinning the data from the ssd. For traditional hotfiles that means moving
2926 * the file data out of the hot region of the disk.
2928 if (hfsmp
->hfs_flags
& HFS_CS_HOTFILE_PIN
) {
2929 uint32_t pinned_blocks
;
2931 hfs_unlock(VTOC(vp
)); // don't need an exclusive lock for this
2932 hfs_lock(VTOC(vp
), HFS_SHARED_LOCK
, HFS_LOCK_ALLOW_NOEXISTS
);
2934 error
= hfs_pin_vnode(hfsmp
, vp
, HFS_UNPIN_IT
, &pinned_blocks
, ctx
);
2935 fileblocks
= pinned_blocks
;
2938 // go back to an exclusive lock since we're going to modify the cnode again
2939 hfs_unlock(VTOC(vp
));
2940 hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
, HFS_LOCK_ALLOW_NOEXISTS
);
2943 error
= hfs_relocate(vp
, HFSTOVCB(hfsmp
)->nextAllocation
, vfs_context_ucred(ctx
), vfs_context_proc(ctx
));
2947 printf("hfs: hotfiles_evict: err %d relocating file %d\n", error
, key
->fileID
);
2949 hfs_unlock(VTOC(vp
));
2951 bt_op
= kBTreeNextRecord
;
2952 goto next
; /* go to next */
2954 VTOC(vp
)->c_attr
.ca_recflags
&= ~kHFSFastDevPinnedMask
;
2955 VTOC(vp
)->c_flag
|= C_MODIFIED
;
2959 // We do not believe that this call to hfs_fsync() is
2960 // necessary and it causes a journal transaction
2961 // deadlock so we are removing it.
2963 // (void) hfs_fsync(vp, MNT_WAIT, 0, p);
2965 hfs_unlock(VTOC(vp
));
2968 hfsmp
->hfs_hotfile_freeblks
+= fileblocks
;
2969 listp
->hfl_reclaimblks
-= fileblocks
;
2970 if (listp
->hfl_reclaimblks
< 0)
2971 listp
->hfl_reclaimblks
= 0;
2972 blksmoved
+= fileblocks
;
2975 /* Start a new transaction before calling BTree code. */
2976 if (hfs_start_transaction(hfsmp
) != 0) {
2982 error
= BTDeleteRecord(filefork
, iterator
);
2984 error
= MacToVFSError(error
);
2987 savedtemp
= key
->temperature
;
2988 key
->temperature
= HFC_LOOKUPTAG
;
2989 error
= BTDeleteRecord(filefork
, iterator
);
2991 error
= MacToVFSError(error
);
2994 key
->temperature
= savedtemp
;
2996 (void) BTFlushPath(filefork
);
2998 /* Transaction complete. */
3000 hfs_end_transaction(hfsmp
);
3007 printf("hfs: hotfiles_evict: moved %d files (%d blks, %d to go)\n", filesmoved
, blksmoved
, listp
->hfl_reclaimblks
);
3009 /* Finish any outstanding transactions. */
3011 save_btree_user_info(hfsmp
);
3013 (void) BTFlushPath(filefork
);
3014 hfs_end_transaction(hfsmp
);
3017 hfs_unlock(VTOC(hfsmp
->hfc_filevp
));
3020 * Move to next stage when finished.
3022 if (listp
->hfl_reclaimblks
<= 0) {
3023 stage
= HFC_ADOPTION
;
3025 printf("hfs: hotfiles_evict: %d blocks free in hot file band\n", hfsmp
->hfs_hotfile_freeblks
);
3028 FREE(iterator
, M_TEMP
);
3029 hfsmp
->hfc_stage
= stage
;
3030 wakeup((caddr_t
)&hfsmp
->hfc_stage
);
3035 * Age the existing records in the hot files b-tree.
3038 hotfiles_age(struct hfsmount
*hfsmp
)
3040 BTreeInfoRec btinfo
;
3041 BTreeIterator
* iterator
= NULL
;
3042 BTreeIterator
* prev_iterator
;
3043 FSBufferDescriptor record
;
3044 FSBufferDescriptor prev_record
;
3046 HotFileKey
* prev_key
;
3047 filefork_t
* filefork
;
3049 u_int32_t prev_data
;
3058 if (hfsmp
->hfs_flags
& HFS_CS_HOTFILE_PIN
) {
3060 // hotfiles don't age on CF
3065 MALLOC(iterator
, BTreeIterator
*, 2 * sizeof(*iterator
), M_TEMP
, M_WAITOK
);
3066 if (iterator
== NULL
) {
3070 bzero(iterator
, 2 * sizeof(*iterator
));
3071 key
= (HotFileKey
*) &iterator
->key
;
3073 prev_iterator
= &iterator
[1];
3074 prev_key
= (HotFileKey
*) &prev_iterator
->key
;
3076 record
.bufferAddress
= &data
;
3077 record
.itemSize
= sizeof(data
);
3078 record
.itemCount
= 1;
3079 prev_record
.bufferAddress
= &prev_data
;
3080 prev_record
.itemSize
= sizeof(prev_data
);
3081 prev_record
.itemCount
= 1;
3084 * Capture b-tree changes inside a transaction
3086 if (hfs_start_transaction(hfsmp
) != 0) {
3090 if (hfs_lock(VTOC(hfsmp
->hfc_filevp
), HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
) != 0) {
3094 filefork
= VTOF(hfsmp
->hfc_filevp
);
3096 error
= BTGetInformation(filefork
, 0, &btinfo
);
3098 error
= MacToVFSError(error
);
3101 if (btinfo
.numRecords
< 2) {
3106 /* Only want 1st half of leaf records */
3107 numrecs
= (btinfo
.numRecords
/= 2) - 1;
3109 error
= BTIterateRecord(filefork
, kBTreeFirstRecord
, iterator
, &record
, &reclen
);
3111 printf("hfs_agehotfiles: BTIterateRecord: %d\n", error
);
3112 error
= MacToVFSError(error
);
3115 bcopy(iterator
, prev_iterator
, sizeof(BTreeIterator
));
3118 for (i
= 0; i
< numrecs
; ++i
) {
3119 error
= BTIterateRecord(filefork
, kBTreeNextRecord
, iterator
, &record
, &reclen
);
3121 if (key
->temperature
< prev_key
->temperature
) {
3122 printf("hfs_agehotfiles: out of order keys!\n");
3126 if (reclen
!= sizeof(data
)) {
3127 printf("hfs_agehotfiles: invalid record length %d\n", reclen
);
3131 if (key
->keyLength
!= HFC_KEYLENGTH
) {
3132 printf("hfs_agehotfiles: invalid key length %d\n", key
->keyLength
);
3136 } else if ((error
== fsBTEndOfIterationErr
|| error
== fsBTRecordNotFoundErr
) &&
3137 (i
== (numrecs
- 1))) {
3140 printf("hfs_agehotfiles: %d of %d BTIterateRecord: %d\n", i
, numrecs
, error
);
3141 error
= MacToVFSError(error
);
3144 if (prev_key
->temperature
== HFC_LOOKUPTAG
) {
3146 printf("hfs_agehotfiles: ran into thread record\n");
3151 error
= BTDeleteRecord(filefork
, prev_iterator
);
3153 printf("hfs_agehotfiles: BTDeleteRecord failed %d (file %d)\n", error
, prev_key
->fileID
);
3154 error
= MacToVFSError(error
);
3158 /* Age by halving the temperature (floor = 4) */
3159 newtemp
= MAX(prev_key
->temperature
>> 1, 4);
3160 prev_key
->temperature
= newtemp
;
3162 error
= BTInsertRecord(filefork
, prev_iterator
, &prev_record
, prev_record
.itemSize
);
3164 printf("hfs_agehotfiles: BTInsertRecord failed %d (file %d)\n", error
, prev_key
->fileID
);
3165 error
= MacToVFSError(error
);
3170 * Update thread entry with latest temperature.
3172 prev_key
->temperature
= HFC_LOOKUPTAG
;
3173 error
= BTUpdateRecord(filefork
, prev_iterator
,
3174 (IterateCallBackProcPtr
)update_callback
,
3177 printf("hfs_agehotfiles: %d of %d BTUpdateRecord failed %d (file %d, %d)\n",
3178 i
, numrecs
, error
, prev_key
->fileID
, newtemp
);
3179 error
= MacToVFSError(error
);
3183 bcopy(iterator
, prev_iterator
, sizeof(BTreeIterator
));
3190 printf("hfs_agehotfiles: aged %d records out of %d\n", aged
, btinfo
.numRecords
);
3192 (void) BTFlushPath(filefork
);
3194 hfs_unlock(VTOC(hfsmp
->hfc_filevp
));
3196 hfs_end_transaction(hfsmp
);
3199 FREE(iterator
, M_TEMP
);
3204 * Return true if any blocks (or all blocks if all is true)
3205 * are contained in the hot file region.
3208 hotextents(struct hfsmount
*hfsmp
, HFSPlusExtentDescriptor
* extents
)
3214 for (i
= 0; i
< kHFSPlusExtentDensity
; ++i
) {
3215 b1
= extents
[i
].startBlock
;
3218 b2
= b1
+ extents
[i
].blockCount
- 1;
3219 if ((b1
>= hfsmp
->hfs_hotfile_start
&&
3220 b2
<= hfsmp
->hfs_hotfile_end
) ||
3221 (b1
< hfsmp
->hfs_hotfile_end
&&
3222 b2
> hfsmp
->hfs_hotfile_end
)) {
3232 *========================================================================
3233 * HOT FILE B-TREE ROUTINES
3234 *========================================================================
3238 * Open the hot files b-tree for writing.
3240 * On successful exit the vnode has a reference but not an iocount.
3243 hfc_btree_open(struct hfsmount
*hfsmp
, struct vnode
**vpp
)
3245 return hfc_btree_open_ext(hfsmp
, vpp
, 0);
3249 hfc_btree_open_ext(struct hfsmount
*hfsmp
, struct vnode
**vpp
, int ignore_btree_errs
)
3253 struct cat_desc cdesc
;
3254 struct cat_attr cattr
;
3255 struct cat_fork cfork
;
3256 static char filename
[] = HFC_FILENAME
;
3260 int newvnode_flags
= 0;
3265 bzero(&cdesc
, sizeof(cdesc
));
3266 cdesc
.cd_parentcnid
= kRootDirID
;
3267 cdesc
.cd_nameptr
= (const u_int8_t
*)filename
;
3268 cdesc
.cd_namelen
= strlen(filename
);
3270 lockflags
= hfs_systemfile_lock(hfsmp
, SFL_CATALOG
, HFS_SHARED_LOCK
);
3272 error
= cat_lookup(hfsmp
, &cdesc
, 0, 0, &cdesc
, &cattr
, &cfork
, NULL
);
3274 hfs_systemfile_unlock(hfsmp
, lockflags
);
3277 printf("hfs: hfc_btree_open: cat_lookup error %d\n", error
);
3281 cdesc
.cd_flags
|= CD_ISMETA
;
3282 error
= hfs_getnewvnode(hfsmp
, NULL
, NULL
, &cdesc
, 0, &cattr
,
3283 &cfork
, &vp
, &newvnode_flags
);
3285 printf("hfs: hfc_btree_open: hfs_getnewvnode error %d\n", error
);
3286 cat_releasedesc(&cdesc
);
3289 if (!vnode_issystem(vp
)) {
3291 printf("hfs: hfc_btree_open: file has UBC, try again\n");
3293 hfs_unlock(VTOC(vp
));
3302 /* Open the B-tree file for writing... */
3303 error
= BTOpenPath(VTOF(vp
), (KeyCompareProcPtr
) hfc_comparekeys
);
3305 if (!ignore_btree_errs
) {
3306 printf("hfs: hfc_btree_open: BTOpenPath error %d; filesize %lld\n", error
, VTOF(vp
)->ff_size
);
3307 error
= MacToVFSError(error
);
3313 hfs_unlock(VTOC(vp
));
3316 vnode_ref(vp
); /* keep a reference while its open */
3320 if (!vnode_issystem(vp
))
3321 panic("hfs: hfc_btree_open: not a system file (vp = %p)", vp
);
3323 HotFilesInfo hotfileinfo
;
3325 if (error
== 0 && (hfsmp
->hfs_flags
& HFS_CS_HOTFILE_PIN
)) {
3326 if ((BTGetUserData(VTOF(vp
), &hotfileinfo
, sizeof(hotfileinfo
)) == 0) && (SWAP_BE32 (hotfileinfo
.magic
) == HFC_MAGIC
)) {
3327 if (hfsmp
->hfs_hotfile_freeblks
== 0) {
3328 hfsmp
->hfs_hotfile_freeblks
= hfsmp
->hfs_hotfile_maxblks
- SWAP_BE32 (hotfileinfo
.usedblocks
);
3331 hfs_hotfile_cur_freeblks(hfsmp
); // factors in any adjustments that happened at run-time
3339 * Close the hot files b-tree.
3341 * On entry the vnode has a reference.
3344 hfc_btree_close(struct hfsmount
*hfsmp
, struct vnode
*vp
)
3346 proc_t p
= current_proc();
3351 hfs_flush(hfsmp
, HFS_FLUSH_JOURNAL
);
3354 if (vnode_get(vp
) == 0) {
3355 error
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
);
3357 (void) hfs_fsync(vp
, MNT_WAIT
, 0, p
);
3358 error
= BTClosePath(VTOF(vp
));
3359 hfs_unlock(VTOC(vp
));
3370 // Assumes that hfsmp->hfc_filevp points to the hotfile btree vnode
3371 // (i.e. you called hfc_btree_open() ahead of time)
3374 hfc_btree_delete_record(struct hfsmount
*hfsmp
, BTreeIterator
*iterator
, HotFileKey
*key
)
3377 filefork_t
*filefork
=VTOF(hfsmp
->hfc_filevp
);
3379 /* Start a new transaction before calling BTree code. */
3380 if (hfs_start_transaction(hfsmp
) != 0) {
3384 error
= BTDeleteRecord(filefork
, iterator
);
3386 error
= MacToVFSError(error
);
3387 printf("hfs: failed to delete record for file-id %d : err %d\n", key
->fileID
, error
);
3392 savedtemp
= key
->temperature
;
3393 key
->temperature
= HFC_LOOKUPTAG
;
3394 error
= BTDeleteRecord(filefork
, iterator
);
3396 error
= MacToVFSError(error
);
3397 printf("hfs:2: failed to delete record for file-id %d : err %d\n", key
->fileID
, error
);
3399 key
->temperature
= savedtemp
;
3401 (void) BTFlushPath(filefork
);
3404 /* Transaction complete. */
3405 hfs_end_transaction(hfsmp
);
3411 // You have to have already opened the hotfile btree so
3412 // that hfsmp->hfc_filevp is filled in.
3415 hfc_btree_delete(struct hfsmount
*hfsmp
)
3417 struct vnode
*dvp
= NULL
;
3418 vfs_context_t ctx
= vfs_context_current();
3419 struct vnode_attr va
;
3420 struct componentname cname
;
3421 static char filename
[] = HFC_FILENAME
;
3424 error
= VFS_ROOT(HFSTOVFS(hfsmp
), &dvp
, ctx
);
3428 cname
.cn_nameiop
= DELETE
;
3429 cname
.cn_flags
= ISLASTCN
;
3430 cname
.cn_context
= ctx
;
3431 cname
.cn_pnbuf
= filename
;
3432 cname
.cn_pnlen
= sizeof(filename
);
3433 cname
.cn_nameptr
= filename
;
3434 cname
.cn_namelen
= strlen(filename
);
3436 cname
.cn_consume
= 0;
3439 VATTR_SET(&va
, va_type
, VREG
);
3440 VATTR_SET(&va
, va_mode
, S_IFREG
| S_IRUSR
| S_IWUSR
);
3441 VATTR_SET(&va
, va_uid
, 0);
3442 VATTR_SET(&va
, va_gid
, 0);
3444 if (hfs_start_transaction(hfsmp
) != 0) {
3449 /* call ourselves directly, ignore the higher-level VFS file creation code */
3450 error
= VNOP_REMOVE(dvp
, hfsmp
->hfc_filevp
, &cname
, 0, ctx
);
3452 printf("hfs: error %d removing HFBT on %s\n", error
, HFSTOVCB(hfsmp
)->vcbVN
);
3455 hfs_end_transaction(hfsmp
);
3470 * Create a hot files btree file.
3474 hfc_btree_create(struct hfsmount
*hfsmp
, unsigned int nodesize
, unsigned int entries
)
3476 struct vnode
*dvp
= NULL
;
3477 struct vnode
*vp
= NULL
;
3478 struct cnode
*cp
= NULL
;
3479 vfs_context_t ctx
= vfs_context_current();
3480 struct vnode_attr va
;
3481 struct componentname cname
;
3482 static char filename
[] = HFC_FILENAME
;
3485 if (hfsmp
->hfc_filevp
)
3486 panic("hfs: hfc_btree_create: hfc_filevp exists (vp = %p)", hfsmp
->hfc_filevp
);
3488 error
= VFS_ROOT(HFSTOVFS(hfsmp
), &dvp
, ctx
);
3492 cname
.cn_nameiop
= CREATE
;
3493 cname
.cn_flags
= ISLASTCN
;
3494 cname
.cn_context
= ctx
;
3495 cname
.cn_pnbuf
= filename
;
3496 cname
.cn_pnlen
= sizeof(filename
);
3497 cname
.cn_nameptr
= filename
;
3498 cname
.cn_namelen
= strlen(filename
);
3500 cname
.cn_consume
= 0;
3503 VATTR_SET(&va
, va_type
, VREG
);
3504 VATTR_SET(&va
, va_mode
, S_IFREG
| S_IRUSR
| S_IWUSR
);
3505 VATTR_SET(&va
, va_uid
, 0);
3506 VATTR_SET(&va
, va_gid
, 0);
3508 if (hfs_start_transaction(hfsmp
) != 0) {
3513 /* call ourselves directly, ignore the higher-level VFS file creation code */
3514 error
= VNOP_CREATE(dvp
, &vp
, &cname
, &va
, ctx
);
3516 printf("hfs: error %d creating HFBT on %s\n", error
, HFSTOVCB(hfsmp
)->vcbVN
);
3523 if ((error
= hfs_lock(VTOC(vp
), HFS_EXCLUSIVE_LOCK
, HFS_LOCK_DEFAULT
))) {
3528 /* Don't use non-regular files or files with links. */
3529 if (!vnode_isreg(vp
) || cp
->c_linkcount
!= 1) {
3534 printf("hfs: created HFBT on %s\n", HFSTOVCB(hfsmp
)->vcbVN
);
3536 if (VTOF(vp
)->ff_size
< nodesize
) {
3540 BTNodeDescriptor
*ndp
;
3542 HotFilesInfo
*hotfileinfo
;
3548 * Mark it invisible (truncate will pull these changes).
3550 ((FndrFileInfo
*)&cp
->c_finderinfo
[0])->fdFlags
|=
3551 SWAP_BE16 (kIsInvisible
+ kNameLocked
);
3553 if (kmem_alloc(kernel_map
, (vm_offset_t
*)&buffer
, nodesize
, VM_KERN_MEMORY_FILE
)) {
3557 bzero(buffer
, nodesize
);
3558 index
= (u_int16_t
*)buffer
;
3560 entirespernode
= (nodesize
- sizeof(BTNodeDescriptor
) - 2) /
3561 (sizeof(HotFileKey
) + 6);
3562 nodecnt
= 2 + howmany(entries
* 2, entirespernode
);
3563 nodecnt
= roundup(nodecnt
, 8);
3564 filesize
= nodecnt
* nodesize
;
3566 /* FILL IN THE NODE DESCRIPTOR: */
3567 ndp
= (BTNodeDescriptor
*)buffer
;
3568 ndp
->kind
= kBTHeaderNode
;
3569 ndp
->numRecords
= SWAP_BE16 (3);
3570 offset
= sizeof(BTNodeDescriptor
);
3571 index
[(nodesize
/ 2) - 1] = SWAP_BE16 (offset
);
3573 /* FILL IN THE HEADER RECORD: */
3574 bthp
= (BTHeaderRec
*)((u_int8_t
*)buffer
+ offset
);
3575 bthp
->nodeSize
= SWAP_BE16 (nodesize
);
3576 bthp
->totalNodes
= SWAP_BE32 (filesize
/ nodesize
);
3577 bthp
->freeNodes
= SWAP_BE32 (nodecnt
- 1);
3578 bthp
->clumpSize
= SWAP_BE32 (filesize
);
3579 bthp
->btreeType
= kUserBTreeType
; /* non-metadata */
3580 bthp
->attributes
|= SWAP_BE32 (kBTBigKeysMask
);
3581 bthp
->maxKeyLength
= SWAP_BE16 (HFC_KEYLENGTH
);
3582 offset
+= sizeof(BTHeaderRec
);
3583 index
[(nodesize
/ 2) - 2] = SWAP_BE16 (offset
);
3585 /* FILL IN THE USER RECORD: */
3586 hotfileinfo
= (HotFilesInfo
*)((u_int8_t
*)buffer
+ offset
);
3587 hotfileinfo
->magic
= SWAP_BE32 (HFC_MAGIC
);
3588 hotfileinfo
->version
= SWAP_BE32 (HFC_VERSION
);
3589 hotfileinfo
->duration
= SWAP_BE32 (HFC_DEFAULT_DURATION
);
3590 hotfileinfo
->timebase
= 0;
3591 hotfileinfo
->timeleft
= 0;
3592 hotfileinfo
->threshold
= SWAP_BE32 (HFC_MINIMUM_TEMPERATURE
);
3593 hotfileinfo
->maxfileblks
= SWAP_BE32 (HFC_MAXIMUM_FILESIZE
/ HFSTOVCB(hfsmp
)->blockSize
);
3594 if (hfsmp
->hfs_flags
& HFS_CS_HOTFILE_PIN
) {
3595 if (hfsmp
->hfs_hotfile_freeblks
== 0) {
3596 hfsmp
->hfs_hotfile_freeblks
= hfsmp
->hfs_hotfile_maxblks
;
3598 hotfileinfo
->usedblocks
= SWAP_BE32 (hfsmp
->hfs_hotfile_maxblks
- hfsmp
->hfs_hotfile_freeblks
);
3600 hotfileinfo
->maxfilecnt
= SWAP_BE32 (HFC_DEFAULT_FILE_COUNT
);
3602 strlcpy((char *)hotfileinfo
->tag
, hfc_tag
,
3603 sizeof hotfileinfo
->tag
);
3604 offset
+= kBTreeHeaderUserBytes
;
3605 index
[(nodesize
/ 2) - 3] = SWAP_BE16 (offset
);
3607 /* FILL IN THE MAP RECORD (only one node in use). */
3608 *((u_int8_t
*)buffer
+ offset
) = 0x80;
3609 offset
+= nodesize
- sizeof(BTNodeDescriptor
) - sizeof(BTHeaderRec
)
3610 - kBTreeHeaderUserBytes
- (4 * sizeof(int16_t));
3611 index
[(nodesize
/ 2) - 4] = SWAP_BE16 (offset
);
3613 vnode_setnoflush(vp
);
3614 error
= hfs_truncate(vp
, (off_t
)filesize
, IO_NDELAY
, 0, ctx
);
3616 printf("hfs: error %d growing HFBT on %s\n", error
, HFSTOVCB(hfsmp
)->vcbVN
);
3619 cp
->c_flag
|= C_ZFWANTSYNC
;
3620 cp
->c_zftimeout
= 1;
3623 struct vnop_write_args args
;
3626 auio
= uio_create(1, 0, UIO_SYSSPACE
, UIO_WRITE
);
3627 uio_addiov(auio
, (uintptr_t)buffer
, nodesize
);
3629 args
.a_desc
= &vnop_write_desc
;
3633 args
.a_context
= ctx
;
3638 error
= hfs_vnop_write(&args
);
3640 printf("hfs: error %d writing HFBT on %s\n", error
, HFSTOVCB(hfsmp
)->vcbVN
);
3644 kmem_free(kernel_map
, (vm_offset_t
)buffer
, nodesize
);
3647 hfs_end_transaction(hfsmp
);
3661 * Compare two hot file b-tree keys.
3663 * Result: +n search key > trial key
3664 * 0 search key = trial key
3665 * -n search key < trial key
3668 hfc_comparekeys(HotFileKey
*searchKey
, HotFileKey
*trialKey
)
3671 * Compared temperatures first.
3673 if (searchKey
->temperature
== trialKey
->temperature
) {
3675 * Temperatures are equal so compare file ids.
3677 if (searchKey
->fileID
== trialKey
->fileID
) {
3679 * File ids are equal so compare fork types.
3681 if (searchKey
->forkType
== trialKey
->forkType
) {
3683 } else if (searchKey
->forkType
> trialKey
->forkType
) {
3686 } else if (searchKey
->fileID
> trialKey
->fileID
) {
3689 } else if (searchKey
->temperature
> trialKey
->temperature
) {
3698 *========================================================================
3699 * HOT FILE DATA COLLECTING ROUTINES
3700 *========================================================================
3704 * Lookup a hot file entry in the tree.
3707 static hotfile_entry_t
*
3708 hf_lookup(hotfile_data_t
*hotdata
, u_int32_t fileid
, u_int32_t temperature
)
3710 hotfile_entry_t
*entry
= hotdata
->rootentry
;
3713 entry
->temperature
!= temperature
&&
3714 entry
->fileid
!= fileid
) {
3716 if (temperature
> entry
->temperature
)
3717 entry
= entry
->right
;
3718 else if (temperature
< entry
->temperature
)
3719 entry
= entry
->left
;
3720 else if (fileid
> entry
->fileid
)
3721 entry
= entry
->right
;
3723 entry
= entry
->left
;
3730 * Insert a hot file entry into the tree.
3733 hf_insert(hotfile_data_t
*hotdata
, hotfile_entry_t
*newentry
)
3735 hotfile_entry_t
*entry
= hotdata
->rootentry
;
3736 u_int32_t fileid
= newentry
->fileid
;
3737 u_int32_t temperature
= newentry
->temperature
;
3739 if (entry
== NULL
) {
3740 hotdata
->rootentry
= newentry
;
3741 hotdata
->coldest
= newentry
;
3742 hotdata
->activefiles
++;
3747 if (temperature
> entry
->temperature
) {
3749 entry
= entry
->right
;
3751 entry
->right
= newentry
;
3754 } else if (temperature
< entry
->temperature
) {
3756 entry
= entry
->left
;
3758 entry
->left
= newentry
;
3761 } else if (fileid
> entry
->fileid
) {
3763 entry
= entry
->right
;
3765 if (entry
->fileid
!= fileid
)
3766 entry
->right
= newentry
;
3771 entry
= entry
->left
;
3773 if (entry
->fileid
!= fileid
) {
3774 entry
->left
= newentry
;
3783 hotdata
->activefiles
++;
3788 * Find the coldest entry in the tree.
3790 static hotfile_entry_t
*
3791 hf_coldest(hotfile_data_t
*hotdata
)
3793 hotfile_entry_t
*entry
= hotdata
->rootentry
;
3797 entry
= entry
->left
;
3803 * Find the hottest entry in the tree.
3805 static hotfile_entry_t
*
3806 hf_hottest(hotfile_data_t
*hotdata
)
3808 hotfile_entry_t
*entry
= hotdata
->rootentry
;
3811 while (entry
->right
)
3812 entry
= entry
->right
;
3818 * Delete a hot file entry from the tree.
3821 hf_delete(hotfile_data_t
*hotdata
, u_int32_t fileid
, u_int32_t temperature
)
3823 hotfile_entry_t
*entry
, *parent
, *next
;
3826 entry
= hotdata
->rootentry
;
3829 entry
->temperature
!= temperature
&&
3830 entry
->fileid
!= fileid
) {
3833 if (temperature
> entry
->temperature
)
3834 entry
= entry
->right
;
3835 else if (temperature
< entry
->temperature
)
3836 entry
= entry
->left
;
3837 else if (fileid
> entry
->fileid
)
3838 entry
= entry
->right
;
3840 entry
= entry
->left
;
3845 * Reorganize the sub-trees spanning from our entry.
3847 if ((next
= entry
->right
)) {
3848 hotfile_entry_t
*pnextl
, *psub
;
3850 * Tree pruning: take the left branch of the
3851 * current entry and place it at the lowest
3852 * left branch of the current right branch
3856 /* Walk the Right/Left sub tree from current entry */
3857 while ((pnextl
= psub
->left
))
3860 /* Plug the old left tree to the new ->Right leftmost entry */
3861 psub
->left
= entry
->left
;
3863 } else /* only left sub-tree, simple case */ {
3867 * Now, plug the current entry sub tree to
3868 * the good pointer of our parent entry.
3871 hotdata
->rootentry
= next
;
3872 else if (parent
->left
== entry
)
3873 parent
->left
= next
;
3875 parent
->right
= next
;
3877 /* Place entry back on the free-list */
3880 entry
->temperature
= 0;
3882 entry
->right
= hotdata
->freelist
;
3883 hotdata
->freelist
= entry
;
3884 hotdata
->activefiles
--;
3886 if (hotdata
->coldest
== entry
|| hotdata
->coldest
== NULL
) {
3887 hotdata
->coldest
= hf_coldest(hotdata
);
3894 * Get a free hot file entry.
3896 static hotfile_entry_t
*
3897 hf_getnewentry(hotfile_data_t
*hotdata
)
3899 hotfile_entry_t
* entry
;
3902 * When the free list is empty then steal the coldest one
3904 if (hotdata
->freelist
== NULL
) {
3905 entry
= hf_coldest(hotdata
);
3906 hf_delete(hotdata
, entry
->fileid
, entry
->temperature
);
3908 entry
= hotdata
->freelist
;
3909 hotdata
->freelist
= entry
->right
;
3917 * Generate a sorted list of hot files (hottest to coldest).
3919 * As a side effect, every node in the hot file tree will be
3920 * deleted (moved to the free list).
3923 hf_getsortedlist(hotfile_data_t
* hotdata
, hotfilelist_t
*sortedlist
)
3926 hotfile_entry_t
*entry
;
3928 while ((entry
= hf_hottest(hotdata
)) != NULL
) {
3929 sortedlist
->hfl_hotfile
[i
].hf_fileid
= entry
->fileid
;
3930 sortedlist
->hfl_hotfile
[i
].hf_temperature
= entry
->temperature
;
3931 sortedlist
->hfl_hotfile
[i
].hf_blocks
= entry
->blocks
;
3932 sortedlist
->hfl_totalblocks
+= entry
->blocks
;
3935 hf_delete(hotdata
, entry
->fileid
, entry
->temperature
);
3938 sortedlist
->hfl_count
= i
;
3941 printf("hfs: hf_getsortedlist returning %d entries w/%d total blocks\n", i
, sortedlist
->hfl_totalblocks
);
3948 hf_maxdepth(hotfile_entry_t
* root
, int depth
, int *maxdepth
)
3952 if (depth
> *maxdepth
)
3954 hf_maxdepth(root
->left
, depth
, maxdepth
);
3955 hf_maxdepth(root
->right
, depth
, maxdepth
);
3960 hf_printtree(hotfile_entry_t
* root
)
3963 hf_printtree(root
->left
);
3964 printf("hfs: temperature: % 8d, fileid %d\n", root
->temperature
, root
->fileid
);
3965 hf_printtree(root
->right
);