Page MenuHomeFreeBSD

D59477.id186257.diff
No OneTemporary

D59477.id186257.diff

diff --git a/share/man/man9/Makefile b/share/man/man9/Makefile
--- a/share/man/man9/Makefile
+++ b/share/man/man9/Makefile
@@ -1821,6 +1821,7 @@
pci.9 pci_alloc_msix.9 \
pci.9 pci_clear_pme.9 \
pci.9 pci_disable_busmaster.9 \
+ pci.9 pci_disable_busmaster_drain.9 \
pci.9 pci_disable_io.9 \
pci.9 pci_enable_busmaster.9 \
pci.9 pci_enable_io.9 \
diff --git a/share/man/man9/pci.9 b/share/man/man9/pci.9
--- a/share/man/man9/pci.9
+++ b/share/man/man9/pci.9
@@ -23,7 +23,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd August 30, 2026
+.Dd September 9, 2026
.Dt PCI 9
.Os
.Sh NAME
@@ -33,6 +33,7 @@
.Nm pci_alloc_msix ,
.Nm pci_clear_pme ,
.Nm pci_disable_busmaster ,
+.Nm pci_disable_busmaster_drain ,
.Nm pci_disable_io ,
.Nm pci_enable_busmaster ,
.Nm pci_enable_io ,
@@ -94,6 +95,8 @@
.Ft int
.Fn pci_disable_busmaster "device_t dev"
.Ft int
+.Fn pci_disable_busmaster_drain "device_t dev"
+.Ft int
.Fn pci_disable_io "device_t dev" "int space"
.Ft int
.Fn pci_enable_busmaster "device_t dev"
@@ -607,6 +610,65 @@
.Fn pci_disable_busmaster
function clears this bit.
.Pp
+Both functions read back the Command register to verify the requested state.
+They return zero on success,
+.Er ENXIO
+if PCI configuration space is inaccessible, or
+.Er EIO
+if the requested state did not take effect.
+They do not wait for outstanding DMA requests to complete.
+Drivers should check the return value before relying on the requested state.
+.Pp
+The
+.Fn pci_disable_busmaster_drain
+function disables PCI bus mastering, verifies that the bus master enable bit
+is clear, and waits for outstanding PCI-express non-posted requests to complete.
+The transaction-pending check returns as soon as the Transactions Pending
+bit is clear.
+Its timeout is at least 10 milliseconds and otherwise uses the maximum
+completion timeout configured for
+.Fa dev .
+The function may sleep.
+.Pp
+Use
+.Fn pci_disable_busmaster
+when device-specific shutdown has already quiesced DMA or no transaction
+drain is required.
+Use
+.Fn pci_disable_busmaster_drain
+when outstanding non-posted requests must also be drained, for example after
+device-specific shutdown fails, and the calling context permits sleeping.
+It does not replace the device-specific shutdown protocol.
+.Pp
+These functions do not acquire a device lock or record persistent PCI state.
+The caller must serialize them against resets, configuration restoration,
+and other bus-mastering changes.
+After failed device shutdown, the driver must keep bus mastering disabled
+until a successful reset or equivalent device-specific operation has
+discarded all stale DMA addresses.
+The driver must also prevent detach and reattachment from losing this
+quarantine state before that boundary is established.
+.Pp
+The Command-register readback also orders earlier posted writes in the
+configuration transaction's ordering domain.
+The Transactions Pending bit does not track posted writes.
+Drivers using other PCI-express traffic classes or ordering domains must
+establish the required posted-write drain before releasing DMA mappings.
+For conventional PCI functions, no Transactions Pending check is available.
+The function does not reset the device, discard cached DMA addresses, stop
+other PCI functions, or revoke mappings in an IOMMU.
+.Pp
+The function returns zero when bus mastering is disabled and the
+transaction-pending check, if available, succeeds.
+It otherwise returns
+.Er ENXIO
+if PCI configuration space becomes inaccessible,
+.Er EIO
+if bus mastering remains enabled, and
+.Er ETIMEDOUT
+if a present PCI-express function retains pending transactions.
+An error does not establish that it is safe to release DMA mappings.
+.Pp
The
.Fn pci_enable_io
function enables memory or I/O port address decoding for the device
diff --git a/sys/dev/pci/hostb_pci.c b/sys/dev/pci/hostb_pci.c
--- a/sys/dev/pci/hostb_pci.c
+++ b/sys/dev/pci/hostb_pci.c
@@ -147,6 +147,13 @@
return (pci_disable_busmaster(dev));
}
+static int
+pci_hostb_disable_busmaster_drain(device_t dev, device_t child)
+{
+
+ return (pci_disable_busmaster_drain(dev));
+}
+
static int
pci_hostb_enable_io(device_t dev, device_t child, int space)
{
@@ -264,6 +271,7 @@
DEVMETHOD(pci_write_config, pci_hostb_write_config),
DEVMETHOD(pci_enable_busmaster, pci_hostb_enable_busmaster),
DEVMETHOD(pci_disable_busmaster, pci_hostb_disable_busmaster),
+ DEVMETHOD(pci_disable_busmaster_drain, pci_hostb_disable_busmaster_drain),
DEVMETHOD(pci_enable_io, pci_hostb_enable_io),
DEVMETHOD(pci_disable_io, pci_hostb_disable_io),
DEVMETHOD(pci_get_powerstate, pci_hostb_get_powerstate),
diff --git a/sys/dev/pci/pci.c b/sys/dev/pci/pci.c
--- a/sys/dev/pci/pci.c
+++ b/sys/dev/pci/pci.c
@@ -186,6 +186,7 @@
DEVMETHOD(pci_write_config, pci_write_config_method),
DEVMETHOD(pci_enable_busmaster, pci_enable_busmaster_method),
DEVMETHOD(pci_disable_busmaster, pci_disable_busmaster_method),
+ DEVMETHOD(pci_disable_busmaster_drain, pci_disable_busmaster_drain_method),
DEVMETHOD(pci_enable_io, pci_enable_io_method),
DEVMETHOD(pci_disable_io, pci_disable_io_method),
DEVMETHOD(pci_get_vpd_ident, pci_get_vpd_ident_method),
@@ -3073,18 +3074,39 @@
PCI_WRITE_CONFIG(dev, child, PCIR_COMMAND, command, 2);
}
+static int
+pci_set_busmaster(device_t dev, device_t child, bool enable)
+{
+ uint16_t command;
+
+ command = PCI_READ_CONFIG(dev, child, PCIR_COMMAND, 2);
+ if (command == 0xffff)
+ return (ENXIO);
+ if (enable)
+ command |= PCIM_CMD_BUSMASTEREN;
+ else
+ command &= ~PCIM_CMD_BUSMASTEREN;
+ PCI_WRITE_CONFIG(dev, child, PCIR_COMMAND, command, 2);
+ command = PCI_READ_CONFIG(dev, child, PCIR_COMMAND, 2);
+ if (command == 0xffff)
+ return (ENXIO);
+ if (((command & PCIM_CMD_BUSMASTEREN) != 0) != enable)
+ return (EIO);
+ return (0);
+}
+
int
pci_enable_busmaster_method(device_t dev, device_t child)
{
- pci_set_command_bit(dev, child, PCIM_CMD_BUSMASTEREN);
- return (0);
+
+ return (pci_set_busmaster(dev, child, true));
}
int
pci_disable_busmaster_method(device_t dev, device_t child)
{
- pci_clear_command_bit(dev, child, PCIM_CMD_BUSMASTEREN);
- return (0);
+
+ return (pci_set_busmaster(dev, child, false));
}
int
@@ -6974,13 +6996,13 @@
}
/*
- * Wait for pending transactions to complete on a PCI-express function.
+ * Wait for outstanding non-posted requests on a PCI-express function.
*
* The maximum delay is specified in milliseconds in max_delay. Note
* that this function may sleep.
*
- * Returns true if the function is idle and false if the timeout is
- * exceeded. If dev is not a PCI-express function, this returns true.
+ * Returns true if Transactions Pending is clear and false if the timeout
+ * is exceeded. If dev is not a PCI-express function, this returns true.
*/
bool
pcie_wait_for_pending_transactions(device_t dev, u_int max_delay)
@@ -7061,6 +7083,39 @@
}
}
+/*
+ * Disable bus mastering and wait for outstanding PCIe non-posted requests.
+ * The command readback also orders prior posted writes in the configuration
+ * transaction's ordering domain. Drivers must account for any other ordering
+ * domains before releasing DMA mappings; this is not a device reset.
+ *
+ * Returns zero once BME and Transactions Pending are clear, ENXIO if PCI
+ * configuration space becomes inaccessible, EIO if bus mastering remains
+ * enabled, or ETIMEDOUT if a present PCI-express function retains pending
+ * transactions. This function may sleep. The caller must serialize it
+ * against configuration restores and enables, and retain responsibility for
+ * preventing stale DMA state from being re-enabled after it returns.
+ */
+int
+pci_disable_busmaster_drain_method(device_t dev, device_t child)
+{
+ u_int timeout;
+ int error;
+
+ error = PCI_DISABLE_BUSMASTER(dev, child);
+ if (error != 0)
+ return (error);
+
+ timeout = max(pcie_get_max_completion_timeout(child) / 1000, 10);
+ if (pcie_wait_for_pending_transactions(child, timeout))
+ return (0);
+
+ /* Report a function which disappeared during the wait separately. */
+ if (PCI_READ_CONFIG(dev, child, PCIR_COMMAND, 2) == 0xffff)
+ return (ENXIO);
+ return (ETIMEDOUT);
+}
+
void
pcie_apei_error(device_t dev, int sev, uint8_t *aerp)
{
diff --git a/sys/dev/pci/pci_if.m b/sys/dev/pci/pci_if.m
--- a/sys/dev/pci/pci_if.m
+++ b/sys/dev/pci/pci_if.m
@@ -117,6 +117,11 @@
device_t child;
};
+METHOD int disable_busmaster_drain {
+ device_t dev;
+ device_t child;
+};
+
METHOD int enable_io {
device_t dev;
device_t child;
diff --git a/sys/dev/pci/pci_private.h b/sys/dev/pci/pci_private.h
--- a/sys/dev/pci/pci_private.h
+++ b/sys/dev/pci/pci_private.h
@@ -82,6 +82,7 @@
pci_write_config_t pci_write_config_method;
pci_enable_busmaster_t pci_enable_busmaster_method;
pci_disable_busmaster_t pci_disable_busmaster_method;
+pci_disable_busmaster_drain_t pci_disable_busmaster_drain_method;
pci_enable_io_t pci_enable_io_method;
pci_disable_io_t pci_disable_io_method;
pci_get_vpd_ident_t pci_get_vpd_ident_method;
diff --git a/sys/dev/pci/pcivar.h b/sys/dev/pci/pcivar.h
--- a/sys/dev/pci/pcivar.h
+++ b/sys/dev/pci/pcivar.h
@@ -454,6 +454,12 @@
return(PCI_DISABLE_BUSMASTER(device_get_parent(dev), dev));
}
+static __inline int
+pci_disable_busmaster_drain(device_t dev)
+{
+ return (PCI_DISABLE_BUSMASTER_DRAIN(device_get_parent(dev), dev));
+}
+
static __inline int
pci_enable_io(device_t dev, int space)
{
diff --git a/sys/dev/pci/vga_pci.c b/sys/dev/pci/vga_pci.c
--- a/sys/dev/pci/vga_pci.c
+++ b/sys/dev/pci/vga_pci.c
@@ -514,6 +514,13 @@
return (pci_disable_busmaster(dev));
}
+static int
+vga_pci_disable_busmaster_drain(device_t dev, device_t child)
+{
+
+ return (pci_disable_busmaster_drain(dev));
+}
+
static int
vga_pci_enable_io(device_t dev, device_t child, int space)
{
@@ -725,6 +732,7 @@
DEVMETHOD(pci_write_config, vga_pci_write_config),
DEVMETHOD(pci_enable_busmaster, vga_pci_enable_busmaster),
DEVMETHOD(pci_disable_busmaster, vga_pci_disable_busmaster),
+ DEVMETHOD(pci_disable_busmaster_drain, vga_pci_disable_busmaster_drain),
DEVMETHOD(pci_enable_io, vga_pci_enable_io),
DEVMETHOD(pci_disable_io, vga_pci_disable_io),
DEVMETHOD(pci_get_vpd_ident, vga_pci_get_vpd_ident),

File Metadata

Mime Type
text/plain
Expires
Sun, Sep 13, 11:14 AM (13 h, 27 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
38780124
Default Alt Text
D59477.id186257.diff (10 KB)

Event Timeline