Commit a42055e8 authored by Vakul Garg's avatar Vakul Garg Committed by David S. Miller

net/tls: Add support for async encryption of records for performance

In current implementation, tls records are encrypted & transmitted
serially. Till the time the previously submitted user data is encrypted,
the implementation waits and on finish starts transmitting the record.
This approach of encrypt-one record at a time is inefficient when
asynchronous crypto accelerators are used. For each record, there are
overheads of interrupts, driver softIRQ scheduling etc. Also the crypto
accelerator sits idle most of time while an encrypted record's pages are
handed over to tcp stack for transmission.

This patch enables encryption of multiple records in parallel when an
async capable crypto accelerator is present in system. This is achieved
by allowing the user space application to send more data using sendmsg()
even while previously issued data is being processed by crypto
accelerator. This requires returning the control back to user space
application after submitting encryption request to accelerator. This
also means that zero-copy mode of encryption cannot be used with async
accelerator as we must be done with user space application buffer before
returning from sendmsg().

There can be multiple records in flight to/from the accelerator. Each of
the record is represented by 'struct tls_rec'. This is used to store the
memory pages for the record.

After the records are encrypted, they are added in a linked list called
tx_ready_list which contains encrypted tls records sorted as per tls
sequence number. The records from tx_ready_list are transmitted using a
newly introduced function called tls_tx_records(). The tx_ready_list is
polled for any record ready to be transmitted in sendmsg(), sendpage()
after initiating encryption of new tls records. This achieves parallel
encryption and transmission of records when async accelerator is

There could be situation when crypto accelerator completes encryption
later than polling of tx_ready_list by sendmsg()/sendpage(). Therefore
we need a deferred work context to be able to transmit records from
tx_ready_list. The deferred work context gets scheduled if applications
are not sending much data through the socket. If the applications issue
sendmsg()/sendpage() in quick succession, then the scheduling of
tx_work_handler gets cancelled as the tx_ready_list would be polled from
application's context itself. This saves scheduling overhead of deferred

