There's the neat little problem that some systems die if vga decoding isn't decoded anywhere, because linux disabled that pci device. Atm we solve that problem by simple not calling pci_disable_device at driver unregister time for drm pci devices. Which isn't to great because it leaks a pci_enable_device reference on module reload and also is rather inconsistent, since the driver load codcode in drm/drm_pci.c _does_ call pci_disable_device on the load error path. To fix this issue, let the vga arbiter hold a pci_enable_device reference on its own when the vga device decodes anything. This leaves the issue still open when the vga arbiter isn't loaded/compiled in, but I guess since drm module unload is riddled with dragons it's ok to say "just don't do this". v2: ret needs to be signed, otherwise ERR_PTR will just store a large positive value on 64bit machines. Noticed by Jani Nikula. Signed-Off-by: Daniel Vetter <daniel.vetter at ffwll.ch> --- drivers/gpu/vga/vgaarb.c | 26 +++++++++++++++++++++++++- 1 files changed, 25 insertions(+), 1 deletions(-) diff --git a/drivers/gpu/vga/vgaarb.c b/drivers/gpu/vga/vgaarb.c index 3df8fc0..c0b1271 100644 --- a/drivers/gpu/vga/vgaarb.c +++ b/drivers/gpu/vga/vgaarb.c @@ -49,7 +49,11 @@ static void vga_arbiter_notify_clients(void); /* * We keep a list of all vga devices in the system to speed - * up the various operations of the arbiter + * up the various operations of the arbiter. Note that vgaarb keeps a + * pci_enable_device reference for all vga devices with owns != 0. This is to + * avoid drm devices from killing the system when they call pci_disable_device + * on module unload (as they should), because some systems don't like it if no + * one decodes vga. */ struct vga_device { struct list_head list; @@ -172,6 +176,7 @@ static struct vga_device *__vga_tryget(struct vga_device *vgadev, unsigned int wants, legacy_wants, match; struct vga_device *conflict; unsigned int pci_bits; + int ret; u32 flags = 0; /* Account for "normal" resources to lock. If we decode the legacy, @@ -264,6 +269,10 @@ static struct vga_device *__vga_tryget(struct vga_device *vgadev, pci_set_vga_state(conflict->pdev, false, pci_bits, flags); conflict->owns &= ~lwants; + + if (conflict->owns == 0) + pci_disable_device(conflict->pdev); + /* If he also owned non-legacy, that is no longer the case */ if (lwants & VGA_RSRC_LEGACY_MEM) conflict->owns &= ~VGA_RSRC_NORMAL_MEM; @@ -290,6 +299,12 @@ enable_them: if (!!(wants & VGA_RSRC_LEGACY_MASK)) flags |= PCI_VGA_STATE_CHANGE_BRIDGE; + if (vgadev->owns == 0) { + ret = pci_enable_device(vgadev->pdev); + if (ret) + return ERR_PTR(ret); + } + pci_set_vga_state(vgadev->pdev, true, pci_bits, flags); if (!vgadev->bridge_has_one_vga) { @@ -376,6 +391,8 @@ int vga_get(struct pci_dev *pdev, unsigned int rsrc, int interruptible) if (conflict == NULL) break; + if (IS_ERR(conflict)) + return PTR_ERR(conflict); /* We have a conflict, we wait until somebody kicks the * work queue. Currently we have one work queue that we @@ -513,6 +530,7 @@ static bool vga_arbiter_add_pci_device(struct pci_dev *pdev) unsigned long flags; struct pci_bus *bus; struct pci_dev *bridge; + int ret; u16 cmd; /* Only deal with VGA class devices */ @@ -582,6 +600,12 @@ static bool vga_arbiter_add_pci_device(struct pci_dev *pdev) vga_arbiter_check_bridge_sharing(vgadev); + if (vgadev->owns != 0) { + ret = pci_enable_device(vgadev->pdev); + if (ret) + goto fail; + } + /* Add to the list */ list_add(&vgadev->list, &vga_list); vga_count++; -- 1.7.7.6