#include <sys/param.h>
#include <sys/systm.h>
#include <sys/fcntl.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/ubc.h>
#include <sys/ubc_internal.h>
#include <sys/vnode.h>
#include <sys/vnode_internal.h>
#include <sys/kauth.h>
#include <hfs/hfs.h>
#include <hfs/hfs_endian.h>
#include <hfs/hfs_format.h>
#include <hfs/hfs_mount.h>
#include <hfs/hfs_hotfiles.h>
#include "hfscommon/headers/BTreeScanner.h"
#define HFC_DEBUG 0
#define HFC_VERBOSE 0
#define HFC_MIN_BASE_TIME 0x424c8f00L
typedef struct hotfileinfo {
u_int32_t hf_fileid;
u_int32_t hf_temperature;
u_int32_t hf_blocks;
} hotfileinfo_t;
typedef struct hotfilelist {
u_int32_t hfl_magic;
u_int32_t hfl_version;
time_t hfl_duration;
int hfl_count;
int hfl_next;
int hfl_totalblocks;
int hfl_reclaimblks;
u_int32_t hfl_spare[2];
hotfileinfo_t hfl_hotfile[1];
} hotfilelist_t;
typedef struct hotfile_entry {
struct hotfile_entry *left;
struct hotfile_entry *right;
u_int32_t fileid;
u_int32_t temperature;
u_int32_t blocks;
} hotfile_entry_t;
#define MAX_NORMAL_TEMP 1000000000
#define HF_TEMP_RANGE MAX_NORMAL_TEMP
uint32_t hfc_default_file_count = 1000;
uint32_t hfc_default_duration = (3600 * 60);
uint32_t hfc_max_file_count = 5000;
uint64_t hfc_max_file_size = (10 * 1024 * 1024);
typedef struct hotfile_data {
struct hfsmount *hfsmp;
long refcount;
u_int32_t activefiles;
u_int32_t threshold;
u_int32_t maxblocks;
hotfile_entry_t *rootentry;
hotfile_entry_t *freelist;
hotfile_entry_t *coldest;
hotfile_entry_t entries[1];
} hotfile_data_t;
static int hfs_recording_start (struct hfsmount *);
static int hfs_recording_stop (struct hfsmount *);
static int hfs_getvnode_and_pin (struct hfsmount *hfsmp, uint32_t fileid, uint32_t *pinned);
static int hfs_pin_extent_record (struct hfsmount *hfsmp, HFSPlusExtentRecord extents, uint32_t *pinned);
static int hfs_pin_catalog_rec (struct hfsmount *hfsmp, HFSPlusCatalogFile *cfp, int rsrc);
static int hf_insert (hotfile_data_t *, hotfile_entry_t *);
static void hf_delete (hotfile_data_t *, u_int32_t, u_int32_t);
static hotfile_entry_t * hf_coldest (hotfile_data_t *);
static hotfile_entry_t * hf_getnewentry (hotfile_data_t *);
static void hf_getsortedlist (hotfile_data_t *, hotfilelist_t *);
#if HFC_DEBUG
static hotfile_entry_t * hf_lookup (hotfile_data_t *, u_int32_t, u_int32_t);
static void hf_maxdepth(hotfile_entry_t *, int, int *);
static void hf_printtree (hotfile_entry_t *);
#endif
static int hotfiles_collect (struct hfsmount *);
static int hotfiles_age (struct hfsmount *);
static int hotfiles_adopt (struct hfsmount *, vfs_context_t);
static int hotfiles_evict (struct hfsmount *, vfs_context_t);
static int hotfiles_refine (struct hfsmount *);
static int hotextents(struct hfsmount *, HFSPlusExtentDescriptor *);
static int hfs_addhotfile_internal(struct vnode *);
static int hfs_hotfile_cur_freeblks(hfsmount_t *hfsmp);
static int hfc_btree_create (struct hfsmount *, unsigned int, unsigned int);
static int hfc_btree_open (struct hfsmount *, struct vnode **);
static int hfc_btree_open_ext(struct hfsmount *hfsmp, struct vnode **vpp, int ignore_btree_errs);
static int hfc_btree_close (struct hfsmount *, struct vnode *);
static int hfc_btree_delete_record(struct hfsmount *hfsmp, BTreeIterator *iterator, HotFileKey *key);
static int hfc_btree_delete(struct hfsmount *hfsmp);
static int hfc_comparekeys (HotFileKey *, HotFileKey *);
char hfc_tag[] = "CLUSTERED HOT FILES B-TREE ";
static int
hfs_recording_start(struct hfsmount *hfsmp)
{
hotfile_data_t *hotdata;
struct timeval tv;
int maxentries;
size_t size;
int i;
int error;
if ((hfsmp->hfs_flags & HFS_READ_ONLY) ||
(hfsmp->jnl == NULL) ||
(hfsmp->hfs_flags & HFS_METADATA_ZONE) == 0) {
return (EPERM);
}
if (HFSTOVCB(hfsmp)->freeBlocks < (2 * (u_int32_t)hfsmp->hfs_hotfile_maxblks)) {
return (ENOSPC);
}
if (hfsmp->hfc_stage != HFC_IDLE) {
return (EBUSY);
}
hfsmp->hfc_stage = HFC_BUSY;
if (hfsmp->hfc_recdata) {
void * tmp;
tmp = hfsmp->hfc_recdata;
hfsmp->hfc_recdata = NULL;
FREE(tmp, M_TEMP);
}
microtime(&tv);
if (hfsmp->hfc_timebase == 0 &&
hfc_btree_open(hfsmp, &hfsmp->hfc_filevp) == 0) {
HotFilesInfo hotfileinfo;
if ((BTGetUserData(VTOF(hfsmp->hfc_filevp), &hotfileinfo,
sizeof(hotfileinfo)) == 0) &&
(SWAP_BE32 (hotfileinfo.magic) == HFC_MAGIC) &&
(SWAP_BE32 (hotfileinfo.timeleft) > 0) &&
(SWAP_BE32 (hotfileinfo.timebase) > 0)) {
if (hfsmp->hfs_flags & HFS_CS_HOTFILE_PIN) {
if (hfsmp->hfs_hotfile_freeblks == 0) {
hfsmp->hfs_hotfile_freeblks = hfsmp->hfs_hotfile_maxblks - SWAP_BE32 (hotfileinfo.usedblocks);
}
hfsmp->hfc_maxfiles = 0x7fffffff;
printf("hfs: %s: %s: hotfile freeblocks: %d, max: %d\n", hfsmp->vcbVN, __FUNCTION__,
hfsmp->hfs_hotfile_freeblks, hfsmp->hfs_hotfile_maxblks);
} else {
hfsmp->hfc_maxfiles = SWAP_BE32 (hotfileinfo.maxfilecnt);
}
hfsmp->hfc_timebase = SWAP_BE32 (hotfileinfo.timebase);
int timeleft = (int)SWAP_BE32(hotfileinfo.timeleft);
if (timeleft < 0 || timeleft > (int)(HFC_DEFAULT_DURATION*2)) {
timeleft = HFC_DEFAULT_DURATION;
}
hfsmp->hfc_timeout = timeleft + tv.tv_sec ;
if (hfsmp->hfc_timebase < HFC_MIN_BASE_TIME) {
hfsmp->hfc_timebase = hfsmp->hfc_timeout - HFC_DEFAULT_DURATION;
}
#if HFC_VERBOSE
printf("hfs: Resume recording hot files on %s (%d secs left (%d); timeout %ld)\n",
hfsmp->vcbVN, SWAP_BE32 (hotfileinfo.timeleft), timeleft, hfsmp->hfc_timeout - tv.tv_sec);
#endif
} else {
hfsmp->hfc_maxfiles = HFC_DEFAULT_FILE_COUNT;
hfsmp->hfc_timebase = tv.tv_sec + 1;
hfsmp->hfc_timeout = hfsmp->hfc_timebase + HFC_DEFAULT_DURATION;
}
(void) hfc_btree_close(hfsmp, hfsmp->hfc_filevp);
hfsmp->hfc_filevp = NULL;
} else {
struct cat_attr cattr;
u_int32_t cnid;
cnid = GetFileInfo(HFSTOVCB(hfsmp), kRootDirID, HFC_FILENAME, &cattr, NULL);
if ((cnid == 0) &&
!S_ISREG(cattr.ca_mode) &&
(error = hfc_btree_create(hfsmp, HFSTOVCB(hfsmp)->blockSize, HFC_DEFAULT_FILE_COUNT))) {
hfsmp->hfc_stage = HFC_IDLE;
wakeup((caddr_t)&hfsmp->hfc_stage);
return (error);
}
#if HFC_VERBOSE
printf("hfs: begin recording hot files on %s (hotfile start/end block: %d - %d; max/free: %d/%d; maxfiles: %d)\n",
hfsmp->vcbVN,
hfsmp->hfs_hotfile_start, hfsmp->hfs_hotfile_end,
hfsmp->hfs_hotfile_maxblks, hfsmp->hfs_hotfile_freeblks, hfsmp->hfc_maxfiles);
#endif
hfsmp->hfc_maxfiles = HFC_DEFAULT_FILE_COUNT;
hfsmp->hfc_timeout = tv.tv_sec + HFC_DEFAULT_DURATION;
if (hfsmp->hfc_timebase == 0) {
hfsmp->hfc_timebase = tv.tv_sec + 1;
} else {
time_t cumulativebase;
cumulativebase = hfsmp->hfc_timeout - (HFC_CUMULATIVE_CYCLES * HFC_DEFAULT_DURATION);
hfsmp->hfc_timebase = MAX(hfsmp->hfc_timebase, cumulativebase);
}
}
if ((hfsmp->hfc_maxfiles == 0) ||
(hfsmp->hfc_maxfiles > HFC_MAXIMUM_FILE_COUNT)) {
hfsmp->hfc_maxfiles = HFC_DEFAULT_FILE_COUNT;
}
maxentries = hfsmp->hfc_maxfiles;
size = sizeof(hotfile_data_t) + (maxentries * sizeof(hotfile_entry_t));
MALLOC(hotdata, hotfile_data_t *, size, M_TEMP, M_WAITOK);
if (hotdata == NULL) {
hfsmp->hfc_recdata = NULL;
hfsmp->hfc_stage = HFC_IDLE;
wakeup((caddr_t)&hfsmp->hfc_stage);
return(ENOMEM);
}
bzero(hotdata, size);
for (i = 1; i < maxentries ; i++)
hotdata->entries[i-1].right = &hotdata->entries[i];
hotdata->freelist = &hotdata->entries[0];
hotdata->threshold = HFC_MINIMUM_TEMPERATURE;
hotdata->maxblocks = HFC_MAXIMUM_FILESIZE / HFSTOVCB(hfsmp)->blockSize;
hotdata->hfsmp = hfsmp;
hfsmp->hfc_recdata = hotdata;
hfsmp->hfc_stage = HFC_RECORDING;
wakeup((caddr_t)&hfsmp->hfc_stage);
return (0);
}
static int
hfs_recording_stop(struct hfsmount *hfsmp)
{
hotfile_data_t *hotdata;
hotfilelist_t *listp;
struct timeval tv;
size_t size;
enum hfc_stage newstage = HFC_IDLE;
int error;
if (hfsmp->hfc_stage != HFC_RECORDING)
return (EPERM);
hfsmp->hfc_stage = HFC_BUSY;
hotfiles_collect(hfsmp);
#if HFC_VERBOSE
printf("hfs: end of hot file recording on %s\n", hfsmp->vcbVN);
#endif
hotdata = (hotfile_data_t *)hfsmp->hfc_recdata;
if (hotdata == NULL)
return (0);
hfsmp->hfc_recdata = NULL;
hfsmp->hfc_stage = HFC_EVALUATION;
wakeup((caddr_t)&hfsmp->hfc_stage);
#if HFC_VERBOSE
printf("hfs: curentries: %d\n", hotdata->activefiles);
#endif
if (hotdata->rootentry == NULL) {
error = 0;
goto out;
}
if (hfsmp->hfc_filevp)
panic("hfs_recording_stop: hfc_filevp exists (vp = %p)", hfsmp->hfc_filevp);
error = hfc_btree_open(hfsmp, &hfsmp->hfc_filevp);
if (error) {
goto out;
}
error = hotfiles_age(hfsmp);
if (error) {
(void) hfc_btree_close(hfsmp, hfsmp->hfc_filevp);
hfsmp->hfc_filevp = NULL;
goto out;
}
size = sizeof(hotfilelist_t);
size += sizeof(hotfileinfo_t) * (hotdata->activefiles - 1);
MALLOC(listp, hotfilelist_t *, size, M_TEMP, M_WAITOK);
if (listp == NULL) {
error = ENOMEM;
(void) hfc_btree_close(hfsmp, hfsmp->hfc_filevp);
hfsmp->hfc_filevp = NULL;
goto out;
}
bzero(listp, size);
hf_getsortedlist(hotdata, listp);
microtime(&tv);
listp->hfl_duration = tv.tv_sec - hfsmp->hfc_timebase;
hfsmp->hfc_recdata = listp;
error = hotfiles_refine(hfsmp);
if (error) {
(void) hfc_btree_close(hfsmp, hfsmp->hfc_filevp);
hfsmp->hfc_filevp = NULL;
goto out;
}
if (listp->hfl_totalblocks > hfs_hotfile_cur_freeblks(hfsmp)) {
listp->hfl_reclaimblks =
MIN(listp->hfl_totalblocks, hfsmp->hfs_hotfile_maxblks) -
hfsmp->hfs_hotfile_freeblks;
#if HFC_VERBOSE
printf("hfs_recording_stop: need to reclaim %d blocks\n", listp->hfl_reclaimblks);
#endif
if (listp->hfl_reclaimblks)
newstage = HFC_EVICTION;
else
newstage = HFC_ADOPTION;
} else {
newstage = HFC_ADOPTION;
}
if (newstage == HFC_ADOPTION && listp->hfl_totalblocks == 0) {
(void) hfc_btree_close(hfsmp, hfsmp->hfc_filevp);
hfsmp->hfc_filevp = NULL;
newstage = HFC_IDLE;
}
out:
#if HFC_VERBOSE
if (newstage == HFC_EVICTION)
printf("hfs: evicting coldest files\n");
else if (newstage == HFC_ADOPTION)
printf("hfs: adopting hotest files\n");
#endif
FREE(hotdata, M_TEMP);
hfsmp->hfc_stage = newstage;
wakeup((caddr_t)&hfsmp->hfc_stage);
return (error);
}
static void
save_btree_user_info(struct hfsmount *hfsmp)
{
HotFilesInfo hotfileinfo;
struct timeval tv;
microtime(&tv);
hotfileinfo.magic = SWAP_BE32 (HFC_MAGIC);
hotfileinfo.version = SWAP_BE32 (HFC_VERSION);
hotfileinfo.duration = SWAP_BE32 (HFC_DEFAULT_DURATION);
hotfileinfo.timebase = SWAP_BE32 (hfsmp->hfc_timebase);
hotfileinfo.timeleft = SWAP_BE32 (hfsmp->hfc_timeout - tv.tv_sec);
hotfileinfo.threshold = SWAP_BE32 (HFC_MINIMUM_TEMPERATURE);
hotfileinfo.maxfileblks = SWAP_BE32 (HFC_MAXIMUM_FILESIZE / HFSTOVCB(hfsmp)->blockSize);
if (hfsmp->hfs_flags & HFS_CS_HOTFILE_PIN) {
hotfileinfo.usedblocks = SWAP_BE32 (hfsmp->hfs_hotfile_maxblks - hfs_hotfile_cur_freeblks(hfsmp));
#if HFC_VERBOSE
printf("hfs: %s: saving usedblocks = %d (timeleft: %d; timeout %ld)\n", hfsmp->vcbVN, (hfsmp->hfs_hotfile_maxblks - hfsmp->hfs_hotfile_freeblks),
SWAP_BE32(hotfileinfo.timeleft), hfsmp->hfc_timeout);
#endif
} else {
hotfileinfo.maxfilecnt = SWAP_BE32 (HFC_DEFAULT_FILE_COUNT);
}
strlcpy((char *)hotfileinfo.tag, hfc_tag, sizeof hotfileinfo.tag);
(void) BTSetUserData(VTOF(hfsmp->hfc_filevp), &hotfileinfo, sizeof(hotfileinfo));
}
int
hfs_recording_suspend(struct hfsmount *hfsmp)
{
hotfile_data_t *hotdata = NULL;
int error;
if (hfsmp->hfc_stage == HFC_DISABLED)
return (0);
lck_mtx_lock(&hfsmp->hfc_mutex);
hotdata = (hotfile_data_t *)hfsmp->hfc_recdata;
if (hotdata == NULL || hfsmp->hfc_stage != HFC_RECORDING) {
error = 0;
goto out;
}
hfsmp->hfc_stage = HFC_BUSY;
#if HFC_VERBOSE
printf("hfs: suspend hot file recording on %s\n", hfsmp->vcbVN);
#endif
error = hfc_btree_open(hfsmp, &hfsmp->hfc_filevp);
if (error) {
printf("hfs_recording_suspend: err %d opening btree\n", error);
goto out;
}
if (hfs_start_transaction(hfsmp) != 0) {
goto out;
}
if (hfs_lock(VTOC(hfsmp->hfc_filevp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT) != 0) {
goto end_transaction;
}
save_btree_user_info(hfsmp);
hfs_unlock(VTOC(hfsmp->hfc_filevp));
end_transaction:
hfs_end_transaction(hfsmp);
out:
if (hfsmp->hfc_filevp) {
(void) hfc_btree_close(hfsmp, hfsmp->hfc_filevp);
hfsmp->hfc_filevp = NULL;
}
if (hotdata) {
FREE(hotdata, M_TEMP);
hfsmp->hfc_recdata = NULL;
}
hfsmp->hfc_stage = HFC_DISABLED;
wakeup((caddr_t)&hfsmp->hfc_stage);
lck_mtx_unlock(&hfsmp->hfc_mutex);
return (error);
}
static void
reset_file_ids(struct hfsmount *hfsmp, uint32_t *fileid_table, int num_ids)
{
int i, error;
for(i=0; i < num_ids; i++) {
struct vnode *vp;
error = hfs_vget(hfsmp, fileid_table[i], &vp, 0, 0);
if (error) {
if (error == ENOENT) {
error = 0;
continue;
}
continue;
}
if ((hfsmp->hfs_flags & HFS_CS_HOTFILE_PIN) && (VTOC(vp)->c_attr.ca_recflags & kHFSFastDevPinnedMask)) {
error = hfs_pin_vnode(hfsmp, vp, HFS_UNPIN_IT, NULL, vfs_context_kernel());
}
hfs_start_transaction(hfsmp);
if (VTOC(vp)->c_attr.ca_recflags & kHFSFastDevCandidateMask) {
vnode_clearfastdevicecandidate(vp);
}
VTOC(vp)->c_attr.ca_recflags &= ~(kHFSFastDevPinnedMask|kHFSDoNotFastDevPinMask|kHFSFastDevCandidateMask|kHFSAutoCandidateMask);
VTOC(vp)->c_flag |= C_MODIFIED;
hfs_update(vp, 0);
hfs_end_transaction(hfsmp);
hfs_unlock(VTOC(vp));
vnode_put(vp);
}
}
static int
flag_hotfile(struct hfsmount *hfsmp, const char *filename)
{
struct vnode *dvp = NULL, *fvp = NULL;
vfs_context_t ctx = vfs_context_kernel();
struct componentname cname;
int error=0;
size_t fname_len;
const char *orig_fname = filename;
if (filename == NULL) {
return EINVAL;
}
fname_len = strlen(filename);
error = VFS_ROOT(HFSTOVFS(hfsmp), &dvp, ctx);
if (error) {
return (error);
}
const char *ptr;
ptr = filename;
while (ptr < (orig_fname + fname_len - 1)) {
for(; ptr < (orig_fname + fname_len) && *ptr && *ptr != '/'; ptr++) {
}
cname.cn_nameiop = LOOKUP;
cname.cn_flags = ISLASTCN;
cname.cn_context = ctx;
cname.cn_ndp = NULL;
cname.cn_pnbuf = __DECONST(char *, orig_fname);
cname.cn_nameptr = __DECONST(char *, filename);
cname.cn_pnlen = fname_len;
cname.cn_namelen = ptr - filename;
cname.cn_hash = 0;
cname.cn_consume = 0;
error = VNOP_LOOKUP(dvp, &fvp, &cname, ctx);
if (error) {
if (dvp) {
vnode_put(dvp);
dvp = NULL;
}
return error;
}
if (ptr < orig_fname + fname_len - 1) {
vnode_put(dvp);
dvp = fvp;
fvp = NULL;
filename = ++ptr; }
}
if (fvp == NULL) {
error = ENOENT;
goto out;
}
struct cnode *cp = VTOC(fvp);
if ((error = hfs_lock(cp, HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT)) != 0) {
goto out;
}
hfs_start_transaction(hfsmp);
cp->c_attr.ca_recflags |= (kHFSFastDevCandidateMask|kHFSAutoCandidateMask);
cp->c_flag |= C_MODIFIED;
hfs_update(fvp, 0);
hfs_end_transaction(hfsmp);
hfs_unlock(cp);
out:
if (fvp) {
vnode_put(fvp);
fvp = NULL;
}
if (dvp) {
vnode_put(dvp);
dvp = NULL;
}
return error;
}
static void
hfs_setup_default_cf_hotfiles(struct hfsmount *hfsmp)
{
const char *system_default_hotfiles[] = {
"usr",
"System",
"Applications",
"private/var/db/dyld"
};
int i;
for(i=0; i < (int)(sizeof(system_default_hotfiles)/sizeof(char *)); i++) {
flag_hotfile(hfsmp, system_default_hotfiles[i]);
}
}
#define NUM_FILE_RESET_IDS 4096 // so we allocate 16k to hold file-ids
static void
hfs_hotfile_reset(struct hfsmount *hfsmp)
{
CatalogKey * keyp;
CatalogRecord * datap;
u_int32_t dataSize;
BTScanState scanstate;
BTreeIterator * iterator = NULL;
FSBufferDescriptor record;
u_int32_t data;
u_int32_t cnid;
int error = 0;
uint32_t *fileids=NULL;
int cur_id_index = 0;
int cleared = 0;
int filecount = 0;
int dircount = 0;
#if HFC_VERBOSE
printf("hfs: %s: %s\n", hfsmp->vcbVN, __FUNCTION__);
#endif
MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
if (iterator == NULL) {
error = ENOMEM;
goto out;
}
bzero(iterator, sizeof(*iterator));
MALLOC(fileids, uint32_t *, NUM_FILE_RESET_IDS * sizeof(uint32_t), M_TEMP, M_WAITOK);
if (fileids == NULL) {
error = ENOMEM;
goto out;
}
record.bufferAddress = &data;
record.itemSize = sizeof(u_int32_t);
record.itemCount = 1;
error = BTScanInitialize(VTOF(HFSTOVCB(hfsmp)->catalogRefNum), 0, 0, 0,
kCatSearchBufferSize, &scanstate);
if (error) {
printf("hfs_hotfile_reset: err %d BTScanInit\n", error);
goto out;
}
for (;;) {
error = BTScanNextRecord(&scanstate, 0, (void **)&keyp, (void **)&datap, &dataSize);
if (error) {
if (error == btNotFound)
error = 0;
else
printf("hfs_hotfile_reset: err %d BTScanNext\n", error);
break;
}
if (datap->recordType == kHFSPlusFolderRecord && (dataSize == sizeof(HFSPlusCatalogFolder))) {
HFSPlusCatalogFolder *dirp = (HFSPlusCatalogFolder *)datap;
dircount++;
if ((dirp->flags & (kHFSFastDevPinnedMask|kHFSDoNotFastDevPinMask|kHFSFastDevCandidateMask|kHFSAutoCandidateMask)) == 0) {
continue;
}
cnid = dirp->folderID;
} else if ((datap->recordType == kHFSPlusFileRecord) && (dataSize == sizeof(HFSPlusCatalogFile))) {
HFSPlusCatalogFile *filep = (HFSPlusCatalogFile *)datap;
filecount++;
if ((filep->flags & (kHFSFastDevPinnedMask|kHFSDoNotFastDevPinMask|kHFSFastDevCandidateMask|kHFSAutoCandidateMask)) == 0) {
continue;
}
cnid = filep->fileID;
} else {
continue;
}
if (cnid == hfsmp->hfs_jnlfileid || cnid == hfsmp->hfs_jnlinfoblkid) {
continue;
}
fileids[cur_id_index++] = cnid;
if (cur_id_index >= NUM_FILE_RESET_IDS) {
(void) BTScanTerminate(&scanstate, &data, &data, &data);
reset_file_ids(hfsmp, fileids, cur_id_index);
cleared += cur_id_index;
cur_id_index = 0;
error = BTScanInitialize(VTOF(HFSTOVCB(hfsmp)->catalogRefNum), 0, 0, 0,
kCatSearchBufferSize, &scanstate);
if (error) {
printf("hfs_hotfile_reset: err %d BTScanInit\n", error);
goto out;
}
continue;
}
}
if (cur_id_index) {
reset_file_ids(hfsmp, fileids, cur_id_index);
cleared += cur_id_index;
cur_id_index = 0;
}
printf("hfs: cleared HotFileCache related bits on %d files out of %d (dircount %d)\n", cleared, filecount, dircount);
(void) BTScanTerminate(&scanstate, &data, &data, &data);
out:
if (fileids)
FREE(fileids, M_TEMP);
if (iterator)
FREE(iterator, M_TEMP);
error = hfc_btree_open_ext(hfsmp, &hfsmp->hfc_filevp, 1);
if (!error) {
printf("hfs: hotfile_reset: deleting existing hotfile btree\n");
hfc_btree_delete(hfsmp);
}
if (hfsmp->hfc_filevp) {
(void) hfc_btree_close(hfsmp, hfsmp->hfc_filevp);
hfsmp->hfc_filevp = NULL;
}
hfsmp->hfs_hotfile_blk_adjust = 0;
hfsmp->hfs_hotfile_freeblks = hfsmp->hfs_hotfile_maxblks;
}
static int
hfs_hotfile_repin_files(struct hfsmount *hfsmp)
{
BTreeIterator * iterator = NULL;
HotFileKey * key;
filefork_t * filefork;
int error = 0;
int bt_op;
enum hfc_stage stage;
uint32_t pinned_blocks;
uint32_t num_files=0, nrsrc=0;
uint32_t total_pinned=0;
if (!(hfsmp->hfs_flags & HFS_CS_HOTFILE_PIN) || !hfsmp->hfc_filevp) {
return 0;
}
#if HFC_VERBOSE
printf("hfs: %s: %s\n", hfsmp->vcbVN, __FUNCTION__);
#endif
if (hfs_lock(VTOC(hfsmp->hfc_filevp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT) != 0) {
return (EPERM);
}
MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
if (iterator == NULL) {
hfs_unlock(VTOC(hfsmp->hfc_filevp));
return (ENOMEM);
}
stage = hfsmp->hfc_stage;
hfsmp->hfc_stage = HFC_BUSY;
bt_op = kBTreeFirstRecord;
bzero(iterator, sizeof(*iterator));
key = (HotFileKey*) &iterator->key;
filefork = VTOF(hfsmp->hfc_filevp);
int lockflags;
while (1) {
lockflags = 0;
if (BTIterateRecord(filefork, bt_op, iterator, NULL, NULL) != 0) {
error = 0;
break;
}
if (key->keyLength != HFC_KEYLENGTH) {
error = EFTYPE;
break;
}
if (key->temperature == HFC_LOOKUPTAG) {
error = 0;
break;
}
struct cat_desc fdesc;
struct cat_attr attr;
struct cat_fork fork;
uint8_t forktype = 0;
lockflags = hfs_systemfile_lock(hfsmp, (SFL_CATALOG | SFL_EXTENTS), HFS_SHARED_LOCK);
if (hfs_chash_snoop (hfsmp, key->fileID, 1, NULL, NULL) == 0) {
pinned_blocks = 0;
hfs_systemfile_unlock(hfsmp, lockflags);
lockflags = 0;
error = hfs_getvnode_and_pin (hfsmp, key->fileID, &pinned_blocks);
if (error) {
if (error == ENOENT) {
hfc_btree_delete_record(hfsmp, iterator, key);
}
}
else { total_pinned += pinned_blocks;
num_files++;
}
goto next;
}
error = cat_idlookup(hfsmp, key->fileID, 1, 0, &fdesc, &attr, &fork);
if (error) {
hfc_btree_delete_record(hfsmp, iterator, key);
goto next;
}
if (fork.cf_size == 0) {
error = cat_idlookup(hfsmp, key->fileID, 1, 1, &fdesc, &attr, &fork);
if (error) {
hfc_btree_delete_record(hfsmp, iterator, key);
goto next;
}
forktype = 0xff;
nrsrc++;
}
pinned_blocks = 0;
error = hfs_pin_extent_record (hfsmp, fork.cf_extents, &pinned_blocks);
if (error) {
goto next; }
total_pinned += pinned_blocks;
pinned_blocks = 0;
if (fork.cf_extents[kHFSPlusExtentDensity-1].startBlock) {
error = hfs_pin_overflow_extents (hfsmp, key->fileID, forktype, &pinned_blocks);
if (error) {
goto next;
}
}
num_files++;
if (pinned_blocks) {
total_pinned += pinned_blocks;
}
next:
if (lockflags) {
hfs_systemfile_unlock(hfsmp, lockflags);
lockflags = 0;
}
bt_op = kBTreeNextRecord;
}
#if HFC_VERBOSE
printf("hfs: hotfiles_repin_files: re-pinned %d files (nrsrc %d, total pinned %d blks; freeblock %d, maxblocks %d, calculated free: %d)\n",
num_files, nrsrc, total_pinned, hfsmp->hfs_hotfile_freeblks, hfsmp->hfs_hotfile_maxblks,
hfsmp->hfs_hotfile_maxblks - total_pinned);
#endif
hfsmp->hfs_hotfile_freeblks = hfsmp->hfs_hotfile_maxblks - total_pinned;
hfs_unlock(VTOC(hfsmp->hfc_filevp));
FREE(iterator, M_TEMP);
hfsmp->hfc_stage = stage;
wakeup((caddr_t)&hfsmp->hfc_stage);
return (error);
}
void
hfs_repin_hotfiles(struct hfsmount *hfsmp)
{
int error, need_close;
lck_mtx_lock(&hfsmp->hfc_mutex);
if (hfsmp->hfc_filevp == NULL) {
error = hfc_btree_open(hfsmp, &hfsmp->hfc_filevp);
if (!error) {
need_close = 1;
} else {
printf("hfs: failed to open the btree err=%d. Unable to re-pin hotfiles.\n", error);
lck_mtx_unlock(&hfsmp->hfc_mutex);
return;
}
} else {
need_close = 0;
}
hfs_pin_vnode(hfsmp, hfsmp->hfc_filevp, HFS_PIN_IT, NULL, vfs_context_kernel());
hfs_hotfile_repin_files(hfsmp);
if (need_close) {
(void) hfc_btree_close(hfsmp, hfsmp->hfc_filevp);
hfsmp->hfc_filevp = NULL;
}
lck_mtx_unlock(&hfsmp->hfc_mutex);
}
int hfs_pin_overflow_extents (struct hfsmount *hfsmp, uint32_t fileid,
uint8_t forktype, uint32_t *pinned) {
struct BTreeIterator *ext_iter = NULL;
ExtentKey *ext_key_ptr = NULL;
ExtentRecord ext_data;
FSBufferDescriptor btRecord;
uint16_t btRecordSize;
int error = 0;
uint32_t pinned_blocks = 0;
MALLOC (ext_iter, struct BTreeIterator*, sizeof (struct BTreeIterator), M_TEMP, M_WAITOK);
if (ext_iter == NULL) {
return ENOMEM;
}
bzero (ext_iter, sizeof(*ext_iter));
BTInvalidateHint (ext_iter);
ext_key_ptr = (ExtentKey*)&ext_iter->key;
btRecord.bufferAddress = &ext_data;
btRecord.itemCount = 1;
btRecord.itemSize = sizeof (HFSPlusExtentRecord);
ext_key_ptr->hfsPlus.keyLength = kHFSPlusExtentKeyMaximumLength;
ext_key_ptr->hfsPlus.forkType = forktype;
ext_key_ptr->hfsPlus.pad = 0;
ext_key_ptr->hfsPlus.fileID = fileid;
ext_key_ptr->hfsPlus.startBlock = 0;
error = BTSearchRecord (VTOF(hfsmp->hfs_extents_vp), ext_iter, &btRecord, &btRecordSize, ext_iter);
if (error == btNotFound) {
error = 0;
}
while (1) {
uint32_t found_fileid;
uint32_t pblocks;
error = BTIterateRecord (VTOF(hfsmp->hfs_extents_vp), kBTreeNextRecord, ext_iter, &btRecord, &btRecordSize);
if (error) {
if (error == btNotFound)
error = 0;
break;
}
found_fileid = ext_key_ptr->hfsPlus.fileID;
if ((found_fileid != fileid) || (ext_key_ptr->hfsPlus.forkType != forktype)) {
error = 0;
break;
}
pblocks = 0;
error = hfs_pin_extent_record (hfsmp, ext_data.hfsPlus, &pblocks);
if (error) {
break;
}
pinned_blocks += pblocks;
if (ext_data.hfsPlus[kHFSPlusExtentDensity-1].startBlock == 0) {
error = 0;
break;
}
}
FREE (ext_iter, M_TEMP);
if (error == 0) {
*pinned = pinned_blocks;
}
return error;
}
static int
hfs_getvnode_and_pin (struct hfsmount *hfsmp, uint32_t fileid, uint32_t *pinned) {
struct vnode *vp;
int error = 0;
*pinned = 0;
uint32_t pblocks;
error = hfs_vget(hfsmp, fileid, &vp, 0, 0);
if (error) {
return error;
}
if (!vnode_isreg(vp)) {
hfs_unlock(VTOC(vp));
vnode_put(vp);
return EPERM;
}
if (!(VTOC(vp)->c_attr.ca_recflags & kHFSFastDevPinnedMask)) {
hfs_unlock(VTOC(vp));
vnode_put(vp);
return EINVAL;
}
error = hfs_pin_vnode(hfsmp, vp, HFS_PIN_IT, &pblocks, vfs_context_kernel());
if (error == 0) {
*pinned = pblocks;
}
hfs_unlock(VTOC(vp));
vnode_put(vp);
return error;
}
static int hfs_pin_extent_record (struct hfsmount *hfsmp, HFSPlusExtentRecord extents, uint32_t *pinned) {
uint32_t pb = 0;
int i;
int error;
if (pinned == NULL) {
return EINVAL;
}
*pinned = 0;
for ( i = 0; i < kHFSPlusExtentDensity; i++) {
if (extents[i].startBlock == 0) {
break;
}
error = hfs_pin_block_range (hfsmp, HFS_PIN_IT, extents[i].startBlock,
extents[i].blockCount, vfs_context_kernel());
if (error) {
break;
}
pb += extents[i].blockCount;
}
*pinned = pb;
return error;
}
static int hfs_pin_catalog_rec (struct hfsmount *hfsmp, HFSPlusCatalogFile *cfp, int rsrc) {
uint32_t pinned_blocks = 0;
HFSPlusForkData *forkdata;
int error = 0;
uint8_t forktype = 0;
if (rsrc) {
forkdata = &cfp->resourceFork;
forktype = 0xff;
}
else {
forkdata = &cfp->dataFork;
}
uint32_t pblocks = 0;
error = hfs_pin_extent_record (hfsmp, forkdata->extents, &pblocks);
if (error) {
return error;
}
pinned_blocks += pblocks;
pblocks = 0;
if (forkdata->extents[kHFSPlusExtentDensity-1].startBlock != 0) {
error = hfs_pin_overflow_extents (hfsmp, cfp->fileID, forktype, &pblocks);
}
pinned_blocks += pblocks;
hfsmp->hfs_hotfile_freeblks -= pinned_blocks;
return error;
}
int
hfs_recording_init(struct hfsmount *hfsmp)
{
CatalogKey * keyp;
CatalogRecord * datap;
u_int32_t dataSize;
HFSPlusCatalogFile *filep;
BTScanState scanstate;
BTreeIterator * iterator = NULL;
FSBufferDescriptor record;
HotFileKey * key;
filefork_t * filefork;
u_int32_t data;
struct cat_attr cattr;
u_int32_t cnid;
int error = 0;
long starting_temp;
int started_tr = 0;
int started_scan = 0;
int inserted = 0;
int filecount = 0;
int uncacheable = 0;
if ((vfs_flags(HFSTOVFS(hfsmp)) & MNT_ROOTFS) == 0) {
hfsmp->hfc_stage = HFC_DISABLED;
return (EPERM);
}
lck_mtx_lock (&hfsmp->hfc_mutex);
if (vfs_flags(HFSTOVFS(hfsmp)) & MNT_NOATIME) {
hfsmp->hfc_stage = HFC_DISABLED;
lck_mtx_unlock (&hfsmp->hfc_mutex);
return EPERM;
}
cnid = GetFileInfo(HFSTOVCB(hfsmp), kRootDirID, ".hotfile-suspend", &cattr, NULL);
if (cnid != 0) {
printf("hfs: %s: %s: hotfiles explicitly disabled! remove /.hotfiles-suspend to re-enable\n", hfsmp->vcbVN, __FUNCTION__);
hfsmp->hfc_stage = HFC_DISABLED;
lck_mtx_unlock (&hfsmp->hfc_mutex);
return EPERM;
}
cnid = GetFileInfo(HFSTOVCB(hfsmp), kRootDirID, ".hotfile-reset", &cattr, NULL);
if (cnid != 0) {
hfs_hotfile_reset(hfsmp);
}
if (hfsmp->hfs_flags & HFS_CS_HOTFILE_PIN) {
hfc_default_file_count = 20000;
hfc_default_duration = 300; hfc_max_file_count = 50000;
hfc_max_file_size = (512ULL * 1024ULL * 1024ULL);
}
cnid = GetFileInfo(HFSTOVCB(hfsmp), kRootDirID, HFC_FILENAME, &cattr, NULL);
if (cnid != 0 && S_ISREG(cattr.ca_mode)) {
int recreate = 0;
if (hfsmp->hfc_stage == HFC_DISABLED)
hfsmp->hfc_stage = HFC_IDLE;
hfsmp->hfs_hotfile_freeblks = 0;
if ((hfsmp->hfs_flags & HFS_CS_HOTFILE_PIN) && cattr.ca_blocks > 0) {
error = hfc_btree_open(hfsmp, &hfsmp->hfc_filevp);
if (!error) {
hfs_pin_vnode(hfsmp, hfsmp->hfc_filevp, HFS_PIN_IT, NULL, vfs_context_kernel());
} else {
printf("hfs: failed to open the btree err=%d. Recreating hotfile btree.\n", error);
recreate = 1;
}
hfs_hotfile_repin_files(hfsmp);
if (hfsmp->hfc_filevp) {
(void) hfc_btree_close(hfsmp, hfsmp->hfc_filevp);
hfsmp->hfc_filevp = NULL;
}
} else if (hfsmp->hfs_flags & HFS_CS_HOTFILE_PIN) {
printf("hfs: hotfile btree is zero bytes long?! recreating it.\n");
recreate = 1;
}
if (!recreate) {
lck_mtx_unlock (&hfsmp->hfc_mutex);
return (0);
} else {
error = hfc_btree_open_ext(hfsmp, &hfsmp->hfc_filevp, 1);
if (!error) {
error = hfc_btree_delete(hfsmp);
(void) hfc_btree_close(hfsmp, hfsmp->hfc_filevp);
hfsmp->hfc_filevp = NULL;
}
}
}
printf("hfs: %s: %s: creating the hotfile btree\n", hfsmp->vcbVN, __FUNCTION__);
if (hfs_start_transaction(hfsmp) != 0) {
lck_mtx_unlock (&hfsmp->hfc_mutex);
return EINVAL;
}
started_tr = 1;
error = hfc_btree_create(hfsmp, HFSTOVCB(hfsmp)->blockSize, HFC_DEFAULT_FILE_COUNT);
if (error) {
#if HFC_VERBOSE
printf("hfs: Error %d creating hot file b-tree on %s \n", error, hfsmp->vcbVN);
#endif
goto recording_init_out;
}
hfs_end_transaction (hfsmp);
started_tr = 0;
hfs_flush (hfsmp, HFS_FLUSH_FULL);
if (hfs_start_transaction (hfsmp) != 0) {
lck_mtx_unlock (&hfsmp->hfc_mutex);
return EINVAL;
}
started_tr = 1;
if (hfsmp->hfc_filevp)
panic("hfs_recording_init: hfc_filevp exists (vp = %p)", hfsmp->hfc_filevp);
error = hfc_btree_open(hfsmp, &hfsmp->hfc_filevp);
if (error) {
#if HFC_VERBOSE
printf("hfs: Error %d opening hot file b-tree on %s \n", error, hfsmp->vcbVN);
#endif
goto recording_init_out;
}
hfs_setup_default_cf_hotfiles(hfsmp);
if (hfs_lock(VTOC(hfsmp->hfc_filevp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT) != 0) {
error = EPERM;
(void) hfc_btree_close(hfsmp, hfsmp->hfc_filevp);
hfsmp->hfc_filevp = NULL;
goto recording_init_out;
}
MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
if (iterator == NULL) {
error = ENOMEM;
hfs_unlock (VTOC(hfsmp->hfc_filevp));
(void) hfc_btree_close(hfsmp, hfsmp->hfc_filevp);
hfsmp->hfc_filevp = NULL;
goto recording_init_out;
}
bzero(iterator, sizeof(*iterator));
key = (HotFileKey*) &iterator->key;
key->keyLength = HFC_KEYLENGTH;
record.bufferAddress = &data;
record.itemSize = sizeof(u_int32_t);
record.itemCount = 1;
#if HFC_VERBOSE
printf("hfs: Evaluating space for \"%s\" metadata zone... (freeblks %d)\n", HFSTOVCB(hfsmp)->vcbVN,
hfsmp->hfs_hotfile_freeblks);
#endif
error = BTScanInitialize(VTOF(HFSTOVCB(hfsmp)->catalogRefNum), 0, 0, 0,
kCatSearchBufferSize, &scanstate);
if (error) {
printf("hfs_recording_init: err %d BTScanInit\n", error);
hfs_unlock(VTOC(hfsmp->hfc_filevp));
(void) hfc_btree_close(hfsmp, hfsmp->hfc_filevp);
hfsmp->hfc_filevp = NULL;
goto recording_init_out;
}
started_scan = 1;
filefork = VTOF(hfsmp->hfc_filevp);
starting_temp = random() % HF_TEMP_RANGE;
for (;;) {
error = BTScanNextRecord(&scanstate, 0, (void **)&keyp, (void **)&datap, &dataSize);
if (error) {
if (error == btNotFound)
error = 0;
else
printf("hfs_recording_init: err %d BTScanNext\n", error);
break;
}
if ((datap->recordType != kHFSPlusFileRecord) ||
(dataSize != sizeof(HFSPlusCatalogFile))) {
continue;
}
filep = (HFSPlusCatalogFile *)datap;
filecount++;
if (hfsmp->hfs_flags & HFS_CS_HOTFILE_PIN) {
if (filep->flags & kHFSDoNotFastDevPinMask) {
uncacheable++;
}
if ((filep->flags & kHFSFastDevPinnedMask) == 0) {
continue;
}
} else if (filep->dataFork.totalBlocks == 0) {
continue;
}
if (!(hfsmp->hfs_flags & HFS_CS_HOTFILE_PIN) && !hotextents(hfsmp, &filep->dataFork.extents[0])) {
continue;
}
cnid = filep->fileID;
if (cnid == hfsmp->hfs_jnlfileid || cnid == hfsmp->hfs_jnlinfoblkid) {
continue;
}
uint32_t temp;
if (hfsmp->hfs_flags & HFS_CS_HOTFILE_PIN) {
int rsrc = 0;
temp = (uint32_t)starting_temp++;
if (filep->flags & kHFSAutoCandidateMask) {
temp += MAX_NORMAL_TEMP;
}
if (filep->dataFork.totalBlocks == 0) {
rsrc = 1;
}
error = hfs_pin_catalog_rec (hfsmp, filep, rsrc);
if (error)
break;
} else {
temp = HFC_MINIMUM_TEMPERATURE;
}
key->keyLength = HFC_KEYLENGTH;
key->temperature = temp;
key->fileID = cnid;
key->forkType = 0;
data = 0x3f3f3f3f;
error = BTInsertRecord(filefork, iterator, &record, record.itemSize);
if (error) {
printf("hfs_recording_init: BTInsertRecord failed %d (fileid %d)\n", error, key->fileID);
error = MacToVFSError(error);
break;
}
key->keyLength = HFC_KEYLENGTH;
key->temperature = HFC_LOOKUPTAG;
key->fileID = cnid;
key->forkType = 0;
data = temp;
error = BTInsertRecord(filefork, iterator, &record, record.itemSize);
if (error) {
printf("hfs_recording_init: BTInsertRecord failed %d (fileid %d)\n", error, key->fileID);
error = MacToVFSError(error);
break;
}
inserted++;
}
save_btree_user_info(hfsmp);
(void) BTFlushPath(filefork);
recording_init_out:
if (hfsmp->hfc_filevp) {
hfs_unlock (VTOC(hfsmp->hfc_filevp));
}
if (started_scan) {
(void) BTScanTerminate (&scanstate, &data, &data, &data);
}
if (started_tr) {
hfs_end_transaction(hfsmp);
}
#if HFC_VERBOSE
printf("hfs: %d files identified out of %d (freeblocks is now: %d)\n", inserted, filecount, hfsmp->hfs_hotfile_freeblks);
if (uncacheable) {
printf("hfs: %d files were marked as uncacheable\n", uncacheable);
}
#endif
if (iterator)
FREE(iterator, M_TEMP);
if (hfsmp->hfc_filevp) {
if (hfsmp->hfs_flags & HFS_CS_HOTFILE_PIN) {
hfs_pin_vnode(hfsmp, hfsmp->hfc_filevp, HFS_PIN_IT, NULL, vfs_context_kernel());
}
(void) hfc_btree_close(hfsmp, hfsmp->hfc_filevp);
hfsmp->hfc_filevp = NULL;
}
if (error == 0)
hfsmp->hfc_stage = HFC_IDLE;
lck_mtx_unlock (&hfsmp->hfc_mutex);
return (error);
}
int
hfs_hotfilesync(struct hfsmount *hfsmp, vfs_context_t ctx)
{
if (hfsmp->hfc_stage) {
struct timeval tv;
lck_mtx_lock(&hfsmp->hfc_mutex);
switch (hfsmp->hfc_stage) {
case HFC_IDLE:
(void) hfs_recording_start(hfsmp);
break;
case HFC_RECORDING:
microtime(&tv);
if (tv.tv_sec > hfsmp->hfc_timeout)
(void) hfs_recording_stop(hfsmp);
break;
case HFC_EVICTION:
(void) hotfiles_evict(hfsmp, ctx);
break;
case HFC_ADOPTION:
(void) hotfiles_adopt(hfsmp, ctx);
break;
default:
break;
}
lck_mtx_unlock(&hfsmp->hfc_mutex);
}
return (0);
}
int
hfs_addhotfile(struct vnode *vp)
{
hfsmount_t *hfsmp;
int error;
hfsmp = VTOHFS(vp);
if (hfsmp->hfc_stage != HFC_RECORDING)
return (0);
lck_mtx_lock(&hfsmp->hfc_mutex);
error = hfs_addhotfile_internal(vp);
lck_mtx_unlock(&hfsmp->hfc_mutex);
return (error);
}
static int
hf_ignore_process(const char *pname, size_t maxlen)
{
if ( strncmp(pname, "mds", maxlen) == 0
|| strncmp(pname, "mdworker", maxlen) == 0
|| strncmp(pname, "mds_stores", maxlen) == 0
|| strncmp(pname, "makewhatis", maxlen) == 0) {
return 1;
}
return 0;
}
static int
hfs_addhotfile_internal(struct vnode *vp)
{
hotfile_data_t *hotdata;
hotfile_entry_t *entry;
hfsmount_t *hfsmp;
cnode_t *cp;
filefork_t *ffp;
u_int32_t temperature;
hfsmp = VTOHFS(vp);
if (hfsmp->hfc_stage != HFC_RECORDING)
return (0);
if (!vnode_isreg(vp) || vnode_issystem(vp)) {
return (0);
}
if (VNODE_IS_RSRC(vp)) {
return (0);
}
if ((hotdata = (hotfile_data_t *)hfsmp->hfc_recdata) == NULL) {
return (0);
}
ffp = VTOF(vp);
cp = VTOC(vp);
if (cp->c_attr.ca_recflags & (kHFSFastDevPinnedMask|kHFSDoNotFastDevPinMask)) {
return 0;
}
if (vnode_isdir(vp) || vnode_issystem(vp) || (cp->c_flag & (C_DELETED | C_NOEXISTS))) {
return 0;
}
if ((hfsmp->hfs_flags & HFS_CS_HOTFILE_PIN) && vnode_isfastdevicecandidate(vp)) {
if (cp->c_flag & (C_DELETED|C_NOEXISTS)) {
return 0;
} else if (ffp->ff_blocks == 0 && !(cp->c_bsdflags & UF_COMPRESSED) && !(cp->c_attr.ca_recflags & kHFSFastDevCandidateMask)) {
return 0;
}
char pname[256];
pname[0] = '\0';
proc_selfname(pname, sizeof(pname));
if (hf_ignore_process(pname, sizeof(pname))) {
return 0;
}
temperature = cp->c_fileid; } else {
if ((ffp->ff_bytesread == 0) ||
(ffp->ff_blocks == 0) ||
(ffp->ff_size == 0) ||
(ffp->ff_blocks > hotdata->maxblocks) ||
(cp->c_bsdflags & (UF_NODUMP | UF_COMPRESSED)) ||
(cp->c_atime < hfsmp->hfc_timebase)) {
return (0);
}
temperature = ffp->ff_bytesread / ffp->ff_size;
if (temperature < hotdata->threshold) {
return (0);
}
}
if ((hotdata->activefiles < hfsmp->hfc_maxfiles) ||
(hotdata->coldest == NULL) ||
(temperature >= hotdata->coldest->temperature)) {
++hotdata->refcount;
entry = hf_getnewentry(hotdata);
entry->temperature = temperature;
entry->fileid = cp->c_fileid;
entry->blocks = ffp->ff_blocks ? ffp->ff_blocks : 1;
if (hf_insert(hotdata, entry) == EEXIST) {
entry->right = hotdata->freelist;
hotdata->freelist = entry;
}
--hotdata->refcount;
}
return (0);
}
int
hfs_removehotfile(struct vnode *vp)
{
hotfile_data_t *hotdata;
hfsmount_t *hfsmp;
cnode_t *cp;
filefork_t *ffp;
u_int32_t temperature;
hfsmp = VTOHFS(vp);
if (hfsmp->hfc_stage != HFC_RECORDING)
return (0);
if ((!vnode_isreg(vp)) || vnode_issystem(vp)) {
return (0);
}
ffp = VTOF(vp);
cp = VTOC(vp);
if ((ffp->ff_bytesread == 0) || (ffp->ff_blocks == 0) ||
(ffp->ff_size == 0) || (cp->c_atime < hfsmp->hfc_timebase)) {
return (0);
}
lck_mtx_lock(&hfsmp->hfc_mutex);
if (hfsmp->hfc_stage != HFC_RECORDING)
goto out;
if ((hotdata = (hotfile_data_t *)hfsmp->hfc_recdata) == NULL)
goto out;
temperature = ffp->ff_bytesread / ffp->ff_size;
if (temperature < hotdata->threshold)
goto out;
if (hotdata->coldest && (temperature >= hotdata->coldest->temperature)) {
++hotdata->refcount;
hf_delete(hotdata, VTOC(vp)->c_fileid, temperature);
--hotdata->refcount;
}
out:
lck_mtx_unlock(&hfsmp->hfc_mutex);
return (0);
}
int
hfs_hotfile_deleted(__unused struct vnode *vp)
{
#if 1
return 0;
#else
hotfile_data_t *hotdata;
hfsmount_t *hfsmp;
cnode_t *cp;
filefork_t *filefork;
u_int32_t temperature;
BTreeIterator * iterator = NULL;
FSBufferDescriptor record;
HotFileKey *key;
u_int32_t data;
int error=0;
cp = VTOC(vp);
if (cp == NULL || !(cp->c_attr.ca_recflags & kHFSFastDevPinnedMask)) {
return 0;
}
hfsmp = VTOHFS(vp);
if (!(hfsmp->hfs_flags & HFS_CS_HOTFILE_PIN)) {
return 0;
}
if (hfc_btree_open(hfsmp, &hfsmp->hfc_filevp) != 0 || hfsmp->hfc_filevp == NULL) {
return EINVAL;
}
filefork = VTOF(hfsmp->hfc_filevp);
if (filefork == NULL) {
return 0;
}
MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
if (iterator == NULL) {
return ENOMEM;
}
bzero(iterator, sizeof(*iterator));
key = (HotFileKey*) &iterator->key;
record.bufferAddress = &data;
record.itemSize = sizeof(u_int32_t);
record.itemCount = 1;
key->keyLength = HFC_KEYLENGTH;
key->temperature = HFC_LOOKUPTAG;
key->fileID = cp->c_fileid;
key->forkType = 0;
lck_mtx_lock(&hfsmp->hfc_mutex);
(void) BTInvalidateHint(iterator);
if (BTSearchRecord(filefork, iterator, &record, NULL, iterator) == 0) {
temperature = key->temperature;
hfc_btree_delete_record(hfsmp, iterator, key);
} else {
error = ENOENT;
}
if ((hotdata = (hotfile_data_t *)hfsmp->hfc_recdata) != NULL) {
++hotdata->refcount;
hf_delete(hotdata, cp->c_fileid, cp->c_fileid);
--hotdata->refcount;
}
lck_mtx_unlock(&hfsmp->hfc_mutex);
FREE(iterator, M_TEMP);
hfc_btree_close(hfsmp, hfsmp->hfc_filevp);
return error;
#endif
}
int
hfs_hotfile_adjust_blocks(struct vnode *vp, int64_t num_blocks)
{
hfsmount_t *hfsmp;
if (vp == NULL) {
return 0;
}
hfsmp = VTOHFS(vp);
if (!(hfsmp->hfs_flags & HFS_CS_HOTFILE_PIN) || num_blocks == 0 || vp == NULL) {
return 0;
}
if (!(VTOC(vp)->c_attr.ca_recflags & kHFSFastDevPinnedMask) || (VTOC(vp)->c_attr.ca_recflags & kHFSDoNotFastDevPinMask)) {
return 0;
}
OSAddAtomic(num_blocks, &hfsmp->hfs_hotfile_blk_adjust);
return (0);
}
static int
hfs_hotfile_cur_freeblks(hfsmount_t *hfsmp)
{
if (hfsmp->hfc_stage < HFC_IDLE) {
return 0;
}
int cur_blk_adjust = hfsmp->hfs_hotfile_blk_adjust;
if (cur_blk_adjust) {
OSAddAtomic(-cur_blk_adjust, &hfsmp->hfs_hotfile_blk_adjust);
hfsmp->hfs_hotfile_freeblks += cur_blk_adjust;
}
return hfsmp->hfs_hotfile_freeblks;
}
static int
hotfiles_collect_callback(struct vnode *vp, __unused void *cargs)
{
if ((vnode_isreg(vp)) && !vnode_issystem(vp))
(void) hfs_addhotfile_internal(vp);
return (VNODE_RETURNED);
}
static int
hotfiles_collect(struct hfsmount *hfsmp)
{
struct mount *mp = HFSTOVFS(hfsmp);
if (vfs_busy(mp, LK_NOWAIT))
return (0);
vnode_iterate(mp, 0, hotfiles_collect_callback, (void *)NULL);
vfs_unbusy(mp);
return (0);
}
static int
update_callback(const HotFileKey *key, u_int32_t *data, u_int32_t *state)
{
if (key->temperature == HFC_LOOKUPTAG)
*data = *state;
return (0);
}
static int
hotfiles_refine(struct hfsmount *hfsmp)
{
BTreeIterator * iterator = NULL;
struct mount *mp;
filefork_t * filefork;
hotfilelist_t *listp;
FSBufferDescriptor record;
HotFileKey * key;
u_int32_t data;
int i;
int error = 0;
if ((listp = (hotfilelist_t *)hfsmp->hfc_recdata) == NULL)
return (0);
if (hfsmp->hfs_flags & HFS_CS_HOTFILE_PIN) {
return 0;
}
mp = HFSTOVFS(hfsmp);
MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
if (iterator == NULL) {
error = ENOMEM;
goto out;
}
bzero(iterator, sizeof(*iterator));
key = (HotFileKey*) &iterator->key;
record.bufferAddress = &data;
record.itemSize = sizeof(u_int32_t);
record.itemCount = 1;
if (hfs_start_transaction(hfsmp) != 0) {
error = EINVAL;
goto out;
}
if (hfs_lock(VTOC(hfsmp->hfc_filevp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT) != 0) {
error = EPERM;
goto out1;
}
filefork = VTOF(hfsmp->hfc_filevp);
for (i = 0; i < listp->hfl_count; ++i) {
key->keyLength = HFC_KEYLENGTH;
key->temperature = HFC_LOOKUPTAG;
key->fileID = listp->hfl_hotfile[i].hf_fileid;
key->forkType = 0;
(void) BTInvalidateHint(iterator);
if (BTSearchRecord(filefork, iterator, &record, NULL, iterator) != 0) {
continue;
}
error = BTUpdateRecord(filefork, iterator,
(IterateCallBackProcPtr)update_callback,
&listp->hfl_hotfile[i].hf_temperature);
if (error) {
printf("hfs: hotfiles_refine: BTUpdateRecord failed %d (file %d)\n", error, key->fileID);
error = MacToVFSError(error);
}
key->keyLength = HFC_KEYLENGTH;
key->temperature = data;
key->fileID = listp->hfl_hotfile[i].hf_fileid;
key->forkType = 0;
(void) BTInvalidateHint(iterator);
(void) BTSearchRecord(filefork, iterator, &record, NULL, iterator);
error = BTDeleteRecord(filefork, iterator);
if (error) {
printf("hfs: hotfiles_refine: BTDeleteRecord failed %d (file %d)\n", error, key->fileID);
error = MacToVFSError(error);
break;
}
key->keyLength = HFC_KEYLENGTH;
key->temperature = listp->hfl_hotfile[i].hf_temperature;
key->fileID = listp->hfl_hotfile[i].hf_fileid;
key->forkType = 0;
error = BTInsertRecord(filefork, iterator, &record, record.itemSize);
if (error) {
printf("hfs: hotfiles_refine: BTInsertRecord failed %d (file %d)\n", error, key->fileID);
error = MacToVFSError(error);
break;
}
listp->hfl_hotfile[i].hf_temperature = 0;
listp->hfl_totalblocks -= listp->hfl_hotfile[i].hf_blocks;
}
(void) BTFlushPath(filefork);
hfs_unlock(VTOC(hfsmp->hfc_filevp));
out1:
hfs_end_transaction(hfsmp);
out:
if (iterator)
FREE(iterator, M_TEMP);
return (error);
}
static int
hotfiles_adopt(struct hfsmount *hfsmp, vfs_context_t ctx)
{
BTreeIterator * iterator = NULL;
struct vnode *vp;
filefork_t * filefork;
hotfilelist_t *listp;
FSBufferDescriptor record;
HotFileKey * key;
u_int32_t data;
enum hfc_stage stage;
int fileblocks;
int blksmoved;
int i;
int last;
int error = 0;
int startedtrans = 0;
long starting_temp = random() % HF_TEMP_RANGE;
long temp_adjust = 0;
if ((listp = (hotfilelist_t *)hfsmp->hfc_recdata) == NULL)
return (0);
if (hfsmp->hfc_stage != HFC_ADOPTION) {
return (EBUSY);
}
if (hfs_lock(VTOC(hfsmp->hfc_filevp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT) != 0) {
return (EPERM);
}
MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
if (iterator == NULL) {
hfs_unlock(VTOC(hfsmp->hfc_filevp));
return (ENOMEM);
}
#if HFC_VERBOSE
printf("hfs:%s: hotfiles_adopt: (hfl_next: %d, hotfile start/end block: %d - %d; max/free: %d/%d; maxfiles: %d)\n",
hfsmp->vcbVN,
listp->hfl_next,
hfsmp->hfs_hotfile_start, hfsmp->hfs_hotfile_end,
hfsmp->hfs_hotfile_maxblks, hfsmp->hfs_hotfile_freeblks, hfsmp->hfc_maxfiles);
#endif
stage = hfsmp->hfc_stage;
hfsmp->hfc_stage = HFC_BUSY;
blksmoved = 0;
last = listp->hfl_next + HFC_FILESPERSYNC;
if (last > listp->hfl_count)
last = listp->hfl_count;
bzero(iterator, sizeof(*iterator));
key = (HotFileKey*) &iterator->key;
key->keyLength = HFC_KEYLENGTH;
record.bufferAddress = &data;
record.itemSize = sizeof(u_int32_t);
record.itemCount = 1;
filefork = VTOF(hfsmp->hfc_filevp);
for (i = listp->hfl_next; (i < last) && (blksmoved < HFC_BLKSPERSYNC); ++i) {
if (listp->hfl_hotfile[i].hf_temperature == 0) {
listp->hfl_next++;
continue;
}
if (listp->hfl_hotfile[i].hf_fileid == VTOC(hfsmp->hfc_filevp)->c_fileid) {
listp->hfl_next++;
continue;
}
if (listp->hfl_hotfile[i].hf_fileid < kHFSFirstUserCatalogNodeID) {
listp->hfl_next++;
continue;
}
error = hfs_vget(hfsmp, listp->hfl_hotfile[i].hf_fileid, &vp, 0, 0);
if (error) {
if (error == ENOENT) {
error = 0;
listp->hfl_next++;
continue;
}
break;
}
if (!vnode_isreg(vp)) {
hfs_unlock(VTOC(vp));
vnode_put(vp);
listp->hfl_hotfile[i].hf_temperature = 0;
listp->hfl_next++;
continue;
}
if ( (VTOC(vp)->c_flag & (C_DELETED | C_NOEXISTS))
|| (!(hfsmp->hfs_flags & HFS_CS_HOTFILE_PIN) && hotextents(hfsmp, &VTOF(vp)->ff_extents[0]))
|| (VTOC(vp)->c_attr.ca_recflags & (kHFSFastDevPinnedMask|kHFSDoNotFastDevPinMask))) {
hfs_unlock(VTOC(vp));
vnode_put(vp);
listp->hfl_hotfile[i].hf_temperature = 0;
listp->hfl_next++;
listp->hfl_totalblocks -= listp->hfl_hotfile[i].hf_blocks;
continue;
}
fileblocks = VTOF(vp)->ff_blocks;
if ((hfsmp->hfs_flags & HFS_CS_HOTFILE_PIN) &&
((fileblocks == 0 && !(VTOC(vp)->c_bsdflags & UF_COMPRESSED)) ||
(unsigned int)fileblocks > (HFC_MAXIMUM_FILESIZE / (uint64_t)HFSTOVCB(hfsmp)->blockSize))) {
vnode_clearfastdevicecandidate(vp);
hfs_unlock(VTOC(vp));
vnode_put(vp);
listp->hfl_hotfile[i].hf_temperature = 0;
listp->hfl_next++;
listp->hfl_totalblocks -= listp->hfl_hotfile[i].hf_blocks;
continue;
}
if (fileblocks > hfs_hotfile_cur_freeblks(hfsmp)) {
hfs_unlock(VTOC(vp));
vnode_put(vp);
listp->hfl_next++;
listp->hfl_totalblocks -= fileblocks;
continue;
}
if ((blksmoved > 0) &&
(blksmoved + fileblocks) > HFC_BLKSPERSYNC) {
hfs_unlock(VTOC(vp));
vnode_put(vp);
break;
}
if (VTOC(vp)->c_desc.cd_nameptr)
data = *(const u_int32_t *)(VTOC(vp)->c_desc.cd_nameptr);
else
data = 0x3f3f3f3f;
if (hfsmp->hfs_flags & HFS_CS_HOTFILE_PIN) {
uint32_t pinned_blocks;
if (vnode_isautocandidate(vp)) {
VTOC(vp)->c_attr.ca_recflags |= kHFSAutoCandidateMask;
}
if (VTOC(vp)->c_attr.ca_recflags & kHFSAutoCandidateMask) {
temp_adjust = MAX_NORMAL_TEMP;
} else {
temp_adjust = 0;
}
hfs_unlock(VTOC(vp)); hfs_lock(VTOC(vp), HFS_SHARED_LOCK, HFS_LOCK_ALLOW_NOEXISTS);
error = hfs_pin_vnode(hfsmp, vp, HFS_PIN_IT, &pinned_blocks, ctx);
fileblocks = pinned_blocks;
hfs_unlock(VTOC(vp));
hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_ALLOW_NOEXISTS);
} else {
error = hfs_relocate(vp, hfsmp->hfs_hotfile_start, kauth_cred_get(), current_proc());
}
if (!error) {
VTOC(vp)->c_attr.ca_recflags |= kHFSFastDevPinnedMask;
VTOC(vp)->c_flag |= C_MODIFIED;
} else if ((hfsmp->hfs_flags & HFS_CS_HOTFILE_PIN) && error == EALREADY) {
VTOC(vp)->c_attr.ca_recflags |= kHFSDoNotFastDevPinMask;
VTOC(vp)->c_flag |= C_MODIFIED;
}
hfs_unlock(VTOC(vp));
vnode_put(vp);
if (error) {
#if HFC_VERBOSE
if (error != EALREADY) {
printf("hfs: hotfiles_adopt: could not relocate file %d (err %d)\n", listp->hfl_hotfile[i].hf_fileid, error);
}
#endif
if (last < listp->hfl_count) {
last++;
}
listp->hfl_next++;
continue;
}
hfsmp->hfs_hotfile_freeblks -= fileblocks;
listp->hfl_totalblocks -= fileblocks;
key->keyLength = HFC_KEYLENGTH;
if (hfsmp->hfs_flags & HFS_CS_HOTFILE_PIN) {
listp->hfl_hotfile[i].hf_temperature = (uint32_t)temp_adjust + starting_temp++;
}
key->temperature = listp->hfl_hotfile[i].hf_temperature;
key->fileID = listp->hfl_hotfile[i].hf_fileid;
key->forkType = 0;
if (hfs_start_transaction(hfsmp) != 0) {
error = EINVAL;
break;
}
startedtrans = 1;
error = BTInsertRecord(filefork, iterator, &record, record.itemSize);
if (error) {
int orig_error = error;
error = MacToVFSError(error);
printf("hfs: hotfiles_adopt:1: BTInsertRecord failed %d/%d (fileid %d)\n", error, orig_error, key->fileID);
stage = HFC_IDLE;
break;
}
key->keyLength = HFC_KEYLENGTH;
key->temperature = HFC_LOOKUPTAG;
key->fileID = listp->hfl_hotfile[i].hf_fileid;
key->forkType = 0;
data = listp->hfl_hotfile[i].hf_temperature;
error = BTInsertRecord(filefork, iterator, &record, record.itemSize);
if (error) {
int orig_error = error;
error = MacToVFSError(error);
printf("hfs: hotfiles_adopt:2: BTInsertRecord failed %d/%d (fileid %d)\n", error, orig_error, key->fileID);
stage = HFC_IDLE;
break;
} else {
(void) BTFlushPath(filefork);
blksmoved += fileblocks;
}
listp->hfl_next++;
if (listp->hfl_next >= listp->hfl_count) {
break;
}
if (startedtrans) {
hfs_end_transaction(hfsmp);
startedtrans = 0;
}
if (hfs_hotfile_cur_freeblks(hfsmp) <= 0) {
#if HFC_VERBOSE
printf("hfs: hotfiles_adopt: free space exhausted (%d)\n", hfsmp->hfs_hotfile_freeblks);
#endif
break;
}
}
#if HFC_VERBOSE
printf("hfs: hotfiles_adopt: [%d] adopted %d blocks (%d files left)\n", listp->hfl_next, blksmoved, listp->hfl_count - i);
#endif
if (!startedtrans) {
if (hfs_start_transaction(hfsmp) == 0) {
startedtrans = 1;
}
}
if (startedtrans) {
save_btree_user_info(hfsmp);
(void) BTFlushPath(filefork);
hfs_end_transaction(hfsmp);
startedtrans = 0;
}
hfs_unlock(VTOC(hfsmp->hfc_filevp));
if ((listp->hfl_next >= listp->hfl_count) || (hfsmp->hfs_hotfile_freeblks <= 0)) {
#if HFC_VERBOSE
printf("hfs: hotfiles_adopt: all done relocating %d files\n", listp->hfl_count);
printf("hfs: hotfiles_adopt: %d blocks free in hot file band\n", hfsmp->hfs_hotfile_freeblks);
#endif
stage = HFC_IDLE;
}
FREE(iterator, M_TEMP);
if (stage != HFC_ADOPTION && hfsmp->hfc_filevp) {
(void) hfc_btree_close(hfsmp, hfsmp->hfc_filevp);
hfsmp->hfc_filevp = NULL;
}
hfsmp->hfc_stage = stage;
wakeup((caddr_t)&hfsmp->hfc_stage);
return (error);
}
static int
hotfiles_evict(struct hfsmount *hfsmp, vfs_context_t ctx)
{
BTreeIterator * iterator = NULL;
struct vnode *vp;
HotFileKey * key;
filefork_t * filefork;
hotfilelist_t *listp;
enum hfc_stage stage;
u_int32_t savedtemp;
int blksmoved;
int filesmoved;
int fileblocks;
int error = 0;
int startedtrans = 0;
int bt_op;
if (hfsmp->hfc_stage != HFC_EVICTION) {
return (EBUSY);
}
if ((listp = (hotfilelist_t *)hfsmp->hfc_recdata) == NULL)
return (0);
if (hfs_lock(VTOC(hfsmp->hfc_filevp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT) != 0) {
return (EPERM);
}
#if HFC_VERBOSE
printf("hfs:%s: hotfiles_evict (hotfile start/end block: %d - %d; max/free: %d/%d; maxfiles: %d)\n",
hfsmp->vcbVN,
hfsmp->hfs_hotfile_start, hfsmp->hfs_hotfile_end,
hfsmp->hfs_hotfile_maxblks, hfsmp->hfs_hotfile_freeblks, hfsmp->hfc_maxfiles);
#endif
MALLOC(iterator, BTreeIterator *, sizeof(*iterator), M_TEMP, M_WAITOK);
if (iterator == NULL) {
hfs_unlock(VTOC(hfsmp->hfc_filevp));
return (ENOMEM);
}
stage = hfsmp->hfc_stage;
hfsmp->hfc_stage = HFC_BUSY;
filesmoved = blksmoved = 0;
bt_op = kBTreeFirstRecord;
bzero(iterator, sizeof(*iterator));
key = (HotFileKey*) &iterator->key;
filefork = VTOF(hfsmp->hfc_filevp);
#if HFC_VERBOSE
printf("hfs: hotfiles_evict: reclaim blks %d\n", listp->hfl_reclaimblks);
#endif
while (listp->hfl_reclaimblks > 0 &&
blksmoved < HFC_BLKSPERSYNC &&
filesmoved < HFC_FILESPERSYNC) {
if (BTIterateRecord(filefork, bt_op, iterator, NULL, NULL) != 0) {
#if HFC_VERBOSE
printf("hfs: hotfiles_evict: no more records\n");
#endif
error = 0;
stage = HFC_ADOPTION;
break;
}
if (key->keyLength != HFC_KEYLENGTH) {
printf("hfs: hotfiles_evict: invalid key length %d\n", key->keyLength);
error = EFTYPE;
break;
}
if (key->temperature == HFC_LOOKUPTAG) {
#if HFC_VERBOSE
printf("hfs: hotfiles_evict: ran into thread records\n");
#endif
error = 0;
stage = HFC_ADOPTION;
break;
}
error = hfs_vget(hfsmp, key->fileID, &vp, 0, 0);
if (error) {
if (error == ENOENT) {
goto delete;
} else {
printf("hfs: hotfiles_evict: err %d getting file %d\n",
error, key->fileID);
}
break;
}
if (!vnode_isreg(vp)) {
hfs_unlock(VTOC(vp));
vnode_put(vp);
goto delete;
}
fileblocks = VTOF(vp)->ff_blocks;
if ((blksmoved > 0) &&
(blksmoved + fileblocks) > HFC_BLKSPERSYNC) {
hfs_unlock(VTOC(vp));
vnode_put(vp);
break;
}
if (!hotextents(hfsmp, &VTOF(vp)->ff_extents[0]) && !(VTOC(vp)->c_attr.ca_recflags & kHFSFastDevPinnedMask)) {
#if HFC_VERBOSE
printf("hfs: hotfiles_evict: file %d isn't hot!\n", key->fileID);
#endif
hfs_unlock(VTOC(vp));
vnode_put(vp);
goto delete;
}
if (hfsmp->hfs_flags & HFS_CS_HOTFILE_PIN) {
uint32_t pinned_blocks;
hfs_unlock(VTOC(vp)); hfs_lock(VTOC(vp), HFS_SHARED_LOCK, HFS_LOCK_ALLOW_NOEXISTS);
error = hfs_pin_vnode(hfsmp, vp, HFS_UNPIN_IT, &pinned_blocks, ctx);
fileblocks = pinned_blocks;
if (!error) {
hfs_unlock(VTOC(vp));
hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_ALLOW_NOEXISTS);
}
} else {
error = hfs_relocate(vp, HFSTOVCB(hfsmp)->nextAllocation, vfs_context_ucred(ctx), vfs_context_proc(ctx));
}
if (error) {
#if HFC_VERBOSE
printf("hfs: hotfiles_evict: err %d relocating file %d\n", error, key->fileID);
#endif
hfs_unlock(VTOC(vp));
vnode_put(vp);
bt_op = kBTreeNextRecord;
goto next;
} else {
VTOC(vp)->c_attr.ca_recflags &= ~kHFSFastDevPinnedMask;
VTOC(vp)->c_flag |= C_MODIFIED;
}
hfs_unlock(VTOC(vp));
vnode_put(vp);
hfsmp->hfs_hotfile_freeblks += fileblocks;
listp->hfl_reclaimblks -= fileblocks;
if (listp->hfl_reclaimblks < 0)
listp->hfl_reclaimblks = 0;
blksmoved += fileblocks;
filesmoved++;
delete:
if (hfs_start_transaction(hfsmp) != 0) {
error = EINVAL;
break;
}
startedtrans = 1;
error = BTDeleteRecord(filefork, iterator);
if (error) {
error = MacToVFSError(error);
break;
}
savedtemp = key->temperature;
key->temperature = HFC_LOOKUPTAG;
error = BTDeleteRecord(filefork, iterator);
if (error) {
error = MacToVFSError(error);
break;
}
key->temperature = savedtemp;
next:
(void) BTFlushPath(filefork);
if (startedtrans) {
hfs_end_transaction(hfsmp);
startedtrans = 0;
}
}
#if HFC_VERBOSE
printf("hfs: hotfiles_evict: moved %d files (%d blks, %d to go)\n", filesmoved, blksmoved, listp->hfl_reclaimblks);
#endif
if (startedtrans) {
save_btree_user_info(hfsmp);
(void) BTFlushPath(filefork);
hfs_end_transaction(hfsmp);
startedtrans = 0;
}
hfs_unlock(VTOC(hfsmp->hfc_filevp));
if (listp->hfl_reclaimblks <= 0) {
stage = HFC_ADOPTION;
#if HFC_VERBOSE
printf("hfs: hotfiles_evict: %d blocks free in hot file band\n", hfsmp->hfs_hotfile_freeblks);
#endif
}
FREE(iterator, M_TEMP);
hfsmp->hfc_stage = stage;
wakeup((caddr_t)&hfsmp->hfc_stage);
return (error);
}
static int
hotfiles_age(struct hfsmount *hfsmp)
{
BTreeInfoRec btinfo;
BTreeIterator * iterator = NULL;
BTreeIterator * prev_iterator;
FSBufferDescriptor record;
FSBufferDescriptor prev_record;
HotFileKey * key;
HotFileKey * prev_key;
filefork_t * filefork;
u_int32_t data;
u_int32_t prev_data;
u_int32_t newtemp;
int error;
int i;
int numrecs;
int aged = 0;
u_int16_t reclen;
if (hfsmp->hfs_flags & HFS_CS_HOTFILE_PIN) {
return 0;
}
MALLOC(iterator, BTreeIterator *, 2 * sizeof(*iterator), M_TEMP, M_WAITOK);
if (iterator == NULL) {
error = ENOMEM;
goto out2;
}
bzero(iterator, 2 * sizeof(*iterator));
key = (HotFileKey*) &iterator->key;
prev_iterator = &iterator[1];
prev_key = (HotFileKey*) &prev_iterator->key;
record.bufferAddress = &data;
record.itemSize = sizeof(data);
record.itemCount = 1;
prev_record.bufferAddress = &prev_data;
prev_record.itemSize = sizeof(prev_data);
prev_record.itemCount = 1;
if (hfs_start_transaction(hfsmp) != 0) {
error = EINVAL;
goto out2;
}
if (hfs_lock(VTOC(hfsmp->hfc_filevp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT) != 0) {
error = EPERM;
goto out1;
}
filefork = VTOF(hfsmp->hfc_filevp);
error = BTGetInformation(filefork, 0, &btinfo);
if (error) {
error = MacToVFSError(error);
goto out;
}
if (btinfo.numRecords < 2) {
error = 0;
goto out;
}
numrecs = (btinfo.numRecords /= 2) - 1;
error = BTIterateRecord(filefork, kBTreeFirstRecord, iterator, &record, &reclen);
if (error) {
printf("hfs_agehotfiles: BTIterateRecord: %d\n", error);
error = MacToVFSError(error);
goto out;
}
bcopy(iterator, prev_iterator, sizeof(BTreeIterator));
prev_data = data;
for (i = 0; i < numrecs; ++i) {
error = BTIterateRecord(filefork, kBTreeNextRecord, iterator, &record, &reclen);
if (error == 0) {
if (key->temperature < prev_key->temperature) {
printf("hfs_agehotfiles: out of order keys!\n");
error = EFTYPE;
break;
}
if (reclen != sizeof(data)) {
printf("hfs_agehotfiles: invalid record length %d\n", reclen);
error = EFTYPE;
break;
}
if (key->keyLength != HFC_KEYLENGTH) {
printf("hfs_agehotfiles: invalid key length %d\n", key->keyLength);
error = EFTYPE;
break;
}
} else if ((error == fsBTEndOfIterationErr || error == fsBTRecordNotFoundErr) &&
(i == (numrecs - 1))) {
error = 0;
} else if (error) {
printf("hfs_agehotfiles: %d of %d BTIterateRecord: %d\n", i, numrecs, error);
error = MacToVFSError(error);
break;
}
if (prev_key->temperature == HFC_LOOKUPTAG) {
#if HFC_VERBOSE
printf("hfs_agehotfiles: ran into thread record\n");
#endif
error = 0;
break;
}
error = BTDeleteRecord(filefork, prev_iterator);
if (error) {
printf("hfs_agehotfiles: BTDeleteRecord failed %d (file %d)\n", error, prev_key->fileID);
error = MacToVFSError(error);
break;
}
newtemp = MAX(prev_key->temperature >> 1, 4);
prev_key->temperature = newtemp;
error = BTInsertRecord(filefork, prev_iterator, &prev_record, prev_record.itemSize);
if (error) {
printf("hfs_agehotfiles: BTInsertRecord failed %d (file %d)\n", error, prev_key->fileID);
error = MacToVFSError(error);
break;
}
++aged;
prev_key->temperature = HFC_LOOKUPTAG;
error = BTUpdateRecord(filefork, prev_iterator,
(IterateCallBackProcPtr)update_callback,
&newtemp);
if (error) {
printf("hfs_agehotfiles: %d of %d BTUpdateRecord failed %d (file %d, %d)\n",
i, numrecs, error, prev_key->fileID, newtemp);
error = MacToVFSError(error);
}
bcopy(iterator, prev_iterator, sizeof(BTreeIterator));
prev_data = data;
}
#if HFC_VERBOSE
if (error == 0)
printf("hfs_agehotfiles: aged %d records out of %d\n", aged, btinfo.numRecords);
#endif
(void) BTFlushPath(filefork);
out:
hfs_unlock(VTOC(hfsmp->hfc_filevp));
out1:
hfs_end_transaction(hfsmp);
out2:
if (iterator)
FREE(iterator, M_TEMP);
return (error);
}
static int
hotextents(struct hfsmount *hfsmp, HFSPlusExtentDescriptor * extents)
{
u_int32_t b1, b2;
int i;
int inside = 0;
for (i = 0; i < kHFSPlusExtentDensity; ++i) {
b1 = extents[i].startBlock;
if (b1 == 0)
break;
b2 = b1 + extents[i].blockCount - 1;
if ((b1 >= hfsmp->hfs_hotfile_start &&
b2 <= hfsmp->hfs_hotfile_end) ||
(b1 < hfsmp->hfs_hotfile_end &&
b2 > hfsmp->hfs_hotfile_end)) {
inside = 1;
break;
}
}
return (inside);
}
static int
hfc_btree_open(struct hfsmount *hfsmp, struct vnode **vpp)
{
return hfc_btree_open_ext(hfsmp, vpp, 0);
}
static int
hfc_btree_open_ext(struct hfsmount *hfsmp, struct vnode **vpp, int ignore_btree_errs)
{
proc_t p;
struct vnode *vp;
struct cat_desc cdesc;
struct cat_attr cattr;
struct cat_fork cfork;
static char filename[] = HFC_FILENAME;
int error;
int retry = 0;
int lockflags;
int newvnode_flags = 0;
*vpp = NULL;
p = current_proc();
bzero(&cdesc, sizeof(cdesc));
cdesc.cd_parentcnid = kRootDirID;
cdesc.cd_nameptr = (const u_int8_t *)filename;
cdesc.cd_namelen = strlen(filename);
lockflags = hfs_systemfile_lock(hfsmp, SFL_CATALOG, HFS_SHARED_LOCK);
error = cat_lookup(hfsmp, &cdesc, 0, 0, &cdesc, &cattr, &cfork, NULL);
hfs_systemfile_unlock(hfsmp, lockflags);
if (error) {
printf("hfs: hfc_btree_open: cat_lookup error %d\n", error);
return (error);
}
again:
cdesc.cd_flags |= CD_ISMETA;
error = hfs_getnewvnode(hfsmp, NULL, NULL, &cdesc, 0, &cattr,
&cfork, &vp, &newvnode_flags);
if (error) {
printf("hfs: hfc_btree_open: hfs_getnewvnode error %d\n", error);
cat_releasedesc(&cdesc);
return (error);
}
if (!vnode_issystem(vp)) {
#if HFC_VERBOSE
printf("hfs: hfc_btree_open: file has UBC, try again\n");
#endif
hfs_unlock(VTOC(vp));
vnode_recycle(vp);
vnode_put(vp);
if (retry++ == 0)
goto again;
else
return (EBUSY);
}
error = BTOpenPath(VTOF(vp), (KeyCompareProcPtr) hfc_comparekeys);
if (error) {
if (!ignore_btree_errs) {
printf("hfs: hfc_btree_open: BTOpenPath error %d; filesize %lld\n", error, VTOF(vp)->ff_size);
error = MacToVFSError(error);
} else {
error = 0;
}
}
hfs_unlock(VTOC(vp));
if (error == 0) {
*vpp = vp;
vnode_ref(vp);
}
vnode_put(vp);
if (!vnode_issystem(vp))
panic("hfs: hfc_btree_open: not a system file (vp = %p)", vp);
HotFilesInfo hotfileinfo;
if (error == 0 && (hfsmp->hfs_flags & HFS_CS_HOTFILE_PIN)) {
if ((BTGetUserData(VTOF(vp), &hotfileinfo, sizeof(hotfileinfo)) == 0) && (SWAP_BE32 (hotfileinfo.magic) == HFC_MAGIC)) {
if (hfsmp->hfs_hotfile_freeblks == 0) {
hfsmp->hfs_hotfile_freeblks = hfsmp->hfs_hotfile_maxblks - SWAP_BE32 (hotfileinfo.usedblocks);
}
hfs_hotfile_cur_freeblks(hfsmp); }
}
return (error);
}
static int
hfc_btree_close(struct hfsmount *hfsmp, struct vnode *vp)
{
proc_t p = current_proc();
int error = 0;
if (hfsmp->jnl) {
hfs_flush(hfsmp, HFS_FLUSH_JOURNAL);
}
if (vnode_get(vp) == 0) {
error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT);
if (error == 0) {
(void) hfs_fsync(vp, MNT_WAIT, 0, p);
error = BTClosePath(VTOF(vp));
hfs_unlock(VTOC(vp));
}
vnode_rele(vp);
vnode_recycle(vp);
vnode_put(vp);
}
return (error);
}
static int
hfc_btree_delete_record(struct hfsmount *hfsmp, BTreeIterator *iterator, HotFileKey *key)
{
int error;
filefork_t *filefork=VTOF(hfsmp->hfc_filevp);
if (hfs_start_transaction(hfsmp) != 0) {
return EINVAL;
}
error = BTDeleteRecord(filefork, iterator);
if (error) {
error = MacToVFSError(error);
printf("hfs: failed to delete record for file-id %d : err %d\n", key->fileID, error);
goto out;
}
int savedtemp;
savedtemp = key->temperature;
key->temperature = HFC_LOOKUPTAG;
error = BTDeleteRecord(filefork, iterator);
if (error) {
error = MacToVFSError(error);
printf("hfs:2: failed to delete record for file-id %d : err %d\n", key->fileID, error);
}
key->temperature = savedtemp;
(void) BTFlushPath(filefork);
out:
hfs_end_transaction(hfsmp);
return error;
}
static int
hfc_btree_delete(struct hfsmount *hfsmp)
{
struct vnode *dvp = NULL;
vfs_context_t ctx = vfs_context_current();
struct vnode_attr va;
struct componentname cname;
static char filename[] = HFC_FILENAME;
int error;
error = VFS_ROOT(HFSTOVFS(hfsmp), &dvp, ctx);
if (error) {
return (error);
}
cname.cn_nameiop = DELETE;
cname.cn_flags = ISLASTCN;
cname.cn_context = ctx;
cname.cn_pnbuf = filename;
cname.cn_pnlen = sizeof(filename);
cname.cn_nameptr = filename;
cname.cn_namelen = strlen(filename);
cname.cn_hash = 0;
cname.cn_consume = 0;
VATTR_INIT(&va);
VATTR_SET(&va, va_type, VREG);
VATTR_SET(&va, va_mode, S_IFREG | S_IRUSR | S_IWUSR);
VATTR_SET(&va, va_uid, 0);
VATTR_SET(&va, va_gid, 0);
if (hfs_start_transaction(hfsmp) != 0) {
error = EINVAL;
goto out;
}
error = VNOP_REMOVE(dvp, hfsmp->hfc_filevp, &cname, 0, ctx);
if (error) {
printf("hfs: error %d removing HFBT on %s\n", error, HFSTOVCB(hfsmp)->vcbVN);
}
hfs_end_transaction(hfsmp);
out:
if (dvp) {
vnode_put(dvp);
dvp = NULL;
}
return 0;
}
static int
hfc_btree_create(struct hfsmount *hfsmp, unsigned int nodesize, unsigned int entries)
{
struct vnode *dvp = NULL;
struct vnode *vp = NULL;
struct cnode *cp = NULL;
vfs_context_t ctx = vfs_context_current();
struct vnode_attr va;
struct componentname cname;
static char filename[] = HFC_FILENAME;
int error;
if (hfsmp->hfc_filevp)
panic("hfs: hfc_btree_create: hfc_filevp exists (vp = %p)", hfsmp->hfc_filevp);
error = VFS_ROOT(HFSTOVFS(hfsmp), &dvp, ctx);
if (error) {
return (error);
}
cname.cn_nameiop = CREATE;
cname.cn_flags = ISLASTCN;
cname.cn_context = ctx;
cname.cn_pnbuf = filename;
cname.cn_pnlen = sizeof(filename);
cname.cn_nameptr = filename;
cname.cn_namelen = strlen(filename);
cname.cn_hash = 0;
cname.cn_consume = 0;
VATTR_INIT(&va);
VATTR_SET(&va, va_type, VREG);
VATTR_SET(&va, va_mode, S_IFREG | S_IRUSR | S_IWUSR);
VATTR_SET(&va, va_uid, 0);
VATTR_SET(&va, va_gid, 0);
if (hfs_start_transaction(hfsmp) != 0) {
error = EINVAL;
goto out;
}
error = VNOP_CREATE(dvp, &vp, &cname, &va, ctx);
if (error) {
printf("hfs: error %d creating HFBT on %s\n", error, HFSTOVCB(hfsmp)->vcbVN);
goto out;
}
if (dvp) {
vnode_put(dvp);
dvp = NULL;
}
if ((error = hfs_lock(VTOC(vp), HFS_EXCLUSIVE_LOCK, HFS_LOCK_DEFAULT))) {
goto out;
}
cp = VTOC(vp);
if (!vnode_isreg(vp) || cp->c_linkcount != 1) {
error = EFTYPE;
goto out;
}
printf("hfs: created HFBT on %s\n", HFSTOVCB(hfsmp)->vcbVN);
if (VTOF(vp)->ff_size < nodesize) {
caddr_t buffer;
u_int16_t *index;
u_int16_t offset;
BTNodeDescriptor *ndp;
BTHeaderRec *bthp;
HotFilesInfo *hotfileinfo;
int nodecnt;
int filesize;
int entirespernode;
((FndrFileInfo *)&cp->c_finderinfo[0])->fdFlags |=
SWAP_BE16 (kIsInvisible + kNameLocked);
if (kmem_alloc(kernel_map, (vm_offset_t *)&buffer, nodesize, VM_KERN_MEMORY_FILE)) {
error = ENOMEM;
goto out;
}
bzero(buffer, nodesize);
index = (u_int16_t *)buffer;
entirespernode = (nodesize - sizeof(BTNodeDescriptor) - 2) /
(sizeof(HotFileKey) + 6);
nodecnt = 2 + howmany(entries * 2, entirespernode);
nodecnt = roundup(nodecnt, 8);
filesize = nodecnt * nodesize;
ndp = (BTNodeDescriptor *)buffer;
ndp->kind = kBTHeaderNode;
ndp->numRecords = SWAP_BE16 (3);
offset = sizeof(BTNodeDescriptor);
index[(nodesize / 2) - 1] = SWAP_BE16 (offset);
bthp = (BTHeaderRec *)((u_int8_t *)buffer + offset);
bthp->nodeSize = SWAP_BE16 (nodesize);
bthp->totalNodes = SWAP_BE32 (filesize / nodesize);
bthp->freeNodes = SWAP_BE32 (nodecnt - 1);
bthp->clumpSize = SWAP_BE32 (filesize);
bthp->btreeType = kUserBTreeType;
bthp->attributes |= SWAP_BE32 (kBTBigKeysMask);
bthp->maxKeyLength = SWAP_BE16 (HFC_KEYLENGTH);
offset += sizeof(BTHeaderRec);
index[(nodesize / 2) - 2] = SWAP_BE16 (offset);
hotfileinfo = (HotFilesInfo *)((u_int8_t *)buffer + offset);
hotfileinfo->magic = SWAP_BE32 (HFC_MAGIC);
hotfileinfo->version = SWAP_BE32 (HFC_VERSION);
hotfileinfo->duration = SWAP_BE32 (HFC_DEFAULT_DURATION);
hotfileinfo->timebase = 0;
hotfileinfo->timeleft = 0;
hotfileinfo->threshold = SWAP_BE32 (HFC_MINIMUM_TEMPERATURE);
hotfileinfo->maxfileblks = SWAP_BE32 (HFC_MAXIMUM_FILESIZE / HFSTOVCB(hfsmp)->blockSize);
if (hfsmp->hfs_flags & HFS_CS_HOTFILE_PIN) {
if (hfsmp->hfs_hotfile_freeblks == 0) {
hfsmp->hfs_hotfile_freeblks = hfsmp->hfs_hotfile_maxblks;
}
hotfileinfo->usedblocks = SWAP_BE32 (hfsmp->hfs_hotfile_maxblks - hfsmp->hfs_hotfile_freeblks);
} else {
hotfileinfo->maxfilecnt = SWAP_BE32 (HFC_DEFAULT_FILE_COUNT);
}
strlcpy((char *)hotfileinfo->tag, hfc_tag,
sizeof hotfileinfo->tag);
offset += kBTreeHeaderUserBytes;
index[(nodesize / 2) - 3] = SWAP_BE16 (offset);
*((u_int8_t *)buffer + offset) = 0x80;
offset += nodesize - sizeof(BTNodeDescriptor) - sizeof(BTHeaderRec)
- kBTreeHeaderUserBytes - (4 * sizeof(int16_t));
index[(nodesize / 2) - 4] = SWAP_BE16 (offset);
vnode_setnoflush(vp);
error = hfs_truncate(vp, (off_t)filesize, IO_NDELAY, 0, ctx);
if (error) {
printf("hfs: error %d growing HFBT on %s\n", error, HFSTOVCB(hfsmp)->vcbVN);
goto out;
}
cp->c_flag |= C_ZFWANTSYNC;
cp->c_zftimeout = 1;
if (error == 0) {
struct vnop_write_args args;
uio_t auio;
auio = uio_create(1, 0, UIO_SYSSPACE, UIO_WRITE);
uio_addiov(auio, (uintptr_t)buffer, nodesize);
args.a_desc = &vnop_write_desc;
args.a_vp = vp;
args.a_uio = auio;
args.a_ioflag = 0;
args.a_context = ctx;
hfs_unlock(cp);
cp = NULL;
error = hfs_vnop_write(&args);
if (error)
printf("hfs: error %d writing HFBT on %s\n", error, HFSTOVCB(hfsmp)->vcbVN);
uio_free(auio);
}
kmem_free(kernel_map, (vm_offset_t)buffer, nodesize);
}
out:
hfs_end_transaction(hfsmp);
if (dvp) {
vnode_put(dvp);
}
if (vp) {
if (cp)
hfs_unlock(cp);
vnode_recycle(vp);
vnode_put(vp);
}
return (error);
}
static int
hfc_comparekeys(HotFileKey *searchKey, HotFileKey *trialKey)
{
if (searchKey->temperature == trialKey->temperature) {
if (searchKey->fileID == trialKey->fileID) {
if (searchKey->forkType == trialKey->forkType) {
return (0);
} else if (searchKey->forkType > trialKey->forkType) {
return (1);
}
} else if (searchKey->fileID > trialKey->fileID) {
return (1);
}
} else if (searchKey->temperature > trialKey->temperature) {
return (1);
}
return (-1);
}
#if HFC_DEBUG
static hotfile_entry_t *
hf_lookup(hotfile_data_t *hotdata, u_int32_t fileid, u_int32_t temperature)
{
hotfile_entry_t *entry = hotdata->rootentry;
while (entry &&
entry->temperature != temperature &&
entry->fileid != fileid) {
if (temperature > entry->temperature)
entry = entry->right;
else if (temperature < entry->temperature)
entry = entry->left;
else if (fileid > entry->fileid)
entry = entry->right;
else
entry = entry->left;
}
return (entry);
}
#endif
static int
hf_insert(hotfile_data_t *hotdata, hotfile_entry_t *newentry)
{
hotfile_entry_t *entry = hotdata->rootentry;
u_int32_t fileid = newentry->fileid;
u_int32_t temperature = newentry->temperature;
if (entry == NULL) {
hotdata->rootentry = newentry;
hotdata->coldest = newentry;
hotdata->activefiles++;
return 0;
}
while (entry) {
if (temperature > entry->temperature) {
if (entry->right) {
entry = entry->right;
} else {
entry->right = newentry;
break;
}
} else if (temperature < entry->temperature) {
if (entry->left) {
entry = entry->left;
} else {
entry->left = newentry;
break;
}
} else if (fileid > entry->fileid) {
if (entry->right) {
entry = entry->right;
} else {
if (entry->fileid != fileid)
entry->right = newentry;
break;
}
} else {
if (entry->left) {
entry = entry->left;
} else {
if (entry->fileid != fileid) {
entry->left = newentry;
} else {
return EEXIST;
}
break;
}
}
}
hotdata->activefiles++;
return 0;
}
static hotfile_entry_t *
hf_coldest(hotfile_data_t *hotdata)
{
hotfile_entry_t *entry = hotdata->rootentry;
if (entry) {
while (entry->left)
entry = entry->left;
}
return (entry);
}
static hotfile_entry_t *
hf_hottest(hotfile_data_t *hotdata)
{
hotfile_entry_t *entry = hotdata->rootentry;
if (entry) {
while (entry->right)
entry = entry->right;
}
return (entry);
}
static void
hf_delete(hotfile_data_t *hotdata, u_int32_t fileid, u_int32_t temperature)
{
hotfile_entry_t *entry, *parent, *next;
parent = NULL;
entry = hotdata->rootentry;
while (entry &&
entry->temperature != temperature &&
entry->fileid != fileid) {
parent = entry;
if (temperature > entry->temperature)
entry = entry->right;
else if (temperature < entry->temperature)
entry = entry->left;
else if (fileid > entry->fileid)
entry = entry->right;
else
entry = entry->left;
}
if (entry) {
if ((next = entry->right)) {
hotfile_entry_t *pnextl, *psub;
psub = next;
while ((pnextl = psub->left))
psub = pnextl;
psub->left = entry->left;
} else {
next = entry->left;
}
if (parent == NULL)
hotdata->rootentry = next;
else if (parent->left == entry)
parent->left = next;
else
parent->right = next;
entry->left = 0;
entry->fileid = 0;
entry->temperature = 0;
entry->right = hotdata->freelist;
hotdata->freelist = entry;
hotdata->activefiles--;
if (hotdata->coldest == entry || hotdata->coldest == NULL) {
hotdata->coldest = hf_coldest(hotdata);
}
}
}
static hotfile_entry_t *
hf_getnewentry(hotfile_data_t *hotdata)
{
hotfile_entry_t * entry;
if (hotdata->freelist == NULL) {
entry = hf_coldest(hotdata);
hf_delete(hotdata, entry->fileid, entry->temperature);
}
entry = hotdata->freelist;
hotdata->freelist = entry->right;
entry->right = 0;
return (entry);
}
static void
hf_getsortedlist(hotfile_data_t * hotdata, hotfilelist_t *sortedlist)
{
int i = 0;
hotfile_entry_t *entry;
while ((entry = hf_hottest(hotdata)) != NULL) {
sortedlist->hfl_hotfile[i].hf_fileid = entry->fileid;
sortedlist->hfl_hotfile[i].hf_temperature = entry->temperature;
sortedlist->hfl_hotfile[i].hf_blocks = entry->blocks;
sortedlist->hfl_totalblocks += entry->blocks;
++i;
hf_delete(hotdata, entry->fileid, entry->temperature);
}
sortedlist->hfl_count = i;
#if HFC_VERBOSE
printf("hfs: hf_getsortedlist returning %d entries w/%d total blocks\n", i, sortedlist->hfl_totalblocks);
#endif
}
#if HFC_DEBUG
static void
hf_maxdepth(hotfile_entry_t * root, int depth, int *maxdepth)
{
if (root) {
depth++;
if (depth > *maxdepth)
*maxdepth = depth;
hf_maxdepth(root->left, depth, maxdepth);
hf_maxdepth(root->right, depth, maxdepth);
}
}
static void
hf_printtree(hotfile_entry_t * root)
{
if (root) {
hf_printtree(root->left);
printf("hfs: temperature: % 8d, fileid %d\n", root->temperature, root->fileid);
hf_printtree(root->right);
}
}
#endif