Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/crypto/async_tx/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 11 kB image not shown  

Quelle  async_xor.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-only
/*
 * xor offload engine api
 *
 * Copyright © 2006, Intel Corporation.
 *
 *      Dan Williams <dan.j.williams@intel.com>
 *
 *      with architecture considerations by:
 *      Neil Brown <neilb@suse.de>
 *      Jeff Garzik <jeff@garzik.org>
 */

#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/mm.h>
#include <linux/dma-mapping.h>
#include <linux/raid/xor.h>
#include <linux/async_tx.h>

/* do_async_xor - dma map the pages and perform the xor with an engine */
static __async_inline struct dma_async_tx_descriptor *
do_async_xor(struct dma_chan *chan, struct dmaengine_unmap_data *unmap,
      struct async_submit_ctl *submit)
{
 struct dma_device *dma = chan->device;
 struct dma_async_tx_descriptor *tx = NULL;
 dma_async_tx_callback cb_fn_orig = submit->cb_fn;
 void *cb_param_orig = submit->cb_param;
 enum async_tx_flags flags_orig = submit->flags;
 enum dma_ctrl_flags dma_flags = 0;
 int src_cnt = unmap->to_cnt;
 int xor_src_cnt;
 dma_addr_t dma_dest = unmap->addr[unmap->to_cnt];
 dma_addr_t *src_list = unmap->addr;

 while (src_cnt) {
  dma_addr_t tmp;

  submit->flags = flags_orig;
  xor_src_cnt = min(src_cnt, (int)dma->max_xor);
  /* if we are submitting additional xors, leave the chain open
 * and clear the callback parameters
 */

  if (src_cnt > xor_src_cnt) {
   submit->flags &= ~ASYNC_TX_ACK;
   submit->flags |= ASYNC_TX_FENCE;
   submit->cb_fn = NULL;
   submit->cb_param = NULL;
  } else {
   submit->cb_fn = cb_fn_orig;
   submit->cb_param = cb_param_orig;
  }
  if (submit->cb_fn)
   dma_flags |= DMA_PREP_INTERRUPT;
  if (submit->flags & ASYNC_TX_FENCE)
   dma_flags |= DMA_PREP_FENCE;

  /* Drivers force forward progress in case they can not provide a
 * descriptor
 */

  tmp = src_list[0];
  if (src_list > unmap->addr)
   src_list[0] = dma_dest;
  tx = dma->device_prep_dma_xor(chan, dma_dest, src_list,
           xor_src_cnt, unmap->len,
           dma_flags);

  if (unlikely(!tx))
   async_tx_quiesce(&submit->depend_tx);

  /* spin wait for the preceding transactions to complete */
  while (unlikely(!tx)) {
   dma_async_issue_pending(chan);
   tx = dma->device_prep_dma_xor(chan, dma_dest,
            src_list,
            xor_src_cnt, unmap->len,
            dma_flags);
  }
  src_list[0] = tmp;

  dma_set_unmap(tx, unmap);
  async_tx_submit(chan, tx, submit);
  submit->depend_tx = tx;

  if (src_cnt > xor_src_cnt) {
   /* drop completed sources */
   src_cnt -= xor_src_cnt;
   /* use the intermediate result a source */
   src_cnt++;
   src_list += xor_src_cnt - 1;
  } else
   break;
 }

 return tx;
}

static void
do_sync_xor_offs(struct page *dest, unsigned int offset,
  struct page **src_list, unsigned int *src_offs,
     int src_cnt, size_t len, struct async_submit_ctl *submit)
{
 int i;
 int xor_src_cnt = 0;
 int src_off = 0;
 void *dest_buf;
 void **srcs;

 if (submit->scribble)
  srcs = submit->scribble;
 else
  srcs = (void **) src_list;

 /* convert to buffer pointers */
 for (i = 0; i < src_cnt; i++)
  if (src_list[i])
   srcs[xor_src_cnt++] = page_address(src_list[i]) +
    (src_offs ? src_offs[i] : offset);
 src_cnt = xor_src_cnt;
 /* set destination address */
 dest_buf = page_address(dest) + offset;

 if (submit->flags & ASYNC_TX_XOR_ZERO_DST)
  memset(dest_buf, 0, len);

 while (src_cnt > 0) {
  /* process up to 'MAX_XOR_BLOCKS' sources */
  xor_src_cnt = min(src_cnt, MAX_XOR_BLOCKS);
  xor_blocks(xor_src_cnt, len, dest_buf, &srcs[src_off]);

  /* drop completed sources */
  src_cnt -= xor_src_cnt;
  src_off += xor_src_cnt;
 }

 async_tx_sync_epilog(submit);
}

