Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

PCI/bwctrl: Replace lbms_count with PCI_LINK_LBMS_SEEN flag

PCIe BW controller counted LBMS assertions for the purposes of the Target
Speed quirk (pcie_failed_link_retrain()). It was also a plan to expose the
LBMS count through sysfs to allow better diagnosing link related issues.
Lukas Wunner suggested, however, that adding a trace event would be better
for diagnostics purposes, leaving only pcie_failed_link_retrain() as a user
of the lbms_count.

The logic in pcie_failed_link_retrain() does not require keeping count of
LBMS assertions, so replace lbms_count with a simple flag in pci_dev's
priv_flags. The reduced complexity allows removing pcie_bwctrl_lbms_rwsem.

Since pcie_failed_link_retrain() runs before bwctrl is probed during boot,
the LBMS in Link Status register still has to be checked by the quirk.

The priv_flags numbering is not continuous because hotplug code added a few
flags to fill numbers 4-5 (hotplug and bwctrl changes are routed through in
different branches).

Suggested-by: Lukas Wunner <lukas@wunner.de>
Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
[bhelgaas: commit log]
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
[kwilczynski: squashed a fix to resolve build failures from
https://lore.kernel.org/all/20250508090036.1528-1-ilpo.jarvinen@linux.intel.com]
Signed-off-by: Krzysztof Wilczyński <kwilczynski@kernel.org>
Reviewed-by: Lukas Wunner <lukas@wunner.de>
Link: https://patch.msgid.link/20250422115548.1483-1-ilpo.jarvinen@linux.intel.com

authored by

Ilpo Järvinen and committed by
Krzysztof Wilczyński
2389d8dc 0af2f6be

+26 -71
+1 -1
drivers/pci/hotplug/pciehp_ctrl.c
··· 131 131 INDICATOR_NOOP); 132 132 133 133 /* Don't carry LBMS indications across */ 134 - pcie_reset_lbms_count(ctrl->pcie->port); 134 + pcie_reset_lbms(ctrl->pcie->port); 135 135 } 136 136 137 137 static int pciehp_enable_slot(struct controller *ctrl);
+1 -1
drivers/pci/pci.c
··· 4757 4757 * to track link speed or width changes made by hardware itself 4758 4758 * in attempt to correct unreliable link operation. 4759 4759 */ 4760 - pcie_reset_lbms_count(pdev); 4760 + pcie_reset_lbms(pdev); 4761 4761 return rc; 4762 4762 } 4763 4763
+3 -7
drivers/pci/pci.h
··· 557 557 #define PCI_DPC_RECOVERED 1 558 558 #define PCI_DPC_RECOVERING 2 559 559 #define PCI_DEV_REMOVED 3 560 + #define PCI_LINK_LBMS_SEEN 6 560 561 561 562 static inline void pci_dev_assign_added(struct pci_dev *dev) 562 563 { ··· 825 824 #endif 826 825 827 826 #ifdef CONFIG_PCIEPORTBUS 828 - void pcie_reset_lbms_count(struct pci_dev *port); 829 - int pcie_lbms_count(struct pci_dev *port, unsigned long *val); 827 + void pcie_reset_lbms(struct pci_dev *port); 830 828 #else 831 - static inline void pcie_reset_lbms_count(struct pci_dev *port) {} 832 - static inline int pcie_lbms_count(struct pci_dev *port, unsigned long *val) 833 - { 834 - return -EOPNOTSUPP; 835 - } 829 + static inline void pcie_reset_lbms(struct pci_dev *port) {} 836 830 #endif 837 831 838 832 struct pci_dev_reset_methods {
+18 -55
drivers/pci/pcie/bwctrl.c
··· 38 38 /** 39 39 * struct pcie_bwctrl_data - PCIe bandwidth controller 40 40 * @set_speed_mutex: Serializes link speed changes 41 - * @lbms_count: Count for LBMS (since last reset) 42 41 * @cdev: Thermal cooling device associated with the port 43 42 */ 44 43 struct pcie_bwctrl_data { 45 44 struct mutex set_speed_mutex; 46 - atomic_t lbms_count; 47 45 struct thermal_cooling_device *cdev; 48 46 }; 49 47 50 - /* 51 - * Prevent port removal during LBMS count accessors and Link Speed changes. 52 - * 53 - * These have to be differentiated because pcie_bwctrl_change_speed() calls 54 - * pcie_retrain_link() which uses LBMS count reset accessor on success 55 - * (using just one rwsem triggers "possible recursive locking detected" 56 - * warning). 57 - */ 58 - static DECLARE_RWSEM(pcie_bwctrl_lbms_rwsem); 48 + /* Prevent port removal during Link Speed changes. */ 59 49 static DECLARE_RWSEM(pcie_bwctrl_setspeed_rwsem); 60 50 61 51 static bool pcie_valid_speed(enum pci_bus_speed speed) ··· 192 202 193 203 static void pcie_bwnotif_enable(struct pcie_device *srv) 194 204 { 195 - struct pcie_bwctrl_data *data = srv->port->link_bwctrl; 196 205 struct pci_dev *port = srv->port; 197 206 u16 link_status; 198 207 int ret; 199 208 200 - /* Count LBMS seen so far as one */ 209 + /* Note if LBMS has been seen so far */ 201 210 ret = pcie_capability_read_word(port, PCI_EXP_LNKSTA, &link_status); 202 211 if (ret == PCIBIOS_SUCCESSFUL && link_status & PCI_EXP_LNKSTA_LBMS) 203 - atomic_inc(&data->lbms_count); 212 + set_bit(PCI_LINK_LBMS_SEEN, &port->priv_flags); 204 213 205 214 pcie_capability_set_word(port, PCI_EXP_LNKCTL, 206 215 PCI_EXP_LNKCTL_LBMIE | PCI_EXP_LNKCTL_LABIE); ··· 222 233 static irqreturn_t pcie_bwnotif_irq(int irq, void *context) 223 234 { 224 235 struct pcie_device *srv = context; 225 - struct pcie_bwctrl_data *data = srv->port->link_bwctrl; 226 236 struct pci_dev *port = srv->port; 227 237 u16 link_status, events; 228 238 int ret; ··· 235 247 return IRQ_NONE; 236 248 237 249 if (events & PCI_EXP_LNKSTA_LBMS) 238 - atomic_inc(&data->lbms_count); 250 + set_bit(PCI_LINK_LBMS_SEEN, &port->priv_flags); 239 251 240 252 pcie_capability_write_word(port, PCI_EXP_LNKSTA, events); 241 253 ··· 250 262 return IRQ_HANDLED; 251 263 } 252 264 253 - void pcie_reset_lbms_count(struct pci_dev *port) 265 + void pcie_reset_lbms(struct pci_dev *port) 254 266 { 255 - struct pcie_bwctrl_data *data; 256 - 257 - guard(rwsem_read)(&pcie_bwctrl_lbms_rwsem); 258 - data = port->link_bwctrl; 259 - if (data) 260 - atomic_set(&data->lbms_count, 0); 261 - else 262 - pcie_capability_write_word(port, PCI_EXP_LNKSTA, 263 - PCI_EXP_LNKSTA_LBMS); 264 - } 265 - 266 - int pcie_lbms_count(struct pci_dev *port, unsigned long *val) 267 - { 268 - struct pcie_bwctrl_data *data; 269 - 270 - guard(rwsem_read)(&pcie_bwctrl_lbms_rwsem); 271 - data = port->link_bwctrl; 272 - if (!data) 273 - return -ENOTTY; 274 - 275 - *val = atomic_read(&data->lbms_count); 276 - 277 - return 0; 267 + clear_bit(PCI_LINK_LBMS_SEEN, &port->priv_flags); 268 + pcie_capability_write_word(port, PCI_EXP_LNKSTA, PCI_EXP_LNKSTA_LBMS); 278 269 } 279 270 280 271 static int pcie_bwnotif_probe(struct pcie_device *srv) ··· 275 308 return ret; 276 309 277 310 scoped_guard(rwsem_write, &pcie_bwctrl_setspeed_rwsem) { 278 - scoped_guard(rwsem_write, &pcie_bwctrl_lbms_rwsem) { 279 - port->link_bwctrl = data; 311 + port->link_bwctrl = data; 280 312 281 - ret = request_irq(srv->irq, pcie_bwnotif_irq, 282 - IRQF_SHARED, "PCIe bwctrl", srv); 283 - if (ret) { 284 - port->link_bwctrl = NULL; 285 - return ret; 286 - } 287 - 288 - pcie_bwnotif_enable(srv); 313 + ret = request_irq(srv->irq, pcie_bwnotif_irq, 314 + IRQF_SHARED, "PCIe bwctrl", srv); 315 + if (ret) { 316 + port->link_bwctrl = NULL; 317 + return ret; 289 318 } 319 + 320 + pcie_bwnotif_enable(srv); 290 321 } 291 322 292 323 pci_dbg(port, "enabled with IRQ %d\n", srv->irq); ··· 304 339 pcie_cooling_device_unregister(data->cdev); 305 340 306 341 scoped_guard(rwsem_write, &pcie_bwctrl_setspeed_rwsem) { 307 - scoped_guard(rwsem_write, &pcie_bwctrl_lbms_rwsem) { 308 - pcie_bwnotif_disable(srv->port); 342 + pcie_bwnotif_disable(srv->port); 309 343 310 - free_irq(srv->irq, srv); 344 + free_irq(srv->irq, srv); 311 345 312 - srv->port->link_bwctrl = NULL; 313 - } 346 + srv->port->link_bwctrl = NULL; 314 347 } 315 348 } 316 349
+3 -7
drivers/pci/quirks.c
··· 38 38 39 39 static bool pcie_lbms_seen(struct pci_dev *dev, u16 lnksta) 40 40 { 41 - unsigned long count; 42 - int ret; 41 + if (test_bit(PCI_LINK_LBMS_SEEN, &dev->priv_flags)) 42 + return true; 43 43 44 - ret = pcie_lbms_count(dev, &count); 45 - if (ret < 0) 46 - return lnksta & PCI_EXP_LNKSTA_LBMS; 47 - 48 - return count > 0; 44 + return lnksta & PCI_EXP_LNKSTA_LBMS; 49 45 } 50 46 51 47 /*