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