From 130b3b8d8ff865a60554f1068e7fef89ed012f3c Mon Sep 17 00:00:00 2001 From: Vivek Pernamitta Date: Tue, 24 Aug 2021 11:54:52 +0530 Subject: [PATCH] msm: pci: Release spinlock before notifying client Release spinlock before notifying client driver and acquire spinlock once done because once host notifies client driver with an event, client can schedule an recovery in same context before returning and expects an new event which could cause an race condition if spinlock is acquired. Also use list_for_each_entry_safe() while travesing the list of registered clients for notification. Change-Id: I6ae55032acdb1a5b14f473eadacb7a009a8df753 Signed-off-by: Vivek Pernamitta --- drivers/pci/controller/pci-msm.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/drivers/pci/controller/pci-msm.c b/drivers/pci/controller/pci-msm.c index 3a6e2a7ec708..6dfd62249124 100644 --- a/drivers/pci/controller/pci-msm.c +++ b/drivers/pci/controller/pci-msm.c @@ -5031,13 +5031,13 @@ EXPORT_SYMBOL(msm_pcie_enumerate); static void msm_pcie_notify_client(struct msm_pcie_dev_t *dev, enum msm_pcie_event event) { - struct msm_pcie_register_event *reg_itr; + struct msm_pcie_register_event *reg_itr, *temp; struct msm_pcie_notify *notify; struct msm_pcie_notify client_notify; unsigned long flags; spin_lock_irqsave(&dev->evt_reg_list_lock, flags); - list_for_each_entry(reg_itr, &dev->event_reg_list, node) { + list_for_each_entry_safe(reg_itr, temp, &dev->event_reg_list, node) { if ((reg_itr->events & event) && reg_itr->callback) { notify = ®_itr->notify; client_notify.event = event; @@ -5046,8 +5046,19 @@ static void msm_pcie_notify_client(struct msm_pcie_dev_t *dev, client_notify.options = notify->options; PCIE_DUMP(dev, "PCIe: callback RC%d for event %d\n", dev->rc_idx, event); + + /* Release spinlock before notifying client driver + * and acquire it once done because once host notifies + * client driver with an event, client can schedule an + * recovery in same context before returning and + * expects an new event which could cause an race + * condition if spinlock is acquired. + */ + spin_unlock_irqrestore(&dev->evt_reg_list_lock, flags); + reg_itr->callback(&client_notify); + spin_lock_irqsave(&dev->evt_reg_list_lock, flags); if ((reg_itr->options & MSM_PCIE_CONFIG_NO_RECOVERY) && (event == MSM_PCIE_EVENT_LINKDOWN)) { dev->user_suspend = true; @@ -8050,7 +8061,7 @@ int msm_pcie_register_event(struct msm_pcie_register_event *reg) { int ret = 0; struct msm_pcie_dev_t *pcie_dev; - struct msm_pcie_register_event *reg_itr; + struct msm_pcie_register_event *reg_itr, *temp; struct pci_dev *pcidev; unsigned long flags; @@ -8074,7 +8085,9 @@ int msm_pcie_register_event(struct msm_pcie_register_event *reg) pcidev = (struct pci_dev *)reg->user; spin_lock_irqsave(&pcie_dev->evt_reg_list_lock, flags); - list_for_each_entry(reg_itr, &pcie_dev->event_reg_list, node) { + list_for_each_entry_safe(reg_itr, temp, + &pcie_dev->event_reg_list, node) { + if (reg_itr->user == reg->user) { PCIE_ERR(pcie_dev, "PCIe: RC%d: EP BDF 0x%4x already registered\n",