fm10k: add Rx/Tx single queue start/stop
authorJeff Shaw <jeffrey.b.shaw@intel.com>
Tue, 3 Feb 2015 11:07:01 +0000 (19:07 +0800)
committerChen Jing D(Mark) <jing.d.chen@intel.com>
Tue, 17 Feb 2015 14:25:30 +0000 (15:25 +0100)
1. Add 4 functions fm10k_dev_rx_queue_start,
   fm10k_dev_rx_queue_stop, fm10k_dev_tx_queue_start,
   and fm10k_dev_tx_queue_stop.
2. verify Rx packet buffer alignment is valid.
   Hardware requires specific alignment for Rx packet buffers. At
   least one of the following two conditions must be satisfied.
       1) Address is 512B aligned
       2) Address is 8B aligned and buffer does not cross 4K boundary.

   Alignment is checked by the driver when the Rx queue is reset. It
   is assumed that if an entire descriptor ring can be filled with
   buffers containing valid alignment, then all buffers in that mempool
   have valid address alignment. It is the responsibility of the user
   to ensure all buffers have valid alignment, as it is the user who
   creates the mempool.

   It is assumed the buffer needs only to store a maximum size Ethernet
   frame.

Signed-off-by: Jeff Shaw <jeffrey.b.shaw@intel.com>
Signed-off-by: Chen Jing D(Mark) <jing.d.chen@intel.com>
lib/librte_pmd_fm10k/fm10k.h
lib/librte_pmd_fm10k/fm10k_ethdev.c

index 1468040..b23a3a6 100644 (file)
@@ -221,4 +221,62 @@ static inline uint16_t fifo_remove(struct fifo *fifo)
                fifo->tail = fifo->list;
        return val;
 }
+
+static inline void
+fm10k_pktmbuf_reset(struct rte_mbuf *mb, uint8_t in_port)
+{
+       rte_mbuf_refcnt_set(mb, 1);
+       mb->next = NULL;
+       mb->nb_segs = 1;
+
+       /* enforce 512B alignment on default Rx virtual addresses */
+       mb->data_off = (uint16_t)(RTE_PTR_ALIGN((char *)mb->buf_addr +
+                       RTE_PKTMBUF_HEADROOM, FM10K_RX_DATABUF_ALIGN)
+                       - (char *)mb->buf_addr);
+       mb->port = in_port;
+}
+
+/*
+ * Verify Rx packet buffer alignment is valid.
+ *
+ * Hardware requires specific alignment for Rx packet buffers. At
+ * least one of the following two conditions must be satisfied.
+ *  1. Address is 512B aligned
+ *  2. Address is 8B aligned and buffer does not cross 4K boundary.
+ *
+ * Return 1 if buffer alignment satisfies at least one condition,
+ * otherwise return 0.
+ *
+ * Note: Alignment is checked by the driver when the Rx queue is reset. It
+ *       is assumed that if an entire descriptor ring can be filled with
+ *       buffers containing valid alignment, then all buffers in that mempool
+ *       have valid address alignment. It is the responsibility of the user
+ *       to ensure all buffers have valid alignment, as it is the user who
+ *       creates the mempool.
+ * Note: It is assumed the buffer needs only to store a maximum size Ethernet
+ *       frame.
+ */
+static inline int
+fm10k_addr_alignment_valid(struct rte_mbuf *mb)
+{
+       uint64_t addr = MBUF_DMA_ADDR_DEFAULT(mb);
+       uint64_t boundary1, boundary2;
+
+       /* 512B aligned? */
+       if (RTE_ALIGN(addr, 512) == addr)
+               return 1;
+
+       /* 8B aligned, and max Ethernet frame would not cross a 4KB boundary? */
+       if (RTE_ALIGN(addr, 8) == addr) {
+               boundary1 = RTE_ALIGN_FLOOR(addr, 4096);
+               boundary2 = RTE_ALIGN_FLOOR(addr + ETHER_MAX_VLAN_FRAME_LEN,
+                                               4096);
+               if (boundary1 == boundary2)
+                       return 1;
+       }
+
+       PMD_INIT_LOG(ERR, "Error: Invalid buffer alignment!");
+
+       return 0;
+}
 #endif
index 47bfe59..0fb7b95 100644 (file)
@@ -68,6 +68,43 @@ fm10k_mbx_unlock(struct fm10k_hw *hw)
        rte_spinlock_unlock(FM10K_DEV_PRIVATE_TO_MBXLOCK(hw->back));
 }
 
+/*
+ * reset queue to initial state, allocate software buffers used when starting
+ * device.
+ * return 0 on success
+ * return -ENOMEM if buffers cannot be allocated
+ * return -EINVAL if buffers do not satisfy alignment condition
+ */
+static inline int
+rx_queue_reset(struct fm10k_rx_queue *q)
+{
+       uint64_t dma_addr;
+       int i, diag;
+       PMD_INIT_FUNC_TRACE();
+
+       diag = rte_mempool_get_bulk(q->mp, (void **)q->sw_ring, q->nb_desc);
+       if (diag != 0)
+               return -ENOMEM;
+
+       for (i = 0; i < q->nb_desc; ++i) {
+               fm10k_pktmbuf_reset(q->sw_ring[i], q->port_id);
+               if (!fm10k_addr_alignment_valid(q->sw_ring[i])) {
+                       rte_mempool_put_bulk(q->mp, (void **)q->sw_ring,
+                                               q->nb_desc);
+                       return -EINVAL;
+               }
+               dma_addr = MBUF_DMA_ADDR_DEFAULT(q->sw_ring[i]);
+               q->hw_ring[i].q.pkt_addr = dma_addr;
+               q->hw_ring[i].q.hdr_addr = dma_addr;
+       }
+
+       q->next_dd = 0;
+       q->next_alloc = 0;
+       q->next_trigger = q->alloc_thresh - 1;
+       FM10K_PCI_REG_WRITE(q->tail_ptr, q->nb_desc - 1);
+       return 0;
+}
+
 /*
  * clean queue, descriptor rings, free software buffers used when stopping
  * device.
@@ -108,6 +145,49 @@ rx_queue_free(struct fm10k_rx_queue *q)
        }
 }
 
+/*
+ * disable RX queue, wait unitl HW finished necessary flush operation
+ */
+static inline int
+rx_queue_disable(struct fm10k_hw *hw, uint16_t qnum)
+{
+       uint32_t reg, i;
+
+       reg = FM10K_READ_REG(hw, FM10K_RXQCTL(qnum));
+       FM10K_WRITE_REG(hw, FM10K_RXQCTL(qnum),
+                       reg & ~FM10K_RXQCTL_ENABLE);
+
+       /* Wait 100us at most */
+       for (i = 0; i < FM10K_QUEUE_DISABLE_TIMEOUT; i++) {
+               rte_delay_us(1);
+               reg = FM10K_READ_REG(hw, FM10K_RXQCTL(i));
+               if (!(reg & FM10K_RXQCTL_ENABLE))
+                       break;
+       }
+
+       if (i == FM10K_QUEUE_DISABLE_TIMEOUT)
+               return -1;
+
+       return 0;
+}
+
+/*
+ * reset queue to initial state, allocate software buffers used when starting
+ * device
+ */
+static inline void
+tx_queue_reset(struct fm10k_tx_queue *q)
+{
+       PMD_INIT_FUNC_TRACE();
+       q->last_free = 0;
+       q->next_free = 0;
+       q->nb_used = 0;
+       q->nb_free = q->nb_desc - 1;
+       q->free_trigger = q->nb_free - q->free_thresh;
+       fifo_reset(&q->rs_tracker, (q->nb_desc + 1) / q->rs_thresh);
+       FM10K_PCI_REG_WRITE(q->tail_ptr, 0);
+}
+
 /*
  * clean queue, descriptor rings, free software buffers used when stopping
  * device
@@ -150,6 +230,32 @@ tx_queue_free(struct fm10k_tx_queue *q)
        }
 }
 
+/*
+ * disable TX queue, wait unitl HW finished necessary flush operation
+ */
+static inline int
+tx_queue_disable(struct fm10k_hw *hw, uint16_t qnum)
+{
+       uint32_t reg, i;
+
+       reg = FM10K_READ_REG(hw, FM10K_TXDCTL(qnum));
+       FM10K_WRITE_REG(hw, FM10K_TXDCTL(qnum),
+                       reg & ~FM10K_TXDCTL_ENABLE);
+
+       /* Wait 100us at most */
+       for (i = 0; i < FM10K_QUEUE_DISABLE_TIMEOUT; i++) {
+               rte_delay_us(1);
+               reg = FM10K_READ_REG(hw, FM10K_TXDCTL(i));
+               if (!(reg & FM10K_TXDCTL_ENABLE))
+                       break;
+       }
+
+       if (i == FM10K_QUEUE_DISABLE_TIMEOUT)
+               return -1;
+
+       return 0;
+}
+
 static int
 fm10k_dev_configure(struct rte_eth_dev *dev)
 {
@@ -161,6 +267,118 @@ fm10k_dev_configure(struct rte_eth_dev *dev)
        return 0;
 }
 
+static int
+fm10k_dev_rx_queue_start(struct rte_eth_dev *dev, uint16_t rx_queue_id)
+{
+       struct fm10k_hw *hw = FM10K_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+       int err = -1;
+       uint32_t reg;
+       struct fm10k_rx_queue *rxq;
+
+       PMD_INIT_FUNC_TRACE();
+
+       if (rx_queue_id < dev->data->nb_rx_queues) {
+               rxq = dev->data->rx_queues[rx_queue_id];
+               err = rx_queue_reset(rxq);
+               if (err == -ENOMEM) {
+                       PMD_INIT_LOG(ERR, "Failed to alloc memory : %d", err);
+                       return err;
+               } else if (err == -EINVAL) {
+                       PMD_INIT_LOG(ERR, "Invalid buffer address alignment :"
+                               " %d", err);
+                       return err;
+               }
+
+               /* Setup the HW Rx Head and Tail Descriptor Pointers
+                * Note: this must be done AFTER the queue is enabled on real
+                * hardware, but BEFORE the queue is enabled when using the
+                * emulation platform. Do it in both places for now and remove
+                * this comment and the following two register writes when the
+                * emulation platform is no longer being used.
+                */
+               FM10K_WRITE_REG(hw, FM10K_RDH(rx_queue_id), 0);
+               FM10K_WRITE_REG(hw, FM10K_RDT(rx_queue_id), rxq->nb_desc - 1);
+
+               /* Set PF ownership flag for PF devices */
+               reg = FM10K_READ_REG(hw, FM10K_RXQCTL(rx_queue_id));
+               if (hw->mac.type == fm10k_mac_pf)
+                       reg |= FM10K_RXQCTL_PF;
+               reg |= FM10K_RXQCTL_ENABLE;
+               /* enable RX queue */
+               FM10K_WRITE_REG(hw, FM10K_RXQCTL(rx_queue_id), reg);
+               FM10K_WRITE_FLUSH(hw);
+
+               /* Setup the HW Rx Head and Tail Descriptor Pointers
+                * Note: this must be done AFTER the queue is enabled
+                */
+               FM10K_WRITE_REG(hw, FM10K_RDH(rx_queue_id), 0);
+               FM10K_WRITE_REG(hw, FM10K_RDT(rx_queue_id), rxq->nb_desc - 1);
+       }
+
+       return err;
+}
+
+static int
+fm10k_dev_rx_queue_stop(struct rte_eth_dev *dev, uint16_t rx_queue_id)
+{
+       struct fm10k_hw *hw = FM10K_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+
+       PMD_INIT_FUNC_TRACE();
+
+       if (rx_queue_id < dev->data->nb_rx_queues) {
+               /* Disable RX queue */
+               rx_queue_disable(hw, rx_queue_id);
+
+               /* Free mbuf and clean HW ring */
+               rx_queue_clean(dev->data->rx_queues[rx_queue_id]);
+       }
+
+       return 0;
+}
+
+static int
+fm10k_dev_tx_queue_start(struct rte_eth_dev *dev, uint16_t tx_queue_id)
+{
+       struct fm10k_hw *hw = FM10K_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+       /** @todo - this should be defined in the shared code */
+#define FM10K_TXDCTL_WRITE_BACK_MIN_DELAY      0x00010000
+       uint32_t txdctl = FM10K_TXDCTL_WRITE_BACK_MIN_DELAY;
+       int err = 0;
+
+       PMD_INIT_FUNC_TRACE();
+
+       if (tx_queue_id < dev->data->nb_tx_queues) {
+               tx_queue_reset(dev->data->tx_queues[tx_queue_id]);
+
+               /* reset head and tail pointers */
+               FM10K_WRITE_REG(hw, FM10K_TDH(tx_queue_id), 0);
+               FM10K_WRITE_REG(hw, FM10K_TDT(tx_queue_id), 0);
+
+               /* enable TX queue */
+               FM10K_WRITE_REG(hw, FM10K_TXDCTL(tx_queue_id),
+                                       FM10K_TXDCTL_ENABLE | txdctl);
+               FM10K_WRITE_FLUSH(hw);
+       } else
+               err = -1;
+
+       return err;
+}
+
+static int
+fm10k_dev_tx_queue_stop(struct rte_eth_dev *dev, uint16_t tx_queue_id)
+{
+       struct fm10k_hw *hw = FM10K_DEV_PRIVATE_TO_HW(dev->data->dev_private);
+
+       PMD_INIT_FUNC_TRACE();
+
+       if (tx_queue_id < dev->data->nb_tx_queues) {
+               tx_queue_disable(hw, tx_queue_id);
+               tx_queue_clean(dev->data->tx_queues[tx_queue_id]);
+       }
+
+       return 0;
+}
+
 static int
 fm10k_link_update(struct rte_eth_dev *dev,
        __rte_unused int wait_to_complete)
@@ -780,6 +998,10 @@ static struct eth_dev_ops fm10k_eth_dev_ops = {
        .stats_reset            = fm10k_stats_reset,
        .link_update            = fm10k_link_update,
        .dev_infos_get          = fm10k_dev_infos_get,
+       .rx_queue_start         = fm10k_dev_rx_queue_start,
+       .rx_queue_stop          = fm10k_dev_rx_queue_stop,
+       .tx_queue_start         = fm10k_dev_tx_queue_start,
+       .tx_queue_stop          = fm10k_dev_tx_queue_stop,
        .rx_queue_setup         = fm10k_rx_queue_setup,
        .rx_queue_release       = fm10k_rx_queue_release,
        .tx_queue_setup         = fm10k_tx_queue_setup,