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",