DF-4646 / nvmebang.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 | /* * nvmebang.c -- DF-4528/DF-4529 PoC trigger v2 (TEST ONLY, never for sys/). * * Bypasses newbus state bookkeeping and invokes the nvme driver's OWN * attach/detach driver-methods on unit 0, so the device_t remains in the * devclass and the operation can be repeated indefinitely: * * sysctl hw.nvmebang.action=1 -> run nvme_pci_detach() (teardown race) * sysctl hw.nvmebang.action=2 -> run nvme_pci_attach() (identify storm) * * This reproduces, on demand and repeatably, what a PCI hot-unplug / * surprise removal (action=1) and hot-plug / rescan (action=2) do to the * driver while user processes hold namespace devices open and are issuing * NVMEIOCGETLOG admin ioctls. */ #include <sys/param.h> #include <sys/module.h> #include <sys/kernel.h> #include <sys/systm.h> #include <sys/bus.h> #include <sys/sysctl.h> #include <sys/kobj.h> static int nvmebang_action(SYSCTL_HANDLER_ARGS); static int nvmebang_act = 0; SYSCTL_NODE(_hw, OID_AUTO, nvmebang, CTLFLAG_RW, 0, "nvme bang test"); SYSCTL_PROC(_hw_nvmebang, OID_AUTO, action, CTLTYPE_INT | CTLFLAG_RW, &nvmebang_act, 0, nvmebang_action, "I", "1=detach 2=attach"); static int nvmebang_action(SYSCTL_HANDLER_ARGS) { devclass_t dc; driver_t *drv; kobj_method_t *m; device_t dev; int error; int (*attach_fn)(device_t) = NULL; int (*detach_fn)(device_t) = NULL; error = sysctl_handle_int(oidp, &nvmebang_act, 0, req); if (error || req->newptr == NULL) return (error); if (nvmebang_act == 0) return (0); dc = devclass_find("nvme"); if (dc == NULL) { kprintf("nvmebang: devclass 'nvme' not found\n"); return (ENOENT); } dev = devclass_get_device(dc, 0); if (dev == NULL) { kprintf("nvmebang: nvme0 not present\n"); return (ENOENT); } drv = device_get_driver(dev); if (drv == NULL || drv->methods == NULL) { kprintf("nvmebang: no driver\n"); return (ENOENT); } for (m = drv->methods; m && m->func; ++m) { if (m->desc == NULL) continue; if (m->desc == &device_attach_desc) attach_fn = (int (*)(device_t))m->func; else if (m->desc == &device_detach_desc) detach_fn = (int (*)(device_t))m->func; } kprintf("nvmebang: methods attach=%p detach=%p\n", attach_fn, detach_fn); switch (nvmebang_act) { case 1: if (detach_fn == NULL) return (ENOENT); kprintf("nvmebang: DETACH method on %s%d...\n", device_get_name(dev), device_get_unit(dev)); error = detach_fn(dev); kprintf("nvmebang: DETACH method done error=%d\n", error); break; case 2: if (attach_fn == NULL) return (ENOENT); kprintf("nvmebang: ATTACH method on %s%d...\n", device_get_name(dev), device_get_unit(dev)); error = attach_fn(dev); kprintf("nvmebang: ATTACH method done error=%d\n", error); break; default: return (EINVAL); } nvmebang_act = 0; return (0); } static int nvmebang_load(module_t mod, int what, void *arg) { switch (what) { case MOD_LOAD: kprintf("nvmebang: loaded (sysctl hw.nvmebang.action)\n"); return (0); case MOD_UNLOAD: return (0); default: return (0); } } static moduledata_t nvmebang_mod = { "nvmebang", nvmebang_load, NULL }; DECLARE_MODULE(nvmebang, nvmebang_mod, SI_SUB_DRIVERS, SI_ORDER_ANY); MODULE_VERSION(nvmebang, 1); |