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
|
module-str = ivshmem
|
||||||
source "subsys/logging/Kconfig.template.log_config"
|
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 # IVSHMEM
|
||||||
|
|
||||||
endif # VIRTUALIZATION
|
endif # VIRTUALIZATION
|
||||||
|
|
|
@ -16,11 +16,89 @@ LOG_MODULE_REGISTER(ivshmem);
|
||||||
#include <soc.h>
|
#include <soc.h>
|
||||||
#include <device.h>
|
#include <device.h>
|
||||||
#include <init.h>
|
#include <init.h>
|
||||||
#include <drivers/pcie/pcie.h>
|
|
||||||
|
|
||||||
#include <drivers/virtualization/ivshmem.h>
|
#include <drivers/virtualization/ivshmem.h>
|
||||||
#include "virt_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)
|
static bool ivshmem_check_on_bdf(pcie_bdf_t bdf)
|
||||||
{
|
{
|
||||||
uint32_t data;
|
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)",
|
LOG_DBG("- Shared memory of %lu bytes at 0x%lx (mapped to 0x%lx)",
|
||||||
data->size, mbar_mem.phys_addr, data->shmem);
|
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,
|
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;
|
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 = {
|
static const struct ivshmem_driver_api ivshmem_api = {
|
||||||
.get_mem = ivshmem_api_get_mem,
|
.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)
|
static int ivshmem_init(const struct device *dev)
|
||||||
|
|
|
@ -7,21 +7,44 @@
|
||||||
#ifndef ZEPHYR_DRIVERS_VIRTUALIZATION_VIRT_IVSHMEM_H_
|
#ifndef ZEPHYR_DRIVERS_VIRTUALIZATION_VIRT_IVSHMEM_H_
|
||||||
#define 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_VENDOR_ID 0x1AF4
|
||||||
#define IVSHMEM_DEVICE_ID 0x1110
|
#define IVSHMEM_DEVICE_ID 0x1110
|
||||||
|
|
||||||
#define IVSHMEM_PCIE_REG_BAR_IDX 0
|
#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_BUS (0xFFFFFFFF & PCIE_BDF_BUS_MASK)
|
||||||
#define MAX_DEV (0xFFFFFFFF & PCIE_BDF_DEV_MASK)
|
#define MAX_DEV (0xFFFFFFFF & PCIE_BDF_DEV_MASK)
|
||||||
#define MAX_FUNC (0xFFFFFFFF & PCIE_BDF_FUNC_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 {
|
struct ivshmem {
|
||||||
DEVICE_MMIO_RAM;
|
DEVICE_MMIO_RAM;
|
||||||
pcie_bdf_t bdf;
|
pcie_bdf_t bdf;
|
||||||
uintptr_t shmem;
|
uintptr_t shmem;
|
||||||
size_t size;
|
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_ */
|
#endif /* ZEPHYR_DRIVERS_VIRTUALIZATION_VIRT_IVSHMEM_H_ */
|
||||||
|
|
|
@ -18,8 +18,23 @@ extern "C" {
|
||||||
typedef size_t (*ivshmem_get_mem_f)(const struct device *dev,
|
typedef size_t (*ivshmem_get_mem_f)(const struct device *dev,
|
||||||
uintptr_t *memmap);
|
uintptr_t *memmap);
|
||||||
|
|
||||||
|
typedef uint32_t (*ivshmem_get_id_f)(const struct device *dev);
|
||||||
|
|
||||||
|
typedef uint16_t (*ivshmem_get_vectors_f)(const struct device *dev);
|
||||||
|
|
||||||
|
typedef int (*ivshmem_int_peer_f)(const struct device *dev,
|
||||||
|
uint32_t peer_id, uint16_t vector);
|
||||||
|
|
||||||
|
typedef int (*ivshmem_register_handler_f)(const struct device *dev,
|
||||||
|
struct k_poll_signal *signal,
|
||||||
|
uint16_t vector);
|
||||||
|
|
||||||
struct ivshmem_driver_api {
|
struct ivshmem_driver_api {
|
||||||
ivshmem_get_mem_f get_mem;
|
ivshmem_get_mem_f get_mem;
|
||||||
|
ivshmem_get_id_f get_id;
|
||||||
|
ivshmem_get_vectors_f get_vectors;
|
||||||
|
ivshmem_int_peer_f int_peer;
|
||||||
|
ivshmem_register_handler_f register_handler;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -39,6 +54,79 @@ static inline size_t ivshmem_get_mem(const struct device *dev,
|
||||||
return api->get_mem(dev, memmap);
|
return api->get_mem(dev, memmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get our VM ID
|
||||||
|
*
|
||||||
|
* @param dev Pointer to the device structure for the driver instance
|
||||||
|
*
|
||||||
|
* @return our VM ID or 0 if we are not running on doorbell version
|
||||||
|
*/
|
||||||
|
static inline uint32_t ivshmem_get_id(const struct device *dev)
|
||||||
|
{
|
||||||
|
const struct ivshmem_driver_api *api =
|
||||||
|
(const struct ivshmem_driver_api *)dev->api;
|
||||||
|
|
||||||
|
return api->get_id(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the number of interrupt vectors we can use
|
||||||
|
*
|
||||||
|
* @param dev Pointer to the device structure for the driver instance
|
||||||
|
*
|
||||||
|
* @return the number of available interrupt vectors
|
||||||
|
*/
|
||||||
|
static inline uint16_t ivshmem_get_vectors(const struct device *dev)
|
||||||
|
{
|
||||||
|
const struct ivshmem_driver_api *api =
|
||||||
|
(const struct ivshmem_driver_api *)dev->api;
|
||||||
|
|
||||||
|
return api->get_vectors(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Interrupt another VM
|
||||||
|
*
|
||||||
|
* @param dev Pointer to the device structure for the driver instance
|
||||||
|
* @param peer_id The VM ID to interrupt
|
||||||
|
* @param vector The interrupt vector to use
|
||||||
|
*
|
||||||
|
* @return 0 on success, a negative errno otherwise
|
||||||
|
*/
|
||||||
|
static inline int ivshmem_int_peer(const struct device *dev,
|
||||||
|
uint32_t peer_id, uint16_t vector)
|
||||||
|
{
|
||||||
|
const struct ivshmem_driver_api *api =
|
||||||
|
(const struct ivshmem_driver_api *)dev->api;
|
||||||
|
|
||||||
|
return api->int_peer(dev, peer_id, vector);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Register a vector notification (interrupt) handler
|
||||||
|
*
|
||||||
|
* @param dev Pointer to the device structure for the driver instance
|
||||||
|
* @param signal A pointer to a valid and ready to be signaled
|
||||||
|
* struct k_poll_signal. Or NULL to unregister any handler
|
||||||
|
* registered for the given vector.
|
||||||
|
* @param vector The interrupt vector to get notification from
|
||||||
|
*
|
||||||
|
* Note: The returned status, if positive, to a raised signal is the vector
|
||||||
|
* that generated the signal. This lets the possibility to the user
|
||||||
|
* to have one signal for all vectors, or one per-vector.
|
||||||
|
*
|
||||||
|
* @return 0 on success, a negative errno otherwise
|
||||||
|
*/
|
||||||
|
static inline int ivshmem_register_handler(const struct device *dev,
|
||||||
|
struct k_poll_signal *signal,
|
||||||
|
uint16_t vector)
|
||||||
|
{
|
||||||
|
const struct ivshmem_driver_api *api =
|
||||||
|
(const struct ivshmem_driver_api *)dev->api;
|
||||||
|
|
||||||
|
return api->register_handler(dev, signal, vector);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue