/* queuelock must be held */ staticint sec_send_request(struct sec_request *sec_req, struct sec_queue *queue)
{ struct sec_request_el *el, *temp; int ret = 0;
mutex_lock(&sec_req->lock);
list_for_each_entry_safe(el, temp, &sec_req->elements, head) { /* * Add to hardware queue only under following circumstances * 1) Software and hardware queue empty so no chain dependencies * 2) No dependencies as new IV - (check software queue empty * to maintain order) * 3) No dependencies because the mode does no chaining. * * In other cases first insert onto the software queue which * is then emptied as requests complete
*/ if (!queue->havesoftqueue ||
(kfifo_is_empty(&queue->softqueue) &&
sec_queue_empty(queue))) {
ret = sec_queue_send(queue, &el->req, sec_req); if (ret == -EAGAIN) { /* Wait unti we can send then try again */ /* DEAD if here - should not happen */
ret = -EBUSY; goto err_unlock;
}
} else {
kfifo_put(&queue->softqueue, el);
}
}
err_unlock:
mutex_unlock(&sec_req->lock);
sec_req_el = list_first_entry(&sec_req->elements, struct sec_request_el,
head);
icv_or_skey_en = (sec_resp->w0 & SEC_BD_W0_ICV_OR_SKEY_EN_M) >>
SEC_BD_W0_ICV_OR_SKEY_EN_S; if (sec_resp->w1 & SEC_BD_W1_BD_INVALID || icv_or_skey_en == 3) {
dev_err(dev, "Got an invalid answer %lu %d\n",
sec_resp->w1 & SEC_BD_W1_BD_INVALID,
icv_or_skey_en);
sec_req->err = -EINVAL; /* * We need to muddle on to avoid getting stuck with elements * on the queue. Error will be reported so requester so * it should be able to handle appropriately.
*/
}
spin_lock_bh(&ctx->queue->queuelock); /* Put the IV in place for chained cases */ switch (ctx->cipher_alg) { case SEC_C_AES_CBC_128: case SEC_C_AES_CBC_192: case SEC_C_AES_CBC_256: if (sec_req_el->req.w0 & SEC_BD_W0_DE)
sg_pcopy_to_buffer(sec_req_el->sgl_out,
sg_nents(sec_req_el->sgl_out),
skreq->iv,
crypto_skcipher_ivsize(atfm),
sec_req_el->el_length -
crypto_skcipher_ivsize(atfm)); else
sg_pcopy_to_buffer(sec_req_el->sgl_in,
sg_nents(sec_req_el->sgl_in),
skreq->iv,
crypto_skcipher_ivsize(atfm),
sec_req_el->el_length -
crypto_skcipher_ivsize(atfm)); /* No need to sync to the device as coherent DMA */ break; case SEC_C_AES_CTR_128: case SEC_C_AES_CTR_192: case SEC_C_AES_CTR_256:
crypto_inc(skreq->iv, 16); break; default: /* Do not update */ break;
}
if (ctx->queue->havesoftqueue &&
!kfifo_is_empty(&ctx->queue->softqueue) &&
sec_queue_empty(ctx->queue)) {
ret = kfifo_get(&ctx->queue->softqueue, &nextrequest); if (ret <= 0)
dev_err(dev, "Error getting next element from kfifo %d\n",
ret); else /* We know there is space so this cannot fail */
sec_queue_send(ctx->queue, &nextrequest->req,
nextrequest->sec_req);
} elseif (!list_empty(&ctx->backlog)) { /* Need to verify there is room first */
backlog_req = list_first_entry(&ctx->backlog,
typeof(*backlog_req),
backlog_head); if (sec_queue_can_enqueue(ctx->queue,
backlog_req->num_elements) ||
(ctx->queue->havesoftqueue &&
kfifo_avail(&ctx->queue->softqueue) >
backlog_req->num_elements)) {
sec_send_request(backlog_req, ctx->queue);
crypto_request_complete(backlog_req->req_base,
-EINPROGRESS);
list_del(&backlog_req->backlog_head);
}
}
spin_unlock_bh(&ctx->queue->queuelock);
/* * Request is done. * The dance is needed as the lock is freed in the completion
*/
mutex_lock(&sec_req->lock);
done = list_empty(&sec_req->elements);
mutex_unlock(&sec_req->lock); if (done) { if (crypto_skcipher_ivsize(atfm)) {
dma_unmap_single(dev, sec_req->dma_iv,
crypto_skcipher_ivsize(atfm),
DMA_TO_DEVICE);
}
dma_unmap_sg(dev, skreq->src, sec_req->len_in,
DMA_BIDIRECTIONAL); if (skreq->src != skreq->dst)
dma_unmap_sg(dev, skreq->dst, sec_req->len_out,
DMA_BIDIRECTIONAL);
skcipher_request_complete(skreq, sec_req->err);
}
}
for (i = 0; i < *steps - 1; i++)
sizes[i] = SEC_REQ_LIMIT;
sizes[*steps - 1] = length - SEC_REQ_LIMIT * (*steps - 1);
*split_sizes = sizes;
return 0;
}
staticint sec_map_and_split_sg(struct scatterlist *sgl, size_t *split_sizes, int steps, struct scatterlist ***splits, int **splits_nents, int sgl_len_in, struct device *dev, gfp_t gfp)
{ int ret, count;
count = dma_map_sg(dev, sgl, sgl_len_in, DMA_BIDIRECTIONAL); if (!count) return -EINVAL;
*splits = kcalloc(steps, sizeof(struct scatterlist *), gfp); if (!*splits) {
ret = -ENOMEM; goto err_unmap_sg;
}
*splits_nents = kcalloc(steps, sizeof(int), gfp); if (!*splits_nents) {
ret = -ENOMEM; goto err_free_splits;
}
/* output the scatter list before and after this */
ret = sg_split(sgl, count, 0, steps, split_sizes,
*splits, *splits_nents, gfp); if (ret) {
ret = -ENOMEM; goto err_free_splits_nents;
}
/* * Reverses the sec_map_and_split_sg call for messages not yet added to * the queues.
*/ staticvoid sec_unmap_sg_on_err(struct scatterlist *sgl, int steps, struct scatterlist **splits, int *splits_nents, int sgl_len_in, struct device *dev)
{ int i;
for (i = 0; i < steps; i++)
kfree(splits[i]);
kfree(splits_nents);
kfree(splits);
/* Writing whole u32 so no need to take care of masking */
req->w2 = ((1 << SEC_BD_W2_GRAN_NUM_S) & SEC_BD_W2_GRAN_NUM_M) |
((el_size << SEC_BD_W2_C_GRAN_SIZE_15_0_S) &
SEC_BD_W2_C_GRAN_SIZE_15_0_M);
mutex_init(&sec_req->lock);
sec_req->req_base = &skreq->base;
sec_req->err = 0; /* SGL mapping out here to allow us to break it up as necessary */
sec_req->len_in = sg_nents(skreq->src);
ret = sec_alg_alloc_and_calc_split_sizes(skreq->cryptlen, &split_sizes,
&steps, gfp); if (ret) return ret;
sec_req->num_elements = steps;
ret = sec_map_and_split_sg(skreq->src, split_sizes, steps, &splits_in,
&splits_in_nents, sec_req->len_in,
info->dev, gfp); if (ret) goto err_free_split_sizes;
if (split) {
sec_req->len_out = sg_nents(skreq->dst);
ret = sec_map_and_split_sg(skreq->dst, split_sizes, steps,
&splits_out, &splits_out_nents,
sec_req->len_out, info->dev, gfp); if (ret) goto err_unmap_in_sg;
} /* Shared info stored in seq_req - applies to all BDs */
sec_req->tfm_ctx = ctx;
sec_req->cb = sec_skcipher_alg_callback;
INIT_LIST_HEAD(&sec_req->elements);
/* * Future optimization. * In the chaining case we can't use a dma pool bounce buffer * but in the case where we know there is no chaining we can
*/ if (crypto_skcipher_ivsize(atfm)) {
sec_req->dma_iv = dma_map_single(info->dev, skreq->iv,
crypto_skcipher_ivsize(atfm),
DMA_TO_DEVICE); if (dma_mapping_error(info->dev, sec_req->dma_iv)) {
ret = -ENOMEM; goto err_unmap_out_sg;
}
}
/* Set them all up then queue - cleaner error handling. */ for (i = 0; i < steps; i++) {
el = sec_alg_alloc_and_fill_el(&ctx->req_template,
encrypt ? 1 : 0,
split_sizes[i],
skreq->src != skreq->dst,
splits_in[i], splits_in_nents[i],
split ? splits_out[i] : NULL,
split ? splits_out_nents[i] : 0,
info, gfp); if (IS_ERR(el)) {
ret = PTR_ERR(el); goto err_free_elements;
}
el->req.cipher_iv_addr_lo = lower_32_bits(sec_req->dma_iv);
el->req.cipher_iv_addr_hi = upper_32_bits(sec_req->dma_iv);
el->sec_req = sec_req;
list_add_tail(&el->head, &sec_req->elements);
}
/* * Only attempt to queue if the whole lot can fit in the queue - * we can't successfully cleanup after a partial queing so this * must succeed or fail atomically. * * Big hammer test of both software and hardware queues - could be * more refined but this is unlikely to happen so no need.
*/
/* Grab a big lock for a long time to avoid concurrency issues */
spin_lock_bh(&queue->queuelock);
/* * Can go on to queue if we have space in either: * 1) The hardware queue and no software queue * 2) The software queue * AND there is nothing in the backlog. If there is backlog we * have to only queue to the backlog queue and return busy.
*/ if ((!sec_queue_can_enqueue(queue, steps) &&
(!queue->havesoftqueue ||
kfifo_avail(&queue->softqueue) > steps)) ||
!list_empty(&ctx->backlog)) {
ret = -EBUSY; if ((skreq->base.flags & CRYPTO_TFM_REQ_MAY_BACKLOG)) {
list_add_tail(&sec_req->backlog_head, &ctx->backlog);
spin_unlock_bh(&queue->queuelock); goto out;
}
spin_unlock_bh(&queue->queuelock); goto err_free_elements;
}
ret = sec_send_request(sec_req, queue);
spin_unlock_bh(&queue->queuelock); if (ret) goto err_free_elements;
ret = -EINPROGRESS;
out: /* Cleanup - all elements in pointer arrays have been copied */
kfree(splits_in_nents);
kfree(splits_in);
kfree(splits_out_nents);
kfree(splits_out);
kfree(split_sizes); return ret;
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.