DF-5299 / usbsoak.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 | /* usbsoak.c - pass-2 usb_device.c state-machine soak (runs as root in guest) * * Hammers the ugen entry points that drive sys/bus/u4b/usb_device.c: * USB_SET_ALTINTERFACE -> usbd_set_alt_interface_index -> usb_fifo_free_wrap * -> usb_config_parse(iface, alt) [zeroing loop] * USB_SET_CONFIG -> usbd_start_set_config -> usbd_set_config_index * (usb_unconfigure / usb_config_parse ALLOC+INIT / * usb_cdev_create churn on the explore proc) * USB_GET_FULL_DESC -> ugen_get_cdesc (cdesc cache + kfree churn) * USB_REQUEST -> control transfers (usbd_do_request_flags) * USB_IFACE_DRIVER_DETACH -> usb_detach_device * concurrent on two devices; expects lots of EINVAL/EBUSY/ENXIO, NEVER a panic. */ #include <sys/ioctl.h> #include <sys/fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <time.h> #include <errno.h> #include <pthread.h> #include <bus/u4b/usb.h> #include <bus/u4b/usb_ioctl.h> #ifndef RUN_SECONDS #define RUN_SECONDS 90 #endif static volatile int stop_flag; static unsigned long ops, errs, ok_alt, ok_cfg; static unsigned int seed; static double now_sec(void) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return ts.tv_sec + ts.tv_nsec / 1e9; } static void do_alt(int fd) { struct usb_alt_interface ai; uint8_t idx[8] = {0, 0, 1, 2, 31, 255, (uint8_t)(seed >> 9), (uint8_t)(seed >> 17)}; uint8_t alt[8] = {0, 0, 1, 2, 255, (uint8_t)(seed >> 5), (uint8_t)(seed >> 13), 0}; int i = rand_r(&seed) % 8, j = rand_r(&seed) % 8; memset(&ai, 0, sizeof(ai)); ai.uai_interface_index = idx[i]; ai.uai_alt_index = alt[j]; if (ioctl(fd, USB_SET_ALTINTERFACE, &ai) == 0) ok_alt++; } static void do_cfg(int fd) { int v[6] = {0, 0, 1, 2, 255, rand_r(&seed) & 0xff}; int i = rand_r(&seed) % 6; if (ioctl(fd, USB_SET_CONFIG, &v[i]) == 0) ok_cfg++; } static void do_fulldesc(int fd) { static char buf[4200]; struct usb_gen_descriptor gd; memset(&gd, 0, sizeof(gd)); gd.ugd_data = buf; gd.ugd_maxlen = 64 + (rand_r(&seed) % 4096); gd.ugd_config_index = rand_r(&seed) % 4; gd.ugd_string_index = rand_r(&seed) % 4; ioctl(fd, USB_GET_FULL_DESC, &gd); } static void do_request(int fd) { static char buf[256]; struct usb_ctl_request req; memset(&req, 0, sizeof(req)); req.ucr_request.bmRequestType = 0x80; /* device IN */ req.ucr_request.bRequest = 0x06; /* GET_DESCRIPTOR */ req.ucr_request.wValue[0] = rand_r(&seed) % 4; /* index */ req.ucr_request.wValue[1] = rand_r(&seed) % 4; /* type: device/config/string */ req.ucr_request.wIndex[0] = 0x09; req.ucr_request.wIndex[1] = 0; req.ucr_request.wLength[0] = 0x20; req.ucr_request.wLength[1] = 0; req.ucr_data = buf; req.ucr_flags = 0; ioctl(fd, USB_DO_REQUEST, &req); } static void do_detach(int fd) { int v = rand_r(&seed) % 3; ioctl(fd, USB_IFACE_DRIVER_DETACH, &v); } static void *worker(void *arg) { const char *dev = arg; unsigned int lseed = seed ^ (unsigned)(size_t)arg; double t_end = now_sec() + RUN_SECONDS; int fd = open(dev, O_RDWR | O_NONBLOCK); if (fd < 0) { printf("open %s: %s\n", dev, strerror(errno)); return NULL; } while (now_sec() < t_end && !stop_flag) { int r = rand_r(&lseed) % 100; if (r < 35) do_alt(fd); else if (r < 55) do_cfg(fd); else if (r < 75) do_fulldesc(fd); else if (r < 95) do_request(fd); else do_detach(fd); ops++; if ((rand_r(&lseed) % 64) == 0) printf("tick ops=%lu alt_ok=%lu cfg_ok=%lu\n", ops, ok_alt, ok_cfg); usleep(200 + (rand_r(&lseed) % 1800)); } close(fd); return NULL; } struct reopen_arg { const char *dev; }; static void *reopener(void *arg) { struct reopen_arg *ra = arg; double t_end = now_sec() + RUN_SECONDS; while (now_sec() < t_end && !stop_flag) { int fd = open(ra->dev, O_RDONLY | O_NONBLOCK); if (fd >= 0) { struct usb_gen_descriptor gd; static char buf[512]; memset(&gd, 0, sizeof(gd)); gd.ugd_data = buf; gd.ugd_maxlen = 64; ioctl(fd, USB_GET_FULL_DESC, &gd); usleep(1000 + (rand_r(&seed) % 5000)); close(fd); } usleep(3000); } return NULL; } int main(int argc, char **argv) { pthread_t t1, t2, t3; seed = argc > 1 ? atoi(argv[1]) : 1234; printf("soak start seed=%u devs: /dev/usb/1.2.0 /dev/usb/1.3.0\n", seed); pthread_create(&t1, NULL, worker, "/dev/usb/1.2.0"); pthread_create(&t2, NULL, worker, "/dev/usb/1.3.0"); pthread_create(&t3, NULL, reopener, &(struct reopen_arg){"/dev/usb/1.3.1"}); pthread_join(t1, NULL); pthread_join(t2, NULL); pthread_join(t3, NULL); printf("soak done: ops=%lu alt_ok=%lu cfg_ok=%lu (errors expected, panic = FAIL)\n", ops, ok_alt, ok_cfg); return 0; } |