static inline bool
dma_xor_aligned_offsets(struct dma_device *device, unsigned int offset,
  unsigned int *src_offs, int src_cnt, int len)
{
 int i;

 if (!is_dma_xor_aligned(device, offset, 0, len))
  return false;

 if (!src_offs)
  return true;

 for (i = 0; i < src_cnt; i++) {
  if (!is_dma_xor_aligned(device, src_offs[i], 0, len))
   return false;
 }
 return true;
}

/**
 * async_xor_offs - attempt to xor a set of blocks with a dma engine.
 * @dest: destination page
 * @offset: dst offset to start transaction
 * @src_list: array of source pages
 * @src_offs: array of source pages offset, NULL means common src/dst offset
 * @src_cnt: number of source pages
 * @len: length in bytes
 * @submit: submission / completion modifiers
 *
 * honored flags: ASYNC_TX_ACK, ASYNC_TX_XOR_ZERO_DST, ASYNC_TX_XOR_DROP_DST
 *
 * xor_blocks always uses the dest as a source so the
 * ASYNC_TX_XOR_ZERO_DST flag must be set to not include dest data in
 * the calculation.  The assumption with dma engines is that they only
 * use the destination buffer as a source when it is explicitly specified
 * in the source list.
 *
 * src_list note: if the dest is also a source it must be at index zero.
 * The contents of this array will be overwritten if a scribble region
 * is not specified.
 */

struct dma_async_tx_descriptor *
async_xor_offs(struct page *dest, unsigned int offset,
  struct page **src_list, unsigned int *src_offs,
  int src_cnt, size_t len, struct async_submit_ctl *submit)
{
 struct dma_chan *chan = async_tx_find_channel(submit, DMA_XOR,
            &dest, 1, src_list,
            src_cnt, len);
 struct dma_device *device = chan ? chan->device : NULL;
 struct dmaengine_unmap_data *unmap = NULL;

 BUG_ON(src_cnt <= 1);

 if (device)
  unmap = dmaengine_get_unmap_data(device->dev, src_cnt+1, GFP_NOWAIT);

 if (unmap && dma_xor_aligned_offsets(device, offset,
    src_offs, src_cnt, len)) {
  struct dma_async_tx_descriptor *tx;
  int i, j;

  /* run the xor asynchronously */
  pr_debug("%s (async): len: %zu\n", __func__, len);

  unmap->len = len;
  for (i = 0, j = 0; i < src_cnt; i++) {
   if (!src_list[i])
    continue;
   unmap->to_cnt++;
   unmap->addr[j++] = dma_map_page(device->dev, src_list[i],
     src_offs ? src_offs[i] : offset,
     len, DMA_TO_DEVICE);
  }

  /* map it bidirectional as it may be re-used as a source */
  unmap->addr[j] = dma_map_page(device->dev, dest, offset, len,
           DMA_BIDIRECTIONAL);
  unmap->bidi_cnt = 1;

  tx = do_async_xor(chan, unmap, submit);
  dmaengine_unmap_put(unmap);
  return tx;
 } else {
  dmaengine_unmap_put(unmap);
  /* run the xor synchronously */
  pr_debug("%s (sync): len: %zu\n", __func__, len);
  WARN_ONCE(chan, "%s: no space for dma address conversion\n",
     __func__);

  /* in the sync case the dest is an implied source
 * (assumes the dest is the first source)
 */

  if (submit->flags & ASYNC_TX_XOR_DROP_DST) {
   src_cnt--;
   src_list++;
   if (src_offs)
    src_offs++;
  }

  /* wait for any prerequisite operations */
  async_tx_quiesce(&submit->depend_tx);

  do_sync_xor_offs(dest, offset, src_list, src_offs,
    src_cnt, len, submit);

  return NULL;
 }
}
EXPORT_SYMBOL_GPL(async_xor_offs);

/**
 * async_xor - attempt to xor a set of blocks with a dma engine.
 * @dest: destination page
 * @src_list: array of source pages
 * @offset: common src/dst offset to start transaction
 * @src_cnt: number of source pages
 * @len: length in bytes
 * @submit: submission / completion modifiers
 *
 * honored flags: ASYNC_TX_ACK, ASYNC_TX_XOR_ZERO_DST, ASYNC_TX_XOR_DROP_DST
 *
 * xor_blocks always uses the dest as a source so the
 * ASYNC_TX_XOR_ZERO_DST flag must be set to not include dest data in
 * the calculation.  The assumption with dma engines is that they only
 * use the destination buffer as a source when it is explicitly specified
 * in the source list.
 *
 * src_list note: if the dest is also a source it must be at index zero.
 * The contents of this array will be overwritten if a scribble region
 * is not specified.
 */

