aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVladimir Kondratyev <wulf@FreeBSD.org>2023-12-24 08:20:00 +0000
committerVladimir Kondratyev <wulf@FreeBSD.org>2023-12-24 08:20:00 +0000
commit808ae4e29b6b9c9acc7eab013c5045370df8182a (patch)
treec43f19f332131ed265ce3ecb9c990dff27fdecb5
parentb8c88a61750174f62db45784d6b4dc98de4073b1 (diff)
downloadsrc-808ae4e29b6b9c9acc7eab013c5045370df8182a.tar.gz
src-808ae4e29b6b9c9acc7eab013c5045370df8182a.zip
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
-rw-r--r--sys/compat/linuxkpi/common/include/linux/pci.h36
-rw-r--r--sys/compat/linuxkpi/common/src/linux_pci.c3
2 files changed, 23 insertions, 16 deletions
diff --git a/sys/compat/linuxkpi/common/include/linux/pci.h b/sys/compat/linuxkpi/common/include/linux/pci.h
index aa99b050ffd9..9c7e39bf4a42 100644
--- a/sys/compat/linuxkpi/common/include/linux/pci.h
+++ b/sys/compat/linuxkpi/common/include/linux/pci.h
@@ -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_clear_word(struct pci_dev *dev, int pos, uint16_t val)
+pcie_capability_set_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;
+ return (pcie_capability_clear_and_set_word(dev, pos, 0, val));
+}
- error = pcie_capability_write_word(dev, pos, v);
- return (error);
+static inline int
+pcie_capability_clear_word(struct pci_dev *dev, int pos, uint16_t val)
+{
+ return (pcie_capability_clear_and_set_word(dev, pos, val, 0));
}
static inline int pcie_get_minimum_link(struct pci_dev *dev,
diff --git a/sys/compat/linuxkpi/common/src/linux_pci.c b/sys/compat/linuxkpi/common/src/linux_pci.c
index 4cd28fac15d3..825ebe319b1a 100644
--- a/sys/compat/linuxkpi/common/src/linux_pci.c
+++ b/sys/compat/linuxkpi/common/src/linux_pci.c
@@ -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);