+/*
+ * Configure the hardware to generate MSI-X interrupts.
+ * If setting up MSIx fails, try setting up MSI (only 1 interrupt vector
+ * which will be disabled to allow lsc to work).
+ *
+ * Returns 0 on success and -1 otherwise.
+ */
+static int
+vmxnet3_configure_msix(struct rte_eth_dev *dev)
+{
+ struct vmxnet3_hw *hw = dev->data->dev_private;
+ struct rte_intr_handle *intr_handle = dev->intr_handle;
+ uint16_t intr_vector;
+ int i;
+
+ hw->intr.event_intr_idx = 0;
+
+ /* only vfio-pci driver can support interrupt mode. */
+ if (!rte_intr_cap_multiple(intr_handle) ||
+ dev->data->dev_conf.intr_conf.rxq == 0)
+ return -1;
+
+ intr_vector = dev->data->nb_rx_queues;
+ if (intr_vector > VMXNET3_MAX_RX_QUEUES) {
+ PMD_INIT_LOG(ERR, "At most %d intr queues supported",
+ VMXNET3_MAX_RX_QUEUES);
+ return -ENOTSUP;
+ }
+
+ if (rte_intr_efd_enable(intr_handle, intr_vector)) {
+ PMD_INIT_LOG(ERR, "Failed to enable fastpath event fd");
+ return -1;
+ }
+
+ if (rte_intr_dp_is_en(intr_handle) && !intr_handle->intr_vec) {
+ intr_handle->intr_vec =
+ rte_zmalloc("intr_vec",
+ dev->data->nb_rx_queues * sizeof(int), 0);
+ if (intr_handle->intr_vec == NULL) {
+ PMD_INIT_LOG(ERR, "Failed to allocate %d Rx queues intr_vec",
+ dev->data->nb_rx_queues);
+ rte_intr_efd_disable(intr_handle);
+ return -ENOMEM;
+ }
+ }
+
+ if (!rte_intr_allow_others(intr_handle) &&
+ dev->data->dev_conf.intr_conf.lsc != 0) {
+ PMD_INIT_LOG(ERR, "not enough intr vector to support both Rx interrupt and LSC");
+ rte_free(intr_handle->intr_vec);
+ intr_handle->intr_vec = NULL;
+ rte_intr_efd_disable(intr_handle);
+ return -1;
+ }
+
+ /* if we cannot allocate one MSI-X vector per queue, don't enable
+ * interrupt mode.
+ */
+ if (hw->intr.num_intrs != (intr_handle->nb_efd + 1)) {
+ PMD_INIT_LOG(ERR, "Device configured with %d Rx intr vectors, expecting %d",
+ hw->intr.num_intrs, intr_handle->nb_efd + 1);
+ rte_free(intr_handle->intr_vec);
+ intr_handle->intr_vec = NULL;
+ rte_intr_efd_disable(intr_handle);
+ return -1;
+ }
+
+ for (i = 0; i < dev->data->nb_rx_queues; i++)
+ intr_handle->intr_vec[i] = i + 1;
+
+ for (i = 0; i < hw->intr.num_intrs; i++)
+ hw->intr.mod_levels[i] = UPT1_IML_ADAPTIVE;
+
+ PMD_INIT_LOG(INFO, "intr type %u, mode %u, %u vectors allocated",
+ hw->intr.type, hw->intr.mask_mode, hw->intr.num_intrs);
+
+ return 0;
+}
+
+static int
+vmxnet3_dev_setup_memreg(struct rte_eth_dev *dev)
+{
+ struct vmxnet3_hw *hw = dev->data->dev_private;
+ Vmxnet3_DriverShared *shared = hw->shared;
+ Vmxnet3_CmdInfo *cmdInfo;
+ struct rte_mempool *mp[VMXNET3_MAX_RX_QUEUES];
+ uint8_t index[VMXNET3_MAX_RX_QUEUES + VMXNET3_MAX_TX_QUEUES];
+ uint32_t num, i, j, size;
+
+ if (hw->memRegsPA == 0) {
+ const struct rte_memzone *mz;
+
+ size = sizeof(Vmxnet3_MemRegs) +
+ (VMXNET3_MAX_RX_QUEUES + VMXNET3_MAX_TX_QUEUES) *
+ sizeof(Vmxnet3_MemoryRegion);
+
+ mz = gpa_zone_reserve(dev, size, "memRegs", rte_socket_id(), 8,
+ 1);
+ if (mz == NULL) {
+ PMD_INIT_LOG(ERR, "ERROR: Creating memRegs zone");
+ return -ENOMEM;
+ }
+ memset(mz->addr, 0, mz->len);
+ hw->memRegs = mz->addr;
+ hw->memRegsPA = mz->iova;
+ }
+
+ num = hw->num_rx_queues;
+
+ for (i = 0; i < num; i++) {
+ vmxnet3_rx_queue_t *rxq = dev->data->rx_queues[i];
+
+ mp[i] = rxq->mp;
+ index[i] = 1 << i;
+ }
+
+ /*
+ * The same mempool could be used by multiple queues. In such a case,
+ * remove duplicate mempool entries. Only one entry is kept with
+ * bitmask indicating queues that are using this mempool.
+ */
+ for (i = 1; i < num; i++) {
+ for (j = 0; j < i; j++) {
+ if (mp[i] == mp[j]) {
+ mp[i] = NULL;
+ index[j] |= 1 << i;
+ break;
+ }
+ }
+ }
+
+ j = 0;
+ for (i = 0; i < num; i++) {
+ if (mp[i] == NULL)
+ continue;
+
+ Vmxnet3_MemoryRegion *mr = &hw->memRegs->memRegs[j];
+
+ mr->startPA =
+ (uintptr_t)STAILQ_FIRST(&mp[i]->mem_list)->iova;
+ mr->length = STAILQ_FIRST(&mp[i]->mem_list)->len <= INT32_MAX ?
+ STAILQ_FIRST(&mp[i]->mem_list)->len : INT32_MAX;
+ mr->txQueueBits = index[i];
+ mr->rxQueueBits = index[i];
+
+ PMD_INIT_LOG(INFO,
+ "index: %u startPA: %" PRIu64 " length: %u, "
+ "rxBits: %x",
+ j, mr->startPA, mr->length, mr->rxQueueBits);
+ j++;
+ }
+ hw->memRegs->numRegs = j;
+ PMD_INIT_LOG(INFO, "numRegs: %u", j);
+
+ size = sizeof(Vmxnet3_MemRegs) +
+ (j - 1) * sizeof(Vmxnet3_MemoryRegion);
+
+ cmdInfo = &shared->cu.cmdInfo;
+ cmdInfo->varConf.confVer = 1;
+ cmdInfo->varConf.confLen = size;
+ cmdInfo->varConf.confPA = hw->memRegsPA;
+
+ return 0;
+}
+