drivers/virtualization: Add doorbell support for ivshmem driver

This basically adds support for an interrupt based ivshmem variant
called "ivshmem-doorbell".

This allows, via MSI-X, to get multiple vectors for notifications, get
assigned and ID and being able to send a message to another ID (another
VM).

Signed-off-by: Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
This commit is contained in:
Tomasz Bursztyka 2020-11-24 10:11:23 +01:00 committed by Anas Nashif
commit d48d5d6147
4 changed files with 278 additions and 3 deletions

View file

@ -30,6 +30,30 @@ module = IVSHMEM
module-str = ivshmem
source "subsys/logging/Kconfig.template.log_config"
config IVSHMEM_DOORBELL
bool "Support interrupt based ivshmem (doorbell version)"
depends on PCIE_MSI_X && PCIE_MSI_MULTI_VECTOR
help
This will enable support of ivshmem-doorbell, i.e. the interrupt
based ivshmem.
config IVSHMEM_MSI_X_VECTORS
int "How many notification vectors should be pre-allocated?"
default 2
depends on IVSHMEM_DOORBELL
help
MSI-X vector holders must be pre-allocated. One can pre-allocate
more or less than necessary. Depends on how many VMs will connect
with each other. These are know to be the notification vectors in
ivshmem.
config IVSHMEM_INT_PRIORITY
int "Interrupt priority"
default 2
depends on IVSHMEM_DOORBELL
help
Interrupt priority used for the MSI-X generated interrupts.
endif # IVSHMEM
endif # VIRTUALIZATION

View file

