mirror of
https://github.com/torvalds/linux.git
synced 2024-12-01 00:21:32 +00:00
crypto: talitos - chain in buffered data for ahash on SEC1
SEC1 doesn't support S/G in descriptors so for hash operations, the CPU has to build a buffer containing the buffered block and the incoming data. This generates a lot of memory copies which represents more than 50% of CPU time of a md5sum operation as shown below with a 'perf record'. |--86.24%-- kcapi_md_digest | | | |--86.18%-- _kcapi_common_vmsplice_chunk_fd | | | | | |--83.68%-- splice | | | | | | | |--83.59%-- ret_from_syscall | | | | | | | | | |--83.52%-- sys_splice | | | | | | | | | | | |--83.49%-- splice_from_pipe | | | | | | | | | | | | | |--83.04%-- __splice_from_pipe | | | | | | | | | | | | | | | |--80.67%-- pipe_to_sendpage | | | | | | | | | | | | | | | | | |--78.25%-- hash_sendpage | | | | | | | | | | | | | | | | | | | |--60.08%-- ahash_process_req | | | | | | | | | | | | | | | | | | | | | |--56.36%-- sg_copy_buffer | | | | | | | | | | | | | | | | | | | | | | | |--55.29%-- memcpy | | | | | | | | | | | | However, unlike SEC2+, SEC1 offers the possibility to chain descriptors. It is therefore possible to build a first descriptor pointing to the buffered data and a second descriptor pointing to the incoming data, hence avoiding the memory copy to a single buffer. With this patch, the time necessary for a md5sum on a 90Mbytes file is approximately 3 seconds. Without the patch it takes 6 seconds. Signed-off-by: Christophe Leroy <christophe.leroy@c-s.fr> Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
This commit is contained in:
parent
49f9783b0c
commit
37b5e8897e
@ -160,6 +160,10 @@ static int reset_channel(struct device *dev, int ch)
|
||||
/* set 36-bit addressing, done writeback enable and done IRQ enable */
|
||||
setbits32(priv->chan[ch].reg + TALITOS_CCCR_LO, TALITOS_CCCR_LO_EAE |
|
||||
TALITOS_CCCR_LO_CDWE | TALITOS_CCCR_LO_CDIE);
|
||||
/* enable chaining descriptors */
|
||||
if (is_sec1)
|
||||
setbits32(priv->chan[ch].reg + TALITOS_CCCR_LO,
|
||||
TALITOS_CCCR_LO_NE);
|
||||
|
||||
/* and ICCR writeback, if available */
|
||||
if (priv->features & TALITOS_FTR_HW_AUTH_CHECK)
|
||||
@ -333,7 +337,12 @@ static void flush_channel(struct device *dev, int ch, int error, int reset_ch)
|
||||
|
||||
/* descriptors with their done bits set don't get the error */
|
||||
rmb();
|
||||
hdr = is_sec1 ? request->desc->hdr1 : request->desc->hdr;
|
||||
if (!is_sec1)
|
||||
hdr = request->desc->hdr;
|
||||
else if (request->desc->next_desc)
|
||||
hdr = (request->desc + 1)->hdr1;
|
||||
else
|
||||
hdr = request->desc->hdr1;
|
||||
|
||||
if ((hdr & DESC_HDR_DONE) == DESC_HDR_DONE)
|
||||
status = 0;
|
||||
@ -454,7 +463,8 @@ static u32 current_desc_hdr(struct device *dev, int ch)
|
||||
tail = priv->chan[ch].tail;
|
||||
|
||||
iter = tail;
|
||||
while (priv->chan[ch].fifo[iter].dma_desc != cur_desc) {
|
||||
while (priv->chan[ch].fifo[iter].dma_desc != cur_desc &&
|
||||
priv->chan[ch].fifo[iter].desc->next_desc != cur_desc) {
|
||||
iter = (iter + 1) & (priv->fifo_len - 1);
|
||||
if (iter == tail) {
|
||||
dev_err(dev, "couldn't locate current descriptor\n");
|
||||
@ -462,6 +472,9 @@ static u32 current_desc_hdr(struct device *dev, int ch)
|
||||
}
|
||||
}
|
||||
|
||||
if (priv->chan[ch].fifo[iter].desc->next_desc == cur_desc)
|
||||
return (priv->chan[ch].fifo[iter].desc + 1)->hdr;
|
||||
|
||||
return priv->chan[ch].fifo[iter].desc->hdr;
|
||||
}
|
||||
|
||||
@ -819,6 +832,7 @@ struct talitos_ctx {
|
||||
unsigned int keylen;
|
||||
unsigned int enckeylen;
|
||||
unsigned int authkeylen;
|
||||
dma_addr_t dma_buf;
|
||||
dma_addr_t dma_hw_context;
|
||||
};
|
||||
|
||||
@ -1380,6 +1394,10 @@ static struct talitos_edesc *talitos_edesc_alloc(struct device *dev,
|
||||
alloc_len += icv_stashing ? authsize : 0;
|
||||
}
|
||||
|
||||
/* if its a ahash, add space for a second desc next to the first one */
|
||||
if (is_sec1 && !dst)
|
||||
alloc_len += sizeof(struct talitos_desc);
|
||||
|
||||
edesc = kmalloc(alloc_len, GFP_DMA | flags);
|
||||
if (!edesc) {
|
||||
dev_err(dev, "could not allocate edescriptor\n");
|
||||
@ -1392,11 +1410,15 @@ static struct talitos_edesc *talitos_edesc_alloc(struct device *dev,
|
||||
edesc->dst_nents = dst_nents;
|
||||
edesc->iv_dma = iv_dma;
|
||||
edesc->dma_len = dma_len;
|
||||
if (dma_len)
|
||||
edesc->dma_link_tbl = dma_map_single(dev, &edesc->link_tbl[0],
|
||||
if (dma_len) {
|
||||
void *addr = &edesc->link_tbl[0];
|
||||
|
||||
if (is_sec1 && !dst)
|
||||
addr += sizeof(struct talitos_desc);
|
||||
edesc->dma_link_tbl = dma_map_single(dev, addr,
|
||||
edesc->dma_len,
|
||||
DMA_BIDIRECTIONAL);
|
||||
|
||||
}
|
||||
return edesc;
|
||||
error_sg:
|
||||
if (iv_dma)
|
||||
@ -1671,6 +1693,9 @@ static void common_nonsnoop_hash_unmap(struct device *dev,
|
||||
dma_unmap_single(dev, edesc->dma_link_tbl, edesc->dma_len,
|
||||
DMA_BIDIRECTIONAL);
|
||||
|
||||
if (edesc->desc.next_desc)
|
||||
dma_unmap_single(dev, be32_to_cpu(edesc->desc.next_desc),
|
||||
TALITOS_DESC_SIZE, DMA_BIDIRECTIONAL);
|
||||
}
|
||||
|
||||
static void ahash_done(struct device *dev,
|
||||
@ -1717,6 +1742,7 @@ static void talitos_handle_buggy_hash(struct talitos_ctx *ctx,
|
||||
|
||||
static int common_nonsnoop_hash(struct talitos_edesc *edesc,
|
||||
struct ahash_request *areq, unsigned int length,
|
||||
unsigned int offset,
|
||||
void (*callback) (struct device *dev,
|
||||
struct talitos_desc *desc,
|
||||
void *context, int error))
|
||||
@ -1748,19 +1774,29 @@ static int common_nonsnoop_hash(struct talitos_edesc *edesc,
|
||||
to_talitos_ptr(&desc->ptr[2], ctx->dma_key, ctx->keylen,
|
||||
is_sec1);
|
||||
|
||||
if (is_sec1 && req_ctx->nbuf)
|
||||
length -= req_ctx->nbuf;
|
||||
|
||||
sg_count = edesc->src_nents ?: 1;
|
||||
if (is_sec1 && sg_count > 1)
|
||||
sg_copy_to_buffer(req_ctx->psrc, sg_count, edesc->buf, length);
|
||||
else
|
||||
sg_pcopy_to_buffer(req_ctx->psrc, sg_count,
|
||||
edesc->buf + sizeof(struct talitos_desc),
|
||||
length, req_ctx->nbuf);
|
||||
else if (length)
|
||||
sg_count = dma_map_sg(dev, req_ctx->psrc, sg_count,
|
||||
DMA_TO_DEVICE);
|
||||
/*
|
||||
* data in
|
||||
*/
|
||||
sg_count = talitos_sg_map(dev, req_ctx->psrc, length, edesc,
|
||||
&desc->ptr[3], sg_count, 0, 0);
|
||||
if (sg_count > 1)
|
||||
sync_needed = true;
|
||||
if (is_sec1 && req_ctx->nbuf) {
|
||||
to_talitos_ptr(&desc->ptr[3], ctx->dma_buf, req_ctx->nbuf,
|
||||
is_sec1);
|
||||
} else {
|
||||
sg_count = talitos_sg_map(dev, req_ctx->psrc, length, edesc,
|
||||
&desc->ptr[3], sg_count, offset, 0);
|
||||
if (sg_count > 1)
|
||||
sync_needed = true;
|
||||
}
|
||||
|
||||
/* fifth DWORD empty */
|
||||
|
||||
@ -1778,6 +1814,36 @@ static int common_nonsnoop_hash(struct talitos_edesc *edesc,
|
||||
if (is_sec1 && from_talitos_ptr_len(&desc->ptr[3], true) == 0)
|
||||
talitos_handle_buggy_hash(ctx, edesc, &desc->ptr[3]);
|
||||
|
||||
if (is_sec1 && req_ctx->nbuf && length) {
|
||||
struct talitos_desc *desc2 = desc + 1;
|
||||
dma_addr_t next_desc;
|
||||
|
||||
memset(desc2, 0, sizeof(*desc2));
|
||||
desc2->hdr = desc->hdr;
|
||||
desc2->hdr &= ~DESC_HDR_MODE0_MDEU_INIT;
|
||||
desc2->hdr1 = desc2->hdr;
|
||||
desc->hdr &= ~DESC_HDR_MODE0_MDEU_PAD;
|
||||
desc->hdr |= DESC_HDR_MODE0_MDEU_CONT;
|
||||
desc->hdr &= ~DESC_HDR_DONE_NOTIFY;
|
||||
|
||||
to_talitos_ptr(&desc2->ptr[1], ctx->dma_hw_context,
|
||||
req_ctx->hw_context_size, is_sec1);
|
||||
|
||||
copy_talitos_ptr(&desc2->ptr[2], &desc->ptr[2], is_sec1);
|
||||
sg_count = talitos_sg_map(dev, req_ctx->psrc, length, edesc,
|
||||
&desc2->ptr[3], sg_count, offset, 0);
|
||||
if (sg_count > 1)
|
||||
sync_needed = true;
|
||||
copy_talitos_ptr(&desc2->ptr[5], &desc->ptr[5], is_sec1);
|
||||
if (req_ctx->last)
|
||||
to_talitos_ptr(&desc->ptr[5], ctx->dma_hw_context,
|
||||
req_ctx->hw_context_size, is_sec1);
|
||||
|
||||
next_desc = dma_map_single(dev, &desc2->hdr1, TALITOS_DESC_SIZE,
|
||||
DMA_BIDIRECTIONAL);
|
||||
desc->next_desc = cpu_to_be32(next_desc);
|
||||
}
|
||||
|
||||
if (sync_needed)
|
||||
dma_sync_single_for_device(dev, edesc->dma_link_tbl,
|
||||
edesc->dma_len, DMA_BIDIRECTIONAL);
|
||||
@ -1796,6 +1862,11 @@ static struct talitos_edesc *ahash_edesc_alloc(struct ahash_request *areq,
|
||||
struct crypto_ahash *tfm = crypto_ahash_reqtfm(areq);
|
||||
struct talitos_ctx *ctx = crypto_ahash_ctx(tfm);
|
||||
struct talitos_ahash_req_ctx *req_ctx = ahash_request_ctx(areq);
|
||||
struct talitos_private *priv = dev_get_drvdata(ctx->dev);
|
||||
bool is_sec1 = has_ftr_sec1(priv);
|
||||
|
||||
if (is_sec1)
|
||||
nbytes -= req_ctx->nbuf;
|
||||
|
||||
return talitos_edesc_alloc(ctx->dev, req_ctx->psrc, NULL, NULL, 0,
|
||||
nbytes, 0, 0, 0, areq->base.flags, false);
|
||||
@ -1808,6 +1879,8 @@ static int ahash_init(struct ahash_request *areq)
|
||||
struct device *dev = ctx->dev;
|
||||
struct talitos_ahash_req_ctx *req_ctx = ahash_request_ctx(areq);
|
||||
unsigned int size;
|
||||
struct talitos_private *priv = dev_get_drvdata(dev);
|
||||
bool is_sec1 = has_ftr_sec1(priv);
|
||||
|
||||
/* Initialize the context */
|
||||
req_ctx->nbuf = 0;
|
||||
@ -1823,6 +1896,13 @@ static int ahash_init(struct ahash_request *areq)
|
||||
DMA_BIDIRECTIONAL);
|
||||
ctx->dma_hw_context = dma_map_single(dev, req_ctx->hw_context, size,
|
||||
DMA_BIDIRECTIONAL);
|
||||
if (ctx->dma_buf)
|
||||
dma_unmap_single(dev, ctx->dma_buf, sizeof(req_ctx->buf),
|
||||
DMA_TO_DEVICE);
|
||||
if (is_sec1)
|
||||
ctx->dma_buf = dma_map_single(dev, req_ctx->buf,
|
||||
sizeof(req_ctx->buf),
|
||||
DMA_TO_DEVICE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1871,6 +1951,10 @@ static int ahash_process_req(struct ahash_request *areq, unsigned int nbytes)
|
||||
unsigned int to_hash_later;
|
||||
unsigned int nsg;
|
||||
int nents;
|
||||
struct device *dev = ctx->dev;
|
||||
struct talitos_private *priv = dev_get_drvdata(dev);
|
||||
bool is_sec1 = has_ftr_sec1(priv);
|
||||
int offset = 0;
|
||||
|
||||
if (!req_ctx->last && (nbytes + req_ctx->nbuf <= blocksize)) {
|
||||
/* Buffer up to one whole block */
|
||||
@ -1901,13 +1985,27 @@ static int ahash_process_req(struct ahash_request *areq, unsigned int nbytes)
|
||||
}
|
||||
|
||||
/* Chain in any previously buffered data */
|
||||
if (req_ctx->nbuf) {
|
||||
if (!is_sec1 && req_ctx->nbuf) {
|
||||
nsg = (req_ctx->nbuf < nbytes_to_hash) ? 2 : 1;
|
||||
sg_init_table(req_ctx->bufsl, nsg);
|
||||
sg_set_buf(req_ctx->bufsl, req_ctx->buf, req_ctx->nbuf);
|
||||
if (nsg > 1)
|
||||
sg_chain(req_ctx->bufsl, 2, areq->src);
|
||||
req_ctx->psrc = req_ctx->bufsl;
|
||||
} else if (is_sec1 && req_ctx->nbuf && req_ctx->nbuf < blocksize) {
|
||||
if (nbytes_to_hash > blocksize)
|
||||
offset = blocksize - req_ctx->nbuf;
|
||||
else
|
||||
offset = nbytes_to_hash - req_ctx->nbuf;
|
||||
nents = sg_nents_for_len(areq->src, offset);
|
||||
if (nents < 0) {
|
||||
dev_err(ctx->dev, "Invalid number of src SG.\n");
|
||||
return nents;
|
||||
}
|
||||
sg_copy_to_buffer(areq->src, nents,
|
||||
req_ctx->buf + req_ctx->nbuf, offset);
|
||||
req_ctx->nbuf += offset;
|
||||
req_ctx->psrc = areq->src;
|
||||
} else
|
||||
req_ctx->psrc = areq->src;
|
||||
|
||||
@ -1940,6 +2038,9 @@ static int ahash_process_req(struct ahash_request *areq, unsigned int nbytes)
|
||||
/* request SEC to INIT hash. */
|
||||
if (req_ctx->first && !req_ctx->swinit)
|
||||
edesc->desc.hdr |= DESC_HDR_MODE0_MDEU_INIT;
|
||||
if (is_sec1)
|
||||
dma_sync_single_for_device(dev, ctx->dma_buf,
|
||||
req_ctx->nbuf, DMA_TO_DEVICE);
|
||||
|
||||
/* When the tfm context has a keylen, it's an HMAC.
|
||||
* A first or last (ie. not middle) descriptor must request HMAC.
|
||||
@ -1947,7 +2048,7 @@ static int ahash_process_req(struct ahash_request *areq, unsigned int nbytes)
|
||||
if (ctx->keylen && (req_ctx->first || req_ctx->last))
|
||||
edesc->desc.hdr |= DESC_HDR_MODE0_MDEU_HMAC;
|
||||
|
||||
return common_nonsnoop_hash(edesc, areq, nbytes_to_hash,
|
||||
return common_nonsnoop_hash(edesc, areq, nbytes_to_hash, offset,
|
||||
ahash_done);
|
||||
}
|
||||
|
||||
@ -2019,6 +2120,8 @@ static int ahash_import(struct ahash_request *areq, const void *in)
|
||||
unsigned int size;
|
||||
struct talitos_ctx *ctx = crypto_ahash_ctx(tfm);
|
||||
struct device *dev = ctx->dev;
|
||||
struct talitos_private *priv = dev_get_drvdata(dev);
|
||||
bool is_sec1 = has_ftr_sec1(priv);
|
||||
|
||||
memset(req_ctx, 0, sizeof(*req_ctx));
|
||||
size = (crypto_ahash_digestsize(tfm) <= SHA256_DIGEST_SIZE)
|
||||
@ -2032,7 +2135,14 @@ static int ahash_import(struct ahash_request *areq, const void *in)
|
||||
memcpy(req_ctx->hw_context, export->hw_context, size);
|
||||
ctx->dma_hw_context = dma_map_single(dev, req_ctx->hw_context, size,
|
||||
DMA_BIDIRECTIONAL);
|
||||
if (ctx->dma_buf)
|
||||
dma_unmap_single(dev, ctx->dma_buf, sizeof(req_ctx->buf),
|
||||
DMA_TO_DEVICE);
|
||||
memcpy(req_ctx->buf, export->buf, export->nbuf);
|
||||
if (is_sec1)
|
||||
ctx->dma_buf = dma_map_single(dev, req_ctx->buf,
|
||||
sizeof(req_ctx->buf),
|
||||
DMA_TO_DEVICE);
|
||||
req_ctx->swinit = export->swinit;
|
||||
req_ctx->first = export->first;
|
||||
req_ctx->last = export->last;
|
||||
@ -2986,6 +3096,9 @@ static void talitos_cra_exit_ahash(struct crypto_tfm *tfm)
|
||||
if (ctx->dma_hw_context)
|
||||
dma_unmap_single(dev, ctx->dma_hw_context, size,
|
||||
DMA_BIDIRECTIONAL);
|
||||
if (ctx->dma_buf)
|
||||
dma_unmap_single(dev, ctx->dma_buf, HASH_MAX_BLOCK_SIZE,
|
||||
DMA_TO_DEVICE);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -236,6 +236,7 @@ static inline bool has_ftr_sec1(struct talitos_private *priv)
|
||||
#define TALITOS_CCCR_LO_IWSE 0x80 /* chan. ICCR writeback enab. */
|
||||
#define TALITOS_CCCR_LO_EAE 0x20 /* extended address enable */
|
||||
#define TALITOS_CCCR_LO_CDWE 0x10 /* chan. done writeback enab. */
|
||||
#define TALITOS_CCCR_LO_NE 0x8 /* fetch next descriptor enab. */
|
||||
#define TALITOS_CCCR_LO_NT 0x4 /* notification type */
|
||||
#define TALITOS_CCCR_LO_CDIE 0x2 /* channel done IRQ enable */
|
||||
#define TALITOS1_CCCR_LO_RESET 0x1 /* channel reset on SEC1 */
|
||||
|
Loading…
Reference in New Issue
Block a user