With the goal of enabling a generic infrastructure that allows NVMe/TCP
offload devices like NICs to seamlessly plug into the NVMe-oF stack, this
patch series introduces the nvme-tcp-offload ULP host layer, which will
be a new transport type called "tcp-offload" and will serve as an
abstraction layer to work with vendor specific nvme-tcp offload drivers.
NVMeTCP offload is a full offload of the NVMeTCP protocol, this includes
both the TCP level and the NVMeTCP level.
The nvme-tcp-offload transport can co-exist with the existing tcp and
other transports. The tcp offload was designed so that stack changes are
kept to a bare minimum: only registering new transports.
All other APIs, ops etc. are identical to the regular tcp transport.
Representing the TCP offload as a new transport allows clear and manageable
differentiation between the connections which should use the offload path
and those that are not offloaded (even on the same device).
The nvme-tcp-offload layers and API compared to nvme-tcp and nvme-rdma:
* NVMe layer: *
[ nvme/nvme-fabrics/blk-mq ]
|
(nvme API and blk-mq API)
|
|
* Vendor agnostic transport layer: *
[ nvme-rdma ] [ nvme-tcp ] [ nvme-tcp-offload ]
| | |
(Verbs)
| | |
| (Socket)
| | |
| | (nvme-tcp-offload API)
| | |
| | |
* Vendor Specific Driver: *
| | |
[ qedr ]
| |
[ qede ]
|
[ qedn ]
Performance:
============
With this implementation on top of the Marvell qedn driver (using the
Marvell FastLinQ NIC), we were able to demonstrate the following CPU
utilization improvement:
On AMD EPYC 7402, 2.80GHz, 28 cores:
- For 16K queued read IOs, 16jobs, 4qd (50Gbps line rate):
Improved the CPU utilization from 15.1% with NVMeTCP SW to 4.7% with
NVMeTCP offload.
On Intel(R) Xeon(R) Gold 5122 CPU, 3.60GHz, 16 cores:
- For 512K queued read IOs, 16jobs, 4qd (25Gbps line rate):
Improved the CPU utilization from 16.3% with NVMeTCP SW to 1.1% with
NVMeTCP offload.
In addition, we were able to demonstrate the following latency improvement:
- For 200K read IOPS (16 jobs, 16 qd, with fio rate limiter):
Improved the average latency from 105 usec with NVMeTCP SW to 39 usec
with NVMeTCP offload.
Improved the 99.99 tail latency from 570 usec with NVMeTCP SW to 91 usec
with NVMeTCP offload.
The end-to-end offload latency was measured from fio while running against
back end of null device.
Upstream plan:
==============
Following this RFC, the series will be sent in a modular way so that changes
in each part will not impact the previous part.
- Part 1 (Patches 1-7):
The qed infrastructure, will be sent to 'netdev@vger.kernel.org'.
- Part 2 (Patch 8-15):
The nvme-tcp-offload patches, will be sent to
'linux-nvme@lists.infradead.org'.
- Part 3 (Packet 16-27):
The qedn patches, will be sent to 'linux-nvme@lists.infradead.org'.
Queue Initialization Design:
============================
The nvme-tcp-offload ULP module shall register with the existing
nvmf_transport_ops (.name = "tcp_offload"), nvme_ctrl_ops and blk_mq_ops.
The nvme-tcp-offload vendor driver shall register to nvme-tcp-offload ULP
with the following ops:
- claim_dev() - in order to resolve the route to the target according to
the paired net_dev.
- create_queue() - in order to create offloaded nvme-tcp queue.
The nvme-tcp-offload ULP module shall manage all the controller level
functionalities, call claim_dev and based on the return values shall call
the relevant module create_queue in order to create the admin queue and
the IO queues.
IO-path Design:
===============
The nvme-tcp-offload shall work at the IO-level - the nvme-tcp-offload
ULP module shall pass the request (the IO) to the nvme-tcp-offload vendor
driver and later, the nvme-tcp-offload vendor driver returns the request
completion (the IO completion).
No additional handling is needed in between; this design will reduce the
CPU utilization as we will describe below.
The nvme-tcp-offload vendor driver shall register to nvme-tcp-offload ULP
with the following IO-path ops:
- init_req()
- send_req() - in order to pass the request to the handling of the
offload driver that shall pass it to the vendor specific device.
- poll_queue()
Once the IO completes, the nvme-tcp-offload vendor driver shall call
command.done() that will invoke the nvme-tcp-offload ULP layer to
complete the request.
TCP events:
===========
The Marvell FastLinQ NIC HW engine handle all the TCP re-transmissions
and OOO events.
Teardown and errors:
====================
In case of NVMeTCP queue error the nvme-tcp-offload vendor driver shall
call the nvme_tcp_ofld_report_queue_err.
The nvme-tcp-offload vendor driver shall register to nvme-tcp-offload ULP
with the following teardown ops:
- drain_queue()
- destroy_queue()
The Marvell FastLinQ NIC HW engine:
====================================
The Marvell NIC HW engine is capable of offloading the entire TCP/IP
stack and managing up to 64K connections per PF, already implemented and
upstream use cases for this include iWARP (by the Marvell qedr driver)
and iSCSI (by the Marvell qedi driver).
In addition, the Marvell NIC HW engine offloads the NVMeTCP queue layer
and is able to manage the IO level also in case of TCP re-transmissions
and OOO events.
The HW engine enables direct data placement (including the data digest CRC
calculation and validation) and direct data transmission (including data
digest CRC calculation).
The Marvell qedn driver:
========================
The new driver will be added under "drivers/nvme/hw" and will be enabled
by the Kconfig "Marvell NVM Express over Fabrics TCP offload".
As part of the qedn init, the driver will register as a pci device driver
and will work with the Marvell fastlinQ NIC.
As part of the probe, the driver will register to the nvme_tcp_offload
(ULP) and to the qed module (qed_nvmetcp_ops) - similar to other
"qed_*_ops" which are used by the qede, qedr, qedf and qedi device
drivers.
QEDN Future work:
=================
- Support extended HW resources.
- Digest support.
- Devlink support for device configuration and TCP offload configurations.
- Statistics
Long term future work:
======================
- The nvme-tcp-offload ULP target abstraction layer.
- The Marvell nvme-tcp-offload "qednt" target driver.
Changes since RFC v1:
=====================
- Fix nvme_tcp_ofld_ops return values.
- Remove NVMF_TRTYPE_TCP_OFFLOAD.
- Add nvme_tcp_ofld_poll() implementation.
- Fix nvme_tcp_ofld_queue_rq() to check map_sg() and send_req() return
values.
Changes since RFC v2:
=====================
- Add qedn - Marvell's NVMeTCP HW offload vendor driver init and probe
(patches 8-11).
- Fixes in controller and queue level (patches 3-6).
Changes since RFC v3:
=====================
- Add the full implementation of the nvme-tcp-offload layer including the
new ops: setup_ctrl(), release_ctrl(), commit_rqs() and new flows (ASYNC
and timeout).
- Add nvme-tcp-offload device maximums: max_hw_sectors, max_segments.
- Add nvme-tcp-offload layer design and optimization changes.
- Add the qedn full implementation for the conn level, IO path and error
handling.
- Add qed support for the new AHP HW.
Arie Gershberg (3):
nvme-fabrics: Move NVMF_ALLOWED_OPTS and NVMF_REQUIRED_OPTS
definitions
nvme-tcp-offload: Add controller level implementation
nvme-tcp-offload: Add controller level error recovery implementation
Dean Balandin (3):
nvme-tcp-offload: Add device scan implementation
nvme-tcp-offload: Add queue level implementation
nvme-tcp-offload: Add IO level implementation
Nikolay Assa (2):
qed: Add IP services APIs support
qedn: Add qedn_claim_dev API support
Omkar Kulkarni (1):
qed: Add qed-NVMeTCP personality
Prabhakar Kushwaha (6):
qed: Add support of HW filter block
qedn: Add connection-level slowpath functionality
qedn: Add support of configuring HW filter block
qedn: Add support of Task and SGL
qedn: Add support of NVME ICReq & ICResp
qedn: Add support of ASYNC
Shai Malin (12):
qed: Add NVMeTCP Offload PF Level FW and HW HSI
qed: Add NVMeTCP Offload Connection Level FW and HW HSI
qed: Add NVMeTCP Offload IO Level FW and HW HSI
qed: Add NVMeTCP Offload IO Level FW Initializations
nvme-tcp-offload: Add nvme-tcp-offload - NVMeTCP HW offload ULP
nvme-tcp-offload: Add Timeout and ASYNC Support
qedn: Add qedn - Marvell's NVMeTCP HW offload vendor driver
qedn: Add qedn probe
qedn: Add IRQ and fast-path resources initializations
qedn: Add IO level nvme_req and fw_cq workqueues
qedn: Add IO level fastpath functionality
qedn: Add Connection and IO level recovery flows
MAINTAINERS | 10 +
drivers/net/ethernet/qlogic/Kconfig | 3 +
drivers/net/ethernet/qlogic/qed/Makefile | 5 +
drivers/net/ethernet/qlogic/qed/qed.h | 16 +
drivers/net/ethernet/qlogic/qed/qed_cxt.c | 32 +
drivers/net/ethernet/qlogic/qed/qed_cxt.h | 1 +
drivers/net/ethernet/qlogic/qed/qed_dev.c | 151 +-
drivers/net/ethernet/qlogic/qed/qed_hsi.h | 4 +-
drivers/net/ethernet/qlogic/qed/qed_ll2.c | 31 +-
drivers/net/ethernet/qlogic/qed/qed_mcp.c | 3 +
drivers/net/ethernet/qlogic/qed/qed_mng_tlv.c | 3 +-
drivers/net/ethernet/qlogic/qed/qed_nvmetcp.c | 868 +++++++++++
drivers/net/ethernet/qlogic/qed/qed_nvmetcp.h | 114 ++
.../qlogic/qed/qed_nvmetcp_fw_funcs.c | 372 +++++
.../qlogic/qed/qed_nvmetcp_fw_funcs.h | 43 +
.../qlogic/qed/qed_nvmetcp_ip_services.c | 239 +++
drivers/net/ethernet/qlogic/qed/qed_ooo.c | 5 +-
drivers/net/ethernet/qlogic/qed/qed_sp.h | 5 +
.../net/ethernet/qlogic/qed/qed_sp_commands.c | 1 +
drivers/nvme/Kconfig | 1 +
drivers/nvme/Makefile | 1 +
drivers/nvme/host/Kconfig | 16 +
drivers/nvme/host/Makefile | 3 +
drivers/nvme/host/fabrics.c | 7 -
drivers/nvme/host/fabrics.h | 7 +
drivers/nvme/host/tcp-offload.c | 1330 +++++++++++++++++
drivers/nvme/host/tcp-offload.h | 209 +++
drivers/nvme/hw/Kconfig | 9 +
drivers/nvme/hw/Makefile | 3 +
drivers/nvme/hw/qedn/Makefile | 4 +
drivers/nvme/hw/qedn/qedn.h | 435 ++++++
drivers/nvme/hw/qedn/qedn_conn.c | 999 +++++++++++++
drivers/nvme/hw/qedn/qedn_main.c | 1153 ++++++++++++++
drivers/nvme/hw/qedn/qedn_task.c | 977 ++++++++++++
include/linux/qed/common_hsi.h | 1 +
include/linux/qed/nvmetcp_common.h | 616 ++++++++
include/linux/qed/qed_if.h | 22 +
include/linux/qed/qed_nvmetcp_if.h | 244 +++
.../linux/qed/qed_nvmetcp_ip_services_if.h | 29 +
39 files changed, 7947 insertions(+), 25 deletions(-)
create mode 100644 drivers/net/ethernet/qlogic/qed/qed_nvmetcp.c
create mode 100644 drivers/net/ethernet/qlogic/qed/qed_nvmetcp.h
create mode 100644 drivers/net/ethernet/qlogic/qed/qed_nvmetcp_fw_funcs.c
create mode 100644 drivers/net/ethernet/qlogic/qed/qed_nvmetcp_fw_funcs.h
create mode 100644 drivers/net/ethernet/qlogic/qed/qed_nvmetcp_ip_services.c
create mode 100644 drivers/nvme/host/tcp-offload.c
create mode 100644 drivers/nvme/host/tcp-offload.h
create mode 100644 drivers/nvme/hw/Kconfig
create mode 100644 drivers/nvme/hw/Makefile
create mode 100644 drivers/nvme/hw/qedn/Makefile
create mode 100644 drivers/nvme/hw/qedn/qedn.h
create mode 100644 drivers/nvme/hw/qedn/qedn_conn.c
create mode 100644 drivers/nvme/hw/qedn/qedn_main.c
create mode 100644 drivers/nvme/hw/qedn/qedn_task.c
create mode 100644 include/linux/qed/nvmetcp_common.h
create mode 100644 include/linux/qed/qed_nvmetcp_if.h
create mode 100644 include/linux/qed/qed_nvmetcp_ip_services_if.h
--
2.22.0
@@ -0,0 +1,282 @@+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)+/* Copyright 2021 Marvell. All rights reserved. */++#include<linux/types.h>+#include<asm/byteorder.h>+#include<asm/param.h>+#include<linux/delay.h>+#include<linux/dma-mapping.h>+#include<linux/etherdevice.h>+#include<linux/kernel.h>+#include<linux/log2.h>+#include<linux/module.h>+#include<linux/pci.h>+#include<linux/stddef.h>+#include<linux/string.h>+#include<linux/errno.h>+#include<linux/list.h>+#include<linux/qed/qed_nvmetcp_if.h>+#include"qed.h"+#include"qed_cxt.h"+#include"qed_dev_api.h"+#include"qed_hsi.h"+#include"qed_hw.h"+#include"qed_int.h"+#include"qed_nvmetcp.h"+#include"qed_ll2.h"+#include"qed_mcp.h"+#include"qed_sp.h"+#include"qed_reg_addr.h"++staticintqed_nvmetcp_async_event(structqed_hwfn*p_hwfn,u8fw_event_code,+u16echo,unionevent_ring_data*data,+u8fw_return_code)+{+if(p_hwfn->p_nvmetcp_info->event_cb){+structqed_nvmetcp_info*p_nvmetcp=p_hwfn->p_nvmetcp_info;++returnp_nvmetcp->event_cb(p_nvmetcp->event_context,+fw_event_code,data);+}else{+DP_NOTICE(p_hwfn,"nvmetcp async completion is not set\n");++return-EINVAL;+}+}++staticintqed_sp_nvmetcp_func_start(structqed_hwfn*p_hwfn,+enumspq_modecomp_mode,+structqed_spq_comp_cb*p_comp_addr,+void*event_context,+nvmetcp_event_cb_tasync_event_cb)+{+structnvmetcp_init_ramrod_params*p_ramrod=NULL;+structqed_nvmetcp_pf_params*p_params=NULL;+structscsi_init_func_queues*p_queue=NULL;+structnvmetcp_spe_func_init*p_init=NULL;+structqed_sp_init_datainit_data={};+structqed_spq_entry*p_ent=NULL;+intrc=0;+u16val;+u8i;++/* Get SPQ entry */+init_data.cid=qed_spq_get_cid(p_hwfn);+init_data.opaque_fid=p_hwfn->hw_info.opaque_fid;+init_data.comp_mode=comp_mode;+init_data.p_comp_data=p_comp_addr;++rc=qed_sp_init_request(p_hwfn,&p_ent,+NVMETCP_RAMROD_CMD_ID_INIT_FUNC,+PROTOCOLID_NVMETCP,&init_data);+if(rc)+returnrc;++p_ramrod=&p_ent->ramrod.nvmetcp_init;+p_init=&p_ramrod->nvmetcp_init_spe;+p_params=&p_hwfn->pf_params.nvmetcp_pf_params;+p_queue=&p_init->q_params;++p_init->num_sq_pages_in_ring=p_params->num_sq_pages_in_ring;+p_init->num_r2tq_pages_in_ring=p_params->num_r2tq_pages_in_ring;+p_init->num_uhq_pages_in_ring=p_params->num_uhq_pages_in_ring;+p_init->ll2_rx_queue_id=RESC_START(p_hwfn,QED_LL2_RAM_QUEUE)++p_params->ll2_ooo_queue_id;++SET_FIELD(p_init->flags,NVMETCP_SPE_FUNC_INIT_NVMETCP_MODE,1);++p_init->func_params.log_page_size=ilog2(PAGE_SIZE);+p_init->func_params.num_tasks=cpu_to_le16(p_params->num_tasks);+p_init->debug_flags=p_params->debug_mode;++DMA_REGPAIR_LE(p_queue->glbl_q_params_addr,+p_params->glbl_q_params_addr);++p_queue->cq_num_entries=cpu_to_le16(QED_NVMETCP_FW_CQ_SIZE);+p_queue->num_queues=p_params->num_queues;+val=RESC_START(p_hwfn,QED_CMDQS_CQS);+p_queue->queue_relative_offset=cpu_to_le16((u16)val);+p_queue->cq_sb_pi=p_params->gl_rq_pi;++for(i=0;i<p_params->num_queues;i++){+val=qed_get_igu_sb_id(p_hwfn,i);+p_queue->cq_cmdq_sb_num_arr[i]=cpu_to_le16(val);+}++SET_FIELD(p_queue->q_validity,+SCSI_INIT_FUNC_QUEUES_CMD_VALID,0);+p_queue->cmdq_num_entries=0;+p_queue->bdq_resource_id=(u8)RESC_START(p_hwfn,QED_BDQ);++/* p_ramrod->tcp_init.min_rto = cpu_to_le16(p_params->min_rto); */+p_ramrod->tcp_init.two_msl_timer=cpu_to_le32(QED_TCP_TWO_MSL_TIMER);+p_ramrod->tcp_init.tx_sws_timer=cpu_to_le16(QED_TCP_SWS_TIMER);+p_init->half_way_close_timeout=cpu_to_le16(QED_TCP_HALF_WAY_CLOSE_TIMEOUT);+p_ramrod->tcp_init.max_fin_rt=QED_TCP_MAX_FIN_RT;++SET_FIELD(p_ramrod->nvmetcp_init_spe.params,+NVMETCP_SPE_FUNC_INIT_MAX_SYN_RT,QED_TCP_MAX_FIN_RT);++p_hwfn->p_nvmetcp_info->event_context=event_context;+p_hwfn->p_nvmetcp_info->event_cb=async_event_cb;++qed_spq_register_async_cb(p_hwfn,PROTOCOLID_NVMETCP,+qed_nvmetcp_async_event);++returnqed_spq_post(p_hwfn,p_ent,NULL);+}++staticintqed_sp_nvmetcp_func_stop(structqed_hwfn*p_hwfn,+enumspq_modecomp_mode,+structqed_spq_comp_cb*p_comp_addr)+{+structqed_spq_entry*p_ent=NULL;+structqed_sp_init_datainit_data;+intrc;++/* Get SPQ entry */+memset(&init_data,0,sizeof(init_data));+init_data.cid=qed_spq_get_cid(p_hwfn);+init_data.opaque_fid=p_hwfn->hw_info.opaque_fid;+init_data.comp_mode=comp_mode;+init_data.p_comp_data=p_comp_addr;++rc=qed_sp_init_request(p_hwfn,&p_ent,+NVMETCP_RAMROD_CMD_ID_DESTROY_FUNC,+PROTOCOLID_NVMETCP,&init_data);+if(rc)+returnrc;++rc=qed_spq_post(p_hwfn,p_ent,NULL);++qed_spq_unregister_async_cb(p_hwfn,PROTOCOLID_NVMETCP);++returnrc;+}++staticintqed_fill_nvmetcp_dev_info(structqed_dev*cdev,+structqed_dev_nvmetcp_info*info)+{+structqed_hwfn*hwfn=QED_AFFIN_HWFN(cdev);+intrc;++memset(info,0,sizeof(*info));+rc=qed_fill_dev_info(cdev,&info->common);++info->port_id=MFW_PORT(hwfn);+info->num_cqs=FEAT_NUM(hwfn,QED_NVMETCP_CQ);++returnrc;+}++staticvoidqed_register_nvmetcp_ops(structqed_dev*cdev,+structqed_nvmetcp_cb_ops*ops,+void*cookie)+{+cdev->protocol_ops.nvmetcp=ops;+cdev->ops_cookie=cookie;+}++staticintqed_nvmetcp_stop(structqed_dev*cdev)+{+intrc;++if(!(cdev->flags&QED_FLAG_STORAGE_STARTED)){+DP_NOTICE(cdev,"nvmetcp already stopped\n");++return0;+}++if(!hash_empty(cdev->connections)){+DP_NOTICE(cdev,+"Can't stop nvmetcp - not all connections were returned\n");++return-EINVAL;+}++/* Stop the nvmetcp */+rc=qed_sp_nvmetcp_func_stop(QED_AFFIN_HWFN(cdev),QED_SPQ_MODE_EBLOCK,+NULL);+cdev->flags&=~QED_FLAG_STORAGE_STARTED;++returnrc;+}++staticintqed_nvmetcp_start(structqed_dev*cdev,+structqed_nvmetcp_tid*tasks,+void*event_context,+nvmetcp_event_cb_tasync_event_cb)+{+structqed_tid_mem*tid_info;+intrc;++if(cdev->flags&QED_FLAG_STORAGE_STARTED){+DP_NOTICE(cdev,"nvmetcp already started;\n");++return0;+}++rc=qed_sp_nvmetcp_func_start(QED_AFFIN_HWFN(cdev),+QED_SPQ_MODE_EBLOCK,NULL,+event_context,async_event_cb);+if(rc){+DP_NOTICE(cdev,"Failed to start nvmetcp\n");++returnrc;+}++cdev->flags|=QED_FLAG_STORAGE_STARTED;+hash_init(cdev->connections);++if(!tasks)+return0;++tid_info=kzalloc(sizeof(*tid_info),GFP_KERNEL);++if(!tid_info){+qed_nvmetcp_stop(cdev);++return-ENOMEM;+}++rc=qed_cxt_get_tid_mem_info(QED_AFFIN_HWFN(cdev),tid_info);+if(rc){+DP_NOTICE(cdev,"Failed to gather task information\n");+qed_nvmetcp_stop(cdev);+kfree(tid_info);++returnrc;+}++/* Fill task information */+tasks->size=tid_info->tid_size;+tasks->num_tids_per_block=tid_info->num_tids_per_block;+memcpy(tasks->blocks,tid_info->blocks,+MAX_TID_BLOCKS_NVMETCP*sizeof(u8*));++kfree(tid_info);++return0;+}++staticconststructqed_nvmetcp_opsqed_nvmetcp_ops_pass={+.common=&qed_common_ops_pass,+.ll2=&qed_ll2_ops_pass,+.fill_dev_info=&qed_fill_nvmetcp_dev_info,+.register_ops=&qed_register_nvmetcp_ops,+.start=&qed_nvmetcp_start,+.stop=&qed_nvmetcp_stop,++/* Placeholder - Connection level ops */+};++conststructqed_nvmetcp_ops*qed_get_nvmetcp_ops(void)+{+return&qed_nvmetcp_ops_pass;+}+EXPORT_SYMBOL(qed_get_nvmetcp_ops);++voidqed_put_nvmetcp_ops(void)+{+}+EXPORT_SYMBOL(qed_put_nvmetcp_ops);
@@ -542,6 +542,26 @@ struct qed_iscsi_pf_params {u8bdq_pbl_num_entries[3];};+structqed_nvmetcp_pf_params{+u64glbl_q_params_addr;+u16cq_num_entries;++u16num_cons;+u16num_tasks;++u8num_sq_pages_in_ring;+u8num_r2tq_pages_in_ring;+u8num_uhq_pages_in_ring;++u8num_queues;+u8gl_rq_pi;+u8gl_cmd_pi;+u8debug_mode;+u8ll2_ooo_queue_id;++u16min_rto;+};+structqed_rdma_pf_params{/* Supplied to QED during resource allocation (may affect the ILT and*thedoorbellBAR).
@@ -0,0 +1,72 @@+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */+/* Copyright 2021 Marvell. All rights reserved. */++#ifndef _QED_NVMETCP_IF_H+#define _QED_NVMETCP_IF_H+#include<linux/types.h>+#include<linux/qed/qed_if.h>++#define QED_NVMETCP_MAX_IO_SIZE 0x800000++typedefint(*nvmetcp_event_cb_t)(void*context,+u8fw_event_code,void*fw_handle);++structqed_dev_nvmetcp_info{+structqed_dev_infocommon;++u8port_id;/* Physical port */+u8num_cqs;+};++#define MAX_TID_BLOCKS_NVMETCP (512)+structqed_nvmetcp_tid{+u32size;/* In bytes per task */+u32num_tids_per_block;+u8*blocks[MAX_TID_BLOCKS_NVMETCP];+};++structqed_nvmetcp_cb_ops{+structqed_common_cb_opscommon;+};++/**+*structqed_nvmetcp_ops-qedNVMeTCPoperations.+*@common:commonoperationspointer+*@ll2:lightL2operationspointer+*@fill_dev_info:fillsNVMeTCPspecificinformation+*@paramcdev+*@paraminfo+*@return0onsuccess,otherwiseerrorvalue.+*@register_ops:registernvmetcpoperations+*@paramcdev+*@paramops-specifiedusingqed_nvmetcp_cb_ops+*@paramcookie-driverprivate+*@start:nvmetcpinFW+*@paramcdev+*@paramtasks-qedwillfillinformationabouttasks+*return0onsuccess,otherwiseerrorvalue.+*@stop:nvmetcpinFW+*@paramcdev+*return0onsuccess,otherwiseerrorvalue.+*/+structqed_nvmetcp_ops{+conststructqed_common_ops*common;++conststructqed_ll2_ops*ll2;++int(*fill_dev_info)(structqed_dev*cdev,+structqed_dev_nvmetcp_info*info);++void(*register_ops)(structqed_dev*cdev,+structqed_nvmetcp_cb_ops*ops,void*cookie);++int(*start)(structqed_dev*cdev,+structqed_nvmetcp_tid*tasks,+void*event_context,nvmetcp_event_cb_tasync_event_cb);++int(*stop)(structqed_dev*cdev);+};++conststructqed_nvmetcp_ops*qed_get_nvmetcp_ops(void);+voidqed_put_nvmetcp_ops(void);+#endif
@@ -5360,3 +5360,110 @@ void qed_set_fw_mac_addr(__le16 *fw_msb,((u8*)fw_lsb)[0]=mac[5];((u8*)fw_lsb)[1]=mac[4];}++staticintqed_llh_shadow_remove_all_filters(structqed_dev*cdev,u8ppfid)+{+structqed_llh_info*p_llh_info=cdev->p_llh_info;+structqed_llh_filter_info*p_filters;+intrc;++rc=qed_llh_shadow_sanity(cdev,ppfid,0,"remove_all");+if(rc)+returnrc;++p_filters=p_llh_info->pp_filters[ppfid];+memset(p_filters,0,NIG_REG_LLH_FUNC_FILTER_EN_SIZE*+sizeof(*p_filters));++return0;+}++intqed_abs_ppfid(structqed_dev*cdev,u8rel_ppfid,u8*p_abs_ppfid)+{+structqed_llh_info*p_llh_info=cdev->p_llh_info;++if(rel_ppfid>=p_llh_info->num_ppfid){+DP_NOTICE(cdev,+"rel_ppfid %d is not valid, available indices are 0..%hhu\n",+rel_ppfid,p_llh_info->num_ppfid-1);++return-EINVAL;+}++*p_abs_ppfid=p_llh_info->ppfid_array[rel_ppfid];++return0;+}++voidqed_llh_clear_ppfid_filters(structqed_dev*cdev,u8ppfid)+{+structqed_hwfn*p_hwfn=QED_LEADING_HWFN(cdev);+structqed_ptt*p_ptt=qed_ptt_acquire(p_hwfn);+u8filter_idx,abs_ppfid;+intrc=0;++if(!p_ptt)+return;++if(!test_bit(QED_MF_LLH_PROTO_CLSS,&cdev->mf_bits)&&+!test_bit(QED_MF_LLH_MAC_CLSS,&cdev->mf_bits))+gotoout;++rc=qed_abs_ppfid(cdev,ppfid,&abs_ppfid);+if(rc)+gotoout;++rc=qed_llh_shadow_remove_all_filters(cdev,ppfid);+if(rc)+gotoout;++for(filter_idx=0;filter_idx<NIG_REG_LLH_FUNC_FILTER_EN_SIZE;+filter_idx++){+rc=qed_llh_remove_filter(p_hwfn,p_ptt,+abs_ppfid,filter_idx);+if(rc)+gotoout;+}+out:+qed_ptt_release(p_hwfn,p_ptt);+}++intqed_llh_add_src_tcp_port_filter(structqed_dev*cdev,u16src_port)+{+returnqed_llh_add_protocol_filter(cdev,0,+QED_LLH_FILTER_TCP_SRC_PORT,+src_port,QED_LLH_DONT_CARE);+}++voidqed_llh_remove_src_tcp_port_filter(structqed_dev*cdev,u16src_port)+{+qed_llh_remove_protocol_filter(cdev,0,+QED_LLH_FILTER_TCP_SRC_PORT,+src_port,QED_LLH_DONT_CARE);+}++intqed_llh_add_dst_tcp_port_filter(structqed_dev*cdev,u16dest_port)+{+returnqed_llh_add_protocol_filter(cdev,0,+QED_LLH_FILTER_TCP_DEST_PORT,+QED_LLH_DONT_CARE,dest_port);+}++voidqed_llh_remove_dst_tcp_port_filter(structqed_dev*cdev,u16dest_port)+{+qed_llh_remove_protocol_filter(cdev,0,+QED_LLH_FILTER_TCP_DEST_PORT,+QED_LLH_DONT_CARE,dest_port);+}++voidqed_llh_clear_all_filters(structqed_dev*cdev)+{+u8ppfid;++if(!test_bit(QED_MF_LLH_PROTO_CLSS,&cdev->mf_bits)&&+!test_bit(QED_MF_LLH_MAC_CLSS,&cdev->mf_bits))+return;++for(ppfid=0;ppfid<cdev->p_llh_info->num_ppfid;ppfid++)+qed_llh_clear_ppfid_filters(cdev,ppfid);+}
@@ -259,6 +259,578 @@ static int qed_nvmetcp_start(struct qed_dev *cdev,return0;}+staticstructqed_hash_nvmetcp_con*qed_nvmetcp_get_hash(structqed_dev*cdev,+u32handle)+{+structqed_hash_nvmetcp_con*hash_con=NULL;++if(!(cdev->flags&QED_FLAG_STORAGE_STARTED))+returnNULL;++hash_for_each_possible(cdev->connections,hash_con,node,handle){+if(hash_con->con->icid==handle)+break;+}++if(!hash_con||hash_con->con->icid!=handle)+returnNULL;++returnhash_con;+}++staticintqed_sp_nvmetcp_conn_offload(structqed_hwfn*p_hwfn,+structqed_nvmetcp_conn*p_conn,+enumspq_modecomp_mode,+structqed_spq_comp_cb*p_comp_addr)+{+structnvmetcp_spe_conn_offload*p_ramrod=NULL;+structtcp_offload_params_opt2*p_tcp2=NULL;+structqed_sp_init_datainit_data={0};+structqed_spq_entry*p_ent=NULL;+dma_addr_tr2tq_pbl_addr;+dma_addr_txhq_pbl_addr;+dma_addr_tuhq_pbl_addr;+u16physical_q;+intrc=0;+u32dval;+u8i;++/* Get SPQ entry */+init_data.cid=p_conn->icid;+init_data.opaque_fid=p_hwfn->hw_info.opaque_fid;+init_data.comp_mode=comp_mode;+init_data.p_comp_data=p_comp_addr;++rc=qed_sp_init_request(p_hwfn,&p_ent,+NVMETCP_RAMROD_CMD_ID_OFFLOAD_CONN,+PROTOCOLID_NVMETCP,&init_data);+if(rc)+returnrc;++p_ramrod=&p_ent->ramrod.nvmetcp_conn_offload;++/* Transmission PQ is the first of the PF */+physical_q=qed_get_cm_pq_idx(p_hwfn,PQ_FLAGS_OFLD);+p_conn->physical_q0=cpu_to_le16(physical_q);+p_ramrod->nvmetcp.physical_q0=cpu_to_le16(physical_q);++/* nvmetcp Pure-ACK PQ */+physical_q=qed_get_cm_pq_idx(p_hwfn,PQ_FLAGS_ACK);+p_conn->physical_q1=cpu_to_le16(physical_q);+p_ramrod->nvmetcp.physical_q1=cpu_to_le16(physical_q);++p_ramrod->conn_id=cpu_to_le16(p_conn->conn_id);++DMA_REGPAIR_LE(p_ramrod->nvmetcp.sq_pbl_addr,p_conn->sq_pbl_addr);++r2tq_pbl_addr=qed_chain_get_pbl_phys(&p_conn->r2tq);+DMA_REGPAIR_LE(p_ramrod->nvmetcp.r2tq_pbl_addr,r2tq_pbl_addr);++xhq_pbl_addr=qed_chain_get_pbl_phys(&p_conn->xhq);+DMA_REGPAIR_LE(p_ramrod->nvmetcp.xhq_pbl_addr,xhq_pbl_addr);++uhq_pbl_addr=qed_chain_get_pbl_phys(&p_conn->uhq);+DMA_REGPAIR_LE(p_ramrod->nvmetcp.uhq_pbl_addr,uhq_pbl_addr);++p_ramrod->nvmetcp.flags=p_conn->offl_flags;+p_ramrod->nvmetcp.default_cq=p_conn->default_cq;+p_ramrod->nvmetcp.initial_ack=0;++DMA_REGPAIR_LE(p_ramrod->nvmetcp.nvmetcp.cccid_itid_table_addr,+p_conn->nvmetcp_cccid_itid_table_addr);+p_ramrod->nvmetcp.nvmetcp.cccid_max_range=+cpu_to_le16(p_conn->nvmetcp_cccid_max_range);++p_tcp2=&p_ramrod->tcp;++qed_set_fw_mac_addr(&p_tcp2->remote_mac_addr_hi,+&p_tcp2->remote_mac_addr_mid,+&p_tcp2->remote_mac_addr_lo,p_conn->remote_mac);+qed_set_fw_mac_addr(&p_tcp2->local_mac_addr_hi,+&p_tcp2->local_mac_addr_mid,+&p_tcp2->local_mac_addr_lo,p_conn->local_mac);++p_tcp2->vlan_id=cpu_to_le16(p_conn->vlan_id);+p_tcp2->flags=cpu_to_le16(p_conn->tcp_flags);++p_tcp2->ip_version=p_conn->ip_version;+for(i=0;i<4;i++){+dval=p_conn->remote_ip[i];+p_tcp2->remote_ip[i]=cpu_to_le32(dval);+dval=p_conn->local_ip[i];+p_tcp2->local_ip[i]=cpu_to_le32(dval);+}++p_tcp2->flow_label=cpu_to_le32(p_conn->flow_label);+p_tcp2->ttl=p_conn->ttl;+p_tcp2->tos_or_tc=p_conn->tos_or_tc;+p_tcp2->remote_port=cpu_to_le16(p_conn->remote_port);+p_tcp2->local_port=cpu_to_le16(p_conn->local_port);+p_tcp2->mss=cpu_to_le16(p_conn->mss);+p_tcp2->rcv_wnd_scale=p_conn->rcv_wnd_scale;+p_tcp2->connect_mode=p_conn->connect_mode;+p_tcp2->cwnd=cpu_to_le32(p_conn->cwnd);+p_tcp2->ka_max_probe_cnt=p_conn->ka_max_probe_cnt;+p_tcp2->ka_timeout=cpu_to_le32(p_conn->ka_timeout);+p_tcp2->max_rt_time=cpu_to_le32(p_conn->max_rt_time);+p_tcp2->ka_interval=cpu_to_le32(p_conn->ka_interval);++returnqed_spq_post(p_hwfn,p_ent,NULL);+}++staticintqed_sp_nvmetcp_conn_update(structqed_hwfn*p_hwfn,+structqed_nvmetcp_conn*p_conn,+enumspq_modecomp_mode,+structqed_spq_comp_cb*p_comp_addr)+{+structnvmetcp_conn_update_ramrod_params*p_ramrod=NULL;+structqed_spq_entry*p_ent=NULL;+structqed_sp_init_datainit_data;+intrc=-EINVAL;+u32dval;++/* Get SPQ entry */+memset(&init_data,0,sizeof(init_data));+init_data.cid=p_conn->icid;+init_data.opaque_fid=p_hwfn->hw_info.opaque_fid;+init_data.comp_mode=comp_mode;+init_data.p_comp_data=p_comp_addr;++rc=qed_sp_init_request(p_hwfn,&p_ent,+NVMETCP_RAMROD_CMD_ID_UPDATE_CONN,+PROTOCOLID_NVMETCP,&init_data);+if(rc)+returnrc;++p_ramrod=&p_ent->ramrod.nvmetcp_conn_update;+p_ramrod->conn_id=cpu_to_le16(p_conn->conn_id);+p_ramrod->flags=p_conn->update_flag;+p_ramrod->max_seq_size=cpu_to_le32(p_conn->max_seq_size);+dval=p_conn->max_recv_pdu_length;+p_ramrod->max_recv_pdu_length=cpu_to_le32(dval);+dval=p_conn->max_send_pdu_length;+p_ramrod->max_send_pdu_length=cpu_to_le32(dval);+dval=p_conn->first_seq_length;+p_ramrod->first_seq_length=cpu_to_le32(dval);++returnqed_spq_post(p_hwfn,p_ent,NULL);+}++staticintqed_sp_nvmetcp_conn_terminate(structqed_hwfn*p_hwfn,+structqed_nvmetcp_conn*p_conn,+enumspq_modecomp_mode,+structqed_spq_comp_cb*p_comp_addr)+{+structnvmetcp_spe_conn_termination*p_ramrod=NULL;+structqed_spq_entry*p_ent=NULL;+structqed_sp_init_datainit_data;+intrc=-EINVAL;++/* Get SPQ entry */+memset(&init_data,0,sizeof(init_data));+init_data.cid=p_conn->icid;+init_data.opaque_fid=p_hwfn->hw_info.opaque_fid;+init_data.comp_mode=comp_mode;+init_data.p_comp_data=p_comp_addr;++rc=qed_sp_init_request(p_hwfn,&p_ent,+NVMETCP_RAMROD_CMD_ID_TERMINATION_CONN,+PROTOCOLID_NVMETCP,&init_data);+if(rc)+returnrc;++p_ramrod=&p_ent->ramrod.nvmetcp_conn_terminate;+p_ramrod->conn_id=cpu_to_le16(p_conn->conn_id);+p_ramrod->abortive=p_conn->abortive_dsconnect;++returnqed_spq_post(p_hwfn,p_ent,NULL);+}++staticintqed_sp_nvmetcp_conn_clear_sq(structqed_hwfn*p_hwfn,+structqed_nvmetcp_conn*p_conn,+enumspq_modecomp_mode,+structqed_spq_comp_cb*p_comp_addr)+{+structqed_spq_entry*p_ent=NULL;+structqed_sp_init_datainit_data;+intrc=-EINVAL;++/* Get SPQ entry */+memset(&init_data,0,sizeof(init_data));+init_data.cid=p_conn->icid;+init_data.opaque_fid=p_hwfn->hw_info.opaque_fid;+init_data.comp_mode=comp_mode;+init_data.p_comp_data=p_comp_addr;++rc=qed_sp_init_request(p_hwfn,&p_ent,+NVMETCP_RAMROD_CMD_ID_CLEAR_SQ,+PROTOCOLID_NVMETCP,&init_data);+if(rc)+returnrc;++returnqed_spq_post(p_hwfn,p_ent,NULL);+}++staticvoid__iomem*qed_nvmetcp_get_db_addr(structqed_hwfn*p_hwfn,u32cid)+{+return(u8__iomem*)p_hwfn->doorbells++qed_db_addr(cid,DQ_DEMS_LEGACY);+}++staticintqed_nvmetcp_allocate_connection(structqed_hwfn*p_hwfn,+structqed_nvmetcp_conn**p_out_conn)+{+structqed_chain_init_paramsparams={+.mode=QED_CHAIN_MODE_PBL,+.intended_use=QED_CHAIN_USE_TO_CONSUME_PRODUCE,+.cnt_type=QED_CHAIN_CNT_TYPE_U16,+};+structqed_nvmetcp_pf_params*p_params=NULL;+structqed_nvmetcp_conn*p_conn=NULL;+intrc=0;++/* Try finding a free connection that can be used */+spin_lock_bh(&p_hwfn->p_nvmetcp_info->lock);+if(!list_empty(&p_hwfn->p_nvmetcp_info->free_list))+p_conn=list_first_entry(&p_hwfn->p_nvmetcp_info->free_list,+structqed_nvmetcp_conn,list_entry);+if(p_conn){+list_del(&p_conn->list_entry);+spin_unlock_bh(&p_hwfn->p_nvmetcp_info->lock);+*p_out_conn=p_conn;++return0;+}+spin_unlock_bh(&p_hwfn->p_nvmetcp_info->lock);++/* Need to allocate a new connection */+p_params=&p_hwfn->pf_params.nvmetcp_pf_params;++p_conn=kzalloc(sizeof(*p_conn),GFP_KERNEL);+if(!p_conn)+return-ENOMEM;++params.num_elems=p_params->num_r2tq_pages_in_ring*+QED_CHAIN_PAGE_SIZE/sizeof(structnvmetcp_wqe);+params.elem_size=sizeof(structnvmetcp_wqe);++rc=qed_chain_alloc(p_hwfn->cdev,&p_conn->r2tq,¶ms);+if(rc)+gotonomem_r2tq;++params.num_elems=p_params->num_uhq_pages_in_ring*+QED_CHAIN_PAGE_SIZE/sizeof(structiscsi_uhqe);+params.elem_size=sizeof(structiscsi_uhqe);++rc=qed_chain_alloc(p_hwfn->cdev,&p_conn->uhq,¶ms);+if(rc)+gotonomem_uhq;++params.elem_size=sizeof(structiscsi_xhqe);++rc=qed_chain_alloc(p_hwfn->cdev,&p_conn->xhq,¶ms);+if(rc)+gotonomem;++p_conn->free_on_delete=true;+*p_out_conn=p_conn;++return0;++nomem:+qed_chain_free(p_hwfn->cdev,&p_conn->uhq);+nomem_uhq:+qed_chain_free(p_hwfn->cdev,&p_conn->r2tq);+nomem_r2tq:+kfree(p_conn);++return-ENOMEM;+}++staticintqed_nvmetcp_acquire_connection(structqed_hwfn*p_hwfn,+structqed_nvmetcp_conn**p_out_conn)+{+structqed_nvmetcp_conn*p_conn=NULL;+intrc=0;+u32icid;++spin_lock_bh(&p_hwfn->p_nvmetcp_info->lock);+rc=qed_cxt_acquire_cid(p_hwfn,PROTOCOLID_NVMETCP,&icid);+spin_unlock_bh(&p_hwfn->p_nvmetcp_info->lock);++if(rc)+returnrc;++rc=qed_nvmetcp_allocate_connection(p_hwfn,&p_conn);+if(rc){+spin_lock_bh(&p_hwfn->p_nvmetcp_info->lock);+qed_cxt_release_cid(p_hwfn,icid);+spin_unlock_bh(&p_hwfn->p_nvmetcp_info->lock);++returnrc;+}++p_conn->icid=icid;+p_conn->conn_id=(u16)icid;+p_conn->fw_cid=(p_hwfn->hw_info.opaque_fid<<16)|icid;+*p_out_conn=p_conn;++returnrc;+}++staticvoidqed_nvmetcp_release_connection(structqed_hwfn*p_hwfn,+structqed_nvmetcp_conn*p_conn)+{+spin_lock_bh(&p_hwfn->p_nvmetcp_info->lock);+list_add_tail(&p_conn->list_entry,&p_hwfn->p_nvmetcp_info->free_list);+qed_cxt_release_cid(p_hwfn,p_conn->icid);+spin_unlock_bh(&p_hwfn->p_nvmetcp_info->lock);+}++staticvoidqed_nvmetcp_free_connection(structqed_hwfn*p_hwfn,+structqed_nvmetcp_conn*p_conn)+{+qed_chain_free(p_hwfn->cdev,&p_conn->xhq);+qed_chain_free(p_hwfn->cdev,&p_conn->uhq);+qed_chain_free(p_hwfn->cdev,&p_conn->r2tq);++kfree(p_conn);+}++intqed_nvmetcp_alloc(structqed_hwfn*p_hwfn)+{+structqed_nvmetcp_info*p_nvmetcp_info;++p_nvmetcp_info=kzalloc(sizeof(*p_nvmetcp_info),GFP_KERNEL);+if(!p_nvmetcp_info)+return-ENOMEM;++INIT_LIST_HEAD(&p_nvmetcp_info->free_list);++p_hwfn->p_nvmetcp_info=p_nvmetcp_info;++return0;+}++voidqed_nvmetcp_setup(structqed_hwfn*p_hwfn)+{+spin_lock_init(&p_hwfn->p_nvmetcp_info->lock);+}++voidqed_nvmetcp_free(structqed_hwfn*p_hwfn)+{+structqed_nvmetcp_conn*p_conn=NULL;++if(!p_hwfn->p_nvmetcp_info)+return;++while(!list_empty(&p_hwfn->p_nvmetcp_info->free_list)){+p_conn=list_first_entry(&p_hwfn->p_nvmetcp_info->free_list,+structqed_nvmetcp_conn,list_entry);+if(p_conn){+list_del(&p_conn->list_entry);+qed_nvmetcp_free_connection(p_hwfn,p_conn);+}+}++kfree(p_hwfn->p_nvmetcp_info);+p_hwfn->p_nvmetcp_info=NULL;+}++staticintqed_nvmetcp_acquire_conn(structqed_dev*cdev,+u32*handle,+u32*fw_cid,void__iomem**p_doorbell)+{+structqed_hash_nvmetcp_con*hash_con;+intrc;++/* Allocate a hashed connection */+hash_con=kzalloc(sizeof(*hash_con),GFP_ATOMIC);+if(!hash_con)+return-ENOMEM;++/* Acquire the connection */+rc=qed_nvmetcp_acquire_connection(QED_AFFIN_HWFN(cdev),+&hash_con->con);+if(rc){+DP_NOTICE(cdev,"Failed to acquire Connection\n");+kfree(hash_con);++returnrc;+}++/* Added the connection to hash table */+*handle=hash_con->con->icid;+*fw_cid=hash_con->con->fw_cid;+hash_add(cdev->connections,&hash_con->node,*handle);++if(p_doorbell)+*p_doorbell=qed_nvmetcp_get_db_addr(QED_AFFIN_HWFN(cdev),+*handle);++return0;+}++staticintqed_nvmetcp_release_conn(structqed_dev*cdev,u32handle)+{+structqed_hash_nvmetcp_con*hash_con;++hash_con=qed_nvmetcp_get_hash(cdev,handle);+if(!hash_con){+DP_NOTICE(cdev,"Failed to find connection for handle %d\n",+handle);++return-EINVAL;+}++hlist_del(&hash_con->node);+qed_nvmetcp_release_connection(QED_AFFIN_HWFN(cdev),hash_con->con);+kfree(hash_con);++return0;+}++staticintqed_nvmetcp_offload_conn(structqed_dev*cdev,u32handle,+structqed_nvmetcp_params_offload*conn_info)+{+structqed_hash_nvmetcp_con*hash_con;+structqed_nvmetcp_conn*con;++hash_con=qed_nvmetcp_get_hash(cdev,handle);+if(!hash_con){+DP_NOTICE(cdev,"Failed to find connection for handle %d\n",+handle);++return-EINVAL;+}++/* Update the connection with information from the params */+con=hash_con->con;++/* FW initializations */+con->layer_code=NVMETCP_SLOW_PATH_LAYER_CODE;+con->sq_pbl_addr=conn_info->sq_pbl_addr;+con->nvmetcp_cccid_max_range=conn_info->nvmetcp_cccid_max_range;+con->nvmetcp_cccid_itid_table_addr=conn_info->nvmetcp_cccid_itid_table_addr;+con->default_cq=conn_info->default_cq;++SET_FIELD(con->offl_flags,NVMETCP_CONN_OFFLOAD_PARAMS_TARGET_MODE,0);+SET_FIELD(con->offl_flags,NVMETCP_CONN_OFFLOAD_PARAMS_NVMETCP_MODE,1);+SET_FIELD(con->offl_flags,NVMETCP_CONN_OFFLOAD_PARAMS_TCP_ON_CHIP_1B,1);++/* Networking and TCP stack initializations */+ether_addr_copy(con->local_mac,conn_info->src.mac);+ether_addr_copy(con->remote_mac,conn_info->dst.mac);+memcpy(con->local_ip,conn_info->src.ip,sizeof(con->local_ip));+memcpy(con->remote_ip,conn_info->dst.ip,sizeof(con->remote_ip));+con->local_port=conn_info->src.port;+con->remote_port=conn_info->dst.port;+con->vlan_id=conn_info->vlan_id;++if(conn_info->timestamp_en)+SET_FIELD(con->tcp_flags,TCP_OFFLOAD_PARAMS_OPT2_TS_EN,1);++if(conn_info->delayed_ack_en)+SET_FIELD(con->tcp_flags,TCP_OFFLOAD_PARAMS_OPT2_DA_EN,1);++if(conn_info->tcp_keep_alive_en)+SET_FIELD(con->tcp_flags,TCP_OFFLOAD_PARAMS_OPT2_KA_EN,1);++if(conn_info->ecn_en)+SET_FIELD(con->tcp_flags,TCP_OFFLOAD_PARAMS_OPT2_ECN_EN,1);++con->ip_version=conn_info->ip_version;+con->flow_label=QED_TCP_FLOW_LABEL;+con->ka_max_probe_cnt=conn_info->ka_max_probe_cnt;+con->ka_timeout=conn_info->ka_timeout;+con->ka_interval=conn_info->ka_interval;+con->max_rt_time=conn_info->max_rt_time;+con->ttl=conn_info->ttl;+con->tos_or_tc=conn_info->tos_or_tc;+con->mss=conn_info->mss;+con->cwnd=conn_info->cwnd;+con->rcv_wnd_scale=conn_info->rcv_wnd_scale;+con->connect_mode=0;/* TCP_CONNECT_ACTIVE */++returnqed_sp_nvmetcp_conn_offload(QED_AFFIN_HWFN(cdev),con,+QED_SPQ_MODE_EBLOCK,NULL);+}++staticintqed_nvmetcp_update_conn(structqed_dev*cdev,+u32handle,+structqed_nvmetcp_params_update*conn_info)+{+structqed_hash_nvmetcp_con*hash_con;+structqed_nvmetcp_conn*con;++hash_con=qed_nvmetcp_get_hash(cdev,handle);+if(!hash_con){+DP_NOTICE(cdev,"Failed to find connection for handle %d\n",+handle);++return-EINVAL;+}++/* Update the connection with information from the params */+con=hash_con->con;++SET_FIELD(con->update_flag,+ISCSI_CONN_UPDATE_RAMROD_PARAMS_INITIAL_R2T,0);+SET_FIELD(con->update_flag,+ISCSI_CONN_UPDATE_RAMROD_PARAMS_IMMEDIATE_DATA,1);++if(conn_info->hdr_digest_en)+SET_FIELD(con->update_flag,ISCSI_CONN_UPDATE_RAMROD_PARAMS_HD_EN,1);++if(conn_info->data_digest_en)+SET_FIELD(con->update_flag,ISCSI_CONN_UPDATE_RAMROD_PARAMS_DD_EN,1);++/* Placeholder - initialize pfv, cpda, hpda */++con->max_seq_size=conn_info->max_io_size;+con->max_recv_pdu_length=conn_info->max_recv_pdu_length;+con->max_send_pdu_length=conn_info->max_send_pdu_length;+con->first_seq_length=conn_info->max_io_size;++returnqed_sp_nvmetcp_conn_update(QED_AFFIN_HWFN(cdev),con,+QED_SPQ_MODE_EBLOCK,NULL);+}++staticintqed_nvmetcp_clear_conn_sq(structqed_dev*cdev,u32handle)+{+structqed_hash_nvmetcp_con*hash_con;++hash_con=qed_nvmetcp_get_hash(cdev,handle);+if(!hash_con){+DP_NOTICE(cdev,"Failed to find connection for handle %d\n",+handle);++return-EINVAL;+}++returnqed_sp_nvmetcp_conn_clear_sq(QED_AFFIN_HWFN(cdev),hash_con->con,+QED_SPQ_MODE_EBLOCK,NULL);+}++staticintqed_nvmetcp_destroy_conn(structqed_dev*cdev,+u32handle,u8abrt_conn)+{+structqed_hash_nvmetcp_con*hash_con;++hash_con=qed_nvmetcp_get_hash(cdev,handle);+if(!hash_con){+DP_NOTICE(cdev,"Failed to find connection for handle %d\n",+handle);++return-EINVAL;+}++hash_con->con->abortive_dsconnect=abrt_conn;++returnqed_sp_nvmetcp_conn_terminate(QED_AFFIN_HWFN(cdev),hash_con->con,+QED_SPQ_MODE_EBLOCK,NULL);+}+staticconststructqed_nvmetcp_opsqed_nvmetcp_ops_pass={.common=&qed_common_ops_pass,.ll2=&qed_ll2_ops_pass,
This patch introduces the NVMeTCP Offload FW and HW HSI in order
to initialize the IO level configuration into a per IO HW
resource ("task") as part of the IO path flow.
Acked-by: Igor Russkikh <redacted>
Signed-off-by: Prabhakar Kushwaha <redacted>
Signed-off-by: Omkar Kulkarni <redacted>
Signed-off-by: Shai Malin <redacted>
Signed-off-by: Michal Kalderon <mkalderon@marvell.com>
Signed-off-by: Ariel Elior <redacted>
---
include/linux/qed/nvmetcp_common.h | 418 ++++++++++++++++++++++++++++-
include/linux/qed/qed_nvmetcp_if.h | 37 +++
2 files changed, 454 insertions(+), 1 deletion(-)
@@ -0,0 +1,372 @@+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)+/* Copyright 2021 Marvell. All rights reserved. */++#include<linux/kernel.h>+#include<linux/module.h>+#include<linux/pci.h>+#include<linux/kernel.h>+#include<linux/list.h>+#include<linux/mm.h>+#include<linux/types.h>+#include<asm/byteorder.h>+#include<linux/qed/common_hsi.h>+#include<linux/qed/storage_common.h>+#include<linux/qed/nvmetcp_common.h>+#include<linux/qed/qed_nvmetcp_if.h>+#include"qed_nvmetcp_fw_funcs.h"++#define NVMETCP_NUM_SGES_IN_CACHE 0x4++boolnvmetcp_is_slow_sgl(u16num_sges,boolsmall_mid_sge)+{+return(num_sges>SCSI_NUM_SGES_SLOW_SGL_THR&&small_mid_sge);+}++voidinit_scsi_sgl_context(structscsi_sgl_params*ctx_sgl_params,+structscsi_cached_sges*ctx_data_desc,+structstorage_sgl_task_params*sgl_params)+{+u8num_sges_to_init=(u8)(sgl_params->num_sges>NVMETCP_NUM_SGES_IN_CACHE?+NVMETCP_NUM_SGES_IN_CACHE:sgl_params->num_sges);+u8sge_index;++/* sgl params */+ctx_sgl_params->sgl_addr.lo=cpu_to_le32(sgl_params->sgl_phys_addr.lo);+ctx_sgl_params->sgl_addr.hi=cpu_to_le32(sgl_params->sgl_phys_addr.hi);+ctx_sgl_params->sgl_total_length=cpu_to_le32(sgl_params->total_buffer_size);+ctx_sgl_params->sgl_num_sges=cpu_to_le16(sgl_params->num_sges);++for(sge_index=0;sge_index<num_sges_to_init;sge_index++){+ctx_data_desc->sge[sge_index].sge_addr.lo=+cpu_to_le32(sgl_params->sgl[sge_index].sge_addr.lo);+ctx_data_desc->sge[sge_index].sge_addr.hi=+cpu_to_le32(sgl_params->sgl[sge_index].sge_addr.hi);+ctx_data_desc->sge[sge_index].sge_len=+cpu_to_le32(sgl_params->sgl[sge_index].sge_len);+}+}++staticinlineu32calc_rw_task_size(structnvmetcp_task_params*task_params,+enumnvmetcp_task_typetask_type)+{+u32io_size;++if(task_type==NVMETCP_TASK_TYPE_HOST_WRITE)+io_size=task_params->tx_io_size;+else+io_size=task_params->rx_io_size;++if(unlikely(!io_size))+return0;++returnio_size;+}++staticinlinevoidinit_sqe(structnvmetcp_task_params*task_params,+structstorage_sgl_task_params*sgl_task_params,+enumnvmetcp_task_typetask_type)+{+if(!task_params->sqe)+return;++memset(task_params->sqe,0,sizeof(*task_params->sqe));+task_params->sqe->task_id=cpu_to_le16(task_params->itid);++switch(task_type){+caseNVMETCP_TASK_TYPE_HOST_WRITE:{+u32buf_size=0;+u32num_sges=0;++SET_FIELD(task_params->sqe->contlen_cdbsize,+NVMETCP_WQE_CDB_SIZE_OR_NVMETCP_CMD,1);+SET_FIELD(task_params->sqe->flags,NVMETCP_WQE_WQE_TYPE,+NVMETCP_WQE_TYPE_NORMAL);+if(task_params->tx_io_size){+if(task_params->send_write_incapsule)+buf_size=calc_rw_task_size(task_params,task_type);++if(nvmetcp_is_slow_sgl(sgl_task_params->num_sges,+sgl_task_params->small_mid_sge))+num_sges=NVMETCP_WQE_NUM_SGES_SLOWIO;+else+num_sges=min((u16)sgl_task_params->num_sges,+(u16)SCSI_NUM_SGES_SLOW_SGL_THR);+}+SET_FIELD(task_params->sqe->flags,NVMETCP_WQE_NUM_SGES,num_sges);+SET_FIELD(task_params->sqe->contlen_cdbsize,NVMETCP_WQE_CONT_LEN,buf_size);+}break;++caseNVMETCP_TASK_TYPE_HOST_READ:{+SET_FIELD(task_params->sqe->flags,NVMETCP_WQE_WQE_TYPE,+NVMETCP_WQE_TYPE_NORMAL);+SET_FIELD(task_params->sqe->contlen_cdbsize,+NVMETCP_WQE_CDB_SIZE_OR_NVMETCP_CMD,1);+}break;++caseNVMETCP_TASK_TYPE_INIT_CONN_REQUEST:{+SET_FIELD(task_params->sqe->flags,NVMETCP_WQE_WQE_TYPE,+NVMETCP_WQE_TYPE_MIDDLE_PATH);++if(task_params->tx_io_size){+SET_FIELD(task_params->sqe->contlen_cdbsize,NVMETCP_WQE_CONT_LEN,+task_params->tx_io_size);+SET_FIELD(task_params->sqe->flags,NVMETCP_WQE_NUM_SGES,+min((u16)sgl_task_params->num_sges,+(u16)SCSI_NUM_SGES_SLOW_SGL_THR));+}+}break;++caseNVMETCP_TASK_TYPE_CLEANUP:+SET_FIELD(task_params->sqe->flags,NVMETCP_WQE_WQE_TYPE,+NVMETCP_WQE_TYPE_TASK_CLEANUP);++default:+break;+}+}++/* The following function initializes of NVMeTCP task params */+staticinlinevoid+init_nvmetcp_task_params(structe5_nvmetcp_task_context*context,+structnvmetcp_task_params*task_params,+enumnvmetcp_task_typetask_type)+{+context->ystorm_st_context.state.cccid=task_params->host_cccid;+SET_FIELD(context->ustorm_st_context.error_flags,USTORM_NVMETCP_TASK_ST_CTX_NVME_TCP,1);+context->ustorm_st_context.nvme_tcp_opaque_lo=cpu_to_le32(task_params->opq.lo);+context->ustorm_st_context.nvme_tcp_opaque_hi=cpu_to_le32(task_params->opq.hi);+}++/* The following function initializes default values to all tasks */+staticinlinevoid+init_default_nvmetcp_task(structnvmetcp_task_params*task_params,void*pdu_header,+enumnvmetcp_task_typetask_type)+{+structe5_nvmetcp_task_context*context=task_params->context;+constu8val_byte=context->mstorm_ag_context.cdu_validation;+u8dw_index;++memset(context,0,sizeof(*context));++init_nvmetcp_task_params(context,task_params,+(enumnvmetcp_task_type)task_type);++if(task_type==NVMETCP_TASK_TYPE_HOST_WRITE||+task_type==NVMETCP_TASK_TYPE_HOST_READ){+for(dw_index=0;dw_index<QED_NVMETCP_CMD_HDR_SIZE/4;dw_index++)+context->ystorm_st_context.pdu_hdr.task_hdr.reg[dw_index]=+cpu_to_le32(((u32*)pdu_header)[dw_index]);+}else{+for(dw_index=0;dw_index<QED_NVMETCP_CMN_HDR_SIZE/4;dw_index++)+context->ystorm_st_context.pdu_hdr.task_hdr.reg[dw_index]=+cpu_to_le32(((u32*)pdu_header)[dw_index]);+}++/* M-Storm Context: */+context->mstorm_ag_context.cdu_validation=val_byte;+context->mstorm_st_context.task_type=(u8)(task_type);+context->mstorm_ag_context.task_cid=cpu_to_le16(task_params->conn_icid);++/* Ustorm Context: */+SET_FIELD(context->ustorm_ag_context.flags1,E5_USTORM_NVMETCP_TASK_AG_CTX_R2T2RECV,1);+context->ustorm_st_context.task_type=(u8)(task_type);+context->ustorm_st_context.cq_rss_number=task_params->cq_rss_number;+context->ustorm_ag_context.icid=cpu_to_le16(task_params->conn_icid);+}++/* The following function initializes the U-Storm Task Contexts */+staticinlinevoid+init_ustorm_task_contexts(structustorm_nvmetcp_task_st_ctx*ustorm_st_context,+structe5_ustorm_nvmetcp_task_ag_ctx*ustorm_ag_context,+u32remaining_recv_len,+u32expected_data_transfer_len,u8num_sges,+booltx_dif_conn_err_en)+{+/* Remaining data to be received in bytes. Used in validations*/+ustorm_st_context->rem_rcv_len=cpu_to_le32(remaining_recv_len);+ustorm_ag_context->exp_data_acked=cpu_to_le32(expected_data_transfer_len);+ustorm_st_context->exp_data_transfer_len=cpu_to_le32(expected_data_transfer_len);+SET_FIELD(ustorm_st_context->reg1.reg1_map,NVMETCP_REG1_NUM_SGES,num_sges);+SET_FIELD(ustorm_ag_context->flags2,E5_USTORM_NVMETCP_TASK_AG_CTX_DIF_ERROR_CF_EN,+tx_dif_conn_err_en?1:0);+}++/* The following function initializes Local Completion Contexts: */+staticinlinevoid+set_local_completion_context(structe5_nvmetcp_task_context*context)+{+SET_FIELD(context->ystorm_st_context.state.flags,+YSTORM_NVMETCP_TASK_STATE_LOCAL_COMP,1);+SET_FIELD(context->ustorm_st_context.flags,+USTORM_NVMETCP_TASK_ST_CTX_LOCAL_COMP,1);+}++/* Common Fastpath task init function: */+staticinlinevoid+init_rw_nvmetcp_task(structnvmetcp_task_params*task_params,+enumnvmetcp_task_typetask_type,+structnvmetcp_conn_params*conn_params,void*pdu_header,+structstorage_sgl_task_params*sgl_task_params)+{+structe5_nvmetcp_task_context*context=task_params->context;+u32task_size=calc_rw_task_size(task_params,task_type);+u32exp_data_transfer_len=conn_params->max_burst_length;+boolslow_io=false;+u8num_sges=0;++init_default_nvmetcp_task(task_params,pdu_header,task_type);++/* Tx/Rx: */+if(task_params->tx_io_size){+/* if data to transmit: */+init_scsi_sgl_context(&context->ystorm_st_context.state.sgl_params,+&context->ystorm_st_context.state.data_desc,+sgl_task_params);+slow_io=nvmetcp_is_slow_sgl(sgl_task_params->num_sges,+sgl_task_params->small_mid_sge);+num_sges=+(u8)(!slow_io?min((u32)sgl_task_params->num_sges,+(u32)SCSI_NUM_SGES_SLOW_SGL_THR):+NVMETCP_WQE_NUM_SGES_SLOWIO);+if(slow_io){+SET_FIELD(context->ystorm_st_context.state.flags,+YSTORM_NVMETCP_TASK_STATE_SLOW_IO,1);+}+}elseif(task_params->rx_io_size){+/* if data to receive: */+init_scsi_sgl_context(&context->mstorm_st_context.sgl_params,+&context->mstorm_st_context.data_desc,+sgl_task_params);+num_sges=+(u8)(!nvmetcp_is_slow_sgl(sgl_task_params->num_sges,+sgl_task_params->small_mid_sge)?+min((u32)sgl_task_params->num_sges,+(u32)SCSI_NUM_SGES_SLOW_SGL_THR):+NVMETCP_WQE_NUM_SGES_SLOWIO);+context->mstorm_st_context.rem_task_size=cpu_to_le32(task_size);+}++/* Ustorm context: */+if(exp_data_transfer_len>task_size)+/* The size of the transmitted task*/+exp_data_transfer_len=task_size;+init_ustorm_task_contexts(&context->ustorm_st_context,+&context->ustorm_ag_context,+/* Remaining Receive length is the Task Size */+task_size,+/* The size of the transmitted task */+exp_data_transfer_len,+/* num_sges */+num_sges,+false);++/* Set exp_data_acked */+if(task_type==NVMETCP_TASK_TYPE_HOST_WRITE){+if(task_params->send_write_incapsule)+context->ustorm_ag_context.exp_data_acked=task_size;+else+context->ustorm_ag_context.exp_data_acked=0;+}elseif(task_type==NVMETCP_TASK_TYPE_HOST_READ){+context->ustorm_ag_context.exp_data_acked=0;+}++context->ustorm_ag_context.exp_cont_len=0;++init_sqe(task_params,sgl_task_params,task_type);+}++staticvoid+init_common_initiator_read_task(structnvmetcp_task_params*task_params,+structnvmetcp_conn_params*conn_params,+structnvmetcp_cmd_capsule_hdr*cmd_pdu_header,+structstorage_sgl_task_params*sgl_task_params)+{+init_rw_nvmetcp_task(task_params,NVMETCP_TASK_TYPE_HOST_READ,+conn_params,cmd_pdu_header,sgl_task_params);+}++voidinit_nvmetcp_host_read_task(structnvmetcp_task_params*task_params,+structnvmetcp_conn_params*conn_params,+structnvmetcp_cmd_capsule_hdr*cmd_pdu_header,+structstorage_sgl_task_params*sgl_task_params)+{+init_common_initiator_read_task(task_params,conn_params,+(void*)cmd_pdu_header,sgl_task_params);+}++staticvoid+init_common_initiator_write_task(structnvmetcp_task_params*task_params,+structnvmetcp_conn_params*conn_params,+structnvmetcp_cmd_capsule_hdr*cmd_pdu_header,+structstorage_sgl_task_params*sgl_task_params)+{+init_rw_nvmetcp_task(task_params,NVMETCP_TASK_TYPE_HOST_WRITE,+conn_params,cmd_pdu_header,sgl_task_params);+}++voidinit_nvmetcp_host_write_task(structnvmetcp_task_params*task_params,+structnvmetcp_conn_params*conn_params,+structnvmetcp_cmd_capsule_hdr*cmd_pdu_header,+structstorage_sgl_task_params*sgl_task_params)+{+init_common_initiator_write_task(task_params,conn_params,+(void*)cmd_pdu_header,+sgl_task_params);+}++staticvoid+init_common_login_request_task(structnvmetcp_task_params*task_params,+void*login_req_pdu_header,+structstorage_sgl_task_params*tx_sgl_task_params,+structstorage_sgl_task_params*rx_sgl_task_params)+{+structe5_nvmetcp_task_context*context=task_params->context;++init_default_nvmetcp_task(task_params,(void*)login_req_pdu_header,+NVMETCP_TASK_TYPE_INIT_CONN_REQUEST);++/* Ustorm Context: */+init_ustorm_task_contexts(&context->ustorm_st_context,+&context->ustorm_ag_context,++/* Remaining Receive length is the Task Size */+task_params->rx_io_size?+rx_sgl_task_params->total_buffer_size:0,++/* The size of the transmitted task */+task_params->tx_io_size?+tx_sgl_task_params->total_buffer_size:0,+0,/* num_sges */+0);/* tx_dif_conn_err_en */++/* SGL context: */+if(task_params->tx_io_size)+init_scsi_sgl_context(&context->ystorm_st_context.state.sgl_params,+&context->ystorm_st_context.state.data_desc,+tx_sgl_task_params);+if(task_params->rx_io_size)+init_scsi_sgl_context(&context->mstorm_st_context.sgl_params,+&context->mstorm_st_context.data_desc,+rx_sgl_task_params);++context->mstorm_st_context.rem_task_size=+cpu_to_le32(task_params->rx_io_size?+rx_sgl_task_params->total_buffer_size:0);++init_sqe(task_params,tx_sgl_task_params,NVMETCP_TASK_TYPE_INIT_CONN_REQUEST);+}++/* The following function initializes Login task in Host mode: */+voidinit_nvmetcp_init_conn_req_task(structnvmetcp_task_params*task_params,+structnvmetcp_init_conn_req_hdr*init_conn_req_pdu_hdr,+structstorage_sgl_task_params*tx_sgl_task_params,+structstorage_sgl_task_params*rx_sgl_task_params)+{+init_common_login_request_task(task_params,init_conn_req_pdu_hdr,+tx_sgl_task_params,rx_sgl_task_params);+}++voidinit_cleanup_task_nvmetcp(structnvmetcp_task_params*task_params)+{+init_sqe(task_params,NULL,NVMETCP_TASK_TYPE_CLEANUP);+}
@@ -2106,6 +2106,30 @@ int qed_cxt_set_pf_params(struct qed_hwfn *p_hwfn, u32 rdma_tasks)}break;}+caseQED_PCI_NVMETCP:+{+structqed_nvmetcp_pf_params*p_params;++p_params=&p_hwfn->pf_params.nvmetcp_pf_params;++if(p_params->num_cons&&p_params->num_tasks){+qed_cxt_set_proto_cid_count(p_hwfn,+PROTOCOLID_NVMETCP,+p_params->num_cons,+0);++qed_cxt_set_proto_tid_count(p_hwfn,+PROTOCOLID_NVMETCP,+QED_CTX_NVMETCP_TID_SEG,+0,+p_params->num_tasks,+true);+}else{+DP_INFO(p_hwfn->cdev,+"NvmeTCP personality used without setting params!\n");+}+break;+}default:return-EINVAL;}
@@ -2132,6 +2156,10 @@ int qed_cxt_get_tid_mem_info(struct qed_hwfn *p_hwfn,proto=PROTOCOLID_ISCSI;seg=QED_CXT_ISCSI_TID_SEG;break;+caseQED_PCI_NVMETCP:+proto=PROTOCOLID_NVMETCP;+seg=QED_CTX_NVMETCP_TID_SEG;+break;default:return-EINVAL;}
@@ -2458,6 +2486,10 @@ int qed_cxt_get_task_ctx(struct qed_hwfn *p_hwfn,proto=PROTOCOLID_ISCSI;seg=QED_CXT_ISCSI_TID_SEG;break;+caseQED_PCI_NVMETCP:+proto=PROTOCOLID_NVMETCP;+seg=QED_CTX_NVMETCP_TID_SEG;+break;default:return-EINVAL;}
@@ -667,7 +668,8 @@ qed_llh_set_engine_affin(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt)}/* Storage PF is bound to a single engine while L2 PF uses both */-if(QED_IS_FCOE_PERSONALITY(p_hwfn)||QED_IS_ISCSI_PERSONALITY(p_hwfn))+if(QED_IS_FCOE_PERSONALITY(p_hwfn)||QED_IS_ISCSI_PERSONALITY(p_hwfn)||+QED_IS_NVMETCP_PERSONALITY(p_hwfn))eng=cdev->fir_affin?QED_ENG1:QED_ENG0;else/* L2_PERSONALITY */eng=QED_BOTH_ENG;
@@ -960,7 +960,8 @@ static int qed_sp_ll2_rx_queue_start(struct qed_hwfn *p_hwfn,if(test_bit(QED_MF_LL2_NON_UNICAST,&p_hwfn->cdev->mf_bits)&&p_ramrod->main_func_queue&&conn_type!=QED_LL2_TYPE_ROCE&&-conn_type!=QED_LL2_TYPE_IWARP){+conn_type!=QED_LL2_TYPE_IWARP&&+(!QED_IS_NVMETCP_PERSONALITY(p_hwfn))){p_ramrod->mf_si_bcast_accept_all=1;p_ramrod->mf_si_mcast_accept_all=1;}else{
@@ -1049,6 +1050,8 @@ static int qed_sp_ll2_tx_queue_start(struct qed_hwfn *p_hwfn,caseQED_LL2_TYPE_OOO:if(p_hwfn->hw_info.personality==QED_PCI_ISCSI)p_ramrod->conn_type=PROTOCOLID_ISCSI;+elseif(p_hwfn->hw_info.personality==QED_PCI_NVMETCP)+p_ramrod->conn_type=PROTOCOLID_NVMETCP;elsep_ramrod->conn_type=PROTOCOLID_IWARP;break;
@@ -1634,7 +1637,8 @@ int qed_ll2_establish_connection(void *cxt, u8 connection_handle)if(rc)gotoout;-if(!QED_IS_RDMA_PERSONALITY(p_hwfn))+if(!QED_IS_RDMA_PERSONALITY(p_hwfn)&&+!QED_IS_NVMETCP_PERSONALITY(p_hwfn))qed_wr(p_hwfn,p_ptt,PRS_REG_USE_LIGHT_L2,1);qed_ll2_establish_connection_ooo(p_hwfn,p_ll2_conn);
@@ -2376,7 +2380,8 @@ static int qed_ll2_start_ooo(struct qed_hwfn *p_hwfn,staticboolqed_ll2_is_storage_eng1(structqed_dev*cdev){return(QED_IS_FCOE_PERSONALITY(QED_LEADING_HWFN(cdev))||-QED_IS_ISCSI_PERSONALITY(QED_LEADING_HWFN(cdev)))&&+QED_IS_ISCSI_PERSONALITY(QED_LEADING_HWFN(cdev))||+QED_IS_NVMETCP_PERSONALITY(QED_LEADING_HWFN(cdev)))&&(QED_AFFIN_HWFN(cdev)!=QED_LEADING_HWFN(cdev));}
@@ -2402,11 +2407,13 @@ static int qed_ll2_stop(struct qed_dev *cdev)if(cdev->ll2->handle==QED_LL2_UNUSED_HANDLE)return0;+if(!QED_IS_NVMETCP_PERSONALITY(p_hwfn))+qed_llh_remove_mac_filter(cdev,0,cdev->ll2_mac_address);qed_llh_remove_mac_filter(cdev,0,cdev->ll2_mac_address);eth_zero_addr(cdev->ll2_mac_address);-if(QED_IS_ISCSI_PERSONALITY(p_hwfn))+if(QED_IS_ISCSI_PERSONALITY(p_hwfn)||QED_IS_NVMETCP_PERSONALITY(p_hwfn))qed_ll2_stop_ooo(p_hwfn);/* In CMT mode, LL2 is always started on engine 0 for a storage PF */
@@ -2442,6 +2449,7 @@ static int __qed_ll2_start(struct qed_hwfn *p_hwfn,conn_type=QED_LL2_TYPE_FCOE;break;caseQED_PCI_ISCSI:+caseQED_PCI_NVMETCP:conn_type=QED_LL2_TYPE_ISCSI;break;caseQED_PCI_ETH_ROCE:
@@ -2576,10 +2584,13 @@ static int qed_ll2_start(struct qed_dev *cdev, struct qed_ll2_params *params)}}-rc=qed_llh_add_mac_filter(cdev,0,params->ll2_mac_address);-if(rc){-DP_NOTICE(cdev,"Failed to add an LLH filter\n");-gotoerr3;+if(!QED_IS_NVMETCP_PERSONALITY(p_hwfn)){+rc=qed_llh_add_mac_filter(cdev,0,params->ll2_mac_address);+if(rc){+DP_NOTICE(cdev,"Failed to add an LLH filter\n");+gotoerr3;+}+}ether_addr_copy(cdev->ll2_mac_address,params->ll2_mac_address);
@@ -0,0 +1,239 @@+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)+/*+*Copyright2021Marvell.Allrightsreserved.+*/++#include<linux/types.h>+#include<asm/byteorder.h>+#include<asm/param.h>+#include<linux/delay.h>+#include<linux/pci.h>+#include<linux/dma-mapping.h>+#include<linux/etherdevice.h>+#include<linux/kernel.h>+#include<linux/stddef.h>+#include<linux/errno.h>++#include<net/tcp.h>++#include<linux/qed/qed_nvmetcp_ip_services_if.h>++#define QED_IP_RESOL_TIMEOUT 4++intqed_route_ipv4(structsockaddr_storage*local_addr,+structsockaddr_storage*remote_addr,+structsockaddr*hardware_address,+structnet_device**ndev)+{+structneighbour*neigh=NULL;+__be32*loc_ip,*rem_ip;+structrtable*rt;+intrc=-ENXIO;+intretry;++loc_ip=&((structsockaddr_in*)local_addr)->sin_addr.s_addr;+rem_ip=&((structsockaddr_in*)remote_addr)->sin_addr.s_addr;+*ndev=NULL;+rt=ip_route_output(&init_net,*rem_ip,*loc_ip,0/*tos*/,0/*oif*/);+if(IS_ERR(rt)){+pr_err("lookup route failed\n");+rc=PTR_ERR(rt);+gotoreturn_err;+}++neigh=dst_neigh_lookup(&rt->dst,rem_ip);+if(!neigh){+rc=-ENOMEM;+ip_rt_put(rt);+gotoreturn_err;+}++*ndev=rt->dst.dev;+ip_rt_put(rt);++/* If not resolved, kick-off state machine towards resolution */+if(!(neigh->nud_state&NUD_VALID))+neigh_event_send(neigh,NULL);++/* query neighbor until resolved or timeout */+retry=QED_IP_RESOL_TIMEOUT;+while(!(neigh->nud_state&NUD_VALID)&&retry>0){+msleep(1000);+retry--;+}++if(neigh->nud_state&NUD_VALID){+/* copy resolved MAC address */+neigh_ha_snapshot(hardware_address->sa_data,neigh,*ndev);++hardware_address->sa_family=(*ndev)->type;+rc=0;+}++neigh_release(neigh);+if(!(*loc_ip)){+*loc_ip=inet_select_addr(*ndev,*rem_ip,RT_SCOPE_UNIVERSE);+local_addr->ss_family=AF_INET;+}++return_err:++returnrc;+}+EXPORT_SYMBOL(qed_route_ipv4);++intqed_route_ipv6(structsockaddr_storage*local_addr,+structsockaddr_storage*remote_addr,+structsockaddr*hardware_address,+structnet_device**ndev)+{+structneighbour*neigh=NULL;+structdst_entry*dst;+structflowi6fl6;+intrc=-ENXIO;+intretry;++memset(&fl6,0,sizeof(fl6));+fl6.saddr=((structsockaddr_in6*)local_addr)->sin6_addr;+fl6.daddr=((structsockaddr_in6*)remote_addr)->sin6_addr;++dst=ip6_route_output(&init_net,NULL,&fl6);+if(!dst||dst->error){+if(dst){+dst_release(dst);+pr_err("lookup route failed %d\n",dst->error);+}++gotoout;+}++neigh=dst_neigh_lookup(dst,&fl6.daddr);+if(neigh){+*ndev=ip6_dst_idev(dst)->dev;++/* If not resolved, kick-off state machine towards resolution */+if(!(neigh->nud_state&NUD_VALID))+neigh_event_send(neigh,NULL);++/* query neighbor until resolved or timeout */+retry=QED_IP_RESOL_TIMEOUT;+while(!(neigh->nud_state&NUD_VALID)&&retry>0){+msleep(1000);+retry--;+}++if(neigh->nud_state&NUD_VALID){+neigh_ha_snapshot((u8*)hardware_address->sa_data,neigh,*ndev);++hardware_address->sa_family=(*ndev)->type;+rc=0;+}++neigh_release(neigh);++if(ipv6_addr_any(&fl6.saddr)){+if(ipv6_dev_get_saddr(dev_net(*ndev),*ndev,+&fl6.daddr,0,&fl6.saddr)){+pr_err("Unable to find source IP address\n");+gotoout;+}++local_addr->ss_family=AF_INET6;+((structsockaddr_in6*)local_addr)->sin6_addr=+fl6.saddr;+}+}++dst_release(dst);++out:++returnrc;+}+EXPORT_SYMBOL(qed_route_ipv6);++voidqed_vlan_get_ndev(structnet_device**ndev,u16*vlan_id)+{+if(is_vlan_dev(*ndev)){+*vlan_id=vlan_dev_vlan_id(*ndev);+*ndev=vlan_dev_real_dev(*ndev);+}+}+EXPORT_SYMBOL(qed_vlan_get_ndev);++structpci_dev*qed_validate_ndev(structnet_device*ndev)+{+structpci_dev*pdev=NULL;+structnet_device*upper;++for_each_pci_dev(pdev){+if(pdev&&pdev->driver&&+!strcmp(pdev->driver->name,"qede")){+upper=pci_get_drvdata(pdev);+if(upper->ifindex==ndev->ifindex)+returnpdev;+}+}++returnNULL;+}+EXPORT_SYMBOL(qed_validate_ndev);++__be16qed_get_in_port(structsockaddr_storage*sa)+{+returnsa->ss_family==AF_INET+?((structsockaddr_in*)sa)->sin_port+:((structsockaddr_in6*)sa)->sin6_port;+}+EXPORT_SYMBOL(qed_get_in_port);++intqed_fetch_tcp_port(structsockaddr_storagelocal_ip_addr,+structsocket**sock,u16*port)+{+structsockaddr_storagesa;+intrc=0;++rc=sock_create(local_ip_addr.ss_family,SOCK_STREAM,IPPROTO_TCP,sock);+if(rc){+pr_warn("failed to create socket: %d\n",rc);+gotoerr;+}++(*sock)->sk->sk_allocation=GFP_KERNEL;+sk_set_memalloc((*sock)->sk);++rc=kernel_bind(*sock,(structsockaddr*)&local_ip_addr,+sizeof(local_ip_addr));++if(rc){+pr_warn("failed to bind socket: %d\n",rc);+gotoerr_sock;+}++rc=kernel_getsockname(*sock,(structsockaddr*)&sa);+if(rc<0){+pr_warn("getsockname() failed: %d\n",rc);+gotoerr_sock;+}++*port=ntohs(qed_get_in_port(&sa));++return0;++err_sock:+sock_release(*sock);+sock=NULL;+err:++returnrc;+}+EXPORT_SYMBOL(qed_fetch_tcp_port);++voidqed_return_tcp_port(structsocket*sock)+{+if(sock&&sock->sk){+tcp_set_state(sock->sk,TCP_CLOSE);+sock_release(sock);+}+}+EXPORT_SYMBOL(qed_return_tcp_port);
This patch will present the structure for the NVMeTCP offload common
layer driver. This module is added under "drivers/nvme/host/" and future
offload drivers which will register to it will be placed under
"drivers/nvme/hw".
This new driver will be enabled by the Kconfig "NVM Express over Fabrics
TCP offload commmon layer".
In order to support the new transport type, for host mode, no change is
needed.
Each new vendor-specific offload driver will register to this ULP during
its probe function, by filling out the nvme_tcp_ofld_dev->ops and
nvme_tcp_ofld_dev->private_data and calling nvme_tcp_ofld_register_dev
with the initialized struct.
The internal implementation:
- tcp-offload.h:
Includes all common structs and ops to be used and shared by offload
drivers.
- tcp-offload.c:
Includes the init function which registers as a NVMf transport just
like any other transport.
Acked-by: Igor Russkikh <redacted>
Signed-off-by: Dean Balandin <redacted>
Signed-off-by: Prabhakar Kushwaha <redacted>
Signed-off-by: Omkar Kulkarni <redacted>
Signed-off-by: Michal Kalderon <mkalderon@marvell.com>
Signed-off-by: Ariel Elior <redacted>
Signed-off-by: Shai Malin <redacted>
---
drivers/nvme/host/Kconfig | 16 +++
drivers/nvme/host/Makefile | 3 +
drivers/nvme/host/tcp-offload.c | 126 +++++++++++++++++++
drivers/nvme/host/tcp-offload.h | 206 ++++++++++++++++++++++++++++++++
4 files changed, 351 insertions(+)
create mode 100644 drivers/nvme/host/tcp-offload.c
create mode 100644 drivers/nvme/host/tcp-offload.h
@@ -0,0 +1,206 @@+/* SPDX-License-Identifier: GPL-2.0 */+/*+*Copyright2021Marvell.Allrightsreserved.+*/++/* Linux includes */+#include<linux/dma-mapping.h>+#include<linux/scatterlist.h>+#include<linux/types.h>+#include<linux/nvme-tcp.h>++/* Driver includes */+#include"nvme.h"+#include"fabrics.h"++/* Forward declarations */+structnvme_tcp_ofld_ops;++/* Representation of a vendor-specific device. This is the struct used to+*registertotheoffloadlayerbythevendor-specificdriverduringitsprobe+*function.+*Allocatedbyvendor-specificdriver.+*/+structnvme_tcp_ofld_dev{+structlist_headentry;+structpci_dev*qede_pdev;+structnet_device*ndev;+structnvme_tcp_ofld_ops*ops;+};++/* Per IO struct holding the nvme_request and command+*Allocatedbyblk-mq.+*/+structnvme_tcp_ofld_req{+structnvme_requestreq;+structnvme_commandnvme_cmd;+structlist_headqueue_entry;+structnvme_tcp_ofld_queue*queue;+structrequest*rq;++/* Vendor specific driver context */+void*private_data;++boolasync;+boollast;++void(*done)(structnvme_tcp_ofld_req*req,+unionnvme_result*result,+__le16status);+};++enumnvme_tcp_ofld_queue_flags{+NVME_TCP_OFLD_Q_ALLOCATED=0,+NVME_TCP_OFLD_Q_LIVE=1,+};++/* Allocated by nvme_tcp_ofld */+structnvme_tcp_ofld_queue{+/* Offload device associated to this queue */+structnvme_tcp_ofld_dev*dev;+structnvme_tcp_ofld_ctrl*ctrl;+unsignedlongflags;+size_tcmnd_capsule_len;++/* Vendor specific driver context */+void*private_data;++/* Error callback function */+int(*report_err)(structnvme_tcp_ofld_queue*queue);+};++/* Connectivity (routing) params used for establishing a connection */+structnvme_tcp_ofld_ctrl_con_params{+/* Input params */+structsockaddr_storageremote_ip_addr;++/* If NVMF_OPT_HOST_TRADDR is provided it will be set in local_ip_addr+*innvme_tcp_ofld_create_ctrl().+*IfNVMF_OPT_HOST_TRADDRisnotprovidedthelocal_ip_addrwillbe+*initializedbyclaim_dev().+*/+structsockaddr_storagelocal_ip_addr;++/* Output params */+structsockaddrremote_mac_addr;+structsockaddrlocal_mac_addr;+u16vlan_id;+};++/* Allocated by nvme_tcp_ofld */+structnvme_tcp_ofld_ctrl{+structnvme_ctrlnctrl;+structlist_headlist;+structnvme_tcp_ofld_dev*dev;++/* admin and IO queues */+structblk_mq_tag_settag_set;+structblk_mq_tag_setadmin_tag_set;+structnvme_tcp_ofld_queue*queues;++structwork_structerr_work;+structdelayed_workconnect_work;++/*+*Eachentryinthearrayindicatesthenumberofqueuesof+*correspondingtype.+*/+u32queue_type_mapping[HCTX_MAX_TYPES];+u32io_queues[HCTX_MAX_TYPES];++/* Connectivity params */+structnvme_tcp_ofld_ctrl_con_paramsconn_params;++/* Vendor specific driver context */+void*private_data;+};++structnvme_tcp_ofld_ops{+constchar*name;+structmodule*module;++/* For vendor-specific driver to report what opts it supports */+intrequired_opts;/* bitmap using enum nvmf_parsing_opts */+intallowed_opts;/* bitmap using enum nvmf_parsing_opts */++/* For vendor-specific max num of segments and IO sizes */+u32max_hw_sectors;+u32max_segments;++/**+*claim_dev:ReturnTrueifaddrisreachableviaoffloaddevice.+*@dev:Theoffloaddevicetocheck.+*@conn_params:ptrtoroutingparamstobefilledbythelower+*driver.Input+Outputargument.+*/+int(*claim_dev)(structnvme_tcp_ofld_dev*dev,+structnvme_tcp_ofld_ctrl_con_params*conn_params);++/**+*setup_ctrl:Setupdevicespecificcontrollerstructures.+*@ctrl:Theoffloadctrl.+*@new:isnewsetup.+*/+int(*setup_ctrl)(structnvme_tcp_ofld_ctrl*ctrl,boolnew);++/**+*release_ctrl:Release/Freedevicespecificcontrollerstructures.+*@ctrl:Theoffloadctrl.+*/+int(*release_ctrl)(structnvme_tcp_ofld_ctrl*ctrl);++/**+*create_queue:CreateoffloadqueueandestablishTCP+NVMeTCP+*(icreq+icresp)connection.Returntrueonsuccessfulconnection.+*Basedonnvme_tcp_alloc_queue.+*@queue:Thequeueitself-usedasinputandoutput.+*@qid:ThequeueIDassociatedwiththerequestedqueue.+*@q_size:Thequeuedepth.+*/+int(*create_queue)(structnvme_tcp_ofld_queue*queue,intqid,+size_tq_size);++/**+*drain_queue:Drainagivenqueue-Returningfromthisfunction+*ensuresthatnoadditionalcompletionswillarriveonthisqueue.+*@queue:Thequeuetodrain.+*/+void(*drain_queue)(structnvme_tcp_ofld_queue*queue);++/**+*destroy_queue:ClosetheTCP+NVMeTCPconnectionofagivenqueue+*andmakesureitsnolongeractive(nocompletionswillarriveonthe+*queue).+*@queue:Thequeuetodestroy.+*/+void(*destroy_queue)(structnvme_tcp_ofld_queue*queue);++/**+*poll_queue:Pollagivenqueueforcompletions.+*@queue:Thequeuetopoll.+*/+int(*poll_queue)(structnvme_tcp_ofld_queue*queue);++/**+*init_req:Initializevendor-specificparamsforanewrequest.+*@req:Ptrtorequesttobeinitialized.Input+Outputargument.+*/+int(*init_req)(structnvme_tcp_ofld_req*req);++/**+*send_req:Dispatcharequest.Returnstheexecutionstatus.+*@req:Ptrtorequesttobesent.+*/+int(*send_req)(structnvme_tcp_ofld_req*req);++/**+*commit_rqs:Servesthepurposeofkickingthehardwareincaseof+*errors,otherwiseitwouldhavebeenkickedbythelastrequest.+*@queue:Thequeuetodrain.+*/+void(*commit_rqs)(structnvme_tcp_ofld_queue*queue);+};++/* Exported functions for lower vendor specific offload drivers */+intnvme_tcp_ofld_register_dev(structnvme_tcp_ofld_dev*dev);+voidnvme_tcp_ofld_unregister_dev(structnvme_tcp_ofld_dev*dev);
From: Dean Balandin <redacted>
As part of create_ctrl(), it scans the registered devices and calls
the claim_dev op on each of them, to find the first devices that matches
the connection params. Once the correct devices is found (claim_dev
returns true), we raise the refcnt of that device and return that device
as the device to be used for ctrl currently being created.
Acked-by: Igor Russkikh <redacted>
Signed-off-by: Dean Balandin <redacted>
Signed-off-by: Prabhakar Kushwaha <redacted>
Signed-off-by: Omkar Kulkarni <redacted>
Signed-off-by: Michal Kalderon <mkalderon@marvell.com>
Signed-off-by: Ariel Elior <redacted>
Signed-off-by: Shai Malin <redacted>
---
drivers/nvme/host/tcp-offload.c | 94 +++++++++++++++++++++++++++++++++
1 file changed, 94 insertions(+)
From: Arie Gershberg <redacted>
In this patch we implement controller level functionality including:
- create_ctrl.
- delete_ctrl.
- free_ctrl.
The implementation is similar to other nvme fabrics modules, the main
difference being that the nvme-tcp-offload ULP calls the vendor specific
claim_dev() op with the given TCP/IP parameters to determine which device
will be used for this controller.
Once found, the vendor specific device and controller will be paired and
kept in a controller list managed by the ULP.
Acked-by: Igor Russkikh <redacted>
Signed-off-by: Arie Gershberg <redacted>
Signed-off-by: Prabhakar Kushwaha <redacted>
Signed-off-by: Omkar Kulkarni <redacted>
Signed-off-by: Michal Kalderon <mkalderon@marvell.com>
Signed-off-by: Ariel Elior <redacted>
Signed-off-by: Shai Malin <redacted>
---
drivers/nvme/host/tcp-offload.c | 467 +++++++++++++++++++++++++++++++-
1 file changed, 459 insertions(+), 8 deletions(-)
From: Arie Gershberg <redacted>
In this patch, we implement controller level error handling and recovery.
Upon an error discovered by the ULP or reset controller initiated by the
nvme-core (using reset_ctrl workqueue), the ULP will initiate a controller
recovery which includes teardown and re-connect of all queues.
Acked-by: Igor Russkikh <redacted>
Signed-off-by: Arie Gershberg <redacted>
Signed-off-by: Prabhakar Kushwaha <redacted>
Signed-off-by: Omkar Kulkarni <redacted>
Signed-off-by: Michal Kalderon <mkalderon@marvell.com>
Signed-off-by: Ariel Elior <redacted>
Signed-off-by: Shai Malin <redacted>
---
drivers/nvme/host/tcp-offload.c | 138 +++++++++++++++++++++++++++++++-
drivers/nvme/host/tcp-offload.h | 1 +
2 files changed, 137 insertions(+), 2 deletions(-)
@@ -296,6 +314,28 @@ nvme_tcp_ofld_configure_io_queues(struct nvme_ctrl *nctrl, bool new)returnrc;}+staticvoidnvme_tcp_ofld_reconnect_or_remove(structnvme_ctrl*nctrl)+{+/* If we are resetting/deleting then do nothing */+if(nctrl->state!=NVME_CTRL_CONNECTING){+WARN_ON_ONCE(nctrl->state==NVME_CTRL_NEW||+nctrl->state==NVME_CTRL_LIVE);++return;+}++if(nvmf_should_reconnect(nctrl)){+dev_info(nctrl->device,"Reconnecting in %d seconds...\n",+nctrl->opts->reconnect_delay);+queue_delayed_work(nvme_wq,+&to_tcp_ofld_ctrl(nctrl)->connect_work,+nctrl->opts->reconnect_delay*HZ);+}else{+dev_info(nctrl->device,"Removing controller...\n");+nvme_delete_ctrl(nctrl);+}+}+staticintnvme_tcp_ofld_setup_ctrl(structnvme_ctrl*nctrl,boolnew){structnvmf_ctrl_options*opts=nctrl->opts;
@@ -407,10 +447,68 @@ nvme_tcp_ofld_teardown_io_queues(struct nvme_ctrl *nctrl, bool remove)/* Placeholder - teardown_io_queues */}+staticvoidnvme_tcp_ofld_reconnect_ctrl_work(structwork_struct*work)+{+structnvme_tcp_ofld_ctrl*ctrl=+container_of(to_delayed_work(work),+structnvme_tcp_ofld_ctrl,+connect_work);+structnvme_ctrl*nctrl=&ctrl->nctrl;++++nctrl->nr_reconnects;++if(ctrl->dev->ops->setup_ctrl(ctrl,false))+gotorequeue;++if(nvme_tcp_ofld_setup_ctrl(nctrl,false))+gotorelease_and_requeue;++dev_info(nctrl->device,"Successfully reconnected (%d attempt)\n",+nctrl->nr_reconnects);++nctrl->nr_reconnects=0;++return;++release_and_requeue:+ctrl->dev->ops->release_ctrl(ctrl);+requeue:+dev_info(nctrl->device,"Failed reconnect attempt %d\n",+nctrl->nr_reconnects);+nvme_tcp_ofld_reconnect_or_remove(nctrl);+}++staticvoidnvme_tcp_ofld_error_recovery_work(structwork_struct*work)+{+structnvme_tcp_ofld_ctrl*ctrl=+container_of(work,structnvme_tcp_ofld_ctrl,err_work);+structnvme_ctrl*nctrl=&ctrl->nctrl;++nvme_stop_keep_alive(nctrl);+nvme_tcp_ofld_teardown_io_queues(nctrl,false);+/* unquiesce to fail fast pending requests */+nvme_start_queues(nctrl);+nvme_tcp_ofld_teardown_admin_queue(nctrl,false);+blk_mq_unquiesce_queue(nctrl->admin_q);++if(!nvme_change_ctrl_state(nctrl,NVME_CTRL_CONNECTING)){+/* state change failure is ok if we started nctrl delete */+WARN_ON_ONCE(nctrl->state!=NVME_CTRL_DELETING&&+nctrl->state!=NVME_CTRL_DELETING_NOIO);++return;+}++nvme_tcp_ofld_reconnect_or_remove(nctrl);+}+staticvoidnvme_tcp_ofld_teardown_ctrl(structnvme_ctrl*nctrl,boolshutdown){-/* Placeholder - err_work and connect_work */+structnvme_tcp_ofld_ctrl*ctrl=to_tcp_ofld_ctrl(nctrl);++cancel_work_sync(&ctrl->err_work);+cancel_delayed_work_sync(&ctrl->connect_work);nvme_tcp_ofld_teardown_io_queues(nctrl,shutdown);blk_mq_quiesce_queue(nctrl->admin_q);if(shutdown)
@@ -425,6 +523,38 @@ static void nvme_tcp_ofld_delete_ctrl(struct nvme_ctrl *nctrl)nvme_tcp_ofld_teardown_ctrl(nctrl,true);}+staticvoidnvme_tcp_ofld_reset_ctrl_work(structwork_struct*work)+{+structnvme_ctrl*nctrl=+container_of(work,structnvme_ctrl,reset_work);+structnvme_tcp_ofld_ctrl*ctrl=to_tcp_ofld_ctrl(nctrl);++nvme_stop_ctrl(nctrl);+nvme_tcp_ofld_teardown_ctrl(nctrl,false);++if(!nvme_change_ctrl_state(nctrl,NVME_CTRL_CONNECTING)){+/* state change failure is ok if we started ctrl delete */+WARN_ON_ONCE(nctrl->state!=NVME_CTRL_DELETING&&+nctrl->state!=NVME_CTRL_DELETING_NOIO);++return;+}++if(ctrl->dev->ops->setup_ctrl(ctrl,false))+gotoout_fail;++if(nvme_tcp_ofld_setup_ctrl(nctrl,false))+gotorelease_ctrl;++return;++release_ctrl:+ctrl->dev->ops->release_ctrl(ctrl);+out_fail:+++nctrl->nr_reconnects;+nvme_tcp_ofld_reconnect_or_remove(nctrl);+}+staticintnvme_tcp_ofld_init_request(structblk_mq_tag_set*set,structrequest*rq,
From: Dean Balandin <redacted>
In this patch we implement queue level functionality.
The implementation is similar to the nvme-tcp module, the main
difference being that we call the vendor specific create_queue op which
creates the TCP connection, and NVMeTPC connection including
icreq+icresp negotiation.
Once create_queue returns successfully, we can move on to the fabrics
connect.
Acked-by: Igor Russkikh <redacted>
Signed-off-by: Dean Balandin <redacted>
Signed-off-by: Prabhakar Kushwaha <redacted>
Signed-off-by: Omkar Kulkarni <redacted>
Signed-off-by: Michal Kalderon <mkalderon@marvell.com>
Signed-off-by: Ariel Elior <redacted>
Signed-off-by: Shai Malin <redacted>
---
drivers/nvme/host/tcp-offload.c | 415 ++++++++++++++++++++++++++++++--
drivers/nvme/host/tcp-offload.h | 2 +-
2 files changed, 390 insertions(+), 27 deletions(-)
@@ -250,7 +340,127 @@ static int nvme_tcp_ofld_configure_admin_queue(struct nvme_ctrl *nctrl,if(new)blk_mq_free_tag_set(nctrl->admin_tagset);out_free_queue:-/* Placeholder - free admin queue */+nvme_tcp_ofld_free_queue(nctrl,0);++returnrc;+}++staticunsignedintnvme_tcp_ofld_nr_io_queues(structnvme_ctrl*nctrl)+{+unsignedintnr_io_queues;++nr_io_queues=min(nctrl->opts->nr_io_queues,num_online_cpus());+nr_io_queues+=min(nctrl->opts->nr_write_queues,num_online_cpus());+nr_io_queues+=min(nctrl->opts->nr_poll_queues,num_online_cpus());++returnnr_io_queues;+}++staticvoid+nvme_tcp_ofld_set_io_queues(structnvme_ctrl*nctrl,unsignedintnr_io_queues)+{+structnvme_tcp_ofld_ctrl*ctrl=to_tcp_ofld_ctrl(nctrl);+structnvmf_ctrl_options*opts=nctrl->opts;++if(opts->nr_write_queues&&opts->nr_io_queues<nr_io_queues){+/*+*separateread/writequeues+*handoutdedicateddefaultqueuesonlyafterwehave+*sufficientreadqueues.+*/+ctrl->io_queues[HCTX_TYPE_READ]=opts->nr_io_queues;+nr_io_queues-=ctrl->io_queues[HCTX_TYPE_READ];+ctrl->io_queues[HCTX_TYPE_DEFAULT]=+min(opts->nr_write_queues,nr_io_queues);+nr_io_queues-=ctrl->io_queues[HCTX_TYPE_DEFAULT];+}else{+/*+*sharedread/writequeues+*eithernowritequeueswererequested,orwedon'thave+*sufficientqueuecounttohavededicateddefaultqueues.+*/+ctrl->io_queues[HCTX_TYPE_DEFAULT]=+min(opts->nr_io_queues,nr_io_queues);+nr_io_queues-=ctrl->io_queues[HCTX_TYPE_DEFAULT];+}++if(opts->nr_poll_queues&&nr_io_queues){+/* map dedicated poll queues only if we have queues left */+ctrl->io_queues[HCTX_TYPE_POLL]=+min(opts->nr_poll_queues,nr_io_queues);+}+}++staticvoid+nvme_tcp_ofld_terminate_io_queues(structnvme_ctrl*nctrl,intstart_from)+{+inti;++/* admin-q will be ignored because of the loop condition */+for(i=start_from;i>=1;i--)+nvme_tcp_ofld_stop_queue(nctrl,i);+}++staticintnvme_tcp_ofld_create_io_queues(structnvme_ctrl*nctrl)+{+structnvme_tcp_ofld_ctrl*ctrl=to_tcp_ofld_ctrl(nctrl);+inti,rc;++for(i=1;i<nctrl->queue_count;i++){+rc=ctrl->dev->ops->create_queue(&ctrl->queues[i],+i,nctrl->sqsize+1);+if(rc)+gotoout_free_queues;++set_bit(NVME_TCP_OFLD_Q_ALLOCATED,&ctrl->queues[i].flags);+}++return0;++out_free_queues:+nvme_tcp_ofld_terminate_io_queues(nctrl,--i);++returnrc;+}++staticintnvme_tcp_ofld_alloc_io_queues(structnvme_ctrl*nctrl)+{+unsignedintnr_io_queues;+intrc;++nr_io_queues=nvme_tcp_ofld_nr_io_queues(nctrl);+rc=nvme_set_queue_count(nctrl,&nr_io_queues);+if(rc)+returnrc;++nctrl->queue_count=nr_io_queues+1;+if(nctrl->queue_count<2){+dev_err(nctrl->device,+"unable to set any I/O queues\n");++return-ENOMEM;+}++dev_info(nctrl->device,"creating %d I/O queues.\n",nr_io_queues);+nvme_tcp_ofld_set_io_queues(nctrl,nr_io_queues);++returnnvme_tcp_ofld_create_io_queues(nctrl);+}++staticintnvme_tcp_ofld_start_io_queues(structnvme_ctrl*nctrl)+{+inti,rc=0;++for(i=1;i<nctrl->queue_count;i++){+rc=nvme_tcp_ofld_start_queue(nctrl,i);+if(rc)+gototerminate_queues;+}++return0;++terminate_queues:+nvme_tcp_ofld_terminate_io_queues(nctrl,--i);returnrc;}
From: Dean Balandin <redacted>
In this patch, we present the IO level functionality.
The nvme-tcp-offload shall work on the IO-level, meaning the
nvme-tcp-offload ULP module shall pass the request to the nvme-tcp-offload
vendor driver and shall expect for the request compilation.
No additional handling is needed in between, this design will reduce the
CPU utilization as we will describe below.
The nvme-tcp-offload vendor driver shall register to nvme-tcp-offload ULP
with the following IO-path ops:
- init_req
- send_req - in order to pass the request to the handling of the offload
driver that shall pass it to the vendor specific device
- poll_queue
The vendor driver will manage the context from which the request will be
executed and the request aggregations.
Once the IO completed, the nvme-tcp-offload vendor driver shall call
command.done() that shall invoke the nvme-tcp-offload ULP layer for
completing the request.
This patch also contains initial definition of nvme_tcp_ofld_queue_rq().
Acked-by: Igor Russkikh <redacted>
Signed-off-by: Dean Balandin <redacted>
Signed-off-by: Prabhakar Kushwaha <redacted>
Signed-off-by: Omkar Kulkarni <redacted>
Signed-off-by: Michal Kalderon <mkalderon@marvell.com>
Signed-off-by: Ariel Elior <redacted>
Signed-off-by: Shai Malin <redacted>
---
drivers/nvme/host/tcp-offload.c | 95 ++++++++++++++++++++++++++++++---
1 file changed, 87 insertions(+), 8 deletions(-)
This patch will present the skeleton of the qedn driver.
The new driver will be added under "drivers/nvme/hw/qedn" and will be
enabled by the Kconfig "Marvell NVM Express over Fabrics TCP offload".
The internal implementation:
- qedn.h:
Includes all common structs to be used by the qedn vendor driver.
- qedn_main.c
Includes the qedn_init and qedn_cleanup implementation.
As part of the qedn init, the driver will register as a pci device and
will work with the Marvell fastlinQ NICs.
As part of the probe, the driver will register to the nvme_tcp_offload
(ULP).
Acked-by: Igor Russkikh <redacted>
Signed-off-by: Arie Gershberg <redacted>
Signed-off-by: Prabhakar Kushwaha <redacted>
Signed-off-by: Omkar Kulkarni <redacted>
Signed-off-by: Michal Kalderon <mkalderon@marvell.com>
Signed-off-by: Ariel Elior <redacted>
Signed-off-by: Shai Malin <redacted>
---
MAINTAINERS | 10 ++
drivers/nvme/Kconfig | 1 +
drivers/nvme/Makefile | 1 +
drivers/nvme/hw/Kconfig | 8 ++
drivers/nvme/hw/Makefile | 3 +
drivers/nvme/hw/qedn/Makefile | 5 +
drivers/nvme/hw/qedn/qedn.h | 19 +++
drivers/nvme/hw/qedn/qedn_main.c | 201 +++++++++++++++++++++++++++++++
8 files changed, 248 insertions(+)
create mode 100644 drivers/nvme/hw/Kconfig
create mode 100644 drivers/nvme/hw/Makefile
create mode 100644 drivers/nvme/hw/qedn/Makefile
create mode 100644 drivers/nvme/hw/qedn/qedn.h
create mode 100644 drivers/nvme/hw/qedn/qedn_main.c
This patch introduces the functionality of loading and unloading
physical function.
qedn_probe() loads the offload device PF(physical function), and
initialize the HW and the FW with the PF parameters using the
HW ops->qed_nvmetcp_ops, which are similar to other "qed_*_ops" which
are used by the qede, qedr, qedf and qedi device drivers.
qedn_remove() unloads the offload device PF, re-initialize the HW and
the FW with the PF parameters.
The struct qedn_ctx is per PF container for PF-specific attributes and
resources.
Acked-by: Igor Russkikh <redacted>
Signed-off-by: Dean Balandin <redacted>
Signed-off-by: Prabhakar Kushwaha <redacted>
Signed-off-by: Omkar Kulkarni <redacted>
Signed-off-by: Michal Kalderon <mkalderon@marvell.com>
Signed-off-by: Ariel Elior <redacted>
Signed-off-by: Shai Malin <redacted>
---
drivers/nvme/hw/Kconfig | 1 +
drivers/nvme/hw/qedn/qedn.h | 49 ++++++++
drivers/nvme/hw/qedn/qedn_main.c | 191 ++++++++++++++++++++++++++++++-
3 files changed, 236 insertions(+), 5 deletions(-)
@@ -14,6 +14,10 @@#define CHIP_NUM_AHP_NVMETCP 0x8194+conststructqed_nvmetcp_ops*qed_ops;++/* Global context instance */+structqedn_globalqedn_glb;staticstructpci_device_idqedn_pci_tbl[]={{PCI_VDEVICE(QLOGIC,CHIP_NUM_AHP_NVMETCP),0},{0,0},
@@ -99,12 +103,132 @@ static struct nvme_tcp_ofld_ops qedn_ofld_ops = {.commit_rqs=qedn_commit_rqs,};+staticinlinevoidqedn_init_pf_struct(structqedn_ctx*qedn)+{+/* Placeholder - Initialize qedn fields */+}++staticinlinevoid+qedn_init_core_probe_params(structqed_probe_params*probe_params)+{+memset(probe_params,0,sizeof(*probe_params));+probe_params->protocol=QED_PROTOCOL_NVMETCP;+probe_params->is_vf=false;+probe_params->recov_in_prog=0;+}++staticinlineintqedn_core_probe(structqedn_ctx*qedn)+{+structqed_probe_paramsprobe_params;+intrc=0;++qedn_init_core_probe_params(&probe_params);+pr_info("Starting QED probe\n");+qedn->cdev=qed_ops->common->probe(qedn->pdev,&probe_params);+if(!qedn->cdev){+rc=-ENODEV;+pr_err("QED probe failed\n");+}++returnrc;+}++staticvoidqedn_add_pf_to_gl_list(structqedn_ctx*qedn)+{+mutex_lock(&qedn_glb.glb_mutex);+list_add_tail(&qedn->gl_pf_entry,&qedn_glb.qedn_pf_list);+mutex_unlock(&qedn_glb.glb_mutex);+}++staticvoidqedn_remove_pf_from_gl_list(structqedn_ctx*qedn)+{+mutex_lock(&qedn_glb.glb_mutex);+list_del_init(&qedn->gl_pf_entry);+mutex_unlock(&qedn_glb.glb_mutex);+}++staticintqedn_set_nvmetcp_pf_param(structqedn_ctx*qedn)+{+u32fw_conn_queue_pages=QEDN_NVMETCP_NUM_FW_CONN_QUEUE_PAGES;+structqed_nvmetcp_pf_params*pf_params;++pf_params=&qedn->pf_params.nvmetcp_pf_params;+memset(pf_params,0,sizeof(*pf_params));+qedn->num_fw_cqs=min_t(u8,qedn->dev_info.num_cqs,num_online_cpus());++pf_params->num_cons=QEDN_MAX_CONNS_PER_PF;+pf_params->num_tasks=QEDN_MAX_TASKS_PER_PF;++/* Placeholder - Initialize function level queues */++/* Placeholder - Initialize TCP params */++/* Queues */+pf_params->num_sq_pages_in_ring=fw_conn_queue_pages;+pf_params->num_r2tq_pages_in_ring=fw_conn_queue_pages;+pf_params->num_uhq_pages_in_ring=fw_conn_queue_pages;+pf_params->num_queues=qedn->num_fw_cqs;+pf_params->cq_num_entries=QEDN_FW_CQ_SIZE;++/* the CQ SB pi */+pf_params->gl_rq_pi=QEDN_PROTO_CQ_PROD_IDX;++return0;+}++staticinlineintqedn_slowpath_start(structqedn_ctx*qedn)+{+structqed_slowpath_paramssp_params={};+intrc=0;++/* Start the Slowpath-process */+sp_params.int_mode=QED_INT_MODE_MSIX;+sp_params.drv_major=QEDN_MAJOR_VERSION;+sp_params.drv_minor=QEDN_MINOR_VERSION;+sp_params.drv_rev=QEDN_REVISION_VERSION;+sp_params.drv_eng=QEDN_ENGINEERING_VERSION;+strscpy(sp_params.name,"qedn NVMeTCP",QED_DRV_VER_STR_SIZE);+rc=qed_ops->common->slowpath_start(qedn->cdev,&sp_params);+if(rc)+pr_err("Cannot start slowpath\n");++returnrc;+}+staticvoid__qedn_remove(structpci_dev*pdev){structqedn_ctx*qedn=pci_get_drvdata(pdev);+intrc;++pr_notice("qedn remove started: abs PF id=%u\n",+qedn->dev_info.common.abs_pf_id);++if(test_and_set_bit(QEDN_STATE_MODULE_REMOVE_ONGOING,&qedn->state)){+pr_err("Remove already ongoing\n");++return;+}++if(test_and_clear_bit(QEDN_STATE_REGISTERED_OFFLOAD_DEV,&qedn->state))+nvme_tcp_ofld_unregister_dev(&qedn->qedn_ofld_dev);++if(test_and_clear_bit(QEDN_STATE_GL_PF_LIST_ADDED,&qedn->state))+qedn_remove_pf_from_gl_list(qedn);+else+pr_err("Failed to remove from global PF list\n");++if(test_and_clear_bit(QEDN_STATE_MFW_STATE,&qedn->state)){+rc=qed_ops->common->update_drv_state(qedn->cdev,false);+if(rc)+pr_err("Failed to send drv state to MFW\n");+}++if(test_and_clear_bit(QEDN_STATE_CORE_OPEN,&qedn->state))+qed_ops->common->slowpath_stop(qedn->cdev);++if(test_and_clear_bit(QEDN_STATE_CORE_PROBED,&qedn->state))+qed_ops->common->remove(qedn->cdev);-pr_notice("Starting qedn_remove\n");-nvme_tcp_ofld_unregister_dev(&qedn->qedn_ofld_dev);kfree(qedn);pr_notice("Ending qedn_remove successfully\n");}
@@ -144,15 +268,55 @@ static int __qedn_probe(struct pci_dev *pdev)if(!qedn)return-ENODEV;+qedn_init_pf_struct(qedn);++/* QED probe */+rc=qedn_core_probe(qedn);+if(rc)+gotoexit_probe_and_release_mem;++set_bit(QEDN_STATE_CORE_PROBED,&qedn->state);++rc=qed_ops->fill_dev_info(qedn->cdev,&qedn->dev_info);+if(rc){+pr_err("fill_dev_info failed\n");+gotoexit_probe_and_release_mem;+}++qedn_add_pf_to_gl_list(qedn);+set_bit(QEDN_STATE_GL_PF_LIST_ADDED,&qedn->state);++rc=qedn_set_nvmetcp_pf_param(qedn);+if(rc)+gotoexit_probe_and_release_mem;++qed_ops->common->update_pf_params(qedn->cdev,&qedn->pf_params);+rc=qedn_slowpath_start(qedn);+if(rc)+gotoexit_probe_and_release_mem;++set_bit(QEDN_STATE_CORE_OPEN,&qedn->state);++rc=qed_ops->common->update_drv_state(qedn->cdev,true);+if(rc){+pr_err("Failed to send drv state to MFW\n");+gotoexit_probe_and_release_mem;+}++set_bit(QEDN_STATE_MFW_STATE,&qedn->state);+qedn->qedn_ofld_dev.ops=&qedn_ofld_ops;INIT_LIST_HEAD(&qedn->qedn_ofld_dev.entry);rc=nvme_tcp_ofld_register_dev(&qedn->qedn_ofld_dev);if(rc)-gotorelease_qedn;+gotoexit_probe_and_release_mem;++set_bit(QEDN_STATE_REGISTERED_OFFLOAD_DEV,&qedn->state);return0;-release_qedn:-kfree(qedn);+exit_probe_and_release_mem:+__qedn_remove(pdev);+pr_err("probe ended with error\n");returnrc;}
@@ -170,10 +334,26 @@ static struct pci_driver qedn_pci_driver = {.shutdown=qedn_shutdown,};+staticinlinevoidqedn_init_global_contxt(void)+{+INIT_LIST_HEAD(&qedn_glb.qedn_pf_list);+INIT_LIST_HEAD(&qedn_glb.ctrl_list);+mutex_init(&qedn_glb.glb_mutex);+}+staticint__initqedn_init(void){intrc;+qedn_init_global_contxt();++qed_ops=qed_get_nvmetcp_ops();+if(!qed_ops){+pr_err("Failed to get QED NVMeTCP ops\n");++return-EINVAL;+}+rc=pci_register_driver(&qedn_pci_driver);if(rc){pr_err("Failed to register pci driver\n");
@@ -189,6 +369,7 @@ static int __init qedn_init(void)staticvoid__exitqedn_cleanup(void){pci_unregister_driver(&qedn_pci_driver);+qed_put_nvmetcp_ops();pr_notice("Unloading qedn ended\n");}
From: Nikolay Assa <redacted>
This patch introduces the qedn_claim_dev() network service which the
offload device (qedn) is using through the paired net-device (qede).
qedn_claim_dev() returns true if the IP addr(IPv4 or IPv6) of the target
server is reachable via the net-device which is paired with the
offloaded device.
Acked-by: Igor Russkikh <redacted>
Signed-off-by: Nikolay Assa <redacted>
Signed-off-by: Prabhakar Kushwaha <redacted>
Signed-off-by: Omkar Kulkarni <redacted>
Signed-off-by: Michal Kalderon <mkalderon@marvell.com>
Signed-off-by: Ariel Elior <redacted>
Signed-off-by: Shai Malin <redacted>
---
drivers/nvme/hw/qedn/qedn.h | 4 +++
drivers/nvme/hw/qedn/qedn_main.c | 42 ++++++++++++++++++++++++++++++--
2 files changed, 44 insertions(+), 2 deletions(-)
@@ -27,9 +27,47 @@ static intqedn_claim_dev(structnvme_tcp_ofld_dev*dev,structnvme_tcp_ofld_ctrl_con_params*conn_params){-/* Placeholder - qedn_claim_dev */+structpci_dev*qede_pdev=NULL;+structnet_device*ndev=NULL;+u16vlan_id=0;+intrc=0;-return0;+/* qedn utilizes host network stack through paired qede device for+*non-offloadtraffic.Firstweverifythereisvalidroutetoremote+*peer.+*/+if(conn_params->remote_ip_addr.ss_family==AF_INET){+rc=qed_route_ipv4(&conn_params->local_ip_addr,+&conn_params->remote_ip_addr,+&conn_params->remote_mac_addr,+&ndev);+}elseif(conn_params->remote_ip_addr.ss_family==AF_INET6){+rc=qed_route_ipv6(&conn_params->local_ip_addr,+&conn_params->remote_ip_addr,+&conn_params->remote_mac_addr,+&ndev);+}else{+pr_err("address family %d not supported\n",+conn_params->remote_ip_addr.ss_family);++returnfalse;+}++if(rc)+returnfalse;++qed_vlan_get_ndev(&ndev,&vlan_id);+conn_params->vlan_id=vlan_id;++/* route found through ndev - validate this is qede*/+qede_pdev=qed_validate_ndev(ndev);+if(!qede_pdev)+returnfalse;++dev->qede_pdev=qede_pdev;+dev->ndev=ndev;++returntrue;}staticintqedn_create_queue(structnvme_tcp_ofld_queue*queue,intqid,
This patch will present the adding of qedn_fp_queue - this is a per cpu
core element which handles all of the connections on that cpu core.
The qedn_fp_queue will handle a group of connections (NVMeoF QPs) which
are handled on the same cpu core, and will only use the same FW-driver
resources with no need to be related to the same NVMeoF controller.
The per qedn_fq_queue resources are the FW CQ and FW status block:
- The FW CQ will be used for the FW to notify the driver that the
the exchange has ended and the FW will pass the incoming NVMeoF CQE
(if exist) to the driver.
- FW status block - which is used for the FW to notify the driver with
the producer update of the FW CQE chain.
The FW fast-path queues are based on qed_chain.h
Acked-by: Igor Russkikh <redacted>
Signed-off-by: Prabhakar Kushwaha <redacted>
Signed-off-by: Omkar Kulkarni <redacted>
Signed-off-by: Michal Kalderon <mkalderon@marvell.com>
Signed-off-by: Ariel Elior <redacted>
Signed-off-by: Shai Malin <redacted>
---
drivers/nvme/hw/qedn/qedn.h | 26 +++
drivers/nvme/hw/qedn/qedn_main.c | 287 ++++++++++++++++++++++++++++++-
2 files changed, 310 insertions(+), 3 deletions(-)
@@ -207,11 +457,14 @@ static int qedn_set_nvmetcp_pf_param(struct qedn_ctx *qedn)pf_params->num_uhq_pages_in_ring=fw_conn_queue_pages;pf_params->num_queues=qedn->num_fw_cqs;pf_params->cq_num_entries=QEDN_FW_CQ_SIZE;+pf_params->glbl_q_params_addr=qedn->fw_cq_array_phy;/* the CQ SB pi */pf_params->gl_rq_pi=QEDN_PROTO_CQ_PROD_IDX;-return0;+err_alloc_mem:++returnrc;}staticinlineintqedn_slowpath_start(structqedn_ctx*qedn)
@@ -255,6 +508,12 @@ static void __qedn_remove(struct pci_dev *pdev)elsepr_err("Failed to remove from global PF list\n");+if(test_and_clear_bit(QEDN_STATE_IRQ_SET,&qedn->state))+qedn_sync_free_irqs(qedn);++if(test_and_clear_bit(QEDN_STATE_NVMETCP_OPEN,&qedn->state))+qed_ops->stop(qedn->cdev);+if(test_and_clear_bit(QEDN_STATE_MFW_STATE,&qedn->state)){rc=qed_ops->common->update_drv_state(qedn->cdev,false);if(rc)
From: Prabhakar Kushwaha <redacted>
This patch will present the connection (queue) level slowpath
implementation relevant for create_queue flow.
The internal implementation:
- Add per controller slowpath workqeueue via pre_setup_ctrl
- qedn_main.c:
Includes qedn's implementation of the create_queue op.
- qedn_conn.c will include main slowpath connection level functions,
including:
1. Per-queue resources allocation.
2. Creating a new connection.
3. Offloading the connection to the FW for TCP handshake.
4. Destroy of a connection.
5. Support of delete and free controller.
6. TCP port management via qed_fetch_tcp_port, qed_return_tcp_port
Acked-by: Igor Russkikh <redacted>
Signed-off-by: Prabhakar Kushwaha <redacted>
Signed-off-by: Omkar Kulkarni <redacted>
Signed-off-by: Michal Kalderon <mkalderon@marvell.com>
Signed-off-by: Ariel Elior <redacted>
Signed-off-by: Shai Malin <redacted>
---
drivers/nvme/hw/qedn/Makefile | 5 +-
drivers/nvme/hw/qedn/qedn.h | 173 ++++++++++-
drivers/nvme/hw/qedn/qedn_conn.c | 508 +++++++++++++++++++++++++++++++
drivers/nvme/hw/qedn/qedn_main.c | 208 ++++++++++++-
4 files changed, 883 insertions(+), 11 deletions(-)
create mode 100644 drivers/nvme/hw/qedn/qedn_conn.c
@@ -78,6 +112,12 @@ struct qedn_ctx {/* Accessed with atomic bit ops, used with enum qedn_state */unsignedlongstate;+u8local_mac_addr[ETH_ALEN];+u16mtu;++/* Connections */+DECLARE_HASHTABLE(conn_ctx_hash,16);+/* Fast path queues */u8num_fw_cqs;structqedn_fp_queue*fp_q_arr;
@@ -85,6 +125,126 @@ struct qedn_ctx {dma_addr_tfw_cq_array_phy;/* Physical address of fw_cq_array_virt */};+structqedn_endpoint{+/* FW Params */+structqed_chainfw_sq_chain;+void__iomem*p_doorbell;++/* TCP Params */+__be32dst_addr[4];/* In network order */+__be32src_addr[4];/* In network order */+u16src_port;+u16dst_port;+u16vlan_id;+u8src_mac[ETH_ALEN];+u8dst_mac[ETH_ALEN];+u8ip_type;+};++enumsp_work_agg_action{+CREATE_CONNECTION=0,+SEND_ICREQ,+HANDLE_ICRESP,+DESTROY_CONNECTION,+};++enumqedn_ctrl_agg_state{+QEDN_CTRL_SET_TO_OFLD_CTRL=0,/* CTRL set to OFLD_CTRL */+QEDN_STATE_SP_WORK_THREAD_SET,/* slow patch WQ was created*/+LLH_FILTER,/* LLH filter added */+QEDN_RECOVERY,+ADMINQ_CONNECTED,/* At least one connection has attempted offload */+ERR_FLOW,+};++enumqedn_ctrl_sp_wq_state{+QEDN_CTRL_STATE_UNINITIALIZED=0,+QEDN_CTRL_STATE_FREE_CTRL,+QEDN_CTRL_STATE_CTRL_ERR,+};++/* Any change to this enum requires an update of qedn_conn_state_str */+enumqedn_conn_state{+CONN_STATE_CONN_IDLE=0,+CONN_STATE_CREATE_CONNECTION,+CONN_STATE_WAIT_FOR_CONNECT_DONE,+CONN_STATE_OFFLOAD_COMPLETE,+CONN_STATE_WAIT_FOR_UPDATE_EQE,+CONN_STATE_WAIT_FOR_IC_COMP,+CONN_STATE_NVMETCP_CONN_ESTABLISHED,+CONN_STATE_DESTROY_CONNECTION,+CONN_STATE_WAIT_FOR_DESTROY_DONE,+CONN_STATE_DESTROY_COMPLETE+};++structqedn_ctrl{+structlist_headglb_entry;+structlist_headpf_entry;++structqedn_ctx*qedn;+structnvme_tcp_ofld_queue*queue;+structnvme_tcp_ofld_ctrl*ctrl;++structworkqueue_struct*sp_wq;+enumqedn_ctrl_sp_wq_statesp_wq_state;++structwork_structsp_wq_entry;++structqedn_llh_filter*llh_filter;++unsignedlongagg_state;++atomic_thost_num_active_conns;+};++/* Connection level struct */+structqedn_conn_ctx{+structqedn_ctx*qedn;+structnvme_tcp_ofld_queue*queue;+structnvme_tcp_ofld_ctrl*ctrl;+u32conn_handle;+u32fw_cid;++atomic_test_conn_indicator;+atomic_tdestroy_conn_indicator;+wait_queue_head_tconn_waitq;++structwork_structsp_wq_entry;++/* Connection aggregative state.+*Canhavedifferentstatesindependently.+*/+unsignedlongagg_work_action;++structhlist_nodehash_node;+structnvmetcp_host_cccid_itid_entry*host_cccid_itid;+dma_addr_thost_cccid_itid_phy_addr;+structqedn_endpointep;+intabrt_flag;++/* Connection resources - turned on to indicate what resource was+*allocated,tothatitcanlaterbereleased.+*/+unsignedlongresrc_state;++/* Connection state */+spinlock_tconn_state_lock;+enumqedn_conn_statestate;++size_tsq_depth;++/* "dummy" socket */+structsocket*sock;+};++enumqedn_conn_resources_state{+QEDN_CONN_RESRC_FW_SQ,+QEDN_CONN_RESRC_ACQUIRE_CONN,+QEDN_CONN_RESRC_CCCID_ITID_MAP,+QEDN_CONN_RESRC_TCP_PORT,+QEDN_CONN_RESRC_MAX=64+};+structqedn_global{structlist_headqedn_pf_list;
@@ -23,6 +23,38 @@ static struct pci_device_id qedn_pci_tbl[] = {{0,0},};+staticboolqedn_matches_qede(structqedn_ctx*qedn,structpci_dev*qede_pdev)+{+structpci_dev*qedn_pdev=qedn->pdev;++return(qede_pdev->bus->number==qedn_pdev->bus->number&&+PCI_SLOT(qede_pdev->devfn)==PCI_SLOT(qedn_pdev->devfn)&&+PCI_FUNC(qede_pdev->devfn)==qedn->dev_info.port_id);+}++staticstructqedn_ctx*qedn_get_pf_from_pdev(structpci_dev*qede_pdev)+{+structlist_head*pf_list=NULL;+structqedn_ctx*qedn=NULL;+intrc;++pf_list=&qedn_glb.qedn_pf_list;+if(!pf_list){+pr_err("Failed fetching pf list for nvmet_add_port\n");+rc=-EFAULT;+gotopf_list_err;+}++list_for_each_entry(qedn,pf_list,gl_pf_entry){+if(qedn_matches_qede(qedn,qede_pdev))+returnqedn;+}++pf_list_err:++returnNULL;+}+staticintqedn_claim_dev(structnvme_tcp_ofld_dev*dev,structnvme_tcp_ofld_ctrl_con_params*conn_params)
@@ -70,22 +102,167 @@ qedn_claim_dev(struct nvme_tcp_ofld_dev *dev,returntrue;}-staticintqedn_create_queue(structnvme_tcp_ofld_queue*queue,intqid,-size_tq_size)+staticintqedn_setup_ctrl(structnvme_tcp_ofld_ctrl*ctrl,boolnew){-/* Placeholder - qedn_create_queue */+structnvme_tcp_ofld_dev*dev=ctrl->dev;+structqedn_ctrl*qctrl=NULL;+structqedn_ctx*qedn=NULL;+intrc=0;++if(new){+qctrl=kzalloc(sizeof(*qctrl),GFP_KERNEL);+if(!qctrl)+return-ENOMEM;++ctrl->private_data=(void*)qctrl;+set_bit(QEDN_CTRL_SET_TO_OFLD_CTRL,&qctrl->agg_state);++qctrl->sp_wq=alloc_workqueue(QEDN_SP_WORKQUEUE,WQ_MEM_RECLAIM,+QEDN_SP_WORKQUEUE_MAX_ACTIVE);+if(!qctrl->sp_wq){+rc=-ENODEV;+pr_err("Unable to create slowpath work queue!\n");+kfree(qctrl);++returnrc;+}++set_bit(QEDN_STATE_SP_WORK_THREAD_SET,&qctrl->agg_state);+}++qedn=qedn_get_pf_from_pdev(dev->qede_pdev);+if(!qedn){+pr_err("Failed locating QEDN for ip=%pIS\n",+&ctrl->conn_params.local_ip_addr);+rc=-EFAULT;+gotoerr_out;+}++qctrl->qedn=qedn;++/* Placeholder - setup LLH filter */++return0;++err_out:+flush_workqueue(qctrl->sp_wq);+kfree(qctrl);++returnrc;+}++staticintqedn_release_ctrl(structnvme_tcp_ofld_ctrl*ctrl)+{+structqedn_ctrl*qctrl=(structqedn_ctrl*)ctrl->private_data;++if(test_and_clear_bit(QEDN_STATE_SP_WORK_THREAD_SET,&qctrl->agg_state))+flush_workqueue(qctrl->sp_wq);++if(test_and_clear_bit(QEDN_CTRL_SET_TO_OFLD_CTRL,&qctrl->agg_state)){+kfree(qctrl);+ctrl->private_data=NULL;+}++qctrl->agg_state=0;+kfree(ctrl);++return0;+}++staticintqedn_create_queue(structnvme_tcp_ofld_queue*queue,intqid,size_tq_size)+{+structnvme_tcp_ofld_ctrl*ctrl=queue->ctrl;+structqedn_conn_ctx*conn_ctx;+structqedn_ctrl*qctrl;+structqedn_ctx*qedn;+intrc;++qctrl=(structqedn_ctrl*)ctrl->private_data;+qedn=qctrl->qedn;++/* Allocate qedn connection context */+conn_ctx=kzalloc(sizeof(*conn_ctx),GFP_KERNEL);+if(!conn_ctx)+return-ENOMEM;++queue->private_data=conn_ctx;+conn_ctx->qedn=qedn;+conn_ctx->queue=queue;+conn_ctx->ctrl=ctrl;+conn_ctx->sq_depth=q_size;++init_waitqueue_head(&conn_ctx->conn_waitq);+atomic_set(&conn_ctx->est_conn_indicator,0);+atomic_set(&conn_ctx->destroy_conn_indicator,0);++spin_lock_init(&conn_ctx->conn_state_lock);++qedn_initialize_endpoint(&conn_ctx->ep,qedn->local_mac_addr,+&ctrl->conn_params);++atomic_inc(&qctrl->host_num_active_conns);++qedn_set_sp_wa(conn_ctx,CREATE_CONNECTION);+qedn_set_con_state(conn_ctx,CONN_STATE_CREATE_CONNECTION);+INIT_WORK(&conn_ctx->sp_wq_entry,qedn_sp_wq_handler);+queue_work(qctrl->sp_wq,&conn_ctx->sp_wq_entry);++/* Wait for the connection establishment to complete - this includes the+*FWTCPconnectionestablishmentandtheNVMeTCPICReq&ICResp+*/+rc=qedn_wait_for_conn_est(conn_ctx);+if(rc)+return-ENXIO;return0;}staticvoidqedn_drain_queue(structnvme_tcp_ofld_queue*queue){-/* Placeholder - qedn_drain_queue */+/* No queue drain is required */+}++#define ATOMIC_READ_DESTROY_IND atomic_read(&conn_ctx->destroy_conn_indicator)+#define TERMINATE_TIMEOUT msecs_to_jiffies(QEDN_RLS_CONS_TMO)+staticinlinevoid+qedn_queue_wait_for_terminate_complete(structqedn_conn_ctx*conn_ctx)+{+/* Returns valid non-0 */+intwrc,state;++wrc=wait_event_interruptible_timeout(conn_ctx->conn_waitq,+ATOMIC_READ_DESTROY_IND>0,+TERMINATE_TIMEOUT);++atomic_set(&conn_ctx->destroy_conn_indicator,0);++spin_lock_bh(&conn_ctx->conn_state_lock);+state=conn_ctx->state;+spin_unlock_bh(&conn_ctx->conn_state_lock);++if(!wrc||state!=CONN_STATE_DESTROY_COMPLETE)+pr_warn("Timed out waiting for clear-SQ on FW conns");}staticvoidqedn_destroy_queue(structnvme_tcp_ofld_queue*queue){-/* Placeholder - qedn_destroy_queue */+structqedn_conn_ctx*conn_ctx;++if(!queue){+pr_err("ctrl has no queues\n");++return;+}++conn_ctx=(structqedn_conn_ctx*)queue->private_data;+if(!conn_ctx)+return;++qedn_terminate_connection(conn_ctx,QEDN_ABORTIVE_TERMINATION);++qedn_queue_wait_for_terminate_complete(conn_ctx);++kfree(conn_ctx);}staticintqedn_poll_queue(structnvme_tcp_ofld_queue*queue)
From: Prabhakar Kushwaha <redacted>
HW filter can be configured to filter TCP packets based on either
source or target TCP port. QEDN leverage this feature to route
NVMeTCP traffic.
This patch configures HW filter block based on source port for all
receiving packets to deliver correct QEDN PF.
Acked-by: Igor Russkikh <redacted>
Signed-off-by: Prabhakar Kushwaha <redacted>
Signed-off-by: Omkar Kulkarni <redacted>
Signed-off-by: Michal Kalderon <mkalderon@marvell.com>
Signed-off-by: Ariel Elior <redacted>
Signed-off-by: Shai Malin <redacted>
---
drivers/nvme/hw/qedn/qedn.h | 15 +++++
drivers/nvme/hw/qedn/qedn_main.c | 108 ++++++++++++++++++++++++++++++-
2 files changed, 122 insertions(+), 1 deletion(-)
@@ -112,6 +118,8 @@ struct qedn_ctx {/* Accessed with atomic bit ops, used with enum qedn_state */unsignedlongstate;+u8num_llh_filters;+structlist_headllh_filter_list;u8local_mac_addr[ETH_ALEN];u16mtu;
This patch will present the IO level workqueues:
- qedn_nvme_req_fp_wq(): process new requests, similar to
nvme_tcp_io_work(). The flow starts from
send_req() and will aggregate all the requests
on this CPU core.
- qedn_fw_cq_fp_wq(): process new FW completions, the flow starts from
the IRQ handler and for a single interrupt it will
process all the pending NVMeoF Completions under
polling mode.
Acked-by: Igor Russkikh <redacted>
Signed-off-by: Prabhakar Kushwaha <redacted>
Signed-off-by: Omkar Kulkarni <redacted>
Signed-off-by: Michal Kalderon <mkalderon@marvell.com>
Signed-off-by: Ariel Elior <redacted>
Signed-off-by: Shai Malin <redacted>
---
drivers/nvme/hw/qedn/Makefile | 2 +-
drivers/nvme/hw/qedn/qedn.h | 29 +++++++
drivers/nvme/hw/qedn/qedn_conn.c | 3 +
drivers/nvme/hw/qedn/qedn_main.c | 114 +++++++++++++++++++++++--
drivers/nvme/hw/qedn/qedn_task.c | 138 +++++++++++++++++++++++++++++++
5 files changed, 278 insertions(+), 8 deletions(-)
create mode 100644 drivers/nvme/hw/qedn/qedn_task.c
@@ -385,12 +403,24 @@ static int qedn_init_req(struct nvme_tcp_ofld_req *req)staticvoidqedn_commit_rqs(structnvme_tcp_ofld_queue*queue){-/* Placeholder - queue work */+structqedn_conn_ctx*conn_ctx;++conn_ctx=(structqedn_conn_ctx*)queue->private_data;++if(!list_empty(&conn_ctx->host_pend_req_list))+queue_work_on(conn_ctx->cpu,conn_ctx->nvme_req_fp_wq,+&conn_ctx->nvme_req_fp_wq_entry);}staticintqedn_send_req(structnvme_tcp_ofld_req*req){-/* Placeholder - qedn_send_req */+structqedn_conn_ctx*qedn_conn=(structqedn_conn_ctx*)req->queue->private_data;++/* Under the assumption that the cccid/tag will be in the range of 0 to sq_depth-1. */+if(!req->async&&qedn_validate_cccid_in_range(qedn_conn,req->rq->tag))+returnBLK_STS_NOTSUPP;++qedn_queue_request(qedn_conn,req);return0;}
@@ -434,9 +464,59 @@ struct qedn_conn_ctx *qedn_get_conn_hash(struct qedn_ctx *qedn, u16 icid)}/* Fastpath IRQ handler */+voidqedn_fw_cq_fp_handler(structqedn_fp_queue*fp_q)+{+u16sb_id,cq_prod_idx,cq_cons_idx;+structqedn_ctx*qedn=fp_q->qedn;+structnvmetcp_fw_cqe*cqe=NULL;++sb_id=fp_q->sb_id;+qed_sb_update_sb_idx(fp_q->sb_info);++/* rmb - to prevent missing new cqes */+rmb();++/* Read the latest cq_prod from the SB */+cq_prod_idx=*fp_q->cq_prod;+cq_cons_idx=qed_chain_get_cons_idx(&fp_q->cq_chain);++while(cq_cons_idx!=cq_prod_idx){+cqe=qed_chain_consume(&fp_q->cq_chain);+if(likely(cqe))+qedn_io_work_cq(qedn,cqe);+else+pr_err("Failed consuming cqe\n");++cq_cons_idx=qed_chain_get_cons_idx(&fp_q->cq_chain);++/* Check if new completions were posted */+if(unlikely(cq_prod_idx==cq_cons_idx)){+/* rmb - to prevent missing new cqes */+rmb();++/* Update the latest cq_prod from the SB */+cq_prod_idx=*fp_q->cq_prod;+}+}+}++staticvoidqedn_fw_cq_fq_wq_handler(structwork_struct*work)+{+structqedn_fp_queue*fp_q=container_of(work,structqedn_fp_queue,fw_cq_fp_wq_entry);++qedn_fw_cq_fp_handler(fp_q);+qed_sb_ack(fp_q->sb_info,IGU_INT_ENABLE,1);+}+staticirqreturn_tqedn_irq_handler(intirq,void*dev_id){-/* Placeholder */+structqedn_fp_queue*fp_q=dev_id;+structqedn_ctx*qedn=fp_q->qedn;++fp_q->cpu=smp_processor_id();++qed_sb_ack(fp_q->sb_info,IGU_INT_DISABLE,0);+queue_work_on(fp_q->cpu,qedn->fw_cq_fp_wq,&fp_q->fw_cq_fp_wq_entry);returnIRQ_HANDLED;}
@@ -584,6 +664,11 @@ static void qedn_free_function_queues(struct qedn_ctx *qedn)inti;/* Free workqueues */+destroy_workqueue(qedn->fw_cq_fp_wq);+qedn->fw_cq_fp_wq=NULL;++destroy_workqueue(qedn->nvme_req_fp_wq);+qedn->nvme_req_fp_wq=NULL;/* Free the fast path queues*/for(i=0;i<qedn->num_fw_cqs;i++){
@@ -651,7 +736,23 @@ static int qedn_alloc_function_queues(struct qedn_ctx *qedn)u64cq_phy_addr;inti;-/* Place holder - IO-path workqueues */+qedn->fw_cq_fp_wq=alloc_workqueue(QEDN_FW_CQ_FP_WQ_WORKQUEUE,+WQ_HIGHPRI|WQ_MEM_RECLAIM,0);+if(!qedn->fw_cq_fp_wq){+rc=-ENODEV;+pr_err("Unable to create fastpath FW CQ workqueue!\n");++returnrc;+}++qedn->nvme_req_fp_wq=alloc_workqueue(QEDN_NVME_REQ_FP_WQ_WORKQUEUE,+WQ_HIGHPRI|WQ_MEM_RECLAIM,1);+if(!qedn->nvme_req_fp_wq){+rc=-ENODEV;+pr_err("Unable to create fastpath qedn nvme workqueue!\n");++returnrc;+}qedn->fp_q_arr=kcalloc(qedn->num_fw_cqs,sizeof(structqedn_fp_queue),GFP_KERNEL);
From: Prabhakar Kushwaha <redacted>
Once a TCP connection established, the host sends an Initialize
Connection Request (ICReq) PDU to the controller.
Further Initialize Connection Response (ICResp) PDU received from
controller is processed by host to establish a connection and
exchange connection configuration parameters.
This patch present support of generation of ICReq and processing of
ICResp. It also update host configuration based on exchanged parameters.
Acked-by: Igor Russkikh <redacted>
Signed-off-by: Prabhakar Kushwaha <redacted>
Signed-off-by: Omkar Kulkarni <redacted>
Signed-off-by: Michal Kalderon <mkalderon@marvell.com>
Signed-off-by: Ariel Elior <redacted>
Signed-off-by: Shai Malin <redacted>
---
drivers/nvme/hw/qedn/qedn.h | 36 ++++
drivers/nvme/hw/qedn/qedn_conn.c | 317 ++++++++++++++++++++++++++++++-
drivers/nvme/hw/qedn/qedn_main.c | 22 +++
drivers/nvme/hw/qedn/qedn_task.c | 8 +-
4 files changed, 379 insertions(+), 4 deletions(-)
@@ -195,6 +213,9 @@ struct qedn_endpoint {structqed_chainfw_sq_chain;void__iomem*p_doorbell;+/* Spinlock for accessing FW queue */+spinlock_tdoorbell_lock;+/* TCP Params */__be32dst_addr[4];/* In network order */__be32src_addr[4];/* In network order */
@@ -34,6 +34,25 @@ inline int qedn_qid(struct nvme_tcp_ofld_queue *queue)returnqueue-queue->ctrl->queues;}+voidqedn_ring_doorbell(structqedn_conn_ctx*conn_ctx)+{+structnvmetcp_db_datadbell={0};+u16prod_idx;++dbell.agg_flags=0;+dbell.params|=DB_DEST_XCM<<NVMETCP_DB_DATA_DEST_SHIFT;+dbell.params|=DB_AGG_CMD_SET<<NVMETCP_DB_DATA_AGG_CMD_SHIFT;+dbell.params|=+DQ_XCM_ISCSI_SQ_PROD_CMD<<NVMETCP_DB_DATA_AGG_VAL_SEL_SHIFT;+dbell.params|=1<<NVMETCP_DB_DATA_BYPASS_EN_SHIFT;+prod_idx=qed_chain_get_prod_idx(&conn_ctx->ep.fw_sq_chain);+dbell.sq_prod=cpu_to_le16(prod_idx);++/* wmb - Make sure fw idx is coherent */+wmb();+writel(*(u32*)&dbell,conn_ctx->ep.p_doorbell);+}+intqedn_set_con_state(structqedn_conn_ctx*conn_ctx,enumqedn_conn_statenew_state){spin_lock_bh(&conn_ctx->conn_state_lock);
@@ -130,6 +149,71 @@ int qedn_initialize_endpoint(struct qedn_endpoint *ep, u8 *local_mac_addr,return-1;}+staticintqedn_alloc_icreq_pad(structqedn_conn_ctx*conn_ctx)+{+structqedn_ctx*qedn=conn_ctx->qedn;+structqedn_icreq_padding*icreq_pad;+u32*buffer;+intrc=0;++icreq_pad=kzalloc(sizeof(*icreq_pad),GFP_KERNEL);+if(!icreq_pad)+return-ENOMEM;++conn_ctx->icreq_pad=icreq_pad;+memset(&icreq_pad->sge,0,sizeof(icreq_pad->sge));+buffer=dma_alloc_coherent(&qedn->pdev->dev,+QEDN_ICREQ_FW_PAYLOAD,+&icreq_pad->pa,+GFP_KERNEL);+if(!buffer){+pr_err("Could not allocate icreq_padding SGE buffer.\n");+rc=-ENOMEM;+gotorelease_icreq_pad;+}++DMA_REGPAIR_LE(icreq_pad->sge.sge_addr,icreq_pad->pa);+icreq_pad->sge.sge_len=cpu_to_le32(QEDN_ICREQ_FW_PAYLOAD);+icreq_pad->buffer=buffer;+set_bit(QEDN_CONN_RESRC_ICREQ_PAD,&conn_ctx->resrc_state);++return0;++release_icreq_pad:+kfree(icreq_pad);+conn_ctx->icreq_pad=NULL;++returnrc;+}++staticvoidqedn_free_icreq_pad(structqedn_conn_ctx*conn_ctx)+{+structqedn_ctx*qedn=conn_ctx->qedn;+structqedn_icreq_padding*icreq_pad;+u32*buffer;++icreq_pad=conn_ctx->icreq_pad;+if(unlikely(!icreq_pad)){+pr_err("null ptr in icreq_pad in conn_ctx\n");+gotofinally;+}++buffer=icreq_pad->buffer;+if(buffer){+dma_free_coherent(&qedn->pdev->dev,+QEDN_ICREQ_FW_PAYLOAD,+(void*)buffer,+icreq_pad->pa);+icreq_pad->buffer=NULL;+}++kfree(icreq_pad);+conn_ctx->icreq_pad=NULL;++finally:+clear_bit(QEDN_CONN_RESRC_ICREQ_PAD,&conn_ctx->resrc_state);+}+staticvoidqedn_release_conn_ctx(structqedn_conn_ctx*conn_ctx){structqedn_ctx*qedn=conn_ctx->qedn;
From: Prabhakar Kushwaha <redacted>
This patch will add support of Task and SGL which is used
for slowpath and fast path IO. here Task is IO granule used
by firmware to perform tasks
The internal implementation:
- Create task/sgl resources used by all connection
- Provide APIs to allocate and free task.
- Add task support during connection establishment i.e. slowpath
Acked-by: Igor Russkikh <redacted>
Signed-off-by: Prabhakar Kushwaha <redacted>
Signed-off-by: Omkar Kulkarni <redacted>
Signed-off-by: Michal Kalderon <mkalderon@marvell.com>
Signed-off-by: Ariel Elior <redacted>
Signed-off-by: Shai Malin <redacted>
---
drivers/nvme/hw/qedn/qedn.h | 66 +++++
drivers/nvme/hw/qedn/qedn_conn.c | 43 +++-
drivers/nvme/hw/qedn/qedn_main.c | 34 ++-
drivers/nvme/hw/qedn/qedn_task.c | 411 +++++++++++++++++++++++++++++++
4 files changed, 550 insertions(+), 4 deletions(-)
@@ -137,6 +167,27 @@ struct qedn_ctx {dma_addr_tfw_cq_array_phy;/* Physical address of fw_cq_array_virt */structworkqueue_struct*nvme_req_fp_wq;structworkqueue_struct*fw_cq_fp_wq;++/* Fast Path Tasks */+structqed_nvmetcp_tidtasks;+};++structqedn_task_ctx{+structqedn_conn_ctx*qedn_conn;+structqedn_ctx*qedn;+void*fw_task_ctx;+structqedn_fp_queue*fp_q;+structscatterlist*nvme_sg;+structnvme_tcp_ofld_req*req;/* currently proccessed request */+structlist_headentry;+spinlock_tlock;/* To protect task resources */+boolvalid;+unsignedlongflags;/* Used by qedn_task_flags */+u32task_size;+u16itid;+u16cccid;+intreq_direction;+structstorage_sgl_task_paramssgl_task_params;};structqedn_endpoint{
@@ -387,6 +401,8 @@ static int qedn_prep_and_offload_queue(struct qedn_conn_ctx *conn_ctx)set_bit(QEDN_CONN_RESRC_FW_SQ,&conn_ctx->resrc_state);INIT_LIST_HEAD(&conn_ctx->host_pend_req_list);spin_lock_init(&conn_ctx->nvme_req_lock);+atomic_set(&conn_ctx->num_active_tasks,0);+atomic_set(&conn_ctx->num_active_fw_tasks,0);rc=qed_ops->acquire_conn(qedn->cdev,&conn_ctx->conn_handle,
@@ -401,7 +417,32 @@ static int qedn_prep_and_offload_queue(struct qedn_conn_ctx *conn_ctx)conn_ctx->conn_handle);set_bit(QEDN_CONN_RESRC_ACQUIRE_CONN,&conn_ctx->resrc_state);-/* Placeholder - Allocate task resources and initialize fields */+qid=qedn_qid(conn_ctx->queue);+default_cq_idx=qid?qid-1:0;/* Offset adminq */++conn_ctx->default_cq=(default_cq_idx%qedn->num_fw_cqs);+fp_q=&qedn->fp_q_arr[conn_ctx->default_cq];+conn_ctx->fp_q=fp_q;+io_resrc=&fp_q->host_resrc;++/* The first connection on each fp_q will fill task+*resources+*/+spin_lock(&io_resrc->resources_lock);+if(io_resrc->num_alloc_tasks==0){+rc=qedn_alloc_tasks(conn_ctx);+if(rc){+pr_err("Failed allocating tasks: CID=0x%x\n",+conn_ctx->fw_cid);+spin_unlock(&io_resrc->resources_lock);+gotorel_conn;+}+}+spin_unlock(&io_resrc->resources_lock);++spin_lock_init(&conn_ctx->task_list_lock);+INIT_LIST_HEAD(&conn_ctx->active_task_list);+set_bit(QEDN_CONN_RESRC_TASKS,&conn_ctx->resrc_state);rc=qedn_fetch_tcp_port(conn_ctx);if(rc)
@@ -657,8 +665,24 @@ static void qedn_remove_pf_from_gl_list(struct qedn_ctx *qedn)mutex_unlock(&qedn_glb.glb_mutex);}+staticvoidqedn_call_destroy_free_tasks(structqedn_fp_queue*fp_q,+structqedn_io_resources*io_resrc)+{+if(list_empty(&io_resrc->task_free_list))+return;++if(io_resrc->num_alloc_tasks!=io_resrc->num_free_tasks)+pr_err("Task Pool:Not all returned allocated=0x%x, free=0x%x\n",+io_resrc->num_alloc_tasks,io_resrc->num_free_tasks);++qedn_destroy_free_tasks(fp_q,io_resrc);+if(io_resrc->num_free_tasks)+pr_err("Expected num_free_tasks to be 0\n");+}+staticvoidqedn_free_function_queues(structqedn_ctx*qedn){+structqedn_io_resources*host_resrc;structqed_sb_info*sb_info=NULL;structqedn_fp_queue*fp_q;inti;
@@ -673,6 +697,9 @@ static void qedn_free_function_queues(struct qedn_ctx *qedn)/* Free the fast path queues*/for(i=0;i<qedn->num_fw_cqs;i++){fp_q=&qedn->fp_q_arr[i];+host_resrc=&fp_q->host_resrc;++qedn_call_destroy_free_tasks(fp_q,host_resrc);/* Free SB */sb_info=fp_q->sb_info;
@@ -11,6 +11,263 @@/* Driver includes */#include"qedn.h"+staticboolqedn_sgl_has_small_mid_sge(structnvmetcp_sge*sgl,u16sge_count)+{+u16sge_num;++if(sge_count>8){+for(sge_num=0;sge_num<sge_count;sge_num++){+if(le32_to_cpu(sgl[sge_num].sge_len)<+QEDN_FW_SLOW_IO_MIN_SGE_LIMIT)+returntrue;/* small middle SGE found */+}+}++returnfalse;/* no small middle SGEs */+}++staticintqedn_init_sgl(structqedn_ctx*qedn,structqedn_task_ctx*qedn_task)+{+structstorage_sgl_task_params*sgl_task_params;+enumdma_data_directiondma_dir;+structscatterlist*sg;+structrequest*rq;+u16num_sges;+intindex;+intrc;++sgl_task_params=&qedn_task->sgl_task_params;+rq=blk_mq_rq_from_pdu(qedn_task->req);+if(qedn_task->task_size==0){+sgl_task_params->num_sges=0;++return0;+}++/* Convert BIO to scatterlist */+num_sges=blk_rq_map_sg(rq->q,rq,qedn_task->nvme_sg);+if(qedn_task->req_direction==WRITE)+dma_dir=DMA_TO_DEVICE;+else+dma_dir=DMA_FROM_DEVICE;++/* DMA map the scatterlist */+if(dma_map_sg(&qedn->pdev->dev,qedn_task->nvme_sg,num_sges,dma_dir)!=num_sges){+pr_err("Couldn't map sgl\n");+rc=-EPERM;++returnrc;+}++sgl_task_params->total_buffer_size=qedn_task->task_size;+sgl_task_params->num_sges=num_sges;++for_each_sg(qedn_task->nvme_sg,sg,num_sges,index){+DMA_REGPAIR_LE(sgl_task_params->sgl[index].sge_addr,sg_dma_address(sg));+sgl_task_params->sgl[index].sge_len=cpu_to_le32(sg_dma_len(sg));+}++/* Relevant for Host Write Only */+sgl_task_params->small_mid_sge=(qedn_task->req_direction==READ)?+false:+qedn_sgl_has_small_mid_sge(sgl_task_params->sgl,+sgl_task_params->num_sges);++return0;+}++staticvoidqedn_free_nvme_sg(structqedn_task_ctx*qedn_task)+{+kfree(qedn_task->nvme_sg);+qedn_task->nvme_sg=NULL;+}++staticvoidqedn_free_fw_sgl(structqedn_task_ctx*qedn_task)+{+structqedn_ctx*qedn=qedn_task->qedn;+dma_addr_tsgl_pa;++sgl_pa=HILO_DMA_REGPAIR(qedn_task->sgl_task_params.sgl_phys_addr);+dma_free_coherent(&qedn->pdev->dev,+QEDN_MAX_FW_SGL_SIZE,+qedn_task->sgl_task_params.sgl,+sgl_pa);+qedn_task->sgl_task_params.sgl=NULL;+}++staticvoidqedn_destroy_single_task(structqedn_task_ctx*qedn_task)+{+u16itid;++itid=qedn_task->itid;+list_del(&qedn_task->entry);+qedn_free_nvme_sg(qedn_task);+qedn_free_fw_sgl(qedn_task);+kfree(qedn_task);+qedn_task=NULL;+}++voidqedn_destroy_free_tasks(structqedn_fp_queue*fp_q,+structqedn_io_resources*io_resrc)+{+structqedn_task_ctx*qedn_task,*task_tmp;++/* Destroy tasks from the free task list */+list_for_each_entry_safe(qedn_task,task_tmp,+&io_resrc->task_free_list,entry){+qedn_destroy_single_task(qedn_task);+io_resrc->num_free_tasks-=1;+}+}++staticintqedn_alloc_nvme_sg(structqedn_task_ctx*qedn_task)+{+intrc;++qedn_task->nvme_sg=kcalloc(QEDN_MAX_SGES_PER_TASK,+sizeof(*qedn_task->nvme_sg),GFP_KERNEL);+if(!qedn_task->nvme_sg){+rc=-ENOMEM;++returnrc;+}++return0;+}++staticintqedn_alloc_fw_sgl(structqedn_task_ctx*qedn_task)+{+structqedn_ctx*qedn=qedn_task->qedn_conn->qedn;+dma_addr_tfw_sgl_phys;++qedn_task->sgl_task_params.sgl=+dma_alloc_coherent(&qedn->pdev->dev,QEDN_MAX_FW_SGL_SIZE,+&fw_sgl_phys,GFP_KERNEL);+if(!qedn_task->sgl_task_params.sgl){+pr_err("Couldn't allocate FW sgl\n");++return-ENOMEM;+}++DMA_REGPAIR_LE(qedn_task->sgl_task_params.sgl_phys_addr,fw_sgl_phys);++return0;+}++staticinlinevoid*qedn_get_fw_task(structqed_nvmetcp_tid*info,u16itid)+{+return(void*)(info->blocks[itid/info->num_tids_per_block]++(itid%info->num_tids_per_block)*info->size);+}++staticstructqedn_task_ctx*qedn_alloc_task(structqedn_conn_ctx*conn_ctx,u16itid)+{+structqedn_ctx*qedn=conn_ctx->qedn;+structqedn_task_ctx*qedn_task;+void*fw_task_ctx;+intrc=0;++qedn_task=kzalloc(sizeof(*qedn_task),GFP_KERNEL);+if(!qedn_task)+returnNULL;++spin_lock_init(&qedn_task->lock);+fw_task_ctx=qedn_get_fw_task(&qedn->tasks,itid);+if(!fw_task_ctx){+pr_err("iTID: 0x%x; Failed getting fw_task_ctx memory\n",itid);+gotorelease_task;+}++/* No need to memset fw_task_ctx - its done in the HSI func */+qedn_task->qedn_conn=conn_ctx;+qedn_task->qedn=qedn;+qedn_task->fw_task_ctx=fw_task_ctx;+qedn_task->valid=0;+qedn_task->flags=0;+qedn_task->itid=itid;+rc=qedn_alloc_fw_sgl(qedn_task);+if(rc){+pr_err("iTID: 0x%x; Failed allocating FW sgl\n",itid);+gotorelease_task;+}++rc=qedn_alloc_nvme_sg(qedn_task);+if(rc){+pr_err("iTID: 0x%x; Failed allocating FW sgl\n",itid);+gotorelease_fw_sgl;+}++returnqedn_task;++release_fw_sgl:+qedn_free_fw_sgl(qedn_task);+release_task:+kfree(qedn_task);++returnNULL;+}++intqedn_alloc_tasks(structqedn_conn_ctx*conn_ctx)+{+structqedn_ctx*qedn=conn_ctx->qedn;+structqedn_task_ctx*qedn_task=NULL;+structqedn_io_resources*io_resrc;+u16itid,start_itid,offset;+structqedn_fp_queue*fp_q;+inti,rc;++fp_q=conn_ctx->fp_q;++offset=fp_q->sb_id;+io_resrc=&fp_q->host_resrc;++start_itid=qedn->num_tasks_per_pool*offset;+for(i=0;i<qedn->num_tasks_per_pool;++i){+itid=start_itid+i;+qedn_task=qedn_alloc_task(conn_ctx,itid);+if(!qedn_task){+pr_err("Failed allocating task\n");+rc=-ENOMEM;+gotorelease_tasks;+}++qedn_task->fp_q=fp_q;+io_resrc->num_free_tasks+=1;+list_add_tail(&qedn_task->entry,&io_resrc->task_free_list);+}++io_resrc->num_alloc_tasks=io_resrc->num_free_tasks;++return0;++release_tasks:+qedn_destroy_free_tasks(fp_q,io_resrc);++returnrc;+}++voidqedn_common_clear_fw_sgl(structstorage_sgl_task_params*sgl_task_params)+{+u16sge_cnt=sgl_task_params->num_sges;++memset(&sgl_task_params->sgl[(sge_cnt-1)],0,+sizeof(structnvmetcp_sge));+sgl_task_params->total_buffer_size=0;+sgl_task_params->small_mid_sge=false;+sgl_task_params->num_sges=0;+}++inlinevoidqedn_host_reset_cccid_itid_entry(structqedn_conn_ctx*conn_ctx,+u16cccid)+{+conn_ctx->host_cccid_itid[cccid].itid=cpu_to_le16(QEDN_INVALID_ITID);+}++inlinevoidqedn_host_set_cccid_itid_entry(structqedn_conn_ctx*conn_ctx,u16cccid,u16itid)+{+conn_ctx->host_cccid_itid[cccid].itid=cpu_to_le16(itid);+}+inlineintqedn_validate_cccid_in_range(structqedn_conn_ctx*conn_ctx,u16cccid){intrc=0;
@@ -23,6 +280,160 @@ inline int qedn_validate_cccid_in_range(struct qedn_conn_ctx *conn_ctx, u16 cccireturnrc;}+staticvoidqedn_clear_sgl(structqedn_ctx*qedn,+structqedn_task_ctx*qedn_task)+{+structstorage_sgl_task_params*sgl_task_params;+enumdma_data_directiondma_dir;+u32sge_cnt;++sgl_task_params=&qedn_task->sgl_task_params;+sge_cnt=sgl_task_params->num_sges;++/* Nothing to do if no SGEs were used */+if(!qedn_task->task_size||!sge_cnt)+return;++dma_dir=(qedn_task->req_direction==WRITE?DMA_TO_DEVICE:DMA_FROM_DEVICE);+dma_unmap_sg(&qedn->pdev->dev,qedn_task->nvme_sg,sge_cnt,dma_dir);+memset(&qedn_task->nvme_sg[(sge_cnt-1)],0,sizeof(structscatterlist));+qedn_common_clear_fw_sgl(sgl_task_params);+qedn_task->task_size=0;+}++staticvoidqedn_clear_task(structqedn_conn_ctx*conn_ctx,+structqedn_task_ctx*qedn_task)+{+/* Task lock isn't needed since it is no longer in use */+qedn_clear_sgl(conn_ctx->qedn,qedn_task);+qedn_task->valid=0;+qedn_task->flags=0;++atomic_dec(&conn_ctx->num_active_tasks);+}++voidqedn_return_active_tasks(structqedn_conn_ctx*conn_ctx)+{+structqedn_fp_queue*fp_q=conn_ctx->fp_q;+structqedn_task_ctx*qedn_task,*task_tmp;+structqedn_io_resources*io_resrc;+intnum_returned_tasks=0;+intnum_active_tasks;++io_resrc=&fp_q->host_resrc;++/* Return tasks that aren't "Used by FW" to the pool */+list_for_each_entry_safe(qedn_task,task_tmp,+&conn_ctx->active_task_list,entry){+qedn_clear_task(conn_ctx,qedn_task);+num_returned_tasks++;+}++if(num_returned_tasks){+spin_lock(&io_resrc->resources_lock);+/* Return tasks to FP_Q pool in one shot */++list_splice_tail_init(&conn_ctx->active_task_list,+&io_resrc->task_free_list);+io_resrc->num_free_tasks+=num_returned_tasks;+spin_unlock(&io_resrc->resources_lock);+}++num_active_tasks=atomic_read(&conn_ctx->num_active_tasks);+if(num_active_tasks)+pr_err("num_active_tasks is %u after cleanup.\n",num_active_tasks);+}++voidqedn_return_task_to_pool(structqedn_conn_ctx*conn_ctx,+structqedn_task_ctx*qedn_task)+{+structqedn_fp_queue*fp_q=conn_ctx->fp_q;+structqedn_io_resources*io_resrc;+unsignedlonglock_flags;++io_resrc=&fp_q->host_resrc;++spin_lock_irqsave(&qedn_task->lock,lock_flags);+qedn_task->valid=0;+qedn_task->flags=0;+qedn_clear_sgl(conn_ctx->qedn,qedn_task);+spin_unlock_irqrestore(&qedn_task->lock,lock_flags);++spin_lock(&conn_ctx->task_list_lock);+list_del(&qedn_task->entry);+qedn_host_reset_cccid_itid_entry(conn_ctx,qedn_task->cccid);+spin_unlock(&conn_ctx->task_list_lock);++atomic_dec(&conn_ctx->num_active_tasks);+atomic_dec(&conn_ctx->num_active_fw_tasks);++spin_lock(&io_resrc->resources_lock);+list_add_tail(&qedn_task->entry,&io_resrc->task_free_list);+io_resrc->num_free_tasks+=1;+spin_unlock(&io_resrc->resources_lock);+}++structqedn_task_ctx*+qedn_get_free_task_from_pool(structqedn_conn_ctx*conn_ctx,u16cccid)+{+structqedn_task_ctx*qedn_task=NULL;+structqedn_io_resources*io_resrc;+structqedn_fp_queue*fp_q;++fp_q=conn_ctx->fp_q;+io_resrc=&fp_q->host_resrc;++spin_lock(&io_resrc->resources_lock);+qedn_task=list_first_entry_or_null(&io_resrc->task_free_list,+structqedn_task_ctx,entry);+if(unlikely(!qedn_task)){+spin_unlock(&io_resrc->resources_lock);++returnNULL;+}+list_del(&qedn_task->entry);+io_resrc->num_free_tasks-=1;+spin_unlock(&io_resrc->resources_lock);++spin_lock(&conn_ctx->task_list_lock);+list_add_tail(&qedn_task->entry,&conn_ctx->active_task_list);+qedn_host_set_cccid_itid_entry(conn_ctx,cccid,qedn_task->itid);+spin_unlock(&conn_ctx->task_list_lock);++atomic_inc(&conn_ctx->num_active_tasks);+qedn_task->cccid=cccid;+qedn_task->qedn_conn=conn_ctx;+qedn_task->valid=1;++returnqedn_task;+}++structqedn_task_ctx*+qedn_get_task_from_pool_insist(structqedn_conn_ctx*conn_ctx,u16cccid)+{+structqedn_task_ctx*qedn_task=NULL;+unsignedlongtimeout;++qedn_task=qedn_get_free_task_from_pool(conn_ctx,cccid);+if(unlikely(!qedn_task)){+timeout=msecs_to_jiffies(QEDN_TASK_INSIST_TMO)+jiffies;+while(1){+qedn_task=qedn_get_free_task_from_pool(conn_ctx,cccid);+if(likely(qedn_task))+break;++msleep(100);+if(time_after(jiffies,timeout)){+pr_err("Failed on timeout of fetching task\n");++returnNULL;+}+}+}++returnqedn_task;+}+staticboolqedn_process_req(structqedn_conn_ctx*qedn_conn){returntrue;
This patch will present the IO level functionality of qedn
nvme-tcp-offload host mode. The qedn_task_ctx structure is containing
various params and state of the current IO, and is mapped 1x1 to the
fw_task_ctx which is a HW and FW IO context.
A qedn_task is mapped directly to its parent connection.
For every new IO a qedn_task structure will be assigned and they will be
linked for the entire IO's life span.
The patch will include 2 flows:
1. Send new command to the FW:
The flow is: nvme_tcp_ofld_queue_rq() which invokes qedn_send_req()
which invokes qedn_queue_request() which will:
- Assign fw_task_ctx.
- Prepare the Read/Write SG buffer.
- Initialize the HW and FW context.
- Pass the IO to the FW.
2. Process the IO completion:
The flow is: qedn_irq_handler() which invokes qedn_fw_cq_fp_handler()
which invokes qedn_io_work_cq() which will:
- process the FW completion.
- Return the fw_task_ctx to the task pool.
- complete the nvme req.
Acked-by: Igor Russkikh <redacted>
Signed-off-by: Prabhakar Kushwaha <redacted>
Signed-off-by: Omkar Kulkarni <redacted>
Signed-off-by: Michal Kalderon <mkalderon@marvell.com>
Signed-off-by: Ariel Elior <redacted>
Signed-off-by: Shai Malin <redacted>
---
drivers/nvme/hw/qedn/qedn.h | 4 +
drivers/nvme/hw/qedn/qedn_conn.c | 1 +
drivers/nvme/hw/qedn/qedn_task.c | 269 ++++++++++++++++++++++++++++++-
3 files changed, 272 insertions(+), 2 deletions(-)
@@ -11,6 +11,8 @@/* Driver includes */#include"qedn.h"+externconststructqed_nvmetcp_ops*qed_ops;+staticboolqedn_sgl_has_small_mid_sge(structnvmetcp_sge*sgl,u16sge_count){u16sge_num;
@@ -434,8 +436,194 @@ qedn_get_task_from_pool_insist(struct qedn_conn_ctx *conn_ctx, u16 cccid)returnqedn_task;}+intqedn_send_read_cmd(structqedn_task_ctx*qedn_task,structqedn_conn_ctx*conn_ctx)+{+structnvme_command*nvme_cmd=&qedn_task->req->nvme_cmd;+structqedn_ctx*qedn=conn_ctx->qedn;+structnvmetcp_cmd_capsule_hdrcmd_hdr;+structnvmetcp_task_paramstask_params;+structnvmetcp_conn_paramsconn_params;+structnvmetcp_wqe*chain_sqe;+structnvmetcp_wqelocal_sqe;+intrc;+inti;++rc=qedn_init_sgl(qedn,qedn_task);+if(rc)+returnrc;++task_params.opq.lo=cpu_to_le32(((u64)(qedn_task))&0xffffffff);+task_params.opq.hi=cpu_to_le32(((u64)(qedn_task))>>32);++/* Initialize task params */+task_params.context=qedn_task->fw_task_ctx;+task_params.sqe=&local_sqe;+task_params.tx_io_size=0;+task_params.rx_io_size=qedn_task->task_size;+task_params.conn_icid=(u16)conn_ctx->conn_handle;+task_params.itid=qedn_task->itid;+task_params.cq_rss_number=conn_ctx->default_cq;+task_params.send_write_incapsule=0;++/* Initialize conn params */+conn_params.max_burst_length=QEDN_MAX_IO_SIZE;++cmd_hdr.chdr.pdu_type=nvme_tcp_cmd;+cmd_hdr.chdr.flags=0;+cmd_hdr.chdr.hlen=sizeof(cmd_hdr);+cmd_hdr.chdr.pdo=0x0;+cmd_hdr.chdr.plen_swapped=cpu_to_le32(__swab32(cmd_hdr.chdr.hlen));++for(i=0;i<16;i++)+cmd_hdr.pshdr.raw_swapped[i]=cpu_to_le32(__swab32(((u32*)nvme_cmd)[i]));++qed_ops->init_read_io(&task_params,&conn_params,&cmd_hdr,&qedn_task->sgl_task_params);++set_bit(QEDN_TASK_USED_BY_FW,&qedn_task->flags);+atomic_inc(&conn_ctx->num_active_fw_tasks);++spin_lock(&conn_ctx->ep.doorbell_lock);+chain_sqe=qed_chain_produce(&conn_ctx->ep.fw_sq_chain);+memcpy(chain_sqe,&local_sqe,sizeof(local_sqe));+qedn_ring_doorbell(conn_ctx);+spin_unlock(&conn_ctx->ep.doorbell_lock);++return0;+}++intqedn_send_write_cmd(structqedn_task_ctx*qedn_task,structqedn_conn_ctx*conn_ctx)+{+structnvme_command*nvme_cmd=&qedn_task->req->nvme_cmd;+structnvmetcp_task_paramstask_params;+structqedn_ctx*qedn=conn_ctx->qedn;+structnvmetcp_cmd_capsule_hdrcmd_hdr;+structnvmetcp_conn_paramsconn_params;+u32pdu_len=sizeof(cmd_hdr);+structnvmetcp_wqe*chain_sqe;+structnvmetcp_wqelocal_sqe;+u8send_write_incapsule;+intrc;+inti;++if(qedn_task->task_size<=nvme_tcp_ofld_inline_data_size(conn_ctx->queue)&&+qedn_task->task_size){+send_write_incapsule=1;+pdu_len+=qedn_task->task_size;++/* Add digest length once supported */+cmd_hdr.chdr.pdo=sizeof(cmd_hdr);+}else{+send_write_incapsule=0;++cmd_hdr.chdr.pdo=0x0;+}++rc=qedn_init_sgl(qedn,qedn_task);+if(rc)+returnrc;++task_params.host_cccid=cpu_to_le16(qedn_task->cccid);+task_params.opq.lo=cpu_to_le32(((u64)(qedn_task))&0xffffffff);+task_params.opq.hi=cpu_to_le32(((u64)(qedn_task))>>32);++/* Initialize task params */+task_params.context=qedn_task->fw_task_ctx;+task_params.sqe=&local_sqe;+task_params.tx_io_size=qedn_task->task_size;+task_params.rx_io_size=0;+task_params.conn_icid=(u16)conn_ctx->conn_handle;+task_params.itid=qedn_task->itid;+task_params.cq_rss_number=conn_ctx->default_cq;+task_params.send_write_incapsule=send_write_incapsule;++/* Initialize conn params */++cmd_hdr.chdr.pdu_type=nvme_tcp_cmd;+cmd_hdr.chdr.flags=0;+cmd_hdr.chdr.hlen=sizeof(cmd_hdr);+cmd_hdr.chdr.plen_swapped=cpu_to_le32(__swab32(pdu_len));+for(i=0;i<16;i++)+cmd_hdr.pshdr.raw_swapped[i]=cpu_to_le32(__swab32(((u32*)nvme_cmd)[i]));++qed_ops->init_write_io(&task_params,&conn_params,&cmd_hdr,&qedn_task->sgl_task_params);++set_bit(QEDN_TASK_USED_BY_FW,&qedn_task->flags);+atomic_inc(&conn_ctx->num_active_fw_tasks);++spin_lock(&conn_ctx->ep.doorbell_lock);+chain_sqe=qed_chain_produce(&conn_ctx->ep.fw_sq_chain);+memcpy(chain_sqe,&local_sqe,sizeof(local_sqe));+qedn_ring_doorbell(conn_ctx);+spin_unlock(&conn_ctx->ep.doorbell_lock);++return0;+}++staticvoidqedn_fetch_request(structqedn_conn_ctx*qedn_conn)+{+spin_lock(&qedn_conn->nvme_req_lock);+qedn_conn->req=list_first_entry_or_null(&qedn_conn->host_pend_req_list,+structnvme_tcp_ofld_req,queue_entry);+if(qedn_conn->req)+list_del(&qedn_conn->req->queue_entry);+spin_unlock(&qedn_conn->nvme_req_lock);+}+staticboolqedn_process_req(structqedn_conn_ctx*qedn_conn){+structqedn_task_ctx*qedn_task;+structnvme_tcp_ofld_req*req;+structrequest*rq;+intrc=0;+u16cccid;++qedn_fetch_request(qedn_conn);+if(!qedn_conn->req)+returnfalse;++req=qedn_conn->req;+rq=blk_mq_rq_from_pdu(req);++/* Placeholder - async */++cccid=rq->tag;+qedn_task=qedn_get_task_from_pool_insist(qedn_conn,cccid);+if(unlikely(!qedn_task)){+pr_err("Not able to allocate task context\n");+gotodoorbell;+}++req->private_data=qedn_task;+qedn_task->req=req;++/* Placeholder - handle (req->async) */++/* Check if there are physical segments in request to determine the task size.+*Thelogicofnvme_tcp_set_sg_null()willbeimplementedaspartof+*qedn_set_sg_host_data().+*/+qedn_task->task_size=blk_rq_nr_phys_segments(rq)?blk_rq_payload_bytes(rq):0;+qedn_task->req_direction=rq_data_dir(rq);+if(qedn_task->req_direction==WRITE)+rc=qedn_send_write_cmd(qedn_task,qedn_conn);+else+rc=qedn_send_read_cmd(qedn_task,qedn_conn);++if(unlikely(rc)){+pr_err("Read/Write command failure\n");+gotodoorbell;+}++/* Don't ring doorbell if this is not the last request */+if(!req->last)+returntrue;++doorbell:+/* Always ring doorbell if reached here, in case there were coalesced+*requestswhichweredelayed+*/+qedn_ring_doorbell(qedn_conn);+returntrue;}
@@ -497,8 +685,71 @@ struct qedn_task_ctx *qedn_cqe_get_active_task(struct nvmetcp_fw_cqe *cqe)+le32_to_cpu(p->lo)));}+staticstructnvme_tcp_ofld_req*qedn_decouple_req_task(structqedn_task_ctx*qedn_task)+{+structnvme_tcp_ofld_req*ulp_req=qedn_task->req;++qedn_task->req=NULL;+if(ulp_req)+ulp_req->private_data=NULL;++returnulp_req;+}++staticinlineintqedn_comp_valid_task(structqedn_task_ctx*qedn_task,+unionnvme_result*result,__le16status)+{+structqedn_conn_ctx*conn_ctx=qedn_task->qedn_conn;+structnvme_tcp_ofld_req*req;++req=qedn_decouple_req_task(qedn_task);+qedn_return_task_to_pool(conn_ctx,qedn_task);+if(!req){+pr_err("req not found\n");++return-EINVAL;+}++/* Call request done to compelete the request */+if(req->done)+req->done(req,result,status);+else+pr_err("request done not Set !!!\n");++return0;+}++intqedn_process_nvme_cqe(structqedn_task_ctx*qedn_task,structnvme_completion*cqe)+{+intrc=0;++/* cqe arrives swapped */+qedn_swap_bytes((u32*)cqe,(sizeof(*cqe)/sizeof(u32)));++/* Placeholder - async */++rc=qedn_comp_valid_task(qedn_task,&cqe->result,cqe->status);++returnrc;+}++intqedn_complete_c2h(structqedn_task_ctx*qedn_task)+{+intrc=0;++__le16status=cpu_to_le16(NVME_SC_SUCCESS<<1);+unionnvme_resultresult={};++rc=qedn_comp_valid_task(qedn_task,&result,status);++returnrc;+}+voidqedn_io_work_cq(structqedn_ctx*qedn,structnvmetcp_fw_cqe*cqe){+intrc=0;++structnvme_completion*nvme_cqe=NULL;structqedn_task_ctx*qedn_task=NULL;structqedn_conn_ctx*conn_ctx=NULL;u16itid;
@@ -525,13 +776,27 @@ void qedn_io_work_cq(struct qedn_ctx *qedn, struct nvmetcp_fw_cqe *cqe)caseNVMETCP_TASK_TYPE_HOST_WRITE:caseNVMETCP_TASK_TYPE_HOST_READ:-/* Placeholder - IO flow */+/* Verify data digest once supported */++nvme_cqe=(structnvme_completion*)&cqe->nvme_cqe;+rc=qedn_process_nvme_cqe(qedn_task,nvme_cqe);+if(rc){+pr_err("Read/Write completion error\n");+return;+}break;caseNVMETCP_TASK_TYPE_HOST_READ_NO_CQE:-/* Placeholder - IO flow */+/* Verify data digest once supported */++rc=qedn_complete_c2h(qedn_task);+if(rc){+pr_err("Controller To Host Data Transfer error error\n");++return;+}break;
This patch will present the connection level functionalities:
- conn clear-sq: will release the FW restrictions in order to flush all
the pending IOs.
- drain: in case clear-sq is stuck, will release all the device FW
restrictions in order to flush all the pending IOs.
- task cleanup - will flush the IO level resources.
Acked-by: Igor Russkikh <redacted>
Signed-off-by: Prabhakar Kushwaha <redacted>
Signed-off-by: Omkar Kulkarni <redacted>
Signed-off-by: Michal Kalderon <mkalderon@marvell.com>
Signed-off-by: Ariel Elior <redacted>
Signed-off-by: Shai Malin <redacted>
---
drivers/nvme/hw/qedn/qedn.h | 8 ++
drivers/nvme/hw/qedn/qedn_conn.c | 133 ++++++++++++++++++++++++++++++-
drivers/nvme/hw/qedn/qedn_main.c | 1 +
drivers/nvme/hw/qedn/qedn_task.c | 27 ++++++-
4 files changed, 166 insertions(+), 3 deletions(-)
@@ -765,8 +784,110 @@ static int qedn_prep_and_offload_queue(struct qedn_conn_ctx *conn_ctx)return-EINVAL;}+staticvoidqedn_cleanup_fw_task(structqedn_ctx*qedn,structqedn_task_ctx*qedn_task)+{+structqedn_conn_ctx*conn_ctx=qedn_task->qedn_conn;+structnvmetcp_task_paramstask_params;+structnvmetcp_wqe*chain_sqe;+structnvmetcp_wqelocal_sqe;+unsignedlonglock_flags;++/* Take lock to prevent race with fastpath, we don't want to+*invokecleanupflowsontasksthatalreadyreturned.+*/+spin_lock_irqsave(&qedn_task->lock,lock_flags);+if(!qedn_task->valid){+spin_unlock_irqrestore(&qedn_task->lock,lock_flags);++return;+}+/* Skip tasks not used by FW */+if(!test_bit(QEDN_TASK_USED_BY_FW,&qedn_task->flags)){+spin_unlock_irqrestore(&qedn_task->lock,lock_flags);++return;+}+/* Skip tasks that were already invoked for cleanup */+if(unlikely(test_bit(QEDN_TASK_WAIT_FOR_CLEANUP,&qedn_task->flags))){+spin_unlock_irqrestore(&qedn_task->lock,lock_flags);++return;+}+set_bit(QEDN_TASK_WAIT_FOR_CLEANUP,&qedn_task->flags);+spin_unlock_irqrestore(&qedn_task->lock,lock_flags);++atomic_inc(&conn_ctx->task_cleanups_cnt);++task_params.sqe=&local_sqe;+task_params.itid=qedn_task->itid;+qed_ops->init_task_cleanup(&task_params);++/* spin_lock - doorbell is accessed both Rx flow and response flow */+spin_lock(&conn_ctx->ep.doorbell_lock);+chain_sqe=qed_chain_produce(&conn_ctx->ep.fw_sq_chain);+memcpy(chain_sqe,&local_sqe,sizeof(local_sqe));+qedn_ring_doorbell(conn_ctx);+spin_unlock(&conn_ctx->ep.doorbell_lock);+}++inlineintqedn_drain(structqedn_conn_ctx*conn_ctx)+{+intdrain_iter=QEDN_DRAIN_MAX_ATTEMPTS;+structqedn_ctx*qedn=conn_ctx->qedn;+intwrc;++while(drain_iter){+qed_ops->common->drain(qedn->cdev);+msleep(100);++wrc=wait_event_interruptible_timeout(conn_ctx->cleanup_waitq,+!atomic_read(&conn_ctx->task_cleanups_cnt),+msecs_to_jiffies(QEDN_DRAIN_TMO));+if(!wrc){+drain_iter--;+continue;+}++return0;+}++pr_err("CID 0x%x: cleanup after drain failed - need hard reset.\n",conn_ctx->fw_cid);++return-EINVAL;+}++voidqedn_cleanup_all_fw_tasks(structqedn_conn_ctx*conn_ctx)+{+structqedn_task_ctx*qedn_task,*task_tmp;+structqedn_ctx*qedn=conn_ctx->qedn;+intwrc;++list_for_each_entry_safe_reverse(qedn_task,task_tmp,&conn_ctx->active_task_list,entry){+qedn_cleanup_fw_task(qedn,qedn_task);+}++wrc=wait_event_interruptible_timeout(conn_ctx->cleanup_waitq,+atomic_read(&conn_ctx->task_cleanups_cnt)==0,+msecs_to_jiffies(QEDN_TASK_CLEANUP_TMO));+if(!wrc){+if(qedn_drain(conn_ctx))+return;+}+}++staticvoidqedn_clear_fw_sq(structqedn_conn_ctx*conn_ctx)+{+structqedn_ctx*qedn=conn_ctx->qedn;+intrc;++rc=qed_ops->clear_sq(qedn->cdev,conn_ctx->conn_handle);+if(rc)+pr_warn("clear_sq failed - rc %u\n",rc);+}+voidqedn_destroy_connection(structqedn_conn_ctx*conn_ctx){+structnvme_tcp_ofld_req*req,*req_tmp;structqedn_ctx*qedn=conn_ctx->qedn;intrc;
@@ -327,6 +327,17 @@ void qedn_return_active_tasks(struct qedn_conn_ctx *conn_ctx)/* Return tasks that aren't "Used by FW" to the pool */list_for_each_entry_safe(qedn_task,task_tmp,&conn_ctx->active_task_list,entry){+/* If we got this far, cleanup was already done+*inwhichcasewewanttoreturnthetasktothepooland+*releaseit.Sowemakesurethecleanupindicationisdown+*/+clear_bit(QEDN_TASK_WAIT_FOR_CLEANUP,&qedn_task->flags);++/* Special handling in case of ICREQ task */+if(unlikely(conn_ctx->state==CONN_STATE_WAIT_FOR_IC_COMP&&+test_bit(QEDN_TASK_IS_ICREQ,&(qedn_task)->flags)))+qedn_common_clear_fw_sgl(&qedn_task->sgl_task_params);+qedn_clear_task(conn_ctx,qedn_task);num_returned_tasks++;}
@@ -770,7 +781,8 @@ void qedn_io_work_cq(struct qedn_ctx *qedn, struct nvmetcp_fw_cqe *cqe)return;if(likely(cqe->cqe_type==NVMETCP_FW_CQE_TYPE_NORMAL)){-/* Placeholder - verify the connection was established */+if(unlikely(test_bit(QEDN_TASK_WAIT_FOR_CLEANUP,&qedn_task->flags)))+return;switch(cqe->task_type){caseNVMETCP_TASK_TYPE_HOST_WRITE:
@@ -811,6 +823,17 @@ void qedn_io_work_cq(struct qedn_ctx *qedn, struct nvmetcp_fw_cqe *cqe)pr_info("Could not identify task type\n");}}else{-/* Placeholder - Recovery flows */+if(cqe->cqe_type==NVMETCP_FW_CQE_TYPE_CLEANUP){+clear_bit(QEDN_TASK_WAIT_FOR_CLEANUP,&qedn_task->flags);+qedn_return_task_to_pool(conn_ctx,qedn_task);+atomic_dec(&conn_ctx->task_cleanups_cnt);+wake_up_interruptible(&conn_ctx->cleanup_waitq);++return;+}++/* The else is NVMETCP_FW_CQE_TYPE_DUMMY - in which don't return the task.+*ThetaskwillreturnduringNVMETCP_FW_CQE_TYPE_CLEANUP.+*/}}
@@ -447,6 +484,67 @@ qedn_get_task_from_pool_insist(struct qedn_conn_ctx *conn_ctx, u16 cccid)returnqedn_task;}+voidqedn_send_async_event_cmd(structqedn_task_ctx*qedn_task,+structqedn_conn_ctx*conn_ctx)+{+structnvme_tcp_ofld_req*async_req=qedn_task->req;+structnvme_command*nvme_cmd=&async_req->nvme_cmd;+structstorage_sgl_task_params*sgl_task_params;+structnvmetcp_task_paramstask_params;+structnvmetcp_cmd_capsule_hdrcmd_hdr;+structnvmetcp_conn_paramsconn_params;+structnvmetcp_wqe*chain_sqe;+structnvmetcp_wqelocal_sqe;+inti;++set_bit(QEDN_TASK_ASYNC,&qedn_task->flags);+nvme_cmd->common.command_id=qedn_task->cccid;+qedn_task->task_size=0;++/* Initialize sgl params */+sgl_task_params=&qedn_task->sgl_task_params;+sgl_task_params->total_buffer_size=0;+sgl_task_params->num_sges=0;+sgl_task_params->small_mid_sge=false;++task_params.opq.lo=cpu_to_le32(((u64)(qedn_task))&0xffffffff);+task_params.opq.hi=cpu_to_le32(((u64)(qedn_task))>>32);++/* Initialize task params */+task_params.context=qedn_task->fw_task_ctx;+task_params.sqe=&local_sqe;+task_params.tx_io_size=0;+task_params.rx_io_size=0;+task_params.conn_icid=(u16)conn_ctx->conn_handle;+task_params.itid=qedn_task->itid;+task_params.cq_rss_number=conn_ctx->default_cq;+task_params.send_write_incapsule=0;++/* Initialize conn params */+conn_params.max_burst_length=QEDN_MAX_IO_SIZE;++/* Internal impl. - async is treated like zero len read */+cmd_hdr.chdr.pdu_type=nvme_tcp_cmd;+cmd_hdr.chdr.flags=0;+cmd_hdr.chdr.hlen=sizeof(cmd_hdr);+cmd_hdr.chdr.pdo=0x0;+cmd_hdr.chdr.plen_swapped=cpu_to_le32(__swab32(cmd_hdr.chdr.hlen));++for(i=0;i<16;i++)+cmd_hdr.pshdr.raw_swapped[i]=cpu_to_le32(__swab32(((u32*)nvme_cmd)[i]));++qed_ops->init_read_io(&task_params,&conn_params,&cmd_hdr,&qedn_task->sgl_task_params);++set_bit(QEDN_TASK_USED_BY_FW,&qedn_task->flags);+atomic_inc(&conn_ctx->num_active_fw_tasks);++spin_lock(&conn_ctx->ep.doorbell_lock);+chain_sqe=qed_chain_produce(&conn_ctx->ep.fw_sq_chain);+memcpy(chain_sqe,&local_sqe,sizeof(local_sqe));+qedn_ring_doorbell(conn_ctx);+spin_unlock(&conn_ctx->ep.doorbell_lock);+}+intqedn_send_read_cmd(structqedn_task_ctx*qedn_task,structqedn_conn_ctx*conn_ctx){structnvme_command*nvme_cmd=&qedn_task->req->nvme_cmd;
@@ -580,6 +678,24 @@ static void qedn_fetch_request(struct qedn_conn_ctx *qedn_conn)spin_unlock(&qedn_conn->nvme_req_lock);}+staticvoidqedn_return_error_req(structnvme_tcp_ofld_req*req)+{+__le16status=cpu_to_le16(NVME_SC_HOST_PATH_ERROR<<1);+unionnvme_resultres={};+structrequest*rq;++if(!req)+return;++rq=blk_mq_rq_from_pdu(req);++/* Call request done to compelete the request */+if(req->done)+req->done(req,&res,status);+else+pr_err("request done not set !!!\n");+}+staticboolqedn_process_req(structqedn_conn_ctx*qedn_conn){structqedn_task_ctx*qedn_task;
@@ -595,9 +711,16 @@ static bool qedn_process_req(struct qedn_conn_ctx *qedn_conn)req=qedn_conn->req;rq=blk_mq_rq_from_pdu(req);-/* Placeholder - async */+if(unlikely(req->async)){+cccid=qedn_get_free_async_cccid(qedn_conn);+if(cccid==QEDN_INVALID_CCCID){+qedn_return_error_req(req);+gotodoorbell;+}+}else{+cccid=rq->tag;+}-cccid=rq->tag;qedn_task=qedn_get_task_from_pool_insist(qedn_conn,cccid);if(unlikely(!qedn_task)){pr_err("Not able to allocate task context\n");
@@ -607,7 +730,10 @@ static bool qedn_process_req(struct qedn_conn_ctx *qedn_conn)req->private_data=qedn_task;qedn_task->req=req;-/* Placeholder - handle (req->async) */+if(unlikely(req->async)){+qedn_send_async_event_cmd(qedn_task,qedn_conn);+gotodoorbell;+}/* Check if there are physical segments in request to determine the task size.*Thelogicofnvme_tcp_set_sg_null()willbeimplementedaspartof
@@ -732,14 +858,26 @@ static inline int qedn_comp_valid_task(struct qedn_task_ctx *qedn_task,intqedn_process_nvme_cqe(structqedn_task_ctx*qedn_task,structnvme_completion*cqe){+structqedn_conn_ctx*conn_ctx=qedn_task->qedn_conn;+structnvme_tcp_ofld_req*req;intrc=0;+boolasync;++async=test_bit(QEDN_TASK_ASYNC,&(qedn_task)->flags);/* cqe arrives swapped */qedn_swap_bytes((u32*)cqe,(sizeof(*cqe)/sizeof(u32)));-/* Placeholder - async */--rc=qedn_comp_valid_task(qedn_task,&cqe->result,cqe->status);+if(unlikely(async)){+qedn_return_task_to_pool(conn_ctx,qedn_task);+req=qedn_task->req;+if(req->done)+req->done(req,&cqe->result,cqe->status);+else+pr_err("request done not set for async request !!!\n");+}else{+rc=qedn_comp_valid_task(qedn_task,&cqe->result,cqe->status);+}returnrc;}
From: Hannes Reinecke <hare@suse.de> Date: 2021-05-01 12:19:15
On 4/29/21 9:09 PM, Shai Malin wrote:
This patch will present the structure for the NVMeTCP offload common
layer driver. This module is added under "drivers/nvme/host/" and future
offload drivers which will register to it will be placed under
"drivers/nvme/hw".
This new driver will be enabled by the Kconfig "NVM Express over Fabrics
TCP offload commmon layer".
In order to support the new transport type, for host mode, no change is
needed.
Each new vendor-specific offload driver will register to this ULP during
its probe function, by filling out the nvme_tcp_ofld_dev->ops and
nvme_tcp_ofld_dev->private_data and calling nvme_tcp_ofld_register_dev
with the initialized struct.
The internal implementation:
- tcp-offload.h:
Includes all common structs and ops to be used and shared by offload
drivers.
- tcp-offload.c:
Includes the init function which registers as a NVMf transport just
like any other transport.
Acked-by: Igor Russkikh <redacted>
Signed-off-by: Dean Balandin <redacted>
Signed-off-by: Prabhakar Kushwaha <redacted>
Signed-off-by: Omkar Kulkarni <redacted>
Signed-off-by: Michal Kalderon <mkalderon@marvell.com>
Signed-off-by: Ariel Elior <redacted>
Signed-off-by: Shai Malin <redacted>
---
drivers/nvme/host/Kconfig | 16 +++
drivers/nvme/host/Makefile | 3 +
drivers/nvme/host/tcp-offload.c | 126 +++++++++++++++++++
drivers/nvme/host/tcp-offload.h | 206 ++++++++++++++++++++++++++++++++
4 files changed, 351 insertions(+)
create mode 100644 drivers/nvme/host/tcp-offload.c
create mode 100644 drivers/nvme/host/tcp-offload.h
It will be tricky to select the correct transport eg when traversing the
discovery log page; the discovery log page only knows about 'tcp' (not
'tcp_offload'), so the offload won't be picked up.
But that can we worked on / fixed later on, as it's arguably a policy
decision.
Reviewed-by: Hannes Reinecke <hare@suse.de>
Cheers,
Hannes
--
Dr. Hannes Reinecke Kernel Storage Architect
hare@suse.de +49 911 74053 688
SUSE Software Solutions GmbH, Maxfeldstr. 5, 90409 Nürnberg
HRB 36809 (AG Nürnberg), Geschäftsführer: Felix Imendörffer
Why do you need them? None of the other transport drivers use them, why you?
Cheers,
Hannes
--
Dr. Hannes Reinecke Kernel Storage Architect
hare@suse.de +49 911 74053 688
SUSE Software Solutions GmbH, Maxfeldstr. 5, 90409 Nürnberg
HRB 36809 (AG Nürnberg), Geschäftsführer: Felix Imendörffer
From: Hannes Reinecke <hare@suse.de> Date: 2021-05-01 12:25:47
On 4/29/21 9:09 PM, Shai Malin wrote:
quoted hunk
From: Dean Balandin <redacted>
As part of create_ctrl(), it scans the registered devices and calls
the claim_dev op on each of them, to find the first devices that matches
the connection params. Once the correct devices is found (claim_dev
returns true), we raise the refcnt of that device and return that device
as the device to be used for ctrl currently being created.
Acked-by: Igor Russkikh <redacted>
Signed-off-by: Dean Balandin <redacted>
Signed-off-by: Prabhakar Kushwaha <redacted>
Signed-off-by: Omkar Kulkarni <redacted>
Signed-off-by: Michal Kalderon <mkalderon@marvell.com>
Signed-off-by: Ariel Elior <redacted>
Signed-off-by: Shai Malin <redacted>
---
drivers/nvme/host/tcp-offload.c | 94 +++++++++++++++++++++++++++++++++
1 file changed, 94 insertions(+)
I wonder if we shouldn't take the approach from Martin Belanger, and
introduce a new option 'host_iface' to select the interface to use.
That is, _if_ the nvme-tcp offload driver would present itself as a
network interface; one might argue that it would put too much
restriction on the implementations.
But if it does not present itself as a network interface, how do we
address it? And if it does, wouldn't we be better off to specify the
interface directly, and not try to imply the interface from the IP address?
Cheers,
Hannes
--
Dr. Hannes Reinecke Kernel Storage Architect
hare@suse.de +49 911 74053 688
SUSE Software Solutions GmbH, Maxfeldstr. 5, 90409 Nürnberg
HRB 36809 (AG Nürnberg), Geschäftsführer: Felix Imendörffer
From: Hannes Reinecke <hare@suse.de> Date: 2021-05-01 16:30:20
On 4/29/21 9:09 PM, Shai Malin wrote:
quoted hunk
From: Arie Gershberg <redacted>
In this patch, we implement controller level error handling and recovery.
Upon an error discovered by the ULP or reset controller initiated by the
nvme-core (using reset_ctrl workqueue), the ULP will initiate a controller
recovery which includes teardown and re-connect of all queues.
Acked-by: Igor Russkikh <redacted>
Signed-off-by: Arie Gershberg <redacted>
Signed-off-by: Prabhakar Kushwaha <redacted>
Signed-off-by: Omkar Kulkarni <redacted>
Signed-off-by: Michal Kalderon <mkalderon@marvell.com>
Signed-off-by: Ariel Elior <redacted>
Signed-off-by: Shai Malin <redacted>
---
drivers/nvme/host/tcp-offload.c | 138 +++++++++++++++++++++++++++++++-
drivers/nvme/host/tcp-offload.h | 1 +
2 files changed, 137 insertions(+), 2 deletions(-)
@@ -296,6 +314,28 @@ nvme_tcp_ofld_configure_io_queues(struct nvme_ctrl *nctrl, bool new)returnrc;}+staticvoidnvme_tcp_ofld_reconnect_or_remove(structnvme_ctrl*nctrl)+{+/* If we are resetting/deleting then do nothing */+if(nctrl->state!=NVME_CTRL_CONNECTING){+WARN_ON_ONCE(nctrl->state==NVME_CTRL_NEW||+nctrl->state==NVME_CTRL_LIVE);++return;+}++if(nvmf_should_reconnect(nctrl)){+dev_info(nctrl->device,"Reconnecting in %d seconds...\n",+nctrl->opts->reconnect_delay);+queue_delayed_work(nvme_wq,+&to_tcp_ofld_ctrl(nctrl)->connect_work,+nctrl->opts->reconnect_delay*HZ);+}else{+dev_info(nctrl->device,"Removing controller...\n");+nvme_delete_ctrl(nctrl);+}+}+staticintnvme_tcp_ofld_setup_ctrl(structnvme_ctrl*nctrl,boolnew){structnvmf_ctrl_options*opts=nctrl->opts;
@@ -407,10 +447,68 @@ nvme_tcp_ofld_teardown_io_queues(struct nvme_ctrl *nctrl, bool remove)/* Placeholder - teardown_io_queues */}+staticvoidnvme_tcp_ofld_reconnect_ctrl_work(structwork_struct*work)+{+structnvme_tcp_ofld_ctrl*ctrl=+container_of(to_delayed_work(work),+structnvme_tcp_ofld_ctrl,+connect_work);+structnvme_ctrl*nctrl=&ctrl->nctrl;++++nctrl->nr_reconnects;++if(ctrl->dev->ops->setup_ctrl(ctrl,false))+gotorequeue;++if(nvme_tcp_ofld_setup_ctrl(nctrl,false))+gotorelease_and_requeue;++dev_info(nctrl->device,"Successfully reconnected (%d attempt)\n",+nctrl->nr_reconnects);++nctrl->nr_reconnects=0;++return;++release_and_requeue:+ctrl->dev->ops->release_ctrl(ctrl);+requeue:+dev_info(nctrl->device,"Failed reconnect attempt %d\n",+nctrl->nr_reconnects);+nvme_tcp_ofld_reconnect_or_remove(nctrl);+}++staticvoidnvme_tcp_ofld_error_recovery_work(structwork_struct*work)+{+structnvme_tcp_ofld_ctrl*ctrl=+container_of(work,structnvme_tcp_ofld_ctrl,err_work);+structnvme_ctrl*nctrl=&ctrl->nctrl;++nvme_stop_keep_alive(nctrl);+nvme_tcp_ofld_teardown_io_queues(nctrl,false);+/* unquiesce to fail fast pending requests */+nvme_start_queues(nctrl);+nvme_tcp_ofld_teardown_admin_queue(nctrl,false);+blk_mq_unquiesce_queue(nctrl->admin_q);++if(!nvme_change_ctrl_state(nctrl,NVME_CTRL_CONNECTING)){+/* state change failure is ok if we started nctrl delete */+WARN_ON_ONCE(nctrl->state!=NVME_CTRL_DELETING&&+nctrl->state!=NVME_CTRL_DELETING_NOIO);++return;+}++nvme_tcp_ofld_reconnect_or_remove(nctrl);+}+staticvoidnvme_tcp_ofld_teardown_ctrl(structnvme_ctrl*nctrl,boolshutdown){-/* Placeholder - err_work and connect_work */+structnvme_tcp_ofld_ctrl*ctrl=to_tcp_ofld_ctrl(nctrl);++cancel_work_sync(&ctrl->err_work);+cancel_delayed_work_sync(&ctrl->connect_work);nvme_tcp_ofld_teardown_io_queues(nctrl,shutdown);blk_mq_quiesce_queue(nctrl->admin_q);if(shutdown)
@@ -425,6 +523,38 @@ static void nvme_tcp_ofld_delete_ctrl(struct nvme_ctrl *nctrl)nvme_tcp_ofld_teardown_ctrl(nctrl,true);}+staticvoidnvme_tcp_ofld_reset_ctrl_work(structwork_struct*work)+{+structnvme_ctrl*nctrl=+container_of(work,structnvme_ctrl,reset_work);+structnvme_tcp_ofld_ctrl*ctrl=to_tcp_ofld_ctrl(nctrl);++nvme_stop_ctrl(nctrl);+nvme_tcp_ofld_teardown_ctrl(nctrl,false);++if(!nvme_change_ctrl_state(nctrl,NVME_CTRL_CONNECTING)){+/* state change failure is ok if we started ctrl delete */+WARN_ON_ONCE(nctrl->state!=NVME_CTRL_DELETING&&+nctrl->state!=NVME_CTRL_DELETING_NOIO);++return;+}++if(ctrl->dev->ops->setup_ctrl(ctrl,false))+gotoout_fail;++if(nvme_tcp_ofld_setup_ctrl(nctrl,false))+gotorelease_ctrl;++return;++release_ctrl:+ctrl->dev->ops->release_ctrl(ctrl);+out_fail:+++nctrl->nr_reconnects;+nvme_tcp_ofld_reconnect_or_remove(nctrl);+}+staticintnvme_tcp_ofld_init_request(structblk_mq_tag_set*set,structrequest*rq,
From: Hannes Reinecke <hare@suse.de> Date: 2021-05-01 16:36:32
On 4/29/21 9:09 PM, Shai Malin wrote:
quoted hunk
From: Dean Balandin <redacted>
In this patch we implement queue level functionality.
The implementation is similar to the nvme-tcp module, the main
difference being that we call the vendor specific create_queue op which
creates the TCP connection, and NVMeTPC connection including
icreq+icresp negotiation.
Once create_queue returns successfully, we can move on to the fabrics
connect.
Acked-by: Igor Russkikh <redacted>
Signed-off-by: Dean Balandin <redacted>
Signed-off-by: Prabhakar Kushwaha <redacted>
Signed-off-by: Omkar Kulkarni <redacted>
Signed-off-by: Michal Kalderon <mkalderon@marvell.com>
Signed-off-by: Ariel Elior <redacted>
Signed-off-by: Shai Malin <redacted>
---
drivers/nvme/host/tcp-offload.c | 415 ++++++++++++++++++++++++++++++--
drivers/nvme/host/tcp-offload.h | 2 +-
2 files changed, 390 insertions(+), 27 deletions(-)
Really? Isn't this hardware-dependent?
I would have expected the hardware to impose some limitations here (# of
MSIx interrupts or something). Hmm?
+static void
+nvme_tcp_ofld_set_io_queues(struct nvme_ctrl *nctrl, unsigned int nr_io_queues)
+{
+ struct nvme_tcp_ofld_ctrl *ctrl = to_tcp_ofld_ctrl(nctrl);
+ struct nvmf_ctrl_options *opts = nctrl->opts;
+
+ if (opts->nr_write_queues && opts->nr_io_queues < nr_io_queues) {
+ /*
+ * separate read/write queues
+ * hand out dedicated default queues only after we have
+ * sufficient read queues.
+ */
+ ctrl->io_queues[HCTX_TYPE_READ] = opts->nr_io_queues;
+ nr_io_queues -= ctrl->io_queues[HCTX_TYPE_READ];
+ ctrl->io_queues[HCTX_TYPE_DEFAULT] =
+ min(opts->nr_write_queues, nr_io_queues);
+ nr_io_queues -= ctrl->io_queues[HCTX_TYPE_DEFAULT];
+ } else {
+ /*
+ * shared read/write queues
+ * either no write queues were requested, or we don't have
+ * sufficient queue count to have dedicated default queues.
+ */
+ ctrl->io_queues[HCTX_TYPE_DEFAULT] =
+ min(opts->nr_io_queues, nr_io_queues);
+ nr_io_queues -= ctrl->io_queues[HCTX_TYPE_DEFAULT];
+ }
+
+ if (opts->nr_poll_queues && nr_io_queues) {
+ /* map dedicated poll queues only if we have queues left */
+ ctrl->io_queues[HCTX_TYPE_POLL] =
+ min(opts->nr_poll_queues, nr_io_queues);
+ }
+}
+
Same here.
Poll queues only ever make sense of the hardware can serve specific
queue pairs without interrupts. Which again relates to the number of
interrupts, and the affinity of those.
Or isn't this a concern with your card?
+static void
+nvme_tcp_ofld_terminate_io_queues(struct nvme_ctrl *nctrl, int start_from)
+{
+ int i;
+
+ /* admin-q will be ignored because of the loop condition */
+ for (i = start_from; i >= 1; i--)
+ nvme_tcp_ofld_stop_queue(nctrl, i);
+}
+
Loop condition? Care to elaborate?
quoted hunk
+static int nvme_tcp_ofld_create_io_queues(struct nvme_ctrl *nctrl)
+{
+ struct nvme_tcp_ofld_ctrl *ctrl = to_tcp_ofld_ctrl(nctrl);
+ int i, rc;
+
+ for (i = 1; i < nctrl->queue_count; i++) {
+ rc = ctrl->dev->ops->create_queue(&ctrl->queues[i],
+ i, nctrl->sqsize + 1);
+ if (rc)
+ goto out_free_queues;
+
+ set_bit(NVME_TCP_OFLD_Q_ALLOCATED, &ctrl->queues[i].flags);
+ }
+
+ return 0;
+
+out_free_queues:
+ nvme_tcp_ofld_terminate_io_queues(nctrl, --i);
+
+ return rc;
+}
+
+static int nvme_tcp_ofld_alloc_io_queues(struct nvme_ctrl *nctrl)
+{
+ unsigned int nr_io_queues;
+ int rc;
+
+ nr_io_queues = nvme_tcp_ofld_nr_io_queues(nctrl);
+ rc = nvme_set_queue_count(nctrl, &nr_io_queues);
+ if (rc)
+ return rc;
+
+ nctrl->queue_count = nr_io_queues + 1;
+ if (nctrl->queue_count < 2) {
+ dev_err(nctrl->device,
+ "unable to set any I/O queues\n");
+
+ return -ENOMEM;
+ }
+
+ dev_info(nctrl->device, "creating %d I/O queues.\n", nr_io_queues);
+ nvme_tcp_ofld_set_io_queues(nctrl, nr_io_queues);
+
+ return nvme_tcp_ofld_create_io_queues(nctrl);
+}
+
+static int nvme_tcp_ofld_start_io_queues(struct nvme_ctrl *nctrl)
+{
+ int i, rc = 0;
+
+ for (i = 1; i < nctrl->queue_count; i++) {
+ rc = nvme_tcp_ofld_start_queue(nctrl, i);
+ if (rc)
+ goto terminate_queues;
+ }
+
+ return 0;
+
+terminate_queues:
+ nvme_tcp_ofld_terminate_io_queues(nctrl, --i);
return rc;
}
@@ -258,9 +468,10 @@ static int nvme_tcp_ofld_configure_admin_queue(struct nvme_ctrl *nctrl, static int nvme_tcp_ofld_configure_io_queues(struct nvme_ctrl *nctrl, bool new) {- int rc;+ int rc = nvme_tcp_ofld_alloc_io_queues(nctrl);- /* Placeholder - alloc_io_queues */+ if (rc)+ return rc; if (new) { nctrl->tagset = nvme_tcp_ofld_alloc_tagset(nctrl, false);
From: Hannes Reinecke <hare@suse.de> Date: 2021-05-01 16:39:20
On 4/29/21 9:09 PM, Shai Malin wrote:
From: Dean Balandin <redacted>
In this patch, we present the IO level functionality.
The nvme-tcp-offload shall work on the IO-level, meaning the
nvme-tcp-offload ULP module shall pass the request to the nvme-tcp-offload
vendor driver and shall expect for the request compilation.
Request compilation? Not request completion?
quoted hunk
No additional handling is needed in between, this design will reduce the
CPU utilization as we will describe below.
The nvme-tcp-offload vendor driver shall register to nvme-tcp-offload ULP
with the following IO-path ops:
- init_req
- send_req - in order to pass the request to the handling of the offload
driver that shall pass it to the vendor specific device
- poll_queue
The vendor driver will manage the context from which the request will be
executed and the request aggregations.
Once the IO completed, the nvme-tcp-offload vendor driver shall call
command.done() that shall invoke the nvme-tcp-offload ULP layer for
completing the request.
This patch also contains initial definition of nvme_tcp_ofld_queue_rq().
Acked-by: Igor Russkikh <redacted>
Signed-off-by: Dean Balandin <redacted>
Signed-off-by: Prabhakar Kushwaha <redacted>
Signed-off-by: Omkar Kulkarni <redacted>
Signed-off-by: Michal Kalderon <mkalderon@marvell.com>
Signed-off-by: Ariel Elior <redacted>
Signed-off-by: Shai Malin <redacted>
---
drivers/nvme/host/tcp-offload.c | 95 ++++++++++++++++++++++++++++++---
1 file changed, 87 insertions(+), 8 deletions(-)
@@ -1024,6 +1060,51 @@ static int nvme_tcp_ofld_poll(struct blk_mq_hw_ctx *hctx)returnops->poll_queue(queue);}+staticvoidnvme_tcp_ofld_complete_timed_out(structrequest*rq)+{+structnvme_tcp_ofld_req*req=blk_mq_rq_to_pdu(rq);+structnvme_ctrl*nctrl=&req->queue->ctrl->nctrl;++nvme_tcp_ofld_stop_queue(nctrl,nvme_tcp_ofld_qid(req->queue));+if(blk_mq_request_started(rq)&&!blk_mq_request_completed(rq)){+nvme_req(rq)->status=NVME_SC_HOST_ABORTED_CMD;+blk_mq_complete_request(rq);+}+}++staticenumblk_eh_timer_returnnvme_tcp_ofld_timeout(structrequest*rq,boolreserved)+{+structnvme_tcp_ofld_req*req=blk_mq_rq_to_pdu(rq);+structnvme_tcp_ofld_ctrl*ctrl=req->queue->ctrl;++dev_warn(ctrl->nctrl.device,+"queue %d: timeout request %#x type %d\n",+nvme_tcp_ofld_qid(req->queue),rq->tag,req->nvme_cmd.common.opcode);++if(ctrl->nctrl.state!=NVME_CTRL_LIVE){+/*+*Ifweareresetting,connectingordeletingweshould+*completeimmediatelybecausewemayblockcontroller+*teardownorsetupsequence+*-ctrldisable/shutdownfabricsrequests+*-connectrequests+*-initializationadminrequests+*-I/Orequeststhatenteredafterunquiescingand+*thecontrollerstoppedresponding+*+*Allotherrequestsshouldbecancelledbytheerror+*recoverywork,soit'sfinethatwefailithere.+*/+nvme_tcp_ofld_complete_timed_out(rq);++returnBLK_EH_DONE;+}
And this particular error code has been causing _so_ _many_ issues
during testing, that I'd rather get rid of it altogether.
But probably not your fault, your just copying what tcp and rdma is doing.
From: Hannes Reinecke <hare@suse.de> Date: 2021-05-01 16:47:25
On 4/29/21 9:08 PM, Shai Malin wrote:
With the goal of enabling a generic infrastructure that allows NVMe/TCP
offload devices like NICs to seamlessly plug into the NVMe-oF stack, this
patch series introduces the nvme-tcp-offload ULP host layer, which will
be a new transport type called "tcp-offload" and will serve as an
abstraction layer to work with vendor specific nvme-tcp offload drivers.
NVMeTCP offload is a full offload of the NVMeTCP protocol, this includes
both the TCP level and the NVMeTCP level.
The nvme-tcp-offload transport can co-exist with the existing tcp and
other transports. The tcp offload was designed so that stack changes are
kept to a bare minimum: only registering new transports.
All other APIs, ops etc. are identical to the regular tcp transport.
Representing the TCP offload as a new transport allows clear and manageable
differentiation between the connections which should use the offload path
and those that are not offloaded (even on the same device).
The nvme-tcp-offload layers and API compared to nvme-tcp and nvme-rdma:
* NVMe layer: *
[ nvme/nvme-fabrics/blk-mq ]
|
(nvme API and blk-mq API)
|
|
* Vendor agnostic transport layer: *
[ nvme-rdma ] [ nvme-tcp ] [ nvme-tcp-offload ]
| | |
(Verbs)
| | |
| (Socket)
| | |
| | (nvme-tcp-offload API)
| | |
| | |
* Vendor Specific Driver: *
| | |
[ qedr ]
| |
[ qede ]
|
[ qedn ]
Performance:
============
With this implementation on top of the Marvell qedn driver (using the
Marvell FastLinQ NIC), we were able to demonstrate the following CPU
utilization improvement:
On AMD EPYC 7402, 2.80GHz, 28 cores:
- For 16K queued read IOs, 16jobs, 4qd (50Gbps line rate):
Improved the CPU utilization from 15.1% with NVMeTCP SW to 4.7% with
NVMeTCP offload.
On Intel(R) Xeon(R) Gold 5122 CPU, 3.60GHz, 16 cores:
- For 512K queued read IOs, 16jobs, 4qd (25Gbps line rate):
Improved the CPU utilization from 16.3% with NVMeTCP SW to 1.1% with
NVMeTCP offload.
In addition, we were able to demonstrate the following latency improvement:
- For 200K read IOPS (16 jobs, 16 qd, with fio rate limiter):
Improved the average latency from 105 usec with NVMeTCP SW to 39 usec
with NVMeTCP offload.
Improved the 99.99 tail latency from 570 usec with NVMeTCP SW to 91 usec
with NVMeTCP offload.
The end-to-end offload latency was measured from fio while running against
back end of null device.
Upstream plan:
==============
Following this RFC, the series will be sent in a modular way so that changes
in each part will not impact the previous part.
- Part 1 (Patches 1-7):
The qed infrastructure, will be sent to 'netdev@vger.kernel.org'.
- Part 2 (Patch 8-15):
The nvme-tcp-offload patches, will be sent to
'linux-nvme@lists.infradead.org'.
- Part 3 (Packet 16-27):
The qedn patches, will be sent to 'linux-nvme@lists.infradead.org'.
Queue Initialization Design:
============================
The nvme-tcp-offload ULP module shall register with the existing
nvmf_transport_ops (.name = "tcp_offload"), nvme_ctrl_ops and blk_mq_ops.
The nvme-tcp-offload vendor driver shall register to nvme-tcp-offload ULP
with the following ops:
- claim_dev() - in order to resolve the route to the target according to
the paired net_dev.
- create_queue() - in order to create offloaded nvme-tcp queue.
The nvme-tcp-offload ULP module shall manage all the controller level
functionalities, call claim_dev and based on the return values shall call
the relevant module create_queue in order to create the admin queue and
the IO queues.
IO-path Design:
===============
The nvme-tcp-offload shall work at the IO-level - the nvme-tcp-offload
ULP module shall pass the request (the IO) to the nvme-tcp-offload vendor
driver and later, the nvme-tcp-offload vendor driver returns the request
completion (the IO completion).
No additional handling is needed in between; this design will reduce the
CPU utilization as we will describe below.
The nvme-tcp-offload vendor driver shall register to nvme-tcp-offload ULP
with the following IO-path ops:
- init_req()
- send_req() - in order to pass the request to the handling of the
offload driver that shall pass it to the vendor specific device.
- poll_queue()
Once the IO completes, the nvme-tcp-offload vendor driver shall call
command.done() that will invoke the nvme-tcp-offload ULP layer to
complete the request.
TCP events:
===========
The Marvell FastLinQ NIC HW engine handle all the TCP re-transmissions
and OOO events.
Teardown and errors:
====================
In case of NVMeTCP queue error the nvme-tcp-offload vendor driver shall
call the nvme_tcp_ofld_report_queue_err.
The nvme-tcp-offload vendor driver shall register to nvme-tcp-offload ULP
with the following teardown ops:
- drain_queue()
- destroy_queue()
The Marvell FastLinQ NIC HW engine:
====================================
The Marvell NIC HW engine is capable of offloading the entire TCP/IP
stack and managing up to 64K connections per PF, already implemented and
upstream use cases for this include iWARP (by the Marvell qedr driver)
and iSCSI (by the Marvell qedi driver).
In addition, the Marvell NIC HW engine offloads the NVMeTCP queue layer
and is able to manage the IO level also in case of TCP re-transmissions
and OOO events.
The HW engine enables direct data placement (including the data digest CRC
calculation and validation) and direct data transmission (including data
digest CRC calculation).
The Marvell qedn driver:
========================
The new driver will be added under "drivers/nvme/hw" and will be enabled
by the Kconfig "Marvell NVM Express over Fabrics TCP offload".
As part of the qedn init, the driver will register as a pci device driver
and will work with the Marvell fastlinQ NIC.
As part of the probe, the driver will register to the nvme_tcp_offload
(ULP) and to the qed module (qed_nvmetcp_ops) - similar to other
"qed_*_ops" which are used by the qede, qedr, qedf and qedi device
drivers.
QEDN Future work:
=================
- Support extended HW resources.
- Digest support.
- Devlink support for device configuration and TCP offload configurations.
- Statistics
Long term future work:
======================
- The nvme-tcp-offload ULP target abstraction layer.
- The Marvell nvme-tcp-offload "qednt" target driver.
Changes since RFC v1:
=====================
- Fix nvme_tcp_ofld_ops return values.
- Remove NVMF_TRTYPE_TCP_OFFLOAD.
- Add nvme_tcp_ofld_poll() implementation.
- Fix nvme_tcp_ofld_queue_rq() to check map_sg() and send_req() return
values.
Changes since RFC v2:
=====================
- Add qedn - Marvell's NVMeTCP HW offload vendor driver init and probe
(patches 8-11).
- Fixes in controller and queue level (patches 3-6).
Changes since RFC v3:
=====================
- Add the full implementation of the nvme-tcp-offload layer including the
new ops: setup_ctrl(), release_ctrl(), commit_rqs() and new flows (ASYNC
and timeout).
- Add nvme-tcp-offload device maximums: max_hw_sectors, max_segments.
- Add nvme-tcp-offload layer design and optimization changes.
- Add the qedn full implementation for the conn level, IO path and error
handling.
- Add qed support for the new AHP HW.
Arie Gershberg (3):
nvme-fabrics: Move NVMF_ALLOWED_OPTS and NVMF_REQUIRED_OPTS
definitions
nvme-tcp-offload: Add controller level implementation
nvme-tcp-offload: Add controller level error recovery implementation
Dean Balandin (3):
nvme-tcp-offload: Add device scan implementation
nvme-tcp-offload: Add queue level implementation
nvme-tcp-offload: Add IO level implementation
Nikolay Assa (2):
qed: Add IP services APIs support
qedn: Add qedn_claim_dev API support
Omkar Kulkarni (1):
qed: Add qed-NVMeTCP personality
Prabhakar Kushwaha (6):
qed: Add support of HW filter block
qedn: Add connection-level slowpath functionality
qedn: Add support of configuring HW filter block
qedn: Add support of Task and SGL
qedn: Add support of NVME ICReq & ICResp
qedn: Add support of ASYNC
Shai Malin (12):
qed: Add NVMeTCP Offload PF Level FW and HW HSI
qed: Add NVMeTCP Offload Connection Level FW and HW HSI
qed: Add NVMeTCP Offload IO Level FW and HW HSI
qed: Add NVMeTCP Offload IO Level FW Initializations
nvme-tcp-offload: Add nvme-tcp-offload - NVMeTCP HW offload ULP
nvme-tcp-offload: Add Timeout and ASYNC Support
qedn: Add qedn - Marvell's NVMeTCP HW offload vendor driver
qedn: Add qedn probe
qedn: Add IRQ and fast-path resources initializations
qedn: Add IO level nvme_req and fw_cq workqueues
qedn: Add IO level fastpath functionality
qedn: Add Connection and IO level recovery flows
MAINTAINERS | 10 +
drivers/net/ethernet/qlogic/Kconfig | 3 +
drivers/net/ethernet/qlogic/qed/Makefile | 5 +
drivers/net/ethernet/qlogic/qed/qed.h | 16 +
drivers/net/ethernet/qlogic/qed/qed_cxt.c | 32 +
drivers/net/ethernet/qlogic/qed/qed_cxt.h | 1 +
drivers/net/ethernet/qlogic/qed/qed_dev.c | 151 +-
drivers/net/ethernet/qlogic/qed/qed_hsi.h | 4 +-
drivers/net/ethernet/qlogic/qed/qed_ll2.c | 31 +-
drivers/net/ethernet/qlogic/qed/qed_mcp.c | 3 +
drivers/net/ethernet/qlogic/qed/qed_mng_tlv.c | 3 +-
drivers/net/ethernet/qlogic/qed/qed_nvmetcp.c | 868 +++++++++++
drivers/net/ethernet/qlogic/qed/qed_nvmetcp.h | 114 ++
.../qlogic/qed/qed_nvmetcp_fw_funcs.c | 372 +++++
.../qlogic/qed/qed_nvmetcp_fw_funcs.h | 43 +
.../qlogic/qed/qed_nvmetcp_ip_services.c | 239 +++
drivers/net/ethernet/qlogic/qed/qed_ooo.c | 5 +-
drivers/net/ethernet/qlogic/qed/qed_sp.h | 5 +
.../net/ethernet/qlogic/qed/qed_sp_commands.c | 1 +
drivers/nvme/Kconfig | 1 +
drivers/nvme/Makefile | 1 +
drivers/nvme/host/Kconfig | 16 +
drivers/nvme/host/Makefile | 3 +
drivers/nvme/host/fabrics.c | 7 -
drivers/nvme/host/fabrics.h | 7 +
drivers/nvme/host/tcp-offload.c | 1330 +++++++++++++++++
drivers/nvme/host/tcp-offload.h | 209 +++
drivers/nvme/hw/Kconfig | 9 +
drivers/nvme/hw/Makefile | 3 +
drivers/nvme/hw/qedn/Makefile | 4 +
drivers/nvme/hw/qedn/qedn.h | 435 ++++++
drivers/nvme/hw/qedn/qedn_conn.c | 999 +++++++++++++
drivers/nvme/hw/qedn/qedn_main.c | 1153 ++++++++++++++
drivers/nvme/hw/qedn/qedn_task.c | 977 ++++++++++++
include/linux/qed/common_hsi.h | 1 +
include/linux/qed/nvmetcp_common.h | 616 ++++++++
include/linux/qed/qed_if.h | 22 +
include/linux/qed/qed_nvmetcp_if.h | 244 +++
.../linux/qed/qed_nvmetcp_ip_services_if.h | 29 +
39 files changed, 7947 insertions(+), 25 deletions(-)
create mode 100644 drivers/net/ethernet/qlogic/qed/qed_nvmetcp.c
create mode 100644 drivers/net/ethernet/qlogic/qed/qed_nvmetcp.h
create mode 100644 drivers/net/ethernet/qlogic/qed/qed_nvmetcp_fw_funcs.c
create mode 100644 drivers/net/ethernet/qlogic/qed/qed_nvmetcp_fw_funcs.h
create mode 100644 drivers/net/ethernet/qlogic/qed/qed_nvmetcp_ip_services.c
create mode 100644 drivers/nvme/host/tcp-offload.c
create mode 100644 drivers/nvme/host/tcp-offload.h
create mode 100644 drivers/nvme/hw/Kconfig
create mode 100644 drivers/nvme/hw/Makefile
create mode 100644 drivers/nvme/hw/qedn/Makefile
create mode 100644 drivers/nvme/hw/qedn/qedn.h
create mode 100644 drivers/nvme/hw/qedn/qedn_conn.c
create mode 100644 drivers/nvme/hw/qedn/qedn_main.c
create mode 100644 drivers/nvme/hw/qedn/qedn_task.c
create mode 100644 include/linux/qed/nvmetcp_common.h
create mode 100644 include/linux/qed/qed_nvmetcp_if.h
create mode 100644 include/linux/qed/qed_nvmetcp_ip_services_if.h
I would structure this patchset slightly different, in putting the
NVMe-oF implementation at the start of the patchset; this will be where
you get most of the comment, and any change there will potentially
reflect back on the driver implementation, too.
Something to consider for the next round.
Cheers,
Hannes
--
Dr. Hannes Reinecke Kernel Storage Architect
hare@suse.de +49 911 74053 688
SUSE Software Solutions GmbH, Maxfeldstr. 5, 90409 Nürnberg
HRB 36809 (AG Nürnberg), Geschäftsführer: Felix Imendörffer
@@ -0,0 +1,282 @@+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)+/* Copyright 2021 Marvell. All rights reserved. */++#include<linux/types.h>+#include<asm/byteorder.h>+#include<asm/param.h>+#include<linux/delay.h>+#include<linux/dma-mapping.h>+#include<linux/etherdevice.h>+#include<linux/kernel.h>+#include<linux/log2.h>+#include<linux/module.h>+#include<linux/pci.h>+#include<linux/stddef.h>+#include<linux/string.h>+#include<linux/errno.h>+#include<linux/list.h>+#include<linux/qed/qed_nvmetcp_if.h>+#include"qed.h"+#include"qed_cxt.h"+#include"qed_dev_api.h"+#include"qed_hsi.h"+#include"qed_hw.h"+#include"qed_int.h"+#include"qed_nvmetcp.h"+#include"qed_ll2.h"+#include"qed_mcp.h"+#include"qed_sp.h"+#include"qed_reg_addr.h"++staticintqed_nvmetcp_async_event(structqed_hwfn*p_hwfn,u8fw_event_code,+u16echo,unionevent_ring_data*data,+u8fw_return_code)+{+if(p_hwfn->p_nvmetcp_info->event_cb){+structqed_nvmetcp_info*p_nvmetcp=p_hwfn->p_nvmetcp_info;++returnp_nvmetcp->event_cb(p_nvmetcp->event_context,+fw_event_code,data);+}else{+DP_NOTICE(p_hwfn,"nvmetcp async completion is not set\n");++return-EINVAL;+}+}++staticintqed_sp_nvmetcp_func_start(structqed_hwfn*p_hwfn,+enumspq_modecomp_mode,+structqed_spq_comp_cb*p_comp_addr,+void*event_context,+nvmetcp_event_cb_tasync_event_cb)+{+structnvmetcp_init_ramrod_params*p_ramrod=NULL;+structqed_nvmetcp_pf_params*p_params=NULL;+structscsi_init_func_queues*p_queue=NULL;+structnvmetcp_spe_func_init*p_init=NULL;+structqed_sp_init_datainit_data={};+structqed_spq_entry*p_ent=NULL;+intrc=0;+u16val;+u8i;++/* Get SPQ entry */+init_data.cid=qed_spq_get_cid(p_hwfn);+init_data.opaque_fid=p_hwfn->hw_info.opaque_fid;+init_data.comp_mode=comp_mode;+init_data.p_comp_data=p_comp_addr;++rc=qed_sp_init_request(p_hwfn,&p_ent,+NVMETCP_RAMROD_CMD_ID_INIT_FUNC,+PROTOCOLID_NVMETCP,&init_data);+if(rc)+returnrc;++p_ramrod=&p_ent->ramrod.nvmetcp_init;+p_init=&p_ramrod->nvmetcp_init_spe;+p_params=&p_hwfn->pf_params.nvmetcp_pf_params;+p_queue=&p_init->q_params;++p_init->num_sq_pages_in_ring=p_params->num_sq_pages_in_ring;+p_init->num_r2tq_pages_in_ring=p_params->num_r2tq_pages_in_ring;+p_init->num_uhq_pages_in_ring=p_params->num_uhq_pages_in_ring;+p_init->ll2_rx_queue_id=RESC_START(p_hwfn,QED_LL2_RAM_QUEUE)++p_params->ll2_ooo_queue_id;++SET_FIELD(p_init->flags,NVMETCP_SPE_FUNC_INIT_NVMETCP_MODE,1);++p_init->func_params.log_page_size=ilog2(PAGE_SIZE);+p_init->func_params.num_tasks=cpu_to_le16(p_params->num_tasks);+p_init->debug_flags=p_params->debug_mode;++DMA_REGPAIR_LE(p_queue->glbl_q_params_addr,+p_params->glbl_q_params_addr);++p_queue->cq_num_entries=cpu_to_le16(QED_NVMETCP_FW_CQ_SIZE);+p_queue->num_queues=p_params->num_queues;+val=RESC_START(p_hwfn,QED_CMDQS_CQS);+p_queue->queue_relative_offset=cpu_to_le16((u16)val);+p_queue->cq_sb_pi=p_params->gl_rq_pi;++for(i=0;i<p_params->num_queues;i++){+val=qed_get_igu_sb_id(p_hwfn,i);+p_queue->cq_cmdq_sb_num_arr[i]=cpu_to_le16(val);+}++SET_FIELD(p_queue->q_validity,+SCSI_INIT_FUNC_QUEUES_CMD_VALID,0);+p_queue->cmdq_num_entries=0;+p_queue->bdq_resource_id=(u8)RESC_START(p_hwfn,QED_BDQ);++/* p_ramrod->tcp_init.min_rto = cpu_to_le16(p_params->min_rto); */+p_ramrod->tcp_init.two_msl_timer=cpu_to_le32(QED_TCP_TWO_MSL_TIMER);+p_ramrod->tcp_init.tx_sws_timer=cpu_to_le16(QED_TCP_SWS_TIMER);+p_init->half_way_close_timeout=cpu_to_le16(QED_TCP_HALF_WAY_CLOSE_TIMEOUT);+p_ramrod->tcp_init.max_fin_rt=QED_TCP_MAX_FIN_RT;++SET_FIELD(p_ramrod->nvmetcp_init_spe.params,+NVMETCP_SPE_FUNC_INIT_MAX_SYN_RT,QED_TCP_MAX_FIN_RT);++p_hwfn->p_nvmetcp_info->event_context=event_context;+p_hwfn->p_nvmetcp_info->event_cb=async_event_cb;++qed_spq_register_async_cb(p_hwfn,PROTOCOLID_NVMETCP,+qed_nvmetcp_async_event);++returnqed_spq_post(p_hwfn,p_ent,NULL);+}++staticintqed_sp_nvmetcp_func_stop(structqed_hwfn*p_hwfn,+enumspq_modecomp_mode,+structqed_spq_comp_cb*p_comp_addr)+{+structqed_spq_entry*p_ent=NULL;+structqed_sp_init_datainit_data;+intrc;++/* Get SPQ entry */+memset(&init_data,0,sizeof(init_data));+init_data.cid=qed_spq_get_cid(p_hwfn);+init_data.opaque_fid=p_hwfn->hw_info.opaque_fid;+init_data.comp_mode=comp_mode;+init_data.p_comp_data=p_comp_addr;++rc=qed_sp_init_request(p_hwfn,&p_ent,+NVMETCP_RAMROD_CMD_ID_DESTROY_FUNC,+PROTOCOLID_NVMETCP,&init_data);+if(rc)+returnrc;++rc=qed_spq_post(p_hwfn,p_ent,NULL);++qed_spq_unregister_async_cb(p_hwfn,PROTOCOLID_NVMETCP);++returnrc;+}++staticintqed_fill_nvmetcp_dev_info(structqed_dev*cdev,+structqed_dev_nvmetcp_info*info)+{+structqed_hwfn*hwfn=QED_AFFIN_HWFN(cdev);+intrc;++memset(info,0,sizeof(*info));+rc=qed_fill_dev_info(cdev,&info->common);++info->port_id=MFW_PORT(hwfn);+info->num_cqs=FEAT_NUM(hwfn,QED_NVMETCP_CQ);++returnrc;+}++staticvoidqed_register_nvmetcp_ops(structqed_dev*cdev,+structqed_nvmetcp_cb_ops*ops,+void*cookie)+{+cdev->protocol_ops.nvmetcp=ops;+cdev->ops_cookie=cookie;+}++staticintqed_nvmetcp_stop(structqed_dev*cdev)+{+intrc;++if(!(cdev->flags&QED_FLAG_STORAGE_STARTED)){+DP_NOTICE(cdev,"nvmetcp already stopped\n");++return0;+}++if(!hash_empty(cdev->connections)){+DP_NOTICE(cdev,+"Can't stop nvmetcp - not all connections were returned\n");++return-EINVAL;+}++/* Stop the nvmetcp */+rc=qed_sp_nvmetcp_func_stop(QED_AFFIN_HWFN(cdev),QED_SPQ_MODE_EBLOCK,+NULL);+cdev->flags&=~QED_FLAG_STORAGE_STARTED;++returnrc;+}++staticintqed_nvmetcp_start(structqed_dev*cdev,+structqed_nvmetcp_tid*tasks,+void*event_context,+nvmetcp_event_cb_tasync_event_cb)+{+structqed_tid_mem*tid_info;+intrc;++if(cdev->flags&QED_FLAG_STORAGE_STARTED){+DP_NOTICE(cdev,"nvmetcp already started;\n");++return0;+}++rc=qed_sp_nvmetcp_func_start(QED_AFFIN_HWFN(cdev),+QED_SPQ_MODE_EBLOCK,NULL,+event_context,async_event_cb);+if(rc){+DP_NOTICE(cdev,"Failed to start nvmetcp\n");++returnrc;+}++cdev->flags|=QED_FLAG_STORAGE_STARTED;+hash_init(cdev->connections);++if(!tasks)+return0;++tid_info=kzalloc(sizeof(*tid_info),GFP_KERNEL);++if(!tid_info){+qed_nvmetcp_stop(cdev);++return-ENOMEM;+}++rc=qed_cxt_get_tid_mem_info(QED_AFFIN_HWFN(cdev),tid_info);+if(rc){+DP_NOTICE(cdev,"Failed to gather task information\n");+qed_nvmetcp_stop(cdev);+kfree(tid_info);++returnrc;+}++/* Fill task information */+tasks->size=tid_info->tid_size;+tasks->num_tids_per_block=tid_info->num_tids_per_block;+memcpy(tasks->blocks,tid_info->blocks,+MAX_TID_BLOCKS_NVMETCP*sizeof(u8*));++kfree(tid_info);++return0;+}++staticconststructqed_nvmetcp_opsqed_nvmetcp_ops_pass={+.common=&qed_common_ops_pass,+.ll2=&qed_ll2_ops_pass,+.fill_dev_info=&qed_fill_nvmetcp_dev_info,+.register_ops=&qed_register_nvmetcp_ops,+.start=&qed_nvmetcp_start,+.stop=&qed_nvmetcp_stop,++/* Placeholder - Connection level ops */+};++conststructqed_nvmetcp_ops*qed_get_nvmetcp_ops(void)+{+return&qed_nvmetcp_ops_pass;+}+EXPORT_SYMBOL(qed_get_nvmetcp_ops);++voidqed_put_nvmetcp_ops(void)+{+}+EXPORT_SYMBOL(qed_put_nvmetcp_ops);
@@ -542,6 +542,26 @@ struct qed_iscsi_pf_params {u8bdq_pbl_num_entries[3];};+structqed_nvmetcp_pf_params{+u64glbl_q_params_addr;+u16cq_num_entries;++u16num_cons;+u16num_tasks;++u8num_sq_pages_in_ring;+u8num_r2tq_pages_in_ring;+u8num_uhq_pages_in_ring;++u8num_queues;+u8gl_rq_pi;+u8gl_cmd_pi;+u8debug_mode;+u8ll2_ooo_queue_id;++u16min_rto;+};+structqed_rdma_pf_params{/* Supplied to QED during resource allocation (may affect the ILT and*thedoorbellBAR).
@@ -0,0 +1,72 @@+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */+/* Copyright 2021 Marvell. All rights reserved. */++#ifndef _QED_NVMETCP_IF_H+#define _QED_NVMETCP_IF_H+#include<linux/types.h>+#include<linux/qed/qed_if.h>++#define QED_NVMETCP_MAX_IO_SIZE 0x800000++typedefint(*nvmetcp_event_cb_t)(void*context,+u8fw_event_code,void*fw_handle);++structqed_dev_nvmetcp_info{+structqed_dev_infocommon;++u8port_id;/* Physical port */+u8num_cqs;+};++#define MAX_TID_BLOCKS_NVMETCP (512)+structqed_nvmetcp_tid{+u32size;/* In bytes per task */+u32num_tids_per_block;+u8*blocks[MAX_TID_BLOCKS_NVMETCP];+};++structqed_nvmetcp_cb_ops{+structqed_common_cb_opscommon;+};++/**+*structqed_nvmetcp_ops-qedNVMeTCPoperations.+*@common:commonoperationspointer+*@ll2:lightL2operationspointer+*@fill_dev_info:fillsNVMeTCPspecificinformation+*@paramcdev+*@paraminfo+*@return0onsuccess,otherwiseerrorvalue.+*@register_ops:registernvmetcpoperations+*@paramcdev+*@paramops-specifiedusingqed_nvmetcp_cb_ops+*@paramcookie-driverprivate+*@start:nvmetcpinFW+*@paramcdev+*@paramtasks-qedwillfillinformationabouttasks+*return0onsuccess,otherwiseerrorvalue.+*@stop:nvmetcpinFW+*@paramcdev+*return0onsuccess,otherwiseerrorvalue.+*/+structqed_nvmetcp_ops{+conststructqed_common_ops*common;++conststructqed_ll2_ops*ll2;++int(*fill_dev_info)(structqed_dev*cdev,+structqed_dev_nvmetcp_info*info);++void(*register_ops)(structqed_dev*cdev,+structqed_nvmetcp_cb_ops*ops,void*cookie);++int(*start)(structqed_dev*cdev,+structqed_nvmetcp_tid*tasks,+void*event_context,nvmetcp_event_cb_tasync_event_cb);++int(*stop)(structqed_dev*cdev);+};++conststructqed_nvmetcp_ops*qed_get_nvmetcp_ops(void);+voidqed_put_nvmetcp_ops(void);+#endif
As mentioned, please rearrange the patchset to have the NVMe-TCP patches
first, then the driver specific bits.
Cheers,
Hannes
--
Dr. Hannes Reinecke Kernel Storage Architect
hare@suse.de +49 911 74053 688
SUSE Software Solutions GmbH, Maxfeldstr. 5, 90409 Nürnberg
HRB 36809 (AG Nürnberg), Geschäftsführer: Felix Imendörffer
@@ -259,6 +259,578 @@ static int qed_nvmetcp_start(struct qed_dev *cdev,return0;}+staticstructqed_hash_nvmetcp_con*qed_nvmetcp_get_hash(structqed_dev*cdev,+u32handle)+{+structqed_hash_nvmetcp_con*hash_con=NULL;++if(!(cdev->flags&QED_FLAG_STORAGE_STARTED))+returnNULL;++hash_for_each_possible(cdev->connections,hash_con,node,handle){+if(hash_con->con->icid==handle)+break;+}++if(!hash_con||hash_con->con->icid!=handle)+returnNULL;++returnhash_con;+}++staticintqed_sp_nvmetcp_conn_offload(structqed_hwfn*p_hwfn,+structqed_nvmetcp_conn*p_conn,+enumspq_modecomp_mode,+structqed_spq_comp_cb*p_comp_addr)+{+structnvmetcp_spe_conn_offload*p_ramrod=NULL;+structtcp_offload_params_opt2*p_tcp2=NULL;+structqed_sp_init_datainit_data={0};+structqed_spq_entry*p_ent=NULL;+dma_addr_tr2tq_pbl_addr;+dma_addr_txhq_pbl_addr;+dma_addr_tuhq_pbl_addr;+u16physical_q;+intrc=0;+u32dval;+u8i;++/* Get SPQ entry */+init_data.cid=p_conn->icid;+init_data.opaque_fid=p_hwfn->hw_info.opaque_fid;+init_data.comp_mode=comp_mode;+init_data.p_comp_data=p_comp_addr;++rc=qed_sp_init_request(p_hwfn,&p_ent,+NVMETCP_RAMROD_CMD_ID_OFFLOAD_CONN,+PROTOCOLID_NVMETCP,&init_data);+if(rc)+returnrc;++p_ramrod=&p_ent->ramrod.nvmetcp_conn_offload;++/* Transmission PQ is the first of the PF */+physical_q=qed_get_cm_pq_idx(p_hwfn,PQ_FLAGS_OFLD);+p_conn->physical_q0=cpu_to_le16(physical_q);+p_ramrod->nvmetcp.physical_q0=cpu_to_le16(physical_q);++/* nvmetcp Pure-ACK PQ */+physical_q=qed_get_cm_pq_idx(p_hwfn,PQ_FLAGS_ACK);+p_conn->physical_q1=cpu_to_le16(physical_q);+p_ramrod->nvmetcp.physical_q1=cpu_to_le16(physical_q);++p_ramrod->conn_id=cpu_to_le16(p_conn->conn_id);++DMA_REGPAIR_LE(p_ramrod->nvmetcp.sq_pbl_addr,p_conn->sq_pbl_addr);++r2tq_pbl_addr=qed_chain_get_pbl_phys(&p_conn->r2tq);+DMA_REGPAIR_LE(p_ramrod->nvmetcp.r2tq_pbl_addr,r2tq_pbl_addr);++xhq_pbl_addr=qed_chain_get_pbl_phys(&p_conn->xhq);+DMA_REGPAIR_LE(p_ramrod->nvmetcp.xhq_pbl_addr,xhq_pbl_addr);++uhq_pbl_addr=qed_chain_get_pbl_phys(&p_conn->uhq);+DMA_REGPAIR_LE(p_ramrod->nvmetcp.uhq_pbl_addr,uhq_pbl_addr);++p_ramrod->nvmetcp.flags=p_conn->offl_flags;+p_ramrod->nvmetcp.default_cq=p_conn->default_cq;+p_ramrod->nvmetcp.initial_ack=0;++DMA_REGPAIR_LE(p_ramrod->nvmetcp.nvmetcp.cccid_itid_table_addr,+p_conn->nvmetcp_cccid_itid_table_addr);+p_ramrod->nvmetcp.nvmetcp.cccid_max_range=+cpu_to_le16(p_conn->nvmetcp_cccid_max_range);++p_tcp2=&p_ramrod->tcp;++qed_set_fw_mac_addr(&p_tcp2->remote_mac_addr_hi,+&p_tcp2->remote_mac_addr_mid,+&p_tcp2->remote_mac_addr_lo,p_conn->remote_mac);+qed_set_fw_mac_addr(&p_tcp2->local_mac_addr_hi,+&p_tcp2->local_mac_addr_mid,+&p_tcp2->local_mac_addr_lo,p_conn->local_mac);++p_tcp2->vlan_id=cpu_to_le16(p_conn->vlan_id);+p_tcp2->flags=cpu_to_le16(p_conn->tcp_flags);++p_tcp2->ip_version=p_conn->ip_version;+for(i=0;i<4;i++){+dval=p_conn->remote_ip[i];+p_tcp2->remote_ip[i]=cpu_to_le32(dval);+dval=p_conn->local_ip[i];+p_tcp2->local_ip[i]=cpu_to_le32(dval);+}+
What is this?
Some convoluted way of assigning the IP address in little endian?
Pointless if it's IPv4, as then each bit is just one byte.
And if it's for IPv6, what do you do for IPv4?
And isn't there a helper for it?
@@ -2106,6 +2106,30 @@ int qed_cxt_set_pf_params(struct qed_hwfn *p_hwfn, u32 rdma_tasks)}break;}+caseQED_PCI_NVMETCP:+{+structqed_nvmetcp_pf_params*p_params;++p_params=&p_hwfn->pf_params.nvmetcp_pf_params;++if(p_params->num_cons&&p_params->num_tasks){+qed_cxt_set_proto_cid_count(p_hwfn,+PROTOCOLID_NVMETCP,+p_params->num_cons,+0);++qed_cxt_set_proto_tid_count(p_hwfn,+PROTOCOLID_NVMETCP,+QED_CTX_NVMETCP_TID_SEG,+0,+p_params->num_tasks,+true);+}else{+DP_INFO(p_hwfn->cdev,+"NvmeTCP personality used without setting params!\n");+}+break;+}default:return-EINVAL;}
@@ -2132,6 +2156,10 @@ int qed_cxt_get_tid_mem_info(struct qed_hwfn *p_hwfn,proto=PROTOCOLID_ISCSI;seg=QED_CXT_ISCSI_TID_SEG;break;+caseQED_PCI_NVMETCP:+proto=PROTOCOLID_NVMETCP;+seg=QED_CTX_NVMETCP_TID_SEG;+break;default:return-EINVAL;}
@@ -2458,6 +2486,10 @@ int qed_cxt_get_task_ctx(struct qed_hwfn *p_hwfn,proto=PROTOCOLID_ISCSI;seg=QED_CXT_ISCSI_TID_SEG;break;+caseQED_PCI_NVMETCP:+proto=PROTOCOLID_NVMETCP;+seg=QED_CTX_NVMETCP_TID_SEG;+break;default:return-EINVAL;}
@@ -667,7 +668,8 @@ qed_llh_set_engine_affin(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt)}/* Storage PF is bound to a single engine while L2 PF uses both */-if(QED_IS_FCOE_PERSONALITY(p_hwfn)||QED_IS_ISCSI_PERSONALITY(p_hwfn))+if(QED_IS_FCOE_PERSONALITY(p_hwfn)||QED_IS_ISCSI_PERSONALITY(p_hwfn)||+QED_IS_NVMETCP_PERSONALITY(p_hwfn))eng=cdev->fir_affin?QED_ENG1:QED_ENG0;else/* L2_PERSONALITY */eng=QED_BOTH_ENG;
@@ -960,7 +960,8 @@ static int qed_sp_ll2_rx_queue_start(struct qed_hwfn *p_hwfn,if(test_bit(QED_MF_LL2_NON_UNICAST,&p_hwfn->cdev->mf_bits)&&p_ramrod->main_func_queue&&conn_type!=QED_LL2_TYPE_ROCE&&-conn_type!=QED_LL2_TYPE_IWARP){+conn_type!=QED_LL2_TYPE_IWARP&&+(!QED_IS_NVMETCP_PERSONALITY(p_hwfn))){p_ramrod->mf_si_bcast_accept_all=1;p_ramrod->mf_si_mcast_accept_all=1;}else{
@@ -1049,6 +1050,8 @@ static int qed_sp_ll2_tx_queue_start(struct qed_hwfn *p_hwfn,caseQED_LL2_TYPE_OOO:if(p_hwfn->hw_info.personality==QED_PCI_ISCSI)p_ramrod->conn_type=PROTOCOLID_ISCSI;+elseif(p_hwfn->hw_info.personality==QED_PCI_NVMETCP)+p_ramrod->conn_type=PROTOCOLID_NVMETCP;elsep_ramrod->conn_type=PROTOCOLID_IWARP;break;
@@ -1634,7 +1637,8 @@ int qed_ll2_establish_connection(void *cxt, u8 connection_handle)if(rc)gotoout;-if(!QED_IS_RDMA_PERSONALITY(p_hwfn))+if(!QED_IS_RDMA_PERSONALITY(p_hwfn)&&+!QED_IS_NVMETCP_PERSONALITY(p_hwfn))qed_wr(p_hwfn,p_ptt,PRS_REG_USE_LIGHT_L2,1);qed_ll2_establish_connection_ooo(p_hwfn,p_ll2_conn);
@@ -2376,7 +2380,8 @@ static int qed_ll2_start_ooo(struct qed_hwfn *p_hwfn,staticboolqed_ll2_is_storage_eng1(structqed_dev*cdev){return(QED_IS_FCOE_PERSONALITY(QED_LEADING_HWFN(cdev))||-QED_IS_ISCSI_PERSONALITY(QED_LEADING_HWFN(cdev)))&&+QED_IS_ISCSI_PERSONALITY(QED_LEADING_HWFN(cdev))||+QED_IS_NVMETCP_PERSONALITY(QED_LEADING_HWFN(cdev)))&&(QED_AFFIN_HWFN(cdev)!=QED_LEADING_HWFN(cdev));}
@@ -2402,11 +2407,13 @@ static int qed_ll2_stop(struct qed_dev *cdev)if(cdev->ll2->handle==QED_LL2_UNUSED_HANDLE)return0;+if(!QED_IS_NVMETCP_PERSONALITY(p_hwfn))+qed_llh_remove_mac_filter(cdev,0,cdev->ll2_mac_address);qed_llh_remove_mac_filter(cdev,0,cdev->ll2_mac_address);eth_zero_addr(cdev->ll2_mac_address);-if(QED_IS_ISCSI_PERSONALITY(p_hwfn))+if(QED_IS_ISCSI_PERSONALITY(p_hwfn)||QED_IS_NVMETCP_PERSONALITY(p_hwfn))qed_ll2_stop_ooo(p_hwfn);/* In CMT mode, LL2 is always started on engine 0 for a storage PF */
@@ -2442,6 +2449,7 @@ static int __qed_ll2_start(struct qed_hwfn *p_hwfn,conn_type=QED_LL2_TYPE_FCOE;break;caseQED_PCI_ISCSI:+caseQED_PCI_NVMETCP:conn_type=QED_LL2_TYPE_ISCSI;break;caseQED_PCI_ETH_ROCE:
@@ -2576,10 +2584,13 @@ static int qed_ll2_start(struct qed_dev *cdev, struct qed_ll2_params *params)}}-rc=qed_llh_add_mac_filter(cdev,0,params->ll2_mac_address);-if(rc){-DP_NOTICE(cdev,"Failed to add an LLH filter\n");-gotoerr3;+if(!QED_IS_NVMETCP_PERSONALITY(p_hwfn)){+rc=qed_llh_add_mac_filter(cdev,0,params->ll2_mac_address);+if(rc){+DP_NOTICE(cdev,"Failed to add an LLH filter\n");+gotoerr3;+}+}ether_addr_copy(cdev->ll2_mac_address,params->ll2_mac_address);
@@ -385,6 +385,7 @@ int qed_sp_pf_start(struct qed_hwfn *p_hwfn,p_ramrod->personality=PERSONALITY_FCOE;break;caseQED_PCI_ISCSI:+caseQED_PCI_NVMETCP:p_ramrod->personality=PERSONALITY_ISCSI;break;caseQED_PCI_ETH_ROCE:
As indicated, I do find this mix of 'nvmetcp is nearly iscsi' a bit
strange. I would have preferred to have distinct types for nvmetcp.
Cheers,
Hannes
--
Dr. Hannes Reinecke Kernel Storage Architect
hare@suse.de +49 911 74053 688
SUSE Software Solutions GmbH, Maxfeldstr. 5, 90409 Nürnberg
HRB 36809 (AG Nürnberg), Geschäftsführer: Felix Imendörffer
From: Hannes Reinecke <hare@suse.de> Date: 2021-05-02 11:22:34
On 4/29/21 9:09 PM, Shai Malin wrote:
quoted hunk
This patch introduces the NVMeTCP Offload FW and HW HSI in order
to initialize the IO level configuration into a per IO HW
resource ("task") as part of the IO path flow.
Acked-by: Igor Russkikh <redacted>
Signed-off-by: Prabhakar Kushwaha <redacted>
Signed-off-by: Omkar Kulkarni <redacted>
Signed-off-by: Shai Malin <redacted>
Signed-off-by: Michal Kalderon <mkalderon@marvell.com>
Signed-off-by: Ariel Elior <redacted>
---
include/linux/qed/nvmetcp_common.h | 418 ++++++++++++++++++++++++++++-
include/linux/qed/qed_nvmetcp_if.h | 37 +++
2 files changed, 454 insertions(+), 1 deletion(-)
Why do you need to define this?
Wouldn't it be easier to use the standard 'struct nvme_tcp_hdr' and swap
the bytes before sending it off?
(Or modify the firmware to do the byte-swapping itself ...)
+/* We don't need the entire 128 Bytes of the ICReq, hence passing only 16
+ * Bytes to the FW in network order.
+ */
+struct nvmetcp_icreq_hdr_psh {
+ __le16 pfv;
+ u8 hpda;
+ u8 digest;
+#define NVMETCP_ICREQ_HDR_PSH_16B_HDGST_EN_MASK 0x1
+#define NVMETCP_ICREQ_HDR_PSH_16B_HDGST_EN_SHIFT 0
+#define NVMETCP_ICREQ_HDR_PSH_16B_DDGST_EN_MASK 0x1
+#define NVMETCP_ICREQ_HDR_PSH_16B_DDGST_EN_SHIFT 1
+#define NVMETCP_ICREQ_HDR_PSH_16B_RESERVED1_MASK 0x3F
+#define NVMETCP_ICREQ_HDR_PSH_16B_RESERVED1_SHIFT 2
+ __le32 maxr2t;
+ u8 reserved[8];
+};
+
One of these short-cuts which will come back to haunt you eventually; I
would consider updating the firmware to process the entire icreq.
@@ -0,0 +1,372 @@+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)+/* Copyright 2021 Marvell. All rights reserved. */++#include<linux/kernel.h>+#include<linux/module.h>+#include<linux/pci.h>+#include<linux/kernel.h>+#include<linux/list.h>+#include<linux/mm.h>+#include<linux/types.h>+#include<asm/byteorder.h>+#include<linux/qed/common_hsi.h>+#include<linux/qed/storage_common.h>+#include<linux/qed/nvmetcp_common.h>+#include<linux/qed/qed_nvmetcp_if.h>+#include"qed_nvmetcp_fw_funcs.h"++#define NVMETCP_NUM_SGES_IN_CACHE 0x4++boolnvmetcp_is_slow_sgl(u16num_sges,boolsmall_mid_sge)+{+return(num_sges>SCSI_NUM_SGES_SLOW_SGL_THR&&small_mid_sge);+}++voidinit_scsi_sgl_context(structscsi_sgl_params*ctx_sgl_params,+structscsi_cached_sges*ctx_data_desc,+structstorage_sgl_task_params*sgl_params)+{+u8num_sges_to_init=(u8)(sgl_params->num_sges>NVMETCP_NUM_SGES_IN_CACHE?+NVMETCP_NUM_SGES_IN_CACHE:sgl_params->num_sges);+u8sge_index;++/* sgl params */+ctx_sgl_params->sgl_addr.lo=cpu_to_le32(sgl_params->sgl_phys_addr.lo);+ctx_sgl_params->sgl_addr.hi=cpu_to_le32(sgl_params->sgl_phys_addr.hi);+ctx_sgl_params->sgl_total_length=cpu_to_le32(sgl_params->total_buffer_size);+ctx_sgl_params->sgl_num_sges=cpu_to_le16(sgl_params->num_sges);++for(sge_index=0;sge_index<num_sges_to_init;sge_index++){+ctx_data_desc->sge[sge_index].sge_addr.lo=+cpu_to_le32(sgl_params->sgl[sge_index].sge_addr.lo);+ctx_data_desc->sge[sge_index].sge_addr.hi=+cpu_to_le32(sgl_params->sgl[sge_index].sge_addr.hi);+ctx_data_desc->sge[sge_index].sge_len=+cpu_to_le32(sgl_params->sgl[sge_index].sge_len);+}+}++staticinlineu32calc_rw_task_size(structnvmetcp_task_params*task_params,+enumnvmetcp_task_typetask_type)+{+u32io_size;++if(task_type==NVMETCP_TASK_TYPE_HOST_WRITE)+io_size=task_params->tx_io_size;+else+io_size=task_params->rx_io_size;++if(unlikely(!io_size))+return0;++returnio_size;+}++staticinlinevoidinit_sqe(structnvmetcp_task_params*task_params,+structstorage_sgl_task_params*sgl_task_params,+enumnvmetcp_task_typetask_type)+{+if(!task_params->sqe)+return;++memset(task_params->sqe,0,sizeof(*task_params->sqe));+task_params->sqe->task_id=cpu_to_le16(task_params->itid);++switch(task_type){+caseNVMETCP_TASK_TYPE_HOST_WRITE:{+u32buf_size=0;+u32num_sges=0;++SET_FIELD(task_params->sqe->contlen_cdbsize,+NVMETCP_WQE_CDB_SIZE_OR_NVMETCP_CMD,1);+SET_FIELD(task_params->sqe->flags,NVMETCP_WQE_WQE_TYPE,+NVMETCP_WQE_TYPE_NORMAL);+if(task_params->tx_io_size){+if(task_params->send_write_incapsule)+buf_size=calc_rw_task_size(task_params,task_type);++if(nvmetcp_is_slow_sgl(sgl_task_params->num_sges,+sgl_task_params->small_mid_sge))+num_sges=NVMETCP_WQE_NUM_SGES_SLOWIO;+else+num_sges=min((u16)sgl_task_params->num_sges,+(u16)SCSI_NUM_SGES_SLOW_SGL_THR);+}+SET_FIELD(task_params->sqe->flags,NVMETCP_WQE_NUM_SGES,num_sges);+SET_FIELD(task_params->sqe->contlen_cdbsize,NVMETCP_WQE_CONT_LEN,buf_size);+}break;++caseNVMETCP_TASK_TYPE_HOST_READ:{+SET_FIELD(task_params->sqe->flags,NVMETCP_WQE_WQE_TYPE,+NVMETCP_WQE_TYPE_NORMAL);+SET_FIELD(task_params->sqe->contlen_cdbsize,+NVMETCP_WQE_CDB_SIZE_OR_NVMETCP_CMD,1);+}break;++caseNVMETCP_TASK_TYPE_INIT_CONN_REQUEST:{+SET_FIELD(task_params->sqe->flags,NVMETCP_WQE_WQE_TYPE,+NVMETCP_WQE_TYPE_MIDDLE_PATH);++if(task_params->tx_io_size){+SET_FIELD(task_params->sqe->contlen_cdbsize,NVMETCP_WQE_CONT_LEN,+task_params->tx_io_size);+SET_FIELD(task_params->sqe->flags,NVMETCP_WQE_NUM_SGES,+min((u16)sgl_task_params->num_sges,+(u16)SCSI_NUM_SGES_SLOW_SGL_THR));+}+}break;++caseNVMETCP_TASK_TYPE_CLEANUP:+SET_FIELD(task_params->sqe->flags,NVMETCP_WQE_WQE_TYPE,+NVMETCP_WQE_TYPE_TASK_CLEANUP);++default:+break;+}+}++/* The following function initializes of NVMeTCP task params */+staticinlinevoid+init_nvmetcp_task_params(structe5_nvmetcp_task_context*context,+structnvmetcp_task_params*task_params,+enumnvmetcp_task_typetask_type)+{+context->ystorm_st_context.state.cccid=task_params->host_cccid;+SET_FIELD(context->ustorm_st_context.error_flags,USTORM_NVMETCP_TASK_ST_CTX_NVME_TCP,1);+context->ustorm_st_context.nvme_tcp_opaque_lo=cpu_to_le32(task_params->opq.lo);+context->ustorm_st_context.nvme_tcp_opaque_hi=cpu_to_le32(task_params->opq.hi);+}++/* The following function initializes default values to all tasks */+staticinlinevoid+init_default_nvmetcp_task(structnvmetcp_task_params*task_params,void*pdu_header,+enumnvmetcp_task_typetask_type)+{+structe5_nvmetcp_task_context*context=task_params->context;+constu8val_byte=context->mstorm_ag_context.cdu_validation;+u8dw_index;++memset(context,0,sizeof(*context));++init_nvmetcp_task_params(context,task_params,+(enumnvmetcp_task_type)task_type);++if(task_type==NVMETCP_TASK_TYPE_HOST_WRITE||+task_type==NVMETCP_TASK_TYPE_HOST_READ){+for(dw_index=0;dw_index<QED_NVMETCP_CMD_HDR_SIZE/4;dw_index++)+context->ystorm_st_context.pdu_hdr.task_hdr.reg[dw_index]=+cpu_to_le32(((u32*)pdu_header)[dw_index]);+}else{+for(dw_index=0;dw_index<QED_NVMETCP_CMN_HDR_SIZE/4;dw_index++)+context->ystorm_st_context.pdu_hdr.task_hdr.reg[dw_index]=+cpu_to_le32(((u32*)pdu_header)[dw_index]);+}+
And this is what I meant. You are twiddling with the bytes already, so
why bother with a separate struct at all?
@@ -0,0 +1,239 @@+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)+/*+*Copyright2021Marvell.Allrightsreserved.+*/++#include<linux/types.h>+#include<asm/byteorder.h>+#include<asm/param.h>+#include<linux/delay.h>+#include<linux/pci.h>+#include<linux/dma-mapping.h>+#include<linux/etherdevice.h>+#include<linux/kernel.h>+#include<linux/stddef.h>+#include<linux/errno.h>++#include<net/tcp.h>++#include<linux/qed/qed_nvmetcp_ip_services_if.h>++#define QED_IP_RESOL_TIMEOUT 4++intqed_route_ipv4(structsockaddr_storage*local_addr,+structsockaddr_storage*remote_addr,+structsockaddr*hardware_address,+structnet_device**ndev)+{+structneighbour*neigh=NULL;+__be32*loc_ip,*rem_ip;+structrtable*rt;+intrc=-ENXIO;+intretry;++loc_ip=&((structsockaddr_in*)local_addr)->sin_addr.s_addr;+rem_ip=&((structsockaddr_in*)remote_addr)->sin_addr.s_addr;+*ndev=NULL;+rt=ip_route_output(&init_net,*rem_ip,*loc_ip,0/*tos*/,0/*oif*/);+if(IS_ERR(rt)){+pr_err("lookup route failed\n");+rc=PTR_ERR(rt);+gotoreturn_err;+}++neigh=dst_neigh_lookup(&rt->dst,rem_ip);+if(!neigh){+rc=-ENOMEM;+ip_rt_put(rt);+gotoreturn_err;+}++*ndev=rt->dst.dev;+ip_rt_put(rt);++/* If not resolved, kick-off state machine towards resolution */+if(!(neigh->nud_state&NUD_VALID))+neigh_event_send(neigh,NULL);++/* query neighbor until resolved or timeout */+retry=QED_IP_RESOL_TIMEOUT;+while(!(neigh->nud_state&NUD_VALID)&&retry>0){+msleep(1000);+retry--;+}++if(neigh->nud_state&NUD_VALID){+/* copy resolved MAC address */+neigh_ha_snapshot(hardware_address->sa_data,neigh,*ndev);++hardware_address->sa_family=(*ndev)->type;+rc=0;+}++neigh_release(neigh);+if(!(*loc_ip)){+*loc_ip=inet_select_addr(*ndev,*rem_ip,RT_SCOPE_UNIVERSE);+local_addr->ss_family=AF_INET;+}++return_err:++returnrc;+}+EXPORT_SYMBOL(qed_route_ipv4);++intqed_route_ipv6(structsockaddr_storage*local_addr,+structsockaddr_storage*remote_addr,+structsockaddr*hardware_address,+structnet_device**ndev)+{+structneighbour*neigh=NULL;+structdst_entry*dst;+structflowi6fl6;+intrc=-ENXIO;+intretry;++memset(&fl6,0,sizeof(fl6));+fl6.saddr=((structsockaddr_in6*)local_addr)->sin6_addr;+fl6.daddr=((structsockaddr_in6*)remote_addr)->sin6_addr;++dst=ip6_route_output(&init_net,NULL,&fl6);+if(!dst||dst->error){+if(dst){+dst_release(dst);+pr_err("lookup route failed %d\n",dst->error);+}++gotoout;+}++neigh=dst_neigh_lookup(dst,&fl6.daddr);+if(neigh){+*ndev=ip6_dst_idev(dst)->dev;++/* If not resolved, kick-off state machine towards resolution */+if(!(neigh->nud_state&NUD_VALID))+neigh_event_send(neigh,NULL);++/* query neighbor until resolved or timeout */+retry=QED_IP_RESOL_TIMEOUT;+while(!(neigh->nud_state&NUD_VALID)&&retry>0){+msleep(1000);+retry--;+}++if(neigh->nud_state&NUD_VALID){+neigh_ha_snapshot((u8*)hardware_address->sa_data,neigh,*ndev);++hardware_address->sa_family=(*ndev)->type;+rc=0;+}++neigh_release(neigh);++if(ipv6_addr_any(&fl6.saddr)){+if(ipv6_dev_get_saddr(dev_net(*ndev),*ndev,+&fl6.daddr,0,&fl6.saddr)){+pr_err("Unable to find source IP address\n");+gotoout;+}++local_addr->ss_family=AF_INET6;+((structsockaddr_in6*)local_addr)->sin6_addr=+fl6.saddr;+}+}++dst_release(dst);++out:++returnrc;+}+EXPORT_SYMBOL(qed_route_ipv6);++voidqed_vlan_get_ndev(structnet_device**ndev,u16*vlan_id)+{+if(is_vlan_dev(*ndev)){+*vlan_id=vlan_dev_vlan_id(*ndev);+*ndev=vlan_dev_real_dev(*ndev);+}+}+EXPORT_SYMBOL(qed_vlan_get_ndev);++structpci_dev*qed_validate_ndev(structnet_device*ndev)+{+structpci_dev*pdev=NULL;+structnet_device*upper;++for_each_pci_dev(pdev){+if(pdev&&pdev->driver&&+!strcmp(pdev->driver->name,"qede")){+upper=pci_get_drvdata(pdev);+if(upper->ifindex==ndev->ifindex)+returnpdev;+}+}++returnNULL;+}+EXPORT_SYMBOL(qed_validate_ndev);++__be16qed_get_in_port(structsockaddr_storage*sa)+{+returnsa->ss_family==AF_INET+?((structsockaddr_in*)sa)->sin_port+:((structsockaddr_in6*)sa)->sin6_port;+}+EXPORT_SYMBOL(qed_get_in_port);++intqed_fetch_tcp_port(structsockaddr_storagelocal_ip_addr,+structsocket**sock,u16*port)+{+structsockaddr_storagesa;+intrc=0;++rc=sock_create(local_ip_addr.ss_family,SOCK_STREAM,IPPROTO_TCP,sock);+if(rc){+pr_warn("failed to create socket: %d\n",rc);+gotoerr;+}++(*sock)->sk->sk_allocation=GFP_KERNEL;+sk_set_memalloc((*sock)->sk);++rc=kernel_bind(*sock,(structsockaddr*)&local_ip_addr,+sizeof(local_ip_addr));++if(rc){+pr_warn("failed to bind socket: %d\n",rc);+gotoerr_sock;+}++rc=kernel_getsockname(*sock,(structsockaddr*)&sa);+if(rc<0){+pr_warn("getsockname() failed: %d\n",rc);+gotoerr_sock;+}++*port=ntohs(qed_get_in_port(&sa));++return0;++err_sock:+sock_release(*sock);+sock=NULL;+err:++returnrc;+}+EXPORT_SYMBOL(qed_fetch_tcp_port);++voidqed_return_tcp_port(structsocket*sock)+{+if(sock&&sock->sk){+tcp_set_state(sock->sk,TCP_CLOSE);+sock_release(sock);+}+}+EXPORT_SYMBOL(qed_return_tcp_port);
From: Hannes Reinecke <hare@suse.de> Date: 2021-05-02 11:27:20
On 4/29/21 9:09 PM, Shai Malin wrote:
This patch will present the skeleton of the qedn driver.
The new driver will be added under "drivers/nvme/hw/qedn" and will be
enabled by the Kconfig "Marvell NVM Express over Fabrics TCP offload".
The internal implementation:
- qedn.h:
Includes all common structs to be used by the qedn vendor driver.
- qedn_main.c
Includes the qedn_init and qedn_cleanup implementation.
As part of the qedn init, the driver will register as a pci device and
will work with the Marvell fastlinQ NICs.
As part of the probe, the driver will register to the nvme_tcp_offload
(ULP).
Acked-by: Igor Russkikh <redacted>
Signed-off-by: Arie Gershberg <redacted>
Signed-off-by: Prabhakar Kushwaha <redacted>
Signed-off-by: Omkar Kulkarni <redacted>
Signed-off-by: Michal Kalderon <mkalderon@marvell.com>
Signed-off-by: Ariel Elior <redacted>
Signed-off-by: Shai Malin <redacted>
---
MAINTAINERS | 10 ++
drivers/nvme/Kconfig | 1 +
drivers/nvme/Makefile | 1 +
drivers/nvme/hw/Kconfig | 8 ++
drivers/nvme/hw/Makefile | 3 +
drivers/nvme/hw/qedn/Makefile | 5 +
drivers/nvme/hw/qedn/qedn.h | 19 +++
drivers/nvme/hw/qedn/qedn_main.c | 201 +++++++++++++++++++++++++++++++
8 files changed, 248 insertions(+)
create mode 100644 drivers/nvme/hw/Kconfig
create mode 100644 drivers/nvme/hw/Makefile
create mode 100644 drivers/nvme/hw/qedn/Makefile
create mode 100644 drivers/nvme/hw/qedn/qedn.h
create mode 100644 drivers/nvme/hw/qedn/qedn_main.c
Reviewed-by: Hannes Reinecke <hare@suse.de>
Cheers,
Hannes
--
Dr. Hannes Reinecke Kernel Storage Architect
hare@suse.de +49 911 74053 688
SUSE Software Solutions GmbH, Maxfeldstr. 5, 90409 Nürnberg
HRB 36809 (AG Nürnberg), Geschäftsführer: Felix Imendörffer
From: Hannes Reinecke <hare@suse.de> Date: 2021-05-02 11:29:03
On 4/29/21 9:09 PM, Shai Malin wrote:
quoted hunk
This patch introduces the functionality of loading and unloading
physical function.
qedn_probe() loads the offload device PF(physical function), and
initialize the HW and the FW with the PF parameters using the
HW ops->qed_nvmetcp_ops, which are similar to other "qed_*_ops" which
are used by the qede, qedr, qedf and qedi device drivers.
qedn_remove() unloads the offload device PF, re-initialize the HW and
the FW with the PF parameters.
The struct qedn_ctx is per PF container for PF-specific attributes and
resources.
Acked-by: Igor Russkikh <redacted>
Signed-off-by: Dean Balandin <redacted>
Signed-off-by: Prabhakar Kushwaha <redacted>
Signed-off-by: Omkar Kulkarni <redacted>
Signed-off-by: Michal Kalderon <mkalderon@marvell.com>
Signed-off-by: Ariel Elior <redacted>
Signed-off-by: Shai Malin <redacted>
---
drivers/nvme/hw/Kconfig | 1 +
drivers/nvme/hw/qedn/qedn.h | 49 ++++++++
drivers/nvme/hw/qedn/qedn_main.c | 191 ++++++++++++++++++++++++++++++-
3 files changed, 236 insertions(+), 5 deletions(-)
@@ -14,6 +14,10 @@#define CHIP_NUM_AHP_NVMETCP 0x8194+conststructqed_nvmetcp_ops*qed_ops;++/* Global context instance */+structqedn_globalqedn_glb;staticstructpci_device_idqedn_pci_tbl[]={{PCI_VDEVICE(QLOGIC,CHIP_NUM_AHP_NVMETCP),0},{0,0},
@@ -99,12 +103,132 @@ static struct nvme_tcp_ofld_ops qedn_ofld_ops = {.commit_rqs=qedn_commit_rqs,};+staticinlinevoidqedn_init_pf_struct(structqedn_ctx*qedn)+{+/* Placeholder - Initialize qedn fields */+}++staticinlinevoid+qedn_init_core_probe_params(structqed_probe_params*probe_params)+{+memset(probe_params,0,sizeof(*probe_params));+probe_params->protocol=QED_PROTOCOL_NVMETCP;+probe_params->is_vf=false;+probe_params->recov_in_prog=0;+}++staticinlineintqedn_core_probe(structqedn_ctx*qedn)+{+structqed_probe_paramsprobe_params;+intrc=0;++qedn_init_core_probe_params(&probe_params);+pr_info("Starting QED probe\n");+qedn->cdev=qed_ops->common->probe(qedn->pdev,&probe_params);+if(!qedn->cdev){+rc=-ENODEV;+pr_err("QED probe failed\n");+}++returnrc;+}++staticvoidqedn_add_pf_to_gl_list(structqedn_ctx*qedn)+{+mutex_lock(&qedn_glb.glb_mutex);+list_add_tail(&qedn->gl_pf_entry,&qedn_glb.qedn_pf_list);+mutex_unlock(&qedn_glb.glb_mutex);+}++staticvoidqedn_remove_pf_from_gl_list(structqedn_ctx*qedn)+{+mutex_lock(&qedn_glb.glb_mutex);+list_del_init(&qedn->gl_pf_entry);+mutex_unlock(&qedn_glb.glb_mutex);+}++staticintqedn_set_nvmetcp_pf_param(structqedn_ctx*qedn)+{+u32fw_conn_queue_pages=QEDN_NVMETCP_NUM_FW_CONN_QUEUE_PAGES;+structqed_nvmetcp_pf_params*pf_params;++pf_params=&qedn->pf_params.nvmetcp_pf_params;+memset(pf_params,0,sizeof(*pf_params));+qedn->num_fw_cqs=min_t(u8,qedn->dev_info.num_cqs,num_online_cpus());++pf_params->num_cons=QEDN_MAX_CONNS_PER_PF;+pf_params->num_tasks=QEDN_MAX_TASKS_PER_PF;++/* Placeholder - Initialize function level queues */++/* Placeholder - Initialize TCP params */++/* Queues */+pf_params->num_sq_pages_in_ring=fw_conn_queue_pages;+pf_params->num_r2tq_pages_in_ring=fw_conn_queue_pages;+pf_params->num_uhq_pages_in_ring=fw_conn_queue_pages;+pf_params->num_queues=qedn->num_fw_cqs;+pf_params->cq_num_entries=QEDN_FW_CQ_SIZE;++/* the CQ SB pi */+pf_params->gl_rq_pi=QEDN_PROTO_CQ_PROD_IDX;++return0;+}++staticinlineintqedn_slowpath_start(structqedn_ctx*qedn)+{+structqed_slowpath_paramssp_params={};+intrc=0;++/* Start the Slowpath-process */+sp_params.int_mode=QED_INT_MODE_MSIX;+sp_params.drv_major=QEDN_MAJOR_VERSION;+sp_params.drv_minor=QEDN_MINOR_VERSION;+sp_params.drv_rev=QEDN_REVISION_VERSION;+sp_params.drv_eng=QEDN_ENGINEERING_VERSION;+strscpy(sp_params.name,"qedn NVMeTCP",QED_DRV_VER_STR_SIZE);+rc=qed_ops->common->slowpath_start(qedn->cdev,&sp_params);+if(rc)+pr_err("Cannot start slowpath\n");++returnrc;+}+staticvoid__qedn_remove(structpci_dev*pdev){structqedn_ctx*qedn=pci_get_drvdata(pdev);+intrc;++pr_notice("qedn remove started: abs PF id=%u\n",+qedn->dev_info.common.abs_pf_id);++if(test_and_set_bit(QEDN_STATE_MODULE_REMOVE_ONGOING,&qedn->state)){+pr_err("Remove already ongoing\n");++return;+}++if(test_and_clear_bit(QEDN_STATE_REGISTERED_OFFLOAD_DEV,&qedn->state))+nvme_tcp_ofld_unregister_dev(&qedn->qedn_ofld_dev);++if(test_and_clear_bit(QEDN_STATE_GL_PF_LIST_ADDED,&qedn->state))+qedn_remove_pf_from_gl_list(qedn);+else+pr_err("Failed to remove from global PF list\n");++if(test_and_clear_bit(QEDN_STATE_MFW_STATE,&qedn->state)){+rc=qed_ops->common->update_drv_state(qedn->cdev,false);+if(rc)+pr_err("Failed to send drv state to MFW\n");+}++if(test_and_clear_bit(QEDN_STATE_CORE_OPEN,&qedn->state))+qed_ops->common->slowpath_stop(qedn->cdev);++if(test_and_clear_bit(QEDN_STATE_CORE_PROBED,&qedn->state))+qed_ops->common->remove(qedn->cdev);-pr_notice("Starting qedn_remove\n");-nvme_tcp_ofld_unregister_dev(&qedn->qedn_ofld_dev);kfree(qedn);pr_notice("Ending qedn_remove successfully\n");}
@@ -144,15 +268,55 @@ static int __qedn_probe(struct pci_dev *pdev)if(!qedn)return-ENODEV;+qedn_init_pf_struct(qedn);++/* QED probe */+rc=qedn_core_probe(qedn);+if(rc)+gotoexit_probe_and_release_mem;++set_bit(QEDN_STATE_CORE_PROBED,&qedn->state);++rc=qed_ops->fill_dev_info(qedn->cdev,&qedn->dev_info);+if(rc){+pr_err("fill_dev_info failed\n");+gotoexit_probe_and_release_mem;+}++qedn_add_pf_to_gl_list(qedn);+set_bit(QEDN_STATE_GL_PF_LIST_ADDED,&qedn->state);++rc=qedn_set_nvmetcp_pf_param(qedn);+if(rc)+gotoexit_probe_and_release_mem;++qed_ops->common->update_pf_params(qedn->cdev,&qedn->pf_params);+rc=qedn_slowpath_start(qedn);+if(rc)+gotoexit_probe_and_release_mem;++set_bit(QEDN_STATE_CORE_OPEN,&qedn->state);++rc=qed_ops->common->update_drv_state(qedn->cdev,true);+if(rc){+pr_err("Failed to send drv state to MFW\n");+gotoexit_probe_and_release_mem;+}++set_bit(QEDN_STATE_MFW_STATE,&qedn->state);+qedn->qedn_ofld_dev.ops=&qedn_ofld_ops;INIT_LIST_HEAD(&qedn->qedn_ofld_dev.entry);rc=nvme_tcp_ofld_register_dev(&qedn->qedn_ofld_dev);if(rc)-gotorelease_qedn;+gotoexit_probe_and_release_mem;++set_bit(QEDN_STATE_REGISTERED_OFFLOAD_DEV,&qedn->state);return0;-release_qedn:-kfree(qedn);+exit_probe_and_release_mem:+__qedn_remove(pdev);+pr_err("probe ended with error\n");returnrc;}
@@ -170,10 +334,26 @@ static struct pci_driver qedn_pci_driver = {.shutdown=qedn_shutdown,};+staticinlinevoidqedn_init_global_contxt(void)+{+INIT_LIST_HEAD(&qedn_glb.qedn_pf_list);+INIT_LIST_HEAD(&qedn_glb.ctrl_list);+mutex_init(&qedn_glb.glb_mutex);+}+staticint__initqedn_init(void){intrc;+qedn_init_global_contxt();++qed_ops=qed_get_nvmetcp_ops();+if(!qed_ops){+pr_err("Failed to get QED NVMeTCP ops\n");++return-EINVAL;+}+rc=pci_register_driver(&qedn_pci_driver);if(rc){pr_err("Failed to register pci driver\n");
@@ -189,6 +369,7 @@ static int __init qedn_init(void)staticvoid__exitqedn_cleanup(void){pci_unregister_driver(&qedn_pci_driver);+qed_put_nvmetcp_ops();pr_notice("Unloading qedn ended\n");}
I do wonder what you need the global list of devices for, but let's see.
Reviewed-by: Hannes Reinecke <hare@suse.de>
Cheers,
Hannes
--
Dr. Hannes Reinecke Kernel Storage Architect
hare@suse.de +49 911 74053 688
SUSE Software Solutions GmbH, Maxfeldstr. 5, 90409 Nürnberg
HRB 36809 (AG Nürnberg), Geschäftsführer: Felix Imendörffer
From: Hannes Reinecke <hare@suse.de> Date: 2021-05-02 11:29:57
On 4/29/21 9:09 PM, Shai Malin wrote:
quoted hunk
From: Nikolay Assa <redacted>
This patch introduces the qedn_claim_dev() network service which the
offload device (qedn) is using through the paired net-device (qede).
qedn_claim_dev() returns true if the IP addr(IPv4 or IPv6) of the target
server is reachable via the net-device which is paired with the
offloaded device.
Acked-by: Igor Russkikh <redacted>
Signed-off-by: Nikolay Assa <redacted>
Signed-off-by: Prabhakar Kushwaha <redacted>
Signed-off-by: Omkar Kulkarni <redacted>
Signed-off-by: Michal Kalderon <mkalderon@marvell.com>
Signed-off-by: Ariel Elior <redacted>
Signed-off-by: Shai Malin <redacted>
---
drivers/nvme/hw/qedn/qedn.h | 4 +++
drivers/nvme/hw/qedn/qedn_main.c | 42 ++++++++++++++++++++++++++++++--
2 files changed, 44 insertions(+), 2 deletions(-)
@@ -27,9 +27,47 @@ static intqedn_claim_dev(structnvme_tcp_ofld_dev*dev,structnvme_tcp_ofld_ctrl_con_params*conn_params){-/* Placeholder - qedn_claim_dev */+structpci_dev*qede_pdev=NULL;+structnet_device*ndev=NULL;+u16vlan_id=0;+intrc=0;-return0;+/* qedn utilizes host network stack through paired qede device for+*non-offloadtraffic.Firstweverifythereisvalidroutetoremote+*peer.+*/+if(conn_params->remote_ip_addr.ss_family==AF_INET){+rc=qed_route_ipv4(&conn_params->local_ip_addr,+&conn_params->remote_ip_addr,+&conn_params->remote_mac_addr,+&ndev);+}elseif(conn_params->remote_ip_addr.ss_family==AF_INET6){+rc=qed_route_ipv6(&conn_params->local_ip_addr,+&conn_params->remote_ip_addr,+&conn_params->remote_mac_addr,+&ndev);+}else{+pr_err("address family %d not supported\n",+conn_params->remote_ip_addr.ss_family);++returnfalse;+}++if(rc)+returnfalse;++qed_vlan_get_ndev(&ndev,&vlan_id);+conn_params->vlan_id=vlan_id;++/* route found through ndev - validate this is qede*/+qede_pdev=qed_validate_ndev(ndev);+if(!qede_pdev)+returnfalse;++dev->qede_pdev=qede_pdev;+dev->ndev=ndev;++returntrue;}staticintqedn_create_queue(structnvme_tcp_ofld_queue*queue,intqid,
Reviewed-by: Hannes Reinecke <hare@suse.de>
Cheers,
Hannes
--
Dr. Hannes Reinecke Kernel Storage Architect
hare@suse.de +49 911 74053 688
SUSE Software Solutions GmbH, Maxfeldstr. 5, 90409 Nürnberg
HRB 36809 (AG Nürnberg), Geschäftsführer: Felix Imendörffer
From: Hannes Reinecke <hare@suse.de> Date: 2021-05-02 11:32:39
On 4/29/21 9:09 PM, Shai Malin wrote:
quoted hunk
This patch will present the adding of qedn_fp_queue - this is a per cpu
core element which handles all of the connections on that cpu core.
The qedn_fp_queue will handle a group of connections (NVMeoF QPs) which
are handled on the same cpu core, and will only use the same FW-driver
resources with no need to be related to the same NVMeoF controller.
The per qedn_fq_queue resources are the FW CQ and FW status block:
- The FW CQ will be used for the FW to notify the driver that the
the exchange has ended and the FW will pass the incoming NVMeoF CQE
(if exist) to the driver.
- FW status block - which is used for the FW to notify the driver with
the producer update of the FW CQE chain.
The FW fast-path queues are based on qed_chain.h
Acked-by: Igor Russkikh <redacted>
Signed-off-by: Prabhakar Kushwaha <redacted>
Signed-off-by: Omkar Kulkarni <redacted>
Signed-off-by: Michal Kalderon <mkalderon@marvell.com>
Signed-off-by: Ariel Elior <redacted>
Signed-off-by: Shai Malin <redacted>
---
drivers/nvme/hw/qedn/qedn.h | 26 +++
drivers/nvme/hw/qedn/qedn_main.c | 287 ++++++++++++++++++++++++++++++-
2 files changed, 310 insertions(+), 3 deletions(-)
@@ -255,6 +508,12 @@ static void __qedn_remove(struct pci_dev *pdev) else pr_err("Failed to remove from global PF list\n");+ if (test_and_clear_bit(QEDN_STATE_IRQ_SET, &qedn->state))+ qedn_sync_free_irqs(qedn);++ if (test_and_clear_bit(QEDN_STATE_NVMETCP_OPEN, &qedn->state))+ qed_ops->stop(qedn->cdev);+ if (test_and_clear_bit(QEDN_STATE_MFW_STATE, &qedn->state)) { rc = qed_ops->common->update_drv_state(qedn->cdev, false); if (rc)
@@ -264,6 +523,9 @@ static void __qedn_remove(struct pci_dev *pdev) if (test_and_clear_bit(QEDN_STATE_CORE_OPEN, &qedn->state)) qed_ops->common->slowpath_stop(qedn->cdev);+ if (test_and_clear_bit(QEDN_STATE_FP_WORK_THREAD_SET, &qedn->state))+ qedn_free_function_queues(qedn);+ if (test_and_clear_bit(QEDN_STATE_CORE_PROBED, &qedn->state)) qed_ops->common->remove(qedn->cdev);
@@ -335,6 +597,25 @@ static int __qedn_probe(struct pci_dev *pdev) set_bit(QEDN_STATE_CORE_OPEN, &qedn->state);+ rc = qedn_setup_irq(qedn);+ if (rc)+ goto exit_probe_and_release_mem;++ set_bit(QEDN_STATE_IRQ_SET, &qedn->state);++ /* NVMeTCP start HW PF */+ rc = qed_ops->start(qedn->cdev,+ NULL /* Placeholder for FW IO-path resources */,+ qedn,+ NULL /* Placeholder for FW Event callback */);+ if (rc) {+ rc = -ENODEV;+ pr_err("Cannot start NVMeTCP Function\n");+ goto exit_probe_and_release_mem;+ }++ set_bit(QEDN_STATE_NVMETCP_OPEN, &qedn->state);+ rc = qed_ops->common->update_drv_state(qedn->cdev, true); if (rc) { pr_err("Failed to send drv state to MFW\n");
So you have a limited number of MSI-x interrupts, but don't limit the
number of hw queues to that. Why?
Cheers,
Hannes
--
Dr. Hannes Reinecke Kernel Storage Architect
hare@suse.de +49 911 74053 688
SUSE Software Solutions GmbH, Maxfeldstr. 5, 90409 Nürnberg
HRB 36809 (AG Nürnberg), Geschäftsführer: Felix Imendörffer
From: Hannes Reinecke <hare@suse.de> Date: 2021-05-02 11:38:12
On 4/29/21 9:09 PM, Shai Malin wrote:
From: Prabhakar Kushwaha <redacted>
This patch will present the connection (queue) level slowpath
implementation relevant for create_queue flow.
The internal implementation:
- Add per controller slowpath workqeueue via pre_setup_ctrl
- qedn_main.c:
Includes qedn's implementation of the create_queue op.
- qedn_conn.c will include main slowpath connection level functions,
including:
1. Per-queue resources allocation.
2. Creating a new connection.
3. Offloading the connection to the FW for TCP handshake.
4. Destroy of a connection.
5. Support of delete and free controller.
6. TCP port management via qed_fetch_tcp_port, qed_return_tcp_port
Acked-by: Igor Russkikh <redacted>
Signed-off-by: Prabhakar Kushwaha <redacted>
Signed-off-by: Omkar Kulkarni <redacted>
Signed-off-by: Michal Kalderon <mkalderon@marvell.com>
Signed-off-by: Ariel Elior <redacted>
Signed-off-by: Shai Malin <redacted>
---
drivers/nvme/hw/qedn/Makefile | 5 +-
drivers/nvme/hw/qedn/qedn.h | 173 ++++++++++-
drivers/nvme/hw/qedn/qedn_conn.c | 508 +++++++++++++++++++++++++++++++
drivers/nvme/hw/qedn/qedn_main.c | 208 ++++++++++++-
4 files changed, 883 insertions(+), 11 deletions(-)
create mode 100644 drivers/nvme/hw/qedn/qedn_conn.c
Reviewed-by: Hannes Reinecke <hare@suse.de>
Cheers,
Hannes
--
Dr. Hannes Reinecke Kernel Storage Architect
hare@suse.de +49 911 74053 688
SUSE Software Solutions GmbH, Maxfeldstr. 5, 90409 Nürnberg
HRB 36809 (AG Nürnberg), Geschäftsführer: Felix Imendörffer
From: Hannes Reinecke <hare@suse.de> Date: 2021-05-02 11:39:13
On 4/29/21 9:09 PM, Shai Malin wrote:
From: Prabhakar Kushwaha <redacted>
HW filter can be configured to filter TCP packets based on either
source or target TCP port. QEDN leverage this feature to route
NVMeTCP traffic.
This patch configures HW filter block based on source port for all
receiving packets to deliver correct QEDN PF.
Acked-by: Igor Russkikh <redacted>
Signed-off-by: Prabhakar Kushwaha <redacted>
Signed-off-by: Omkar Kulkarni <redacted>
Signed-off-by: Michal Kalderon <mkalderon@marvell.com>
Signed-off-by: Ariel Elior <redacted>
Signed-off-by: Shai Malin <redacted>
---
drivers/nvme/hw/qedn/qedn.h | 15 +++++
drivers/nvme/hw/qedn/qedn_main.c | 108 ++++++++++++++++++++++++++++++-
2 files changed, 122 insertions(+), 1 deletion(-)
Reviewed-by: Hannes Reinecke <hare@suse.de>
Cheers,
Hannes
--
Dr. Hannes Reinecke Kernel Storage Architect
hare@suse.de +49 911 74053 688
SUSE Software Solutions GmbH, Maxfeldstr. 5, 90409 Nürnberg
HRB 36809 (AG Nürnberg), Geschäftsführer: Felix Imendörffer
From: Hannes Reinecke <hare@suse.de> Date: 2021-05-02 11:42:40
On 4/29/21 9:09 PM, Shai Malin wrote:
quoted hunk
This patch will present the IO level workqueues:
- qedn_nvme_req_fp_wq(): process new requests, similar to
nvme_tcp_io_work(). The flow starts from
send_req() and will aggregate all the requests
on this CPU core.
- qedn_fw_cq_fp_wq(): process new FW completions, the flow starts from
the IRQ handler and for a single interrupt it will
process all the pending NVMeoF Completions under
polling mode.
Acked-by: Igor Russkikh <redacted>
Signed-off-by: Prabhakar Kushwaha <redacted>
Signed-off-by: Omkar Kulkarni <redacted>
Signed-off-by: Michal Kalderon <mkalderon@marvell.com>
Signed-off-by: Ariel Elior <redacted>
Signed-off-by: Shai Malin <redacted>
---
drivers/nvme/hw/qedn/Makefile | 2 +-
drivers/nvme/hw/qedn/qedn.h | 29 +++++++
drivers/nvme/hw/qedn/qedn_conn.c | 3 +
drivers/nvme/hw/qedn/qedn_main.c | 114 +++++++++++++++++++++++--
drivers/nvme/hw/qedn/qedn_task.c | 138 +++++++++++++++++++++++++++++++
5 files changed, 278 insertions(+), 8 deletions(-)
create mode 100644 drivers/nvme/hw/qedn/qedn_task.c
@@ -385,12 +403,24 @@ static int qedn_init_req(struct nvme_tcp_ofld_req *req)staticvoidqedn_commit_rqs(structnvme_tcp_ofld_queue*queue){-/* Placeholder - queue work */+structqedn_conn_ctx*conn_ctx;++conn_ctx=(structqedn_conn_ctx*)queue->private_data;++if(!list_empty(&conn_ctx->host_pend_req_list))+queue_work_on(conn_ctx->cpu,conn_ctx->nvme_req_fp_wq,+&conn_ctx->nvme_req_fp_wq_entry);}staticintqedn_send_req(structnvme_tcp_ofld_req*req){-/* Placeholder - qedn_send_req */+structqedn_conn_ctx*qedn_conn=(structqedn_conn_ctx*)req->queue->private_data;++/* Under the assumption that the cccid/tag will be in the range of 0 to sq_depth-1. */+if(!req->async&&qedn_validate_cccid_in_range(qedn_conn,req->rq->tag))+returnBLK_STS_NOTSUPP;++qedn_queue_request(qedn_conn,req);return0;}
@@ -434,9 +464,59 @@ struct qedn_conn_ctx *qedn_get_conn_hash(struct qedn_ctx *qedn, u16 icid)}/* Fastpath IRQ handler */+voidqedn_fw_cq_fp_handler(structqedn_fp_queue*fp_q)+{+u16sb_id,cq_prod_idx,cq_cons_idx;+structqedn_ctx*qedn=fp_q->qedn;+structnvmetcp_fw_cqe*cqe=NULL;++sb_id=fp_q->sb_id;+qed_sb_update_sb_idx(fp_q->sb_info);++/* rmb - to prevent missing new cqes */+rmb();++/* Read the latest cq_prod from the SB */+cq_prod_idx=*fp_q->cq_prod;+cq_cons_idx=qed_chain_get_cons_idx(&fp_q->cq_chain);++while(cq_cons_idx!=cq_prod_idx){+cqe=qed_chain_consume(&fp_q->cq_chain);+if(likely(cqe))+qedn_io_work_cq(qedn,cqe);+else+pr_err("Failed consuming cqe\n");++cq_cons_idx=qed_chain_get_cons_idx(&fp_q->cq_chain);++/* Check if new completions were posted */+if(unlikely(cq_prod_idx==cq_cons_idx)){+/* rmb - to prevent missing new cqes */+rmb();++/* Update the latest cq_prod from the SB */+cq_prod_idx=*fp_q->cq_prod;+}+}+}++staticvoidqedn_fw_cq_fq_wq_handler(structwork_struct*work)+{+structqedn_fp_queue*fp_q=container_of(work,structqedn_fp_queue,fw_cq_fp_wq_entry);++qedn_fw_cq_fp_handler(fp_q);+qed_sb_ack(fp_q->sb_info,IGU_INT_ENABLE,1);+}+staticirqreturn_tqedn_irq_handler(intirq,void*dev_id){-/* Placeholder */+structqedn_fp_queue*fp_q=dev_id;+structqedn_ctx*qedn=fp_q->qedn;++fp_q->cpu=smp_processor_id();++qed_sb_ack(fp_q->sb_info,IGU_INT_DISABLE,0);+queue_work_on(fp_q->cpu,qedn->fw_cq_fp_wq,&fp_q->fw_cq_fp_wq_entry);returnIRQ_HANDLED;}
@@ -584,6 +664,11 @@ static void qedn_free_function_queues(struct qedn_ctx *qedn)inti;/* Free workqueues */+destroy_workqueue(qedn->fw_cq_fp_wq);+qedn->fw_cq_fp_wq=NULL;++destroy_workqueue(qedn->nvme_req_fp_wq);+qedn->nvme_req_fp_wq=NULL;/* Free the fast path queues*/for(i=0;i<qedn->num_fw_cqs;i++){
@@ -651,7 +736,23 @@ static int qedn_alloc_function_queues(struct qedn_ctx *qedn)u64cq_phy_addr;inti;-/* Place holder - IO-path workqueues */+qedn->fw_cq_fp_wq=alloc_workqueue(QEDN_FW_CQ_FP_WQ_WORKQUEUE,+WQ_HIGHPRI|WQ_MEM_RECLAIM,0);+if(!qedn->fw_cq_fp_wq){+rc=-ENODEV;+pr_err("Unable to create fastpath FW CQ workqueue!\n");++returnrc;+}++qedn->nvme_req_fp_wq=alloc_workqueue(QEDN_NVME_REQ_FP_WQ_WORKQUEUE,+WQ_HIGHPRI|WQ_MEM_RECLAIM,1);+if(!qedn->nvme_req_fp_wq){+rc=-ENODEV;+pr_err("Unable to create fastpath qedn nvme workqueue!\n");++returnrc;+}qedn->fp_q_arr=kcalloc(qedn->num_fw_cqs,sizeof(structqedn_fp_queue),GFP_KERNEL);
Why don't you use threaded interrupts if you're spinning off a workqueue
for handling interrupts anyway?
@@ -0,0 +1,138 @@+// SPDX-License-Identifier: GPL-2.0+/*+*Copyright2021Marvell.Allrightsreserved.+*/++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt++/* Kernel includes */+#include<linux/kernel.h>++/* Driver includes */+#include"qedn.h"++inlineintqedn_validate_cccid_in_range(structqedn_conn_ctx*conn_ctx,u16cccid)+{+intrc=0;++if(unlikely(cccid>=conn_ctx->sq_depth)){+pr_err("cccid 0x%x out of range ( > sq depth)\n",cccid);+rc=-EINVAL;+}++returnrc;+}++staticboolqedn_process_req(structqedn_conn_ctx*qedn_conn)+{+returntrue;+}++/* The WQ handler can be call from 3 flows:+*1.queue_rq.+*2.async.+*3.selfrequeued+*Trytosendrequestsfromthependinglist.Ifarequestproccesshasfailed,+*re-registertotheworkqueue.+*Iftherearenoadditionalpendingrequests-exitthehandler.+*/+voidqedn_nvme_req_fp_wq_handler(structwork_struct*work)+{+structqedn_conn_ctx*qedn_conn;+boolmore=false;++qedn_conn=container_of(work,structqedn_conn_ctx,nvme_req_fp_wq_entry);+do{+if(mutex_trylock(&qedn_conn->nvme_req_mutex)){+more=qedn_process_req(qedn_conn);+qedn_conn->req=NULL;+mutex_unlock(&qedn_conn->nvme_req_mutex);+}+}while(more);++if(!list_empty(&qedn_conn->host_pend_req_list))+queue_work_on(qedn_conn->cpu,qedn_conn->nvme_req_fp_wq,+&qedn_conn->nvme_req_fp_wq_entry);+}++voidqedn_queue_request(structqedn_conn_ctx*qedn_conn,structnvme_tcp_ofld_req*req)+{+boolempty,res=false;++spin_lock(&qedn_conn->nvme_req_lock);+empty=list_empty(&qedn_conn->host_pend_req_list)&&!qedn_conn->req;+list_add_tail(&req->queue_entry,&qedn_conn->host_pend_req_list);+spin_unlock(&qedn_conn->nvme_req_lock);++/* attempt workqueue bypass */+if(qedn_conn->cpu==smp_processor_id()&&empty&&+mutex_trylock(&qedn_conn->nvme_req_mutex)){+res=qedn_process_req(qedn_conn);+qedn_conn->req=NULL;+mutex_unlock(&qedn_conn->nvme_req_mutex);+if(res||list_empty(&qedn_conn->host_pend_req_list))+return;+}elseif(req->last){+queue_work_on(qedn_conn->cpu,qedn_conn->nvme_req_fp_wq,+&qedn_conn->nvme_req_fp_wq_entry);+}+}+
Queueing a request?
Does wonders for your latency ... Can't you do without?
From: Hannes Reinecke <hare@suse.de> Date: 2021-05-02 11:49:06
On 4/29/21 9:09 PM, Shai Malin wrote:
quoted hunk
From: Prabhakar Kushwaha <redacted>
This patch will add support of Task and SGL which is used
for slowpath and fast path IO. here Task is IO granule used
by firmware to perform tasks
The internal implementation:
- Create task/sgl resources used by all connection
- Provide APIs to allocate and free task.
- Add task support during connection establishment i.e. slowpath
Acked-by: Igor Russkikh <redacted>
Signed-off-by: Prabhakar Kushwaha <redacted>
Signed-off-by: Omkar Kulkarni <redacted>
Signed-off-by: Michal Kalderon <mkalderon@marvell.com>
Signed-off-by: Ariel Elior <redacted>
Signed-off-by: Shai Malin <redacted>
---
drivers/nvme/hw/qedn/qedn.h | 66 +++++
drivers/nvme/hw/qedn/qedn_conn.c | 43 +++-
drivers/nvme/hw/qedn/qedn_main.c | 34 ++-
drivers/nvme/hw/qedn/qedn_task.c | 411 +++++++++++++++++++++++++++++++
4 files changed, 550 insertions(+), 4 deletions(-)
@@ -137,6 +167,27 @@ struct qedn_ctx {dma_addr_tfw_cq_array_phy;/* Physical address of fw_cq_array_virt */structworkqueue_struct*nvme_req_fp_wq;structworkqueue_struct*fw_cq_fp_wq;++/* Fast Path Tasks */+structqed_nvmetcp_tidtasks;+};++structqedn_task_ctx{+structqedn_conn_ctx*qedn_conn;+structqedn_ctx*qedn;+void*fw_task_ctx;+structqedn_fp_queue*fp_q;+structscatterlist*nvme_sg;+structnvme_tcp_ofld_req*req;/* currently proccessed request */+structlist_headentry;+spinlock_tlock;/* To protect task resources */+boolvalid;+unsignedlongflags;/* Used by qedn_task_flags */+u32task_size;+u16itid;+u16cccid;+intreq_direction;+structstorage_sgl_task_paramssgl_task_params;};structqedn_endpoint{
@@ -387,6 +401,8 @@ static int qedn_prep_and_offload_queue(struct qedn_conn_ctx *conn_ctx)set_bit(QEDN_CONN_RESRC_FW_SQ,&conn_ctx->resrc_state);INIT_LIST_HEAD(&conn_ctx->host_pend_req_list);spin_lock_init(&conn_ctx->nvme_req_lock);+atomic_set(&conn_ctx->num_active_tasks,0);+atomic_set(&conn_ctx->num_active_fw_tasks,0);rc=qed_ops->acquire_conn(qedn->cdev,&conn_ctx->conn_handle,
@@ -401,7 +417,32 @@ static int qedn_prep_and_offload_queue(struct qedn_conn_ctx *conn_ctx)conn_ctx->conn_handle);set_bit(QEDN_CONN_RESRC_ACQUIRE_CONN,&conn_ctx->resrc_state);-/* Placeholder - Allocate task resources and initialize fields */+qid=qedn_qid(conn_ctx->queue);+default_cq_idx=qid?qid-1:0;/* Offset adminq */++conn_ctx->default_cq=(default_cq_idx%qedn->num_fw_cqs);+fp_q=&qedn->fp_q_arr[conn_ctx->default_cq];+conn_ctx->fp_q=fp_q;+io_resrc=&fp_q->host_resrc;++/* The first connection on each fp_q will fill task+*resources+*/+spin_lock(&io_resrc->resources_lock);+if(io_resrc->num_alloc_tasks==0){+rc=qedn_alloc_tasks(conn_ctx);+if(rc){+pr_err("Failed allocating tasks: CID=0x%x\n",+conn_ctx->fw_cid);+spin_unlock(&io_resrc->resources_lock);+gotorel_conn;+}+}+spin_unlock(&io_resrc->resources_lock);++spin_lock_init(&conn_ctx->task_list_lock);+INIT_LIST_HEAD(&conn_ctx->active_task_list);+set_bit(QEDN_CONN_RESRC_TASKS,&conn_ctx->resrc_state);>rc=qedn_fetch_tcp_port(conn_ctx);if(rc)
@@ -657,8 +665,24 @@ static void qedn_remove_pf_from_gl_list(struct qedn_ctx *qedn)mutex_unlock(&qedn_glb.glb_mutex);}+staticvoidqedn_call_destroy_free_tasks(structqedn_fp_queue*fp_q,+structqedn_io_resources*io_resrc)+{+if(list_empty(&io_resrc->task_free_list))+return;++if(io_resrc->num_alloc_tasks!=io_resrc->num_free_tasks)+pr_err("Task Pool:Not all returned allocated=0x%x, free=0x%x\n",+io_resrc->num_alloc_tasks,io_resrc->num_free_tasks);++qedn_destroy_free_tasks(fp_q,io_resrc);+if(io_resrc->num_free_tasks)+pr_err("Expected num_free_tasks to be 0\n");+}+staticvoidqedn_free_function_queues(structqedn_ctx*qedn){+structqedn_io_resources*host_resrc;structqed_sb_info*sb_info=NULL;structqedn_fp_queue*fp_q;inti;
@@ -673,6 +697,9 @@ static void qedn_free_function_queues(struct qedn_ctx *qedn)/* Free the fast path queues*/for(i=0;i<qedn->num_fw_cqs;i++){fp_q=&qedn->fp_q_arr[i];+host_resrc=&fp_q->host_resrc;++qedn_call_destroy_free_tasks(fp_q,host_resrc);/* Free SB */sb_info=fp_q->sb_info;
@@ -11,6 +11,263 @@/* Driver includes */#include"qedn.h"+staticboolqedn_sgl_has_small_mid_sge(structnvmetcp_sge*sgl,u16sge_count)+{+u16sge_num;++if(sge_count>8){+for(sge_num=0;sge_num<sge_count;sge_num++){+if(le32_to_cpu(sgl[sge_num].sge_len)<+QEDN_FW_SLOW_IO_MIN_SGE_LIMIT)+returntrue;/* small middle SGE found */+}+}++returnfalse;/* no small middle SGEs */+}++staticintqedn_init_sgl(structqedn_ctx*qedn,structqedn_task_ctx*qedn_task)+{+structstorage_sgl_task_params*sgl_task_params;+enumdma_data_directiondma_dir;+structscatterlist*sg;+structrequest*rq;+u16num_sges;+intindex;+intrc;++sgl_task_params=&qedn_task->sgl_task_params;+rq=blk_mq_rq_from_pdu(qedn_task->req);+if(qedn_task->task_size==0){+sgl_task_params->num_sges=0;++return0;+}++/* Convert BIO to scatterlist */+num_sges=blk_rq_map_sg(rq->q,rq,qedn_task->nvme_sg);+if(qedn_task->req_direction==WRITE)+dma_dir=DMA_TO_DEVICE;+else+dma_dir=DMA_FROM_DEVICE;++/* DMA map the scatterlist */+if(dma_map_sg(&qedn->pdev->dev,qedn_task->nvme_sg,num_sges,dma_dir)!=num_sges){+pr_err("Couldn't map sgl\n");+rc=-EPERM;++returnrc;+}++sgl_task_params->total_buffer_size=qedn_task->task_size;+sgl_task_params->num_sges=num_sges;++for_each_sg(qedn_task->nvme_sg,sg,num_sges,index){+DMA_REGPAIR_LE(sgl_task_params->sgl[index].sge_addr,sg_dma_address(sg));+sgl_task_params->sgl[index].sge_len=cpu_to_le32(sg_dma_len(sg));+}++/* Relevant for Host Write Only */+sgl_task_params->small_mid_sge=(qedn_task->req_direction==READ)?+false:+qedn_sgl_has_small_mid_sge(sgl_task_params->sgl,+sgl_task_params->num_sges);++return0;+}++staticvoidqedn_free_nvme_sg(structqedn_task_ctx*qedn_task)+{+kfree(qedn_task->nvme_sg);+qedn_task->nvme_sg=NULL;+}++staticvoidqedn_free_fw_sgl(structqedn_task_ctx*qedn_task)+{+structqedn_ctx*qedn=qedn_task->qedn;+dma_addr_tsgl_pa;++sgl_pa=HILO_DMA_REGPAIR(qedn_task->sgl_task_params.sgl_phys_addr);+dma_free_coherent(&qedn->pdev->dev,+QEDN_MAX_FW_SGL_SIZE,+qedn_task->sgl_task_params.sgl,+sgl_pa);+qedn_task->sgl_task_params.sgl=NULL;+}++staticvoidqedn_destroy_single_task(structqedn_task_ctx*qedn_task)+{+u16itid;++itid=qedn_task->itid;+list_del(&qedn_task->entry);+qedn_free_nvme_sg(qedn_task);+qedn_free_fw_sgl(qedn_task);+kfree(qedn_task);+qedn_task=NULL;+}++voidqedn_destroy_free_tasks(structqedn_fp_queue*fp_q,+structqedn_io_resources*io_resrc)+{+structqedn_task_ctx*qedn_task,*task_tmp;++/* Destroy tasks from the free task list */+list_for_each_entry_safe(qedn_task,task_tmp,+&io_resrc->task_free_list,entry){+qedn_destroy_single_task(qedn_task);+io_resrc->num_free_tasks-=1;+}+}++staticintqedn_alloc_nvme_sg(structqedn_task_ctx*qedn_task)+{+intrc;++qedn_task->nvme_sg=kcalloc(QEDN_MAX_SGES_PER_TASK,+sizeof(*qedn_task->nvme_sg),GFP_KERNEL);+if(!qedn_task->nvme_sg){+rc=-ENOMEM;++returnrc;+}++return0;+}++staticintqedn_alloc_fw_sgl(structqedn_task_ctx*qedn_task)+{+structqedn_ctx*qedn=qedn_task->qedn_conn->qedn;+dma_addr_tfw_sgl_phys;++qedn_task->sgl_task_params.sgl=+dma_alloc_coherent(&qedn->pdev->dev,QEDN_MAX_FW_SGL_SIZE,+&fw_sgl_phys,GFP_KERNEL);+if(!qedn_task->sgl_task_params.sgl){+pr_err("Couldn't allocate FW sgl\n");++return-ENOMEM;+}++DMA_REGPAIR_LE(qedn_task->sgl_task_params.sgl_phys_addr,fw_sgl_phys);++return0;+}++staticinlinevoid*qedn_get_fw_task(structqed_nvmetcp_tid*info,u16itid)+{+return(void*)(info->blocks[itid/info->num_tids_per_block]++(itid%info->num_tids_per_block)*info->size);+}++staticstructqedn_task_ctx*qedn_alloc_task(structqedn_conn_ctx*conn_ctx,u16itid)+{+structqedn_ctx*qedn=conn_ctx->qedn;+structqedn_task_ctx*qedn_task;+void*fw_task_ctx;+intrc=0;++qedn_task=kzalloc(sizeof(*qedn_task),GFP_KERNEL);+if(!qedn_task)+returnNULL;+
As this is a pool, why don't you use mempools here?
From: Hannes Reinecke <hare@suse.de> Date: 2021-05-02 11:53:49
On 4/29/21 9:09 PM, Shai Malin wrote:
quoted hunk
From: Prabhakar Kushwaha <redacted>
Once a TCP connection established, the host sends an Initialize
Connection Request (ICReq) PDU to the controller.
Further Initialize Connection Response (ICResp) PDU received from
controller is processed by host to establish a connection and
exchange connection configuration parameters.
This patch present support of generation of ICReq and processing of
ICResp. It also update host configuration based on exchanged parameters.
Acked-by: Igor Russkikh <redacted>
Signed-off-by: Prabhakar Kushwaha <redacted>
Signed-off-by: Omkar Kulkarni <redacted>
Signed-off-by: Michal Kalderon <mkalderon@marvell.com>
Signed-off-by: Ariel Elior <redacted>
Signed-off-by: Shai Malin <redacted>
---
drivers/nvme/hw/qedn/qedn.h | 36 ++++
drivers/nvme/hw/qedn/qedn_conn.c | 317 ++++++++++++++++++++++++++++++-
drivers/nvme/hw/qedn/qedn_main.c | 22 +++
drivers/nvme/hw/qedn/qedn_task.c | 8 +-
4 files changed, 379 insertions(+), 4 deletions(-)
@@ -195,6 +213,9 @@ struct qedn_endpoint {structqed_chainfw_sq_chain;void__iomem*p_doorbell;+/* Spinlock for accessing FW queue */+spinlock_tdoorbell_lock;+/* TCP Params */__be32dst_addr[4];/* In network order */__be32src_addr[4];/* In network order */
@@ -34,6 +34,25 @@ inline int qedn_qid(struct nvme_tcp_ofld_queue *queue)returnqueue-queue->ctrl->queues;}+voidqedn_ring_doorbell(structqedn_conn_ctx*conn_ctx)+{+structnvmetcp_db_datadbell={0};+u16prod_idx;++dbell.agg_flags=0;+dbell.params|=DB_DEST_XCM<<NVMETCP_DB_DATA_DEST_SHIFT;+dbell.params|=DB_AGG_CMD_SET<<NVMETCP_DB_DATA_AGG_CMD_SHIFT;+dbell.params|=+DQ_XCM_ISCSI_SQ_PROD_CMD<<NVMETCP_DB_DATA_AGG_VAL_SEL_SHIFT;+dbell.params|=1<<NVMETCP_DB_DATA_BYPASS_EN_SHIFT;+prod_idx=qed_chain_get_prod_idx(&conn_ctx->ep.fw_sq_chain);+dbell.sq_prod=cpu_to_le16(prod_idx);++/* wmb - Make sure fw idx is coherent */+wmb();+writel(*(u32*)&dbell,conn_ctx->ep.p_doorbell);+}+intqedn_set_con_state(structqedn_conn_ctx*conn_ctx,enumqedn_conn_statenew_state){spin_lock_bh(&conn_ctx->conn_state_lock);
@@ -130,6 +149,71 @@ int qedn_initialize_endpoint(struct qedn_endpoint *ep, u8 *local_mac_addr,return-1;}+staticintqedn_alloc_icreq_pad(structqedn_conn_ctx*conn_ctx)+{+structqedn_ctx*qedn=conn_ctx->qedn;+structqedn_icreq_padding*icreq_pad;+u32*buffer;+intrc=0;++icreq_pad=kzalloc(sizeof(*icreq_pad),GFP_KERNEL);+if(!icreq_pad)+return-ENOMEM;++conn_ctx->icreq_pad=icreq_pad;+memset(&icreq_pad->sge,0,sizeof(icreq_pad->sge));+buffer=dma_alloc_coherent(&qedn->pdev->dev,+QEDN_ICREQ_FW_PAYLOAD,+&icreq_pad->pa,+GFP_KERNEL);+if(!buffer){+pr_err("Could not allocate icreq_padding SGE buffer.\n");+rc=-ENOMEM;+gotorelease_icreq_pad;+}++DMA_REGPAIR_LE(icreq_pad->sge.sge_addr,icreq_pad->pa);+icreq_pad->sge.sge_len=cpu_to_le32(QEDN_ICREQ_FW_PAYLOAD);+icreq_pad->buffer=buffer;+set_bit(QEDN_CONN_RESRC_ICREQ_PAD,&conn_ctx->resrc_state);++return0;++release_icreq_pad:+kfree(icreq_pad);+conn_ctx->icreq_pad=NULL;++returnrc;+}++staticvoidqedn_free_icreq_pad(structqedn_conn_ctx*conn_ctx)+{+structqedn_ctx*qedn=conn_ctx->qedn;+structqedn_icreq_padding*icreq_pad;+u32*buffer;++icreq_pad=conn_ctx->icreq_pad;+if(unlikely(!icreq_pad)){+pr_err("null ptr in icreq_pad in conn_ctx\n");+gotofinally;+}++buffer=icreq_pad->buffer;+if(buffer){+dma_free_coherent(&qedn->pdev->dev,+QEDN_ICREQ_FW_PAYLOAD,+(void*)buffer,+icreq_pad->pa);+icreq_pad->buffer=NULL;+}++kfree(icreq_pad);+conn_ctx->icreq_pad=NULL;++finally:+clear_bit(QEDN_CONN_RESRC_ICREQ_PAD,&conn_ctx->resrc_state);+}+staticvoidqedn_release_conn_ctx(structqedn_conn_ctx*conn_ctx){structqedn_ctx*qedn=conn_ctx->qedn;
@@ -309,6 +396,194 @@ void qedn_terminate_connection(struct qedn_conn_ctx *conn_ctx, int abrt_flag)queue_work(qctrl->sp_wq,&conn_ctx->sp_wq_entry);}+staticintqedn_nvmetcp_update_conn(structqedn_ctx*qedn,structqedn_conn_ctx*conn_ctx)+{+structqedn_negotiation_params*pdu_params=&conn_ctx->pdu_params;+structqed_nvmetcp_params_update*conn_info;+intrc;++conn_info=kzalloc(sizeof(*conn_info),GFP_KERNEL);+if(!conn_info)+return-ENOMEM;++conn_info->hdr_digest_en=pdu_params->hdr_digest;+conn_info->data_digest_en=pdu_params->data_digest;+conn_info->max_recv_pdu_length=QEDN_MAX_PDU_SIZE;+conn_info->max_io_size=QEDN_MAX_IO_SIZE;+conn_info->max_send_pdu_length=pdu_params->maxh2cdata;++rc=qed_ops->update_conn(qedn->cdev,conn_ctx->conn_handle,conn_info);+if(rc){+pr_err("Could not update connection\n");+rc=-ENXIO;+}++kfree(conn_info);++returnrc;+}++staticintqedn_update_ramrod(structqedn_conn_ctx*conn_ctx)+{+structqedn_ctx*qedn=conn_ctx->qedn;+intrc=0;++rc=qedn_set_con_state(conn_ctx,CONN_STATE_WAIT_FOR_UPDATE_EQE);+if(rc)+returnrc;++rc=qedn_nvmetcp_update_conn(qedn,conn_ctx);+if(rc)+returnrc;++if(conn_ctx->state!=CONN_STATE_WAIT_FOR_UPDATE_EQE){+pr_err("cid 0x%x: Unexpected state 0x%x after update ramrod\n",+conn_ctx->fw_cid,conn_ctx->state);++return-EINVAL;+}++returnrc;+}++staticintqedn_send_icreq(structqedn_conn_ctx*conn_ctx)+{+structnvmetcp_init_conn_req_hdr*icreq_ptr=NULL;+structstorage_sgl_task_params*sgl_task_params;+structnvmetcp_task_paramstask_params;+structqedn_task_ctx*qedn_task=NULL;+structnvme_tcp_icreq_pduicreq;+structnvmetcp_wqe*chain_sqe;+structnvmetcp_wqelocal_sqe;++qedn_task=qedn_get_task_from_pool_insist(conn_ctx,QEDN_ICREQ_CCCID);+if(!qedn_task)+return-EINVAL;++memset(&icreq,0,sizeof(icreq));+memset(&local_sqe,0,sizeof(local_sqe));++/* Initialize ICReq */+icreq.hdr.type=nvme_tcp_icreq;+icreq.hdr.hlen=sizeof(icreq);+icreq.hdr.pdo=0;+icreq.hdr.plen=cpu_to_le32(icreq.hdr.hlen);+icreq.pfv=cpu_to_le16(conn_ctx->required_params.pfv);+icreq.maxr2t=cpu_to_le32(conn_ctx->required_params.maxr2t);+icreq.hpda=conn_ctx->required_params.hpda;+if(conn_ctx->required_params.hdr_digest)+icreq.digest|=NVME_TCP_HDR_DIGEST_ENABLE;+if(conn_ctx->required_params.data_digest)+icreq.digest|=NVME_TCP_DATA_DIGEST_ENABLE;++qedn_swap_bytes((u32*)&icreq,+(sizeof(icreq)-QEDN_ICREQ_FW_PAYLOAD)/+sizeof(u32));++/* Initialize task params */+task_params.opq.lo=cpu_to_le32(((u64)(qedn_task))&0xffffffff);+task_params.opq.hi=cpu_to_le32(((u64)(qedn_task))>>32);+task_params.context=qedn_task->fw_task_ctx;+task_params.sqe=&local_sqe;+task_params.conn_icid=(u16)conn_ctx->conn_handle;+task_params.itid=qedn_task->itid;+task_params.cq_rss_number=conn_ctx->default_cq;+task_params.tx_io_size=QEDN_ICREQ_FW_PAYLOAD;+task_params.rx_io_size=0;/* Rx doesn't use SGL for icresp */++/* Init SGE for ICReq padding */+sgl_task_params=&qedn_task->sgl_task_params;+sgl_task_params->total_buffer_size=task_params.tx_io_size;+sgl_task_params->small_mid_sge=false;+sgl_task_params->num_sges=1;+memcpy(sgl_task_params->sgl,&conn_ctx->icreq_pad->sge,+sizeof(conn_ctx->icreq_pad->sge));+icreq_ptr=(structnvmetcp_init_conn_req_hdr*)&icreq;++qed_ops->init_icreq_exchange(&task_params,icreq_ptr,sgl_task_params,NULL);++qedn_set_con_state(conn_ctx,CONN_STATE_WAIT_FOR_IC_COMP);+atomic_inc(&conn_ctx->num_active_fw_tasks);++/* spin_lock - doorbell is accessed both Rx flow and response flow */+spin_lock(&conn_ctx->ep.doorbell_lock);+chain_sqe=qed_chain_produce(&conn_ctx->ep.fw_sq_chain);+memcpy(chain_sqe,&local_sqe,sizeof(local_sqe));+qedn_ring_doorbell(conn_ctx);+spin_unlock(&conn_ctx->ep.doorbell_lock);++return0;+}+
And this is what I meant. You _do_ swab bytes before sending it off to
the HW, _and_ you use the standard nvme-tcp PDU definitions.
So why do you have your own, byte-swapped versions of the PDUs?
From: Hannes Reinecke <hare@suse.de> Date: 2021-05-02 11:55:23
On 4/29/21 9:09 PM, Shai Malin wrote:
This patch will present the IO level functionality of qedn
nvme-tcp-offload host mode. The qedn_task_ctx structure is containing
various params and state of the current IO, and is mapped 1x1 to the
fw_task_ctx which is a HW and FW IO context.
A qedn_task is mapped directly to its parent connection.
For every new IO a qedn_task structure will be assigned and they will be
linked for the entire IO's life span.
The patch will include 2 flows:
1. Send new command to the FW:
The flow is: nvme_tcp_ofld_queue_rq() which invokes qedn_send_req()
which invokes qedn_queue_request() which will:
- Assign fw_task_ctx.
- Prepare the Read/Write SG buffer.
- Initialize the HW and FW context.
- Pass the IO to the FW.
2. Process the IO completion:
The flow is: qedn_irq_handler() which invokes qedn_fw_cq_fp_handler()
which invokes qedn_io_work_cq() which will:
- process the FW completion.
- Return the fw_task_ctx to the task pool.
- complete the nvme req.
Acked-by: Igor Russkikh <redacted>
Signed-off-by: Prabhakar Kushwaha <redacted>
Signed-off-by: Omkar Kulkarni <redacted>
Signed-off-by: Michal Kalderon <mkalderon@marvell.com>
Signed-off-by: Ariel Elior <redacted>
Signed-off-by: Shai Malin <redacted>
---
drivers/nvme/hw/qedn/qedn.h | 4 +
drivers/nvme/hw/qedn/qedn_conn.c | 1 +
drivers/nvme/hw/qedn/qedn_task.c | 269 ++++++++++++++++++++++++++++++-
3 files changed, 272 insertions(+), 2 deletions(-)
Reviewed-by: Hannes Reinecke <hare@suse.de>
Cheers,
Hannes
--
Dr. Hannes Reinecke Kernel Storage Architect
hare@suse.de +49 911 74053 688
SUSE Software Solutions GmbH, Maxfeldstr. 5, 90409 Nürnberg
HRB 36809 (AG Nürnberg), Geschäftsführer: Felix Imendörffer
From: Hannes Reinecke <hare@suse.de> Date: 2021-05-02 11:58:17
On 4/29/21 9:09 PM, Shai Malin wrote:
This patch will present the connection level functionalities:
- conn clear-sq: will release the FW restrictions in order to flush all
the pending IOs.
- drain: in case clear-sq is stuck, will release all the device FW
restrictions in order to flush all the pending IOs.
- task cleanup - will flush the IO level resources.
Acked-by: Igor Russkikh <redacted>
Signed-off-by: Prabhakar Kushwaha <redacted>
Signed-off-by: Omkar Kulkarni <redacted>
Signed-off-by: Michal Kalderon <mkalderon@marvell.com>
Signed-off-by: Ariel Elior <redacted>
Signed-off-by: Shai Malin <redacted>
---
drivers/nvme/hw/qedn/qedn.h | 8 ++
drivers/nvme/hw/qedn/qedn_conn.c | 133 ++++++++++++++++++++++++++++++-
drivers/nvme/hw/qedn/qedn_main.c | 1 +
drivers/nvme/hw/qedn/qedn_task.c | 27 ++++++-
4 files changed, 166 insertions(+), 3 deletions(-)
Reviewed-by: Hannes Reinecke <hare@suse.de>
Cheers,
Hannes
--
Dr. Hannes Reinecke Kernel Storage Architect
hare@suse.de +49 911 74053 688
SUSE Software Solutions GmbH, Maxfeldstr. 5, 90409 Nürnberg
HRB 36809 (AG Nürnberg), Geschäftsführer: Felix Imendörffer
With the goal of enabling a generic infrastructure that allows NVMe/TCP
offload devices like NICs to seamlessly plug into the NVMe-oF stack, this
patch series introduces the nvme-tcp-offload ULP host layer, which will
be a new transport type called "tcp-offload" and will serve as an
abstraction layer to work with vendor specific nvme-tcp offload drivers.
NVMeTCP offload is a full offload of the NVMeTCP protocol, this includes
both the TCP level and the NVMeTCP level.
The nvme-tcp-offload transport can co-exist with the existing tcp and
other transports. The tcp offload was designed so that stack changes are
kept to a bare minimum: only registering new transports.
All other APIs, ops etc. are identical to the regular tcp transport.
Representing the TCP offload as a new transport allows clear and manageable
differentiation between the connections which should use the offload path
and those that are not offloaded (even on the same device).
The nvme-tcp-offload layers and API compared to nvme-tcp and nvme-rdma:
* NVMe layer: *
[ nvme/nvme-fabrics/blk-mq ]
|
(nvme API and blk-mq API)
|
|
* Vendor agnostic transport layer: *
[ nvme-rdma ] [ nvme-tcp ] [ nvme-tcp-offload ]
| | |
(Verbs)
| | |
| (Socket)
| | |
| | (nvme-tcp-offload API)
| | |
| | |
* Vendor Specific Driver: *
| | |
[ qedr ]
| |
[ qede ]
|
[ qedn ]
Performance:
============
With this implementation on top of the Marvell qedn driver (using the
Marvell FastLinQ NIC), we were able to demonstrate the following CPU
utilization improvement:
On AMD EPYC 7402, 2.80GHz, 28 cores:
- For 16K queued read IOs, 16jobs, 4qd (50Gbps line rate):
Improved the CPU utilization from 15.1% with NVMeTCP SW to 4.7% with
NVMeTCP offload.
On Intel(R) Xeon(R) Gold 5122 CPU, 3.60GHz, 16 cores:
- For 512K queued read IOs, 16jobs, 4qd (25Gbps line rate):
Improved the CPU utilization from 16.3% with NVMeTCP SW to 1.1% with
NVMeTCP offload.
In addition, we were able to demonstrate the following latency improvement:
- For 200K read IOPS (16 jobs, 16 qd, with fio rate limiter):
Improved the average latency from 105 usec with NVMeTCP SW to 39 usec
with NVMeTCP offload.
Improved the 99.99 tail latency from 570 usec with NVMeTCP SW to 91 usec
with NVMeTCP offload.
The end-to-end offload latency was measured from fio while running against
back end of null device.
Upstream plan:
==============
Following this RFC, the series will be sent in a modular way so that changes
in each part will not impact the previous part.
- Part 1 (Patches 1-7):
The qed infrastructure, will be sent to 'netdev@vger.kernel.org'.
- Part 2 (Patch 8-15):
The nvme-tcp-offload patches, will be sent to
'linux-nvme@lists.infradead.org'.
- Part 3 (Packet 16-27):
The qedn patches, will be sent to 'linux-nvme@lists.infradead.org'.
Queue Initialization Design:
============================
The nvme-tcp-offload ULP module shall register with the existing
nvmf_transport_ops (.name = "tcp_offload"), nvme_ctrl_ops and blk_mq_ops.
The nvme-tcp-offload vendor driver shall register to nvme-tcp-offload ULP
with the following ops:
- claim_dev() - in order to resolve the route to the target according to
the paired net_dev.
- create_queue() - in order to create offloaded nvme-tcp queue.
The nvme-tcp-offload ULP module shall manage all the controller level
functionalities, call claim_dev and based on the return values shall call
the relevant module create_queue in order to create the admin queue and
the IO queues.
IO-path Design:
===============
The nvme-tcp-offload shall work at the IO-level - the nvme-tcp-offload
ULP module shall pass the request (the IO) to the nvme-tcp-offload vendor
driver and later, the nvme-tcp-offload vendor driver returns the request
completion (the IO completion).
No additional handling is needed in between; this design will reduce the
CPU utilization as we will describe below.
The nvme-tcp-offload vendor driver shall register to nvme-tcp-offload ULP
with the following IO-path ops:
- init_req()
- send_req() - in order to pass the request to the handling of the
offload driver that shall pass it to the vendor specific device.
- poll_queue()
Once the IO completes, the nvme-tcp-offload vendor driver shall call
command.done() that will invoke the nvme-tcp-offload ULP layer to
complete the request.
TCP events:
===========
The Marvell FastLinQ NIC HW engine handle all the TCP re-transmissions
and OOO events.
Teardown and errors:
====================
In case of NVMeTCP queue error the nvme-tcp-offload vendor driver shall
call the nvme_tcp_ofld_report_queue_err.
The nvme-tcp-offload vendor driver shall register to nvme-tcp-offload ULP
with the following teardown ops:
- drain_queue()
- destroy_queue()
The Marvell FastLinQ NIC HW engine:
====================================
The Marvell NIC HW engine is capable of offloading the entire TCP/IP
stack and managing up to 64K connections per PF, already implemented and
upstream use cases for this include iWARP (by the Marvell qedr driver)
and iSCSI (by the Marvell qedi driver).
In addition, the Marvell NIC HW engine offloads the NVMeTCP queue layer
and is able to manage the IO level also in case of TCP re-transmissions
and OOO events.
The HW engine enables direct data placement (including the data digest CRC
calculation and validation) and direct data transmission (including data
digest CRC calculation).
The Marvell qedn driver:
========================
The new driver will be added under "drivers/nvme/hw" and will be enabled
by the Kconfig "Marvell NVM Express over Fabrics TCP offload".
As part of the qedn init, the driver will register as a pci device driver
and will work with the Marvell fastlinQ NIC.
As part of the probe, the driver will register to the nvme_tcp_offload
(ULP) and to the qed module (qed_nvmetcp_ops) - similar to other
"qed_*_ops" which are used by the qede, qedr, qedf and qedi device
drivers.
QEDN Future work:
=================
- Support extended HW resources.
- Digest support.
- Devlink support for device configuration and TCP offload configurations.
- Statistics
Long term future work:
======================
- The nvme-tcp-offload ULP target abstraction layer.
- The Marvell nvme-tcp-offload "qednt" target driver.
Changes since RFC v1:
=====================
- Fix nvme_tcp_ofld_ops return values.
- Remove NVMF_TRTYPE_TCP_OFFLOAD.
- Add nvme_tcp_ofld_poll() implementation.
- Fix nvme_tcp_ofld_queue_rq() to check map_sg() and send_req() return
values.
Changes since RFC v2:
=====================
- Add qedn - Marvell's NVMeTCP HW offload vendor driver init and probe
(patches 8-11).
- Fixes in controller and queue level (patches 3-6).
Changes since RFC v3:
=====================
- Add the full implementation of the nvme-tcp-offload layer including the
new ops: setup_ctrl(), release_ctrl(), commit_rqs() and new flows (ASYNC
and timeout).
- Add nvme-tcp-offload device maximums: max_hw_sectors, max_segments.
- Add nvme-tcp-offload layer design and optimization changes.
- Add the qedn full implementation for the conn level, IO path and error
handling.
- Add qed support for the new AHP HW.
Arie Gershberg (3):
nvme-fabrics: Move NVMF_ALLOWED_OPTS and NVMF_REQUIRED_OPTS
definitions
nvme-tcp-offload: Add controller level implementation
nvme-tcp-offload: Add controller level error recovery implementation
Dean Balandin (3):
nvme-tcp-offload: Add device scan implementation
nvme-tcp-offload: Add queue level implementation
nvme-tcp-offload: Add IO level implementation
Nikolay Assa (2):
qed: Add IP services APIs support
qedn: Add qedn_claim_dev API support
Omkar Kulkarni (1):
qed: Add qed-NVMeTCP personality
Prabhakar Kushwaha (6):
qed: Add support of HW filter block
qedn: Add connection-level slowpath functionality
qedn: Add support of configuring HW filter block
qedn: Add support of Task and SGL
qedn: Add support of NVME ICReq & ICResp
qedn: Add support of ASYNC
Shai Malin (12):
qed: Add NVMeTCP Offload PF Level FW and HW HSI
qed: Add NVMeTCP Offload Connection Level FW and HW HSI
qed: Add NVMeTCP Offload IO Level FW and HW HSI
qed: Add NVMeTCP Offload IO Level FW Initializations
nvme-tcp-offload: Add nvme-tcp-offload - NVMeTCP HW offload ULP
nvme-tcp-offload: Add Timeout and ASYNC Support
qedn: Add qedn - Marvell's NVMeTCP HW offload vendor driver
qedn: Add qedn probe
qedn: Add IRQ and fast-path resources initializations
qedn: Add IO level nvme_req and fw_cq workqueues
qedn: Add IO level fastpath functionality
qedn: Add Connection and IO level recovery flows
MAINTAINERS | 10 +
drivers/net/ethernet/qlogic/Kconfig | 3 +
drivers/net/ethernet/qlogic/qed/Makefile | 5 +
drivers/net/ethernet/qlogic/qed/qed.h | 16 +
drivers/net/ethernet/qlogic/qed/qed_cxt.c | 32 +
drivers/net/ethernet/qlogic/qed/qed_cxt.h | 1 +
drivers/net/ethernet/qlogic/qed/qed_dev.c | 151 +-
drivers/net/ethernet/qlogic/qed/qed_hsi.h | 4 +-
drivers/net/ethernet/qlogic/qed/qed_ll2.c | 31 +-
drivers/net/ethernet/qlogic/qed/qed_mcp.c | 3 +
drivers/net/ethernet/qlogic/qed/qed_mng_tlv.c | 3 +-
drivers/net/ethernet/qlogic/qed/qed_nvmetcp.c | 868 +++++++++++
drivers/net/ethernet/qlogic/qed/qed_nvmetcp.h | 114 ++
.../qlogic/qed/qed_nvmetcp_fw_funcs.c | 372 +++++
.../qlogic/qed/qed_nvmetcp_fw_funcs.h | 43 +
.../qlogic/qed/qed_nvmetcp_ip_services.c | 239 +++
drivers/net/ethernet/qlogic/qed/qed_ooo.c | 5 +-
drivers/net/ethernet/qlogic/qed/qed_sp.h | 5 +
.../net/ethernet/qlogic/qed/qed_sp_commands.c | 1 +
drivers/nvme/Kconfig | 1 +
drivers/nvme/Makefile | 1 +
drivers/nvme/host/Kconfig | 16 +
drivers/nvme/host/Makefile | 3 +
drivers/nvme/host/fabrics.c | 7 -
drivers/nvme/host/fabrics.h | 7 +
drivers/nvme/host/tcp-offload.c | 1330 +++++++++++++++++
drivers/nvme/host/tcp-offload.h | 209 +++
drivers/nvme/hw/Kconfig | 9 +
drivers/nvme/hw/Makefile | 3 +
drivers/nvme/hw/qedn/Makefile | 4 +
drivers/nvme/hw/qedn/qedn.h | 435 ++++++
drivers/nvme/hw/qedn/qedn_conn.c | 999 +++++++++++++
drivers/nvme/hw/qedn/qedn_main.c | 1153 ++++++++++++++
drivers/nvme/hw/qedn/qedn_task.c | 977 ++++++++++++
include/linux/qed/common_hsi.h | 1 +
include/linux/qed/nvmetcp_common.h | 616 ++++++++
include/linux/qed/qed_if.h | 22 +
include/linux/qed/qed_nvmetcp_if.h | 244 +++
.../linux/qed/qed_nvmetcp_ip_services_if.h | 29 +
39 files changed, 7947 insertions(+), 25 deletions(-)
create mode 100644 drivers/net/ethernet/qlogic/qed/qed_nvmetcp.c
create mode 100644 drivers/net/ethernet/qlogic/qed/qed_nvmetcp.h
create mode 100644 drivers/net/ethernet/qlogic/qed/qed_nvmetcp_fw_funcs.c
create mode 100644 drivers/net/ethernet/qlogic/qed/qed_nvmetcp_fw_funcs.h
create mode 100644 drivers/net/ethernet/qlogic/qed/qed_nvmetcp_ip_services.c
create mode 100644 drivers/nvme/host/tcp-offload.c
create mode 100644 drivers/nvme/host/tcp-offload.h
create mode 100644 drivers/nvme/hw/Kconfig
create mode 100644 drivers/nvme/hw/Makefile
create mode 100644 drivers/nvme/hw/qedn/Makefile
create mode 100644 drivers/nvme/hw/qedn/qedn.h
create mode 100644 drivers/nvme/hw/qedn/qedn_conn.c
create mode 100644 drivers/nvme/hw/qedn/qedn_main.c
create mode 100644 drivers/nvme/hw/qedn/qedn_task.c
create mode 100644 include/linux/qed/nvmetcp_common.h
create mode 100644 include/linux/qed/qed_nvmetcp_if.h
create mode 100644 include/linux/qed/qed_nvmetcp_ip_services_if.h
I would structure this patchset slightly different, in putting the
NVMe-oF implementation at the start of the patchset; this will be where
you get most of the comment, and any change there will potentially
reflect back on the driver implementation, too.
Something to consider for the next round.
Will do. Thanks.
Cheers,
Hannes
--
Dr. Hannes Reinecke Kernel Storage Architect
hare@suse.de +49 911 74053 688
SUSE Software Solutions GmbH, Maxfeldstr. 5, 90409 Nürnberg
HRB 36809 (AG Nürnberg), Geschäftsführer: Felix Imendörffer
@@ -0,0 +1,282 @@+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)+/* Copyright 2021 Marvell. All rights reserved. */++#include<linux/types.h>+#include<asm/byteorder.h>+#include<asm/param.h>+#include<linux/delay.h>+#include<linux/dma-mapping.h>+#include<linux/etherdevice.h>+#include<linux/kernel.h>+#include<linux/log2.h>+#include<linux/module.h>+#include<linux/pci.h>+#include<linux/stddef.h>+#include<linux/string.h>+#include<linux/errno.h>+#include<linux/list.h>+#include<linux/qed/qed_nvmetcp_if.h>+#include"qed.h"+#include"qed_cxt.h"+#include"qed_dev_api.h"+#include"qed_hsi.h"+#include"qed_hw.h"+#include"qed_int.h"+#include"qed_nvmetcp.h"+#include"qed_ll2.h"+#include"qed_mcp.h"+#include"qed_sp.h"+#include"qed_reg_addr.h"++staticintqed_nvmetcp_async_event(structqed_hwfn*p_hwfn,u8fw_event_code,+u16echo,unionevent_ring_data*data,+u8fw_return_code)+{+if(p_hwfn->p_nvmetcp_info->event_cb){+structqed_nvmetcp_info*p_nvmetcp=p_hwfn->p_nvmetcp_info;++returnp_nvmetcp->event_cb(p_nvmetcp->event_context,+fw_event_code,data);+}else{+DP_NOTICE(p_hwfn,"nvmetcp async completion is not set\n");++return-EINVAL;+}+}++staticintqed_sp_nvmetcp_func_start(structqed_hwfn*p_hwfn,+enumspq_modecomp_mode,+structqed_spq_comp_cb*p_comp_addr,+void*event_context,+nvmetcp_event_cb_tasync_event_cb)+{+structnvmetcp_init_ramrod_params*p_ramrod=NULL;+structqed_nvmetcp_pf_params*p_params=NULL;+structscsi_init_func_queues*p_queue=NULL;+structnvmetcp_spe_func_init*p_init=NULL;+structqed_sp_init_datainit_data={};+structqed_spq_entry*p_ent=NULL;+intrc=0;+u16val;+u8i;++/* Get SPQ entry */+init_data.cid=qed_spq_get_cid(p_hwfn);+init_data.opaque_fid=p_hwfn->hw_info.opaque_fid;+init_data.comp_mode=comp_mode;+init_data.p_comp_data=p_comp_addr;++rc=qed_sp_init_request(p_hwfn,&p_ent,+NVMETCP_RAMROD_CMD_ID_INIT_FUNC,+PROTOCOLID_NVMETCP,&init_data);+if(rc)+returnrc;++p_ramrod=&p_ent->ramrod.nvmetcp_init;+p_init=&p_ramrod->nvmetcp_init_spe;+p_params=&p_hwfn->pf_params.nvmetcp_pf_params;+p_queue=&p_init->q_params;++p_init->num_sq_pages_in_ring=p_params->num_sq_pages_in_ring;+p_init->num_r2tq_pages_in_ring=p_params->num_r2tq_pages_in_ring;+p_init->num_uhq_pages_in_ring=p_params->num_uhq_pages_in_ring;+p_init->ll2_rx_queue_id=RESC_START(p_hwfn,QED_LL2_RAM_QUEUE)++p_params->ll2_ooo_queue_id;++SET_FIELD(p_init->flags,NVMETCP_SPE_FUNC_INIT_NVMETCP_MODE,1);++p_init->func_params.log_page_size=ilog2(PAGE_SIZE);+p_init->func_params.num_tasks=cpu_to_le16(p_params->num_tasks);+p_init->debug_flags=p_params->debug_mode;++DMA_REGPAIR_LE(p_queue->glbl_q_params_addr,+p_params->glbl_q_params_addr);++p_queue->cq_num_entries=cpu_to_le16(QED_NVMETCP_FW_CQ_SIZE);+p_queue->num_queues=p_params->num_queues;+val=RESC_START(p_hwfn,QED_CMDQS_CQS);+p_queue->queue_relative_offset=cpu_to_le16((u16)val);+p_queue->cq_sb_pi=p_params->gl_rq_pi;++for(i=0;i<p_params->num_queues;i++){+val=qed_get_igu_sb_id(p_hwfn,i);+p_queue->cq_cmdq_sb_num_arr[i]=cpu_to_le16(val);+}++SET_FIELD(p_queue->q_validity,+SCSI_INIT_FUNC_QUEUES_CMD_VALID,0);+p_queue->cmdq_num_entries=0;+p_queue->bdq_resource_id=(u8)RESC_START(p_hwfn,QED_BDQ);++/* p_ramrod->tcp_init.min_rto = cpu_to_le16(p_params->min_rto); */+p_ramrod->tcp_init.two_msl_timer=cpu_to_le32(QED_TCP_TWO_MSL_TIMER);+p_ramrod->tcp_init.tx_sws_timer=cpu_to_le16(QED_TCP_SWS_TIMER);+p_init->half_way_close_timeout=cpu_to_le16(QED_TCP_HALF_WAY_CLOSE_TIMEOUT);+p_ramrod->tcp_init.max_fin_rt=QED_TCP_MAX_FIN_RT;++SET_FIELD(p_ramrod->nvmetcp_init_spe.params,+NVMETCP_SPE_FUNC_INIT_MAX_SYN_RT,QED_TCP_MAX_FIN_RT);++p_hwfn->p_nvmetcp_info->event_context=event_context;+p_hwfn->p_nvmetcp_info->event_cb=async_event_cb;++qed_spq_register_async_cb(p_hwfn,PROTOCOLID_NVMETCP,+qed_nvmetcp_async_event);++returnqed_spq_post(p_hwfn,p_ent,NULL);+}++staticintqed_sp_nvmetcp_func_stop(structqed_hwfn*p_hwfn,+enumspq_modecomp_mode,+structqed_spq_comp_cb*p_comp_addr)+{+structqed_spq_entry*p_ent=NULL;+structqed_sp_init_datainit_data;+intrc;++/* Get SPQ entry */+memset(&init_data,0,sizeof(init_data));+init_data.cid=qed_spq_get_cid(p_hwfn);+init_data.opaque_fid=p_hwfn->hw_info.opaque_fid;+init_data.comp_mode=comp_mode;+init_data.p_comp_data=p_comp_addr;++rc=qed_sp_init_request(p_hwfn,&p_ent,+NVMETCP_RAMROD_CMD_ID_DESTROY_FUNC,+PROTOCOLID_NVMETCP,&init_data);+if(rc)+returnrc;++rc=qed_spq_post(p_hwfn,p_ent,NULL);++qed_spq_unregister_async_cb(p_hwfn,PROTOCOLID_NVMETCP);++returnrc;+}++staticintqed_fill_nvmetcp_dev_info(structqed_dev*cdev,+structqed_dev_nvmetcp_info*info)+{+structqed_hwfn*hwfn=QED_AFFIN_HWFN(cdev);+intrc;++memset(info,0,sizeof(*info));+rc=qed_fill_dev_info(cdev,&info->common);++info->port_id=MFW_PORT(hwfn);+info->num_cqs=FEAT_NUM(hwfn,QED_NVMETCP_CQ);++returnrc;+}++staticvoidqed_register_nvmetcp_ops(structqed_dev*cdev,+structqed_nvmetcp_cb_ops*ops,+void*cookie)+{+cdev->protocol_ops.nvmetcp=ops;+cdev->ops_cookie=cookie;+}++staticintqed_nvmetcp_stop(structqed_dev*cdev)+{+intrc;++if(!(cdev->flags&QED_FLAG_STORAGE_STARTED)){+DP_NOTICE(cdev,"nvmetcp already stopped\n");++return0;+}++if(!hash_empty(cdev->connections)){+DP_NOTICE(cdev,+"Can't stop nvmetcp - not all connections were returned\n");++return-EINVAL;+}++/* Stop the nvmetcp */+rc=qed_sp_nvmetcp_func_stop(QED_AFFIN_HWFN(cdev),QED_SPQ_MODE_EBLOCK,+NULL);+cdev->flags&=~QED_FLAG_STORAGE_STARTED;++returnrc;+}++staticintqed_nvmetcp_start(structqed_dev*cdev,+structqed_nvmetcp_tid*tasks,+void*event_context,+nvmetcp_event_cb_tasync_event_cb)+{+structqed_tid_mem*tid_info;+intrc;++if(cdev->flags&QED_FLAG_STORAGE_STARTED){+DP_NOTICE(cdev,"nvmetcp already started;\n");++return0;+}++rc=qed_sp_nvmetcp_func_start(QED_AFFIN_HWFN(cdev),+QED_SPQ_MODE_EBLOCK,NULL,+event_context,async_event_cb);+if(rc){+DP_NOTICE(cdev,"Failed to start nvmetcp\n");++returnrc;+}++cdev->flags|=QED_FLAG_STORAGE_STARTED;+hash_init(cdev->connections);++if(!tasks)+return0;++tid_info=kzalloc(sizeof(*tid_info),GFP_KERNEL);++if(!tid_info){+qed_nvmetcp_stop(cdev);++return-ENOMEM;+}++rc=qed_cxt_get_tid_mem_info(QED_AFFIN_HWFN(cdev),tid_info);+if(rc){+DP_NOTICE(cdev,"Failed to gather task information\n");+qed_nvmetcp_stop(cdev);+kfree(tid_info);++returnrc;+}++/* Fill task information */+tasks->size=tid_info->tid_size;+tasks->num_tids_per_block=tid_info->num_tids_per_block;+memcpy(tasks->blocks,tid_info->blocks,+MAX_TID_BLOCKS_NVMETCP*sizeof(u8*));++kfree(tid_info);++return0;+}++staticconststructqed_nvmetcp_opsqed_nvmetcp_ops_pass={+.common=&qed_common_ops_pass,+.ll2=&qed_ll2_ops_pass,+.fill_dev_info=&qed_fill_nvmetcp_dev_info,+.register_ops=&qed_register_nvmetcp_ops,+.start=&qed_nvmetcp_start,+.stop=&qed_nvmetcp_stop,++/* Placeholder - Connection level ops */+};++conststructqed_nvmetcp_ops*qed_get_nvmetcp_ops(void)+{+return&qed_nvmetcp_ops_pass;+}+EXPORT_SYMBOL(qed_get_nvmetcp_ops);++voidqed_put_nvmetcp_ops(void)+{+}+EXPORT_SYMBOL(qed_put_nvmetcp_ops);
Why not a separate Protocol ID?
Don't you expect iSCSI and NVMe-TCP to be run at the same time?
PROTOCOLID determines the FW resource layout, which is the same for iSCSI
and NVMeTCP.
I will change PROTOCOLID_NVMETCP and PROTOCOLID_ISCSI to
PROTOCOLID_TCP_ULP.
iSCSI and NVMeTCP can run concurrently on the device, but not on the same PF.
Both iSCSI and NVMeTCP PFs will use PROTOCOLID_TCP_ULP
@@ -542,6 +542,26 @@ struct qed_iscsi_pf_params {u8bdq_pbl_num_entries[3];};+structqed_nvmetcp_pf_params{+u64glbl_q_params_addr;+u16cq_num_entries;++u16num_cons;+u16num_tasks;++u8num_sq_pages_in_ring;+u8num_r2tq_pages_in_ring;+u8num_uhq_pages_in_ring;++u8num_queues;+u8gl_rq_pi;+u8gl_cmd_pi;+u8debug_mode;+u8ll2_ooo_queue_id;++u16min_rto;+};+structqed_rdma_pf_params{/* Supplied to QED during resource allocation (may affect the ILT and*thedoorbellBAR).
@@ -0,0 +1,72 @@+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */+/* Copyright 2021 Marvell. All rights reserved. */++#ifndef _QED_NVMETCP_IF_H+#define _QED_NVMETCP_IF_H+#include<linux/types.h>+#include<linux/qed/qed_if.h>++#define QED_NVMETCP_MAX_IO_SIZE 0x800000++typedefint(*nvmetcp_event_cb_t)(void*context,+u8fw_event_code,void*fw_handle);++structqed_dev_nvmetcp_info{+structqed_dev_infocommon;++u8port_id;/* Physical port */+u8num_cqs;+};++#define MAX_TID_BLOCKS_NVMETCP (512)+structqed_nvmetcp_tid{+u32size;/* In bytes per task */+u32num_tids_per_block;+u8*blocks[MAX_TID_BLOCKS_NVMETCP];+};++structqed_nvmetcp_cb_ops{+structqed_common_cb_opscommon;+};++/**+*structqed_nvmetcp_ops-qedNVMeTCPoperations.+*@common:commonoperationspointer+*@ll2:lightL2operationspointer+*@fill_dev_info:fillsNVMeTCPspecificinformation+*@paramcdev+*@paraminfo+*@return0onsuccess,otherwiseerrorvalue.+*@register_ops:registernvmetcpoperations+*@paramcdev+*@paramops-specifiedusingqed_nvmetcp_cb_ops+*@paramcookie-driverprivate+*@start:nvmetcpinFW+*@paramcdev+*@paramtasks-qedwillfillinformationabouttasks+*return0onsuccess,otherwiseerrorvalue.+*@stop:nvmetcpinFW+*@paramcdev+*return0onsuccess,otherwiseerrorvalue.+*/+structqed_nvmetcp_ops{+conststructqed_common_ops*common;++conststructqed_ll2_ops*ll2;++int(*fill_dev_info)(structqed_dev*cdev,+structqed_dev_nvmetcp_info*info);++void(*register_ops)(structqed_dev*cdev,+structqed_nvmetcp_cb_ops*ops,void*cookie);++int(*start)(structqed_dev*cdev,+structqed_nvmetcp_tid*tasks,+void*event_context,nvmetcp_event_cb_tasync_event_cb);++int(*stop)(structqed_dev*cdev);+};++conststructqed_nvmetcp_ops*qed_get_nvmetcp_ops(void);+voidqed_put_nvmetcp_ops(void);+#endif
As mentioned, please rearrange the patchset to have the NVMe-TCP patches
first, then the driver specific bits.
Sure.
Cheers,
Hannes
--
Dr. Hannes Reinecke Kernel Storage Architect
hare@suse.de +49 911 74053 688
SUSE Software Solutions GmbH, Maxfeldstr. 5, 90409 Nürnberg
HRB 36809 (AG Nürnberg), Geschäftsführer: Felix Imendörffer
@@ -259,6 +259,578 @@ static int qed_nvmetcp_start(struct qed_dev *cdev,return0;}+staticstructqed_hash_nvmetcp_con*qed_nvmetcp_get_hash(structqed_dev*cdev,+u32handle)+{+structqed_hash_nvmetcp_con*hash_con=NULL;++if(!(cdev->flags&QED_FLAG_STORAGE_STARTED))+returnNULL;++hash_for_each_possible(cdev->connections,hash_con,node,handle){+if(hash_con->con->icid==handle)+break;+}++if(!hash_con||hash_con->con->icid!=handle)+returnNULL;++returnhash_con;+}++staticintqed_sp_nvmetcp_conn_offload(structqed_hwfn*p_hwfn,+structqed_nvmetcp_conn*p_conn,+enumspq_modecomp_mode,+structqed_spq_comp_cb*p_comp_addr)+{+structnvmetcp_spe_conn_offload*p_ramrod=NULL;+structtcp_offload_params_opt2*p_tcp2=NULL;+structqed_sp_init_datainit_data={0};+structqed_spq_entry*p_ent=NULL;+dma_addr_tr2tq_pbl_addr;+dma_addr_txhq_pbl_addr;+dma_addr_tuhq_pbl_addr;+u16physical_q;+intrc=0;+u32dval;+u8i;++/* Get SPQ entry */+init_data.cid=p_conn->icid;+init_data.opaque_fid=p_hwfn->hw_info.opaque_fid;+init_data.comp_mode=comp_mode;+init_data.p_comp_data=p_comp_addr;++rc=qed_sp_init_request(p_hwfn,&p_ent,+NVMETCP_RAMROD_CMD_ID_OFFLOAD_CONN,+PROTOCOLID_NVMETCP,&init_data);+if(rc)+returnrc;++p_ramrod=&p_ent->ramrod.nvmetcp_conn_offload;++/* Transmission PQ is the first of the PF */+physical_q=qed_get_cm_pq_idx(p_hwfn,PQ_FLAGS_OFLD);+p_conn->physical_q0=cpu_to_le16(physical_q);+p_ramrod->nvmetcp.physical_q0=cpu_to_le16(physical_q);++/* nvmetcp Pure-ACK PQ */+physical_q=qed_get_cm_pq_idx(p_hwfn,PQ_FLAGS_ACK);+p_conn->physical_q1=cpu_to_le16(physical_q);+p_ramrod->nvmetcp.physical_q1=cpu_to_le16(physical_q);++p_ramrod->conn_id=cpu_to_le16(p_conn->conn_id);++DMA_REGPAIR_LE(p_ramrod->nvmetcp.sq_pbl_addr,p_conn->sq_pbl_addr);++r2tq_pbl_addr=qed_chain_get_pbl_phys(&p_conn->r2tq);+DMA_REGPAIR_LE(p_ramrod->nvmetcp.r2tq_pbl_addr,r2tq_pbl_addr);++xhq_pbl_addr=qed_chain_get_pbl_phys(&p_conn->xhq);+DMA_REGPAIR_LE(p_ramrod->nvmetcp.xhq_pbl_addr,xhq_pbl_addr);++uhq_pbl_addr=qed_chain_get_pbl_phys(&p_conn->uhq);+DMA_REGPAIR_LE(p_ramrod->nvmetcp.uhq_pbl_addr,uhq_pbl_addr);++p_ramrod->nvmetcp.flags=p_conn->offl_flags;+p_ramrod->nvmetcp.default_cq=p_conn->default_cq;+p_ramrod->nvmetcp.initial_ack=0;++DMA_REGPAIR_LE(p_ramrod->nvmetcp.nvmetcp.cccid_itid_table_addr,+p_conn->nvmetcp_cccid_itid_table_addr);+p_ramrod->nvmetcp.nvmetcp.cccid_max_range=+cpu_to_le16(p_conn->nvmetcp_cccid_max_range);++p_tcp2=&p_ramrod->tcp;++qed_set_fw_mac_addr(&p_tcp2->remote_mac_addr_hi,+&p_tcp2->remote_mac_addr_mid,+&p_tcp2->remote_mac_addr_lo,p_conn->remote_mac);+qed_set_fw_mac_addr(&p_tcp2->local_mac_addr_hi,+&p_tcp2->local_mac_addr_mid,+&p_tcp2->local_mac_addr_lo,p_conn->local_mac);++p_tcp2->vlan_id=cpu_to_le16(p_conn->vlan_id);+p_tcp2->flags=cpu_to_le16(p_conn->tcp_flags);++p_tcp2->ip_version=p_conn->ip_version;+for(i=0;i<4;i++){+dval=p_conn->remote_ip[i];+p_tcp2->remote_ip[i]=cpu_to_le32(dval);+dval=p_conn->local_ip[i];+p_tcp2->local_ip[i]=cpu_to_le32(dval);+}+
What is this?
Some convoluted way of assigning the IP address in little endian?
Pointless if it's IPv4, as then each bit is just one byte.
And if it's for IPv6, what do you do for IPv4?
And isn't there a helper for it?
Endianity here is only for BE machines.
I haven't found a relevant helper function,
Will re-write to have cleaner implementation separately for ipv4 and ipv6.
@@ -2106,6 +2106,30 @@ int qed_cxt_set_pf_params(struct qed_hwfn *p_hwfn, u32 rdma_tasks)}break;}+caseQED_PCI_NVMETCP:+{+structqed_nvmetcp_pf_params*p_params;++p_params=&p_hwfn->pf_params.nvmetcp_pf_params;++if(p_params->num_cons&&p_params->num_tasks){+qed_cxt_set_proto_cid_count(p_hwfn,+PROTOCOLID_NVMETCP,+p_params->num_cons,+0);++qed_cxt_set_proto_tid_count(p_hwfn,+PROTOCOLID_NVMETCP,+QED_CTX_NVMETCP_TID_SEG,+0,+p_params->num_tasks,+true);+}else{+DP_INFO(p_hwfn->cdev,+"NvmeTCP personality used without setting params!\n");+}+break;+}default:return-EINVAL;}
@@ -2132,6 +2156,10 @@ int qed_cxt_get_tid_mem_info(struct qed_hwfn *p_hwfn,proto=PROTOCOLID_ISCSI;seg=QED_CXT_ISCSI_TID_SEG;break;+caseQED_PCI_NVMETCP:+proto=PROTOCOLID_NVMETCP;+seg=QED_CTX_NVMETCP_TID_SEG;+break;default:return-EINVAL;}
@@ -2458,6 +2486,10 @@ int qed_cxt_get_task_ctx(struct qed_hwfn *p_hwfn,proto=PROTOCOLID_ISCSI;seg=QED_CXT_ISCSI_TID_SEG;break;+caseQED_PCI_NVMETCP:+proto=PROTOCOLID_NVMETCP;+seg=QED_CTX_NVMETCP_TID_SEG;+break;default:return-EINVAL;}
@@ -667,7 +668,8 @@ qed_llh_set_engine_affin(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt)}/* Storage PF is bound to a single engine while L2 PF uses both */-if(QED_IS_FCOE_PERSONALITY(p_hwfn)||QED_IS_ISCSI_PERSONALITY(p_hwfn))+if(QED_IS_FCOE_PERSONALITY(p_hwfn)||QED_IS_ISCSI_PERSONALITY(p_hwfn)||+QED_IS_NVMETCP_PERSONALITY(p_hwfn))eng=cdev->fir_affin?QED_ENG1:QED_ENG0;else/* L2_PERSONALITY */eng=QED_BOTH_ENG;
@@ -960,7 +960,8 @@ static int qed_sp_ll2_rx_queue_start(struct qed_hwfn *p_hwfn,if(test_bit(QED_MF_LL2_NON_UNICAST,&p_hwfn->cdev->mf_bits)&&p_ramrod->main_func_queue&&conn_type!=QED_LL2_TYPE_ROCE&&-conn_type!=QED_LL2_TYPE_IWARP){+conn_type!=QED_LL2_TYPE_IWARP&&+(!QED_IS_NVMETCP_PERSONALITY(p_hwfn))){p_ramrod->mf_si_bcast_accept_all=1;p_ramrod->mf_si_mcast_accept_all=1;}else{
@@ -1049,6 +1050,8 @@ static int qed_sp_ll2_tx_queue_start(struct qed_hwfn *p_hwfn,caseQED_LL2_TYPE_OOO:if(p_hwfn->hw_info.personality==QED_PCI_ISCSI)p_ramrod->conn_type=PROTOCOLID_ISCSI;+elseif(p_hwfn->hw_info.personality==QED_PCI_NVMETCP)+p_ramrod->conn_type=PROTOCOLID_NVMETCP;elsep_ramrod->conn_type=PROTOCOLID_IWARP;break;
@@ -1634,7 +1637,8 @@ int qed_ll2_establish_connection(void *cxt, u8 connection_handle)if(rc)gotoout;-if(!QED_IS_RDMA_PERSONALITY(p_hwfn))+if(!QED_IS_RDMA_PERSONALITY(p_hwfn)&&+!QED_IS_NVMETCP_PERSONALITY(p_hwfn))qed_wr(p_hwfn,p_ptt,PRS_REG_USE_LIGHT_L2,1);qed_ll2_establish_connection_ooo(p_hwfn,p_ll2_conn);
@@ -2376,7 +2380,8 @@ static int qed_ll2_start_ooo(struct qed_hwfn *p_hwfn,staticboolqed_ll2_is_storage_eng1(structqed_dev*cdev){return(QED_IS_FCOE_PERSONALITY(QED_LEADING_HWFN(cdev))||-QED_IS_ISCSI_PERSONALITY(QED_LEADING_HWFN(cdev)))&&+QED_IS_ISCSI_PERSONALITY(QED_LEADING_HWFN(cdev))||+QED_IS_NVMETCP_PERSONALITY(QED_LEADING_HWFN(cdev)))&&(QED_AFFIN_HWFN(cdev)!=QED_LEADING_HWFN(cdev));}
@@ -2402,11 +2407,13 @@ static int qed_ll2_stop(struct qed_dev *cdev)if(cdev->ll2->handle==QED_LL2_UNUSED_HANDLE)return0;+if(!QED_IS_NVMETCP_PERSONALITY(p_hwfn))+qed_llh_remove_mac_filter(cdev,0,cdev->ll2_mac_address);qed_llh_remove_mac_filter(cdev,0,cdev->ll2_mac_address);eth_zero_addr(cdev->ll2_mac_address);-if(QED_IS_ISCSI_PERSONALITY(p_hwfn))+if(QED_IS_ISCSI_PERSONALITY(p_hwfn)||QED_IS_NVMETCP_PERSONALITY(p_hwfn))qed_ll2_stop_ooo(p_hwfn);/* In CMT mode, LL2 is always started on engine 0 for a storage PF */
@@ -2442,6 +2449,7 @@ static int __qed_ll2_start(struct qed_hwfn *p_hwfn,conn_type=QED_LL2_TYPE_FCOE;break;caseQED_PCI_ISCSI:+caseQED_PCI_NVMETCP:conn_type=QED_LL2_TYPE_ISCSI;break;caseQED_PCI_ETH_ROCE:
@@ -2576,10 +2584,13 @@ static int qed_ll2_start(struct qed_dev *cdev, struct qed_ll2_params *params)}}-rc=qed_llh_add_mac_filter(cdev,0,params->ll2_mac_address);-if(rc){-DP_NOTICE(cdev,"Failed to add an LLH filter\n");-gotoerr3;+if(!QED_IS_NVMETCP_PERSONALITY(p_hwfn)){+rc=qed_llh_add_mac_filter(cdev,0,params->ll2_mac_address);+if(rc){+DP_NOTICE(cdev,"Failed to add an LLH filter\n");+gotoerr3;+}+}ether_addr_copy(cdev->ll2_mac_address,params->ll2_mac_address);
@@ -0,0 +1,239 @@+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)+/*+*Copyright2021Marvell.Allrightsreserved.+*/++#include<linux/types.h>+#include<asm/byteorder.h>+#include<asm/param.h>+#include<linux/delay.h>+#include<linux/pci.h>+#include<linux/dma-mapping.h>+#include<linux/etherdevice.h>+#include<linux/kernel.h>+#include<linux/stddef.h>+#include<linux/errno.h>++#include<net/tcp.h>++#include<linux/qed/qed_nvmetcp_ip_services_if.h>++#define QED_IP_RESOL_TIMEOUT 4++intqed_route_ipv4(structsockaddr_storage*local_addr,+structsockaddr_storage*remote_addr,+structsockaddr*hardware_address,+structnet_device**ndev)+{+structneighbour*neigh=NULL;+__be32*loc_ip,*rem_ip;+structrtable*rt;+intrc=-ENXIO;+intretry;++loc_ip=&((structsockaddr_in*)local_addr)->sin_addr.s_addr;+rem_ip=&((structsockaddr_in*)remote_addr)->sin_addr.s_addr;+*ndev=NULL;+rt=ip_route_output(&init_net,*rem_ip,*loc_ip,0/*tos*/,0/*oif*/);+if(IS_ERR(rt)){+pr_err("lookup route failed\n");+rc=PTR_ERR(rt);+gotoreturn_err;+}++neigh=dst_neigh_lookup(&rt->dst,rem_ip);+if(!neigh){+rc=-ENOMEM;+ip_rt_put(rt);+gotoreturn_err;+}++*ndev=rt->dst.dev;+ip_rt_put(rt);++/* If not resolved, kick-off state machine towards resolution */+if(!(neigh->nud_state&NUD_VALID))+neigh_event_send(neigh,NULL);++/* query neighbor until resolved or timeout */+retry=QED_IP_RESOL_TIMEOUT;+while(!(neigh->nud_state&NUD_VALID)&&retry>0){+msleep(1000);+retry--;+}++if(neigh->nud_state&NUD_VALID){+/* copy resolved MAC address */+neigh_ha_snapshot(hardware_address->sa_data,neigh,*ndev);++hardware_address->sa_family=(*ndev)->type;+rc=0;+}++neigh_release(neigh);+if(!(*loc_ip)){+*loc_ip=inet_select_addr(*ndev,*rem_ip,RT_SCOPE_UNIVERSE);+local_addr->ss_family=AF_INET;+}++return_err:++returnrc;+}+EXPORT_SYMBOL(qed_route_ipv4);++intqed_route_ipv6(structsockaddr_storage*local_addr,+structsockaddr_storage*remote_addr,+structsockaddr*hardware_address,+structnet_device**ndev)+{+structneighbour*neigh=NULL;+structdst_entry*dst;+structflowi6fl6;+intrc=-ENXIO;+intretry;++memset(&fl6,0,sizeof(fl6));+fl6.saddr=((structsockaddr_in6*)local_addr)->sin6_addr;+fl6.daddr=((structsockaddr_in6*)remote_addr)->sin6_addr;++dst=ip6_route_output(&init_net,NULL,&fl6);+if(!dst||dst->error){+if(dst){+dst_release(dst);+pr_err("lookup route failed %d\n",dst->error);+}++gotoout;+}++neigh=dst_neigh_lookup(dst,&fl6.daddr);+if(neigh){+*ndev=ip6_dst_idev(dst)->dev;++/* If not resolved, kick-off state machine towards resolution */+if(!(neigh->nud_state&NUD_VALID))+neigh_event_send(neigh,NULL);++/* query neighbor until resolved or timeout */+retry=QED_IP_RESOL_TIMEOUT;+while(!(neigh->nud_state&NUD_VALID)&&retry>0){+msleep(1000);+retry--;+}++if(neigh->nud_state&NUD_VALID){+neigh_ha_snapshot((u8*)hardware_address->sa_data,neigh,*ndev);++hardware_address->sa_family=(*ndev)->type;+rc=0;+}++neigh_release(neigh);++if(ipv6_addr_any(&fl6.saddr)){+if(ipv6_dev_get_saddr(dev_net(*ndev),*ndev,+&fl6.daddr,0,&fl6.saddr)){+pr_err("Unable to find source IP address\n");+gotoout;+}++local_addr->ss_family=AF_INET6;+((structsockaddr_in6*)local_addr)->sin6_addr=+fl6.saddr;+}+}++dst_release(dst);++out:++returnrc;+}+EXPORT_SYMBOL(qed_route_ipv6);++voidqed_vlan_get_ndev(structnet_device**ndev,u16*vlan_id)+{+if(is_vlan_dev(*ndev)){+*vlan_id=vlan_dev_vlan_id(*ndev);+*ndev=vlan_dev_real_dev(*ndev);+}+}+EXPORT_SYMBOL(qed_vlan_get_ndev);++structpci_dev*qed_validate_ndev(structnet_device*ndev)+{+structpci_dev*pdev=NULL;+structnet_device*upper;++for_each_pci_dev(pdev){+if(pdev&&pdev->driver&&+!strcmp(pdev->driver->name,"qede")){+upper=pci_get_drvdata(pdev);+if(upper->ifindex==ndev->ifindex)+returnpdev;+}+}++returnNULL;+}+EXPORT_SYMBOL(qed_validate_ndev);++__be16qed_get_in_port(structsockaddr_storage*sa)+{+returnsa->ss_family==AF_INET+?((structsockaddr_in*)sa)->sin_port+:((structsockaddr_in6*)sa)->sin6_port;+}+EXPORT_SYMBOL(qed_get_in_port);++intqed_fetch_tcp_port(structsockaddr_storagelocal_ip_addr,+structsocket**sock,u16*port)+{+structsockaddr_storagesa;+intrc=0;++rc=sock_create(local_ip_addr.ss_family,SOCK_STREAM,IPPROTO_TCP,sock);+if(rc){+pr_warn("failed to create socket: %d\n",rc);+gotoerr;+}++(*sock)->sk->sk_allocation=GFP_KERNEL;+sk_set_memalloc((*sock)->sk);++rc=kernel_bind(*sock,(structsockaddr*)&local_ip_addr,+sizeof(local_ip_addr));++if(rc){+pr_warn("failed to bind socket: %d\n",rc);+gotoerr_sock;+}++rc=kernel_getsockname(*sock,(structsockaddr*)&sa);+if(rc<0){+pr_warn("getsockname() failed: %d\n",rc);+gotoerr_sock;+}++*port=ntohs(qed_get_in_port(&sa));++return0;++err_sock:+sock_release(*sock);+sock=NULL;+err:++returnrc;+}+EXPORT_SYMBOL(qed_fetch_tcp_port);++voidqed_return_tcp_port(structsocket*sock)+{+if(sock&&sock->sk){+tcp_set_state(sock->sk,TCP_CLOSE);+sock_release(sock);+}+}+EXPORT_SYMBOL(qed_return_tcp_port);
This patch will present the structure for the NVMeTCP offload common
layer driver. This module is added under "drivers/nvme/host/" and future
offload drivers which will register to it will be placed under
"drivers/nvme/hw".
This new driver will be enabled by the Kconfig "NVM Express over Fabrics
TCP offload commmon layer".
In order to support the new transport type, for host mode, no change is
needed.
Each new vendor-specific offload driver will register to this ULP during
its probe function, by filling out the nvme_tcp_ofld_dev->ops and
nvme_tcp_ofld_dev->private_data and calling nvme_tcp_ofld_register_dev
with the initialized struct.
The internal implementation:
- tcp-offload.h:
Includes all common structs and ops to be used and shared by offload
drivers.
- tcp-offload.c:
Includes the init function which registers as a NVMf transport just
like any other transport.
Acked-by: Igor Russkikh <redacted>
Signed-off-by: Dean Balandin <redacted>
Signed-off-by: Prabhakar Kushwaha <redacted>
Signed-off-by: Omkar Kulkarni <redacted>
Signed-off-by: Michal Kalderon <mkalderon@marvell.com>
Signed-off-by: Ariel Elior <redacted>
Signed-off-by: Shai Malin <redacted>
---
drivers/nvme/host/Kconfig | 16 +++
drivers/nvme/host/Makefile | 3 +
drivers/nvme/host/tcp-offload.c | 126 +++++++++++++++++++
drivers/nvme/host/tcp-offload.h | 206 ++++++++++++++++++++++++++++++++
4 files changed, 351 insertions(+)
create mode 100644 drivers/nvme/host/tcp-offload.c
create mode 100644 drivers/nvme/host/tcp-offload.h
It will be tricky to select the correct transport eg when traversing the
discovery log page; the discovery log page only knows about 'tcp' (not
'tcp_offload'), so the offload won't be picked up.
But that can we worked on / fixed later on, as it's arguably a policy
decision.
I agree that we should improve the policy decision and allow additional
capabilities and it may be discussed as a new NVMe TPAR.
Reviewed-by: Hannes Reinecke <hare@suse.de>
Thanks.
Cheers,
Hannes
--
Dr. Hannes Reinecke Kernel Storage Architect
hare@suse.de +49 911 74053 688
SUSE Software Solutions GmbH, Maxfeldstr. 5, 90409 Nürnberg
HRB 36809 (AG Nürnberg), Geschäftsführer: Felix Imendörffer
Why do you need them? None of the other transport drivers use them, why you?
Different HW devices that are offloading the NVMeTCP might have different
limitations of the allowed options.
For example, a device that does not support all the queue types.
With tcp and rdma, only the nvme-tcp and nvme-rdma layers handle those
attributes and the HW devices do not create any limitations for the allowed
options.
An alternative design could be to add separate fields in nvme_tcp_ofld_ops such
as max_hw_sectors and max_segments that we already have in this series.
Which would you prefer?
Cheers,
Hannes
--
Dr. Hannes Reinecke Kernel Storage Architect
hare@suse.de +49 911 74053 688
SUSE Software Solutions GmbH, Maxfeldstr. 5, 90409 Nürnberg
HRB 36809 (AG Nürnberg), Geschäftsführer: Felix Imendörffer
From: Arie Gershberg <redacted>
In this patch, we implement controller level error handling and recovery.
Upon an error discovered by the ULP or reset controller initiated by the
nvme-core (using reset_ctrl workqueue), the ULP will initiate a controller
recovery which includes teardown and re-connect of all queues.
Acked-by: Igor Russkikh <redacted>
Signed-off-by: Arie Gershberg <redacted>
Signed-off-by: Prabhakar Kushwaha <redacted>
Signed-off-by: Omkar Kulkarni <redacted>
Signed-off-by: Michal Kalderon <mkalderon@marvell.com>
Signed-off-by: Ariel Elior <redacted>
Signed-off-by: Shai Malin <redacted>
---
drivers/nvme/host/tcp-offload.c | 138 +++++++++++++++++++++++++++++++-
drivers/nvme/host/tcp-offload.h | 1 +
2 files changed, 137 insertions(+), 2 deletions(-)
@@ -296,6 +314,28 @@ nvme_tcp_ofld_configure_io_queues(struct nvme_ctrl *nctrl, bool new)returnrc;}+staticvoidnvme_tcp_ofld_reconnect_or_remove(structnvme_ctrl*nctrl)+{+/* If we are resetting/deleting then do nothing */+if(nctrl->state!=NVME_CTRL_CONNECTING){+WARN_ON_ONCE(nctrl->state==NVME_CTRL_NEW||+nctrl->state==NVME_CTRL_LIVE);++return;+}++if(nvmf_should_reconnect(nctrl)){+dev_info(nctrl->device,"Reconnecting in %d seconds...\n",+nctrl->opts->reconnect_delay);+queue_delayed_work(nvme_wq,+&to_tcp_ofld_ctrl(nctrl)->connect_work,+nctrl->opts->reconnect_delay*HZ);+}else{+dev_info(nctrl->device,"Removing controller...\n");+nvme_delete_ctrl(nctrl);+}+}+staticintnvme_tcp_ofld_setup_ctrl(structnvme_ctrl*nctrl,boolnew){structnvmf_ctrl_options*opts=nctrl->opts;
@@ -407,10 +447,68 @@ nvme_tcp_ofld_teardown_io_queues(struct nvme_ctrl *nctrl, bool remove)/* Placeholder - teardown_io_queues */}+staticvoidnvme_tcp_ofld_reconnect_ctrl_work(structwork_struct*work)+{+structnvme_tcp_ofld_ctrl*ctrl=+container_of(to_delayed_work(work),+structnvme_tcp_ofld_ctrl,+connect_work);+structnvme_ctrl*nctrl=&ctrl->nctrl;++++nctrl->nr_reconnects;++if(ctrl->dev->ops->setup_ctrl(ctrl,false))+gotorequeue;++if(nvme_tcp_ofld_setup_ctrl(nctrl,false))+gotorelease_and_requeue;++dev_info(nctrl->device,"Successfully reconnected (%d attempt)\n",+nctrl->nr_reconnects);++nctrl->nr_reconnects=0;++return;++release_and_requeue:+ctrl->dev->ops->release_ctrl(ctrl);+requeue:+dev_info(nctrl->device,"Failed reconnect attempt %d\n",+nctrl->nr_reconnects);+nvme_tcp_ofld_reconnect_or_remove(nctrl);+}++staticvoidnvme_tcp_ofld_error_recovery_work(structwork_struct*work)+{+structnvme_tcp_ofld_ctrl*ctrl=+container_of(work,structnvme_tcp_ofld_ctrl,err_work);+structnvme_ctrl*nctrl=&ctrl->nctrl;++nvme_stop_keep_alive(nctrl);+nvme_tcp_ofld_teardown_io_queues(nctrl,false);+/* unquiesce to fail fast pending requests */+nvme_start_queues(nctrl);+nvme_tcp_ofld_teardown_admin_queue(nctrl,false);+blk_mq_unquiesce_queue(nctrl->admin_q);++if(!nvme_change_ctrl_state(nctrl,NVME_CTRL_CONNECTING)){+/* state change failure is ok if we started nctrl delete */+WARN_ON_ONCE(nctrl->state!=NVME_CTRL_DELETING&&+nctrl->state!=NVME_CTRL_DELETING_NOIO);++return;+}++nvme_tcp_ofld_reconnect_or_remove(nctrl);+}+staticvoidnvme_tcp_ofld_teardown_ctrl(structnvme_ctrl*nctrl,boolshutdown){-/* Placeholder - err_work and connect_work */+structnvme_tcp_ofld_ctrl*ctrl=to_tcp_ofld_ctrl(nctrl);++cancel_work_sync(&ctrl->err_work);+cancel_delayed_work_sync(&ctrl->connect_work);nvme_tcp_ofld_teardown_io_queues(nctrl,shutdown);blk_mq_quiesce_queue(nctrl->admin_q);if(shutdown)
@@ -425,6 +523,38 @@ static void nvme_tcp_ofld_delete_ctrl(struct nvme_ctrl *nctrl)nvme_tcp_ofld_teardown_ctrl(nctrl,true);}+staticvoidnvme_tcp_ofld_reset_ctrl_work(structwork_struct*work)+{+structnvme_ctrl*nctrl=+container_of(work,structnvme_ctrl,reset_work);+structnvme_tcp_ofld_ctrl*ctrl=to_tcp_ofld_ctrl(nctrl);++nvme_stop_ctrl(nctrl);+nvme_tcp_ofld_teardown_ctrl(nctrl,false);++if(!nvme_change_ctrl_state(nctrl,NVME_CTRL_CONNECTING)){+/* state change failure is ok if we started ctrl delete */+WARN_ON_ONCE(nctrl->state!=NVME_CTRL_DELETING&&+nctrl->state!=NVME_CTRL_DELETING_NOIO);++return;+}++if(ctrl->dev->ops->setup_ctrl(ctrl,false))+gotoout_fail;++if(nvme_tcp_ofld_setup_ctrl(nctrl,false))+gotorelease_ctrl;++return;++release_ctrl:+ctrl->dev->ops->release_ctrl(ctrl);+out_fail:+++nctrl->nr_reconnects;+nvme_tcp_ofld_reconnect_or_remove(nctrl);+}+staticintnvme_tcp_ofld_init_request(structblk_mq_tag_set*set,structrequest*rq,
From: Dean Balandin <redacted>
In this patch we implement queue level functionality.
The implementation is similar to the nvme-tcp module, the main
difference being that we call the vendor specific create_queue op which
creates the TCP connection, and NVMeTPC connection including
icreq+icresp negotiation.
Once create_queue returns successfully, we can move on to the fabrics
connect.
Acked-by: Igor Russkikh <redacted>
Signed-off-by: Dean Balandin <redacted>
Signed-off-by: Prabhakar Kushwaha <redacted>
Signed-off-by: Omkar Kulkarni <redacted>
Signed-off-by: Michal Kalderon <mkalderon@marvell.com>
Signed-off-by: Ariel Elior <redacted>
Signed-off-by: Shai Malin <redacted>
---
drivers/nvme/host/tcp-offload.c | 415 ++++++++++++++++++++++++++++++--
drivers/nvme/host/tcp-offload.h | 2 +-
2 files changed, 390 insertions(+), 27 deletions(-)
Really? Isn't this hardware-dependent?
I would have expected the hardware to impose some limitations here (# of
MSIx interrupts or something). Hmm?
Right! We will be added to nvme_tcp_ofld_ops.
quoted
+static void
+nvme_tcp_ofld_set_io_queues(struct nvme_ctrl *nctrl, unsigned int nr_io_queues)
+{
+ struct nvme_tcp_ofld_ctrl *ctrl = to_tcp_ofld_ctrl(nctrl);
+ struct nvmf_ctrl_options *opts = nctrl->opts;
+
+ if (opts->nr_write_queues && opts->nr_io_queues < nr_io_queues) {
+ /*
+ * separate read/write queues
+ * hand out dedicated default queues only after we have
+ * sufficient read queues.
+ */
+ ctrl->io_queues[HCTX_TYPE_READ] = opts->nr_io_queues;
+ nr_io_queues -= ctrl->io_queues[HCTX_TYPE_READ];
+ ctrl->io_queues[HCTX_TYPE_DEFAULT] =
+ min(opts->nr_write_queues, nr_io_queues);
+ nr_io_queues -= ctrl->io_queues[HCTX_TYPE_DEFAULT];
+ } else {
+ /*
+ * shared read/write queues
+ * either no write queues were requested, or we don't have
+ * sufficient queue count to have dedicated default queues.
+ */
+ ctrl->io_queues[HCTX_TYPE_DEFAULT] =
+ min(opts->nr_io_queues, nr_io_queues);
+ nr_io_queues -= ctrl->io_queues[HCTX_TYPE_DEFAULT];
+ }
+
+ if (opts->nr_poll_queues && nr_io_queues) {
+ /* map dedicated poll queues only if we have queues left */
+ ctrl->io_queues[HCTX_TYPE_POLL] =
+ min(opts->nr_poll_queues, nr_io_queues);
+ }
+}
+
Same here.
Poll queues only ever make sense of the hardware can serve specific
queue pairs without interrupts. Which again relates to the number of
interrupts, and the affinity of those.
Or isn't this a concern with your card?
Right! We will be added to nvme_tcp_ofld_ops.
Our NVMeTCP offload HW supports 256 interrupt lines across the offload devices,
meaning 64-256 per offload device, (depends on the number of ports).
quoted
+static void
+nvme_tcp_ofld_terminate_io_queues(struct nvme_ctrl *nctrl, int start_from)
+{
+ int i;
+
+ /* admin-q will be ignored because of the loop condition */
+ for (i = start_from; i >= 1; i--)
+ nvme_tcp_ofld_stop_queue(nctrl, i);
+}
+
Loop condition? Care to elaborate?
Similar code (with the same loop condition) exists in the other transports,
e.g. __nvme_tcp_alloc_io_queues(), which calls nvme_tcp_free_queues().
Will rephrase comment to: "Loop condition will stop before index 0 which
is the admin queue."
quoted
+static int nvme_tcp_ofld_create_io_queues(struct nvme_ctrl *nctrl)
+{
+ struct nvme_tcp_ofld_ctrl *ctrl = to_tcp_ofld_ctrl(nctrl);
+ int i, rc;
+
+ for (i = 1; i < nctrl->queue_count; i++) {
+ rc = ctrl->dev->ops->create_queue(&ctrl->queues[i],
+ i, nctrl->sqsize + 1);
+ if (rc)
+ goto out_free_queues;
+
+ set_bit(NVME_TCP_OFLD_Q_ALLOCATED, &ctrl->queues[i].flags);
+ }
+
+ return 0;
+
+out_free_queues:
+ nvme_tcp_ofld_terminate_io_queues(nctrl, --i);
+
+ return rc;
+}
+
+static int nvme_tcp_ofld_alloc_io_queues(struct nvme_ctrl *nctrl)
+{
+ unsigned int nr_io_queues;
+ int rc;
+
+ nr_io_queues = nvme_tcp_ofld_nr_io_queues(nctrl);
+ rc = nvme_set_queue_count(nctrl, &nr_io_queues);
+ if (rc)
+ return rc;
+
+ nctrl->queue_count = nr_io_queues + 1;
+ if (nctrl->queue_count < 2) {
+ dev_err(nctrl->device,
+ "unable to set any I/O queues\n");
+
+ return -ENOMEM;
+ }
+
+ dev_info(nctrl->device, "creating %d I/O queues.\n", nr_io_queues);
+ nvme_tcp_ofld_set_io_queues(nctrl, nr_io_queues);
+
+ return nvme_tcp_ofld_create_io_queues(nctrl);
+}
+
+static int nvme_tcp_ofld_start_io_queues(struct nvme_ctrl *nctrl)
+{
+ int i, rc = 0;
+
+ for (i = 1; i < nctrl->queue_count; i++) {
+ rc = nvme_tcp_ofld_start_queue(nctrl, i);
+ if (rc)
+ goto terminate_queues;
+ }
+
+ return 0;
+
+terminate_queues:
+ nvme_tcp_ofld_terminate_io_queues(nctrl, --i);
return rc;
}
@@ -258,9 +468,10 @@ static int nvme_tcp_ofld_configure_admin_queue(struct nvme_ctrl *nctrl, static int nvme_tcp_ofld_configure_io_queues(struct nvme_ctrl *nctrl, bool new) {- int rc;+ int rc = nvme_tcp_ofld_alloc_io_queues(nctrl);- /* Placeholder - alloc_io_queues */+ if (rc)+ return rc; if (new) { nctrl->tagset = nvme_tcp_ofld_alloc_tagset(nctrl, false);
This patch introduces the NVMeTCP Offload FW and HW HSI in order
to initialize the IO level configuration into a per IO HW
resource ("task") as part of the IO path flow.
Acked-by: Igor Russkikh <redacted>
Signed-off-by: Prabhakar Kushwaha <redacted>
Signed-off-by: Omkar Kulkarni <redacted>
Signed-off-by: Shai Malin <redacted>
Signed-off-by: Michal Kalderon <mkalderon@marvell.com>
Signed-off-by: Ariel Elior <redacted>
---
include/linux/qed/nvmetcp_common.h | 418 ++++++++++++++++++++++++++++-
include/linux/qed/qed_nvmetcp_if.h | 37 +++
2 files changed, 454 insertions(+), 1 deletion(-)
@@ -0,0 +1,372 @@+// SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)+/* Copyright 2021 Marvell. All rights reserved. */++#include<linux/kernel.h>+#include<linux/module.h>+#include<linux/pci.h>+#include<linux/kernel.h>+#include<linux/list.h>+#include<linux/mm.h>+#include<linux/types.h>+#include<asm/byteorder.h>+#include<linux/qed/common_hsi.h>+#include<linux/qed/storage_common.h>+#include<linux/qed/nvmetcp_common.h>+#include<linux/qed/qed_nvmetcp_if.h>+#include"qed_nvmetcp_fw_funcs.h"++#define NVMETCP_NUM_SGES_IN_CACHE 0x4++boolnvmetcp_is_slow_sgl(u16num_sges,boolsmall_mid_sge)+{+return(num_sges>SCSI_NUM_SGES_SLOW_SGL_THR&&small_mid_sge);+}++voidinit_scsi_sgl_context(structscsi_sgl_params*ctx_sgl_params,+structscsi_cached_sges*ctx_data_desc,+structstorage_sgl_task_params*sgl_params)+{+u8num_sges_to_init=(u8)(sgl_params->num_sges>NVMETCP_NUM_SGES_IN_CACHE?+NVMETCP_NUM_SGES_IN_CACHE:sgl_params->num_sges);+u8sge_index;++/* sgl params */+ctx_sgl_params->sgl_addr.lo=cpu_to_le32(sgl_params->sgl_phys_addr.lo);+ctx_sgl_params->sgl_addr.hi=cpu_to_le32(sgl_params->sgl_phys_addr.hi);+ctx_sgl_params->sgl_total_length=cpu_to_le32(sgl_params->total_buffer_size);+ctx_sgl_params->sgl_num_sges=cpu_to_le16(sgl_params->num_sges);++for(sge_index=0;sge_index<num_sges_to_init;sge_index++){+ctx_data_desc->sge[sge_index].sge_addr.lo=+cpu_to_le32(sgl_params->sgl[sge_index].sge_addr.lo);+ctx_data_desc->sge[sge_index].sge_addr.hi=+cpu_to_le32(sgl_params->sgl[sge_index].sge_addr.hi);+ctx_data_desc->sge[sge_index].sge_len=+cpu_to_le32(sgl_params->sgl[sge_index].sge_len);+}+}++staticinlineu32calc_rw_task_size(structnvmetcp_task_params*task_params,+enumnvmetcp_task_typetask_type)+{+u32io_size;++if(task_type==NVMETCP_TASK_TYPE_HOST_WRITE)+io_size=task_params->tx_io_size;+else+io_size=task_params->rx_io_size;++if(unlikely(!io_size))+return0;++returnio_size;+}++staticinlinevoidinit_sqe(structnvmetcp_task_params*task_params,+structstorage_sgl_task_params*sgl_task_params,+enumnvmetcp_task_typetask_type)+{+if(!task_params->sqe)+return;++memset(task_params->sqe,0,sizeof(*task_params->sqe));+task_params->sqe->task_id=cpu_to_le16(task_params->itid);++switch(task_type){+caseNVMETCP_TASK_TYPE_HOST_WRITE:{+u32buf_size=0;+u32num_sges=0;++SET_FIELD(task_params->sqe->contlen_cdbsize,+NVMETCP_WQE_CDB_SIZE_OR_NVMETCP_CMD,1);+SET_FIELD(task_params->sqe->flags,NVMETCP_WQE_WQE_TYPE,+NVMETCP_WQE_TYPE_NORMAL);+if(task_params->tx_io_size){+if(task_params->send_write_incapsule)+buf_size=calc_rw_task_size(task_params,task_type);++if(nvmetcp_is_slow_sgl(sgl_task_params->num_sges,+sgl_task_params->small_mid_sge))+num_sges=NVMETCP_WQE_NUM_SGES_SLOWIO;+else+num_sges=min((u16)sgl_task_params->num_sges,+(u16)SCSI_NUM_SGES_SLOW_SGL_THR);+}+SET_FIELD(task_params->sqe->flags,NVMETCP_WQE_NUM_SGES,num_sges);+SET_FIELD(task_params->sqe->contlen_cdbsize,NVMETCP_WQE_CONT_LEN,buf_size);+}break;++caseNVMETCP_TASK_TYPE_HOST_READ:{+SET_FIELD(task_params->sqe->flags,NVMETCP_WQE_WQE_TYPE,+NVMETCP_WQE_TYPE_NORMAL);+SET_FIELD(task_params->sqe->contlen_cdbsize,+NVMETCP_WQE_CDB_SIZE_OR_NVMETCP_CMD,1);+}break;++caseNVMETCP_TASK_TYPE_INIT_CONN_REQUEST:{+SET_FIELD(task_params->sqe->flags,NVMETCP_WQE_WQE_TYPE,+NVMETCP_WQE_TYPE_MIDDLE_PATH);++if(task_params->tx_io_size){+SET_FIELD(task_params->sqe->contlen_cdbsize,NVMETCP_WQE_CONT_LEN,+task_params->tx_io_size);+SET_FIELD(task_params->sqe->flags,NVMETCP_WQE_NUM_SGES,+min((u16)sgl_task_params->num_sges,+(u16)SCSI_NUM_SGES_SLOW_SGL_THR));+}+}break;++caseNVMETCP_TASK_TYPE_CLEANUP:+SET_FIELD(task_params->sqe->flags,NVMETCP_WQE_WQE_TYPE,+NVMETCP_WQE_TYPE_TASK_CLEANUP);++default:+break;+}+}++/* The following function initializes of NVMeTCP task params */+staticinlinevoid+init_nvmetcp_task_params(structe5_nvmetcp_task_context*context,+structnvmetcp_task_params*task_params,+enumnvmetcp_task_typetask_type)+{+context->ystorm_st_context.state.cccid=task_params->host_cccid;+SET_FIELD(context->ustorm_st_context.error_flags,USTORM_NVMETCP_TASK_ST_CTX_NVME_TCP,1);+context->ustorm_st_context.nvme_tcp_opaque_lo=cpu_to_le32(task_params->opq.lo);+context->ustorm_st_context.nvme_tcp_opaque_hi=cpu_to_le32(task_params->opq.hi);+}++/* The following function initializes default values to all tasks */+staticinlinevoid+init_default_nvmetcp_task(structnvmetcp_task_params*task_params,void*pdu_header,+enumnvmetcp_task_typetask_type)+{+structe5_nvmetcp_task_context*context=task_params->context;+constu8val_byte=context->mstorm_ag_context.cdu_validation;+u8dw_index;++memset(context,0,sizeof(*context));++init_nvmetcp_task_params(context,task_params,+(enumnvmetcp_task_type)task_type);++if(task_type==NVMETCP_TASK_TYPE_HOST_WRITE||+task_type==NVMETCP_TASK_TYPE_HOST_READ){+for(dw_index=0;dw_index<QED_NVMETCP_CMD_HDR_SIZE/4;dw_index++)+context->ystorm_st_context.pdu_hdr.task_hdr.reg[dw_index]=+cpu_to_le32(((u32*)pdu_header)[dw_index]);+}else{+for(dw_index=0;dw_index<QED_NVMETCP_CMN_HDR_SIZE/4;dw_index++)+context->ystorm_st_context.pdu_hdr.task_hdr.reg[dw_index]=+cpu_to_le32(((u32*)pdu_header)[dw_index]);+}+
And this is what I meant. You are twiddling with the bytes already, so
why bother with a separate struct at all?
From: Dean Balandin <redacted>
In this patch, we present the IO level functionality.
The nvme-tcp-offload shall work on the IO-level, meaning the
nvme-tcp-offload ULP module shall pass the request to the nvme-tcp-offload
vendor driver and shall expect for the request compilation.
Request compilation? Not request completion?
Completion. Thanks!
quoted
No additional handling is needed in between, this design will reduce the
CPU utilization as we will describe below.
The nvme-tcp-offload vendor driver shall register to nvme-tcp-offload ULP
with the following IO-path ops:
- init_req
- send_req - in order to pass the request to the handling of the offload
driver that shall pass it to the vendor specific device
- poll_queue
The vendor driver will manage the context from which the request will be
executed and the request aggregations.
Once the IO completed, the nvme-tcp-offload vendor driver shall call
command.done() that shall invoke the nvme-tcp-offload ULP layer for
completing the request.
This patch also contains initial definition of nvme_tcp_ofld_queue_rq().
Acked-by: Igor Russkikh <redacted>
Signed-off-by: Dean Balandin <redacted>
Signed-off-by: Prabhakar Kushwaha <redacted>
Signed-off-by: Omkar Kulkarni <redacted>
Signed-off-by: Michal Kalderon <mkalderon@marvell.com>
Signed-off-by: Ariel Elior <redacted>
Signed-off-by: Shai Malin <redacted>
---
drivers/nvme/host/tcp-offload.c | 95 ++++++++++++++++++++++++++++++---
1 file changed, 87 insertions(+), 8 deletions(-)
@@ -1024,6 +1060,51 @@ static int nvme_tcp_ofld_poll(struct blk_mq_hw_ctx *hctx)returnops->poll_queue(queue);}+staticvoidnvme_tcp_ofld_complete_timed_out(structrequest*rq)+{+structnvme_tcp_ofld_req*req=blk_mq_rq_to_pdu(rq);+structnvme_ctrl*nctrl=&req->queue->ctrl->nctrl;++nvme_tcp_ofld_stop_queue(nctrl,nvme_tcp_ofld_qid(req->queue));+if(blk_mq_request_started(rq)&&!blk_mq_request_completed(rq)){+nvme_req(rq)->status=NVME_SC_HOST_ABORTED_CMD;+blk_mq_complete_request(rq);+}+}++staticenumblk_eh_timer_returnnvme_tcp_ofld_timeout(structrequest*rq,boolreserved)+{+structnvme_tcp_ofld_req*req=blk_mq_rq_to_pdu(rq);+structnvme_tcp_ofld_ctrl*ctrl=req->queue->ctrl;++dev_warn(ctrl->nctrl.device,+"queue %d: timeout request %#x type %d\n",+nvme_tcp_ofld_qid(req->queue),rq->tag,req->nvme_cmd.common.opcode);++if(ctrl->nctrl.state!=NVME_CTRL_LIVE){+/*+*Ifweareresetting,connectingordeletingweshould+*completeimmediatelybecausewemayblockcontroller+*teardownorsetupsequence+*-ctrldisable/shutdownfabricsrequests+*-connectrequests+*-initializationadminrequests+*-I/Orequeststhatenteredafterunquiescingand+*thecontrollerstoppedresponding+*+*Allotherrequestsshouldbecancelledbytheerror+*recoverywork,soit'sfinethatwefailithere.+*/+nvme_tcp_ofld_complete_timed_out(rq);++returnBLK_EH_DONE;+}
And this particular error code has been causing _so_ _many_ issues
during testing, that I'd rather get rid of it altogether.
But probably not your fault, your just copying what tcp and rdma is doing.
I agree. We preferred to keep all the teardown/error flows similar to the tcp
and rdma design in order to be able to align the tcp-offload to any future
changes. Would you like us to do anything differently?
This patch will present the skeleton of the qedn driver.
The new driver will be added under "drivers/nvme/hw/qedn" and will be
enabled by the Kconfig "Marvell NVM Express over Fabrics TCP offload".
The internal implementation:
- qedn.h:
Includes all common structs to be used by the qedn vendor driver.
- qedn_main.c
Includes the qedn_init and qedn_cleanup implementation.
As part of the qedn init, the driver will register as a pci device and
will work with the Marvell fastlinQ NICs.
As part of the probe, the driver will register to the nvme_tcp_offload
(ULP).
Acked-by: Igor Russkikh <redacted>
Signed-off-by: Arie Gershberg <redacted>
Signed-off-by: Prabhakar Kushwaha <redacted>
Signed-off-by: Omkar Kulkarni <redacted>
Signed-off-by: Michal Kalderon <mkalderon@marvell.com>
Signed-off-by: Ariel Elior <redacted>
Signed-off-by: Shai Malin <redacted>
---
MAINTAINERS | 10 ++
drivers/nvme/Kconfig | 1 +
drivers/nvme/Makefile | 1 +
drivers/nvme/hw/Kconfig | 8 ++
drivers/nvme/hw/Makefile | 3 +
drivers/nvme/hw/qedn/Makefile | 5 +
drivers/nvme/hw/qedn/qedn.h | 19 +++
drivers/nvme/hw/qedn/qedn_main.c | 201 +++++++++++++++++++++++++++++++
8 files changed, 248 insertions(+)
create mode 100644 drivers/nvme/hw/Kconfig
create mode 100644 drivers/nvme/hw/Makefile
create mode 100644 drivers/nvme/hw/qedn/Makefile
create mode 100644 drivers/nvme/hw/qedn/qedn.h
create mode 100644 drivers/nvme/hw/qedn/qedn_main.c
Reviewed-by: Hannes Reinecke <hare@suse.de>
Thanks.
Cheers,
Hannes
--
Dr. Hannes Reinecke Kernel Storage Architect
hare@suse.de +49 911 74053 688
SUSE Software Solutions GmbH, Maxfeldstr. 5, 90409 Nürnberg
HRB 36809 (AG Nürnberg), Geschäftsführer: Felix Imendörffer
From: Dean Balandin <redacted>
As part of create_ctrl(), it scans the registered devices and calls
the claim_dev op on each of them, to find the first devices that matches
the connection params. Once the correct devices is found (claim_dev
returns true), we raise the refcnt of that device and return that device
as the device to be used for ctrl currently being created.
Acked-by: Igor Russkikh <redacted>
Signed-off-by: Dean Balandin <redacted>
Signed-off-by: Prabhakar Kushwaha <redacted>
Signed-off-by: Omkar Kulkarni <redacted>
Signed-off-by: Michal Kalderon <mkalderon@marvell.com>
Signed-off-by: Ariel Elior <redacted>
Signed-off-by: Shai Malin <redacted>
---
drivers/nvme/host/tcp-offload.c | 94 +++++++++++++++++++++++++++++++++
1 file changed, 94 insertions(+)
I wonder if we shouldn't take the approach from Martin Belanger, and
introduce a new option 'host_iface' to select the interface to use.
That is, _if_ the nvme-tcp offload driver would present itself as a
network interface; one might argue that it would put too much
restriction on the implementations.
We should add the new option 'host_iface' also to nvme-tcp-offload and
each vendor-specific driver shall register with this to nvme_tcp_ofld_ops.
But if it does not present itself as a network interface, how do we
address it? And if it does, wouldn't we be better off to specify the
interface directly, and not try to imply the interface from the IP address?
Specifically for the Marvell qedn driver, we are pairing between the net-device
(qede) and the offload-device (qedn) on each port.
The usage for the user is:
Assign IP to the net-device (from any existing linux tool):
ip addr add 100.100.0.101/24 dev p1p1
This IP will be used by both net-device (qede) and offload-device (qedn).
In order to connect from "sw" nvme-tcp through the net-device (qede):
nvme connect -t tcp -s 4420 -a 100.100.0.100 -n testnqn
In order to connect from "offload" nvme-tcp through the offload-device (qedn):
nvme connect -t tcp_offload -s 4420 -a 100.100.0.100 -n testnqn
A suggestion (by Hannes Reinecke), as a future enhancement, and in
order to simplify the usage, we suggest modifying nvme-cli with a new flag
that will determine if "-t tcp" should be the regular nvme-tcp (which will
be the default) or nvme-tcp-offload.
For exmaple:
nvme connect -t tcp -s 4420 -a 100.100.0.100 -n testnqn -[new flag]
Cheers,
Hannes
--
Dr. Hannes Reinecke Kernel Storage Architect
hare@suse.de +49 911 74053 688
SUSE Software Solutions GmbH, Maxfeldstr. 5, 90409 Nürnberg
HRB 36809 (AG Nürnberg), Geschäftsführer: Felix Imendörffer
This patch will present the adding of qedn_fp_queue - this is a per cpu
core element which handles all of the connections on that cpu core.
The qedn_fp_queue will handle a group of connections (NVMeoF QPs) which
are handled on the same cpu core, and will only use the same FW-driver
resources with no need to be related to the same NVMeoF controller.
The per qedn_fq_queue resources are the FW CQ and FW status block:
- The FW CQ will be used for the FW to notify the driver that the
the exchange has ended and the FW will pass the incoming NVMeoF CQE
(if exist) to the driver.
- FW status block - which is used for the FW to notify the driver with
the producer update of the FW CQE chain.
The FW fast-path queues are based on qed_chain.h
Acked-by: Igor Russkikh <redacted>
Signed-off-by: Prabhakar Kushwaha <redacted>
Signed-off-by: Omkar Kulkarni <redacted>
Signed-off-by: Michal Kalderon <mkalderon@marvell.com>
Signed-off-by: Ariel Elior <redacted>
Signed-off-by: Shai Malin <redacted>
---
drivers/nvme/hw/qedn/qedn.h | 26 +++
drivers/nvme/hw/qedn/qedn_main.c | 287 ++++++++++++++++++++++++++++++-
2 files changed, 310 insertions(+), 3 deletions(-)
@@ -255,6 +508,12 @@ static void __qedn_remove(struct pci_dev *pdev) else pr_err("Failed to remove from global PF list\n");+ if (test_and_clear_bit(QEDN_STATE_IRQ_SET, &qedn->state))+ qedn_sync_free_irqs(qedn);++ if (test_and_clear_bit(QEDN_STATE_NVMETCP_OPEN, &qedn->state))+ qed_ops->stop(qedn->cdev);+ if (test_and_clear_bit(QEDN_STATE_MFW_STATE, &qedn->state)) { rc = qed_ops->common->update_drv_state(qedn->cdev, false); if (rc)
@@ -264,6 +523,9 @@ static void __qedn_remove(struct pci_dev *pdev) if (test_and_clear_bit(QEDN_STATE_CORE_OPEN, &qedn->state)) qed_ops->common->slowpath_stop(qedn->cdev);+ if (test_and_clear_bit(QEDN_STATE_FP_WORK_THREAD_SET, &qedn->state))+ qedn_free_function_queues(qedn);+ if (test_and_clear_bit(QEDN_STATE_CORE_PROBED, &qedn->state)) qed_ops->common->remove(qedn->cdev);
@@ -335,6 +597,25 @@ static int __qedn_probe(struct pci_dev *pdev) set_bit(QEDN_STATE_CORE_OPEN, &qedn->state);+ rc = qedn_setup_irq(qedn);+ if (rc)+ goto exit_probe_and_release_mem;++ set_bit(QEDN_STATE_IRQ_SET, &qedn->state);++ /* NVMeTCP start HW PF */+ rc = qed_ops->start(qedn->cdev,+ NULL /* Placeholder for FW IO-path resources */,+ qedn,+ NULL /* Placeholder for FW Event callback */);+ if (rc) {+ rc = -ENODEV;+ pr_err("Cannot start NVMeTCP Function\n");+ goto exit_probe_and_release_mem;+ }++ set_bit(QEDN_STATE_NVMETCP_OPEN, &qedn->state);+ rc = qed_ops->common->update_drv_state(qedn->cdev, true); if (rc) { pr_err("Failed to send drv state to MFW\n");
So you have a limited number of MSI-x interrupts, but don't limit the
number of hw queues to that. Why?
Will be fixed in V5.
Cheers,
Hannes
--
Dr. Hannes Reinecke Kernel Storage Architect
hare@suse.de +49 911 74053 688
SUSE Software Solutions GmbH, Maxfeldstr. 5, 90409 Nürnberg
HRB 36809 (AG Nürnberg), Geschäftsführer: Felix Imendörffer
From: Prabhakar Kushwaha <redacted>
This patch will present the connection (queue) level slowpath
implementation relevant for create_queue flow.
The internal implementation:
- Add per controller slowpath workqeueue via pre_setup_ctrl
- qedn_main.c:
Includes qedn's implementation of the create_queue op.
- qedn_conn.c will include main slowpath connection level functions,
including:
1. Per-queue resources allocation.
2. Creating a new connection.
3. Offloading the connection to the FW for TCP handshake.
4. Destroy of a connection.
5. Support of delete and free controller.
6. TCP port management via qed_fetch_tcp_port, qed_return_tcp_port
Acked-by: Igor Russkikh <redacted>
Signed-off-by: Prabhakar Kushwaha <redacted>
Signed-off-by: Omkar Kulkarni <redacted>
Signed-off-by: Michal Kalderon <mkalderon@marvell.com>
Signed-off-by: Ariel Elior <redacted>
Signed-off-by: Shai Malin <redacted>
---
drivers/nvme/hw/qedn/Makefile | 5 +-
drivers/nvme/hw/qedn/qedn.h | 173 ++++++++++-
drivers/nvme/hw/qedn/qedn_conn.c | 508 +++++++++++++++++++++++++++++++
drivers/nvme/hw/qedn/qedn_main.c | 208 ++++++++++++-
4 files changed, 883 insertions(+), 11 deletions(-)
create mode 100644 drivers/nvme/hw/qedn/qedn_conn.c
Reviewed-by: Hannes Reinecke <hare@suse.de>
Thanks.
Cheers,
Hannes
--
Dr. Hannes Reinecke Kernel Storage Architect
hare@suse.de +49 911 74053 688
SUSE Software Solutions GmbH, Maxfeldstr. 5, 90409 Nürnberg
HRB 36809 (AG Nürnberg), Geschäftsführer: Felix Imendörffer
From: Prabhakar Kushwaha <redacted>
HW filter can be configured to filter TCP packets based on either
source or target TCP port. QEDN leverage this feature to route
NVMeTCP traffic.
This patch configures HW filter block based on source port for all
receiving packets to deliver correct QEDN PF.
Acked-by: Igor Russkikh <redacted>
Signed-off-by: Prabhakar Kushwaha <redacted>
Signed-off-by: Omkar Kulkarni <redacted>
Signed-off-by: Michal Kalderon <mkalderon@marvell.com>
Signed-off-by: Ariel Elior <redacted>
Signed-off-by: Shai Malin <redacted>
---
drivers/nvme/hw/qedn/qedn.h | 15 +++++
drivers/nvme/hw/qedn/qedn_main.c | 108 ++++++++++++++++++++++++++++++-
2 files changed, 122 insertions(+), 1 deletion(-)
Reviewed-by: Hannes Reinecke <hare@suse.de>
Thanks.
Cheers,
Hannes
--
Dr. Hannes Reinecke Kernel Storage Architect
hare@suse.de +49 911 74053 688
SUSE Software Solutions GmbH, Maxfeldstr. 5, 90409 Nürnberg
HRB 36809 (AG Nürnberg), Geschäftsführer: Felix Imendörffer
From: Prabhakar Kushwaha <redacted>
Once a TCP connection established, the host sends an Initialize
Connection Request (ICReq) PDU to the controller.
Further Initialize Connection Response (ICResp) PDU received from
controller is processed by host to establish a connection and
exchange connection configuration parameters.
This patch present support of generation of ICReq and processing of
ICResp. It also update host configuration based on exchanged parameters.
Acked-by: Igor Russkikh <redacted>
Signed-off-by: Prabhakar Kushwaha <redacted>
Signed-off-by: Omkar Kulkarni <redacted>
Signed-off-by: Michal Kalderon <mkalderon@marvell.com>
Signed-off-by: Ariel Elior <redacted>
Signed-off-by: Shai Malin <redacted>
---
drivers/nvme/hw/qedn/qedn.h | 36 ++++
drivers/nvme/hw/qedn/qedn_conn.c | 317 ++++++++++++++++++++++++++++++-
drivers/nvme/hw/qedn/qedn_main.c | 22 +++
drivers/nvme/hw/qedn/qedn_task.c | 8 +-
4 files changed, 379 insertions(+), 4 deletions(-)
@@ -195,6 +213,9 @@ struct qedn_endpoint {structqed_chainfw_sq_chain;void__iomem*p_doorbell;+/* Spinlock for accessing FW queue */+spinlock_tdoorbell_lock;+/* TCP Params */__be32dst_addr[4];/* In network order */__be32src_addr[4];/* In network order */
@@ -34,6 +34,25 @@ inline int qedn_qid(struct nvme_tcp_ofld_queue *queue)returnqueue-queue->ctrl->queues;}+voidqedn_ring_doorbell(structqedn_conn_ctx*conn_ctx)+{+structnvmetcp_db_datadbell={0};+u16prod_idx;++dbell.agg_flags=0;+dbell.params|=DB_DEST_XCM<<NVMETCP_DB_DATA_DEST_SHIFT;+dbell.params|=DB_AGG_CMD_SET<<NVMETCP_DB_DATA_AGG_CMD_SHIFT;+dbell.params|=+DQ_XCM_ISCSI_SQ_PROD_CMD<<NVMETCP_DB_DATA_AGG_VAL_SEL_SHIFT;+dbell.params|=1<<NVMETCP_DB_DATA_BYPASS_EN_SHIFT;+prod_idx=qed_chain_get_prod_idx(&conn_ctx->ep.fw_sq_chain);+dbell.sq_prod=cpu_to_le16(prod_idx);++/* wmb - Make sure fw idx is coherent */+wmb();+writel(*(u32*)&dbell,conn_ctx->ep.p_doorbell);+}+intqedn_set_con_state(structqedn_conn_ctx*conn_ctx,enumqedn_conn_statenew_state){spin_lock_bh(&conn_ctx->conn_state_lock);
@@ -130,6 +149,71 @@ int qedn_initialize_endpoint(struct qedn_endpoint *ep, u8 *local_mac_addr,return-1;}+staticintqedn_alloc_icreq_pad(structqedn_conn_ctx*conn_ctx)+{+structqedn_ctx*qedn=conn_ctx->qedn;+structqedn_icreq_padding*icreq_pad;+u32*buffer;+intrc=0;++icreq_pad=kzalloc(sizeof(*icreq_pad),GFP_KERNEL);+if(!icreq_pad)+return-ENOMEM;++conn_ctx->icreq_pad=icreq_pad;+memset(&icreq_pad->sge,0,sizeof(icreq_pad->sge));+buffer=dma_alloc_coherent(&qedn->pdev->dev,+QEDN_ICREQ_FW_PAYLOAD,+&icreq_pad->pa,+GFP_KERNEL);+if(!buffer){+pr_err("Could not allocate icreq_padding SGE buffer.\n");+rc=-ENOMEM;+gotorelease_icreq_pad;+}++DMA_REGPAIR_LE(icreq_pad->sge.sge_addr,icreq_pad->pa);+icreq_pad->sge.sge_len=cpu_to_le32(QEDN_ICREQ_FW_PAYLOAD);+icreq_pad->buffer=buffer;+set_bit(QEDN_CONN_RESRC_ICREQ_PAD,&conn_ctx->resrc_state);++return0;++release_icreq_pad:+kfree(icreq_pad);+conn_ctx->icreq_pad=NULL;++returnrc;+}++staticvoidqedn_free_icreq_pad(structqedn_conn_ctx*conn_ctx)+{+structqedn_ctx*qedn=conn_ctx->qedn;+structqedn_icreq_padding*icreq_pad;+u32*buffer;++icreq_pad=conn_ctx->icreq_pad;+if(unlikely(!icreq_pad)){+pr_err("null ptr in icreq_pad in conn_ctx\n");+gotofinally;+}++buffer=icreq_pad->buffer;+if(buffer){+dma_free_coherent(&qedn->pdev->dev,+QEDN_ICREQ_FW_PAYLOAD,+(void*)buffer,+icreq_pad->pa);+icreq_pad->buffer=NULL;+}++kfree(icreq_pad);+conn_ctx->icreq_pad=NULL;++finally:+clear_bit(QEDN_CONN_RESRC_ICREQ_PAD,&conn_ctx->resrc_state);+}+staticvoidqedn_release_conn_ctx(structqedn_conn_ctx*conn_ctx){structqedn_ctx*qedn=conn_ctx->qedn;
@@ -309,6 +396,194 @@ void qedn_terminate_connection(struct qedn_conn_ctx *conn_ctx, int abrt_flag)queue_work(qctrl->sp_wq,&conn_ctx->sp_wq_entry);}+staticintqedn_nvmetcp_update_conn(structqedn_ctx*qedn,structqedn_conn_ctx*conn_ctx)+{+structqedn_negotiation_params*pdu_params=&conn_ctx->pdu_params;+structqed_nvmetcp_params_update*conn_info;+intrc;++conn_info=kzalloc(sizeof(*conn_info),GFP_KERNEL);+if(!conn_info)+return-ENOMEM;++conn_info->hdr_digest_en=pdu_params->hdr_digest;+conn_info->data_digest_en=pdu_params->data_digest;+conn_info->max_recv_pdu_length=QEDN_MAX_PDU_SIZE;+conn_info->max_io_size=QEDN_MAX_IO_SIZE;+conn_info->max_send_pdu_length=pdu_params->maxh2cdata;++rc=qed_ops->update_conn(qedn->cdev,conn_ctx->conn_handle,conn_info);+if(rc){+pr_err("Could not update connection\n");+rc=-ENXIO;+}++kfree(conn_info);++returnrc;+}++staticintqedn_update_ramrod(structqedn_conn_ctx*conn_ctx)+{+structqedn_ctx*qedn=conn_ctx->qedn;+intrc=0;++rc=qedn_set_con_state(conn_ctx,CONN_STATE_WAIT_FOR_UPDATE_EQE);+if(rc)+returnrc;++rc=qedn_nvmetcp_update_conn(qedn,conn_ctx);+if(rc)+returnrc;++if(conn_ctx->state!=CONN_STATE_WAIT_FOR_UPDATE_EQE){+pr_err("cid 0x%x: Unexpected state 0x%x after update ramrod\n",+conn_ctx->fw_cid,conn_ctx->state);++return-EINVAL;+}++returnrc;+}++staticintqedn_send_icreq(structqedn_conn_ctx*conn_ctx)+{+structnvmetcp_init_conn_req_hdr*icreq_ptr=NULL;+structstorage_sgl_task_params*sgl_task_params;+structnvmetcp_task_paramstask_params;+structqedn_task_ctx*qedn_task=NULL;+structnvme_tcp_icreq_pduicreq;+structnvmetcp_wqe*chain_sqe;+structnvmetcp_wqelocal_sqe;++qedn_task=qedn_get_task_from_pool_insist(conn_ctx,QEDN_ICREQ_CCCID);+if(!qedn_task)+return-EINVAL;++memset(&icreq,0,sizeof(icreq));+memset(&local_sqe,0,sizeof(local_sqe));++/* Initialize ICReq */+icreq.hdr.type=nvme_tcp_icreq;+icreq.hdr.hlen=sizeof(icreq);+icreq.hdr.pdo=0;+icreq.hdr.plen=cpu_to_le32(icreq.hdr.hlen);+icreq.pfv=cpu_to_le16(conn_ctx->required_params.pfv);+icreq.maxr2t=cpu_to_le32(conn_ctx->required_params.maxr2t);+icreq.hpda=conn_ctx->required_params.hpda;+if(conn_ctx->required_params.hdr_digest)+icreq.digest|=NVME_TCP_HDR_DIGEST_ENABLE;+if(conn_ctx->required_params.data_digest)+icreq.digest|=NVME_TCP_DATA_DIGEST_ENABLE;++qedn_swap_bytes((u32*)&icreq,+(sizeof(icreq)-QEDN_ICREQ_FW_PAYLOAD)/+sizeof(u32));++/* Initialize task params */+task_params.opq.lo=cpu_to_le32(((u64)(qedn_task))&0xffffffff);+task_params.opq.hi=cpu_to_le32(((u64)(qedn_task))>>32);+task_params.context=qedn_task->fw_task_ctx;+task_params.sqe=&local_sqe;+task_params.conn_icid=(u16)conn_ctx->conn_handle;+task_params.itid=qedn_task->itid;+task_params.cq_rss_number=conn_ctx->default_cq;+task_params.tx_io_size=QEDN_ICREQ_FW_PAYLOAD;+task_params.rx_io_size=0;/* Rx doesn't use SGL for icresp */++/* Init SGE for ICReq padding */+sgl_task_params=&qedn_task->sgl_task_params;+sgl_task_params->total_buffer_size=task_params.tx_io_size;+sgl_task_params->small_mid_sge=false;+sgl_task_params->num_sges=1;+memcpy(sgl_task_params->sgl,&conn_ctx->icreq_pad->sge,+sizeof(conn_ctx->icreq_pad->sge));+icreq_ptr=(structnvmetcp_init_conn_req_hdr*)&icreq;++qed_ops->init_icreq_exchange(&task_params,icreq_ptr,sgl_task_params,NULL);++qedn_set_con_state(conn_ctx,CONN_STATE_WAIT_FOR_IC_COMP);+atomic_inc(&conn_ctx->num_active_fw_tasks);++/* spin_lock - doorbell is accessed both Rx flow and response flow */+spin_lock(&conn_ctx->ep.doorbell_lock);+chain_sqe=qed_chain_produce(&conn_ctx->ep.fw_sq_chain);+memcpy(chain_sqe,&local_sqe,sizeof(local_sqe));+qedn_ring_doorbell(conn_ctx);+spin_unlock(&conn_ctx->ep.doorbell_lock);++return0;+}+
And this is what I meant. You _do_ swab bytes before sending it off to
the HW, _and_ you use the standard nvme-tcp PDU definitions.
So why do you have your own, byte-swapped versions of the PDUs?
We will change it and use the standard struct 'nvme_tcp_icreq_pdu'.
Again here; you could treat the received icresp as a binaray blob.
byteswap it, and then cast is to the standard icresp structure.
Hmm?
With the existing FW, the driver receives only the meaningful fields from
the icresp. As a future enhancement, we will consider modifying the FW
to pass the entire icresp to the driver.