/*
* vgaarb.c: Implements the VGA arbitration. For details refer to
* Documentation/vgaarbiter.txt
*
*
* (C) Copyright 2005 Benjamin Herrenschmidt <benh@kernel.crashing.org>
* (C) Copyright 2007 Paulo R. Zanoni <przanoni@gmail.com>
* (C) Copyright 2007, 2009 Tiago Vignatti <vignatti@freedesktop.org>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS
* IN THE SOFTWARE.
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/pci.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/list.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/spinlock.h>
#include <linux/poll.h>
#include <linux/miscdevice.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/vgaarb.h>
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
*/
struct vga_device {
struct list_head list;
struct pci_dev *pdev;
unsigned int decodes; /* what does it decodes */
unsigned int owns; /* what does it owns */
unsigned int locks; /* what does it locks */
unsigned int io_lock_cnt; /* legacy IO lock count */
unsigned int mem_lock_cnt; /* legacy MEM lock count */
unsigned int io_norm_cnt; /* normal IO count */
unsigned int mem_norm_cnt; /* normal MEM count */
/* allow IRQ enable/disable hook */
void *cookie;
void (*irq_set_state)(void *cookie, bool enable);
unsigned int (*set_vga_decode)(void *cookie, bool decode);
};
static LIST_HEAD(vga_list);
static int vga_count, vga_decode_count;
static bool vga_arbiter_used;
static DEFINE_SPINLOCK(vga_lock);
static DECLARE_WAIT_QUEUE_HEAD(vga_wait_queue);
static const char *vga_iostate_to_str(unsigned int iostate)
{
/* Ignore VGA_RSRC_IO and VGA_RSRC_MEM */
iostate &= VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM;
switch (iostate) {
case VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM:
return "io+mem";
case VGA_RSRC_LEGACY_IO:
return "io";
case VGA_RSRC_LEGACY_MEM:
return "mem";
}
return "none";
}
static int vga_str_to_iostate(char *buf, int str_size, int *io_state)
{
/* we could in theory hand out locks on IO and mem
* separately to userspace but it can cause deadlocks */
if (strncmp(buf, "none", 4) == 0) {
*io_state = VGA_RSRC_NONE;
return 1;
}
/* XXX We're not chekcing the str_size! */
if (strncmp(buf, "io+mem", 6) == 0)
goto both;
else if (strncmp(buf, "io", 2) == 0)
goto both;
else if (strncmp(buf, "mem", 3) == 0)
goto both;
return 0;
both:
*io_state = VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM;
return 1;
}
#ifndef __ARCH_HAS_VGA_DEFAULT_DEVICE
/* this is only used a cookie - it should not be dereferenced */
static struct pci_dev *vga_default;
#endif
static void vga_arb_device_card_gone(struct pci_dev *pdev);
/* Find somebody in our list */
static struct vga_device *vgadev_find(struct pci_dev *pdev)
{
struct vga_device *vgadev;
list_for_each_entry(vgadev, &vga_list, list)
if (pdev == vgadev->pdev)
return vgadev;
return NULL;
}
/* Returns the default VGA device (vgacon's babe) */
#ifndef __ARCH_HAS_VGA_DEFAULT_DEVICE
struct pci_dev *vga_default_device(void)
{
return vga_default;
}
#endif
static inline void vga_irq_set_state(struct vga_device *vgadev, bool state)
{
if (vgadev->irq_set_state)
vgadev->irq_set_state(vgadev->cookie, state);
}
/* If we don't ever use VGA arb we should avoid
turning off anything anywhere due to old X servers getting
confused about the boot device not being VGA */
static void vga_check_first_use(void)
{
/* we should inform all GPUs in the system that
* VGA arb has occured and to try and disable resources
* if they can */
if (!vga_arbiter_used) {
vga_arbiter_used = true;
vga_arbiter_notify_clients();
}
}
static struct vga_device *__vga_tryget(struct vga_device *vgadev,
unsigned int rsrc)
{
unsigned int wants, legacy_wants, match;
struct vga_device *conflict;
unsigned int pci_bits;
/* Account for "normal" resources to lock. If we decode the legacy,
* counterpart, we need to request it as well
*/
if ((rsrc & VGA_RSRC_NORMAL_IO) &&
(vgadev->decodes & VGA_RSRC_LEGACY_IO))
rsrc |= VGA_RSRC_LEGACY_IO;
if ((rsrc & VGA_RSRC_NORMAL_MEM) &&
(vgadev->decodes & VGA_RSRC_LEGACY_MEM))
rsrc |= VGA_RSRC_LEGACY_MEM;
pr_debug("%s: %d\n", __func__, rsrc);
pr_debug("%s: owns: %d\n", __func__, vgadev->owns);
/* Check what resources we need to acquire */
wants = rsrc & ~vgadev->owns;
/* We already own everything, just mark locked & bye bye */
if (wants == 0)
goto lock_them;
/* We don't need to request a legacy resource, we just enable
* appropriate decoding and go
*/
legacy_wants = wants & VGA_RSRC_LEGACY_MASK;
if (legacy_wants == 0)
goto enable_them;
/* Ok, we don't, let's find out how we need to kick off */
list_for_each_entry(conflict, &vga_list, list) {
unsigned int lwants = legacy_wants;
unsigned int change_bridge = 0;
/* Don't conflict with myself */
if (vgadev == conflict)
continue;
/* Check if the architecture allows a conflict between those
* 2 devices or if they are on separate domains
*/
if (!vga_conflicts(vgadev->pdev, conflict->pdev))
continue;
/* We have a possible conflict. before we go further, we must
* check if we sit on the same bus as the conflicting device.
* if we don't, then we must tie both IO and MEM resources
* together since there is only a single bit controlling
* VGA forwarding on P2P bridges
*/
if (vgadev->pdev->bus != conflict->pdev->bus) {
change_bridge = 1;
lwants = VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM;
}
/* Check if the guy has a lock on the resource. If he does,
* return the conflicting entry
*/
if (conflict->locks & lwants)
return conflict;
/* Ok, now check if he owns the resource we want. We don't need
* to check "decodes" since it should be impossible to own
* own legacy resources you don't decode unless I have a bug
* in this code...
*/
WARN_ON(conflict->owns & ~conflict->decodes);
match = lwants & conflict->owns;
if (!match)
continue;
/* looks like he doesn't have a lock, we can steal
* them from him
*/
vga_irq_set_state(conflict, false);
pci_bits = 0;
if (lwants & (VGA_RSRC_LEGACY_MEM|VGA_RSRC_NORMAL_MEM))
pci_bits |= PCI_COMMAND_MEMORY;
if (lwants & (VGA_RSRC_LEGACY_IO|VGA_RSRC_NORMAL_IO))
pci_bits |= PCI_COMMAND_IO;
pci_set_vga_state(conflict->pdev, false, pci_bits,
change_bridge);
conflict->owns &= ~lwants;
/* If he also owned non-legacy, that is no longer the case */
if (lwants & VGA_RSRC_LEGACY_MEM)
conflict->owns &= ~VGA_RSRC_NORMAL_MEM;
if (lwants & VGA_RSRC_LEGACY_IO)
conflict->owns &= ~VGA_RSRC_NORMAL_IO;
}
enable_them:
/* ok dude, we got it, everybody conflicting has been disabled, let's
* enable us. Make sure we don't mark a bit in "owns" that we don't
* also have in "decodes". We can lock resources we don't decode bu