VGA Arbiter

Graphic devices are accessed through ranges in I/O or memory space. While mostmodern devices allow relocation of such ranges, some “Legacy” VGA devicesimplemented on PCI will typically have the same “hard-decoded” addresses asthey did on ISA. For more details see “PCI Bus Binding to IEEE Std 1275-1994Standard for Boot (Initialization Configuration) Firmware Revision 2.1”Section 7, Legacy Devices.

The Resource Access Control (RAC) module inside the X server [0] existed forthe legacy VGA arbitration task (besides other bus management tasks) when morethan one legacy device co-exists on the same machine. But the problem happenswhen these devices are trying to be accessed by different userspace clients(e.g. two server in parallel). Their address assignments conflict. Moreover,ideally, being a userspace application, it is not the role of the X server tocontrol bus resources. Therefore an arbitration scheme outside of the X serveris needed to control the sharing of these resources. This document introducesthe operation of the VGA arbiter implemented for the Linux kernel.

vgaarb kernel/userspace ABI

The vgaarb is a module of the Linux Kernel. When it is initially loaded, itscans all PCI devices and adds the VGA ones inside the arbitration. Thearbiter then enables/disables the decoding on different devices of the VGAlegacy instructions. Devices which do not want/need to use the arbiter mayexplicitly tell it by callingvga_set_legacy_decoding().

The kernel exports a char device interface (/dev/vga_arbiter) to the clients,which has the following semantics:

open
Opens a user instance of the arbiter. By default, it’s attached to thedefault VGA device of the system.
close
Close a user instance. Release locks made by the user
read

Return a string indicating the status of the target like:

“<card_ID>,decodes=<io_state>,owns=<io_state>,locks=<io_state> (ic,mc)”

An IO state string is of the form {io,mem,io+mem,none}, mc andic are respectively mem and io lock counts (for debugging/diagnostic only). “decodes” indicate what the card currentlydecodes, “owns” indicates what is currently enabled on it, and“locks” indicates what is locked by this card. If the card isunplugged, we get “invalid” then for card_ID and an -ENODEVerror is returned for any command until a new card is targeted.

write

Write a command to the arbiter. List of commands:

target <card_ID>
switch target to card <card_ID> (see below)
lock <io_state>
acquires locks on target (“none” is an invalid io_state)
trylock <io_state>
non-blocking acquire locks on target (returns EBUSY ifunsuccessful)
unlock <io_state>
release locks on target
unlock all
release all locks on target held by this user (not implementedyet)
decodes <io_state>
set the legacy decoding attributes for the card
poll
event if something changes on any card (not just the target)

card_ID is of the form “PCI:domain:bus:dev.fn”. It can be set to “default”to go back to the system default card (TODO: not implemented yet). Currently,only PCI is supported as a prefix, but the userland API may support other bustypes in the future, even if the current kernel implementation doesn’t.

Note about locks:

The driver keeps track of which user has which locks on which card. Itsupports stacking, like the kernel one. This complexifies the implementationa bit, but makes the arbiter more tolerant to user space problems and ableto properly cleanup in all cases when a process dies.Currently, a max of 16 cards can have locks simultaneously issued fromuser space for a given user (file descriptor instance) of the arbiter.

In the case of devices hot-{un,}plugged, there is a hook - pci_notify() - tonotify them being added/removed in the system and automatically added/removedin the arbiter.

There is also an in-kernel API of the arbiter in case DRM, vgacon, or otherdrivers want to use it.

In-kernel interface

voidvga_set_legacy_decoding(struct pci_dev * pdev, unsigned int decodes)

Parameters

structpci_dev*pdev
pci device of the VGA card
unsignedintdecodes

bit mask of what legacy regions the card decodes

