Thread (171 messages) 171 messages, 14 authors, 2017-03-29
STALE3413d

Revision v3 of 6 in this series.

Revisions (6)
  1. v1 [diff vs current]
  2. v2 [diff vs current]
  3. v3 current
  4. v4 [diff vs current]
  5. v5 [diff vs current]
  6. v6 [diff vs current]

[PATCH v3 08/16] net/avp: device initialization

From: Allain Legacy <hidden>
Date: 2017-03-02 00:20:33
Subsystem: networking drivers, the rest · Maintainers: Andrew Lunn, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linus Torvalds

Adds support for initialization newly probed AVP PCI devices.  Initial
queue translations are setup in preparation for device configuration.

Signed-off-by: Allain Legacy <redacted>
Signed-off-by: Matt Peters <redacted>
---
 drivers/net/avp/avp_ethdev.c | 733 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 733 insertions(+)
diff --git a/drivers/net/avp/avp_ethdev.c b/drivers/net/avp/avp_ethdev.c
index f0bf862..467cec8 100644
--- a/drivers/net/avp/avp_ethdev.c
+++ b/drivers/net/avp/avp_ethdev.c
@@ -53,6 +53,7 @@
 #include <rte_dev.h>
 #include <rte_memory.h>
 #include <rte_eal.h>
+#include <rte_io.h>
 
 #include "rte_avp_common.h"
 #include "rte_avp_fifo.h"
@@ -60,6 +61,8 @@
 #include "avp_logs.h"
 
 
+static int avp_dev_create(struct rte_pci_device *pci_dev,
+			  struct rte_eth_dev *eth_dev);
 
 static int eth_avp_dev_init(struct rte_eth_dev *eth_dev);
 static int eth_avp_dev_uninit(struct rte_eth_dev *eth_dev);
@@ -103,6 +106,16 @@
 };
 
 
+/**@{ AVP device flags */
+#define AVP_F_PROMISC (1 << 1)
+#define AVP_F_CONFIGURED (1 << 2)
+#define AVP_F_LINKUP (1 << 3)
+#define AVP_F_DETACHED (1 << 4)
+/**@} */
+
+/* Ethernet device validation marker */
+#define AVP_ETHDEV_MAGIC 0x92972862
+
 /*
  * Defines the AVP device attributes which are attached to an RTE ethernet
  * device
@@ -150,11 +163,701 @@ struct avp_adapter {
 	struct avp_dev avp;
 } __rte_cache_aligned;
 
+
+/* 32-bit MMIO register write */
+#define AVP_WRITE32(_value, _addr) rte_write32_relaxed((_value), (_addr))
+
+/* 32-bit MMIO register read */
+#define AVP_READ32(_addr) rte_read32_relaxed((_addr))
+
 /* Macro to cast the ethernet device private data to a AVP object */
 #define AVP_DEV_PRIVATE_TO_HW(adapter) \
 	(&((struct avp_adapter *)adapter)->avp)
 
 /*
+ * Defines the structure of a AVP device queue for the purpose of handling the
+ * receive and transmit burst callback functions
+ */
+struct avp_queue {
+	struct rte_eth_dev_data *dev_data;
+	/**< Backpointer to ethernet device data */
+	struct avp_dev *avp; /**< Backpointer to AVP device */
+	uint16_t queue_id;
+	/**< Queue identifier used for indexing current queue */
+	uint16_t queue_base;
+	/**< Base queue identifier for queue servicing */
+	uint16_t queue_limit;
+	/**< Maximum queue identifier for queue servicing */
+
+	uint64_t packets;
+	uint64_t bytes;
+	uint64_t errors;
+};
+
+/* send a request and wait for a response
+ *
+ * @warning must be called while holding the avp->lock spinlock.
+ */
+static int
+avp_dev_process_request(struct avp_dev *avp, struct rte_avp_request *request)
+{
+	unsigned int retry = AVP_MAX_REQUEST_RETRY;
+	void *resp_addr = NULL;
+	unsigned int count;
+	int ret;
+
+	PMD_DRV_LOG(DEBUG, "Sending request %u to host\n", request->req_id);
+
+	request->result = -ENOTSUP;
+
+	/* Discard any stale responses before starting a new request */
+	while (avp_fifo_get(avp->resp_q, (void **)&resp_addr, 1))
+		PMD_DRV_LOG(DEBUG, "Discarding stale response\n");
+
+	rte_memcpy(avp->sync_addr, request, sizeof(*request));
+	count = avp_fifo_put(avp->req_q, &avp->host_sync_addr, 1);
+	if (count < 1) {
+		PMD_DRV_LOG(ERR, "Cannot send request %u to host\n",
+			    request->req_id);
+		ret = -EBUSY;
+		goto done;
+	}
+
+	while (retry--) {
+		/* wait for a response */
+		usleep(AVP_REQUEST_DELAY_USECS);
+
+		count = avp_fifo_count(avp->resp_q);
+		if (count >= 1) {
+			/* response received */
+			break;
+		}
+
+		if ((count < 1) && (retry == 0)) {
+			PMD_DRV_LOG(ERR, "Timeout while waiting for a response for %u\n",
+				    request->req_id);
+			ret = -ETIME;
+			goto done;
+		}
+	}
+
+	/* retrieve the response */
+	count = avp_fifo_get(avp->resp_q, (void **)&resp_addr, 1);
+	if ((count != 1) || (resp_addr != avp->host_sync_addr)) {
+		PMD_DRV_LOG(ERR, "Invalid response from host, count=%u resp=%p host_sync_addr=%p\n",
+			    count, resp_addr, avp->host_sync_addr);
+		ret = -ENODATA;
+		goto done;
+	}
+
+	/* copy to user buffer */
+	rte_memcpy(request, avp->sync_addr, sizeof(*request));
+	ret = 0;
+
+	PMD_DRV_LOG(DEBUG, "Result %d received for request %u\n",
+		    request->result, request->req_id);
+
+done:
+	return ret;
+}
+
+static int
+avp_dev_ctrl_set_config(struct rte_eth_dev *eth_dev,
+			struct rte_avp_device_config *config)
+{
+	struct avp_dev *avp = AVP_DEV_PRIVATE_TO_HW(eth_dev->data->dev_private);
+	struct rte_avp_request request;
+	int ret;
+
+	/* setup a configure request */
+	memset(&request, 0, sizeof(request));
+	request.req_id = RTE_AVP_REQ_CFG_DEVICE;
+	memcpy(&request.config, config, sizeof(request.config));
+
+	ret = avp_dev_process_request(avp, &request);
+
+	return ret == 0 ? request.result : ret;
+}
+
+static int
+avp_dev_ctrl_shutdown(struct rte_eth_dev *eth_dev)
+{
+	struct avp_dev *avp = AVP_DEV_PRIVATE_TO_HW(eth_dev->data->dev_private);
+	struct rte_avp_request request;
+	int ret;
+
+	/* setup a shutdown request */
+	memset(&request, 0, sizeof(request));
+	request.req_id = RTE_AVP_REQ_SHUTDOWN_DEVICE;
+
+	ret = avp_dev_process_request(avp, &request);
+
+	return ret == 0 ? request.result : ret;
+}
+
+/* translate from host physical address to guest virtual address */
+static void *
+avp_dev_translate_address(struct rte_eth_dev *eth_dev,
+			  phys_addr_t host_phys_addr)
+{
+	struct rte_pci_device *pci_dev = AVP_DEV_TO_PCI(eth_dev);
+	struct rte_mem_resource *resource;
+	struct rte_avp_memmap_info *info;
+	struct rte_avp_memmap *map;
+	off_t offset;
+	void *addr;
+	unsigned int i;
+
+	addr = pci_dev->mem_resource[RTE_AVP_PCI_MEMORY_BAR].addr;
+	resource = &pci_dev->mem_resource[RTE_AVP_PCI_MEMMAP_BAR];
+	info = (struct rte_avp_memmap_info *)resource->addr;
+
+	offset = 0;
+	for (i = 0; i < info->nb_maps; i++) {
+		/* search all segments looking for a matching address */
+		map = &info->maps[i];
+
+		if ((host_phys_addr >= map->phys_addr) &&
+			(host_phys_addr < (map->phys_addr + map->length))) {
+			/* address is within this segment */
+			offset += (host_phys_addr - map->phys_addr);
+			addr = RTE_PTR_ADD(addr, offset);
+
+			PMD_DRV_LOG(DEBUG, "Translating host physical 0x%" PRIx64 " to guest virtual 0x%p\n",
+				    host_phys_addr, addr);
+
+			return addr;
+		}
+		offset += map->length;
+	}
+
+	return NULL;
+}
+
+/* verify that the incoming device version is compatible with our version */
+static int
+avp_dev_version_check(uint32_t version)
+{
+	uint32_t driver = RTE_AVP_STRIP_MINOR_VERSION(AVP_DPDK_DRIVER_VERSION);
+	uint32_t device = RTE_AVP_STRIP_MINOR_VERSION(version);
+
+	if (device <= driver) {
+		/* the host driver version is less than or equal to ours */
+		return 0;
+	}
+
+	return 1;
+}
+
+/* verify that memory regions have expected version and validation markers */
+static int
+avp_dev_check_regions(struct rte_eth_dev *eth_dev)
+{
+	struct rte_pci_device *pci_dev = AVP_DEV_TO_PCI(eth_dev);
+	struct rte_avp_memmap_info *memmap;
+	struct rte_avp_device_info *info;
+	struct rte_mem_resource *resource;
+	unsigned int i;
+
+	/* Dump resource info for debug */
+	for (i = 0; i < PCI_MAX_RESOURCE; i++) {
+		resource = &pci_dev->mem_resource[i];
+		if ((resource->phys_addr == 0) || (resource->len == 0))
+			continue;
+
+		PMD_DRV_LOG(DEBUG, "resource[%u]: phys=0x%" PRIx64 " len=%" PRIu64 " addr=%p\n",
+			    i, resource->phys_addr,
+			    resource->len, resource->addr);
+
+		switch (i) {
+		case RTE_AVP_PCI_MEMMAP_BAR:
+			memmap = (struct rte_avp_memmap_info *)resource->addr;
+			if ((memmap->magic != RTE_AVP_MEMMAP_MAGIC) ||
+			    (memmap->version != RTE_AVP_MEMMAP_VERSION)) {
+				PMD_DRV_LOG(ERR, "Invalid memmap magic 0x%08x and version %u\n",
+					    memmap->magic, memmap->version);
+				return -EINVAL;
+			}
+			break;
+
+		case RTE_AVP_PCI_DEVICE_BAR:
+			info = (struct rte_avp_device_info *)resource->addr;
+			if ((info->magic != RTE_AVP_DEVICE_MAGIC) ||
+			    avp_dev_version_check(info->version)) {
+				PMD_DRV_LOG(ERR, "Invalid device info magic 0x%08x or version 0x%08x > 0x%08x\n",
+					    info->magic, info->version,
+					    AVP_DPDK_DRIVER_VERSION);
+				return -EINVAL;
+			}
+			break;
+
+		case RTE_AVP_PCI_MEMORY_BAR:
+		case RTE_AVP_PCI_MMIO_BAR:
+			if (resource->addr == NULL) {
+				PMD_DRV_LOG(ERR, "Missing address space for BAR%u\n",
+					    i);
+				return -EINVAL;
+			}
+			break;
+
+		case RTE_AVP_PCI_MSIX_BAR:
+		default:
+			/* no validation required */
+			break;
+		}
+	}
+
+	return 0;
+}
+
+static int
+avp_dev_detach(struct rte_eth_dev *eth_dev)
+{
+	struct avp_dev *avp = AVP_DEV_PRIVATE_TO_HW(eth_dev->data->dev_private);
+	int ret;
+
+	PMD_DRV_LOG(NOTICE, "Detaching port %u from AVP device 0x%" PRIx64 "\n",
+		    eth_dev->data->port_id, avp->device_id);
+
+	rte_spinlock_lock(&avp->lock);
+
+	if (avp->flags & AVP_F_DETACHED) {
+		PMD_DRV_LOG(NOTICE, "port %u already detached\n",
+			    eth_dev->data->port_id);
+		ret = 0;
+		goto unlock;
+	}
+
+	/* shutdown the device first so the host stops sending us packets. */
+	ret = avp_dev_ctrl_shutdown(eth_dev);
+	if (ret < 0) {
+		PMD_DRV_LOG(ERR, "Failed to send/recv shutdown to host, ret=%d\n",
+			    ret);
+		avp->flags &= ~AVP_F_DETACHED;
+		goto unlock;
+	}
+
+	avp->flags |= AVP_F_DETACHED;
+	rte_wmb();
+
+	/* wait for queues to acknowledge the presence of the detach flag */
+	rte_delay_ms(1);
+
+	ret = 0;
+
+unlock:
+	rte_spinlock_unlock(&avp->lock);
+	return ret;
+}
+
+static void
+_avp_set_rx_queue_mappings(struct rte_eth_dev *eth_dev, uint16_t rx_queue_id)
+{
+	struct avp_dev *avp =
+		AVP_DEV_PRIVATE_TO_HW(eth_dev->data->dev_private);
+	struct avp_queue *rxq;
+	uint16_t queue_count;
+	uint16_t remainder;
+
+	rxq = (struct avp_queue *)eth_dev->data->rx_queues[rx_queue_id];
+
+	/*
+	 * Must map all AVP fifos as evenly as possible between the configured
+	 * device queues.  Each device queue will service a subset of the AVP
+	 * fifos. If there is an odd number of device queues the first set of
+	 * device queues will get the extra AVP fifos.
+	 */
+	queue_count = avp->num_rx_queues / eth_dev->data->nb_rx_queues;
+	remainder = avp->num_rx_queues % eth_dev->data->nb_rx_queues;
+	if (rx_queue_id < remainder) {
+		/* these queues must service one extra FIFO */
+		rxq->queue_base = rx_queue_id * (queue_count + 1);
+		rxq->queue_limit = rxq->queue_base + (queue_count + 1) - 1;
+	} else {
+		/* these queues service the regular number of FIFO */
+		rxq->queue_base = ((remainder * (queue_count + 1)) +
+				   ((rx_queue_id - remainder) * queue_count));
+		rxq->queue_limit = rxq->queue_base + queue_count - 1;
+	}
+
+	PMD_DRV_LOG(DEBUG, "rxq %u at %p base %u limit %u\n",
+		    rx_queue_id, rxq, rxq->queue_base, rxq->queue_limit);
+
+	rxq->queue_id = rxq->queue_base;
+}
+
+static void
+_avp_set_queue_counts(struct rte_eth_dev *eth_dev)
+{
+	struct rte_pci_device *pci_dev = AVP_DEV_TO_PCI(eth_dev);
+	struct avp_dev *avp = AVP_DEV_PRIVATE_TO_HW(eth_dev->data->dev_private);
+	struct rte_avp_device_info *host_info;
+	void *addr;
+
+	addr = pci_dev->mem_resource[RTE_AVP_PCI_DEVICE_BAR].addr;
+	host_info = (struct rte_avp_device_info *)addr;
+
+	/*
+	 * the transmit direction is not negotiated beyond respecting the max
+	 * number of queues because the host can handle arbitrary guest tx
+	 * queues (host rx queues).
+	 */
+	avp->num_tx_queues = eth_dev->data->nb_tx_queues;
+
+	/*
+	 * the receive direction is more restrictive.  The host requires a
+	 * minimum number of guest rx queues (host tx queues) therefore
+	 * negotiate a value that is at least as large as the host minimum
+	 * requirement.  If the host and guest values are not identical then a
+	 * mapping will be established in the receive_queue_setup function.
+	 */
+	avp->num_rx_queues = RTE_MAX(host_info->min_rx_queues,
+				     eth_dev->data->nb_rx_queues);
+
+	PMD_DRV_LOG(DEBUG, "Requesting %u Tx and %u Rx queues from host\n",
+		    avp->num_tx_queues, avp->num_rx_queues);
+}
+
+static int
+avp_dev_attach(struct rte_eth_dev *eth_dev)
+{
+	struct avp_dev *avp = AVP_DEV_PRIVATE_TO_HW(eth_dev->data->dev_private);
+	struct rte_avp_device_config config;
+	unsigned int i;
+	int ret;
+
+	PMD_DRV_LOG(NOTICE, "Attaching port %u to AVP device 0x%" PRIx64 "\n",
+		    eth_dev->data->port_id, avp->device_id);
+
+	rte_spinlock_lock(&avp->lock);
+
+	if (!(avp->flags & AVP_F_DETACHED)) {
+		PMD_DRV_LOG(NOTICE, "port %u already attached\n",
+			    eth_dev->data->port_id);
+		ret = 0;
+		goto unlock;
+	}
+
+	/*
+	 * make sure that the detached flag is set prior to reconfiguring the
+	 * queues.
+	 */
+	avp->flags |= AVP_F_DETACHED;
+	rte_wmb();
+
+	/*
+	 * re-run the device create utility which will parse the new host info
+	 * and setup the AVP device queue pointers.
+	 */
+	ret = avp_dev_create(AVP_DEV_TO_PCI(eth_dev), eth_dev);
+	if (ret < 0) {
+		PMD_DRV_LOG(ERR, "Failed to re-create AVP device, ret=%d\n",
+			    ret);
+		goto unlock;
+	}
+
+	if (avp->flags & AVP_F_CONFIGURED) {
+		/*
+		 * Update the receive queue mapping to handle cases where the
+		 * source and destination hosts have different queue
+		 * requirements.  As long as the DETACHED flag is asserted the
+		 * queue table should not be referenced so it should be safe to
+		 * update it.
+		 */
+		_avp_set_queue_counts(eth_dev);
+		for (i = 0; i < eth_dev->data->nb_rx_queues; i++)
+			_avp_set_rx_queue_mappings(eth_dev, i);
+
+		/*
+		 * Update the host with our config details so that it knows the
+		 * device is active.
+		 */
+		memset(&config, 0, sizeof(config));
+		config.device_id = avp->device_id;
+		config.driver_type = RTE_AVP_DRIVER_TYPE_DPDK;
+		config.driver_version = AVP_DPDK_DRIVER_VERSION;
+		config.features = avp->features;
+		config.num_tx_queues = avp->num_tx_queues;
+		config.num_rx_queues = avp->num_rx_queues;
+		config.if_up = !!(avp->flags & AVP_F_LINKUP);
+
+		ret = avp_dev_ctrl_set_config(eth_dev, &config);
+		if (ret < 0) {
+			PMD_DRV_LOG(ERR, "Config request failed by host, ret=%d\n",
+				    ret);
+			goto unlock;
+		}
+	}
+
+	rte_wmb();
+	avp->flags &= ~AVP_F_DETACHED;
+
+	ret = 0;
+
+unlock:
+	rte_spinlock_unlock(&avp->lock);
+	return ret;
+}
+
+static void
+avp_dev_interrupt_handler(struct rte_intr_handle *intr_handle,
+						  void *data)
+{
+	struct rte_eth_dev *eth_dev = data;
+	struct rte_pci_device *pci_dev = AVP_DEV_TO_PCI(eth_dev);
+	void *registers = pci_dev->mem_resource[RTE_AVP_PCI_MMIO_BAR].addr;
+	uint32_t status, value;
+	int ret;
+
+	if (registers == NULL)
+		rte_panic("no mapped MMIO register space\n");
+
+	/* read the interrupt status register
+	 * note: this register clears on read so all raised interrupts must be
+	 *    handled or remembered for later processing
+	 */
+	status = AVP_READ32(
+		RTE_PTR_ADD(registers,
+			    RTE_AVP_INTERRUPT_STATUS_OFFSET));
+
+	if (status | RTE_AVP_MIGRATION_INTERRUPT_MASK) {
+		/* handle interrupt based on current status */
+		value = AVP_READ32(
+			RTE_PTR_ADD(registers,
+				    RTE_AVP_MIGRATION_STATUS_OFFSET));
+		switch (value) {
+		case RTE_AVP_MIGRATION_DETACHED:
+			ret = avp_dev_detach(eth_dev);
+			break;
+		case RTE_AVP_MIGRATION_ATTACHED:
+			ret = avp_dev_attach(eth_dev);
+			break;
+		default:
+			PMD_DRV_LOG(ERR, "unexpected migration status, status=%u\n",
+				    value);
+			ret = -EINVAL;
+		}
+
+		/* acknowledge the request by writing out our current status */
+		value = (ret == 0 ? value : RTE_AVP_MIGRATION_ERROR);
+		AVP_WRITE32(value,
+			    RTE_PTR_ADD(registers,
+					RTE_AVP_MIGRATION_ACK_OFFSET));
+
+		PMD_DRV_LOG(NOTICE, "AVP migration interrupt handled\n");
+	}
+
+	if (status & ~RTE_AVP_MIGRATION_INTERRUPT_MASK)
+		PMD_DRV_LOG(WARNING, "AVP unexpected interrupt, status=0x%08x\n",
+			    status);
+
+	/* re-enable UIO interrupt handling */
+	ret = rte_intr_enable(intr_handle);
+	if (ret < 0) {
+		PMD_DRV_LOG(ERR, "Failed to re-enable UIO interrupts, ret=%d\n",
+			    ret);
+		/* continue */
+	}
+}
+
+static int
+avp_dev_enable_interrupts(struct rte_eth_dev *eth_dev)
+{
+	struct rte_pci_device *pci_dev = AVP_DEV_TO_PCI(eth_dev);
+	void *registers = pci_dev->mem_resource[RTE_AVP_PCI_MMIO_BAR].addr;
+	int ret;
+
+	if (registers == NULL)
+		return -EINVAL;
+
+	/* enable UIO interrupt handling */
+	ret = rte_intr_enable(&pci_dev->intr_handle);
+	if (ret < 0) {
+		PMD_DRV_LOG(ERR, "Failed to enable UIO interrupts, ret=%d\n",
+			    ret);
+		return ret;
+	}
+
+	/* inform the device that all interrupts are enabled */
+	AVP_WRITE32(RTE_AVP_APP_INTERRUPTS_MASK,
+		    RTE_PTR_ADD(registers, RTE_AVP_INTERRUPT_MASK_OFFSET));
+
+	return 0;
+}
+
+static int
+avp_dev_setup_interrupts(struct rte_eth_dev *eth_dev)
+{
+	struct rte_pci_device *pci_dev = AVP_DEV_TO_PCI(eth_dev);
+	int ret;
+
+	/* register a callback handler with UIO for interrupt notifications */
+	ret = rte_intr_callback_register(&pci_dev->intr_handle,
+					 avp_dev_interrupt_handler,
+					 (void *)eth_dev);
+	if (ret < 0) {
+		PMD_DRV_LOG(ERR, "Failed to register UIO interrupt callback, ret=%d\n",
+			    ret);
+		return ret;
+	}
+
+	/* enable interrupt processing */
+	return avp_dev_enable_interrupts(eth_dev);
+}
+
+static int
+avp_dev_migration_pending(struct rte_eth_dev *eth_dev)
+{
+	struct rte_pci_device *pci_dev = AVP_DEV_TO_PCI(eth_dev);
+	void *registers = pci_dev->mem_resource[RTE_AVP_PCI_MMIO_BAR].addr;
+	uint32_t value;
+
+	if (registers == NULL)
+		return 0;
+
+	value = AVP_READ32(RTE_PTR_ADD(registers,
+				       RTE_AVP_MIGRATION_STATUS_OFFSET));
+	if (value == RTE_AVP_MIGRATION_DETACHED) {
+		/* migration is in progress; ack it if we have not already */
+		AVP_WRITE32(value,
+			    RTE_PTR_ADD(registers,
+					RTE_AVP_MIGRATION_ACK_OFFSET));
+		return 1;
+	}
+	return 0;
+}
+
+/*
+ * create a AVP device using the supplied device info by first translating it
+ * to guest address space(s).
+ */
+static int
+avp_dev_create(struct rte_pci_device *pci_dev,
+	       struct rte_eth_dev *eth_dev)
+{
+	struct avp_dev *avp = AVP_DEV_PRIVATE_TO_HW(eth_dev->data->dev_private);
+	struct rte_avp_device_info *host_info;
+	struct rte_mem_resource *resource;
+	unsigned int i;
+
+	resource = &pci_dev->mem_resource[RTE_AVP_PCI_DEVICE_BAR];
+	if (resource->addr == NULL) {
+		PMD_DRV_LOG(ERR, "BAR%u is not mapped\n",
+			    RTE_AVP_PCI_DEVICE_BAR);
+		return -EFAULT;
+	}
+	host_info = (struct rte_avp_device_info *)resource->addr;
+
+	if ((host_info->magic != RTE_AVP_DEVICE_MAGIC) ||
+		avp_dev_version_check(host_info->version)) {
+		PMD_DRV_LOG(ERR, "Invalid AVP PCI device, magic 0x%08x version 0x%08x > 0x%08x\n",
+			    host_info->magic, host_info->version,
+			    AVP_DPDK_DRIVER_VERSION);
+		return -EINVAL;
+	}
+
+	PMD_DRV_LOG(DEBUG, "AVP host device is v%u.%u.%u\n",
+		    RTE_AVP_GET_RELEASE_VERSION(host_info->version),
+		    RTE_AVP_GET_MAJOR_VERSION(host_info->version),
+		    RTE_AVP_GET_MINOR_VERSION(host_info->version));
+
+	PMD_DRV_LOG(DEBUG, "AVP host supports %u to %u TX queue(s)\n",
+		    host_info->min_tx_queues, host_info->max_tx_queues);
+	PMD_DRV_LOG(DEBUG, "AVP host supports %u to %u RX queue(s)\n",
+		    host_info->min_rx_queues, host_info->max_rx_queues);
+	PMD_DRV_LOG(DEBUG, "AVP host supports features 0x%08x\n",
+		    host_info->features);
+
+	if (avp->magic != AVP_ETHDEV_MAGIC) {
+		/*
+		 * First time initialization (i.e., not during a VM
+		 * migration)
+		 */
+		memset(avp, 0, sizeof(*avp));
+		avp->magic = AVP_ETHDEV_MAGIC;
+		avp->dev_data = eth_dev->data;
+		avp->port_id = eth_dev->data->port_id;
+		avp->host_mbuf_size = host_info->mbuf_size;
+		avp->host_features = host_info->features;
+		rte_spinlock_init(&avp->lock);
+		memcpy(&avp->ethaddr.addr_bytes[0],
+		       host_info->ethaddr, ETHER_ADDR_LEN);
+		/* adjust max values to not exceed our max */
+		avp->max_tx_queues =
+			RTE_MIN(host_info->max_tx_queues, RTE_AVP_MAX_QUEUES);
+		avp->max_rx_queues =
+			RTE_MIN(host_info->max_rx_queues, RTE_AVP_MAX_QUEUES);
+	} else {
+		/* Re-attaching during migration */
+
+		/* TODO... requires validation of host values */
+		if ((host_info->features & avp->features) != avp->features) {
+			PMD_DRV_LOG(ERR, "AVP host features mismatched; 0x%08x, host=0x%08x\n",
+				    avp->features, host_info->features);
+			/* this should not be possible; continue for now */
+		}
+	}
+
+	/* the device id is allowed to change over migrations */
+	avp->device_id = host_info->device_id;
+
+	/* translate incoming host addresses to guest address space */
+	PMD_DRV_LOG(DEBUG, "AVP first host tx queue at 0x%" PRIx64 "\n",
+		    host_info->tx_phys);
+	PMD_DRV_LOG(DEBUG, "AVP first host alloc queue at 0x%" PRIx64 "\n",
+		    host_info->alloc_phys);
+	for (i = 0; i < avp->max_tx_queues; i++) {
+		avp->tx_q[i] = avp_dev_translate_address(eth_dev,
+			host_info->tx_phys + (i * host_info->tx_size));
+
+		avp->alloc_q[i] = avp_dev_translate_address(eth_dev,
+			host_info->alloc_phys + (i * host_info->alloc_size));
+	}
+
+	PMD_DRV_LOG(DEBUG, "AVP first host rx queue at 0x%" PRIx64 "\n",
+		    host_info->rx_phys);
+	PMD_DRV_LOG(DEBUG, "AVP first host free queue at 0x%" PRIx64 "\n",
+		    host_info->free_phys);
+	for (i = 0; i < avp->max_rx_queues; i++) {
+		avp->rx_q[i] = avp_dev_translate_address(eth_dev,
+			host_info->rx_phys + (i * host_info->rx_size));
+		avp->free_q[i] = avp_dev_translate_address(eth_dev,
+			host_info->free_phys + (i * host_info->free_size));
+	}
+
+	PMD_DRV_LOG(DEBUG, "AVP host request queue at 0x%" PRIx64 "\n",
+		    host_info->req_phys);
+	PMD_DRV_LOG(DEBUG, "AVP host response queue at 0x%" PRIx64 "\n",
+		    host_info->resp_phys);
+	PMD_DRV_LOG(DEBUG, "AVP host sync address at 0x%" PRIx64 "\n",
+		    host_info->sync_phys);
+	PMD_DRV_LOG(DEBUG, "AVP host mbuf address at 0x%" PRIx64 "\n",
+		    host_info->mbuf_phys);
+	avp->req_q = avp_dev_translate_address(eth_dev, host_info->req_phys);
+	avp->resp_q = avp_dev_translate_address(eth_dev, host_info->resp_phys);
+	avp->sync_addr =
+		avp_dev_translate_address(eth_dev, host_info->sync_phys);
+	avp->mbuf_addr =
+		avp_dev_translate_address(eth_dev, host_info->mbuf_phys);
+
+	/*
+	 * store the host mbuf virtual address so that we can calculate
+	 * relative offsets for each mbuf as they are processed
+	 */
+	avp->host_mbuf_addr = host_info->mbuf_va;
+	avp->host_sync_addr = host_info->sync_va;
+
+	/*
+	 * store the maximum packet length that is supported by the host.
+	 */
+	avp->max_rx_pkt_len = host_info->max_rx_pkt_len;
+	PMD_DRV_LOG(DEBUG, "AVP host max receive packet length is %u\n",
+				host_info->max_rx_pkt_len);
+
+	return 0;
+}
+
+/*
  * This function is based on probe() function in avp_pci.c
  * It returns 0 on success.
  */
@@ -164,6 +867,7 @@ struct avp_adapter {
 	struct avp_dev *avp =
 		AVP_DEV_PRIVATE_TO_HW(eth_dev->data->dev_private);
 	struct rte_pci_device *pci_dev;
+	int ret;
 
 	pci_dev = AVP_DEV_TO_PCI(eth_dev);
 
@@ -181,6 +885,34 @@ struct avp_adapter {
 
 	eth_dev->data->dev_flags |= RTE_ETH_DEV_DETACHABLE;
 
+	/* Check current migration status */
+	if (avp_dev_migration_pending(eth_dev)) {
+		PMD_DRV_LOG(ERR, "VM live migration operation in progress\n");
+		return -EBUSY;
+	}
+
+	/* Check BAR resources */
+	ret = avp_dev_check_regions(eth_dev);
+	if (ret < 0) {
+		PMD_DRV_LOG(ERR, "Failed to validate BAR resources, ret=%d\n",
+			    ret);
+		return ret;
+	}
+
+	/* Enable interrupts */
+	ret = avp_dev_setup_interrupts(eth_dev);
+	if (ret < 0) {
+		PMD_DRV_LOG(ERR, "Failed to enable interrupts, ret=%d\n", ret);
+		return ret;
+	}
+
+	/* Handle each subtype */
+	ret = avp_dev_create(pci_dev, eth_dev);
+	if (ret < 0) {
+		PMD_DRV_LOG(ERR, "Failed to create device, ret=%d\n", ret);
+		return ret;
+	}
+
 	/* Allocate memory for storing MAC addresses */
 	eth_dev->data->mac_addrs = rte_zmalloc("avp_ethdev", ETHER_ADDR_LEN, 0);
 	if (eth_dev->data->mac_addrs == NULL) {
@@ -189,6 +921,7 @@ struct avp_adapter {
 		return -ENOMEM;
 	}
 
+	/* Get a mac from device config */
 	ether_addr_copy(&avp->ethaddr, &eth_dev->data->mac_addrs[0]);
 
 	return 0;
-- 
1.8.3.1
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help