@@ -683,19 +683,156 @@ mlx5e_nvmeotcp_queue_teardown(struct net_device *netdev,
mlx5e_nvmeotcp_put_queue(queue);
}
+static bool
+mlx5e_nvmeotcp_validate_small_sgl_suffix(struct scatterlist *sg, int sg_len, int mtu)
+{
+ int i, hole_size, hole_len, chunk_size = 0;
+
+ for (i = 1; i < sg_len; i++)
+ chunk_size += sg_dma_len(&sg[i]);
+
+ if (chunk_size >= mtu)
+ return true;
+
+ hole_size = mtu - chunk_size - 1;
+ hole_len = DIV_ROUND_UP(hole_size, PAGE_SIZE);
+
+ if (sg_len + hole_len > MAX_SKB_FRAGS)
+ return false;
+
+ return true;
+}
+
+static bool
+mlx5e_nvmeotcp_validate_big_sgl_suffix(struct scatterlist *sg, int sg_len, int mtu)
+{
+ int i, j, last_elem, window_idx, window_size = MAX_SKB_FRAGS - 1;
+ int chunk_size = 0;
+
+ last_elem = sg_len - window_size;
+ window_idx = window_size;
+
+ for (j = 1; j < window_size; j++)
+ chunk_size += sg_dma_len(&sg[j]);
+
+ for (i = 1; i <= last_elem; i++, window_idx++) {
+ chunk_size += sg_dma_len(&sg[window_idx]);
+ if (chunk_size < mtu - 1)
+ return false;
+
+ chunk_size -= sg_dma_len(&sg[i]);
+ }
+
+ return true;
+}
+
+/* This function makes sure that the middle/suffix of a PDU SGL meets the
+ * restriction of MAX_SKB_FRAGS. There are two cases here:
+ * 1. sg_len < MAX_SKB_FRAGS - the extreme case here is a packet that consists
+ * of one byte from the first SG element + the rest of the SGL and the remaining
+ * space of the packet will be scattered to the WQE and will be pointed by
+ * SKB frags.
+ * 2. sg_len => MAX_SKB_FRAGS - the extreme case here is a packet that consists
+ * of one byte from middle SG element + 15 continuous SG elements + one byte
+ * from a sequential SG element or the rest of the packet.
+ */
+static bool
+mlx5e_nvmeotcp_validate_sgl_suffix(struct scatterlist *sg, int sg_len, int mtu)
+{
+ int ret;
+
+ if (sg_len < MAX_SKB_FRAGS)
+ ret = mlx5e_nvmeotcp_validate_small_sgl_suffix(sg, sg_len, mtu);
+ else
+ ret = mlx5e_nvmeotcp_validate_big_sgl_suffix(sg, sg_len, mtu);
+
+ return ret;
+}
+
+static bool
+mlx5e_nvmeotcp_validate_sgl_prefix(struct scatterlist *sg, int sg_len, int mtu)
+{
+ int i, hole_size, hole_len, tmp_len, chunk_size = 0;
+
+ tmp_len = min_t(int, sg_len, MAX_SKB_FRAGS);
+
+ for (i = 0; i < tmp_len; i++)
+ chunk_size += sg_dma_len(&sg[i]);
+
+ if (chunk_size >= mtu)
+ return true;
+
+ hole_size = mtu - chunk_size;
+ hole_len = DIV_ROUND_UP(hole_size, PAGE_SIZE);
+
+ if (tmp_len + hole_len > MAX_SKB_FRAGS)
+ return false;
+
+ return true;
+}
+
+/* This function is responsible to ensure that a PDU could be offloaded.
+ * PDU is offloaded by building a non-linear SKB such that each SGL element is
+ * placed in frag, thus this function should ensure that all packets that
+ * represent part of the PDU won't exaggerate from MAX_SKB_FRAGS SGL.
+ * In addition NVMEoTCP offload has one PDU offload for packet restriction.
+ * Packet could start with a new PDU and then we should check that the prefix
+ * of the PDU meets the requirement or a packet can start in the middle of SG
+ * element and then we should check that the suffix of PDU meets the requirement.
+ */
+static bool
+mlx5e_nvmeotcp_validate_sgl(struct scatterlist *sg, int sg_len, int mtu)
+{
+ int max_hole_frags;
+
+ max_hole_frags = DIV_ROUND_UP(mtu, PAGE_SIZE);
+ if (sg_len + max_hole_frags <= MAX_SKB_FRAGS)
+ return true;
+
+ if (!mlx5e_nvmeotcp_validate_sgl_prefix(sg, sg_len, mtu) ||
+ !mlx5e_nvmeotcp_validate_sgl_suffix(sg, sg_len, mtu))
+ return false;
+
+ return true;
+}
+
static int
mlx5e_nvmeotcp_ddp_setup(struct net_device *netdev,
struct sock *sk,
struct ulp_ddp_io *ddp)
{
+ struct scatterlist *sg = ddp->sg_table.sgl;
+ struct mlx5e_nvmeotcp_queue_entry *nvqt;
struct mlx5e_nvmeotcp_queue *queue;
+ struct mlx5_core_dev *mdev;
+ int i, size = 0, count = 0;
queue = container_of(ulp_ddp_get_ctx(sk),
struct mlx5e_nvmeotcp_queue, ulp_ddp_ctx);
+ mdev = queue->priv->mdev;
+ count = dma_map_sg(mdev->device, ddp->sg_table.sgl, ddp->nents,
+ DMA_FROM_DEVICE);
+
+ if (count <= 0)
+ return -EINVAL;
- /* Placeholder - map_sg and initializing the count */
+ if (WARN_ON(count > mlx5e_get_max_sgl(mdev)))
+ return -ENOSPC;
+
+ if (!mlx5e_nvmeotcp_validate_sgl(sg, count, READ_ONCE(netdev->mtu)))
+ return -EOPNOTSUPP;
+
+ for (i = 0; i < count; i++)
+ size += sg_dma_len(&sg[i]);
+
+ nvqt = &queue->ccid_table[ddp->command_id];
+ nvqt->size = size;
+ nvqt->ddp = ddp;
+ nvqt->sgl = sg;
+ nvqt->ccid_gen++;
+ nvqt->sgl_length = count;
+ mlx5e_nvmeotcp_post_klm_wqe(queue, KLM_UMR, ddp->command_id, count);
- mlx5e_nvmeotcp_post_klm_wqe(queue, KLM_UMR, ddp->command_id, 0);
return 0;
}
@@ -718,6 +855,11 @@ static void
mlx5e_nvmeotcp_ddp_resync(struct net_device *netdev,
struct sock *sk, u32 seq)
{
+ struct mlx5e_nvmeotcp_queue *queue =
+ container_of(ulp_ddp_get_ctx(sk), struct mlx5e_nvmeotcp_queue, ulp_ddp_ctx);
+
+ queue->after_resync_cqe = 1;
+ mlx5e_nvmeotcp_rx_post_static_params_wqe(queue, seq);
}
struct mlx5e_nvmeotcp_queue *