diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c index c358c074f925..14311535661d 100644 --- a/drivers/thunderbolt/nhi.c +++ b/drivers/thunderbolt/nhi.c @@ -403,6 +403,8 @@ void ring_start(struct tb_ring *ring) { mutex_lock(&ring->nhi->lock); mutex_lock(&ring->lock); + if (ring->nhi->going_away) + goto err; if (ring->running) { dev_WARN(&ring->nhi->pdev->dev, "ring already started\n"); goto err; @@ -449,6 +451,8 @@ void ring_stop(struct tb_ring *ring) mutex_lock(&ring->lock); dev_info(&ring->nhi->pdev->dev, "stopping %s %d\n", RING_TYPE(ring), ring->hop); + if (ring->nhi->going_away) + goto err; if (!ring->running) { dev_WARN(&ring->nhi->pdev->dev, "%s %d already stopped\n", RING_TYPE(ring), ring->hop); @@ -653,6 +657,14 @@ static int nhi_resume_noirq(struct device *dev) struct pci_dev *pdev = to_pci_dev(dev); struct tb *tb = pci_get_drvdata(pdev); + /* + * Check that the device is still there. It may be that the user + * unplugged last device which causes the host controller to go + * away on PCs. + */ + if (!pci_device_is_present(pdev)) + tb->nhi->going_away = true; + return tb_domain_resume_noirq(tb); } diff --git a/drivers/thunderbolt/nhi.h b/drivers/thunderbolt/nhi.h index 446ff6dac91d..953864ae0ab3 100644 --- a/drivers/thunderbolt/nhi.h +++ b/drivers/thunderbolt/nhi.h @@ -20,6 +20,8 @@ * @tx_rings: All Tx rings available on this host controller * @rx_rings: All Rx rings available on this host controller * @msix_ida: Used to allocate MSI-X vectors for rings + * @going_away: The host controller device is about to disappear so when + * this flag is set, avoid touching the hardware anymore. * @interrupt_work: Work scheduled to handle ring interrupt when no * MSI-X is used. * @hop_count: Number of rings (end point hops) supported by NHI. @@ -31,6 +33,7 @@ struct tb_nhi { struct tb_ring **tx_rings; struct tb_ring **rx_rings; struct ida msix_ida; + bool going_away; struct work_struct interrupt_work; u32 hop_count; };