1#include <uuid/uuid.h>
2
3/*
4 * Files.
5 */
6#define BC_ROOT_PLAYLIST	"/var/db/BootCache.playlist"
7#define BC_ROOT_EXTRA_LOGICAL_PLAYLIST "/var/db/BootCaches/RootExtra.logical_playlist"
8#define BC_LOGIN_EXTRA_LOGICAL_PLAYLIST "/var/db/BootCaches/LoginExtra.logical_playlist"
9#define BC_ROOT_LOGICAL_PLAYLIST "/var/db/BootCaches/Root.logical_playlist"
10#define BC_LOGIN_LOGICAL_PLAYLIST "/var/db/BootCaches/Login.logical_playlist"
11#define BC_BOOT_BACKINGFILE	"/var/db/BootCache.data"
12#define BC_BOOT_FLAGFILE	"/var/db/BootCache.flag"
13#define BC_BOOT_STATFILE	"/tmp/BootCache.statistics"
14#define BC_BOOT_HISTFILE	"/tmp/BootCache.history"
15#define BC_BOOT_LOGFILE		"/tmp/BootCache.log"
16#define BC_CONTROL_TOOL		"/usr/sbin/BootCacheControl"
17#define BC_KEXTUNLOAD		"/sbin/kextunload"
18#define BC_BUNDLE_ID		"com.apple.BootCache"
19
20/*
21 * Files we read in during every boot.
22 */
23#define BC_DYLD_SHARED_CACHE    "/var/db/dyld/dyld_shared_cache_x86_64"
24#define BC_DYLD_SHARED_CACHE_H  "/var/db/dyld/dyld_shared_cache_x86_64h"
25#define BC_DYLD_SHARED_CACHE_32 "/var/db/dyld/dyld_shared_cache_i386"
26
27/*
28 * If defined, entries/extents are sorted by their location on disk
29 * (rather than chronologically or some other sorting).
30 */
31#define BOOTCACHE_ENTRIES_SORTED_BY_DISK_OFFSET
32
33/*
34 * Command stucture, passed via sysctl.
35 */
36struct BC_command {
37	/* magic number identifies the structure */
38	int	bc_magic;
39#define	BC_MAGIC_0	0x10102021	/* old version with DEV_BSIZE blocks */
40#define	BC_MAGIC_1	0x10102021	/* all disk addresses in bytes */
41#define	BC_MAGIC_2	0x10102021	/* added flags field to playlist entry */
42#define	BC_MAGIC_3	0x10102022	/* major restructure, added mounts */
43#define	BC_MAGIC	0x10102023	/* add pid/shared flag to history */
44
45	/* opcode determines function of this command */
46	int	bc_opcode;
47#define BC_OP_START    0x01
48#define BC_OP_STOP     0x02
49#define BC_OP_HISTORY  0x03
50#define BC_OP_STATS    0x04
51#define BC_OP_TAG      0x05
52#define BC_OP_JETTISON 0x06
53#define BC_OP_MOUNT    0x07
54#define BC_OP_TEST     0x08
55
56	/* user-space data buffers, use varies with opcode */
57	unsigned int bc_data1_size;
58	unsigned int bc_data2_size;
59	u_int64_t    bc_data1;
60	u_int64_t    bc_data2;
61};
62
63#define BC_SYSCTL	"kern.BootCache"
64
65/*
66 * BC_OP_START - start the cache engine with an optional playlist.
67 *
68 * If bc_data1 is not NULL, it points to bc_data1_size bytes of
69 * BC_playlist_mount structures.
70 *
71 * If bc_data2 is not NULL, it points to bc_data2_size bytes of
72 * BC_playlist_entry structures.
73 */
74
75/*
76 * On-disk playlist header, also used in the preload case.
77 */
78struct BC_playlist_header {
79	int	ph_magic;
80#define PH_MAGIC_0	0xa1b2c3d4	/* old format with no blocksize */
81#define PH_MAGIC_1	0xa1b2c3d5	/* blocksize, offsets in bytes */
82#define PH_MAGIC_2	0xa1b2c3d6	/* added flags field */
83#define PH_MAGIC_3	0xa1b2c3d7	/* added non-root mounts */
84#define PH_MAGIC	0xa1b2c3d8	/* added low-priority extents */
85	int	ph_nmounts;
86	int ph_nentries;
87};
88
89/*
90 * Playlist mounts represent mount points to be cached.
91 */
92struct BC_playlist_mount {
93	uuid_t pm_uuid;         /* The UUID of the mount */
94	int    pm_nentries;     /* The number of playlist entries that refer to this mount */
95};
96
97/*
98 * Playlist entries represent regions of the mount points to be cached.
99 */
100struct BC_playlist_entry {
101	u_int64_t	pe_offset;    /* block address */
102	u_int64_t	pe_length;    /* size */
103	u_int16_t	pe_batch;     /* batch number */
104	u_int16_t	pe_flags;     /* flags */
105#define BC_PE_LOWPRIORITY 0x1
106#define BC_PE_SHARED      0x2
107	u_int32_t   pe_mount_idx; /* index of mount in mount array */
108};
109
110/* number of entries we copyin at a time */
111#define BC_PLC_CHUNK	512
112
113/* sanity check for the upper bound on number of entries and batches */
114#define BC_MAXENTRIES	100000
115#define BC_MAXBATCHES   255
116
117/*
118 * BC_OP_STOP - stop the cache engine.
119 *
120 * The history list mount size is returned in bc_data1_size and
121 * entry list size is returned in bc_data2_size (in bytes).  If the history
122 * list was truncated, bc_data1_size will be 0 and BC_OP_HISTORY must still be
123 * called to clear the buffer.
124 */
125
126/*
127 * BC_OP_JETTISON - jettison the cache.
128 *
129 * No parameters
130 */
131
132/*
133 * BC_OP_HISTORY - copy out history and clear.
134 *
135 * The kernel's copy of the mount and entry history is copied into the array of
136 * BC_history_mount and BC_history_entry structures at bc_data1 and bc_data2,
137 * respectively. The cache must previously have been stopped before calling this
138 * interface out.
139 */
140
141/*
142 * Playlist mounts represent mount points to be cached.
143 */
144struct BC_history_mount {
145	uuid_t hm_uuid;         /* The UUID of the mount */
146	int hm_nentries;        /* The number of history entries that refer to this mount */
147};
148
149struct BC_history_entry {
150	u_int64_t	he_offset;	  /* data offset on device */
151	u_int64_t	he_length;	  /* length of data */
152	u_int32_t	he_pid;       /* pid of the process that issued this IO */
153	u_int16_t	he_flags;
154#define	BC_HE_HIT    0x1		  /* read was satisfied from cache */
155#define BC_HE_WRITE  0x2		  /* write-through operation */
156#define BC_HE_TAG    0x4		  /* userland-set tag */
157#define BC_HE_SHARED 0x8		  /* IO was from the shared cache */
158	u_int16_t   he_mount_idx; /* index of the mount this read was on */
159};
160
161
162/* Number of disks and batches we can collect statistics on */
163#define STAT_DISKMAX	8
164#define STAT_MOUNTMAX	32
165#define STAT_BATCHMAX	16
166
167/*
168 * BC_OP_STATS - return statistics.
169 *
170 * The kernel's copy of the statistics is copied into
171 * the BC_statictics structure at bc_data1
172 */
173struct BC_statistics {
174	/* readahead */
175	u_int   ss_readahead_threads; /* number of readahead threads */
176	u_int	ss_initiated_reads;	/* read operations we initiated */
177	u_int	ss_disk_initiated_reads[STAT_DISKMAX];	/* read operations we initiated per disk */
178	u_int	ss_read_blocks;		/* number of blocks read */
179	u_int	ss_read_bytes;		/* number of bytes read */
180	u_int	ss_read_errors;		/* read errors encountered */
181	u_int	ss_read_errors_bytes;	/* bytes discarded due to read errors */
182	u_int	ss_batch_bytes[STAT_DISKMAX][STAT_BATCHMAX+1];	/* number of bytes in read per batch, +1 for sum of extra batches */
183	u_int	ss_batch_late_bytes[STAT_DISKMAX][STAT_BATCHMAX+1];	/* number of bytes read during this batch
184																	that were scheduled for an earlier batch */
185	u_int	ss_batch_time[STAT_DISKMAX][STAT_BATCHMAX+1];	/* msecs per batch, +1 for sum of extra batches */
186	u_int	ss_cache_time;		/* msecs cache was alive */
187	u_int	ss_preload_time;	/* msecs before cache started */
188
189	u_int	ss_read_bytes_lowpri;                         /* number of bytes read */
190	u_int	ss_batch_bytes_lowpri[STAT_DISKMAX];          /* number of bytes in read per disk */
191	u_int	ss_batch_time_lowpri[STAT_DISKMAX];           /* msecs per disk */
192	u_int	ss_disk_initiated_reads_lowpri[STAT_DISKMAX]; /* read operations we initiated per disk */
193
194	/* inbound strategy calls (while we're recording) */
195	u_int	ss_strategy_calls;			/* total strategy calls we received */
196	u_int	ss_strategy_bypassed;		/* strategy calls we bypassed */
197	u_int	ss_strategy_nonread;		/* strategy calls that were not reads */
198	u_int	ss_strategy_throttled;		/* strategy calls marked low priority */
199	u_int	ss_strategy_noncached_mount;	/* strategy calls from non-cached mounts */
200	u_int	ss_strategy_unknown;		/* strategy calls that were unidentifiable */
201	u_int	ss_strategy_unknown_bytes;	/* bytes of unidentifiable strategy calls */
202	u_int	ss_strategy_nonblocksize;	/* strategy calls of non-blocksize-multiple size */
203
204	/* non-cached IOs */
205	u_int	ss_strategy_hit_nocache;	/* cache hits that were IOs we won't cache for next boot */
206	u_int	ss_strategy_bypass_nocache;	/* cache misses that were IOs we won't cache for next boot */
207	u_int	ss_strategy_bypass_duringio_nocache;	/* cache misses that were IOs we won't cache for next boot */
208	u_int	ss_hit_nocache_bytes;		/* bytes hit by IOs we won't cache for next boot */
209	u_int	ss_bypass_nocache_bytes;	/* bytes missed by IOs we won't cache for next boot */
210	u_int	ss_bypass_nocache_discards;	/* bytes discarded by IOs we won't cache for next boot */
211
212	/* io during readahead */
213	u_int	ss_strategy_duringio;			/* total strategy calls */
214	u_int	ss_strategy_bypass_duringio;	/* strategy calls we bypassed */
215	u_int	ss_strategy_bypass_duringio_rootdisk_read;		/* read strategy calls we missed for the root disk */
216	u_int	ss_strategy_bypass_duringio_rootdisk_failure;	/* read strategy calls we hit but failed to fulfil for the root disk */
217	u_int	ss_strategy_bypass_duringio_rootdisk_nonread;	/* nonread strategy calls we bypassed for the root disk */
218	u_int	ss_strategy_forced_throttled;			/* strategy calls we forced to throttle due to cutting through our readahead */
219	u_int	ss_hit_duringio;				/* cache hits during active readahead */
220	u_int   ss_strategy_bypass_duringio_unfilled;           /* strategy calls that hit an unfilled extent (for SSDs) */
221	u_int   ss_strategy_unfilled_lowpri;    /* strategy calls that hit an unfilled low priority extent */
222	u_int	ss_strategy_blocked;			/* strategy calls that blocked on future readhead */
223	u_int	ss_strategy_timedout;			/* strategy calls that timed out */
224	u_int	ss_strategy_time_longest_blocked;		/* longest time a strategy calls spent blocked on our cache (in milliseconds) */
225	u_int	ss_strategy_time_blocked;		/* milliseconds strategy calls spent blocked on our cache */
226
227	/* extents */
228	u_int	ss_total_extents;	/* number of extents in the cache */
229	u_int	ss_extents_clipped;	/* number of extents clipped due to page boundaries */
230	u_int	ss_extent_lookups;	/* number of extents searched for */
231	u_int	ss_extent_hits;		/* number of extents matched (cache hits) */
232	u_int	ss_hit_multiple;	/* cache hits that touched more than one extent */
233	u_int	ss_hit_aborted;		/* cache hits not filled due to aborted extents */
234	u_int	ss_hit_blkmissing;	/* cache hits not filled due to missing blocks */
235	u_int	ss_hit_stolen;		/* cache hits not filled due to stolen pages */
236	u_int	ss_hit_failure;		/* cache hits not filled due to other failures */
237
238	/* mounts */
239	u_int	ss_total_mounts;	/* number of mounts in the cache */
240	u_int	ss_history_mount_no_uuid;	/* number of mounts seen without a uuid */
241
242	/* byte/page activity */
243	u_int	ss_requested_bytes;
244	u_int   ss_requested_bytes_m[STAT_MOUNTMAX];
245	u_int	ss_hit_bytes;		/* number of bytes vacated due to read hits */
246	u_int	ss_hit_bytes_m[STAT_MOUNTMAX];		/* number of bytes vacated due to read hits */
247	u_int	ss_shared_bytes;		/* number of bytes read from the shared cache */
248	u_int	ss_hit_shared_bytes;	/* number of bytes hit IOs in the shared cache */
249	u_int	ss_stolen_bytes;		/* number of bytes lost to pageout or contig mem */
250	u_int	ss_write_discards;	/* bytes discarded due to overwriting */
251	u_int	ss_read_discards;	/* bytes discarded due to incoming reads */
252	u_int	ss_error_discards;	/* bytes discarded due to failure to fulfil a cache hit */
253	u_int	ss_spurious_bytes;	/* number of btyes not consumed */
254	u_int	ss_hit_bytes_afterhistory;		/* bytes fulfilled after history recording was complete */
255	u_int	ss_lost_bytes_afterhistory;	/* bytes lost after history recording was complete */
256
257	/* history activity */
258	u_int	ss_history_bytes;			/* number of bytes contained in the history we've seen for this boot */
259	u_int	ss_history_time;			/* msecs hisotry was active */
260	u_int	ss_history_reads;			/* number of reads we saw during initial boot */
261	u_int	ss_history_writes;			/* number of writes we saw during initial boot */
262	u_int	ss_history_entries;			/* number of history entries we've created this boot */
263	u_int	ss_history_mounts;			/* number of allocated history mounts we had for the last recording */
264	u_int	ss_history_unknown;			/* history calls we couldn't find a mount for */
265	u_int	ss_history_unknown_bytes;	/* bytes history calls we couldn't find a mount for */
266	u_int	ss_history_num_recordings;	/* number of recordings we've has this boot */
267
268	/* current status */
269	u_int	ss_cache_flags;		/* current cache flags */
270};
271
272/*
273 * In-memory BootCache playlist structure
274 */
275struct BC_playlist {
276	int                       p_nmounts;  /* number of mounts */
277	int                       p_nentries; /* number of entries */
278	struct BC_playlist_mount *p_mounts;   /* array of mounts */
279	struct BC_playlist_entry *p_entries;  /* array of entries */
280};
281
282/*
283 * In-memory BootCache history structure
284 */
285struct BC_history {
286	int                      h_nmounts;  /* number of mounts */
287	int                      h_nentries; /* number of entries */
288	struct BC_history_mount *h_mounts;   /* array of mounts */
289	struct BC_history_entry *h_entries;  /* array of entries */
290};
291
292#ifndef KERNEL
293/*
294 * Support library functions.
295 */
296extern int	BC_read_playlist(const char *, struct BC_playlist **);
297extern int	BC_write_playlist(const char *, const struct BC_playlist *);
298extern int	BC_merge_playlists(struct BC_playlist *, const struct BC_playlist *);
299extern void	BC_sort_playlist(struct BC_playlist *);
300extern int	BC_coalesce_playlist(struct BC_playlist *);
301extern int	BC_playlist_for_file(int fd, struct BC_playlist** ppc);
302extern int  BC_playlist_for_filename(int fd, const char *fname, off_t maxsize, struct BC_playlist** ppc);
303extern int	BC_verify_playlist(const struct BC_playlist *);
304extern void BC_free_playlist(struct BC_playlist *);
305#define PC_FREE_ZERO(pc) do { if (pc) { BC_free_playlist(pc); (pc) = NULL; } } while (0)
306extern void BC_free_history(struct BC_history *);
307#define HC_FREE_ZERO(hc) do { if (hc) { BC_free_history(hc); (hc) = NULL; } } while (0)
308extern int	BC_fetch_statistics(struct BC_statistics **);
309extern int	BC_convert_history(const struct BC_history *, struct BC_playlist **);
310extern int	BC_start(struct BC_playlist *);
311extern int	BC_stop(struct BC_history **);
312extern int	BC_notify_mount(void);
313extern int	BC_test(void);
314extern int	BC_jettison(void);
315extern int	BC_print_statistics(char *, struct BC_statistics *);
316extern int	BC_print_history(char *, struct BC_history *);
317extern int	BC_tag_history(void);
318extern int	BC_unload(void);
319#endif
320