Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

vgaarb: We can own non-decoded resources

The VGA arbiter does not allow devices to "own" resources that it
doesn't "decode". However, it does allow devices to "lock" resources
that it doesn't decode. This gets us into trouble because locking
the resource goes through the same bridge routing updates regardless
of whether we decode the resource. This means that when a non-decoded
resource is released, the bridge is left with VGA routing enabled and
locking a different device won't clear it.

This happens in the following scenario:

VGA device 01:00.0 (VGA1) is owned by the radeon driver, which
registers a set_vga_decode function which releases legacy VGA decodes.

VGA device 02:00.0 (VGA2) is any VGA device.

VGA1 user locks VGA resources triggering first_use callback of
set_vga_decoded, clearing "decode" and "owns" of legacy resources
on VGA1.

VGA1 user unlocks VGA resources.

VGA2 user locks VGA resources, which skips VGA1 as conflicting as it
does not "own" legacy resources, although VGA routing is still enabled
for the VGA1 bridge. VGA routing is enabled on VGA2 bridge.

VGA2 may or may not receive VGA transactions depending on the bus
priority of VGA1 vs VGA2 bridge.

To resolve this, we need to allow devices to "own" resources that they
do not "decode". This way we can track bus ownership of VGA. When a
device decodes VGA, it only means that we must update the command bits
in cases where the conflicting device is on the same bus.

Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
Cc: Daniel Vetter <daniel.vetter@ffwll.ch>
Cc: Dave Airlie <airlied@redhat.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>

authored by

Alex Williamson and committed by
Dave Airlie
4e4e7dc5 f71c5d9d

+22 -18
+22 -18
drivers/gpu/vga/vgaarb.c
··· 237 237 if (conflict->locks & lwants) 238 238 return conflict; 239 239 240 - /* Ok, now check if he owns the resource we want. We don't need 241 - * to check "decodes" since it should be impossible to own 242 - * own legacy resources you don't decode unless I have a bug 243 - * in this code... 240 + /* Ok, now check if it owns the resource we want. We can 241 + * lock resources that are not decoded, therefore a device 242 + * can own resources it doesn't decode. 244 243 */ 245 - WARN_ON(conflict->owns & ~conflict->decodes); 246 244 match = lwants & conflict->owns; 247 245 if (!match) 248 246 continue; ··· 252 254 flags = 0; 253 255 pci_bits = 0; 254 256 257 + /* If we can't control legacy resources via the bridge, we 258 + * also need to disable normal decoding. 259 + */ 255 260 if (!conflict->bridge_has_one_vga) { 256 - vga_irq_set_state(conflict, false); 257 - flags |= PCI_VGA_STATE_CHANGE_DECODES; 258 - if (match & (VGA_RSRC_LEGACY_MEM|VGA_RSRC_NORMAL_MEM)) 261 + if ((match & conflict->decodes) & VGA_RSRC_LEGACY_MEM) 259 262 pci_bits |= PCI_COMMAND_MEMORY; 260 - if (match & (VGA_RSRC_LEGACY_IO|VGA_RSRC_NORMAL_IO)) 263 + if ((match & conflict->decodes) & VGA_RSRC_LEGACY_IO) 261 264 pci_bits |= PCI_COMMAND_IO; 265 + 266 + if (pci_bits) { 267 + vga_irq_set_state(conflict, false); 268 + flags |= PCI_VGA_STATE_CHANGE_DECODES; 269 + } 262 270 } 263 271 264 272 if (change_bridge) ··· 272 268 273 269 pci_set_vga_state(conflict->pdev, false, pci_bits, flags); 274 270 conflict->owns &= ~match; 275 - /* If he also owned non-legacy, that is no longer the case */ 276 - if (match & VGA_RSRC_LEGACY_MEM) 271 + 272 + /* If we disabled normal decoding, reflect it in owns */ 273 + if (pci_bits & PCI_COMMAND_MEMORY) 277 274 conflict->owns &= ~VGA_RSRC_NORMAL_MEM; 278 - if (match & VGA_RSRC_LEGACY_IO) 275 + if (pci_bits & PCI_COMMAND_IO) 279 276 conflict->owns &= ~VGA_RSRC_NORMAL_IO; 280 277 } 281 278 282 279 enable_them: 283 280 /* ok dude, we got it, everybody conflicting has been disabled, let's 284 - * enable us. Make sure we don't mark a bit in "owns" that we don't 285 - * also have in "decodes". We can lock resources we don't decode but 286 - * not own them. 281 + * enable us. Mark any bits in "owns" regardless of whether we 282 + * decoded them. We can lock resources we don't decode, therefore 283 + * we must track them via "owns". 287 284 */ 288 285 flags = 0; 289 286 pci_bits = 0; ··· 296 291 if (wants & (VGA_RSRC_LEGACY_IO|VGA_RSRC_NORMAL_IO)) 297 292 pci_bits |= PCI_COMMAND_IO; 298 293 } 299 - if (!!(wants & VGA_RSRC_LEGACY_MASK)) 294 + if (wants & VGA_RSRC_LEGACY_MASK) 300 295 flags |= PCI_VGA_STATE_CHANGE_BRIDGE; 301 296 302 297 pci_set_vga_state(vgadev->pdev, true, pci_bits, flags); ··· 304 299 if (!vgadev->bridge_has_one_vga) { 305 300 vga_irq_set_state(vgadev, true); 306 301 } 307 - vgadev->owns |= (wants & vgadev->decodes); 302 + vgadev->owns |= wants; 308 303 lock_them: 309 304 vgadev->locks |= (rsrc & VGA_RSRC_LEGACY_MASK); 310 305 if (rsrc & VGA_RSRC_LEGACY_IO) ··· 654 649 old_decodes = vgadev->decodes; 655 650 decodes_removed = ~new_decodes & old_decodes; 656 651 decodes_unlocked = vgadev->locks & decodes_removed; 657 - vgadev->owns &= ~decodes_removed; 658 652 vgadev->decodes = new_decodes; 659 653 660 654 pr_info("vgaarb: device changed decodes: PCI:%s,olddecodes=%s,decodes=%s:owns=%s\n",