struct dma_async_tx_descriptor *
async_xor(struct page *dest, struct page **src_list, unsigned int offset,
   int src_cnt, size_t len, struct async_submit_ctl *submit)
{
 return async_xor_offs(dest, offset, src_list, NULL,
   src_cnt, len, submit);
}
EXPORT_SYMBOL_GPL(async_xor);

static int page_is_zero(struct page *p, unsigned int offset, size_t len)
{
 return !memchr_inv(page_address(p) + offset, 0, len);
}

static inline struct dma_chan *
xor_val_chan(struct async_submit_ctl *submit, struct page *dest,
   struct page **src_list, int src_cnt, size_t len)
{
 #ifdef CONFIG_ASYNC_TX_DISABLE_XOR_VAL_DMA
 return NULL;
 #endif
 return async_tx_find_channel(submit, DMA_XOR_VAL, &dest, 1, src_list,
         src_cnt, len);
}

/**
 * async_xor_val_offs - attempt a xor parity check with a dma engine.
 * @dest: destination page used if the xor is performed synchronously
 * @offset: des offset in pages to start transaction
 * @src_list: array of source pages
 * @src_offs: array of source pages offset, NULL means common src/det offset
 * @src_cnt: number of source pages
 * @len: length in bytes
 * @result: 0 if sum == 0 else non-zero
 * @submit: submission / completion modifiers
 *
 * honored flags: ASYNC_TX_ACK
 *
 * src_list note: if the dest is also a source it must be at index zero.
 * The contents of this array will be overwritten if a scribble region
 * is not specified.
 */

struct dma_async_tx_descriptor *
async_xor_val_offs(struct page *dest, unsigned int offset,
  struct page **src_list, unsigned int *src_offs,
  int src_cnt, size_t len, enum sum_check_flags *result,
  struct async_submit_ctl *submit)
{
 struct dma_chan *chan = xor_val_chan(submit, dest, src_list, src_cnt, len);
 struct dma_device *device = chan ? chan->device : NULL;
 struct dma_async_tx_descriptor *tx = NULL;
 struct dmaengine_unmap_data *unmap = NULL;

 BUG_ON(src_cnt <= 1);

 if (device)
  unmap = dmaengine_get_unmap_data(device->dev, src_cnt, GFP_NOWAIT);

 if (unmap && src_cnt <= device->max_xor &&
     dma_xor_aligned_offsets(device, offset, src_offs, src_cnt, len)) {
  unsigned long dma_prep_flags = 0;
  int i;

  pr_debug("%s: (async) len: %zu\n", __func__, len);

  if (submit->cb_fn)
   dma_prep_flags |= DMA_PREP_INTERRUPT;
  if (submit->flags & ASYNC_TX_FENCE)
   dma_prep_flags |= DMA_PREP_FENCE;

  for (i = 0; i < src_cnt; i++) {
   unmap->addr[i] = dma_map_page(device->dev, src_list[i],
     src_offs ? src_offs[i] : offset,
     len, DMA_TO_DEVICE);
   unmap->to_cnt++;
  }
  unmap->len = len;

  tx = device->device_prep_dma_xor_val(chan, unmap->addr, src_cnt,
           len, result,
           dma_prep_flags);
  if (unlikely(!tx)) {
   async_tx_quiesce(&submit->depend_tx);

   while (!tx) {
    dma_async_issue_pending(chan);
    tx = device->device_prep_dma_xor_val(chan,
     unmap->addr, src_cnt, len, result,
     dma_prep_flags);
   }
  }
  dma_set_unmap(tx, unmap);
  async_tx_submit(chan, tx, submit);
 } else {
  enum async_tx_flags flags_orig = submit->flags;

  pr_debug("%s: (sync) len: %zu\n", __func__, len);
  WARN_ONCE(device && src_cnt <= device->max_xor,
     "%s: no space for dma address conversion\n",
     __func__);

  submit->flags |= ASYNC_TX_XOR_DROP_DST;
  submit->flags &= ~ASYNC_TX_ACK;

  tx = async_xor_offs(dest, offset, src_list, src_offs,
    src_cnt, len, submit);

  async_tx_quiesce(&tx);

  *result = !page_is_zero(dest, offset, len) << SUM_CHECK_P;

  async_tx_sync_epilog(submit);
  submit->flags = flags_orig;
 }
 dmaengine_unmap_put(unmap);

 return tx;
}
EXPORT_SYMBOL_GPL(async_xor_val_offs);

MODULE_AUTHOR("Intel Corporation");
MODULE_DESCRIPTION("asynchronous xor/xor-zero-sum api");
MODULE_LICENSE("GPL");

Messung V0.5
C=96 H=89 G=92

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