DF-5071 / df5071.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 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 | /* * DF-5071 sink demo โ amr/amrd: bios submitted while the controller is * in AMR_STATE_SHUTDOWN (or wedged) are parked on sc->amr_bioq forever; * nothing ever drains or fails them * * Root cause chain replicated verbatim: * * sys/dev/raid/amr/amr_disk.c:166-194 amrd_strategy(): * if (sc == NULL) { ...biodone... } * bio->bio_driver_info = sc; * devstat_start_transaction(...); * amr_submit_bio(sc->amrd_controller, bio); <-- NO GATE on * return (0); AMR_STATE_SHUTDOWN * sys/dev/raid/amr/amr_disk.c:126-127 amrd_open(): <-- the gate EXISTS * if (sc->amrd_controller->amr_state & AMR_STATE_SHUTDOWN) * return(ENXIO); in open only * sys/dev/raid/amr/amr.c:407-416 amr_submit_bio(): * lockmgr(...); amr_enqueue_bio(sc, bio); amr_startio(sc); ... * sys/dev/raid/amr/amr.c:1155-1188 amr_startio(): * ... if (ac == NULL) (void)amr_bio_command(sc, &ac); ... * if (ac == NULL) break; <-- ENOMEM return IGNORED; bios stay * sys/dev/raid/amr/amr.c:1224-1247 amr_bio_command(): * if ((ac = amr_alloccmd(sc)) == NULL) return (ENOMEM); * <-- with every command stuck busy on a dead adapter (there is NO * timeout/watchdog anywhere in amr) every bio parks on amr_bioq * sys/dev/raid/amr/amr_pci.c:387-417 amr_pci_shutdown() and * sys/dev/raid/amr/amr.c:454-485 amr_rescan_drives(): * sc->amr_state |= AMR_STATE_SHUTDOWN; ... device_delete_child(...) * <-- amr_bioq is NEVER drained; the comment at amr_pci.c:383-384 * ("we can assume that the bioq ... is empty, as we won't allow * shutdown if any device is open") is false: AMR_STATE_OPEN is * set only by the CONTROL device /dev/amr0 (amr.c:430); amrd_open * sets nothing, so open disk fds and queued bios do not block it. * sys/dev/raid/amr/amr.c:371-400 amr_free() + kfree(sc) at detach: * bios still on amr_bioq are never biodone'd (and feed the DF-2082 * in-flight UAF window). * * Consequence: an issuer whose bio parks sleeps in biowait forever โ * unkillable D-state; queue grows unbounded. Trigger: root kldunload amr / * AMR_RESCAN_DRIVES ioctl with I/O queued or in flight, or any adapter * wedge (no watchdog exists to recover). * * Following the DF-4986/DF-5012/DF-5026 house precedent (guest has no * MegaRAID adapter), this module wires the exact enqueue/start/swallow * topology to a raw D_DISK device (/dev/df50710). amrd_open's verbatim * ENXIO gate is kept on the raw device (it is part of the asymmetry being * demonstrated); the demo control plane (shutdown/poke/online toggles) * lives on a separate ungated char device /dev/df5071ctl so the demo is * self-driving and the guest can always be returned to a clean state. */ #include <sys/param.h> #include <sys/kernel.h> #include <sys/module.h> #include <sys/systm.h> #include <sys/conf.h> #include <sys/disk.h> #include <sys/bio.h> #include <sys/buf2.h> #include <sys/lock.h> #include <sys/malloc.h> #include <sys/types.h> #include <sys/device.h> #include <sys/devfs.h> #ifdef DF5071_FIXED #define PREFIX "df5071f" #else #define PREFIX "df5071" #endif #define AMR_STATE_SHUTDOWN (1<<3) /* amrvar.h:222 */ /* control-plane ioctls on /dev/df5071ctl */ #define DF5071_CTL_SHUTDOWN _IOW('D', 71, int) /* amr_pci_shutdown */ #define DF5071_CTL_POKE _IOW('D', 72, int) /* external drain */ #define DF5071_CTL_ONLINE _IOW('D', 73, int) /* amr_startup */ #define PARK_OFF (2UL << 20) /* data-path read offset (in-media) */ static struct lock amr_list_lock; /* sc->amr_list_lock */ static struct bio_queue_head amr_bioq; /* sc->amr_bioq */ static int amr_state; /* sc->amr_state */ static int cmd_stuck; /* adapter stops completing */ static int parked; /* observability only */ /* * amr.c:1881-1904 amr_alloccmd() analog: returns NULL when no command is * free. On a wedged adapter every command sits busy forever (amr has no * timeout/watchdog to complete or reclaim them), so every call fails. */ static void * sim_alloccmd(void) { if (cmd_stuck) return (NULL); /* all commands stuck busy */ return ((void *)1); } /* amr_disk.c:196-214 amrd_intr() โ healthy completion path */ static void sim_amrd_intr(struct bio *bio) { struct buf *bp = bio->bio_buf; /* devstat_end_transaction_buf elided */ if (bp->b_flags & B_ERROR) { bp->b_error = EIO; } else { bp->b_resid = 0; /* amr_disk.c:210 */ } biodone(bio); /* amr_disk.c:213 */ parked--; /* observability only */ } /* * amr.c:1224-1304 amr_bio_command() โ VERBATIM structure. The bio is only * dequeued AFTER a command was obtained; on ENOMEM it stays queued. * *acp is set ONLY when a command was actually built (bio dequeued) โ * the caller's `ac` stays NULL otherwise, exactly as in amr.c. */ static int sim_bio_command(int *acp) { struct bio *bio; *acp = 0; if (sim_alloccmd() == NULL) return (ENOMEM); /* amr.c:1240-1241 */ if ((bio = bioq_first(&amr_bioq)) == NULL) return (0); /* amr.c:1244-1246: releasecmd, * ac stays NULL */ bioq_remove(&amr_bioq, bio); *acp = 1; /* command built */ sim_amrd_intr(bio); /* command completes (online) */ return (0); } /* amr.c:1155-1188 amr_startio() โ VERBATIM structure */ static void sim_startio(void) { int havecmd; for (;;) { havecmd = 0; (void)sim_bio_command(&havecmd); /* amr.c:1171: return * IGNORED */ if (havecmd == 0) break; /* amr.c:1178: give up -- bios STAY * QUEUED on amr_bioq forever */ /* amr_start() succeeds in the sim; loop for the next bio */ } } /* what amr_pci_shutdown/amr_rescan_drives must do but never do */ static void sim_drain_bios(int error) { struct bio *bio; while ((bio = bioq_first(&amr_bioq)) != NULL) { struct buf *bp = bio->bio_buf; bioq_remove(&amr_bioq, bio); bp->b_flags |= B_ERROR; bp->b_error = error; bp->b_resid = bp->b_bcount; biodone(bio); parked--; } } /* ---- raw disk device (the amrd replica) ---- */ static d_open_t df5071_open; static d_close_t df5071_close; static d_strategy_t df5071_strategy; static struct dev_ops df5071_ops = { #ifdef DF5071_FIXED { "df5071f", 0, D_DISK | D_MPSAFE }, #else { "df5071", 0, D_DISK | D_MPSAFE }, #endif .d_open = df5071_open, .d_close = df5071_close, .d_strategy = df5071_strategy, }; static struct disk df5071_disk; /* * amrd_open(), amr_disk.c:116-130 โ VERBATIM: the SHUTDOWN gate that the * real driver HAS in open() and LACKS in strategy(). */ static int df5071_open(struct dev_open_args *ap) { /* controller not active? */ if (amr_state & AMR_STATE_SHUTDOWN) return (ENXIO); /* amr_disk.c:126-127 */ return (0); } static int df5071_close(struct dev_close_args *ap) { return (0); } /* * amrd_strategy(), amr_disk.c:166-194 โ VERBATIM structure (a parked-bio * counter is added for observability). */ static int df5071_strategy(struct dev_strategy_args *ap) { struct bio *bio = ap->a_bio; #ifdef DF5071_FIXED struct buf *bp = bio->bio_buf; /* THE FIX (part 1): mirror the amrd_open() gate (:126-127) */ if (amr_state & AMR_STATE_SHUTDOWN) { bp->b_error = ENXIO; goto bad; } #endif bio->bio_driver_info = NULL; /* amr_disk.c:179 (sc elided) */ /* devstat_start_transaction elided (amr_disk.c:181) */ lockmgr(&amr_list_lock, LK_EXCLUSIVE | LK_RETRY); /* amr.c:411 */ bioq_insert_tail(&amr_bioq, bio); /* amr_enqueue_bio, amrvar.h:309 */ parked++; /* observability only */ sim_startio(); /* amr.c:413 โ parks if stuck */ lockmgr(&amr_list_lock, LK_RELEASE); /* amr.c:414 */ return (0); /* amr_submit_bio returns 0 */ #ifdef DF5071_FIXED bad: bp->b_flags |= B_ERROR; /* amr_disk.c:186-192 */ bp->b_resid = bp->b_bcount; biodone(bio); return (0); #endif } /* ---- control device (demo plane; NOT part of the replicated code) ---- */ static d_open_t df5071ctl_open; static d_close_t df5071ctl_close; static d_ioctl_t df5071ctl_ioctl; static struct dev_ops df5071ctl_ops = { { "df5071ctl", 0, D_MEM }, .d_open = df5071ctl_open, .d_close = df5071ctl_close, .d_ioctl = df5071ctl_ioctl, }; static int df5071ctl_open(struct dev_open_args *ap) { return (0); } static int df5071ctl_close(struct dev_close_args *ap) { return (0); } static int df5071ctl_ioctl(struct dev_ioctl_args *ap) { switch (ap->a_cmd) { case DF5071_CTL_SHUTDOWN: /* amr_pci_shutdown (amr_pci.c:394-411) / * amr_rescan_drives (amr.c:465-481) */ lockmgr(&amr_list_lock, LK_EXCLUSIVE | LK_RETRY); amr_state |= AMR_STATE_SHUTDOWN; /* amr_pci.c:395 */ cmd_stuck = 1; /* adapter wedged / children deleted */ kprintf(PREFIX ": amr_pci_shutdown/amr_rescan_drives: " "AMR_STATE_SHUTDOWN set; amr_bioq NOT drained " "(%d bio(s) already parked); amrd_strategy has NO " "shutdown gate\n", parked); #ifdef DF5071_FIXED /* THE FIX (part 2): drain queued bios before deleting * children โ they can never complete now */ if (parked) { kprintf(PREFIX ": (fixed) draining %d parked bio(s) " "with ENXIO\n", parked); sim_drain_bios(ENXIO); } #endif lockmgr(&amr_list_lock, LK_RELEASE); break; case DF5071_CTL_POKE: /* what the shutdown path never does */ lockmgr(&amr_list_lock, LK_EXCLUSIVE | LK_RETRY); kprintf(PREFIX ": poke -- draining %d parked bio(s) with EIO " "(what the shutdown path should have done)\n", parked); sim_drain_bios(EIO); lockmgr(&amr_list_lock, LK_RELEASE); break; case DF5071_CTL_ONLINE: /* amr_startup (amr.c:294-344) */ lockmgr(&amr_list_lock, LK_EXCLUSIVE | LK_RETRY); amr_state &= ~AMR_STATE_SHUTDOWN; /* amr.c:338 */ cmd_stuck = 0; kprintf(PREFIX ": amr_startup: back online, restarting io\n"); sim_startio(); lockmgr(&amr_list_lock, LK_RELEASE); break; default: return (EINVAL); } return (0); } static int df5071_modevent(module_t mod, int type, void *data) { struct disk_info info; int error = 0; switch (type) { case MOD_LOAD: lockinit(&amr_list_lock, PREFIX "q", 0, 0); bioq_init(&amr_bioq); bzero(&df5071_disk, sizeof(df5071_disk)); bzero(&info, sizeof(info)); info.d_media_blksize = 512; info.d_media_blocks = 16384; /* 8 MiB test media */ info.d_nheads = 64; info.d_secpertrack = 32; disk_create(0, &df5071_disk, &df5071_ops); disk_setdiskinfo(&df5071_disk, &info); #ifdef DF5071_FIXED make_dev(&df5071ctl_ops, 0, UID_ROOT, GID_WHEEL, 0666, "df5071fctl"); #else make_dev(&df5071ctl_ops, 0, UID_ROOT, GID_WHEEL, 0666, "df5071ctl"); #endif amr_state = 0; cmd_stuck = 0; parked = 0; kprintf(PREFIX ": raw disk \"%s\" + ctl up (ONLINE) โ ctl " "ioctls 71/72/73 = shutdown/poke/online; open() carries " "the verbatim amrd_open ENXIO gate\n", df5071_disk.d_cdev->si_name); break; case MOD_UNLOAD: /* hygiene: never unload with bios still parked */ lockmgr(&amr_list_lock, LK_EXCLUSIVE | LK_RETRY); if (parked) kprintf(PREFIX ": unload -- draining %d parked " "bio(s)\n", parked); sim_drain_bios(ENXIO); lockmgr(&amr_list_lock, LK_RELEASE); disk_destroy(&df5071_disk); #ifdef DF5071_FIXED destroy_dev(devfs_find_device_by_name("df5071fctl")); #else destroy_dev(devfs_find_device_by_name("df5071ctl")); #endif break; default: error = EOPNOTSUPP; break; } return (error); } #ifdef DF5071_FIXED DEV_MODULE(df5071f, df5071_modevent, NULL); #else DEV_MODULE(df5071, df5071_modevent, NULL); #endif |