Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Linux/drivers/net/ethernet/microchip/lan966x/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 23 kB image not shown  

Quelle  lan966x_fdma.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0+

#include <linux/bpf.h>
#include <linux/filter.h>
#include <net/page_pool/helpers.h>

#include "lan966x_main.h"

static int lan966x_fdma_rx_dataptr_cb(struct fdma *fdma, int dcb, int db,
          u64 *dataptr)
{
 struct lan966x *lan966x = (struct lan966x *)fdma->priv;
 struct lan966x_rx *rx = &lan966x->rx;
 struct page *page;

 page = page_pool_dev_alloc_pages(rx->page_pool);
 if (unlikely(!page))
  return -ENOMEM;

 rx->page[dcb][db] = page;
 *dataptr = page_pool_get_dma_addr(page) + XDP_PACKET_HEADROOM;

 return 0;
}

static int lan966x_fdma_tx_dataptr_cb(struct fdma *fdma, int dcb, int db,
          u64 *dataptr)
{
 struct lan966x *lan966x = (struct lan966x *)fdma->priv;

 *dataptr = lan966x->tx.dcbs_buf[dcb].dma_addr;

 return 0;
}

static int lan966x_fdma_xdp_tx_dataptr_cb(struct fdma *fdma, int dcb, int db,
       u64 *dataptr)
{
 struct lan966x *lan966x = (struct lan966x *)fdma->priv;

 *dataptr = lan966x->tx.dcbs_buf[dcb].dma_addr + XDP_PACKET_HEADROOM;

 return 0;
}

static int lan966x_fdma_channel_active(struct lan966x *lan966x)
{
 return lan_rd(lan966x, FDMA_CH_ACTIVE);
}

static void lan966x_fdma_rx_free_pages(struct lan966x_rx *rx)
{
 struct fdma *fdma = &rx->fdma;
 int i, j;

 for (i = 0; i < fdma->n_dcbs; ++i) {
  for (j = 0; j < fdma->n_dbs; ++j)
   page_pool_put_full_page(rx->page_pool,
      rx->page[i][j], false);
 }
}

static void lan966x_fdma_rx_free_page(struct lan966x_rx *rx)
{
 struct fdma *fdma = &rx->fdma;
 struct page *page;

 page = rx->page[fdma->dcb_index][fdma->db_index];
 if (unlikely(!page))
  return;

 page_pool_recycle_direct(rx->page_pool, page);
}

static int lan966x_fdma_rx_alloc_page_pool(struct lan966x_rx *rx)
{
 struct lan966x *lan966x = rx->lan966x;
 struct page_pool_params pp_params = {
  .order = rx->page_order,
  .flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV,
  .pool_size = rx->fdma.n_dcbs,
  .nid = NUMA_NO_NODE,
  .dev = lan966x->dev,
  .dma_dir = DMA_FROM_DEVICE,
  .offset = XDP_PACKET_HEADROOM,
  .max_len = rx->max_mtu -
      SKB_DATA_ALIGN(sizeof(struct skb_shared_info)),
 };

 if (lan966x_xdp_present(lan966x))
  pp_params.dma_dir = DMA_BIDIRECTIONAL;

 rx->page_pool = page_pool_create(&pp_params);

 for (int i = 0; i < lan966x->num_phys_ports; ++i) {
  struct lan966x_port *port;

  if (!lan966x->ports[i])
   continue;

  port = lan966x->ports[i];
  xdp_rxq_info_unreg_mem_model(&port->xdp_rxq);
  xdp_rxq_info_reg_mem_model(&port->xdp_rxq, MEM_TYPE_PAGE_POOL,
        rx->page_pool);
 }

 return PTR_ERR_OR_ZERO(rx->page_pool);
}

static int lan966x_fdma_rx_alloc(struct lan966x_rx *rx)
{
 struct lan966x *lan966x = rx->lan966x;
 struct fdma *fdma = &rx->fdma;
 int err;

 if (lan966x_fdma_rx_alloc_page_pool(rx))
  return PTR_ERR(rx->page_pool);

 err = fdma_alloc_coherent(lan966x->dev, fdma);
 if (err)
  return err;

 fdma_dcbs_init(fdma, FDMA_DCB_INFO_DATAL(fdma->db_size),
         FDMA_DCB_STATUS_INTR);

 return 0;
}

static void lan966x_fdma_rx_start(struct lan966x_rx *rx)
{
 struct lan966x *lan966x = rx->lan966x;
 struct fdma *fdma = &rx->fdma;
 u32 mask;

 /* When activating a channel, first is required to write the first DCB
 * address and then to activate it
 */

 lan_wr(lower_32_bits((u64)fdma->dma), lan966x,
        FDMA_DCB_LLP(fdma->channel_id));
 lan_wr(upper_32_bits((u64)fdma->dma), lan966x,
        FDMA_DCB_LLP1(fdma->channel_id));

 lan_wr(FDMA_CH_CFG_CH_DCB_DB_CNT_SET(fdma->n_dbs) |
        FDMA_CH_CFG_CH_INTR_DB_EOF_ONLY_SET(1) |
        FDMA_CH_CFG_CH_INJ_PORT_SET(0) |
        FDMA_CH_CFG_CH_MEM_SET(1),
        lan966x, FDMA_CH_CFG(fdma->channel_id));

 /* Start fdma */
 lan_rmw(FDMA_PORT_CTRL_XTR_STOP_SET(0),
  FDMA_PORT_CTRL_XTR_STOP,
  lan966x, FDMA_PORT_CTRL(0));

 /* Enable interrupts */
 mask = lan_rd(lan966x, FDMA_INTR_DB_ENA);
 mask = FDMA_INTR_DB_ENA_INTR_DB_ENA_GET(mask);
 mask |= BIT(fdma->channel_id);
 lan_rmw(FDMA_INTR_DB_ENA_INTR_DB_ENA_SET(mask),
  FDMA_INTR_DB_ENA_INTR_DB_ENA,
  lan966x, FDMA_INTR_DB_ENA);

 /* Activate the channel */
 lan_rmw(FDMA_CH_ACTIVATE_CH_ACTIVATE_SET(BIT(fdma->channel_id)),
  FDMA_CH_ACTIVATE_CH_ACTIVATE,
  lan966x, FDMA_CH_ACTIVATE);
}

static void lan966x_fdma_rx_disable(struct lan966x_rx *rx)
{
 struct lan966x *lan966x = rx->lan966x;
 struct fdma *fdma = &rx->fdma;
 u32 val;

 /* Disable the channel */
 lan_rmw(FDMA_CH_DISABLE_CH_DISABLE_SET(BIT(fdma->channel_id)),
  FDMA_CH_DISABLE_CH_DISABLE,
  lan966x, FDMA_CH_DISABLE);

 readx_poll_timeout_atomic(lan966x_fdma_channel_active, lan966x,
      val, !(val & BIT(fdma->channel_id)),
      READL_SLEEP_US, READL_TIMEOUT_US);

 lan_rmw(FDMA_CH_DB_DISCARD_DB_DISCARD_SET(BIT(fdma->channel_id)),
  FDMA_CH_DB_DISCARD_DB_DISCARD,
  lan966x, FDMA_CH_DB_DISCARD);
}

static void lan966x_fdma_rx_reload(struct lan966x_rx *rx)
{
 struct lan966x *lan966x = rx->lan966x;

 lan_rmw(FDMA_CH_RELOAD_CH_RELOAD_SET(BIT(rx->fdma.channel_id)),
  FDMA_CH_RELOAD_CH_RELOAD,
  lan966x, FDMA_CH_RELOAD);
}

static int lan966x_fdma_tx_alloc(struct lan966x_tx *tx)
{
 struct lan966x *lan966x = tx->lan966x;
 struct fdma *fdma = &tx->fdma;
 int err;

 tx->dcbs_buf = kcalloc(fdma->n_dcbs, sizeof(struct lan966x_tx_dcb_buf),
          GFP_KERNEL);
 if (!tx->dcbs_buf)
  return -ENOMEM;

 err = fdma_alloc_coherent(lan966x->dev, fdma);
 if (err)
  goto out;

 fdma_dcbs_init(fdma, 0, 0);

 return 0;

out:
 kfree(tx->dcbs_buf);
 return -ENOMEM;
}

static void lan966x_fdma_tx_free(struct lan966x_tx *tx)
{
 struct lan966x *lan966x = tx->lan966x;

 kfree(tx->dcbs_buf);
 fdma_free_coherent(lan966x->dev, &tx->fdma);
}

static void lan966x_fdma_tx_activate(struct lan966x_tx *tx)
{
 struct lan966x *lan966x = tx->lan966x;
 struct fdma *fdma = &tx->fdma;
 u32 mask;

 /* When activating a channel, first is required to write the first DCB
 * address and then to activate it
 */

 lan_wr(lower_32_bits((u64)fdma->dma), lan966x,
        FDMA_DCB_LLP(fdma->channel_id));
 lan_wr(upper_32_bits((u64)fdma->dma), lan966x,
        FDMA_DCB_LLP1(fdma->channel_id));

 lan_wr(FDMA_CH_CFG_CH_DCB_DB_CNT_SET(fdma->n_dbs) |
        FDMA_CH_CFG_CH_INTR_DB_EOF_ONLY_SET(1) |
        FDMA_CH_CFG_CH_INJ_PORT_SET(0) |
        FDMA_CH_CFG_CH_MEM_SET(1),
        lan966x, FDMA_CH_CFG(fdma->channel_id));

 /* Start fdma */
 lan_rmw(FDMA_PORT_CTRL_INJ_STOP_SET(0),
  FDMA_PORT_CTRL_INJ_STOP,
  lan966x, FDMA_PORT_CTRL(0));

 /* Enable interrupts */
 mask = lan_rd(lan966x, FDMA_INTR_DB_ENA);
 mask = FDMA_INTR_DB_ENA_INTR_DB_ENA_GET(mask);
 mask |= BIT(fdma->channel_id);
 lan_rmw(FDMA_INTR_DB_ENA_INTR_DB_ENA_SET(mask),
  FDMA_INTR_DB_ENA_INTR_DB_ENA,
  lan966x, FDMA_INTR_DB_ENA);

 /* Activate the channel */
 lan_rmw(FDMA_CH_ACTIVATE_CH_ACTIVATE_SET(BIT(fdma->channel_id)),
  FDMA_CH_ACTIVATE_CH_ACTIVATE,
  lan966x, FDMA_CH_ACTIVATE);
}

static void lan966x_fdma_tx_disable(struct lan966x_tx *tx)
{
 struct lan966x *lan966x = tx->lan966x;
 struct fdma *fdma = &tx->fdma;
 u32 val;

 /* Disable the channel */
 lan_rmw(FDMA_CH_DISABLE_CH_DISABLE_SET(BIT(fdma->channel_id)),
  FDMA_CH_DISABLE_CH_DISABLE,
  lan966x, FDMA_CH_DISABLE);

 readx_poll_timeout_atomic(lan966x_fdma_channel_active, lan966x,
      val, !(val & BIT(fdma->channel_id)),
      READL_SLEEP_US, READL_TIMEOUT_US);

 lan_rmw(FDMA_CH_DB_DISCARD_DB_DISCARD_SET(BIT(fdma->channel_id)),
  FDMA_CH_DB_DISCARD_DB_DISCARD,
  lan966x, FDMA_CH_DB_DISCARD);

 tx->activated = false;
}

static void lan966x_fdma_tx_reload(struct lan966x_tx *tx)
{
 struct lan966x *lan966x = tx->lan966x;

 /* Write the registers to reload the channel */
 lan_rmw(FDMA_CH_RELOAD_CH_RELOAD_SET(BIT(tx->fdma.channel_id)),
  FDMA_CH_RELOAD_CH_RELOAD,
  lan966x, FDMA_CH_RELOAD);
}

static void lan966x_fdma_wakeup_netdev(struct lan966x *lan966x)
{
 struct lan966x_port *port;
 int i;

 for (i = 0; i < lan966x->num_phys_ports; ++i) {
  port = lan966x->ports[i];
  if (!port)
   continue;

  if (netif_queue_stopped(port->dev))
   netif_wake_queue(port->dev);
 }
}

static void lan966x_fdma_stop_netdev(struct lan966x *lan966x)
{
 struct lan966x_port *port;
 int i;

 for (i = 0; i < lan966x->num_phys_ports; ++i) {
  port = lan966x->ports[i];
  if (!port)
   continue;

  netif_stop_queue(port->dev);
 }
}

static void lan966x_fdma_tx_clear_buf(struct lan966x *lan966x, int weight)
{
 struct lan966x_tx *tx = &lan966x->tx;
 struct lan966x_rx *rx = &lan966x->rx;
 struct lan966x_tx_dcb_buf *dcb_buf;
 struct fdma *fdma = &tx->fdma;
 struct xdp_frame_bulk bq;
 unsigned long flags;
 bool clear = false;
 struct fdma_db *db;
 int i;

 xdp_frame_bulk_init(&bq);

 spin_lock_irqsave(&lan966x->tx_lock, flags);
 for (i = 0; i < fdma->n_dcbs; ++i) {
  dcb_buf = &tx->dcbs_buf[i];

  if (!dcb_buf->used)
   continue;

  db = fdma_db_get(fdma, i, 0);
  if (!fdma_db_is_done(db))
   continue;

  dcb_buf->dev->stats.tx_packets++;
  dcb_buf->dev->stats.tx_bytes += dcb_buf->len;

  dcb_buf->used = false;
  if (dcb_buf->use_skb) {
   dma_unmap_single(lan966x->dev,
      dcb_buf->dma_addr,
      dcb_buf->len,
      DMA_TO_DEVICE);

   if (!dcb_buf->ptp)
    napi_consume_skb(dcb_buf->data.skb, weight);
  } else {
   if (dcb_buf->xdp_ndo)
    dma_unmap_single(lan966x->dev,
       dcb_buf->dma_addr,
       dcb_buf->len,
       DMA_TO_DEVICE);

   if (dcb_buf->xdp_ndo)
    xdp_return_frame_bulk(dcb_buf->data.xdpf, &bq);
   else
    page_pool_recycle_direct(rx->page_pool,
        dcb_buf->data.page);
  }

  clear = true;
 }

 xdp_flush_frame_bulk(&bq);

 if (clear)
  lan966x_fdma_wakeup_netdev(lan966x);

 spin_unlock_irqrestore(&lan966x->tx_lock, flags);
}

static int lan966x_fdma_rx_check_frame(struct lan966x_rx *rx, u64 *src_port)
{
 struct lan966x *lan966x = rx->lan966x;
 struct fdma *fdma = &rx->fdma;
 struct lan966x_port *port;
 struct fdma_db *db;
 struct page *page;

 db = fdma_db_next_get(fdma);
 page = rx->page[fdma->dcb_index][fdma->db_index];
 if (unlikely(!page))
  return FDMA_ERROR;

 dma_sync_single_for_cpu(lan966x->dev,
    (dma_addr_t)db->dataptr + XDP_PACKET_HEADROOM,
    FDMA_DCB_STATUS_BLOCKL(db->status),
    DMA_FROM_DEVICE);

 lan966x_ifh_get_src_port(page_address(page) + XDP_PACKET_HEADROOM,
     src_port);
 if (WARN_ON(*src_port >= lan966x->num_phys_ports))
  return FDMA_ERROR;

 port = lan966x->ports[*src_port];
 if (!lan966x_xdp_port_present(port))
  return FDMA_PASS;

 return lan966x_xdp_run(port, page, FDMA_DCB_STATUS_BLOCKL(db->status));
}

static struct sk_buff *lan966x_fdma_rx_get_frame(struct lan966x_rx *rx,
       u64 src_port)
{
 struct lan966x *lan966x = rx->lan966x;
 struct fdma *fdma = &rx->fdma;
 struct sk_buff *skb;
 struct fdma_db *db;
 struct page *page;
 u64 timestamp;

 /* Get the received frame and unmap it */
 db = fdma_db_next_get(fdma);
 page = rx->page[fdma->dcb_index][fdma->db_index];

 skb = build_skb(page_address(page), fdma->db_size);
 if (unlikely(!skb))
  goto free_page;

 skb_mark_for_recycle(skb);

 skb_reserve(skb, XDP_PACKET_HEADROOM);
 skb_put(skb, FDMA_DCB_STATUS_BLOCKL(db->status));

 lan966x_ifh_get_timestamp(skb->data, ×tamp);

 skb->dev = lan966x->ports[src_port]->dev;
 skb_pull(skb, IFH_LEN_BYTES);

 if (likely(!(skb->dev->features & NETIF_F_RXFCS)))
  skb_trim(skb, skb->len - ETH_FCS_LEN);

 lan966x_ptp_rxtstamp(lan966x, skb, src_port, timestamp);
 skb->protocol = eth_type_trans(skb, skb->dev);

 if (lan966x->bridge_mask & BIT(src_port)) {
  skb->offload_fwd_mark = 1;

  skb_reset_network_header(skb);
  if (!lan966x_hw_offload(lan966x, src_port, skb))
   skb->offload_fwd_mark = 0;
 }

 skb->dev->stats.rx_bytes += skb->len;
 skb->dev->stats.rx_packets++;

 return skb;

free_page:
 page_pool_recycle_direct(rx->page_pool, page);

 return NULL;
}

static int lan966x_fdma_napi_poll(struct napi_struct *napi, int weight)
{
 struct lan966x *lan966x = container_of(napi, struct lan966x, napi);
 struct lan966x_rx *rx = &lan966x->rx;
 int old_dcb, dcb_reload, counter = 0;
 struct fdma *fdma = &rx->fdma;
 bool redirect = false;
 struct sk_buff *skb;
 u64 src_port;

 dcb_reload = fdma->dcb_index;

 lan966x_fdma_tx_clear_buf(lan966x, weight);

 /* Get all received skb */
 while (counter < weight) {
  if (!fdma_has_frames(fdma))
   break;

  counter++;

  switch (lan966x_fdma_rx_check_frame(rx, &src_port)) {
  case FDMA_PASS:
   break;
  case FDMA_ERROR:
   lan966x_fdma_rx_free_page(rx);
   fdma_dcb_advance(fdma);
   goto allocate_new;
  case FDMA_REDIRECT:
   redirect = true;
   fallthrough;
  case FDMA_TX:
   fdma_dcb_advance(fdma);
   continue;
  case FDMA_DROP:
   lan966x_fdma_rx_free_page(rx);
   fdma_dcb_advance(fdma);
   continue;
  }

  skb = lan966x_fdma_rx_get_frame(rx, src_port);
  fdma_dcb_advance(fdma);
  if (!skb)
   goto allocate_new;

  napi_gro_receive(&lan966x->napi, skb);
 }

allocate_new:
 /* Allocate new pages and map them */
 while (dcb_reload != fdma->dcb_index) {
  old_dcb = dcb_reload;
  dcb_reload++;
  dcb_reload &= fdma->n_dcbs - 1;

  fdma_dcb_add(fdma, old_dcb, FDMA_DCB_INFO_DATAL(fdma->db_size),
        FDMA_DCB_STATUS_INTR);

  lan966x_fdma_rx_reload(rx);
 }

 if (redirect)
  xdp_do_flush();

 if (counter < weight && napi_complete_done(napi, counter))
  lan_wr(0xff, lan966x, FDMA_INTR_DB_ENA);

 return counter;
}

irqreturn_t lan966x_fdma_irq_handler(int irq, void *args)
{
 struct lan966x *lan966x = args;
 u32 db, err, err_type;

 db = lan_rd(lan966x, FDMA_INTR_DB);
 err = lan_rd(lan966x, FDMA_INTR_ERR);

 if (db) {
  lan_wr(0, lan966x, FDMA_INTR_DB_ENA);
  lan_wr(db, lan966x, FDMA_INTR_DB);

  napi_schedule(&lan966x->napi);
 }

 if (err) {
  err_type = lan_rd(lan966x, FDMA_ERRORS);

  WARN(1, "Unexpected error: %d, error_type: %d\n", err, err_type);

  lan_wr(err, lan966x, FDMA_INTR_ERR);
  lan_wr(err_type, lan966x, FDMA_ERRORS);
 }

 return IRQ_HANDLED;
}

static int lan966x_fdma_get_next_dcb(struct lan966x_tx *tx)
{
 struct lan966x_tx_dcb_buf *dcb_buf;
 struct fdma *fdma = &tx->fdma;
 int i;

 for (i = 0; i < fdma->n_dcbs; ++i) {
  dcb_buf = &tx->dcbs_buf[i];
  if (!dcb_buf->used &&
      !fdma_is_last(&tx->fdma, &tx->fdma.dcbs[i]))
   return i;
 }

 return -1;
}

static void lan966x_fdma_tx_start(struct lan966x_tx *tx)
{
 struct lan966x *lan966x = tx->lan966x;

 if (likely(lan966x->tx.activated)) {
  lan966x_fdma_tx_reload(tx);
 } else {
  /* Because it is first time, then just activate */
  lan966x->tx.activated = true;
  lan966x_fdma_tx_activate(tx);
 }
}

int lan966x_fdma_xmit_xdpf(struct lan966x_port *port, void *ptr, u32 len)
{
 struct lan966x *lan966x = port->lan966x;
 struct lan966x_tx_dcb_buf *next_dcb_buf;
 struct lan966x_tx *tx = &lan966x->tx;
 struct xdp_frame *xdpf;
 dma_addr_t dma_addr;
 struct page *page;
 int next_to_use;
 __be32 *ifh;
 int ret = 0;

 spin_lock(&lan966x->tx_lock);

 /* Get next index */
 next_to_use = lan966x_fdma_get_next_dcb(tx);
 if (next_to_use < 0) {
  netif_stop_queue(port->dev);
  ret = NETDEV_TX_BUSY;
  goto out;
 }

 /* Get the next buffer */
 next_dcb_buf = &tx->dcbs_buf[next_to_use];

 /* Generate new IFH */
 if (!len) {
  xdpf = ptr;

  if (xdpf->headroom < IFH_LEN_BYTES) {
   ret = NETDEV_TX_OK;
   goto out;
  }

  ifh = xdpf->data - IFH_LEN_BYTES;
  memset(ifh, 0x0, sizeof(__be32) * IFH_LEN);
  lan966x_ifh_set_bypass(ifh, 1);
  lan966x_ifh_set_port(ifh, BIT_ULL(port->chip_port));

  dma_addr = dma_map_single(lan966x->dev,
       xdpf->data - IFH_LEN_BYTES,
       xdpf->len + IFH_LEN_BYTES,
       DMA_TO_DEVICE);
  if (dma_mapping_error(lan966x->dev, dma_addr)) {
   ret = NETDEV_TX_OK;
   goto out;
  }

  next_dcb_buf->data.xdpf = xdpf;
  next_dcb_buf->len = xdpf->len + IFH_LEN_BYTES;
 } else {
  page = ptr;

  ifh = page_address(page) + XDP_PACKET_HEADROOM;
  memset(ifh, 0x0, sizeof(__be32) * IFH_LEN);
  lan966x_ifh_set_bypass(ifh, 1);
  lan966x_ifh_set_port(ifh, BIT_ULL(port->chip_port));

  dma_addr = page_pool_get_dma_addr(page);
  dma_sync_single_for_device(lan966x->dev,
        dma_addr + XDP_PACKET_HEADROOM,
        len + IFH_LEN_BYTES,
        DMA_TO_DEVICE);

  next_dcb_buf->data.page = page;
  next_dcb_buf->len = len + IFH_LEN_BYTES;
 }

 /* Fill up the buffer */
 next_dcb_buf->use_skb = false;
 next_dcb_buf->xdp_ndo = !len;
 next_dcb_buf->dma_addr = dma_addr;
 next_dcb_buf->used = true;
 next_dcb_buf->ptp = false;
 next_dcb_buf->dev = port->dev;

 __fdma_dcb_add(&tx->fdma,
         next_to_use,
         0,
         FDMA_DCB_STATUS_INTR |
         FDMA_DCB_STATUS_SOF |
         FDMA_DCB_STATUS_EOF |
         FDMA_DCB_STATUS_BLOCKO(0) |
         FDMA_DCB_STATUS_BLOCKL(next_dcb_buf->len),
         &fdma_nextptr_cb,
         &lan966x_fdma_xdp_tx_dataptr_cb);

 /* Start the transmission */
 lan966x_fdma_tx_start(tx);

out:
 spin_unlock(&lan966x->tx_lock);

 return ret;
}

int lan966x_fdma_xmit(struct sk_buff *skb, __be32 *ifh, struct net_device *dev)
{
 struct lan966x_port *port = netdev_priv(dev);
 struct lan966x *lan966x = port->lan966x;
 struct lan966x_tx_dcb_buf *next_dcb_buf;
 struct lan966x_tx *tx = &lan966x->tx;
 int needed_headroom;
 int needed_tailroom;
 dma_addr_t dma_addr;
 int next_to_use;
 int err;

 /* Get next index */
 next_to_use = lan966x_fdma_get_next_dcb(tx);
 if (next_to_use < 0) {
  netif_stop_queue(dev);
  return NETDEV_TX_BUSY;
 }

 if (skb_put_padto(skb, ETH_ZLEN)) {
  dev->stats.tx_dropped++;
  return NETDEV_TX_OK;
 }

 /* skb processing */
 needed_headroom = max_t(int, IFH_LEN_BYTES - skb_headroom(skb), 0);
 needed_tailroom = max_t(int, ETH_FCS_LEN - skb_tailroom(skb), 0);
 if (needed_headroom || needed_tailroom || skb_header_cloned(skb)) {
  err = pskb_expand_head(skb, needed_headroom, needed_tailroom,
           GFP_ATOMIC);
  if (unlikely(err)) {
   dev->stats.tx_dropped++;
   err = NETDEV_TX_OK;
   goto release;
  }
 }

 skb_tx_timestamp(skb);
 skb_push(skb, IFH_LEN_BYTES);
 memcpy(skb->data, ifh, IFH_LEN_BYTES);
 skb_put(skb, 4);

 dma_addr = dma_map_single(lan966x->dev, skb->data, skb->len,
      DMA_TO_DEVICE);
 if (dma_mapping_error(lan966x->dev, dma_addr)) {
  dev->stats.tx_dropped++;
  err = NETDEV_TX_OK;
  goto release;
 }

 /* Fill up the buffer */
 next_dcb_buf = &tx->dcbs_buf[next_to_use];
 next_dcb_buf->use_skb = true;
 next_dcb_buf->data.skb = skb;
 next_dcb_buf->xdp_ndo = false;
 next_dcb_buf->len = skb->len;
 next_dcb_buf->dma_addr = dma_addr;
 next_dcb_buf->used = true;
 next_dcb_buf->ptp = false;
 next_dcb_buf->dev = dev;

 fdma_dcb_add(&tx->fdma,
       next_to_use,
       0,
       FDMA_DCB_STATUS_INTR |
       FDMA_DCB_STATUS_SOF |
       FDMA_DCB_STATUS_EOF |
       FDMA_DCB_STATUS_BLOCKO(0) |
       FDMA_DCB_STATUS_BLOCKL(skb->len));

 if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP &&
     LAN966X_SKB_CB(skb)->rew_op == IFH_REW_OP_TWO_STEP_PTP)
  next_dcb_buf->ptp = true;

 /* Start the transmission */
 lan966x_fdma_tx_start(tx);

 return NETDEV_TX_OK;

release:
 if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP &&
     LAN966X_SKB_CB(skb)->rew_op == IFH_REW_OP_TWO_STEP_PTP)
  lan966x_ptp_txtstamp_release(port, skb);

 dev_kfree_skb_any(skb);
 return err;
}

static int lan966x_fdma_get_max_mtu(struct lan966x *lan966x)
{
 int max_mtu = 0;
 int i;

 for (i = 0; i < lan966x->num_phys_ports; ++i) {
  struct lan966x_port *port;
  int mtu;

  port = lan966x->ports[i];
  if (!port)
   continue;

  mtu = lan_rd(lan966x, DEV_MAC_MAXLEN_CFG(port->chip_port));
  if (mtu > max_mtu)
   max_mtu = mtu;
 }

 return max_mtu;
}

static int lan966x_qsys_sw_status(struct lan966x *lan966x)
{
 return lan_rd(lan966x, QSYS_SW_STATUS(CPU_PORT));
}

static int lan966x_fdma_reload(struct lan966x *lan966x, int new_mtu)
{
 struct page_pool *page_pool;
 struct fdma fdma_rx_old;
 int err;

 /* Store these for later to free them */
 memcpy(&fdma_rx_old, &lan966x->rx.fdma, sizeof(struct fdma));
 page_pool = lan966x->rx.page_pool;

 napi_synchronize(&lan966x->napi);
 napi_disable(&lan966x->napi);
 lan966x_fdma_stop_netdev(lan966x);

 lan966x_fdma_rx_disable(&lan966x->rx);
 lan966x_fdma_rx_free_pages(&lan966x->rx);
 lan966x->rx.page_order = round_up(new_mtu, PAGE_SIZE) / PAGE_SIZE - 1;
 lan966x->rx.max_mtu = new_mtu;
 err = lan966x_fdma_rx_alloc(&lan966x->rx);
 if (err)
  goto restore;
 lan966x_fdma_rx_start(&lan966x->rx);

 fdma_free_coherent(lan966x->dev, &fdma_rx_old);

 page_pool_destroy(page_pool);

 lan966x_fdma_wakeup_netdev(lan966x);
 napi_enable(&lan966x->napi);

 return err;
restore:
 lan966x->rx.page_pool = page_pool;
 memcpy(&lan966x->rx.fdma, &fdma_rx_old, sizeof(struct fdma));
 lan966x_fdma_rx_start(&lan966x->rx);

 return err;
}

static int lan966x_fdma_get_max_frame(struct lan966x *lan966x)
{
 return lan966x_fdma_get_max_mtu(lan966x) +
        IFH_LEN_BYTES +
        SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) +
        VLAN_HLEN * 2 +
        XDP_PACKET_HEADROOM;
}

static int __lan966x_fdma_reload(struct lan966x *lan966x, int max_mtu)
{
 int err;
 u32 val;

 /* Disable the CPU port */
 lan_rmw(QSYS_SW_PORT_MODE_PORT_ENA_SET(0),
  QSYS_SW_PORT_MODE_PORT_ENA,
  lan966x, QSYS_SW_PORT_MODE(CPU_PORT));

 /* Flush the CPU queues */
 readx_poll_timeout(lan966x_qsys_sw_status, lan966x,
      val, !(QSYS_SW_STATUS_EQ_AVAIL_GET(val)),
      READL_SLEEP_US, READL_TIMEOUT_US);

 /* Add a sleep in case there are frames between the queues and the CPU
 * port
 */

 usleep_range(1000, 2000);

 err = lan966x_fdma_reload(lan966x, max_mtu);

 /* Enable back the CPU port */
 lan_rmw(QSYS_SW_PORT_MODE_PORT_ENA_SET(1),
  QSYS_SW_PORT_MODE_PORT_ENA,
  lan966x,  QSYS_SW_PORT_MODE(CPU_PORT));

 return err;
}

int lan966x_fdma_change_mtu(struct lan966x *lan966x)
{
 int max_mtu;

 max_mtu = lan966x_fdma_get_max_frame(lan966x);
 if (max_mtu == lan966x->rx.max_mtu)
  return 0;

 return __lan966x_fdma_reload(lan966x, max_mtu);
}

int lan966x_fdma_reload_page_pool(struct lan966x *lan966x)
{
 int max_mtu;

 max_mtu = lan966x_fdma_get_max_frame(lan966x);
 return __lan966x_fdma_reload(lan966x, max_mtu);
}

void lan966x_fdma_netdev_init(struct lan966x *lan966x, struct net_device *dev)
{
 if (lan966x->fdma_ndev)
  return;

 lan966x->fdma_ndev = dev;
 netif_napi_add(dev, &lan966x->napi, lan966x_fdma_napi_poll);
 napi_enable(&lan966x->napi);
}

void lan966x_fdma_netdev_deinit(struct lan966x *lan966x, struct net_device *dev)
{
 if (lan966x->fdma_ndev == dev) {
  netif_napi_del(&lan966x->napi);
  lan966x->fdma_ndev = NULL;
 }
}

int lan966x_fdma_init(struct lan966x *lan966x)
{
 int err;

 if (!lan966x->fdma)
  return 0;

 lan966x->rx.lan966x = lan966x;
 lan966x->rx.fdma.channel_id = FDMA_XTR_CHANNEL;
 lan966x->rx.fdma.n_dcbs = FDMA_DCB_MAX;
 lan966x->rx.fdma.n_dbs = FDMA_RX_DCB_MAX_DBS;
 lan966x->rx.fdma.priv = lan966x;
 lan966x->rx.fdma.size = fdma_get_size(&lan966x->rx.fdma);
 lan966x->rx.fdma.db_size = PAGE_SIZE << lan966x->rx.page_order;
 lan966x->rx.fdma.ops.nextptr_cb = &fdma_nextptr_cb;
 lan966x->rx.fdma.ops.dataptr_cb = &lan966x_fdma_rx_dataptr_cb;
 lan966x->rx.max_mtu = lan966x_fdma_get_max_frame(lan966x);
 lan966x->tx.lan966x = lan966x;
 lan966x->tx.fdma.channel_id = FDMA_INJ_CHANNEL;
 lan966x->tx.fdma.n_dcbs = FDMA_DCB_MAX;
 lan966x->tx.fdma.n_dbs = FDMA_TX_DCB_MAX_DBS;
 lan966x->tx.fdma.priv = lan966x;
 lan966x->tx.fdma.size = fdma_get_size(&lan966x->tx.fdma);
 lan966x->tx.fdma.db_size = PAGE_SIZE << lan966x->rx.page_order;
 lan966x->tx.fdma.ops.nextptr_cb = &fdma_nextptr_cb;
 lan966x->tx.fdma.ops.dataptr_cb = &lan966x_fdma_tx_dataptr_cb;

 err = lan966x_fdma_rx_alloc(&lan966x->rx);
 if (err)
  return err;

 err = lan966x_fdma_tx_alloc(&lan966x->tx);
 if (err) {
  fdma_free_coherent(lan966x->dev, &lan966x->rx.fdma);
  return err;
 }

 lan966x_fdma_rx_start(&lan966x->rx);

 return 0;
}

void lan966x_fdma_deinit(struct lan966x *lan966x)
{
 if (!lan966x->fdma)
  return;

 lan966x_fdma_rx_disable(&lan966x->rx);
 lan966x_fdma_tx_disable(&lan966x->tx);

 napi_synchronize(&lan966x->napi);
 napi_disable(&lan966x->napi);

 lan966x_fdma_rx_free_pages(&lan966x->rx);
 fdma_free_coherent(lan966x->dev, &lan966x->rx.fdma);
 page_pool_destroy(lan966x->rx.page_pool);
 lan966x_fdma_tx_free(&lan966x->tx);
}

Messung V0.5
C=96 H=93 G=94

¤ Dauer der Verarbeitung: 0.2 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.