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