]> git.saurik.com Git - apple/hfs.git/blob - core/hfs_hotfiles.c
hfs-556.100.11.tar.gz
[apple/hfs.git] / core / hfs_hotfiles.c
1 /*
2 * Copyright (c) 2003-2015 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
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.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
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.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29 #include <libkern/OSAtomic.h>
30
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/fcntl.h>
34 #include <sys/kernel.h>
35 #include <sys/malloc.h>
36 #include <sys/ubc.h>
37 #include <sys/vnode.h>
38 #include <sys/kauth.h>
39 #include <sys/vm.h>
40
41 #include "hfs.h"
42 #include "hfs_endian.h"
43 #include "hfs_format.h"
44 #include "hfs_mount.h"
45 #include "hfs_hotfiles.h"
46
47 #include "BTreeScanner.h"
48
49
50 #define HFC_DEBUG 0
51 #define HFC_VERBOSE 0
52
53
54 /*
55 * Minimum post Tiger base time.
56 * Thu Mar 31 17:00:00 2005
57 */
58 #define HFC_MIN_BASE_TIME 0x424c8f00L
59
60 /*
61 * Hot File List (runtime).
62 */
63 typedef struct hotfileinfo {
64 u_int32_t hf_fileid;
65 u_int32_t hf_temperature;
66 u_int32_t hf_blocks;
67 } hotfileinfo_t;
68
69 typedef struct hotfilelist {
70 size_t hfl_size;
71 u_int32_t hfl_magic;
72 u_int32_t hfl_version;
73 time_t hfl_duration; /* duration of sample period */
74 int hfl_count; /* count of hot files recorded */
75 int hfl_next; /* next file to move */
76 int hfl_totalblocks; /* total hot file blocks */
77 int hfl_reclaimblks; /* blocks to reclaim in HFV */
78 u_int32_t hfl_spare[2];
79 hotfileinfo_t hfl_hotfile[1]; /* array of hot files */
80 } hotfilelist_t;
81
82
83 /*
84 * Hot File Entry (runtime).
85 */
86 typedef struct hotfile_entry {
87 struct hotfile_entry *left;
88 struct hotfile_entry *right;
89 u_int32_t fileid;
90 u_int32_t temperature;
91 u_int32_t blocks;
92 } hotfile_entry_t;
93
94
95 //
96 // We cap the max temperature for non-system files to "MAX_NORMAL_TEMP"
97 // so that they will always have a lower temperature than system (aka
98 // "auto-cached") files. System files have MAX_NORMAL_TEMP added to
99 // their temperature which produces two bands of files (all non-system
100 // files will have a temp less than MAX_NORMAL_TEMP and all system
101 // files will have a temp greatern than MAX_NORMAL_TEMP).
102 //
103 // This puts non-system files on the left side of the hotfile btree
104 // (and we start evicting from the left-side of the tree). The idea is
105 // that we will evict non-system files more aggressively since their
106 // working set changes much more dynamically than system files (which
107 // are for the most part, static).
108 //
109 // NOTE: these values have to fit into a 32-bit int. We use a
110 // value of 1-billion which gives a pretty broad range
111 // and yet should not run afoul of any sign issues.
112 //
113 #define MAX_NORMAL_TEMP 1000000000
114 #define HF_TEMP_RANGE MAX_NORMAL_TEMP
115
116
117 //
118 // These used to be defines of the hard coded values. But if
119 // we're on an cooperative fusion (CF) system we need to change
120 // the values (which happens in hfs_recording_init()
121 //
122 uint32_t hfc_default_file_count = 1000;
123 uint32_t hfc_default_duration = (3600 * 60);
124 uint32_t hfc_max_file_count = 5000;
125 uint64_t hfc_max_file_size = (10 * 1024 * 1024);
126
127
128 /*
129 * Hot File Recording Data (runtime).
130 */
131 typedef struct hotfile_data {
132 size_t size;
133 struct hfsmount *hfsmp;
134 long refcount;
135 u_int32_t activefiles; /* active number of hot files */
136 u_int32_t threshold;
137 u_int32_t maxblocks;
138 hotfile_entry_t *rootentry;
139 hotfile_entry_t *freelist;
140 hotfile_entry_t *coldest;
141 hotfile_entry_t entries[];
142 } hotfile_data_t;
143
144 static int hfs_recording_start (struct hfsmount *);
145 static int hfs_recording_stop (struct hfsmount *);
146
147 /* Hotfiles pinning routines */
148 static int hfs_getvnode_and_pin (struct hfsmount *hfsmp, uint32_t fileid, uint32_t *pinned);
149 static int hfs_pin_extent_record (struct hfsmount *hfsmp, HFSPlusExtentRecord extents, uint32_t *pinned);
150 static int hfs_pin_catalog_rec (struct hfsmount *hfsmp, HFSPlusCatalogFile *cfp, int rsrc);
151
152 /*
153 * Hot File Data recording functions (in-memory binary tree).
154 */
155 static int hf_insert (hotfile_data_t *, hotfile_entry_t *);
156 static void hf_delete (hotfile_data_t *, u_int32_t, u_int32_t);
157 static hotfile_entry_t * hf_coldest (hotfile_data_t *);
158 static hotfile_entry_t * hf_getnewentry (hotfile_data_t *);
159 static void hf_getsortedlist (hotfile_data_t *, hotfilelist_t *);
160
161 #if HFC_DEBUG
162 static hotfile_entry_t * hf_lookup (hotfile_data_t *, u_int32_t, u_int32_t);
163 static void hf_maxdepth(hotfile_entry_t *, int, int *);
164 static void hf_printtree (hotfile_entry_t *);
165 #endif
166
167 /*
168 * Hot File misc support functions.
169 */
170 static int hotfiles_collect (struct hfsmount *);
171 static int hotfiles_age (struct hfsmount *);
172 static int hotfiles_adopt (struct hfsmount *);
173 static int hotfiles_evict (struct hfsmount *, vfs_context_t);
174 static int hotfiles_refine (struct hfsmount *);
175 static int hotextents(struct hfsmount *, HFSPlusExtentDescriptor *);
176 static int hfs_addhotfile_internal(struct vnode *);
177 static int hfs_hotfile_cur_freeblks(hfsmount_t *hfsmp);
178
179
180 /*
181 * Hot File Cluster B-tree (on disk) functions.
182 */
183 static int hfc_btree_create (struct hfsmount *, unsigned int, unsigned int);
184 static int hfc_btree_open (struct hfsmount *, struct vnode **);
185 static int hfc_btree_open_ext(struct hfsmount *hfsmp, struct vnode **vpp, int ignore_btree_errs);
186 static int hfc_btree_close (struct hfsmount *, struct vnode *);
187 static int hfc_btree_delete_record(struct hfsmount *hfsmp, BTreeIterator *iterator, HotFileKey *key);
188 static int hfc_btree_delete(struct hfsmount *hfsmp);
189 static int hfc_comparekeys (HotFileKey *, HotFileKey *);
190
191
192 char hfc_tag[] = "CLUSTERED HOT FILES B-TREE ";
193
194
195 /*
196 *========================================================================
197 * HOT FILE INTERFACE ROUTINES
198 *========================================================================
199 */
200
201 /*
202 * Start recording the hottest files on a file system.
203 *
204 * Requires that the hfc_mutex be held.
205 */
206 static int
207 hfs_recording_start(struct hfsmount *hfsmp)
208 {
209 hotfile_data_t *hotdata;
210 struct timeval tv;
211 int maxentries;
212 size_t size;
213 int i;
214 int error;
215
216 if ((hfsmp->hfs_flags & HFS_READ_ONLY) ||
217 (hfsmp->jnl == NULL) ||
218 (hfsmp->hfs_flags & HFS_METADATA_ZONE) == 0) {
219 return (EPERM);
220 }
221 if (HFSTOVCB(hfsmp)->freeBlocks < (2 * (u_int32_t)hfsmp->hfs_hotfile_maxblks)) {
222 return (ENOSPC);
223 }
224 if (hfsmp->hfc_stage != HFC_IDLE) {
225 return (EBUSY);
226 }
227 hfsmp->hfc_stage = HFC_BUSY;
228
229 if (hfsmp->hfc_recdata) {
230 hfs_free(hfsmp->hfc_recdata, hfsmp->hfc_recdata->size);
231 hfsmp->hfc_recdata = NULL;
232 }
233 if (hfsmp->hfc_filelist) {
234 hfs_free(hfsmp->hfc_filelist, hfsmp->hfc_filelist->hfl_size);
235 hfsmp->hfc_filelist = NULL;
236 }
237
238 microtime(&tv); /* Times are base on GMT time. */
239
240 /*
241 * On first startup check for suspended recording.
242 */
243 if (hfsmp->hfc_timebase == 0 &&
244 hfc_btree_open(hfsmp, &hfsmp->hfc_filevp) == 0) {
245 HotFilesInfo hotfileinfo;
246
247 if ((BTGetUserData(VTOF(hfsmp->hfc_filevp), &hotfileinfo,
248 sizeof(hotfileinfo)) == 0) &&
249 (SWAP_BE32 (hotfileinfo.magic) == HFC_MAGIC) &&
250 (SWAP_BE32 (hotfileinfo.timeleft) > 0) &&
251 (SWAP_BE32 (hotfileinfo.timebase) > 0)) {
252 if (hfsmp->hfs_flags & HFS_CS_HOTFILE_PIN) {
253 if (hfsmp->hfs_hotfile_freeblks == 0) {
254 hfsmp->hfs_hotfile_freeblks = hfsmp->hfs_hotfile_maxblks - SWAP_BE32 (hotfileinfo.usedblocks);
255 }
256 hfsmp->hfc_maxfiles = 0x7fffffff;
257 printf("hfs: %s: %s: hotfile freeblocks: %d, max: %d\n", hfsmp->vcbVN, __FUNCTION__,
258 hfsmp->hfs_hotfile_freeblks, hfsmp->hfs_hotfile_maxblks);
259 } else {
260 hfsmp->hfc_maxfiles = SWAP_BE32 (hotfileinfo.maxfilecnt);
261 }
262 hfsmp->hfc_timebase = SWAP_BE32 (hotfileinfo.timebase);
263 int timeleft = (int)SWAP_BE32(hotfileinfo.timeleft);
264 if (timeleft < 0 || timeleft > (int)(HFC_DEFAULT_DURATION*2)) {
265 // in case this field got botched, don't let it screw things up
266 // printf("hfs: hotfiles: bogus looking timeleft: %d\n", timeleft);
267 timeleft = HFC_DEFAULT_DURATION;
268 }
269 hfsmp->hfc_timeout = timeleft + tv.tv_sec ;
270 /* Fix up any bogus timebase values. */
271 if (hfsmp->hfc_timebase < HFC_MIN_BASE_TIME) {
272 hfsmp->hfc_timebase = hfsmp->hfc_timeout - HFC_DEFAULT_DURATION;
273 }
274 #if HFC_VERBOSE
275 printf("hfs: Resume recording hot files on %s (%d secs left (%d); timeout %ld)\n",
276 hfsmp->vcbVN, SWAP_BE32 (hotfileinfo.timeleft), timeleft, hfsmp->hfc_timeout - tv.tv_sec);
277 #endif
278 } else {
279 hfsmp->hfc_maxfiles = HFC_DEFAULT_FILE_COUNT;
280 hfsmp->hfc_timebase = tv.tv_sec + 1;
281 hfsmp->hfc_timeout = hfsmp->hfc_timebase + HFC_DEFAULT_DURATION;
282 }
283 (void) hfc_btree_close(hfsmp, hfsmp->hfc_filevp);
284 hfsmp->hfc_filevp = NULL;
285 } else {
286 struct cat_attr cattr;
287 u_int32_t cnid;
288
289 /*
290 * Make sure a btree file exists.
291 */
292 cnid = GetFileInfo(HFSTOVCB(hfsmp), kRootDirID, HFC_FILENAME, &cattr, NULL);
293 if ((cnid == 0) &&
294 !S_ISREG(cattr.ca_mode) &&
295 (error = hfc_btree_create(hfsmp, HFSTOVCB(hfsmp)->blockSize, HFC_DEFAULT_FILE_COUNT))) {
296 hfsmp->hfc_stage = HFC_IDLE;
297 wakeup((caddr_t)&hfsmp->hfc_stage);
298 return (error);
299 }
300 #if HFC_VERBOSE
301 printf("hfs: begin recording hot files on %s (hotfile start/end block: %d - %d; max/free: %d/%d; maxfiles: %d)\n",
302 hfsmp->vcbVN,
303 hfsmp->hfs_hotfile_start, hfsmp->hfs_hotfile_end,
304 hfsmp->hfs_hotfile_maxblks, hfsmp->hfs_hotfile_freeblks, hfsmp->hfc_maxfiles);
305 #endif
306 hfsmp->hfc_maxfiles = HFC_DEFAULT_FILE_COUNT;
307 hfsmp->hfc_timeout = tv.tv_sec + HFC_DEFAULT_DURATION;
308
309 /* Reset time base. */
310 if (hfsmp->hfc_timebase == 0) {
311 hfsmp->hfc_timebase = tv.tv_sec + 1;
312 } else {
313 time_t cumulativebase;
314
315 cumulativebase = hfsmp->hfc_timeout - (HFC_CUMULATIVE_CYCLES * HFC_DEFAULT_DURATION);
316 hfsmp->hfc_timebase = MAX(hfsmp->hfc_timebase, cumulativebase);
317 }
318 }
319
320 if ((hfsmp->hfc_maxfiles == 0) ||
321 (hfsmp->hfc_maxfiles > HFC_MAXIMUM_FILE_COUNT)) {
322 hfsmp->hfc_maxfiles = HFC_DEFAULT_FILE_COUNT;
323 }
324 maxentries = hfsmp->hfc_maxfiles;
325
326 size = sizeof(hotfile_data_t) + maxentries * sizeof(hotfile_entry_t);
327 hotdata = hfs_mallocz(size);
328 hotdata->size = size;
329
330 for (i = 1; i < maxentries ; i++)
331 hotdata->entries[i-1].right = &hotdata->entries[i];
332
333 hotdata->freelist = &hotdata->entries[0];
334 /*
335 * Establish minimum temperature and maximum file size.
336 */
337 hotdata->threshold = HFC_MINIMUM_TEMPERATURE;
338 hotdata->maxblocks = HFC_MAXIMUM_FILESIZE / HFSTOVCB(hfsmp)->blockSize;
339 hotdata->hfsmp = hfsmp;
340
341 hfsmp->hfc_recdata = hotdata;
342 hfsmp->hfc_stage = HFC_RECORDING;
343 wakeup((caddr_t)&hfsmp->hfc_stage);
344 return (0);
345 }
346
347 /*
348 * Stop recording the hotest files on a file system.
349 *
350 * Requires that the hfc_mutex be held.
351 */
352 static int
353 hfs_recording_stop(struct hfsmount *hfsmp)
354 {
355 hotfile_data_t *hotdata;
356 hotfilelist_t *listp;
357 struct timeval tv;
358 size_t size;
359 enum hfc_stage newstage = HFC_IDLE;
360 int error;
361
362 if (hfsmp->hfc_stage != HFC_RECORDING)
363 return (EPERM);
364
365 hfsmp->hfc_stage = HFC_BUSY;
366
367 hotfiles_collect(hfsmp);
368
369
370 /*
371 * Convert hot file data into a simple file id list....
372 *
373 * then dump the sample data
374 */
375 #if HFC_VERBOSE
376 printf("hfs: end of hot file recording on %s\n", hfsmp->vcbVN);
377 #endif
378 hotdata = hfsmp->hfc_recdata;
379 if (hotdata == NULL)
380 return (0);
381 hfsmp->hfc_recdata = NULL;
382 hfsmp->hfc_stage = HFC_EVALUATION;
383 wakeup((caddr_t)&hfsmp->hfc_stage);
384
385 #if HFC_VERBOSE
386 printf("hfs: curentries: %d\n", hotdata->activefiles);
387 #endif
388 /*
389 * If no hot files recorded then we're done.
390 */
391 if (hotdata->rootentry == NULL) {
392 error = 0;
393 goto out;
394 }
395
396 /* Open the B-tree file for writing... */
397 if (hfsmp->hfc_filevp)
398 panic("hfs_recording_stop: hfc_filevp exists (vp = %p)", hfsmp->hfc_filevp);
399
400 error = hfc_btree_open(hfsmp, &hfsmp->hfc_filevp);
401 if (error) {
402 goto out;
403 }
404
405 /*
406 * Age the previous set of clustered hot files.
407 */
408 error = hotfiles_age(hfsmp);
409 if (error) {
410 (void) hfc_btree_close(hfsmp, hfsmp->hfc_filevp);
411 hfsmp->hfc_filevp = NULL;
412 goto out;
413 }
414
415 /*
416 * Create a sorted list of hotest files.
417 */
418 size = sizeof(hotfilelist_t);
419 size += sizeof(hotfileinfo_t) * (hotdata->activefiles - 1);
420 listp = hfs_mallocz(size);
421 listp->hfl_size = size;
422
423 hf_getsortedlist(hotdata, listp); /* NOTE: destroys hot file tree! */
424 microtime(&tv);
425 listp->hfl_duration = tv.tv_sec - hfsmp->hfc_timebase;
426 hfs_assert(!hfsmp->hfc_filelist);
427 hfsmp->hfc_filelist = listp;
428
429 /*
430 * Account for duplicates.
431 */
432 error = hotfiles_refine(hfsmp);
433 if (error) {
434 (void) hfc_btree_close(hfsmp, hfsmp->hfc_filevp);
435 hfsmp->hfc_filevp = NULL;
436 goto out;
437 }
438
439 /*
440 * Compute the amount of space to reclaim...
441 */
442 if (listp->hfl_totalblocks > hfs_hotfile_cur_freeblks(hfsmp)) {
443 listp->hfl_reclaimblks =
444 MIN(listp->hfl_totalblocks, hfsmp->hfs_hotfile_maxblks) -
445 hfsmp->hfs_hotfile_freeblks;
446 #if HFC_VERBOSE
447 printf("hfs_recording_stop: need to reclaim %d blocks\n", listp->hfl_reclaimblks);
448 #endif
449 if (listp->hfl_reclaimblks)
450 newstage = HFC_EVICTION;
451 else
452 newstage = HFC_ADOPTION;
453 } else {
454 newstage = HFC_ADOPTION;
455 }
456
457 if (newstage == HFC_ADOPTION && listp->hfl_totalblocks == 0) {
458 (void) hfc_btree_close(hfsmp, hfsmp->hfc_filevp);
459 hfsmp->hfc_filevp = NULL;
460 newstage = HFC_IDLE;
461 }
462 out:
463 #if HFC_VERBOSE
464 if (newstage == HFC_EVICTION)
465 printf("hfs: evicting coldest files\n");
466 else if (newstage == HFC_ADOPTION)
467 printf("hfs: adopting hotest files\n");
468 #endif
469 hfs_free(hotdata, hotdata->size);
470
471 hfsmp->hfc_stage = newstage;
472 wakeup((caddr_t)&hfsmp->hfc_stage);
473 return (error);
474 }
475
476 static void
477 save_btree_user_info(struct hfsmount *hfsmp)
478 {
479 HotFilesInfo hotfileinfo;
480 struct timeval tv;
481
482 microtime(&tv);
483 hotfileinfo.magic = SWAP_BE32 (HFC_MAGIC);
484 hotfileinfo.version = SWAP_BE32 (HFC_VERSION);
485 hotfileinfo.duration = SWAP_BE32 (HFC_DEFAULT_DURATION);
486 hotfileinfo.timebase = SWAP_BE32 (hfsmp->hfc_timebase);
487 hotfileinfo.timeleft = SWAP_BE32 (hfsmp->hfc_timeout - tv.tv_sec);
488 hotfileinfo.threshold = SWAP_BE32 (HFC_MINIMUM_TEMPERATURE);
489 hotfileinfo.maxfileblks = SWAP_BE32 (HFC_MAXIMUM_FILESIZE / HFSTOVCB(hfsmp)->blockSize);
490 if (hfsmp->hfs_flags & HFS_CS_HOTFILE_PIN) {
491 hotfileinfo.usedblocks = SWAP_BE32 (hfsmp->hfs_hotfile_maxblks - hfs_hotfile_cur_freeblks(hfsmp));
492 #if HFC_VERBOSE
493 printf("hfs: %s: saving usedblocks = %d (timeleft: %d; timeout %ld)\n", hfsmp->vcbVN, (hfsmp->hfs_hotfile_maxblks - hfsmp->hfs_hotfile_freeblks),
494 SWAP_BE32(hotfileinfo.timeleft), hfsmp->hfc_timeout);
495 #endif
496 } else {
497 hotfileinfo.maxfilecnt = SWAP_BE32 (HFC_DEFAULT_FILE_COUNT);
498 }
499 strlcpy((char *)hotfileinfo.tag, hfc_tag, sizeof hotfileinfo.tag);
500 (void) BTSetUserData(VTOF(hfsmp->hfc_filevp), &hotfileinfo, sizeof(hotfileinfo));
501 }
502
503 /*
504 * Suspend recording the hotest files on a file system.
505 */
506 int
507 hfs_recording_suspend(struct hfsmount *hfsmp)
508 {
509 hotfile_data_t *hotdata = NULL;
510 int error;
511
512 if (hfsmp->hfc_stage == HFC_DISABLED)
513 return (0);
514
515 lck_mtx_lock(&hfsmp->hfc_mutex);
516
517 /*
518 * XXX NOTE
519 * A suspend can occur during eval/evict/adopt stage.
520 * In that case we would need to write out info and
521 * flush our HFBT vnode. Currently we just bail.
522 */
523
524 hotdata = hfsmp->hfc_recdata;
525 if (hotdata == NULL || hfsmp->hfc_stage != HFC_RECORDING) {
526 error = 0;
527 goto out;
528 }
529 hfsmp->hfc_stage = HFC_BUSY;
530
531 #if HFC_VERBOSE
532 printf("hfs: suspend hot file recording on %s\n", hfsmp->vcbVN);
533 #endif
534 error = hfc_btree_open(hfsmp, &hfsmp->hfc_filevp);
535 if (error) {
536 printf("hfs_recording_suspend: err %d opening btree\n", error);
537 goto out;
538 }
539
540 if (hfs_start_transaction(hfsmp) != 0) {
541 goto out;
542 }
543 if (hfs_lock(VTOC(hfsmp->hfc_filevp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT) != 0) {
544 goto end_transaction;
545 }
546
547 save_btree_user_info(hfsmp);
548
549 hfs_unlock(VTOC(hfsmp->hfc_filevp));
550
551 end_transaction:
552 hfs_end_transaction(hfsmp);
553
554 out:
555 if (hfsmp->hfc_filevp) {
556 (void) hfc_btree_close(hfsmp, hfsmp->hfc_filevp);
557 hfsmp->hfc_filevp = NULL;
558 }
559 if (hotdata) {
560 hfs_free(hotdata, hotdata->size);
561 hfsmp->hfc_recdata = NULL;
562 }
563 hfsmp->hfc_stage = HFC_DISABLED;
564 wakeup((caddr_t)&hfsmp->hfc_stage);
565
566 lck_mtx_unlock(&hfsmp->hfc_mutex);
567 return (error);
568 }
569
570
571 static void
572 reset_file_ids(struct hfsmount *hfsmp, uint32_t *fileid_table, int num_ids)
573 {
574 int i, error;
575
576 for(i=0; i < num_ids; i++) {
577 struct vnode *vp;
578
579 error = hfs_vget(hfsmp, fileid_table[i], &vp, 0, 0);
580 if (error) {
581 if (error == ENOENT) {
582 error = 0;
583 continue; /* stale entry, go to next */
584 }
585 continue;
586 }
587
588 // hfs_vget returns a locked cnode so no need to lock here
589
590 if ((hfsmp->hfs_flags & HFS_CS_HOTFILE_PIN) && (VTOC(vp)->c_attr.ca_recflags & kHFSFastDevPinnedMask)) {
591 error = hfs_pin_vnode(hfsmp, vp, HFS_UNPIN_IT, NULL);
592 }
593
594 /*
595 * The updates to the catalog must be journaled
596 */
597 hfs_start_transaction(hfsmp);
598
599 //
600 // turn off _all_ the hotfile related bits since we're resetting state
601 //
602 if (VTOC(vp)->c_attr.ca_recflags & kHFSFastDevCandidateMask) {
603 vnode_clearfastdevicecandidate(vp);
604 }
605
606 VTOC(vp)->c_attr.ca_recflags &= ~(kHFSFastDevPinnedMask|kHFSDoNotFastDevPinMask|kHFSFastDevCandidateMask|kHFSAutoCandidateMask);
607 VTOC(vp)->c_flag |= C_MODIFIED;
608
609 hfs_update(vp, 0);
610
611 hfs_end_transaction(hfsmp);
612
613 hfs_unlock(VTOC(vp));
614 vnode_put(vp);
615 }
616 }
617
618 static int
619 flag_hotfile(struct hfsmount *hfsmp, const char *filename)
620 {
621 struct vnode *dvp = NULL, *fvp = NULL;
622 vfs_context_t ctx = vfs_context_kernel();
623 int error=0;
624 size_t fname_len;
625 const char *orig_fname = filename;
626
627 if (filename == NULL) {
628 return EINVAL;
629 }
630
631 fname_len = strlen(filename); // do NOT include the trailing '\0' so that we break out of the loop below
632
633 error = hfs_vfs_root(HFSTOVFS(hfsmp), &dvp, ctx);
634 if (error) {
635 return (error);
636 }
637
638 /* At this point, 'dvp' must be considered iocounted */
639 const char *ptr;
640 ptr = filename;
641
642 while (ptr < (orig_fname + fname_len - 1)) {
643 for(; ptr < (orig_fname + fname_len) && *ptr && *ptr != '/'; ptr++) {
644 /* just keep advancing till we reach the end of the string or a slash */
645 }
646
647 struct componentname cname = {
648 .cn_nameiop = LOOKUP,
649 .cn_flags = ISLASTCN,
650 .cn_pnbuf = __DECONST(char *, orig_fname),
651 .cn_nameptr = __DECONST(char *, filename),
652 .cn_pnlen = fname_len,
653 .cn_namelen = ptr - filename
654 };
655
656 struct vnop_lookup_args ap = {
657 .a_dvp = dvp,
658 .a_vpp = &fvp,
659 .a_cnp = &cname,
660 .a_context = ctx
661 };
662
663 error = hfs_vnop_lookup(&ap);
664 if (error) {
665 /*
666 * If 'dvp' is non-NULL, then it has an iocount. Make sure to release it
667 * before bailing out. VNOP_LOOKUP could legitimately return ENOENT
668 * if the item didn't exist or if we raced with a delete.
669 */
670 if (dvp) {
671 vnode_put(dvp);
672 dvp = NULL;
673 }
674 return error;
675 }
676
677 if (ptr < orig_fname + fname_len - 1) {
678 //
679 // we've got a multi-part pathname so drop the ref on the dir,
680 // make dvp become what we just looked up, and advance over
681 // the slash character in the pathname to get to the next part
682 // of the component
683 //
684 vnode_put(dvp);
685 dvp = fvp;
686 fvp = NULL;
687
688 filename = ++ptr; // skip the slash character
689 }
690 }
691
692 if (fvp == NULL) {
693 error = ENOENT;
694 goto out;
695 }
696
697 struct cnode *cp = VTOC(fvp);
698 if ((error = hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT)) != 0) {
699 goto out;
700 }
701
702 hfs_start_transaction(hfsmp);
703
704 cp->c_attr.ca_recflags |= (kHFSFastDevCandidateMask|kHFSAutoCandidateMask);
705 cp->c_flag |= C_MODIFIED;
706
707 hfs_update(fvp, 0);
708
709 hfs_end_transaction(hfsmp);
710
711 hfs_unlock(cp);
712 //printf("hfs: flagged /%s with the fast-dev-candidate|auto-candidate flags\n", filename);
713
714
715 out:
716 if (fvp) {
717 vnode_put(fvp);
718 fvp = NULL;
719 }
720
721 if (dvp) {
722 vnode_put(dvp);
723 dvp = NULL;
724 }
725
726 return error;
727 }
728
729
730 static void
731 hfs_setup_default_cf_hotfiles(struct hfsmount *hfsmp)
732 {
733 const char *system_default_hotfiles[] = {
734 "usr",
735 "System",
736 "Applications",
737 "private/var/db/dyld"
738 };
739 int i;
740
741 for(i=0; i < (int)(sizeof(system_default_hotfiles)/sizeof(char *)); i++) {
742 flag_hotfile(hfsmp, system_default_hotfiles[i]);
743 }
744 }
745
746
747 #define NUM_FILE_RESET_IDS 4096 // so we allocate 16k to hold file-ids
748
749 static void
750 hfs_hotfile_reset(struct hfsmount *hfsmp)
751 {
752 CatalogKey * keyp;
753 CatalogRecord * datap;
754 u_int32_t dataSize;
755 BTScanState scanstate;
756 BTreeIterator * iterator = NULL;
757 FSBufferDescriptor record;
758 u_int32_t data;
759 u_int32_t cnid;
760 int error = 0;
761 uint32_t *fileids=NULL;
762 int cur_id_index = 0;
763
764 int cleared = 0; /* debug variables */
765 int filecount = 0;
766 int dircount = 0;
767
768 #if HFC_VERBOSE
769 printf("hfs: %s: %s\n", hfsmp->vcbVN, __FUNCTION__);
770 #endif
771
772 iterator = hfs_mallocz(sizeof(*iterator));
773
774 fileids = hfs_malloc(NUM_FILE_RESET_IDS * sizeof(uint32_t));
775
776 record.bufferAddress = &data;
777 record.itemSize = sizeof(u_int32_t);
778 record.itemCount = 1;
779
780 /*
781 * Get ready to scan the Catalog file.
782 */
783 error = BTScanInitialize(VTOF(HFSTOVCB(hfsmp)->catalogRefNum), 0, 0, 0,
784 kCatSearchBufferSize, &scanstate);
785 if (error) {
786 printf("hfs_hotfile_reset: err %d BTScanInit\n", error);
787 goto out;
788 }
789
790 /*
791 * Visit all the catalog btree leaf records, clearing any that have the
792 * HotFileCached bit set.
793 */
794 for (;;) {
795 error = BTScanNextRecord(&scanstate, 0, (void **)&keyp, (void **)&datap, &dataSize);
796 if (error) {
797 if (error == btNotFound)
798 error = 0;
799 else
800 printf("hfs_hotfile_reset: err %d BTScanNext\n", error);
801 break;
802 }
803
804 if (datap->recordType == kHFSPlusFolderRecord && (dataSize == sizeof(HFSPlusCatalogFolder))) {
805 HFSPlusCatalogFolder *dirp = (HFSPlusCatalogFolder *)datap;
806
807 dircount++;
808
809 if ((dirp->flags & (kHFSFastDevPinnedMask|kHFSDoNotFastDevPinMask|kHFSFastDevCandidateMask|kHFSAutoCandidateMask)) == 0) {
810 continue;
811 }
812
813 cnid = dirp->folderID;
814 } else if ((datap->recordType == kHFSPlusFileRecord) && (dataSize == sizeof(HFSPlusCatalogFile))) {
815 HFSPlusCatalogFile *filep = (HFSPlusCatalogFile *)datap;
816
817 filecount++;
818
819 /*
820 * If the file doesn't have any of the HotFileCached bits set, ignore it.
821 */
822 if ((filep->flags & (kHFSFastDevPinnedMask|kHFSDoNotFastDevPinMask|kHFSFastDevCandidateMask|kHFSAutoCandidateMask)) == 0) {
823 continue;
824 }
825
826 cnid = filep->fileID;
827 } else {
828 continue;
829 }
830
831 /* Skip over journal files. */
832 if (cnid == hfsmp->hfs_jnlfileid || cnid == hfsmp->hfs_jnlinfoblkid) {
833 continue;
834 }
835
836 //
837 // Just record the cnid of the file for now. We will modify it separately
838 // because we can't modify the catalog while we're scanning it.
839 //
840 fileids[cur_id_index++] = cnid;
841 if (cur_id_index >= NUM_FILE_RESET_IDS) {
842 //
843 // We're over the limit of file-ids so we have to terminate this
844 // scan, go modify all the catalog records, then restart the scan.
845 // This is required because it's not permissible to modify the
846 // catalog while scanning it.
847 //
848 (void) BTScanTerminate(&scanstate, &data, &data, &data);
849
850 reset_file_ids(hfsmp, fileids, cur_id_index);
851 cleared += cur_id_index;
852 cur_id_index = 0;
853
854 // restart the scan
855 error = BTScanInitialize(VTOF(HFSTOVCB(hfsmp)->catalogRefNum), 0, 0, 0,
856 kCatSearchBufferSize, &scanstate);
857 if (error) {
858 printf("hfs_hotfile_reset: err %d BTScanInit\n", error);
859 goto out;
860 }
861 continue;
862 }
863 }
864
865 if (cur_id_index) {
866 reset_file_ids(hfsmp, fileids, cur_id_index);
867 cleared += cur_id_index;
868 cur_id_index = 0;
869 }
870
871 printf("hfs: cleared HotFileCache related bits on %d files out of %d (dircount %d)\n", cleared, filecount, dircount);
872
873 (void) BTScanTerminate(&scanstate, &data, &data, &data);
874
875 out:
876 hfs_free(fileids, NUM_FILE_RESET_IDS * sizeof(uint32_t));
877 hfs_free(iterator, sizeof(*iterator));
878
879 //
880 // If the hotfile btree exists, delete it. We need to open
881 // it to be able to delete it because we need the hfc_filevp
882 // for deletion.
883 //
884 error = hfc_btree_open_ext(hfsmp, &hfsmp->hfc_filevp, 1);
885 if (!error) {
886 printf("hfs: hotfile_reset: deleting existing hotfile btree\n");
887 hfc_btree_delete(hfsmp);
888 }
889
890 if (hfsmp->hfc_filevp) {
891 (void) hfc_btree_close(hfsmp, hfsmp->hfc_filevp);
892 hfsmp->hfc_filevp = NULL;
893 }
894
895 hfsmp->hfs_hotfile_blk_adjust = 0;
896 hfsmp->hfs_hotfile_freeblks = hfsmp->hfs_hotfile_maxblks;
897 }
898
899
900 //
901 // This should ONLY be called by hfs_recording_init() and the special fsctl.
902 //
903 // We assume that the hotfile btree is already opened.
904 //
905 static int
906 hfs_hotfile_repin_files(struct hfsmount *hfsmp)
907 {
908 BTreeIterator * iterator = NULL;
909 HotFileKey * key;
910 filefork_t * filefork;
911 int error = 0;
912 int bt_op;
913 enum hfc_stage stage;
914 uint32_t pinned_blocks;
915 uint32_t num_files=0, nrsrc=0;
916 uint32_t total_pinned=0;
917
918 if (!(hfsmp->hfs_flags & HFS_CS_HOTFILE_PIN) || !hfsmp->hfc_filevp) {
919 //
920 // this is only meaningful if we're pinning hotfiles
921 // (as opposed to the regular form of hotfiles that
922 // get relocated to the hotfile zone)
923 //
924 return 0;
925 }
926
927 #if HFC_VERBOSE
928 printf("hfs: %s: %s\n", hfsmp->vcbVN, __FUNCTION__);
929 #endif
930
931 if (hfs_lock(VTOC(hfsmp->hfc_filevp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT) != 0) {
932 return (EPERM);
933 }
934
935
936 iterator = hfs_mallocz(sizeof(*iterator));
937
938 stage = hfsmp->hfc_stage;
939 hfsmp->hfc_stage = HFC_BUSY;
940
941 bt_op = kBTreeFirstRecord;
942
943 key = (HotFileKey*) &iterator->key;
944
945 filefork = VTOF(hfsmp->hfc_filevp);
946 int lockflags;
947
948 while (1) {
949
950 lockflags = 0;
951 /*
952 * Obtain the first record (ie the coldest one).
953 */
954 if (BTIterateRecord(filefork, bt_op, iterator, NULL, NULL) != 0) {
955 // no more records
956 error = 0;
957 break;
958 }
959 if (key->keyLength != HFC_KEYLENGTH) {
960 // printf("hfs: hotfiles_repin_files: invalid key length %d\n", key->keyLength);
961 error = EFTYPE;
962 break;
963 }
964 if (key->temperature == HFC_LOOKUPTAG) {
965 // ran into thread records in the hotfile btree
966 error = 0;
967 break;
968 }
969
970 //
971 // Just lookup the records in the catalog and pin the direct
972 // mapped extents. Faster than instantiating full vnodes
973 // (and thereby thrashing the system vnode cache).
974 //
975 struct cat_desc fdesc;
976 struct cat_attr attr;
977 struct cat_fork fork;
978 uint8_t forktype = 0;
979
980 lockflags = hfs_systemfile_lock(hfsmp, (SFL_CATALOG | SFL_EXTENTS), HFS_SHARED_LOCK);
981 /*
982 * Snoop the cnode hash to find out if the item we want is in-core already.
983 *
984 * We largely expect this function to fail (the items we want are probably not in the hash).
985 * we use the special variant which bails out as soon as it finds a vnode (even if it is
986 * marked as open-unlinked or actually removed on-disk. If we find a vnode, then we
987 * release the systemfile locks and go through the pin-vnode path instead.
988 */
989 if (hfs_chash_snoop (hfsmp, key->fileID, 1, NULL, NULL) == 0) {
990 pinned_blocks = 0;
991
992 /* unlock immediately and go through the in-core path */
993 hfs_systemfile_unlock(hfsmp, lockflags);
994 lockflags = 0;
995
996 error = hfs_getvnode_and_pin (hfsmp, key->fileID, &pinned_blocks);
997 if (error) {
998 /* if ENOENT, then it was deleted in the catalog. Remove from our hotfiles tracking */
999 if (error == ENOENT) {
1000 hfc_btree_delete_record(hfsmp, iterator, key);
1001 }
1002 /* other errors, just ignore and move on with life */
1003 }
1004 else { //!error
1005 total_pinned += pinned_blocks;
1006 num_files++;
1007 }
1008
1009 goto next;
1010 }
1011
1012 /* If we get here, we're still holding the systemfile locks */
1013 error = cat_idlookup(hfsmp, key->fileID, 1, 0, &fdesc, &attr, &fork);
1014 if (error) {
1015 //
1016 // this file system could have been mounted while booted from a
1017 // different partition and thus the hotfile btree would not have
1018 // been maintained. thus a file that was hotfile cached could
1019 // have been deleted while booted from a different partition which
1020 // means we need to delete it from the hotfile btree.
1021 //
1022 // block accounting is taken care of at the end: we re-assign
1023 // hfsmp->hfs_hotfile_freeblks based on how many blocks we actually
1024 // pinned.
1025 //
1026 hfc_btree_delete_record(hfsmp, iterator, key);
1027
1028 goto next;
1029 }
1030
1031 if (fork.cf_size == 0) {
1032 // hmmm, the data is probably in the resource fork (aka a compressed file)
1033 error = cat_idlookup(hfsmp, key->fileID, 1, 1, &fdesc, &attr, &fork);
1034 if (error) {
1035 hfc_btree_delete_record(hfsmp, iterator, key);
1036 goto next;
1037 }
1038 forktype = 0xff;
1039 nrsrc++;
1040 }
1041
1042 pinned_blocks = 0;
1043
1044 /* Can't release the catalog /extents lock yet, we may need to go find the overflow blocks */
1045 error = hfs_pin_extent_record (hfsmp, fork.cf_extents, &pinned_blocks);
1046 if (error) {
1047 goto next; //skip to next
1048 }
1049 /* add in the blocks from the inline 8 */
1050 total_pinned += pinned_blocks;
1051 pinned_blocks = 0;
1052
1053 /* Could this file have overflow extents? */
1054 if (fork.cf_extents[kHFSPlusExtentDensity-1].startBlock) {
1055 /* better pin them, too */
1056 error = hfs_pin_overflow_extents (hfsmp, key->fileID, forktype, &pinned_blocks);
1057 if (error) {
1058 /* If we fail to pin all of the overflow extents, then just skip to the next file */
1059 goto next;
1060 }
1061 }
1062
1063 num_files++;
1064 if (pinned_blocks) {
1065 /* now add in any overflow also */
1066 total_pinned += pinned_blocks;
1067 }
1068
1069 next:
1070 if (lockflags) {
1071 hfs_systemfile_unlock(hfsmp, lockflags);
1072 lockflags = 0;
1073 }
1074 bt_op = kBTreeNextRecord;
1075
1076 } /* end while */
1077
1078 #if HFC_VERBOSE
1079 printf("hfs: hotfiles_repin_files: re-pinned %d files (nrsrc %d, total pinned %d blks; freeblock %d, maxblocks %d, calculated free: %d)\n",
1080 num_files, nrsrc, total_pinned, hfsmp->hfs_hotfile_freeblks, hfsmp->hfs_hotfile_maxblks,
1081 hfsmp->hfs_hotfile_maxblks - total_pinned);
1082 #endif
1083 //
1084 // make sure this is accurate based on how many blocks we actually pinned
1085 //
1086 hfsmp->hfs_hotfile_freeblks = hfsmp->hfs_hotfile_maxblks - total_pinned;
1087
1088 hfs_unlock(VTOC(hfsmp->hfc_filevp));
1089
1090 hfs_free(iterator, sizeof(*iterator));
1091 hfsmp->hfc_stage = stage;
1092 wakeup((caddr_t)&hfsmp->hfc_stage);
1093 return (error);
1094 }
1095
1096 void
1097 hfs_repin_hotfiles(struct hfsmount *hfsmp)
1098 {
1099 int error, need_close;
1100
1101 lck_mtx_lock(&hfsmp->hfc_mutex);
1102
1103 if (hfsmp->hfc_filevp == NULL) {
1104 error = hfc_btree_open(hfsmp, &hfsmp->hfc_filevp);
1105 if (!error) {
1106 need_close = 1;
1107 } else {
1108 printf("hfs: failed to open the btree err=%d. Unable to re-pin hotfiles.\n", error);
1109 lck_mtx_unlock(&hfsmp->hfc_mutex);
1110 return;
1111 }
1112 } else {
1113 need_close = 0;
1114 }
1115
1116 hfs_pin_vnode(hfsmp, hfsmp->hfc_filevp, HFS_PIN_IT, NULL);
1117
1118 hfs_hotfile_repin_files(hfsmp);
1119
1120 if (need_close) {
1121 (void) hfc_btree_close(hfsmp, hfsmp->hfc_filevp);
1122 hfsmp->hfc_filevp = NULL;
1123 }
1124
1125 lck_mtx_unlock(&hfsmp->hfc_mutex);
1126 }
1127
1128 /*
1129 * For a given file ID, find and pin all of its overflow extents to the underlying CS
1130 * device. Assumes that the extents overflow b-tree is locked for the duration of this call.
1131 *
1132 * Emit the number of blocks pinned in output argument 'pinned'
1133 *
1134 * Return success or failure (errno) in return value.
1135 *
1136 */
1137 int hfs_pin_overflow_extents (struct hfsmount *hfsmp, uint32_t fileid,
1138 uint8_t forktype, uint32_t *pinned) {
1139
1140 struct BTreeIterator *ext_iter = NULL;
1141 ExtentKey *ext_key_ptr = NULL;
1142 ExtentRecord ext_data;
1143 FSBufferDescriptor btRecord;
1144 uint16_t btRecordSize;
1145 int error = 0;
1146
1147 uint32_t pinned_blocks = 0;
1148
1149
1150 ext_iter = hfs_mallocz(sizeof (*ext_iter));
1151
1152 BTInvalidateHint (ext_iter);
1153 ext_key_ptr = (ExtentKey*)&ext_iter->key;
1154 btRecord.bufferAddress = &ext_data;
1155 btRecord.itemCount = 1;
1156
1157 /*
1158 * This is like when you delete a file; we don't actually need most of the search machinery because
1159 * we are going to need all of the extent records that belong to this file (for a given fork type),
1160 * so we might as well use a straight-up iterator.
1161 *
1162 * Position the B-Tree iterator at the first record with this file ID
1163 */
1164 btRecord.itemSize = sizeof (HFSPlusExtentRecord);
1165 ext_key_ptr->hfsPlus.keyLength = kHFSPlusExtentKeyMaximumLength;
1166 ext_key_ptr->hfsPlus.forkType = forktype;
1167 ext_key_ptr->hfsPlus.pad = 0;
1168 ext_key_ptr->hfsPlus.fileID = fileid;
1169 ext_key_ptr->hfsPlus.startBlock = 0;
1170
1171 error = BTSearchRecord (VTOF(hfsmp->hfs_extents_vp), ext_iter, &btRecord, &btRecordSize, ext_iter);
1172 if (error == btNotFound) {
1173 /* empty b-tree, so that's ok. we'll fall out during error check below. */
1174 error = 0;
1175 }
1176
1177 while (1) {
1178 uint32_t found_fileid;
1179 uint32_t pblocks;
1180
1181 error = BTIterateRecord (VTOF(hfsmp->hfs_extents_vp), kBTreeNextRecord, ext_iter, &btRecord, &btRecordSize);
1182 if (error) {
1183 /* swallow it if it's btNotFound, otherwise just bail out */
1184 if (error == btNotFound)
1185 error = 0;
1186 break;
1187 }
1188
1189 found_fileid = ext_key_ptr->hfsPlus.fileID;
1190 /*
1191 * We only do one fork type at a time. So if either the fork-type doesn't
1192 * match what we are looking for (resource or data), OR the file id doesn't match
1193 * which indicates that there's nothing more with this file ID as the key, then bail out
1194 */
1195 if ((found_fileid != fileid) || (ext_key_ptr->hfsPlus.forkType != forktype)) {
1196 error = 0;
1197 break;
1198 }
1199
1200 /* Otherwise, we now have an extent record. Process and pin all of the file extents. */
1201 pblocks = 0;
1202 error = hfs_pin_extent_record (hfsmp, ext_data.hfsPlus, &pblocks);
1203
1204 if (error) {
1205 break;
1206 }
1207 pinned_blocks += pblocks;
1208
1209 /* if 8th extent is empty, then bail out */
1210 if (ext_data.hfsPlus[kHFSPlusExtentDensity-1].startBlock == 0) {
1211 error = 0;
1212 break;
1213 }
1214
1215 } // end extent-getting loop
1216
1217 /* dump the iterator */
1218 hfs_free(ext_iter, sizeof(*ext_iter));
1219
1220 if (error == 0) {
1221 /*
1222 * In the event that the file has no overflow extents, pinned_blocks
1223 * will never be updated, so we'll properly export 0 pinned blocks to caller
1224 */
1225 *pinned = pinned_blocks;
1226 }
1227
1228 return error;
1229
1230 }
1231
1232
1233 static int
1234 hfs_getvnode_and_pin (struct hfsmount *hfsmp, uint32_t fileid, uint32_t *pinned) {
1235 struct vnode *vp;
1236 int error = 0;
1237 *pinned = 0;
1238 uint32_t pblocks;
1239
1240 /*
1241 * Acquire the vnode for this file. This returns a locked cnode on success
1242 */
1243 error = hfs_vget(hfsmp, fileid, &vp, 0, 0);
1244 if (error) {
1245 /* It's possible the file was open-unlinked. In this case, we'll get ENOENT back. */
1246 return error;
1247 }
1248
1249 /*
1250 * Symlinks that may have been inserted into the hotfile zone during a previous OS are now stuck
1251 * here. We do not want to move them.
1252 */
1253 if (!vnode_isreg(vp)) {
1254 hfs_unlock(VTOC(vp));
1255 vnode_put(vp);
1256 return EPERM;
1257 }
1258
1259 if (!(VTOC(vp)->c_attr.ca_recflags & kHFSFastDevPinnedMask)) {
1260 hfs_unlock(VTOC(vp));
1261 vnode_put(vp);
1262 return EINVAL;
1263 }
1264
1265 error = hfs_pin_vnode(hfsmp, vp, HFS_PIN_IT, &pblocks);
1266 if (error == 0) {
1267 *pinned = pblocks;
1268 }
1269
1270 hfs_unlock(VTOC(vp));
1271 vnode_put(vp);
1272
1273 return error;
1274
1275 }
1276
1277 /*
1278 * Pins an HFS Extent record to the underlying CoreStorage. Assumes that Catalog & Extents overflow
1279 * B-trees are held locked, as needed.
1280 *
1281 * Returns the number of blocks pinned in the output argument 'pinned'
1282 *
1283 * Returns error status (0 || errno) in return value.
1284 */
1285 static int hfs_pin_extent_record (struct hfsmount *hfsmp, HFSPlusExtentRecord extents, uint32_t *pinned) {
1286 uint32_t pb = 0;
1287 int i;
1288 int error = 0;
1289
1290 if (pinned == NULL) {
1291 return EINVAL;
1292 }
1293 *pinned = 0;
1294
1295
1296
1297 /* iterate through the extents */
1298 for ( i = 0; i < kHFSPlusExtentDensity; i++) {
1299 if (extents[i].startBlock == 0) {
1300 break;
1301 }
1302
1303 error = hfs_pin_block_range (hfsmp, HFS_PIN_IT, extents[i].startBlock,
1304 extents[i].blockCount);
1305
1306 if (error) {
1307 break;
1308 }
1309 pb += extents[i].blockCount;
1310 }
1311
1312 *pinned = pb;
1313
1314 return error;
1315 }
1316
1317 /*
1318 * Consume an HFS Plus on-disk catalog record and pin its blocks
1319 * to the underlying CS devnode.
1320 *
1321 * NOTE: This is an important distinction!
1322 * This function takes in an HFSPlusCatalogFile* which is the actual
1323 * 200-some-odd-byte on-disk representation in the Catalog B-Tree (not
1324 * one of the run-time structs that we normally use.
1325 *
1326 * This assumes that the catalog and extents-overflow btrees
1327 * are locked, at least in shared mode
1328 */
1329 static int hfs_pin_catalog_rec (struct hfsmount *hfsmp, HFSPlusCatalogFile *cfp, int rsrc) {
1330 uint32_t pinned_blocks = 0;
1331 HFSPlusForkData *forkdata;
1332 int error = 0;
1333 uint8_t forktype = 0;
1334
1335 if (rsrc) {
1336 forkdata = &cfp->resourceFork;
1337 forktype = 0xff;
1338 }
1339 else {
1340 forkdata = &cfp->dataFork;
1341 }
1342
1343 uint32_t pblocks = 0;
1344
1345 /* iterate through the inline extents */
1346 error = hfs_pin_extent_record (hfsmp, forkdata->extents, &pblocks);
1347 if (error) {
1348 return error;
1349 }
1350
1351 pinned_blocks += pblocks;
1352 pblocks = 0;
1353
1354 /* it may have overflow extents */
1355 if (forkdata->extents[kHFSPlusExtentDensity-1].startBlock != 0) {
1356 error = hfs_pin_overflow_extents (hfsmp, cfp->fileID, forktype, &pblocks);
1357 }
1358 pinned_blocks += pblocks;
1359
1360 hfsmp->hfs_hotfile_freeblks -= pinned_blocks;
1361
1362 return error;
1363 }
1364
1365
1366 /*
1367 *
1368 */
1369 int
1370 hfs_recording_init(struct hfsmount *hfsmp)
1371 {
1372 CatalogKey * keyp;
1373 CatalogRecord * datap;
1374 u_int32_t dataSize;
1375 HFSPlusCatalogFile *filep;
1376 BTScanState scanstate;
1377 BTreeIterator * iterator = NULL;
1378 FSBufferDescriptor record;
1379 HotFileKey * key;
1380 filefork_t * filefork;
1381 u_int32_t data;
1382 struct cat_attr cattr;
1383 u_int32_t cnid;
1384 int error = 0;
1385 long starting_temp;
1386
1387 int started_tr = 0;
1388 int started_scan = 0;
1389
1390 int inserted = 0; /* debug variables */
1391 int filecount = 0;
1392 int uncacheable = 0;
1393
1394 /*
1395 * For now, only the boot volume is supported.
1396 */
1397 if ((vfs_flags(HFSTOVFS(hfsmp)) & MNT_ROOTFS) == 0) {
1398 hfsmp->hfc_stage = HFC_DISABLED;
1399 return (EPERM);
1400 }
1401
1402 /* We grab the HFC mutex even though we're not fully mounted yet, just for orderliness */
1403 lck_mtx_lock (&hfsmp->hfc_mutex);
1404
1405 /*
1406 * Tracking of hot files requires up-to-date access times.
1407 * So if access time updates are disabled, then we disable
1408 * hot files, too.
1409 */
1410 if (vfs_flags(HFSTOVFS(hfsmp)) & MNT_NOATIME) {
1411 hfsmp->hfc_stage = HFC_DISABLED;
1412 lck_mtx_unlock (&hfsmp->hfc_mutex);
1413 return EPERM;
1414 }
1415
1416 //
1417 // Check if we've been asked to suspend operation
1418 //
1419 cnid = GetFileInfo(HFSTOVCB(hfsmp), kRootDirID, ".hotfile-suspend", &cattr, NULL);
1420 if (cnid != 0) {
1421 printf("hfs: %s: %s: hotfiles explicitly disabled! remove /.hotfiles-suspend to re-enable\n", hfsmp->vcbVN, __FUNCTION__);
1422 hfsmp->hfc_stage = HFC_DISABLED;
1423 lck_mtx_unlock (&hfsmp->hfc_mutex);
1424 return EPERM;
1425 }
1426
1427 //
1428 // Check if we've been asked to reset our state.
1429 //
1430 cnid = GetFileInfo(HFSTOVCB(hfsmp), kRootDirID, ".hotfile-reset", &cattr, NULL);
1431 if (cnid != 0) {
1432 hfs_hotfile_reset(hfsmp);
1433 }
1434
1435 if (hfsmp->hfs_flags & HFS_CS_HOTFILE_PIN) {
1436 //
1437 // Cooperative Fusion (CF) systems use different constants
1438 // than traditional hotfile systems. These were picked after a bit of
1439 // experimentation - we can cache many more files on the
1440 // ssd in an CF system and we can do so more rapidly
1441 // so bump the limits considerably (and turn down the
1442 // duration so that it doesn't take weeks to adopt all
1443 // the files).
1444 //
1445 hfc_default_file_count = 20000;
1446 hfc_default_duration = 300; // 5min
1447 hfc_max_file_count = 50000;
1448 hfc_max_file_size = (512ULL * 1024ULL * 1024ULL);
1449 }
1450
1451 /*
1452 * If the Hot File btree exists then metadata zone is ready.
1453 */
1454 cnid = GetFileInfo(HFSTOVCB(hfsmp), kRootDirID, HFC_FILENAME, &cattr, NULL);
1455 if (cnid != 0 && S_ISREG(cattr.ca_mode)) {
1456 int recreate = 0;
1457
1458 if (hfsmp->hfc_stage == HFC_DISABLED)
1459 hfsmp->hfc_stage = HFC_IDLE;
1460 hfsmp->hfs_hotfile_freeblks = 0;
1461
1462 if ((hfsmp->hfs_flags & HFS_CS_HOTFILE_PIN) && cattr.ca_blocks > 0) {
1463 //
1464 // make sure the hotfile btree is pinned
1465 //
1466 error = hfc_btree_open(hfsmp, &hfsmp->hfc_filevp);
1467 if (!error) {
1468 /* XXX: must fix hfs_pin_vnode too */
1469 hfs_pin_vnode(hfsmp, hfsmp->hfc_filevp, HFS_PIN_IT, NULL);
1470
1471 } else {
1472 printf("hfs: failed to open the btree err=%d. Recreating hotfile btree.\n", error);
1473 recreate = 1;
1474 }
1475
1476 hfs_hotfile_repin_files(hfsmp);
1477
1478 if (hfsmp->hfc_filevp) {
1479 (void) hfc_btree_close(hfsmp, hfsmp->hfc_filevp);
1480 hfsmp->hfc_filevp = NULL;
1481 }
1482
1483 } else if (hfsmp->hfs_flags & HFS_CS_HOTFILE_PIN) {
1484 // hmmm, the hotfile btree is zero bytes long? how odd. let's recreate it.
1485 printf("hfs: hotfile btree is zero bytes long?! recreating it.\n");
1486 recreate = 1;
1487 }
1488
1489 if (!recreate) {
1490 /* don't forget to unlock the mutex */
1491 lck_mtx_unlock (&hfsmp->hfc_mutex);
1492 return (0);
1493 } else {
1494 //
1495 // open the hotfile btree file ignoring errors because
1496 // we need the vnode pointer for hfc_btree_delete() to
1497 // be able to do its work
1498 //
1499 error = hfc_btree_open_ext(hfsmp, &hfsmp->hfc_filevp, 1);
1500 if (!error) {
1501 // and delete it!
1502 error = hfc_btree_delete(hfsmp);
1503 (void) hfc_btree_close(hfsmp, hfsmp->hfc_filevp);
1504 hfsmp->hfc_filevp = NULL;
1505 }
1506 }
1507 }
1508
1509 printf("hfs: %s: %s: creating the hotfile btree\n", hfsmp->vcbVN, __FUNCTION__);
1510 if (hfs_start_transaction(hfsmp) != 0) {
1511 lck_mtx_unlock (&hfsmp->hfc_mutex);
1512 return EINVAL;
1513 }
1514
1515 /* B-tree creation must be journaled */
1516 started_tr = 1;
1517
1518 error = hfc_btree_create(hfsmp, HFSTOVCB(hfsmp)->blockSize, HFC_DEFAULT_FILE_COUNT);
1519 if (error) {
1520 #if HFC_VERBOSE
1521 printf("hfs: Error %d creating hot file b-tree on %s \n", error, hfsmp->vcbVN);
1522 #endif
1523 goto recording_init_out;
1524 }
1525
1526 hfs_end_transaction (hfsmp);
1527 started_tr = 0;
1528 /*
1529 * Do a journal flush + flush track cache. We have to ensure that the async I/Os have been issued to the media
1530 * before proceeding.
1531 */
1532 hfs_flush (hfsmp, HFS_FLUSH_FULL);
1533
1534 /* now re-start a new transaction */
1535 if (hfs_start_transaction (hfsmp) != 0) {
1536 lck_mtx_unlock (&hfsmp->hfc_mutex);
1537 return EINVAL;
1538 }
1539 started_tr = 1;
1540
1541 /*
1542 * Open the Hot File B-tree file for writing.
1543 */
1544 if (hfsmp->hfc_filevp)
1545 panic("hfs_recording_init: hfc_filevp exists (vp = %p)", hfsmp->hfc_filevp);
1546
1547 error = hfc_btree_open(hfsmp, &hfsmp->hfc_filevp);
1548 if (error) {
1549 #if HFC_VERBOSE
1550 printf("hfs: Error %d opening hot file b-tree on %s \n", error, hfsmp->vcbVN);
1551 #endif
1552 goto recording_init_out;
1553 }
1554
1555 /*
1556 * This function performs work similar to namei; we must NOT hold the catalog lock while
1557 * calling it. This will decorate catalog records as being pinning candidates. (no hotfiles work)
1558 */
1559 hfs_setup_default_cf_hotfiles(hfsmp);
1560
1561 /*
1562 * now grab the hotfiles b-tree vnode/cnode lock first, as it is not classified as a systemfile.
1563 */
1564 if (hfs_lock(VTOC(hfsmp->hfc_filevp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT) != 0) {
1565 error = EPERM;
1566 (void) hfc_btree_close(hfsmp, hfsmp->hfc_filevp);
1567 /* zero it out to avoid pinning later on */
1568 hfsmp->hfc_filevp = NULL;
1569 goto recording_init_out;
1570 }
1571
1572 iterator = hfs_mallocz(sizeof(*iterator));
1573
1574 key = (HotFileKey*) &iterator->key;
1575 key->keyLength = HFC_KEYLENGTH;
1576
1577 record.bufferAddress = &data;
1578 record.itemSize = sizeof(u_int32_t);
1579 record.itemCount = 1;
1580
1581 #if HFC_VERBOSE
1582 printf("hfs: Evaluating space for \"%s\" metadata zone... (freeblks %d)\n", HFSTOVCB(hfsmp)->vcbVN,
1583 hfsmp->hfs_hotfile_freeblks);
1584 #endif
1585
1586 /*
1587 * Get ready to scan the Catalog file. We explicitly do NOT grab the catalog lock because
1588 * we're fully single-threaded at the moment (by virtue of being called during mount()),
1589 * and if we have to grow the hotfile btree, then we would need to grab the catalog lock
1590 * and if we take a shared lock here, it would deadlock (see <rdar://problem/21486585>)
1591 *
1592 * We already started a transaction so we should already be holding the journal lock at this point.
1593 * Note that we have to hold the journal lock / start a txn BEFORE the systemfile locks.
1594 */
1595
1596 error = BTScanInitialize(VTOF(HFSTOVCB(hfsmp)->catalogRefNum), 0, 0, 0,
1597 kCatSearchBufferSize, &scanstate);
1598 if (error) {
1599 printf("hfs_recording_init: err %d BTScanInit\n", error);
1600
1601 /* drop the systemfile locks */
1602 hfs_unlock(VTOC(hfsmp->hfc_filevp));
1603
1604 (void) hfc_btree_close(hfsmp, hfsmp->hfc_filevp);
1605
1606 /* zero it out to avoid pinning */
1607 hfsmp->hfc_filevp = NULL;
1608 goto recording_init_out;
1609 }
1610
1611 started_scan = 1;
1612
1613 filefork = VTOF(hfsmp->hfc_filevp);
1614
1615 starting_temp = random() % HF_TEMP_RANGE;
1616
1617 /*
1618 * Visit all the catalog btree leaf records. We have to hold the catalog lock to do this.
1619 *
1620 * NOTE: The B-Tree scanner reads from the media itself. Under normal circumstances it would be
1621 * fine to simply use b-tree routines to read blocks that correspond to b-tree nodes, because the
1622 * block cache is going to ensure you always get the cached copy of a block (even if a journal
1623 * txn has modified one of those blocks). That is NOT true when
1624 * using the scanner. In particular, it will always read whatever is on-disk. So we have to ensure
1625 * that the journal has flushed and that the async I/Os to the metadata files have been issued.
1626 */
1627 for (;;) {
1628 error = BTScanNextRecord(&scanstate, 0, (void **)&keyp, (void **)&datap, &dataSize);
1629 if (error) {
1630 if (error == btNotFound)
1631 error = 0;
1632 else
1633 printf("hfs_recording_init: err %d BTScanNext\n", error);
1634 break;
1635 }
1636 if ((datap->recordType != kHFSPlusFileRecord) ||
1637 (dataSize != sizeof(HFSPlusCatalogFile))) {
1638 continue;
1639 }
1640 filep = (HFSPlusCatalogFile *)datap;
1641 filecount++;
1642
1643 if (hfsmp->hfs_flags & HFS_CS_HOTFILE_PIN) {
1644 if (filep->flags & kHFSDoNotFastDevPinMask) {
1645 uncacheable++;
1646 }
1647
1648 //
1649 // If the file does not have the FastDevPinnedMask set, we
1650 // can ignore it and just go to the next record.
1651 //
1652 if ((filep->flags & kHFSFastDevPinnedMask) == 0) {
1653 continue;
1654 }
1655 } else if (filep->dataFork.totalBlocks == 0) {
1656 continue;
1657 }
1658
1659 /*
1660 * On a regular hdd, any file that has blocks inside
1661 * the hot file space is recorded for later eviction.
1662 *
1663 * For now, resource forks are ignored.
1664 *
1665 * We don't do this on CF systems as there is no real
1666 * hotfile area - we just pin/unpin blocks belonging to
1667 * interesting files.
1668 */
1669 if (!(hfsmp->hfs_flags & HFS_CS_HOTFILE_PIN) && !hotextents(hfsmp, &filep->dataFork.extents[0])) {
1670 continue;
1671 }
1672 cnid = filep->fileID;
1673
1674 /* Skip over journal files and the hotfiles B-Tree file. */
1675 if (cnid == hfsmp->hfs_jnlfileid
1676 || cnid == hfsmp->hfs_jnlinfoblkid
1677 || cnid == VTOC(hfsmp->hfc_filevp)->c_fileid) {
1678 continue;
1679 }
1680 /*
1681 * XXX - need to skip quota files as well.
1682 */
1683
1684 uint32_t temp;
1685
1686 if (hfsmp->hfs_flags & HFS_CS_HOTFILE_PIN) {
1687 int rsrc = 0;
1688
1689 temp = (uint32_t)starting_temp++;
1690 if (filep->flags & kHFSAutoCandidateMask) {
1691 temp += MAX_NORMAL_TEMP;
1692 }
1693
1694 /* use the data fork by default */
1695 if (filep->dataFork.totalBlocks == 0) {
1696 /*
1697 * but if empty, switch to rsrc as its likely
1698 * a compressed file
1699 */
1700 rsrc = 1;
1701 }
1702
1703 error = hfs_pin_catalog_rec (hfsmp, filep, rsrc);
1704 if (error)
1705 break;
1706
1707 } else {
1708 temp = HFC_MINIMUM_TEMPERATURE;
1709 }
1710
1711 /* Insert a hot file entry. */
1712 key->keyLength = HFC_KEYLENGTH;
1713 key->temperature = temp;
1714 key->fileID = cnid;
1715 key->forkType = 0;
1716 data = 0x3f3f3f3f;
1717 error = BTInsertRecord(filefork, iterator, &record, record.itemSize);
1718 if (error) {
1719 printf("hfs_recording_init: BTInsertRecord failed %d (fileid %d)\n", error, key->fileID);
1720 error = MacToVFSError(error);
1721 break;
1722 }
1723
1724 /* Insert the corresponding thread record. */
1725 key->keyLength = HFC_KEYLENGTH;
1726 key->temperature = HFC_LOOKUPTAG;
1727 key->fileID = cnid;
1728 key->forkType = 0;
1729 data = temp;
1730 error = BTInsertRecord(filefork, iterator, &record, record.itemSize);
1731 if (error) {
1732 printf("hfs_recording_init: BTInsertRecord failed %d (fileid %d)\n", error, key->fileID);
1733 error = MacToVFSError(error);
1734 break;
1735 }
1736 inserted++;
1737 } // end catalog iteration loop
1738
1739 save_btree_user_info(hfsmp);
1740 (void) BTFlushPath(filefork);
1741
1742 recording_init_out:
1743
1744 /* Unlock first, then pin after releasing everything else */
1745 if (hfsmp->hfc_filevp) {
1746 hfs_unlock (VTOC(hfsmp->hfc_filevp));
1747 }
1748
1749 if (started_scan) {
1750 (void) BTScanTerminate (&scanstate, &data, &data, &data);
1751 }
1752
1753 if (started_tr) {
1754 hfs_end_transaction(hfsmp);
1755 }
1756
1757 #if HFC_VERBOSE
1758 printf("hfs: %d files identified out of %d (freeblocks is now: %d)\n", inserted, filecount, hfsmp->hfs_hotfile_freeblks);
1759 if (uncacheable) {
1760 printf("hfs: %d files were marked as uncacheable\n", uncacheable);
1761 }
1762 #endif
1763
1764 if (iterator)
1765 hfs_free(iterator, sizeof(*iterator));
1766
1767 if (hfsmp->hfc_filevp) {
1768 if (hfsmp->hfs_flags & HFS_CS_HOTFILE_PIN) {
1769 hfs_pin_vnode(hfsmp, hfsmp->hfc_filevp, HFS_PIN_IT, NULL);
1770 }
1771 (void) hfc_btree_close(hfsmp, hfsmp->hfc_filevp);
1772 hfsmp->hfc_filevp = NULL;
1773 }
1774
1775 if (error == 0)
1776 hfsmp->hfc_stage = HFC_IDLE;
1777
1778 /* Finally, unlock the HFC mutex */
1779 lck_mtx_unlock (&hfsmp->hfc_mutex);
1780
1781 return (error);
1782 }
1783
1784 /*
1785 * Use sync to perform ocassional background work.
1786 */
1787 int
1788 hfs_hotfilesync(struct hfsmount *hfsmp, vfs_context_t ctx)
1789 {
1790 if (hfsmp->hfc_stage) {
1791 struct timeval tv;
1792
1793 lck_mtx_lock(&hfsmp->hfc_mutex);
1794
1795 switch (hfsmp->hfc_stage) {
1796 case HFC_IDLE:
1797 (void) hfs_recording_start(hfsmp);
1798 break;
1799
1800 case HFC_RECORDING:
1801 microtime(&tv);
1802 if (tv.tv_sec > hfsmp->hfc_timeout)
1803 (void) hfs_recording_stop(hfsmp);
1804 break;
1805
1806 case HFC_EVICTION:
1807 (void) hotfiles_evict(hfsmp, ctx);
1808 break;
1809
1810 case HFC_ADOPTION:
1811 (void) hotfiles_adopt(hfsmp);
1812 break;
1813 default:
1814 break;
1815 }
1816
1817 lck_mtx_unlock(&hfsmp->hfc_mutex);
1818 }
1819 return (0);
1820 }
1821
1822 /*
1823 * Add a hot file to the recording list.
1824 *
1825 * This can happen when a hot file gets reclaimed or at the
1826 * end of the recording period for any active hot file.
1827 *
1828 * NOTE: Since both the data and resource fork can be hot,
1829 * there can be two entries for the same file id.
1830 *
1831 * Note: the cnode is locked on entry.
1832 */
1833 int
1834 hfs_addhotfile(struct vnode *vp)
1835 {
1836 hfsmount_t *hfsmp;
1837 int error;
1838
1839 hfsmp = VTOHFS(vp);
1840 if (hfsmp->hfc_stage != HFC_RECORDING)
1841 return (0);
1842
1843 lck_mtx_lock(&hfsmp->hfc_mutex);
1844 error = hfs_addhotfile_internal(vp);
1845 lck_mtx_unlock(&hfsmp->hfc_mutex);
1846 return (error);
1847 }
1848
1849 static int
1850 hf_ignore_process(const char *pname, size_t maxlen)
1851 {
1852 if ( strncmp(pname, "mds", maxlen) == 0
1853 || strncmp(pname, "mdworker", maxlen) == 0
1854 || strncmp(pname, "mds_stores", maxlen) == 0
1855 || strncmp(pname, "makewhatis", maxlen) == 0) {
1856 return 1;
1857 }
1858
1859 return 0;
1860
1861 }
1862
1863 static int
1864 hfs_addhotfile_internal(struct vnode *vp)
1865 {
1866 hotfile_data_t *hotdata;
1867 hotfile_entry_t *entry;
1868 hfsmount_t *hfsmp;
1869 cnode_t *cp;
1870 filefork_t *ffp;
1871 u_int32_t temperature;
1872
1873 hfsmp = VTOHFS(vp);
1874 if (hfsmp->hfc_stage != HFC_RECORDING)
1875 return (0);
1876
1877 /*
1878 * Only regular files are eligible for hotfiles addition.
1879 *
1880 * Symlinks were previously added to the list and may exist in
1881 * extant hotfiles regions, but no new ones will be added, and no
1882 * symlinks will now be relocated/evicted from the hotfiles region.
1883 */
1884 if (!vnode_isreg(vp) || vnode_issystem(vp)) {
1885 return (0);
1886 }
1887
1888 /* Skip resource forks for now. */
1889 if (VNODE_IS_RSRC(vp)) {
1890 return (0);
1891 }
1892 if ((hotdata = hfsmp->hfc_recdata) == NULL) {
1893 return (0);
1894 }
1895 ffp = VTOF(vp);
1896 cp = VTOC(vp);
1897
1898 if (cp->c_attr.ca_recflags & (kHFSFastDevPinnedMask|kHFSDoNotFastDevPinMask)) {
1899 // it's already a hotfile or can't be a hotfile...
1900 return 0;
1901 }
1902
1903 if (vnode_isdir(vp) || vnode_issystem(vp) || (cp->c_flag & (C_DELETED | C_NOEXISTS))) {
1904 return 0;
1905 }
1906
1907 if ((hfsmp->hfs_flags & HFS_CS_HOTFILE_PIN) && vnode_isfastdevicecandidate(vp)) {
1908 //
1909 // On cooperative fusion (CF) systems we have different criteria for whether something
1910 // can be pinned to the ssd.
1911 //
1912 if (cp->c_flag & (C_DELETED|C_NOEXISTS)) {
1913 //
1914 // dead files are definitely not worth caching
1915 //
1916 return 0;
1917 } else if (ffp->ff_blocks == 0 && !(cp->c_bsdflags & UF_COMPRESSED) && !(cp->c_attr.ca_recflags & kHFSFastDevCandidateMask)) {
1918 //
1919 // empty files aren't worth caching but compressed ones might be, as are
1920 // newly created files that live in WorthCaching directories...
1921 //
1922 return 0;
1923 }
1924
1925 char pname[256];
1926 pname[0] = '\0';
1927 proc_selfname(pname, sizeof(pname));
1928 if (hf_ignore_process(pname, sizeof(pname))) {
1929 // ignore i/o's from certain system daemons
1930 return 0;
1931 }
1932
1933 temperature = cp->c_fileid; // in memory we just keep it sorted by file-id
1934 } else {
1935 // the normal hard drive based hotfile checks
1936 if ((ffp->ff_bytesread == 0) ||
1937 (ffp->ff_blocks == 0) ||
1938 (ffp->ff_size == 0) ||
1939 (ffp->ff_blocks > hotdata->maxblocks) ||
1940 (cp->c_bsdflags & (UF_NODUMP | UF_COMPRESSED)) ||
1941 (cp->c_atime < hfsmp->hfc_timebase)) {
1942 return (0);
1943 }
1944
1945 temperature = ffp->ff_bytesread / ffp->ff_size;
1946 if (temperature < hotdata->threshold) {
1947 return (0);
1948 }
1949 }
1950
1951 /*
1952 * If there is room or this file is hotter than
1953 * the coldest one then add it to the list.
1954 *
1955 */
1956 if ((hotdata->activefiles < hfsmp->hfc_maxfiles) ||
1957 (hotdata->coldest == NULL) ||
1958 (temperature >= hotdata->coldest->temperature)) {
1959 ++hotdata->refcount;
1960 entry = hf_getnewentry(hotdata);
1961 entry->temperature = temperature;
1962 entry->fileid = cp->c_fileid;
1963 //
1964 // if ffp->ff_blocks is zero, it might be compressed so make sure we record
1965 // that there's at least one block.
1966 //
1967 entry->blocks = ffp->ff_blocks ? ffp->ff_blocks : 1;
1968 if (hf_insert(hotdata, entry) == EEXIST) {
1969 // entry is already present, don't need to add it again
1970 entry->right = hotdata->freelist;
1971 hotdata->freelist = entry;
1972 }
1973 --hotdata->refcount;
1974 }
1975
1976 return (0);
1977 }
1978
1979 /*
1980 * Remove a hot file from the recording list.
1981 *
1982 * This can happen when a hot file becomes
1983 * an active vnode (active hot files are
1984 * not kept in the recording list until the
1985 * end of the recording period).
1986 *
1987 * Note: the cnode is locked on entry.
1988 */
1989 int
1990 hfs_removehotfile(struct vnode *vp)
1991 {
1992 hotfile_data_t *hotdata;
1993 hfsmount_t *hfsmp;
1994 cnode_t *cp;
1995 filefork_t *ffp;
1996 u_int32_t temperature;
1997
1998 hfsmp = VTOHFS(vp);
1999 if (hfsmp->hfc_stage != HFC_RECORDING)
2000 return (0);
2001
2002 if ((!vnode_isreg(vp)) || vnode_issystem(vp)) {
2003 return (0);
2004 }
2005
2006 ffp = VTOF(vp);
2007 cp = VTOC(vp);
2008
2009 if ((ffp->ff_bytesread == 0) || (ffp->ff_blocks == 0) ||
2010 (ffp->ff_size == 0) || (cp->c_atime < hfsmp->hfc_timebase)) {
2011 return (0);
2012 }
2013
2014 lck_mtx_lock(&hfsmp->hfc_mutex);
2015 if (hfsmp->hfc_stage != HFC_RECORDING)
2016 goto out;
2017 if ((hotdata = hfsmp->hfc_recdata) == NULL)
2018 goto out;
2019
2020 temperature = ffp->ff_bytesread / ffp->ff_size;
2021 if (temperature < hotdata->threshold)
2022 goto out;
2023
2024 if (hotdata->coldest && (temperature >= hotdata->coldest->temperature)) {
2025 ++hotdata->refcount;
2026 hf_delete(hotdata, VTOC(vp)->c_fileid, temperature);
2027 --hotdata->refcount;
2028 }
2029 out:
2030 lck_mtx_unlock(&hfsmp->hfc_mutex);
2031 return (0);
2032 }
2033
2034 int
2035 hfs_hotfile_deleted(__unused struct vnode *vp)
2036 {
2037 #if 1
2038 return 0;
2039 #else
2040 //
2041 // XXXdbg - this code, while it would work, would introduce a huge inefficiency
2042 // to deleting files as the way it's written would require us to open
2043 // the hotfile btree on every open, delete two records in it and then
2044 // close the hotfile btree (which involves more writes).
2045 //
2046 // We actually can be lazy about deleting hotfile records for files
2047 // that get deleted. When it's time to evict things, if we encounter
2048 // a record that references a dead file (i.e. a fileid which no
2049 // longer exists), the eviction code will remove the records. Likewise
2050 // the code that scans the HotFile B-Tree at boot time to re-pin files
2051 // will remove dead records.
2052 //
2053
2054 hotfile_data_t *hotdata;
2055 hfsmount_t *hfsmp;
2056 cnode_t *cp;
2057 filefork_t *filefork;
2058 u_int32_t temperature;
2059 BTreeIterator * iterator = NULL;
2060 FSBufferDescriptor record;
2061 HotFileKey *key;
2062 u_int32_t data;
2063 int error=0;
2064
2065 cp = VTOC(vp);
2066 if (cp == NULL || !(cp->c_attr.ca_recflags & kHFSFastDevPinnedMask)) {
2067 return 0;
2068 }
2069
2070 hfsmp = VTOHFS(vp);
2071 if (!(hfsmp->hfs_flags & HFS_CS_HOTFILE_PIN)) {
2072 return 0;
2073 }
2074
2075 if (hfc_btree_open(hfsmp, &hfsmp->hfc_filevp) != 0 || hfsmp->hfc_filevp == NULL) {
2076 // either there is no hotfile info or it's damaged
2077 return EINVAL;
2078 }
2079
2080 filefork = VTOF(hfsmp->hfc_filevp);
2081 if (filefork == NULL) {
2082 return 0;
2083 }
2084
2085 iterator = hfs_mallocz(sizeof(*iterator));
2086
2087 key = (HotFileKey*) &iterator->key;
2088
2089 record.bufferAddress = &data;
2090 record.itemSize = sizeof(u_int32_t);
2091 record.itemCount = 1;
2092
2093 key->keyLength = HFC_KEYLENGTH;
2094 key->temperature = HFC_LOOKUPTAG;
2095 key->fileID = cp->c_fileid;
2096 key->forkType = 0;
2097
2098 lck_mtx_lock(&hfsmp->hfc_mutex);
2099 (void) BTInvalidateHint(iterator);
2100 if (BTSearchRecord(filefork, iterator, &record, NULL, iterator) == 0) {
2101 temperature = key->temperature;
2102 hfc_btree_delete_record(hfsmp, iterator, key);
2103 } else {
2104 //printf("hfs: hotfile_deleted: did not find fileid %d\n", cp->c_fileid);
2105 error = ENOENT;
2106 }
2107
2108 if ((hotdata = hfsmp->hfc_recdata) != NULL) {
2109 // just in case, also make sure it's removed from the in-memory list as well
2110 ++hotdata->refcount;
2111 hf_delete(hotdata, cp->c_fileid, cp->c_fileid);
2112 --hotdata->refcount;
2113 }
2114
2115 lck_mtx_unlock(&hfsmp->hfc_mutex);
2116 hfs_free(iterator, sizeof(*iterator));
2117
2118 hfc_btree_close(hfsmp, hfsmp->hfc_filevp);
2119
2120 return error;
2121 #endif
2122 }
2123
2124 int
2125 hfs_hotfile_adjust_blocks(struct vnode *vp, int64_t num_blocks)
2126 {
2127 hfsmount_t *hfsmp;
2128
2129 if (vp == NULL) {
2130 return 0;
2131 }
2132
2133 hfsmp = VTOHFS(vp);
2134
2135 if (!(hfsmp->hfs_flags & HFS_CS_HOTFILE_PIN) || num_blocks == 0 || vp == NULL) {
2136 return 0;
2137 }
2138
2139 //
2140 // if file is not HotFileCached or it has the CanNotHotFile cache
2141 // bit set then there is nothing to do
2142 //
2143 if (!(VTOC(vp)->c_attr.ca_recflags & kHFSFastDevPinnedMask) || (VTOC(vp)->c_attr.ca_recflags & kHFSDoNotFastDevPinMask)) {
2144 // it's not a hot file or can't be one so don't bother tracking
2145 return 0;
2146 }
2147
2148 OSAddAtomic(num_blocks, &hfsmp->hfs_hotfile_blk_adjust);
2149
2150 return (0);
2151 }
2152
2153 //
2154 // Assumes hfsmp->hfc_mutex is LOCKED
2155 //
2156 static int
2157 hfs_hotfile_cur_freeblks(hfsmount_t *hfsmp)
2158 {
2159 if (hfsmp->hfc_stage < HFC_IDLE) {
2160 return 0;
2161 }
2162
2163 int cur_blk_adjust = hfsmp->hfs_hotfile_blk_adjust; // snap a copy of this value
2164
2165 if (cur_blk_adjust) {
2166 OSAddAtomic(-cur_blk_adjust, &hfsmp->hfs_hotfile_blk_adjust);
2167 hfsmp->hfs_hotfile_freeblks += cur_blk_adjust;
2168 }
2169
2170 return hfsmp->hfs_hotfile_freeblks;
2171 }
2172
2173
2174 /*
2175 *========================================================================
2176 * HOT FILE MAINTENANCE ROUTINES
2177 *========================================================================
2178 */
2179
2180 static int
2181 hotfiles_collect_callback(struct vnode *vp, __unused void *cargs)
2182 {
2183 if ((vnode_isreg(vp)) && !vnode_issystem(vp))
2184 (void) hfs_addhotfile_internal(vp);
2185
2186 return (VNODE_RETURNED);
2187 }
2188
2189 /*
2190 * Add all active hot files to the recording list.
2191 */
2192 static int
2193 hotfiles_collect(struct hfsmount *hfsmp)
2194 {
2195 struct mount *mp = HFSTOVFS(hfsmp);
2196
2197 if (vfs_busy(mp, LK_NOWAIT))
2198 return (0);
2199
2200 /*
2201 * hotfiles_collect_callback will be called for each vnode
2202 * hung off of this mount point
2203 * the vnode will be
2204 * properly referenced and unreferenced around the callback
2205 */
2206 vnode_iterate(mp, 0, hotfiles_collect_callback, (void *)NULL);
2207
2208 vfs_unbusy(mp);
2209
2210 return (0);
2211 }
2212
2213
2214 /*
2215 * Update the data of a btree record
2216 * This is called from within BTUpdateRecord.
2217 */
2218 static int
2219 update_callback(const HotFileKey *key, u_int32_t *data, u_int32_t *state)
2220 {
2221 if (key->temperature == HFC_LOOKUPTAG)
2222 *data = *state;
2223 return (0);
2224 }
2225
2226 /*
2227 * Identify files already in hot area.
2228 */
2229 static int
2230 hotfiles_refine(struct hfsmount *hfsmp)
2231 {
2232 BTreeIterator * iterator = NULL;
2233 struct mount *mp;
2234 filefork_t * filefork;
2235 hotfilelist_t *listp;
2236 FSBufferDescriptor record;
2237 HotFileKey * key;
2238 u_int32_t data;
2239 int i;
2240 int error = 0;
2241
2242 if ((listp = hfsmp->hfc_filelist) == NULL)
2243 return (0);
2244
2245 if (hfsmp->hfs_flags & HFS_CS_HOTFILE_PIN) {
2246 // on ssd's we don't refine the temperature since the
2247 // replacement algorithm is simply random
2248 return 0;
2249 }
2250
2251 mp = HFSTOVFS(hfsmp);
2252
2253 iterator = hfs_mallocz(sizeof(*iterator));
2254
2255 key = (HotFileKey*) &iterator->key;
2256
2257 record.bufferAddress = &data;
2258 record.itemSize = sizeof(u_int32_t);
2259 record.itemCount = 1;
2260
2261 if (hfs_start_transaction(hfsmp) != 0) {
2262 error = EINVAL;
2263 goto out;
2264 }
2265 if (hfs_lock(VTOC(hfsmp->hfc_filevp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT) != 0) {
2266 error = EPERM;
2267 goto out1;
2268 }
2269 filefork = VTOF(hfsmp->hfc_filevp);
2270
2271 for (i = 0; i < listp->hfl_count; ++i) {
2272 /*
2273 * Check if entry (thread) is already in hot area.
2274 */
2275 key->keyLength = HFC_KEYLENGTH;
2276 key->temperature = HFC_LOOKUPTAG;
2277 key->fileID = listp->hfl_hotfile[i].hf_fileid;
2278 key->forkType = 0;
2279 (void) BTInvalidateHint(iterator);
2280 if (BTSearchRecord(filefork, iterator, &record, NULL, iterator) != 0) {
2281 continue; /* not in hot area, so skip */
2282 }
2283
2284 /*
2285 * Update thread entry with latest temperature.
2286 */
2287 error = BTUpdateRecord(filefork, iterator,
2288 (IterateCallBackProcPtr)update_callback,
2289 &listp->hfl_hotfile[i].hf_temperature);
2290 if (error) {
2291 printf("hfs: hotfiles_refine: BTUpdateRecord failed %d (file %d)\n", error, key->fileID);
2292 error = MacToVFSError(error);
2293 // break;
2294 }
2295 /*
2296 * Re-key entry with latest temperature.
2297 */
2298 key->keyLength = HFC_KEYLENGTH;
2299 key->temperature = data;
2300 key->fileID = listp->hfl_hotfile[i].hf_fileid;
2301 key->forkType = 0;
2302 /* Pick up record data. */
2303 (void) BTInvalidateHint(iterator);
2304 (void) BTSearchRecord(filefork, iterator, &record, NULL, iterator);
2305 error = BTDeleteRecord(filefork, iterator);
2306 if (error) {
2307 printf("hfs: hotfiles_refine: BTDeleteRecord failed %d (file %d)\n", error, key->fileID);
2308 error = MacToVFSError(error);
2309 break;
2310 }
2311 key->keyLength = HFC_KEYLENGTH;
2312 key->temperature = listp->hfl_hotfile[i].hf_temperature;
2313 key->fileID = listp->hfl_hotfile[i].hf_fileid;
2314 key->forkType = 0;
2315 error = BTInsertRecord(filefork, iterator, &record, record.itemSize);
2316 if (error) {
2317 printf("hfs: hotfiles_refine: BTInsertRecord failed %d (file %d)\n", error, key->fileID);
2318 error = MacToVFSError(error);
2319 break;
2320 }
2321 /*
2322 * Invalidate this entry in the list.
2323 */
2324 listp->hfl_hotfile[i].hf_temperature = 0;
2325 listp->hfl_totalblocks -= listp->hfl_hotfile[i].hf_blocks;
2326
2327 } /* end for */
2328
2329 (void) BTFlushPath(filefork);
2330 hfs_unlock(VTOC(hfsmp->hfc_filevp));
2331
2332 out1:
2333 hfs_end_transaction(hfsmp);
2334 out:
2335 if (iterator)
2336 hfs_free(iterator, sizeof(*iterator));
2337 return (error);
2338 }
2339
2340 /*
2341 * Move new hot files into hot area.
2342 *
2343 * Requires that the hfc_mutex be held.
2344 */
2345 static int
2346 hotfiles_adopt(struct hfsmount *hfsmp)
2347 {
2348 BTreeIterator * iterator = NULL;
2349 struct vnode *vp;
2350 filefork_t * filefork;
2351 hotfilelist_t *listp;
2352 FSBufferDescriptor record;
2353 HotFileKey * key;
2354 u_int32_t data;
2355 enum hfc_stage stage;
2356 int fileblocks;
2357 int blksmoved;
2358 int i;
2359 int last;
2360 int error = 0;
2361 int startedtrans = 0;
2362 //
2363 // all files in a given adoption phase have a temperature
2364 // that starts at a random value and then increases linearly.
2365 // the idea is that during eviction, files that were adopted
2366 // together will be evicted together
2367 //
2368 long starting_temp = random() % HF_TEMP_RANGE;
2369 long temp_adjust = 0;
2370
2371 if ((listp = hfsmp->hfc_filelist) == NULL)
2372 return (0);
2373
2374 if (hfsmp->hfc_stage != HFC_ADOPTION) {
2375 return (EBUSY);
2376 }
2377 if (hfs_lock(VTOC(hfsmp->hfc_filevp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT) != 0) {
2378 return (EPERM);
2379 }
2380
2381 iterator = hfs_mallocz(sizeof(*iterator));
2382
2383 #if HFC_VERBOSE
2384 printf("hfs:%s: hotfiles_adopt: (hfl_next: %d, hotfile start/end block: %d - %d; max/free: %d/%d; maxfiles: %d)\n",
2385 hfsmp->vcbVN,
2386 listp->hfl_next,
2387 hfsmp->hfs_hotfile_start, hfsmp->hfs_hotfile_end,
2388 hfsmp->hfs_hotfile_maxblks, hfsmp->hfs_hotfile_freeblks, hfsmp->hfc_maxfiles);
2389 #endif
2390
2391 stage = hfsmp->hfc_stage;
2392 hfsmp->hfc_stage = HFC_BUSY;
2393
2394 blksmoved = 0;
2395 last = listp->hfl_next + HFC_FILESPERSYNC;
2396 if (last > listp->hfl_count)
2397 last = listp->hfl_count;
2398
2399 key = (HotFileKey*) &iterator->key;
2400 key->keyLength = HFC_KEYLENGTH;
2401
2402 record.bufferAddress = &data;
2403 record.itemSize = sizeof(u_int32_t);
2404 record.itemCount = 1;
2405
2406 filefork = VTOF(hfsmp->hfc_filevp);
2407
2408 for (i = listp->hfl_next; (i < last) && (blksmoved < HFC_BLKSPERSYNC); ++i) {
2409 /*
2410 * Skip entries that aren't going to work.
2411 */
2412 if (listp->hfl_hotfile[i].hf_temperature == 0) {
2413 //printf("hfs: zero temp on file-id %d\n", listp->hfl_hotfile[i].hf_fileid);
2414 listp->hfl_next++;
2415 continue;
2416 }
2417 if (listp->hfl_hotfile[i].hf_fileid == VTOC(hfsmp->hfc_filevp)->c_fileid) {
2418 //printf("hfs: cannot adopt the hotfile b-tree itself! (file-id %d)\n", listp->hfl_hotfile[i].hf_fileid);
2419 listp->hfl_next++;
2420 continue;
2421 }
2422 if (listp->hfl_hotfile[i].hf_fileid < kHFSFirstUserCatalogNodeID) {
2423 //printf("hfs: cannot adopt system files (file-id %d)\n", listp->hfl_hotfile[i].hf_fileid);
2424 listp->hfl_next++;
2425 continue;
2426 }
2427
2428 /*
2429 * Acquire a vnode for this file.
2430 */
2431 error = hfs_vget(hfsmp, listp->hfl_hotfile[i].hf_fileid, &vp, 0, 0);
2432 if (error) {
2433 //printf("failed to get fileid %d (err %d)\n", listp->hfl_hotfile[i].hf_fileid, error);
2434 if (error == ENOENT) {
2435 error = 0;
2436 listp->hfl_next++;
2437 continue; /* stale entry, go to next */
2438 }
2439 break;
2440 }
2441
2442 //printf("hfs: examining hotfile entry w/fileid %d, temp %d, blocks %d (HotFileCached: %s)\n",
2443 // listp->hfl_hotfile[i].hf_fileid, listp->hfl_hotfile[i].hf_temperature,
2444 // listp->hfl_hotfile[i].hf_blocks,
2445 // (VTOC(vp)->c_attr.ca_recflags & kHFSFastDevPinnedMask) ? "YES" : "NO");
2446
2447 if (!vnode_isreg(vp)) {
2448 /* Symlinks are ineligible for adoption into the hotfile zone. */
2449 //printf("hfs: hotfiles_adopt: huh, not a file %d (%d)\n", listp->hfl_hotfile[i].hf_fileid, VTOC(vp)->c_cnid);
2450 hfs_unlock(VTOC(vp));
2451 vnode_put(vp);
2452 listp->hfl_hotfile[i].hf_temperature = 0;
2453 listp->hfl_next++;
2454 continue; /* stale entry, go to next */
2455 }
2456 if ( (VTOC(vp)->c_flag & (C_DELETED | C_NOEXISTS))
2457 || (!(hfsmp->hfs_flags & HFS_CS_HOTFILE_PIN) && hotextents(hfsmp, &VTOF(vp)->ff_extents[0]))
2458 || (VTOC(vp)->c_attr.ca_recflags & (kHFSFastDevPinnedMask|kHFSDoNotFastDevPinMask))) {
2459 hfs_unlock(VTOC(vp));
2460 vnode_put(vp);
2461 listp->hfl_hotfile[i].hf_temperature = 0;
2462 listp->hfl_next++;
2463 listp->hfl_totalblocks -= listp->hfl_hotfile[i].hf_blocks;
2464 continue; /* stale entry, go to next */
2465 }
2466
2467 fileblocks = VTOF(vp)->ff_blocks;
2468
2469 //
2470 // for CF, if the file is empty (and not compressed) or it is too large,
2471 // do not try to pin it. (note: if fileblocks == 0 but the file is marked
2472 // as compressed, we may still be able to cache it).
2473 //
2474 if ((hfsmp->hfs_flags & HFS_CS_HOTFILE_PIN) &&
2475 ((fileblocks == 0 && !(VTOC(vp)->c_bsdflags & UF_COMPRESSED)) ||
2476 (unsigned int)fileblocks > (HFC_MAXIMUM_FILESIZE / (uint64_t)HFSTOVCB(hfsmp)->blockSize))) {
2477 // don't try to cache something too large or that's zero-bytes
2478
2479 vnode_clearfastdevicecandidate(vp); // turn off the fast-dev-candidate flag so we don't keep trying to cache it.
2480
2481 hfs_unlock(VTOC(vp));
2482 vnode_put(vp);
2483 listp->hfl_hotfile[i].hf_temperature = 0;
2484 listp->hfl_next++;
2485 listp->hfl_totalblocks -= listp->hfl_hotfile[i].hf_blocks;
2486 continue; /* entry is too big, just carry on with the next guy */
2487 }
2488
2489 //
2490 // If a file is not an autocandidate (i.e. it's a user-tagged file desirous of
2491 // being hotfile cached) but it is already bigger than 4 megs, don't bother
2492 // hotfile caching it. Note that if a user tagged file starts small, gets
2493 // adopted and then grows over time we will allow it to grow bigger than 4 megs
2494 // which is intentional for things like the Mail or Photos database files which
2495 // grow slowly over time and benefit from being on the FastDevice.
2496 //
2497 if ((hfsmp->hfs_flags & HFS_CS_HOTFILE_PIN) &&
2498 !(VTOC(vp)->c_attr.ca_recflags & kHFSAutoCandidateMask) &&
2499 (VTOC(vp)->c_attr.ca_recflags & kHFSFastDevCandidateMask) &&
2500 (unsigned int)fileblocks > ((4*1024*1024) / (uint64_t)HFSTOVCB(hfsmp)->blockSize)) {
2501
2502 vnode_clearfastdevicecandidate(vp); // turn off the fast-dev-candidate flag so we don't keep trying to cache it.
2503
2504 hfs_unlock(VTOC(vp));
2505 vnode_put(vp);
2506 listp->hfl_hotfile[i].hf_temperature = 0;
2507 listp->hfl_next++;
2508 listp->hfl_totalblocks -= listp->hfl_hotfile[i].hf_blocks;
2509 continue; /* entry is too big, just carry on with the next guy */
2510 }
2511
2512 if (fileblocks > hfs_hotfile_cur_freeblks(hfsmp)) {
2513 //
2514 // No room for this file. Although eviction should have made space
2515 // it's best that we check here as well since writes to existing
2516 // hotfiles may have eaten up space since we performed eviction
2517 //
2518 hfs_unlock(VTOC(vp));
2519 vnode_put(vp);
2520 listp->hfl_next++;
2521 listp->hfl_totalblocks -= fileblocks;
2522 continue; /* entry too big, go to next */
2523 }
2524
2525 if ((blksmoved > 0) &&
2526 (blksmoved + fileblocks) > HFC_BLKSPERSYNC) {
2527 //
2528 // we've done enough work, let's be nice to the system and
2529 // stop until the next iteration
2530 //
2531 hfs_unlock(VTOC(vp));
2532 vnode_put(vp);
2533 break; /* adopt this entry the next time around */
2534 }
2535
2536 //
2537 // The size of data for a hot file record is 4 bytes. The data
2538 // stored in hot file record is not really meaningful. However
2539 // to aid debugging, we store first four bytes of the file name
2540 // or the ASCII text "????"
2541 //
2542 if (VTOC(vp)->c_desc.cd_nameptr && (VTOC(vp)->c_desc.cd_namelen > 0)) {
2543 size_t max_len;
2544
2545 max_len = sizeof(u_int32_t);
2546 if (max_len > (unsigned)VTOC(vp)->c_desc.cd_namelen)
2547 max_len = VTOC(vp)->c_desc.cd_namelen;
2548
2549 memcpy(&data, VTOC(vp)->c_desc.cd_nameptr, max_len);
2550 } else
2551 data = 0x3f3f3f3f;
2552
2553
2554 if (hfsmp->hfs_flags & HFS_CS_HOTFILE_PIN) {
2555 //
2556 // For CF we pin the blocks belonging to the file
2557 // to the "fast" (aka ssd) media
2558 //
2559 uint32_t pinned_blocks;
2560
2561 if (vnode_isautocandidate(vp)) {
2562 VTOC(vp)->c_attr.ca_recflags |= kHFSAutoCandidateMask;
2563 }
2564 if (VTOC(vp)->c_attr.ca_recflags & kHFSAutoCandidateMask) {
2565 //
2566 // this moves auto-cached files to the higher tier
2567 // of "temperatures" which means they are less likely
2568 // to get evicted (user selected hotfiles will get
2569 // evicted first in the theory that they change more
2570 // frequently compared to system files)
2571 //
2572 temp_adjust = MAX_NORMAL_TEMP;
2573 } else {
2574 temp_adjust = 0;
2575 }
2576
2577 hfs_unlock(VTOC(vp)); // don't need an exclusive lock for this
2578 hfs_lock(VTOC(vp), HFS_SHARED_LOCK, HFS_LOCK_ALLOW_NOEXISTS);
2579
2580 error = hfs_pin_vnode(hfsmp, vp, HFS_PIN_IT, &pinned_blocks);
2581
2582 fileblocks = pinned_blocks;
2583
2584 // go back to an exclusive lock since we're going to modify the cnode again
2585 hfs_unlock(VTOC(vp));
2586 hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_ALLOW_NOEXISTS);
2587 } else {
2588 //
2589 // Old style hotfiles moves the data to the center (aka "hot")
2590 // region of the disk
2591 //
2592 error = hfs_relocate(vp, hfsmp->hfs_hotfile_start, kauth_cred_get(), current_proc());
2593 }
2594
2595 if (!error) {
2596 VTOC(vp)->c_attr.ca_recflags |= kHFSFastDevPinnedMask;
2597 VTOC(vp)->c_flag |= C_MODIFIED;
2598 } else if ((hfsmp->hfs_flags & HFS_CS_HOTFILE_PIN) && error == EALREADY) {
2599 //
2600 // If hfs_pin_vnode() returned EALREADY then this file is not
2601 // ever able to be hotfile cached the normal way. This can
2602 // happen with compressed files which have their data stored
2603 // in an extended attribute. We flag them so that we won't
2604 // bother to try and hotfile cache them again the next time
2605 // they're read.
2606 //
2607 VTOC(vp)->c_attr.ca_recflags |= kHFSDoNotFastDevPinMask;
2608 VTOC(vp)->c_flag |= C_MODIFIED;
2609 }
2610
2611 hfs_unlock(VTOC(vp));
2612 vnode_put(vp);
2613 if (error) {
2614 #if HFC_VERBOSE
2615 if (error != EALREADY) {
2616 printf("hfs: hotfiles_adopt: could not relocate file %d (err %d)\n", listp->hfl_hotfile[i].hf_fileid, error);
2617 }
2618 #endif
2619
2620 if (last < listp->hfl_count) {
2621 last++;
2622 }
2623 /* Move on to next item. */
2624 listp->hfl_next++;
2625 continue;
2626 }
2627 /* Keep hot file free space current. */
2628 hfsmp->hfs_hotfile_freeblks -= fileblocks;
2629 listp->hfl_totalblocks -= fileblocks;
2630
2631 /* Insert hot file entry */
2632 key->keyLength = HFC_KEYLENGTH;
2633
2634 if (hfsmp->hfs_flags & HFS_CS_HOTFILE_PIN) {
2635 //
2636 // The "temperature" for a CF hotfile is simply a random
2637 // number that we sequentially increment for each file in
2638 // the set of files we're currently adopting. This has the
2639 // nice property that all of the files we pin to the ssd
2640 // in the current phase will sort together in the hotfile
2641 // btree. When eviction time comes we will evict them
2642 // together as well. This gives the eviction phase temporal
2643 // locality - things written together get evicted together
2644 // which is what ssd's like.
2645 //
2646 listp->hfl_hotfile[i].hf_temperature = (uint32_t)temp_adjust + starting_temp++;
2647 }
2648
2649 key->temperature = listp->hfl_hotfile[i].hf_temperature;
2650 key->fileID = listp->hfl_hotfile[i].hf_fileid;
2651 key->forkType = 0;
2652
2653 /* Start a new transaction before calling BTree code. */
2654 if (hfs_start_transaction(hfsmp) != 0) {
2655 error = EINVAL;
2656 break;
2657 }
2658 startedtrans = 1;
2659
2660 error = BTInsertRecord(filefork, iterator, &record, record.itemSize);
2661 if (error) {
2662 int orig_error = error;
2663 error = MacToVFSError(error);
2664 printf("hfs: hotfiles_adopt:1: BTInsertRecord failed %d/%d (fileid %d)\n", error, orig_error, key->fileID);
2665 stage = HFC_IDLE;
2666 break;
2667 }
2668
2669 /* Insert thread record */
2670 key->keyLength = HFC_KEYLENGTH;
2671 key->temperature = HFC_LOOKUPTAG;
2672 key->fileID = listp->hfl_hotfile[i].hf_fileid;
2673 key->forkType = 0;
2674 data = listp->hfl_hotfile[i].hf_temperature;
2675 error = BTInsertRecord(filefork, iterator, &record, record.itemSize);
2676 if (error) {
2677 int orig_error = error;
2678 error = MacToVFSError(error);
2679 printf("hfs: hotfiles_adopt:2: BTInsertRecord failed %d/%d (fileid %d)\n", error, orig_error, key->fileID);
2680 stage = HFC_IDLE;
2681 break;
2682 } else {
2683 (void) BTFlushPath(filefork);
2684 blksmoved += fileblocks;
2685 }
2686
2687 listp->hfl_next++;
2688 if (listp->hfl_next >= listp->hfl_count) {
2689 break;
2690 }
2691
2692 /* Transaction complete. */
2693 if (startedtrans) {
2694 hfs_end_transaction(hfsmp);
2695 startedtrans = 0;
2696 }
2697
2698 if (hfs_hotfile_cur_freeblks(hfsmp) <= 0) {
2699 #if HFC_VERBOSE
2700 printf("hfs: hotfiles_adopt: free space exhausted (%d)\n", hfsmp->hfs_hotfile_freeblks);
2701 #endif
2702 break;
2703 }
2704 } /* end for */
2705
2706 #if HFC_VERBOSE
2707 printf("hfs: hotfiles_adopt: [%d] adopted %d blocks (%d files left)\n", listp->hfl_next, blksmoved, listp->hfl_count - i);
2708 #endif
2709 if (!startedtrans) {
2710 // start a txn so we'll save the btree summary info
2711 if (hfs_start_transaction(hfsmp) == 0) {
2712 startedtrans = 1;
2713 }
2714 }
2715
2716 /* Finish any outstanding transactions. */
2717 if (startedtrans) {
2718 save_btree_user_info(hfsmp);
2719
2720 (void) BTFlushPath(filefork);
2721 hfs_end_transaction(hfsmp);
2722 startedtrans = 0;
2723 }
2724 hfs_unlock(VTOC(hfsmp->hfc_filevp));
2725
2726 if ((listp->hfl_next >= listp->hfl_count) || (hfsmp->hfs_hotfile_freeblks <= 0)) {
2727 #if HFC_VERBOSE
2728 printf("hfs: hotfiles_adopt: all done relocating %d files\n", listp->hfl_count);
2729 printf("hfs: hotfiles_adopt: %d blocks free in hot file band\n", hfsmp->hfs_hotfile_freeblks);
2730 #endif
2731 stage = HFC_IDLE;
2732 }
2733 hfs_free(iterator, sizeof(*iterator));
2734
2735 if (stage != HFC_ADOPTION && hfsmp->hfc_filevp) {
2736 (void) hfc_btree_close(hfsmp, hfsmp->hfc_filevp);
2737 hfsmp->hfc_filevp = NULL;
2738 }
2739 hfsmp->hfc_stage = stage;
2740 wakeup((caddr_t)&hfsmp->hfc_stage);
2741 return (error);
2742 }
2743
2744 /*
2745 * Reclaim space by evicting the coldest files.
2746 *
2747 * Requires that the hfc_mutex be held.
2748 */
2749 static int
2750 hotfiles_evict(struct hfsmount *hfsmp, vfs_context_t ctx)
2751 {
2752 BTreeIterator * iterator = NULL;
2753 struct vnode *vp;
2754 HotFileKey * key;
2755 filefork_t * filefork;
2756 hotfilelist_t *listp;
2757 enum hfc_stage stage;
2758 u_int32_t savedtemp;
2759 int blksmoved;
2760 int filesmoved;
2761 int fileblocks;
2762 int error = 0;
2763 int startedtrans = 0;
2764 int bt_op;
2765
2766 if (hfsmp->hfc_stage != HFC_EVICTION) {
2767 return (EBUSY);
2768 }
2769
2770 if ((listp = hfsmp->hfc_filelist) == NULL)
2771 return (0);
2772
2773 if (hfs_lock(VTOC(hfsmp->hfc_filevp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT) != 0) {
2774 return (EPERM);
2775 }
2776
2777 #if HFC_VERBOSE
2778 printf("hfs:%s: hotfiles_evict (hotfile start/end block: %d - %d; max/free: %d/%d; maxfiles: %d)\n",
2779 hfsmp->vcbVN,
2780 hfsmp->hfs_hotfile_start, hfsmp->hfs_hotfile_end,
2781 hfsmp->hfs_hotfile_maxblks, hfsmp->hfs_hotfile_freeblks, hfsmp->hfc_maxfiles);
2782 #endif
2783
2784 iterator = hfs_mallocz(sizeof(*iterator));
2785
2786 stage = hfsmp->hfc_stage;
2787 hfsmp->hfc_stage = HFC_BUSY;
2788
2789 filesmoved = blksmoved = 0;
2790 bt_op = kBTreeFirstRecord;
2791
2792 key = (HotFileKey*) &iterator->key;
2793
2794 filefork = VTOF(hfsmp->hfc_filevp);
2795
2796 #if HFC_VERBOSE
2797 printf("hfs: hotfiles_evict: reclaim blks %d\n", listp->hfl_reclaimblks);
2798 #endif
2799
2800 while (listp->hfl_reclaimblks > 0 &&
2801 blksmoved < HFC_BLKSPERSYNC &&
2802 filesmoved < HFC_FILESPERSYNC) {
2803
2804 /*
2805 * Obtain the first record (ie the coldest one).
2806 */
2807 if (BTIterateRecord(filefork, bt_op, iterator, NULL, NULL) != 0) {
2808 #if HFC_VERBOSE
2809 printf("hfs: hotfiles_evict: no more records\n");
2810 #endif
2811 error = 0;
2812 stage = HFC_ADOPTION;
2813 break;
2814 }
2815 if (key->keyLength != HFC_KEYLENGTH) {
2816 printf("hfs: hotfiles_evict: invalid key length %d\n", key->keyLength);
2817 error = EFTYPE;
2818 break;
2819 }
2820 if (key->temperature == HFC_LOOKUPTAG) {
2821 #if HFC_VERBOSE
2822 printf("hfs: hotfiles_evict: ran into thread records\n");
2823 #endif
2824 error = 0;
2825 stage = HFC_ADOPTION;
2826 break;
2827 }
2828
2829 // Jump straight to delete for some files...
2830 if (key->fileID == VTOC(hfsmp->hfc_filevp)->c_fileid
2831 || key->fileID == hfsmp->hfs_jnlfileid
2832 || key->fileID == hfsmp->hfs_jnlinfoblkid
2833 || key->fileID < kHFSFirstUserCatalogNodeID) {
2834 goto delete;
2835 }
2836
2837 /*
2838 * Aquire the vnode for this file.
2839 */
2840 error = hfs_vget(hfsmp, key->fileID, &vp, 0, 0);
2841 if (error) {
2842 if (error == ENOENT) {
2843 goto delete; /* stale entry, go to next */
2844 } else {
2845 printf("hfs: hotfiles_evict: err %d getting file %d\n",
2846 error, key->fileID);
2847 }
2848 break;
2849 }
2850
2851 /*
2852 * Symlinks that may have been inserted into the hotfile zone during a previous OS are now stuck
2853 * here. We do not want to move them.
2854 */
2855 if (!vnode_isreg(vp)) {
2856 //printf("hfs: hotfiles_evict: huh, not a file %d\n", key->fileID);
2857 hfs_unlock(VTOC(vp));
2858 vnode_put(vp);
2859 goto delete; /* invalid entry, go to next */
2860 }
2861
2862 fileblocks = VTOF(vp)->ff_blocks;
2863 if ((blksmoved > 0) &&
2864 (blksmoved + fileblocks) > HFC_BLKSPERSYNC) {
2865 hfs_unlock(VTOC(vp));
2866 vnode_put(vp);
2867 break;
2868 }
2869 /*
2870 * Make sure file is in the hot area.
2871 */
2872 if (!hotextents(hfsmp, &VTOF(vp)->ff_extents[0]) && !(VTOC(vp)->c_attr.ca_recflags & kHFSFastDevPinnedMask)) {
2873 #if HFC_VERBOSE
2874 printf("hfs: hotfiles_evict: file %d isn't hot!\n", key->fileID);
2875 #endif
2876 hfs_unlock(VTOC(vp));
2877 vnode_put(vp);
2878 goto delete; /* stale entry, go to next */
2879 }
2880
2881 /*
2882 * Relocate file out of hot area. On cooperative fusion (CF) that just
2883 * means un-pinning the data from the ssd. For traditional hotfiles that means moving
2884 * the file data out of the hot region of the disk.
2885 */
2886 if (hfsmp->hfs_flags & HFS_CS_HOTFILE_PIN) {
2887 uint32_t pinned_blocks;
2888
2889 hfs_unlock(VTOC(vp)); // don't need an exclusive lock for this
2890 hfs_lock(VTOC(vp), HFS_SHARED_LOCK, HFS_LOCK_ALLOW_NOEXISTS);
2891
2892 error = hfs_pin_vnode(hfsmp, vp, HFS_UNPIN_IT, &pinned_blocks);
2893 fileblocks = pinned_blocks;
2894
2895 if (!error) {
2896 // go back to an exclusive lock since we're going to modify the cnode again
2897 hfs_unlock(VTOC(vp));
2898 hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_ALLOW_NOEXISTS);
2899 }
2900 } else {
2901 error = hfs_relocate(vp, HFSTOVCB(hfsmp)->nextAllocation, vfs_context_ucred(ctx), vfs_context_proc(ctx));
2902 }
2903 if (error) {
2904 #if HFC_VERBOSE
2905 printf("hfs: hotfiles_evict: err %d relocating file %d\n", error, key->fileID);
2906 #endif
2907 hfs_unlock(VTOC(vp));
2908 vnode_put(vp);
2909 bt_op = kBTreeNextRecord;
2910 goto next; /* go to next */
2911 } else {
2912 VTOC(vp)->c_attr.ca_recflags &= ~kHFSFastDevPinnedMask;
2913 VTOC(vp)->c_flag |= C_MODIFIED;
2914 }
2915
2916 //
2917 // We do not believe that this call to hfs_fsync() is
2918 // necessary and it causes a journal transaction
2919 // deadlock so we are removing it.
2920 //
2921 // (void) hfs_fsync(vp, MNT_WAIT, 0, p);
2922
2923 hfs_unlock(VTOC(vp));
2924 vnode_put(vp);
2925
2926 hfsmp->hfs_hotfile_freeblks += fileblocks;
2927 listp->hfl_reclaimblks -= fileblocks;
2928 if (listp->hfl_reclaimblks < 0)
2929 listp->hfl_reclaimblks = 0;
2930 blksmoved += fileblocks;
2931 filesmoved++;
2932 delete:
2933 /* Start a new transaction before calling BTree code. */
2934 if (hfs_start_transaction(hfsmp) != 0) {
2935 error = EINVAL;
2936 break;
2937 }
2938 startedtrans = 1;
2939
2940 error = BTDeleteRecord(filefork, iterator);
2941 if (error) {
2942 error = MacToVFSError(error);
2943 break;
2944 }
2945 savedtemp = key->temperature;
2946 key->temperature = HFC_LOOKUPTAG;
2947 error = BTDeleteRecord(filefork, iterator);
2948 if (error) {
2949 error = MacToVFSError(error);
2950 break;
2951 }
2952 key->temperature = savedtemp;
2953 next:
2954 (void) BTFlushPath(filefork);
2955
2956 /* Transaction complete. */
2957 if (startedtrans) {
2958 hfs_end_transaction(hfsmp);
2959 startedtrans = 0;
2960 }
2961
2962 } /* end while */
2963
2964 #if HFC_VERBOSE
2965 printf("hfs: hotfiles_evict: moved %d files (%d blks, %d to go)\n", filesmoved, blksmoved, listp->hfl_reclaimblks);
2966 #endif
2967 /* Finish any outstanding transactions. */
2968 if (startedtrans) {
2969 save_btree_user_info(hfsmp);
2970
2971 (void) BTFlushPath(filefork);
2972 hfs_end_transaction(hfsmp);
2973 startedtrans = 0;
2974 }
2975 hfs_unlock(VTOC(hfsmp->hfc_filevp));
2976
2977 /*
2978 * Move to next stage when finished.
2979 */
2980 if (listp->hfl_reclaimblks <= 0) {
2981 stage = HFC_ADOPTION;
2982 #if HFC_VERBOSE
2983 printf("hfs: hotfiles_evict: %d blocks free in hot file band\n", hfsmp->hfs_hotfile_freeblks);
2984 #endif
2985 }
2986 hfs_free(iterator, sizeof(*iterator));
2987 hfsmp->hfc_stage = stage;
2988 wakeup((caddr_t)&hfsmp->hfc_stage);
2989 return (error);
2990 }
2991
2992 /*
2993 * Age the existing records in the hot files b-tree.
2994 */
2995 static int
2996 hotfiles_age(struct hfsmount *hfsmp)
2997 {
2998 BTreeInfoRec btinfo;
2999 BTreeIterator * iterator = NULL;
3000 BTreeIterator * prev_iterator;
3001 FSBufferDescriptor record;
3002 FSBufferDescriptor prev_record;
3003 HotFileKey * key;
3004 HotFileKey * prev_key;
3005 filefork_t * filefork;
3006 u_int32_t data;
3007 u_int32_t prev_data;
3008 u_int32_t newtemp;
3009 int error;
3010 int i;
3011 int numrecs;
3012 int aged = 0;
3013 u_int16_t reclen;
3014
3015
3016 if (hfsmp->hfs_flags & HFS_CS_HOTFILE_PIN) {
3017 //
3018 // hotfiles don't age on CF
3019 //
3020 return 0;
3021 }
3022
3023 iterator = hfs_mallocz(2 * sizeof(*iterator));
3024
3025 key = (HotFileKey*) &iterator->key;
3026
3027 prev_iterator = &iterator[1];
3028 prev_key = (HotFileKey*) &prev_iterator->key;
3029
3030 record.bufferAddress = &data;
3031 record.itemSize = sizeof(data);
3032 record.itemCount = 1;
3033 prev_record.bufferAddress = &prev_data;
3034 prev_record.itemSize = sizeof(prev_data);
3035 prev_record.itemCount = 1;
3036
3037 /*
3038 * Capture b-tree changes inside a transaction
3039 */
3040 if (hfs_start_transaction(hfsmp) != 0) {
3041 error = EINVAL;
3042 goto out2;
3043 }
3044 if (hfs_lock(VTOC(hfsmp->hfc_filevp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT) != 0) {
3045 error = EPERM;
3046 goto out1;
3047 }
3048 filefork = VTOF(hfsmp->hfc_filevp);
3049
3050 error = BTGetInformation(filefork, 0, &btinfo);
3051 if (error) {
3052 error = MacToVFSError(error);
3053 goto out;
3054 }
3055 if (btinfo.numRecords < 2) {
3056 error = 0;
3057 goto out;
3058 }
3059
3060 /* Only want 1st half of leaf records */
3061 numrecs = (btinfo.numRecords /= 2) - 1;
3062
3063 error = BTIterateRecord(filefork, kBTreeFirstRecord, iterator, &record, &reclen);
3064 if (error) {
3065 printf("hfs_agehotfiles: BTIterateRecord: %d\n", error);
3066 error = MacToVFSError(error);
3067 goto out;
3068 }
3069 bcopy(iterator, prev_iterator, sizeof(BTreeIterator));
3070 prev_data = data;
3071
3072 for (i = 0; i < numrecs; ++i) {
3073 error = BTIterateRecord(filefork, kBTreeNextRecord, iterator, &record, &reclen);
3074 if (error == 0) {
3075 if (key->temperature < prev_key->temperature) {
3076 printf("hfs_agehotfiles: out of order keys!\n");
3077 error = EFTYPE;
3078 break;
3079 }
3080 if (reclen != sizeof(data)) {
3081 printf("hfs_agehotfiles: invalid record length %d\n", reclen);
3082 error = EFTYPE;
3083 break;
3084 }
3085 if (key->keyLength != HFC_KEYLENGTH) {
3086 printf("hfs_agehotfiles: invalid key length %d\n", key->keyLength);
3087 error = EFTYPE;
3088 break;
3089 }
3090 } else if ((error == fsBTEndOfIterationErr || error == fsBTRecordNotFoundErr) &&
3091 (i == (numrecs - 1))) {
3092 error = 0;
3093 } else if (error) {
3094 printf("hfs_agehotfiles: %d of %d BTIterateRecord: %d\n", i, numrecs, error);
3095 error = MacToVFSError(error);
3096 break;
3097 }
3098 if (prev_key->temperature == HFC_LOOKUPTAG) {
3099 #if HFC_VERBOSE
3100 printf("hfs_agehotfiles: ran into thread record\n");
3101 #endif
3102 error = 0;
3103 break;
3104 }
3105 error = BTDeleteRecord(filefork, prev_iterator);
3106 if (error) {
3107 printf("hfs_agehotfiles: BTDeleteRecord failed %d (file %d)\n", error, prev_key->fileID);
3108 error = MacToVFSError(error);
3109 break;
3110 }
3111
3112 /* Age by halving the temperature (floor = 4) */
3113 newtemp = MAX(prev_key->temperature >> 1, 4);
3114 prev_key->temperature = newtemp;
3115
3116 error = BTInsertRecord(filefork, prev_iterator, &prev_record, prev_record.itemSize);
3117 if (error) {
3118 printf("hfs_agehotfiles: BTInsertRecord failed %d (file %d)\n", error, prev_key->fileID);
3119 error = MacToVFSError(error);
3120 break;
3121 }
3122 ++aged;
3123 /*
3124 * Update thread entry with latest temperature.
3125 */
3126 prev_key->temperature = HFC_LOOKUPTAG;
3127 error = BTUpdateRecord(filefork, prev_iterator,
3128 (IterateCallBackProcPtr)update_callback,
3129 &newtemp);
3130 if (error) {
3131 printf("hfs_agehotfiles: %d of %d BTUpdateRecord failed %d (file %d, %d)\n",
3132 i, numrecs, error, prev_key->fileID, newtemp);
3133 error = MacToVFSError(error);
3134 // break;
3135 }
3136
3137 bcopy(iterator, prev_iterator, sizeof(BTreeIterator));
3138 prev_data = data;
3139
3140 } /* end for */
3141
3142 #if HFC_VERBOSE
3143 if (error == 0)
3144 printf("hfs_agehotfiles: aged %d records out of %d\n", aged, btinfo.numRecords);
3145 #endif
3146 (void) BTFlushPath(filefork);
3147 out:
3148 hfs_unlock(VTOC(hfsmp->hfc_filevp));
3149 out1:
3150 hfs_end_transaction(hfsmp);
3151 out2:
3152 if (iterator)
3153 hfs_free(iterator, 2 * sizeof(*iterator));
3154 return (error);
3155 }
3156
3157 /*
3158 * Return true if any blocks (or all blocks if all is true)
3159 * are contained in the hot file region.
3160 */
3161 static int
3162 hotextents(struct hfsmount *hfsmp, HFSPlusExtentDescriptor * extents)
3163 {
3164 u_int32_t b1, b2;
3165 int i;
3166 int inside = 0;
3167
3168 for (i = 0; i < kHFSPlusExtentDensity; ++i) {
3169 b1 = extents[i].startBlock;
3170 if (b1 == 0)
3171 break;
3172 b2 = b1 + extents[i].blockCount - 1;
3173 if ((b1 >= hfsmp->hfs_hotfile_start &&
3174 b2 <= hfsmp->hfs_hotfile_end) ||
3175 (b1 < hfsmp->hfs_hotfile_end &&
3176 b2 > hfsmp->hfs_hotfile_end)) {
3177 inside = 1;
3178 break;
3179 }
3180 }
3181 return (inside);
3182 }
3183
3184
3185 /*
3186 *========================================================================
3187 * HOT FILE B-TREE ROUTINES
3188 *========================================================================
3189 */
3190
3191 /*
3192 * Open the hot files b-tree for writing.
3193 *
3194 * On successful exit the vnode has a reference but not an iocount.
3195 */
3196 static int
3197 hfc_btree_open(struct hfsmount *hfsmp, struct vnode **vpp)
3198 {
3199 return hfc_btree_open_ext(hfsmp, vpp, 0);
3200 }
3201
3202 static int
3203 hfc_btree_open_ext(struct hfsmount *hfsmp, struct vnode **vpp, int ignore_btree_errs)
3204 {
3205 proc_t p;
3206 struct vnode *vp;
3207 struct cat_desc cdesc;
3208 struct cat_attr cattr;
3209 struct cat_fork cfork;
3210 static char filename[] = HFC_FILENAME;
3211 int error;
3212 int retry = 0;
3213 int lockflags;
3214 int newvnode_flags = 0;
3215
3216 *vpp = NULL;
3217 p = current_proc();
3218
3219 bzero(&cdesc, sizeof(cdesc));
3220 cdesc.cd_parentcnid = kRootDirID;
3221 cdesc.cd_nameptr = (const u_int8_t *)filename;
3222 cdesc.cd_namelen = strlen(filename);
3223
3224 lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
3225
3226 error = cat_lookup(hfsmp, &cdesc, 0, 0, &cdesc, &cattr, &cfork, NULL);
3227
3228 hfs_systemfile_unlock(hfsmp, lockflags);
3229
3230 if (error) {
3231 printf("hfs: hfc_btree_open: cat_lookup error %d\n", error);
3232 return (error);
3233 }
3234 again:
3235 cdesc.cd_flags |= CD_ISMETA;
3236 error = hfs_getnewvnode(hfsmp, NULL, NULL, &cdesc, 0, &cattr,
3237 &cfork, &vp, &newvnode_flags);
3238 if (error) {
3239 printf("hfs: hfc_btree_open: hfs_getnewvnode error %d\n", error);
3240 cat_releasedesc(&cdesc);
3241 return (error);
3242 }
3243 if (!vnode_issystem(vp)) {
3244 #if HFC_VERBOSE
3245 printf("hfs: hfc_btree_open: file has UBC, try again\n");
3246 #endif
3247 hfs_unlock(VTOC(vp));
3248 vnode_recycle(vp);
3249 vnode_put(vp);
3250 if (retry++ == 0)
3251 goto again;
3252 else
3253 return (EBUSY);
3254 }
3255
3256 /* Open the B-tree file for writing... */
3257 error = BTOpenPath(VTOF(vp), (KeyCompareProcPtr) hfc_comparekeys);
3258 if (error) {
3259 if (!ignore_btree_errs) {
3260 printf("hfs: hfc_btree_open: BTOpenPath error %d; filesize %lld\n", error, VTOF(vp)->ff_size);
3261 error = MacToVFSError(error);
3262 } else {
3263 error = 0;
3264 }
3265 }
3266
3267 hfs_unlock(VTOC(vp));
3268 if (error == 0) {
3269 *vpp = vp;
3270 vnode_ref(vp); /* keep a reference while its open */
3271 }
3272 vnode_put(vp);
3273
3274 if (!vnode_issystem(vp))
3275 panic("hfs: hfc_btree_open: not a system file (vp = %p)", vp);
3276
3277 HotFilesInfo hotfileinfo;
3278
3279 if (error == 0 && (hfsmp->hfs_flags & HFS_CS_HOTFILE_PIN)) {
3280 if ((BTGetUserData(VTOF(vp), &hotfileinfo, sizeof(hotfileinfo)) == 0) && (SWAP_BE32 (hotfileinfo.magic) == HFC_MAGIC)) {
3281 if (hfsmp->hfs_hotfile_freeblks == 0) {
3282 hfsmp->hfs_hotfile_freeblks = hfsmp->hfs_hotfile_maxblks - SWAP_BE32 (hotfileinfo.usedblocks);
3283 }
3284
3285 hfs_hotfile_cur_freeblks(hfsmp); // factors in any adjustments that happened at run-time
3286 }
3287 }
3288
3289 return (error);
3290 }
3291
3292 /*
3293 * Close the hot files b-tree.
3294 *
3295 * On entry the vnode has a reference.
3296 */
3297 static int
3298 hfc_btree_close(struct hfsmount *hfsmp, struct vnode *vp)
3299 {
3300 proc_t p = current_proc();
3301 int error = 0;
3302
3303
3304 if (hfsmp->jnl) {
3305 hfs_flush(hfsmp, HFS_FLUSH_JOURNAL);
3306 }
3307
3308 if (vnode_get(vp) == 0) {
3309 error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT);
3310 if (error == 0) {
3311 (void) hfs_fsync(vp, MNT_WAIT, 0, p);
3312 error = BTClosePath(VTOF(vp));
3313 hfs_unlock(VTOC(vp));
3314 }
3315 vnode_rele(vp);
3316 vnode_recycle(vp);
3317 vnode_put(vp);
3318 }
3319
3320 return (error);
3321 }
3322
3323 //
3324 // Assumes that hfsmp->hfc_filevp points to the hotfile btree vnode
3325 // (i.e. you called hfc_btree_open() ahead of time)
3326 //
3327 static int
3328 hfc_btree_delete_record(struct hfsmount *hfsmp, BTreeIterator *iterator, HotFileKey *key)
3329 {
3330 int error;
3331 filefork_t *filefork=VTOF(hfsmp->hfc_filevp);
3332
3333 /* Start a new transaction before calling BTree code. */
3334 if (hfs_start_transaction(hfsmp) != 0) {
3335 return EINVAL;
3336 }
3337
3338 error = BTDeleteRecord(filefork, iterator);
3339 if (error) {
3340 error = MacToVFSError(error);
3341 printf("hfs: failed to delete record for file-id %d : err %d\n", key->fileID, error);
3342 goto out;
3343 }
3344
3345 int savedtemp;
3346 savedtemp = key->temperature;
3347 key->temperature = HFC_LOOKUPTAG;
3348 error = BTDeleteRecord(filefork, iterator);
3349 if (error) {
3350 error = MacToVFSError(error);
3351 printf("hfs:2: failed to delete record for file-id %d : err %d\n", key->fileID, error);
3352 }
3353 key->temperature = savedtemp;
3354
3355 (void) BTFlushPath(filefork);
3356
3357 out:
3358 /* Transaction complete. */
3359 hfs_end_transaction(hfsmp);
3360
3361 return error;
3362 }
3363
3364 //
3365 // You have to have already opened the hotfile btree so
3366 // that hfsmp->hfc_filevp is filled in.
3367 //
3368 static int
3369 hfc_btree_delete(struct hfsmount *hfsmp)
3370 {
3371 struct vnode *dvp = NULL;
3372 vfs_context_t ctx = vfs_context_current();
3373 struct vnode_attr va;
3374 static char filename[] = HFC_FILENAME;
3375 int error;
3376
3377 error = hfs_vfs_root(HFSTOVFS(hfsmp), &dvp, ctx);
3378 if (error) {
3379 return (error);
3380 }
3381
3382 struct componentname cname = {
3383 .cn_nameiop = DELETE,
3384 .cn_flags = ISLASTCN,
3385 .cn_pnbuf = filename,
3386 .cn_pnlen = sizeof(filename),
3387 .cn_nameptr = filename,
3388 .cn_namelen = strlen(filename),
3389 };
3390
3391 VATTR_INIT(&va);
3392 VATTR_SET(&va, va_type, VREG);
3393 VATTR_SET(&va, va_mode, S_IFREG | S_IRUSR | S_IWUSR);
3394 VATTR_SET(&va, va_uid, 0);
3395 VATTR_SET(&va, va_gid, 0);
3396
3397 if (hfs_start_transaction(hfsmp) != 0) {
3398 error = EINVAL;
3399 goto out;
3400 }
3401
3402 struct vnop_remove_args ap = {
3403 .a_dvp = dvp,
3404 .a_vp = hfsmp->hfc_filevp,
3405 .a_cnp = &cname,
3406 };
3407
3408 error = hfs_vnop_remove(&ap);
3409 if (error) {
3410 printf("hfs: error %d removing HFBT on %s\n", error, HFSTOVCB(hfsmp)->vcbVN);
3411 }
3412
3413 hfs_end_transaction(hfsmp);
3414
3415 out:
3416 if (dvp) {
3417 vnode_put(dvp);
3418 dvp = NULL;
3419 }
3420
3421 return 0;
3422 }
3423
3424
3425
3426
3427 /*
3428 * Create a hot files btree file.
3429 *
3430 */
3431 static int
3432 hfc_btree_create(struct hfsmount *hfsmp, unsigned int nodesize, unsigned int entries)
3433 {
3434 struct vnode *dvp = NULL;
3435 struct vnode *vp = NULL;
3436 struct cnode *cp = NULL;
3437 vfs_context_t ctx = vfs_context_current();
3438 struct vnode_attr va;
3439 static char filename[] = HFC_FILENAME;
3440 int error;
3441
3442 if (hfsmp->hfc_filevp)
3443 panic("hfs: hfc_btree_create: hfc_filevp exists (vp = %p)", hfsmp->hfc_filevp);
3444
3445 error = hfs_vfs_root(HFSTOVFS(hfsmp), &dvp, ctx);
3446 if (error) {
3447 return (error);
3448 }
3449
3450 struct componentname cname = {
3451 .cn_nameiop = CREATE,
3452 .cn_flags = ISLASTCN,
3453 .cn_pnbuf = filename,
3454 .cn_pnlen = sizeof(filename),
3455 .cn_nameptr = filename,
3456 .cn_namelen = strlen(filename)
3457 };
3458
3459 VATTR_INIT(&va);
3460 VATTR_SET(&va, va_type, VREG);
3461 VATTR_SET(&va, va_mode, S_IFREG | S_IRUSR | S_IWUSR);
3462 VATTR_SET(&va, va_uid, 0);
3463 VATTR_SET(&va, va_gid, 0);
3464
3465 if (hfs_start_transaction(hfsmp) != 0) {
3466 vnode_put(dvp);
3467 return EINVAL;
3468 }
3469
3470 /* call ourselves directly, ignore the higher-level VFS file creation code */
3471
3472 struct vnop_create_args ap = {
3473 .a_dvp = dvp,
3474 .a_vpp = &vp,
3475 .a_cnp = &cname,
3476 .a_vap = &va
3477 };
3478
3479 error = hfs_vnop_create(&ap);
3480 if (error) {
3481 printf("hfs: error %d creating HFBT on %s\n", error, HFSTOVCB(hfsmp)->vcbVN);
3482 goto out;
3483 }
3484 if (dvp) {
3485 vnode_put(dvp);
3486 dvp = NULL;
3487 }
3488 if ((error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) {
3489 goto out;
3490 }
3491 cp = VTOC(vp);
3492
3493 /* Don't use non-regular files or files with links. */
3494 if (!vnode_isreg(vp) || cp->c_linkcount != 1) {
3495 error = EFTYPE;
3496 goto out;
3497 }
3498
3499 printf("hfs: created HFBT on %s\n", HFSTOVCB(hfsmp)->vcbVN);
3500
3501 if (VTOF(vp)->ff_size < nodesize) {
3502 caddr_t buffer;
3503 u_int16_t *index;
3504 u_int16_t offset;
3505 BTNodeDescriptor *ndp;
3506 BTHeaderRec *bthp;
3507 HotFilesInfo *hotfileinfo;
3508 int nodecnt;
3509 int filesize;
3510 int entirespernode;
3511
3512 /*
3513 * Mark it invisible (truncate will pull these changes).
3514 */
3515 ((FndrFileInfo *)&cp->c_finderinfo[0])->fdFlags |=
3516 SWAP_BE16 (kIsInvisible + kNameLocked);
3517
3518 buffer = hfs_mallocz(nodesize);
3519 index = (u_int16_t *)buffer;
3520
3521 entirespernode = (nodesize - sizeof(BTNodeDescriptor) - 2) /
3522 (sizeof(HotFileKey) + 6);
3523 nodecnt = 2 + howmany(entries * 2, entirespernode);
3524 nodecnt = roundup(nodecnt, 8);
3525 filesize = nodecnt * nodesize;
3526
3527 /* FILL IN THE NODE DESCRIPTOR: */
3528 ndp = (BTNodeDescriptor *)buffer;
3529 ndp->kind = kBTHeaderNode;
3530 ndp->numRecords = SWAP_BE16 (3);
3531 offset = sizeof(BTNodeDescriptor);
3532 index[(nodesize / 2) - 1] = SWAP_BE16 (offset);
3533
3534 /* FILL IN THE HEADER RECORD: */
3535 bthp = (BTHeaderRec *)((u_int8_t *)buffer + offset);
3536 bthp->nodeSize = SWAP_BE16 (nodesize);
3537 bthp->totalNodes = SWAP_BE32 (filesize / nodesize);
3538 bthp->freeNodes = SWAP_BE32 (nodecnt - 1);
3539 bthp->clumpSize = SWAP_BE32 (filesize);
3540 bthp->btreeType = kUserBTreeType; /* non-metadata */
3541 bthp->attributes |= SWAP_BE32 (kBTBigKeysMask);
3542 bthp->maxKeyLength = SWAP_BE16 (HFC_KEYLENGTH);
3543 offset += sizeof(BTHeaderRec);
3544 index[(nodesize / 2) - 2] = SWAP_BE16 (offset);
3545
3546 /* FILL IN THE USER RECORD: */
3547 hotfileinfo = (HotFilesInfo *)((u_int8_t *)buffer + offset);
3548 hotfileinfo->magic = SWAP_BE32 (HFC_MAGIC);
3549 hotfileinfo->version = SWAP_BE32 (HFC_VERSION);
3550 hotfileinfo->duration = SWAP_BE32 (HFC_DEFAULT_DURATION);
3551 hotfileinfo->timebase = 0;
3552 hotfileinfo->timeleft = 0;
3553 hotfileinfo->threshold = SWAP_BE32 (HFC_MINIMUM_TEMPERATURE);
3554 hotfileinfo->maxfileblks = SWAP_BE32 (HFC_MAXIMUM_FILESIZE / HFSTOVCB(hfsmp)->blockSize);
3555 if (hfsmp->hfs_flags & HFS_CS_HOTFILE_PIN) {
3556 if (hfsmp->hfs_hotfile_freeblks == 0) {
3557 hfsmp->hfs_hotfile_freeblks = hfsmp->hfs_hotfile_maxblks;
3558 }
3559 hotfileinfo->usedblocks = SWAP_BE32 (hfsmp->hfs_hotfile_maxblks - hfsmp->hfs_hotfile_freeblks);
3560 } else {
3561 hotfileinfo->maxfilecnt = SWAP_BE32 (HFC_DEFAULT_FILE_COUNT);
3562 }
3563 strlcpy((char *)hotfileinfo->tag, hfc_tag,
3564 sizeof hotfileinfo->tag);
3565 offset += kBTreeHeaderUserBytes;
3566 index[(nodesize / 2) - 3] = SWAP_BE16 (offset);
3567
3568 /* FILL IN THE MAP RECORD (only one node in use). */
3569 *((u_int8_t *)buffer + offset) = 0x80;
3570 offset += nodesize - sizeof(BTNodeDescriptor) - sizeof(BTHeaderRec)
3571 - kBTreeHeaderUserBytes - (4 * sizeof(int16_t));
3572 index[(nodesize / 2) - 4] = SWAP_BE16 (offset);
3573
3574 vnode_setnoflush(vp);
3575 error = hfs_truncate(vp, (off_t)filesize, IO_NDELAY, 0, ctx);
3576 if (error) {
3577 printf("hfs: error %d growing HFBT on %s\n", error, HFSTOVCB(hfsmp)->vcbVN);
3578 goto out;
3579 }
3580 cp->c_flag |= C_ZFWANTSYNC;
3581 cp->c_zftimeout = 1;
3582
3583 if (error == 0) {
3584 struct vnop_write_args args;
3585 uio_t auio;
3586
3587 auio = uio_create(1, 0, UIO_SYSSPACE, UIO_WRITE);
3588 uio_addiov(auio, (uintptr_t)buffer, nodesize);
3589
3590 args.a_desc = &vnop_write_desc;
3591 args.a_vp = vp;
3592 args.a_uio = auio;
3593 args.a_ioflag = 0;
3594 args.a_context = ctx;
3595
3596 hfs_unlock(cp);
3597 cp = NULL;
3598
3599 error = hfs_vnop_write(&args);
3600 if (error)
3601 printf("hfs: error %d writing HFBT on %s\n", error, HFSTOVCB(hfsmp)->vcbVN);
3602
3603 uio_free(auio);
3604 }
3605 hfs_free(buffer, nodesize);
3606 }
3607 out:
3608 hfs_end_transaction(hfsmp);
3609 if (dvp) {
3610 vnode_put(dvp);
3611 }
3612 if (vp) {
3613 if (cp)
3614 hfs_unlock(cp);
3615 vnode_recycle(vp);
3616 vnode_put(vp);
3617 }
3618 return (error);
3619 }
3620
3621 /*
3622 * Compare two hot file b-tree keys.
3623 *
3624 * Result: +n search key > trial key
3625 * 0 search key = trial key
3626 * -n search key < trial key
3627 */
3628 static int
3629 hfc_comparekeys(HotFileKey *searchKey, HotFileKey *trialKey)
3630 {
3631 /*
3632 * Compared temperatures first.
3633 */
3634 if (searchKey->temperature == trialKey->temperature) {
3635 /*
3636 * Temperatures are equal so compare file ids.
3637 */
3638 if (searchKey->fileID == trialKey->fileID) {
3639 /*
3640 * File ids are equal so compare fork types.
3641 */
3642 if (searchKey->forkType == trialKey->forkType) {
3643 return (0);
3644 } else if (searchKey->forkType > trialKey->forkType) {
3645 return (1);
3646 }
3647 } else if (searchKey->fileID > trialKey->fileID) {
3648 return (1);
3649 }
3650 } else if (searchKey->temperature > trialKey->temperature) {
3651 return (1);
3652 }
3653
3654 return (-1);
3655 }
3656
3657
3658 /*
3659 *========================================================================
3660 * HOT FILE DATA COLLECTING ROUTINES
3661 *========================================================================
3662 */
3663
3664 /*
3665 * Lookup a hot file entry in the tree.
3666 */
3667 #if HFC_DEBUG
3668 static hotfile_entry_t *
3669 hf_lookup(hotfile_data_t *hotdata, u_int32_t fileid, u_int32_t temperature)
3670 {
3671 hotfile_entry_t *entry = hotdata->rootentry;
3672
3673 while (entry &&
3674 entry->temperature != temperature &&
3675 entry->fileid != fileid) {
3676
3677 if (temperature > entry->temperature)
3678 entry = entry->right;
3679 else if (temperature < entry->temperature)
3680 entry = entry->left;
3681 else if (fileid > entry->fileid)
3682 entry = entry->right;
3683 else
3684 entry = entry->left;
3685 }
3686 return (entry);
3687 }
3688 #endif
3689
3690 /*
3691 * Insert a hot file entry into the tree.
3692 */
3693 static int
3694 hf_insert(hotfile_data_t *hotdata, hotfile_entry_t *newentry)
3695 {
3696 hotfile_entry_t *entry = hotdata->rootentry;
3697 u_int32_t fileid = newentry->fileid;
3698 u_int32_t temperature = newentry->temperature;
3699
3700 if (entry == NULL) {
3701 hotdata->rootentry = newentry;
3702 hotdata->coldest = newentry;
3703 hotdata->activefiles++;
3704 return 0;
3705 }
3706
3707 while (entry) {
3708 if (temperature > entry->temperature) {
3709 if (entry->right) {
3710 entry = entry->right;
3711 } else {
3712 entry->right = newentry;
3713 break;
3714 }
3715 } else if (temperature < entry->temperature) {
3716 if (entry->left) {
3717 entry = entry->left;
3718 } else {
3719 entry->left = newentry;
3720 break;
3721 }
3722 } else if (fileid > entry->fileid) {
3723 if (entry->right) {
3724 entry = entry->right;
3725 } else {
3726 if (entry->fileid != fileid)
3727 entry->right = newentry;
3728 break;
3729 }
3730 } else {
3731 if (entry->left) {
3732 entry = entry->left;
3733 } else {
3734 if (entry->fileid != fileid) {
3735 entry->left = newentry;
3736 } else {
3737 return EEXIST;
3738 }
3739 break;
3740 }
3741 }
3742 }
3743
3744 hotdata->activefiles++;
3745 return 0;
3746 }
3747
3748 /*
3749 * Find the coldest entry in the tree.
3750 */
3751 static hotfile_entry_t *
3752 hf_coldest(hotfile_data_t *hotdata)
3753 {
3754 hotfile_entry_t *entry = hotdata->rootentry;
3755
3756 if (entry) {
3757 while (entry->left)
3758 entry = entry->left;
3759 }
3760 return (entry);
3761 }
3762
3763 /*
3764 * Find the hottest entry in the tree.
3765 */
3766 static hotfile_entry_t *
3767 hf_hottest(hotfile_data_t *hotdata)
3768 {
3769 hotfile_entry_t *entry = hotdata->rootentry;
3770
3771 if (entry) {
3772 while (entry->right)
3773 entry = entry->right;
3774 }
3775 return (entry);
3776 }
3777
3778 /*
3779 * Delete a hot file entry from the tree.
3780 */
3781 static void
3782 hf_delete(hotfile_data_t *hotdata, u_int32_t fileid, u_int32_t temperature)
3783 {
3784 hotfile_entry_t *entry, *parent, *next;
3785
3786 parent = NULL;
3787 entry = hotdata->rootentry;
3788
3789 while (entry &&
3790 entry->temperature != temperature &&
3791 entry->fileid != fileid) {
3792
3793 parent = entry;
3794 if (temperature > entry->temperature)
3795 entry = entry->right;
3796 else if (temperature < entry->temperature)
3797 entry = entry->left;
3798 else if (fileid > entry->fileid)
3799 entry = entry->right;
3800 else
3801 entry = entry->left;
3802 }
3803
3804 if (entry) {
3805 /*
3806 * Reorganize the sub-trees spanning from our entry.
3807 */
3808 if ((next = entry->right)) {
3809 hotfile_entry_t *pnextl, *psub;
3810 /*
3811 * Tree pruning: take the left branch of the
3812 * current entry and place it at the lowest
3813 * left branch of the current right branch
3814 */
3815 psub = next;
3816
3817 /* Walk the Right/Left sub tree from current entry */
3818 while ((pnextl = psub->left))
3819 psub = pnextl;
3820
3821 /* Plug the old left tree to the new ->Right leftmost entry */
3822 psub->left = entry->left;
3823
3824 } else /* only left sub-tree, simple case */ {
3825 next = entry->left;
3826 }
3827 /*
3828 * Now, plug the current entry sub tree to
3829 * the good pointer of our parent entry.
3830 */
3831 if (parent == NULL)
3832 hotdata->rootentry = next;
3833 else if (parent->left == entry)
3834 parent->left = next;
3835 else
3836 parent->right = next;
3837
3838 /* Place entry back on the free-list */
3839 entry->left = 0;
3840 entry->fileid = 0;
3841 entry->temperature = 0;
3842
3843 entry->right = hotdata->freelist;
3844 hotdata->freelist = entry;
3845 hotdata->activefiles--;
3846
3847 if (hotdata->coldest == entry || hotdata->coldest == NULL) {
3848 hotdata->coldest = hf_coldest(hotdata);
3849 }
3850
3851 }
3852 }
3853
3854 /*
3855 * Get a free hot file entry.
3856 */
3857 static hotfile_entry_t *
3858 hf_getnewentry(hotfile_data_t *hotdata)
3859 {
3860 hotfile_entry_t * entry;
3861
3862 /*
3863 * When the free list is empty then steal the coldest one
3864 */
3865 if (hotdata->freelist == NULL) {
3866 entry = hf_coldest(hotdata);
3867 hf_delete(hotdata, entry->fileid, entry->temperature);
3868 }
3869 entry = hotdata->freelist;
3870 hotdata->freelist = entry->right;
3871 entry->right = 0;
3872
3873 return (entry);
3874 }
3875
3876
3877 /*
3878 * Generate a sorted list of hot files (hottest to coldest).
3879 *
3880 * As a side effect, every node in the hot file tree will be
3881 * deleted (moved to the free list).
3882 */
3883 static void
3884 hf_getsortedlist(hotfile_data_t * hotdata, hotfilelist_t *sortedlist)
3885 {
3886 int i = 0;
3887 hotfile_entry_t *entry;
3888
3889 while ((entry = hf_hottest(hotdata)) != NULL) {
3890 sortedlist->hfl_hotfile[i].hf_fileid = entry->fileid;
3891 sortedlist->hfl_hotfile[i].hf_temperature = entry->temperature;
3892 sortedlist->hfl_hotfile[i].hf_blocks = entry->blocks;
3893 sortedlist->hfl_totalblocks += entry->blocks;
3894 ++i;
3895
3896 hf_delete(hotdata, entry->fileid, entry->temperature);
3897 }
3898
3899 sortedlist->hfl_count = i;
3900
3901 #if HFC_VERBOSE
3902 printf("hfs: hf_getsortedlist returning %d entries w/%d total blocks\n", i, sortedlist->hfl_totalblocks);
3903 #endif
3904 }
3905
3906
3907 #if HFC_DEBUG
3908 static void
3909 hf_maxdepth(hotfile_entry_t * root, int depth, int *maxdepth)
3910 {
3911 if (root) {
3912 depth++;
3913 if (depth > *maxdepth)
3914 *maxdepth = depth;
3915 hf_maxdepth(root->left, depth, maxdepth);
3916 hf_maxdepth(root->right, depth, maxdepth);
3917 }
3918 }
3919
3920 static void
3921 hf_printtree(hotfile_entry_t * root)
3922 {
3923 if (root) {
3924 hf_printtree(root->left);
3925 printf("hfs: temperature: % 8d, fileid %d\n", root->temperature, root->fileid);
3926 hf_printtree(root->right);
3927 }
3928 }
3929 #endif