@ -16,11 +16,89 @@ LOG_MODULE_REGISTER(ivshmem);
#include <soc.h>
#include <device.h>
#include <init.h>
#include <drivers/pcie/pcie.h>
#include <drivers/virtualization/ivshmem.h>
#include "virt_ivshmem.h"
#ifdef CONFIG_IVSHMEM_DOORBELL
static void ivshmem_doorbell(const void *arg)
{
const struct ivshmem_param *param = arg;
LOG_DBG("Interrupt received on vector %u", param->vector);
if (param->signal != NULL) {
k_poll_signal_raise(param->signal, param->vector);
}
}
static bool ivshmem_configure_interrupts(const struct device *dev)
{
struct ivshmem *data = dev->data;
bool ret = false;
uint8_t n_vectors;
uint32_t key;
int i;
key = irq_lock();
n_vectors = pcie_msi_vectors_allocate(data->bdf,
CONFIG_IVSHMEM_INT_PRIORITY,
data->vectors,
CONFIG_IVSHMEM_MSI_X_VECTORS);
if (n_vectors == 0) {
LOG_ERR("Could not allocate %u MSI-X vectors",
CONFIG_IVSHMEM_MSI_X_VECTORS);
goto out;
}
LOG_DBG("Allocated %u vectors", n_vectors);
for (i = 0; i < n_vectors; i++) {
data->params[i].dev = dev;
data->params[i].vector = i;
if (!pcie_msi_vector_connect(data->bdf,
&data->vectors[i],
ivshmem_doorbell,
&data->params[i], 0)) {
LOG_ERR("Failed to connect MSI-X vector %u", i);
goto out;
}
}
LOG_DBG("%u MSI-X Vectors connected", n_vectors);
if (!pcie_msi_enable(data->bdf, data->vectors, n_vectors)) {
LOG_ERR("Could not enable MSI-X");
goto out;
}
data->n_vectors = n_vectors;
ret = true;
LOG_DBG("MSI-X configured");
out:
irq_unlock(key);
return ret;
}
static void register_signal(const struct device *dev,
struct k_poll_signal *signal,
uint16_t vector)
{
struct ivshmem *data = dev->data;
data->params[vector].signal = signal;
}
#else
#define ivshmem_configure_interrupts(...) true
#define register_signal(...)
#endif /* CONFIG_IVSHMEM_DOORBELL */
static bool ivshmem_check_on_bdf(pcie_bdf_t bdf)
{
uint32_t data;
@ -90,7 +168,7 @@ static bool ivshmem_configure(const struct device *dev)
LOG_DBG("- Shared memory of %lu bytes at 0x%lx (mapped to 0x%lx)",
data->size, mbar_mem.phys_addr, data->shmem);
return true;
return ivshmem_configure_interrupts(dev);
}
static size_t ivshmem_api_get_mem(const struct device *dev,
@ -103,8 +181,70 @@ static size_t ivshmem_api_get_mem(const struct device *dev,
return data->size;
}
static uint32_t ivshmem_api_get_id(const struct device *dev)
{
struct ivshmem_reg *regs = (struct ivshmem_reg *)DEVICE_MMIO_GET(dev);
return regs->iv_position;
}
static uint16_t ivshmem_api_get_vectors(const struct device *dev)
{
#if CONFIG_IVSHMEM_DOORBELL
struct ivshmem *data = dev->data;
return data->n_vectors;
#else
return 0;
#endif
}
static int ivshmem_api_int_peer(const struct device *dev,
uint32_t peer_id, uint16_t vector)
{
#if CONFIG_IVSHMEM_DOORBELL
struct ivshmem_reg *regs = (struct ivshmem_reg *)DEVICE_MMIO_GET(dev);
struct ivshmem *data = dev->data;
uint32_t doorbell;
if (vector >= data->n_vectors) {
return -EINVAL;
}
doorbell = IVSHMEM_GEN_DOORBELL(peer_id, vector);
regs->doorbell = doorbell;
return 0;
#else
return -ENOSYS;
#endif
}
static int ivshmem_api_register_handler(const struct device *dev,
struct k_poll_signal *signal,
uint16_t vector)
{
#if CONFIG_IVSHMEM_DOORBELL
struct ivshmem *data = dev->data;
if (vector >= data->n_vectors) {
return -EINVAL;
}
register_signal(dev, signal, vector);
return 0;
#else
return -ENOSYS;
#endif
}
static const struct ivshmem_driver_api ivshmem_api = {
.get_mem = ivshmem_api_get_mem,
.get_id = ivshmem_api_get_id,
.get_vectors = ivshmem_api_get_vectors,
.int_peer = ivshmem_api_int_peer,
.register_handler = ivshmem_api_register_handler
};
static int ivshmem_init(const struct device *dev)

View file

@ -7,21 +7,44 @@
#ifndef ZEPHYR_DRIVERS_VIRTUALIZATION_VIRT_IVSHMEM_H_
#define ZEPHYR_DRIVERS_VIRTUALIZATION_VIRT_IVSHMEM_H_
#include <drivers/pcie/pcie.h>
#include <drivers/pcie/msi.h>
#define IVSHMEM_VENDOR_ID 0x1AF4
#define IVSHMEM_DEVICE_ID 0x1110
#define IVSHMEM_PCIE_REG_BAR_IDX 0
#define IVSHMEM_PCIE_SHMEM_BAR_IDX 1
#define IVSHMEM_PCIE_SHMEM_BAR_IDX 2
#define MAX_BUS (0xFFFFFFFF & PCIE_BDF_BUS_MASK)
#define MAX_DEV (0xFFFFFFFF & PCIE_BDF_DEV_MASK)
#define MAX_FUNC (0xFFFFFFFF & PCIE_BDF_FUNC_MASK)
struct ivshmem_param {
const struct device *dev;
struct k_poll_signal *signal;
uint8_t vector;
};
struct ivshmem {
DEVICE_MMIO_RAM;
pcie_bdf_t bdf;
uintptr_t shmem;
size_t size;
#ifdef CONFIG_IVSHMEM_DOORBELL
msi_vector_t vectors[CONFIG_IVSHMEM_MSI_X_VECTORS];
struct ivshmem_param params[CONFIG_IVSHMEM_MSI_X_VECTORS];
uint16_t n_vectors;
#endif
};
struct ivshmem_reg {
uint32_t int_mask;
uint32_t int_status;
uint32_t iv_position;
uint32_t doorbell;
};
#define IVSHMEM_GEN_DOORBELL(i, v) ((i << 16) | (v & 0xFFFF))
#endif /* ZEPHYR_DRIVERS_VIRTUALIZATION_VIRT_IVSHMEM_H_ */