lib/vmd: hotplug / hotremove support
This patch adds support for hotplug / hotremove detection for devices behind the VMD. The detection acts similarly to the one implemented for regular PCIe devices, that is user has to periodically call probe function. Additionally, for applications not using SPDK's event framework, spdk_vmd_hotplug_monitor has to be called periodically as well. Change-Id: I9f6839560efcf16c839b01976639d835f119cb47 Signed-off-by: orden smith <orden.e.smith@intel.com> Signed-off-by: Konrad Sztyber <konrad.sztyber@intel.com> Reviewed-on: https://review.gerrithub.io/c/spdk/spdk/+/472741 Tested-by: SPDK CI Jenkins <sys_sgci@intel.com> Reviewed-by: Jim Harris <james.r.harris@intel.com> Reviewed-by: Changpeng Liu <changpeng.liu@intel.com> Reviewed-by: Shuhei Matsumoto <shuhei.matsumoto.xt@hitachi.com>
This commit is contained in:
parent
2e2f383c3a
commit
af4e2a3277
@ -96,6 +96,14 @@ int spdk_vmd_set_led_state(struct spdk_pci_device *pci_device, enum spdk_vmd_led
|
|||||||
*/
|
*/
|
||||||
int spdk_vmd_get_led_state(struct spdk_pci_device *pci_device, enum spdk_vmd_led_state *state);
|
int spdk_vmd_get_led_state(struct spdk_pci_device *pci_device, enum spdk_vmd_led_state *state);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks for hotplug/hotremove events of the devices behind the VMD. Needs to be called
|
||||||
|
* periodically to detect them.
|
||||||
|
*
|
||||||
|
* \return number of hotplug events detected or negative errno in case of errors
|
||||||
|
*/
|
||||||
|
int spdk_vmd_hotplug_monitor(void);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
150
lib/vmd/vmd.c
150
lib/vmd/vmd.c
@ -467,6 +467,24 @@ vmd_init_hotplug(struct vmd_pci_device *dev, struct vmd_pci_bus *bus)
|
|||||||
bus->self->header->one.mem_base, bus->self->header->one.mem_limit);
|
bus->self->header->one.mem_base, bus->self->header->one.mem_limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
vmd_bus_device_present(struct vmd_pci_bus *bus, uint32_t devfn)
|
||||||
|
{
|
||||||
|
volatile struct pci_header *header;
|
||||||
|
|
||||||
|
header = (volatile struct pci_header *)(bus->vmd->cfg_vaddr +
|
||||||
|
CONFIG_OFFSET_ADDR(bus->bus_number, devfn, 0, 0));
|
||||||
|
if (!vmd_is_valid_cfg_addr(bus, (uint64_t)header)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (header->common.vendor_id == PCI_INVALID_VENDORID || header->common.vendor_id == 0x0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static struct vmd_pci_device *
|
static struct vmd_pci_device *
|
||||||
vmd_alloc_dev(struct vmd_pci_bus *bus, uint32_t devfn)
|
vmd_alloc_dev(struct vmd_pci_bus *bus, uint32_t devfn)
|
||||||
{
|
{
|
||||||
@ -475,15 +493,19 @@ vmd_alloc_dev(struct vmd_pci_bus *bus, uint32_t devfn)
|
|||||||
uint8_t header_type;
|
uint8_t header_type;
|
||||||
uint32_t rev_class;
|
uint32_t rev_class;
|
||||||
|
|
||||||
header = (struct pci_header * volatile)(bus->vmd->cfg_vaddr +
|
/* Make sure we're not creating two devices on the same dev/fn */
|
||||||
CONFIG_OFFSET_ADDR(bus->bus_number, devfn, 0, 0));
|
TAILQ_FOREACH(dev, &bus->dev_list, tailq) {
|
||||||
if (!vmd_is_valid_cfg_addr(bus, (uint64_t)header)) {
|
if (dev->devfn == devfn) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!vmd_bus_device_present(bus, devfn)) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (header->common.vendor_id == PCI_INVALID_VENDORID || header->common.vendor_id == 0x0) {
|
header = (struct pci_header * volatile)(bus->vmd->cfg_vaddr +
|
||||||
return NULL;
|
CONFIG_OFFSET_ADDR(bus->bus_number, devfn, 0, 0));
|
||||||
}
|
|
||||||
|
|
||||||
SPDK_DEBUGLOG(SPDK_LOG_VMD, "PCI device found: %04x:%04x ***\n",
|
SPDK_DEBUGLOG(SPDK_LOG_VMD, "PCI device found: %04x:%04x ***\n",
|
||||||
header->common.vendor_id, header->common.device_id);
|
header->common.vendor_id, header->common.device_id);
|
||||||
@ -724,7 +746,13 @@ vmd_dev_cfg_write(struct spdk_pci_device *_dev, void *value,
|
|||||||
static void
|
static void
|
||||||
vmd_dev_detach(struct spdk_pci_device *dev)
|
vmd_dev_detach(struct spdk_pci_device *dev)
|
||||||
{
|
{
|
||||||
return;
|
struct vmd_pci_device *vmd_device = (struct vmd_pci_device *)dev;
|
||||||
|
struct vmd_pci_bus *bus = vmd_device->bus;
|
||||||
|
|
||||||
|
spdk_pci_unhook_device(dev);
|
||||||
|
|
||||||
|
TAILQ_REMOVE(&bus->dev_list, vmd_device, tailq);
|
||||||
|
free(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -744,8 +772,10 @@ vmd_dev_init(struct vmd_pci_device *dev)
|
|||||||
dev->pci.cfg_read = vmd_dev_cfg_read;
|
dev->pci.cfg_read = vmd_dev_cfg_read;
|
||||||
dev->pci.cfg_write = vmd_dev_cfg_write;
|
dev->pci.cfg_write = vmd_dev_cfg_write;
|
||||||
dev->pci.detach = vmd_dev_detach;
|
dev->pci.detach = vmd_dev_detach;
|
||||||
dev->cached_slot_control = dev->pcie_cap->slot_control;
|
|
||||||
dev->hotplug_capable = false;
|
dev->hotplug_capable = false;
|
||||||
|
if (dev->pcie_cap != NULL) {
|
||||||
|
dev->cached_slot_control = dev->pcie_cap->slot_control;
|
||||||
|
}
|
||||||
|
|
||||||
if (vmd_is_supported_device(dev)) {
|
if (vmd_is_supported_device(dev)) {
|
||||||
spdk_pci_addr_fmt(bdf, sizeof(bdf), &dev->pci.addr);
|
spdk_pci_addr_fmt(bdf, sizeof(bdf), &dev->pci.addr);
|
||||||
@ -817,7 +847,14 @@ vmd_scan_single_bus(struct vmd_pci_bus *bus, struct vmd_pci_device *parent_bridg
|
|||||||
new_dev->pcie_cap->express_cap_register.bit_field.slot_implemented) {
|
new_dev->pcie_cap->express_cap_register.bit_field.slot_implemented) {
|
||||||
new_bus->hotplug_buses = vmd_get_hotplug_bus_numbers(new_dev);
|
new_bus->hotplug_buses = vmd_get_hotplug_bus_numbers(new_dev);
|
||||||
new_bus->subordinate_bus += new_bus->hotplug_buses;
|
new_bus->subordinate_bus += new_bus->hotplug_buses;
|
||||||
|
|
||||||
|
/* Attach hot plug instance if HP is supported */
|
||||||
|
/* Hot inserted SSDs can be assigned port bus of sub-ordinate + 1 */
|
||||||
|
SPDK_DEBUGLOG(SPDK_LOG_VMD, "hotplug_capable/slot_implemented = "
|
||||||
|
"%x:%x\n", slot_cap.bit_field.hotplug_capable,
|
||||||
|
new_dev->pcie_cap->express_cap_register.bit_field.slot_implemented);
|
||||||
}
|
}
|
||||||
|
|
||||||
new_dev->parent_bridge = parent_bridge;
|
new_dev->parent_bridge = parent_bridge;
|
||||||
new_dev->header->one.primary = new_bus->primary_bus;
|
new_dev->header->one.primary = new_bus->primary_bus;
|
||||||
new_dev->header->one.secondary = new_bus->secondary_bus;
|
new_dev->header->one.secondary = new_bus->secondary_bus;
|
||||||
@ -826,15 +863,9 @@ vmd_scan_single_bus(struct vmd_pci_bus *bus, struct vmd_pci_device *parent_bridg
|
|||||||
vmd_bus_update_bridge_info(new_dev);
|
vmd_bus_update_bridge_info(new_dev);
|
||||||
TAILQ_INSERT_TAIL(&bus->vmd->bus_list, new_bus, tailq);
|
TAILQ_INSERT_TAIL(&bus->vmd->bus_list, new_bus, tailq);
|
||||||
|
|
||||||
/* Attach hot plug instance if HP is supported */
|
|
||||||
/* Hot inserted SSDs can be assigned port bus of sub-ordinate + 1 */
|
|
||||||
SPDK_DEBUGLOG(SPDK_LOG_VMD, "bit_field.hotplug_capable:slot_implemented = %x:%x\n",
|
|
||||||
slot_cap.bit_field.hotplug_capable,
|
|
||||||
new_dev->pcie_cap->express_cap_register.bit_field.slot_implemented);
|
|
||||||
|
|
||||||
vmd_dev_init(new_dev);
|
vmd_dev_init(new_dev);
|
||||||
|
|
||||||
if (slot_cap.bit_field.hotplug_capable &&
|
if (slot_cap.bit_field.hotplug_capable && new_dev->pcie_cap != NULL &&
|
||||||
new_dev->pcie_cap->express_cap_register.bit_field.slot_implemented) {
|
new_dev->pcie_cap->express_cap_register.bit_field.slot_implemented) {
|
||||||
vmd_init_hotplug(new_dev, new_bus);
|
vmd_init_hotplug(new_dev, new_bus);
|
||||||
}
|
}
|
||||||
@ -1105,6 +1136,95 @@ spdk_vmd_pci_device_list(struct spdk_pci_addr vmd_addr, struct spdk_pci_device *
|
|||||||
return cnt;
|
return cnt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
vmd_clear_hotplug_status(struct vmd_pci_bus *bus)
|
||||||
|
{
|
||||||
|
struct vmd_pci_device *device = bus->self;
|
||||||
|
uint16_t status __attribute__((unused));
|
||||||
|
|
||||||
|
status = device->pcie_cap->slot_status.as_uint16_t;
|
||||||
|
device->pcie_cap->slot_status.as_uint16_t = status;
|
||||||
|
status = device->pcie_cap->slot_status.as_uint16_t;
|
||||||
|
|
||||||
|
status = device->pcie_cap->link_status.as_uint16_t;
|
||||||
|
device->pcie_cap->link_status.as_uint16_t = status;
|
||||||
|
status = device->pcie_cap->link_status.as_uint16_t;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
vmd_bus_handle_hotplug(struct vmd_pci_bus *bus)
|
||||||
|
{
|
||||||
|
uint8_t num_devices, sleep_count;
|
||||||
|
|
||||||
|
for (sleep_count = 0; sleep_count < 20; ++sleep_count) {
|
||||||
|
/* Scan until a new device is found */
|
||||||
|
num_devices = vmd_scan_single_bus(bus, bus->self);
|
||||||
|
if (num_devices > 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
spdk_delay_us(200000);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (num_devices == 0) {
|
||||||
|
SPDK_ERRLOG("Timed out while scanning for hotplugged devices\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
vmd_bus_handle_hotremove(struct vmd_pci_bus *bus)
|
||||||
|
{
|
||||||
|
struct vmd_pci_device *device, *tmpdev;
|
||||||
|
|
||||||
|
TAILQ_FOREACH_SAFE(device, &bus->dev_list, tailq, tmpdev) {
|
||||||
|
if (!vmd_bus_device_present(bus, device->devfn)) {
|
||||||
|
device->pci.internal.pending_removal = true;
|
||||||
|
|
||||||
|
/* If the device isn't attached, remove it immediately */
|
||||||
|
if (!device->pci.internal.attached) {
|
||||||
|
vmd_dev_detach(&device->pci);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
spdk_vmd_hotplug_monitor(void)
|
||||||
|
{
|
||||||
|
struct vmd_pci_bus *bus;
|
||||||
|
struct vmd_pci_device *device;
|
||||||
|
int num_hotplugs = 0;
|
||||||
|
uint32_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < g_vmd_container.count; ++i) {
|
||||||
|
TAILQ_FOREACH(bus, &g_vmd_container.vmd[i].bus_list, tailq) {
|
||||||
|
device = bus->self;
|
||||||
|
if (device == NULL || !device->hotplug_capable) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (device->pcie_cap->slot_status.bit_field.datalink_state_changed != 1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (device->pcie_cap->link_status.bit_field.datalink_layer_active == 1) {
|
||||||
|
SPDK_DEBUGLOG(SPDK_LOG_VMD, "Device hotplug detected on bus "
|
||||||
|
"%"PRIu32"\n", bus->bus_number);
|
||||||
|
vmd_bus_handle_hotplug(bus);
|
||||||
|
} else {
|
||||||
|
SPDK_DEBUGLOG(SPDK_LOG_VMD, "Device hotremove detected on bus "
|
||||||
|
"%"PRIu32"\n", bus->bus_number);
|
||||||
|
vmd_bus_handle_hotremove(bus);
|
||||||
|
}
|
||||||
|
|
||||||
|
vmd_clear_hotplug_status(bus);
|
||||||
|
num_hotplugs++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return num_hotplugs;
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
spdk_vmd_init(void)
|
spdk_vmd_init(void)
|
||||||
{
|
{
|
||||||
|
@ -383,6 +383,52 @@ union express_root_control_register {
|
|||||||
uint16_t as_uint16_t;
|
uint16_t as_uint16_t;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
union express_link_capability_register {
|
||||||
|
struct {
|
||||||
|
uint32_t maximum_link_speed : 4;
|
||||||
|
uint32_t maximum_link_width : 6;
|
||||||
|
uint32_t active_state_pms_support : 2;
|
||||||
|
uint32_t l0_exit_latency : 3;
|
||||||
|
uint32_t l1_exit_latency : 3;
|
||||||
|
uint32_t clock_power_management : 1;
|
||||||
|
uint32_t surprise_down_error_reporting_capable : 1;
|
||||||
|
uint32_t datalink_layer_active_reporting_capable : 1;
|
||||||
|
uint32_t link_bandwidth_notification_capability : 1;
|
||||||
|
uint32_t aspm_optionality_compliance : 1;
|
||||||
|
uint32_t rsvd : 1;
|
||||||
|
uint32_t port_number : 8;
|
||||||
|
} bit_field;
|
||||||
|
uint32_t as_uint32_t;
|
||||||
|
};
|
||||||
|
|
||||||
|
union express_link_control_register {
|
||||||
|
struct {
|
||||||
|
uint16_t active_state_pm_control : 2;
|
||||||
|
uint16_t rsvd1 : 1;
|
||||||
|
uint16_t read_completion_boundary : 1;
|
||||||
|
uint16_t link_disable : 1;
|
||||||
|
uint16_t retrain_link : 1;
|
||||||
|
uint16_t common_clock_config : 1;
|
||||||
|
uint16_t extended_synch : 1;
|
||||||
|
uint16_t enable_clock_power_management : 1;
|
||||||
|
uint16_t rsvd2 : 7;
|
||||||
|
} bit_field;
|
||||||
|
uint16_t as_uint16_t;
|
||||||
|
};
|
||||||
|
|
||||||
|
union express_link_status_register {
|
||||||
|
struct {
|
||||||
|
uint16_t link_speed : 4;
|
||||||
|
uint16_t link_width : 6;
|
||||||
|
uint16_t undefined : 1;
|
||||||
|
uint16_t link_training : 1;
|
||||||
|
uint16_t slot_clock_config : 1;
|
||||||
|
uint16_t datalink_layer_active : 1;
|
||||||
|
uint16_t asvd : 2;
|
||||||
|
} bit_field;
|
||||||
|
uint16_t as_uint16_t;
|
||||||
|
};
|
||||||
|
|
||||||
struct pci_express_cap {
|
struct pci_express_cap {
|
||||||
uint8_t capid;
|
uint8_t capid;
|
||||||
uint8_t next_cap;
|
uint8_t next_cap;
|
||||||
@ -390,9 +436,9 @@ struct pci_express_cap {
|
|||||||
uint32_t device_cap;
|
uint32_t device_cap;
|
||||||
uint16_t device_control;
|
uint16_t device_control;
|
||||||
uint16_t device_status;
|
uint16_t device_status;
|
||||||
uint32_t link_cap;
|
union express_link_capability_register link_cap;
|
||||||
uint16_t link_control;
|
union express_link_control_register link_control;
|
||||||
uint16_t link_status;
|
union express_link_status_register link_status;
|
||||||
union express_slot_capabilities_register slot_cap;
|
union express_slot_capabilities_register slot_cap;
|
||||||
union express_slot_control_register slot_control;
|
union express_slot_control_register slot_control;
|
||||||
union express_slot_status_register slot_status;
|
union express_slot_status_register slot_status;
|
||||||
|
@ -145,7 +145,7 @@ DEPDIRS-app_rpc := log util thread event $(JSON_LIBS)
|
|||||||
# the SPDK event subsystem code.
|
# the SPDK event subsystem code.
|
||||||
DEPDIRS-event_copy := copy event
|
DEPDIRS-event_copy := copy event
|
||||||
DEPDIRS-event_net := sock net event
|
DEPDIRS-event_net := sock net event
|
||||||
DEPDIRS-event_vmd := vmd conf $(JSON_LIBS) event
|
DEPDIRS-event_vmd := vmd conf $(JSON_LIBS) event log thread
|
||||||
|
|
||||||
DEPDIRS-event_bdev := bdev event event_copy event_vmd
|
DEPDIRS-event_bdev := bdev event event_copy event_vmd
|
||||||
|
|
||||||
|
@ -33,16 +33,46 @@
|
|||||||
|
|
||||||
#include "spdk/stdinc.h"
|
#include "spdk/stdinc.h"
|
||||||
#include "spdk/conf.h"
|
#include "spdk/conf.h"
|
||||||
|
#include "spdk/thread.h"
|
||||||
|
#include "spdk/likely.h"
|
||||||
|
|
||||||
#include "spdk/vmd.h"
|
#include "spdk/vmd.h"
|
||||||
|
|
||||||
#include "spdk_internal/event.h"
|
#include "spdk_internal/event.h"
|
||||||
#include "event_vmd.h"
|
#include "event_vmd.h"
|
||||||
|
|
||||||
|
static struct spdk_poller *g_hotplug_poller;
|
||||||
|
|
||||||
|
static int
|
||||||
|
vmd_hotplug_monitor(void *ctx)
|
||||||
|
{
|
||||||
|
return spdk_vmd_hotplug_monitor();
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
vmd_subsystem_init(void)
|
vmd_subsystem_init(void)
|
||||||
{
|
{
|
||||||
return spdk_vmd_init();
|
int rc;
|
||||||
|
|
||||||
|
/* If the poller is registered, the initialization already took place */
|
||||||
|
if (g_hotplug_poller != NULL) {
|
||||||
|
SPDK_ERRLOG("The initialization has already been performed\n");
|
||||||
|
return -EBUSY;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = spdk_vmd_init();
|
||||||
|
if (spdk_likely(rc != 0)) {
|
||||||
|
SPDK_ERRLOG("Failed to initialize the VMD library\n");
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_hotplug_poller = spdk_poller_register(vmd_hotplug_monitor, NULL, 1000000ULL);
|
||||||
|
if (g_hotplug_poller == NULL) {
|
||||||
|
SPDK_ERRLOG("Failed to register hotplug monitor poller\n");
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -64,6 +94,8 @@ spdk_vmd_subsystem_init(void)
|
|||||||
static void
|
static void
|
||||||
spdk_vmd_subsystem_fini(void)
|
spdk_vmd_subsystem_fini(void)
|
||||||
{
|
{
|
||||||
|
spdk_poller_unregister(&g_hotplug_poller);
|
||||||
|
|
||||||
spdk_subsystem_fini_next();
|
spdk_subsystem_fini_next();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user