Index: sys/dev/nvme/nvme_ctrlr.c =================================================================== --- sys/dev/nvme/nvme_ctrlr.c +++ sys/dev/nvme/nvme_ctrlr.c @@ -263,6 +263,7 @@ static int nvme_ctrlr_wait_for_ready(struct nvme_controller *ctrlr, int desired_val) { + int sanity2 = TICKS_2_USEC(1); int timeout = ticks + MSEC_2_TICKS(ctrlr->ready_timeout_in_ms); uint32_t csts; @@ -278,7 +279,18 @@ "within %d ms\n", desired_val, ctrlr->ready_timeout_in_ms); return (ENXIO); } - pause("nvmerdy", 1); + + /* + * Adatively spin for the first tick. Many cards will be ready + * in less than a tick, especially when hz == 100, as is common + * for VMs. + */ + if (sanity2 == 0) { + pause("nvmerdy", 1); + } else { + sanity2--; + DELAY(1); + } } return (0); Index: sys/dev/nvme/nvme_private.h =================================================================== --- sys/dev/nvme/nvme_private.h +++ sys/dev/nvme/nvme_private.h @@ -455,21 +455,28 @@ int nvme_detach(device_t dev); /* - * Wait for a command to complete using the nvme_completion_poll_cb. - * Used in limited contexts where the caller knows it's OK to block - * briefly while the command runs. The ISR will run the callback which - * will set status->done to true, usually within microseconds. If not, - * then after one second timeout handler should reset the controller - * and abort all outstanding requests including this polled one. If - * still not after ten seconds, then something is wrong with the driver, - * and panic is the only way to recover. + * Wait for a command to complete using the nvme_completion_poll_cb. Used in + * limited contexts where the caller knows it's OK to block briefly while the + * command runs. The ISR will run the callback which will set status->done to + * true, usually within microseconds. If not, then after one second timeout + * handler should reset the controller and abort all outstanding requests + * including this polled one. If still not after ten seconds, then something is + * wrong with the driver, and panic is the only way to recover. + * + * Most commands using this interface aren't actual I/O to the drive's media so + * complete within a few microseconds. Adaptively spin for one tick to catch the + * vast majority of these without waiting for a tick plus scheduling delays. Since + * these are on startup, this drastically reduces startup time. */ static __inline void nvme_completion_poll(struct nvme_completion_poll_status *status) { int sanity = hz * 10; + int sanity2 = TICKS_2_USEC(1); + while (!atomic_load_acq_int(&status->done) && --sanity2 > 0) + DELAY(1); while (!atomic_load_acq_int(&status->done) && --sanity > 0) pause("nvme", 1); if (sanity <= 0)