1793
test_atapi_engine_restart_in_flight(true);
1794
}
1795
1796
+/*
1797
+ * Regression test: an unplug runs no device reset, so it is the last chance to
1798
+ * detach an outstanding request. Its completion would otherwise walk the
1799
+ * AHCIDevice array that ahci_uninit() has freed, which each of the NCQ, DMA
1800
+ * and PIO completions reaches by a different route.
1801
+ *
1802
+ * The ACPI ejection register is what a guest writes to finish a PCI unplug.
1803
+ * -M pc is what puts it in reach: q35 has no ACPI hotplug on pcie.0, so the
1804
+ * unplug never happens there. Unlike the pciehp attention button this reaches
1805
+ * the unplug with no secondary bus reset, which is the ordering that leaves a
1806
+ * request outstanding.
1807
+ */
1808
+static void test_unplug_in_flight(uint8_t ide_cmd)
1809
+{
1810
+ AHCIQState *ahci;
1811
+ AHCICommand *cmd;
1812
+ uint64_t ptr;
1813
+ uint8_t port;
1814
+ QTestState *qts;
1815
+
1816
+ /*
1817
+ * The latency keeps the backend read in flight across the unplug. A
1818
+ * blkdebug breakpoint cannot stand in for it: cancelling a suspended
1819
+ * request waits for it, so the unplug would never return.
1820
+ */
1821
+ ahci = ahci_boot_and_enable(
1822
+ "-M pc "
1823
+ "-blockdev driver=null-co,node-name=drive0,read-zeroes=on,"
1824
+ "latency-ns=100000000 "
1825
+ "-device ich9-ahci,addr=1f.2,id=ahci0 "
1826
+ "-device ide-hd,drive=drive0,bus=ahci0.0 ");
1827
+ qts = ahci->parent->qts;
1828
+ port = ahci_port_select(ahci);
1829
+ ahci_port_clear(ahci, port);
1830
+
1831
+ ptr = ahci_alloc(ahci, AHCI_SECTOR_SIZE);
1832
+ g_assert(ptr);
1833
+
1834
+ cmd = ahci_command_create(ide_cmd);
1835
+ ahci_command_adjust(cmd, 0, ptr, AHCI_SECTOR_SIZE, 0);
1836
+ ahci_command_commit(ahci, cmd, port);
1837
+ ahci_command_issue_async(ahci, cmd);
1838
+
1839
+ /* Eject slot 0x1f of the root bus, which frees the AHCIDevice array. */
1840
+ qtest_outl(qts, 0xae10, 0);
1841
+ qtest_outl(qts, 0xae08, 1u << 0x1f);
1842
+ qtest_qmp_eventwait(qts, "DEVICE_DELETED");
1843
+
1844
+ /* Four times the backend latency, so the completion has surely run. */
1845
+ g_usleep(400 * 1000);
1846
+ qtest_qmp_assert_success(qts, "{ 'execute': 'query-status' }");
1847
+
1848
+ ahci_command_free(cmd);
1849
+ ahci_free(ahci, ptr);
1850
+ ahci_shutdown(ahci);
1851
+}
1852
+
1853
+static void test_unplug_ncq(void)
1854
+{
1855
+ test_unplug_in_flight(READ_FPDMA_QUEUED);
1856
+}
1857
+
1858
+static void test_unplug_dma(void)
1859
+{
1860
+ test_unplug_in_flight(CMD_READ_DMA);
1861
+}
1862
+
1863
+static void test_unplug_pio(void)
1864
+{
1865
+ test_unplug_in_flight(CMD_READ_PIO);
1866
+}
1867
+
1868
/*
1869
* Regression test: a PIO write outlives the command list it was issued from.
1870
* ide_cancel_dma_sync() does not reach s->pio_aiocb, so the second DRQ phase
2423
test_atapi_engine_restart_pio);
2424
qtest_add_func("/ahci/cdrom/engine_restart/dma",
2425
test_atapi_engine_restart_dma);
2426
+ qtest_add_func("/ahci/io/ncq/unplug", test_unplug_ncq);
2427
+ qtest_add_func("/ahci/io/dma/unplug", test_unplug_dma);
2428
+ qtest_add_func("/ahci/io/pio/unplug", test_unplug_pio);
2429
qtest_add_func("/ahci/io/pio/engine_stop",
2430
test_write_engine_stop_in_flight);
2431
qtest_add_func("/ahci/cdrom/drain/pio", test_atapi_drain_pio);