Indicates to the arbiter if the card decodes legacy VGA IOs,legacy VGA Memory, both, or none. All cards default to both,the card driver (fbdev for example) should tell the arbiterif it has disabled legacy decoding, so the card can be leftout of the arbitration process (and can be safe to takeinterrupts at any time.

intvga_get_interruptible(struct pci_dev * pdev, unsigned int rsrc)

Parameters

structpci_dev*pdev
pci device of the VGA card or NULL for the system default
unsignedintrsrc
bit mask of resources to acquire and lock

Description

Shortcut to vga_get with interruptible set to true.

On success, release the VGA resource again withvga_put().

intvga_get_uninterruptible(struct pci_dev * pdev, unsigned int rsrc)

shortcut tovga_get()

Parameters

structpci_dev*pdev
pci device of the VGA card or NULL for the system default
unsignedintrsrc
bit mask of resources to acquire and lock

Description

Shortcut to vga_get with interruptible set to false.

On success, release the VGA resource again withvga_put().

struct pci_dev *vga_default_device(void)

return the default VGA device, for vgacon

Parameters

void
no arguments

Description

This can be defined by the platform. The default implementationis rather dumb and will probably only work properly on singlevga card setups and/or x86 platforms.

If your VGA default device is not PCI, you’ll have to returnNULL here. In this case, I assume it will not conflict withany PCI card. If this is not true, I’ll have to define two archshooks for enabling/disabling the VGA default device if that ispossible. This may be a problem with real _ISA_ VGA cards, inaddition to a PCI one. I don’t know at this point how to dealwith that card. Can theirs IOs be disabled at all ? If not, thenI suppose it’s a matter of having the proper arch hook tellingus about it, so we basically never allow anybody to succeed avga_get()

intvga_remove_vgacon(struct pci_dev * pdev)

deactivete vga console

Parameters

structpci_dev*pdev
pci device.

Description

Unbind and unregister vgacon in case pdev is the default vgadevice. Can be called by gpu drivers on initialization to makesure vga register access done by vgacon will not disturb thedevice.

intvga_get(struct pci_dev * pdev, unsigned int rsrc, int interruptible)

acquire & locks VGA resources

Parameters

structpci_dev*pdev
pci device of the VGA card or NULL for the system default
unsignedintrsrc
bit mask of resources to acquire and lock
intinterruptible
blocking should be interruptible by signals ?

Description

This function acquires VGA resources for the given card and mark thoseresources locked. If the resource requested are “normal” (and not legacy)resources, the arbiter will first check whether the card is doing legacydecoding for that type of resource. If yes, the lock is “converted” into alegacy resource lock.

The arbiter will first look for all VGA cards that might conflict and disabletheir IOs and/or Memory access, including VGA forwarding on P2P bridges ifnecessary, so that the requested resources can be used. Then, the card ismarked as locking these resources and the IO and/or Memory accesses areenabled on the card (including VGA forwarding on parent P2P bridges if any).

This function will block if some conflicting card is already locking one ofthe required resources (or any resource on a different bus segment, since P2Pbridges don’t differentiate VGA memory and IO afaik). You can indicatewhether this blocking should be interruptible by a signal (for userlandinterface) or not.

Must not be called at interrupt time or in atomic context. If the cardalready owns the resources, the function succeeds. Nested calls aresupported (a per-resource counter is maintained)

On success, release the VGA resource again withvga_put().

0 on success, negative error code on failure.

Return

voidvga_put(struct pci_dev * pdev, unsigned int rsrc)

release lock on legacy VGA resources

Parameters

structpci_dev*pdev
pci device of VGA card or NULL for system default
unsignedintrsrc
but mask of resource to release

Description

This fuction releases resources previously locked byvga_get() orvga_tryget(). The resources aren’t disabled right away, so that a subsequencevga_get() on the same card will succeed immediately. Resources have acounter, so locks are only released if the counter reaches 0.

intvga_client_register(struct pci_dev * pdev, void * cookie, void (*irq_set_state)(void *cookie, bool state), unsigned int (*set_vga_decode) (void *cookie, bool decode))

register or unregister a VGA arbitration client

Parameters

structpci_dev*pdev
pci device of the VGA client
void*cookie
client cookie to be used in callbacks
void(*)(void*cookie,boolstate)irq_set_state
irq state change callback
unsignedint(*)(void*cookie,booldecode)set_vga_decode
vga decode change callback

Description

Clients have two callback mechanisms they can use.

irq_set_state callback: If a client can’t disable its GPUs VGAresources, then we need to be able to ask it to turn off its irqs when weturn off its mem and io decoding.

set_vga_decode callback: If a client can disable its GPU VGA resource, itwill get a callback from this to set the encode/decode state.

Rationale: we cannot disable VGA decode resources unconditionally some singleGPU laptops seem to require ACPI or BIOS access to the VGA registers tocontrol things like backlights etc. Hopefully newer multi-GPU laptops dosomething saner, and desktops won’t have any special ACPI for this. Thedriver will get a callback when VGA arbitration is first used by userspacesince some older X servers have issues.

This function does not check whether a client forpdev has been registeredalready.

To unregister just call this function withirq_set_state andset_vga_decodeboth set to NULL for the samepdev as originally used to register them.

Return

0 on success, -1 on failure

libpciaccess

To use the vga arbiter char device it was implemented an API inside thelibpciaccess library. One field was added to struct pci_device (each deviceon the system):

/* the type of resource decoded by the device */int vgaarb_rsrc;

Besides it, in pci_system were added:

int vgaarb_fd;int vga_count;struct pci_device *vga_target;struct pci_device *vga_default_dev;

The vga_count is used to track how many cards are being arbitrated, so forinstance, if there is only one card, then it can completely escape arbitration.

These functions below acquire VGA resources for the given card and mark thoseresources as locked. If the resources requested are “normal” (and not legacy)resources, the arbiter will first check whether the card is doing legacydecoding for that type of resource. If yes, the lock is “converted” into alegacy resource lock. The arbiter will first look for all VGA cards thatmight conflict and disable their IOs and/or Memory access, including VGAforwarding on P2P bridges if necessary, so that the requested resources canbe used. Then, the card is marked as locking these resources and the IO and/orMemory access is enabled on the card (including VGA forwarding on parentP2P bridges if any). In the case of vga_arb_lock(), the function will blockif some conflicting card is already locking one of the required resources (orany resource on a different bus segment, since P2P bridges don’t differentiateVGA memory and IO afaik). If the card already owns the resources, the functionsucceeds. vga_arb_trylock() will return (-EBUSY) instead of blocking. Nestedcalls are supported (a per-resource counter is maintained).

Set the target device of this client.

int  pci_device_vgaarb_set_target   (struct pci_device *dev);

For instance, in x86 if two devices on the same bus want to lock differentresources, both will succeed (lock). If devices are in different buses andtrying to lock different resources, only the first who tried succeeds.

int  pci_device_vgaarb_lock         (void);int  pci_device_vgaarb_trylock      (void);

Unlock resources of device.

int  pci_device_vgaarb_unlock       (void);

Indicates to the arbiter if the card decodes legacy VGA IOs, legacy VGAMemory, both, or none. All cards default to both, the card driver (fbdev forexample) should tell the arbiter if it has disabled legacy decoding, so thecard can be left out of the arbitration process (and can be safe to takeinterrupts at any time.

int  pci_device_vgaarb_decodes      (int new_vgaarb_rsrc);

Connects to the arbiter device, allocates the struct

int  pci_device_vgaarb_init         (void);

Close the connection

void pci_device_vgaarb_fini         (void);

xf86VGAArbiter (X server implementation)

X server basically wraps all the functions that touch VGA registers somehow.

References

Benjamin Herrenschmidt (IBM?) started this work when he discussed such designwith the Xorg community in 2005 [1, 2]. In the end of 2007, Paulo Zanoni andTiago Vignatti (both of C3SL/Federal University of Paraná) proceeded his workenhancing the kernel code to adapt as a kernel module and also did theimplementation of the user space side [3]. Now (2009) Tiago Vignatti and DaveAirlie finally put this work in shape and queued to Jesse Barnes’ PCI tree.

  1. https://cgit.freedesktop.org/xorg/xserver/commit/?id=4b42448a2388d40f257774fbffdccaea87bd0347
  2. https://lists.freedesktop.org/archives/xorg/2005-March/006663.html
  3. https://lists.freedesktop.org/archives/xorg/2005-March/006745.html
  4. https://lists.freedesktop.org/archives/xorg/2007-October/029507.html