[PATCH 01/17] PCI: Add concurrency safe clear_and_set variants for LNKCTL{,2}

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



A few places write LNKCTL and LNKCTL2 registers without proper
concurrency control and this could result in losing the changes
one of the writers intended to make.

Add pcie_capability_clear_and_set_word_locked() and helpers to use it
with LNKCTL and LNKCTL2. The concurrency control is provided using a
spinlock in the struct pci_dev.

Suggested-by: Lukas Wunner <lukas@xxxxxxxxx>
Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@xxxxxxxxxxxxxxx>
---
 drivers/pci/access.c | 14 ++++++++++++++
 drivers/pci/probe.c  |  1 +
 include/linux/pci.h  | 17 +++++++++++++++++
 3 files changed, 32 insertions(+)

diff --git a/drivers/pci/access.c b/drivers/pci/access.c
index 3c230ca3de58..d92a3daadd0c 100644
--- a/drivers/pci/access.c
+++ b/drivers/pci/access.c
@@ -531,6 +531,20 @@ int pcie_capability_clear_and_set_dword(struct pci_dev *dev, int pos,
 }
 EXPORT_SYMBOL(pcie_capability_clear_and_set_dword);
 
+int pcie_capability_clear_and_set_word_locked(struct pci_dev *dev, int pos,
+					      u16 clear, u16 set)
+{
+	unsigned long flags;
+	int ret;
+
+	spin_lock_irqsave(&dev->cap_lock, flags);
+	ret = pcie_capability_clear_and_set_word(dev, pos, clear, set);
+	spin_unlock_irqrestore(&dev->cap_lock, flags);
+
+	return ret;
+}
+EXPORT_SYMBOL(pcie_capability_clear_and_set_word_locked);
+
 int pci_read_config_byte(const struct pci_dev *dev, int where, u8 *val)
 {
 	if (pci_dev_is_disconnected(dev)) {
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 0b2826c4a832..0c14a283f1c7 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -2318,6 +2318,7 @@ struct pci_dev *pci_alloc_dev(struct pci_bus *bus)
 		.end = -1,
 	};
 
+	spin_lock_init(&dev->cap_lock);
 #ifdef CONFIG_PCI_MSI
 	raw_spin_lock_init(&dev->msi_lock);
 #endif
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 60b8772b5bd4..82faea085d95 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -467,6 +467,7 @@ struct pci_dev {
 	pci_dev_flags_t dev_flags;
 	atomic_t	enable_cnt;	/* pci_enable_device has been called */
 
+	spinlock_t	cap_lock;		/* Protects RMW ops done with locked RMW capability accessors */
 	u32		saved_config_space[16]; /* Config space saved at suspend time */
 	struct hlist_head saved_cap_space;
 	int		rom_attr_enabled;	/* Display of ROM attribute enabled? */
@@ -1221,6 +1222,8 @@ int pcie_capability_clear_and_set_word(struct pci_dev *dev, int pos,
 				       u16 clear, u16 set);
 int pcie_capability_clear_and_set_dword(struct pci_dev *dev, int pos,
 					u32 clear, u32 set);
+int pcie_capability_clear_and_set_word_locked(struct pci_dev *dev, int pos,
+					      u16 clear, u16 set);
 
 static inline int pcie_capability_set_word(struct pci_dev *dev, int pos,
 					   u16 set)
@@ -1246,6 +1249,20 @@ static inline int pcie_capability_clear_dword(struct pci_dev *dev, int pos,
 	return pcie_capability_clear_and_set_dword(dev, pos, clear, 0);
 }
 
+static inline int pcie_lnkctl_clear_and_set(struct pci_dev *dev, u16 clear,
+					    u16 set)
+{
+	return pcie_capability_clear_and_set_word_locked(dev, PCI_EXP_LNKCTL,
+							 clear, set);
+}
+
+static inline int pcie_lnkctl2_clear_and_set(struct pci_dev *dev, u16 clear,
+					    u16 set)
+{
+	return pcie_capability_clear_and_set_word_locked(dev, PCI_EXP_LNKCTL2,
+							 clear, set);
+}
+
 /* User-space driven config access */
 int pci_user_read_config_byte(struct pci_dev *dev, int where, u8 *val);
 int pci_user_read_config_word(struct pci_dev *dev, int where, u16 *val);
-- 
2.30.2




[Index of Archives]     [DMA Engine]     [Linux Coverity]     [Linux USB]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Greybus]

  Powered by Linux