LinuxKPI: Add pcie_capability_clear_and_set_word() function

It does a Read-Modify-Write operation using clear and set bitmasks on
PCI Express Capability Register at pos. As certain PCI Express
Capability Registers are accessed concurrently in RMW fashion, hence
require locking which is handled transparently to the caller.

Sponsored by:	Serenity CyberSecurity, LLC
Reviewed by:	manu, bz
MFC after:	1 week
MFC TODO:	Move pcie_cap_lock to bottom to preserve KBI compatibility
Differential Revision:	https://reviews.freebsd.org/D42821
This commit is contained in:
Vladimir Kondratyev 2023-12-24 11:20:00 +03:00
parent b8c88a6175
commit 808ae4e29b
2 changed files with 23 additions and 16 deletions

View file

@ -347,6 +347,7 @@ struct pci_dev {
size_t romlen;
struct msi_desc **msi_desc;
char *path_name;
spinlock_t pcie_cap_lock;
TAILQ_HEAD(, pci_mmio_region) mmio;
};
@ -1012,35 +1013,38 @@ pcie_capability_write_word(struct pci_dev *dev, int pos, u16 val)
}
static inline int
pcie_capability_set_word(struct pci_dev *dev, int pos, uint16_t val)
pcie_capability_clear_and_set_word(struct pci_dev *dev, int pos,
uint16_t clear, uint16_t set)
{
int error;
uint16_t v;
if (pos == PCI_EXP_LNKCTL || pos == PCI_EXP_RTCTL)
spin_lock(&dev->pcie_cap_lock);
error = pcie_capability_read_word(dev, pos, &v);
if (error != 0)
return (error);
if (error == 0) {
v &= ~clear;
v |= set;
error = pcie_capability_write_word(dev, pos, v);
}
v |= val;
if (pos == PCI_EXP_LNKCTL || pos == PCI_EXP_RTCTL)
spin_unlock(&dev->pcie_cap_lock);
error = pcie_capability_write_word(dev, pos, v);
return (error);
}
static inline int
pcie_capability_set_word(struct pci_dev *dev, int pos, uint16_t val)
{
return (pcie_capability_clear_and_set_word(dev, pos, 0, val));
}
static inline int
pcie_capability_clear_word(struct pci_dev *dev, int pos, uint16_t val)
{
int error;
uint16_t v;
error = pcie_capability_read_word(dev, pos, &v);
if (error != 0)
return (error);
v &= ~val;
error = pcie_capability_write_word(dev, pos, v);
return (error);
return (pcie_capability_clear_and_set_word(dev, pos, val, 0));
}
static inline int pcie_get_minimum_link(struct pci_dev *dev,

View file

@ -525,6 +525,7 @@ linux_pci_attach_device(device_t dev, struct pci_driver *pdrv,
goto out_dma_init;
TAILQ_INIT(&pdev->mmio);
spin_lock_init(&pdev->pcie_cap_lock);
spin_lock(&pci_lock);
list_add(&pdev->links, &pci_devices);
@ -539,6 +540,7 @@ linux_pci_attach_device(device_t dev, struct pci_driver *pdrv,
out_probe:
free(pdev->bus, M_DEVBUF);
spin_lock_destroy(&pdev->pcie_cap_lock);
linux_pdev_dma_uninit(pdev);
out_dma_init:
spin_lock(&pci_lock);
@ -579,6 +581,7 @@ linux_pci_detach_device(struct pci_dev *pdev)
spin_lock(&pci_lock);
list_del(&pdev->links);
spin_unlock(&pci_lock);
spin_lock_destroy(&pdev->pcie_cap_lock);
put_device(&pdev->dev);
return (0);