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