• Alex Williamson's avatar
    PCI: Add pci_try_reset_function(), pci_try_reset_slot(), pci_try_reset_bus() · 61cf16d8
    Alex Williamson authored
    When doing a function/slot/bus reset PCI grabs the device_lock for each
    device to block things like suspend and driver probes, but call paths exist
    where this lock may already be held.  This creates an opportunity for
    deadlock.  For instance, vfio allows userspace to issue resets so long as
    it owns the device(s).  If a driver unbind .remove callback races with
    userspace issuing a reset, we have a deadlock as userspace gets stuck
    waiting on device_lock while another thread has device_lock and waits for
    .remove to complete.  To resolve this, we can make a version of the reset
    interfaces which use trylock.  With this, we can safely attempt a reset and
    return error to userspace if there is contention.
    
    [bhelgaas: the deadlock happens when A (userspace) has a file descriptor for
    the device, and B waits in this path:
    
      driver_detach
        device_lock                     # take device_lock
        __device_release_driver
          pci_device_remove             # pci_bus_type.remove
            vfio_pci_remove             # pci_driver .remove
              vfio_del_group_dev
                wait_event(vfio.release_q, !vfio_dev_present)   # wait (holding device_lock)
    
    Now B is stuck until A gives up the file descriptor.  If A tries to acquire
    device_lock for any reason, we deadlock because A is waiting for B to release
    the lock, and B is waiting for A to release the file descriptor.]
    Signed-off-by: default avatarAlex Williamson <alex.williamson@redhat.com>
    Signed-off-by: default avatarBjorn Helgaas <bhelgaas@google.com>
    61cf16d8
pci.c 113 KB