DF-5026 / df5026.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 | /* * DF-5026 sink demo โ ips: bios queued to an offline adapter are never * biodone'd; ips_start_io_request() swallows ips_get_free_cmd()'s EIO * * Root cause chain replicated verbatim: * * sys/dev/raid/ips/ips.c:200-236 ips_get_free_cmd(): * if (sc->state & IPS_OFFLINE) { error = EIO; ... } * sys/dev/raid/ips/ips_commands.c:238-253 ips_start_io_request(): * bio = bioq_first(&sc->bio_queue); * if (bio == NULL) return; * if (ips_get_free_cmd(sc, &command, 0) != 0) * return; <-- EIO/EBUSY SWALLOWED: * bio stays queued forever * sys/dev/raid/ips/ips_disk.c:128-143 ipsd_strategy(): * lockmgr(&dsc->sc->queue_lock, LK_EXCLUSIVE|LK_RETRY); * bioqdisksort(&dsc->sc->bio_queue, bio); * ips_start_io_request(dsc->sc); <-- no fail-fast on * lockmgr(...LK_RELEASE); IPS_OFFLINE/IPS_TIMEOUT * * sc->state IPS_OFFLINE is set by ips_timeout() (ips.c:358) after a failed * forced adapter reinit ("AIEE! adapter reset failed ... giving up") and by * ips_cmdqueue_free() (ips.c:137). From then on every bio enqueued by * ipsd_strategy() waits forever: no biodone(), no B_ERROR, no b_resid โ * unkillable D-state for every issuer, and bio_queue grows without bound. * * Following the DF-4986/DF-5012 precedent (guest has no IBM ServeRAID * adapter), this module wires the exact enqueue/start/swallow pattern to a * raw D_DISK device: * * read @ 3M -> toggle the simulated IPS_OFFLINE state * read @ 2M -> bio parked in bio_queue forever while offline * read @ 1M -> drain-and-complete (what a correct error path must do) */ #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/buf2.h> #include <sys/lock.h> #include <sys/malloc.h> #define PARK_OFF (2UL << 20) /* offset that parks the bio */ #define POKE_OFF (1UL << 20) /* offset that drains the queue */ #define TOGGLE_OFF (3UL << 20) /* offset that toggles offline */ static struct lock queue_lock; /* sc->queue_lock analog */ static struct bio_queue_head bio_queue; /* sc->bio_queue analog */ static int offline; /* sc->state & IPS_OFFLINE */ static int parked; /* leaked-bio counter */ static d_open_t df5026_open; static d_close_t df5026_close; static d_strategy_t df5026_strategy; static struct dev_ops df5026_ops = { { "df5026", 0, D_DISK | D_MPSAFE }, .d_open = df5026_open, .d_close = df5026_close, .d_strategy = df5026_strategy, }; static struct disk df5026_disk; static int df5026_open(struct dev_open_args *ap) { return (0); } static int df5026_close(struct dev_close_args *ap) { return (0); } /* ips.c:207-209 analog โ OFFLINE yields EIO from get_free_cmd */ static int sim_get_free_cmd(void) { if (offline) return (EIO); /* also EBUSY for IPS_TIMEOUT, ips.c:220 */ return (0); } static void sim_complete_bio(struct bio *bio) { struct buf *bp = bio->bio_buf; bp->b_resid = 0; /* ipsd_finish() healthy path, ips_disk.c:119 */ biodone(bio); } /* ips_commands.c:238-253 ips_start_io_request() โ VERBATIM structure */ static void sim_start_io_request(void) { struct bio *bio; bio = bioq_first(&bio_queue); if (bio == NULL) return; if (sim_get_free_cmd() != 0) return; /* <-- error swallowed, bio parked */ bioq_remove(&bio_queue, bio); sim_complete_bio(bio); /* healthy adapter: command + completion */ } /* what a correct driver must do when the adapter is dead: fail everything */ static void sim_drain_queue(int error) { struct bio *bio; while ((bio = bioq_first(&bio_queue)) != NULL) { struct buf *bp = bio->bio_buf; bioq_remove(&bio_queue, bio); bp->b_flags |= B_ERROR; bp->b_error = error; bp->b_resid = bp->b_bcount; biodone(bio); parked--; } } /* * ips_disk.c:128-143 ipsd_strategy() โ VERBATIM structure (control offsets * muxed in front, DF-5012 style, so the demo is self-driving and the guest * can always be returned to a clean state). */ static int df5026_strategy(struct dev_strategy_args *ap) { struct bio *bio = ap->a_bio; struct buf *bp = bio->bio_buf; /* ---- demo control plane (NOT part of the replicated code) ---- */ if (bio->bio_offset == TOGGLE_OFF) { lockmgr(&queue_lock, LK_EXCLUSIVE | LK_RETRY); if (!offline) { offline = 1; kprintf("df5026: OFFLINE (ips_timeout reinit failed) " "-- ips_get_free_cmd now returns EIO, " "ips_start_io_request will swallow it\n"); } else { offline = 0; kprintf("df5026: back ONLINE; restarting queue\n"); sim_start_io_request(); } lockmgr(&queue_lock, LK_RELEASE); bp->b_resid = 0; biodone(bio); return (0); } if (bio->bio_offset == POKE_OFF) { lockmgr(&queue_lock, LK_EXCLUSIVE | LK_RETRY); kprintf("df5026: poke -- draining %d parked bio(s) with EIO " "(what the driver should have done)\n", parked); sim_drain_queue(EIO); lockmgr(&queue_lock, LK_RELEASE); bp->b_resid = 0; biodone(bio); return (0); } /* ---- VERBATIM ipsd_strategy (ips_disk.c:134-141) ---- */ bio->bio_driver_info = NULL; /* ips_disk.c:136 (dsc elided) */ lockmgr(&queue_lock, LK_EXCLUSIVE | LK_RETRY); bioqdisksort(&bio_queue, bio); /* ips_disk.c:139 */ parked++; /* observability only */ sim_start_io_request(); /* ips_disk.c:140 โ parks if offline */ lockmgr(&queue_lock, LK_RELEASE);/* ips_disk.c:141 */ return (0); } static int df5026_modevent(module_t mod, int type, void *data) { struct disk_info info; int error = 0; switch (type) { case MOD_LOAD: lockinit(&queue_lock, "df5026q", 0, 0); bioq_init(&bio_queue); bzero(&df5026_disk, sizeof(df5026_disk)); bzero(&info, sizeof(info)); info.d_media_blksize = 512; info.d_media_blocks = 8192; /* 4 MiB test media */ info.d_nheads = 64; info.d_secpertrack = 32; disk_create(0, &df5026_disk, &df5026_ops); disk_setdiskinfo(&df5026_disk, &info); offline = 0; parked = 0; kprintf("df5026: raw disk \"%s\" up (ONLINE) โ " "read@3M toggle-offline, read@2M park, read@1M drain\n", df5026_disk.d_cdev->si_name); break; case MOD_UNLOAD: /* hygiene: never unload with bios still parked */ lockmgr(&queue_lock, LK_EXCLUSIVE | LK_RETRY); if (parked) kprintf("df5026: unload -- draining %d parked bio(s)\n", parked); sim_drain_queue(ENXIO); lockmgr(&queue_lock, LK_RELEASE); disk_destroy(&df5026_disk); break; default: error = EOPNOTSUPP; break; } return (error); } DEV_MODULE(df5026, df5026_modevent, NULL); |