Files
Will Deacon b59816369a kvm tools: irq: move irq line allocation into device registration
For the MMIO and PCI buses, drivers typically allocate an IRQ line for
their device before registering the device with the device tree for the
relevant bus.

This patch moves the IRQ allocation into the bus code, which is then
called directly by the device tree when a new device is registered.
IOPORT devices, however, tend to use hardcoded IRQs for legacy reasons,
so they are still required to deal with their interrupts (which also
require remapping for non-x86 architectures).

Signed-off-by: Will Deacon <will.deacon@arm.com>
Signed-off-by: Pekka Enberg <penberg@kernel.org>
2015-06-01 16:39:55 +01:00

106 lines
2.2 KiB
C

#include "kvm/devices.h"
#include "kvm/kvm.h"
#include "kvm/pci.h"
#include "kvm/virtio-mmio.h"
#include <linux/err.h>
#include <linux/rbtree.h>
struct device_bus {
struct rb_root root;
int dev_num;
};
static struct device_bus device_trees[DEVICE_BUS_MAX] = {
[0 ... (DEVICE_BUS_MAX - 1)] = { RB_ROOT, 0 },
};
int device__register(struct device_header *dev)
{
struct device_bus *bus;
struct rb_node **node, *parent = NULL;
if (dev->bus_type >= DEVICE_BUS_MAX) {
pr_warning("Ignoring device registration on unknown bus %d\n",
dev->bus_type);
return -EINVAL;
}
bus = &device_trees[dev->bus_type];
dev->dev_num = bus->dev_num++;
switch (dev->bus_type) {
case DEVICE_BUS_PCI:
pci__assign_irq(dev);
break;
case DEVICE_BUS_MMIO:
virtio_mmio_assign_irq(dev);
break;
default:
break;
}
node = &bus->root.rb_node;
while (*node) {
int num = rb_entry(*node, struct device_header, node)->dev_num;
int result = dev->dev_num - num;
if (result < 0)
node = &((*node)->rb_left);
else if (result > 0)
node = &((*node)->rb_right);
else
return -EEXIST;
}
rb_link_node(&dev->node, parent, node);
rb_insert_color(&dev->node, &bus->root);
return 0;
}
void device__unregister(struct device_header *dev)
{
struct device_bus *bus = &device_trees[dev->bus_type];
rb_erase(&dev->node, &bus->root);
}
struct device_header *device__find_dev(enum device_bus_type bus_type, u8 dev_num)
{
struct rb_node *node;
if (bus_type >= DEVICE_BUS_MAX)
return ERR_PTR(-EINVAL);
node = device_trees[bus_type].root.rb_node;
while (node) {
struct device_header *dev = rb_entry(node, struct device_header,
node);
if (dev_num < dev->dev_num) {
node = node->rb_left;
} else if (dev_num > dev->dev_num) {
node = node->rb_right;
} else {
return dev;
}
}
return NULL;
}
struct device_header *device__first_dev(enum device_bus_type bus_type)
{
struct rb_node *node;
if (bus_type >= DEVICE_BUS_MAX)
return NULL;
node = rb_first(&device_trees[bus_type].root);
return node ? rb_entry(node, struct device_header, node) : NULL;
}
struct device_header *device__next_dev(struct device_header *dev)
{
struct rb_node *node = rb_next(&dev->node);
return node ? rb_entry(node, struct device_header, node) : NULL;
}