From nobody Fri Aug 7 05:01:56 2026 X-Original-To: dev-commits-src-all@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4hGX7S5R9hz6ntM0 for ; Fri, 07 Aug 2026 05:01:56 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "YR1" (not verified)) by mx1.freebsd.org (Postfix) with ESMTPS id 4hGX7S42tyz3GGw for ; Fri, 07 Aug 2026 05:01:56 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1786078916; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=DAn03EbqY4aX/EJXxRBeCNIqBy3svPQDh4okAKLgBzI=; b=Jcz9SgNhVsGhENTWXMbvU7a6mRMIxzfJOLfy4vKNUH65H0vj85NRbYNhEzXTPELvC09Ttt 0cvkJpewetiXqIMgu4bOhVqp/ZXHp3W+AWJBDtb48TVOJZpvnpLJybmgotwbqzUCzCfRHz SpHVvrT2TDCf2j5YFyyYVlbFtNk7kEE8SN9/Y38lyBskRmgoqiQxXzg4kXdriemy3dFfeD /pEB7DgyPKtv0eJMKJsq9hmlDVu91LeRyPD9WQYOS4SixDEQgonCkHuOqQ960qAb6Vlb8x NHAp+Na2w79DnebvCWM+BI9nuK0SJDS83hUrjXqhQ5ekEBoYBQp1i/pHU+Ab5g== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1786078916; a=rsa-sha256; cv=none; b=jK05PSAyWpTzJLmvQw7hxHtbHX3jD7kLRyJN6RrVSap2atDck8t+p4cq+YPXKY7Ki8HhdP dx9987T030koKxI0WRaAxzA53aVt4QiYoC7wEqsNUb93vZEFN5pwC6BawKCmONXFLInH1R ceg6KCbNV8TnKITetA9pPG9Ahns74N49g7g8xkvFjBVQXPkrqu8tVZ31lnjkc9JsxoYMJF t61P35eomKYNz3+ZFnWDY2Xm+hZtqPacRsDFwwhs5lceeuAQeFDhmDKJxXioaKL5m5kLnq UNGvfCtDiLbWE27j0/6zDg7aJy59UXjpmILUMWLP5AQ5CuJeDuUoXBe4/4fgfA== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1786078916; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=DAn03EbqY4aX/EJXxRBeCNIqBy3svPQDh4okAKLgBzI=; b=CdgGufNGEgxJ++qhG9dE92FjQzGm4mGExWBb9cPl+/0Q9zN7bQ6+FMMKvOx43C+NOHcGTm zQS9V2RWHqbiLdze1wT41UFBCBLbs+nx+3Q8fsDf5SJR2IF8f3bECmapA2TOVQL2PFMnW/ 1rzNUASvt/OVlYOEjiR3A7bCH5SvpKSE59bMNaYAqNdOBMQIbMVvifaS6ZCjFCFyVjkhWx XASe5QWfoCQsZEu+riwm5I0O4xzV51MFOfyMRQQ/v121XuOgTFLRH3JhVwLz8BQPayn6yY dEsqF8ULX0wP6CEULuYGrmrok0HGPLgKukWpHxofKziqSgBXaW2lUqGK66UOAg== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4hGX7S36TTzsd9 for ; Fri, 07 Aug 2026 05:01:56 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 30ecd by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Fri, 07 Aug 2026 05:01:56 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Kevin Bowling Subject: git: 8e9fe9996a1f - main - pci: Reconcile MPS before attaching PCIe devices List-Id: Commit messages for all branches of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-all@freebsd.org Sender: owner-dev-commits-src-all@FreeBSD.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: kbowling X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: 8e9fe9996a1fbdb79033b082e6a96b9e1266e33f Auto-Submitted: auto-generated Date: Fri, 07 Aug 2026 05:01:56 +0000 Message-Id: <6a7566c4.30ecd.3c4e1f93@gitrepo.freebsd.org> The branch main has been updated by kbowling: URL: https://cgit.FreeBSD.org/src/commit/?id=8e9fe9996a1fbdb79033b082e6a96b9e1266e33f commit 8e9fe9996a1fbdb79033b082e6a96b9e1266e33f Author: Kevin Bowling AuthorDate: 2026-08-06 06:40:55 +0000 Commit: Kevin Bowling CommitDate: 2026-08-07 05:01:33 +0000 pci: Reconcile MPS before attaching PCIe devices Reconcile each newly enumerated link as a unit before child drivers attach. Firmware may leave Bus Master Enable set after handoff, so use the bus attachment state rather than that bit to identify the cold phase. Preserve an established hierarchy during rescan and hot-add. Refuse a reduction below a switch because recursive enumeration may already have made a sibling subtree live; lowering only the local port or Root Port would produce an inconsistent path. Report capability and active-use conflicts distinctly. Handle OFW PCI buses that clone the generic enumeration path. MFC after: 2 weeks --- share/man/man4/pci.4 | 3 + sys/dev/pci/pci.c | 293 +++++++++++++++++++++++++++++++++++++++---- sys/dev/pci/pci_private.h | 2 + sys/dev/pci/pcivar.h | 4 +- sys/powerpc/ofw/ofw_pcibus.c | 1 + 5 files changed, 279 insertions(+), 24 deletions(-) diff --git a/share/man/man4/pci.4 b/share/man/man4/pci.4 index 71ca2f50ed90..4ff03740b2c5 100644 --- a/share/man/man4/pci.4 +++ b/share/man/man4/pci.4 @@ -593,6 +593,9 @@ MSI-X interrupts can be disabled by setting this tunable to 0. Configure PCI-express Maximum Payload Size .Pq MPS during device enumeration. +A reduction required below a PCI-express switch is reported but not applied, +because recursively attached sibling subtrees may already be active and the +complete shared ancestor hierarchy cannot be safely retuned. SR-IOV Virtual Functions are excluded because their MPS fields are reserved and the Physical Function setting applies to them. Configuration-space tools may therefore display a Virtual Function's diff --git a/sys/dev/pci/pci.c b/sys/dev/pci/pci.c index 95b8e831e743..dd771c2e8fd3 100644 --- a/sys/dev/pci/pci.c +++ b/sys/dev/pci/pci.c @@ -4297,6 +4297,7 @@ pci_add_children(device_t dev, int domain, int busno) for (f = first_func; f <= pcifunchigh; f++) pci_identify_function(pcib, dev, domain, busno, s, f); } + pcie_reconcile_link_mps(dev); #undef REG } @@ -4426,16 +4427,154 @@ pci_create_iov_child_method(device_t bus, device_t pf, uint16_t rid, } #endif +static int +pcie_mps_bytes(uint16_t mps) +{ + + return (128 << (mps >> 5)); +} + +/* Return the smallest configured MPS above dev, if the walk reaches a root. */ +static bool +pcie_path_mps(device_t dev, uint16_t *mpsp) +{ + struct pci_devinfo *dinfo; + device_t bus, pcib, start; + uint16_t mps; + bool found; + + start = dev; + found = false; + for (;;) { + bus = device_get_parent(dev); + if (bus == NULL) + break; + pcib = device_get_parent(bus); + if (pcib == NULL || !is_pci_device(pcib)) + break; + dinfo = device_get_ivars(pcib); + if (dinfo->cfg.pcie.pcie_location != 0) { + mps = pcie_read_config(pcib, PCIER_DEVICE_CTL, 2) & + PCIEM_CTL_MAX_PAYLOAD; + if (!found || mps < *mpsp) + *mpsp = mps; + found = true; + if (dinfo->cfg.pcie.pcie_type == PCIEM_TYPE_ROOT_PORT) + return (true); + } + dev = pcib; + } + if (found && bootverbose) + device_printf(start, + "PCIe MPS path walk did not reach a Root Port\n"); + return (false); +} + +static bool +pcie_mps_first_warning(device_t dev) +{ + struct pci_devinfo *dinfo; + + dinfo = device_get_ivars(dev); + if ((dinfo->cfg.flags & PCICFG_MPS_WARNED) != 0) + return (false); + dinfo->cfg.flags |= PCICFG_MPS_WARNED; + return (true); +} + +static void +pcie_mps_conflict(device_t dev, uint16_t path_mps, uint16_t max_mps) +{ + + if (!pcie_mps_first_warning(dev)) + return; + device_printf(dev, + "maximum supported MPS %d is below configured path MPS %d; " + "cannot safely retune the shared ancestor hierarchy\n", + pcie_mps_bytes(max_mps), pcie_mps_bytes(path_mps)); +} + +static void +pcie_mps_active_conflict(device_t dev, uint16_t path_mps, + uint16_t device_mps) +{ + + if (!pcie_mps_first_warning(dev)) + return; + device_printf(dev, + "configured MPS %d does not match path MPS %d while bus " + "mastering is enabled; leaving device unchanged\n", + pcie_mps_bytes(device_mps), pcie_mps_bytes(path_mps)); +} + +static void +pcie_mps_mark_unreconciled(device_t dev) +{ + struct pci_devinfo *dinfo; + + dinfo = device_get_ivars(dev); + dinfo->cfg.flags |= PCICFG_MPS_UNRECONCILED; +} + +static void +pcie_mps_unreconciled(device_t dev, uint16_t path_mps, uint16_t max_mps) +{ + + pcie_mps_conflict(dev, path_mps, max_mps); + pcie_mps_mark_unreconciled(dev); +} + +static void +pcie_mps_active_unreconciled(device_t dev, uint16_t path_mps, + uint16_t device_mps) +{ + + pcie_mps_active_conflict(dev, path_mps, device_mps); + pcie_mps_mark_unreconciled(dev); +} + +static void +pcie_mps_mark_link_unreconciled(device_t *devlist, int count, + uint16_t path_mps, + bool all) +{ + struct pci_devinfo *dinfo; + device_t child; + uint16_t mmps; + int i; + + for (i = 0; i < count; i++) { + child = devlist[i]; + dinfo = device_get_ivars(child); + if ((dinfo->cfg.flags & (PCICFG_VF | + PCICFG_MPS_UNRECONCILED)) != 0 || + dinfo->cfg.pcie.pcie_location == 0) + continue; + if (all) { + pcie_mps_mark_unreconciled(child); + continue; + } + mmps = (pcie_read_config(child, PCIER_DEVICE_CAP, 2) & + PCIEM_CAP_MAX_PAYLOAD) << 5; + if (mmps < path_mps) + pcie_mps_unreconciled(child, path_mps, mmps); + } +} + /* - * For PCIe device set Max_Payload_Size to match PCIe root's. + * Tune a function discovered by rescan or hot-add against the established + * path. Never change a shared upstream port here: doing so requires + * quiescing every driver and draining all outstanding transactions in the + * hierarchy. Cold enumeration is reconciled by pcie_reconcile_link_mps(). */ static void pcie_setup_mps(device_t dev) { - struct pci_devinfo *dinfo = device_get_ivars(dev); - device_t root; - uint16_t rmps, mmps, mps; + struct pci_devinfo *dinfo; + device_t bus; + uint16_t mmps, mps, path_mps; + dinfo = device_get_ivars(dev); /* * PCIe r4.0, sec 9.3.5.4 defines the VF MPS and MRRS fields as * Reserved and Preserved, with the PF settings applying to the VF. @@ -4445,31 +4584,139 @@ pcie_setup_mps(device_t dev) return; if (dinfo->cfg.pcie.pcie_location == 0) return; - root = pci_find_pcie_root_port(dev); - if (root == NULL) + + /* Cold enumeration is reconciled one complete link at a time. */ + bus = device_get_parent(dev); + if (!device_is_attached(bus)) return; - /* Check whether the MPS is already configured. */ - rmps = pcie_read_config(root, PCIER_DEVICE_CTL, 2) & - PCIEM_CTL_MAX_PAYLOAD; + path_mps = 0; + if (!pcie_path_mps(dev, &path_mps)) + return; + + mmps = (pcie_read_config(dev, PCIER_DEVICE_CAP, 2) & + PCIEM_CAP_MAX_PAYLOAD) << 5; + if (path_mps > mmps) { + pcie_mps_unreconciled(dev, path_mps, mmps); + return; + } mps = pcie_read_config(dev, PCIER_DEVICE_CTL, 2) & PCIEM_CTL_MAX_PAYLOAD; - if (mps == rmps) + if (mps == path_mps) return; - /* Check whether the device is capable of the root's MPS. */ - mmps = (pcie_read_config(dev, PCIER_DEVICE_CAP, 2) & + if ((pci_read_config(dev, PCIR_COMMAND, 2) & + PCIM_CMD_BUSMASTEREN) != 0) { + pcie_mps_active_unreconciled(dev, path_mps, mps); + return; + } + pcie_adjust_config(dev, PCIER_DEVICE_CTL, PCIEM_CTL_MAX_PAYLOAD, + path_mps, 2); +} + +/* + * Reconcile a newly enumerated link before attaching any child drivers. A + * Root Port may be lowered because its complete downstream hierarchy is still + * idle. A late reduction below a switch is not propagated through ancestors, + * since sibling subtrees may already be active. + */ +void +pcie_reconcile_link_mps(device_t bus) +{ + struct pci_devinfo *dinfo, *upinfo; + device_t child, limiting, pcib, *devlist; + uint16_t mmps, mps, target, up_mmps, up_mps; + int count, error, i; + + if (!pci_enable_mps_tune) + return; + /* Shared-path tuning is only safe before this bus attaches children. */ + if (device_is_attached(bus)) + return; + pcib = device_get_parent(bus); + if (!is_pci_device(pcib)) + return; + upinfo = device_get_ivars(pcib); + if (upinfo->cfg.pcie.pcie_location == 0) + return; + error = device_get_children(bus, &devlist, &count); + if (error != 0) + return; + + up_mps = pcie_read_config(pcib, PCIER_DEVICE_CTL, 2) & + PCIEM_CTL_MAX_PAYLOAD; + target = up_mps; + limiting = NULL; + up_mmps = (pcie_read_config(pcib, PCIER_DEVICE_CAP, 2) & PCIEM_CAP_MAX_PAYLOAD) << 5; - if (rmps > mmps) { - /* - * The device is unable to handle root's MPS. Limit root. - * XXX: We should traverse through all the tree, applying - * it to all the devices. - */ - pcie_adjust_config(root, PCIER_DEVICE_CTL, - PCIEM_CTL_MAX_PAYLOAD, mmps, 2); - } else { - pcie_adjust_config(dev, PCIER_DEVICE_CTL, - PCIEM_CTL_MAX_PAYLOAD, rmps, 2); + if (target > up_mmps) { + target = up_mmps; + limiting = pcib; + } + /* + * Firmware may leave Bus Master Enable set after handoff. Since no + * child driver has attached during this cold pass, it is not a proxy + * for a live FreeBSD consumer. + */ + for (i = 0; i < count; i++) { + child = devlist[i]; + dinfo = device_get_ivars(child); + if ((dinfo->cfg.flags & (PCICFG_VF | + PCICFG_MPS_UNRECONCILED)) != 0 || + dinfo->cfg.pcie.pcie_location == 0) + continue; + mmps = (pcie_read_config(child, PCIER_DEVICE_CAP, 2) & + PCIEM_CAP_MAX_PAYLOAD) << 5; + if (target > mmps) { + target = mmps; + limiting = child; + } + } + + /* + * Do not lower one link below a switch without also reconciling every + * ancestor and sibling subtree. Recursive newbus attachment may already + * have made another subtree live, so leave the established path intact. + */ + if (target < up_mps && + upinfo->cfg.pcie.pcie_type != PCIEM_TYPE_ROOT_PORT) { + pcie_mps_conflict(limiting, up_mps, target); + pcie_mps_mark_link_unreconciled(devlist, count, up_mps, + up_mps > up_mmps); + /* Keep compatible functions at the established path MPS. */ + target = up_mps; + } + /* Lower downstream producers before lowering the shared Root Port. */ + for (i = 0; i < count; i++) { + child = devlist[i]; + dinfo = device_get_ivars(child); + if ((dinfo->cfg.flags & (PCICFG_VF | + PCICFG_MPS_UNRECONCILED)) != 0 || + dinfo->cfg.pcie.pcie_location == 0) + continue; + mps = pcie_read_config(child, PCIER_DEVICE_CTL, 2) & + PCIEM_CTL_MAX_PAYLOAD; + if (mps > target) + pcie_adjust_config(child, PCIER_DEVICE_CTL, + PCIEM_CTL_MAX_PAYLOAD, target, 2); + } + if (up_mps > target) + pcie_adjust_config(pcib, PCIER_DEVICE_CTL, + PCIEM_CTL_MAX_PAYLOAD, target, 2); + + /* Raise idle children only after the upstream port is configured. */ + for (i = 0; i < count; i++) { + child = devlist[i]; + dinfo = device_get_ivars(child); + if ((dinfo->cfg.flags & (PCICFG_VF | + PCICFG_MPS_UNRECONCILED)) != 0 || + dinfo->cfg.pcie.pcie_location == 0) + continue; + mps = pcie_read_config(child, PCIER_DEVICE_CTL, 2) & + PCIEM_CTL_MAX_PAYLOAD; + if (mps < target) + pcie_adjust_config(child, PCIER_DEVICE_CTL, + PCIEM_CTL_MAX_PAYLOAD, target, 2); } + free(devlist, M_TEMP); } static void diff --git a/sys/dev/pci/pci_private.h b/sys/dev/pci/pci_private.h index da8f9c02088e..8a63af046aae 100644 --- a/sys/dev/pci/pci_private.h +++ b/sys/dev/pci/pci_private.h @@ -116,6 +116,8 @@ pci_create_iov_child_t pci_create_iov_child_method; void pci_add_children(device_t dev, int domain, int busno); void pci_add_child(device_t bus, struct pci_devinfo *dinfo); +/* Call after cold enumeration and before attaching the bus's children. */ +void pcie_reconcile_link_mps(device_t bus); device_t pci_add_iov_child(device_t bus, device_t pf, uint16_t rid, uint16_t vid, uint16_t did); void pci_add_resources(device_t bus, device_t dev, int force, diff --git a/sys/dev/pci/pcivar.h b/sys/dev/pci/pcivar.h index bcd4d2d35b54..27bac22c7896 100644 --- a/sys/dev/pci/pcivar.h +++ b/sys/dev/pci/pcivar.h @@ -171,7 +171,9 @@ struct pcicfg_ea { STAILQ_HEAD(, pci_ea_entry) ea_entries; /* EA entries */ }; -#define PCICFG_VF 0x0001 /* Device is an SR-IOV Virtual Function */ +#define PCICFG_VF 0x0001 /* Device is an SR-IOV Virtual Function */ +#define PCICFG_MPS_WARNED 0x0002 /* MPS conflict already reported */ +#define PCICFG_MPS_UNRECONCILED 0x0004 /* MPS conflict left unchanged */ /* config header information common to all header types */ typedef struct pcicfg { diff --git a/sys/powerpc/ofw/ofw_pcibus.c b/sys/powerpc/ofw/ofw_pcibus.c index ff0fe39592e2..22a1634a0389 100644 --- a/sys/powerpc/ofw/ofw_pcibus.c +++ b/sys/powerpc/ofw/ofw_pcibus.c @@ -145,6 +145,7 @@ ofw_pcibus_attach(device_t dev) if (!ofw_devices_only) ofw_pcibus_enum_bus(dev, domain, busno); + pcie_reconcile_link_mps(dev); bus_attach_children(dev); return (0); }