This patch adds support to add and delete Security Association
(SA) xfrm ops. Hardware maintains SA context in memory allocated
by software. Each SA context is 128 byte aligned and size of
each context is multiple of 128-byte. Add support for transport
and tunnel ipsec mode, ESP protocol, aead aes-gcm-icv16, key size
128/192/256-bits with 32bit salt.
Signed-off-by: Bharat Bhushan <[email protected]>
---
v1->v2:
- Use dma_wmb() instead of architecture specific barrier
.../marvell/octeontx2/nic/cn10k_ipsec.c | 433 +++++++++++++++++-
.../marvell/octeontx2/nic/cn10k_ipsec.h | 114 +++++
2 files changed, 546 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/cn10k_ipsec.c b/drivers/net/ethernet/marvell/octeontx2/nic/cn10k_ipsec.c
index 46db9fd94ca3..4262458342c1 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/cn10k_ipsec.c
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/cn10k_ipsec.c
@@ -153,7 +153,7 @@ static void cn10k_outb_cptlf_iq_disable(struct otx2_nic *pf)
usleep_range(10000, 20000);
if (timeout-- < 0) {
- dev_err(pf->dev, "Error CPT LF is still busy\n");
+ netdev_err(pf->netdev, "Timeout to empty IQ\n");
break;
}
} while (1);
@@ -336,6 +336,12 @@ static int cn10k_outb_cpt_clean(struct otx2_nic *pf)
/* Set inline ipsec disabled for this device */
pf->flags &= ~OTX2_FLAG_INLINE_IPSEC_ENABLED;
+ if (!bitmap_empty(pf->ipsec.sa_bitmap, CN10K_IPSEC_OUTB_MAX_SA)) {
+ netdev_err(pf->netdev, "SA installed on this device\n");
+ mutex_unlock(&pf->ipsec.lock);
+ return -EBUSY;
+ }
+
/* Disable CPTLF Instruction Queue (IQ) */
cn10k_outb_cptlf_iq_disable(pf);
@@ -356,6 +362,414 @@ static int cn10k_outb_cpt_clean(struct otx2_nic *pf)
return err;
}
+static int cn10k_outb_get_sa_index(struct otx2_nic *pf,
+ struct cn10k_tx_sa_s *sa_entry)
+{
+ u32 sa_size = pf->ipsec.sa_size;
+ u32 sa_index;
+
+ if (!sa_entry || ((void *)sa_entry < pf->ipsec.outb_sa->base))
+ return -EINVAL;
+
+ sa_index = ((void *)sa_entry - pf->ipsec.outb_sa->base) / sa_size;
+ if (sa_index >= CN10K_IPSEC_OUTB_MAX_SA)
+ return -EINVAL;
+
+ return sa_index;
+}
+
+static dma_addr_t cn10k_outb_get_sa_iova(struct otx2_nic *pf,
+ struct cn10k_tx_sa_s *sa_entry)
+{
+ u32 sa_index = cn10k_outb_get_sa_index(pf, sa_entry);
+
+ if (sa_index < 0)
+ return 0;
+ return pf->ipsec.outb_sa->iova + sa_index * pf->ipsec.sa_size;
+}
+
+static struct cn10k_tx_sa_s *cn10k_outb_alloc_sa(struct otx2_nic *pf)
+{
+ u32 sa_size = pf->ipsec.sa_size;
+ struct cn10k_tx_sa_s *sa_entry;
+ u32 sa_index;
+
+ sa_index = find_first_zero_bit(pf->ipsec.sa_bitmap,
+ CN10K_IPSEC_OUTB_MAX_SA);
+ if (sa_index == CN10K_IPSEC_OUTB_MAX_SA)
+ return NULL;
+
+ set_bit(sa_index, pf->ipsec.sa_bitmap);
+
+ sa_entry = pf->ipsec.outb_sa->base + sa_index * sa_size;
+ return sa_entry;
+}
+
+static void cn10k_outb_free_sa(struct otx2_nic *pf,
+ struct cn10k_tx_sa_s *sa_entry)
+{
+ u32 sa_index = cn10k_outb_get_sa_index(pf, sa_entry);
+
+ if (sa_index < 0)
+ return;
+ clear_bit(sa_index, pf->ipsec.sa_bitmap);
+}
+
+static void cn10k_cpt_inst_flush(struct otx2_nic *pf, struct cpt_inst_s *inst,
+ u64 size)
+{
+ struct otx2_lmt_info *lmt_info;
+ u64 val = 0, tar_addr = 0;
+
+ lmt_info = per_cpu_ptr(pf->hw.lmt_info, smp_processor_id());
+ /* FIXME: val[0:10] LMT_ID.
+ * [12:15] no of LMTST - 1 in the burst.
+ * [19:63] data size of each LMTST in the burst except first.
+ */
+ val = (lmt_info->lmt_id & 0x7FF);
+ /* Target address for LMTST flush tells HW how many 128bit
+ * words are present.
+ * tar_addr[6:4] size of first LMTST - 1 in units of 128b.
+ */
+ tar_addr |= pf->ipsec.io_addr | (((size / 16) - 1) & 0x7) << 4;
+ dma_wmb();
+ memcpy((u64 *)lmt_info->lmt_addr, inst, size);
+ cn10k_lmt_flush(val, tar_addr);
+}
+
+static int cn10k_wait_for_cpt_respose(struct otx2_nic *pf,
+ struct cpt_res_s *res)
+{
+ unsigned long timeout = jiffies + msecs_to_jiffies(10000);
+
+ do {
+ if (time_after(jiffies, timeout)) {
+ netdev_err(pf->netdev, "CPT response timeout\n");
+ return -EBUSY;
+ }
+ } while (res->compcode == CN10K_CPT_COMP_E_NOTDONE);
+
+ if (!(res->compcode == CN10K_CPT_COMP_E_GOOD ||
+ res->compcode == CN10K_CPT_COMP_E_WARN) || res->uc_compcode) {
+ netdev_err(pf->netdev, "compcode=%x doneint=%x\n",
+ res->compcode, res->doneint);
+ netdev_err(pf->netdev, "uc_compcode=%x uc_info=%llx esn=%llx\n",
+ res->uc_compcode, (u64)res->uc_info, res->esn);
+ }
+ return 0;
+}
+
+static int cn10k_outb_write_sa(struct otx2_nic *pf, struct cn10k_tx_sa_s *sa_cptr)
+{
+ dma_addr_t res_iova, dptr_iova, sa_iova;
+ struct cn10k_tx_sa_s *sa_dptr;
+ struct cpt_inst_s inst;
+ struct cpt_res_s *res;
+ u32 sa_size, off;
+ u64 reg_val;
+ int ret;
+
+ sa_iova = cn10k_outb_get_sa_iova(pf, sa_cptr);
+ if (!sa_iova)
+ return -EINVAL;
+
+ res = dma_alloc_coherent(pf->dev, sizeof(struct cpt_res_s),
+ &res_iova, GFP_ATOMIC);
+ if (!res)
+ return -ENOMEM;
+
+ sa_size = sizeof(struct cn10k_tx_sa_s);
+ sa_dptr = dma_alloc_coherent(pf->dev, sa_size, &dptr_iova, GFP_ATOMIC);
+ if (!sa_dptr) {
+ dma_free_coherent(pf->dev, sizeof(struct cpt_res_s), res,
+ res_iova);
+ return -ENOMEM;
+ }
+
+ for (off = 0; off < (sa_size / 8); off++)
+ *((u64 *)sa_dptr + off) = cpu_to_be64(*((u64 *)sa_cptr + off));
+
+ memset(&inst, 0, sizeof(struct cpt_inst_s));
+
+ res->compcode = CN10K_CPT_COMP_E_NOTDONE;
+ inst.res_addr = res_iova;
+ inst.dptr = (u64)dptr_iova;
+ inst.param2 = sa_size >> 3;
+ inst.dlen = sa_size;
+ inst.opcode_major = CN10K_IPSEC_MAJOR_OP_WRITE_SA;
+ inst.opcode_minor = CN10K_IPSEC_MINOR_OP_WRITE_SA;
+ inst.cptr = sa_iova;
+ inst.ctx_val = 1;
+ inst.egrp = CN10K_DEF_CPT_IPSEC_EGRP;
+
+ cn10k_cpt_inst_flush(pf, &inst, sizeof(struct cpt_inst_s));
+ dma_wmb();
+ ret = cn10k_wait_for_cpt_respose(pf, res);
+ if (ret)
+ goto out;
+
+ /* Trigger CTX flush to write dirty data back to DRAM */
+ reg_val = FIELD_PREP(CPT_LF_CTX_FLUSH, sa_iova >> 7);
+ otx2_write64(pf, CN10K_CPT_LF_CTX_FLUSH, reg_val);
+
+out:
+ dma_free_coherent(pf->dev, sa_size, sa_dptr, dptr_iova);
+ dma_free_coherent(pf->dev, sizeof(struct cpt_res_s), res, res_iova);
+ return ret;
+}
+
+static int cn10k_ipsec_get_hw_ctx_offset(void)
+{
+ /* Offset on Hardware-context offset in word */
+ return (offsetof(struct cn10k_tx_sa_s, hw_ctx) / sizeof(u64)) & 0x7F;
+}
+
+static int cn10k_ipsec_get_ctx_push_size(void)
+{
+ /* Context push size is round up and in multiple of 8 Byte */
+ return (roundup(offsetof(struct cn10k_tx_sa_s, hw_ctx), 8) / 8) & 0x7F;
+}
+
+static int cn10k_ipsec_get_aes_key_len(int key_len)
+{
+ if (key_len == 16)
+ return CN10K_IPSEC_SA_AES_KEY_LEN_128;
+ else if (key_len == 24)
+ return CN10K_IPSEC_SA_AES_KEY_LEN_192;
+ else
+ return CN10K_IPSEC_SA_AES_KEY_LEN_256;
+}
+
+static void cn10k_outb_prepare_sa(struct xfrm_state *x,
+ struct cn10k_tx_sa_s *sa_entry)
+{
+ int key_len = (x->aead->alg_key_len + 7) / 8;
+ struct net_device *netdev = x->xso.dev;
+ u8 *key = x->aead->alg_key;
+ struct otx2_nic *pf;
+ u32 *tmp_salt;
+ u64 *tmp_key;
+ int idx;
+
+ memset(sa_entry, 0, sizeof(struct cn10k_tx_sa_s));
+
+ /* context size, 128 Byte aligned up */
+ pf = netdev_priv(netdev);
+ sa_entry->ctx_size = (pf->ipsec.sa_size / OTX2_ALIGN) & 0xF;
+ sa_entry->hw_ctx_off = cn10k_ipsec_get_hw_ctx_offset();
+ sa_entry->ctx_push_size = cn10k_ipsec_get_ctx_push_size();
+
+ /* Ucode to skip two words of CPT_CTX_HW_S */
+ sa_entry->ctx_hdr_size = 1;
+
+ /* Allow Atomic operation (AOP) */
+ sa_entry->aop_valid = 1;
+
+ /* Outbound, ESP TRANSPORT/TUNNEL Mode, AES-GCM with AES key length
+ * 128bit.
+ */
+ sa_entry->sa_dir = CN10K_IPSEC_SA_DIR_OUTB;
+ sa_entry->ipsec_protocol = CN10K_IPSEC_SA_IPSEC_PROTO_ESP;
+ sa_entry->enc_type = CN10K_IPSEC_SA_ENCAP_TYPE_AES_GCM;
+ if (x->props.mode == XFRM_MODE_TUNNEL)
+ sa_entry->ipsec_mode = CN10K_IPSEC_SA_IPSEC_MODE_TUNNEL;
+ else
+ sa_entry->ipsec_mode = CN10K_IPSEC_SA_IPSEC_MODE_TRANSPORT;
+
+ sa_entry->spi = cpu_to_be32(x->id.spi);
+
+ /* Last 4 bytes are salt */
+ key_len -= 4;
+ sa_entry->aes_key_len = cn10k_ipsec_get_aes_key_len(key_len);
+ memcpy(sa_entry->cipher_key, key, key_len);
+ tmp_key = (u64 *)sa_entry->cipher_key;
+
+ for (idx = 0; idx < key_len / 8; idx++)
+ tmp_key[idx] = be64_to_cpu(tmp_key[idx]);
+
+ memcpy(&sa_entry->iv_gcm_salt, key + key_len, 4);
+ tmp_salt = (u32 *)&sa_entry->iv_gcm_salt;
+ *tmp_salt = be32_to_cpu(*tmp_salt);
+
+ /* Write SA context data to memory before enabling */
+ wmb();
+
+ /* Enable SA */
+ sa_entry->sa_valid = 1;
+}
+
+static int cn10k_ipsec_validate_state(struct xfrm_state *x)
+{
+ struct net_device *netdev = x->xso.dev;
+
+ if (x->props.aalgo != SADB_AALG_NONE) {
+ netdev_err(netdev, "Cannot offload authenticated xfrm states\n");
+ return -EINVAL;
+ }
+ if (x->props.ealgo != SADB_X_EALG_AES_GCM_ICV16) {
+ netdev_err(netdev, "Only AES-GCM-ICV16 xfrm state may be offloaded\n");
+ return -EINVAL;
+ }
+ if (x->props.calgo != SADB_X_CALG_NONE) {
+ netdev_err(netdev, "Cannot offload compressed xfrm states\n");
+ return -EINVAL;
+ }
+ if (x->props.flags & XFRM_STATE_ESN) {
+ netdev_err(netdev, "Cannot offload ESN xfrm states\n");
+ return -EINVAL;
+ }
+ if (x->props.family != AF_INET && x->props.family != AF_INET6) {
+ netdev_err(netdev, "Only IPv4/v6 xfrm states may be offloaded\n");
+ return -EINVAL;
+ }
+ if (x->props.mode != XFRM_MODE_TRANSPORT &&
+ x->props.mode != XFRM_MODE_TUNNEL) {
+ dev_info(&netdev->dev, "Only tunnel/transport xfrm states may be offloaded\n");
+ return -EINVAL;
+ }
+ if (x->id.proto != IPPROTO_ESP) {
+ netdev_err(netdev, "Only ESP xfrm state may be offloaded\n");
+ return -EINVAL;
+ }
+ if (x->encap) {
+ netdev_err(netdev, "Encapsulated xfrm state may not be offloaded\n");
+ return -EINVAL;
+ }
+ if (!x->aead) {
+ netdev_err(netdev, "Cannot offload xfrm states without aead\n");
+ return -EINVAL;
+ }
+
+ if (x->aead->alg_icv_len != 128) {
+ netdev_err(netdev, "Cannot offload xfrm states with AEAD ICV length other than 128bit\n");
+ return -EINVAL;
+ }
+ if (x->aead->alg_key_len != 128 + 32 &&
+ x->aead->alg_key_len != 192 + 32 &&
+ x->aead->alg_key_len != 256 + 32) {
+ netdev_err(netdev, "Cannot offload xfrm states with AEAD key length other than 128/192/256bit\n");
+ return -EINVAL;
+ }
+ if (x->tfcpad) {
+ netdev_err(netdev, "Cannot offload xfrm states with tfc padding\n");
+ return -EINVAL;
+ }
+ if (!x->geniv) {
+ netdev_err(netdev, "Cannot offload xfrm states without geniv\n");
+ return -EINVAL;
+ }
+ if (strcmp(x->geniv, "seqiv")) {
+ netdev_err(netdev, "Cannot offload xfrm states with geniv other than seqiv\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int cn10k_ipsec_add_state(struct xfrm_state *x,
+ struct netlink_ext_ack *extack)
+{
+ struct net_device *netdev = x->xso.dev;
+ struct cn10k_tx_sa_s *sa_entry;
+ struct cpt_ctx_info_s *sa_info;
+ struct otx2_nic *pf;
+ int err;
+
+ err = cn10k_ipsec_validate_state(x);
+ if (err)
+ return err;
+
+ if (x->xso.dir == XFRM_DEV_OFFLOAD_IN) {
+ netdev_err(netdev, "xfrm inbound offload not supported\n");
+ err = -ENODEV;
+ } else {
+ pf = netdev_priv(netdev);
+ if (!mutex_trylock(&pf->ipsec.lock)) {
+ netdev_err(netdev, "IPSEC device is busy\n");
+ return -EBUSY;
+ }
+
+ if (!(pf->flags & OTX2_FLAG_INLINE_IPSEC_ENABLED)) {
+ netdev_err(netdev, "IPSEC not enabled/supported on device\n");
+ err = -ENODEV;
+ goto unlock;
+ }
+
+ sa_entry = cn10k_outb_alloc_sa(pf);
+ if (!sa_entry) {
+ netdev_err(netdev, "SA maximum limit %x reached\n",
+ CN10K_IPSEC_OUTB_MAX_SA);
+ err = -EBUSY;
+ goto unlock;
+ }
+
+ cn10k_outb_prepare_sa(x, sa_entry);
+
+ err = cn10k_outb_write_sa(pf, sa_entry);
+ if (err) {
+ netdev_err(netdev, "Error writing outbound SA\n");
+ cn10k_outb_free_sa(pf, sa_entry);
+ goto unlock;
+ }
+
+ sa_info = kmalloc(sizeof(*sa_info), GFP_KERNEL);
+ sa_info->sa_entry = sa_entry;
+ sa_info->sa_iova = cn10k_outb_get_sa_iova(pf, sa_entry);
+ x->xso.offload_handle = (unsigned long)sa_info;
+ }
+
+unlock:
+ mutex_unlock(&pf->ipsec.lock);
+ return err;
+}
+
+static void cn10k_ipsec_del_state(struct xfrm_state *x)
+{
+ struct net_device *netdev = x->xso.dev;
+ struct cn10k_tx_sa_s *sa_entry;
+ struct cpt_ctx_info_s *sa_info;
+ struct otx2_nic *pf;
+ u32 sa_index;
+
+ if (x->xso.dir == XFRM_DEV_OFFLOAD_IN)
+ return;
+
+ pf = netdev_priv(netdev);
+ if (!mutex_trylock(&pf->ipsec.lock)) {
+ netdev_err(netdev, "IPSEC device is busy\n");
+ return;
+ }
+
+ sa_info = (struct cpt_ctx_info_s *)x->xso.offload_handle;
+ sa_entry = sa_info->sa_entry;
+ sa_index = cn10k_outb_get_sa_index(pf, sa_entry);
+ if (sa_index < 0 || !test_bit(sa_index, pf->ipsec.sa_bitmap)) {
+ netdev_err(netdev, "Invalid SA (sa-index %d)\n", sa_index);
+ goto error;
+ }
+
+ memset(sa_entry, 0, sizeof(struct cn10k_tx_sa_s));
+
+ /* Disable SA in CPT h/w */
+ sa_entry->ctx_push_size = cn10k_ipsec_get_ctx_push_size();
+ sa_entry->ctx_size = (pf->ipsec.sa_size / OTX2_ALIGN) & 0xF;
+ sa_entry->aop_valid = 1;
+
+ if (cn10k_outb_write_sa(pf, sa_entry)) {
+ netdev_err(netdev, "Failed to delete sa index %d\n", sa_index);
+ goto error;
+ }
+ x->xso.offload_handle = 0;
+ clear_bit(sa_index, pf->ipsec.sa_bitmap);
+ kfree(sa_info);
+error:
+ mutex_unlock(&pf->ipsec.lock);
+}
+
+static const struct xfrmdev_ops cn10k_ipsec_xfrmdev_ops = {
+ .xdo_dev_state_add = cn10k_ipsec_add_state,
+ .xdo_dev_state_delete = cn10k_ipsec_del_state,
+};
+
int cn10k_ipsec_ethtool_init(struct net_device *netdev, bool enable)
{
struct otx2_nic *pf = netdev_priv(netdev);
@@ -374,10 +788,25 @@ int cn10k_ipsec_ethtool_init(struct net_device *netdev, bool enable)
int cn10k_ipsec_init(struct net_device *netdev)
{
struct otx2_nic *pf = netdev_priv(netdev);
+ u32 sa_size;
+ int err;
if (!is_dev_support_inline_ipsec(pf->pdev))
return 0;
+ /* Each SA entry size is 128 Byte round up in size */
+ sa_size = sizeof(struct cn10k_tx_sa_s) % OTX2_ALIGN ?
+ (sizeof(struct cn10k_tx_sa_s) / OTX2_ALIGN + 1) *
+ OTX2_ALIGN : sizeof(struct cn10k_tx_sa_s);
+ err = qmem_alloc(pf->dev, &pf->ipsec.outb_sa, CN10K_IPSEC_OUTB_MAX_SA,
+ sa_size);
+ if (err)
+ return err;
+
+ pf->ipsec.sa_size = sa_size;
+ memset(pf->ipsec.outb_sa->base, 0, sa_size * CN10K_IPSEC_OUTB_MAX_SA);
+ bitmap_zero(pf->ipsec.sa_bitmap, CN10K_IPSEC_OUTB_MAX_SA);
+
mutex_init(&pf->ipsec.lock);
return 0;
}
@@ -388,6 +817,8 @@ void cn10k_ipsec_clean(struct otx2_nic *pf)
if (!is_dev_support_inline_ipsec(pf->pdev))
return;
+ bitmap_zero(pf->ipsec.sa_bitmap, CN10K_IPSEC_OUTB_MAX_SA);
+ qmem_free(pf->dev, pf->ipsec.outb_sa);
cn10k_outb_cpt_clean(pf);
}
EXPORT_SYMBOL(cn10k_ipsec_clean);
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/cn10k_ipsec.h b/drivers/net/ethernet/marvell/octeontx2/nic/cn10k_ipsec.h
index 679583c3a18a..d4fab47b7845 100644
--- a/drivers/net/ethernet/marvell/octeontx2/nic/cn10k_ipsec.h
+++ b/drivers/net/ethernet/marvell/octeontx2/nic/cn10k_ipsec.h
@@ -50,6 +50,22 @@
#define CN10K_CPT_LF_NQX(a) (CPT_LFBASE | 0x400 | (a) << 3)
#define CN10K_CPT_LF_CTX_FLUSH (CPT_LFBASE | 0x510)
+/* Outbound SA */
+#define CN10K_IPSEC_OUTB_MAX_SA 64
+
+/* IPSEC Instruction opcodes */
+#define CN10K_IPSEC_MAJOR_OP_WRITE_SA 0x01UL
+#define CN10K_IPSEC_MINOR_OP_WRITE_SA 0x09UL
+
+enum cn10k_cpt_comp_e {
+ CN10K_CPT_COMP_E_NOTDONE = 0x00,
+ CN10K_CPT_COMP_E_GOOD = 0x01,
+ CN10K_CPT_COMP_E_FAULT = 0x02,
+ CN10K_CPT_COMP_E_HWERR = 0x04,
+ CN10K_CPT_COMP_E_INSTERR = 0x05,
+ CN10K_CPT_COMP_E_WARN = 0x06
+};
+
struct cn10k_cpt_inst_queue {
u8 *vaddr;
u8 *real_vaddr;
@@ -64,6 +80,101 @@ struct cn10k_ipsec {
/* Lock to protect SA management */
struct mutex lock;
struct cn10k_cpt_inst_queue iq;
+ /* SA info */
+ struct qmem *outb_sa;
+ u32 sa_size;
+ DECLARE_BITMAP(sa_bitmap, CN10K_IPSEC_OUTB_MAX_SA);
+};
+
+/* CN10K IPSEC Security Association (SA) */
+/* SA direction */
+#define CN10K_IPSEC_SA_DIR_INB 0
+#define CN10K_IPSEC_SA_DIR_OUTB 1
+/* SA protocol */
+#define CN10K_IPSEC_SA_IPSEC_PROTO_AH 0
+#define CN10K_IPSEC_SA_IPSEC_PROTO_ESP 1
+/* SA Encryption Type */
+#define CN10K_IPSEC_SA_ENCAP_TYPE_AES_GCM 5
+/* SA IPSEC mode Transport/Tunnel */
+#define CN10K_IPSEC_SA_IPSEC_MODE_TRANSPORT 0
+#define CN10K_IPSEC_SA_IPSEC_MODE_TUNNEL 1
+/* SA AES Key Length */
+#define CN10K_IPSEC_SA_AES_KEY_LEN_128 1
+#define CN10K_IPSEC_SA_AES_KEY_LEN_192 2
+#define CN10K_IPSEC_SA_AES_KEY_LEN_256 3
+
+struct cn10k_tx_sa_s {
+ u64 esn_en : 1; /* W0 */
+ u64 rsvd_w0_1_8 : 8;
+ u64 hw_ctx_off : 7;
+ u64 ctx_id : 16;
+ u64 rsvd_w0_32_47 : 16;
+ u64 ctx_push_size : 7;
+ u64 rsvd_w0_55 : 1;
+ u64 ctx_hdr_size : 2;
+ u64 aop_valid : 1;
+ u64 rsvd_w0_59 : 1;
+ u64 ctx_size : 4;
+ u64 w1; /* W1 */
+ u64 sa_valid : 1; /* W2 */
+ u64 sa_dir : 1;
+ u64 rsvd_w2_2_3 : 2;
+ u64 ipsec_mode : 1;
+ u64 ipsec_protocol : 1;
+ u64 aes_key_len : 2;
+ u64 enc_type : 3;
+ u64 rsvd_w2_11_31 : 21;
+ u64 spi : 32;
+ u64 w3; /* W3 */
+ u8 cipher_key[32]; /* W4 - W7 */
+ u32 rsvd_w8_0_31; /* W8 : IV */
+ u32 iv_gcm_salt;
+ u64 rsvd_w9_w30[22]; /* W9 - W30 */
+ u64 hw_ctx[6]; /* W31 - W36 */
+};
+
+/* CPT Instruction Structure */
+struct cpt_inst_s {
+ u64 nixtxl : 3; /* W0 */
+ u64 doneint : 1;
+ u64 rsvd_w0_4_15 : 12;
+ u64 dat_offset : 8;
+ u64 ext_param1 : 8;
+ u64 nixtx_offset : 20;
+ u64 rsvd_w0_52_63 : 12;
+ u64 res_addr; /* W1 */
+ u64 tag : 32; /* W2 */
+ u64 tt : 2;
+ u64 grp : 10;
+ u64 rsvd_w2_44_47 : 4;
+ u64 rvu_pf_func : 16;
+ u64 qord : 1; /* W3 */
+ u64 rsvd_w3_1_2 : 2;
+ u64 wqe_ptr : 61;
+ u64 dlen : 16; /* W4 */
+ u64 param2 : 16;
+ u64 param1 : 16;
+ u64 opcode_major : 8;
+ u64 opcode_minor : 8;
+ u64 dptr; /* W5 */
+ u64 rptr; /* W6 */
+ u64 cptr : 60; /* W7 */
+ u64 ctx_val : 1;
+ u64 egrp : 3;
+};
+
+/* CPT Instruction Result Structure */
+struct cpt_res_s {
+ u64 compcode : 7; /* W0 */
+ u64 doneint : 1;
+ u64 uc_compcode : 8;
+ u64 uc_info : 48;
+ u64 esn; /* W1 */
+};
+
+struct cpt_ctx_info_s {
+ struct cn10k_tx_sa_s *sa_entry;
+ dma_addr_t sa_iova;
};
/* CPT LF_INPROG Register */
@@ -81,6 +192,9 @@ struct cn10k_ipsec {
/* CPT LF_Q_SIZE Register */
#define CPT_LF_Q_SIZE_DIV40 GENMASK_ULL(14, 0)
+/* CPT LF CTX Flush Register */
+#define CPT_LF_CTX_FLUSH GENMASK_ULL(45, 0)
+
#ifdef CONFIG_XFRM_OFFLOAD
int cn10k_ipsec_init(struct net_device *netdev);
void cn10k_ipsec_clean(struct otx2_nic *pf);
--
2.34.1
On Mon, May 13, 2024 at 04:24:43PM +0530, Bharat Bhushan wrote:
> This patch adds support to add and delete Security Association
> (SA) xfrm ops. Hardware maintains SA context in memory allocated
> by software. Each SA context is 128 byte aligned and size of
> each context is multiple of 128-byte. Add support for transport
> and tunnel ipsec mode, ESP protocol, aead aes-gcm-icv16, key size
> 128/192/256-bits with 32bit salt.
>
> Signed-off-by: Bharat Bhushan <[email protected]>
> ---
> v1->v2:
> - Use dma_wmb() instead of architecture specific barrier
>
> .../marvell/octeontx2/nic/cn10k_ipsec.c | 433 +++++++++++++++++-
> .../marvell/octeontx2/nic/cn10k_ipsec.h | 114 +++++
> 2 files changed, 546 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/cn10k_ipsec.c b/drivers/net/ethernet/marvell/octeontx2/nic/cn10k_ipsec.c
..
> @@ -356,6 +362,414 @@ static int cn10k_outb_cpt_clean(struct otx2_nic *pf)
> return err;
> }
>
> +static int cn10k_outb_get_sa_index(struct otx2_nic *pf,
> + struct cn10k_tx_sa_s *sa_entry)
> +{
> + u32 sa_size = pf->ipsec.sa_size;
> + u32 sa_index;
> +
> + if (!sa_entry || ((void *)sa_entry < pf->ipsec.outb_sa->base))
> + return -EINVAL;
> +
> + sa_index = ((void *)sa_entry - pf->ipsec.outb_sa->base) / sa_size;
> + if (sa_index >= CN10K_IPSEC_OUTB_MAX_SA)
> + return -EINVAL;
> +
> + return sa_index;
> +}
> +
> +static dma_addr_t cn10k_outb_get_sa_iova(struct otx2_nic *pf,
> + struct cn10k_tx_sa_s *sa_entry)
> +{
> + u32 sa_index = cn10k_outb_get_sa_index(pf, sa_entry);
> +
> + if (sa_index < 0)
> + return 0;
Should the type of sa_index be int?
That would match the return type of cn10k_outb_get_sa_index.
Otherwise, testing for < 0 will always be false.
Likewise in cn10k_outb_free_sa and cn10k_ipsec_del_state.
Flagged by Smatch.
> + return pf->ipsec.outb_sa->iova + sa_index * pf->ipsec.sa_size;
> +}
..
> +static int cn10k_outb_write_sa(struct otx2_nic *pf, struct cn10k_tx_sa_s *sa_cptr)
> +{
> + dma_addr_t res_iova, dptr_iova, sa_iova;
> + struct cn10k_tx_sa_s *sa_dptr;
> + struct cpt_inst_s inst;
> + struct cpt_res_s *res;
> + u32 sa_size, off;
> + u64 reg_val;
> + int ret;
> +
> + sa_iova = cn10k_outb_get_sa_iova(pf, sa_cptr);
> + if (!sa_iova)
> + return -EINVAL;
> +
> + res = dma_alloc_coherent(pf->dev, sizeof(struct cpt_res_s),
> + &res_iova, GFP_ATOMIC);
> + if (!res)
> + return -ENOMEM;
> +
> + sa_size = sizeof(struct cn10k_tx_sa_s);
> + sa_dptr = dma_alloc_coherent(pf->dev, sa_size, &dptr_iova, GFP_ATOMIC);
> + if (!sa_dptr) {
> + dma_free_coherent(pf->dev, sizeof(struct cpt_res_s), res,
> + res_iova);
> + return -ENOMEM;
> + }
> +
> + for (off = 0; off < (sa_size / 8); off++)
> + *((u64 *)sa_dptr + off) = cpu_to_be64(*((u64 *)sa_cptr + off));
Given the layout of struct cn10k_tx_sa_s, it's not clear
to me how it makes sense for it to be used to store big endian quadwords.
Which is a something that probably ought to be addressed.
But if not, Sparse complains about the endienness of the types used
above. I think it wants:
*((__be64 *)sa_dptr + off)
> +
> + memset(&inst, 0, sizeof(struct cpt_inst_s));
> +
> + res->compcode = CN10K_CPT_COMP_E_NOTDONE;
> + inst.res_addr = res_iova;
> + inst.dptr = (u64)dptr_iova;
> + inst.param2 = sa_size >> 3;
> + inst.dlen = sa_size;
> + inst.opcode_major = CN10K_IPSEC_MAJOR_OP_WRITE_SA;
> + inst.opcode_minor = CN10K_IPSEC_MINOR_OP_WRITE_SA;
> + inst.cptr = sa_iova;
> + inst.ctx_val = 1;
> + inst.egrp = CN10K_DEF_CPT_IPSEC_EGRP;
> +
> + cn10k_cpt_inst_flush(pf, &inst, sizeof(struct cpt_inst_s));
> + dma_wmb();
> + ret = cn10k_wait_for_cpt_respose(pf, res);
> + if (ret)
> + goto out;
> +
> + /* Trigger CTX flush to write dirty data back to DRAM */
> + reg_val = FIELD_PREP(CPT_LF_CTX_FLUSH, sa_iova >> 7);
> + otx2_write64(pf, CN10K_CPT_LF_CTX_FLUSH, reg_val);
> +
> +out:
> + dma_free_coherent(pf->dev, sa_size, sa_dptr, dptr_iova);
> + dma_free_coherent(pf->dev, sizeof(struct cpt_res_s), res, res_iova);
> + return ret;
> +}
..
> +static void cn10k_outb_prepare_sa(struct xfrm_state *x,
> + struct cn10k_tx_sa_s *sa_entry)
> +{
> + int key_len = (x->aead->alg_key_len + 7) / 8;
> + struct net_device *netdev = x->xso.dev;
> + u8 *key = x->aead->alg_key;
> + struct otx2_nic *pf;
> + u32 *tmp_salt;
> + u64 *tmp_key;
> + int idx;
> +
> + memset(sa_entry, 0, sizeof(struct cn10k_tx_sa_s));
> +
> + /* context size, 128 Byte aligned up */
> + pf = netdev_priv(netdev);
> + sa_entry->ctx_size = (pf->ipsec.sa_size / OTX2_ALIGN) & 0xF;
> + sa_entry->hw_ctx_off = cn10k_ipsec_get_hw_ctx_offset();
> + sa_entry->ctx_push_size = cn10k_ipsec_get_ctx_push_size();
> +
> + /* Ucode to skip two words of CPT_CTX_HW_S */
> + sa_entry->ctx_hdr_size = 1;
> +
> + /* Allow Atomic operation (AOP) */
> + sa_entry->aop_valid = 1;
> +
> + /* Outbound, ESP TRANSPORT/TUNNEL Mode, AES-GCM with AES key length
> + * 128bit.
> + */
> + sa_entry->sa_dir = CN10K_IPSEC_SA_DIR_OUTB;
> + sa_entry->ipsec_protocol = CN10K_IPSEC_SA_IPSEC_PROTO_ESP;
> + sa_entry->enc_type = CN10K_IPSEC_SA_ENCAP_TYPE_AES_GCM;
> + if (x->props.mode == XFRM_MODE_TUNNEL)
> + sa_entry->ipsec_mode = CN10K_IPSEC_SA_IPSEC_MODE_TUNNEL;
> + else
> + sa_entry->ipsec_mode = CN10K_IPSEC_SA_IPSEC_MODE_TRANSPORT;
> +
> + sa_entry->spi = cpu_to_be32(x->id.spi);
The type of spi is a 32-bit bitfield of a 64-bit unsigned host endien integer.
1. I suspect it would make more sense to declare that field as a 32bit integer.
2. It is being assigned a big endian value. That doesn't seem right.
The second issue was flagged by Sparse.
> +
> + /* Last 4 bytes are salt */
> + key_len -= 4;
> + sa_entry->aes_key_len = cn10k_ipsec_get_aes_key_len(key_len);
> + memcpy(sa_entry->cipher_key, key, key_len);
> + tmp_key = (u64 *)sa_entry->cipher_key;
> +
> + for (idx = 0; idx < key_len / 8; idx++)
> + tmp_key[idx] = be64_to_cpu(tmp_key[idx]);
More endian problems flagged by Sparse on this line.
An integer variable should typically be used to store
a big endian value, a little endian value, or a host endian value.
Not more than one of these.
This is because tooling such as Sparse can then be used to verify
the correctness of the endian used.
> +
> + memcpy(&sa_entry->iv_gcm_salt, key + key_len, 4);
> + tmp_salt = (u32 *)&sa_entry->iv_gcm_salt;
> + *tmp_salt = be32_to_cpu(*tmp_salt);
Likewise here.
> +
> + /* Write SA context data to memory before enabling */
> + wmb();
> +
> + /* Enable SA */
> + sa_entry->sa_valid = 1;
> +}
..
> +static int cn10k_ipsec_add_state(struct xfrm_state *x,
> + struct netlink_ext_ack *extack)
> +{
> + struct net_device *netdev = x->xso.dev;
> + struct cn10k_tx_sa_s *sa_entry;
> + struct cpt_ctx_info_s *sa_info;
> + struct otx2_nic *pf;
> + int err;
> +
> + err = cn10k_ipsec_validate_state(x);
> + if (err)
> + return err;
> +
> + if (x->xso.dir == XFRM_DEV_OFFLOAD_IN) {
> + netdev_err(netdev, "xfrm inbound offload not supported\n");
> + err = -ENODEV;
This path results in pf being dereferenced while uninitialised
towards the bottom of this function.
Flagged by Smatch, and Clang-18 W=1 build
> + } else {
> + pf = netdev_priv(netdev);
> + if (!mutex_trylock(&pf->ipsec.lock)) {
> + netdev_err(netdev, "IPSEC device is busy\n");
> + return -EBUSY;
> + }
> +
> + if (!(pf->flags & OTX2_FLAG_INLINE_IPSEC_ENABLED)) {
> + netdev_err(netdev, "IPSEC not enabled/supported on device\n");
> + err = -ENODEV;
> + goto unlock;
> + }
> +
> + sa_entry = cn10k_outb_alloc_sa(pf);
> + if (!sa_entry) {
> + netdev_err(netdev, "SA maximum limit %x reached\n",
> + CN10K_IPSEC_OUTB_MAX_SA);
> + err = -EBUSY;
> + goto unlock;
> + }
> +
> + cn10k_outb_prepare_sa(x, sa_entry);
> +
> + err = cn10k_outb_write_sa(pf, sa_entry);
> + if (err) {
> + netdev_err(netdev, "Error writing outbound SA\n");
> + cn10k_outb_free_sa(pf, sa_entry);
> + goto unlock;
> + }
> +
> + sa_info = kmalloc(sizeof(*sa_info), GFP_KERNEL);
> + sa_info->sa_entry = sa_entry;
> + sa_info->sa_iova = cn10k_outb_get_sa_iova(pf, sa_entry);
> + x->xso.offload_handle = (unsigned long)sa_info;
> + }
> +
> +unlock:
> + mutex_unlock(&pf->ipsec.lock);
> + return err;
> +}
..
> +static const struct xfrmdev_ops cn10k_ipsec_xfrmdev_ops = {
> + .xdo_dev_state_add = cn10k_ipsec_add_state,
> + .xdo_dev_state_delete = cn10k_ipsec_del_state,
> +};
> +
cn10k_ipsec_xfrmdev_ops is unused.
Perhaps it, along with it's callbacks,
should be added by the function that uses it?
Flagged by W=1 builds.
> int cn10k_ipsec_ethtool_init(struct net_device *netdev, bool enable)
> {
> struct otx2_nic *pf = netdev_priv(netdev);
..
> diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/cn10k_ipsec.h b/drivers/net/ethernet/marvell/octeontx2/nic/cn10k_ipsec.h
..
> +struct cn10k_tx_sa_s {
> + u64 esn_en : 1; /* W0 */
> + u64 rsvd_w0_1_8 : 8;
> + u64 hw_ctx_off : 7;
> + u64 ctx_id : 16;
> + u64 rsvd_w0_32_47 : 16;
> + u64 ctx_push_size : 7;
> + u64 rsvd_w0_55 : 1;
> + u64 ctx_hdr_size : 2;
> + u64 aop_valid : 1;
> + u64 rsvd_w0_59 : 1;
> + u64 ctx_size : 4;
> + u64 w1; /* W1 */
> + u64 sa_valid : 1; /* W2 */
> + u64 sa_dir : 1;
> + u64 rsvd_w2_2_3 : 2;
> + u64 ipsec_mode : 1;
> + u64 ipsec_protocol : 1;
> + u64 aes_key_len : 2;
> + u64 enc_type : 3;
> + u64 rsvd_w2_11_31 : 21;
> + u64 spi : 32;
> + u64 w3; /* W3 */
> + u8 cipher_key[32]; /* W4 - W7 */
> + u32 rsvd_w8_0_31; /* W8 : IV */
> + u32 iv_gcm_salt;
> + u64 rsvd_w9_w30[22]; /* W9 - W30 */
> + u64 hw_ctx[6]; /* W31 - W36 */
> +};
..
Please see inline
> -----Original Message-----
> From: Simon Horman <[email protected]>
> Sent: Monday, May 13, 2024 10:22 PM
> To: Bharat Bhushan <[email protected]>
> Cc: [email protected]; [email protected]; Sunil Kovvuri
> Goutham <[email protected]>; Geethasowjanya Akula
> <[email protected]>; Subbaraya Sundeep Bhatta <[email protected]>;
> Hariprasad Kelam <[email protected]>; [email protected];
> [email protected]; [email protected]; [email protected]; Jerin Jacob
> <[email protected]>; Linu Cherian <[email protected]>;
> [email protected]
> Subject: [EXTERNAL] Re: [net-next,v2 5/8] cn10k-ipsec: Add SA add/delete
> support for outb inline ipsec
>
>
> ----------------------------------------------------------------------
> On Mon, May 13, 2024 at 04:24:43PM +0530, Bharat Bhushan wrote:
> > This patch adds support to add and delete Security Association
> > (SA) xfrm ops. Hardware maintains SA context in memory allocated by
> > software. Each SA context is 128 byte aligned and size of each context
> > is multiple of 128-byte. Add support for transport and tunnel ipsec
> > mode, ESP protocol, aead aes-gcm-icv16, key size 128/192/256-bits with
> > 32bit salt.
> >
> > Signed-off-by: Bharat Bhushan <[email protected]>
> > ---
> > v1->v2:
> > - Use dma_wmb() instead of architecture specific barrier
> >
> > .../marvell/octeontx2/nic/cn10k_ipsec.c | 433 +++++++++++++++++-
> > .../marvell/octeontx2/nic/cn10k_ipsec.h | 114 +++++
> > 2 files changed, 546 insertions(+), 1 deletion(-)
> >
> > diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/cn10k_ipsec.c
> > b/drivers/net/ethernet/marvell/octeontx2/nic/cn10k_ipsec.c
>
> ...
>
> > @@ -356,6 +362,414 @@ static int cn10k_outb_cpt_clean(struct otx2_nic
> *pf)
> > return err;
> > }
> >
> > +static int cn10k_outb_get_sa_index(struct otx2_nic *pf,
> > + struct cn10k_tx_sa_s *sa_entry) {
> > + u32 sa_size = pf->ipsec.sa_size;
> > + u32 sa_index;
> > +
> > + if (!sa_entry || ((void *)sa_entry < pf->ipsec.outb_sa->base))
> > + return -EINVAL;
> > +
> > + sa_index = ((void *)sa_entry - pf->ipsec.outb_sa->base) / sa_size;
> > + if (sa_index >= CN10K_IPSEC_OUTB_MAX_SA)
> > + return -EINVAL;
> > +
> > + return sa_index;
> > +}
> > +
> > +static dma_addr_t cn10k_outb_get_sa_iova(struct otx2_nic *pf,
> > + struct cn10k_tx_sa_s *sa_entry) {
> > + u32 sa_index = cn10k_outb_get_sa_index(pf, sa_entry);
> > +
> > + if (sa_index < 0)
> > + return 0;
>
> Should the type of sa_index be int?
> That would match the return type of cn10k_outb_get_sa_index.
>
> Otherwise, testing for < 0 will always be false.
>
> Likewise in cn10k_outb_free_sa and cn10k_ipsec_del_state.
>
> Flagged by Smatch.
Fill fix all error reported by smatch in next version.
>
> > + return pf->ipsec.outb_sa->iova + sa_index * pf->ipsec.sa_size; }
>
> ...
>
> > +static int cn10k_outb_write_sa(struct otx2_nic *pf, struct
> > +cn10k_tx_sa_s *sa_cptr) {
> > + dma_addr_t res_iova, dptr_iova, sa_iova;
> > + struct cn10k_tx_sa_s *sa_dptr;
> > + struct cpt_inst_s inst;
> > + struct cpt_res_s *res;
> > + u32 sa_size, off;
> > + u64 reg_val;
> > + int ret;
> > +
> > + sa_iova = cn10k_outb_get_sa_iova(pf, sa_cptr);
> > + if (!sa_iova)
> > + return -EINVAL;
> > +
> > + res = dma_alloc_coherent(pf->dev, sizeof(struct cpt_res_s),
> > + &res_iova, GFP_ATOMIC);
> > + if (!res)
> > + return -ENOMEM;
> > +
> > + sa_size = sizeof(struct cn10k_tx_sa_s);
> > + sa_dptr = dma_alloc_coherent(pf->dev, sa_size, &dptr_iova,
> GFP_ATOMIC);
> > + if (!sa_dptr) {
> > + dma_free_coherent(pf->dev, sizeof(struct cpt_res_s), res,
> > + res_iova);
> > + return -ENOMEM;
> > + }
> > +
> > + for (off = 0; off < (sa_size / 8); off++)
> > + *((u64 *)sa_dptr + off) = cpu_to_be64(*((u64 *)sa_cptr +
> off));
>
> Given the layout of struct cn10k_tx_sa_s, it's not clear to me how it makes
> sense for it to be used to store big endian quadwords.
> Which is a something that probably ought to be addressed.
>
> But if not, Sparse complains about the endienness of the types used above I
> think it wants:
>
> *((__be64 *)sa_dptr + off)
Will fix this and other errors reported by Sparse.
>
> > +
> > + memset(&inst, 0, sizeof(struct cpt_inst_s));
> > +
> > + res->compcode = CN10K_CPT_COMP_E_NOTDONE;
> > + inst.res_addr = res_iova;
> > + inst.dptr = (u64)dptr_iova;
> > + inst.param2 = sa_size >> 3;
> > + inst.dlen = sa_size;
> > + inst.opcode_major = CN10K_IPSEC_MAJOR_OP_WRITE_SA;
> > + inst.opcode_minor = CN10K_IPSEC_MINOR_OP_WRITE_SA;
> > + inst.cptr = sa_iova;
> > + inst.ctx_val = 1;
> > + inst.egrp = CN10K_DEF_CPT_IPSEC_EGRP;
> > +
> > + cn10k_cpt_inst_flush(pf, &inst, sizeof(struct cpt_inst_s));
> > + dma_wmb();
> > + ret = cn10k_wait_for_cpt_respose(pf, res);
> > + if (ret)
> > + goto out;
> > +
> > + /* Trigger CTX flush to write dirty data back to DRAM */
> > + reg_val = FIELD_PREP(CPT_LF_CTX_FLUSH, sa_iova >> 7);
> > + otx2_write64(pf, CN10K_CPT_LF_CTX_FLUSH, reg_val);
> > +
> > +out:
> > + dma_free_coherent(pf->dev, sa_size, sa_dptr, dptr_iova);
> > + dma_free_coherent(pf->dev, sizeof(struct cpt_res_s), res, res_iova);
> > + return ret;
> > +}
>
> ...
>
> > +static void cn10k_outb_prepare_sa(struct xfrm_state *x,
> > + struct cn10k_tx_sa_s *sa_entry) {
> > + int key_len = (x->aead->alg_key_len + 7) / 8;
> > + struct net_device *netdev = x->xso.dev;
> > + u8 *key = x->aead->alg_key;
> > + struct otx2_nic *pf;
> > + u32 *tmp_salt;
> > + u64 *tmp_key;
> > + int idx;
> > +
> > + memset(sa_entry, 0, sizeof(struct cn10k_tx_sa_s));
> > +
> > + /* context size, 128 Byte aligned up */
> > + pf = netdev_priv(netdev);
> > + sa_entry->ctx_size = (pf->ipsec.sa_size / OTX2_ALIGN) & 0xF;
> > + sa_entry->hw_ctx_off = cn10k_ipsec_get_hw_ctx_offset();
> > + sa_entry->ctx_push_size = cn10k_ipsec_get_ctx_push_size();
> > +
> > + /* Ucode to skip two words of CPT_CTX_HW_S */
> > + sa_entry->ctx_hdr_size = 1;
> > +
> > + /* Allow Atomic operation (AOP) */
> > + sa_entry->aop_valid = 1;
> > +
> > + /* Outbound, ESP TRANSPORT/TUNNEL Mode, AES-GCM with AES
> key length
> > + * 128bit.
> > + */
> > + sa_entry->sa_dir = CN10K_IPSEC_SA_DIR_OUTB;
> > + sa_entry->ipsec_protocol = CN10K_IPSEC_SA_IPSEC_PROTO_ESP;
> > + sa_entry->enc_type = CN10K_IPSEC_SA_ENCAP_TYPE_AES_GCM;
> > + if (x->props.mode == XFRM_MODE_TUNNEL)
> > + sa_entry->ipsec_mode =
> CN10K_IPSEC_SA_IPSEC_MODE_TUNNEL;
> > + else
> > + sa_entry->ipsec_mode =
> CN10K_IPSEC_SA_IPSEC_MODE_TRANSPORT;
> > +
> > + sa_entry->spi = cpu_to_be32(x->id.spi);
>
> The type of spi is a 32-bit bitfield of a 64-bit unsigned host endien integer.
>
> 1. I suspect it would make more sense to declare that field as a 32bit integer.
> 2. It is being assigned a big endian value. That doesn't seem right.
>
> The second issue was flagged by Sparse.
>
> > +
> > + /* Last 4 bytes are salt */
> > + key_len -= 4;
> > + sa_entry->aes_key_len = cn10k_ipsec_get_aes_key_len(key_len);
> > + memcpy(sa_entry->cipher_key, key, key_len);
> > + tmp_key = (u64 *)sa_entry->cipher_key;
> > +
> > + for (idx = 0; idx < key_len / 8; idx++)
> > + tmp_key[idx] = be64_to_cpu(tmp_key[idx]);
>
> More endian problems flagged by Sparse on this line.
> An integer variable should typically be used to store a big endian value, a little
> endian value, or a host endian value.
> Not more than one of these.
>
> This is because tooling such as Sparse can then be used to verify the
> correctness of the endian used.
Will fix in next version
>
> > +
> > + memcpy(&sa_entry->iv_gcm_salt, key + key_len, 4);
> > + tmp_salt = (u32 *)&sa_entry->iv_gcm_salt;
> > + *tmp_salt = be32_to_cpu(*tmp_salt);
>
> Likewise here.
>
> > +
> > + /* Write SA context data to memory before enabling */
> > + wmb();
> > +
> > + /* Enable SA */
> > + sa_entry->sa_valid = 1;
> > +}
>
> ...
>
> > +static int cn10k_ipsec_add_state(struct xfrm_state *x,
> > + struct netlink_ext_ack *extack)
> > +{
> > + struct net_device *netdev = x->xso.dev;
> > + struct cn10k_tx_sa_s *sa_entry;
> > + struct cpt_ctx_info_s *sa_info;
> > + struct otx2_nic *pf;
> > + int err;
> > +
> > + err = cn10k_ipsec_validate_state(x);
> > + if (err)
> > + return err;
> > +
> > + if (x->xso.dir == XFRM_DEV_OFFLOAD_IN) {
> > + netdev_err(netdev, "xfrm inbound offload not supported\n");
> > + err = -ENODEV;
>
> This path results in pf being dereferenced while uninitialised towards the
> bottom of this function.
>
> Flagged by Smatch, and Clang-18 W=1 build
>
> > + } else {
> > + pf = netdev_priv(netdev);
> > + if (!mutex_trylock(&pf->ipsec.lock)) {
> > + netdev_err(netdev, "IPSEC device is busy\n");
> > + return -EBUSY;
> > + }
> > +
> > + if (!(pf->flags & OTX2_FLAG_INLINE_IPSEC_ENABLED)) {
> > + netdev_err(netdev, "IPSEC not enabled/supported on
> device\n");
> > + err = -ENODEV;
> > + goto unlock;
> > + }
> > +
> > + sa_entry = cn10k_outb_alloc_sa(pf);
> > + if (!sa_entry) {
> > + netdev_err(netdev, "SA maximum limit %x
> reached\n",
> > + CN10K_IPSEC_OUTB_MAX_SA);
> > + err = -EBUSY;
> > + goto unlock;
> > + }
> > +
> > + cn10k_outb_prepare_sa(x, sa_entry);
> > +
> > + err = cn10k_outb_write_sa(pf, sa_entry);
> > + if (err) {
> > + netdev_err(netdev, "Error writing outbound SA\n");
> > + cn10k_outb_free_sa(pf, sa_entry);
> > + goto unlock;
> > + }
> > +
> > + sa_info = kmalloc(sizeof(*sa_info), GFP_KERNEL);
> > + sa_info->sa_entry = sa_entry;
> > + sa_info->sa_iova = cn10k_outb_get_sa_iova(pf, sa_entry);
> > + x->xso.offload_handle = (unsigned long)sa_info;
> > + }
> > +
> > +unlock:
> > + mutex_unlock(&pf->ipsec.lock);
> > + return err;
> > +}
>
> ...
>
> > +static const struct xfrmdev_ops cn10k_ipsec_xfrmdev_ops = {
> > + .xdo_dev_state_add = cn10k_ipsec_add_state,
> > + .xdo_dev_state_delete = cn10k_ipsec_del_state,
> > +};
> > +
>
> cn10k_ipsec_xfrmdev_ops is unused.
> Perhaps it, along with it's callbacks,
> should be added by the function that uses it?
I wanted to enable ipsec offload in last patch of the series
("[net-next,v2 8/8] cn10k-ipsec: Enable outbound inline ipsec offload")
Is it okay to set xfrmdev_ops in this patch without setting NETIF_F_HW_ESP (below two lines of last patch)
+ /* Set xfrm device ops */
+ netdev->xfrmdev_ops = &cn10k_ipsec_xfrmdev_ops;
Last patch will set below flags.
+ netdev->hw_features |= NETIF_F_HW_ESP;
+ netdev->hw_enc_features |= NETIF_F_HW_ESP;
+
Thanks
-Bharat
>
> Flagged by W=1 builds.
>
> > int cn10k_ipsec_ethtool_init(struct net_device *netdev, bool enable)
> > {
> > struct otx2_nic *pf = netdev_priv(netdev);
>
> ...
>
> > diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/cn10k_ipsec.h
> > b/drivers/net/ethernet/marvell/octeontx2/nic/cn10k_ipsec.h
>
> ...
>
> > +struct cn10k_tx_sa_s {
> > + u64 esn_en : 1; /* W0 */
> > + u64 rsvd_w0_1_8 : 8;
> > + u64 hw_ctx_off : 7;
> > + u64 ctx_id : 16;
> > + u64 rsvd_w0_32_47 : 16;
> > + u64 ctx_push_size : 7;
> > + u64 rsvd_w0_55 : 1;
> > + u64 ctx_hdr_size : 2;
> > + u64 aop_valid : 1;
> > + u64 rsvd_w0_59 : 1;
> > + u64 ctx_size : 4;
> > + u64 w1; /* W1 */
> > + u64 sa_valid : 1; /* W2 */
> > + u64 sa_dir : 1;
> > + u64 rsvd_w2_2_3 : 2;
> > + u64 ipsec_mode : 1;
> > + u64 ipsec_protocol : 1;
> > + u64 aes_key_len : 2;
> > + u64 enc_type : 3;
> > + u64 rsvd_w2_11_31 : 21;
> > + u64 spi : 32;
> > + u64 w3; /* W3 */
> > + u8 cipher_key[32]; /* W4 - W7 */
> > + u32 rsvd_w8_0_31; /* W8 : IV */
> > + u32 iv_gcm_salt;
> > + u64 rsvd_w9_w30[22]; /* W9 - W30 */
> > + u64 hw_ctx[6]; /* W31 - W36 */
> > +};
>
> ...
Hi Bharat,
kernel test robot noticed the following build warnings:
[auto build test WARNING on next-20240513]
[cannot apply to linus/master horms-ipvs/master v6.9 v6.9-rc7 v6.9-rc6 v6.9]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Bharat-Bhushan/octeontx2-pf-map-skb-data-as-device-writeable/20240513-185803
base: next-20240513
patch link: https://lore.kernel.org/r/20240513105446.297451-6-bbhushan2%40marvell.com
patch subject: [net-next,v2 5/8] cn10k-ipsec: Add SA add/delete support for outb inline ipsec
config: s390-allmodconfig (https://download.01.org/0day-ci/archive/20240514/[email protected]/config)
compiler: clang version 19.0.0git (https://github.com/llvm/llvm-project b910bebc300dafb30569cecc3017b446ea8eafa0)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20240514/[email protected]/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <[email protected]>
| Closes: https://lore.kernel.org/oe-kbuild-all/[email protected]/
All warnings (new ones prefixed by >>):
In file included from drivers/net/ethernet/marvell/octeontx2/nic/cn10k_ipsec.c:7:
In file included from include/net/xfrm.h:9:
In file included from include/linux/skbuff.h:17:
In file included from include/linux/bvec.h:10:
In file included from include/linux/highmem.h:10:
In file included from include/linux/mm.h:2253:
include/linux/vmstat.h:500:43: warning: arithmetic between different enumeration types ('enum zone_stat_item' and 'enum numa_stat_item') [-Wenum-enum-conversion]
500 | return vmstat_text[NR_VM_ZONE_STAT_ITEMS +
| ~~~~~~~~~~~~~~~~~~~~~ ^
501 | item];
| ~~~~
include/linux/vmstat.h:507:43: warning: arithmetic between different enumeration types ('enum zone_stat_item' and 'enum numa_stat_item') [-Wenum-enum-conversion]
507 | return vmstat_text[NR_VM_ZONE_STAT_ITEMS +
| ~~~~~~~~~~~~~~~~~~~~~ ^
508 | NR_VM_NUMA_EVENT_ITEMS +
| ~~~~~~~~~~~~~~~~~~~~~~
include/linux/vmstat.h:514:36: warning: arithmetic between different enumeration types ('enum node_stat_item' and 'enum lru_list') [-Wenum-enum-conversion]
514 | return node_stat_name(NR_LRU_BASE + lru) + 3; // skip "nr_"
| ~~~~~~~~~~~ ^ ~~~
include/linux/vmstat.h:519:43: warning: arithmetic between different enumeration types ('enum zone_stat_item' and 'enum numa_stat_item') [-Wenum-enum-conversion]
519 | return vmstat_text[NR_VM_ZONE_STAT_ITEMS +
| ~~~~~~~~~~~~~~~~~~~~~ ^
520 | NR_VM_NUMA_EVENT_ITEMS +
| ~~~~~~~~~~~~~~~~~~~~~~
include/linux/vmstat.h:528:43: warning: arithmetic between different enumeration types ('enum zone_stat_item' and 'enum numa_stat_item') [-Wenum-enum-conversion]
528 | return vmstat_text[NR_VM_ZONE_STAT_ITEMS +
| ~~~~~~~~~~~~~~~~~~~~~ ^
529 | NR_VM_NUMA_EVENT_ITEMS +
| ~~~~~~~~~~~~~~~~~~~~~~
In file included from drivers/net/ethernet/marvell/octeontx2/nic/cn10k_ipsec.c:7:
In file included from include/net/xfrm.h:9:
In file included from include/linux/skbuff.h:28:
In file included from include/linux/dma-mapping.h:11:
In file included from include/linux/scatterlist.h:9:
In file included from arch/s390/include/asm/io.h:93:
include/asm-generic/io.h:548:31: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic]
548 | val = __raw_readb(PCI_IOBASE + addr);
| ~~~~~~~~~~ ^
include/asm-generic/io.h:561:61: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic]
561 | val = __le16_to_cpu((__le16 __force)__raw_readw(PCI_IOBASE + addr));
| ~~~~~~~~~~ ^
include/uapi/linux/byteorder/big_endian.h:37:59: note: expanded from macro '__le16_to_cpu'
37 | #define __le16_to_cpu(x) __swab16((__force __u16)(__le16)(x))
| ^
include/uapi/linux/swab.h:102:54: note: expanded from macro '__swab16'
102 | #define __swab16(x) (__u16)__builtin_bswap16((__u16)(x))
| ^
In file included from drivers/net/ethernet/marvell/octeontx2/nic/cn10k_ipsec.c:7:
In file included from include/net/xfrm.h:9:
In file included from include/linux/skbuff.h:28:
In file included from include/linux/dma-mapping.h:11:
In file included from include/linux/scatterlist.h:9:
In file included from arch/s390/include/asm/io.h:93:
include/asm-generic/io.h:574:61: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic]
574 | val = __le32_to_cpu((__le32 __force)__raw_readl(PCI_IOBASE + addr));
| ~~~~~~~~~~ ^
include/uapi/linux/byteorder/big_endian.h:35:59: note: expanded from macro '__le32_to_cpu'
35 | #define __le32_to_cpu(x) __swab32((__force __u32)(__le32)(x))
| ^
include/uapi/linux/swab.h:115:54: note: expanded from macro '__swab32'
115 | #define __swab32(x) (__u32)__builtin_bswap32((__u32)(x))
| ^
In file included from drivers/net/ethernet/marvell/octeontx2/nic/cn10k_ipsec.c:7:
In file included from include/net/xfrm.h:9:
In file included from include/linux/skbuff.h:28:
In file included from include/linux/dma-mapping.h:11:
In file included from include/linux/scatterlist.h:9:
In file included from arch/s390/include/asm/io.h:93:
include/asm-generic/io.h:585:33: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic]
585 | __raw_writeb(value, PCI_IOBASE + addr);
| ~~~~~~~~~~ ^
include/asm-generic/io.h:595:59: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic]
595 | __raw_writew((u16 __force)cpu_to_le16(value), PCI_IOBASE + addr);
| ~~~~~~~~~~ ^
include/asm-generic/io.h:605:59: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic]
605 | __raw_writel((u32 __force)cpu_to_le32(value), PCI_IOBASE + addr);
| ~~~~~~~~~~ ^
include/asm-generic/io.h:693:20: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic]
693 | readsb(PCI_IOBASE + addr, buffer, count);
| ~~~~~~~~~~ ^
include/asm-generic/io.h:701:20: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic]
701 | readsw(PCI_IOBASE + addr, buffer, count);
| ~~~~~~~~~~ ^
include/asm-generic/io.h:709:20: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic]
709 | readsl(PCI_IOBASE + addr, buffer, count);
| ~~~~~~~~~~ ^
include/asm-generic/io.h:718:21: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic]
718 | writesb(PCI_IOBASE + addr, buffer, count);
| ~~~~~~~~~~ ^
include/asm-generic/io.h:727:21: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic]
727 | writesw(PCI_IOBASE + addr, buffer, count);
| ~~~~~~~~~~ ^
include/asm-generic/io.h:736:21: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic]
736 | writesl(PCI_IOBASE + addr, buffer, count);
| ~~~~~~~~~~ ^
>> drivers/net/ethernet/marvell/octeontx2/nic/cn10k_ipsec.c:681:6: warning: variable 'pf' is used uninitialized whenever 'if' condition is true [-Wsometimes-uninitialized]
681 | if (x->xso.dir == XFRM_DEV_OFFLOAD_IN) {
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
drivers/net/ethernet/marvell/octeontx2/nic/cn10k_ipsec.c:721:16: note: uninitialized use occurs here
721 | mutex_unlock(&pf->ipsec.lock);
| ^~
drivers/net/ethernet/marvell/octeontx2/nic/cn10k_ipsec.c:681:2: note: remove the 'if' if its condition is always false
681 | if (x->xso.dir == XFRM_DEV_OFFLOAD_IN) {
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
682 | netdev_err(netdev, "xfrm inbound offload not supported\n");
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
683 | err = -ENODEV;
| ~~~~~~~~~~~~~~
684 | } else {
| ~~~~~~
drivers/net/ethernet/marvell/octeontx2/nic/cn10k_ipsec.c:674:21: note: initialize the variable 'pf' to silence this warning
674 | struct otx2_nic *pf;
| ^
| = NULL
drivers/net/ethernet/marvell/octeontx2/nic/cn10k_ipsec.c:768:33: warning: unused variable 'cn10k_ipsec_xfrmdev_ops' [-Wunused-const-variable]
768 | static const struct xfrmdev_ops cn10k_ipsec_xfrmdev_ops = {
| ^~~~~~~~~~~~~~~~~~~~~~~
19 warnings generated.
vim +681 drivers/net/ethernet/marvell/octeontx2/nic/cn10k_ipsec.c
667
668 static int cn10k_ipsec_add_state(struct xfrm_state *x,
669 struct netlink_ext_ack *extack)
670 {
671 struct net_device *netdev = x->xso.dev;
672 struct cn10k_tx_sa_s *sa_entry;
673 struct cpt_ctx_info_s *sa_info;
674 struct otx2_nic *pf;
675 int err;
676
677 err = cn10k_ipsec_validate_state(x);
678 if (err)
679 return err;
680
> 681 if (x->xso.dir == XFRM_DEV_OFFLOAD_IN) {
682 netdev_err(netdev, "xfrm inbound offload not supported\n");
683 err = -ENODEV;
684 } else {
685 pf = netdev_priv(netdev);
686 if (!mutex_trylock(&pf->ipsec.lock)) {
687 netdev_err(netdev, "IPSEC device is busy\n");
688 return -EBUSY;
689 }
690
691 if (!(pf->flags & OTX2_FLAG_INLINE_IPSEC_ENABLED)) {
692 netdev_err(netdev, "IPSEC not enabled/supported on device\n");
693 err = -ENODEV;
694 goto unlock;
695 }
696
697 sa_entry = cn10k_outb_alloc_sa(pf);
698 if (!sa_entry) {
699 netdev_err(netdev, "SA maximum limit %x reached\n",
700 CN10K_IPSEC_OUTB_MAX_SA);
701 err = -EBUSY;
702 goto unlock;
703 }
704
705 cn10k_outb_prepare_sa(x, sa_entry);
706
707 err = cn10k_outb_write_sa(pf, sa_entry);
708 if (err) {
709 netdev_err(netdev, "Error writing outbound SA\n");
710 cn10k_outb_free_sa(pf, sa_entry);
711 goto unlock;
712 }
713
714 sa_info = kmalloc(sizeof(*sa_info), GFP_KERNEL);
715 sa_info->sa_entry = sa_entry;
716 sa_info->sa_iova = cn10k_outb_get_sa_iova(pf, sa_entry);
717 x->xso.offload_handle = (unsigned long)sa_info;
718 }
719
720 unlock:
721 mutex_unlock(&pf->ipsec.lock);
722 return err;
723 }
724
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
On Tue, May 14, 2024 at 06:52:38AM +0000, Bharat Bhushan wrote:
> Please see inline
>
> > -----Original Message-----
> > From: Simon Horman <[email protected]>
..
> > > +static const struct xfrmdev_ops cn10k_ipsec_xfrmdev_ops = {
> > > + .xdo_dev_state_add = cn10k_ipsec_add_state,
> > > + .xdo_dev_state_delete = cn10k_ipsec_del_state,
> > > +};
> > > +
> >
> > cn10k_ipsec_xfrmdev_ops is unused.
> > Perhaps it, along with it's callbacks,
> > should be added by the function that uses it?
>
> I wanted to enable ipsec offload in last patch of the series
> ("[net-next,v2 8/8] cn10k-ipsec: Enable outbound inline ipsec offload")
I appreciate the patchset being split up like this.
> Is it okay to set xfrmdev_ops in this patch without setting NETIF_F_HW_ESP (below two lines of last patch)
> + /* Set xfrm device ops */
> + netdev->xfrmdev_ops = &cn10k_ipsec_xfrmdev_ops;
>
> Last patch will set below flags.
> + netdev->hw_features |= NETIF_F_HW_ESP;
> + netdev->hw_enc_features |= NETIF_F_HW_ESP;
> +
IMHO, yes, something like that would be fine, as long as it leads to a
working system (with a feature not enabled). Perhaps it would be good to
include a comment in the code about this to make it clear what is going on.
..