Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F171651900
D59477.id186257.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
10 KB
Referenced Files
None
Subscribers
None
D59477.id186257.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D59477: pci: Verify bus mastering changes and add a transaction drain
Attached
Detach File
Event Timeline
Log In to Comment