The patch also brings some side benefit. We are able to get rid of the
concept of CLOSED record. This is because the records once closed are
either encrypted and then placed into tx_ready_list or if encryption
fails, the socket error is set. This simplifies the kernel tls
sendpath. However since tls_device.c is still using macros, accessory
functions for CLOSED records have been retained.
Signed-off-by: default avatarVakul Garg <>
Signed-off-by: default avatarDavid S. Miller <>
parent 06983aa5
......@@ -41,7 +41,7 @@
#include <linux/tcp.h>
#include <net/tcp.h>
#include <net/strparser.h>
#include <crypto/aead.h>
#include <uapi/linux/tls.h>
......@@ -93,24 +93,47 @@ enum {
struct tls_sw_context_tx {
struct crypto_aead *aead_send;
struct crypto_wait async_wait;
char aad_space[TLS_AAD_SPACE_SIZE];
unsigned int sg_plaintext_size;
int sg_plaintext_num_elem;
/* TLS records are maintained in 'struct tls_rec'. It stores the memory pages
* allocated or mapped for each TLS record. After encryption, the records are
* stores in a linked list.
struct tls_rec {
struct list_head list;
int tx_flags;
struct scatterlist sg_plaintext_data[MAX_SKB_FRAGS];
unsigned int sg_encrypted_size;
int sg_encrypted_num_elem;
struct scatterlist sg_encrypted_data[MAX_SKB_FRAGS];
/* AAD | sg_plaintext_data | sg_tag */
struct scatterlist sg_aead_in[2];
/* AAD | sg_encrypted_data (data contain overhead for hdr&iv&tag) */
struct scatterlist sg_aead_out[2];
unsigned int sg_plaintext_size;
unsigned int sg_encrypted_size;
int sg_plaintext_num_elem;
int sg_encrypted_num_elem;
char aad_space[TLS_AAD_SPACE_SIZE];
struct aead_request aead_req;
u8 aead_req_ctx[];
struct tx_work {
struct delayed_work work;
struct sock *sk;
struct tls_sw_context_tx {
struct crypto_aead *aead_send;
struct crypto_wait async_wait;
struct tx_work tx_work;
struct tls_rec *open_rec;
struct list_head tx_ready_list;
atomic_t encrypt_pending;
int async_notify;
unsigned long tx_bitmask;
struct tls_sw_context_rx {
......@@ -197,6 +220,8 @@ struct tls_context {
struct scatterlist *partially_sent_record;
u16 partially_sent_offset;
u64 tx_seq_number; /* Next TLS seqnum to be transmitted */
unsigned long flags;
bool in_tcp_sendpages;
......@@ -261,6 +286,7 @@ int tls_device_sendpage(struct sock *sk, struct page *page,
void tls_device_sk_destruct(struct sock *sk);
void tls_device_init(void);
void tls_device_cleanup(void);
int tls_tx_records(struct sock *sk, int flags);
struct tls_record_info *tls_get_record(struct tls_offload_context_tx *context,
u32 seq, u64 *p_record_sn);
......@@ -279,6 +305,9 @@ void tls_sk_destruct(struct sock *sk, struct tls_context *ctx);
int tls_push_sg(struct sock *sk, struct tls_context *ctx,
struct scatterlist *sg, u16 first_offset,
int flags);
int tls_push_partial_record(struct sock *sk, struct tls_context *ctx,
int flags);
int tls_push_pending_closed_record(struct sock *sk, struct tls_context *ctx,
int flags, long *timeo);
......@@ -312,6 +341,23 @@ static inline bool tls_is_pending_open_record(struct tls_context *tls_ctx)
return tls_ctx->pending_open_record_frags;
static inline bool is_tx_ready(struct tls_context *tls_ctx,
struct tls_sw_context_tx *ctx)
struct tls_rec *rec;
u64 seq;
rec = list_first_entry(&ctx->tx_ready_list, struct tls_rec, list);
if (!rec)
return false;
seq = be64_to_cpup((const __be64 *)&rec->aad_space);
if (seq == tls_ctx->tx_seq_number)
return true;
return false;
struct sk_buff *
tls_validate_xmit_skb(struct sock *sk, struct net_device *dev,
struct sk_buff *skb);
......@@ -141,7 +141,6 @@ int tls_push_sg(struct sock *sk,
size = sg->length;
clear_bit(TLS_PENDING_CLOSED_RECORD, &ctx->flags);
ctx->in_tcp_sendpages = false;
......@@ -193,15 +192,12 @@ int tls_proccess_cmsg(struct sock *sk, struct msghdr *msg,
return rc;
int tls_push_pending_closed_record(struct sock *sk, struct tls_context *ctx,
int flags, long *timeo)
int tls_push_partial_record(struct sock *sk, struct tls_context *ctx,
int flags)
struct scatterlist *sg;
u16 offset;
if (!tls_is_partially_sent_record(ctx))
return ctx->push_pending_record(sk, flags);
sg = ctx->partially_sent_record;
offset = ctx->partially_sent_offset;
......@@ -209,9 +205,23 @@ int tls_push_pending_closed_record(struct sock *sk, struct tls_context *ctx,
return tls_push_sg(sk, ctx, sg, offset, flags);
int tls_push_pending_closed_record(struct sock *sk,
struct tls_context *tls_ctx,
int flags, long *timeo)
struct tls_sw_context_tx *ctx = tls_sw_ctx_tx(tls_ctx);
if (tls_is_partially_sent_record(tls_ctx) ||
return tls_tx_records(sk, flags);
return tls_ctx->push_pending_record(sk, flags);
static void tls_write_space(struct sock *sk)
struct tls_context *ctx = tls_get_ctx(sk);
struct tls_sw_context_tx *tx_ctx = tls_sw_ctx_tx(ctx);
/* If in_tcp_sendpages call lower protocol write space handler
* to ensure we wake up any waiting operations there. For example
......@@ -222,20 +232,11 @@ static void tls_write_space(struct sock *sk)
if (!sk->sk_write_pending && tls_is_pending_closed_record(ctx)) {
gfp_t sk_allocation = sk->sk_allocation;
int rc;
long timeo = 0;
sk->sk_allocation = GFP_ATOMIC;
rc = tls_push_pending_closed_record(sk, ctx,
sk->sk_allocation = sk_allocation;
if (rc < 0)
/* Schedule the transmission if tx list is ready */
if (is_tx_ready(ctx, tx_ctx) && !sk->sk_write_pending) {
/* Schedule the transmission */
if (!test_and_set_bit(BIT_TX_SCHEDULED, &tx_ctx->tx_bitmask))
schedule_delayed_work(&tx_ctx->, 0);
......@@ -270,19 +271,6 @@ static void tls_sk_proto_close(struct sock *sk, long timeout)
if (!tls_complete_pending_work(sk, ctx, 0, &timeo))
tls_handle_open_record(sk, 0);
if (ctx->partially_sent_record) {
struct scatterlist *sg = ctx->partially_sent_record;
while (1) {
sk_mem_uncharge(sk, sg->length);
if (sg_is_last(sg))
/* We need these for tls_sw_fallback handling of other packets */
if (ctx->tx_conf == TLS_SW) {
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment