xref: /xnu-8020.140.41/tests/disk_mount_conditioner.c (revision 27b03b360a988dfd3dfdf34262bb0042026747cc)
1*27b03b36SApple OSS Distributions #ifdef T_NAMESPACE
2*27b03b36SApple OSS Distributions #undef T_NAMESPACE
3*27b03b36SApple OSS Distributions #endif
4*27b03b36SApple OSS Distributions #include <darwintest.h>
5*27b03b36SApple OSS Distributions #include <darwintest_utils.h>
6*27b03b36SApple OSS Distributions 
7*27b03b36SApple OSS Distributions #include <stdlib.h>
8*27b03b36SApple OSS Distributions #include <unistd.h>
9*27b03b36SApple OSS Distributions #include <fcntl.h>
10*27b03b36SApple OSS Distributions #include <System/sys/fsctl.h>
11*27b03b36SApple OSS Distributions #include <paths.h>
12*27b03b36SApple OSS Distributions 
13*27b03b36SApple OSS Distributions static char *mktempdir(void);
14*27b03b36SApple OSS Distributions static char *mktempmount(void);
15*27b03b36SApple OSS Distributions 
16*27b03b36SApple OSS Distributions #ifndef TEST_UNENTITLED
17*27b03b36SApple OSS Distributions static int system_legal(const char *command);
18*27b03b36SApple OSS Distributions static char *mkramdisk(void);
19*27b03b36SApple OSS Distributions static uint64_t time_for_read(int fd, const char *expected);
20*27b03b36SApple OSS Distributions static void perf_setup(char **path, int *fd);
21*27b03b36SApple OSS Distributions 
22*27b03b36SApple OSS Distributions #define READSIZE 1024L
23*27b03b36SApple OSS Distributions #endif /* !TEST_UNENTITLED */
24*27b03b36SApple OSS Distributions 
25*27b03b36SApple OSS Distributions T_GLOBAL_META(
26*27b03b36SApple OSS Distributions 	T_META_NAMESPACE("xnu.vfs.dmc"),
27*27b03b36SApple OSS Distributions 	T_META_ASROOT(true),
28*27b03b36SApple OSS Distributions 	T_META_RUN_CONCURRENTLY(true)
29*27b03b36SApple OSS Distributions 	);
30*27b03b36SApple OSS Distributions 
31*27b03b36SApple OSS Distributions #pragma mark Entitled Tests
32*27b03b36SApple OSS Distributions 
33*27b03b36SApple OSS Distributions #ifndef TEST_UNENTITLED
34*27b03b36SApple OSS Distributions T_DECL(fsctl_get_uninitialized,
35*27b03b36SApple OSS Distributions     "Initial fsctl.get should return zeros",
36*27b03b36SApple OSS Distributions     T_META_ASROOT(false))
37*27b03b36SApple OSS Distributions {
38*27b03b36SApple OSS Distributions 	int err;
39*27b03b36SApple OSS Distributions 	char *mount_path;
40*27b03b36SApple OSS Distributions 	disk_conditioner_info info = {0};
41*27b03b36SApple OSS Distributions 	disk_conditioner_info expected_info = {0};
42*27b03b36SApple OSS Distributions 
43*27b03b36SApple OSS Distributions 	T_SETUPBEGIN;
44*27b03b36SApple OSS Distributions 	mount_path = mktempmount();
45*27b03b36SApple OSS Distributions 	T_SETUPEND;
46*27b03b36SApple OSS Distributions 
47*27b03b36SApple OSS Distributions 	info.enabled = true;
48*27b03b36SApple OSS Distributions 	info.is_ssd = true;
49*27b03b36SApple OSS Distributions 	err = fsctl(mount_path, DISK_CONDITIONER_IOC_GET, &info, 0);
50*27b03b36SApple OSS Distributions 	T_WITH_ERRNO;
51*27b03b36SApple OSS Distributions 	T_ASSERT_EQ_INT(0, err, "fsctl(DISK_CONDITIONER_IOC_GET)");
52*27b03b36SApple OSS Distributions 
53*27b03b36SApple OSS Distributions 	err = memcmp(&info, &expected_info, sizeof(info));
54*27b03b36SApple OSS Distributions 	T_ASSERT_EQ_INT(0, err, "initial DMC info is zeroed");
55*27b03b36SApple OSS Distributions }
56*27b03b36SApple OSS Distributions 
57*27b03b36SApple OSS Distributions T_DECL(fsctl_set,
58*27b03b36SApple OSS Distributions     "fsctl.set should succeed and fsctl.get should verify")
59*27b03b36SApple OSS Distributions {
60*27b03b36SApple OSS Distributions 	int err;
61*27b03b36SApple OSS Distributions 	char *mount_path;
62*27b03b36SApple OSS Distributions 	disk_conditioner_info info = {0};
63*27b03b36SApple OSS Distributions 	disk_conditioner_info expected_info = {0};
64*27b03b36SApple OSS Distributions 
65*27b03b36SApple OSS Distributions 	T_SETUPBEGIN;
66*27b03b36SApple OSS Distributions 	mount_path = mktempmount();
67*27b03b36SApple OSS Distributions 	T_SETUPEND;
68*27b03b36SApple OSS Distributions 
69*27b03b36SApple OSS Distributions 	info.enabled = 1;
70*27b03b36SApple OSS Distributions 	info.access_time_usec = 10;
71*27b03b36SApple OSS Distributions 	info.read_throughput_mbps = 40;
72*27b03b36SApple OSS Distributions 	info.write_throughput_mbps = 40;
73*27b03b36SApple OSS Distributions 	info.is_ssd = 0;
74*27b03b36SApple OSS Distributions 	info.ioqueue_depth = 8;
75*27b03b36SApple OSS Distributions 	info.maxreadcnt = 8;
76*27b03b36SApple OSS Distributions 	info.maxwritecnt = 8;
77*27b03b36SApple OSS Distributions 	info.segreadcnt = 8;
78*27b03b36SApple OSS Distributions 	info.segwritecnt = 8;
79*27b03b36SApple OSS Distributions 	expected_info = info;
80*27b03b36SApple OSS Distributions 
81*27b03b36SApple OSS Distributions 	err = fsctl(mount_path, DISK_CONDITIONER_IOC_SET, &info, 0);
82*27b03b36SApple OSS Distributions 	T_WITH_ERRNO;
83*27b03b36SApple OSS Distributions 	T_ASSERT_EQ_INT(0, err, "fsctl(DISK_CONDITIONER_IOC_SET)");
84*27b03b36SApple OSS Distributions 
85*27b03b36SApple OSS Distributions 	err = fsctl(mount_path, DISK_CONDITIONER_IOC_GET, &info, 0);
86*27b03b36SApple OSS Distributions 	T_WITH_ERRNO;
87*27b03b36SApple OSS Distributions 	T_ASSERT_EQ_INT(0, err, "fsctl(DISK_CONDITIONER_IOC_GET) after SET");
88*27b03b36SApple OSS Distributions 
89*27b03b36SApple OSS Distributions 	err = memcmp(&info, &expected_info, sizeof(info));
90*27b03b36SApple OSS Distributions 	T_ASSERT_EQ_INT(0, err, "fsctl.get is the info configured by fsctl.set");
91*27b03b36SApple OSS Distributions }
92*27b03b36SApple OSS Distributions 
93*27b03b36SApple OSS Distributions static void
verify_mount_fallback_values(const char * mount_path,disk_conditioner_info * info)94*27b03b36SApple OSS Distributions verify_mount_fallback_values(const char *mount_path, disk_conditioner_info *info)
95*27b03b36SApple OSS Distributions {
96*27b03b36SApple OSS Distributions 	int err;
97*27b03b36SApple OSS Distributions 	disk_conditioner_info newinfo = {0};
98*27b03b36SApple OSS Distributions 
99*27b03b36SApple OSS Distributions 	err = fsctl(mount_path, DISK_CONDITIONER_IOC_SET, info, 0);
100*27b03b36SApple OSS Distributions 	T_WITH_ERRNO;
101*27b03b36SApple OSS Distributions 	T_ASSERT_EQ_INT(0, err, "fsctl(DISK_CONDITIONER_IOC_SET)");
102*27b03b36SApple OSS Distributions 
103*27b03b36SApple OSS Distributions 	err = fsctl(mount_path, DISK_CONDITIONER_IOC_GET, &newinfo, 0);
104*27b03b36SApple OSS Distributions 	T_WITH_ERRNO;
105*27b03b36SApple OSS Distributions 	T_ASSERT_EQ_INT(0, err, "fsctl(DISK_CONDITIONER_IOC_GET) after SET");
106*27b03b36SApple OSS Distributions 
107*27b03b36SApple OSS Distributions 	// without querying the drive for the expected values, the best we can do is
108*27b03b36SApple OSS Distributions 	// assert that they are not zero (impossible) or less than UINT32_MAX (unlikely)
109*27b03b36SApple OSS Distributions 	T_ASSERT_GT(newinfo.ioqueue_depth, 0u, "ioqueue_depth is the value from the mount");
110*27b03b36SApple OSS Distributions 	T_ASSERT_GT(newinfo.maxreadcnt, 0u, "maxreadcnt is value from the mount");
111*27b03b36SApple OSS Distributions 	T_ASSERT_GT(newinfo.maxwritecnt, 0u, "maxwritecnt is value from the mount");
112*27b03b36SApple OSS Distributions 	T_ASSERT_GT(newinfo.segreadcnt, 0u, "segreadcnt is value from the mount");
113*27b03b36SApple OSS Distributions 	T_ASSERT_GT(newinfo.segwritecnt, 0u, "segwritecnt is value from the mount");
114*27b03b36SApple OSS Distributions 	T_ASSERT_LT(newinfo.ioqueue_depth, UINT32_MAX, "ioqueue_depth is the value from the mount");
115*27b03b36SApple OSS Distributions 	T_ASSERT_LT(newinfo.maxreadcnt, UINT32_MAX, "maxreadcnt is value from the mount");
116*27b03b36SApple OSS Distributions 	T_ASSERT_LT(newinfo.maxwritecnt, UINT32_MAX, "maxwritecnt is value from the mount");
117*27b03b36SApple OSS Distributions 	T_ASSERT_LT(newinfo.segreadcnt, UINT32_MAX, "segreadcnt is value from the mount");
118*27b03b36SApple OSS Distributions 	T_ASSERT_LT(newinfo.segwritecnt, UINT32_MAX, "segwritecnt is value from the mount");
119*27b03b36SApple OSS Distributions }
120*27b03b36SApple OSS Distributions 
121*27b03b36SApple OSS Distributions T_DECL(fsctl_set_zero,
122*27b03b36SApple OSS Distributions     "fsctl.set zero values should fall back to original mount settings")
123*27b03b36SApple OSS Distributions {
124*27b03b36SApple OSS Distributions 	char *mount_path;
125*27b03b36SApple OSS Distributions 	disk_conditioner_info info = {0};
126*27b03b36SApple OSS Distributions 
127*27b03b36SApple OSS Distributions 	T_SETUPBEGIN;
128*27b03b36SApple OSS Distributions 	mount_path = mktempmount();
129*27b03b36SApple OSS Distributions 
130*27b03b36SApple OSS Distributions 	info.enabled = 1;
131*27b03b36SApple OSS Distributions 	/* everything else is 0 */
132*27b03b36SApple OSS Distributions 
133*27b03b36SApple OSS Distributions 	T_SETUPEND;
134*27b03b36SApple OSS Distributions 
135*27b03b36SApple OSS Distributions 	verify_mount_fallback_values(mount_path, &info);
136*27b03b36SApple OSS Distributions }
137*27b03b36SApple OSS Distributions 
138*27b03b36SApple OSS Distributions T_DECL(fsctl_set_out_of_bounds,
139*27b03b36SApple OSS Distributions     "fsctl.set out-of-bounds values should fall back to original mount settings")
140*27b03b36SApple OSS Distributions {
141*27b03b36SApple OSS Distributions 	char *mount_path;
142*27b03b36SApple OSS Distributions 	disk_conditioner_info info;
143*27b03b36SApple OSS Distributions 
144*27b03b36SApple OSS Distributions 	T_SETUPBEGIN;
145*27b03b36SApple OSS Distributions 	mount_path = mktempmount();
146*27b03b36SApple OSS Distributions 
147*27b03b36SApple OSS Distributions 	memset(&info, UINT32_MAX, sizeof(info));
148*27b03b36SApple OSS Distributions 	info.enabled = 1;
149*27b03b36SApple OSS Distributions 	info.access_time_usec = 0;
150*27b03b36SApple OSS Distributions 	info.read_throughput_mbps = 0;
151*27b03b36SApple OSS Distributions 	info.write_throughput_mbps = 0;
152*27b03b36SApple OSS Distributions 	/* everything else is UINT32_MAX */
153*27b03b36SApple OSS Distributions 
154*27b03b36SApple OSS Distributions 	T_SETUPEND;
155*27b03b36SApple OSS Distributions 
156*27b03b36SApple OSS Distributions 	verify_mount_fallback_values(mount_path, &info);
157*27b03b36SApple OSS Distributions }
158*27b03b36SApple OSS Distributions 
159*27b03b36SApple OSS Distributions T_DECL(fsctl_restore_mount_fields,
160*27b03b36SApple OSS Distributions     "fsctl.set should restore fields on mount_t that it temporarily overrides")
161*27b03b36SApple OSS Distributions {
162*27b03b36SApple OSS Distributions 	int err;
163*27b03b36SApple OSS Distributions 	char *mount_path;
164*27b03b36SApple OSS Distributions 	disk_conditioner_info info;
165*27b03b36SApple OSS Distributions 	disk_conditioner_info mount_fields;
166*27b03b36SApple OSS Distributions 
167*27b03b36SApple OSS Distributions 	T_SETUPBEGIN;
168*27b03b36SApple OSS Distributions 	mount_path = mktempmount();
169*27b03b36SApple OSS Distributions 	T_SETUPEND;
170*27b03b36SApple OSS Distributions 
171*27b03b36SApple OSS Distributions 	/* first set out-of-bounds values to retrieve the original mount_t fields */
172*27b03b36SApple OSS Distributions 	memset(&info, UINT32_MAX, sizeof(info));
173*27b03b36SApple OSS Distributions 	info.enabled = 1;
174*27b03b36SApple OSS Distributions 	info.access_time_usec = 0;
175*27b03b36SApple OSS Distributions 	info.read_throughput_mbps = 0;
176*27b03b36SApple OSS Distributions 	info.write_throughput_mbps = 0;
177*27b03b36SApple OSS Distributions 	/* everything else is UINT32_MAX */
178*27b03b36SApple OSS Distributions 	err = fsctl(mount_path, DISK_CONDITIONER_IOC_SET, &info, 0);
179*27b03b36SApple OSS Distributions 	T_WITH_ERRNO;
180*27b03b36SApple OSS Distributions 	T_ASSERT_EQ_INT(0, err, "fsctl(DISK_CONDITIONER_IOC_SET)");
181*27b03b36SApple OSS Distributions 
182*27b03b36SApple OSS Distributions 	err = fsctl(mount_path, DISK_CONDITIONER_IOC_GET, &mount_fields, 0);
183*27b03b36SApple OSS Distributions 	T_WITH_ERRNO;
184*27b03b36SApple OSS Distributions 	T_ASSERT_EQ_INT(0, err, "fsctl(DISK_CONDITIONER_IOC_GET)");
185*27b03b36SApple OSS Distributions 
186*27b03b36SApple OSS Distributions 	/* now turn off the disk conditioner which should restore fields on the mount_t */
187*27b03b36SApple OSS Distributions 	memset(&info, 1, sizeof(info));
188*27b03b36SApple OSS Distributions 	info.enabled = 0;
189*27b03b36SApple OSS Distributions 	err = fsctl(mount_path, DISK_CONDITIONER_IOC_SET, &info, 0);
190*27b03b36SApple OSS Distributions 	T_WITH_ERRNO;
191*27b03b36SApple OSS Distributions 	T_ASSERT_EQ_INT(0, err, "fsctl(DISK_CONDITIONER_IOC_SET)");
192*27b03b36SApple OSS Distributions 
193*27b03b36SApple OSS Distributions 	/* and finally set out-of-bounds values again to retrieve the new mount_t fields which should not have changed */
194*27b03b36SApple OSS Distributions 	memset(&info, UINT32_MAX, sizeof(info));
195*27b03b36SApple OSS Distributions 	info.enabled = 0;
196*27b03b36SApple OSS Distributions 	info.access_time_usec = 0;
197*27b03b36SApple OSS Distributions 	info.read_throughput_mbps = 0;
198*27b03b36SApple OSS Distributions 	info.write_throughput_mbps = 0;
199*27b03b36SApple OSS Distributions 	/* everything else is UINT32_MAX */
200*27b03b36SApple OSS Distributions 	err = fsctl(mount_path, DISK_CONDITIONER_IOC_SET, &info, 0);
201*27b03b36SApple OSS Distributions 	T_WITH_ERRNO;
202*27b03b36SApple OSS Distributions 	T_ASSERT_EQ_INT(0, err, "fsctl(DISK_CONDITIONER_IOC_SET)");
203*27b03b36SApple OSS Distributions 
204*27b03b36SApple OSS Distributions 	err = fsctl(mount_path, DISK_CONDITIONER_IOC_GET, &info, 0);
205*27b03b36SApple OSS Distributions 	T_WITH_ERRNO;
206*27b03b36SApple OSS Distributions 	T_ASSERT_EQ_INT(0, err, "fsctl(DISK_CONDITIONER_IOC_GET)");
207*27b03b36SApple OSS Distributions 
208*27b03b36SApple OSS Distributions 	T_ASSERT_EQ(info.maxreadcnt, mount_fields.maxreadcnt, "mount_t maxreadcnt restored");
209*27b03b36SApple OSS Distributions 	T_ASSERT_EQ(info.maxwritecnt, mount_fields.maxwritecnt, "mount_t maxwritecnt restored");
210*27b03b36SApple OSS Distributions 	T_ASSERT_EQ(info.segreadcnt, mount_fields.segreadcnt, "mount_t segreadcnt restored");
211*27b03b36SApple OSS Distributions 	T_ASSERT_EQ(info.segwritecnt, mount_fields.segwritecnt, "mount_t segwritecnt restored");
212*27b03b36SApple OSS Distributions 	T_ASSERT_EQ(info.ioqueue_depth, mount_fields.ioqueue_depth, "mount_t ioqueue_depth restored");
213*27b03b36SApple OSS Distributions }
214*27b03b36SApple OSS Distributions 
215*27b03b36SApple OSS Distributions T_DECL(fsctl_get_nonroot,
216*27b03b36SApple OSS Distributions     "fsctl.get should not require root",
217*27b03b36SApple OSS Distributions     T_META_ASROOT(false))
218*27b03b36SApple OSS Distributions {
219*27b03b36SApple OSS Distributions 	int err;
220*27b03b36SApple OSS Distributions 	char *mount_path;
221*27b03b36SApple OSS Distributions 	disk_conditioner_info info;
222*27b03b36SApple OSS Distributions 
223*27b03b36SApple OSS Distributions 	T_SETUPBEGIN;
224*27b03b36SApple OSS Distributions 	// make sure we're not root
225*27b03b36SApple OSS Distributions 	if (0 == geteuid()) {
226*27b03b36SApple OSS Distributions 		seteuid(5000);
227*27b03b36SApple OSS Distributions 	}
228*27b03b36SApple OSS Distributions 
229*27b03b36SApple OSS Distributions 	mount_path = mktempmount();
230*27b03b36SApple OSS Distributions 	T_SETUPEND;
231*27b03b36SApple OSS Distributions 
232*27b03b36SApple OSS Distributions 	err = fsctl(mount_path, DISK_CONDITIONER_IOC_GET, &info, 0);
233*27b03b36SApple OSS Distributions 	T_WITH_ERRNO;
234*27b03b36SApple OSS Distributions 	T_ASSERT_EQ_INT(0, err, "fsctl.get without root");
235*27b03b36SApple OSS Distributions }
236*27b03b36SApple OSS Distributions 
237*27b03b36SApple OSS Distributions T_DECL(fsctl_set_nonroot,
238*27b03b36SApple OSS Distributions     "fsctl.set should require root",
239*27b03b36SApple OSS Distributions     T_META_ASROOT(false))
240*27b03b36SApple OSS Distributions {
241*27b03b36SApple OSS Distributions 	int err;
242*27b03b36SApple OSS Distributions 	char *mount_path;
243*27b03b36SApple OSS Distributions 	disk_conditioner_info info = {0};
244*27b03b36SApple OSS Distributions 	disk_conditioner_info expected_info = {0};
245*27b03b36SApple OSS Distributions 
246*27b03b36SApple OSS Distributions 	T_SETUPBEGIN;
247*27b03b36SApple OSS Distributions 	// make sure we're not root
248*27b03b36SApple OSS Distributions 	if (0 == geteuid()) {
249*27b03b36SApple OSS Distributions 		seteuid(5000);
250*27b03b36SApple OSS Distributions 	}
251*27b03b36SApple OSS Distributions 
252*27b03b36SApple OSS Distributions 	mount_path = mktempmount();
253*27b03b36SApple OSS Distributions 	T_SETUPEND;
254*27b03b36SApple OSS Distributions 
255*27b03b36SApple OSS Distributions 	// save original info
256*27b03b36SApple OSS Distributions 	err = fsctl(mount_path, DISK_CONDITIONER_IOC_GET, &expected_info, 0);
257*27b03b36SApple OSS Distributions 	T_WITH_ERRNO;
258*27b03b36SApple OSS Distributions 	T_ASSERT_EQ_INT(0, err, "Get original DMC info");
259*27b03b36SApple OSS Distributions 
260*27b03b36SApple OSS Distributions 	info.enabled = 1;
261*27b03b36SApple OSS Distributions 	info.access_time_usec = 10;
262*27b03b36SApple OSS Distributions 	err = fsctl(mount_path, DISK_CONDITIONER_IOC_SET, &info, 0);
263*27b03b36SApple OSS Distributions 	T_WITH_ERRNO;
264*27b03b36SApple OSS Distributions 	T_ASSERT_NE_INT(0, err, "fsctl.set returns error without root");
265*27b03b36SApple OSS Distributions 
266*27b03b36SApple OSS Distributions 	err = fsctl(mount_path, DISK_CONDITIONER_IOC_GET, &info, 0);
267*27b03b36SApple OSS Distributions 	T_WITH_ERRNO;
268*27b03b36SApple OSS Distributions 	T_ASSERT_EQ_INT(0, err, "fsctl.get after nonroot fsctl.set");
269*27b03b36SApple OSS Distributions 
270*27b03b36SApple OSS Distributions 	err = memcmp(&info, &expected_info, sizeof(info));
271*27b03b36SApple OSS Distributions 	T_ASSERT_EQ_INT(0, err, "fsctl.set should not change info without root");
272*27b03b36SApple OSS Distributions }
273*27b03b36SApple OSS Distributions 
274*27b03b36SApple OSS Distributions T_DECL(fsctl_delays,
275*27b03b36SApple OSS Distributions     "Validate I/O delays when DMC is enabled",
276*27b03b36SApple OSS Distributions     T_META_RUN_CONCURRENTLY(false))
277*27b03b36SApple OSS Distributions {
278*27b03b36SApple OSS Distributions 	char *path;
279*27b03b36SApple OSS Distributions 	int fd;
280*27b03b36SApple OSS Distributions 	int err;
281*27b03b36SApple OSS Distributions 	uint64_t elapsed_nsec, expected_nsec;
282*27b03b36SApple OSS Distributions 	disk_conditioner_info info = {0};
283*27b03b36SApple OSS Distributions 	char buf[READSIZE];
284*27b03b36SApple OSS Distributions 
285*27b03b36SApple OSS Distributions 	T_SETUPBEGIN;
286*27b03b36SApple OSS Distributions 	perf_setup(&path, &fd);
287*27b03b36SApple OSS Distributions 	memset(buf, 0xFF, sizeof(buf));
288*27b03b36SApple OSS Distributions 	T_ASSERT_EQ_LONG((long)sizeof(buf), write(fd, buf, sizeof(buf)), "write random data to temp file");
289*27b03b36SApple OSS Distributions 	fcntl(fd, F_FULLFSYNC);
290*27b03b36SApple OSS Distributions 	T_SETUPEND;
291*27b03b36SApple OSS Distributions 
292*27b03b36SApple OSS Distributions 	expected_nsec = NSEC_PER_SEC / 2;
293*27b03b36SApple OSS Distributions 
294*27b03b36SApple OSS Distributions 	// measure delay before setting parameters (should be none)
295*27b03b36SApple OSS Distributions 	elapsed_nsec = time_for_read(fd, buf);
296*27b03b36SApple OSS Distributions 	T_ASSERT_LT_ULLONG(elapsed_nsec, expected_nsec, "DMC disabled read(%ld) from %s is reasonably fast", READSIZE, path);
297*27b03b36SApple OSS Distributions 
298*27b03b36SApple OSS Distributions 	// measure delay after setting parameters
299*27b03b36SApple OSS Distributions 	info.enabled = 1;
300*27b03b36SApple OSS Distributions 	info.access_time_usec = expected_nsec / NSEC_PER_USEC;
301*27b03b36SApple OSS Distributions 	info.read_throughput_mbps = 40;
302*27b03b36SApple OSS Distributions 	info.write_throughput_mbps = 40;
303*27b03b36SApple OSS Distributions 	info.is_ssd = 1; // is_ssd will ensure we get constant access_time delays rather than scaled
304*27b03b36SApple OSS Distributions 	err = fsctl(path, DISK_CONDITIONER_IOC_SET, &info, 0);
305*27b03b36SApple OSS Distributions 	T_WITH_ERRNO;
306*27b03b36SApple OSS Distributions 	T_ASSERT_EQ_INT(0, err, "fsctl(DISK_CONDITIONER_IOC_SET) delay");
307*27b03b36SApple OSS Distributions 
308*27b03b36SApple OSS Distributions 	elapsed_nsec = time_for_read(fd, buf);
309*27b03b36SApple OSS Distributions 	T_ASSERT_GT_ULLONG(elapsed_nsec, expected_nsec, "DMC enabled read(%ld) from %s is at least the expected delay", READSIZE, path);
310*27b03b36SApple OSS Distributions 	T_ASSERT_LT_ULLONG(elapsed_nsec, 2 * expected_nsec, "DMC enabled read(%ld) from %s is no more than twice the expected delay", READSIZE, path);
311*27b03b36SApple OSS Distributions 
312*27b03b36SApple OSS Distributions 	// measure delay after resetting parameters (should be none)
313*27b03b36SApple OSS Distributions 	info.enabled = 0;
314*27b03b36SApple OSS Distributions 	err = fsctl(path, DISK_CONDITIONER_IOC_SET, &info, 0);
315*27b03b36SApple OSS Distributions 	T_WITH_ERRNO;
316*27b03b36SApple OSS Distributions 	T_ASSERT_EQ_INT(0, err, "fsctl(DISK_CONDITIONER_IOC_SET) reset delay");
317*27b03b36SApple OSS Distributions 
318*27b03b36SApple OSS Distributions 	usleep(USEC_PER_SEC / 2); // might still be other I/O inflight
319*27b03b36SApple OSS Distributions 	elapsed_nsec = time_for_read(fd, buf);
320*27b03b36SApple OSS Distributions 	T_ASSERT_LT_ULLONG(elapsed_nsec, expected_nsec, "After disabling DMC read(%ld) from %s is reasonably fast", READSIZE, path);
321*27b03b36SApple OSS Distributions }
322*27b03b36SApple OSS Distributions 
323*27b03b36SApple OSS Distributions #else /* TEST_UNENTITLED */
324*27b03b36SApple OSS Distributions 
325*27b03b36SApple OSS Distributions #pragma mark Unentitled Tests
326*27b03b36SApple OSS Distributions 
327*27b03b36SApple OSS Distributions T_DECL(fsctl_get_unentitled,
328*27b03b36SApple OSS Distributions     "fsctl.get should not require entitlement")
329*27b03b36SApple OSS Distributions {
330*27b03b36SApple OSS Distributions 	int err;
331*27b03b36SApple OSS Distributions 	char *mount_path;
332*27b03b36SApple OSS Distributions 	disk_conditioner_info info;
333*27b03b36SApple OSS Distributions 
334*27b03b36SApple OSS Distributions 	T_SETUPBEGIN;
335*27b03b36SApple OSS Distributions 	mount_path = mktempmount();
336*27b03b36SApple OSS Distributions 	T_SETUPEND;
337*27b03b36SApple OSS Distributions 
338*27b03b36SApple OSS Distributions 	err = fsctl(mount_path, DISK_CONDITIONER_IOC_GET, &info, 0);
339*27b03b36SApple OSS Distributions 	T_WITH_ERRNO;
340*27b03b36SApple OSS Distributions 	T_ASSERT_EQ_INT(0, err, "fsctl.get without entitlement");
341*27b03b36SApple OSS Distributions }
342*27b03b36SApple OSS Distributions 
343*27b03b36SApple OSS Distributions T_DECL(fsctl_set_unentitled,
344*27b03b36SApple OSS Distributions     "fsctl.set should require entitlement")
345*27b03b36SApple OSS Distributions {
346*27b03b36SApple OSS Distributions 	int err;
347*27b03b36SApple OSS Distributions 	char *mount_path;
348*27b03b36SApple OSS Distributions 	disk_conditioner_info info = {0};
349*27b03b36SApple OSS Distributions 	disk_conditioner_info expected_info = {0};
350*27b03b36SApple OSS Distributions 
351*27b03b36SApple OSS Distributions 	T_SETUPBEGIN;
352*27b03b36SApple OSS Distributions 	mount_path = mktempmount();
353*27b03b36SApple OSS Distributions 	T_SETUPEND;
354*27b03b36SApple OSS Distributions 
355*27b03b36SApple OSS Distributions 	// save original info
356*27b03b36SApple OSS Distributions 	err = fsctl(mount_path, DISK_CONDITIONER_IOC_GET, &expected_info, 0);
357*27b03b36SApple OSS Distributions 	T_WITH_ERRNO;
358*27b03b36SApple OSS Distributions 	T_ASSERT_EQ_INT(0, err, "Get original DMC info");
359*27b03b36SApple OSS Distributions 
360*27b03b36SApple OSS Distributions 	info.enabled = 1;
361*27b03b36SApple OSS Distributions 	info.access_time_usec = 10;
362*27b03b36SApple OSS Distributions 	err = fsctl(mount_path, DISK_CONDITIONER_IOC_SET, &info, 0);
363*27b03b36SApple OSS Distributions 	T_WITH_ERRNO;
364*27b03b36SApple OSS Distributions 	T_ASSERT_NE_INT(0, err, "fsctl.set returns error without entitlement");
365*27b03b36SApple OSS Distributions 
366*27b03b36SApple OSS Distributions 	err = fsctl(mount_path, DISK_CONDITIONER_IOC_GET, &info, 0);
367*27b03b36SApple OSS Distributions 	T_WITH_ERRNO;
368*27b03b36SApple OSS Distributions 	T_ASSERT_EQ_INT(0, err, "fsctl.get after unentitled fsctl.set");
369*27b03b36SApple OSS Distributions 
370*27b03b36SApple OSS Distributions 	err = memcmp(&info, &expected_info, sizeof(info));
371*27b03b36SApple OSS Distributions 	T_ASSERT_EQ_INT(0, err, "fsctl.set should not change info without entitlement");
372*27b03b36SApple OSS Distributions }
373*27b03b36SApple OSS Distributions 
374*27b03b36SApple OSS Distributions #endif /* TEST_UNENTITLED */
375*27b03b36SApple OSS Distributions 
376*27b03b36SApple OSS Distributions #pragma mark Helpers
377*27b03b36SApple OSS Distributions 
378*27b03b36SApple OSS Distributions static char *
mktempdir(void)379*27b03b36SApple OSS Distributions mktempdir(void)
380*27b03b36SApple OSS Distributions {
381*27b03b36SApple OSS Distributions 	char *path = malloc(PATH_MAX);
382*27b03b36SApple OSS Distributions 	strcpy(path, "/tmp/dmc.XXXXXXXX");
383*27b03b36SApple OSS Distributions 	atexit_b(^{ free(path); });
384*27b03b36SApple OSS Distributions 
385*27b03b36SApple OSS Distributions 	// create a temporary mount to run the fsctl on
386*27b03b36SApple OSS Distributions 	T_WITH_ERRNO;
387*27b03b36SApple OSS Distributions 	T_ASSERT_NOTNULL(mkdtemp(path), "Create temporary directory");
388*27b03b36SApple OSS Distributions 	atexit_b(^{ remove(path); });
389*27b03b36SApple OSS Distributions 
390*27b03b36SApple OSS Distributions 	return path;
391*27b03b36SApple OSS Distributions }
392*27b03b36SApple OSS Distributions 
393*27b03b36SApple OSS Distributions /*
394*27b03b36SApple OSS Distributions  * Return the path to a temporary mount
395*27b03b36SApple OSS Distributions  * with no usable filesystem but still
396*27b03b36SApple OSS Distributions  * can be configured by the disk conditioner
397*27b03b36SApple OSS Distributions  *
398*27b03b36SApple OSS Distributions  * Faster than creating a ram disk to test with
399*27b03b36SApple OSS Distributions  * when access to the filesystem is not necessary
400*27b03b36SApple OSS Distributions  */
401*27b03b36SApple OSS Distributions static char *
mktempmount(void)402*27b03b36SApple OSS Distributions mktempmount(void)
403*27b03b36SApple OSS Distributions {
404*27b03b36SApple OSS Distributions 	char *mount_path = mktempdir();
405*27b03b36SApple OSS Distributions 
406*27b03b36SApple OSS Distributions 	T_WITH_ERRNO;
407*27b03b36SApple OSS Distributions 	T_ASSERT_EQ_INT(0, mount("devfs", mount_path, MNT_RDONLY, NULL), "Create temporary devfs mount");
408*27b03b36SApple OSS Distributions 	atexit_b(^{ unmount(mount_path, MNT_FORCE); });
409*27b03b36SApple OSS Distributions 
410*27b03b36SApple OSS Distributions 	return mount_path;
411*27b03b36SApple OSS Distributions }
412*27b03b36SApple OSS Distributions 
413*27b03b36SApple OSS Distributions #ifndef TEST_UNENTITLED
414*27b03b36SApple OSS Distributions 
415*27b03b36SApple OSS Distributions /*
416*27b03b36SApple OSS Distributions  * Wrapper around dt_launch_tool/dt_waitpid
417*27b03b36SApple OSS Distributions  * that works like libc:system()
418*27b03b36SApple OSS Distributions  */
419*27b03b36SApple OSS Distributions static int
system_legal(const char * command)420*27b03b36SApple OSS Distributions system_legal(const char *command)
421*27b03b36SApple OSS Distributions {
422*27b03b36SApple OSS Distributions 	pid_t pid = -1;
423*27b03b36SApple OSS Distributions 	int exit_status = 0;
424*27b03b36SApple OSS Distributions 	const char *argv[] = {
425*27b03b36SApple OSS Distributions 		_PATH_BSHELL,
426*27b03b36SApple OSS Distributions 		"-c",
427*27b03b36SApple OSS Distributions 		command,
428*27b03b36SApple OSS Distributions 		NULL
429*27b03b36SApple OSS Distributions 	};
430*27b03b36SApple OSS Distributions 
431*27b03b36SApple OSS Distributions 	int rc = dt_launch_tool(&pid, (char **)(void *)argv, false, NULL, NULL);
432*27b03b36SApple OSS Distributions 	if (rc != 0) {
433*27b03b36SApple OSS Distributions 		return -1;
434*27b03b36SApple OSS Distributions 	}
435*27b03b36SApple OSS Distributions 	if (!dt_waitpid(pid, &exit_status, NULL, 30)) {
436*27b03b36SApple OSS Distributions 		if (exit_status != 0) {
437*27b03b36SApple OSS Distributions 			return exit_status;
438*27b03b36SApple OSS Distributions 		}
439*27b03b36SApple OSS Distributions 		return -1;
440*27b03b36SApple OSS Distributions 	}
441*27b03b36SApple OSS Distributions 
442*27b03b36SApple OSS Distributions 	return exit_status;
443*27b03b36SApple OSS Distributions }
444*27b03b36SApple OSS Distributions 
445*27b03b36SApple OSS Distributions /*
446*27b03b36SApple OSS Distributions  * Return the path to a temporary mount
447*27b03b36SApple OSS Distributions  * that contains a usable HFS+ filesystem
448*27b03b36SApple OSS Distributions  * mounted via a ram disk
449*27b03b36SApple OSS Distributions  */
450*27b03b36SApple OSS Distributions static char *
mkramdisk(void)451*27b03b36SApple OSS Distributions mkramdisk(void)
452*27b03b36SApple OSS Distributions {
453*27b03b36SApple OSS Distributions 	char cmd[1024];
454*27b03b36SApple OSS Distributions 	char *mount_path = mktempdir();
455*27b03b36SApple OSS Distributions 	char *dev_disk_file = malloc(256);
456*27b03b36SApple OSS Distributions 	atexit_b(^{ free(dev_disk_file); });
457*27b03b36SApple OSS Distributions 	strcpy(dev_disk_file, "/tmp/dmc.ramdisk.XXXXXXXX");
458*27b03b36SApple OSS Distributions 
459*27b03b36SApple OSS Distributions 	T_WITH_ERRNO;
460*27b03b36SApple OSS Distributions 	T_ASSERT_NOTNULL(mktemp(dev_disk_file), "Create temporary file to store dev disk for ramdisk");
461*27b03b36SApple OSS Distributions 	atexit_b(^{ remove(dev_disk_file); });
462*27b03b36SApple OSS Distributions 
463*27b03b36SApple OSS Distributions 	// create the RAM disk device
464*27b03b36SApple OSS Distributions 	snprintf(cmd, sizeof(cmd), "hdik -nomount ram://10000 > %s", dev_disk_file);
465*27b03b36SApple OSS Distributions 	T_ASSERT_EQ_INT(0, system_legal(cmd), "Create ramdisk");
466*27b03b36SApple OSS Distributions 
467*27b03b36SApple OSS Distributions 	atexit_b(^{
468*27b03b36SApple OSS Distributions 		char eject_cmd[1024];
469*27b03b36SApple OSS Distributions 		unmount(mount_path, MNT_FORCE);
470*27b03b36SApple OSS Distributions 		snprintf(eject_cmd, sizeof(eject_cmd), "hdik -e `cat %s`", dev_disk_file);
471*27b03b36SApple OSS Distributions 		system_legal(eject_cmd);
472*27b03b36SApple OSS Distributions 		remove(dev_disk_file);
473*27b03b36SApple OSS Distributions 	});
474*27b03b36SApple OSS Distributions 
475*27b03b36SApple OSS Distributions 	// initialize as an HFS volume
476*27b03b36SApple OSS Distributions 	snprintf(cmd, sizeof(cmd), "newfs_hfs `cat %s`", dev_disk_file);
477*27b03b36SApple OSS Distributions 	T_ASSERT_EQ_INT(0, system_legal(cmd), "Initialize ramdisk as HFS");
478*27b03b36SApple OSS Distributions 
479*27b03b36SApple OSS Distributions 	// mount it
480*27b03b36SApple OSS Distributions 	snprintf(cmd, sizeof(cmd), "mount -t hfs `cat %s` %s", dev_disk_file, mount_path);
481*27b03b36SApple OSS Distributions 	T_ASSERT_EQ_INT(0, system_legal(cmd), "Mount ramdisk");
482*27b03b36SApple OSS Distributions 
483*27b03b36SApple OSS Distributions 	return mount_path;
484*27b03b36SApple OSS Distributions }
485*27b03b36SApple OSS Distributions 
486*27b03b36SApple OSS Distributions static uint64_t
time_for_read(int fd,const char * expected)487*27b03b36SApple OSS Distributions time_for_read(int fd, const char *expected)
488*27b03b36SApple OSS Distributions {
489*27b03b36SApple OSS Distributions 	int err;
490*27b03b36SApple OSS Distributions 	ssize_t ret;
491*27b03b36SApple OSS Distributions 	char buf[READSIZE];
492*27b03b36SApple OSS Distributions 	uint64_t start, stop;
493*27b03b36SApple OSS Distributions 
494*27b03b36SApple OSS Distributions 	bzero(buf, sizeof(buf));
495*27b03b36SApple OSS Distributions 	lseek(fd, 0, SEEK_SET);
496*27b03b36SApple OSS Distributions 
497*27b03b36SApple OSS Distributions 	start = dt_nanoseconds();
498*27b03b36SApple OSS Distributions 	ret = read(fd, buf, READSIZE);
499*27b03b36SApple OSS Distributions 	stop = dt_nanoseconds();
500*27b03b36SApple OSS Distributions 
501*27b03b36SApple OSS Distributions 	T_ASSERT_GE_LONG(ret, 0L, "read from temporary file");
502*27b03b36SApple OSS Distributions 	T_ASSERT_EQ_LONG(ret, READSIZE, "read %ld bytes from temporary file", READSIZE);
503*27b03b36SApple OSS Distributions 	err = memcmp(buf, expected, sizeof(buf));
504*27b03b36SApple OSS Distributions 	T_ASSERT_EQ_INT(0, err, "read expected contents from temporary file");
505*27b03b36SApple OSS Distributions 
506*27b03b36SApple OSS Distributions 	return stop - start;
507*27b03b36SApple OSS Distributions }
508*27b03b36SApple OSS Distributions 
509*27b03b36SApple OSS Distributions static void
perf_setup(char ** path,int * fd)510*27b03b36SApple OSS Distributions perf_setup(char **path, int *fd)
511*27b03b36SApple OSS Distributions {
512*27b03b36SApple OSS Distributions 	int temp_fd;
513*27b03b36SApple OSS Distributions 	char *temp_path;
514*27b03b36SApple OSS Distributions 
515*27b03b36SApple OSS Distributions 	char *mount_path = mkramdisk();
516*27b03b36SApple OSS Distributions 	temp_path = *path = malloc(PATH_MAX);
517*27b03b36SApple OSS Distributions 	snprintf(temp_path, PATH_MAX, "%s/dmc.XXXXXXXX", mount_path);
518*27b03b36SApple OSS Distributions 	atexit_b(^{ free(temp_path); });
519*27b03b36SApple OSS Distributions 
520*27b03b36SApple OSS Distributions 	T_ASSERT_NOTNULL(mktemp(temp_path), "Create temporary file");
521*27b03b36SApple OSS Distributions 	atexit_b(^{ remove(temp_path); });
522*27b03b36SApple OSS Distributions 
523*27b03b36SApple OSS Distributions 	temp_fd = *fd = open(temp_path, O_RDWR | O_CREAT);
524*27b03b36SApple OSS Distributions 	T_WITH_ERRNO;
525*27b03b36SApple OSS Distributions 	T_ASSERT_GE_INT(temp_fd, 0, "Open temporary file for read/write");
526*27b03b36SApple OSS Distributions 	atexit_b(^{ close(temp_fd); });
527*27b03b36SApple OSS Distributions 	fcntl(temp_fd, F_NOCACHE, 1);
528*27b03b36SApple OSS Distributions }
529*27b03b36SApple OSS Distributions #endif /* !TEST_UNENTITLED */
530