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