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:
parent
d11f2f184d
commit
d48d5d6147
4 changed files with 278 additions and 3 deletions
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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_ */
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue