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

Quelle  qed_chain.c   Sprache: C

 
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
/* Copyright (c) 2020 Marvell International Ltd. */

#include <linux/dma-mapping.h>
#include <linux/qed/qed_chain.h>
#include <linux/vmalloc.h>

#include "qed_dev_api.h"

static void qed_chain_init(struct qed_chain *chain,
      const struct qed_chain_init_params *params,
      u32 page_cnt)
{
 memset(chain, 0, sizeof(*chain));

 chain->elem_size = params->elem_size;
 chain->intended_use = params->intended_use;
 chain->mode = params->mode;
 chain->cnt_type = params->cnt_type;

 chain->elem_per_page = ELEMS_PER_PAGE(params->elem_size,
           params->page_size);
 chain->usable_per_page = USABLE_ELEMS_PER_PAGE(params->elem_size,
             params->page_size,
             params->mode);
 chain->elem_unusable = UNUSABLE_ELEMS_PER_PAGE(params->elem_size,
             params->mode);

 chain->elem_per_page_mask = chain->elem_per_page - 1;
 chain->next_page_mask = chain->usable_per_page &
    chain->elem_per_page_mask;

 chain->page_size = params->page_size;
 chain->page_cnt = page_cnt;
 chain->capacity = chain->usable_per_page * page_cnt;
 chain->size = chain->elem_per_page * page_cnt;

 if (params->ext_pbl_virt) {
  chain->pbl_sp.table_virt = params->ext_pbl_virt;
  chain->pbl_sp.table_phys = params->ext_pbl_phys;

  chain->b_external_pbl = true;
 }
}

static void qed_chain_init_next_ptr_elem(const struct qed_chain *chain,
      void *virt_curr, void *virt_next,
      dma_addr_t phys_next)
{
 struct qed_chain_next *next;
 u32 size;

 size = chain->elem_size * chain->usable_per_page;
 next = virt_curr + size;

 DMA_REGPAIR_LE(next->next_phys, phys_next);
 next->next_virt = virt_next;
}

static void qed_chain_init_mem(struct qed_chain *chain, void *virt_addr,
          dma_addr_t phys_addr)
{
 chain->p_virt_addr = virt_addr;
 chain->p_phys_addr = phys_addr;
}

static void qed_chain_free_next_ptr(struct qed_dev *cdev,
        struct qed_chain *chain)
{
 struct device *dev = &cdev->pdev->dev;
 struct qed_chain_next *next;
 dma_addr_t phys, phys_next;
 void *virt, *virt_next;
 u32 size, i;

 size = chain->elem_size * chain->usable_per_page;
 virt = chain->p_virt_addr;
 phys = chain->p_phys_addr;

 for (i = 0; i < chain->page_cnt; i++) {
  if (!virt)
   break;

  next = virt + size;
  virt_next = next->next_virt;
  phys_next = HILO_DMA_REGPAIR(next->next_phys);

  dma_free_coherent(dev, chain->page_size, virt, phys);

  virt = virt_next;
  phys = phys_next;
 }
}

static void qed_chain_free_single(struct qed_dev *cdev,
      struct qed_chain *chain)
{
 if (!chain->p_virt_addr)
  return;

 dma_free_coherent(&cdev->pdev->dev, chain->page_size,
     chain->p_virt_addr, chain->p_phys_addr);
}

static void qed_chain_free_pbl(struct qed_dev *cdev, struct qed_chain *chain)
{
 struct device *dev = &cdev->pdev->dev;
 struct addr_tbl_entry *entry;
 u32 i;

 if (!chain->pbl.pp_addr_tbl)
  return;

 for (i = 0; i < chain->page_cnt; i++) {
  entry = chain->pbl.pp_addr_tbl + i;
  if (!entry->virt_addr)
   break;

  dma_free_coherent(dev, chain->page_size, entry->virt_addr,
      entry->dma_map);
 }

 if (!chain->b_external_pbl)
  dma_free_coherent(dev, chain->pbl_sp.table_size,
      chain->pbl_sp.table_virt,
      chain->pbl_sp.table_phys);

 vfree(chain->pbl.pp_addr_tbl);
 chain->pbl.pp_addr_tbl = NULL;
}

/**
 * qed_chain_free() - Free chain DMA memory.
 *
 * @cdev: Main device structure.
 * @chain: Chain to free.
 */

void qed_chain_free(struct qed_dev *cdev, struct qed_chain *chain)
{
 switch (chain->mode) {
 case QED_CHAIN_MODE_NEXT_PTR:
  qed_chain_free_next_ptr(cdev, chain);
  break;
 case QED_CHAIN_MODE_SINGLE:
  qed_chain_free_single(cdev, chain);
  break;
 case QED_CHAIN_MODE_PBL:
  qed_chain_free_pbl(cdev, chain);
  break;
 default:
  return;
 }

 qed_chain_init_mem(chain, NULL, 0);
}

static int
qed_chain_alloc_sanity_check(struct qed_dev *cdev,
        const struct qed_chain_init_params *params,
        u32 page_cnt)
{
 u64 chain_size;

 chain_size = ELEMS_PER_PAGE(params->elem_size, params->page_size);
 chain_size *= page_cnt;

 if (!chain_size)
  return -EINVAL;

 /* The actual chain size can be larger than the maximal possible value
 * after rounding up the requested elements number to pages, and after
 * taking into account the unusuable elements (next-ptr elements).
 * The size of a "u16" chain can be (U16_MAX + 1) since the chain
 * size/capacity fields are of u32 type.
 */

 switch (params->cnt_type) {
 case QED_CHAIN_CNT_TYPE_U16:
  if (chain_size > U16_MAX + 1)
   break;

  return 0;
 case QED_CHAIN_CNT_TYPE_U32:
  if (chain_size > U32_MAX)
   break;

  return 0;
 default:
  return -EINVAL;
 }

 DP_NOTICE(cdev,
    "The actual chain size (0x%llx) is larger than the maximal possible value\n",
    chain_size);

 return -EINVAL;
}

static int qed_chain_alloc_next_ptr(struct qed_dev *cdev,
        struct qed_chain *chain)
{
 struct device *dev = &cdev->pdev->dev;
 void *virt, *virt_prev = NULL;
 dma_addr_t phys;
 u32 i;

 for (i = 0; i < chain->page_cnt; i++) {
  virt = dma_alloc_coherent(dev, chain->page_size, &phys,
       GFP_KERNEL);
  if (!virt)
   return -ENOMEM;

  if (i == 0) {
   qed_chain_init_mem(chain, virt, phys);
   qed_chain_reset(chain);
  } else {
   qed_chain_init_next_ptr_elem(chain, virt_prev, virt,
           phys);
  }

  virt_prev = virt;
 }

 /* Last page's next element should point to the beginning of the
 * chain.
 */

 qed_chain_init_next_ptr_elem(chain, virt_prev, chain->p_virt_addr,
         chain->p_phys_addr);

 return 0;
}

static int qed_chain_alloc_single(struct qed_dev *cdev,
      struct qed_chain *chain)
{
 dma_addr_t phys;
 void *virt;

 virt = dma_alloc_coherent(&cdev->pdev->dev, chain->page_size,
      &phys, GFP_KERNEL);
 if (!virt)
  return -ENOMEM;

 qed_chain_init_mem(chain, virt, phys);
 qed_chain_reset(chain);

 return 0;
}

static int qed_chain_alloc_pbl(struct qed_dev *cdev, struct qed_chain *chain)
{
 struct device *dev = &cdev->pdev->dev;
 struct addr_tbl_entry *addr_tbl;
 dma_addr_t phys, pbl_phys;
 __le64 *pbl_virt;
 u32 page_cnt, i;
 size_t size;
 void *virt;

 page_cnt = chain->page_cnt;

 size = array_size(page_cnt, sizeof(*addr_tbl));
 if (unlikely(size == SIZE_MAX))
  return -EOVERFLOW;

 addr_tbl = vzalloc(size);
 if (!addr_tbl)
  return -ENOMEM;

 chain->pbl.pp_addr_tbl = addr_tbl;

 if (chain->b_external_pbl) {
  pbl_virt = chain->pbl_sp.table_virt;
  goto alloc_pages;
 }

 size = array_size(page_cnt, sizeof(*pbl_virt));
 if (unlikely(size == SIZE_MAX))
  return -EOVERFLOW;

 pbl_virt = dma_alloc_coherent(dev, size, &pbl_phys, GFP_KERNEL);
 if (!pbl_virt)
  return -ENOMEM;

 chain->pbl_sp.table_virt = pbl_virt;
 chain->pbl_sp.table_phys = pbl_phys;
 chain->pbl_sp.table_size = size;

alloc_pages:
 for (i = 0; i < page_cnt; i++) {
  virt = dma_alloc_coherent(dev, chain->page_size, &phys,
       GFP_KERNEL);
  if (!virt)
   return -ENOMEM;

  if (i == 0) {
   qed_chain_init_mem(chain, virt, phys);
   qed_chain_reset(chain);
  }

  /* Fill the PBL table with the physical address of the page */
  pbl_virt[i] = cpu_to_le64(phys);

  /* Keep the virtual address of the page */
  addr_tbl[i].virt_addr = virt;
  addr_tbl[i].dma_map = phys;
 }

 return 0;
}

/**
 * qed_chain_alloc() - Allocate and initialize a chain.
 *
 * @cdev: Main device structure.
 * @chain: Chain to be processed.
 * @params: Chain initialization parameters.
 *
 * Return: 0 on success, negative errno otherwise.
 */

int qed_chain_alloc(struct qed_dev *cdev, struct qed_chain *chain,
      struct qed_chain_init_params *params)
{
 u32 page_cnt;
 int rc;

 if (!params->page_size)
  params->page_size = QED_CHAIN_PAGE_SIZE;

 if (params->mode == QED_CHAIN_MODE_SINGLE)
  page_cnt = 1;
 else
  page_cnt = QED_CHAIN_PAGE_CNT(params->num_elems,
           params->elem_size,
           params->page_size,
           params->mode);

 rc = qed_chain_alloc_sanity_check(cdev, params, page_cnt);
 if (rc) {
  DP_NOTICE(cdev,
     "Cannot allocate a chain with the given arguments:\n");
  DP_NOTICE(cdev,
     "[use_mode %d, mode %d, cnt_type %d, num_elems %d, elem_size %zu, page_size %u]\n",
     params->intended_use, params->mode, params->cnt_type,
     params->num_elems, params->elem_size,
     params->page_size);
  return rc;
 }

 qed_chain_init(chain, params, page_cnt);

 switch (params->mode) {
 case QED_CHAIN_MODE_NEXT_PTR:
  rc = qed_chain_alloc_next_ptr(cdev, chain);
  break;
 case QED_CHAIN_MODE_SINGLE:
  rc = qed_chain_alloc_single(cdev, chain);
  break;
 case QED_CHAIN_MODE_PBL:
  rc = qed_chain_alloc_pbl(cdev, chain);
  break;
 default:
  return -EINVAL;
 }

 if (!rc)
  return 0;

 qed_chain_free(cdev, chain);

 return rc;
}

Messung V0.5
C=98 H=92 G=94

¤ Dauer der Verarbeitung: 0.12 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.