All of lore.kernel.org
 help / color / mirror / Atom feed
* [Intel-wired-lan] [PATCH net-next 00/19] Add iecm and idpf
@ 2022-01-28  0:09 Alan Brady
  2022-01-28  0:09 ` [Intel-wired-lan] [PATCH net-next 01/19] virtchnl: Add new virtchnl2 ops Alan Brady
                   ` (19 more replies)
  0 siblings, 20 replies; 89+ messages in thread
From: Alan Brady @ 2022-01-28  0:09 UTC (permalink / raw)
  To: intel-wired-lan

This series introduces both the Intel Ethernet Common Module and the
Intel Data Plane Function.  This also adds extended features and
functionality to virtchnl with virtchnl_2.h.
 
The format of the series generally follows the flow of driver init to
interface open. We go from probe into a hard reset followed by an init
task. From there the rest of the netdev_ops and data path are added.
Then lastly advanced features and idpf are introduced.

Currently this common layer (iecm) is initially only being used by only
the idpf driver (PF driver for SmartNIC).  However, the plan is to
eventually switch our iavf driver along with future drivers to use this
common module.  The hope is to better enable code sharing going forward
as well as support other developers writing drivers for our hardware

Alan Brady (17):
  virtchnl: Add new virtchnl2 ops
  iecm: add basic module init and documentation
  iecm: add probe and remove
  iecm: add api_init and controlq init
  iecm: add vport alloc and virtchnl messages
  iecm: add virtchnl messages for queues
  iecm: finish virtchnl messages
  iecm: add interrupts and configure netdev
  iecm: alloc vport TX resources
  iecm: alloc vport RX resources
  iecm: add start_xmit and set_rx_mode
  iecm: finish netdev_ops
  iecm: implement splitq napi_poll
  iecm: implement singleq napi_poll
  iecm: implement ethtool callbacks
  iecm: implement cloud filters
  idpf: introduce idpf driver

Haiyue Wang (2):
  iecm: implement flow director
  iecm: add advanced rss

 .../device_drivers/ethernet/intel/idpf.rst    |   47 +
 .../device_drivers/ethernet/intel/iecm.rst    |   93 +
 MAINTAINERS                                   |    1 +
 drivers/net/ethernet/intel/Kconfig            |   31 +
 drivers/net/ethernet/intel/Makefile           |    2 +
 drivers/net/ethernet/intel/idpf/Makefile      |   15 +
 drivers/net/ethernet/intel/idpf/idpf_dev.h    |   17 +
 drivers/net/ethernet/intel/idpf/idpf_devids.h |   10 +
 drivers/net/ethernet/intel/idpf/idpf_main.c   |  140 +
 drivers/net/ethernet/intel/idpf/idpf_reg.c    |  130 +
 drivers/net/ethernet/intel/iecm/Makefile      |   21 +
 .../net/ethernet/intel/iecm/iecm_controlq.c   |  649 ++
 .../ethernet/intel/iecm/iecm_controlq_setup.c |  175 +
 .../net/ethernet/intel/iecm/iecm_ethtool.c    | 1332 ++++
 drivers/net/ethernet/intel/iecm/iecm_lib.c    | 5717 +++++++++++++++++
 drivers/net/ethernet/intel/iecm/iecm_main.c   |   40 +
 .../ethernet/intel/iecm/iecm_singleq_txrx.c   | 1229 ++++
 drivers/net/ethernet/intel/iecm/iecm_txrx.c   | 4577 +++++++++++++
 .../net/ethernet/intel/iecm/iecm_virtchnl.c   | 4240 ++++++++++++
 drivers/net/ethernet/intel/include/iecm.h     |  973 +++
 .../ethernet/intel/include/iecm_controlq.h    |  117 +
 .../intel/include/iecm_controlq_api.h         |  185 +
 .../ethernet/intel/include/iecm_lan_pf_regs.h |  131 +
 .../ethernet/intel/include/iecm_lan_txrx.h    |  394 ++
 drivers/net/ethernet/intel/include/iecm_mem.h |   20 +
 .../net/ethernet/intel/include/iecm_txrx.h    |  733 +++
 include/linux/avf/virtchnl.h                  | 1507 ++++-
 include/linux/avf/virtchnl_2.h                | 1243 ++++
 include/linux/avf/virtchnl_lan_desc.h         |  603 ++
 29 files changed, 24255 insertions(+), 117 deletions(-)
 create mode 100644 Documentation/networking/device_drivers/ethernet/intel/idpf.rst
 create mode 100644 Documentation/networking/device_drivers/ethernet/intel/iecm.rst
 create mode 100644 drivers/net/ethernet/intel/idpf/Makefile
 create mode 100644 drivers/net/ethernet/intel/idpf/idpf_dev.h
 create mode 100644 drivers/net/ethernet/intel/idpf/idpf_devids.h
 create mode 100644 drivers/net/ethernet/intel/idpf/idpf_main.c
 create mode 100644 drivers/net/ethernet/intel/idpf/idpf_reg.c
 create mode 100644 drivers/net/ethernet/intel/iecm/Makefile
 create mode 100644 drivers/net/ethernet/intel/iecm/iecm_controlq.c
 create mode 100644 drivers/net/ethernet/intel/iecm/iecm_controlq_setup.c
 create mode 100644 drivers/net/ethernet/intel/iecm/iecm_ethtool.c
 create mode 100644 drivers/net/ethernet/intel/iecm/iecm_lib.c
 create mode 100644 drivers/net/ethernet/intel/iecm/iecm_main.c
 create mode 100644 drivers/net/ethernet/intel/iecm/iecm_singleq_txrx.c
 create mode 100644 drivers/net/ethernet/intel/iecm/iecm_txrx.c
 create mode 100644 drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
 create mode 100644 drivers/net/ethernet/intel/include/iecm.h
 create mode 100644 drivers/net/ethernet/intel/include/iecm_controlq.h
 create mode 100644 drivers/net/ethernet/intel/include/iecm_controlq_api.h
 create mode 100644 drivers/net/ethernet/intel/include/iecm_lan_pf_regs.h
 create mode 100644 drivers/net/ethernet/intel/include/iecm_lan_txrx.h
 create mode 100644 drivers/net/ethernet/intel/include/iecm_mem.h
 create mode 100644 drivers/net/ethernet/intel/include/iecm_txrx.h
 create mode 100644 include/linux/avf/virtchnl_2.h
 create mode 100644 include/linux/avf/virtchnl_lan_desc.h

-- 
2.33.0


^ permalink raw reply	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 01/19] virtchnl: Add new virtchnl2 ops
  2022-01-28  0:09 [Intel-wired-lan] [PATCH net-next 00/19] Add iecm and idpf Alan Brady
@ 2022-01-28  0:09 ` Alan Brady
  2022-02-02 22:13   ` Brady, Alan
  2022-01-28  0:09 ` [Intel-wired-lan] [PATCH net-next 02/19] iecm: add basic module init and documentation Alan Brady
                   ` (18 subsequent siblings)
  19 siblings, 1 reply; 89+ messages in thread
From: Alan Brady @ 2022-01-28  0:09 UTC (permalink / raw)
  To: intel-wired-lan

This extends the virtchnl interface to add new virtchnl ops and defines
needed to implement virtchnl 2.0.

Signed-off-by: Phani Burra <phani.r.burra@intel.com>
Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
Signed-off-by: Alan Brady <alan.brady@intel.com>
---
 MAINTAINERS                           |    1 +
 include/linux/avf/virtchnl.h          | 1507 +++++++++++++++++++++++--
 include/linux/avf/virtchnl_2.h        | 1243 ++++++++++++++++++++
 include/linux/avf/virtchnl_lan_desc.h |  603 ++++++++++
 4 files changed, 3237 insertions(+), 117 deletions(-)
 create mode 100644 include/linux/avf/virtchnl_2.h
 create mode 100644 include/linux/avf/virtchnl_lan_desc.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 0d7883977e9b..5685d64afd76 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -9602,6 +9602,7 @@ F:	Documentation/networking/device_drivers/ethernet/intel/
 F:	drivers/net/ethernet/intel/
 F:	drivers/net/ethernet/intel/*/
 F:	include/linux/avf/virtchnl.h
+F:	include/linux/avf/virtchnl_2.h
 F:	include/linux/net/intel/iidc.h
 
 INTEL ETHERNET PROTOCOL DRIVER FOR RDMA
diff --git a/include/linux/avf/virtchnl.h b/include/linux/avf/virtchnl.h
index 2ce27e8e4f19..aee5e2677e1c 100644
--- a/include/linux/avf/virtchnl.h
+++ b/include/linux/avf/virtchnl.h
@@ -14,8 +14,9 @@
 #define _VIRTCHNL_H_
 
 /* Description:
- * This header file describes the VF-PF communication protocol used
- * by the drivers for all devices starting from our 40G product line
+ * This header file describes the Virtual Function (VF) - Physical Function
+ * (PF) communication protocol used by the drivers for all devices starting
+ * from our 40G product line
  *
  * Admin queue buffer usage:
  * desc->opcode is always aqc_opc_send_msg_to_pf
@@ -29,8 +30,8 @@
  * have a maximum of sixteen queues for all of its VSIs.
  *
  * The PF is required to return a status code in v_retval for all messages
- * except RESET_VF, which does not require any response. The return value
- * is of status_code type, defined in the shared type.h.
+ * except RESET_VF, which does not require any response. The returned value
+ * is of virtchnl_status_code type, defined here.
  *
  * In general, VF driver initialization should roughly follow the order of
  * these opcodes. The VF driver must first validate the API version of the
@@ -45,7 +46,21 @@
  * value in current and future projects
  */
 
-/* Error Codes */
+/* These macros are used to generate compilation errors if a structure/union
+ * is not exactly the correct length. It gives a divide by zero error if the
+ * structure/union is not of the correct size, otherwise it creates an enum
+ * that is never used.
+ */
+#define VIRTCHNL_CHECK_STRUCT_LEN(n, X) enum virtchnl_static_assert_enum_##X \
+	{ virtchnl_static_assert_##X = (n) / ((sizeof(struct X) == (n)) ? 1 : 0) }
+#define VIRTCHNL_CHECK_UNION_LEN(n, X) enum virtchnl_static_asset_enum_##X \
+	{ virtchnl_static_assert_##X = (n) / ((sizeof(union X) == (n)) ? 1 : 0) }
+
+/* Error Codes
+ * Note that many older versions of various iAVF drivers convert the reported
+ * status code directly into an iavf_status enumeration. For this reason, it
+ * is important that the values of these enumerations line up.
+ */
 enum virtchnl_status_code {
 	VIRTCHNL_STATUS_SUCCESS				= 0,
 	VIRTCHNL_STATUS_ERR_PARAM			= -5,
@@ -92,6 +107,9 @@ enum virtchnl_rx_hsplit {
 	VIRTCHNL_RX_HSPLIT_SPLIT_SCTP    = 8,
 };
 
+enum virtchnl_bw_limit_type {
+	VIRTCHNL_BW_SHAPER = 0,
+};
 /* END GENERIC DEFINES */
 
 /* Opcodes for VF-PF communication. These are placed in the v_opcode field
@@ -136,11 +154,14 @@ enum virtchnl_ops {
 	VIRTCHNL_OP_DISABLE_CHANNELS = 31,
 	VIRTCHNL_OP_ADD_CLOUD_FILTER = 32,
 	VIRTCHNL_OP_DEL_CLOUD_FILTER = 33,
-	/* opcode 34 - 44 are reserved */
+	/* opcode 34 is reserved */
+	/* opcodes 38, 39, 40, 41, 42 and 43 are reserved */
+	VIRTCHNL_OP_GET_SUPPORTED_RXDIDS = 44,
 	VIRTCHNL_OP_ADD_RSS_CFG = 45,
 	VIRTCHNL_OP_DEL_RSS_CFG = 46,
 	VIRTCHNL_OP_ADD_FDIR_FILTER = 47,
 	VIRTCHNL_OP_DEL_FDIR_FILTER = 48,
+	VIRTCHNL_OP_GET_MAX_RSS_QREGION = 50,
 	VIRTCHNL_OP_GET_OFFLOAD_VLAN_V2_CAPS = 51,
 	VIRTCHNL_OP_ADD_VLAN_V2 = 52,
 	VIRTCHNL_OP_DEL_VLAN_V2 = 53,
@@ -148,31 +169,206 @@ enum virtchnl_ops {
 	VIRTCHNL_OP_DISABLE_VLAN_STRIPPING_V2 = 55,
 	VIRTCHNL_OP_ENABLE_VLAN_INSERTION_V2 = 56,
 	VIRTCHNL_OP_DISABLE_VLAN_INSERTION_V2 = 57,
+	VIRTCHNL_OP_ENABLE_VLAN_FILTERING_V2 = 58,
+	VIRTCHNL_OP_DISABLE_VLAN_FILTERING_V2 = 59,
+	VIRTCHNL_OP_1588_PTP_GET_CAPS = 60,
+	VIRTCHNL_OP_1588_PTP_GET_TIME = 61,
+	VIRTCHNL_OP_1588_PTP_SET_TIME = 62,
+	VIRTCHNL_OP_1588_PTP_ADJ_TIME = 63,
+	VIRTCHNL_OP_1588_PTP_ADJ_FREQ = 64,
+	VIRTCHNL_OP_1588_PTP_TX_TIMESTAMP = 65,
+	VIRTCHNL_OP_GET_QOS_CAPS = 66,
+	VIRTCHNL_OP_CONFIG_QUEUE_TC_MAP = 67,
+	VIRTCHNL_OP_1588_PTP_GET_PIN_CFGS = 68,
+	VIRTCHNL_OP_1588_PTP_SET_PIN_CFG = 69,
+	VIRTCHNL_OP_1588_PTP_EXT_TIMESTAMP = 70,
+	VIRTCHNL_OP_ENABLE_QUEUES_V2 = 107,
+	VIRTCHNL_OP_DISABLE_QUEUES_V2 = 108,
+	VIRTCHNL_OP_MAP_QUEUE_VECTOR = 111,
+	/* New major set of opcodes introduced and so leaving room for
+	 * old misc opcodes to be added in future. Also these opcodes may only
+	 * be used if both the PF and VF have successfully negotiated the
+	 * VIRTCHNL version as 2.0 during VIRTCHNL_OP_VERSION exchange.
+	 */
+	VIRTCHNL2_OP_GET_CAPS = 500,
+	VIRTCHNL2_OP_CREATE_VPORT = 501,
+	VIRTCHNL2_OP_DESTROY_VPORT = 502,
+	VIRTCHNL2_OP_ENABLE_VPORT = 503,
+	VIRTCHNL2_OP_DISABLE_VPORT = 504,
+	VIRTCHNL2_OP_CONFIG_TX_QUEUES = 505,
+	VIRTCHNL2_OP_CONFIG_RX_QUEUES = 506,
+	VIRTCHNL2_OP_ENABLE_QUEUES = 507,
+	VIRTCHNL2_OP_DISABLE_QUEUES = 508,
+	VIRTCHNL2_OP_ADD_QUEUES = 509,
+	VIRTCHNL2_OP_DEL_QUEUES = 510,
+	VIRTCHNL2_OP_MAP_QUEUE_VECTOR = 511,
+	VIRTCHNL2_OP_UNMAP_QUEUE_VECTOR = 512,
+	VIRTCHNL2_OP_GET_RSS_KEY = 513,
+	VIRTCHNL2_OP_SET_RSS_KEY = 514,
+	VIRTCHNL2_OP_GET_RSS_LUT = 515,
+	VIRTCHNL2_OP_SET_RSS_LUT = 516,
+	VIRTCHNL2_OP_GET_RSS_HASH = 517,
+	VIRTCHNL2_OP_SET_RSS_HASH = 518,
+	VIRTCHNL2_OP_SET_SRIOV_VFS = 519,
+	VIRTCHNL2_OP_ALLOC_VECTORS = 520,
+	VIRTCHNL2_OP_DEALLOC_VECTORS = 521,
+	VIRTCHNL2_OP_EVENT = 522,
+	VIRTCHNL2_OP_GET_STATS = 523,
+	VIRTCHNL2_OP_RESET_VF = 524,
+	/* opcode 525 is reserved */
+	VIRTCHNL2_OP_GET_PTYPE_INFO = 526,
+	/* opcode 527 and 528 are reserved for VIRTCHNL2_OP_GET_PTYPE_ID and
+	 * VIRTCHNL2_OP_GET_PTYPE_INFO_RAW
+	 */
+	/* opcodes 529, 530, and 531 are reserved */
 	VIRTCHNL_OP_MAX,
 };
 
-/* These macros are used to generate compilation errors if a structure/union
- * is not exactly the correct length. It gives a divide by zero error if the
- * structure/union is not of the correct size, otherwise it creates an enum
- * that is never used.
- */
-#define VIRTCHNL_CHECK_STRUCT_LEN(n, X) enum virtchnl_static_assert_enum_##X \
-	{ virtchnl_static_assert_##X = (n)/((sizeof(struct X) == (n)) ? 1 : 0) }
-#define VIRTCHNL_CHECK_UNION_LEN(n, X) enum virtchnl_static_asset_enum_##X \
-	{ virtchnl_static_assert_##X = (n)/((sizeof(union X) == (n)) ? 1 : 0) }
-
-/* Virtual channel message descriptor. This overlays the admin queue
- * descriptor. All other data is passed in external buffers.
- */
-
-struct virtchnl_msg {
-	u8 pad[8];			 /* AQ flags/opcode/len/retval fields */
-	enum virtchnl_ops v_opcode; /* avoid confusion with desc->opcode */
-	enum virtchnl_status_code v_retval;  /* ditto for desc->retval */
-	u32 vfid;			 /* used by PF when sending to VF */
-};
+static inline const char *virtchnl_op_str(enum virtchnl_ops v_opcode)
+{
+	switch (v_opcode) {
+	case VIRTCHNL_OP_UNKNOWN:
+		return "VIRTCHNL_OP_UNKNOWN";
+	case VIRTCHNL_OP_VERSION:
+		return "VIRTCHNL_OP_VERSION";
+	case VIRTCHNL_OP_RESET_VF:
+		return "VIRTCHNL_OP_RESET_VF";
+	case VIRTCHNL_OP_GET_VF_RESOURCES:
+		return "VIRTCHNL_OP_GET_VF_RESOURCES";
+	case VIRTCHNL_OP_CONFIG_TX_QUEUE:
+		return "VIRTCHNL_OP_CONFIG_TX_QUEUE";
+	case VIRTCHNL_OP_CONFIG_RX_QUEUE:
+		return "VIRTCHNL_OP_CONFIG_RX_QUEUE";
+	case VIRTCHNL_OP_CONFIG_VSI_QUEUES:
+		return "VIRTCHNL_OP_CONFIG_VSI_QUEUES";
+	case VIRTCHNL_OP_CONFIG_IRQ_MAP:
+		return "VIRTCHNL_OP_CONFIG_IRQ_MAP";
+	case VIRTCHNL_OP_ENABLE_QUEUES:
+		return "VIRTCHNL_OP_ENABLE_QUEUES";
+	case VIRTCHNL_OP_DISABLE_QUEUES:
+		return "VIRTCHNL_OP_DISABLE_QUEUES";
+	case VIRTCHNL_OP_ADD_ETH_ADDR:
+		return "VIRTCHNL_OP_ADD_ETH_ADDR";
+	case VIRTCHNL_OP_DEL_ETH_ADDR:
+		return "VIRTCHNL_OP_DEL_ETH_ADDR";
+	case VIRTCHNL_OP_ADD_VLAN:
+		return "VIRTCHNL_OP_ADD_VLAN";
+	case VIRTCHNL_OP_DEL_VLAN:
+		return "VIRTCHNL_OP_DEL_VLAN";
+	case VIRTCHNL_OP_CONFIG_PROMISCUOUS_MODE:
+		return "VIRTCHNL_OP_CONFIG_PROMISCUOUS_MODE";
+	case VIRTCHNL_OP_GET_STATS:
+		return "VIRTCHNL_OP_GET_STATS";
+	case VIRTCHNL_OP_RSVD:
+		return "VIRTCHNL_OP_RSVD";
+	case VIRTCHNL_OP_EVENT:
+		return "VIRTCHNL_OP_EVENT";
+	case VIRTCHNL_OP_CONFIG_RSS_KEY:
+		return "VIRTCHNL_OP_CONFIG_RSS_KEY";
+	case VIRTCHNL_OP_CONFIG_RSS_LUT:
+		return "VIRTCHNL_OP_CONFIG_RSS_LUT";
+	case VIRTCHNL_OP_GET_RSS_HENA_CAPS:
+		return "VIRTCHNL_OP_GET_RSS_HENA_CAPS";
+	case VIRTCHNL_OP_SET_RSS_HENA:
+		return "VIRTCHNL_OP_SET_RSS_HENA";
+	case VIRTCHNL_OP_ENABLE_VLAN_STRIPPING:
+		return "VIRTCHNL_OP_ENABLE_VLAN_STRIPPING";
+	case VIRTCHNL_OP_DISABLE_VLAN_STRIPPING:
+		return "VIRTCHNL_OP_DISABLE_VLAN_STRIPPING";
+	case VIRTCHNL_OP_REQUEST_QUEUES:
+		return "VIRTCHNL_OP_REQUEST_QUEUES";
+	case VIRTCHNL_OP_ENABLE_CHANNELS:
+		return "VIRTCHNL_OP_ENABLE_CHANNELS";
+	case VIRTCHNL_OP_DISABLE_CHANNELS:
+		return "VIRTCHNL_OP_DISABLE_CHANNELS";
+	case VIRTCHNL_OP_ADD_CLOUD_FILTER:
+		return "VIRTCHNL_OP_ADD_CLOUD_FILTER";
+	case VIRTCHNL_OP_DEL_CLOUD_FILTER:
+		return "VIRTCHNL_OP_DEL_CLOUD_FILTER";
+	case VIRTCHNL_OP_GET_SUPPORTED_RXDIDS:
+		return "VIRTCHNL_OP_GET_SUPPORTED_RXDIDS";
+	case VIRTCHNL_OP_ADD_RSS_CFG:
+		return "VIRTCHNL_OP_ADD_RSS_CFG";
+	case VIRTCHNL_OP_DEL_RSS_CFG:
+		return "VIRTCHNL_OP_DEL_RSS_CFG";
+	case VIRTCHNL_OP_ADD_FDIR_FILTER:
+		return "VIRTCHNL_OP_ADD_FDIR_FILTER";
+	case VIRTCHNL_OP_DEL_FDIR_FILTER:
+		return "VIRTCHNL_OP_DEL_FDIR_FILTER";
+	case VIRTCHNL_OP_GET_MAX_RSS_QREGION:
+		return "VIRTCHNL_OP_GET_MAX_RSS_QREGION";
+	case VIRTCHNL_OP_GET_OFFLOAD_VLAN_V2_CAPS:
+		return "VIRTCHNL_OP_GET_OFFLOAD_VLAN_V2_CAPS";
+	case VIRTCHNL_OP_ADD_VLAN_V2:
+		return "VIRTCHNL_OP_ADD_VLAN_V2";
+	case VIRTCHNL_OP_DEL_VLAN_V2:
+		return "VIRTCHNL_OP_DEL_VLAN_V2";
+	case VIRTCHNL_OP_ENABLE_VLAN_STRIPPING_V2:
+		return "VIRTCHNL_OP_ENABLE_VLAN_STRIPPING_V2";
+	case VIRTCHNL_OP_DISABLE_VLAN_STRIPPING_V2:
+		return "VIRTCHNL_OP_DISABLE_VLAN_STRIPPING_V2";
+	case VIRTCHNL_OP_ENABLE_VLAN_INSERTION_V2:
+		return "VIRTCHNL_OP_ENABLE_VLAN_INSERTION_V2";
+	case VIRTCHNL_OP_DISABLE_VLAN_INSERTION_V2:
+		return "VIRTCHNL_OP_DISABLE_VLAN_INSERTION_V2";
+	case VIRTCHNL_OP_ENABLE_VLAN_FILTERING_V2:
+		return "VIRTCHNL_OP_ENABLE_VLAN_FILTERING_V2";
+	case VIRTCHNL_OP_DISABLE_VLAN_FILTERING_V2:
+		return "VIRTCHNL_OP_DISABLE_VLAN_FILTERING_V2";
+	case VIRTCHNL_OP_1588_PTP_GET_CAPS:
+		return "VIRTCHNL_OP_1588_PTP_GET_CAPS";
+	case VIRTCHNL_OP_1588_PTP_GET_TIME:
+		return "VIRTCHNL_OP_1588_PTP_GET_TIME";
+	case VIRTCHNL_OP_1588_PTP_SET_TIME:
+		return "VIRTCHNL_OP_1588_PTP_SET_TIME";
+	case VIRTCHNL_OP_1588_PTP_ADJ_TIME:
+		return "VIRTCHNL_OP_1588_PTP_ADJ_TIME";
+	case VIRTCHNL_OP_1588_PTP_ADJ_FREQ:
+		return "VIRTCHNL_OP_1588_PTP_ADJ_FREQ";
+	case VIRTCHNL_OP_1588_PTP_TX_TIMESTAMP:
+		return "VIRTCHNL_OP_1588_PTP_TX_TIMESTAMP";
+	case VIRTCHNL_OP_1588_PTP_GET_PIN_CFGS:
+		return "VIRTCHNL_OP_1588_PTP_GET_PIN_CFGS";
+	case VIRTCHNL_OP_1588_PTP_SET_PIN_CFG:
+		return "VIRTCHNL_OP_1588_PTP_SET_PIN_CFG";
+	case VIRTCHNL_OP_1588_PTP_EXT_TIMESTAMP:
+		return "VIRTCHNL_OP_1588_PTP_EXT_TIMESTAMP";
+	case VIRTCHNL_OP_ENABLE_QUEUES_V2:
+		return "VIRTCHNL_OP_ENABLE_QUEUES_V2";
+	case VIRTCHNL_OP_DISABLE_QUEUES_V2:
+		return "VIRTCHNL_OP_DISABLE_QUEUES_V2";
+	case VIRTCHNL_OP_MAP_QUEUE_VECTOR:
+		return "VIRTCHNL_OP_MAP_QUEUE_VECTOR";
+	case VIRTCHNL_OP_MAX:
+		return "VIRTCHNL_OP_MAX";
+	default:
+		return "Unsupported (update virtchnl.h)";
+	}
+}
 
-VIRTCHNL_CHECK_STRUCT_LEN(20, virtchnl_msg);
+static inline const char *virtchnl_stat_str(enum virtchnl_status_code v_status)
+{
+	switch (v_status) {
+	case VIRTCHNL_STATUS_SUCCESS:
+		return "VIRTCHNL_STATUS_SUCCESS";
+	case VIRTCHNL_STATUS_ERR_PARAM:
+		return "VIRTCHNL_STATUS_ERR_PARAM";
+	case VIRTCHNL_STATUS_ERR_NO_MEMORY:
+		return "VIRTCHNL_STATUS_ERR_NO_MEMORY";
+	case VIRTCHNL_STATUS_ERR_OPCODE_MISMATCH:
+		return "VIRTCHNL_STATUS_ERR_OPCODE_MISMATCH";
+	case VIRTCHNL_STATUS_ERR_CQP_COMPL_ERROR:
+		return "VIRTCHNL_STATUS_ERR_CQP_COMPL_ERROR";
+	case VIRTCHNL_STATUS_ERR_INVALID_VF_ID:
+		return "VIRTCHNL_STATUS_ERR_INVALID_VF_ID";
+	case VIRTCHNL_STATUS_ERR_ADMIN_QUEUE_ERROR:
+		return "VIRTCHNL_STATUS_ERR_ADMIN_QUEUE_ERROR";
+	case VIRTCHNL_STATUS_ERR_NOT_SUPPORTED:
+		return "VIRTCHNL_STATUS_ERR_NOT_SUPPORTED";
+	default:
+		return "Unknown status code (update virtchnl.h)";
+	}
+}
 
 /* Message descriptions and data structures. */
 
@@ -190,6 +386,8 @@ VIRTCHNL_CHECK_STRUCT_LEN(20, virtchnl_msg);
  */
 #define VIRTCHNL_VERSION_MAJOR		1
 #define VIRTCHNL_VERSION_MINOR		1
+#define VIRTCHNL_VERSION_MAJOR_2	2
+#define VIRTCHNL_VERSION_MINOR_0	0
 #define VIRTCHNL_VERSION_MINOR_NO_VF_CAPS	0
 
 struct virtchnl_version_info {
@@ -199,8 +397,9 @@ struct virtchnl_version_info {
 
 VIRTCHNL_CHECK_STRUCT_LEN(8, virtchnl_version_info);
 
-#define VF_IS_V10(_v) (((_v)->major == 1) && ((_v)->minor == 0))
+#define VF_IS_V10(_ver) (((_ver)->major == 1) && ((_ver)->minor == 0))
 #define VF_IS_V11(_ver) (((_ver)->major == 1) && ((_ver)->minor == 1))
+#define VF_IS_V20(_ver) (((_ver)->major == 2) && ((_ver)->minor == 0))
 
 /* VIRTCHNL_OP_RESET_VF
  * VF sends this request to PF with no parameters
@@ -234,7 +433,9 @@ enum virtchnl_vsi_type {
 struct virtchnl_vsi_resource {
 	u16 vsi_id;
 	u16 num_queue_pairs;
-	enum virtchnl_vsi_type vsi_type;
+
+	/* see enum virtchnl_vsi_type */
+	s32 vsi_type;
 	u16 qset_handle;
 	u8 default_mac_addr[ETH_ALEN];
 };
@@ -247,12 +448,16 @@ VIRTCHNL_CHECK_STRUCT_LEN(16, virtchnl_vsi_resource);
  */
 #define VIRTCHNL_VF_OFFLOAD_L2			BIT(0)
 #define VIRTCHNL_VF_OFFLOAD_IWARP		BIT(1)
+#define VIRTCHNL_VF_CAP_RDMA			VIRTCHNL_VF_OFFLOAD_IWARP
 #define VIRTCHNL_VF_OFFLOAD_RSS_AQ		BIT(3)
 #define VIRTCHNL_VF_OFFLOAD_RSS_REG		BIT(4)
 #define VIRTCHNL_VF_OFFLOAD_WB_ON_ITR		BIT(5)
 #define VIRTCHNL_VF_OFFLOAD_REQ_QUEUES		BIT(6)
 /* used to negotiate communicating link speeds in Mbps */
 #define VIRTCHNL_VF_CAP_ADV_LINK_SPEED		BIT(7)
+	/* BIT(8) is reserved */
+#define VIRTCHNL_VF_LARGE_NUM_QPAIRS		BIT(9)
+#define VIRTCHNL_VF_OFFLOAD_CRC			BIT(10)
 #define VIRTCHNL_VF_OFFLOAD_VLAN_V2		BIT(15)
 #define VIRTCHNL_VF_OFFLOAD_VLAN		BIT(16)
 #define VIRTCHNL_VF_OFFLOAD_RX_POLLING		BIT(17)
@@ -262,9 +467,14 @@ VIRTCHNL_CHECK_STRUCT_LEN(16, virtchnl_vsi_resource);
 #define VIRTCHNL_VF_OFFLOAD_ENCAP_CSUM		BIT(21)
 #define VIRTCHNL_VF_OFFLOAD_RX_ENCAP_CSUM	BIT(22)
 #define VIRTCHNL_VF_OFFLOAD_ADQ			BIT(23)
+#define VIRTCHNL_VF_OFFLOAD_ADQ_V2		BIT(24)
 #define VIRTCHNL_VF_OFFLOAD_USO			BIT(25)
+#define VIRTCHNL_VF_OFFLOAD_RX_FLEX_DESC	BIT(26)
 #define VIRTCHNL_VF_OFFLOAD_ADV_RSS_PF		BIT(27)
 #define VIRTCHNL_VF_OFFLOAD_FDIR_PF		BIT(28)
+#define VIRTCHNL_VF_OFFLOAD_QOS			BIT(29)
+	/* BIT(30) is reserved */
+#define VIRTCHNL_VF_CAP_PTP			BIT(31)
 
 #define VF_BASE_MODE_OFFLOADS (VIRTCHNL_VF_OFFLOAD_L2 | \
 			       VIRTCHNL_VF_OFFLOAD_VLAN | \
@@ -303,10 +513,64 @@ struct virtchnl_txq_info {
 
 VIRTCHNL_CHECK_STRUCT_LEN(24, virtchnl_txq_info);
 
+/* RX descriptor IDs (range from 0 to 63) */
+enum virtchnl_rx_desc_ids {
+	VIRTCHNL_RXDID_0_16B_BASE		= 0,
+	/* 32B_BASE and FLEX_SPLITQ share desc ids as default descriptors
+	 * because they can be differentiated based on queue model; e.g. single
+	 * queue model can only use 32B_BASE and split queue model can only use
+	 * FLEX_SPLITQ.  Having these as 1 allows them to be used as default
+	 * descriptors without negotiation.
+	 */
+	VIRTCHNL_RXDID_1_32B_BASE		= 1,
+	VIRTCHNL_RXDID_1_FLEX_SPLITQ		= 1,
+	VIRTCHNL_RXDID_2_FLEX_SQ_NIC		= 2,
+	VIRTCHNL_RXDID_3_FLEX_SQ_SW		= 3,
+	VIRTCHNL_RXDID_4_FLEX_SQ_NIC_VEB	= 4,
+	VIRTCHNL_RXDID_5_FLEX_SQ_NIC_ACL	= 5,
+	VIRTCHNL_RXDID_6_FLEX_SQ_NIC_2		= 6,
+	VIRTCHNL_RXDID_7_HW_RSVD		= 7,
+	/* 9 through 15 are reserved */
+	VIRTCHNL_RXDID_16_COMMS_GENERIC		= 16,
+	VIRTCHNL_RXDID_17_COMMS_AUX_VLAN	= 17,
+	VIRTCHNL_RXDID_18_COMMS_AUX_IPV4	= 18,
+	VIRTCHNL_RXDID_19_COMMS_AUX_IPV6	= 19,
+	VIRTCHNL_RXDID_20_COMMS_AUX_FLOW	= 20,
+	VIRTCHNL_RXDID_21_COMMS_AUX_TCP		= 21,
+	/* 22 through 63 are reserved */
+};
+
+/* RX descriptor ID bitmasks */
+enum virtchnl_rx_desc_id_bitmasks {
+	VIRTCHNL_RXDID_0_16B_BASE_M		= BIT(VIRTCHNL_RXDID_0_16B_BASE),
+	VIRTCHNL_RXDID_1_32B_BASE_M		= BIT(VIRTCHNL_RXDID_1_32B_BASE),
+	VIRTCHNL_RXDID_1_FLEX_SPLITQ_M		= BIT(VIRTCHNL_RXDID_1_FLEX_SPLITQ),
+	VIRTCHNL_RXDID_2_FLEX_SQ_NIC_M		= BIT(VIRTCHNL_RXDID_2_FLEX_SQ_NIC),
+	VIRTCHNL_RXDID_3_FLEX_SQ_SW_M		= BIT(VIRTCHNL_RXDID_3_FLEX_SQ_SW),
+	VIRTCHNL_RXDID_4_FLEX_SQ_NIC_VEB_M	= BIT(VIRTCHNL_RXDID_4_FLEX_SQ_NIC_VEB),
+	VIRTCHNL_RXDID_5_FLEX_SQ_NIC_ACL_M	= BIT(VIRTCHNL_RXDID_5_FLEX_SQ_NIC_ACL),
+	VIRTCHNL_RXDID_6_FLEX_SQ_NIC_2_M	= BIT(VIRTCHNL_RXDID_6_FLEX_SQ_NIC_2),
+	VIRTCHNL_RXDID_7_HW_RSVD_M		= BIT(VIRTCHNL_RXDID_7_HW_RSVD),
+	/* 9 through 15 are reserved */
+	VIRTCHNL_RXDID_16_COMMS_GENERIC_M	= BIT(VIRTCHNL_RXDID_16_COMMS_GENERIC),
+	VIRTCHNL_RXDID_17_COMMS_AUX_VLAN_M	= BIT(VIRTCHNL_RXDID_17_COMMS_AUX_VLAN),
+	VIRTCHNL_RXDID_18_COMMS_AUX_IPV4_M	= BIT(VIRTCHNL_RXDID_18_COMMS_AUX_IPV4),
+	VIRTCHNL_RXDID_19_COMMS_AUX_IPV6_M	= BIT(VIRTCHNL_RXDID_19_COMMS_AUX_IPV6),
+	VIRTCHNL_RXDID_20_COMMS_AUX_FLOW_M	= BIT(VIRTCHNL_RXDID_20_COMMS_AUX_FLOW),
+	VIRTCHNL_RXDID_21_COMMS_AUX_TCP_M	= BIT(VIRTCHNL_RXDID_21_COMMS_AUX_TCP),
+	/* 22 through 63 are reserved */
+};
+
 /* VIRTCHNL_OP_CONFIG_RX_QUEUE
  * VF sends this message to set up parameters for one RX queue.
  * External data buffer contains one instance of virtchnl_rxq_info.
- * PF configures requested queue and returns a status code.
+ * PF configures requested queue and returns a status code. The
+ * crc_disable flag disables CRC stripping on the VF. Setting
+ * the crc_disable flag to 1 will disable CRC stripping for each
+ * queue in the VF where the flag is set. The VIRTCHNL_VF_OFFLOAD_CRC
+ * offload must have been set prior to sending this info or the PF
+ * will ignore the request. This flag should be set the same for
+ * all of the queues for a VF.
  */
 
 /* Rx queue config info */
@@ -318,20 +582,32 @@ struct virtchnl_rxq_info {
 	u16 splithdr_enabled; /* deprecated with AVF 1.0 */
 	u32 databuffer_size;
 	u32 max_pkt_size;
-	u32 pad1;
+	u8 crc_disable;
+	/* see enum virtchnl_rx_desc_ids;
+	 * only used when VIRTCHNL_VF_OFFLOAD_RX_FLEX_DESC is supported. Note
+	 * that when the offload is not supported, the descriptor format aligns
+	 * with VIRTCHNL_RXDID_1_32B_BASE.
+	 */
+	u8 rxdid;
+	u8 pad1[2];
 	u64 dma_ring_addr;
-	enum virtchnl_rx_hsplit rx_split_pos; /* deprecated with AVF 1.0 */
+
+	/* see enum virtchnl_rx_hsplit; deprecated with AVF 1.0 */
+	s32 rx_split_pos;
 	u32 pad2;
 };
 
 VIRTCHNL_CHECK_STRUCT_LEN(40, virtchnl_rxq_info);
 
 /* VIRTCHNL_OP_CONFIG_VSI_QUEUES
- * VF sends this message to set parameters for all active TX and RX queues
+ * VF sends this message to set parameters for active TX and RX queues
  * associated with the specified VSI.
  * PF configures queues and returns status.
  * If the number of queues specified is greater than the number of queues
  * associated with the VSI, an error is returned and no queues are configured.
+ * NOTE: The VF is not required to configure all queues in a single request.
+ * It may send multiple messages. PF drivers must correctly handle all VF
+ * requests.
  */
 struct virtchnl_queue_pair_info {
 	/* NOTE: vsi_id and queue_id should be identical for both queues. */
@@ -369,8 +645,13 @@ struct virtchnl_vf_res_request {
  * VF uses this message to map vectors to queues.
  * The rxq_map and txq_map fields are bitmaps used to indicate which queues
  * are to be associated with the specified vector.
- * The "other" causes are always mapped to vector 0.
+ * The "other" causes are always mapped to vector 0. The VF may not request
+ * that vector 0 be used for traffic.
  * PF configures interrupt mapping and returns status.
+ * NOTE: due to hardware requirements, all active queues (both TX and RX)
+ * should be mapped to interrupts, even if the driver intends to operate
+ * only in polling mode. In this case the interrupt may be disabled, but
+ * the ITR timer will still run to trigger writebacks.
  */
 struct virtchnl_vector_map {
 	u16 vsi_id;
@@ -397,6 +678,9 @@ VIRTCHNL_CHECK_STRUCT_LEN(14, virtchnl_irq_map_info);
  * (Currently, we only support 16 queues per VF, but we make the field
  * u32 to allow for expansion.)
  * PF performs requested action and returns status.
+ * NOTE: The VF is not required to enable/disable all queues in a single
+ * request. It may send multiple messages.
+ * PF drivers must correctly handle all VF requests.
  */
 struct virtchnl_queue_select {
 	u16 vsi_id;
@@ -407,6 +691,35 @@ struct virtchnl_queue_select {
 
 VIRTCHNL_CHECK_STRUCT_LEN(12, virtchnl_queue_select);
 
+/* VIRTCHNL_OP_GET_MAX_RSS_QREGION
+ *
+ * if VIRTCHNL_VF_LARGE_NUM_QPAIRS was negotiated in VIRTCHNL_OP_GET_VF_RESOURCES
+ * then this op must be supported.
+ *
+ * VF sends this message in order to query the max RSS queue region
+ * size supported by PF, when VIRTCHNL_VF_LARGE_NUM_QPAIRS is enabled.
+ * This information should be used when configuring the RSS LUT and/or
+ * configuring queue region based filters.
+ *
+ * The maximum RSS queue region is 2^qregion_width. So, a qregion_width
+ * of 6 would inform the VF that the PF supports a maximum RSS queue region
+ * of 64.
+ *
+ * A queue region represents a range of queues that can be used to configure
+ * a RSS LUT. For example, if a VF is given 64 queues, but only a max queue
+ * region size of 16 (i.e. 2^qregion_width = 16) then it will only be able
+ * to configure the RSS LUT with queue indices from 0 to 15. However, other
+ * filters can be used to direct packets to queues >15 via specifying a queue
+ * base/offset and queue region width.
+ */
+struct virtchnl_max_rss_qregion {
+	u16 vport_id;
+	u16 qregion_width;
+	u8 pad[4];
+};
+
+VIRTCHNL_CHECK_STRUCT_LEN(8, virtchnl_max_rss_qregion);
+
 /* VIRTCHNL_OP_ADD_ETH_ADDR
  * VF sends this message in order to add one or more unicast or multicast
  * address filters for the specified VSI.
@@ -538,17 +851,17 @@ VIRTCHNL_CHECK_STRUCT_LEN(6, virtchnl_vlan_filter_list);
  */
 enum virtchnl_vlan_support {
 	VIRTCHNL_VLAN_UNSUPPORTED =		0,
-	VIRTCHNL_VLAN_ETHERTYPE_8100 =		BIT(0),
-	VIRTCHNL_VLAN_ETHERTYPE_88A8 =		BIT(1),
-	VIRTCHNL_VLAN_ETHERTYPE_9100 =		BIT(2),
-	VIRTCHNL_VLAN_TAG_LOCATION_L2TAG1 =	BIT(8),
-	VIRTCHNL_VLAN_TAG_LOCATION_L2TAG2 =	BIT(9),
-	VIRTCHNL_VLAN_TAG_LOCATION_L2TAG2_2 =	BIT(10),
-	VIRTCHNL_VLAN_PRIO =			BIT(24),
-	VIRTCHNL_VLAN_FILTER_MASK =		BIT(28),
-	VIRTCHNL_VLAN_ETHERTYPE_AND =		BIT(29),
-	VIRTCHNL_VLAN_ETHERTYPE_XOR =		BIT(30),
-	VIRTCHNL_VLAN_TOGGLE =			BIT(31),
+	VIRTCHNL_VLAN_ETHERTYPE_8100 =		0x00000001,
+	VIRTCHNL_VLAN_ETHERTYPE_88A8 =		0x00000002,
+	VIRTCHNL_VLAN_ETHERTYPE_9100 =		0x00000004,
+	VIRTCHNL_VLAN_TAG_LOCATION_L2TAG1 =	0x00000100,
+	VIRTCHNL_VLAN_TAG_LOCATION_L2TAG2 =	0x00000200,
+	VIRTCHNL_VLAN_TAG_LOCATION_L2TAG2_2 =	0x00000400,
+	VIRTCHNL_VLAN_PRIO =			0x01000000,
+	VIRTCHNL_VLAN_FILTER_MASK =		0x10000000,
+	VIRTCHNL_VLAN_ETHERTYPE_AND =		0x20000000,
+	VIRTCHNL_VLAN_ETHERTYPE_XOR =		0x40000000,
+	VIRTCHNL_VLAN_TOGGLE =			0x80000000
 };
 
 /* This structure is used as part of the VIRTCHNL_OP_GET_OFFLOAD_VLAN_V2_CAPS
@@ -818,6 +1131,43 @@ VIRTCHNL_CHECK_STRUCT_LEN(40, virtchnl_vlan_filter_list_v2);
  *
  * virtchnl_vlan_setting.vport_id = vport_id or vsi_id assigned to the VF on
  * initialization.
+ *
+ * VIRTCHNL_OP_ENABLE_VLAN_FILTERING_V2
+ * VIRTCHNL_OP_DISABLE_VLAN_FILTERING_V2
+ *
+ * VF sends this message to enable or disable VLAN filtering. It also needs to
+ * specify an ethertype. The VF knows which VLAN ethertypes are allowed and
+ * whether or not it's allowed to enable/disable filtering via the
+ * VIRTCHNL_OP_GET_OFFLOAD_VLAN_V2_CAPS message. The VF needs to
+ * parse the virtchnl_vlan_caps.filtering fields to determine which, if any,
+ * filtering messages are allowed.
+ *
+ * For example, if the PF populates the virtchnl_vlan_caps.filtering in the
+ * following manner the VF will be allowed to enable/disable 0x8100 and 0x88a8
+ * outer VLAN filtering together. Note, that the VIRTCHNL_VLAN_ETHERTYPE_AND
+ * means that all filtering ethertypes will to be enabled and disabled together
+ * regardless of the request from the VF. This means that the underlying
+ * hardware only supports VLAN filtering for all VLAN the specified ethertypes
+ * or none of them.
+ *
+ * virtchnl_vlan_caps.filtering.filtering_support.outer =
+ *			VIRTCHNL_VLAN_TOGGLE |
+ *			VIRTCHNL_VLAN_ETHERTYPE_8100 |
+ *			VIRTHCNL_VLAN_ETHERTYPE_88A8 |
+ *			VIRTCHNL_VLAN_ETHERTYPE_9100 |
+ *			VIRTCHNL_VLAN_ETHERTYPE_AND;
+ *
+ * In order to enable outer VLAN filtering for 0x88a8 and 0x8100 VLANs (0x9100
+ * VLANs aren't supported by the VF driver), the VF would populate the
+ * virtchnl_vlan_setting structure in the following manner and send the
+ * VIRTCHNL_OP_ENABLE_VLAN_FILTERING_V2. The same message format would be used
+ * to disable outer VLAN filtering for 0x88a8 and 0x8100 VLANs, but the
+ * VIRTCHNL_OP_DISABLE_VLAN_FILTERING_V2 opcode is used.
+ *
+ * virtchnl_vlan_setting.outer_ethertype_setting =
+ *			VIRTCHNL_VLAN_ETHERTYPE_8100 |
+ *			VIRTCHNL_VLAN_ETHERTYPE_88A8;
+ *
  */
 struct virtchnl_vlan_setting {
 	u32 outer_ethertype_setting;
@@ -848,9 +1198,24 @@ VIRTCHNL_CHECK_STRUCT_LEN(4, virtchnl_promisc_info);
  * the virtchnl_queue_select struct to specify the VSI. The queue_id
  * field is ignored by the PF.
  *
- * PF replies with struct eth_stats in an external buffer.
+ * PF replies with struct virtchnl_eth_stats in an external buffer.
  */
 
+struct virtchnl_eth_stats {
+	u64 rx_bytes;			/* received bytes */
+	u64 rx_unicast;			/* received unicast pkts */
+	u64 rx_multicast;		/* received multicast pkts */
+	u64 rx_broadcast;		/* received broadcast pkts */
+	u64 rx_discards;
+	u64 rx_unknown_protocol;
+	u64 tx_bytes;			/* transmitted bytes */
+	u64 tx_unicast;			/* transmitted unicast pkts */
+	u64 tx_multicast;		/* transmitted multicast pkts */
+	u64 tx_broadcast;		/* transmitted broadcast pkts */
+	u64 tx_discards;
+	u64 tx_errors;
+};
+
 /* VIRTCHNL_OP_CONFIG_RSS_KEY
  * VIRTCHNL_OP_CONFIG_RSS_LUT
  * VF sends these messages to configure RSS. Only supported if both PF
@@ -889,6 +1254,21 @@ struct virtchnl_rss_hena {
 
 VIRTCHNL_CHECK_STRUCT_LEN(8, virtchnl_rss_hena);
 
+/* Type of RSS algorithm */
+enum virtchnl_rss_algorithm {
+	VIRTCHNL_RSS_ALG_TOEPLITZ_ASYMMETRIC	= 0,
+	VIRTCHNL_RSS_ALG_R_ASYMMETRIC		= 1,
+	VIRTCHNL_RSS_ALG_TOEPLITZ_SYMMETRIC	= 2,
+	VIRTCHNL_RSS_ALG_XOR_SYMMETRIC		= 3,
+};
+
+/* This is used by PF driver to enforce how many channels can be supported.
+ * When ADQ_V2 capability is negotiated, it will allow 16 channels otherwise
+ * PF driver will allow only max 4 channels
+ */
+#define VIRTCHNL_MAX_ADQ_CHANNELS 4
+#define VIRTCHNL_MAX_ADQ_V2_CHANNELS 16
+
 /* VIRTCHNL_OP_ENABLE_CHANNELS
  * VIRTCHNL_OP_DISABLE_CHANNELS
  * VF sends these messages to enable or disable channels based on
@@ -924,6 +1304,11 @@ VIRTCHNL_CHECK_STRUCT_LEN(24, virtchnl_tc_info);
 struct virtchnl_l4_spec {
 	u8	src_mac[ETH_ALEN];
 	u8	dst_mac[ETH_ALEN];
+	/* vlan_prio is part of this 16 bit field even from OS perspective
+	 * vlan_id:12 is actual vlan_id, then vlanid:bit14..12 is vlan_prio
+	 * in future, when decided to offload vlan_prio, pass that information
+	 * as part of the "vlan_id" field, Bit14..12
+	 */
 	__be16	vlan_id;
 	__be16	pad; /* reserved for future use */
 	__be32	src_ip[4];
@@ -956,20 +1341,40 @@ enum virtchnl_flow_type {
 	/* flow types */
 	VIRTCHNL_TCP_V4_FLOW = 0,
 	VIRTCHNL_TCP_V6_FLOW,
+	VIRTCHNL_UDP_V4_FLOW,
+	VIRTCHNL_UDP_V6_FLOW,
 };
 
 struct virtchnl_filter {
-	union	virtchnl_flow_spec data;
-	union	virtchnl_flow_spec mask;
-	enum	virtchnl_flow_type flow_type;
-	enum	virtchnl_action action;
-	u32	action_meta;
-	u8	field_flags;
-	u8	pad[3];
+	union virtchnl_flow_spec data;
+	union virtchnl_flow_spec mask;
+
+	/* see enum virtchnl_flow_type */
+	s32 flow_type;
+
+	/* see enum virtchnl_action */
+	s32 action;
+	u32 action_meta;
+	u8 field_flags;
 };
 
 VIRTCHNL_CHECK_STRUCT_LEN(272, virtchnl_filter);
 
+struct virtchnl_shaper_bw {
+	/* Unit is Kbps */
+	u32 committed;
+	u32 peak;
+};
+
+VIRTCHNL_CHECK_STRUCT_LEN(8, virtchnl_shaper_bw);
+
+struct virtchnl_supported_rxdids {
+	/* see enum virtchnl_rx_desc_id_bitmasks */
+	u64 supported_rxdids;
+};
+
+VIRTCHNL_CHECK_STRUCT_LEN(8, virtchnl_supported_rxdids);
+
 /* VIRTCHNL_OP_EVENT
  * PF sends this message to inform the VF driver of events that may affect it.
  * No direct response is expected from the VF, though it may generate other
@@ -986,7 +1391,8 @@ enum virtchnl_event_codes {
 #define PF_EVENT_SEVERITY_CERTAIN_DOOM	255
 
 struct virtchnl_pf_event {
-	enum virtchnl_event_codes event;
+	/* see enum virtchnl_event_codes */
+	s32 event;
 	union {
 		/* If the PF driver does not support the new speed reporting
 		 * capabilities then use link_event else use link_event_adv to
@@ -999,6 +1405,7 @@ struct virtchnl_pf_event {
 		struct {
 			enum virtchnl_link_speed link_speed;
 			bool link_status;
+			u8 pad[3];
 		} link_event;
 		struct {
 			/* link_speed provided in Mbps */
@@ -1008,7 +1415,7 @@ struct virtchnl_pf_event {
 		} link_event_adv;
 	} event_data;
 
-	int severity;
+	s32 severity;
 };
 
 VIRTCHNL_CHECK_STRUCT_LEN(16, virtchnl_pf_event);
@@ -1059,17 +1466,10 @@ enum virtchnl_vfr_states {
 	VIRTCHNL_VFR_VFACTIVE,
 };
 
-/* Type of RSS algorithm */
-enum virtchnl_rss_algorithm {
-	VIRTCHNL_RSS_ALG_TOEPLITZ_ASYMMETRIC	= 0,
-	VIRTCHNL_RSS_ALG_R_ASYMMETRIC		= 1,
-	VIRTCHNL_RSS_ALG_TOEPLITZ_SYMMETRIC	= 2,
-	VIRTCHNL_RSS_ALG_XOR_SYMMETRIC		= 3,
-};
-
 #define VIRTCHNL_MAX_NUM_PROTO_HDRS	32
 #define PROTO_HDR_SHIFT			5
-#define PROTO_HDR_FIELD_START(proto_hdr_type) ((proto_hdr_type) << PROTO_HDR_SHIFT)
+#define PROTO_HDR_FIELD_START(proto_hdr_type) \
+					((proto_hdr_type) << PROTO_HDR_SHIFT)
 #define PROTO_HDR_FIELD_MASK ((1UL << PROTO_HDR_SHIFT) - 1)
 
 /* VF use these macros to configure each protocol header.
@@ -1099,10 +1499,10 @@ enum virtchnl_rss_algorithm {
 #define VIRTCHNL_GET_PROTO_HDR_TYPE(hdr) \
 	(((hdr)->type) >> PROTO_HDR_SHIFT)
 #define VIRTCHNL_TEST_PROTO_HDR_TYPE(hdr, val) \
-	((hdr)->type == ((val) >> PROTO_HDR_SHIFT))
+	((hdr)->type == ((s32)((val) >> PROTO_HDR_SHIFT)))
 #define VIRTCHNL_TEST_PROTO_HDR(hdr, val) \
-	(VIRTCHNL_TEST_PROTO_HDR_TYPE((hdr), (val)) && \
-	 VIRTCHNL_TEST_PROTO_HDR_FIELD((hdr), (val)))
+	(VIRTCHNL_TEST_PROTO_HDR_TYPE(hdr, val) && \
+	 VIRTCHNL_TEST_PROTO_HDR_FIELD(hdr, val))
 
 /* Protocol header type within a packet segment. A segment consists of one or
  * more protocol headers that make up a logical group of protocol headers. Each
@@ -1128,6 +1528,17 @@ enum virtchnl_proto_hdr_type {
 	VIRTCHNL_PROTO_HDR_ESP,
 	VIRTCHNL_PROTO_HDR_AH,
 	VIRTCHNL_PROTO_HDR_PFCP,
+	VIRTCHNL_PROTO_HDR_GTPC,
+	VIRTCHNL_PROTO_HDR_ECPRI,
+	VIRTCHNL_PROTO_HDR_L2TPV2,
+	VIRTCHNL_PROTO_HDR_PPP,
+	/* IPv4 and IPv6 Fragment header types are only associated to
+	 * VIRTCHNL_PROTO_HDR_IPV4 and VIRTCHNL_PROTO_HDR_IPV6 respectively,
+	 * cannot be used independently.
+	 */
+	VIRTCHNL_PROTO_HDR_IPV4_FRAG,
+	VIRTCHNL_PROTO_HDR_IPV6_EH_FRAG,
+	VIRTCHNL_PROTO_HDR_GRE,
 };
 
 /* Protocol header field within a protocol header. */
@@ -1150,6 +1561,7 @@ enum virtchnl_proto_hdr_field {
 	VIRTCHNL_PROTO_HDR_IPV4_DSCP,
 	VIRTCHNL_PROTO_HDR_IPV4_TTL,
 	VIRTCHNL_PROTO_HDR_IPV4_PROT,
+	VIRTCHNL_PROTO_HDR_IPV4_CHKSUM,
 	/* IPV6 */
 	VIRTCHNL_PROTO_HDR_IPV6_SRC =
 		PROTO_HDR_FIELD_START(VIRTCHNL_PROTO_HDR_IPV6),
@@ -1157,18 +1569,34 @@ enum virtchnl_proto_hdr_field {
 	VIRTCHNL_PROTO_HDR_IPV6_TC,
 	VIRTCHNL_PROTO_HDR_IPV6_HOP_LIMIT,
 	VIRTCHNL_PROTO_HDR_IPV6_PROT,
+	/* IPV6 Prefix */
+	VIRTCHNL_PROTO_HDR_IPV6_PREFIX32_SRC,
+	VIRTCHNL_PROTO_HDR_IPV6_PREFIX32_DST,
+	VIRTCHNL_PROTO_HDR_IPV6_PREFIX40_SRC,
+	VIRTCHNL_PROTO_HDR_IPV6_PREFIX40_DST,
+	VIRTCHNL_PROTO_HDR_IPV6_PREFIX48_SRC,
+	VIRTCHNL_PROTO_HDR_IPV6_PREFIX48_DST,
+	VIRTCHNL_PROTO_HDR_IPV6_PREFIX56_SRC,
+	VIRTCHNL_PROTO_HDR_IPV6_PREFIX56_DST,
+	VIRTCHNL_PROTO_HDR_IPV6_PREFIX64_SRC,
+	VIRTCHNL_PROTO_HDR_IPV6_PREFIX64_DST,
+	VIRTCHNL_PROTO_HDR_IPV6_PREFIX96_SRC,
+	VIRTCHNL_PROTO_HDR_IPV6_PREFIX96_DST,
 	/* TCP */
 	VIRTCHNL_PROTO_HDR_TCP_SRC_PORT =
 		PROTO_HDR_FIELD_START(VIRTCHNL_PROTO_HDR_TCP),
 	VIRTCHNL_PROTO_HDR_TCP_DST_PORT,
+	VIRTCHNL_PROTO_HDR_TCP_CHKSUM,
 	/* UDP */
 	VIRTCHNL_PROTO_HDR_UDP_SRC_PORT =
 		PROTO_HDR_FIELD_START(VIRTCHNL_PROTO_HDR_UDP),
 	VIRTCHNL_PROTO_HDR_UDP_DST_PORT,
+	VIRTCHNL_PROTO_HDR_UDP_CHKSUM,
 	/* SCTP */
 	VIRTCHNL_PROTO_HDR_SCTP_SRC_PORT =
 		PROTO_HDR_FIELD_START(VIRTCHNL_PROTO_HDR_SCTP),
 	VIRTCHNL_PROTO_HDR_SCTP_DST_PORT,
+	VIRTCHNL_PROTO_HDR_SCTP_CHKSUM,
 	/* GTPU_IP */
 	VIRTCHNL_PROTO_HDR_GTPU_IP_TEID =
 		PROTO_HDR_FIELD_START(VIRTCHNL_PROTO_HDR_GTPU_IP),
@@ -1192,10 +1620,29 @@ enum virtchnl_proto_hdr_field {
 	VIRTCHNL_PROTO_HDR_PFCP_S_FIELD =
 		PROTO_HDR_FIELD_START(VIRTCHNL_PROTO_HDR_PFCP),
 	VIRTCHNL_PROTO_HDR_PFCP_SEID,
+	/* GTPC */
+	VIRTCHNL_PROTO_HDR_GTPC_TEID =
+		PROTO_HDR_FIELD_START(VIRTCHNL_PROTO_HDR_GTPC),
+	/* ECPRI */
+	VIRTCHNL_PROTO_HDR_ECPRI_MSG_TYPE =
+		PROTO_HDR_FIELD_START(VIRTCHNL_PROTO_HDR_ECPRI),
+	VIRTCHNL_PROTO_HDR_ECPRI_PC_RTC_ID,
+	/* IPv4 Dummy Fragment */
+	VIRTCHNL_PROTO_HDR_IPV4_FRAG_PKID =
+		PROTO_HDR_FIELD_START(VIRTCHNL_PROTO_HDR_IPV4_FRAG),
+	/* IPv6 Extension Fragment */
+	VIRTCHNL_PROTO_HDR_IPV6_EH_FRAG_PKID =
+		PROTO_HDR_FIELD_START(VIRTCHNL_PROTO_HDR_IPV6_EH_FRAG),
+	/* GTPU_DWN/UP */
+	VIRTCHNL_PROTO_HDR_GTPU_DWN_QFI =
+		PROTO_HDR_FIELD_START(VIRTCHNL_PROTO_HDR_GTPU_EH_PDU_DWN),
+	VIRTCHNL_PROTO_HDR_GTPU_UP_QFI =
+		PROTO_HDR_FIELD_START(VIRTCHNL_PROTO_HDR_GTPU_EH_PDU_UP),
 };
 
 struct virtchnl_proto_hdr {
-	enum virtchnl_proto_hdr_type type;
+	/* see enum virtchnl_proto_hdr_type */
+	s32 type;
 	u32 field_selector; /* a bit mask to select field for header type */
 	u8 buffer[64];
 	/**
@@ -1209,7 +1656,6 @@ VIRTCHNL_CHECK_STRUCT_LEN(72, virtchnl_proto_hdr);
 
 struct virtchnl_proto_hdrs {
 	u8 tunnel_level;
-	u8 pad[3];
 	/**
 	 * specify where protocol header start from.
 	 * 0 - from the outer layer
@@ -1225,15 +1671,18 @@ VIRTCHNL_CHECK_STRUCT_LEN(2312, virtchnl_proto_hdrs);
 
 struct virtchnl_rss_cfg {
 	struct virtchnl_proto_hdrs proto_hdrs;	   /* protocol headers */
-	enum virtchnl_rss_algorithm rss_algorithm; /* RSS algorithm type */
-	u8 reserved[128];			   /* reserve for future */
+
+	/* see enum virtchnl_rss_algorithm; rss algorithm type */
+	s32 rss_algorithm;
+	u8 reserved[128];                          /* reserve for future */
 };
 
 VIRTCHNL_CHECK_STRUCT_LEN(2444, virtchnl_rss_cfg);
 
 /* action configuration for FDIR */
 struct virtchnl_filter_action {
-	enum virtchnl_action type;
+	/* see enum virtchnl_action type */
+	s32 type;
 	union {
 		/* used for queue and qgroup action */
 		struct {
@@ -1275,7 +1724,7 @@ VIRTCHNL_CHECK_STRUCT_LEN(2604, virtchnl_fdir_rule);
 /* Status returned to VF after VF requests FDIR commands
  * VIRTCHNL_FDIR_SUCCESS
  * VF FDIR related request is successfully done by PF
- * The request can be OP_ADD/DEL.
+ * The request can be OP_ADD/DEL/QUERY_FDIR_FILTER.
  *
  * VIRTCHNL_FDIR_FAILURE_RULE_NORESOURCE
  * OP_ADD_FDIR_FILTER request is failed due to no Hardware resource.
@@ -1296,6 +1745,10 @@ VIRTCHNL_CHECK_STRUCT_LEN(2604, virtchnl_fdir_rule);
  * VIRTCHNL_FDIR_FAILURE_RULE_TIMEOUT
  * OP_ADD/DEL_FDIR_FILTER request is failed due to timing out
  * for programming.
+ *
+ * VIRTCHNL_FDIR_FAILURE_QUERY_INVALID
+ * OP_QUERY_FDIR_FILTER request is failed due to parameters validation,
+ * for example, VF query counter of a rule who has no counter action.
  */
 enum virtchnl_fdir_prgm_status {
 	VIRTCHNL_FDIR_SUCCESS = 0,
@@ -1305,6 +1758,7 @@ enum virtchnl_fdir_prgm_status {
 	VIRTCHNL_FDIR_FAILURE_RULE_NONEXIST,
 	VIRTCHNL_FDIR_FAILURE_RULE_INVALID,
 	VIRTCHNL_FDIR_FAILURE_RULE_TIMEOUT,
+	VIRTCHNL_FDIR_FAILURE_QUERY_INVALID,
 };
 
 /* VIRTCHNL_OP_ADD_FDIR_FILTER
@@ -1321,7 +1775,9 @@ struct virtchnl_fdir_add {
 	u16 validate_only; /* INPUT */
 	u32 flow_id;       /* OUTPUT */
 	struct virtchnl_fdir_rule rule_cfg; /* INPUT */
-	enum virtchnl_fdir_prgm_status status; /* OUTPUT */
+
+	/* see enum virtchnl_fdir_prgm_status; OUTPUT */
+	s32 status;
 };
 
 VIRTCHNL_CHECK_STRUCT_LEN(2616, virtchnl_fdir_add);
@@ -1334,11 +1790,743 @@ struct virtchnl_fdir_del {
 	u16 vsi_id;  /* INPUT */
 	u16 pad;
 	u32 flow_id; /* INPUT */
-	enum virtchnl_fdir_prgm_status status; /* OUTPUT */
+
+	/* see enum virtchnl_fdir_prgm_status; OUTPUT */
+	s32 status;
 };
 
 VIRTCHNL_CHECK_STRUCT_LEN(12, virtchnl_fdir_del);
 
+/* VIRTCHNL_OP_GET_QOS_CAPS
+ * VF sends this message to get its QoS Caps, such as
+ * TC number, Arbiter and Bandwidth.
+ */
+struct virtchnl_qos_cap_elem {
+	u8 tc_num;
+	u8 tc_prio;
+#define VIRTCHNL_ABITER_STRICT      0
+#define VIRTCHNL_ABITER_ETS         2
+	u8 arbiter;
+#define VIRTCHNL_STRICT_WEIGHT      1
+	u8 weight;
+	enum virtchnl_bw_limit_type type;
+	union {
+		struct virtchnl_shaper_bw shaper;
+		u8 pad2[32];
+	};
+};
+
+VIRTCHNL_CHECK_STRUCT_LEN(40, virtchnl_qos_cap_elem);
+
+struct virtchnl_qos_cap_list {
+	u16 vsi_id;
+	u16 num_elem;
+	struct virtchnl_qos_cap_elem cap[1];
+};
+
+VIRTCHNL_CHECK_STRUCT_LEN(44, virtchnl_qos_cap_list);
+
+/* VIRTCHNL_OP_CONFIG_QUEUE_TC_MAP
+ * VF sends message virtchnl_queue_tc_mapping to set queue to tc
+ * mapping for all the Tx and Rx queues with a specified VSI, and
+ * would get response about bitmap of valid user priorities
+ * associated with queues.
+ */
+struct virtchnl_queue_tc_mapping {
+	u16 vsi_id;
+	u16 num_tc;
+	u16 num_queue_pairs;
+	u8 pad[2];
+	union {
+		struct {
+			u16 start_queue_id;
+			u16 queue_count;
+		} req;
+		struct {
+#define VIRTCHNL_USER_PRIO_TYPE_UP	0
+#define VIRTCHNL_USER_PRIO_TYPE_DSCP	1
+			u16 prio_type;
+			u16 valid_prio_bitmap;
+		} resp;
+	} tc[1];
+};
+
+VIRTCHNL_CHECK_STRUCT_LEN(12, virtchnl_queue_tc_mapping);
+
+/* queue types */
+enum virtchnl_queue_type {
+	VIRTCHNL_QUEUE_TYPE_TX			= 0,
+	VIRTCHNL_QUEUE_TYPE_RX			= 1,
+};
+
+/* structure to specify a chunk of contiguous queues */
+struct virtchnl_queue_chunk {
+	/* see enum virtchnl_queue_type */
+	s32 type;
+	u16 start_queue_id;
+	u16 num_queues;
+};
+
+VIRTCHNL_CHECK_STRUCT_LEN(8, virtchnl_queue_chunk);
+
+/* structure to specify several chunks of contiguous queues */
+struct virtchnl_queue_chunks {
+	u16 num_chunks;
+	u16 rsvd;
+	struct virtchnl_queue_chunk chunks[1];
+};
+
+VIRTCHNL_CHECK_STRUCT_LEN(12, virtchnl_queue_chunks);
+
+/* VIRTCHNL_OP_ENABLE_QUEUES_V2
+ * VIRTCHNL_OP_DISABLE_QUEUES_V2
+ *
+ * These opcodes can be used if VIRTCHNL_VF_LARGE_NUM_QPAIRS was negotiated in
+ * VIRTCHNL_OP_GET_VF_RESOURCES
+ *
+ * VF sends virtchnl_ena_dis_queues struct to specify the queues to be
+ * enabled/disabled in chunks. Also applicable to single queue RX or
+ * TX. PF performs requested action and returns status.
+ */
+struct virtchnl_del_ena_dis_queues {
+	u16 vport_id;
+	u16 pad;
+	struct virtchnl_queue_chunks chunks;
+};
+
+VIRTCHNL_CHECK_STRUCT_LEN(16, virtchnl_del_ena_dis_queues);
+
+/* Virtchannel interrupt throttling rate index */
+enum virtchnl_itr_idx {
+	VIRTCHNL_ITR_IDX_0	= 0,
+	VIRTCHNL_ITR_IDX_1	= 1,
+	VIRTCHNL_ITR_IDX_NO_ITR	= 3,
+};
+
+/* Queue to vector mapping */
+struct virtchnl_queue_vector {
+	u16 queue_id;
+	u16 vector_id;
+	u8 pad[4];
+
+	/* see enum virtchnl_itr_idx */
+	s32 itr_idx;
+
+	/* see enum virtchnl_queue_type */
+	s32 queue_type;
+};
+
+VIRTCHNL_CHECK_STRUCT_LEN(16, virtchnl_queue_vector);
+
+/* VIRTCHNL_OP_MAP_QUEUE_VECTOR
+ *
+ * This opcode can be used only if VIRTCHNL_VF_LARGE_NUM_QPAIRS was negotiated
+ * in VIRTCHNL_OP_GET_VF_RESOURCES
+ *
+ * VF sends this message to map queues to vectors and ITR index registers.
+ * External data buffer contains virtchnl_queue_vector_maps structure
+ * that contains num_qv_maps of virtchnl_queue_vector structures.
+ * PF maps the requested queue vector maps after validating the queue and vector
+ * ids and returns a status code.
+ */
+struct virtchnl_queue_vector_maps {
+	u16 vport_id;
+	u16 num_qv_maps;
+	u8 pad[4];
+	struct virtchnl_queue_vector qv_maps[1];
+};
+
+VIRTCHNL_CHECK_STRUCT_LEN(24, virtchnl_queue_vector_maps);
+
+/* VIRTCHNL_VF_CAP_PTP
+ *   VIRTCHNL_OP_1588_PTP_GET_CAPS
+ *   VIRTCHNL_OP_1588_PTP_GET_TIME
+ *   VIRTCHNL_OP_1588_PTP_SET_TIME
+ *   VIRTCHNL_OP_1588_PTP_ADJ_TIME
+ *   VIRTCHNL_OP_1588_PTP_ADJ_FREQ
+ *   VIRTCHNL_OP_1588_PTP_TX_TIMESTAMP
+ *   VIRTCHNL_OP_1588_PTP_GET_PIN_CFGS
+ *   VIRTCHNL_OP_1588_PTP_SET_PIN_CFG
+ *   VIRTCHNL_OP_1588_PTP_EXT_TIMESTAMP
+ *
+ * Support for offloading control of the device PTP hardware clock (PHC) is enabled
+ * by VIRTCHNL_VF_CAP_PTP. This capability allows a VF to request that PF
+ * enable Tx and Rx timestamps, and request access to read and/or write the
+ * PHC on the device, as well as query if the VF has direct access to the PHC
+ * time registers.
+ *
+ * The VF must set VIRTCHNL_VF_CAP_PTP in its capabilities when requesting
+ * resources. If the capability is set in reply, the VF must then send
+ * a VIRTCHNL_OP_1588_PTP_GET_CAPS request during initialization. The VF indicates
+ * what extended capabilities it wants by setting the appropriate flags in the
+ * caps field. The PF reply will indicate what features are enabled for
+ * that VF.
+ */
+#define VIRTCHNL_1588_PTP_CAP_TX_TSTAMP		BIT(0)
+#define VIRTCHNL_1588_PTP_CAP_RX_TSTAMP		BIT(1)
+#define VIRTCHNL_1588_PTP_CAP_READ_PHC		BIT(2)
+#define VIRTCHNL_1588_PTP_CAP_WRITE_PHC		BIT(3)
+#define VIRTCHNL_1588_PTP_CAP_PHC_REGS		BIT(4)
+#define VIRTCHNL_1588_PTP_CAP_PIN_CFG		BIT(5)
+
+/**
+ * virtchnl_phc_regs
+ *
+ * Structure defines how the VF should access PHC related registers. The VF
+ * must request VIRTCHNL_1588_PTP_CAP_PHC_REGS. If the VF has access to PHC
+ * registers, the PF will reply with the capability flag set, and with this
+ * structure detailing what PCIe region and what offsets to use. If direct
+ * access is not available, this entire structure is reserved and the fields
+ * will be zero.
+ *
+ * If necessary in a future extension, a separate capability mutually
+ * exclusive with VIRTCHNL_1588_PTP_CAP_PHC_REGS might be used to change the
+ * entire format of this structure within virtchnl_ptp_caps.
+ *
+ * @clock_hi: Register offset of the high 32 bits of clock time
+ * @clock_lo: Register offset of the low 32 bits of clock time
+ * @pcie_region: The PCIe region the registers are located in.
+ * @rsvd: Reserved bits for future extension
+ */
+struct virtchnl_phc_regs {
+	u32 clock_hi;
+	u32 clock_lo;
+	u8 pcie_region;
+	u8 rsvd[15];
+};
+
+VIRTCHNL_CHECK_STRUCT_LEN(24, virtchnl_phc_regs);
+
+/* timestamp format enumeration
+ *
+ * VIRTCHNL_1588_PTP_TSTAMP_40BIT
+ *
+ *   This format indicates a timestamp that uses the 40bit format from the
+ *   flexible Rx descriptors. It is also the default Tx timestamp format used
+ *   today.
+ *
+ *   Such a timestamp has the following 40bit format:
+ *
+ *   *--------------------------------*-------------------------------*-----------*
+ *   | 32 bits of time in nanoseconds | 7 bits of sub-nanosecond time | valid bit |
+ *   *--------------------------------*-------------------------------*-----------*
+ *
+ *   The timestamp is passed in a u64, with the upper 24bits of the field
+ *   reserved as zero.
+ *
+ *   With this format, in order to report a full 64bit timestamp to userspace
+ *   applications, the VF is responsible for performing timestamp extension by
+ *   carefully comparing the timestamp with the PHC time. This can correctly
+ *   be achieved with a recent cached copy of the PHC time by doing delta
+ *   comparison between the 32bits of nanoseconds in the timestamp with the
+ *   lower 32 bits of the clock time. For this to work, the cached PHC time
+ *   must be from within 2^31 nanoseconds (~2.1 seconds) of when the timestamp
+ *   was captured.
+ *
+ * VIRTCHNL_1588_PTP_TSTAMP_64BIT_NS
+ *
+ *   This format indicates a timestamp that is 64 bits of nanoseconds.
+ */
+enum virtchnl_ptp_tstamp_format {
+	VIRTCHNL_1588_PTP_TSTAMP_40BIT = 0,
+	VIRTCHNL_1588_PTP_TSTAMP_64BIT_NS = 1,
+};
+
+/**
+ * virtchnl_ptp_caps
+ *
+ * Structure that defines the PTP capabilities available to the VF. The VF
+ * sends VIRTCHNL_OP_1588_PTP_GET_CAPS, and must fill in the ptp_caps field
+ * indicating what capabilities it is requesting. The PF will respond with the
+ * same message with the virtchnl_ptp_caps structure indicating what is
+ * enabled for the VF.
+ *
+ * @phc_regs: If VIRTCHNL_1588_PTP_CAP_PHC_REGS is set, contains information
+ *            on the PHC related registers available to the VF.
+ * @caps: On send, VF sets what capabilities it requests. On reply, PF
+ *        indicates what has been enabled for this VF. The PF shall not set
+ *        bits which were not requested by the VF.
+ * @max_adj: The maximum adjustment capable of being requested by
+ *           VIRTCHNL_OP_1588_PTP_ADJ_FREQ, in parts per billion. Note that 1 ppb
+ *           is approximately 65.5 scaled_ppm. The PF shall clamp any
+ *           frequency adjustment in VIRTCHNL_op_1588_ADJ_FREQ to +/- max_adj.
+ *           Use of ppb in this field allows fitting the value into 4 bytes
+ *           instead of potentially requiring 8 if scaled_ppm units were used.
+ * @tx_tstamp_idx: The Tx timestamp index to set in the transmit descriptor
+ *                 when requesting a timestamp for an outgoing packet.
+ *                 Reserved if VIRTCHNL_1588_PTP_CAP_TX_TSTAMP is not enabled.
+ * @n_ext_ts: Number of external timestamp functions available. Reserved
+ *            if VIRTCHNL_1588_PTP_CAP_PIN_CFG is not enabled.
+ * @n_per_out: Number of periodic output functions available. Reserved if
+ *             VIRTCHNL_1588_PTP_CAP_PIN_CFG is not enabled.
+ * @n_pins: Number of physical programmable pins able to be controlled.
+ *          Reserved if VIRTCHNL_1588_PTP_CAP_PIN_CFG is not enabled.
+ * @tx_tstamp_format: Format of the Tx timestamps. Valid formats are defined
+ *                    by the virtchnl_ptp_tstamp enumeration. Note that Rx
+ *                    timestamps are tied to the descriptor format, and do not
+ *                    have a separate format field.
+ * @rsvd: Reserved bits for future extension.
+ *
+ * PTP capabilities
+ *
+ * VIRTCHNL_1588_PTP_CAP_TX_TSTAMP indicates that the VF can request transmit
+ * timestamps for packets in its transmit descriptors. If this is unset,
+ * transmit timestamp requests are ignored. Note that only one outstanding Tx
+ * timestamp request will be honored at a time. The PF shall handle receipt of
+ * the timestamp from the hardware, and will forward this to the VF by sending
+ * a VIRTCHNL_OP_1588_TX_TIMESTAMP message.
+ *
+ * VIRTCHNL_1588_PTP_CAP_RX_TSTAMP indicates that the VF receive queues have
+ * receive timestamps enabled in the flexible descriptors. Note that this
+ * requires a VF to also negotiate to enable advanced flexible descriptors in
+ * the receive path instead of the default legacy descriptor format.
+ *
+ * For a detailed description of the current Tx and Rx timestamp format, see
+ * the section on virtchnl_phc_tx_tstamp. Future extensions may indicate
+ * timestamp format in the capability structure.
+ *
+ * VIRTCHNL_1588_PTP_CAP_READ_PHC indicates that the VF may read the PHC time
+ * via the VIRTCHNL_OP_1588_PTP_GET_TIME command, or by directly reading PHC
+ * registers if VIRTCHNL_1588_PTP_CAP_PHC_REGS is also set.
+ *
+ * VIRTCHNL_1588_PTP_CAP_WRITE_PHC indicates that the VF may request updates
+ * to the PHC time via VIRTCHNL_OP_1588_PTP_SET_TIME,
+ * VIRTCHNL_OP_1588_PTP_ADJ_TIME, and VIRTCHNL_OP_1588_PTP_ADJ_FREQ.
+ *
+ * VIRTCHNL_1588_PTP_CAP_PHC_REGS indicates that the VF has direct access to
+ * certain PHC related registers, primarily for lower latency access to the
+ * PHC time. If this is set, the VF shall read the virtchnl_phc_regs section
+ * of the capabilities to determine the location of the clock registers. If
+ * this capability is not set, the entire 24 bytes of virtchnl_phc_regs is
+ * reserved as zero. Future extensions define alternative formats for this
+ * data, in which case they will be mutually exclusive with this capability.
+ *
+ * VIRTCHNL_1588_PTP_CAP_PIN_CFG indicates that the VF has the capability to
+ * control software defined pins. These pins can be assigned either as an
+ * input to timestamp external events, or as an output to cause a periodic
+ * signal output.
+ *
+ * Note that in the future, additional capability flags may be added which
+ * indicate additional extended support. All fields marked as reserved by this
+ * header will be set to zero. VF implementations should verify this to ensure
+ * that future extensions do not break compatibility.
+ */
+struct virtchnl_ptp_caps {
+	struct virtchnl_phc_regs phc_regs;
+	u32 caps;
+	s32 max_adj;
+	u8 tx_tstamp_idx;
+	u8 n_ext_ts;
+	u8 n_per_out;
+	u8 n_pins;
+	/* see enum virtchnl_ptp_tstamp_format */
+	u8 tx_tstamp_format;
+	u8 rsvd[11];
+};
+
+VIRTCHNL_CHECK_STRUCT_LEN(48, virtchnl_ptp_caps);
+
+/**
+ * virtchnl_phc_time
+ * @time: PHC time in nanoseconds
+ * @rsvd: Reserved for future extension
+ *
+ * Structure sent with VIRTCHNL_OP_1588_PTP_SET_TIME and received with
+ * VIRTCHNL_OP_1588_PTP_GET_TIME. Contains the 64bits of PHC clock time in
+ * nanoseconds.
+ *
+ * VIRTCHNL_OP_1588_PTP_SET_TIME may be sent by the VF if
+ * VIRTCHNL_1588_PTP_CAP_WRITE_PHC is set. This will request that the PHC time
+ * be set to the requested value. This operation is non-atomic and thus does
+ * not adjust for the delay between request and completion. It is recommended
+ * that the VF use VIRTCHNL_OP_1588_PTP_ADJ_TIME and
+ * VIRTCHNL_OP_1588_PTP_ADJ_FREQ when possible to steer the PHC clock.
+ *
+ * VIRTCHNL_OP_1588_PTP_GET_TIME may be sent to request the current time of
+ * the PHC. This op is available in case direct access via the PHC registers
+ * is not available.
+ */
+struct virtchnl_phc_time {
+	u64 time;
+	u8 rsvd[8];
+};
+
+VIRTCHNL_CHECK_STRUCT_LEN(16, virtchnl_phc_time);
+
+/**
+ * virtchnl_phc_adj_time
+ * @delta: offset requested to adjust clock by
+ * @rsvd: reserved for future extension
+ *
+ * Sent with VIRTCHNL_OP_1588_PTP_ADJ_TIME. Used to request an adjustment of
+ * the clock time by the provided delta, with negative values representing
+ * subtraction. VIRTCHNL_OP_1588_PTP_ADJ_TIME may not be sent unless
+ * VIRTCHNL_1588_PTP_CAP_WRITE_PHC is set.
+ *
+ * The atomicity of this operation is not guaranteed. The PF should perform an
+ * atomic update using appropriate mechanisms if possible. However, this is
+ * not guaranteed.
+ */
+struct virtchnl_phc_adj_time {
+	s64 delta;
+	u8 rsvd[8];
+};
+
+VIRTCHNL_CHECK_STRUCT_LEN(16, virtchnl_phc_adj_time);
+
+/**
+ * virtchnl_phc_adj_freq
+ * @scaled_ppm: frequency adjustment represented in scaled parts per million
+ * @rsvd: Reserved for future extension
+ *
+ * Sent with the VIRTCHNL_OP_1588_PTP_ADJ_FREQ to request an adjustment to the
+ * clock frequency. The adjustment is in scaled_ppm, which is parts per
+ * million with a 16bit binary fractional portion. 1 part per billion is
+ * approximately 65.5 scaled_ppm.
+ *
+ *  ppm = scaled_ppm / 2^16
+ *
+ *  ppb = scaled_ppm * 1000 / 2^16 or
+ *
+ *  ppb = scaled_ppm * 125 / 2^13
+ *
+ * The PF shall clamp any adjustment request to plus or minus the specified
+ * max_adj in the PTP capabilities.
+ *
+ * Requests for adjustment are always based off of nominal clock frequency and
+ * not compounding. To reset clock frequency, send a request with a scaled_ppm
+ * of 0.
+ */
+struct virtchnl_phc_adj_freq {
+	s64 scaled_ppm;
+	u8 rsvd[8];
+};
+
+VIRTCHNL_CHECK_STRUCT_LEN(16, virtchnl_phc_adj_freq);
+
+/**
+ * virtchnl_phc_tx_stamp
+ * @tstamp: timestamp value
+ * @rsvd: Reserved for future extension
+ *
+ * Sent along with VIRTCHNL_OP_1588_PTP_TX_TIMESTAMP from the PF when a Tx
+ * timestamp for the index associated with this VF in the tx_tstamp_idx field
+ * is captured by hardware.
+ *
+ * If VIRTCHNL_1588_PTP_CAP_TX_TSTAMP is set, the VF may request a timestamp
+ * for a packet in its transmit context descriptor by setting the appropriate
+ * flag and setting the timestamp index provided by the PF. On transmission,
+ * the timestamp will be captured and sent to the PF. The PF will forward this
+ * timestamp to the VF via the VIRTCHNL_1588_PTP_CAP_TX_TSTAMP op.
+ *
+ * The timestamp format is defined by the tx_tstamp_format field of the
+ * virtchnl_ptp_caps structure.
+ */
+struct virtchnl_phc_tx_tstamp {
+	u64 tstamp;
+	u8 rsvd[8];
+};
+
+VIRTCHNL_CHECK_STRUCT_LEN(16, virtchnl_phc_tx_tstamp);
+
+enum virtchnl_phc_pin_func {
+	VIRTCHNL_PHC_PIN_FUNC_NONE = 0, /* Not assigned to any function */
+	VIRTCHNL_PHC_PIN_FUNC_EXT_TS = 1, /* Assigned to external timestamp */
+	VIRTCHNL_PHC_PIN_FUNC_PER_OUT = 2, /* Assigned to periodic output */
+};
+
+/* Length of the pin configuration data. All pin configurations belong within
+ * the same union and *must* have this length in bytes.
+ */
+#define VIRTCHNL_PIN_CFG_LEN 64
+
+/* virtchnl_phc_ext_ts_mode
+ *
+ * Mode of the external timestamp, indicating which edges of the input signal
+ * to timestamp.
+ */
+enum virtchnl_phc_ext_ts_mode {
+	VIRTCHNL_PHC_EXT_TS_NONE = 0,
+	VIRTCHNL_PHC_EXT_TS_RISING_EDGE = 1,
+	VIRTCHNL_PHC_EXT_TS_FALLING_EDGE = 2,
+	VIRTCHNL_PHC_EXT_TS_BOTH_EDGES = 3,
+};
+
+/**
+ * virtchnl_phc_ext_ts
+ * @mode: mode of external timestamp request
+ * @rsvd: reserved for future extension
+ *
+ * External timestamp configuration. Defines the configuration for this
+ * external timestamp function.
+ *
+ * If mode is VIRTCHNL_PHC_EXT_TS_NONE, the function is essentially disabled,
+ * timestamping nothing.
+ *
+ * If mode is VIRTCHNL_PHC_EXT_TS_RISING_EDGE, the function shall timestamp
+ * the rising edge of the input when it transitions from low to high signal.
+ *
+ * If mode is VIRTCHNL_PHC_EXT_TS_FALLING_EDGE, the function shall timestamp
+ * the falling edge of the input when it transitions from high to low signal.
+ *
+ * If mode is VIRTCHNL_PHC_EXT_TS_BOTH_EDGES, the function shall timestamp
+ * both the rising and falling edge of the signal whenever it changes.
+ *
+ * The PF shall return an error if the requested mode cannot be implemented on
+ * the function.
+ */
+struct virtchnl_phc_ext_ts {
+	u8 mode; /* see virtchnl_phc_ext_ts_mode */
+	u8 rsvd[63];
+};
+
+VIRTCHNL_CHECK_STRUCT_LEN(VIRTCHNL_PIN_CFG_LEN, virtchnl_phc_ext_ts);
+
+/* virtchnl_phc_per_out_flags
+ *
+ * Flags defining periodic output functionality.
+ */
+enum virtchnl_phc_per_out_flags {
+	VIRTCHNL_PHC_PER_OUT_PHASE_START = BIT(0),
+};
+
+/**
+ * virtchnl_phc_per_out
+ * @start: absolute start time (if VIRTCHNL_PHC_PER_OUT_PHASE_START unset)
+ * @phase: phase offset to start (if VIRTCHNL_PHC_PER_OUT_PHASE_START set)
+ * @period: time to complete a full clock cycle (low - > high -> low)
+ * @on: length of time the signal should stay high
+ * @flags: flags defining the periodic output operation.
+ * rsvd: reserved for future extension
+ *
+ * Configuration for a periodic output signal. Used to define the signal that
+ * should be generated on a given function.
+ *
+ * The period field determines the full length of the clock cycle, including
+ * both duration hold high transition and duration to hold low transition in
+ * nanoseconds.
+ *
+ * The on field determines how long the signal should remain high. For
+ * a traditional square wave clock that is on for some duration and off for
+ * the same duration, use an on length of precisely half the period. The duty
+ * cycle of the clock is period/on.
+ *
+ * If VIRTCHNL_PHC_PER_OUT_PHASE_START is unset, then the request is to start
+ * a clock an absolute time. This means that the clock should start precisely
+ * at the specified time in the start field. If the start time is in the past,
+ * then the periodic output should start at the next valid multiple of the
+ * period plus the start time:
+ *
+ *   new_start = (n * period) + start
+ *     (choose n such that new start is in the future)
+ *
+ * Note that the PF should not reject a start time in the past because it is
+ * possible that such a start time was valid when the request was made, but
+ * became invalid due to delay in programming the pin.
+ *
+ * If VIRTCHNL_PHC_PER_OUT_PHASE_START is set, then the request is to start
+ * the next multiple of the period plus the phase offset. The phase must be
+ * less than the period. In this case, the clock should start as soon possible
+ * at the next available multiple of the period. To calculate a start time
+ * when programming this mode, use:
+ *
+ *   start = (n * period) + phase
+ *     (choose n such that start is in the future)
+ *
+ * A period of zero should be treated as a request to disable the clock
+ * output.
+ */
+struct virtchnl_phc_per_out {
+	union {
+		u64 start;
+		u64 phase;
+	};
+	u64 period;
+	u64 on;
+	u32 flags;
+	u8 rsvd[36];
+};
+
+VIRTCHNL_CHECK_STRUCT_LEN(VIRTCHNL_PIN_CFG_LEN, virtchnl_phc_per_out);
+
+/* virtchnl_phc_pin_cfg_flags
+ *
+ * Definition of bits in the flags field of the virtchnl_phc_pin_cfg
+ * structure.
+ */
+enum virtchnl_phc_pin_cfg_flags {
+	/* Valid for VIRTCHNL_OP_1588_PTP_SET_PIN_CFG. If set, indicates this
+	 * is a request to verify if the function can be assigned to the
+	 * provided pin. In this case, the ext_ts and per_out fields are
+	 * ignored, and the PF response must be an error if the pin cannot be
+	 * assigned to that function index.
+	 */
+	VIRTCHNL_PHC_PIN_CFG_VERIFY = BIT(0),
+};
+
+/**
+ * virtchnl_phc_set_pin
+ * @pin_index: The pin to get or set
+ * @func: the function type the pin is assigned to
+ * @func_index: the index of the function the pin is assigned to
+ * @ext_ts: external timestamp configuration
+ * @per_out: periodic output configuration
+ * @rsvd1: Reserved for future extension
+ * @rsvd2: Reserved for future extension
+ *
+ * Sent along with the VIRTCHNL_OP_1588_PTP_SET_PIN_CFG op.
+ *
+ * The VF issues a VIRTCHNL_OP_1588_PTP_SET_PIN_CFG to assign the pin to one
+ * of the functions. It must set the pin_index field, the func field, and
+ * the func_index field. The pin_index must be less than n_pins, and the
+ * func_index must be less than the n_ext_ts or n_per_out depending on which
+ * function type is selected. If func is for an external timestamp, the
+ * ext_ts field must be filled in with the desired configuration. Similarly,
+ * if the function is for a periodic output, the per_out field must be
+ * configured.
+ *
+ * If the VIRTCHNL_PHC_PIN_CFG_VERIFY bit of the flag field is set, this is
+ * a request only to verify the configuration, not to set it. In this case,
+ * the PF should simply report an error if the requested pin cannot be
+ * assigned to the requested function. This allows VF to determine whether or
+ * not a given function can be assigned to a specific pin. Other flag bits are
+ * currently reserved and must be verified as zero on both sides. They may be
+ * extended in the future.
+ */
+struct virtchnl_phc_set_pin {
+	u32 flags; /* see virtchnl_phc_pin_cfg_flags */
+	u8 pin_index;
+	u8 func; /* see virtchnl_phc_pin_func */
+	u8 func_index;
+	u8 rsvd1;
+	union {
+		struct virtchnl_phc_ext_ts ext_ts;
+		struct virtchnl_phc_per_out per_out;
+	};
+	u8 rsvd2[8];
+};
+
+VIRTCHNL_CHECK_STRUCT_LEN(80, virtchnl_phc_set_pin);
+
+/**
+ * virtchnl_phc_pin
+ * @pin_index: The pin to get or set
+ * @func: the function type the pin is assigned to
+ * @func_index: the index of the function the pin is assigned to
+ * @rsvd: Reserved for future extension
+ * @name: human readable pin name, supplied by PF on GET_PIN_CFGS
+ *
+ * Sent by the PF as part of the VIRTCHNL_OP_1588_PTP_GET_PIN_CFGS response.
+ *
+ * The VF issues a VIRTCHNL_OP_1588_PTP_GET_PIN_CFGS request to the PF in
+ * order to obtain the current pin configuration for all of the pins that were
+ * assigned to this VF.
+ *
+ * This structure details the pin configuration state, including a pin name
+ * and which function is assigned to the pin currently.
+ */
+struct virtchnl_phc_pin {
+	u8 pin_index;
+	u8 func; /* see virtchnl_phc_pin_func */
+	u8 func_index;
+	u8 rsvd[5];
+	char name[64];
+};
+
+VIRTCHNL_CHECK_STRUCT_LEN(72, virtchnl_phc_pin);
+
+/**
+ * virtchnl_phc_pin_cfg
+ * @len: length of the variable pin config array
+ * @pins: variable length pin configuration array
+ *
+ * Variable structure sent by the PF in reply to
+ * VIRTCHNL_OP_1588_PTP_GET_PIN_CFGS. The VF does not send this structure with
+ * its request of the operation.
+ *
+ * It is possible that the PF may need to send more pin configuration data
+ * than can be sent in one virtchnl message. To handle this, the PF should
+ * issue multiple VIRTCHNL_OP_1588_PTP_GET_PIN_CFGS responses. Each response
+ * will indicate the number of pins it covers. The VF should be ready to wait
+ * for multiple responses until it has received a total length equal to the
+ * number of n_pins negotiated during extended PTP capabilities exchange.
+ */
+struct virtchnl_phc_get_pins {
+	u8 len;
+	u8 rsvd[7];
+	struct virtchnl_phc_pin pins[1];
+};
+
+VIRTCHNL_CHECK_STRUCT_LEN(80, virtchnl_phc_get_pins);
+
+/**
+ * virtchnl_phc_ext_stamp
+ * @tstamp: timestamp value
+ * @tstamp_rsvd: Reserved for future extension of the timestamp value.
+ * @tstamp_format: format of the timstamp
+ * @func_index: external timestamp function this timestamp is for
+ * @rsvd2: Reserved for future extension
+ *
+ * Sent along with the VIRTCHNL_OP_1588_PTP_EXT_TIMESTAMP from the PF when an
+ * external timestamp function is triggered.
+ *
+ * This will be sent only if one of the external timestamp functions is
+ * configured by the VF, and is only valid if VIRTCHNL_1588_PTP_CAP_PIN_CFG is
+ * negotiated with the PF.
+ *
+ * The timestamp format is defined by the tstamp_format field using the
+ * virtchnl_ptp_tstamp_format enumeration. The tstamp_rsvd field is
+ * exclusively reserved for possible future variants of the timestamp format,
+ * and its access will be controlled by the tstamp_format field.
+ */
+struct virtchnl_phc_ext_tstamp {
+	u64 tstamp;
+	u8 tstamp_rsvd[8];
+	u8 tstamp_format;
+	u8 func_index;
+	u8 rsvd2[6];
+};
+
+VIRTCHNL_CHECK_STRUCT_LEN(24, virtchnl_phc_ext_tstamp);
+
+/* Since VF messages are limited by u16 size, precalculate the maximum possible
+ * values of nested elements in virtchnl structures that virtual channel can
+ * possibly handle in a single message.
+ */
+enum virtchnl_vector_limits {
+	VIRTCHNL_OP_CONFIG_VSI_QUEUES_MAX	=
+		((u16)(~0) - sizeof(struct virtchnl_vsi_queue_config_info)) /
+		sizeof(struct virtchnl_queue_pair_info),
+
+	VIRTCHNL_OP_CONFIG_IRQ_MAP_MAX		=
+		((u16)(~0) - sizeof(struct virtchnl_irq_map_info)) /
+		sizeof(struct virtchnl_vector_map),
+
+	VIRTCHNL_OP_ADD_DEL_ETH_ADDR_MAX	=
+		((u16)(~0) - sizeof(struct virtchnl_ether_addr_list)) /
+		sizeof(struct virtchnl_ether_addr),
+
+	VIRTCHNL_OP_ADD_DEL_VLAN_MAX		=
+		((u16)(~0) - sizeof(struct virtchnl_vlan_filter_list)) /
+		sizeof(u16),
+
+	VIRTCHNL_OP_ENABLE_CHANNELS_MAX		=
+		((u16)(~0) - sizeof(struct virtchnl_tc_info)) /
+		sizeof(struct virtchnl_channel_info),
+
+	VIRTCHNL_OP_ENABLE_DISABLE_DEL_QUEUES_V2_MAX	=
+		((u16)(~0) - sizeof(struct virtchnl_del_ena_dis_queues)) /
+		sizeof(struct virtchnl_queue_chunk),
+
+	VIRTCHNL_OP_MAP_UNMAP_QUEUE_VECTOR_MAX	=
+		((u16)(~0) - sizeof(struct virtchnl_queue_vector_maps)) /
+		sizeof(struct virtchnl_queue_vector),
+
+	VIRTCHNL_OP_ADD_DEL_VLAN_V2_MAX		=
+		((u16)(~0) - sizeof(struct virtchnl_vlan_filter_list_v2)) /
+		sizeof(struct virtchnl_vlan_filter),
+};
+
 /**
  * virtchnl_vc_validate_vf_msg
  * @ver: Virtchnl version info
@@ -1353,7 +2541,7 @@ virtchnl_vc_validate_vf_msg(struct virtchnl_version_info *ver, u32 v_opcode,
 			    u8 *msg, u16 msglen)
 {
 	bool err_msg_format = false;
-	int valid_len = 0;
+	u32 valid_len = 0;
 
 	/* Validate message length. */
 	switch (v_opcode) {
@@ -1377,11 +2565,16 @@ virtchnl_vc_validate_vf_msg(struct virtchnl_version_info *ver, u32 v_opcode,
 		if (msglen >= valid_len) {
 			struct virtchnl_vsi_queue_config_info *vqc =
 			    (struct virtchnl_vsi_queue_config_info *)msg;
+
+			if (vqc->num_queue_pairs == 0 || vqc->num_queue_pairs >
+			    VIRTCHNL_OP_CONFIG_VSI_QUEUES_MAX) {
+				err_msg_format = true;
+				break;
+			}
+
 			valid_len += (vqc->num_queue_pairs *
 				      sizeof(struct
 					     virtchnl_queue_pair_info));
-			if (vqc->num_queue_pairs == 0)
-				err_msg_format = true;
 		}
 		break;
 	case VIRTCHNL_OP_CONFIG_IRQ_MAP:
@@ -1389,26 +2582,38 @@ virtchnl_vc_validate_vf_msg(struct virtchnl_version_info *ver, u32 v_opcode,
 		if (msglen >= valid_len) {
 			struct virtchnl_irq_map_info *vimi =
 			    (struct virtchnl_irq_map_info *)msg;
+
+			if (vimi->num_vectors == 0 || vimi->num_vectors >
+			    VIRTCHNL_OP_CONFIG_IRQ_MAP_MAX) {
+				err_msg_format = true;
+				break;
+			}
+
 			valid_len += (vimi->num_vectors *
 				      sizeof(struct virtchnl_vector_map));
-			if (vimi->num_vectors == 0)
-				err_msg_format = true;
 		}
 		break;
 	case VIRTCHNL_OP_ENABLE_QUEUES:
 	case VIRTCHNL_OP_DISABLE_QUEUES:
 		valid_len = sizeof(struct virtchnl_queue_select);
 		break;
+	case VIRTCHNL_OP_GET_MAX_RSS_QREGION:
+		break;
 	case VIRTCHNL_OP_ADD_ETH_ADDR:
 	case VIRTCHNL_OP_DEL_ETH_ADDR:
 		valid_len = sizeof(struct virtchnl_ether_addr_list);
 		if (msglen >= valid_len) {
 			struct virtchnl_ether_addr_list *veal =
 			    (struct virtchnl_ether_addr_list *)msg;
+
+			if (veal->num_elements == 0 || veal->num_elements >
+			    VIRTCHNL_OP_ADD_DEL_ETH_ADDR_MAX) {
+				err_msg_format = true;
+				break;
+			}
+
 			valid_len += veal->num_elements *
 			    sizeof(struct virtchnl_ether_addr);
-			if (veal->num_elements == 0)
-				err_msg_format = true;
 		}
 		break;
 	case VIRTCHNL_OP_ADD_VLAN:
@@ -1417,9 +2622,14 @@ virtchnl_vc_validate_vf_msg(struct virtchnl_version_info *ver, u32 v_opcode,
 		if (msglen >= valid_len) {
 			struct virtchnl_vlan_filter_list *vfl =
 			    (struct virtchnl_vlan_filter_list *)msg;
-			valid_len += vfl->num_elements * sizeof(u16);
-			if (vfl->num_elements == 0)
+
+			if (vfl->num_elements == 0 || vfl->num_elements >
+			    VIRTCHNL_OP_ADD_DEL_VLAN_MAX) {
 				err_msg_format = true;
+				break;
+			}
+
+			valid_len += vfl->num_elements * sizeof(u16);
 		}
 		break;
 	case VIRTCHNL_OP_CONFIG_PROMISCUOUS_MODE:
@@ -1428,36 +2638,17 @@ virtchnl_vc_validate_vf_msg(struct virtchnl_version_info *ver, u32 v_opcode,
 	case VIRTCHNL_OP_GET_STATS:
 		valid_len = sizeof(struct virtchnl_queue_select);
 		break;
-	case VIRTCHNL_OP_IWARP:
-		/* These messages are opaque to us and will be validated in
-		 * the RDMA client code. We just need to check for nonzero
-		 * length. The firmware will enforce max length restrictions.
-		 */
-		if (msglen)
-			valid_len = msglen;
-		else
-			err_msg_format = true;
-		break;
-	case VIRTCHNL_OP_RELEASE_IWARP_IRQ_MAP:
-		break;
-	case VIRTCHNL_OP_CONFIG_IWARP_IRQ_MAP:
-		valid_len = sizeof(struct virtchnl_iwarp_qvlist_info);
-		if (msglen >= valid_len) {
-			struct virtchnl_iwarp_qvlist_info *qv =
-				(struct virtchnl_iwarp_qvlist_info *)msg;
-			if (qv->num_vectors == 0) {
-				err_msg_format = true;
-				break;
-			}
-			valid_len += ((qv->num_vectors - 1) *
-				sizeof(struct virtchnl_iwarp_qv_info));
-		}
-		break;
 	case VIRTCHNL_OP_CONFIG_RSS_KEY:
 		valid_len = sizeof(struct virtchnl_rss_key);
 		if (msglen >= valid_len) {
 			struct virtchnl_rss_key *vrk =
 				(struct virtchnl_rss_key *)msg;
+
+			if (vrk->key_len == 0) {
+				/* zero length is allowed as input */
+				break;
+			}
+
 			valid_len += vrk->key_len - 1;
 		}
 		break;
@@ -1466,6 +2657,12 @@ virtchnl_vc_validate_vf_msg(struct virtchnl_version_info *ver, u32 v_opcode,
 		if (msglen >= valid_len) {
 			struct virtchnl_rss_lut *vrl =
 				(struct virtchnl_rss_lut *)msg;
+
+			if (vrl->lut_entries == 0) {
+				/* zero entries is allowed as input */
+				break;
+			}
+
 			valid_len += vrl->lut_entries - 1;
 		}
 		break;
@@ -1485,20 +2682,25 @@ virtchnl_vc_validate_vf_msg(struct virtchnl_version_info *ver, u32 v_opcode,
 		if (msglen >= valid_len) {
 			struct virtchnl_tc_info *vti =
 				(struct virtchnl_tc_info *)msg;
+
+			if (vti->num_tc == 0 || vti->num_tc >
+			    VIRTCHNL_OP_ENABLE_CHANNELS_MAX) {
+				err_msg_format = true;
+				break;
+			}
+
 			valid_len += (vti->num_tc - 1) *
 				     sizeof(struct virtchnl_channel_info);
-			if (vti->num_tc == 0)
-				err_msg_format = true;
 		}
 		break;
 	case VIRTCHNL_OP_DISABLE_CHANNELS:
 		break;
 	case VIRTCHNL_OP_ADD_CLOUD_FILTER:
-		valid_len = sizeof(struct virtchnl_filter);
-		break;
 	case VIRTCHNL_OP_DEL_CLOUD_FILTER:
 		valid_len = sizeof(struct virtchnl_filter);
 		break;
+	case VIRTCHNL_OP_GET_SUPPORTED_RXDIDS:
+		break;
 	case VIRTCHNL_OP_ADD_RSS_CFG:
 	case VIRTCHNL_OP_DEL_RSS_CFG:
 		valid_len = sizeof(struct virtchnl_rss_cfg);
@@ -1509,6 +2711,21 @@ virtchnl_vc_validate_vf_msg(struct virtchnl_version_info *ver, u32 v_opcode,
 	case VIRTCHNL_OP_DEL_FDIR_FILTER:
 		valid_len = sizeof(struct virtchnl_fdir_del);
 		break;
+	case VIRTCHNL_OP_GET_QOS_CAPS:
+		break;
+	case VIRTCHNL_OP_CONFIG_QUEUE_TC_MAP:
+		valid_len = sizeof(struct virtchnl_queue_tc_mapping);
+		if (msglen >= valid_len) {
+			struct virtchnl_queue_tc_mapping *q_tc =
+				(struct virtchnl_queue_tc_mapping *)msg;
+			if (q_tc->num_tc == 0) {
+				err_msg_format = true;
+				break;
+			}
+			valid_len += (q_tc->num_tc - 1) *
+					 sizeof(q_tc->tc[0]);
+		}
+		break;
 	case VIRTCHNL_OP_GET_OFFLOAD_VLAN_V2_CAPS:
 		break;
 	case VIRTCHNL_OP_ADD_VLAN_V2:
@@ -1518,21 +2735,77 @@ virtchnl_vc_validate_vf_msg(struct virtchnl_version_info *ver, u32 v_opcode,
 			struct virtchnl_vlan_filter_list_v2 *vfl =
 			    (struct virtchnl_vlan_filter_list_v2 *)msg;
 
-			valid_len += (vfl->num_elements - 1) *
-				sizeof(struct virtchnl_vlan_filter);
-
-			if (vfl->num_elements == 0) {
+			if (vfl->num_elements == 0 || vfl->num_elements >
+			    VIRTCHNL_OP_ADD_DEL_VLAN_V2_MAX) {
 				err_msg_format = true;
 				break;
 			}
+
+			valid_len += (vfl->num_elements - 1) *
+				sizeof(struct virtchnl_vlan_filter);
 		}
 		break;
 	case VIRTCHNL_OP_ENABLE_VLAN_STRIPPING_V2:
 	case VIRTCHNL_OP_DISABLE_VLAN_STRIPPING_V2:
 	case VIRTCHNL_OP_ENABLE_VLAN_INSERTION_V2:
 	case VIRTCHNL_OP_DISABLE_VLAN_INSERTION_V2:
+	case VIRTCHNL_OP_ENABLE_VLAN_FILTERING_V2:
+	case VIRTCHNL_OP_DISABLE_VLAN_FILTERING_V2:
 		valid_len = sizeof(struct virtchnl_vlan_setting);
 		break;
+	case VIRTCHNL_OP_1588_PTP_GET_CAPS:
+		valid_len = sizeof(struct virtchnl_ptp_caps);
+		break;
+	case VIRTCHNL_OP_1588_PTP_GET_TIME:
+	case VIRTCHNL_OP_1588_PTP_SET_TIME:
+		valid_len = sizeof(struct virtchnl_phc_time);
+		break;
+	case VIRTCHNL_OP_1588_PTP_ADJ_TIME:
+		valid_len = sizeof(struct virtchnl_phc_adj_time);
+		break;
+	case VIRTCHNL_OP_1588_PTP_ADJ_FREQ:
+		valid_len = sizeof(struct virtchnl_phc_adj_freq);
+		break;
+	case VIRTCHNL_OP_1588_PTP_TX_TIMESTAMP:
+		valid_len = sizeof(struct virtchnl_phc_tx_tstamp);
+		break;
+	case VIRTCHNL_OP_1588_PTP_SET_PIN_CFG:
+		valid_len = sizeof(struct virtchnl_phc_set_pin);
+		break;
+	case VIRTCHNL_OP_1588_PTP_GET_PIN_CFGS:
+		break;
+	case VIRTCHNL_OP_1588_PTP_EXT_TIMESTAMP:
+		valid_len = sizeof(struct virtchnl_phc_ext_tstamp);
+		break;
+	case VIRTCHNL_OP_ENABLE_QUEUES_V2:
+	case VIRTCHNL_OP_DISABLE_QUEUES_V2:
+		valid_len = sizeof(struct virtchnl_del_ena_dis_queues);
+		if (msglen >= valid_len) {
+			struct virtchnl_del_ena_dis_queues *qs =
+				(struct virtchnl_del_ena_dis_queues *)msg;
+			if (qs->chunks.num_chunks == 0 ||
+			    qs->chunks.num_chunks > VIRTCHNL_OP_ENABLE_DISABLE_DEL_QUEUES_V2_MAX) {
+				err_msg_format = true;
+				break;
+			}
+			valid_len += (qs->chunks.num_chunks - 1) *
+				      sizeof(struct virtchnl_queue_chunk);
+		}
+		break;
+	case VIRTCHNL_OP_MAP_QUEUE_VECTOR:
+		valid_len = sizeof(struct virtchnl_queue_vector_maps);
+		if (msglen >= valid_len) {
+			struct virtchnl_queue_vector_maps *v_qp =
+				(struct virtchnl_queue_vector_maps *)msg;
+			if (v_qp->num_qv_maps == 0 ||
+			    v_qp->num_qv_maps > VIRTCHNL_OP_MAP_UNMAP_QUEUE_VECTOR_MAX) {
+				err_msg_format = true;
+				break;
+			}
+			valid_len += (v_qp->num_qv_maps - 1) *
+				      sizeof(struct virtchnl_queue_vector);
+		}
+		break;
 	/* These are always errors coming from the VF. */
 	case VIRTCHNL_OP_EVENT:
 	case VIRTCHNL_OP_UNKNOWN:
diff --git a/include/linux/avf/virtchnl_2.h b/include/linux/avf/virtchnl_2.h
new file mode 100644
index 000000000000..54a917713789
--- /dev/null
+++ b/include/linux/avf/virtchnl_2.h
@@ -0,0 +1,1243 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (c) 2020, Intel Corporation. */
+
+#ifndef _VIRTCHNL_2_H_
+#define _VIRTCHNL_2_H_
+
+/* All opcodes associated with virtchnl 2 are prefixed with virtchnl2 or
+ * VIRTCHNL2. This is done so opcodes, offloads/capabilities, structures,
+ * and defines used for virtchnl 1.x (i.e. VIRTCHNL_VF_OPCODE_*_V2,
+ * VIRTCHNL_VF_CAP_*_V2, virtchnl_*_v2) are clearly separated. Any future
+ * opcodes, offloads/capabilities, structures, and defines must be prefixed
+ * with virtchnl2 or VIRTCHNL2 to avoid confusion.
+ */
+
+#include "virtchnl_lan_desc.h"
+#include "virtchnl.h"
+
+#define VIRTCHNL2_MAX_NUM_PROTO_HDRS		32
+
+#define VIRTCHNL2_RDMA_INVALID_QUEUE_IDX	0xFFFF
+
+/* VIRTCHNL2_VPORT_TYPE
+ * Type of virtual port
+ */
+#define VIRTCHNL2_VPORT_TYPE_DEFAULT		0
+#define VIRTCHNL2_VPORT_TYPE_SRIOV		1
+#define VIRTCHNL2_VPORT_TYPE_SIOV		2
+#define VIRTCHNL2_VPORT_TYPE_SUBDEV		3
+#define VIRTCHNL2_VPORT_TYPE_MNG		4
+
+/* VIRTCHNL2_QUEUE_MODEL
+ * Type of queue model
+ *
+ * In the single queue model, the same transmit descriptor queue is used by
+ * software to post descriptors to hardware and by hardware to post completed
+ * descriptors to software.
+ * Likewise, the same receive descriptor queue is used by hardware to post
+ * completions to software and by software to post buffers to hardware.
+ */
+#define VIRTCHNL2_QUEUE_MODEL_SINGLE		0
+/* In the split queue model, hardware uses transmit completion queues to post
+ * descriptor/buffer completions to software, while software uses transmit
+ * descriptor queues to post descriptors to hardware.
+ * Likewise, hardware posts descriptor completions to the receive descriptor
+ * queue, while software uses receive buffer queues to post buffers to hardware.
+ */
+#define VIRTCHNL2_QUEUE_MODEL_SPLIT		1
+
+/* VIRTCHNL2_CHECKSUM_OFFLOAD_CAPS
+ * Checksum offload capability flags
+ */
+#define VIRTCHNL2_CAP_TX_CSUM_L3_IPV4		BIT(0)
+#define VIRTCHNL2_CAP_TX_CSUM_L4_IPV4_TCP	BIT(1)
+#define VIRTCHNL2_CAP_TX_CSUM_L4_IPV4_UDP	BIT(2)
+#define VIRTCHNL2_CAP_TX_CSUM_L4_IPV4_SCTP	BIT(3)
+#define VIRTCHNL2_CAP_TX_CSUM_L4_IPV6_TCP	BIT(4)
+#define VIRTCHNL2_CAP_TX_CSUM_L4_IPV6_UDP	BIT(5)
+#define VIRTCHNL2_CAP_TX_CSUM_L4_IPV6_SCTP	BIT(6)
+#define VIRTCHNL2_CAP_TX_CSUM_GENERIC		BIT(7)
+#define VIRTCHNL2_CAP_RX_CSUM_L3_IPV4		BIT(8)
+#define VIRTCHNL2_CAP_RX_CSUM_L4_IPV4_TCP	BIT(9)
+#define VIRTCHNL2_CAP_RX_CSUM_L4_IPV4_UDP	BIT(10)
+#define VIRTCHNL2_CAP_RX_CSUM_L4_IPV4_SCTP	BIT(11)
+#define VIRTCHNL2_CAP_RX_CSUM_L4_IPV6_TCP	BIT(12)
+#define VIRTCHNL2_CAP_RX_CSUM_L4_IPV6_UDP	BIT(13)
+#define VIRTCHNL2_CAP_RX_CSUM_L4_IPV6_SCTP	BIT(14)
+#define VIRTCHNL2_CAP_RX_CSUM_GENERIC		BIT(15)
+
+/* VIRTCHNL2_SEGMENTATION_OFFLOAD_CAPS
+ * Segmentation offload capability flags
+ */
+#define VIRTCHNL2_CAP_SEG_IPV4_TCP		BIT(0)
+#define VIRTCHNL2_CAP_SEG_IPV4_UDP		BIT(1)
+#define VIRTCHNL2_CAP_SEG_IPV4_SCTP		BIT(2)
+#define VIRTCHNL2_CAP_SEG_IPV6_TCP		BIT(3)
+#define VIRTCHNL2_CAP_SEG_IPV6_UDP		BIT(4)
+#define VIRTCHNL2_CAP_SEG_IPV6_SCTP		BIT(5)
+#define VIRTCHNL2_CAP_SEG_GENERIC		BIT(6)
+
+/* VIRTCHNL2_RSS_FLOW_TYPE_CAPS
+ * Receive Side Scaling Flow type capability flags
+ */
+#define VIRTCHNL2_CAP_RSS_IPV4_TCP		BIT(0)
+#define VIRTCHNL2_CAP_RSS_IPV4_UDP		BIT(1)
+#define VIRTCHNL2_CAP_RSS_IPV4_SCTP		BIT(2)
+#define VIRTCHNL2_CAP_RSS_IPV4_OTHER		BIT(3)
+#define VIRTCHNL2_CAP_RSS_IPV6_TCP		BIT(4)
+#define VIRTCHNL2_CAP_RSS_IPV6_UDP		BIT(5)
+#define VIRTCHNL2_CAP_RSS_IPV6_SCTP		BIT(6)
+#define VIRTCHNL2_CAP_RSS_IPV6_OTHER		BIT(7)
+#define VIRTCHNL2_CAP_RSS_IPV4_AH		BIT(8)
+#define VIRTCHNL2_CAP_RSS_IPV4_ESP		BIT(9)
+#define VIRTCHNL2_CAP_RSS_IPV4_AH_ESP		BIT(10)
+#define VIRTCHNL2_CAP_RSS_IPV6_AH		BIT(11)
+#define VIRTCHNL2_CAP_RSS_IPV6_ESP		BIT(12)
+#define VIRTCHNL2_CAP_RSS_IPV6_AH_ESP		BIT(13)
+
+/* VIRTCHNL2_HEADER_SPLIT_CAPS
+ * Header split capability flags
+ */
+/* for prepended metadata  */
+#define VIRTCHNL2_CAP_RX_HSPLIT_AT_L2		BIT(0)
+/* all VLANs go into header buffer */
+#define VIRTCHNL2_CAP_RX_HSPLIT_AT_L3		BIT(1)
+#define VIRTCHNL2_CAP_RX_HSPLIT_AT_L4V4		BIT(2)
+#define VIRTCHNL2_CAP_RX_HSPLIT_AT_L4V6		BIT(3)
+
+/* VIRTCHNL2_RSC_OFFLOAD_CAPS
+ * Receive Side Coalescing offload capability flags
+ */
+#define VIRTCHNL2_CAP_RSC_IPV4_TCP		BIT(0)
+#define VIRTCHNL2_CAP_RSC_IPV4_SCTP		BIT(1)
+#define VIRTCHNL2_CAP_RSC_IPV6_TCP		BIT(2)
+#define VIRTCHNL2_CAP_RSC_IPV6_SCTP		BIT(3)
+
+/* VIRTCHNL2_OTHER_CAPS
+ * Other capability flags
+ * SPLITQ_QSCHED: Queue based scheduling using split queue model
+ * TX_VLAN: VLAN tag insertion
+ * RX_VLAN: VLAN tag stripping
+ */
+#define VIRTCHNL2_CAP_RDMA			BIT(0)
+#define VIRTCHNL2_CAP_SRIOV			BIT(1)
+#define VIRTCHNL2_CAP_MACFILTER			BIT(2)
+#define VIRTCHNL2_CAP_FLOW_DIRECTOR		BIT(3)
+#define VIRTCHNL2_CAP_SPLITQ_QSCHED		BIT(4)
+#define VIRTCHNL2_CAP_CRC			BIT(5)
+#define VIRTCHNL2_CAP_ADQ			BIT(6)
+#define VIRTCHNL2_CAP_WB_ON_ITR			BIT(7)
+#define VIRTCHNL2_CAP_PROMISC			BIT(8)
+#define VIRTCHNL2_CAP_LINK_SPEED		BIT(9)
+#define VIRTCHNL2_CAP_INLINE_IPSEC		BIT(10)
+#define VIRTCHNL2_CAP_LARGE_NUM_QUEUES		BIT(11)
+/* require additional info */
+#define VIRTCHNL2_CAP_VLAN			BIT(12)
+#define VIRTCHNL2_CAP_PTP			BIT(13)
+#define VIRTCHNL2_CAP_ADV_RSS			BIT(15)
+#define VIRTCHNL2_CAP_FDIR			BIT(16)
+#define VIRTCHNL2_CAP_RX_FLEX_DESC		BIT(17)
+#define VIRTCHNL2_CAP_PTYPE			BIT(18)
+
+/* VIRTCHNL2_DEVICE_TYPE */
+/* underlying device type */
+#define VIRTCHNL2_MEV_DEVICE			0
+
+/* VIRTCHNL2_TXQ_SCHED_MODE
+ * Transmit Queue Scheduling Modes - Queue mode is the legacy mode i.e. inorder
+ * completions where descriptors and buffers are completed at the same time.
+ * Flow scheduling mode allows for out of order packet processing where
+ * descriptors are cleaned in order, but buffers can be completed out of order.
+ */
+#define VIRTCHNL2_TXQ_SCHED_MODE_QUEUE		0
+#define VIRTCHNL2_TXQ_SCHED_MODE_FLOW		1
+
+/* VIRTCHNL2_TXQ_FLAGS
+ * Transmit Queue feature flags
+ *
+ * Enable rule miss completion type; packet completion for a packet
+ * sent on exception path; only relevant in flow scheduling mode
+ */
+#define VIRTCHNL2_TXQ_ENABLE_MISS_COMPL		BIT(0)
+
+/* VIRTCHNL2_PEER_TYPE
+ * Transmit mailbox peer type
+ */
+#define VIRTCHNL2_RDMA_CPF			0
+#define VIRTCHNL2_NVME_CPF			1
+#define VIRTCHNL2_ATE_CPF			2
+#define VIRTCHNL2_LCE_CPF			3
+
+/* VIRTCHNL2_RXQ_FLAGS
+ * Receive Queue Feature flags
+ */
+#define VIRTCHNL2_RXQ_RSC			BIT(0)
+#define VIRTCHNL2_RXQ_HDR_SPLIT			BIT(1)
+/* When set, packet descriptors are flushed by hardware immediately after
+ * processing each packet.
+ */
+#define VIRTCHNL2_RXQ_IMMEDIATE_WRITE_BACK	BIT(2)
+#define VIRTCHNL2_RX_DESC_SIZE_16BYTE		BIT(3)
+#define VIRTCHNL2_RX_DESC_SIZE_32BYTE		BIT(4)
+
+/* VIRTCHNL2_RSS_ALGORITHM
+ * Type of RSS algorithm
+ */
+#define VIRTCHNL2_RSS_ALG_TOEPLITZ_ASYMMETRIC		0
+#define VIRTCHNL2_RSS_ALG_R_ASYMMETRIC			1
+#define VIRTCHNL2_RSS_ALG_TOEPLITZ_SYMMETRIC		2
+#define VIRTCHNL2_RSS_ALG_XOR_SYMMETRIC			3
+
+/* VIRTCHNL2_EVENT_CODES
+ * VIRTCHNL2_OP_EVENT
+ * CP sends this message to inform the PF/VF driver of events that may affect
+ * it. No direct response is expected from the driver, though it may generate
+ * other messages in response to this one.
+ */
+#define VIRTCHNL2_EVENT_UNKNOWN			0
+#define VIRTCHNL2_EVENT_LINK_CHANGE		1
+
+/* VIRTCHNL2_QUEUE_TYPE
+ * Transmit and Receive queue types are valid in legacy as well as split queue
+ * models. With Split Queue model, 2 additional types are introduced -
+ * TX_COMPLETION and RX_BUFFER. In split queue model, receive  corresponds to
+ * the queue where hardware posts completions.
+ */
+#define VIRTCHNL2_QUEUE_TYPE_TX			0
+#define VIRTCHNL2_QUEUE_TYPE_RX			1
+#define VIRTCHNL2_QUEUE_TYPE_TX_COMPLETION	2
+#define VIRTCHNL2_QUEUE_TYPE_RX_BUFFER		3
+#define VIRTCHNL2_QUEUE_TYPE_CONFIG_TX		4
+#define VIRTCHNL2_QUEUE_TYPE_CONFIG_RX		5
+
+/* VIRTCHNL2_ITR_IDX
+ * Virtchannel interrupt throttling rate index
+ */
+#define VIRTCHNL2_ITR_IDX_0			0
+#define VIRTCHNL2_ITR_IDX_1			1
+#define VIRTCHNL2_ITR_IDX_2			2
+#define VIRTCHNL2_ITR_IDX_NO_ITR		3
+
+/* VIRTCHNL2_VECTOR_LIMITS
+ * Since PF/VF messages are limited by __le16 size, precalculate the maximum
+ * possible values of nested elements in virtchnl structures that virtual
+ * channel can possibly handle in a single message.
+ */
+
+#define VIRTCHNL2_OP_DEL_ENABLE_DISABLE_QUEUES_MAX (\
+		((__le16)(~0) - sizeof(struct virtchnl2_del_ena_dis_queues)) / \
+		sizeof(struct virtchnl2_queue_chunk))
+
+#define VIRTCHNL2_OP_MAP_UNMAP_QUEUE_VECTOR_MAX (\
+		((__le16)(~0) - sizeof(struct virtchnl2_queue_vector_maps)) / \
+		sizeof(struct virtchnl2_queue_vector))
+
+/* VIRTCHNL2_PROTO_HDR_TYPE
+ * Protocol header type within a packet segment. A segment consists of one or
+ * more protocol headers that make up a logical group of protocol headers. Each
+ * logical group of protocol headers encapsulates or is encapsulated using/by
+ * tunneling or encapsulation protocols for network virtualization.
+ */
+/* VIRTCHNL2_PROTO_HDR_ANY is a mandatory protocol id */
+#define VIRTCHNL2_PROTO_HDR_ANY			0
+#define VIRTCHNL2_PROTO_HDR_PRE_MAC		1
+/* VIRTCHNL2_PROTO_HDR_MAC is a mandatory protocol id */
+#define VIRTCHNL2_PROTO_HDR_MAC			2
+#define VIRTCHNL2_PROTO_HDR_POST_MAC		3
+#define VIRTCHNL2_PROTO_HDR_ETHERTYPE		4
+#define VIRTCHNL2_PROTO_HDR_VLAN		5
+#define VIRTCHNL2_PROTO_HDR_SVLAN		6
+#define VIRTCHNL2_PROTO_HDR_CVLAN		7
+#define VIRTCHNL2_PROTO_HDR_MPLS		8
+#define VIRTCHNL2_PROTO_HDR_UMPLS		9
+#define VIRTCHNL2_PROTO_HDR_MMPLS		10
+#define VIRTCHNL2_PROTO_HDR_PTP			11
+#define VIRTCHNL2_PROTO_HDR_CTRL		12
+#define VIRTCHNL2_PROTO_HDR_LLDP		13
+#define VIRTCHNL2_PROTO_HDR_ARP			14
+#define VIRTCHNL2_PROTO_HDR_ECP			15
+#define VIRTCHNL2_PROTO_HDR_EAPOL		16
+#define VIRTCHNL2_PROTO_HDR_PPPOD		17
+#define VIRTCHNL2_PROTO_HDR_PPPOE		18
+/* VIRTCHNL2_PROTO_HDR_IPV4 is a mandatory protocol id */
+#define VIRTCHNL2_PROTO_HDR_IPV4		19
+/* IPv4 and IPv6 Fragment header types are only associated to
+ * VIRTCHNL2_PROTO_HDR_IPV4 and VIRTCHNL2_PROTO_HDR_IPV6 respectively,
+ * cannot be used independently.
+ */
+/* VIRTCHNL2_PROTO_HDR_IPV4_FRAG is a mandatory protocol id */
+#define VIRTCHNL2_PROTO_HDR_IPV4_FRAG		20
+/* VIRTCHNL2_PROTO_HDR_IPV6 is a mandatory protocol id */
+#define VIRTCHNL2_PROTO_HDR_IPV6		21
+/* VIRTCHNL2_PROTO_HDR_IPV6_FRAG is a mandatory protocol id */
+#define VIRTCHNL2_PROTO_HDR_IPV6_FRAG		22
+#define VIRTCHNL2_PROTO_HDR_IPV6_EH		23
+/* VIRTCHNL2_PROTO_HDR_UDP is a mandatory protocol id */
+#define VIRTCHNL2_PROTO_HDR_UDP			24
+/* VIRTCHNL2_PROTO_HDR_TCP is a mandatory protocol id */
+#define VIRTCHNL2_PROTO_HDR_TCP			25
+/* VIRTCHNL2_PROTO_HDR_SCTP is a mandatory protocol id */
+#define VIRTCHNL2_PROTO_HDR_SCTP		26
+/* VIRTCHNL2_PROTO_HDR_ICMP is a mandatory protocol id */
+#define VIRTCHNL2_PROTO_HDR_ICMP		27
+/* VIRTCHNL2_PROTO_HDR_ICMPV6 is a mandatory protocol id */
+#define VIRTCHNL2_PROTO_HDR_ICMPV6		28
+#define VIRTCHNL2_PROTO_HDR_IGMP		29
+#define VIRTCHNL2_PROTO_HDR_AH			30
+#define VIRTCHNL2_PROTO_HDR_ESP			31
+#define VIRTCHNL2_PROTO_HDR_IKE			32
+#define VIRTCHNL2_PROTO_HDR_NATT_KEEP		33
+/* VIRTCHNL2_PROTO_HDR_PAY is a mandatory protocol id */
+#define VIRTCHNL2_PROTO_HDR_PAY			34
+#define VIRTCHNL2_PROTO_HDR_L2TPV2		35
+#define VIRTCHNL2_PROTO_HDR_L2TPV2_CONTROL	36
+#define VIRTCHNL2_PROTO_HDR_L2TPV3		37
+#define VIRTCHNL2_PROTO_HDR_GTP			38
+#define VIRTCHNL2_PROTO_HDR_GTP_EH		39
+#define VIRTCHNL2_PROTO_HDR_GTPCV2		40
+#define VIRTCHNL2_PROTO_HDR_GTPC_TEID		41
+#define VIRTCHNL2_PROTO_HDR_GTPU		42
+#define VIRTCHNL2_PROTO_HDR_GTPU_UL		43
+#define VIRTCHNL2_PROTO_HDR_GTPU_DL		44
+#define VIRTCHNL2_PROTO_HDR_ECPRI		45
+#define VIRTCHNL2_PROTO_HDR_VRRP		46
+#define VIRTCHNL2_PROTO_HDR_OSPF		47
+/* VIRTCHNL2_PROTO_HDR_TUN is a mandatory protocol id */
+#define VIRTCHNL2_PROTO_HDR_TUN			48
+#define VIRTCHNL2_PROTO_HDR_GRE			49
+#define VIRTCHNL2_PROTO_HDR_NVGRE		50
+#define VIRTCHNL2_PROTO_HDR_VXLAN		51
+#define VIRTCHNL2_PROTO_HDR_VXLAN_GPE		52
+#define VIRTCHNL2_PROTO_HDR_GENEVE		53
+#define VIRTCHNL2_PROTO_HDR_NSH			54
+#define VIRTCHNL2_PROTO_HDR_QUIC		55
+#define VIRTCHNL2_PROTO_HDR_PFCP		56
+#define VIRTCHNL2_PROTO_HDR_PFCP_NODE		57
+#define VIRTCHNL2_PROTO_HDR_PFCP_SESSION	58
+#define VIRTCHNL2_PROTO_HDR_RTP			59
+#define VIRTCHNL2_PROTO_HDR_ROCE		60
+#define VIRTCHNL2_PROTO_HDR_ROCEV1		61
+#define VIRTCHNL2_PROTO_HDR_ROCEV2		62
+/* protocol ids upto 32767 are reserved for AVF use */
+/* 32768 - 65534 are used for user defined protocol ids */
+/* VIRTCHNL2_PROTO_HDR_NO_PROTO is a mandatory protocol id */
+#define VIRTCHNL2_PROTO_HDR_NO_PROTO		65535
+
+/* VIRTCHNL2_OP_GET_CAPS
+ * Dataplane driver sends this message to CP to negotiate capabilities and
+ * provides a virtchnl2_get_capabilities structure with its desired
+ * capabilities, max_sriov_vfs and num_allocated_vectors.
+ * CP responds with a virtchnl2_get_capabilities structure updated
+ * with allowed capabilities and the other fields as below.
+ * If PF sets max_sriov_vfs as 0, CP will respond with max number of VFs
+ * that can be created by this PF. For any other value 'n', CP responds
+ * with max_sriov_vfs set to min(n, x) where x is the max number of VFs
+ * allowed by CP's policy. max_sriov_vfs is not applicable for VFs.
+ * If dataplane driver sets num_allocated_vectors as 0, CP will respond with 1
+ * which is default vector associated with the default mailbox. For any other
+ * value 'n', CP responds with a value <= n based on the CP's policy of
+ * max number of vectors for a PF.
+ * CP will respond with the vector ID of mailbox allocated to the PF in
+ * mailbox_vector_id and the number of itr index registers in itr_idx_map.
+ * It also responds with default number of vports that the dataplane driver
+ * should comeup with in default_num_vports and maximum number of vports that
+ * can be supported in max_vports
+ */
+struct virtchnl2_get_capabilities {
+	/* see VIRTCHNL2_CHECKSUM_OFFLOAD_CAPS definitions */
+	__le32 csum_caps;
+
+	/* see VIRTCHNL2_SEGMENTATION_OFFLOAD_CAPS definitions */
+	__le32 seg_caps;
+
+	/* see VIRTCHNL2_HEADER_SPLIT_CAPS definitions */
+	__le32 hsplit_caps;
+
+	/* see VIRTCHNL2_RSC_OFFLOAD_CAPS definitions */
+	__le32 rsc_caps;
+
+	/* see VIRTCHNL2_RSS_FLOW_TYPE_CAPS definitions  */
+	__le64 rss_caps;
+
+	/* see VIRTCHNL2_OTHER_CAPS definitions  */
+	__le64 other_caps;
+
+	/* DYN_CTL register offset and vector id for mailbox provided by CP */
+	__le32 mailbox_dyn_ctl;
+	__le16 mailbox_vector_id;
+	/* Maximum number of allocated vectors for the device */
+	__le16 num_allocated_vectors;
+
+	/* Maximum number of queues that can be supported */
+	__le16 max_rx_q;
+	__le16 max_tx_q;
+	__le16 max_rx_bufq;
+	__le16 max_tx_complq;
+
+	/* The PF sends the maximum VFs it is requesting. The CP responds with
+	 * the maximum VFs granted.
+	 */
+	__le16 max_sriov_vfs;
+
+	/* maximum number of vports that can be supported */
+	__le16 max_vports;
+	/* default number of vports driver should allocate on load */
+	__le16 default_num_vports;
+
+	/* Max header length hardware can parse/checksum, in bytes */
+	__le16 max_tx_hdr_size;
+
+	/* Max number of scatter gather buffers that can be sent per transmit
+	 * packet without needing to be linearized
+	 */
+	u8 max_sg_bufs_per_tx_pkt;
+
+	/* see VIRTCHNL2_ITR_IDX definition */
+	u8 itr_idx_map;
+
+	__le16 pad1;
+
+	/* version of Control Plane that is running */
+	__le16 oem_cp_ver_major;
+	__le16 oem_cp_ver_minor;
+	/* see VIRTCHNL2_DEVICE_TYPE definitions */
+	__le32 device_type;
+
+	u8 reserved[12];
+};
+
+VIRTCHNL_CHECK_STRUCT_LEN(80, virtchnl2_get_capabilities);
+
+struct virtchnl2_queue_reg_chunk {
+	/* see VIRTCHNL2_QUEUE_TYPE definitions */
+	__le32 type;
+	__le32 start_queue_id;
+	__le32 num_queues;
+	__le32 pad;
+
+	/* Queue tail register offset and spacing provided by CP */
+	__le64 qtail_reg_start;
+	__le32 qtail_reg_spacing;
+
+	u8 reserved[4];
+};
+
+VIRTCHNL_CHECK_STRUCT_LEN(32, virtchnl2_queue_reg_chunk);
+
+/* structure to specify several chunks of contiguous queues */
+struct virtchnl2_queue_reg_chunks {
+	__le16 num_chunks;
+	u8 reserved[6];
+	struct virtchnl2_queue_reg_chunk chunks[1];
+};
+
+VIRTCHNL_CHECK_STRUCT_LEN(40, virtchnl2_queue_reg_chunks);
+
+/* VIRTCHNL2_OP_CREATE_VPORT
+ * PF sends this message to CP to create a vport by filling in required
+ * fields of virtchnl2_create_vport structure.
+ * CP responds with the updated virtchnl2_create_vport structure containing the
+ * necessary fields followed by chunks which in turn will have an array of
+ * num_chunks entries of virtchnl2_queue_chunk structures.
+ */
+struct virtchnl2_create_vport {
+	/* PF/VF populates the following fields on request */
+	/* see VIRTCHNL2_VPORT_TYPE definitions */
+	__le16 vport_type;
+
+	/* see VIRTCHNL2_QUEUE_MODEL definitions */
+	__le16 txq_model;
+
+	/* see VIRTCHNL2_QUEUE_MODEL definitions */
+	__le16 rxq_model;
+	__le16 num_tx_q;
+	/* valid only if txq_model is split queue */
+	__le16 num_tx_complq;
+	__le16 num_rx_q;
+	/* valid only if rxq_model is split queue */
+	__le16 num_rx_bufq;
+	/* relative receive queue index to be used as default */
+	__le16 default_rx_q;
+	/* used to align PF and CP in case of default multiple vports, it is
+	 * filled by the PF and CP returns the same value, to enable the driver
+	 * to support multiple asynchronous parallel CREATE_VPORT requests and
+	 * associate a response to a specific request
+	 */
+	__le16 vport_index;
+
+	/* CP populates the following fields on response */
+	__le16 max_mtu;
+	__le32 vport_id;
+	u8 default_mac_addr[ETH_ALEN];
+	__le16 pad;
+	/* see VIRTCHNL2_RX_DESC_IDS definitions */
+	__le64 rx_desc_ids;
+	/* see VIRTCHNL2_TX_DESC_IDS definitions */
+	__le64 tx_desc_ids;
+
+#define MAX_Q_REGIONS 16
+	__le32 max_qs_per_qregion[MAX_Q_REGIONS];
+	__le32 qregion_total_qs;
+	__le16 qregion_type;
+	__le16 pad2;
+
+	/* see VIRTCHNL2_RSS_ALGORITHM definitions */
+	__le32 rss_algorithm;
+	__le16 rss_key_size;
+	__le16 rss_lut_size;
+
+	/* see VIRTCHNL2_HEADER_SPLIT_CAPS definitions */
+	__le32 rx_split_pos;
+
+	u8 reserved[20];
+	struct virtchnl2_queue_reg_chunks chunks;
+};
+
+VIRTCHNL_CHECK_STRUCT_LEN(192, virtchnl2_create_vport);
+
+/* VIRTCHNL2_OP_DESTROY_VPORT
+ * VIRTCHNL2_OP_ENABLE_VPORT
+ * VIRTCHNL2_OP_DISABLE_VPORT
+ * PF sends this message to CP to destroy, enable or disable a vport by filling
+ * in the vport_id in virtchnl2_vport structure.
+ * CP responds with the status of the requested operation.
+ */
+struct virtchnl2_vport {
+	__le32 vport_id;
+	u8 reserved[4];
+};
+
+VIRTCHNL_CHECK_STRUCT_LEN(8, virtchnl2_vport);
+
+/* Transmit queue config info */
+struct virtchnl2_txq_info {
+	__le64 dma_ring_addr;
+
+	/* see VIRTCHNL2_QUEUE_TYPE definitions */
+	__le32 type;
+
+	__le32 queue_id;
+	/* valid only if queue model is split and type is trasmit queue. Used
+	 * in many to one mapping of transmit queues to completion queue
+	 */
+	__le16 relative_queue_id;
+
+	/* see VIRTCHNL2_QUEUE_MODEL definitions */
+	__le16 model;
+
+	/* see VIRTCHNL2_TXQ_SCHED_MODE definitions */
+	__le16 sched_mode;
+
+	/* see VIRTCHNL2_TXQ_FLAGS definitions */
+	__le16 qflags;
+	__le16 ring_len;
+
+	/* valid only if queue model is split and type is transmit queue */
+	__le16 tx_compl_queue_id;
+	/* valid only if queue type is VIRTCHNL2_QUEUE_TYPE_MAILBOX_TX */
+	/* see VIRTCHNL2_PEER_TYPE definitions */
+	__le16 peer_type;
+	/* valid only if queue type is CONFIG_TX and used to deliver messages
+	 * for the respective CONFIG_TX queue
+	 */
+	__le16 peer_rx_queue_id;
+
+	/* value ranges from 0 to 15 */
+	__le16 qregion_id;
+	u8 pad[2];
+
+	/* Egress pasid is used for SIOV use case */
+	__le32 egress_pasid;
+	__le32 egress_hdr_pasid;
+	__le32 egress_buf_pasid;
+
+	u8 reserved[8];
+};
+
+VIRTCHNL_CHECK_STRUCT_LEN(56, virtchnl2_txq_info);
+
+/* VIRTCHNL2_OP_CONFIG_TX_QUEUES
+ * PF sends this message to set up parameters for one or more transmit queues.
+ * This message contains an array of num_qinfo instances of virtchnl2_txq_info
+ * structures. CP configures requested queues and returns a status code. If
+ * num_qinfo specified is greater than the number of queues associated with the
+ * vport, an error is returned and no queues are configured.
+ */
+struct virtchnl2_config_tx_queues {
+	__le32 vport_id;
+	__le16 num_qinfo;
+
+	u8 reserved[10];
+	struct virtchnl2_txq_info qinfo[1];
+};
+
+VIRTCHNL_CHECK_STRUCT_LEN(72, virtchnl2_config_tx_queues);
+
+/* Receive queue config info */
+struct virtchnl2_rxq_info {
+	/* see VIRTCHNL2_RX_DESC_IDS definitions */
+	__le64 desc_ids;
+	__le64 dma_ring_addr;
+
+	/* see VIRTCHNL2_QUEUE_TYPE definitions */
+	__le32 type;
+	__le32 queue_id;
+
+	/* see QUEUE_MODEL definitions */
+	__le16 model;
+
+	__le16 hdr_buffer_size;
+	__le32 data_buffer_size;
+	__le32 max_pkt_size;
+
+	__le16 ring_len;
+	u8 buffer_notif_stride;
+	u8 pad[1];
+
+	/* Applicable only for receive buffer queues */
+	__le64 dma_head_wb_addr;
+
+	/* Applicable only for receive completion queues */
+	/* see VIRTCHNL2_RXQ_FLAGS definitions */
+	__le16 qflags;
+
+	__le16 rx_buffer_low_watermark;
+
+	/* valid only in split queue model */
+	__le16 rx_bufq1_id;
+	/* valid only in split queue model */
+	__le16 rx_bufq2_id;
+	/* it indicates if there is a second buffer, rx_bufq2_id is valid only
+	 * if this field is set
+	 */
+	u8 bufq2_ena;
+	u8 pad2;
+
+	/* value ranges from 0 to 15 */
+	__le16 qregion_id;
+
+	/* Ingress pasid is used for SIOV use case */
+	__le32 ingress_pasid;
+	__le32 ingress_hdr_pasid;
+	__le32 ingress_buf_pasid;
+
+	u8 reserved[16];
+};
+
+VIRTCHNL_CHECK_STRUCT_LEN(88, virtchnl2_rxq_info);
+
+/* VIRTCHNL2_OP_CONFIG_RX_QUEUES
+ * PF sends this message to set up parameters for one or more receive queues.
+ * This message contains an array of num_qinfo instances of virtchnl2_rxq_info
+ * structures. CP configures requested queues and returns a status code.
+ * If the number of queues specified is greater than the number of queues
+ * associated with the vport, an error is returned and no queues are configured.
+ */
+struct virtchnl2_config_rx_queues {
+	__le32 vport_id;
+	__le16 num_qinfo;
+
+	u8 reserved[18];
+	struct virtchnl2_rxq_info qinfo[1];
+};
+
+VIRTCHNL_CHECK_STRUCT_LEN(112, virtchnl2_config_rx_queues);
+
+/* VIRTCHNL2_OP_ADD_QUEUES
+ * PF sends this message to request additional transmit/receive queues beyond
+ * the ones that were assigned via CREATE_VPORT request. virtchnl2_add_queues
+ * structure is used to specify the number of each type of queues.
+ * CP responds with the same structure with the actual number of queues assigned
+ * followed by num_chunks of virtchnl2_queue_chunk structures.
+ */
+struct virtchnl2_add_queues {
+	__le32 vport_id;
+	__le16 num_tx_q;
+	__le16 num_tx_complq;
+	__le16 num_rx_q;
+	__le16 num_rx_bufq;
+	u8 reserved[4];
+	struct virtchnl2_queue_reg_chunks chunks;
+};
+
+VIRTCHNL_CHECK_STRUCT_LEN(56, virtchnl2_add_queues);
+
+/* Structure to specify a chunk of contiguous interrupt vectors */
+struct virtchnl2_vector_chunk {
+	__le16 start_vector_id;
+	__le16 start_evv_id;
+	__le16 num_vectors;
+	__le16 pad1;
+
+	/* Register offsets and spacing provided by CP.
+	 * dynamic control registers are used for enabling/disabling/re-enabling
+	 * interrupts and updating interrupt rates in the hotpath. Any changes
+	 * to interrupt rates in the dynamic control registers will be reflected
+	 * in the interrupt throttling rate registers.
+	 * itrn registers are used to update interrupt rates for specific
+	 * interrupt indices without modifying the state of the interrupt.
+	 */
+	__le32 dynctl_reg_start;
+	__le32 dynctl_reg_spacing;
+
+	__le32 itrn_reg_start;
+	__le32 itrn_reg_spacing;
+	u8 reserved[8];
+};
+
+VIRTCHNL_CHECK_STRUCT_LEN(32, virtchnl2_vector_chunk);
+
+/* Structure to specify several chunks of contiguous interrupt vectors */
+struct virtchnl2_vector_chunks {
+	__le16 num_vchunks;
+	u8 reserved[14];
+	struct virtchnl2_vector_chunk vchunks[1];
+};
+
+VIRTCHNL_CHECK_STRUCT_LEN(48, virtchnl2_vector_chunks);
+
+/* VIRTCHNL2_OP_ALLOC_VECTORS
+ * PF sends this message to request additional interrupt vectors beyond the
+ * ones that were assigned via GET_CAPS request. virtchnl2_alloc_vectors
+ * structure is used to specify the number of vectors requested. CP responds
+ * with the same structure with the actual number of vectors assigned followed
+ * by virtchnl2_vector_chunks structure identifying the vector ids.
+ */
+struct virtchnl2_alloc_vectors {
+	__le16 num_vectors;
+	u8 reserved[14];
+	struct virtchnl2_vector_chunks vchunks;
+};
+
+VIRTCHNL_CHECK_STRUCT_LEN(64, virtchnl2_alloc_vectors);
+
+/* VIRTCHNL2_OP_DEALLOC_VECTORS
+ * PF sends this message to release the vectors.
+ * PF sends virtchnl2_vector_chunks struct to specify the vectors it is giving
+ * away. CP performs requested action and returns status.
+ */
+
+/* VIRTCHNL2_OP_GET_RSS_LUT
+ * VIRTCHNL2_OP_SET_RSS_LUT
+ * PF sends this message to get or set RSS lookup table. Only supported if
+ * both PF and CP drivers set the VIRTCHNL2_CAP_RSS bit during configuration
+ * negotiation. Uses the virtchnl2_rss_lut structure
+ */
+struct virtchnl2_rss_lut {
+	__le32 vport_id;
+	__le16 lut_entries_start;
+	__le16 lut_entries;
+	u8 reserved[4];
+	__le32 lut[1]; /* RSS lookup table */
+};
+
+VIRTCHNL_CHECK_STRUCT_LEN(16, virtchnl2_rss_lut);
+
+struct virtchnl2_proto_hdr {
+	/* see VIRTCHNL2_PROTO_HDR_TYPE definitions */
+	__le32 type;
+	__le32 field_selector; /* a bit mask to select field for header type */
+	u8 buffer[64];
+	/*
+	 * binary buffer in network order for specific header type.
+	 * For example, if type = VIRTCHNL2_PROTO_HDR_IPV4, a IPv4
+	 * header is expected to be copied into the buffer.
+	 */
+};
+
+VIRTCHNL_CHECK_STRUCT_LEN(72, virtchnl2_proto_hdr);
+
+struct virtchnl2_proto_hdrs {
+	u8 tunnel_level;
+	/*
+	 * specify where protocol header start from.
+	 * 0 - from the outer layer
+	 * 1 - from the first inner layer
+	 * 2 - from the second inner layer
+	 * ....
+	 */
+	__le32 count; /* the proto layers must < VIRTCHNL2_MAX_NUM_PROTO_HDRS */
+	struct virtchnl2_proto_hdr proto_hdr[VIRTCHNL2_MAX_NUM_PROTO_HDRS];
+};
+
+VIRTCHNL_CHECK_STRUCT_LEN(2312, virtchnl2_proto_hdrs);
+
+struct virtchnl2_rss_cfg {
+	struct virtchnl2_proto_hdrs proto_hdrs;
+
+	/* see VIRTCHNL2_RSS_ALGORITHM definitions */
+	__le32 rss_algorithm;
+	u8 reserved[128];
+};
+
+VIRTCHNL_CHECK_STRUCT_LEN(2444, virtchnl2_rss_cfg);
+
+/* VIRTCHNL2_OP_GET_RSS_KEY
+ * PF sends this message to get RSS key. Only supported if both PF and CP
+ * drivers set the VIRTCHNL2_CAP_RSS bit during configuration negotiation. Uses
+ * the virtchnl2_rss_key structure
+ */
+
+/* VIRTCHNL2_OP_GET_RSS_HASH
+ * VIRTCHNL2_OP_SET_RSS_HASH
+ * PF sends these messages to get and set the hash filter enable bits for RSS.
+ * By default, the CP sets these to all possible traffic types that the
+ * hardware supports. The PF can query this value if it wants to change the
+ * traffic types that are hashed by the hardware.
+ * Only supported if both PF and CP drivers set the VIRTCHNL2_CAP_RSS bit
+ * during configuration negotiation.
+ */
+struct virtchnl2_rss_hash {
+	/* Packet Type Groups bitmap */
+	__le64 ptype_groups;
+	__le32 vport_id;
+	u8 reserved[4];
+};
+
+VIRTCHNL_CHECK_STRUCT_LEN(16, virtchnl2_rss_hash);
+
+/* VIRTCHNL2_OP_SET_SRIOV_VFS
+ * This message is used to set number of SRIOV VFs to be created. The actual
+ * allocation of resources for the VFs in terms of vport, queues and interrupts
+ * is done by CP. When this call completes, the APF driver calls
+ * pci_enable_sriov to let the OS instantiate the SRIOV PCIE devices.
+ * The number of VFs set to 0 will destroy all the VFs of this function.
+ */
+
+struct virtchnl2_sriov_vfs_info {
+	__le16 num_vfs;
+	__le16 pad;
+};
+
+VIRTCHNL_CHECK_STRUCT_LEN(4, virtchnl2_sriov_vfs_info);
+
+/* Based on the descriptor type the PF supports, CP fills ptype_id_10 or
+ * ptype_id_8 for flex and base descriptor respectively. If ptype_id_10 value
+ * is set to 0xFFFF, PF should consider this ptype as dummy one and it is the
+ * last ptype.
+ */
+struct virtchnl2_ptype {
+	__le16 ptype_id_10;
+	u8 ptype_id_8;
+	/* number of protocol ids the packet supports, maximum of 32
+	 * protocol ids are supported
+	 */
+	u8 proto_id_count;
+	__le16 pad;
+	/* proto_id_count decides the allocation of protocol id array */
+	/* see VIRTCHNL2_PROTO_HDR_TYPE */
+	__le16 proto_id[1];
+};
+
+VIRTCHNL_CHECK_STRUCT_LEN(8, virtchnl2_ptype);
+
+/* VIRTCHNL2_OP_GET_PTYPE_INFO
+ * PF sends this message to CP to get all supported packet types. It does by
+ * filling in start_ptype_id and num_ptypes. Depending on descriptor type the
+ * PF supports, it sets num_ptypes to 1024 (10-bit ptype) for flex descriptor
+ * and 256 (8-bit ptype) for base descriptor support. CP responds back to PF by
+ * populating start_ptype_id, num_ptypes and array of ptypes. If all ptypes
+ * doesn't fit into one mailbox buffer, CP splits ptype info into multiple
+ * messages, where each message will have the start ptype id, number of ptypes
+ * sent in that message and the ptype array itself. When CP is done updating
+ * all ptype information it extracted from the package (number of ptypes
+ * extracted might be less than what PF expects), it will append a dummy ptype
+ * (which has 'ptype_id_10' of 'struct virtchnl2_ptype' as 0xFFFF) to the ptype
+ * array. PF is expected to receive multiple VIRTCHNL2_OP_GET_PTYPE_INFO
+ * messages.
+ */
+struct virtchnl2_get_ptype_info {
+	__le16 start_ptype_id;
+	__le16 num_ptypes;
+	__le32 pad;
+	struct virtchnl2_ptype ptype[1];
+};
+
+VIRTCHNL_CHECK_STRUCT_LEN(16, virtchnl2_get_ptype_info);
+
+/* VIRTCHNL2_OP_GET_STATS
+ * PF/VF sends this message to CP to get the update stats by specifying the
+ * vport_id. CP responds with stats in struct virtchnl2_vport_stats.
+ */
+struct virtchnl2_vport_stats {
+	__le32 vport_id;
+	u8 pad[4];
+
+	__le64 rx_bytes;		/* received bytes */
+	__le64 rx_unicast;		/* received unicast pkts */
+	__le64 rx_multicast;		/* received multicast pkts */
+	__le64 rx_broadcast;		/* received broadcast pkts */
+	__le64 rx_discards;
+	__le64 rx_errors;
+	__le64 rx_unknown_protocol;
+	__le64 tx_bytes;		/* transmitted bytes */
+	__le64 tx_unicast;		/* transmitted unicast pkts */
+	__le64 tx_multicast;		/* transmitted multicast pkts */
+	__le64 tx_broadcast;		/* transmitted broadcast pkts */
+	__le64 tx_discards;
+	__le64 tx_errors;
+	u8 reserved[16];
+};
+
+VIRTCHNL_CHECK_STRUCT_LEN(128, virtchnl2_vport_stats);
+
+struct virtchnl2_event {
+	/* see VIRTCHNL2_EVENT_CODES definitions */
+	__le32 event;
+	/* link_speed provided in Mbps */
+	__le32 link_speed;
+	__le32 vport_id;
+	u8 link_status;
+	u8 pad[3];
+};
+
+VIRTCHNL_CHECK_STRUCT_LEN(16, virtchnl2_event);
+
+/* VIRTCHNL2_OP_GET_RSS_KEY
+ * VIRTCHNL2_OP_SET_RSS_KEY
+ * PF/VF sends this message to get or set RSS key. Only supported if both
+ * PF/VF and CP drivers set the VIRTCHNL2_CAP_RSS bit during configuration
+ * negotiation. Uses the virtchnl2_rss_key structure
+ */
+struct virtchnl2_rss_key {
+	__le32 vport_id;
+	__le16 key_len;
+	u8 pad;
+	u8 key[1];         /* RSS hash key, packed bytes */
+};
+
+VIRTCHNL_CHECK_STRUCT_LEN(8, virtchnl2_rss_key);
+
+/* structure to specify a chunk of contiguous queues */
+struct virtchnl2_queue_chunk {
+	/* see VIRTCHNL2_QUEUE_TYPE definitions */
+	__le32 type;
+	__le32 start_queue_id;
+	__le32 num_queues;
+	u8 reserved[4];
+};
+
+VIRTCHNL_CHECK_STRUCT_LEN(16, virtchnl2_queue_chunk);
+
+/* structure to specify several chunks of contiguous queues */
+struct virtchnl2_queue_chunks {
+	__le16 num_chunks;
+	u8 reserved[6];
+	struct virtchnl2_queue_chunk chunks[1];
+};
+
+VIRTCHNL_CHECK_STRUCT_LEN(24, virtchnl2_queue_chunks);
+
+/* VIRTCHNL2_OP_ENABLE_QUEUES
+ * VIRTCHNL2_OP_DISABLE_QUEUES
+ * VIRTCHNL2_OP_DEL_QUEUES
+ *
+ * PF sends these messages to enable, disable or delete queues specified in
+ * chunks. PF sends virtchnl2_del_ena_dis_queues struct to specify the queues
+ * to be enabled/disabled/deleted. Also applicable to single queue receive or
+ * transmit. CP performs requested action and returns status.
+ */
+struct virtchnl2_del_ena_dis_queues {
+	__le32 vport_id;
+	u8 reserved[4];
+	struct virtchnl2_queue_chunks chunks;
+};
+
+VIRTCHNL_CHECK_STRUCT_LEN(32, virtchnl2_del_ena_dis_queues);
+
+/* Queue to vector mapping */
+struct virtchnl2_queue_vector {
+	__le32 queue_id;
+	__le16 vector_id;
+	u8 pad[2];
+
+	/* see VIRTCHNL2_ITR_IDX definitions */
+	__le32 itr_idx;
+
+	/* see VIRTCHNL2_QUEUE_TYPE definitions */
+	__le32 queue_type;
+	u8 reserved[8];
+};
+
+VIRTCHNL_CHECK_STRUCT_LEN(24, virtchnl2_queue_vector);
+
+/* VIRTCHNL2_OP_MAP_QUEUE_VECTOR
+ * VIRTCHNL2_OP_UNMAP_QUEUE_VECTOR
+ *
+ * PF sends this message to map or unmap queues to vectors and interrupt
+ * throttling rate index registers. External data buffer contains
+ * virtchnl2_queue_vector_maps structure that contains num_qv_maps of
+ * virtchnl2_queue_vector structures. CP maps the requested queue vector maps
+ * after validating the queue and vector ids and returns a status code.
+ */
+struct virtchnl2_queue_vector_maps {
+	__le32 vport_id;
+	__le16 num_qv_maps;
+	u8 pad[10];
+	struct virtchnl2_queue_vector qv_maps[1];
+};
+
+VIRTCHNL_CHECK_STRUCT_LEN(40, virtchnl2_queue_vector_maps);
+
+static inline const char *virtchnl2_op_str(enum virtchnl_ops v_opcode)
+{
+	switch (v_opcode) {
+	case VIRTCHNL2_OP_GET_CAPS:
+		return "VIRTCHNL2_OP_GET_CAPS";
+	case VIRTCHNL2_OP_CREATE_VPORT:
+		return "VIRTCHNL2_OP_CREATE_VPORT";
+	case VIRTCHNL2_OP_DESTROY_VPORT:
+		return "VIRTCHNL2_OP_DESTROY_VPORT";
+	case VIRTCHNL2_OP_ENABLE_VPORT:
+		return "VIRTCHNL2_OP_ENABLE_VPORT";
+	case VIRTCHNL2_OP_DISABLE_VPORT:
+		return "VIRTCHNL2_OP_DISABLE_VPORT";
+	case VIRTCHNL2_OP_CONFIG_TX_QUEUES:
+		return "VIRTCHNL2_OP_CONFIG_TX_QUEUES";
+	case VIRTCHNL2_OP_CONFIG_RX_QUEUES:
+		return "VIRTCHNL2_OP_CONFIG_RX_QUEUES";
+	case VIRTCHNL2_OP_ENABLE_QUEUES:
+		return "VIRTCHNL2_OP_ENABLE_QUEUES";
+	case VIRTCHNL2_OP_DISABLE_QUEUES:
+		return "VIRTCHNL2_OP_DISABLE_QUEUES";
+	case VIRTCHNL2_OP_ADD_QUEUES:
+		return "VIRTCHNL2_OP_ADD_QUEUES";
+	case VIRTCHNL2_OP_DEL_QUEUES:
+		return "VIRTCHNL2_OP_DEL_QUEUES";
+	case VIRTCHNL2_OP_MAP_QUEUE_VECTOR:
+		return "VIRTCHNL2_OP_MAP_QUEUE_VECTOR";
+	case VIRTCHNL2_OP_UNMAP_QUEUE_VECTOR:
+		return "VIRTCHNL2_OP_UNMAP_QUEUE_VECTOR";
+	case VIRTCHNL2_OP_GET_RSS_KEY:
+		return "VIRTCHNL2_OP_GET_RSS_KEY";
+	case VIRTCHNL2_OP_SET_RSS_KEY:
+		return "VIRTCHNL2_OP_SET_RSS_KEY";
+	case VIRTCHNL2_OP_GET_RSS_LUT:
+		return "VIRTCHNL2_OP_GET_RSS_LUT";
+	case VIRTCHNL2_OP_SET_RSS_LUT:
+		return "VIRTCHNL2_OP_SET_RSS_LUT";
+	case VIRTCHNL2_OP_GET_RSS_HASH:
+		return "VIRTCHNL2_OP_GET_RSS_HASH";
+	case VIRTCHNL2_OP_SET_RSS_HASH:
+		return "VIRTCHNL2_OP_SET_RSS_HASH";
+	case VIRTCHNL2_OP_SET_SRIOV_VFS:
+		return "VIRTCHNL2_OP_SET_SRIOV_VFS";
+	case VIRTCHNL2_OP_ALLOC_VECTORS:
+		return "VIRTCHNL2_OP_ALLOC_VECTORS";
+	case VIRTCHNL2_OP_DEALLOC_VECTORS:
+		return "VIRTCHNL2_OP_DEALLOC_VECTORS";
+	case VIRTCHNL2_OP_GET_PTYPE_INFO:
+		return "VIRTCHNL2_OP_GET_PTYPE_INFO";
+	case VIRTCHNL2_OP_GET_STATS:
+		return "VIRTCHNL2_OP_GET_STATS";
+	case VIRTCHNL2_OP_EVENT:
+		return "VIRTCHNL2_OP_EVENT";
+	case VIRTCHNL2_OP_RESET_VF:
+		return "VIRTCHNL2_OP_RESET_VF";
+	default:
+		return virtchnl_op_str(v_opcode);
+	}
+}
+
+/**
+ * virtchnl2_vc_validate_vf_msg
+ * @ver: Virtchnl version info
+ * @v_opcode: Opcode for the message
+ * @msg: pointer to the msg buffer
+ * @msglen: msg length
+ *
+ * validate msg format against struct for each opcode
+ */
+static inline int
+virtchnl2_vc_validate_vf_msg(struct virtchnl_version_info *ver, u32 v_opcode,
+			     u8 *msg, __le16 msglen)
+{
+	bool err_msg_format = false;
+	__le32 valid_len = 0;
+
+	/* Validate message length. */
+	switch (v_opcode) {
+	case VIRTCHNL_OP_VERSION:
+		valid_len = sizeof(struct virtchnl_version_info);
+		break;
+	case VIRTCHNL2_OP_GET_CAPS:
+		valid_len = sizeof(struct virtchnl2_get_capabilities);
+		break;
+	case VIRTCHNL2_OP_CREATE_VPORT:
+		valid_len = sizeof(struct virtchnl2_create_vport);
+		if (msglen >= valid_len) {
+			struct virtchnl2_create_vport *cvport =
+				(struct virtchnl2_create_vport *)msg;
+
+			if (cvport->chunks.num_chunks == 0) {
+				/* zero chunks is allowed as input */
+				break;
+			}
+
+			valid_len += (cvport->chunks.num_chunks - 1) *
+				      sizeof(struct virtchnl2_queue_reg_chunk);
+		}
+		break;
+	case VIRTCHNL2_OP_DESTROY_VPORT:
+	case VIRTCHNL2_OP_ENABLE_VPORT:
+	case VIRTCHNL2_OP_DISABLE_VPORT:
+		valid_len = sizeof(struct virtchnl2_vport);
+		break;
+	case VIRTCHNL2_OP_CONFIG_TX_QUEUES:
+		valid_len = sizeof(struct virtchnl2_config_tx_queues);
+		if (msglen >= valid_len) {
+			struct virtchnl2_config_tx_queues *ctq =
+				(struct virtchnl2_config_tx_queues *)msg;
+			if (ctq->num_qinfo == 0) {
+				err_msg_format = true;
+				break;
+			}
+			valid_len += (ctq->num_qinfo - 1) *
+				     sizeof(struct virtchnl2_txq_info);
+		}
+		break;
+	case VIRTCHNL2_OP_CONFIG_RX_QUEUES:
+		valid_len = sizeof(struct virtchnl2_config_rx_queues);
+		if (msglen >= valid_len) {
+			struct virtchnl2_config_rx_queues *crq =
+				(struct virtchnl2_config_rx_queues *)msg;
+			if (crq->num_qinfo == 0) {
+				err_msg_format = true;
+				break;
+			}
+			valid_len += (crq->num_qinfo - 1) *
+				     sizeof(struct virtchnl2_rxq_info);
+		}
+		break;
+	case VIRTCHNL2_OP_ADD_QUEUES:
+		valid_len = sizeof(struct virtchnl2_add_queues);
+		if (msglen >= valid_len) {
+			struct virtchnl2_add_queues *add_q =
+				(struct virtchnl2_add_queues *)msg;
+
+			if (add_q->chunks.num_chunks == 0) {
+				/* zero chunks is allowed as input */
+				break;
+			}
+
+			valid_len += (add_q->chunks.num_chunks - 1) *
+				      sizeof(struct virtchnl2_queue_reg_chunk);
+		}
+		break;
+	case VIRTCHNL2_OP_ENABLE_QUEUES:
+	case VIRTCHNL2_OP_DISABLE_QUEUES:
+	case VIRTCHNL2_OP_DEL_QUEUES:
+		valid_len = sizeof(struct virtchnl2_del_ena_dis_queues);
+		if (msglen >= valid_len) {
+			struct virtchnl2_del_ena_dis_queues *qs =
+				(struct virtchnl2_del_ena_dis_queues *)msg;
+			if (qs->chunks.num_chunks == 0 ||
+			    qs->chunks.num_chunks > VIRTCHNL2_OP_DEL_ENABLE_DISABLE_QUEUES_MAX) {
+				err_msg_format = true;
+				break;
+			}
+			valid_len += (qs->chunks.num_chunks - 1) *
+				      sizeof(struct virtchnl2_queue_chunk);
+		}
+		break;
+	case VIRTCHNL2_OP_MAP_QUEUE_VECTOR:
+	case VIRTCHNL2_OP_UNMAP_QUEUE_VECTOR:
+		valid_len = sizeof(struct virtchnl2_queue_vector_maps);
+		if (msglen >= valid_len) {
+			struct virtchnl2_queue_vector_maps *v_qp =
+				(struct virtchnl2_queue_vector_maps *)msg;
+			if (v_qp->num_qv_maps == 0 ||
+			    v_qp->num_qv_maps > VIRTCHNL2_OP_MAP_UNMAP_QUEUE_VECTOR_MAX) {
+				err_msg_format = true;
+				break;
+			}
+			valid_len += (v_qp->num_qv_maps - 1) *
+				      sizeof(struct virtchnl2_queue_vector);
+		}
+		break;
+	case VIRTCHNL2_OP_ALLOC_VECTORS:
+		valid_len = sizeof(struct virtchnl2_alloc_vectors);
+		if (msglen >= valid_len) {
+			struct virtchnl2_alloc_vectors *v_av =
+				(struct virtchnl2_alloc_vectors *)msg;
+
+			if (v_av->vchunks.num_vchunks == 0) {
+				/* zero chunks is allowed as input */
+				break;
+			}
+
+			valid_len += (v_av->vchunks.num_vchunks - 1) *
+				      sizeof(struct virtchnl2_vector_chunk);
+		}
+		break;
+	case VIRTCHNL2_OP_DEALLOC_VECTORS:
+		valid_len = sizeof(struct virtchnl2_vector_chunks);
+		if (msglen >= valid_len) {
+			struct virtchnl2_vector_chunks *v_chunks =
+				(struct virtchnl2_vector_chunks *)msg;
+			if (v_chunks->num_vchunks == 0) {
+				err_msg_format = true;
+				break;
+			}
+			valid_len += (v_chunks->num_vchunks - 1) *
+				      sizeof(struct virtchnl2_vector_chunk);
+		}
+		break;
+	case VIRTCHNL2_OP_GET_RSS_KEY:
+	case VIRTCHNL2_OP_SET_RSS_KEY:
+		valid_len = sizeof(struct virtchnl2_rss_key);
+		if (msglen >= valid_len) {
+			struct virtchnl2_rss_key *vrk =
+				(struct virtchnl2_rss_key *)msg;
+
+			if (vrk->key_len == 0) {
+				/* zero length is allowed as input */
+				break;
+			}
+
+			valid_len += vrk->key_len - 1;
+		}
+		break;
+	case VIRTCHNL2_OP_GET_RSS_LUT:
+	case VIRTCHNL2_OP_SET_RSS_LUT:
+		valid_len = sizeof(struct virtchnl2_rss_lut);
+		if (msglen >= valid_len) {
+			struct virtchnl2_rss_lut *vrl =
+				(struct virtchnl2_rss_lut *)msg;
+
+			if (vrl->lut_entries == 0) {
+				/* zero entries is allowed as input */
+				break;
+			}
+
+			valid_len += (vrl->lut_entries - 1) * sizeof(__le16);
+		}
+		break;
+	case VIRTCHNL2_OP_GET_RSS_HASH:
+	case VIRTCHNL2_OP_SET_RSS_HASH:
+		valid_len = sizeof(struct virtchnl2_rss_hash);
+		break;
+	case VIRTCHNL2_OP_SET_SRIOV_VFS:
+		valid_len = sizeof(struct virtchnl2_sriov_vfs_info);
+		break;
+	case VIRTCHNL2_OP_GET_PTYPE_INFO:
+		valid_len = sizeof(struct virtchnl2_get_ptype_info);
+		break;
+	case VIRTCHNL2_OP_GET_STATS:
+		valid_len = sizeof(struct virtchnl2_vport_stats);
+		break;
+	case VIRTCHNL2_OP_RESET_VF:
+		break;
+	case VIRTCHNL2_OP_EVENT:
+		return VIRTCHNL_STATUS_ERR_PARAM;
+	default:
+		return virtchnl_vc_validate_vf_msg(ver, v_opcode, msg, msglen);
+	}
+	/* few more checks */
+	if (err_msg_format || valid_len != msglen)
+		return VIRTCHNL_STATUS_ERR_OPCODE_MISMATCH;
+
+	return 0;
+}
+
+#endif /* _VIRTCHNL_2_H_ */
diff --git a/include/linux/avf/virtchnl_lan_desc.h b/include/linux/avf/virtchnl_lan_desc.h
new file mode 100644
index 000000000000..0cd4a9e49395
--- /dev/null
+++ b/include/linux/avf/virtchnl_lan_desc.h
@@ -0,0 +1,603 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (c) 2020, Intel Corporation. */
+
+/*
+ * Copyright (C) 2019 Intel Corporation
+ *
+ * For licensing information, see the file 'LICENSE' in the root folder
+ */
+#ifndef _VIRTCHNL_LAN_DESC_H_
+#define _VIRTCHNL_LAN_DESC_H_
+
+/* VIRTCHNL2_TX_DESC_IDS
+ * Transmit descriptor ID flags
+ */
+#define VIRTCHNL2_TXDID_DATA				BIT(0)
+#define VIRTCHNL2_TXDID_CTX				BIT(1)
+#define VIRTCHNL2_TXDID_REINJECT_CTX			BIT(2)
+#define VIRTCHNL2_TXDID_FLEX_DATA			BIT(3)
+#define VIRTCHNL2_TXDID_FLEX_CTX			BIT(4)
+#define VIRTCHNL2_TXDID_FLEX_TSO_CTX			BIT(5)
+#define VIRTCHNL2_TXDID_FLEX_TSYN_L2TAG1		BIT(6)
+#define VIRTCHNL2_TXDID_FLEX_L2TAG1_L2TAG2		BIT(7)
+#define VIRTCHNL2_TXDID_FLEX_TSO_L2TAG2_PARSTAG_CTX	BIT(8)
+#define VIRTCHNL2_TXDID_FLEX_HOSTSPLIT_SA_TSO_CTX	BIT(9)
+#define VIRTCHNL2_TXDID_FLEX_HOSTSPLIT_SA_CTX		BIT(10)
+#define VIRTCHNL2_TXDID_FLEX_L2TAG2_CTX			BIT(11)
+#define VIRTCHNL2_TXDID_FLEX_FLOW_SCHED			BIT(12)
+#define VIRTCHNL2_TXDID_FLEX_HOSTSPLIT_TSO_CTX		BIT(13)
+#define VIRTCHNL2_TXDID_FLEX_HOSTSPLIT_CTX		BIT(14)
+#define VIRTCHNL2_TXDID_DESC_DONE			BIT(15)
+
+/* VIRTCHNL2_RX_DESC_IDS
+ * Receive descriptor IDs (range from 0 to 63)
+ */
+#define VIRTCHNL2_RXDID_0_16B_BASE			0
+/* 32B_BASE and FLEX_SPLITQ share desc ids as default descriptors
+ * because they can be differentiated based on queue model; e.g. single
+ * queue model can only use 32B_BASE and split queue model can only use
+ * FLEX_SPLITQ.  Having these as 1 allows them to be used as default
+ * descriptors without negotiation.
+ */
+#define VIRTCHNL2_RXDID_1_32B_BASE			1
+#define VIRTCHNL2_RXDID_1_FLEX_SPLITQ			1
+#define VIRTCHNL2_RXDID_2_FLEX_SQ_NIC			2
+#define VIRTCHNL2_RXDID_3_FLEX_SQ_SW			3
+#define VIRTCHNL2_RXDID_4_FLEX_SQ_NIC_VEB		4
+#define VIRTCHNL2_RXDID_5_FLEX_SQ_NIC_ACL		5
+#define VIRTCHNL2_RXDID_6_FLEX_SQ_NIC_2			6
+#define VIRTCHNL2_RXDID_7_HW_RSVD			7
+/* 9 through 15 are reserved */
+#define VIRTCHNL2_RXDID_16_COMMS_GENERIC		16
+#define VIRTCHNL2_RXDID_17_COMMS_AUX_VLAN		17
+#define VIRTCHNL2_RXDID_18_COMMS_AUX_IPV4		18
+#define VIRTCHNL2_RXDID_19_COMMS_AUX_IPV6		19
+#define VIRTCHNL2_RXDID_20_COMMS_AUX_FLOW		20
+#define VIRTCHNL2_RXDID_21_COMMS_AUX_TCP		21
+/* 22 through 63 are reserved */
+
+/* VIRTCHNL2_RX_DESC_ID_BITMASKS
+ * Receive descriptor ID bitmasks
+ */
+#define VIRTCHNL2_RXDID_0_16B_BASE_M		BIT(VIRTCHNL2_RXDID_0_16B_BASE)
+#define VIRTCHNL2_RXDID_1_32B_BASE_M		BIT(VIRTCHNL2_RXDID_1_32B_BASE)
+#define VIRTCHNL2_RXDID_1_FLEX_SPLITQ_M		BIT(VIRTCHNL2_RXDID_1_FLEX_SPLITQ)
+#define VIRTCHNL2_RXDID_2_FLEX_SQ_NIC_M		BIT(VIRTCHNL2_RXDID_2_FLEX_SQ_NIC)
+#define VIRTCHNL2_RXDID_3_FLEX_SQ_SW_M		BIT(VIRTCHNL2_RXDID_3_FLEX_SQ_SW)
+#define VIRTCHNL2_RXDID_4_FLEX_SQ_NIC_VEB_M	BIT(VIRTCHNL2_RXDID_4_FLEX_SQ_NIC_VEB)
+#define VIRTCHNL2_RXDID_5_FLEX_SQ_NIC_ACL_M	BIT(VIRTCHNL2_RXDID_5_FLEX_SQ_NIC_ACL)
+#define VIRTCHNL2_RXDID_6_FLEX_SQ_NIC_2_M	BIT(VIRTCHNL2_RXDID_6_FLEX_SQ_NIC_2)
+#define VIRTCHNL2_RXDID_7_HW_RSVD_M		BIT(VIRTCHNL2_RXDID_7_HW_RSVD)
+/* 9 through 15 are reserved */
+#define VIRTCHNL2_RXDID_16_COMMS_GENERIC_M	BIT(VIRTCHNL2_RXDID_16_COMMS_GENERIC)
+#define VIRTCHNL2_RXDID_17_COMMS_AUX_VLAN_M	BIT(VIRTCHNL2_RXDID_17_COMMS_AUX_VLAN)
+#define VIRTCHNL2_RXDID_18_COMMS_AUX_IPV4_M	BIT(VIRTCHNL2_RXDID_18_COMMS_AUX_IPV4)
+#define VIRTCHNL2_RXDID_19_COMMS_AUX_IPV6_M	BIT(VIRTCHNL2_RXDID_19_COMMS_AUX_IPV6)
+#define VIRTCHNL2_RXDID_20_COMMS_AUX_FLOW_M	BIT(VIRTCHNL2_RXDID_20_COMMS_AUX_FLOW)
+#define VIRTCHNL2_RXDID_21_COMMS_AUX_TCP_M	BIT(VIRTCHNL2_RXDID_21_COMMS_AUX_TCP)
+/* 22 through 63 are reserved */
+
+/* Rx */
+/* For splitq virtchnl2_rx_flex_desc_adv desc members */
+#define VIRTCHNL2_RX_FLEX_DESC_ADV_RXDID_S		0
+#define VIRTCHNL2_RX_FLEX_DESC_ADV_RXDID_M		\
+	MAKEMASK(0xFUL, VIRTCHNL2_RX_FLEX_DESC_ADV_RXDID_S)
+#define VIRTCHNL2_RX_FLEX_DESC_ADV_PTYPE_S		0
+#define VIRTCHNL2_RX_FLEX_DESC_ADV_PTYPE_M		\
+	MAKEMASK(0x3FFUL, VIRTCHNL2_RX_FLEX_DESC_ADV_PTYPE_S)
+#define VIRTCHNL2_RX_FLEX_DESC_ADV_UMBCAST_S		10
+#define VIRTCHNL2_RX_FLEX_DESC_ADV_UMBCAST_M		\
+	MAKEMASK(0x3UL, VIRTCHNL2_RX_FLEX_DESC_ADV_UMBCAST_S)
+#define VIRTCHNL2_RX_FLEX_DESC_ADV_FF0_S		12
+#define VIRTCHNL2_RX_FLEX_DESC_ADV_FF0_M			\
+	MAKEMASK(0xFUL, VIRTCHNL2_RX_FLEX_DESC_ADV_FF0_S)
+#define VIRTCHNL2_RX_FLEX_DESC_ADV_LEN_PBUF_S		0
+#define VIRTCHNL2_RX_FLEX_DESC_ADV_LEN_PBUF_M	\
+	MAKEMASK(0x3FFFUL, VIRTCHNL2_RX_FLEX_DESC_ADV_LEN_PBUF_S)
+#define VIRTCHNL2_RX_FLEX_DESC_ADV_GEN_S		14
+#define VIRTCHNL2_RX_FLEX_DESC_ADV_GEN_M			\
+	BIT_ULL(VIRTCHNL2_RX_FLEX_DESC_ADV_GEN_S)
+#define VIRTCHNL2_RX_FLEX_DESC_ADV_BUFQ_ID_S		15
+#define VIRTCHNL2_RX_FLEX_DESC_ADV_BUFQ_ID_M		\
+	BIT_ULL(VIRTCHNL2_RX_FLEX_DESC_ADV_BUFQ_ID_S)
+#define VIRTCHNL2_RX_FLEX_DESC_ADV_LEN_HDR_S		0
+#define VIRTCHNL2_RX_FLEX_DESC_ADV_LEN_HDR_M		\
+	MAKEMASK(0x3FFUL, VIRTCHNL2_RX_FLEX_DESC_ADV_LEN_HDR_S)
+#define VIRTCHNL2_RX_FLEX_DESC_ADV_RSC_S		10
+#define VIRTCHNL2_RX_FLEX_DESC_ADV_RSC_M			\
+	BIT_ULL(VIRTCHNL2_RX_FLEX_DESC_ADV_RSC_S)
+#define VIRTCHNL2_RX_FLEX_DESC_ADV_SPH_S		11
+#define VIRTCHNL2_RX_FLEX_DESC_ADV_SPH_M			\
+	BIT_ULL(VIRTCHNL2_RX_FLEX_DESC_ADV_SPH_S)
+#define VIRTCHNL2_RX_FLEX_DESC_ADV_MISS_S		12
+#define VIRTCHNL2_RX_FLEX_DESC_ADV_MISS_M		\
+	BIT_ULL(VIRTCHNL2_RX_FLEX_DESC_ADV_MISS_S)
+#define VIRTCHNL2_RX_FLEX_DESC_ADV_FF1_S		13
+#define VIRTCHNL2_RX_FLEX_DESC_ADV_FF1_M			\
+	MAKEMASK(0x7UL, VIRTCHNL2_RX_FLEX_DESC_ADV_FF1_M)
+
+/* VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS_ERROR_0_QW1_BITS
+ * for splitq virtchnl2_rx_flex_desc_adv
+ * Note: These are predefined bit offsets
+ */
+#define VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_DD_S			0
+#define VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_EOF_S		1
+#define VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_HBO_S		2
+#define VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_L3L4P_S		3
+#define VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_XSUM_IPE_S		4
+#define VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_XSUM_L4E_S		5
+#define VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_XSUM_EIPE_S		6
+#define VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_XSUM_EUDPE_S		7
+
+/* VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS_ERROR_0_QW0_BITS
+ * for splitq virtchnl2_rx_flex_desc_adv
+ * Note: These are predefined bit offsets
+ */
+#define VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_LPBK_S		0
+#define VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_IPV6EXADD_S		1
+#define VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_RXE_S		2
+#define VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_CRCP_S		3
+#define VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_RSS_VALID_S		4
+#define VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_L2TAG1P_S		5
+#define VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_XTRMD0_VALID_S	6
+#define VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_XTRMD1_VALID_S	7
+#define VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_LAST			8 /* this entry must be last!!! */
+
+/* VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS_ERROR_1_BITS
+ * for splitq virtchnl2_rx_flex_desc_adv
+ * Note: These are predefined bit offsets
+ */
+#define VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS1_RSVD_S		0 /* 2 bits */
+#define VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS1_ATRAEFAIL_S		2
+#define VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS1_L2TAG2P_S		3
+#define VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS1_XTRMD2_VALID_S	4
+#define VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS1_XTRMD3_VALID_S	5
+#define VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS1_XTRMD4_VALID_S	6
+#define VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS1_XTRMD5_VALID_S	7
+#define VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS1_LAST			8 /* this entry must be last!!! */
+
+/* for singleq (flex) virtchnl2_rx_flex_desc fields */
+/* for virtchnl2_rx_flex_desc.ptype_flex_flags0 member */
+#define VIRTCHNL2_RX_FLEX_DESC_PTYPE_S			0
+#define VIRTCHNL2_RX_FLEX_DESC_PTYPE_M			\
+	MAKEMASK(0x3FFUL, VIRTCHNL2_RX_FLEX_DESC_PTYPE_S) /* 10 bits */
+
+/* for virtchnl2_rx_flex_desc.pkt_length member */
+#define VIRTCHNL2_RX_FLEX_DESC_PKT_LEN_S			0
+#define VIRTCHNL2_RX_FLEX_DESC_PKT_LEN_M			\
+	MAKEMASK(0x3FFFUL, VIRTCHNL2_RX_FLEX_DESC_PKT_LEN_S) /* 14 bits */
+
+/* VIRTCHNL2_RX_FLEX_DESC_STATUS_ERROR_0_BITS
+ * for singleq (flex) virtchnl2_rx_flex_desc
+ * Note: These are predefined bit offsets
+ */
+#define VIRTCHNL2_RX_FLEX_DESC_STATUS0_DD_S			0
+#define VIRTCHNL2_RX_FLEX_DESC_STATUS0_EOF_S			1
+#define VIRTCHNL2_RX_FLEX_DESC_STATUS0_HBO_S			2
+#define VIRTCHNL2_RX_FLEX_DESC_STATUS0_L3L4P_S			3
+#define VIRTCHNL2_RX_FLEX_DESC_STATUS0_XSUM_IPE_S		4
+#define VIRTCHNL2_RX_FLEX_DESC_STATUS0_XSUM_L4E_S		5
+#define VIRTCHNL2_RX_FLEX_DESC_STATUS0_XSUM_EIPE_S		6
+#define VIRTCHNL2_RX_FLEX_DESC_STATUS0_XSUM_EUDPE_S		7
+#define VIRTCHNL2_RX_FLEX_DESC_STATUS0_LPBK_S			8
+#define VIRTCHNL2_RX_FLEX_DESC_STATUS0_IPV6EXADD_S		9
+#define VIRTCHNL2_RX_FLEX_DESC_STATUS0_RXE_S			10
+#define VIRTCHNL2_RX_FLEX_DESC_STATUS0_CRCP_S			11
+#define VIRTCHNL2_RX_FLEX_DESC_STATUS0_RSS_VALID_S		12
+#define VIRTCHNL2_RX_FLEX_DESC_STATUS0_L2TAG1P_S		13
+#define VIRTCHNL2_RX_FLEX_DESC_STATUS0_XTRMD0_VALID_S		14
+#define VIRTCHNL2_RX_FLEX_DESC_STATUS0_XTRMD1_VALID_S		15
+#define VIRTCHNL2_RX_FLEX_DESC_STATUS0_LAST			16 /* this entry must be last!!! */
+
+/* VIRTCHNL2_RX_FLEX_DESC_STATUS_ERROR_1_BITS
+ * for singleq (flex) virtchnl2_rx_flex_desc
+ * Note: These are predefined bit offsets
+ */
+#define VIRTCHNL2_RX_FLEX_DESC_STATUS1_CPM_S			0 /* 4 bits */
+#define VIRTCHNL2_RX_FLEX_DESC_STATUS1_NAT_S			4
+#define VIRTCHNL2_RX_FLEX_DESC_STATUS1_CRYPTO_S			5
+/* [10:6] reserved */
+#define VIRTCHNL2_RX_FLEX_DESC_STATUS1_L2TAG2P_S		11
+#define VIRTCHNL2_RX_FLEX_DESC_STATUS1_XTRMD2_VALID_S		12
+#define VIRTCHNL2_RX_FLEX_DESC_STATUS1_XTRMD3_VALID_S		13
+#define VIRTCHNL2_RX_FLEX_DESC_STATUS1_XTRMD4_VALID_S		14
+#define VIRTCHNL2_RX_FLEX_DESC_STATUS1_XTRMD5_VALID_S		15
+#define VIRTCHNL2_RX_FLEX_DESC_STATUS1_LAST			16 /* this entry must be last!!! */
+
+/* For singleq (non flex) virtchnl2_singleq_base_rx_desc legacy desc members */
+#define VIRTCHNL2_RX_BASE_DESC_QW1_LEN_SPH_S	63
+#define VIRTCHNL2_RX_BASE_DESC_QW1_LEN_SPH_M	\
+	BIT_ULL(VIRTCHNL2_RX_BASE_DESC_QW1_LEN_SPH_S)
+#define VIRTCHNL2_RX_BASE_DESC_QW1_LEN_HBUF_S	52
+#define VIRTCHNL2_RX_BASE_DESC_QW1_LEN_HBUF_M	\
+	MAKEMASK(0x7FFULL, VIRTCHNL2_RX_BASE_DESC_QW1_LEN_HBUF_S)
+#define VIRTCHNL2_RX_BASE_DESC_QW1_LEN_PBUF_S	38
+#define VIRTCHNL2_RX_BASE_DESC_QW1_LEN_PBUF_M	\
+	MAKEMASK(0x3FFFULL, VIRTCHNL2_RX_BASE_DESC_QW1_LEN_PBUF_S)
+#define VIRTCHNL2_RX_BASE_DESC_QW1_PTYPE_S	30
+#define VIRTCHNL2_RX_BASE_DESC_QW1_PTYPE_M	\
+	MAKEMASK(0xFFULL, VIRTCHNL2_RX_BASE_DESC_QW1_PTYPE_S)
+#define VIRTCHNL2_RX_BASE_DESC_QW1_ERROR_S	19
+#define VIRTCHNL2_RX_BASE_DESC_QW1_ERROR_M	\
+	MAKEMASK(0xFFUL, VIRTCHNL2_RX_BASE_DESC_QW1_ERROR_S)
+#define VIRTCHNL2_RX_BASE_DESC_QW1_STATUS_S	0
+#define VIRTCHNL2_RX_BASE_DESC_QW1_STATUS_M	\
+	MAKEMASK(0x7FFFFUL, VIRTCHNL2_RX_BASE_DESC_QW1_STATUS_S)
+
+/* VIRTCHNL2_RX_BASE_DESC_STATUS_BITS
+ * for singleq (base) virtchnl2_rx_base_desc
+ * Note: These are predefined bit offsets
+ */
+#define VIRTCHNL2_RX_BASE_DESC_STATUS_DD_S		0
+#define VIRTCHNL2_RX_BASE_DESC_STATUS_EOF_S		1
+#define VIRTCHNL2_RX_BASE_DESC_STATUS_L2TAG1P_S		2
+#define VIRTCHNL2_RX_BASE_DESC_STATUS_L3L4P_S		3
+#define VIRTCHNL2_RX_BASE_DESC_STATUS_CRCP_S		4
+#define VIRTCHNL2_RX_BASE_DESC_STATUS_RSVD_S		5 /* 3 bits */
+#define VIRTCHNL2_RX_BASE_DESC_STATUS_EXT_UDP_0_S	8
+#define VIRTCHNL2_RX_BASE_DESC_STATUS_UMBCAST_S		9 /* 2 bits */
+#define VIRTCHNL2_RX_BASE_DESC_STATUS_FLM_S		11
+#define VIRTCHNL2_RX_BASE_DESC_STATUS_FLTSTAT_S		12 /* 2 bits */
+#define VIRTCHNL2_RX_BASE_DESC_STATUS_LPBK_S		14
+#define VIRTCHNL2_RX_BASE_DESC_STATUS_IPV6EXADD_S	15
+#define VIRTCHNL2_RX_BASE_DESC_STATUS_RSVD1_S		16 /* 2 bits */
+#define VIRTCHNL2_RX_BASE_DESC_STATUS_INT_UDP_0_S	18
+#define VIRTCHNL2_RX_BASE_DESC_STATUS_LAST		19 /* this entry must be last!!! */
+
+/* VIRTCHNL2_RX_BASE_DESC_EXT_STATUS_BITS
+ * for singleq (base) virtchnl2_rx_base_desc
+ * Note: These are predefined bit offsets
+ */
+#define VIRTCHNL2_RX_BASE_DESC_EXT_STATUS_L2TAG2P_S	0
+
+/* VIRTCHNL2_RX_BASE_DESC_ERROR_BITS
+ * for singleq (base) virtchnl2_rx_base_desc
+ * Note: These are predefined bit offsets
+ */
+#define VIRTCHNL2_RX_BASE_DESC_ERROR_RXE_S		0
+#define VIRTCHNL2_RX_BASE_DESC_ERROR_ATRAEFAIL_S	1
+#define VIRTCHNL2_RX_BASE_DESC_ERROR_HBO_S		2
+#define VIRTCHNL2_RX_BASE_DESC_ERROR_L3L4E_S		3 /* 3 bits */
+#define VIRTCHNL2_RX_BASE_DESC_ERROR_IPE_S		3
+#define VIRTCHNL2_RX_BASE_DESC_ERROR_L4E_S		4
+#define VIRTCHNL2_RX_BASE_DESC_ERROR_EIPE_S		5
+#define VIRTCHNL2_RX_BASE_DESC_ERROR_OVERSIZE_S		6
+#define VIRTCHNL2_RX_BASE_DESC_ERROR_PPRS_S		7
+
+/* VIRTCHNL2_RX_BASE_DESC_FLTSTAT_VALUES
+ * for singleq (base) virtchnl2_rx_base_desc
+ * Note: These are predefined bit offsets
+ */
+#define VIRTCHNL2_RX_BASE_DESC_FLTSTAT_NO_DATA		0
+#define VIRTCHNL2_RX_BASE_DESC_FLTSTAT_FD_ID		1
+#define VIRTCHNL2_RX_BASE_DESC_FLTSTAT_RSV		2
+#define VIRTCHNL2_RX_BASE_DESC_FLTSTAT_RSS_HASH		3
+
+/* Receive Descriptors */
+/* splitq buf
+ * |                                       16|                   0|
+ * ----------------------------------------------------------------
+ * | RSV                                     | Buffer ID          |
+ * ----------------------------------------------------------------
+ * | Rx packet buffer adresss                                     |
+ * ----------------------------------------------------------------
+ * | Rx header buffer adresss                                     |
+ * ----------------------------------------------------------------
+ * | RSV                                                          |
+ * ----------------------------------------------------------------
+ * |                                                             0|
+ */
+struct virtchnl2_splitq_rx_buf_desc {
+	struct {
+		__le16  buf_id; /* Buffer Identifier */
+		__le16  rsvd0;
+		__le32  rsvd1;
+	} qword0;
+	__le64  pkt_addr; /* Packet buffer address */
+	__le64  hdr_addr; /* Header buffer address */
+	__le64  rsvd2;
+}; /* read used with buffer queues*/
+
+/* singleq buf
+ * |                                                             0|
+ * ----------------------------------------------------------------
+ * | Rx packet buffer adresss                                     |
+ * ----------------------------------------------------------------
+ * | Rx header buffer adresss                                     |
+ * ----------------------------------------------------------------
+ * | RSV                                                          |
+ * ----------------------------------------------------------------
+ * | RSV                                                          |
+ * ----------------------------------------------------------------
+ * |                                                             0|
+ */
+struct virtchnl2_singleq_rx_buf_desc {
+	__le64  pkt_addr; /* Packet buffer address */
+	__le64  hdr_addr; /* Header buffer address */
+	__le64  rsvd1;
+	__le64  rsvd2;
+}; /* read used with buffer queues*/
+
+union virtchnl2_rx_buf_desc {
+	struct virtchnl2_singleq_rx_buf_desc		read;
+	struct virtchnl2_splitq_rx_buf_desc		split_rd;
+};
+
+/* (0x00) singleq wb(compl) */
+struct virtchnl2_singleq_base_rx_desc {
+	struct {
+		struct {
+			__le16 mirroring_status;
+			__le16 l2tag1;
+		} lo_dword;
+		union {
+			__le32 rss; /* RSS Hash */
+			__le32 fd_id; /* Flow Director filter id */
+		} hi_dword;
+	} qword0;
+	struct {
+		/* status/error/PTYPE/length */
+		__le64 status_error_ptype_len;
+	} qword1;
+	struct {
+		__le16 ext_status; /* extended status */
+		__le16 rsvd;
+		__le16 l2tag2_1;
+		__le16 l2tag2_2;
+	} qword2;
+	struct {
+		__le32 reserved;
+		__le32 fd_id;
+	} qword3;
+}; /* writeback */
+
+/* (0x01) singleq flex compl */
+struct virtchnl2_rx_flex_desc {
+	/* Qword 0 */
+	u8 rxdid; /* descriptor builder profile id */
+	u8 mir_id_umb_cast; /* mirror=[5:0], umb=[7:6] */
+	__le16 ptype_flex_flags0; /* ptype=[9:0], ff0=[15:10] */
+	__le16 pkt_len; /* [15:14] are reserved */
+	__le16 hdr_len_sph_flex_flags1; /* header=[10:0] */
+					/* sph=[11:11] */
+					/* ff1/ext=[15:12] */
+
+	/* Qword 1 */
+	__le16 status_error0;
+	__le16 l2tag1;
+	__le16 flex_meta0;
+	__le16 flex_meta1;
+
+	/* Qword 2 */
+	__le16 status_error1;
+	u8 flex_flags2;
+	u8 time_stamp_low;
+	__le16 l2tag2_1st;
+	__le16 l2tag2_2nd;
+
+	/* Qword 3 */
+	__le16 flex_meta2;
+	__le16 flex_meta3;
+	union {
+		struct {
+			__le16 flex_meta4;
+			__le16 flex_meta5;
+		} flex;
+		__le32 ts_high;
+	} flex_ts;
+};
+
+/* (0x02) */
+struct virtchnl2_rx_flex_desc_nic {
+	/* Qword 0 */
+	u8 rxdid;
+	u8 mir_id_umb_cast;
+	__le16 ptype_flex_flags0;
+	__le16 pkt_len;
+	__le16 hdr_len_sph_flex_flags1;
+
+	/* Qword 1 */
+	__le16 status_error0;
+	__le16 l2tag1;
+	__le32 rss_hash;
+
+	/* Qword 2 */
+	__le16 status_error1;
+	u8 flexi_flags2;
+	u8 ts_low;
+	__le16 l2tag2_1st;
+	__le16 l2tag2_2nd;
+
+	/* Qword 3 */
+	__le32 flow_id;
+	union {
+		struct {
+			__le16 rsvd;
+			__le16 flow_id_ipv6;
+		} flex;
+		__le32 ts_high;
+	} flex_ts;
+};
+
+/* Rx Flex Descriptor Switch Profile
+ * RxDID Profile Id 3
+ * Flex-field 0: Source Vsi
+ */
+struct virtchnl2_rx_flex_desc_sw {
+	/* Qword 0 */
+	u8 rxdid;
+	u8 mir_id_umb_cast;
+	__le16 ptype_flex_flags0;
+	__le16 pkt_len;
+	__le16 hdr_len_sph_flex_flags1;
+
+	/* Qword 1 */
+	__le16 status_error0;
+	__le16 l2tag1;
+	__le16 src_vsi; /* [10:15] are reserved */
+	__le16 flex_md1_rsvd;
+
+	/* Qword 2 */
+	__le16 status_error1;
+	u8 flex_flags2;
+	u8 ts_low;
+	__le16 l2tag2_1st;
+	__le16 l2tag2_2nd;
+
+	/* Qword 3 */
+	__le32 rsvd; /* flex words 2-3 are reserved */
+	__le32 ts_high;
+};
+
+/* Rx Flex Descriptor NIC Profile
+ * RxDID Profile Id 6
+ * Flex-field 0: RSS hash lower 16-bits
+ * Flex-field 1: RSS hash upper 16-bits
+ * Flex-field 2: Flow Id lower 16-bits
+ * Flex-field 3: Source Vsi
+ * Flex-field 4: reserved, Vlan id taken from L2Tag
+ */
+struct virtchnl2_rx_flex_desc_nic_2 {
+	/* Qword 0 */
+	u8 rxdid;
+	u8 mir_id_umb_cast;
+	__le16 ptype_flex_flags0;
+	__le16 pkt_len;
+	__le16 hdr_len_sph_flex_flags1;
+
+	/* Qword 1 */
+	__le16 status_error0;
+	__le16 l2tag1;
+	__le32 rss_hash;
+
+	/* Qword 2 */
+	__le16 status_error1;
+	u8 flexi_flags2;
+	u8 ts_low;
+	__le16 l2tag2_1st;
+	__le16 l2tag2_2nd;
+
+	/* Qword 3 */
+	__le16 flow_id;
+	__le16 src_vsi;
+	union {
+		struct {
+			__le16 rsvd;
+			__le16 flow_id_ipv6;
+		} flex;
+		__le32 ts_high;
+	} flex_ts;
+};
+
+/* Rx Flex Descriptor Advanced (Split Queue Model)
+ * RxDID Profile Id 7
+ */
+struct virtchnl2_rx_flex_desc_adv {
+	/* Qword 0 */
+	u8 rxdid_ucast; /* profile_id=[3:0] */
+			/* rsvd=[5:4] */
+			/* ucast=[7:6] */
+	u8 status_err0_qw0;
+	__le16 ptype_err_fflags0;	/* ptype=[9:0] */
+					/* ip_hdr_err=[10:10] */
+					/* udp_len_err=[11:11] */
+					/* ff0=[15:12] */
+	__le16 pktlen_gen_bufq_id;	/* plen=[13:0] */
+					/* gen=[14:14]  only in splitq */
+					/* bufq_id=[15:15] only in splitq */
+	__le16 hdrlen_flags;		/* header=[9:0] */
+					/* rsc=[10:10] only in splitq */
+					/* sph=[11:11] only in splitq */
+					/* ext_udp_0=[12:12] */
+					/* int_udp_0=[13:13] */
+					/* trunc_mirr=[14:14] */
+					/* miss_prepend=[15:15] */
+	/* Qword 1 */
+	u8 status_err0_qw1;
+	u8 status_err1;
+	u8 fflags1;
+	u8 ts_low;
+	__le16 fmd0;
+	__le16 fmd1;
+	/* Qword 2 */
+	__le16 fmd2;
+	u8 fflags2;
+	u8 hash3;
+	__le16 fmd3;
+	__le16 fmd4;
+	/* Qword 3 */
+	__le16 fmd5;
+	__le16 fmd6;
+	__le16 fmd7_0;
+	__le16 fmd7_1;
+}; /* writeback */
+
+/* Rx Flex Descriptor Advanced (Split Queue Model) NIC Profile
+ * RxDID Profile Id 8
+ * Flex-field 0: BufferID
+ * Flex-field 1: Raw checksum/L2TAG1/RSC Seg Len (determined by HW)
+ * Flex-field 2: Hash[15:0]
+ * Flex-flags 2: Hash[23:16]
+ * Flex-field 3: L2TAG2
+ * Flex-field 5: L2TAG1
+ * Flex-field 7: Timestamp (upper 32 bits)
+ */
+struct virtchnl2_rx_flex_desc_adv_nic_3 {
+	/* Qword 0 */
+	u8 rxdid_ucast; /* profile_id=[3:0] */
+			/* rsvd=[5:4] */
+			/* ucast=[7:6] */
+	u8 status_err0_qw0;
+	__le16 ptype_err_fflags0;	/* ptype=[9:0] */
+					/* ip_hdr_err=[10:10] */
+					/* udp_len_err=[11:11] */
+					/* ff0=[15:12] */
+	__le16 pktlen_gen_bufq_id;	/* plen=[13:0] */
+					/* gen=[14:14]  only in splitq */
+					/* bufq_id=[15:15] only in splitq */
+	__le16 hdrlen_flags;		/* header=[9:0] */
+					/* rsc=[10:10] only in splitq */
+					/* sph=[11:11] only in splitq */
+					/* ext_udp_0=[12:12] */
+					/* int_udp_0=[13:13] */
+					/* trunc_mirr=[14:14] */
+					/* miss_prepend=[15:15] */
+	/* Qword 1 */
+	u8 status_err0_qw1;
+	u8 status_err1;
+	u8 fflags1;
+	u8 ts_low;
+	__le16 buf_id; /* only in splitq */
+	union {
+		__le16 raw_cs;
+		__le16 l2tag1;
+		__le16 rscseglen;
+	} misc;
+	/* Qword 2 */
+	__le16 hash1;
+	union {
+		u8 fflags2;
+		u8 mirrorid;
+		u8 hash2;
+	} ff2_mirrid_hash2;
+	u8 hash3;
+	__le16 l2tag2;
+	__le16 fmd4;
+	/* Qword 3 */
+	__le16 l2tag1;
+	__le16 fmd6;
+	__le32 ts_high;
+}; /* writeback */
+
+union virtchnl2_rx_desc {
+	struct virtchnl2_singleq_rx_buf_desc		read;
+	struct virtchnl2_singleq_base_rx_desc		base_wb;
+	struct virtchnl2_rx_flex_desc			flex_wb;
+	struct virtchnl2_rx_flex_desc_nic		flex_nic_wb;
+	struct virtchnl2_rx_flex_desc_sw		flex_sw_wb;
+	struct virtchnl2_rx_flex_desc_nic_2		flex_nic_2_wb;
+	struct virtchnl2_rx_flex_desc_adv		flex_adv_wb;
+	struct virtchnl2_rx_flex_desc_adv_nic_3		flex_adv_nic_3_wb;
+};
+
+#endif /* _VIRTCHNL_LAN_DESC_H_ */
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 02/19] iecm: add basic module init and documentation
  2022-01-28  0:09 [Intel-wired-lan] [PATCH net-next 00/19] Add iecm and idpf Alan Brady
  2022-01-28  0:09 ` [Intel-wired-lan] [PATCH net-next 01/19] virtchnl: Add new virtchnl2 ops Alan Brady
@ 2022-01-28  0:09 ` Alan Brady
  2022-01-28 11:56   ` Alexander Lobakin
  2022-02-01 19:44   ` Shannon Nelson
  2022-01-28  0:09 ` [Intel-wired-lan] [PATCH net-next 03/19] iecm: add probe and remove Alan Brady
                   ` (17 subsequent siblings)
  19 siblings, 2 replies; 89+ messages in thread
From: Alan Brady @ 2022-01-28  0:09 UTC (permalink / raw)
  To: intel-wired-lan

This adds the basics needed to make a kernel module and documentation
needed to use iecm module.

Signed-off-by: Alan Brady <alan.brady@intel.com>
Signed-off-by: Phani Burra <phani.r.burra@intel.com>
Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
---
 .../device_drivers/ethernet/intel/iecm.rst    | 93 +++++++++++++++++++
 drivers/net/ethernet/intel/Kconfig            | 15 +++
 drivers/net/ethernet/intel/Makefile           |  1 +
 drivers/net/ethernet/intel/iecm/Makefile      | 13 +++
 drivers/net/ethernet/intel/iecm/iecm_main.c   | 40 ++++++++
 drivers/net/ethernet/intel/include/iecm.h     | 10 ++
 6 files changed, 172 insertions(+)
 create mode 100644 Documentation/networking/device_drivers/ethernet/intel/iecm.rst
 create mode 100644 drivers/net/ethernet/intel/iecm/Makefile
 create mode 100644 drivers/net/ethernet/intel/iecm/iecm_main.c
 create mode 100644 drivers/net/ethernet/intel/include/iecm.h

diff --git a/Documentation/networking/device_drivers/ethernet/intel/iecm.rst b/Documentation/networking/device_drivers/ethernet/intel/iecm.rst
new file mode 100644
index 000000000000..5634e3e65c74
--- /dev/null
+++ b/Documentation/networking/device_drivers/ethernet/intel/iecm.rst
@@ -0,0 +1,93 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+========================
+Intel Ethernet Common Module
+========================
+
+The Intel Ethernet Common Module is meant to serve as an abstraction layer
+between device specific implementation details and common net device driver
+flows. This library provides several function hooks which allow a device driver
+to specify register addresses, control queue communications, and other device
+specific functionality.  Some functions are required to be implemented while
+others have a default implementation that is used when none is supplied by the
+device driver.  Doing this, a device driver can be written to take advantage
+of existing code while also giving the flexibility to implement device specific
+features.
+
+The common use case for this library is for a network device driver that wants
+specify its own device specific details but also leverage the more common
+code flows found in network device drivers.
+
+Sections in this document:
+	Entry Point
+	Exit Point
+	Register Operations API
+	Virtchnl Operations API
+
+Entry Point
+~~~~~~~~~~~
+The primary entry point to the library is the iecm_probe function.  Prior to
+calling this, device drivers must have allocated an iecm_adapter struct and
+initialized it with the required API functions.  The adapter struct, along with
+the pci_dev struct and the pci_device_id struct, is provided to iecm_probe
+which finalizes device initialization and prepares the device for open.
+
+The iecm_dev_ops struct within the iecm_adapter struct is the primary vehicle
+for passing information from device drivers to the common module.  A dependent
+module must define and assign a reg_ops_init function which will assign the
+respective function pointers to initialize register values (see iecm_reg_ops
+struct).  These are required to be provided by the dependent device driver as
+no suitable default can be assumed for register addresses.
+
+The vc_ops_init function pointer and the related iecm_virtchnl_ops struct are
+optional and should only be necessary for device drivers which use a different
+method/timing for communicating across a mailbox to the hardware.  Within iecm
+is a default interface provided in the case where one is not provided by the
+device driver.
+
+Exit Point
+~~~~~~~~~~
+When the device driver is being prepared to be removed through the pci_driver
+remove callback, it is required for the device driver to call iecm_remove with
+the pci_dev struct provided.  This is required to ensure all resources are
+properly freed and returned to the operating system.
+
+Register Operations API
+~~~~~~~~~~~~~~~~~~~~~~~
+iecm_reg_ops contains three different function pointers relating to initializing
+registers for the specific net device using the library.
+
+ctlq_reg_init relates specifically to setting up registers related to control
+queue/mailbox communications.  Registers that should be defined include: head,
+tail, len, bah, bal, len_mask, len_ena_mask, and head_mask.
+
+vportq_reg_init relates to setting up queue registers.  The tail registers to
+be assigned to the iecm_queue struct for each RX/TX queue.
+
+intr_reg_init relates to any registers needed to setup interrupts.  These
+include registers needed to enable the interrupt and change ITR settings.
+
+If the initialization function finds that one or more required function
+pointers were not provided, an error will be issued and the device will be
+inoperable.
+
+
+Virtchnl Operations API
+~~~~~~~~~~~~~~~~~~~~~~~
+The virtchnl is a conduit between driver and hardware that allows device
+drivers to send and receive control messages to/from hardware.  This is
+optional to be specified as there is a general interface that can be assumed
+when using this library.  However, if a device deviates in some way to
+communicate across the mailbox correctly, this interface is provided to allow
+that.
+
+If vc_ops_init is set in the dev_ops field of the iecm_adapter struct, then it
+is assumed the device driver is using providing it's own interface to do
+virtchnl communications.  While providing vc_ops_init is optional, if it is
+provided, it is required that the device driver provide function pointers for
+those functions in vc_ops, with exception for the enable_vport, disable_vport,
+and destroy_vport functions which may not be required for all devices.
+
+If the initialization function finds that vc_ops_init was defined but one or
+more required function pointers were not provided, an error will be issued and
+the device will be inoperable.
diff --git a/drivers/net/ethernet/intel/Kconfig b/drivers/net/ethernet/intel/Kconfig
index 3facb55b7161..754dc7677ad5 100644
--- a/drivers/net/ethernet/intel/Kconfig
+++ b/drivers/net/ethernet/intel/Kconfig
@@ -372,4 +372,19 @@ config IGC
 	  To compile this driver as a module, choose M here. The module
 	  will be called igc.
 
+config IECM
+	tristate "Intel(R) Ethernet Common Module Support"
+	default n
+	depends on PCI_MSI
+	select DIMLIB
+	help
+      This supplies needed functions to device specific device drivers
+      implementing common module.
+
+	  More specific information on configuring the driver is in
+	  <file:Documentation/networking/device_drivers/ethernet/intel/iecm.rst>.
+
+      To compile this as a module, choose M here. The module will be called
+      iecm.
+
 endif # NET_VENDOR_INTEL
diff --git a/drivers/net/ethernet/intel/Makefile b/drivers/net/ethernet/intel/Makefile
index 3075290063f6..c9eba9cc5087 100644
--- a/drivers/net/ethernet/intel/Makefile
+++ b/drivers/net/ethernet/intel/Makefile
@@ -16,3 +16,4 @@ obj-$(CONFIG_IXGB) += ixgb/
 obj-$(CONFIG_IAVF) += iavf/
 obj-$(CONFIG_FM10K) += fm10k/
 obj-$(CONFIG_ICE) += ice/
+obj-$(CONFIG_IECM) += iecm/
diff --git a/drivers/net/ethernet/intel/iecm/Makefile b/drivers/net/ethernet/intel/iecm/Makefile
new file mode 100644
index 000000000000..d2d087ac71e9
--- /dev/null
+++ b/drivers/net/ethernet/intel/iecm/Makefile
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0-only
+# Copyright (C) 2019 Intel Corporation
+
+#
+# Makefile for the Intel(R) Data Plane Function Linux Driver
+#
+
+obj-$(CONFIG_IECM) += iecm.o
+
+ccflags-y += -I$(srctree)/drivers/net/ethernet/intel/include
+
+iecm-y := \
+	iecm_main.o
diff --git a/drivers/net/ethernet/intel/iecm/iecm_main.c b/drivers/net/ethernet/intel/iecm/iecm_main.c
new file mode 100644
index 000000000000..7c09403c6918
--- /dev/null
+++ b/drivers/net/ethernet/intel/iecm/iecm_main.c
@@ -0,0 +1,40 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (C) 2019 Intel Corporation */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "iecm.h"
+
+#define DRV_SUMMARY	"Intel(R) Ethernet Common Module"
+static const char iecm_driver_string[] = DRV_SUMMARY;
+static const char iecm_copyright[] = "Copyright (c) 2020, Intel Corporation.";
+
+MODULE_DESCRIPTION(DRV_SUMMARY);
+MODULE_LICENSE("GPL v2");
+
+/**
+ * iecm_module_init - Driver registration routine
+ *
+ * iecm_module_init is the first routine called when the driver is
+ * loaded. All it does is register with the PCI subsystem.
+ */
+static int __init iecm_module_init(void)
+{
+	pr_info("%s - version %d\n", iecm_driver_string, LINUX_VERSION_CODE);
+	pr_info("%s\n", iecm_copyright);
+
+	return 0;
+}
+module_init(iecm_module_init);
+
+/**
+ * iecm_module_exit - Driver exit cleanup routine
+ *
+ * iecm_module_exit is called just before the driver is removed
+ * from memory.
+ */
+static void __exit iecm_module_exit(void)
+{
+	pr_info("module unloaded\n");
+}
+module_exit(iecm_module_exit);
diff --git a/drivers/net/ethernet/intel/include/iecm.h b/drivers/net/ethernet/intel/include/iecm.h
new file mode 100644
index 000000000000..f66f0d7db8e7
--- /dev/null
+++ b/drivers/net/ethernet/intel/include/iecm.h
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (C) 2019 Intel Corporation */
+
+#ifndef _IECM_H_
+#define _IECM_H_
+
+#include <linux/etherdevice.h>
+#include <linux/version.h>
+
+#endif /* !_IECM_H_ */
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 03/19] iecm: add probe and remove
  2022-01-28  0:09 [Intel-wired-lan] [PATCH net-next 00/19] Add iecm and idpf Alan Brady
  2022-01-28  0:09 ` [Intel-wired-lan] [PATCH net-next 01/19] virtchnl: Add new virtchnl2 ops Alan Brady
  2022-01-28  0:09 ` [Intel-wired-lan] [PATCH net-next 02/19] iecm: add basic module init and documentation Alan Brady
@ 2022-01-28  0:09 ` Alan Brady
  2022-02-01 20:02   ` Shannon Nelson
  2022-01-28  0:09 ` [Intel-wired-lan] [PATCH net-next 04/19] iecm: add api_init and controlq init Alan Brady
                   ` (16 subsequent siblings)
  19 siblings, 1 reply; 89+ messages in thread
From: Alan Brady @ 2022-01-28  0:09 UTC (permalink / raw)
  To: intel-wired-lan

This adds everything we need in probe and remove as well as a few stubs
which will kick off the next step in the init process of device driver
coming up.

Signed-off-by: Phani Burra <phani.r.burra@intel.com>
Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
Signed-off-by: Alan Brady <alan.brady@intel.com>
---
 drivers/net/ethernet/intel/iecm/Makefile      |   1 +
 drivers/net/ethernet/intel/iecm/iecm_lib.c    | 231 ++++++++++++++++++
 drivers/net/ethernet/intel/include/iecm.h     | 178 +++++++++++++-
 .../net/ethernet/intel/include/iecm_txrx.h    |  33 +++
 4 files changed, 442 insertions(+), 1 deletion(-)
 create mode 100644 drivers/net/ethernet/intel/iecm/iecm_lib.c
 create mode 100644 drivers/net/ethernet/intel/include/iecm_txrx.h

diff --git a/drivers/net/ethernet/intel/iecm/Makefile b/drivers/net/ethernet/intel/iecm/Makefile
index d2d087ac71e9..4f497723419d 100644
--- a/drivers/net/ethernet/intel/iecm/Makefile
+++ b/drivers/net/ethernet/intel/iecm/Makefile
@@ -10,4 +10,5 @@ obj-$(CONFIG_IECM) += iecm.o
 ccflags-y += -I$(srctree)/drivers/net/ethernet/intel/include
 
 iecm-y := \
+	iecm_lib.o \
 	iecm_main.o
diff --git a/drivers/net/ethernet/intel/iecm/iecm_lib.c b/drivers/net/ethernet/intel/iecm/iecm_lib.c
new file mode 100644
index 000000000000..e6d0b418a27f
--- /dev/null
+++ b/drivers/net/ethernet/intel/iecm/iecm_lib.c
@@ -0,0 +1,231 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (C) 2019 Intel Corporation */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "iecm.h"
+
+/**
+ * iecm_statistics_task - Delayed task to get statistics over mailbox
+ * @work: work_struct handle to our data
+ */
+static void iecm_statistics_task(struct work_struct *work)
+{
+	/* stub */
+}
+
+/**
+ * iecm_service_task - Delayed task for handling mailbox responses
+ * @work: work_struct handle to our data
+ *
+ */
+static void iecm_service_task(struct work_struct *work)
+{
+	/* stub */
+}
+
+/**
+ * iecm_init_task - Delayed initialization task
+ * @work: work_struct handle to our data
+ *
+ * Init task finishes up pending work started in probe.  Due to the asynchronous
+ * nature in which the device communicates with hardware, we may have to wait
+ * several milliseconds to get a response.  Instead of busy polling in probe,
+ * pulling it out into a delayed work task prevents us from bogging down the
+ * whole system waiting for a response from hardware.
+ */
+static void iecm_init_task(struct work_struct *work)
+{
+	/* stub */
+}
+
+/**
+ * iecm_deinit_task - Device deinit routine
+ * @adapter: Driver specific private structue
+ *
+ * Extended remove logic which will be used for
+ * hard reset as well
+ */
+static void iecm_deinit_task(struct iecm_adapter *adapter)
+{
+	/* stub */
+}
+
+/**
+ * iecm_vc_event_task - Handle virtchannel event logic
+ * @work: work queue struct
+ */
+static void iecm_vc_event_task(struct work_struct *work)
+{
+	/* stub */
+}
+
+/**
+ * iecm_probe - Device initialization routine
+ * @pdev: PCI device information struct
+ * @ent: entry in iecm_pci_tbl
+ * @adapter: driver specific private structure
+ *
+ * Returns 0 on success, negative on failure
+ */
+int iecm_probe(struct pci_dev *pdev,
+	       const struct pci_device_id __always_unused *ent,
+	       struct iecm_adapter *adapter)
+{
+	int err;
+
+	adapter->pdev = pdev;
+
+	err = pcim_enable_device(pdev);
+	if (err)
+		return err;
+
+	err = pcim_iomap_regions(pdev, BIT(IECM_BAR0), pci_name(pdev));
+	if (err) {
+		dev_err(&pdev->dev, "BAR0 I/O map error %d\n", err);
+		return err;
+	}
+
+	/* set up for high or low dma */
+	err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
+	if (err)
+		err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
+	if (err) {
+		dev_err(&pdev->dev, "DMA configuration failed: 0x%x\n", err);
+		return err;
+	}
+
+	pci_enable_pcie_error_reporting(pdev);
+	pci_set_master(pdev);
+	pci_set_drvdata(pdev, adapter);
+
+	adapter->init_wq =
+		alloc_workqueue("%s", WQ_MEM_RECLAIM, 0, KBUILD_MODNAME);
+	if (!adapter->init_wq) {
+		dev_err(&pdev->dev, "Failed to allocate workqueue\n");
+		err = -ENOMEM;
+		goto err_wq_alloc;
+	}
+
+	adapter->serv_wq =
+		alloc_workqueue("%s", WQ_MEM_RECLAIM, 0, KBUILD_MODNAME);
+	if (!adapter->serv_wq) {
+		dev_err(&pdev->dev, "Failed to allocate workqueue\n");
+		err = -ENOMEM;
+		goto err_mbx_wq_alloc;
+	}
+
+	adapter->stats_wq =
+		alloc_workqueue("%s", WQ_MEM_RECLAIM, 0, KBUILD_MODNAME);
+	if (!adapter->stats_wq) {
+		dev_err(&pdev->dev, "Failed to allocate workqueue\n");
+		err = -ENOMEM;
+		goto err_stats_wq_alloc;
+	}
+	adapter->vc_event_wq =
+		alloc_workqueue("%s", WQ_MEM_RECLAIM, 0, KBUILD_MODNAME);
+	if (!adapter->vc_event_wq) {
+		dev_err(&pdev->dev, "Failed to allocate workqueue\n");
+		err = -ENOMEM;
+		goto err_vc_event_wq_alloc;
+	}
+
+	/* setup msglvl */
+	adapter->msg_enable = netif_msg_init(-1, IECM_AVAIL_NETIF_M);
+
+	adapter->vports = kcalloc(IECM_MAX_NUM_VPORTS,
+				  sizeof(*adapter->vports), GFP_KERNEL);
+	if (!adapter->vports) {
+		err = -ENOMEM;
+		goto err_vport_alloc;
+	}
+
+	adapter->netdevs = kcalloc(IECM_MAX_NUM_VPORTS,
+				   sizeof(struct net_device *), GFP_KERNEL);
+	if (!adapter->netdevs) {
+		err = -ENOMEM;
+		goto err_netdev_alloc;
+	}
+
+	mutex_init(&adapter->sw_mutex);
+	mutex_init(&adapter->reset_lock);
+	init_waitqueue_head(&adapter->vchnl_wq);
+	init_waitqueue_head(&adapter->sw_marker_wq);
+
+	spin_lock_init(&adapter->cloud_filter_list_lock);
+	spin_lock_init(&adapter->mac_filter_list_lock);
+	spin_lock_init(&adapter->vlan_list_lock);
+	spin_lock_init(&adapter->adv_rss_list_lock);
+	spin_lock_init(&adapter->fdir_fltr_list_lock);
+	INIT_LIST_HEAD(&adapter->config_data.mac_filter_list);
+	INIT_LIST_HEAD(&adapter->config_data.vlan_filter_list);
+	INIT_LIST_HEAD(&adapter->config_data.adv_rss_list);
+
+	INIT_DELAYED_WORK(&adapter->stats_task, iecm_statistics_task);
+	INIT_DELAYED_WORK(&adapter->serv_task, iecm_service_task);
+	INIT_DELAYED_WORK(&adapter->init_task, iecm_init_task);
+	INIT_DELAYED_WORK(&adapter->vc_event_task, iecm_vc_event_task);
+
+	set_bit(__IECM_HR_DRV_LOAD, adapter->flags);
+	queue_delayed_work(adapter->vc_event_wq, &adapter->vc_event_task,
+			   msecs_to_jiffies(10 * (pdev->devfn & 0x07)));
+
+	return 0;
+err_netdev_alloc:
+	kfree(adapter->vports);
+err_vport_alloc:
+	destroy_workqueue(adapter->vc_event_wq);
+err_vc_event_wq_alloc:
+	destroy_workqueue(adapter->stats_wq);
+err_stats_wq_alloc:
+	destroy_workqueue(adapter->serv_wq);
+err_mbx_wq_alloc:
+	destroy_workqueue(adapter->init_wq);
+err_wq_alloc:
+	pci_disable_pcie_error_reporting(pdev);
+	return err;
+}
+EXPORT_SYMBOL(iecm_probe);
+
+/**
+ * iecm_del_user_cfg_data - delete all user configuration data
+ * @adapter: Driver specific private structue
+ */
+static void iecm_del_user_cfg_data(struct iecm_adapter *adapter)
+{
+	/* stub */
+}
+
+/**
+ * iecm_remove - Device removal routine
+ * @pdev: PCI device information struct
+ */
+void iecm_remove(struct pci_dev *pdev)
+{
+	struct iecm_adapter *adapter = pci_get_drvdata(pdev);
+
+	if (!adapter)
+		return;
+	/* Wait until vc_event_task is done to consider if any hard reset is
+	 * in progress else we may go ahead and release the resources but the
+	 * thread doing the hard reset might continue the init path and
+	 * end up in bad state.
+	 */
+	cancel_delayed_work_sync(&adapter->vc_event_task);
+	iecm_deinit_task(adapter);
+	iecm_del_user_cfg_data(adapter);
+	msleep(20);
+	destroy_workqueue(adapter->serv_wq);
+	destroy_workqueue(adapter->vc_event_wq);
+	destroy_workqueue(adapter->stats_wq);
+	destroy_workqueue(adapter->init_wq);
+	kfree(adapter->vports);
+	kfree(adapter->netdevs);
+	kfree(adapter->vlan_caps);
+	mutex_destroy(&adapter->sw_mutex);
+	mutex_destroy(&adapter->reset_lock);
+	pci_disable_pcie_error_reporting(pdev);
+	pcim_iounmap_regions(pdev, BIT(IECM_BAR0));
+	pci_disable_device(pdev);
+}
+EXPORT_SYMBOL(iecm_remove);
diff --git a/drivers/net/ethernet/intel/include/iecm.h b/drivers/net/ethernet/intel/include/iecm.h
index f66f0d7db8e7..e19e014e9817 100644
--- a/drivers/net/ethernet/intel/include/iecm.h
+++ b/drivers/net/ethernet/intel/include/iecm.h
@@ -4,7 +4,183 @@
 #ifndef _IECM_H_
 #define _IECM_H_
 
-#include <linux/etherdevice.h>
+#include <linux/aer.h>
+#include <linux/pci.h>
+#include <linux/netdevice.h>
+#include <linux/ethtool.h>
 #include <linux/version.h>
+#include <linux/dim.h>
 
+#include "iecm_txrx.h"
+
+#define IECM_BAR0			0
+#define IECM_NO_FREE_SLOT		0xffff
+
+#define IECM_MAX_NUM_VPORTS		1
+
+/* available message levels */
+#define IECM_AVAIL_NETIF_M (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK)
+
+enum iecm_state {
+	__IECM_STARTUP,
+	__IECM_VER_CHECK,
+	__IECM_GET_CAPS,
+	__IECM_GET_DFLT_VPORT_PARAMS,
+	__IECM_INIT_SW,
+	__IECM_DOWN,
+	__IECM_UP,
+	__IECM_STATE_LAST /* this member MUST be last */
+};
+
+enum iecm_flags {
+	/* Soft reset causes */
+	__IECM_SR_Q_CHANGE, /* Soft reset to do queue change */
+	__IECM_SR_Q_DESC_CHANGE,
+	__IECM_SR_Q_SCH_CHANGE, /* Scheduling mode change in queue context */
+	__IECM_SR_MTU_CHANGE,
+	__IECM_SR_TC_CHANGE,
+	__IECM_SR_RSC_CHANGE,
+	__IECM_SR_HSPLIT_CHANGE,
+	/* Hard reset causes */
+	__IECM_HR_FUNC_RESET, /* Hard reset when txrx timeout */
+	__IECM_HR_CORE_RESET, /* when reset event is received on virtchannel */
+	__IECM_HR_DRV_LOAD, /* Set on driver load for a clean HW */
+	/* Reset in progress */
+	__IECM_HR_RESET_IN_PROG,
+	/* Resources release in progress*/
+	__IECM_REL_RES_IN_PROG,
+	/* Generic bits to share a message */
+	__IECM_DEL_QUEUES,
+	__IECM_UP_REQUESTED, /* Set if open to be called explicitly by driver */
+	/* Mailbox interrupt event */
+	__IECM_MB_INTR_MODE,
+	__IECM_MB_INTR_TRIGGER,
+	/* Stats message pending on mailbox */
+	__IECM_MB_STATS_PENDING,
+	/* Device specific bits */
+	/* Request split queue model when creating vport */
+	__IECM_REQ_TX_SPLITQ,
+	__IECM_REQ_RX_SPLITQ,
+	/* Asynchronous add/del ether address in flight */
+	__IECM_ADD_ETH_REQ,
+	__IECM_DEL_ETH_REQ,
+	/* Virtchnl message buffer received needs to be processed */
+	__IECM_VC_MSG_PENDING,
+	/* To process software marker packets */
+	__IECM_SW_MARKER,
+	/* must be last */
+	__IECM_FLAGS_NBITS,
+};
+
+struct iecm_reset_reg {
+	u32 rstat;
+	u32 rstat_m;
+};
+
+/* stub */
+struct iecm_vport {
+};
+
+enum iecm_user_flags {
+	__IECM_PRIV_FLAGS_HDR_SPLIT = 0,
+	__IECM_PROMISC_UC = 32,
+	__IECM_PROMISC_MC,
+	__IECM_USER_FLAGS_NBITS,
+};
+
+/* User defined configuration values */
+struct iecm_user_config_data {
+	u32 num_req_tx_qs; /* user requested TX queues through ethtool */
+	u32 num_req_rx_qs; /* user requested RX queues through ethtool */
+	u32 num_req_txq_desc;
+	u32 num_req_rxq_desc;
+	u16 vlan_ethertype;
+	void *req_qs_chunks;
+	DECLARE_BITMAP(user_flags, __IECM_USER_FLAGS_NBITS);
+	DECLARE_BITMAP(etf_qenable, IECM_LARGE_MAX_Q);
+	struct list_head mac_filter_list;
+	struct list_head vlan_filter_list;
+	struct list_head adv_rss_list;
+};
+
+struct iecm_rss_data {
+	u64 rss_hash;
+	u16 rss_key_size;
+	u8 *rss_key;
+	u16 rss_lut_size;
+	u32 *rss_lut;
+};
+
+struct iecm_adapter {
+	struct pci_dev *pdev;
+	const char *drv_name;
+	const char *drv_ver;
+	u32 virt_ver_maj;
+	u32 virt_ver_min;
+
+	u32 tx_timeout_count;
+	u32 msg_enable;
+	enum iecm_state state;
+	DECLARE_BITMAP(flags, __IECM_FLAGS_NBITS);
+	struct mutex reset_lock; /* lock to protect reset flows */
+	struct iecm_reset_reg reset_reg;
+
+	u16 num_req_msix;
+	u16 num_msix_entries;
+	struct msix_entry *msix_entries;
+	struct virtchnl2_alloc_vectors *req_vec_chunks;
+
+	/* vport structs */
+	struct iecm_vport **vports;	/* vports created by the driver */
+	struct net_device **netdevs;	/* associated vport netdevs */
+	u16 num_alloc_vport;
+	u16 next_vport;		/* Next free slot in pf->vport[] - 0-based! */
+
+	struct delayed_work init_task; /* delayed init task */
+	struct workqueue_struct *init_wq;
+	u32 mb_wait_count;
+	struct delayed_work serv_task; /* delayed service task */
+	struct workqueue_struct *serv_wq;
+	struct delayed_work stats_task; /* delayed statistics task */
+	struct workqueue_struct *stats_wq;
+	struct delayed_work vc_event_task; /* delayed virtchannel event task */
+	struct workqueue_struct *vc_event_wq;
+	/* Store the resources data received from control plane */
+	void **vport_params_reqd;
+	void **vport_params_recvd;
+	/* User set parameters */
+	struct iecm_user_config_data config_data;
+	void *caps;
+	struct virtchnl_vlan_caps *vlan_caps;
+
+	wait_queue_head_t vchnl_wq;
+	wait_queue_head_t sw_marker_wq;
+	struct iecm_rss_data rss_data;
+	s32 link_speed;
+	/* This is only populated if the VIRTCHNL_VF_CAP_ADV_LINK_SPEED is set
+	 * in vf_res->vf_cap_flags. This field should be used going forward and
+	 * the enum virtchnl_link_speed above should be considered the legacy
+	 * way of storing/communicating link speeds.
+	 */
+	u32 link_speed_mbps;
+	bool link_up;
+	int num_vfs;
+
+	struct mutex sw_mutex;		/* lock to protect vport alloc flow */
+	/* lock to protect cloud filters*/
+	spinlock_t cloud_filter_list_lock;
+	/* lock to protect mac filters */
+	spinlock_t mac_filter_list_lock;
+	/* lock to protect vlan filters */
+	spinlock_t vlan_list_lock;
+	/* lock to protect advanced RSS filters */
+	spinlock_t adv_rss_list_lock;
+	/* lock to protect the Flow Director filters */
+	spinlock_t fdir_fltr_list_lock;
+};
+
+int iecm_probe(struct pci_dev *pdev,
+	       const struct pci_device_id __always_unused *ent,
+	       struct iecm_adapter *adapter);
+void iecm_remove(struct pci_dev *pdev);
 #endif /* !_IECM_H_ */
diff --git a/drivers/net/ethernet/intel/include/iecm_txrx.h b/drivers/net/ethernet/intel/include/iecm_txrx.h
new file mode 100644
index 000000000000..602d3b3b19dd
--- /dev/null
+++ b/drivers/net/ethernet/intel/include/iecm_txrx.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (C) 2019 Intel Corporation */
+
+#ifndef _IECM_TXRX_H_
+#define _IECM_TXRX_H_
+
+#define IECM_LARGE_MAX_Q			256
+#define IECM_MAX_Q				16
+/* Mailbox Queue */
+#define IECM_MAX_NONQ				1
+#define IECM_MAX_TXQ_DESC			4096
+#define IECM_MAX_RXQ_DESC			4096
+#define IECM_MIN_TXQ_DESC			32
+#define IECM_MIN_TXQ_COMPLQ_DESC		64
+#define IECM_MIN_RXQ_DESC			32
+#define IECM_REQ_DESC_MULTIPLE			32
+#define IECM_REQ_SPLITQ_RXQ_DESC_MULTIPLE	64
+#define IECM_MIN_TX_DESC_NEEDED (MAX_SKB_FRAGS + 6)
+#define IECM_TX_WAKE_THRESH ((s16)IECM_MIN_TX_DESC_NEEDED * 2)
+
+#define IECM_DFLT_SINGLEQ_TX_Q_GROUPS		1
+#define IECM_DFLT_SINGLEQ_RX_Q_GROUPS		1
+#define IECM_DFLT_SINGLEQ_TXQ_PER_GROUP		4
+#define IECM_DFLT_SINGLEQ_RXQ_PER_GROUP		4
+
+#define IECM_COMPLQ_PER_GROUP			1
+#define IECM_MAX_BUFQS_PER_RXQ_GRP		2
+
+#define IECM_DFLT_SPLITQ_TX_Q_GROUPS		4
+#define IECM_DFLT_SPLITQ_RX_Q_GROUPS		4
+#define IECM_DFLT_SPLITQ_TXQ_PER_GROUP		1
+#define IECM_DFLT_SPLITQ_RXQ_PER_GROUP		1
+#endif /* !_IECM_TXRX_H_ */
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 04/19] iecm: add api_init and controlq init
  2022-01-28  0:09 [Intel-wired-lan] [PATCH net-next 00/19] Add iecm and idpf Alan Brady
                   ` (2 preceding siblings ...)
  2022-01-28  0:09 ` [Intel-wired-lan] [PATCH net-next 03/19] iecm: add probe and remove Alan Brady
@ 2022-01-28  0:09 ` Alan Brady
  2022-01-28 12:09   ` Alexander Lobakin
  2022-02-01 21:26   ` Shannon Nelson
  2022-01-28  0:09 ` [Intel-wired-lan] [PATCH net-next 05/19] iecm: add vport alloc and virtchnl messages Alan Brady
                   ` (15 subsequent siblings)
  19 siblings, 2 replies; 89+ messages in thread
From: Alan Brady @ 2022-01-28  0:09 UTC (permalink / raw)
  To: intel-wired-lan

Initializing device registers is offloaded into function pointers given
to iecm from the dependent device driver for a given device, as offsets
can vary wildly. This also adds everything needed to setup and use a
controlq which uses some of those registers.

At the end of probe we kicked off a hard reset and this implements what's
needed to handle that reset and continue init.

Signed-off-by: Phani Burra <phani.r.burra@intel.com>
Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
Signed-off-by: Alan Brady <alan.brady@intel.com>
---
 drivers/net/ethernet/intel/iecm/Makefile      |   3 +
 .../net/ethernet/intel/iecm/iecm_controlq.c   | 649 ++++++++++++++++++
 .../ethernet/intel/iecm/iecm_controlq_setup.c | 175 +++++
 drivers/net/ethernet/intel/iecm/iecm_lib.c    | 191 +++++-
 .../net/ethernet/intel/iecm/iecm_virtchnl.c   | 172 +++++
 drivers/net/ethernet/intel/include/iecm.h     |  52 ++
 .../ethernet/intel/include/iecm_controlq.h    | 117 ++++
 .../intel/include/iecm_controlq_api.h         | 185 +++++
 drivers/net/ethernet/intel/include/iecm_mem.h |  20 +
 9 files changed, 1563 insertions(+), 1 deletion(-)
 create mode 100644 drivers/net/ethernet/intel/iecm/iecm_controlq.c
 create mode 100644 drivers/net/ethernet/intel/iecm/iecm_controlq_setup.c
 create mode 100644 drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
 create mode 100644 drivers/net/ethernet/intel/include/iecm_controlq.h
 create mode 100644 drivers/net/ethernet/intel/include/iecm_controlq_api.h
 create mode 100644 drivers/net/ethernet/intel/include/iecm_mem.h

diff --git a/drivers/net/ethernet/intel/iecm/Makefile b/drivers/net/ethernet/intel/iecm/Makefile
index 4f497723419d..db8fecb075a6 100644
--- a/drivers/net/ethernet/intel/iecm/Makefile
+++ b/drivers/net/ethernet/intel/iecm/Makefile
@@ -11,4 +11,7 @@ ccflags-y += -I$(srctree)/drivers/net/ethernet/intel/include
 
 iecm-y := \
 	iecm_lib.o \
+	iecm_virtchnl.o \
+	iecm_controlq.o \
+	iecm_controlq_setup.o \
 	iecm_main.o
diff --git a/drivers/net/ethernet/intel/iecm/iecm_controlq.c b/drivers/net/ethernet/intel/iecm/iecm_controlq.c
new file mode 100644
index 000000000000..f9682a7b3e44
--- /dev/null
+++ b/drivers/net/ethernet/intel/iecm/iecm_controlq.c
@@ -0,0 +1,649 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (c) 2020, Intel Corporation. */
+
+#include "iecm_controlq.h"
+
+/**
+ * iecm_ctlq_setup_regs - initialize control queue registers
+ * @cq: pointer to the specific control queue
+ * @q_create_info: structs containing info for each queue to be initialized
+ */
+static void
+iecm_ctlq_setup_regs(struct iecm_ctlq_info *cq,
+		     struct iecm_ctlq_create_info *q_create_info)
+{
+	/* set head and tail registers in our local struct */
+	cq->reg.head = q_create_info->reg.head;
+	cq->reg.tail = q_create_info->reg.tail;
+	cq->reg.len = q_create_info->reg.len;
+	cq->reg.bah = q_create_info->reg.bah;
+	cq->reg.bal = q_create_info->reg.bal;
+	cq->reg.len_mask = q_create_info->reg.len_mask;
+	cq->reg.len_ena_mask = q_create_info->reg.len_ena_mask;
+	cq->reg.head_mask = q_create_info->reg.head_mask;
+}
+
+/**
+ * iecm_ctlq_init_regs - Initialize control queue registers
+ * @hw: pointer to hw struct
+ * @cq: pointer to the specific Control queue
+ * @is_rxq: true if receive control queue, false otherwise
+ *
+ * Initialize registers. The caller is expected to have already initialized the
+ * descriptor ring memory and buffer memory
+ */
+static void iecm_ctlq_init_regs(struct iecm_hw *hw, struct iecm_ctlq_info *cq,
+				bool is_rxq)
+{
+	/* Update tail to post pre-allocated buffers for rx queues */
+	if (is_rxq)
+		wr32(hw, cq->reg.tail, (u32)(cq->ring_size - 1));
+
+	/* For non-Mailbox control queues only TAIL need to be set */
+	if (cq->q_id != -1)
+		return;
+
+	/* Clear Head for both send or receive */
+	wr32(hw, cq->reg.head, 0);
+
+	/* set starting point */
+	wr32(hw, cq->reg.bal, lower_32_bits(cq->desc_ring.pa));
+	wr32(hw, cq->reg.bah, upper_32_bits(cq->desc_ring.pa));
+	wr32(hw, cq->reg.len, (cq->ring_size | cq->reg.len_ena_mask));
+}
+
+/**
+ * iecm_ctlq_init_rxq_bufs - populate receive queue descriptors with buf
+ * @cq: pointer to the specific Control queue
+ *
+ * Record the address of the receive queue DMA buffers in the descriptors.
+ * The buffers must have been previously allocated.
+ */
+static void iecm_ctlq_init_rxq_bufs(struct iecm_ctlq_info *cq)
+{
+	int i = 0;
+
+	for (i = 0; i < cq->ring_size; i++) {
+		struct iecm_ctlq_desc *desc = IECM_CTLQ_DESC(cq, i);
+		struct iecm_dma_mem *bi = cq->bi.rx_buff[i];
+
+		/* No buffer to post to descriptor, continue */
+		if (!bi)
+			continue;
+
+		desc->flags =
+			cpu_to_le16(IECM_CTLQ_FLAG_BUF | IECM_CTLQ_FLAG_RD);
+		desc->opcode = 0;
+		desc->datalen = (__le16)cpu_to_le16(bi->size);
+		desc->ret_val = 0;
+		desc->cookie_high = 0;
+		desc->cookie_low = 0;
+		desc->params.indirect.addr_high =
+			cpu_to_le32(upper_32_bits(bi->pa));
+		desc->params.indirect.addr_low =
+			cpu_to_le32(lower_32_bits(bi->pa));
+		desc->params.indirect.param0 = 0;
+		desc->params.indirect.param1 = 0;
+	}
+}
+
+/**
+ * iecm_ctlq_shutdown - shutdown the CQ
+ * @hw: pointer to hw struct
+ * @cq: pointer to the specific Control queue
+ *
+ * The main shutdown routine for any controq queue
+ */
+static void iecm_ctlq_shutdown(struct iecm_hw *hw, struct iecm_ctlq_info *cq)
+{
+	mutex_lock(&cq->cq_lock);
+
+	if (!cq->ring_size)
+		goto shutdown_sq_out;
+
+	/* free ring buffers and the ring itself */
+	iecm_ctlq_dealloc_ring_res(hw, cq);
+
+	/* Set ring_size to 0 to indicate uninitialized queue */
+	cq->ring_size = 0;
+
+shutdown_sq_out:
+	mutex_unlock(&cq->cq_lock);
+	mutex_destroy(&cq->cq_lock);
+}
+
+/**
+ * iecm_ctlq_add - add one control queue
+ * @hw: pointer to hardware struct
+ * @qinfo: info for queue to be created
+ * @cq_out: (output) double pointer to control queue to be created
+ *
+ * Allocate and initialize a control queue and add it to the control queue list.
+ * The cq parameter will be allocated/initialized and passed back to the caller
+ * if no errors occur.
+ *
+ * Note: iecm_ctlq_init must be called prior to any calls to iecm_ctlq_add
+ */
+int iecm_ctlq_add(struct iecm_hw *hw,
+		  struct iecm_ctlq_create_info *qinfo,
+		  struct iecm_ctlq_info **cq_out)
+{
+	bool is_rxq = false;
+	int status = 0;
+
+	if (!qinfo->len || !qinfo->buf_size ||
+	    qinfo->len > IECM_CTLQ_MAX_RING_SIZE ||
+	    qinfo->buf_size > IECM_CTLQ_MAX_BUF_LEN)
+		return -EINVAL;
+
+	*cq_out = kcalloc(1, sizeof(struct iecm_ctlq_info), GFP_KERNEL);
+	if (!(*cq_out))
+		return -ENOMEM;
+
+	(*cq_out)->cq_type = qinfo->type;
+	(*cq_out)->q_id = qinfo->id;
+	(*cq_out)->buf_size = qinfo->buf_size;
+	(*cq_out)->ring_size = qinfo->len;
+
+	(*cq_out)->next_to_use = 0;
+	(*cq_out)->next_to_clean = 0;
+	(*cq_out)->next_to_post = (*cq_out)->ring_size - 1;
+
+	switch (qinfo->type) {
+	case IECM_CTLQ_TYPE_MAILBOX_RX:
+		is_rxq = true;
+		fallthrough;
+	case IECM_CTLQ_TYPE_MAILBOX_TX:
+		status = iecm_ctlq_alloc_ring_res(hw, *cq_out);
+		break;
+	default:
+		status = -EBADR;
+		break;
+	}
+
+	if (status)
+		goto init_free_q;
+
+	if (is_rxq) {
+		iecm_ctlq_init_rxq_bufs(*cq_out);
+	} else {
+		/* Allocate the array of msg pointers for TX queues */
+		(*cq_out)->bi.tx_msg = kcalloc(qinfo->len,
+					       sizeof(struct iecm_ctlq_msg *),
+					       GFP_KERNEL);
+		if (!(*cq_out)->bi.tx_msg) {
+			status = -ENOMEM;
+			goto init_dealloc_q_mem;
+		}
+	}
+
+	iecm_ctlq_setup_regs(*cq_out, qinfo);
+
+	iecm_ctlq_init_regs(hw, *cq_out, is_rxq);
+
+	mutex_init(&(*cq_out)->cq_lock);
+
+	list_add(&(*cq_out)->cq_list, &hw->cq_list_head);
+
+	return status;
+
+init_dealloc_q_mem:
+	/* free ring buffers and the ring itself */
+	iecm_ctlq_dealloc_ring_res(hw, *cq_out);
+init_free_q:
+	kfree(*cq_out);
+
+	return status;
+}
+
+/**
+ * iecm_ctlq_remove - deallocate and remove specified control queue
+ * @hw: pointer to hardware struct
+ * @cq: pointer to control queue to be removed
+ */
+void iecm_ctlq_remove(struct iecm_hw *hw,
+		      struct iecm_ctlq_info *cq)
+{
+	list_del(&cq->cq_list);
+	iecm_ctlq_shutdown(hw, cq);
+	kfree(cq);
+}
+
+/**
+ * iecm_ctlq_init - main initialization routine for all control queues
+ * @hw: pointer to hardware struct
+ * @num_q: number of queues to initialize
+ * @q_info: array of structs containing info for each queue to be initialized
+ *
+ * This initializes any number and any type of control queues. This is an all
+ * or nothing routine; if one fails, all previously allocated queues will be
+ * destroyed. This must be called prior to using the individual add/remove
+ * APIs.
+ */
+int iecm_ctlq_init(struct iecm_hw *hw, u8 num_q,
+		   struct iecm_ctlq_create_info *q_info)
+{
+	struct iecm_ctlq_info *cq = NULL, *tmp = NULL;
+	int ret_code = 0;
+	int i = 0;
+
+	INIT_LIST_HEAD(&hw->cq_list_head);
+
+	for (i = 0; i < num_q; i++) {
+		struct iecm_ctlq_create_info *qinfo = q_info + i;
+
+		ret_code = iecm_ctlq_add(hw, qinfo, &cq);
+		if (ret_code)
+			goto init_destroy_qs;
+	}
+
+	return ret_code;
+
+init_destroy_qs:
+	list_for_each_entry_safe(cq, tmp, &hw->cq_list_head, cq_list)
+		iecm_ctlq_remove(hw, cq);
+
+	return ret_code;
+}
+
+/**
+ * iecm_ctlq_deinit - destroy all control queues
+ * @hw: pointer to hw struct
+ */
+int iecm_ctlq_deinit(struct iecm_hw *hw)
+{
+	struct iecm_ctlq_info *cq = NULL, *tmp = NULL;
+	int ret_code = 0;
+
+	list_for_each_entry_safe(cq, tmp, &hw->cq_list_head, cq_list)
+		iecm_ctlq_remove(hw, cq);
+
+	return ret_code;
+}
+
+/**
+ * iecm_ctlq_send - send command to Control Queue (CTQ)
+ * @hw: pointer to hw struct
+ * @cq: handle to control queue struct to send on
+ * @num_q_msg: number of messages to send on control queue
+ * @q_msg: pointer to array of queue messages to be sent
+ *
+ * The caller is expected to allocate DMAable buffers and pass them to the
+ * send routine via the q_msg struct / control queue specific data struct.
+ * The control queue will hold a reference to each send message until
+ * the completion for that message has been cleaned.
+ */
+int iecm_ctlq_send(struct iecm_hw *hw, struct iecm_ctlq_info *cq,
+		   u16 num_q_msg, struct iecm_ctlq_msg q_msg[])
+{
+	struct iecm_ctlq_desc *desc;
+	int num_desc_avail = 0;
+	int status = 0;
+	int i = 0;
+
+	if (!cq || !cq->ring_size)
+		return -ENOBUFS;
+
+	mutex_lock(&cq->cq_lock);
+
+	/* Ensure there are enough descriptors to send all messages */
+	num_desc_avail = IECM_CTLQ_DESC_UNUSED(cq);
+	if (num_desc_avail == 0 || num_desc_avail < num_q_msg) {
+		status = -ENOSPC;
+		goto sq_send_command_out;
+	}
+
+	for (i = 0; i < num_q_msg; i++) {
+		struct iecm_ctlq_msg *msg = &q_msg[i];
+		u64 msg_cookie;
+
+		desc = IECM_CTLQ_DESC(cq, cq->next_to_use);
+
+		desc->opcode = cpu_to_le16(msg->opcode);
+		desc->pfid_vfid = cpu_to_le16(msg->func_id);
+
+		msg_cookie = *(u64 *)&msg->cookie;
+		desc->cookie_high =
+			cpu_to_le32(upper_32_bits(msg_cookie));
+		desc->cookie_low =
+			cpu_to_le32(lower_32_bits(msg_cookie));
+
+		if (msg->data_len) {
+			struct iecm_dma_mem *buff = msg->ctx.indirect.payload;
+
+			desc->datalen = cpu_to_le16(msg->data_len);
+			desc->flags |= cpu_to_le16(IECM_CTLQ_FLAG_BUF);
+			desc->flags |= cpu_to_le16(IECM_CTLQ_FLAG_RD);
+
+			/* Update the address values in the desc with the pa
+			 * value for respective buffer
+			 */
+			desc->params.indirect.addr_high =
+				cpu_to_le32(upper_32_bits(buff->pa));
+			desc->params.indirect.addr_low =
+				cpu_to_le32(lower_32_bits(buff->pa));
+
+			memcpy(&desc->params, msg->ctx.indirect.context,
+			       IECM_INDIRECT_CTX_SIZE);
+		} else {
+			memcpy(&desc->params, msg->ctx.direct,
+			       IECM_DIRECT_CTX_SIZE);
+		}
+
+		/* Store buffer info */
+		cq->bi.tx_msg[cq->next_to_use] = msg;
+
+		(cq->next_to_use)++;
+		if (cq->next_to_use == cq->ring_size)
+			cq->next_to_use = 0;
+	}
+
+	/* Force memory write to complete before letting hardware
+	 * know that there are new descriptors to fetch.
+	 */
+	dma_wmb();
+
+	wr32(hw, cq->reg.tail, cq->next_to_use);
+
+sq_send_command_out:
+	mutex_unlock(&cq->cq_lock);
+
+	return status;
+}
+
+/**
+ * iecm_ctlq_clean_sq - reclaim send descriptors on HW write back for the
+ * requested queue
+ * @cq: pointer to the specific Control queue
+ * @clean_count: (input|output) number of descriptors to clean as input, and
+ * number of descriptors actually cleaned as output
+ * @msg_status: (output) pointer to msg pointer array to be populated; needs
+ * to be allocated by caller
+ *
+ * Returns an array of message pointers associated with the cleaned
+ * descriptors. The pointers are to the original ctlq_msgs sent on the cleaned
+ * descriptors.  The status will be returned for each; any messages that failed
+ * to send will have a non-zero status. The caller is expected to free original
+ * ctlq_msgs and free or reuse the DMA buffers.
+ */
+int iecm_ctlq_clean_sq(struct iecm_ctlq_info *cq, u16 *clean_count,
+		       struct iecm_ctlq_msg *msg_status[])
+{
+	struct iecm_ctlq_desc *desc;
+	u16 i = 0, num_to_clean;
+	u16 ntc, desc_err;
+	int ret = 0;
+
+	if (!cq || !cq->ring_size)
+		return -ENOBUFS;
+
+	if (*clean_count == 0)
+		return 0;
+	if (*clean_count > cq->ring_size)
+		return -EBADR;
+
+	mutex_lock(&cq->cq_lock);
+
+	ntc = cq->next_to_clean;
+
+	num_to_clean = *clean_count;
+
+	for (i = 0; i < num_to_clean; i++) {
+		/* Fetch next descriptor and check if marked as done */
+		desc = IECM_CTLQ_DESC(cq, ntc);
+		if (!(le16_to_cpu(desc->flags) & IECM_CTLQ_FLAG_DD))
+			break;
+
+		desc_err = le16_to_cpu(desc->ret_val);
+		if (desc_err) {
+			/* strip off FW internal code */
+			desc_err &= 0xff;
+		}
+
+		msg_status[i] = cq->bi.tx_msg[ntc];
+		msg_status[i]->status = desc_err;
+
+		cq->bi.tx_msg[ntc] = NULL;
+
+		/* Zero out any stale data */
+		memset(desc, 0, sizeof(*desc));
+
+		ntc++;
+		if (ntc == cq->ring_size)
+			ntc = 0;
+	}
+
+	cq->next_to_clean = ntc;
+
+	mutex_unlock(&cq->cq_lock);
+
+	/* Return number of descriptors actually cleaned */
+	*clean_count = i;
+
+	return ret;
+}
+
+/**
+ * iecm_ctlq_post_rx_buffs - post buffers to descriptor ring
+ * @hw: pointer to hw struct
+ * @cq: pointer to control queue handle
+ * @buff_count: (input|output) input is number of buffers caller is trying to
+ * return; output is number of buffers that were not posted
+ * @buffs: array of pointers to dma mem structs to be given to hardware
+ *
+ * Caller uses this function to return DMA buffers to the descriptor ring after
+ * consuming them; buff_count will be the number of buffers.
+ *
+ * Note: this function needs to be called after a receive call even
+ * if there are no DMA buffers to be returned, i.e. buff_count = 0,
+ * buffs = NULL to support direct commands
+ */
+int iecm_ctlq_post_rx_buffs(struct iecm_hw *hw, struct iecm_ctlq_info *cq,
+			    u16 *buff_count, struct iecm_dma_mem **buffs)
+{
+	struct iecm_ctlq_desc *desc;
+	u16 ntp = cq->next_to_post;
+	bool buffs_avail = false;
+	u16 tbp = ntp + 1;
+	int status = 0;
+	int i = 0;
+
+	if (*buff_count > cq->ring_size)
+		return -EBADR;
+
+	if (*buff_count > 0)
+		buffs_avail = true;
+
+	mutex_lock(&cq->cq_lock);
+
+	if (tbp >= cq->ring_size)
+		tbp = 0;
+
+	if (tbp == cq->next_to_clean)
+		/* Nothing to do */
+		goto post_buffs_out;
+
+	/* Post buffers for as many as provided or up until the last one used */
+	while (ntp != cq->next_to_clean) {
+		desc = IECM_CTLQ_DESC(cq, ntp);
+
+		if (cq->bi.rx_buff[ntp])
+			goto fill_desc;
+		if (!buffs_avail) {
+			/* If the caller hasn't given us any buffers or
+			 * there are none left, search the ring itself
+			 * for an available buffer to move to this
+			 * entry starting at the next entry in the ring
+			 */
+			tbp = ntp + 1;
+
+			/* Wrap ring if necessary */
+			if (tbp >= cq->ring_size)
+				tbp = 0;
+
+			while (tbp != cq->next_to_clean) {
+				if (cq->bi.rx_buff[tbp]) {
+					cq->bi.rx_buff[ntp] =
+						cq->bi.rx_buff[tbp];
+					cq->bi.rx_buff[tbp] = NULL;
+
+					/* Found a buffer, no need to
+					 * search anymore
+					 */
+					break;
+				}
+
+				/* Wrap ring if necessary */
+				tbp++;
+				if (tbp >= cq->ring_size)
+					tbp = 0;
+			}
+
+			if (tbp == cq->next_to_clean)
+				goto post_buffs_out;
+		} else {
+			/* Give back pointer to DMA buffer */
+			cq->bi.rx_buff[ntp] = buffs[i];
+			i++;
+
+			if (i >= *buff_count)
+				buffs_avail = false;
+		}
+
+fill_desc:
+		desc->flags =
+			cpu_to_le16(IECM_CTLQ_FLAG_BUF | IECM_CTLQ_FLAG_RD);
+
+		/* Post buffers to descriptor */
+		desc->datalen = cpu_to_le16(cq->bi.rx_buff[ntp]->size);
+		desc->params.indirect.addr_high =
+			cpu_to_le32(upper_32_bits(cq->bi.rx_buff[ntp]->pa));
+		desc->params.indirect.addr_low =
+			cpu_to_le32(lower_32_bits(cq->bi.rx_buff[ntp]->pa));
+
+		ntp++;
+		if (ntp == cq->ring_size)
+			ntp = 0;
+	}
+
+post_buffs_out:
+	/* Only update tail if buffers were actually posted */
+	if (cq->next_to_post != ntp) {
+		if (ntp)
+			/* Update next_to_post to ntp - 1 since current ntp
+			 * will not have a buffer
+			 */
+			cq->next_to_post = ntp - 1;
+		else
+			/* Wrap to end of end ring since current ntp is 0 */
+			cq->next_to_post = cq->ring_size - 1;
+
+		wr32(hw, cq->reg.tail, cq->next_to_post);
+	}
+
+	mutex_unlock(&cq->cq_lock);
+
+	/* return the number of buffers that were not posted */
+	*buff_count = *buff_count - i;
+
+	return status;
+}
+
+/**
+ * iecm_ctlq_recv - receive control queue message call back
+ * @cq: pointer to control queue handle to receive on
+ * @num_q_msg: (input|output) input number of messages that should be received;
+ * output number of messages actually received
+ * @q_msg: (output) array of received control queue messages on this q;
+ * needs to be pre-allocated by caller for as many messages as requested
+ *
+ * Called by interrupt handler or polling mechanism. Caller is expected
+ * to free buffers
+ */
+int iecm_ctlq_recv(struct iecm_ctlq_info *cq, u16 *num_q_msg,
+		   struct iecm_ctlq_msg *q_msg)
+{
+	u16 num_to_clean, ntc, ret_val, flags;
+	struct iecm_ctlq_desc *desc;
+	int ret_code = 0;
+	u16 i = 0;
+
+	if (!cq || !cq->ring_size)
+		return -ENOBUFS;
+
+	if (*num_q_msg == 0)
+		return 0;
+	else if (*num_q_msg > cq->ring_size)
+		return -EBADR;
+
+	/* take the lock before we start messing with the ring */
+	mutex_lock(&cq->cq_lock);
+
+	ntc = cq->next_to_clean;
+
+	num_to_clean = *num_q_msg;
+
+	for (i = 0; i < num_to_clean; i++) {
+		u64 msg_cookie;
+
+		/* Fetch next descriptor and check if marked as done */
+		desc = IECM_CTLQ_DESC(cq, ntc);
+		flags = le16_to_cpu(desc->flags);
+
+		if (!(flags & IECM_CTLQ_FLAG_DD))
+			break;
+
+		ret_val = le16_to_cpu(desc->ret_val);
+
+		q_msg[i].vmvf_type = (flags &
+				      (IECM_CTLQ_FLAG_FTYPE_VM |
+				       IECM_CTLQ_FLAG_FTYPE_PF)) >>
+				      IECM_CTLQ_FLAG_FTYPE_S;
+
+		if (flags & IECM_CTLQ_FLAG_ERR)
+			ret_code = -EBADMSG;
+
+		msg_cookie = (u64)le32_to_cpu(desc->cookie_high) << 32;
+		msg_cookie |= (u64)le32_to_cpu(desc->cookie_low);
+		memcpy(&q_msg[i].cookie, &msg_cookie, sizeof(u64));
+
+		q_msg[i].opcode = le16_to_cpu(desc->opcode);
+		q_msg[i].data_len = le16_to_cpu(desc->datalen);
+		q_msg[i].status = ret_val;
+
+		if (desc->datalen) {
+			memcpy(q_msg[i].ctx.indirect.context,
+			       &desc->params.indirect, IECM_INDIRECT_CTX_SIZE);
+
+			/* Assign pointer to dma buffer to ctlq_msg array
+			 * to be given to upper layer
+			 */
+			q_msg[i].ctx.indirect.payload = cq->bi.rx_buff[ntc];
+
+			/* Zero out pointer to DMA buffer info;
+			 * will be repopulated by post buffers API
+			 */
+			cq->bi.rx_buff[ntc] = NULL;
+		} else {
+			memcpy(q_msg[i].ctx.direct, desc->params.raw,
+			       IECM_DIRECT_CTX_SIZE);
+		}
+
+		/* Zero out stale data in descriptor */
+		memset(desc, 0, sizeof(struct iecm_ctlq_desc));
+
+		ntc++;
+		if (ntc == cq->ring_size)
+			ntc = 0;
+	};
+
+	cq->next_to_clean = ntc;
+
+	mutex_unlock(&cq->cq_lock);
+
+	*num_q_msg = i;
+	if (*num_q_msg == 0)
+		ret_code = -ENOMSG;
+
+	return ret_code;
+}
diff --git a/drivers/net/ethernet/intel/iecm/iecm_controlq_setup.c b/drivers/net/ethernet/intel/iecm/iecm_controlq_setup.c
new file mode 100644
index 000000000000..a36fc88d6bb5
--- /dev/null
+++ b/drivers/net/ethernet/intel/iecm/iecm_controlq_setup.c
@@ -0,0 +1,175 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (c) 2020, Intel Corporation. */
+
+#include "iecm_controlq.h"
+
+/**
+ * iecm_ctlq_alloc_desc_ring - Allocate Control Queue (CQ) rings
+ * @hw: pointer to hw struct
+ * @cq: pointer to the specific Control queue
+ */
+static int
+iecm_ctlq_alloc_desc_ring(struct iecm_hw *hw,
+			  struct iecm_ctlq_info *cq)
+{
+	size_t size = cq->ring_size * sizeof(struct iecm_ctlq_desc);
+
+	cq->desc_ring.va = iecm_alloc_dma_mem(hw, &cq->desc_ring, size);
+	if (!cq->desc_ring.va)
+		return -ENOMEM;
+
+	return 0;
+}
+
+/**
+ * iecm_ctlq_alloc_bufs - Allocate Control Queue (CQ) buffers
+ * @hw: pointer to hw struct
+ * @cq: pointer to the specific Control queue
+ *
+ * Allocate the buffer head for all control queues, and if it's a receive
+ * queue, allocate DMA buffers
+ */
+static int iecm_ctlq_alloc_bufs(struct iecm_hw *hw,
+				struct iecm_ctlq_info *cq)
+{
+	int i = 0;
+
+	/* Do not allocate DMA buffers for transmit queues */
+	if (cq->cq_type == IECM_CTLQ_TYPE_MAILBOX_TX)
+		return 0;
+
+	/* We'll be allocating the buffer info memory first, then we can
+	 * allocate the mapped buffers for the event processing
+	 */
+	cq->bi.rx_buff = kcalloc(cq->ring_size, sizeof(struct iecm_dma_mem *),
+				 GFP_KERNEL);
+	if (!cq->bi.rx_buff)
+		return -ENOMEM;
+
+	/* allocate the mapped buffers (except for the last one) */
+	for (i = 0; i < cq->ring_size - 1; i++) {
+		struct iecm_dma_mem *bi;
+		int num = 1; /* number of iecm_dma_mem to be allocated */
+
+		cq->bi.rx_buff[i] = kcalloc(num, sizeof(struct iecm_dma_mem),
+					    GFP_KERNEL);
+		if (!cq->bi.rx_buff[i])
+			goto unwind_alloc_cq_bufs;
+
+		bi = cq->bi.rx_buff[i];
+
+		bi->va = iecm_alloc_dma_mem(hw, bi, cq->buf_size);
+		if (!bi->va) {
+			/* unwind will not free the failed entry */
+			kfree(cq->bi.rx_buff[i]);
+			goto unwind_alloc_cq_bufs;
+		}
+	}
+
+	return 0;
+
+unwind_alloc_cq_bufs:
+	/* don't try to free the one that failed... */
+	i--;
+	for (; i >= 0; i--) {
+		iecm_free_dma_mem(hw, cq->bi.rx_buff[i]);
+		kfree(cq->bi.rx_buff[i]);
+	}
+	kfree(cq->bi.rx_buff);
+
+	return -ENOMEM;
+}
+
+/**
+ * iecm_ctlq_free_desc_ring - Free Control Queue (CQ) rings
+ * @hw: pointer to hw struct
+ * @cq: pointer to the specific Control queue
+ *
+ * This assumes the posted send buffers have already been cleaned
+ * and de-allocated
+ */
+static void iecm_ctlq_free_desc_ring(struct iecm_hw *hw,
+				     struct iecm_ctlq_info *cq)
+{
+	iecm_free_dma_mem(hw, &cq->desc_ring);
+}
+
+/**
+ * iecm_ctlq_free_bufs - Free CQ buffer info elements
+ * @hw: pointer to hw struct
+ * @cq: pointer to the specific Control queue
+ *
+ * Free the DMA buffers for RX queues, and DMA buffer header for both RX and TX
+ * queues.  The upper layers are expected to manage freeing of TX DMA buffers
+ */
+static void iecm_ctlq_free_bufs(struct iecm_hw *hw, struct iecm_ctlq_info *cq)
+{
+	void *bi;
+
+	if (cq->cq_type == IECM_CTLQ_TYPE_MAILBOX_RX) {
+		int i;
+
+		/* free DMA buffers for rx queues*/
+		for (i = 0; i < cq->ring_size; i++) {
+			if (cq->bi.rx_buff[i]) {
+				iecm_free_dma_mem(hw, cq->bi.rx_buff[i]);
+				kfree(cq->bi.rx_buff[i]);
+			}
+		}
+
+		bi = (void *)cq->bi.rx_buff;
+	} else {
+		bi = (void *)cq->bi.tx_msg;
+	}
+
+	/* free the buffer header */
+	kfree(bi);
+}
+
+/**
+ * iecm_ctlq_dealloc_ring_res - Free memory allocated for control queue
+ * @hw: pointer to hw struct
+ * @cq: pointer to the specific Control queue
+ *
+ * Free the memory used by the ring, buffers and other related structures
+ */
+void iecm_ctlq_dealloc_ring_res(struct iecm_hw *hw, struct iecm_ctlq_info *cq)
+{
+	/* free ring buffers and the ring itself */
+	iecm_ctlq_free_bufs(hw, cq);
+	iecm_ctlq_free_desc_ring(hw, cq);
+}
+
+/**
+ * iecm_ctlq_alloc_ring_res - allocate memory for descriptor ring and bufs
+ * @hw: pointer to hw struct
+ * @cq: pointer to control queue struct
+ *
+ * Do *NOT* hold the lock when calling this as the memory allocation routines
+ * called are not going to be atomic context safe
+ */
+int iecm_ctlq_alloc_ring_res(struct iecm_hw *hw, struct iecm_ctlq_info *cq)
+{
+	int ret_code;
+
+	/* verify input for valid configuration */
+	if (!cq->ring_size || !cq->buf_size)
+		return -EINVAL;
+
+	/* allocate the ring memory */
+	ret_code = iecm_ctlq_alloc_desc_ring(hw, cq);
+	if (ret_code)
+		return ret_code;
+
+	/* allocate buffers in the rings */
+	ret_code = iecm_ctlq_alloc_bufs(hw, cq);
+	if (ret_code)
+		goto iecm_init_cq_free_ring;
+
+	/* success! */
+	return 0;
+
+iecm_init_cq_free_ring:
+	iecm_free_dma_mem(hw, &cq->desc_ring);
+	return ret_code;
+}
diff --git a/drivers/net/ethernet/intel/iecm/iecm_lib.c b/drivers/net/ethernet/intel/iecm/iecm_lib.c
index e6d0b418a27f..64cdbce2c842 100644
--- a/drivers/net/ethernet/intel/iecm/iecm_lib.c
+++ b/drivers/net/ethernet/intel/iecm/iecm_lib.c
@@ -5,6 +5,25 @@
 
 #include "iecm.h"
 
+/**
+ * iecm_cfg_hw - Initialize HW struct
+ * @adapter: adapter to setup hw struct for
+ *
+ * Returns 0 on success, negative on failure
+ */
+static int iecm_cfg_hw(struct iecm_adapter *adapter)
+{
+	struct pci_dev *pdev = adapter->pdev;
+	struct iecm_hw *hw = &adapter->hw;
+
+	hw->hw_addr = pcim_iomap_table(pdev)[IECM_BAR0];
+	if (!hw->hw_addr)
+		return -EIO;
+	hw->back = adapter;
+
+	return 0;
+}
+
 /**
  * iecm_statistics_task - Delayed task to get statistics over mailbox
  * @work: work_struct handle to our data
@@ -39,6 +58,32 @@ static void iecm_init_task(struct work_struct *work)
 	/* stub */
 }
 
+/**
+ * iecm_api_init - Initialize and verify device API
+ * @adapter: driver specific private structure
+ *
+ * Returns 0 on success, negative on failure
+ */
+static int iecm_api_init(struct iecm_adapter *adapter)
+{
+	struct iecm_reg_ops *reg_ops = &adapter->dev_ops.reg_ops;
+	struct pci_dev *pdev = adapter->pdev;
+
+	if (!adapter->dev_ops.reg_ops_init) {
+		dev_err(&pdev->dev, "Invalid device, register API init not defined\n");
+		return -EINVAL;
+	}
+	adapter->dev_ops.reg_ops_init(adapter);
+	if (!(reg_ops->ctlq_reg_init && reg_ops->intr_reg_init &&
+	      reg_ops->mb_intr_reg_init && reg_ops->reset_reg_init &&
+	      reg_ops->trigger_reset)) {
+		dev_err(&pdev->dev, "Invalid device, missing one or more register functions\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
 /**
  * iecm_deinit_task - Device deinit routine
  * @adapter: Driver specific private structue
@@ -51,13 +96,108 @@ static void iecm_deinit_task(struct iecm_adapter *adapter)
 	/* stub */
 }
 
+/**
+ * iecm_check_reset_complete - check that reset is complete
+ * @hw: pointer to hw struct
+ * @reset_reg: struct with reset registers
+ *
+ * Returns 0 if device is ready to use, or -EBUSY if it's in reset.
+ **/
+static int iecm_check_reset_complete(struct iecm_hw *hw,
+				     struct iecm_reset_reg *reset_reg)
+{
+	struct iecm_adapter *adapter = (struct iecm_adapter *)hw->back;
+	int i;
+
+	for (i = 0; i < 2000; i++) {
+		u32 reg_val = rd32(hw, reset_reg->rstat);
+
+		/* 0xFFFFFFFF might be read if other side hasn't cleared the
+		 * register for us yet and 0xFFFFFFFF is not a valid value for
+		 * the register, so treat that as invalid.
+		 */
+		if (reg_val != 0xFFFFFFFF && (reg_val & reset_reg->rstat_m))
+			return 0;
+		usleep_range(5000, 10000);
+	}
+
+	dev_warn(&adapter->pdev->dev, "Device reset timeout!\n");
+	return -EBUSY;
+}
+
+/**
+ * iecm_init_hard_reset - Initiate a hardware reset
+ * @adapter: Driver specific private structure
+ *
+ * Deallocate the vports and all the resources associated with them and
+ * reallocate. Also reinitialize the mailbox. Return 0 on success,
+ * negative on failure.
+ */
+static int iecm_init_hard_reset(struct iecm_adapter *adapter)
+{
+	int err = 0;
+
+	mutex_lock(&adapter->reset_lock);
+
+	/* Prepare for reset */
+	if (test_and_clear_bit(__IECM_HR_DRV_LOAD, adapter->flags)) {
+		adapter->dev_ops.reg_ops.trigger_reset(adapter,
+						       __IECM_HR_DRV_LOAD);
+	} else if (test_and_clear_bit(__IECM_HR_FUNC_RESET, adapter->flags)) {
+		bool is_reset = iecm_is_reset_detected(adapter);
+
+		if (adapter->state == __IECM_UP)
+			set_bit(__IECM_UP_REQUESTED, adapter->flags);
+		iecm_deinit_task(adapter);
+		if (!is_reset)
+			adapter->dev_ops.reg_ops.trigger_reset(adapter,
+							       __IECM_HR_FUNC_RESET);
+		iecm_deinit_dflt_mbx(adapter);
+	} else if (test_and_clear_bit(__IECM_HR_CORE_RESET, adapter->flags)) {
+		if (adapter->state == __IECM_UP)
+			set_bit(__IECM_UP_REQUESTED, adapter->flags);
+		iecm_deinit_task(adapter);
+	} else {
+		dev_err(&adapter->pdev->dev, "Unhandled hard reset cause\n");
+		err = -EBADRQC;
+		goto handle_err;
+	}
+
+	/* Wait for reset to complete */
+	err = iecm_check_reset_complete(&adapter->hw, &adapter->reset_reg);
+	if (err) {
+		dev_err(&adapter->pdev->dev, "The driver was unable to contact the device's firmware.  Check that the FW is running. Driver state=%u\n",
+			adapter->state);
+		goto handle_err;
+	}
+
+	/* Reset is complete and so start building the driver resources again */
+	err = iecm_init_dflt_mbx(adapter);
+	if (err) {
+		dev_err(&adapter->pdev->dev, "Failed to initialize default mailbox: %d\n",
+			err);
+	}
+handle_err:
+	mutex_unlock(&adapter->reset_lock);
+	return err;
+}
+
 /**
  * iecm_vc_event_task - Handle virtchannel event logic
  * @work: work queue struct
  */
 static void iecm_vc_event_task(struct work_struct *work)
 {
-	/* stub */
+	struct iecm_adapter *adapter = container_of(work,
+						    struct iecm_adapter,
+						    vc_event_task.work);
+
+	if (test_bit(__IECM_HR_CORE_RESET, adapter->flags) ||
+	    test_bit(__IECM_HR_FUNC_RESET, adapter->flags) ||
+	    test_bit(__IECM_HR_DRV_LOAD, adapter->flags)) {
+		set_bit(__IECM_HR_RESET_IN_PROG, adapter->flags);
+		iecm_init_hard_reset(adapter);
+	}
 }
 
 /**
@@ -75,6 +215,11 @@ int iecm_probe(struct pci_dev *pdev,
 	int err;
 
 	adapter->pdev = pdev;
+	err = iecm_api_init(adapter);
+	if (err) {
+		dev_err(&pdev->dev, "Device API is incorrectly configured\n");
+		return err;
+	}
 
 	err = pcim_enable_device(pdev);
 	if (err)
@@ -147,6 +292,20 @@ int iecm_probe(struct pci_dev *pdev,
 		goto err_netdev_alloc;
 	}
 
+	err = iecm_vport_params_buf_alloc(adapter);
+	if (err) {
+		dev_err(&pdev->dev, "Failed to alloc vport params buffer: %d\n",
+			err);
+		goto err_mb_res;
+	}
+
+	err = iecm_cfg_hw(adapter);
+	if (err) {
+		dev_err(&pdev->dev, "Failed to configure HW structure for adapter: %d\n",
+			err);
+		goto err_cfg_hw;
+	}
+
 	mutex_init(&adapter->sw_mutex);
 	mutex_init(&adapter->reset_lock);
 	init_waitqueue_head(&adapter->vchnl_wq);
@@ -166,11 +325,16 @@ int iecm_probe(struct pci_dev *pdev,
 	INIT_DELAYED_WORK(&adapter->init_task, iecm_init_task);
 	INIT_DELAYED_WORK(&adapter->vc_event_task, iecm_vc_event_task);
 
+	adapter->dev_ops.reg_ops.reset_reg_init(&adapter->reset_reg);
 	set_bit(__IECM_HR_DRV_LOAD, adapter->flags);
 	queue_delayed_work(adapter->vc_event_wq, &adapter->vc_event_task,
 			   msecs_to_jiffies(10 * (pdev->devfn & 0x07)));
 
 	return 0;
+err_cfg_hw:
+	iecm_vport_params_buf_rel(adapter);
+err_mb_res:
+	kfree(adapter->netdevs);
 err_netdev_alloc:
 	kfree(adapter->vports);
 err_vport_alloc:
@@ -214,6 +378,7 @@ void iecm_remove(struct pci_dev *pdev)
 	cancel_delayed_work_sync(&adapter->vc_event_task);
 	iecm_deinit_task(adapter);
 	iecm_del_user_cfg_data(adapter);
+	iecm_deinit_dflt_mbx(adapter);
 	msleep(20);
 	destroy_workqueue(adapter->serv_wq);
 	destroy_workqueue(adapter->vc_event_wq);
@@ -222,6 +387,7 @@ void iecm_remove(struct pci_dev *pdev)
 	kfree(adapter->vports);
 	kfree(adapter->netdevs);
 	kfree(adapter->vlan_caps);
+	iecm_vport_params_buf_rel(adapter);
 	mutex_destroy(&adapter->sw_mutex);
 	mutex_destroy(&adapter->reset_lock);
 	pci_disable_pcie_error_reporting(pdev);
@@ -229,3 +395,26 @@ void iecm_remove(struct pci_dev *pdev)
 	pci_disable_device(pdev);
 }
 EXPORT_SYMBOL(iecm_remove);
+
+void *iecm_alloc_dma_mem(struct iecm_hw *hw, struct iecm_dma_mem *mem, u64 size)
+{
+	struct iecm_adapter *adapter = (struct iecm_adapter *)hw->back;
+	size_t sz = ALIGN(size, 4096);
+
+	mem->va = dma_alloc_coherent(&adapter->pdev->dev, sz,
+				     &mem->pa, GFP_KERNEL | __GFP_ZERO);
+	mem->size = size;
+
+	return mem->va;
+}
+
+void iecm_free_dma_mem(struct iecm_hw *hw, struct iecm_dma_mem *mem)
+{
+	struct iecm_adapter *adapter = (struct iecm_adapter *)hw->back;
+
+	dma_free_coherent(&adapter->pdev->dev, mem->size,
+			  mem->va, mem->pa);
+	mem->size = 0;
+	mem->va = NULL;
+	mem->pa = 0;
+}
diff --git a/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
new file mode 100644
index 000000000000..b8f54b8c700a
--- /dev/null
+++ b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
@@ -0,0 +1,172 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (C) 2019 Intel Corporation */
+
+#include "iecm.h"
+
+/**
+ * iecm_mb_clean - Reclaim the send mailbox queue entries
+ * @adapter: Driver specific private structure
+ *
+ * Reclaim the send mailbox queue entries to be used to send further messages
+ *
+ * Returns 0 on success, negative on failure
+ */
+static int iecm_mb_clean(struct iecm_adapter *adapter)
+{
+	u16 i, num_q_msg = IECM_DFLT_MBX_Q_LEN;
+	struct iecm_ctlq_msg **q_msg;
+	struct iecm_dma_mem *dma_mem;
+	int err = 0;
+
+	q_msg = kcalloc(num_q_msg, sizeof(struct iecm_ctlq_msg *), GFP_KERNEL);
+	if (!q_msg)
+		return -ENOMEM;
+
+	err = iecm_ctlq_clean_sq(adapter->hw.asq, &num_q_msg, q_msg);
+	if (err)
+		goto error;
+
+	for (i = 0; i < num_q_msg; i++) {
+		dma_mem = q_msg[i]->ctx.indirect.payload;
+		if (dma_mem)
+			dmam_free_coherent(&adapter->pdev->dev, dma_mem->size,
+					   dma_mem->va, dma_mem->pa);
+		kfree(q_msg[i]);
+		kfree(dma_mem);
+	}
+error:
+	kfree(q_msg);
+	return err;
+}
+
+/**
+ * iecm_find_ctlq - Given a type and id, find ctlq info
+ * @hw: hardware struct
+ * @type: type of ctrlq to find
+ * @id: ctlq id to find
+ *
+ * Returns pointer to found ctlq info struct, NULL otherwise.
+ */
+static struct iecm_ctlq_info *iecm_find_ctlq(struct iecm_hw *hw,
+					     enum iecm_ctlq_type type, int id)
+{
+	struct iecm_ctlq_info *cq, *tmp;
+
+	list_for_each_entry_safe(cq, tmp, &hw->cq_list_head, cq_list) {
+		if (cq->q_id == id && cq->cq_type == type)
+			return cq;
+	}
+
+	return NULL;
+}
+
+/**
+ * iecm_init_dflt_mbx - Setup default mailbox parameters and make request
+ * @adapter: adapter info struct
+ *
+ * Returns 0 on success, negative otherwise
+ */
+int iecm_init_dflt_mbx(struct iecm_adapter *adapter)
+{
+	struct iecm_ctlq_create_info ctlq_info[] = {
+		{
+			.type = IECM_CTLQ_TYPE_MAILBOX_TX,
+			.id = IECM_DFLT_MBX_ID,
+			.len = IECM_DFLT_MBX_Q_LEN,
+			.buf_size = IECM_DFLT_MBX_BUF_SIZE
+		},
+		{
+			.type = IECM_CTLQ_TYPE_MAILBOX_RX,
+			.id = IECM_DFLT_MBX_ID,
+			.len = IECM_DFLT_MBX_Q_LEN,
+			.buf_size = IECM_DFLT_MBX_BUF_SIZE
+		}
+	};
+	struct iecm_hw *hw = &adapter->hw;
+	int err;
+
+	adapter->dev_ops.reg_ops.ctlq_reg_init(ctlq_info);
+
+#define NUM_Q 2
+	err = iecm_ctlq_init(hw, NUM_Q, ctlq_info);
+	if (err)
+		return err;
+
+	hw->asq = iecm_find_ctlq(hw, IECM_CTLQ_TYPE_MAILBOX_TX,
+				 IECM_DFLT_MBX_ID);
+	hw->arq = iecm_find_ctlq(hw, IECM_CTLQ_TYPE_MAILBOX_RX,
+				 IECM_DFLT_MBX_ID);
+
+	if (!hw->asq || !hw->arq) {
+		iecm_ctlq_deinit(hw);
+		return -ENOENT;
+	}
+	adapter->state = __IECM_STARTUP;
+	/* Skew the delay for init tasks for each function based on fn number
+	 * to prevent every function from making the same call simulatenously.
+	 */
+	queue_delayed_work(adapter->init_wq, &adapter->init_task,
+			   msecs_to_jiffies(5 * (adapter->pdev->devfn & 0x07)));
+	return 0;
+}
+
+/**
+ * iecm_deinit_dflt_mbx - Free up ctlqs setup
+ * @adapter: Driver specific private data structure
+ */
+void iecm_deinit_dflt_mbx(struct iecm_adapter *adapter)
+{
+	if (adapter->hw.arq && adapter->hw.asq) {
+		iecm_mb_clean(adapter);
+		iecm_ctlq_deinit(&adapter->hw);
+	}
+	adapter->hw.arq = NULL;
+	adapter->hw.asq = NULL;
+}
+
+/**
+ * iecm_vport_params_buf_alloc - Allocate memory for MailBox resources
+ * @adapter: Driver specific private data structure
+ *
+ * Will alloc memory to hold the vport parameters received on MailBox
+ */
+int iecm_vport_params_buf_alloc(struct iecm_adapter *adapter)
+{
+	adapter->vport_params_reqd = kcalloc(IECM_MAX_NUM_VPORTS,
+					     sizeof(*adapter->vport_params_reqd),
+					     GFP_KERNEL);
+	if (!adapter->vport_params_reqd)
+		return -ENOMEM;
+
+	adapter->vport_params_recvd = kcalloc(IECM_MAX_NUM_VPORTS,
+					      sizeof(*adapter->vport_params_recvd),
+					      GFP_KERNEL);
+	if (!adapter->vport_params_recvd) {
+		kfree(adapter->vport_params_reqd);
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+/**
+ * iecm_vport_params_buf_rel - Release memory for MailBox resources
+ * @adapter: Driver specific private data structure
+ *
+ * Will release memory to hold the vport parameters received on MailBox
+ */
+void iecm_vport_params_buf_rel(struct iecm_adapter *adapter)
+{
+	int i = 0;
+
+	for (i = 0; i < IECM_MAX_NUM_VPORTS; i++) {
+		kfree(adapter->vport_params_recvd[i]);
+		kfree(adapter->vport_params_reqd[i]);
+	}
+
+	kfree(adapter->vport_params_recvd);
+	kfree(adapter->vport_params_reqd);
+
+	kfree(adapter->caps);
+	kfree(adapter->config_data.req_qs_chunks);
+}
diff --git a/drivers/net/ethernet/intel/include/iecm.h b/drivers/net/ethernet/intel/include/iecm.h
index e19e014e9817..ca9029224e06 100644
--- a/drivers/net/ethernet/intel/include/iecm.h
+++ b/drivers/net/ethernet/intel/include/iecm.h
@@ -12,15 +12,33 @@
 #include <linux/dim.h>
 
 #include "iecm_txrx.h"
+#include "iecm_controlq.h"
 
 #define IECM_BAR0			0
 #define IECM_NO_FREE_SLOT		0xffff
 
+/* Default Mailbox settings */
+#define IECM_DFLT_MBX_BUF_SIZE		(4 * 1024)
+#define IECM_NUM_QCTX_PER_MSG		3
+#define IECM_NUM_FILTERS_PER_MSG	20
+#define IECM_VLANS_PER_MSG \
+	((IECM_DFLT_MBX_BUF_SIZE - sizeof(struct virtchnl_vlan_filter_list)) \
+	 / sizeof(u16))
+#define IECM_DFLT_MBX_Q_LEN		64
+#define IECM_DFLT_MBX_ID		-1
+/* maximum number of times to try before resetting mailbox */
+#define IECM_MB_MAX_ERR			20
+#define IECM_NUM_CHUNKS_PER_MSG(a, b)	((IECM_DFLT_MBX_BUF_SIZE - (a)) / (b))
+
 #define IECM_MAX_NUM_VPORTS		1
 
 /* available message levels */
 #define IECM_AVAIL_NETIF_M (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK)
 
+/* Forward declaration */
+struct iecm_adapter;
+struct iecm_vport;
+
 enum iecm_state {
 	__IECM_STARTUP,
 	__IECM_VER_CHECK,
@@ -77,6 +95,22 @@ struct iecm_reset_reg {
 	u32 rstat_m;
 };
 
+/* product specific register API */
+struct iecm_reg_ops {
+	void (*ctlq_reg_init)(struct iecm_ctlq_create_info *cq);
+	int (*intr_reg_init)(struct iecm_vport *vport);
+	void (*mb_intr_reg_init)(struct iecm_adapter *adapter);
+	void (*reset_reg_init)(struct iecm_reset_reg *reset_reg);
+	void (*trigger_reset)(struct iecm_adapter *adapter,
+			      enum iecm_flags trig_cause);
+};
+
+struct iecm_dev_ops {
+	void (*reg_ops_init)(struct iecm_adapter *adapter);
+	void (*crc_enable)(u64 *td_cmd);
+	struct iecm_reg_ops reg_ops;
+};
+
 /* stub */
 struct iecm_vport {
 };
@@ -124,6 +158,7 @@ struct iecm_adapter {
 	DECLARE_BITMAP(flags, __IECM_FLAGS_NBITS);
 	struct mutex reset_lock; /* lock to protect reset flows */
 	struct iecm_reset_reg reset_reg;
+	struct iecm_hw hw;
 
 	u16 num_req_msix;
 	u16 num_msix_entries;
@@ -156,6 +191,7 @@ struct iecm_adapter {
 	wait_queue_head_t vchnl_wq;
 	wait_queue_head_t sw_marker_wq;
 	struct iecm_rss_data rss_data;
+	struct iecm_dev_ops dev_ops;
 	s32 link_speed;
 	/* This is only populated if the VIRTCHNL_VF_CAP_ADV_LINK_SPEED is set
 	 * in vf_res->vf_cap_flags. This field should be used going forward and
@@ -179,8 +215,24 @@ struct iecm_adapter {
 	spinlock_t fdir_fltr_list_lock;
 };
 
+/**
+ * iecm_is_reset_detected - check if we were reset at some point
+ * @adapter: driver specific private structure
+ *
+ * Returns true if we are either in reset currently or were previously reset.
+ */
+static inline bool iecm_is_reset_detected(struct iecm_adapter *adapter)
+{
+	return !(rd32(&adapter->hw, adapter->hw.arq->reg.len) &
+		 adapter->hw.arq->reg.len_ena_mask);
+}
+
 int iecm_probe(struct pci_dev *pdev,
 	       const struct pci_device_id __always_unused *ent,
 	       struct iecm_adapter *adapter);
 void iecm_remove(struct pci_dev *pdev);
+int iecm_init_dflt_mbx(struct iecm_adapter *adapter);
+void iecm_deinit_dflt_mbx(struct iecm_adapter *adapter);
+int iecm_vport_params_buf_alloc(struct iecm_adapter *adapter);
+void iecm_vport_params_buf_rel(struct iecm_adapter *adapter);
 #endif /* !_IECM_H_ */
diff --git a/drivers/net/ethernet/intel/include/iecm_controlq.h b/drivers/net/ethernet/intel/include/iecm_controlq.h
new file mode 100644
index 000000000000..f2539baa2ce1
--- /dev/null
+++ b/drivers/net/ethernet/intel/include/iecm_controlq.h
@@ -0,0 +1,117 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (c) 2020, Intel Corporation. */
+
+#ifndef _IECM_CONTROLQ_H_
+#define _IECM_CONTROLQ_H_
+
+#include <linux/slab.h>
+
+#include "iecm_controlq_api.h"
+
+/* Maximum buffer lengths for all control queue types */
+#define IECM_CTLQ_MAX_RING_SIZE 1024
+#define IECM_CTLQ_MAX_BUF_LEN	4096
+
+#define IECM_CTLQ_DESC(R, i) \
+	(&(((struct iecm_ctlq_desc *)((R)->desc_ring.va))[i]))
+
+#define IECM_CTLQ_DESC_UNUSED(R) \
+	((u16)((((R)->next_to_clean > (R)->next_to_use) ? 0 : (R)->ring_size) + \
+	      (R)->next_to_clean - (R)->next_to_use - 1))
+
+/* Control Queue default settings */
+#define IECM_CTRL_SQ_CMD_TIMEOUT	250  /* msecs */
+
+struct iecm_ctlq_desc {
+	__le16	flags;
+	__le16	opcode;
+	__le16	datalen;	/* 0 for direct commands */
+	union {
+		__le16 ret_val;
+		__le16 pfid_vfid;
+#define IECM_CTLQ_DESC_VF_ID_S	0
+#define IECM_CTLQ_DESC_VF_ID_M	(0x7FF << IECM_CTLQ_DESC_VF_ID_S)
+#define IECM_CTLQ_DESC_PF_ID_S	11
+#define IECM_CTLQ_DESC_PF_ID_M	(0x1F << IECM_CTLQ_DESC_PF_ID_S)
+	};
+	__le32 cookie_high;
+	__le32 cookie_low;
+	union {
+		struct {
+			__le32 param0;
+			__le32 param1;
+			__le32 param2;
+			__le32 param3;
+		} direct;
+		struct {
+			__le32 param0;
+			__le32 param1;
+			__le32 addr_high;
+			__le32 addr_low;
+		} indirect;
+		u8 raw[16];
+	} params;
+};
+
+/* Flags sub-structure
+ * |0  |1  |2  |3  |4  |5  |6  |7  |8  |9  |10 |11 |12 |13 |14 |15 |
+ * |DD |CMP|ERR|  * RSV *  |FTYPE  | *RSV* |RD |VFC|BUF|  * RSV *  |
+ */
+/* command flags and offsets */
+#define IECM_CTLQ_FLAG_DD_S	0
+#define IECM_CTLQ_FLAG_CMP_S	1
+#define IECM_CTLQ_FLAG_ERR_S	2
+#define IECM_CTLQ_FLAG_FTYPE_S	6
+#define IECM_CTLQ_FLAG_RD_S	10
+#define IECM_CTLQ_FLAG_VFC_S	11
+#define IECM_CTLQ_FLAG_BUF_S	12
+
+#define IECM_CTLQ_FLAG_DD	BIT(IECM_CTLQ_FLAG_DD_S)	/* 0x1	  */
+#define IECM_CTLQ_FLAG_CMP	BIT(IECM_CTLQ_FLAG_CMP_S)	/* 0x2	  */
+#define IECM_CTLQ_FLAG_ERR	BIT(IECM_CTLQ_FLAG_ERR_S)	/* 0x4	  */
+#define IECM_CTLQ_FLAG_FTYPE_VM	BIT(IECM_CTLQ_FLAG_FTYPE_S)	/* 0x40	  */
+#define IECM_CTLQ_FLAG_FTYPE_PF	BIT(IECM_CTLQ_FLAG_FTYPE_S + 1)	/* 0x80   */
+#define IECM_CTLQ_FLAG_RD	BIT(IECM_CTLQ_FLAG_RD_S)	/* 0x400  */
+#define IECM_CTLQ_FLAG_VFC	BIT(IECM_CTLQ_FLAG_VFC_S)	/* 0x800  */
+#define IECM_CTLQ_FLAG_BUF	BIT(IECM_CTLQ_FLAG_BUF_S)	/* 0x1000 */
+
+struct iecm_mbxq_desc {
+	u8 pad[8];		/* CTLQ flags/opcode/len/retval fields */
+	u32 chnl_opcode;	/* avoid confusion with desc->opcode */
+	u32 chnl_retval;	/* ditto for desc->retval */
+	u32 pf_vf_id;		/* used by CP when sending to PF */
+};
+
+/* Define the APF hardware struct to replace other control structs as needed
+ * Align to ctlq_hw_info
+ */
+struct iecm_hw {
+	u8 __iomem *hw_addr;
+	u64 hw_addr_len;
+	void *back;
+
+	/* control queue - send and receive */
+	struct iecm_ctlq_info *asq;
+	struct iecm_ctlq_info *arq;
+
+	/* pci info */
+	u16 device_id;
+	u16 vendor_id;
+	u16 subsystem_device_id;
+	u16 subsystem_vendor_id;
+	u8 revision_id;
+	bool adapter_stopped;
+
+	struct list_head cq_list_head;
+};
+
+int iecm_ctlq_alloc_ring_res(struct iecm_hw *hw,
+			     struct iecm_ctlq_info *cq);
+
+void iecm_ctlq_dealloc_ring_res(struct iecm_hw *hw, struct iecm_ctlq_info *cq);
+
+/* prototype for functions used for dynamic memory allocation */
+void *iecm_alloc_dma_mem(struct iecm_hw *hw, struct iecm_dma_mem *mem,
+			 u64 size);
+void iecm_free_dma_mem(struct iecm_hw *hw, struct iecm_dma_mem *mem);
+#endif /* _IECM_CONTROLQ_H_ */
diff --git a/drivers/net/ethernet/intel/include/iecm_controlq_api.h b/drivers/net/ethernet/intel/include/iecm_controlq_api.h
new file mode 100644
index 000000000000..5f624f005d33
--- /dev/null
+++ b/drivers/net/ethernet/intel/include/iecm_controlq_api.h
@@ -0,0 +1,185 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (c) 2020, Intel Corporation. */
+
+#ifndef _IECM_CONTROLQ_API_H_
+#define _IECM_CONTROLQ_API_H_
+
+#include "iecm_mem.h"
+
+struct iecm_hw;
+
+/* Used for queue init, response and events */
+enum iecm_ctlq_type {
+	IECM_CTLQ_TYPE_MAILBOX_TX	= 0,
+	IECM_CTLQ_TYPE_MAILBOX_RX	= 1,
+	IECM_CTLQ_TYPE_CONFIG_TX	= 2,
+	IECM_CTLQ_TYPE_CONFIG_RX	= 3,
+	IECM_CTLQ_TYPE_EVENT_RX		= 4,
+	IECM_CTLQ_TYPE_RDMA_TX		= 5,
+	IECM_CTLQ_TYPE_RDMA_RX		= 6,
+	IECM_CTLQ_TYPE_RDMA_COMPL	= 7
+};
+
+/* Generic Control Queue Structures */
+struct iecm_ctlq_reg {
+	/* used for queue tracking */
+	u32 head;
+	u32 tail;
+	/* Below applies only to default mb (if present) */
+	u32 len;
+	u32 bah;
+	u32 bal;
+	u32 len_mask;
+	u32 len_ena_mask;
+	u32 head_mask;
+};
+
+/* Generic queue msg structure */
+struct iecm_ctlq_msg {
+	u16 vmvf_type; /* represents the source of the message on recv */
+#define IECM_VMVF_TYPE_VF 0
+#define IECM_VMVF_TYPE_VM 1
+#define IECM_VMVF_TYPE_PF 2
+	u16 opcode;
+	u16 data_len;	/* data_len = 0 when no payload is attached */
+	union {
+		u16 func_id;	/* when sending a message */
+		u16 status;	/* when receiving a message */
+	};
+	union {
+		struct {
+			u32 chnl_retval;
+			u32 chnl_opcode;
+		} mbx;
+	} cookie;
+	union {
+#define IECM_DIRECT_CTX_SIZE	16
+#define IECM_INDIRECT_CTX_SIZE	8
+		/* 16 bytes of context can be provided or 8 bytes of context
+		 * plus the address of a DMA buffer
+		 */
+		u8 direct[IECM_DIRECT_CTX_SIZE];
+		struct {
+			u8 context[IECM_INDIRECT_CTX_SIZE];
+			struct iecm_dma_mem *payload;
+		} indirect;
+	} ctx;
+};
+
+/* Generic queue info structures */
+/* MB, CONFIG and EVENT q do not have extended info */
+struct iecm_ctlq_create_info {
+	enum iecm_ctlq_type type;
+	int id; /* absolute queue offset passed as input
+		 * -1 for default mailbox if present
+		 */
+	u16 len; /* Queue length passed as input */
+	u16 buf_size; /* buffer size passed as input */
+	u64 base_address; /* output, HPA of the Queue start  */
+	struct iecm_ctlq_reg reg; /* registers accessed by ctlqs */
+
+	int ext_info_size;
+	void *ext_info; /* Specific to q type */
+};
+
+/* Control Queue information */
+struct iecm_ctlq_info {
+	struct list_head cq_list;
+
+	enum iecm_ctlq_type cq_type;
+	int q_id;
+	/* control queue lock */
+	struct mutex cq_lock;
+
+	/* used for interrupt processing */
+	u16 next_to_use;
+	u16 next_to_clean;
+	u16 next_to_post;		/* starting descriptor to post buffers
+					 * to after recev
+					 */
+
+	struct iecm_dma_mem desc_ring;	/* descriptor ring memory
+					 * iecm_dma_mem is defined in OSdep.h
+					 */
+	union {
+		struct iecm_dma_mem **rx_buff;
+		struct iecm_ctlq_msg **tx_msg;
+	} bi;
+
+	u16 buf_size;			/* queue buffer size */
+	u16 ring_size;			/* Number of descriptors */
+	struct iecm_ctlq_reg reg;	/* registers accessed by ctlqs */
+};
+
+/* PF/VF mailbox commands */
+enum iecm_mbx_opc {
+	/* iecm_mbq_opc_send_msg_to_pf:
+	 *	usage: used by PF or VF to send a message to its CPF
+	 *	target: RX queue and function ID of parent PF taken from HW
+	 */
+	iecm_mbq_opc_send_msg_to_pf		= 0x0801,
+
+	/* iecm_mbq_opc_send_msg_to_vf:
+	 *	usage: used by PF to send message to a VF
+	 *	target: VF control queue ID must be specified in descriptor
+	 */
+	iecm_mbq_opc_send_msg_to_vf		= 0x0802,
+
+	/* iecm_mbq_opc_send_msg_to_peer_pf:
+	 *	usage: used by any function to send message to any peer PF
+	 *	target: RX queue and host of parent PF taken from HW
+	 */
+	iecm_mbq_opc_send_msg_to_peer_pf	= 0x0803,
+
+	/* iecm_mbq_opc_send_msg_to_peer_drv:
+	 *	usage: used by any function to send message to any peer driver
+	 *	target: RX queue and target host must be specific in descriptor
+	 */
+	iecm_mbq_opc_send_msg_to_peer_drv	= 0x0804,
+};
+
+/* API support for control queue management */
+
+/* Will init all required q including default mb.  "q_info" is an array of
+ * create_info structs equal to the number of control queues to be created.
+ */
+int iecm_ctlq_init(struct iecm_hw *hw, u8 num_q,
+		   struct iecm_ctlq_create_info *q_info);
+
+/* Allocate and initialize a single control queue, which will be added to the
+ * control queue list; returns a handle to the created control queue
+ */
+int iecm_ctlq_add(struct iecm_hw *hw,
+		  struct iecm_ctlq_create_info *qinfo,
+		  struct iecm_ctlq_info **cq);
+
+/* Deinitialize and deallocate a single control queue */
+void iecm_ctlq_remove(struct iecm_hw *hw,
+		      struct iecm_ctlq_info *cq);
+
+/* Sends messages to HW and will also free the buffer*/
+int iecm_ctlq_send(struct iecm_hw *hw,
+		   struct iecm_ctlq_info *cq,
+		   u16 num_q_msg,
+		   struct iecm_ctlq_msg q_msg[]);
+
+/* Receives messages and called by interrupt handler/polling
+ * initiated by app/process. Also caller is supposed to free the buffers
+ */
+int iecm_ctlq_recv(struct iecm_ctlq_info *cq, u16 *num_q_msg,
+		   struct iecm_ctlq_msg *q_msg);
+
+/* Reclaims send descriptors on HW write back */
+int iecm_ctlq_clean_sq(struct iecm_ctlq_info *cq, u16 *clean_count,
+		       struct iecm_ctlq_msg *msg_status[]);
+
+/* Indicate RX buffers are done being processed */
+int iecm_ctlq_post_rx_buffs(struct iecm_hw *hw,
+			    struct iecm_ctlq_info *cq,
+			    u16 *buff_count,
+			    struct iecm_dma_mem **buffs);
+
+/* Will destroy all q including the default mb */
+int iecm_ctlq_deinit(struct iecm_hw *hw);
+
+#endif /* _IECM_CONTROLQ_API_H_ */
diff --git a/drivers/net/ethernet/intel/include/iecm_mem.h b/drivers/net/ethernet/intel/include/iecm_mem.h
new file mode 100644
index 000000000000..064dd6e10c24
--- /dev/null
+++ b/drivers/net/ethernet/intel/include/iecm_mem.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (C) 2019 Intel Corporation */
+
+#ifndef _IECM_MEM_H_
+#define _IECM_MEM_H_
+
+#include <linux/io.h>
+
+struct iecm_dma_mem {
+	void *va;
+	dma_addr_t pa;
+	size_t size;
+};
+
+#define wr32(a, reg, value)	writel((value), ((a)->hw_addr + (reg)))
+#define rd32(a, reg)		readl((a)->hw_addr + (reg))
+#define wr64(a, reg, value)	writeq((value), ((a)->hw_addr + (reg)))
+#define rd64(a, reg)		readq((a)->hw_addr + (reg))
+
+#endif /* _IECM_MEM_H_ */
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 05/19] iecm: add vport alloc and virtchnl messages
  2022-01-28  0:09 [Intel-wired-lan] [PATCH net-next 00/19] Add iecm and idpf Alan Brady
                   ` (3 preceding siblings ...)
  2022-01-28  0:09 ` [Intel-wired-lan] [PATCH net-next 04/19] iecm: add api_init and controlq init Alan Brady
@ 2022-01-28  0:09 ` Alan Brady
  2022-01-28  4:19     ` kernel test robot
  2022-01-28 12:32   ` Alexander Lobakin
  2022-01-28  0:09 ` [Intel-wired-lan] [PATCH net-next 06/19] iecm: add virtchnl messages for queues Alan Brady
                   ` (14 subsequent siblings)
  19 siblings, 2 replies; 89+ messages in thread
From: Alan Brady @ 2022-01-28  0:09 UTC (permalink / raw)
  To: intel-wired-lan

After handling hard reset, we end up in init task. This starts by
allocating and setting up a vport. To do that we need implement virtchnl
messages.

The virtchnl messages are also offloaded into function pointers so that a
device driver may override them. Here a default implementation is provided
for devices using virtchnl 2.0 but there exists the flexibility add
virtchnl 1.1 support through function pointers.

Signed-off-by: Phani Burra <phani.r.burra@intel.com>
Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
Signed-off-by: Alan Brady <alan.brady@intel.com>
---
 drivers/net/ethernet/intel/iecm/Makefile      |    4 +-
 drivers/net/ethernet/intel/iecm/iecm_lib.c    |  167 ++-
 drivers/net/ethernet/intel/iecm/iecm_txrx.c   |   22 +
 .../net/ethernet/intel/iecm/iecm_virtchnl.c   | 1299 +++++++++++++++++
 drivers/net/ethernet/intel/include/iecm.h     |  316 +++-
 .../net/ethernet/intel/include/iecm_txrx.h    |   94 ++
 6 files changed, 1898 insertions(+), 4 deletions(-)
 create mode 100644 drivers/net/ethernet/intel/iecm/iecm_txrx.c

diff --git a/drivers/net/ethernet/intel/iecm/Makefile b/drivers/net/ethernet/intel/iecm/Makefile
index db8fecb075a6..fcb49402334f 100644
--- a/drivers/net/ethernet/intel/iecm/Makefile
+++ b/drivers/net/ethernet/intel/iecm/Makefile
@@ -7,11 +7,13 @@
 
 obj-$(CONFIG_IECM) += iecm.o
 
-ccflags-y += -I$(srctree)/drivers/net/ethernet/intel/include
+ccflags-y += -I$(srctree)/drivers/net/ethernet/intel/include \
+	     -I$(srctree)/include/linux/avf
 
 iecm-y := \
 	iecm_lib.o \
 	iecm_virtchnl.o \
+	iecm_txrx.o \
 	iecm_controlq.o \
 	iecm_controlq_setup.o \
 	iecm_main.o
diff --git a/drivers/net/ethernet/intel/iecm/iecm_lib.c b/drivers/net/ethernet/intel/iecm/iecm_lib.c
index 64cdbce2c842..e2e523f0700e 100644
--- a/drivers/net/ethernet/intel/iecm/iecm_lib.c
+++ b/drivers/net/ethernet/intel/iecm/iecm_lib.c
@@ -5,6 +5,11 @@
 
 #include "iecm.h"
 
+const char * const iecm_vport_vc_state_str[] = {
+	IECM_FOREACH_VPORT_VC_STATE(IECM_GEN_STRING)
+};
+EXPORT_SYMBOL(iecm_vport_vc_state_str);
+
 /**
  * iecm_cfg_hw - Initialize HW struct
  * @adapter: adapter to setup hw struct for
@@ -24,6 +29,113 @@ static int iecm_cfg_hw(struct iecm_adapter *adapter)
 	return 0;
 }
 
+/**
+ * iecm_get_free_slot - get the next non-NULL location index in array
+ * @array: array to search
+ * @size: size of the array
+ * @curr: last known occupied index to be used as a search hint
+ *
+ * void * is being used to keep the functionality generic. This lets us use this
+ * function on any array of pointers.
+ */
+static int iecm_get_free_slot(void *array, int size, int curr)
+{
+	int **tmp_array = (int **)array;
+	int next;
+
+	if (curr < (size - 1) && !tmp_array[curr + 1]) {
+		next = curr + 1;
+	} else {
+		int i = 0;
+
+		while ((i < size) && (tmp_array[i]))
+			i++;
+		if (i == size)
+			next = IECM_NO_FREE_SLOT;
+		else
+			next = i;
+	}
+	return next;
+}
+
+/**
+ * iecm_vport_rel - Delete a vport and free its resources
+ * @vport: the vport being removed
+ */
+static void iecm_vport_rel(struct iecm_vport *vport)
+{
+	mutex_destroy(&vport->stop_mutex);
+	kfree(vport);
+}
+
+/**
+ * iecm_vport_rel_all - Delete all vports
+ * @adapter: adapter from which all vports are being removed
+ */
+static void iecm_vport_rel_all(struct iecm_adapter *adapter)
+{
+	int i;
+
+	if (!adapter->vports)
+		return;
+
+	for (i = 0; i < adapter->num_alloc_vport; i++) {
+		if (!adapter->vports[i])
+			continue;
+
+		iecm_vport_rel(adapter->vports[i]);
+		adapter->vports[i] = NULL;
+		adapter->next_vport = 0;
+	}
+	adapter->num_alloc_vport = 0;
+}
+
+/**
+ * iecm_vport_alloc - Allocates the next available struct vport in the adapter
+ * @adapter: board private structure
+ * @vport_id: vport identifier
+ *
+ * returns a pointer to a vport on success, NULL on failure.
+ */
+static struct iecm_vport *
+iecm_vport_alloc(struct iecm_adapter *adapter, int vport_id)
+{
+	struct iecm_vport *vport = NULL;
+
+	if (adapter->next_vport == IECM_NO_FREE_SLOT)
+		return vport;
+
+	/* Need to protect the allocation of the vports at the adapter level */
+	mutex_lock(&adapter->sw_mutex);
+
+	vport = kzalloc(sizeof(*vport), GFP_KERNEL);
+	if (!vport)
+		goto unlock_adapter;
+
+	vport->adapter = adapter;
+	vport->idx = adapter->next_vport;
+	vport->compln_clean_budget = IECM_TX_COMPLQ_CLEAN_BUDGET;
+	adapter->num_alloc_vport++;
+
+	/* Setup default MSIX irq handler for the vport */
+	vport->irq_q_handler = iecm_vport_intr_clean_queues;
+	vport->q_vector_base = IECM_NONQ_VEC;
+
+	mutex_init(&vport->stop_mutex);
+
+	/* fill vport slot in the adapter struct */
+	adapter->vports[adapter->next_vport] = vport;
+
+	/* prepare adapter->next_vport for next use */
+	adapter->next_vport = iecm_get_free_slot(adapter->vports,
+						 adapter->num_alloc_vport,
+						 adapter->next_vport);
+
+unlock_adapter:
+	mutex_unlock(&adapter->sw_mutex);
+	return vport;
+}
+
 /**
  * iecm_statistics_task - Delayed task to get statistics over mailbox
  * @work: work_struct handle to our data
@@ -55,7 +167,25 @@ static void iecm_service_task(struct work_struct *work)
  */
 static void iecm_init_task(struct work_struct *work)
 {
-	/* stub */
+	struct iecm_adapter *adapter = container_of(work,
+						    struct iecm_adapter,
+						    init_task.work);
+	struct iecm_vport *vport;
+	struct pci_dev *pdev;
+	int vport_id, err;
+
+	err = adapter->dev_ops.vc_ops.core_init(adapter, &vport_id);
+	if (err)
+		return;
+
+	pdev = adapter->pdev;
+	vport = iecm_vport_alloc(adapter, vport_id);
+	if (!vport) {
+		err = -EFAULT;
+		dev_err(&pdev->dev, "failed to allocate vport: %d\n",
+			err);
+		return;
+	}
 }
 
 /**
@@ -81,6 +211,31 @@ static int iecm_api_init(struct iecm_adapter *adapter)
 		return -EINVAL;
 	}
 
+	if (adapter->dev_ops.vc_ops_init) {
+		struct iecm_virtchnl_ops *vc_ops;
+
+		adapter->dev_ops.vc_ops_init(adapter);
+		vc_ops = &adapter->dev_ops.vc_ops;
+		if (!(vc_ops->core_init &&
+		      vc_ops->vport_init &&
+		      vc_ops->vport_queue_ids_init &&
+		      vc_ops->get_caps &&
+		      vc_ops->config_queues &&
+		      vc_ops->enable_queues &&
+		      vc_ops->disable_queues &&
+		      vc_ops->irq_map_unmap &&
+		      vc_ops->get_set_rss_lut &&
+		      vc_ops->get_set_rss_hash &&
+		      vc_ops->adjust_qs &&
+		      vc_ops->get_ptype &&
+		      vc_ops->init_max_queues)) {
+			dev_err(&pdev->dev, "Invalid device, missing one or more virtchnl functions\n");
+			return -EINVAL;
+		}
+	} else {
+		iecm_vc_ops_init(adapter);
+	}
+
 	return 0;
 }
 
@@ -93,7 +248,15 @@ static int iecm_api_init(struct iecm_adapter *adapter)
  */
 static void iecm_deinit_task(struct iecm_adapter *adapter)
 {
-	/* stub */
+	set_bit(__IECM_REL_RES_IN_PROG, adapter->flags);
+	/* Wait until the init_task is done else this thread might release
+	 * the resources first and the other thread might end up in a bad state
+	 */
+	cancel_delayed_work_sync(&adapter->init_task);
+	iecm_vport_rel_all(adapter);
+
+	cancel_delayed_work_sync(&adapter->serv_task);
+	cancel_delayed_work_sync(&adapter->stats_task);
 }
 
 /**
diff --git a/drivers/net/ethernet/intel/iecm/iecm_txrx.c b/drivers/net/ethernet/intel/iecm/iecm_txrx.c
new file mode 100644
index 000000000000..2f5c16a28266
--- /dev/null
+++ b/drivers/net/ethernet/intel/iecm/iecm_txrx.c
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (C) 2019 Intel Corporation */
+
+#include "iecm.h"
+
+/**
+ * iecm_vport_intr_clean_queues - MSIX mode Interrupt Handler
+ * @irq: interrupt number
+ * @data: pointer to a q_vector
+ *
+ */
+irqreturn_t
+iecm_vport_intr_clean_queues(int __always_unused irq, void *data)
+{
+	struct iecm_q_vector *q_vector = (struct iecm_q_vector *)data;
+
+	q_vector->total_events++;
+	napi_schedule(&q_vector->napi);
+
+	return IRQ_HANDLED;
+}
+
diff --git a/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
index b8f54b8c700a..aae06064d706 100644
--- a/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
+++ b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
@@ -3,6 +3,74 @@
 
 #include "iecm.h"
 
+/**
+ * iecm_recv_event_msg - Receive virtchnl event message
+ * @vport: virtual port structure
+ *
+ * Receive virtchnl event message
+ */
+static void iecm_recv_event_msg(struct iecm_vport *vport)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	struct virtchnl_pf_event *vpe;
+	struct virtchnl2_event *v2e;
+	bool link_status;
+	u32 event;
+
+	if (adapter->virt_ver_maj < VIRTCHNL_VERSION_MAJOR_2) {
+		vpe = (struct virtchnl_pf_event *)adapter->vc_msg;
+		event = vpe->event;
+	} else {
+		v2e = (struct virtchnl2_event *)adapter->vc_msg;
+		event = le32_to_cpu(v2e->event);
+	}
+
+	switch (event) {
+	case VIRTCHNL2_EVENT_LINK_CHANGE:
+		if (adapter->virt_ver_maj < VIRTCHNL_VERSION_MAJOR_2) {
+			if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
+					    VIRTCHNL2_CAP_LINK_SPEED)) {
+				adapter->link_speed_mbps =
+				vpe->event_data.link_event_adv.link_speed;
+				link_status =
+				vpe->event_data.link_event_adv.link_status;
+			} else {
+				adapter->link_speed =
+				vpe->event_data.link_event.link_speed;
+				link_status =
+				vpe->event_data.link_event.link_status;
+			}
+		} else {
+			adapter->link_speed_mbps = le32_to_cpu(v2e->link_speed);
+			link_status = v2e->link_status;
+		}
+		if (adapter->link_up != link_status) {
+			adapter->link_up = link_status;
+			if (adapter->state == __IECM_UP) {
+				if (adapter->link_up) {
+					netif_tx_start_all_queues(vport->netdev);
+					netif_carrier_on(vport->netdev);
+				} else {
+					netif_tx_stop_all_queues(vport->netdev);
+					netif_carrier_off(vport->netdev);
+				}
+			}
+		}
+		break;
+	case VIRTCHNL_EVENT_RESET_IMPENDING:
+		set_bit(__IECM_HR_CORE_RESET, adapter->flags);
+		queue_delayed_work(adapter->vc_event_wq,
+				   &adapter->vc_event_task,
+				   msecs_to_jiffies(10));
+		break;
+	default:
+		dev_err(&vport->adapter->pdev->dev,
+			"Unknown event %d from PF\n", event);
+		break;
+	}
+	clear_bit(__IECM_VC_MSG_PENDING, adapter->flags);
+}
+
 /**
  * iecm_mb_clean - Reclaim the send mailbox queue entries
  * @adapter: Driver specific private structure
@@ -39,6 +107,865 @@ static int iecm_mb_clean(struct iecm_adapter *adapter)
 	return err;
 }
 
+/**
+ * iecm_send_mb_msg - Send message over mailbox
+ * @adapter: Driver specific private structure
+ * @op: virtchnl opcode
+ * @msg_size: size of the payload
+ * @msg: pointer to buffer holding the payload
+ *
+ * Will prepare the control queue message and initiates the send api
+ *
+ * Returns 0 on success, negative on failure
+ */
+int iecm_send_mb_msg(struct iecm_adapter *adapter, enum virtchnl_ops op,
+		     u16 msg_size, u8 *msg)
+{
+	struct iecm_ctlq_msg *ctlq_msg;
+	struct iecm_dma_mem *dma_mem;
+	int err = 0;
+
+	if (iecm_is_reset_detected(adapter))
+		return -EBUSY;
+
+	err = iecm_mb_clean(adapter);
+	if (err)
+		return err;
+
+	ctlq_msg = kzalloc(sizeof(*ctlq_msg), GFP_KERNEL);
+	if (!ctlq_msg)
+		return -ENOMEM;
+
+	dma_mem = kzalloc(sizeof(*dma_mem), GFP_KERNEL);
+	if (!dma_mem) {
+		err = -ENOMEM;
+		goto dma_mem_error;
+	}
+
+	memset(ctlq_msg, 0, sizeof(struct iecm_ctlq_msg));
+	ctlq_msg->opcode = iecm_mbq_opc_send_msg_to_pf;
+	ctlq_msg->func_id = 0;
+	ctlq_msg->data_len = msg_size;
+	ctlq_msg->cookie.mbx.chnl_opcode = op;
+	ctlq_msg->cookie.mbx.chnl_retval = VIRTCHNL_STATUS_SUCCESS;
+	dma_mem->size = IECM_DFLT_MBX_BUF_SIZE;
+	dma_mem->va = dmam_alloc_coherent(&adapter->pdev->dev, dma_mem->size,
+					  &dma_mem->pa, GFP_KERNEL);
+	if (!dma_mem->va) {
+		err = -ENOMEM;
+		goto dma_alloc_error;
+	}
+	memcpy(dma_mem->va, msg, msg_size);
+	ctlq_msg->ctx.indirect.payload = dma_mem;
+
+	err = iecm_ctlq_send(&adapter->hw, adapter->hw.asq, 1, ctlq_msg);
+	if (err)
+		goto send_error;
+
+	return 0;
+send_error:
+	dmam_free_coherent(&adapter->pdev->dev, dma_mem->size, dma_mem->va,
+			   dma_mem->pa);
+dma_alloc_error:
+	kfree(dma_mem);
+dma_mem_error:
+	kfree(ctlq_msg);
+	return err;
+}
+EXPORT_SYMBOL(iecm_send_mb_msg);
+
+/**
+ * iecm_set_msg_pending_bit - Wait for clear and set msg pending
+ * @adapter: driver specific private structure
+ *
+ * If clear sets msg pending bit, otherwise waits for it to clear before
+ * setting it again. Returns 0 on success, negative on failure.
+ */
+static int iecm_set_msg_pending_bit(struct iecm_adapter *adapter)
+{
+	unsigned int retries = 100;
+
+	/* If msg pending bit already set, there's a message waiting to be
+	 * parsed and we must wait for it to be cleared before copying a new
+	 * message into the vc_msg buffer or else we'll stomp all over the
+	 * previous message.
+	 */
+	while (retries) {
+		if (!test_and_set_bit(__IECM_VC_MSG_PENDING,
+				      adapter->flags))
+			break;
+		msleep(20);
+		retries--;
+	}
+	return retries ? 0 : -ETIMEDOUT;
+}
+
+/**
+ * iecm_set_msg_pending - Wait for msg pending bit and copy msg to buf
+ * @adapter: driver specific private structure
+ * @ctlq_msg: msg to copy from
+ * @err_enum: err bit to set on error
+ *
+ * Copies payload from ctlq_msg into vc_msg buf in adapter and sets msg pending
+ * bit. Returns 0 on success, negative on failure.
+ */
+int iecm_set_msg_pending(struct iecm_adapter *adapter,
+			 struct iecm_ctlq_msg *ctlq_msg,
+			 enum iecm_vport_vc_state err_enum)
+{
+	if (ctlq_msg->cookie.mbx.chnl_retval) {
+		set_bit(err_enum, adapter->vc_state);
+		return -EINVAL;
+	}
+
+	if (iecm_set_msg_pending_bit(adapter)) {
+		set_bit(err_enum, adapter->vc_state);
+		dev_info(&adapter->pdev->dev, "Timed out setting msg pending\n");
+		return -ETIMEDOUT;
+	}
+
+	memcpy(adapter->vc_msg, ctlq_msg->ctx.indirect.payload->va,
+	       min_t(int, ctlq_msg->ctx.indirect.payload->size,
+		     IECM_DFLT_MBX_BUF_SIZE));
+	return 0;
+}
+EXPORT_SYMBOL(iecm_set_msg_pending);
+
+/**
+ * iecm_recv_mb_msg - Receive message over mailbox
+ * @adapter: Driver specific private structure
+ * @op: virtchannel operation code
+ * @msg: Received message holding buffer
+ * @msg_size: message size
+ *
+ * Will receive control queue message and posts the receive buffer. Returns 0
+ * on success and negative on failure.
+ */
+int iecm_recv_mb_msg(struct iecm_adapter *adapter, enum virtchnl_ops op,
+		     void *msg, int msg_size)
+{
+	struct iecm_ctlq_msg ctlq_msg;
+	struct iecm_dma_mem *dma_mem;
+	struct iecm_vport *vport;
+	bool work_done = false;
+	int num_retry = 2000;
+	int payload_size = 0;
+	u16 num_q_msg;
+	int err = 0;
+
+	vport = adapter->vports[0];
+	while (1) {
+		/* Try to get one message */
+		num_q_msg = 1;
+		dma_mem = NULL;
+		err = iecm_ctlq_recv(adapter->hw.arq, &num_q_msg, &ctlq_msg);
+		/* If no message then decide if we have to retry based on
+		 * opcode
+		 */
+		if (err || !num_q_msg) {
+			/* Increasing num_retry to consider the delayed
+			 * responses because of large number of VF's mailbox
+			 * messages. If the mailbox message is received from
+			 * the other side, we come out of the sleep cycle
+			 * immediately else we wait for more time.
+			 */
+			if (op && num_retry-- &&
+			    !test_bit(__IECM_REL_RES_IN_PROG, adapter->flags)) {
+				msleep(20);
+				continue;
+			} else {
+				break;
+			}
+		}
+
+		/* If we are here a message is received. Check if we are looking
+		 * for a specific message based on opcode. If it is different
+		 * ignore and post buffers
+		 */
+		if (op && ctlq_msg.cookie.mbx.chnl_opcode != op)
+			goto post_buffs;
+
+		if (ctlq_msg.data_len)
+			payload_size = ctlq_msg.ctx.indirect.payload->size;
+
+		/* All conditions are met. Either a message requested is
+		 * received or we received a message to be processed
+		 */
+		switch (ctlq_msg.cookie.mbx.chnl_opcode) {
+		case VIRTCHNL_OP_VERSION:
+		case VIRTCHNL2_OP_GET_CAPS:
+		case VIRTCHNL2_OP_CREATE_VPORT:
+			if (ctlq_msg.cookie.mbx.chnl_retval) {
+				dev_info(&adapter->pdev->dev, "Failure initializing, vc op: %u retval: %u\n",
+					 ctlq_msg.cookie.mbx.chnl_opcode,
+					 ctlq_msg.cookie.mbx.chnl_retval);
+				err = -EBADMSG;
+			} else if (msg) {
+				memcpy(msg, ctlq_msg.ctx.indirect.payload->va,
+				       min_t(int,
+					     payload_size, msg_size));
+			}
+			work_done = true;
+			break;
+		case VIRTCHNL2_OP_ENABLE_VPORT:
+			if (ctlq_msg.cookie.mbx.chnl_retval)
+				set_bit(IECM_VC_ENA_VPORT_ERR,
+					adapter->vc_state);
+			set_bit(IECM_VC_ENA_VPORT, adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL2_OP_DISABLE_VPORT:
+			if (ctlq_msg.cookie.mbx.chnl_retval)
+				set_bit(IECM_VC_DIS_VPORT_ERR,
+					adapter->vc_state);
+			set_bit(IECM_VC_DIS_VPORT, adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL2_OP_DESTROY_VPORT:
+			if (ctlq_msg.cookie.mbx.chnl_retval)
+				set_bit(IECM_VC_DESTROY_VPORT_ERR,
+					adapter->vc_state);
+			set_bit(IECM_VC_DESTROY_VPORT, adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL2_OP_CONFIG_TX_QUEUES:
+			if (ctlq_msg.cookie.mbx.chnl_retval)
+				set_bit(IECM_VC_CONFIG_TXQ_ERR,
+					adapter->vc_state);
+			set_bit(IECM_VC_CONFIG_TXQ, adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL2_OP_CONFIG_RX_QUEUES:
+			if (ctlq_msg.cookie.mbx.chnl_retval)
+				set_bit(IECM_VC_CONFIG_RXQ_ERR,
+					adapter->vc_state);
+			set_bit(IECM_VC_CONFIG_RXQ, adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL2_OP_ENABLE_QUEUES:
+			if (ctlq_msg.cookie.mbx.chnl_retval)
+				set_bit(IECM_VC_ENA_QUEUES_ERR,
+					adapter->vc_state);
+			set_bit(IECM_VC_ENA_QUEUES, adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL2_OP_DISABLE_QUEUES:
+			if (ctlq_msg.cookie.mbx.chnl_retval)
+				set_bit(IECM_VC_DIS_QUEUES_ERR,
+					adapter->vc_state);
+			set_bit(IECM_VC_DIS_QUEUES, adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL2_OP_ADD_QUEUES:
+			iecm_set_msg_pending(adapter, &ctlq_msg,
+					     IECM_VC_ADD_QUEUES_ERR);
+			set_bit(IECM_VC_ADD_QUEUES, adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL2_OP_DEL_QUEUES:
+			if (ctlq_msg.cookie.mbx.chnl_retval)
+				set_bit(IECM_VC_DEL_QUEUES_ERR,
+					adapter->vc_state);
+			set_bit(IECM_VC_DEL_QUEUES, adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL2_OP_MAP_QUEUE_VECTOR:
+			if (ctlq_msg.cookie.mbx.chnl_retval)
+				set_bit(IECM_VC_MAP_IRQ_ERR,
+					adapter->vc_state);
+			set_bit(IECM_VC_MAP_IRQ, adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL2_OP_UNMAP_QUEUE_VECTOR:
+			if (ctlq_msg.cookie.mbx.chnl_retval)
+				set_bit(IECM_VC_UNMAP_IRQ_ERR,
+					adapter->vc_state);
+			set_bit(IECM_VC_UNMAP_IRQ, adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL2_OP_GET_STATS:
+			iecm_set_msg_pending(adapter, &ctlq_msg,
+					     IECM_VC_GET_STATS_ERR);
+			set_bit(IECM_VC_GET_STATS, adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL2_OP_GET_RSS_HASH:
+			iecm_set_msg_pending(adapter, &ctlq_msg,
+					     IECM_VC_GET_RSS_HASH_ERR);
+			set_bit(IECM_VC_GET_RSS_HASH, adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL2_OP_SET_RSS_HASH:
+			if (ctlq_msg.cookie.mbx.chnl_retval)
+				set_bit(IECM_VC_SET_RSS_HASH_ERR,
+					adapter->vc_state);
+			set_bit(IECM_VC_SET_RSS_HASH, adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL2_OP_GET_RSS_LUT:
+			iecm_set_msg_pending(adapter, &ctlq_msg,
+					     IECM_VC_GET_RSS_LUT_ERR);
+			set_bit(IECM_VC_GET_RSS_LUT, adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL2_OP_SET_RSS_LUT:
+			if (ctlq_msg.cookie.mbx.chnl_retval)
+				set_bit(IECM_VC_SET_RSS_LUT_ERR,
+					adapter->vc_state);
+			set_bit(IECM_VC_SET_RSS_LUT, adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL2_OP_GET_RSS_KEY:
+			iecm_set_msg_pending(adapter, &ctlq_msg,
+					     IECM_VC_GET_RSS_KEY_ERR);
+			set_bit(IECM_VC_GET_RSS_KEY, adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL2_OP_SET_RSS_KEY:
+			if (ctlq_msg.cookie.mbx.chnl_retval)
+				set_bit(IECM_VC_SET_RSS_KEY_ERR,
+					adapter->vc_state);
+			set_bit(IECM_VC_SET_RSS_KEY, adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL2_OP_ALLOC_VECTORS:
+			iecm_set_msg_pending(adapter, &ctlq_msg,
+					     IECM_VC_ALLOC_VECTORS_ERR);
+			set_bit(IECM_VC_ALLOC_VECTORS, adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL2_OP_DEALLOC_VECTORS:
+			if (ctlq_msg.cookie.mbx.chnl_retval)
+				set_bit(IECM_VC_DEALLOC_VECTORS_ERR,
+					adapter->vc_state);
+			set_bit(IECM_VC_DEALLOC_VECTORS, adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL2_OP_GET_PTYPE_INFO:
+			iecm_set_msg_pending(adapter, &ctlq_msg,
+					     IECM_VC_GET_PTYPE_INFO_ERR);
+			set_bit(IECM_VC_GET_PTYPE_INFO, adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL2_OP_EVENT:
+		case VIRTCHNL_OP_EVENT:
+			if (iecm_set_msg_pending_bit(adapter)) {
+				dev_info(&adapter->pdev->dev, "Timed out setting msg pending\n");
+			} else {
+				memcpy(adapter->vc_msg,
+				       ctlq_msg.ctx.indirect.payload->va,
+				       min_t(int, payload_size,
+					     IECM_DFLT_MBX_BUF_SIZE));
+				iecm_recv_event_msg(vport);
+			}
+			break;
+		case VIRTCHNL_OP_ADD_ETH_ADDR:
+			if (test_and_clear_bit(__IECM_ADD_ETH_REQ, adapter->flags)) {
+				/* Message was sent asynchronously. We don't
+				 * normally print errors here, instead
+				 * preferring to handle errors in the function
+				 * calling wait_for_event. However, we will
+				 * have lost the context in which we sent the
+				 * message if asynchronous. We can't really do
+				 * anything about at it this point, but we
+				 * should at a minimum indicate that it looks
+				 * like something went wrong. Also don't bother
+				 * setting ERR bit or waking vchnl_wq since no
+				 * one will be waiting to read the async
+				 * message.
+				 */
+				if (ctlq_msg.cookie.mbx.chnl_retval) {
+					dev_err(&adapter->pdev->dev, "Failed to add MAC address: %d\n",
+						ctlq_msg.cookie.mbx.chnl_retval);
+				}
+				break;
+			}
+			if (ctlq_msg.cookie.mbx.chnl_retval) {
+				set_bit(IECM_VC_ADD_ETH_ADDR_ERR,
+					adapter->vc_state);
+			}
+			set_bit(IECM_VC_ADD_ETH_ADDR, adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL_OP_DEL_ETH_ADDR:
+			if (test_and_clear_bit(__IECM_DEL_ETH_REQ, adapter->flags)) {
+				/* Message was sent asynchronously. We don't
+				 * normally print errors here, instead
+				 * preferring to handle errors in the function
+				 * calling wait_for_event. However, we will
+				 * have lost the context in which we sent the
+				 * message if asynchronous. We can't really do
+				 * anything about at it this point, but we
+				 * should at a minimum indicate that it looks
+				 * like something went wrong. Also don't bother
+				 * setting ERR bit or waking vchnl_wq since no
+				 * one will be waiting to read the async
+				 * message.
+				 */
+				if (ctlq_msg.cookie.mbx.chnl_retval) {
+					dev_err(&adapter->pdev->dev, "Failed to delete MAC address: %d\n",
+						ctlq_msg.cookie.mbx.chnl_retval);
+				}
+				break;
+			}
+			if (ctlq_msg.cookie.mbx.chnl_retval) {
+				set_bit(IECM_VC_DEL_ETH_ADDR_ERR,
+					adapter->vc_state);
+			}
+			set_bit(IECM_VC_DEL_ETH_ADDR, adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL_OP_GET_OFFLOAD_VLAN_V2_CAPS:
+			if (ctlq_msg.cookie.mbx.chnl_retval) {
+				set_bit(IECM_VC_OFFLOAD_VLAN_V2_CAPS_ERR, adapter->vc_state);
+			} else {
+				memcpy(adapter->vc_msg,
+				       ctlq_msg.ctx.indirect.payload->va,
+				       min_t(int, payload_size,
+					     IECM_DFLT_MBX_BUF_SIZE));
+			}
+			set_bit(IECM_VC_OFFLOAD_VLAN_V2_CAPS, adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL_OP_ADD_VLAN_V2:
+			if (ctlq_msg.cookie.mbx.chnl_retval) {
+				dev_err(&adapter->pdev->dev, "Failed to add vlan filter: %d\n",
+					ctlq_msg.cookie.mbx.chnl_retval);
+			}
+			break;
+		case VIRTCHNL_OP_DEL_VLAN_V2:
+			if (ctlq_msg.cookie.mbx.chnl_retval) {
+				dev_err(&adapter->pdev->dev, "Failed to delete vlan filter: %d\n",
+					ctlq_msg.cookie.mbx.chnl_retval);
+			}
+			break;
+		case VIRTCHNL_OP_ENABLE_VLAN_INSERTION_V2:
+			if (ctlq_msg.cookie.mbx.chnl_retval) {
+				set_bit(IECM_VC_INSERTION_ENA_VLAN_V2_ERR,
+					adapter->vc_state);
+			}
+			set_bit(IECM_VC_INSERTION_ENA_VLAN_V2,
+				adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL_OP_DISABLE_VLAN_INSERTION_V2:
+			if (ctlq_msg.cookie.mbx.chnl_retval) {
+				set_bit(IECM_VC_INSERTION_DIS_VLAN_V2_ERR,
+					adapter->vc_state);
+			}
+			set_bit(IECM_VC_INSERTION_DIS_VLAN_V2,
+				adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL_OP_ENABLE_VLAN_STRIPPING_V2:
+			if (ctlq_msg.cookie.mbx.chnl_retval) {
+				set_bit(IECM_VC_STRIPPING_ENA_VLAN_V2_ERR,
+					adapter->vc_state);
+			}
+			set_bit(IECM_VC_STRIPPING_ENA_VLAN_V2,
+				adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL_OP_DISABLE_VLAN_STRIPPING_V2:
+			if (ctlq_msg.cookie.mbx.chnl_retval) {
+				set_bit(IECM_VC_STRIPPING_DIS_VLAN_V2_ERR,
+					adapter->vc_state);
+			}
+			set_bit(IECM_VC_STRIPPING_DIS_VLAN_V2,
+				adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL_OP_CONFIG_PROMISCUOUS_MODE:
+			/* This message can only be sent asynchronously. As
+			 * such we'll have lost the context in which it was
+			 * called and thus can only really report if it looks
+			 * like an error occurred. Don't bother setting ERR bit
+			 * or waking chnl_wq since no will be waiting to
+			 * reading message.
+			 */
+			if (ctlq_msg.cookie.mbx.chnl_retval) {
+				dev_err(&adapter->pdev->dev, "Failed to set promiscuous mode: %d\n",
+					ctlq_msg.cookie.mbx.chnl_retval);
+			}
+			break;
+		case VIRTCHNL_OP_ADD_CLOUD_FILTER:
+			if (ctlq_msg.cookie.mbx.chnl_retval) {
+				dev_err(&adapter->pdev->dev, "Failed to add cloud filter: %d\n",
+					ctlq_msg.cookie.mbx.chnl_retval);
+				set_bit(IECM_VC_ADD_CLOUD_FILTER_ERR,
+					adapter->vc_state);
+			}
+			set_bit(IECM_VC_ADD_CLOUD_FILTER, adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL_OP_DEL_CLOUD_FILTER:
+			if (ctlq_msg.cookie.mbx.chnl_retval) {
+				dev_err(&adapter->pdev->dev, "Failed to delete cloud filter: %d\n",
+					ctlq_msg.cookie.mbx.chnl_retval);
+				set_bit(IECM_VC_DEL_CLOUD_FILTER_ERR,
+					adapter->vc_state);
+			}
+			set_bit(IECM_VC_DEL_CLOUD_FILTER, adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL_OP_ADD_RSS_CFG:
+			if (ctlq_msg.cookie.mbx.chnl_retval) {
+				dev_err(&adapter->pdev->dev, "Failed to add RSS configuration: %d\n",
+					ctlq_msg.cookie.mbx.chnl_retval);
+				set_bit(IECM_VC_ADD_RSS_CFG_ERR,
+					adapter->vc_state);
+			}
+			set_bit(IECM_VC_ADD_RSS_CFG, adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL_OP_DEL_RSS_CFG:
+			if (ctlq_msg.cookie.mbx.chnl_retval) {
+				dev_err(&adapter->pdev->dev, "Failed to delete RSS configuration: %d\n",
+					ctlq_msg.cookie.mbx.chnl_retval);
+				set_bit(IECM_VC_DEL_RSS_CFG_ERR,
+					adapter->vc_state);
+			}
+			set_bit(IECM_VC_DEL_RSS_CFG, adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL_OP_ADD_FDIR_FILTER:
+			iecm_set_msg_pending(adapter, &ctlq_msg,
+					     IECM_VC_ADD_FDIR_FILTER_ERR);
+			set_bit(IECM_VC_ADD_FDIR_FILTER, adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL_OP_DEL_FDIR_FILTER:
+			iecm_set_msg_pending(adapter, &ctlq_msg,
+					     IECM_VC_DEL_FDIR_FILTER_ERR);
+			set_bit(IECM_VC_DEL_FDIR_FILTER, adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL_OP_ENABLE_CHANNELS:
+			if (ctlq_msg.cookie.mbx.chnl_retval) {
+				dev_err(&adapter->pdev->dev, "Failed to enable channels: %d\n",
+					ctlq_msg.cookie.mbx.chnl_retval);
+				set_bit(IECM_VC_ENA_CHANNELS_ERR,
+					adapter->vc_state);
+			}
+			set_bit(IECM_VC_ENA_CHANNELS, adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		case VIRTCHNL_OP_DISABLE_CHANNELS:
+			if (ctlq_msg.cookie.mbx.chnl_retval) {
+				dev_err(&adapter->pdev->dev, "Failed to disable channels: %d\n",
+					ctlq_msg.cookie.mbx.chnl_retval);
+				set_bit(IECM_VC_DIS_CHANNELS_ERR,
+					adapter->vc_state);
+			}
+			set_bit(IECM_VC_DIS_CHANNELS, adapter->vc_state);
+			wake_up(&adapter->vchnl_wq);
+			break;
+		default:
+			if (adapter->dev_ops.vc_ops.recv_mbx_msg)
+				err =
+				adapter->dev_ops.vc_ops.recv_mbx_msg(adapter,
+								   msg,
+								   msg_size,
+								   &ctlq_msg,
+								   &work_done);
+			else
+				dev_warn(&adapter->pdev->dev,
+					 "Unhandled virtchnl response %d\n",
+					 ctlq_msg.cookie.mbx.chnl_opcode);
+			break;
+		} /* switch v_opcode */
+post_buffs:
+		if (ctlq_msg.data_len)
+			dma_mem = ctlq_msg.ctx.indirect.payload;
+		else
+			num_q_msg = 0;
+
+		err = iecm_ctlq_post_rx_buffs(&adapter->hw, adapter->hw.arq,
+					      &num_q_msg, &dma_mem);
+		/* If post failed clear the only buffer we supplied */
+		if (err && dma_mem)
+			dmam_free_coherent(&adapter->pdev->dev, dma_mem->size,
+					   dma_mem->va, dma_mem->pa);
+		/* Applies only if we are looking for a specific opcode */
+		if (work_done)
+			break;
+	}
+
+	return err;
+}
+EXPORT_SYMBOL(iecm_recv_mb_msg);
+
+/**
+ * iecm_send_ver_msg - send virtchnl version message
+ * @adapter: Driver specific private structure
+ *
+ * Send virtchnl version message.  Returns 0 on success, negative on failure.
+ */
+static int iecm_send_ver_msg(struct iecm_adapter *adapter)
+{
+	struct virtchnl_version_info vvi;
+
+	if (adapter->virt_ver_maj) {
+		vvi.major = adapter->virt_ver_maj;
+		vvi.minor = adapter->virt_ver_min;
+	} else {
+		vvi.major = IECM_VIRTCHNL_VERSION_MAJOR;
+		vvi.minor = IECM_VIRTCHNL_VERSION_MINOR;
+	}
+
+	return iecm_send_mb_msg(adapter, VIRTCHNL_OP_VERSION, sizeof(vvi),
+				(u8 *)&vvi);
+}
+
+/**
+ * iecm_recv_ver_msg - Receive virtchnl version message
+ * @adapter: Driver specific private structure
+ *
+ * Receive virtchnl version message. Returns 0 on success, -EAGAIN if we need
+ * to send version message again, otherwise negative on failure.
+ */
+static int iecm_recv_ver_msg(struct iecm_adapter *adapter)
+{
+	struct virtchnl_version_info vvi;
+	int err = 0;
+
+	err = iecm_recv_mb_msg(adapter, VIRTCHNL_OP_VERSION, &vvi, sizeof(vvi));
+	if (err)
+		return err;
+
+	if (vvi.major > IECM_VIRTCHNL_VERSION_MAJOR) {
+		dev_warn(&adapter->pdev->dev, "Virtchnl major version greater than supported\n");
+		return -EINVAL;
+	}
+	if (vvi.major == IECM_VIRTCHNL_VERSION_MAJOR &&
+	    vvi.minor > IECM_VIRTCHNL_VERSION_MINOR)
+		dev_warn(&adapter->pdev->dev, "Virtchnl minor version not matched\n");
+
+	/* If we have a mismatch, resend version to update receiver on what
+	 * version we will use.
+	 */
+	if (!adapter->virt_ver_maj &&
+	    vvi.major != IECM_VIRTCHNL_VERSION_MAJOR &&
+	    vvi.minor != IECM_VIRTCHNL_VERSION_MINOR)
+		err = -EAGAIN;
+
+	adapter->virt_ver_maj = vvi.major;
+	adapter->virt_ver_min = vvi.minor;
+
+	return err;
+}
+
+/**
+ * iecm_send_get_caps_msg - Send virtchnl get capabilities message
+ * @adapter: Driver specific private structure
+ *
+ * Send virtchl get capabilities message. Returns 0 on success, negative on
+ * failure.
+ */
+int iecm_send_get_caps_msg(struct iecm_adapter *adapter)
+{
+	struct virtchnl2_get_capabilities caps = {0};
+	int buf_size;
+
+	buf_size = sizeof(struct virtchnl2_get_capabilities);
+	adapter->caps = kzalloc(buf_size, GFP_KERNEL);
+	if (!adapter->caps)
+		return -ENOMEM;
+
+	caps.csum_caps =
+		cpu_to_le32(VIRTCHNL2_CAP_TX_CSUM_L3_IPV4	|
+			    VIRTCHNL2_CAP_TX_CSUM_L4_IPV4_TCP	|
+			    VIRTCHNL2_CAP_TX_CSUM_L4_IPV4_UDP	|
+			    VIRTCHNL2_CAP_TX_CSUM_L4_IPV4_SCTP	|
+			    VIRTCHNL2_CAP_TX_CSUM_L4_IPV6_TCP	|
+			    VIRTCHNL2_CAP_TX_CSUM_L4_IPV6_UDP	|
+			    VIRTCHNL2_CAP_TX_CSUM_L4_IPV6_SCTP	|
+			    VIRTCHNL2_CAP_TX_CSUM_GENERIC	|
+			    VIRTCHNL2_CAP_RX_CSUM_L3_IPV4	|
+			    VIRTCHNL2_CAP_RX_CSUM_L4_IPV4_TCP	|
+			    VIRTCHNL2_CAP_RX_CSUM_L4_IPV4_UDP	|
+			    VIRTCHNL2_CAP_RX_CSUM_L4_IPV4_SCTP	|
+			    VIRTCHNL2_CAP_RX_CSUM_L4_IPV6_TCP	|
+			    VIRTCHNL2_CAP_RX_CSUM_L4_IPV6_UDP	|
+			    VIRTCHNL2_CAP_RX_CSUM_L4_IPV6_SCTP	|
+			    VIRTCHNL2_CAP_RX_CSUM_GENERIC);
+
+	caps.seg_caps =
+		cpu_to_le32(VIRTCHNL2_CAP_SEG_IPV4_TCP		|
+			    VIRTCHNL2_CAP_SEG_IPV4_UDP		|
+			    VIRTCHNL2_CAP_SEG_IPV4_SCTP		|
+			    VIRTCHNL2_CAP_SEG_IPV6_TCP		|
+			    VIRTCHNL2_CAP_SEG_IPV6_UDP		|
+			    VIRTCHNL2_CAP_SEG_IPV6_SCTP		|
+			    VIRTCHNL2_CAP_SEG_GENERIC);
+
+	caps.rss_caps =
+		cpu_to_le64(VIRTCHNL2_CAP_RSS_IPV4_TCP		|
+			    VIRTCHNL2_CAP_RSS_IPV4_UDP		|
+			    VIRTCHNL2_CAP_RSS_IPV4_SCTP		|
+			    VIRTCHNL2_CAP_RSS_IPV4_OTHER	|
+			    VIRTCHNL2_CAP_RSS_IPV6_TCP		|
+			    VIRTCHNL2_CAP_RSS_IPV6_UDP		|
+			    VIRTCHNL2_CAP_RSS_IPV6_SCTP		|
+			    VIRTCHNL2_CAP_RSS_IPV6_OTHER	|
+			    VIRTCHNL2_CAP_RSS_IPV4_AH		|
+			    VIRTCHNL2_CAP_RSS_IPV4_ESP		|
+			    VIRTCHNL2_CAP_RSS_IPV4_AH_ESP	|
+			    VIRTCHNL2_CAP_RSS_IPV6_AH		|
+			    VIRTCHNL2_CAP_RSS_IPV6_ESP		|
+			    VIRTCHNL2_CAP_RSS_IPV6_AH_ESP);
+
+	caps.hsplit_caps =
+		cpu_to_le32(VIRTCHNL2_CAP_RX_HSPLIT_AT_L2	|
+			    VIRTCHNL2_CAP_RX_HSPLIT_AT_L3	|
+			    VIRTCHNL2_CAP_RX_HSPLIT_AT_L4V4	|
+			    VIRTCHNL2_CAP_RX_HSPLIT_AT_L4V6);
+
+	caps.rsc_caps =
+		cpu_to_le32(VIRTCHNL2_CAP_RSC_IPV4_TCP		|
+			    VIRTCHNL2_CAP_RSC_IPV4_SCTP		|
+			    VIRTCHNL2_CAP_RSC_IPV6_TCP		|
+			    VIRTCHNL2_CAP_RSC_IPV6_SCTP);
+
+	caps.other_caps =
+		cpu_to_le64(VIRTCHNL2_CAP_RDMA			|
+			    VIRTCHNL2_CAP_SRIOV			|
+			    VIRTCHNL2_CAP_MACFILTER		|
+			    VIRTCHNL2_CAP_FLOW_DIRECTOR		|
+			    VIRTCHNL2_CAP_SPLITQ_QSCHED		|
+			    VIRTCHNL2_CAP_CRC			|
+			    VIRTCHNL2_CAP_ADQ			|
+			    VIRTCHNL2_CAP_WB_ON_ITR		|
+			    VIRTCHNL2_CAP_PROMISC		|
+			    VIRTCHNL2_CAP_INLINE_IPSEC		|
+			    VIRTCHNL2_CAP_VLAN			|
+			    VIRTCHNL2_CAP_RX_FLEX_DESC);
+
+	return iecm_send_mb_msg(adapter, VIRTCHNL2_OP_GET_CAPS, sizeof(caps),
+				(u8 *)&caps);
+}
+EXPORT_SYMBOL(iecm_send_get_caps_msg);
+
+/**
+ * iecm_recv_get_caps_msg - Receive virtchnl get capabilities message
+ * @adapter: Driver specific private structure
+ *
+ * Receive virtchnl get capabilities message.  Returns 0 on succes, negative on
+ * failure.
+ */
+static int iecm_recv_get_caps_msg(struct iecm_adapter *adapter)
+{
+	return iecm_recv_mb_msg(adapter, VIRTCHNL2_OP_GET_CAPS, adapter->caps,
+				sizeof(struct virtchnl2_get_capabilities));
+}
+
+/**
+ * iecm_send_create_vport_msg - Send virtchnl create vport message
+ * @adapter: Driver specific private structure
+ *
+ * send virtchnl creae vport message
+ *
+ * Returns 0 on success, negative on failure
+ */
+static int iecm_send_create_vport_msg(struct iecm_adapter *adapter)
+{
+	/* stub */
+	return 0;
+}
+
+/**
+ * iecm_recv_create_vport_msg - Receive virtchnl create vport message
+ * @adapter: Driver specific private structure
+ * @vport_id: Virtual port identifier
+ *
+ * Receive virtchnl create vport message.  Returns 0 on success, negative on
+ * failure.
+ */
+static int iecm_recv_create_vport_msg(struct iecm_adapter *adapter,
+				      int *vport_id)
+{
+	/* stub */
+	return 0;
+}
+
+/**
+ * __iecm_wait_for_event - wrapper function for wait on virtchannel response
+ * @adapter: Driver private data structure
+ * @state: check on state upon timeout
+ * @err_check: check if this specific error bit is set
+ * @timeout: Max time to wait
+ *
+ * Checks if state is set upon expiry of timeout.  Returns 0 on success,
+ * negative on failure.
+ */
+static int __iecm_wait_for_event(struct iecm_adapter *adapter,
+				 enum iecm_vport_vc_state state,
+				 enum iecm_vport_vc_state err_check,
+				 int timeout)
+{
+	int event;
+
+	event = wait_event_timeout(adapter->vchnl_wq,
+				   test_and_clear_bit(state, adapter->vc_state),
+				   msecs_to_jiffies(timeout));
+	if (event) {
+		if (test_and_clear_bit(err_check, adapter->vc_state)) {
+			dev_err(&adapter->pdev->dev, "VC response error %s\n",
+				iecm_vport_vc_state_str[err_check]);
+			return -EINVAL;
+		}
+		return 0;
+	}
+
+	/* Timeout occurred */
+	dev_err(&adapter->pdev->dev, "VC timeout, state = %s\n",
+		iecm_vport_vc_state_str[state]);
+	return -ETIMEDOUT;
+}
+
+/**
+ * iecm_min_wait_for_event - wait for virtchannel response
+ * @adapter: Driver private data structure
+ * @state: check on state upon timeout
+ * @err_check: check if this specific error bit is set
+ *
+ * Returns 0 on success, negative on failure.
+ */
+int iecm_min_wait_for_event(struct iecm_adapter *adapter,
+			    enum iecm_vport_vc_state state,
+			    enum iecm_vport_vc_state err_check)
+{
+	int timeout = 2000;
+
+	return __iecm_wait_for_event(adapter, state, err_check, timeout);
+}
+EXPORT_SYMBOL(iecm_min_wait_for_event);
+
+/**
+ * iecm_wait_for_event - wait for virtchannel response
+ * @adapter: Driver private data structure
+ * @state: check on state upon timeout after 500ms
+ * @err_check: check if this specific error bit is set
+ *
+ * Returns 0 on success, negative on failure.
+ */
+int iecm_wait_for_event(struct iecm_adapter *adapter,
+			enum iecm_vport_vc_state state,
+			enum iecm_vport_vc_state err_check)
+{
+	/* Increasing the timeout in __IECM_INIT_SW flow to consider large
+	 * number of VF's mailbox message responses. When a message is received
+	 * on mailbox, this thread is wake up by the iecm_recv_mb_msg before the
+	 * timeout expires. Only in the error case i.e. if no message is
+	 * received on mailbox, we wait for the complete timeout which is
+	 * less likely to happen.
+	 */
+	int timeout = 60000;
+
+	return __iecm_wait_for_event(adapter, state, err_check, timeout);
+}
+EXPORT_SYMBOL(iecm_wait_for_event);
+
 /**
  * iecm_find_ctlq - Given a type and id, find ctlq info
  * @hw: hardware struct
@@ -170,3 +1097,375 @@ void iecm_vport_params_buf_rel(struct iecm_adapter *adapter)
 	kfree(adapter->caps);
 	kfree(adapter->config_data.req_qs_chunks);
 }
+
+/**
+ * iecm_vc_core_init - Initialize mailbox and get resources
+ * @adapter: Driver specific private structure
+ * @vport_id: Virtual port identifier
+ *
+ * Will check if HW is ready with reset complete. Initializes the mailbox and
+ * communicate with master to get all the default vport parameters. Returns 0
+ * on success, -EAGAIN function will get called again, otherwise negative on
+ * failure.
+ */
+int iecm_vc_core_init(struct iecm_adapter *adapter, int *vport_id)
+{
+	int err;
+
+	switch (adapter->state) {
+	case __IECM_STARTUP:
+		if (iecm_send_ver_msg(adapter))
+			goto init_failed;
+		adapter->state = __IECM_VER_CHECK;
+		goto restart;
+	case __IECM_VER_CHECK:
+		err = iecm_recv_ver_msg(adapter);
+		if (err == -EAGAIN) {
+			adapter->state = __IECM_STARTUP;
+			goto restart;
+		} else if (err) {
+			goto init_failed;
+		}
+		adapter->state = __IECM_GET_CAPS;
+		if (adapter->dev_ops.vc_ops.get_caps(adapter))
+			goto init_failed;
+		goto restart;
+	case __IECM_GET_CAPS:
+		if (iecm_recv_get_caps_msg(adapter))
+			goto init_failed;
+		if (iecm_send_create_vport_msg(adapter))
+			goto init_failed;
+		adapter->state = __IECM_GET_DFLT_VPORT_PARAMS;
+		goto restart;
+	case __IECM_GET_DFLT_VPORT_PARAMS:
+		if (iecm_recv_create_vport_msg(adapter, vport_id))
+			goto init_failed;
+		adapter->state = __IECM_INIT_SW;
+		break;
+	case __IECM_INIT_SW:
+		break;
+	default:
+		dev_err(&adapter->pdev->dev, "Device is in bad state: %d\n",
+			adapter->state);
+		goto init_failed;
+	}
+
+	return 0;
+restart:
+	queue_delayed_work(adapter->init_wq, &adapter->init_task,
+			   msecs_to_jiffies(30));
+	/* Not an error. Using try again to continue with state machine */
+	return -EAGAIN;
+init_failed:
+	if (++adapter->mb_wait_count > IECM_MB_MAX_ERR) {
+		dev_err(&adapter->pdev->dev, "Failed to establish mailbox communications with hardware\n");
+		return -EFAULT;
+	}
+	adapter->state = __IECM_STARTUP;
+	/* If it reaches here, it is possible that mailbox queue initialization
+	 * register writes might not have taken effect. Retry to initialize
+	 * the mailbox again
+	 */
+	iecm_deinit_dflt_mbx(adapter);
+	set_bit(__IECM_HR_DRV_LOAD, adapter->flags);
+	queue_delayed_work(adapter->vc_event_wq, &adapter->vc_event_task,
+			   msecs_to_jiffies(20));
+	return -EAGAIN;
+}
+EXPORT_SYMBOL(iecm_vc_core_init);
+
+/**
+ * iecm_vport_init - Initialize virtual port
+ * @vport: virtual port to be initialized
+ * @vport_id: Unique identification number of vport
+ *
+ * Will initialize vport with the info received through MB earlier
+ */
+static void iecm_vport_init(struct iecm_vport *vport,
+			    __always_unused int vport_id)
+{
+	struct virtchnl2_create_vport *vport_msg;
+	u16 rx_itr[] = {2, 8, 32, 96, 128};
+	u16 tx_itr[] = {2, 8, 64, 128, 256};
+
+	vport_msg = (struct virtchnl2_create_vport *)
+				vport->adapter->vport_params_recvd[0];
+	vport->txq_model = le16_to_cpu(vport_msg->txq_model);
+	vport->rxq_model = le16_to_cpu(vport_msg->rxq_model);
+	vport->vport_type = le16_to_cpu(vport_msg->vport_type);
+	vport->vport_id = le32_to_cpu(vport_msg->vport_id);
+	vport->adapter->rss_data.rss_key_size =
+				min_t(u16, NETDEV_RSS_KEY_LEN,
+				      le16_to_cpu(vport_msg->rss_key_size));
+	vport->adapter->rss_data.rss_lut_size =
+				le16_to_cpu(vport_msg->rss_lut_size);
+	ether_addr_copy(vport->default_mac_addr, vport_msg->default_mac_addr);
+	vport->max_mtu = IECM_MAX_MTU;
+
+	if (iecm_is_queue_model_split(vport->rxq_model)) {
+		vport->num_bufqs_per_qgrp = IECM_MAX_BUFQS_PER_RXQ_GRP;
+		/* Bufq[0] default buffer size is 4K
+		 * Bufq[1] default buffer size is 2K
+		 */
+		vport->bufq_size[0] = IECM_RX_BUF_4096;
+		vport->bufq_size[1] = IECM_RX_BUF_2048;
+	} else {
+		vport->num_bufqs_per_qgrp = 0;
+		vport->bufq_size[0] = IECM_RX_BUF_2048;
+	}
+
+	/*Initialize Tx and Rx profiles for Dynamic Interrupt Moderation */
+	memcpy(vport->rx_itr_profile, rx_itr, IECM_DIM_PROFILE_SLOTS);
+	memcpy(vport->tx_itr_profile, tx_itr, IECM_DIM_PROFILE_SLOTS);
+}
+
+/**
+ * iecm_get_vec_ids - Initialize vector id from Mailbox parameters
+ * @adapter: adapter structure to get the mailbox vector id
+ * @vecids: Array of vector ids
+ * @num_vecids: number of vector ids
+ * @chunks: vector ids received over mailbox
+ *
+ * Will initialize the mailbox vector id which is received from the
+ * get capabilities and data queue vector ids with ids received as
+ * mailbox parameters.
+ * Returns number of ids filled
+ */
+int iecm_get_vec_ids(struct iecm_adapter *adapter,
+		     u16 *vecids, int num_vecids,
+		     struct virtchnl2_vector_chunks *chunks)
+{
+	u16 num_chunks = le16_to_cpu(chunks->num_vchunks);
+	u16 start_vecid, num_vec;
+	int num_vecid_filled = 0;
+	int i, j;
+
+	vecids[num_vecid_filled] = adapter->mb_vector.v_idx;
+	num_vecid_filled++;
+
+	for (j = 0; j < num_chunks; j++) {
+		struct virtchnl2_vector_chunk *chunk = &chunks->vchunks[j];
+
+		num_vec = le16_to_cpu(chunk->num_vectors);
+		start_vecid = le16_to_cpu(chunk->start_vector_id);
+		for (i = 0; i < num_vec; i++) {
+			if ((num_vecid_filled + i) < num_vecids) {
+				vecids[num_vecid_filled + i] = start_vecid;
+				start_vecid++;
+			} else {
+				break;
+			}
+		}
+		num_vecid_filled = num_vecid_filled + i;
+	}
+
+	return num_vecid_filled;
+}
+
+/**
+ * iecm_vport_get_queue_ids - Initialize queue id from Mailbox parameters
+ * @qids: Array of queue ids
+ * @num_qids: number of queue ids
+ * @q_type: queue model
+ * @chunks: queue ids received over mailbox
+ *
+ * Will initialize all queue ids with ids received as mailbox parameters
+ * Returns number of ids filled
+ */
+static int
+iecm_vport_get_queue_ids(u32 *qids, int num_qids, u16 q_type,
+			 struct virtchnl2_queue_reg_chunks *chunks)
+{
+	u16 num_chunks = le16_to_cpu(chunks->num_chunks);
+	u32 num_q_id_filled = 0, i;
+	u32 start_q_id, num_q;
+
+	while (num_chunks) {
+		struct virtchnl2_queue_reg_chunk *chunk = &chunks->chunks[num_chunks - 1];
+
+		if (le32_to_cpu(chunk->type) == q_type) {
+			num_q = le32_to_cpu(chunk->num_queues);
+			start_q_id = le32_to_cpu(chunk->start_queue_id);
+			for (i = 0; i < num_q; i++) {
+				if ((num_q_id_filled + i) < num_qids) {
+					qids[num_q_id_filled + i] = start_q_id;
+					start_q_id++;
+				} else {
+					break;
+				}
+			}
+			num_q_id_filled = num_q_id_filled + i;
+		}
+		num_chunks--;
+	}
+
+	return num_q_id_filled;
+}
+
+/**
+ * __iecm_vport_queue_ids_init - Initialize queue ids from Mailbox parameters
+ * @vport: virtual port for which the queues ids are initialized
+ * @qids: queue ids
+ * @num_qids: number of queue ids
+ * @q_type: type of queue
+ *
+ * Will initialize all queue ids with ids received as mailbox
+ * parameters. Returns number of queue ids initialized.
+ */
+static int
+__iecm_vport_queue_ids_init(struct iecm_vport *vport, u32 *qids,
+			    int num_qids, u32 q_type)
+{
+	/* stub */
+	return 0;
+}
+
+/**
+ * iecm_vport_queue_ids_init - Initialize queue ids from Mailbox parameters
+ * @vport: virtual port for which the queues ids are initialized
+ *
+ * Will initialize all queue ids with ids received as mailbox parameters.
+ * Returns 0 on success, negative if all the queues are not initialized.
+ */
+static int iecm_vport_queue_ids_init(struct iecm_vport *vport)
+{
+	struct virtchnl2_create_vport *vport_params;
+	struct virtchnl2_queue_reg_chunks *chunks;
+	/* We may never deal with more than 256 same type of queues */
+#define IECM_MAX_QIDS	256
+	u32 qids[IECM_MAX_QIDS];
+	int num_ids;
+	u16 q_type;
+
+	if (vport->adapter->config_data.req_qs_chunks) {
+		struct virtchnl2_add_queues *vc_aq =
+			(struct virtchnl2_add_queues *)
+			vport->adapter->config_data.req_qs_chunks;
+		chunks = &vc_aq->chunks;
+	} else {
+		vport_params = (struct virtchnl2_create_vport *)
+				vport->adapter->vport_params_recvd[0];
+		chunks = &vport_params->chunks;
+	}
+
+	num_ids = iecm_vport_get_queue_ids(qids, IECM_MAX_QIDS,
+					   VIRTCHNL2_QUEUE_TYPE_TX,
+					   chunks);
+	if (num_ids != vport->num_txq)
+		return -EINVAL;
+	num_ids = __iecm_vport_queue_ids_init(vport, qids, num_ids,
+					      VIRTCHNL2_QUEUE_TYPE_TX);
+	if (num_ids != vport->num_txq)
+		return -EINVAL;
+	num_ids = iecm_vport_get_queue_ids(qids, IECM_MAX_QIDS,
+					   VIRTCHNL2_QUEUE_TYPE_RX,
+					   chunks);
+	if (num_ids != vport->num_rxq)
+		return -EINVAL;
+	num_ids = __iecm_vport_queue_ids_init(vport, qids, num_ids,
+					      VIRTCHNL2_QUEUE_TYPE_RX);
+	if (num_ids != vport->num_rxq)
+		return -EINVAL;
+
+	if (iecm_is_queue_model_split(vport->txq_model)) {
+		q_type = VIRTCHNL2_QUEUE_TYPE_TX_COMPLETION;
+		num_ids = iecm_vport_get_queue_ids(qids, IECM_MAX_QIDS, q_type,
+						   chunks);
+		if (num_ids != vport->num_complq)
+			return -EINVAL;
+		num_ids = __iecm_vport_queue_ids_init(vport, qids,
+						      num_ids,
+						      q_type);
+		if (num_ids != vport->num_complq)
+			return -EINVAL;
+	}
+
+	if (iecm_is_queue_model_split(vport->rxq_model)) {
+		q_type = VIRTCHNL2_QUEUE_TYPE_RX_BUFFER;
+		num_ids = iecm_vport_get_queue_ids(qids, IECM_MAX_QIDS, q_type,
+						   chunks);
+		if (num_ids != vport->num_bufq)
+			return -EINVAL;
+		num_ids = __iecm_vport_queue_ids_init(vport, qids, num_ids,
+						      q_type);
+		if (num_ids != vport->num_bufq)
+			return -EINVAL;
+	}
+
+	return 0;
+}
+
+/**
+ * iecm_is_capability_ena - Default implementation of capability checking
+ * @adapter: Private data struct
+ * @all: all or one flag
+ * @field: caps field to check for flags
+ * @flag: flag to check
+ *
+ * Return true if all capabilities are supported, false otherwise
+ */
+static bool iecm_is_capability_ena(struct iecm_adapter *adapter, bool all,
+				   enum iecm_cap_field field, u64 flag)
+{
+	u8 *caps = (u8 *)adapter->caps;
+	u32 *cap_field;
+
+	if (field == IECM_BASE_CAPS)
+		return false;
+	if (field >= IECM_CAP_FIELD_LAST) {
+		dev_err(&adapter->pdev->dev, "Bad capability field: %d\n",
+			field);
+		return false;
+	}
+	cap_field = (u32 *)(caps + field);
+
+	if (all)
+		return (*cap_field & flag) == flag;
+	else
+		return !!(*cap_field & flag);
+}
+
+/**
+ * iecm_vc_ops_init - Initialize virtchnl common api
+ * @adapter: Driver specific private structure
+ *
+ * Initialize the function pointers with the extended feature set functions
+ * as APF will deal only with new set of opcodes.
+ */
+void iecm_vc_ops_init(struct iecm_adapter *adapter)
+{
+	struct iecm_virtchnl_ops *vc_ops = &adapter->dev_ops.vc_ops;
+
+	vc_ops->core_init = iecm_vc_core_init;
+	vc_ops->vport_init = iecm_vport_init;
+	vc_ops->vport_queue_ids_init = iecm_vport_queue_ids_init;
+	vc_ops->get_caps = iecm_send_get_caps_msg;
+	vc_ops->is_cap_ena = iecm_is_capability_ena;
+	vc_ops->get_reserved_vecs = NULL;
+	vc_ops->config_queues = NULL;
+	vc_ops->enable_queues = NULL;
+	vc_ops->disable_queues = NULL;
+	vc_ops->add_queues = NULL;
+	vc_ops->delete_queues = NULL;
+	vc_ops->irq_map_unmap = NULL;
+	vc_ops->enable_vport = NULL;
+	vc_ops->disable_vport = NULL;
+	vc_ops->destroy_vport = NULL;
+	vc_ops->get_ptype = NULL;
+	vc_ops->get_set_rss_key = NULL;
+	vc_ops->get_set_rss_lut = NULL;
+	vc_ops->get_set_rss_hash = NULL;
+	vc_ops->adjust_qs = NULL;
+	vc_ops->add_del_vlans = NULL;
+	vc_ops->strip_vlan_msg = NULL;
+	vc_ops->insert_vlan_msg = NULL;
+	vc_ops->init_max_queues = NULL;
+	vc_ops->get_max_tx_bufs = NULL;
+	vc_ops->vportq_reg_init = NULL;
+	vc_ops->alloc_vectors = NULL;
+	vc_ops->dealloc_vectors = NULL;
+	vc_ops->get_supported_desc_ids = NULL;
+	vc_ops->get_stats_msg = NULL;
+	vc_ops->recv_mbx_msg = NULL;
+}
+EXPORT_SYMBOL(iecm_vc_ops_init);
diff --git a/drivers/net/ethernet/intel/include/iecm.h b/drivers/net/ethernet/intel/include/iecm.h
index ca9029224e06..994664dfe419 100644
--- a/drivers/net/ethernet/intel/include/iecm.h
+++ b/drivers/net/ethernet/intel/include/iecm.h
@@ -7,10 +7,13 @@
 #include <linux/aer.h>
 #include <linux/pci.h>
 #include <linux/netdevice.h>
+#include <linux/etherdevice.h>
 #include <linux/ethtool.h>
+#include <net/tcp.h>
 #include <linux/version.h>
 #include <linux/dim.h>
 
+#include "virtchnl_2.h"
 #include "iecm_txrx.h"
 #include "iecm_controlq.h"
 
@@ -35,10 +38,34 @@
 /* available message levels */
 #define IECM_AVAIL_NETIF_M (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK)
 
+#define IECM_VIRTCHNL_VERSION_MAJOR VIRTCHNL_VERSION_MAJOR_2
+#define IECM_VIRTCHNL_VERSION_MINOR VIRTCHNL_VERSION_MINOR_0
+
 /* Forward declaration */
 struct iecm_adapter;
 struct iecm_vport;
 
+struct iecm_mac_filter {
+	struct list_head list;
+	u8 macaddr[ETH_ALEN];
+	bool remove;		/* filter needs to be removed */
+	bool add;		/* filter needs to be added */
+};
+
+#define IECM_VLAN(vid, tpid) ((struct iecm_vlan){ vid, tpid })
+
+struct iecm_vlan {
+	u16 vid;
+	u16 tpid;
+};
+
+struct iecm_vlan_filter {
+	struct list_head list;
+	struct iecm_vlan vlan;
+	bool remove;		/* filter needs to be removed */
+	bool add;		/* filter needs to be added */
+};
+
 enum iecm_state {
 	__IECM_STARTUP,
 	__IECM_VER_CHECK,
@@ -90,6 +117,24 @@ enum iecm_flags {
 	__IECM_FLAGS_NBITS,
 };
 
+/* enum used to distinquish which capability field to check */
+enum iecm_cap_field {
+	IECM_BASE_CAPS		= -1,
+	IECM_CSUM_CAPS		= offsetof(struct virtchnl2_get_capabilities,
+					   csum_caps),
+	IECM_SEG_CAPS		= offsetof(struct virtchnl2_get_capabilities,
+					   seg_caps),
+	IECM_RSS_CAPS		= offsetof(struct virtchnl2_get_capabilities,
+					   rss_caps),
+	IECM_HSPLIT_CAPS	= offsetof(struct virtchnl2_get_capabilities,
+					   hsplit_caps),
+	IECM_RSC_CAPS		= offsetof(struct virtchnl2_get_capabilities,
+					   rsc_caps),
+	IECM_OTHER_CAPS		= offsetof(struct virtchnl2_get_capabilities,
+					   other_caps),
+	IECM_CAP_FIELD_LAST,
+};
+
 struct iecm_reset_reg {
 	u32 rstat;
 	u32 rstat_m;
@@ -105,14 +150,229 @@ struct iecm_reg_ops {
 			      enum iecm_flags trig_cause);
 };
 
+struct iecm_virtchnl_ops {
+	int (*core_init)(struct iecm_adapter *adapter, int *vport_id);
+	void (*vport_init)(struct iecm_vport *vport, int vport_id);
+	int (*vport_queue_ids_init)(struct iecm_vport *vport);
+	int (*get_caps)(struct iecm_adapter *adapter);
+	int (*config_queues)(struct iecm_vport *vport);
+	int (*enable_queues)(struct iecm_vport *vport);
+	int (*disable_queues)(struct iecm_vport *vport);
+	int (*add_queues)(struct iecm_vport *vport, u16 num_tx_q,
+			  u16 num_complq, u16 num_rx_q,
+			  u16 num_rx_bufq);
+	int (*delete_queues)(struct iecm_vport *vport);
+	int (*irq_map_unmap)(struct iecm_vport *vport, bool map);
+	int (*enable_vport)(struct iecm_vport *vport);
+	int (*disable_vport)(struct iecm_vport *vport);
+	int (*destroy_vport)(struct iecm_vport *vport);
+	int (*get_ptype)(struct iecm_vport *vport);
+	int (*get_set_rss_key)(struct iecm_vport *vport, bool get);
+	int (*get_set_rss_lut)(struct iecm_vport *vport, bool get);
+	int (*get_set_rss_hash)(struct iecm_vport *vport, bool get);
+	void (*adjust_qs)(struct iecm_vport *vport);
+	int (*recv_mbx_msg)(struct iecm_adapter *adapter,
+			    void *msg, int msg_size,
+			    struct iecm_ctlq_msg *ctlq_msg, bool *work_done);
+	bool (*is_cap_ena)(struct iecm_adapter *adapter, bool all,
+			   enum iecm_cap_field field, u64 flag);
+	u16 (*get_reserved_vecs)(struct iecm_adapter *adapter);
+	void (*add_del_vlans)(struct iecm_vport *vport, bool add);
+	int (*strip_vlan_msg)(struct iecm_vport *vport, bool ena);
+	int (*insert_vlan_msg)(struct iecm_vport *vport, bool ena);
+	void (*init_max_queues)(struct iecm_adapter *adapter);
+	unsigned int (*get_max_tx_bufs)(struct iecm_adapter *adapter);
+	int (*vportq_reg_init)(struct iecm_vport *vport);
+	int (*alloc_vectors)(struct iecm_adapter *adapter, u16 num_vectors);
+	int (*dealloc_vectors)(struct iecm_adapter *adapter);
+	int (*get_supported_desc_ids)(struct iecm_vport *vport);
+	int (*get_stats_msg)(struct iecm_vport *vport);
+};
+
 struct iecm_dev_ops {
 	void (*reg_ops_init)(struct iecm_adapter *adapter);
+	void (*vc_ops_init)(struct iecm_adapter *adapter);
 	void (*crc_enable)(u64 *td_cmd);
 	struct iecm_reg_ops reg_ops;
+	struct iecm_virtchnl_ops vc_ops;
+};
+
+/* These macros allow us to generate an enum and a matching char * array of
+ * stringified enums that are always in sync. Checkpatch issues a bogus warning
+ * about this being a complex macro; but it's wrong, these are never used as a
+ * statement and instead only used to define the enum and array.
+ */
+#define IECM_FOREACH_VPORT_VC_STATE(STATE)	\
+	STATE(IECM_VC_ENA_VPORT)		\
+	STATE(IECM_VC_ENA_VPORT_ERR)		\
+	STATE(IECM_VC_DIS_VPORT)		\
+	STATE(IECM_VC_DIS_VPORT_ERR)		\
+	STATE(IECM_VC_DESTROY_VPORT)		\
+	STATE(IECM_VC_DESTROY_VPORT_ERR)	\
+	STATE(IECM_VC_CONFIG_TXQ)		\
+	STATE(IECM_VC_CONFIG_TXQ_ERR)		\
+	STATE(IECM_VC_CONFIG_RXQ)		\
+	STATE(IECM_VC_CONFIG_RXQ_ERR)		\
+	STATE(IECM_VC_CONFIG_Q)			\
+	STATE(IECM_VC_CONFIG_Q_ERR)		\
+	STATE(IECM_VC_ENA_QUEUES)		\
+	STATE(IECM_VC_ENA_QUEUES_ERR)		\
+	STATE(IECM_VC_DIS_QUEUES)		\
+	STATE(IECM_VC_DIS_QUEUES_ERR)		\
+	STATE(IECM_VC_ENA_CHANNELS)		\
+	STATE(IECM_VC_ENA_CHANNELS_ERR)		\
+	STATE(IECM_VC_DIS_CHANNELS)		\
+	STATE(IECM_VC_DIS_CHANNELS_ERR)		\
+	STATE(IECM_VC_MAP_IRQ)			\
+	STATE(IECM_VC_MAP_IRQ_ERR)		\
+	STATE(IECM_VC_UNMAP_IRQ)		\
+	STATE(IECM_VC_UNMAP_IRQ_ERR)		\
+	STATE(IECM_VC_ADD_QUEUES)		\
+	STATE(IECM_VC_ADD_QUEUES_ERR)		\
+	STATE(IECM_VC_DEL_QUEUES)		\
+	STATE(IECM_VC_REQUEST_QUEUES)		\
+	STATE(IECM_VC_REQUEST_QUEUES_ERR)	\
+	STATE(IECM_VC_DEL_QUEUES_ERR)		\
+	STATE(IECM_VC_ALLOC_VECTORS)		\
+	STATE(IECM_VC_ALLOC_VECTORS_ERR)	\
+	STATE(IECM_VC_DEALLOC_VECTORS)		\
+	STATE(IECM_VC_DEALLOC_VECTORS_ERR)	\
+	STATE(IECM_VC_SET_SRIOV_VFS)		\
+	STATE(IECM_VC_SET_SRIOV_VFS_ERR)	\
+	STATE(IECM_VC_GET_RSS_HASH)		\
+	STATE(IECM_VC_GET_RSS_HASH_ERR)		\
+	STATE(IECM_VC_SET_RSS_HASH)		\
+	STATE(IECM_VC_SET_RSS_HASH_ERR)		\
+	STATE(IECM_VC_GET_RSS_LUT)		\
+	STATE(IECM_VC_GET_RSS_LUT_ERR)		\
+	STATE(IECM_VC_SET_RSS_LUT)		\
+	STATE(IECM_VC_SET_RSS_LUT_ERR)		\
+	STATE(IECM_VC_GET_RSS_KEY)		\
+	STATE(IECM_VC_GET_RSS_KEY_ERR)		\
+	STATE(IECM_VC_SET_RSS_KEY)		\
+	STATE(IECM_VC_SET_RSS_KEY_ERR)		\
+	STATE(IECM_VC_GET_STATS)		\
+	STATE(IECM_VC_GET_STATS_ERR)		\
+	STATE(IECM_VC_ENA_STRIP_VLAN_TAG)	\
+	STATE(IECM_VC_ENA_STRIP_VLAN_TAG_ERR)	\
+	STATE(IECM_VC_DIS_STRIP_VLAN_TAG)	\
+	STATE(IECM_VC_DIS_STRIP_VLAN_TAG_ERR)	\
+	STATE(IECM_VC_IWARP_IRQ_MAP)		\
+	STATE(IECM_VC_IWARP_IRQ_MAP_ERR)	\
+	STATE(IECM_VC_ADD_ETH_ADDR)		\
+	STATE(IECM_VC_ADD_ETH_ADDR_ERR)		\
+	STATE(IECM_VC_DEL_ETH_ADDR)		\
+	STATE(IECM_VC_DEL_ETH_ADDR_ERR)		\
+	STATE(IECM_VC_PROMISC)			\
+	STATE(IECM_VC_ADD_CLOUD_FILTER)		\
+	STATE(IECM_VC_ADD_CLOUD_FILTER_ERR)	\
+	STATE(IECM_VC_DEL_CLOUD_FILTER)		\
+	STATE(IECM_VC_DEL_CLOUD_FILTER_ERR)	\
+	STATE(IECM_VC_ADD_RSS_CFG)		\
+	STATE(IECM_VC_ADD_RSS_CFG_ERR)		\
+	STATE(IECM_VC_DEL_RSS_CFG)		\
+	STATE(IECM_VC_DEL_RSS_CFG_ERR)		\
+	STATE(IECM_VC_ADD_FDIR_FILTER)		\
+	STATE(IECM_VC_ADD_FDIR_FILTER_ERR)	\
+	STATE(IECM_VC_DEL_FDIR_FILTER)		\
+	STATE(IECM_VC_DEL_FDIR_FILTER_ERR)	\
+	STATE(IECM_VC_OFFLOAD_VLAN_V2_CAPS)	\
+	STATE(IECM_VC_OFFLOAD_VLAN_V2_CAPS_ERR)	\
+	STATE(IECM_VC_INSERTION_ENA_VLAN_V2)	\
+	STATE(IECM_VC_INSERTION_ENA_VLAN_V2_ERR)\
+	STATE(IECM_VC_INSERTION_DIS_VLAN_V2)	\
+	STATE(IECM_VC_INSERTION_DIS_VLAN_V2_ERR)\
+	STATE(IECM_VC_STRIPPING_ENA_VLAN_V2)	\
+	STATE(IECM_VC_STRIPPING_ENA_VLAN_V2_ERR)\
+	STATE(IECM_VC_STRIPPING_DIS_VLAN_V2)	\
+	STATE(IECM_VC_STRIPPING_DIS_VLAN_V2_ERR)\
+	STATE(IECM_VC_GET_SUPPORTED_RXDIDS)	\
+	STATE(IECM_VC_GET_SUPPORTED_RXDIDS_ERR)	\
+	STATE(IECM_VC_GET_PTYPE_INFO)		\
+	STATE(IECM_VC_GET_PTYPE_INFO_ERR)	\
+	STATE(IECM_VC_NBITS)
+
+#define IECM_GEN_ENUM(ENUM) ENUM,
+#define IECM_GEN_STRING(STRING) #STRING,
+
+enum iecm_vport_vc_state {
+	IECM_FOREACH_VPORT_VC_STATE(IECM_GEN_ENUM)
+};
+
+extern const char * const iecm_vport_vc_state_str[];
+
+enum iecm_vport_flags {
+	__IECM_VPORT_INIT_PROMISC,
+	__IECM_VPORT_FLAGS_NBITS,
+};
+
+struct iecm_port_stats {
+	struct u64_stats_sync stats_sync;
+	u64 rx_hw_csum_err;
+	u64 rx_hsplit;
+	u64 rx_hsplit_hbo;
+	u64 tx_linearize;
+	u64 rx_bad_descs;
+	struct virtchnl2_vport_stats vport_stats;
+	struct virtchnl_eth_stats eth_stats;
 };
 
-/* stub */
 struct iecm_vport {
+	/* TX */
+	int num_txq;
+	int num_complq;
+	/* It makes more sense for descriptor count to be part of only idpf
+	 * queue structure. But when user changes the count via ethtool, driver
+	 * has to store that value somewhere other than queue structure as the
+	 * queues will be freed and allocated again.
+	 */
+	int txq_desc_count;
+	int complq_desc_count;
+	int compln_clean_budget;
+	int num_txq_grp;
+	struct iecm_txq_group *txq_grps;
+	u32 txq_model;
+	/* Used only in hotpath to get to the right queue very fast */
+	struct iecm_queue **txqs;
+	DECLARE_BITMAP(flags, __IECM_VPORT_FLAGS_NBITS);
+
+	/* RX */
+	int num_rxq;
+	int num_bufq;
+	int rxq_desc_count;
+	u8 num_bufqs_per_qgrp;
+	int bufq_desc_count[IECM_MAX_BUFQS_PER_RXQ_GRP];
+	u32 bufq_size[IECM_MAX_BUFQS_PER_RXQ_GRP];
+	int num_rxq_grp;
+	struct iecm_rxq_group *rxq_grps;
+	u32 rxq_model;
+
+	struct iecm_adapter *adapter;
+	struct net_device *netdev;
+	u16 vport_type;
+	u16 vport_id;
+	u16 idx;		 /* software index in adapter vports struct */
+	bool base_rxd;
+
+	/* handler for hard interrupt */
+	irqreturn_t (*irq_q_handler)(int irq, void *data);
+	struct iecm_q_vector *q_vectors;	/* q vector array */
+	u16 num_q_vectors;
+	u16 q_vector_base;
+	u16 max_mtu;
+	u8 default_mac_addr[ETH_ALEN];
+	u16 qset_handle;
+	/* ITR profiles for the DIM algorithm */
+#define IECM_DIM_PROFILE_SLOTS	5
+	u16 rx_itr_profile[IECM_DIM_PROFILE_SLOTS];
+	u16 tx_itr_profile[IECM_DIM_PROFILE_SLOTS];
+	struct rtnl_link_stats64 netstats;
+	struct iecm_port_stats port_stats;
+
+	/* lock to protect against multiple stop threads, which can happen when
+	 * the driver is in a namespace in a system that is being shutdown
+	 */
+	struct mutex stop_mutex;
 };
 
 enum iecm_user_flags {
@@ -164,6 +424,7 @@ struct iecm_adapter {
 	u16 num_msix_entries;
 	struct msix_entry *msix_entries;
 	struct virtchnl2_alloc_vectors *req_vec_chunks;
+	struct iecm_q_vector mb_vector;
 
 	/* vport structs */
 	struct iecm_vport **vports;	/* vports created by the driver */
@@ -190,6 +451,8 @@ struct iecm_adapter {
 
 	wait_queue_head_t vchnl_wq;
 	wait_queue_head_t sw_marker_wq;
+	DECLARE_BITMAP(vc_state, IECM_VC_NBITS);
+	char vc_msg[IECM_DFLT_MBX_BUF_SIZE];
 	struct iecm_rss_data rss_data;
 	struct iecm_dev_ops dev_ops;
 	s32 link_speed;
@@ -215,6 +478,38 @@ struct iecm_adapter {
 	spinlock_t fdir_fltr_list_lock;
 };
 
+/**
+ * iecm_is_queue_model_split - check if queue model is split
+ * @q_model: queue model single or split
+ *
+ * Returns true if queue model is split else false
+ */
+static inline int iecm_is_queue_model_split(u16 q_model)
+{
+	return (q_model == VIRTCHNL2_QUEUE_MODEL_SPLIT);
+}
+
+#define iecm_is_cap_ena(adapter, field, flag) \
+	__iecm_is_cap_ena(adapter, false, field, flag)
+#define iecm_is_cap_ena_all(adapter, field, flag) \
+	__iecm_is_cap_ena(adapter, true, field, flag)
+/**
+ * __iecm_is_cap_ena - Determine if HW capability is supported
+ * @adapter: private data struct
+ * @all: all or one flag
+ * @field: cap field to check
+ * @flag: Feature flag to check
+ *
+ * iecm_is_cap_ena_all is used to check if all the capability bits are set
+ * ('AND' operation) where as iecm_is_cap_ena is used to check if
+ * any one of the capability bits is set ('OR' operation)
+ */
+static inline bool __iecm_is_cap_ena(struct iecm_adapter *adapter, bool all,
+				     enum iecm_cap_field field, u64 flag)
+{
+	return adapter->dev_ops.vc_ops.is_cap_ena(adapter, all, field, flag);
+}
+
 /**
  * iecm_is_reset_detected - check if we were reset at some point
  * @adapter: driver specific private structure
@@ -233,6 +528,25 @@ int iecm_probe(struct pci_dev *pdev,
 void iecm_remove(struct pci_dev *pdev);
 int iecm_init_dflt_mbx(struct iecm_adapter *adapter);
 void iecm_deinit_dflt_mbx(struct iecm_adapter *adapter);
+void iecm_vc_ops_init(struct iecm_adapter *adapter);
+int iecm_vc_core_init(struct iecm_adapter *adapter, int *vport_id);
+int iecm_wait_for_event(struct iecm_adapter *adapter,
+			enum iecm_vport_vc_state state,
+			enum iecm_vport_vc_state err_check);
+int iecm_min_wait_for_event(struct iecm_adapter *adapter,
+			    enum iecm_vport_vc_state state,
+			    enum iecm_vport_vc_state err_check);
+int iecm_send_get_caps_msg(struct iecm_adapter *adapter);
 int iecm_vport_params_buf_alloc(struct iecm_adapter *adapter);
 void iecm_vport_params_buf_rel(struct iecm_adapter *adapter);
+int iecm_get_vec_ids(struct iecm_adapter *adapter,
+		     u16 *vecids, int num_vecids,
+		     struct virtchnl2_vector_chunks *chunks);
+int iecm_recv_mb_msg(struct iecm_adapter *adapter, enum virtchnl_ops op,
+		     void *msg, int msg_size);
+int iecm_send_mb_msg(struct iecm_adapter *adapter, enum virtchnl_ops op,
+		     u16 msg_size, u8 *msg);
+int iecm_set_msg_pending(struct iecm_adapter *adapter,
+			 struct iecm_ctlq_msg *ctlq_msg,
+			 enum iecm_vport_vc_state err_enum);
 #endif /* !_IECM_H_ */
diff --git a/drivers/net/ethernet/intel/include/iecm_txrx.h b/drivers/net/ethernet/intel/include/iecm_txrx.h
index 602d3b3b19dd..e1348011c991 100644
--- a/drivers/net/ethernet/intel/include/iecm_txrx.h
+++ b/drivers/net/ethernet/intel/include/iecm_txrx.h
@@ -30,4 +30,98 @@
 #define IECM_DFLT_SPLITQ_RX_Q_GROUPS		4
 #define IECM_DFLT_SPLITQ_TXQ_PER_GROUP		1
 #define IECM_DFLT_SPLITQ_RXQ_PER_GROUP		1
+
+/* Default vector sharing */
+#define IECM_NONQ_VEC		1
+#define IECM_MAX_Q_VEC		4 /* For Tx Completion queue and Rx queue */
+#define IECM_MIN_Q_VEC		1
+#define IECM_MAX_RDMA_VEC	2 /* To share with RDMA */
+#define IECM_MIN_RDMA_VEC	1 /* Minimum vectors to be shared with RDMA */
+#define IECM_MIN_VEC		3 /* One for mailbox, one for data queues, one
+				   * for RDMA
+				   */
+
+#define IECM_DFLT_TX_Q_DESC_COUNT		512
+#define IECM_DFLT_TX_COMPLQ_DESC_COUNT		512
+#define IECM_DFLT_RX_Q_DESC_COUNT		512
+/* IMPORTANT: We absolutely _cannot_ have more buffers in the system than a
+ * given RX completion queue has descriptors. This includes _ALL_ buffer
+ * queues. E.g.: If you have two buffer queues of 512 descriptors and buffers,
+ * you have a total of 1024 buffers so your RX queue _must_ have at least that
+ * many descriptors. This macro divides a given number of RX descriptors by
+ * number of buffer queues to calculate how many descriptors each buffer queue
+ * can have without overrunning the RX queue.
+ *
+ * If you give hardware more buffers than completion descriptors what will
+ * happen is that if hardware gets a chance to post more than ring wrap of
+ * descriptors before SW gets an interrupt and overwrites SW head, the gen bit
+ * in the descriptor will be wrong. Any overwritten descriptors' buffers will
+ * be gone forever and SW has no reasonable way to tell that this has happened.
+ * From SW perspective, when we finally get an interrupt, it looks like we're
+ * still waiting for descriptor to be done, stalling forever.
+ */
+#define IECM_RX_BUFQ_DESC_COUNT(RXD, NUM_BUFQ)	((RXD) / (NUM_BUFQ))
+
+#define IECM_RX_BUFQ_WORKING_SET(R)		((R)->desc_count - 1)
+#define IECM_RX_BUFQ_NON_WORKING_SET(R)		((R)->desc_count - \
+						 IECM_RX_BUFQ_WORKING_SET(R))
+
+#define IECM_RX_HDR_SIZE			256
+#define IECM_RX_BUF_2048			2048
+#define IECM_RX_BUF_4096			4096
+#define IECM_RX_BUF_STRIDE			64
+#define IECM_LOW_WATERMARK			64
+#define IECM_HDR_BUF_SIZE			256
+#define IECM_PACKET_HDR_PAD	\
+	(ETH_HLEN + ETH_FCS_LEN + (VLAN_HLEN * 2))
+#define IECM_MAX_RXBUFFER			9728
+#define IECM_MAX_MTU		\
+	(IECM_MAX_RXBUFFER - IECM_PACKET_HDR_PAD)
+#define IECM_INT_NAME_STR_LEN	(IFNAMSIZ + 16)
+
+#define IECM_TX_COMPLQ_CLEAN_BUDGET	256
+
+struct iecm_intr_reg {
+	u32 dyn_ctl;
+	u32 dyn_ctl_intena_m;
+	u32 dyn_ctl_clrpba_m;
+	u32 dyn_ctl_itridx_s;
+	u32 dyn_ctl_itridx_m;
+	u32 dyn_ctl_intrvl_s;
+	u32 rx_itr;
+	u32 tx_itr;
+	u32 icr_ena;
+	u32 icr_ena_ctlq_m;
+};
+
+struct iecm_q_vector {
+	struct iecm_vport *vport;
+	cpumask_t affinity_mask;
+	struct napi_struct napi;
+	u16 v_idx;		/* index in the vport->q_vector array */
+	struct iecm_intr_reg intr_reg;
+
+	int num_txq;
+	struct iecm_queue **tx;
+	struct dim tx_dim;	/* data for net_dim algorithm */
+	u16 tx_itr_value;
+	bool tx_intr_mode;
+	u32 tx_itr_idx;
+
+	int num_rxq;
+	struct iecm_queue **rx;
+	struct dim rx_dim;	/* data for net_dim algorithm */
+	u16 rx_itr_value;
+	bool rx_intr_mode;
+	u32 rx_itr_idx;
+
+	int num_bufq;
+	struct iecm_queue **bufq;
+
+	u16 total_events;       /* net_dim(): number of interrupts processed */
+	char name[IECM_INT_NAME_STR_LEN];
+};
+
+irqreturn_t
+iecm_vport_intr_clean_queues(int __always_unused irq, void *data);
 #endif /* !_IECM_TXRX_H_ */
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 06/19] iecm: add virtchnl messages for queues
  2022-01-28  0:09 [Intel-wired-lan] [PATCH net-next 00/19] Add iecm and idpf Alan Brady
                   ` (4 preceding siblings ...)
  2022-01-28  0:09 ` [Intel-wired-lan] [PATCH net-next 05/19] iecm: add vport alloc and virtchnl messages Alan Brady
@ 2022-01-28  0:09 ` Alan Brady
  2022-01-28 13:03   ` Alexander Lobakin
  2022-01-28  0:09 ` [Intel-wired-lan] [PATCH net-next 07/19] iecm: finish virtchnl messages Alan Brady
                   ` (13 subsequent siblings)
  19 siblings, 1 reply; 89+ messages in thread
From: Alan Brady @ 2022-01-28  0:09 UTC (permalink / raw)
  To: intel-wired-lan

This continues adding virtchnl messages. This largely relates to adding
messages needed to negotiate and setup traffic queues.

Signed-off-by: Phani Burra <phani.r.burra@intel.com>
Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
Signed-off-by: Alice Michael <alice.michael@intel.com>
Signed-off-by: Alan Brady <alan.brady@intel.com>
---
 drivers/net/ethernet/intel/iecm/iecm_lib.c    |   14 +
 drivers/net/ethernet/intel/iecm/iecm_txrx.c   |  161 +++
 .../net/ethernet/intel/iecm/iecm_virtchnl.c   | 1127 ++++++++++++++++-
 drivers/net/ethernet/intel/include/iecm.h     |   22 +
 .../net/ethernet/intel/include/iecm_txrx.h    |  196 +++
 5 files changed, 1505 insertions(+), 15 deletions(-)

diff --git a/drivers/net/ethernet/intel/iecm/iecm_lib.c b/drivers/net/ethernet/intel/iecm/iecm_lib.c
index e2e523f0700e..4e9cc7f2d138 100644
--- a/drivers/net/ethernet/intel/iecm/iecm_lib.c
+++ b/drivers/net/ethernet/intel/iecm/iecm_lib.c
@@ -90,6 +90,20 @@ static void iecm_vport_rel_all(struct iecm_adapter *adapter)
 	adapter->num_alloc_vport = 0;
 }
 
+/**
+ * iecm_vport_set_hsplit - enable or disable header split on a given vport
+ * @vport: virtual port
+ * @ena: flag controlling header split, On (true) or Off (false)
+ */
+void iecm_vport_set_hsplit(struct iecm_vport *vport, bool ena)
+{
+	if (iecm_is_cap_ena_all(vport->adapter, IECM_HSPLIT_CAPS,
+				IECM_CAP_HSPLIT) &&
+	    iecm_is_queue_model_split(vport->rxq_model))
+		set_bit(__IECM_PRIV_FLAGS_HDR_SPLIT,
+			vport->adapter->config_data.user_flags);
+}
+
 /**
  * iecm_vport_alloc - Allocates the next available struct vport in the adapter
  * @adapter: board private structure
diff --git a/drivers/net/ethernet/intel/iecm/iecm_txrx.c b/drivers/net/ethernet/intel/iecm/iecm_txrx.c
index 2f5c16a28266..2dfb0be002e3 100644
--- a/drivers/net/ethernet/intel/iecm/iecm_txrx.c
+++ b/drivers/net/ethernet/intel/iecm/iecm_txrx.c
@@ -20,3 +20,164 @@ iecm_vport_intr_clean_queues(int __always_unused irq, void *data)
 	return IRQ_HANDLED;
 }
 
+/**
+ * iecm_vport_init_num_qs - Initialize number of queues
+ * @vport: vport to initialize queues
+ * @vport_msg: data to be filled into vport
+ */
+void iecm_vport_init_num_qs(struct iecm_vport *vport, struct virtchnl2_create_vport *vport_msg)
+{
+	vport->num_txq = le16_to_cpu(vport_msg->num_tx_q);
+	vport->num_rxq = le16_to_cpu(vport_msg->num_rx_q);
+	/* number of txqs and rxqs in config data will be zeros only in the
+	 * driver load path and we dont update them there after
+	 */
+	if (!vport->adapter->config_data.num_req_tx_qs &&
+	    !vport->adapter->config_data.num_req_rx_qs) {
+		vport->adapter->config_data.num_req_tx_qs =
+					le16_to_cpu(vport_msg->num_tx_q);
+		vport->adapter->config_data.num_req_rx_qs =
+					le16_to_cpu(vport_msg->num_rx_q);
+	}
+
+	if (iecm_is_queue_model_split(vport->txq_model))
+		vport->num_complq = le16_to_cpu(vport_msg->num_tx_complq);
+	if (iecm_is_queue_model_split(vport->rxq_model))
+		vport->num_bufq = le16_to_cpu(vport_msg->num_rx_bufq);
+}
+
+/**
+ * iecm_vport_calc_num_q_desc - Calculate number of queue groups
+ * @vport: vport to calculate q groups for
+ */
+void iecm_vport_calc_num_q_desc(struct iecm_vport *vport)
+{
+	int num_req_txq_desc = vport->adapter->config_data.num_req_txq_desc;
+	int num_req_rxq_desc = vport->adapter->config_data.num_req_rxq_desc;
+	int num_bufqs = vport->num_bufqs_per_qgrp;
+	int i = 0;
+
+	vport->complq_desc_count = 0;
+	if (num_req_txq_desc) {
+		vport->txq_desc_count = num_req_txq_desc;
+		if (iecm_is_queue_model_split(vport->txq_model)) {
+			vport->complq_desc_count = num_req_txq_desc;
+			if (vport->complq_desc_count < IECM_MIN_TXQ_COMPLQ_DESC)
+				vport->complq_desc_count =
+					IECM_MIN_TXQ_COMPLQ_DESC;
+		}
+	} else {
+		vport->txq_desc_count =
+			IECM_DFLT_TX_Q_DESC_COUNT;
+		if (iecm_is_queue_model_split(vport->txq_model)) {
+			vport->complq_desc_count =
+				IECM_DFLT_TX_COMPLQ_DESC_COUNT;
+		}
+	}
+
+	if (num_req_rxq_desc)
+		vport->rxq_desc_count = num_req_rxq_desc;
+	else
+		vport->rxq_desc_count = IECM_DFLT_RX_Q_DESC_COUNT;
+
+	for (i = 0; i < num_bufqs; i++) {
+		if (!vport->bufq_desc_count[i])
+			vport->bufq_desc_count[i] =
+				IECM_RX_BUFQ_DESC_COUNT(vport->rxq_desc_count,
+							num_bufqs);
+	}
+}
+EXPORT_SYMBOL(iecm_vport_calc_num_q_desc);
+
+/**
+ * iecm_vport_calc_total_qs - Calculate total number of queues
+ * @adapter: private data struct
+ * @vport_msg: message to fill with data
+ */
+void iecm_vport_calc_total_qs(struct iecm_adapter *adapter,
+			      struct virtchnl2_create_vport *vport_msg)
+{
+	unsigned int num_req_tx_qs = adapter->config_data.num_req_tx_qs;
+	unsigned int num_req_rx_qs = adapter->config_data.num_req_rx_qs;
+	int dflt_splitq_txq_grps, dflt_singleq_txqs;
+	int dflt_splitq_rxq_grps, dflt_singleq_rxqs;
+	int num_txq_grps, num_rxq_grps;
+	int num_cpus;
+	u16 max_q;
+
+	/* Restrict num of queues to cpus online as a default configuration to
+	 * give best performance. User can always override to a max number
+	 * of queues via ethtool.
+	 */
+	num_cpus = num_online_cpus();
+	max_q = adapter->max_queue_limit;
+
+	dflt_splitq_txq_grps = min_t(int, max_q, num_cpus);
+	dflt_singleq_txqs = min_t(int, max_q, num_cpus);
+	dflt_splitq_rxq_grps = min_t(int, max_q, num_cpus);
+	dflt_singleq_rxqs = min_t(int, max_q, num_cpus);
+
+	if (iecm_is_queue_model_split(le16_to_cpu(vport_msg->txq_model))) {
+		num_txq_grps = num_req_tx_qs ? num_req_tx_qs : dflt_splitq_txq_grps;
+		vport_msg->num_tx_complq = cpu_to_le16(num_txq_grps *
+						       IECM_COMPLQ_PER_GROUP);
+		vport_msg->num_tx_q = cpu_to_le16(num_txq_grps *
+						  IECM_DFLT_SPLITQ_TXQ_PER_GROUP);
+	} else {
+		num_txq_grps = IECM_DFLT_SINGLEQ_TX_Q_GROUPS;
+		vport_msg->num_tx_q =
+				cpu_to_le16(num_txq_grps *
+					    (num_req_tx_qs ? num_req_tx_qs :
+					    dflt_singleq_txqs));
+		vport_msg->num_tx_complq = 0;
+	}
+	if (iecm_is_queue_model_split(le16_to_cpu(vport_msg->rxq_model))) {
+		num_rxq_grps = num_req_rx_qs ? num_req_rx_qs : dflt_splitq_rxq_grps;
+		vport_msg->num_rx_bufq =
+					cpu_to_le16(num_rxq_grps *
+						    IECM_MAX_BUFQS_PER_RXQ_GRP);
+
+		vport_msg->num_rx_q = cpu_to_le16(num_rxq_grps *
+						  IECM_DFLT_SPLITQ_RXQ_PER_GROUP);
+	} else {
+		num_rxq_grps = IECM_DFLT_SINGLEQ_RX_Q_GROUPS;
+		vport_msg->num_rx_bufq = 0;
+		vport_msg->num_rx_q =
+				cpu_to_le16(num_rxq_grps *
+					    (num_req_rx_qs ? num_req_rx_qs :
+					    dflt_singleq_rxqs));
+	}
+}
+
+/**
+ * iecm_vport_calc_num_q_groups - Calculate number of queue groups
+ * @vport: vport to calculate q groups for
+ */
+void iecm_vport_calc_num_q_groups(struct iecm_vport *vport)
+{
+	if (iecm_is_queue_model_split(vport->txq_model))
+		vport->num_txq_grp = vport->num_txq;
+	else
+		vport->num_txq_grp = IECM_DFLT_SINGLEQ_TX_Q_GROUPS;
+
+	if (iecm_is_queue_model_split(vport->rxq_model))
+		vport->num_rxq_grp = vport->num_rxq;
+	else
+		vport->num_rxq_grp = IECM_DFLT_SINGLEQ_RX_Q_GROUPS;
+}
+EXPORT_SYMBOL(iecm_vport_calc_num_q_groups);
+
+/**
+ * iecm_vport_calc_num_q_vec - Calculate total number of vectors required for
+ * this vport
+ * @vport: virtual port
+ *
+ */
+void iecm_vport_calc_num_q_vec(struct iecm_vport *vport)
+{
+	if (iecm_is_queue_model_split(vport->txq_model))
+		vport->num_q_vectors = vport->num_txq_grp;
+	else
+		vport->num_q_vectors = vport->num_txq;
+}
+EXPORT_SYMBOL(iecm_vport_calc_num_q_vec);
diff --git a/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
index aae06064d706..d8152e657e24 100644
--- a/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
+++ b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
@@ -859,6 +859,48 @@ static int iecm_recv_get_caps_msg(struct iecm_adapter *adapter)
 				sizeof(struct virtchnl2_get_capabilities));
 }
 
+/**
+ * iecm_get_reg_intr_vecs - Get vector queue register offset
+ * @vport: virtual port structure
+ * @reg_vals: Register offsets to store in
+ * @num_vecs: Number of vector registers
+ *
+ * Returns number of regsiters that got populated
+ */
+int iecm_get_reg_intr_vecs(struct iecm_vport *vport,
+			   struct iecm_vec_regs *reg_vals, int num_vecs)
+{
+	struct virtchnl2_vector_chunks *chunks;
+	struct iecm_vec_regs reg_val;
+	u16 num_vchunks, num_vec;
+	int num_regs = 0, i, j;
+
+	chunks = &vport->adapter->req_vec_chunks->vchunks;
+	num_vchunks = le16_to_cpu(chunks->num_vchunks);
+
+	for (j = 0; j < num_vchunks; j++) {
+		struct virtchnl2_vector_chunk *chunk = &chunks->vchunks[j];
+
+		num_vec = le16_to_cpu(chunk->num_vectors);
+		reg_val.dyn_ctl_reg = le32_to_cpu(chunk->dynctl_reg_start);
+		reg_val.itrn_reg = le32_to_cpu(chunk->itrn_reg_start);
+		for (i = 0; i < num_vec; i++) {
+			if (num_regs == num_vecs)
+				break;
+			reg_vals[i].dyn_ctl_reg = reg_val.dyn_ctl_reg;
+			reg_vals[i].itrn_reg = reg_val.itrn_reg;
+			reg_val.dyn_ctl_reg +=
+				le32_to_cpu(chunk->dynctl_reg_spacing);
+			reg_val.itrn_reg +=
+				le32_to_cpu(chunk->itrn_reg_spacing);
+			num_regs++;
+		}
+	}
+
+	return num_regs;
+}
+EXPORT_SYMBOL(iecm_get_reg_intr_vecs);
+
 /**
  * iecm_send_create_vport_msg - Send virtchnl create vport message
  * @adapter: Driver specific private structure
@@ -869,8 +911,36 @@ static int iecm_recv_get_caps_msg(struct iecm_adapter *adapter)
  */
 static int iecm_send_create_vport_msg(struct iecm_adapter *adapter)
 {
-	/* stub */
-	return 0;
+	struct virtchnl2_create_vport *vport_msg;
+	int buf_size;
+
+	buf_size = sizeof(struct virtchnl2_create_vport);
+	if (!adapter->vport_params_reqd[0]) {
+		adapter->vport_params_reqd[0] = kzalloc(buf_size, GFP_KERNEL);
+		if (!adapter->vport_params_reqd[0])
+			return -ENOMEM;
+	}
+
+	vport_msg = (struct virtchnl2_create_vport *)
+			adapter->vport_params_reqd[0];
+	vport_msg->vport_type = cpu_to_le16(VIRTCHNL2_VPORT_TYPE_DEFAULT);
+
+	if (test_bit(__IECM_REQ_TX_SPLITQ, adapter->flags))
+		vport_msg->txq_model = cpu_to_le16(VIRTCHNL2_QUEUE_MODEL_SPLIT);
+	else
+		vport_msg->txq_model = cpu_to_le16(VIRTCHNL2_QUEUE_MODEL_SINGLE);
+
+	if (test_bit(__IECM_REQ_RX_SPLITQ, adapter->flags))
+		vport_msg->rxq_model = cpu_to_le16(VIRTCHNL2_QUEUE_MODEL_SPLIT);
+	else
+		vport_msg->rxq_model = cpu_to_le16(VIRTCHNL2_QUEUE_MODEL_SINGLE);
+
+	adapter->dev_ops.vc_ops.init_max_queues(adapter);
+
+	iecm_vport_calc_total_qs(adapter, vport_msg);
+
+	return iecm_send_mb_msg(adapter, VIRTCHNL2_OP_CREATE_VPORT, buf_size,
+				(u8 *)vport_msg);
 }
 
 /**
@@ -884,7 +954,25 @@ static int iecm_send_create_vport_msg(struct iecm_adapter *adapter)
 static int iecm_recv_create_vport_msg(struct iecm_adapter *adapter,
 				      int *vport_id)
 {
-	/* stub */
+	struct virtchnl2_create_vport *vport_msg;
+	int err;
+
+	if (!adapter->vport_params_recvd[0]) {
+		adapter->vport_params_recvd[0] = kzalloc(IECM_DFLT_MBX_BUF_SIZE,
+							 GFP_KERNEL);
+		if (!adapter->vport_params_recvd[0])
+			return -ENOMEM;
+	}
+
+	vport_msg = (struct virtchnl2_create_vport *)
+			adapter->vport_params_recvd[0];
+
+	err = iecm_recv_mb_msg(adapter, VIRTCHNL2_OP_CREATE_VPORT, vport_msg,
+			       IECM_DFLT_MBX_BUF_SIZE);
+	if (err)
+		return err;
+
+	*vport_id = le32_to_cpu(vport_msg->vport_id);
 	return 0;
 }
 
@@ -966,6 +1054,920 @@ int iecm_wait_for_event(struct iecm_adapter *adapter,
 }
 EXPORT_SYMBOL(iecm_wait_for_event);
 
+/**
+ * iecm_wait_for_marker_event - wait for software marker response
+ * @vport: virtual port data structure
+ *
+ * Returns 0 success, negative on failure.
+ **/
+static int iecm_wait_for_marker_event(struct iecm_vport *vport)
+{
+	int event = 0;
+	int i;
+
+	for (i = 0; i < vport->num_txq; i++)
+		set_bit(__IECM_Q_SW_MARKER, vport->txqs[i]->flags);
+
+	event = wait_event_timeout(vport->adapter->sw_marker_wq,
+				   test_and_clear_bit(__IECM_SW_MARKER,
+						      vport->adapter->flags),
+				   msecs_to_jiffies(500));
+	if (event)
+		return 0;
+	return -ETIMEDOUT;
+}
+
+/**
+ * iecm_send_destroy_vport_msg - Send virtchnl destroy vport message
+ * @vport: virtual port data structure
+ *
+ * Send virtchnl destroy vport message.  Returns 0 on success, negative on
+ * failure.
+ */
+int iecm_send_destroy_vport_msg(struct iecm_vport *vport)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	struct virtchnl2_vport v_id;
+	int err;
+
+	v_id.vport_id = cpu_to_le32(vport->vport_id);
+
+	err = iecm_send_mb_msg(adapter, VIRTCHNL2_OP_DESTROY_VPORT,
+			       sizeof(v_id), (u8 *)&v_id);
+	if (err)
+		return err;
+
+	return iecm_min_wait_for_event(adapter, IECM_VC_DESTROY_VPORT,
+				       IECM_VC_DESTROY_VPORT_ERR);
+}
+
+/**
+ * iecm_send_enable_vport_msg - Send virtchnl enable vport message
+ * @vport: virtual port data structure
+ *
+ * Send enable vport virtchnl message.  Returns 0 on success, negative on
+ * failure.
+ */
+int iecm_send_enable_vport_msg(struct iecm_vport *vport)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	struct virtchnl2_vport v_id;
+	int err;
+
+	v_id.vport_id = cpu_to_le32(vport->vport_id);
+
+	err = iecm_send_mb_msg(adapter, VIRTCHNL2_OP_ENABLE_VPORT,
+			       sizeof(v_id), (u8 *)&v_id);
+	if (err)
+		return err;
+
+	return iecm_wait_for_event(adapter, IECM_VC_ENA_VPORT,
+				   IECM_VC_ENA_VPORT_ERR);
+}
+
+/**
+ * iecm_send_disable_vport_msg - Send virtchnl disable vport message
+ * @vport: virtual port data structure
+ *
+ * Send disable vport virtchnl message.  Returns 0 on success, negative on
+ * failure.
+ */
+int iecm_send_disable_vport_msg(struct iecm_vport *vport)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	struct virtchnl2_vport v_id;
+	int err;
+
+	v_id.vport_id = cpu_to_le32(vport->vport_id);
+
+	err = iecm_send_mb_msg(adapter, VIRTCHNL2_OP_DISABLE_VPORT,
+			       sizeof(v_id), (u8 *)&v_id);
+	if (err)
+		return err;
+
+	return iecm_min_wait_for_event(adapter, IECM_VC_DIS_VPORT,
+				       IECM_VC_DIS_VPORT_ERR);
+}
+
+/**
+ * iecm_send_config_tx_queues_msg - Send virtchnl config tx queues message
+ * @vport: virtual port data structure
+ *
+ * Send config tx queues virtchnl message. Returns 0 on success, negative on
+ * failure.
+ */
+int iecm_send_config_tx_queues_msg(struct iecm_vport *vport)
+{
+	struct virtchnl2_config_tx_queues *ctq = NULL;
+	int config_data_size, chunk_size, buf_size = 0;
+	int totqs, num_msgs, num_chunks;
+	struct virtchnl2_txq_info *qi;
+	int err = 0, i, k = 0;
+	bool alloc = false;
+
+	totqs = vport->num_txq + vport->num_complq;
+	qi = kcalloc(totqs, sizeof(struct virtchnl2_txq_info), GFP_KERNEL);
+	if (!qi)
+		return -ENOMEM;
+
+	/* Populate the queue info buffer with all queue context info */
+	for (i = 0; i < vport->num_txq_grp; i++) {
+		struct iecm_txq_group *tx_qgrp = &vport->txq_grps[i];
+		int j;
+
+		for (j = 0; j < tx_qgrp->num_txq; j++, k++) {
+			qi[k].queue_id =
+				cpu_to_le32(tx_qgrp->txqs[j]->q_id);
+			qi[k].model =
+				cpu_to_le16(vport->txq_model);
+			qi[k].type =
+				cpu_to_le32(tx_qgrp->txqs[j]->q_type);
+			qi[k].ring_len =
+				cpu_to_le16(tx_qgrp->txqs[j]->desc_count);
+			qi[k].dma_ring_addr =
+				cpu_to_le64(tx_qgrp->txqs[j]->dma);
+			if (iecm_is_queue_model_split(vport->txq_model)) {
+				struct iecm_queue *q = tx_qgrp->txqs[j];
+
+				qi[k].tx_compl_queue_id =
+					cpu_to_le16(tx_qgrp->complq->q_id);
+
+				if (test_bit(__IECM_Q_FLOW_SCH_EN, q->flags))
+					qi[k].sched_mode =
+					cpu_to_le16(VIRTCHNL2_TXQ_SCHED_MODE_FLOW);
+				else
+					qi[k].sched_mode =
+					cpu_to_le16(VIRTCHNL2_TXQ_SCHED_MODE_QUEUE);
+			} else {
+				qi[k].sched_mode =
+					cpu_to_le16(VIRTCHNL2_TXQ_SCHED_MODE_QUEUE);
+			}
+		}
+
+		if (iecm_is_queue_model_split(vport->txq_model)) {
+			qi[k].queue_id =
+				cpu_to_le32(tx_qgrp->complq->q_id);
+			qi[k].model =
+				cpu_to_le16(vport->txq_model);
+			qi[k].type =
+				cpu_to_le32(tx_qgrp->complq->q_type);
+			qi[k].ring_len =
+				cpu_to_le16(tx_qgrp->complq->desc_count);
+			qi[k].dma_ring_addr =
+				cpu_to_le64(tx_qgrp->complq->dma);
+			k++;
+		}
+	}
+
+	/* Make sure accounting agrees */
+	if (k != totqs) {
+		err = -EINVAL;
+		goto error;
+	}
+
+	/* Chunk up the queue contexts into multiple messages to avoid
+	 * sending a control queue message buffer that is too large
+	 */
+	config_data_size = sizeof(struct virtchnl2_config_tx_queues);
+	chunk_size = sizeof(struct virtchnl2_txq_info);
+
+	num_chunks = IECM_NUM_CHUNKS_PER_MSG(config_data_size, chunk_size) + 1;
+	if (totqs < num_chunks)
+		num_chunks = totqs;
+
+	num_msgs = totqs / num_chunks;
+	if (totqs % num_chunks)
+		num_msgs++;
+
+	for (i = 0, k = 0; i < num_msgs; i++) {
+		if (!ctq || alloc) {
+			buf_size = (chunk_size * (num_chunks - 1)) +
+					config_data_size;
+			kfree(ctq);
+			ctq = kzalloc(buf_size, GFP_KERNEL);
+			if (!ctq) {
+				err = -ENOMEM;
+				goto error;
+			}
+		} else {
+			memset(ctq, 0, buf_size);
+		}
+
+		ctq->vport_id = cpu_to_le32(vport->vport_id);
+		ctq->num_qinfo = cpu_to_le16(num_chunks);
+		memcpy(ctq->qinfo, &qi[k], chunk_size * num_chunks);
+
+		err = iecm_send_mb_msg(vport->adapter,
+				       VIRTCHNL2_OP_CONFIG_TX_QUEUES,
+				       buf_size, (u8 *)ctq);
+		if (err)
+			goto mbx_error;
+
+		err = iecm_wait_for_event(vport->adapter, IECM_VC_CONFIG_TXQ,
+					  IECM_VC_CONFIG_TXQ_ERR);
+		if (err)
+			goto mbx_error;
+
+		k += num_chunks;
+		totqs -= num_chunks;
+		if (totqs < num_chunks) {
+			num_chunks = totqs;
+			alloc = true;
+		}
+	}
+
+mbx_error:
+	kfree(ctq);
+error:
+	kfree(qi);
+	return err;
+}
+
+/**
+ * iecm_send_config_rx_queues_msg - Send virtchnl config rx queues message
+ * @vport: virtual port data structure
+ *
+ * Send config rx queues virtchnl message.  Returns 0 on success, negative on
+ * failure.
+ */
+int iecm_send_config_rx_queues_msg(struct iecm_vport *vport)
+{
+	struct virtchnl2_config_rx_queues *crq = NULL;
+	int config_data_size, chunk_size, buf_size = 0;
+	int totqs, num_msgs, num_chunks;
+	struct virtchnl2_rxq_info *qi;
+	int err = 0, i, k = 0;
+	bool alloc = false;
+
+	totqs = vport->num_rxq + vport->num_bufq;
+	qi = kcalloc(totqs, sizeof(struct virtchnl2_rxq_info), GFP_KERNEL);
+	if (!qi)
+		return -ENOMEM;
+
+	/* Populate the queue info buffer with all queue context info */
+	for (i = 0; i < vport->num_rxq_grp; i++) {
+		struct iecm_rxq_group *rx_qgrp = &vport->rxq_grps[i];
+		int num_rxq;
+		int j;
+
+		if (iecm_is_queue_model_split(vport->rxq_model)) {
+			for (j = 0; j < vport->num_bufqs_per_qgrp; j++, k++) {
+				struct iecm_queue *bufq =
+					&rx_qgrp->splitq.bufq_sets[j].bufq;
+
+				qi[k].queue_id =
+					cpu_to_le32(bufq->q_id);
+				qi[k].model =
+					cpu_to_le16(vport->rxq_model);
+				qi[k].type =
+					cpu_to_le32(bufq->q_type);
+				qi[k].desc_ids =
+					cpu_to_le64(VIRTCHNL2_RXDID_1_FLEX_SPLITQ_M);
+				qi[k].ring_len =
+					cpu_to_le16(bufq->desc_count);
+				qi[k].dma_ring_addr =
+					cpu_to_le64(bufq->dma);
+				qi[k].data_buffer_size =
+					cpu_to_le32(bufq->rx_buf_size);
+				qi[k].buffer_notif_stride =
+					bufq->rx_buf_stride;
+				qi[k].rx_buffer_low_watermark =
+					cpu_to_le16(bufq->rx_buffer_low_watermark);
+			}
+		}
+
+		if (iecm_is_queue_model_split(vport->rxq_model))
+			num_rxq = rx_qgrp->splitq.num_rxq_sets;
+		else
+			num_rxq = rx_qgrp->singleq.num_rxq;
+
+		for (j = 0; j < num_rxq; j++, k++) {
+			struct iecm_queue *rxq;
+
+			if (iecm_is_queue_model_split(vport->rxq_model)) {
+				rxq = &rx_qgrp->splitq.rxq_sets[j]->rxq;
+				qi[k].rx_bufq1_id =
+				  cpu_to_le16(rxq->rxq_grp->splitq.bufq_sets[0].bufq.q_id);
+				qi[k].rx_bufq2_id =
+				  cpu_to_le16(rxq->rxq_grp->splitq.bufq_sets[1].bufq.q_id);
+				qi[k].hdr_buffer_size =
+					cpu_to_le16(rxq->rx_hbuf_size);
+				qi[k].rx_buffer_low_watermark =
+					cpu_to_le16(rxq->rx_buffer_low_watermark);
+
+				if (rxq->rx_hsplit_en) {
+					qi[k].qflags =
+						cpu_to_le16(VIRTCHNL2_RXQ_HDR_SPLIT);
+					qi[k].hdr_buffer_size =
+						cpu_to_le16(rxq->rx_hbuf_size);
+				}
+			} else {
+				rxq = rx_qgrp->singleq.rxqs[j];
+			}
+
+			qi[k].queue_id =
+				cpu_to_le32(rxq->q_id);
+			qi[k].model =
+				cpu_to_le16(vport->rxq_model);
+			qi[k].type =
+				cpu_to_le32(rxq->q_type);
+			qi[k].ring_len =
+				cpu_to_le16(rxq->desc_count);
+			qi[k].dma_ring_addr =
+				cpu_to_le64(rxq->dma);
+			qi[k].max_pkt_size =
+				cpu_to_le32(rxq->rx_max_pkt_size);
+			qi[k].data_buffer_size =
+				cpu_to_le32(rxq->rx_buf_size);
+			qi[k].qflags |=
+				cpu_to_le16(VIRTCHNL2_RX_DESC_SIZE_32BYTE);
+			qi[k].desc_ids =
+				cpu_to_le64(rxq->rxdids);
+		}
+	}
+
+	/* Make sure accounting agrees */
+	if (k != totqs) {
+		err = -EINVAL;
+		goto error;
+	}
+
+	/* Chunk up the queue contexts into multiple messages to avoid
+	 * sending a control queue message buffer that is too large
+	 */
+	config_data_size = sizeof(struct virtchnl2_config_rx_queues);
+	chunk_size = sizeof(struct virtchnl2_rxq_info);
+
+	num_chunks = IECM_NUM_CHUNKS_PER_MSG(config_data_size, chunk_size) + 1;
+	if (totqs < num_chunks)
+		num_chunks = totqs;
+
+	num_msgs = totqs / num_chunks;
+	if (totqs % num_chunks)
+		num_msgs++;
+
+	for (i = 0, k = 0; i < num_msgs; i++) {
+		if (!crq || alloc) {
+			buf_size = (chunk_size * (num_chunks - 1)) +
+					config_data_size;
+			kfree(crq);
+			crq = kzalloc(buf_size, GFP_KERNEL);
+			if (!crq) {
+				err = -ENOMEM;
+				goto error;
+			}
+		} else {
+			memset(crq, 0, buf_size);
+		}
+
+		crq->vport_id = cpu_to_le32(vport->vport_id);
+		crq->num_qinfo = cpu_to_le16(num_chunks);
+		memcpy(crq->qinfo, &qi[k], chunk_size * num_chunks);
+
+		err = iecm_send_mb_msg(vport->adapter,
+				       VIRTCHNL2_OP_CONFIG_RX_QUEUES,
+				       buf_size, (u8 *)crq);
+		if (err)
+			goto mbx_error;
+
+		err = iecm_wait_for_event(vport->adapter, IECM_VC_CONFIG_RXQ,
+					  IECM_VC_CONFIG_RXQ_ERR);
+		if (err)
+			goto mbx_error;
+
+		k += num_chunks;
+		totqs -= num_chunks;
+		if (totqs < num_chunks) {
+			num_chunks = totqs;
+			alloc = true;
+		}
+	}
+
+mbx_error:
+	kfree(crq);
+error:
+	kfree(qi);
+	return err;
+}
+
+/**
+ * iecm_send_ena_dis_queues_msg - Send virtchnl enable or disable
+ * queues message
+ * @vport: virtual port data structure
+ * @vc_op: virtchnl op code to send
+ *
+ * Send enable or disable queues virtchnl message. Returns 0 on success,
+ * negative on failure.
+ */
+static int iecm_send_ena_dis_queues_msg(struct iecm_vport *vport,
+					enum virtchnl_ops vc_op)
+{
+	int num_msgs, num_chunks, config_data_size, chunk_size;
+	int num_txq, num_rxq, num_q, buf_size, err = 0;
+	struct virtchnl2_del_ena_dis_queues *eq = NULL;
+	struct virtchnl2_queue_chunk *qc;
+	bool alloc = false;
+	int i, j, k = 0;
+
+	/* validate virtchnl op */
+	switch (vc_op) {
+	case VIRTCHNL2_OP_ENABLE_QUEUES:
+	case VIRTCHNL2_OP_DISABLE_QUEUES:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	num_txq = vport->num_txq + vport->num_complq;
+	num_rxq = vport->num_rxq + vport->num_bufq;
+	num_q = num_txq + num_rxq;
+	buf_size = sizeof(struct virtchnl2_queue_chunk) * (num_q);
+	qc = kzalloc(buf_size, GFP_KERNEL);
+	if (!qc)
+		return -ENOMEM;
+
+	for (i = 0; i < vport->num_txq_grp; i++) {
+		struct iecm_txq_group *tx_qgrp = &vport->txq_grps[i];
+
+		for (j = 0; j < tx_qgrp->num_txq; j++, k++) {
+			qc[k].type = cpu_to_le32(tx_qgrp->txqs[j]->q_type);
+			qc[k].start_queue_id =
+					cpu_to_le32(tx_qgrp->txqs[j]->q_id);
+			qc[k].num_queues = cpu_to_le32(1);
+		}
+	}
+	if (vport->num_txq != k) {
+		err = -EINVAL;
+		goto error;
+	}
+
+	if (iecm_is_queue_model_split(vport->txq_model)) {
+		for (i = 0; i < vport->num_txq_grp; i++, k++) {
+			struct iecm_txq_group *tx_qgrp = &vport->txq_grps[i];
+
+			qc[k].type = cpu_to_le32(tx_qgrp->complq->q_type);
+			qc[k].start_queue_id =
+					cpu_to_le32(tx_qgrp->complq->q_id);
+			qc[k].num_queues = cpu_to_le32(1);
+		}
+		if (vport->num_complq != (k - vport->num_txq)) {
+			err = -EINVAL;
+			goto error;
+		}
+	}
+
+	for (i = 0; i < vport->num_rxq_grp; i++) {
+		struct iecm_rxq_group *rx_qgrp = &vport->rxq_grps[i];
+
+		if (iecm_is_queue_model_split(vport->rxq_model))
+			num_rxq = rx_qgrp->splitq.num_rxq_sets;
+		else
+			num_rxq = rx_qgrp->singleq.num_rxq;
+
+		for (j = 0; j < num_rxq; j++, k++) {
+			if (iecm_is_queue_model_split(vport->rxq_model)) {
+				qc[k].start_queue_id =
+				cpu_to_le32(rx_qgrp->splitq.rxq_sets[j]->rxq.q_id);
+				qc[k].type =
+				cpu_to_le32(rx_qgrp->splitq.rxq_sets[j]->rxq.q_type);
+			} else {
+				qc[k].start_queue_id =
+				cpu_to_le32(rx_qgrp->singleq.rxqs[j]->q_id);
+				qc[k].type =
+				cpu_to_le32(rx_qgrp->singleq.rxqs[j]->q_type);
+			}
+			qc[k].num_queues = cpu_to_le32(1);
+		}
+	}
+	if (vport->num_rxq != k - (vport->num_txq + vport->num_complq)) {
+		err = -EINVAL;
+		goto error;
+	}
+
+	if (iecm_is_queue_model_split(vport->rxq_model)) {
+		for (i = 0; i < vport->num_rxq_grp; i++) {
+			struct iecm_rxq_group *rx_qgrp = &vport->rxq_grps[i];
+
+			for (j = 0; j < vport->num_bufqs_per_qgrp; j++, k++) {
+				struct iecm_queue *q = &rx_qgrp->splitq.bufq_sets[j].bufq;
+
+				qc[k].type = cpu_to_le32(q->q_type);
+				qc[k].start_queue_id = cpu_to_le32(q->q_id);
+				qc[k].num_queues = cpu_to_le32(1);
+			}
+		}
+		if (vport->num_bufq != k - (vport->num_txq +
+					       vport->num_complq +
+					       vport->num_rxq)) {
+			err = -EINVAL;
+			goto error;
+		}
+	}
+
+	/* Chunk up the queue info into multiple messages */
+	config_data_size = sizeof(struct virtchnl2_del_ena_dis_queues);
+	chunk_size = sizeof(struct virtchnl2_queue_chunk);
+
+	num_chunks = IECM_NUM_CHUNKS_PER_MSG(config_data_size, chunk_size) + 1;
+	if (num_q < num_chunks)
+		num_chunks = num_q;
+
+	num_msgs = num_q / num_chunks;
+	if (num_q % num_chunks)
+		num_msgs++;
+
+	for (i = 0, k = 0; i < num_msgs; i++) {
+		if (!eq || alloc) {
+			buf_size = (chunk_size * (num_chunks - 1)) +
+					config_data_size;
+			kfree(eq);
+			eq = kzalloc(buf_size, GFP_KERNEL);
+			if (!eq) {
+				err = -ENOMEM;
+				goto error;
+			}
+		} else {
+			memset(eq, 0, buf_size);
+		}
+		eq->vport_id = cpu_to_le32(vport->vport_id);
+		eq->chunks.num_chunks = cpu_to_le16(num_chunks);
+		memcpy(eq->chunks.chunks, &qc[k], chunk_size * num_chunks);
+
+		err = iecm_send_mb_msg(vport->adapter, vc_op, buf_size,
+				       (u8 *)eq);
+		if (err)
+			goto mbx_error;
+		k += num_chunks;
+		num_q -= num_chunks;
+		if (num_q < num_chunks) {
+			num_chunks = num_q;
+			alloc = true;
+		}
+	}
+mbx_error:
+	kfree(eq);
+error:
+	kfree(qc);
+	return err;
+}
+
+/**
+ * iecm_send_map_unmap_queue_vector_msg - Send virtchnl map or unmap queue
+ * vector message
+ * @vport: virtual port data structure
+ * @map: true for map and false for unmap
+ *
+ * Send map or unmap queue vector virtchnl message.  Returns 0 on success,
+ * negative on failure.
+ */
+int iecm_send_map_unmap_queue_vector_msg(struct iecm_vport *vport, bool map)
+{
+	int num_msgs, num_chunks, config_data_size, chunk_size;
+	struct virtchnl2_queue_vector_maps *vqvm = NULL;
+	struct iecm_adapter *adapter = vport->adapter;
+	struct virtchnl2_queue_vector *vqv;
+	int buf_size, num_q, err = 0;
+	bool alloc = false;
+	int i, j, k = 0;
+
+	num_q = vport->num_txq + vport->num_rxq;
+
+	buf_size = sizeof(struct virtchnl2_queue_vector) * num_q;
+	vqv = kzalloc(buf_size, GFP_KERNEL);
+	if (!vqv)
+		return -ENOMEM;
+
+	for (i = 0; i < vport->num_txq_grp; i++) {
+		struct iecm_txq_group *tx_qgrp = &vport->txq_grps[i];
+
+		for (j = 0; j < tx_qgrp->num_txq; j++, k++) {
+			vqv[k].queue_type = cpu_to_le32(tx_qgrp->txqs[j]->q_type);
+			vqv[k].queue_id = cpu_to_le32(tx_qgrp->txqs[j]->q_id);
+
+			if (iecm_is_queue_model_split(vport->txq_model)) {
+				vqv[k].vector_id =
+				cpu_to_le16(tx_qgrp->complq->q_vector->v_idx);
+				vqv[k].itr_idx =
+				cpu_to_le32(tx_qgrp->complq->q_vector->tx_itr_idx);
+			} else {
+				vqv[k].vector_id =
+				cpu_to_le16(tx_qgrp->txqs[j]->q_vector->v_idx);
+				vqv[k].itr_idx =
+				cpu_to_le32(tx_qgrp->txqs[j]->q_vector->tx_itr_idx);
+			}
+		}
+	}
+
+	if (vport->num_txq != k) {
+		err = -EINVAL;
+		goto error;
+	}
+
+	for (i = 0; i < vport->num_rxq_grp; i++) {
+		struct iecm_rxq_group *rx_qgrp = &vport->rxq_grps[i];
+		int num_rxq;
+
+		if (iecm_is_queue_model_split(vport->rxq_model))
+			num_rxq = rx_qgrp->splitq.num_rxq_sets;
+		else
+			num_rxq = rx_qgrp->singleq.num_rxq;
+
+		for (j = 0; j < num_rxq; j++, k++) {
+			struct iecm_queue *rxq;
+
+			if (iecm_is_queue_model_split(vport->rxq_model))
+				rxq = &rx_qgrp->splitq.rxq_sets[j]->rxq;
+			else
+				rxq = rx_qgrp->singleq.rxqs[j];
+
+			vqv[k].queue_type = cpu_to_le32(rxq->q_type);
+			vqv[k].queue_id = cpu_to_le32(rxq->q_id);
+			vqv[k].vector_id = cpu_to_le16(rxq->q_vector->v_idx);
+			vqv[k].itr_idx = cpu_to_le32(rxq->q_vector->rx_itr_idx);
+		}
+	}
+
+	if (iecm_is_queue_model_split(vport->txq_model)) {
+		if (vport->num_rxq != k - vport->num_complq) {
+			err = -EINVAL;
+			goto error;
+		}
+	} else {
+		if (vport->num_rxq != k - vport->num_txq) {
+			err = -EINVAL;
+			goto error;
+		}
+	}
+
+	/* Chunk up the vector info into multiple messages */
+	config_data_size = sizeof(struct virtchnl2_queue_vector_maps);
+	chunk_size = sizeof(struct virtchnl2_queue_vector);
+
+	num_chunks = IECM_NUM_CHUNKS_PER_MSG(config_data_size, chunk_size) + 1;
+	if (num_q < num_chunks)
+		num_chunks = num_q;
+
+	num_msgs = num_q / num_chunks;
+	if (num_q % num_chunks)
+		num_msgs++;
+
+	for (i = 0, k = 0; i < num_msgs; i++) {
+		if (!vqvm || alloc) {
+			buf_size = (chunk_size * (num_chunks - 1)) +
+					config_data_size;
+			kfree(vqvm);
+			vqvm = kzalloc(buf_size, GFP_KERNEL);
+			if (!vqvm) {
+				err = -ENOMEM;
+				goto error;
+			}
+		} else {
+			memset(vqvm, 0, buf_size);
+		}
+		vqvm->vport_id = cpu_to_le32(vport->vport_id);
+		vqvm->num_qv_maps = cpu_to_le16(num_chunks);
+		memcpy(vqvm->qv_maps, &vqv[k], chunk_size * num_chunks);
+
+		if (map) {
+			err = iecm_send_mb_msg(adapter,
+					       VIRTCHNL2_OP_MAP_QUEUE_VECTOR,
+					       buf_size, (u8 *)vqvm);
+			if (!err)
+				err = iecm_wait_for_event(adapter,
+							  IECM_VC_MAP_IRQ,
+							  IECM_VC_MAP_IRQ_ERR);
+		} else {
+			err = iecm_send_mb_msg(adapter,
+					       VIRTCHNL2_OP_UNMAP_QUEUE_VECTOR,
+					       buf_size, (u8 *)vqvm);
+			if (!err)
+				err =
+				iecm_min_wait_for_event(adapter,
+							IECM_VC_UNMAP_IRQ,
+							IECM_VC_UNMAP_IRQ_ERR);
+		}
+		if (err)
+			goto mbx_error;
+
+		k += num_chunks;
+		num_q -= num_chunks;
+		if (num_q < num_chunks) {
+			num_chunks = num_q;
+			alloc = true;
+		}
+	}
+mbx_error:
+	kfree(vqvm);
+error:
+	kfree(vqv);
+	return err;
+}
+EXPORT_SYMBOL(iecm_send_map_unmap_queue_vector_msg);
+
+/**
+ * iecm_send_enable_queues_msg - send enable queues virtchnl message
+ * @vport: Virtual port private data structure
+ *
+ * Will send enable queues virtchnl message.  Returns 0 on success, negative on
+ * failure.
+ */
+static int iecm_send_enable_queues_msg(struct iecm_vport *vport)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	int err;
+
+	err = iecm_send_ena_dis_queues_msg(vport,
+					   VIRTCHNL2_OP_ENABLE_QUEUES);
+	if (err)
+		return err;
+
+	return iecm_wait_for_event(adapter, IECM_VC_ENA_QUEUES,
+				   IECM_VC_ENA_QUEUES_ERR);
+}
+
+/**
+ * iecm_send_disable_queues_msg - send disable queues virtchnl message
+ * @vport: Virtual port private data structure
+ *
+ * Will send disable queues virtchnl message.  Returns 0 on success, negative
+ * on failure.
+ */
+static int iecm_send_disable_queues_msg(struct iecm_vport *vport)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	int err;
+
+	err = iecm_send_ena_dis_queues_msg(vport,
+					   VIRTCHNL2_OP_DISABLE_QUEUES);
+	if (err)
+		return err;
+
+	err = iecm_min_wait_for_event(adapter, IECM_VC_DIS_QUEUES,
+				      IECM_VC_DIS_QUEUES_ERR);
+	if (err)
+		return err;
+
+	return iecm_wait_for_marker_event(vport);
+}
+
+/**
+ * iecm_convert_reg_to_queue_chunks - Copy queue chunk information to the right
+ * structure
+ * @dchunks: Destination chunks to store data to
+ * @schunks: Source chunks to copy data from
+ * @num_chunks: number of chunks to copy
+ */
+static void
+iecm_convert_reg_to_queue_chunks(struct virtchnl2_queue_chunk *dchunks,
+				 struct virtchnl2_queue_reg_chunk *schunks,
+				 u16 num_chunks)
+{
+	u16 i;
+
+	for (i = 0; i < num_chunks; i++) {
+		dchunks[i].type = schunks[i].type;
+		dchunks[i].start_queue_id = schunks[i].start_queue_id;
+		dchunks[i].num_queues = schunks[i].num_queues;
+	}
+}
+
+/**
+ * iecm_send_delete_queues_msg - send delete queues virtchnl message
+ * @vport: Virtual port private data structure
+ *
+ * Will send delete queues virtchnl message. Return 0 on success, negative on
+ * failure.
+ */
+int iecm_send_delete_queues_msg(struct iecm_vport *vport)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	struct virtchnl2_create_vport *vport_params;
+	struct virtchnl2_queue_reg_chunks *chunks;
+	struct virtchnl2_del_ena_dis_queues *eq;
+	int buf_size, err;
+	u16 num_chunks;
+
+	if (vport->adapter->config_data.req_qs_chunks) {
+		struct virtchnl2_add_queues *vc_aq =
+			(struct virtchnl2_add_queues *)
+			vport->adapter->config_data.req_qs_chunks;
+		chunks = &vc_aq->chunks;
+	} else {
+		vport_params = (struct virtchnl2_create_vport *)
+				vport->adapter->vport_params_recvd[0];
+		 chunks = &vport_params->chunks;
+	}
+
+	num_chunks = le16_to_cpu(chunks->num_chunks);
+	buf_size = sizeof(struct virtchnl2_del_ena_dis_queues) +
+			  (sizeof(struct virtchnl2_queue_chunk) *
+			  (num_chunks - 1));
+
+	eq = kzalloc(buf_size, GFP_KERNEL);
+	if (!eq)
+		return -ENOMEM;
+
+	eq->vport_id = cpu_to_le32(vport->vport_id);
+	eq->chunks.num_chunks = cpu_to_le16(num_chunks);
+
+	iecm_convert_reg_to_queue_chunks(eq->chunks.chunks, chunks->chunks,
+					 num_chunks);
+
+	err = iecm_send_mb_msg(vport->adapter, VIRTCHNL2_OP_DEL_QUEUES,
+			       buf_size, (u8 *)eq);
+	if (err)
+		goto error;
+
+	err = iecm_min_wait_for_event(adapter, IECM_VC_DEL_QUEUES,
+				      IECM_VC_DEL_QUEUES_ERR);
+error:
+	kfree(eq);
+	return err;
+}
+
+/**
+ * iecm_send_config_queues_msg - Send config queues virtchnl message
+ * @vport: Virtual port private data structure
+ *
+ * Will send config queues virtchnl message. Returns 0 on success, negative on
+ * failure.
+ */
+static int iecm_send_config_queues_msg(struct iecm_vport *vport)
+{
+	int err;
+
+	err = iecm_send_config_tx_queues_msg(vport);
+	if (err)
+		return err;
+
+	return iecm_send_config_rx_queues_msg(vport);
+}
+
+/**
+ * iecm_send_add_queues_msg - Send virtchnl add queues message
+ * @vport: Virtual port private data structure
+ * @num_tx_q: number of transmit queues
+ * @num_complq: number of transmit completion queues
+ * @num_rx_q: number of receive queues
+ * @num_rx_bufq: number of receive buffer queues
+ *
+ * Returns 0 on success, negative on failure.
+ */
+int iecm_send_add_queues_msg(struct iecm_vport *vport, u16 num_tx_q,
+			     u16 num_complq, u16 num_rx_q, u16 num_rx_bufq)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	struct virtchnl2_add_queues aq = {0};
+	struct virtchnl2_add_queues *vc_msg;
+	int size, err;
+
+	vc_msg = (struct virtchnl2_add_queues *)adapter->vc_msg;
+
+	aq.vport_id = cpu_to_le32(vport->vport_id);
+	aq.num_tx_q = cpu_to_le16(num_tx_q);
+	aq.num_tx_complq = cpu_to_le16(num_complq);
+	aq.num_rx_q = cpu_to_le16(num_rx_q);
+	aq.num_rx_bufq = cpu_to_le16(num_rx_bufq);
+
+	err = iecm_send_mb_msg(adapter,
+			       VIRTCHNL2_OP_ADD_QUEUES,
+			       sizeof(struct virtchnl2_add_queues), (u8 *)&aq);
+	if (err)
+		return err;
+
+	err = iecm_wait_for_event(adapter, IECM_VC_ADD_QUEUES,
+				  IECM_VC_ADD_QUEUES_ERR);
+	if (err)
+		return err;
+
+	kfree(adapter->config_data.req_qs_chunks);
+	adapter->config_data.req_qs_chunks = NULL;
+
+	/* compare vc_msg num queues with vport num queues */
+	if (le16_to_cpu(vc_msg->num_tx_q) != num_tx_q ||
+	    le16_to_cpu(vc_msg->num_rx_q) != num_rx_q ||
+	    le16_to_cpu(vc_msg->num_tx_complq) != num_complq ||
+	    le16_to_cpu(vc_msg->num_rx_bufq) != num_rx_bufq) {
+		err = -EINVAL;
+		goto error;
+	}
+
+	size = sizeof(struct virtchnl2_add_queues) +
+			((le16_to_cpu(vc_msg->chunks.num_chunks) - 1) *
+			sizeof(struct virtchnl2_queue_reg_chunk));
+	adapter->config_data.req_qs_chunks =
+		kzalloc(size, GFP_KERNEL);
+	if (!adapter->config_data.req_qs_chunks) {
+		err = -ENOMEM;
+		goto error;
+	}
+	memcpy(adapter->config_data.req_qs_chunks,
+	       adapter->vc_msg, size);
+error:
+	clear_bit(__IECM_VC_MSG_PENDING, adapter->flags);
+	return err;
+}
+
 /**
  * iecm_find_ctlq - Given a type and id, find ctlq info
  * @hw: hardware struct
@@ -1217,6 +2219,13 @@ static void iecm_vport_init(struct iecm_vport *vport,
 	/*Initialize Tx and Rx profiles for Dynamic Interrupt Moderation */
 	memcpy(vport->rx_itr_profile, rx_itr, IECM_DIM_PROFILE_SLOTS);
 	memcpy(vport->tx_itr_profile, tx_itr, IECM_DIM_PROFILE_SLOTS);
+
+	iecm_vport_set_hsplit(vport, true);
+
+	iecm_vport_init_num_qs(vport, vport_msg);
+	iecm_vport_calc_num_q_desc(vport);
+	iecm_vport_calc_num_q_groups(vport);
+	iecm_vport_calc_num_q_vec(vport);
 }
 
 /**
@@ -1316,8 +2325,82 @@ static int
 __iecm_vport_queue_ids_init(struct iecm_vport *vport, u32 *qids,
 			    int num_qids, u32 q_type)
 {
-	/* stub */
-	return 0;
+	struct iecm_queue *q;
+	int i, j, k = 0;
+
+	switch (q_type) {
+	case VIRTCHNL2_QUEUE_TYPE_TX:
+		for (i = 0; i < vport->num_txq_grp; i++) {
+			struct iecm_txq_group *tx_qgrp = &vport->txq_grps[i];
+
+			for (j = 0; j < tx_qgrp->num_txq; j++) {
+				if (k < num_qids) {
+					tx_qgrp->txqs[j]->q_id = qids[k];
+					tx_qgrp->txqs[j]->q_type =
+						VIRTCHNL2_QUEUE_TYPE_TX;
+					k++;
+				} else {
+					break;
+				}
+			}
+		}
+		break;
+	case VIRTCHNL2_QUEUE_TYPE_RX:
+		for (i = 0; i < vport->num_rxq_grp; i++) {
+			struct iecm_rxq_group *rx_qgrp = &vport->rxq_grps[i];
+			int num_rxq;
+
+			if (iecm_is_queue_model_split(vport->rxq_model))
+				num_rxq = rx_qgrp->splitq.num_rxq_sets;
+			else
+				num_rxq = rx_qgrp->singleq.num_rxq;
+
+			for (j = 0; j < num_rxq && k < num_qids; j++, k++) {
+				if (iecm_is_queue_model_split(vport->rxq_model))
+					q = &rx_qgrp->splitq.rxq_sets[j]->rxq;
+				else
+					q = rx_qgrp->singleq.rxqs[j];
+				q->q_id = qids[k];
+				q->q_type = VIRTCHNL2_QUEUE_TYPE_RX;
+			}
+		}
+		break;
+	case VIRTCHNL2_QUEUE_TYPE_TX_COMPLETION:
+		for (i = 0; i < vport->num_txq_grp; i++) {
+			struct iecm_txq_group *tx_qgrp = &vport->txq_grps[i];
+
+			if (k < num_qids) {
+				tx_qgrp->complq->q_id = qids[k];
+				tx_qgrp->complq->q_type =
+					VIRTCHNL2_QUEUE_TYPE_TX_COMPLETION;
+				k++;
+			} else {
+				break;
+			}
+		}
+		break;
+	case VIRTCHNL2_QUEUE_TYPE_RX_BUFFER:
+		for (i = 0; i < vport->num_rxq_grp; i++) {
+			struct iecm_rxq_group *rx_qgrp = &vport->rxq_grps[i];
+
+			for (j = 0; j < vport->num_bufqs_per_qgrp; j++) {
+				if (k < num_qids) {
+					q = &rx_qgrp->splitq.bufq_sets[j].bufq;
+					q->q_id = qids[k];
+					q->q_type =
+						VIRTCHNL2_QUEUE_TYPE_RX_BUFFER;
+					k++;
+				} else {
+					break;
+				}
+			}
+		}
+		break;
+	default:
+		break;
+	}
+
+	return k;
 }
 
 /**
@@ -1425,6 +2508,20 @@ static bool iecm_is_capability_ena(struct iecm_adapter *adapter, bool all,
 		return !!(*cap_field & flag);
 }
 
+/**
+ * iecm_get_reserved_vectors - Default implementation to get reserved vectors
+ * @adapter: Private data struct
+ *
+ * Return number of vectors reserved
+ */
+static u16 iecm_get_reserved_vectors(struct iecm_adapter *adapter)
+{
+	struct virtchnl2_get_capabilities *caps;
+
+	caps = (struct virtchnl2_get_capabilities *)adapter->caps;
+	return le16_to_cpu(caps->num_allocated_vectors);
+}
+
 /**
  * iecm_vc_ops_init - Initialize virtchnl common api
  * @adapter: Driver specific private structure
@@ -1441,16 +2538,16 @@ void iecm_vc_ops_init(struct iecm_adapter *adapter)
 	vc_ops->vport_queue_ids_init = iecm_vport_queue_ids_init;
 	vc_ops->get_caps = iecm_send_get_caps_msg;
 	vc_ops->is_cap_ena = iecm_is_capability_ena;
-	vc_ops->get_reserved_vecs = NULL;
-	vc_ops->config_queues = NULL;
-	vc_ops->enable_queues = NULL;
-	vc_ops->disable_queues = NULL;
-	vc_ops->add_queues = NULL;
-	vc_ops->delete_queues = NULL;
-	vc_ops->irq_map_unmap = NULL;
-	vc_ops->enable_vport = NULL;
-	vc_ops->disable_vport = NULL;
-	vc_ops->destroy_vport = NULL;
+	vc_ops->get_reserved_vecs = iecm_get_reserved_vectors;
+	vc_ops->config_queues = iecm_send_config_queues_msg;
+	vc_ops->enable_queues = iecm_send_enable_queues_msg;
+	vc_ops->disable_queues = iecm_send_disable_queues_msg;
+	vc_ops->add_queues = iecm_send_add_queues_msg;
+	vc_ops->delete_queues = iecm_send_delete_queues_msg;
+	vc_ops->irq_map_unmap = iecm_send_map_unmap_queue_vector_msg;
+	vc_ops->enable_vport = iecm_send_enable_vport_msg;
+	vc_ops->disable_vport = iecm_send_disable_vport_msg;
+	vc_ops->destroy_vport = iecm_send_destroy_vport_msg;
 	vc_ops->get_ptype = NULL;
 	vc_ops->get_set_rss_key = NULL;
 	vc_ops->get_set_rss_lut = NULL;
diff --git a/drivers/net/ethernet/intel/include/iecm.h b/drivers/net/ethernet/intel/include/iecm.h
index 994664dfe419..8dd6272db7d3 100644
--- a/drivers/net/ethernet/intel/include/iecm.h
+++ b/drivers/net/ethernet/intel/include/iecm.h
@@ -432,6 +432,8 @@ struct iecm_adapter {
 	u16 num_alloc_vport;
 	u16 next_vport;		/* Next free slot in pf->vport[] - 0-based! */
 
+	u16 max_queue_limit;	/* Max number of queues user can request */
+
 	struct delayed_work init_task; /* delayed init task */
 	struct workqueue_struct *init_wq;
 	u32 mb_wait_count;
@@ -510,6 +512,12 @@ static inline bool __iecm_is_cap_ena(struct iecm_adapter *adapter, bool all,
 	return adapter->dev_ops.vc_ops.is_cap_ena(adapter, all, field, flag);
 }
 
+#define IECM_CAP_HSPLIT (\
+	VIRTCHNL2_CAP_RX_HSPLIT_AT_L2   |\
+	VIRTCHNL2_CAP_RX_HSPLIT_AT_L3   |\
+	VIRTCHNL2_CAP_RX_HSPLIT_AT_L4V4 |\
+	VIRTCHNL2_CAP_RX_HSPLIT_AT_L4V6)
+
 /**
  * iecm_is_reset_detected - check if we were reset at some point
  * @adapter: driver specific private structure
@@ -530,6 +538,8 @@ int iecm_init_dflt_mbx(struct iecm_adapter *adapter);
 void iecm_deinit_dflt_mbx(struct iecm_adapter *adapter);
 void iecm_vc_ops_init(struct iecm_adapter *adapter);
 int iecm_vc_core_init(struct iecm_adapter *adapter, int *vport_id);
+int iecm_get_reg_intr_vecs(struct iecm_vport *vport,
+			   struct iecm_vec_regs *reg_vals, int num_vecs);
 int iecm_wait_for_event(struct iecm_adapter *adapter,
 			enum iecm_vport_vc_state state,
 			enum iecm_vport_vc_state err_check);
@@ -537,6 +547,14 @@ int iecm_min_wait_for_event(struct iecm_adapter *adapter,
 			    enum iecm_vport_vc_state state,
 			    enum iecm_vport_vc_state err_check);
 int iecm_send_get_caps_msg(struct iecm_adapter *adapter);
+int iecm_send_delete_queues_msg(struct iecm_vport *vport);
+int iecm_send_add_queues_msg(struct iecm_vport *vport, u16 num_tx_q,
+			     u16 num_complq, u16 num_rx_q, u16 num_rx_bufq);
+int iecm_send_config_tx_queues_msg(struct iecm_vport *vport);
+int iecm_send_config_rx_queues_msg(struct iecm_vport *vport);
+int iecm_send_enable_vport_msg(struct iecm_vport *vport);
+int iecm_send_disable_vport_msg(struct iecm_vport *vport);
+int iecm_send_destroy_vport_msg(struct iecm_vport *vport);
 int iecm_vport_params_buf_alloc(struct iecm_adapter *adapter);
 void iecm_vport_params_buf_rel(struct iecm_adapter *adapter);
 int iecm_get_vec_ids(struct iecm_adapter *adapter,
@@ -546,7 +564,11 @@ int iecm_recv_mb_msg(struct iecm_adapter *adapter, enum virtchnl_ops op,
 		     void *msg, int msg_size);
 int iecm_send_mb_msg(struct iecm_adapter *adapter, enum virtchnl_ops op,
 		     u16 msg_size, u8 *msg);
+void iecm_vport_set_hsplit(struct iecm_vport *vport, bool ena);
+int iecm_send_enable_channels_msg(struct iecm_vport *vport);
+int iecm_send_disable_channels_msg(struct iecm_vport *vport);
 int iecm_set_msg_pending(struct iecm_adapter *adapter,
 			 struct iecm_ctlq_msg *ctlq_msg,
 			 enum iecm_vport_vc_state err_enum);
+int iecm_send_map_unmap_queue_vector_msg(struct iecm_vport *vport, bool map);
 #endif /* !_IECM_H_ */
diff --git a/drivers/net/ethernet/intel/include/iecm_txrx.h b/drivers/net/ethernet/intel/include/iecm_txrx.h
index e1348011c991..448cae0bf6e7 100644
--- a/drivers/net/ethernet/intel/include/iecm_txrx.h
+++ b/drivers/net/ethernet/intel/include/iecm_txrx.h
@@ -81,6 +81,22 @@
 
 #define IECM_TX_COMPLQ_CLEAN_BUDGET	256
 
+enum iecm_queue_flags_t {
+	__IECM_Q_GEN_CHK,
+	__IECM_RFLQ_GEN_CHK,
+	__IECM_Q_FLOW_SCH_EN,
+	__IECM_Q_ETF_EN,
+	__IECM_Q_SW_MARKER,
+	__IECM_Q_VLAN_TAG_LOC_L2TAG1,
+	__IECM_Q_VLAN_TAG_LOC_L2TAG2,
+	__IECM_Q_FLAGS_NBITS,
+};
+
+struct iecm_vec_regs {
+	u32 dyn_ctl_reg;
+	u32 itrn_reg;
+};
+
 struct iecm_intr_reg {
 	u32 dyn_ctl;
 	u32 dyn_ctl_intena_m;
@@ -122,6 +138,186 @@ struct iecm_q_vector {
 	char name[IECM_INT_NAME_STR_LEN];
 };
 
+struct iecm_rx_queue_stats {
+	u64 packets;
+	u64 bytes;
+	u64 rsc_pkts;
+};
+
+struct iecm_tx_queue_stats {
+	u64 packets;
+	u64 bytes;
+	u64 lso_pkts;
+};
+
+union iecm_queue_stats {
+	struct iecm_rx_queue_stats rx;
+	struct iecm_tx_queue_stats tx;
+};
+
+/* queue associated with a vport */
+struct iecm_queue {
+	struct device *dev;		/* Used for DMA mapping */
+	struct iecm_vport *vport;	/* Backreference to associated vport */
+	union {
+		struct iecm_txq_group *txq_grp;
+		struct iecm_rxq_group *rxq_grp;
+	};
+	/* bufq: Used as group id, either 0 or 1, on clean Buf Q uses this
+	 *       index to determine which group of refill queues to clean.
+	 *       Bufqs are use in splitq only.
+	 * txq: Index to map between Tx Q group and hot path Tx ptrs stored in
+	 *      vport.  Used in both single Q/split Q
+	 * rxq: Index to total rxq across groups, used for skb reporting
+	 */
+	u16 idx;
+	/* Used for both Q models single and split. In split Q model relevant
+	 * only to Tx Q and Rx Q
+	 */
+	u8 __iomem *tail;
+	/* Used in both single and split Q.  In single Q, Tx Q uses tx_buf and
+	 * Rx Q uses rx_buf.  In split Q, Tx Q uses tx_buf, Rx Q uses skb, and
+	 * Buf Q uses rx_buf.
+	 */
+	union {
+		struct iecm_tx_buf *tx_buf;
+		struct {
+			struct iecm_rx_buf *buf;
+			struct iecm_dma_mem **hdr_buf;
+		} rx_buf;
+		struct sk_buff *skb;
+	};
+	u16 q_type;
+	/* Queue id(Tx/Tx compl/Rx/Bufq) */
+	u32 q_id;
+	u16 desc_count;		/* Number of descriptors */
+
+	/* Relevant in both split & single Tx Q & Buf Q*/
+	u16 next_to_use;
+	/* In split q model only relevant for Tx Compl Q and Rx Q */
+	u16 next_to_clean;	/* used in interrupt processing */
+	/* Used only for Rx. In split Q model only relevant to Rx Q */
+	u16 next_to_alloc;
+	/* Generation bit check stored, as HW flips the bit at Queue end */
+	DECLARE_BITMAP(flags, __IECM_Q_FLAGS_NBITS);
+
+	union iecm_queue_stats q_stats;
+	struct u64_stats_sync stats_sync;
+
+	bool rx_hsplit_en;
+
+	u16 rx_hbuf_size;	/* Header buffer size */
+	u16 rx_buf_size;
+	u16 rx_max_pkt_size;
+	u16 rx_buf_stride;
+	u8 rx_buffer_low_watermark;
+	u64 rxdids;
+	/* Used for both Q models single and split. In split Q model relavant
+	 * only to Tx compl Q and Rx compl Q
+	 */
+	struct iecm_q_vector *q_vector;	/* Backreference to associated vector */
+	unsigned int size;		/* length of descriptor ring in bytes */
+	dma_addr_t dma;			/* physical address of ring */
+	void *desc_ring;		/* Descriptor ring memory */
+
+	u16 tx_buf_key;			/* 16 bit unique "identifier" (index)
+					 * to be used as the completion tag when
+					 * queue is using flow based scheduling
+					 */
+	u16 tx_max_bufs;		/* Max buffers that can be transmitted
+					 * with scatter-gather
+					 */
+	DECLARE_HASHTABLE(sched_buf_hash, 12);
+} ____cacheline_internodealigned_in_smp;
+
+/* Software queues are used in splitq mode to manage buffers between rxq
+ * producer and the bufq consumer.  These are required in order to maintain a
+ * lockless buffer management system and are strictly software only constructs.
+ */
+struct iecm_sw_queue {
+	u16 next_to_clean ____cacheline_aligned_in_smp;
+	u16 next_to_alloc ____cacheline_aligned_in_smp;
+	u16 next_to_use ____cacheline_aligned_in_smp;
+	DECLARE_BITMAP(flags, __IECM_Q_FLAGS_NBITS)
+		____cacheline_aligned_in_smp;
+	u16 *ring ____cacheline_aligned_in_smp;
+	u16 desc_count;
+	u16 buf_size;
+	struct device *dev;
+} ____cacheline_internodealigned_in_smp;
+
+/* Splitq only.  iecm_rxq_set associates an rxq with at an array of refillqs.
+ * Each rxq needs a refillq to return used buffers back to the respective bufq.
+ * Bufqs then clean these refillqs for buffers to give to hardware.
+ */
+struct iecm_rxq_set {
+	struct iecm_queue rxq;
+	/* refillqs assoc with bufqX mapped to this rxq */
+	struct iecm_sw_queue *refillq0;
+	struct iecm_sw_queue *refillq1;
+};
+
+/* Splitq only.  iecm_bufq_set associates a bufq to an array of refillqs.
+ * In this bufq_set, there will be one refillq for each rxq in this rxq_group.
+ * Used buffers received by rxqs will be put on refillqs which bufqs will
+ * clean to return new buffers back to hardware.
+ *
+ * Buffers needed by some number of rxqs associated in this rxq_group are
+ * managed by at most two bufqs (depending on performance configuration).
+ */
+struct iecm_bufq_set {
+	struct iecm_queue bufq;
+	/* This is always equal to num_rxq_sets in iecm_rxq_group */
+	int num_refillqs;
+	struct iecm_sw_queue *refillqs;
+};
+
+/* In singleq mode, an rxq_group is simply an array of rxqs.  In splitq, a
+ * rxq_group contains all the rxqs, bufqs and refillqs needed to
+ * manage buffers in splitq mode.
+ */
+struct iecm_rxq_group {
+	struct iecm_vport *vport; /* back pointer */
+
+	union {
+		struct {
+			int num_rxq;
+			/* store queue pointers */
+			struct iecm_queue *rxqs[IECM_LARGE_MAX_Q];
+		} singleq;
+		struct {
+			int num_rxq_sets;
+			/* store queue pointers */
+			struct iecm_rxq_set *rxq_sets[IECM_LARGE_MAX_Q];
+			struct iecm_bufq_set *bufq_sets;
+		} splitq;
+	};
+};
+
+/* Between singleq and splitq, a txq_group is largely the same except for the
+ * complq.  In splitq a single complq is responsible for handling completions
+ * for some number of txqs associated in this txq_group.
+ */
+struct iecm_txq_group {
+	struct iecm_vport *vport; /* back pointer */
+
+	int num_txq;
+	/* store queue pointers */
+	struct iecm_queue *txqs[IECM_LARGE_MAX_Q];
+
+	/* splitq only */
+	struct iecm_queue *complq;
+};
+
+struct iecm_adapter;
+
+void iecm_vport_init_num_qs(struct iecm_vport *vport,
+			    struct virtchnl2_create_vport *vport_msg);
+void iecm_vport_calc_num_q_desc(struct iecm_vport *vport);
+void iecm_vport_calc_total_qs(struct iecm_adapter *adapter,
+			      struct virtchnl2_create_vport *vport_msg);
+void iecm_vport_calc_num_q_groups(struct iecm_vport *vport);
+void iecm_vport_calc_num_q_vec(struct iecm_vport *vport);
 irqreturn_t
 iecm_vport_intr_clean_queues(int __always_unused irq, void *data);
 #endif /* !_IECM_TXRX_H_ */
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 07/19] iecm: finish virtchnl messages
  2022-01-28  0:09 [Intel-wired-lan] [PATCH net-next 00/19] Add iecm and idpf Alan Brady
                   ` (5 preceding siblings ...)
  2022-01-28  0:09 ` [Intel-wired-lan] [PATCH net-next 06/19] iecm: add virtchnl messages for queues Alan Brady
@ 2022-01-28  0:09 ` Alan Brady
  2022-01-28 13:19   ` Alexander Lobakin
  2022-01-28  0:09 ` [Intel-wired-lan] [PATCH net-next 08/19] iecm: add interrupts and configure netdev Alan Brady
                   ` (12 subsequent siblings)
  19 siblings, 1 reply; 89+ messages in thread
From: Alan Brady @ 2022-01-28  0:09 UTC (permalink / raw)
  To: intel-wired-lan

This adds the rest of the needed virtchnl messages mostly related to
negotiating ptypes and initializing queue registers.

Signed-off-by: Phani Burra <phani.r.burra@intel.com>
Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
Signed-off-by: Alice Michael <alice.michael@intel.com>
Signed-off-by: Alan Brady <alan.brady@intel.com>
---
 drivers/net/ethernet/intel/iecm/iecm_lib.c    |   21 +-
 drivers/net/ethernet/intel/iecm/iecm_txrx.c   |  226 +++-
 .../net/ethernet/intel/iecm/iecm_virtchnl.c   | 1187 ++++++++++++++++-
 drivers/net/ethernet/intel/include/iecm.h     |   36 +
 .../net/ethernet/intel/include/iecm_txrx.h    |  198 ++-
 5 files changed, 1635 insertions(+), 33 deletions(-)

diff --git a/drivers/net/ethernet/intel/iecm/iecm_lib.c b/drivers/net/ethernet/intel/iecm/iecm_lib.c
index 4e9cc7f2d138..aab8ee40424e 100644
--- a/drivers/net/ethernet/intel/iecm/iecm_lib.c
+++ b/drivers/net/ethernet/intel/iecm/iecm_lib.c
@@ -10,6 +10,25 @@ const char * const iecm_vport_vc_state_str[] = {
 };
 EXPORT_SYMBOL(iecm_vport_vc_state_str);
 
+/**
+ * iecm_is_feature_ena - Determine if a particular feature is enabled
+ * @vport: vport to check
+ * @feature: netdev flag to check
+ *
+ * Returns true or false if a particular feature is enabled.
+ */
+bool iecm_is_feature_ena(struct iecm_vport *vport, netdev_features_t feature)
+{
+	bool ena;
+
+	switch (feature) {
+	default:
+		ena = vport->netdev->features & feature;
+		break;
+	}
+	return ena;
+}
+
 /**
  * iecm_cfg_hw - Initialize HW struct
  * @adapter: adapter to setup hw struct for
@@ -132,7 +151,7 @@ iecm_vport_alloc(struct iecm_adapter *adapter, int vport_id)
 	adapter->num_alloc_vport++;
 
 	/* Setup default MSIX irq handler for the vport */
-	vport->irq_q_handler = iecm_vport_intr_clean_queues;
+	vport->irq_q_handler = NULL;
 	vport->q_vector_base = IECM_NONQ_VEC;
 
 	mutex_init(&vport->stop_mutex);
diff --git a/drivers/net/ethernet/intel/iecm/iecm_txrx.c b/drivers/net/ethernet/intel/iecm/iecm_txrx.c
index 2dfb0be002e3..bd0cfd89bf03 100644
--- a/drivers/net/ethernet/intel/iecm/iecm_txrx.c
+++ b/drivers/net/ethernet/intel/iecm/iecm_txrx.c
@@ -3,22 +3,220 @@
 
 #include "iecm.h"
 
-/**
- * iecm_vport_intr_clean_queues - MSIX mode Interrupt Handler
- * @irq: interrupt number
- * @data: pointer to a q_vector
- *
- */
-irqreturn_t
-iecm_vport_intr_clean_queues(int __always_unused irq, void *data)
-{
-	struct iecm_q_vector *q_vector = (struct iecm_q_vector *)data;
+const struct iecm_rx_ptype_decoded iecm_ptype_lookup[IECM_RX_MAX_PTYPE] = {
+	/* ptype indices are dynamic and package dependent. Indices represented
+	 * in this lookup table are for reference and will be replaced by the
+	 * values which CP sends. Also these values are static for older
+	 * versions of virtchnl and if VIRTCHNL2_CAP_PTYPE is not set in
+	 * virtchnl2_get_capabilities.
+	 */
+	/* L2 Packet types */
+	IECM_PTT_UNUSED_ENTRY(0),
+	IECM_PTT(1,  L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY2),
+	IECM_PTT(2,  L2, NONE, NOF, NONE, NONE, NOF, TS,   PAY2),
+	IECM_PTT(3,  L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY2),
+	IECM_PTT_UNUSED_ENTRY(4),
+	IECM_PTT_UNUSED_ENTRY(5),
+	IECM_PTT(6,  L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY2),
+	IECM_PTT(7,  L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY2),
+	IECM_PTT_UNUSED_ENTRY(8),
+	IECM_PTT_UNUSED_ENTRY(9),
+	IECM_PTT(10, L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY2),
+	IECM_PTT(11, L2, NONE, NOF, NONE, NONE, NOF, NONE, NONE),
+	IECM_PTT(12, L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY3),
+	IECM_PTT(13, L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY3),
+	IECM_PTT(14, L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY3),
+	IECM_PTT(15, L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY3),
+	IECM_PTT(16, L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY3),
+	IECM_PTT(17, L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY3),
+	IECM_PTT(18, L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY3),
+	IECM_PTT(19, L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY3),
+	IECM_PTT(20, L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY3),
+	IECM_PTT(21, L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY3),
 
-	q_vector->total_events++;
-	napi_schedule(&q_vector->napi);
+	/* Non Tunneled IPv4 */
+	IECM_PTT(22, IP, IPV4, FRG, NONE, NONE, NOF, NONE, PAY3),
+	IECM_PTT(23, IP, IPV4, NOF, NONE, NONE, NOF, NONE, PAY3),
+	IECM_PTT(24, IP, IPV4, NOF, NONE, NONE, NOF, UDP,  PAY4),
+	IECM_PTT_UNUSED_ENTRY(25),
+	IECM_PTT(26, IP, IPV4, NOF, NONE, NONE, NOF, TCP,  PAY4),
+	IECM_PTT(27, IP, IPV4, NOF, NONE, NONE, NOF, SCTP, PAY4),
+	IECM_PTT(28, IP, IPV4, NOF, NONE, NONE, NOF, ICMP, PAY4),
 
-	return IRQ_HANDLED;
-}
+	/* IPv4 --> IPv4 */
+	IECM_PTT(29, IP, IPV4, NOF, IP_IP, IPV4, FRG, NONE, PAY3),
+	IECM_PTT(30, IP, IPV4, NOF, IP_IP, IPV4, NOF, NONE, PAY3),
+	IECM_PTT(31, IP, IPV4, NOF, IP_IP, IPV4, NOF, UDP,  PAY4),
+	IECM_PTT_UNUSED_ENTRY(32),
+	IECM_PTT(33, IP, IPV4, NOF, IP_IP, IPV4, NOF, TCP,  PAY4),
+	IECM_PTT(34, IP, IPV4, NOF, IP_IP, IPV4, NOF, SCTP, PAY4),
+	IECM_PTT(35, IP, IPV4, NOF, IP_IP, IPV4, NOF, ICMP, PAY4),
+
+	/* IPv4 --> IPv6 */
+	IECM_PTT(36, IP, IPV4, NOF, IP_IP, IPV6, FRG, NONE, PAY3),
+	IECM_PTT(37, IP, IPV4, NOF, IP_IP, IPV6, NOF, NONE, PAY3),
+	IECM_PTT(38, IP, IPV4, NOF, IP_IP, IPV6, NOF, UDP,  PAY4),
+	IECM_PTT_UNUSED_ENTRY(39),
+	IECM_PTT(40, IP, IPV4, NOF, IP_IP, IPV6, NOF, TCP,  PAY4),
+	IECM_PTT(41, IP, IPV4, NOF, IP_IP, IPV6, NOF, SCTP, PAY4),
+	IECM_PTT(42, IP, IPV4, NOF, IP_IP, IPV6, NOF, ICMP, PAY4),
+
+	/* IPv4 --> GRE/NAT */
+	IECM_PTT(43, IP, IPV4, NOF, IP_GRENAT, NONE, NOF, NONE, PAY3),
+
+	/* IPv4 --> GRE/NAT --> IPv4 */
+	IECM_PTT(44, IP, IPV4, NOF, IP_GRENAT, IPV4, FRG, NONE, PAY3),
+	IECM_PTT(45, IP, IPV4, NOF, IP_GRENAT, IPV4, NOF, NONE, PAY3),
+	IECM_PTT(46, IP, IPV4, NOF, IP_GRENAT, IPV4, NOF, UDP,  PAY4),
+	IECM_PTT_UNUSED_ENTRY(47),
+	IECM_PTT(48, IP, IPV4, NOF, IP_GRENAT, IPV4, NOF, TCP,  PAY4),
+	IECM_PTT(49, IP, IPV4, NOF, IP_GRENAT, IPV4, NOF, SCTP, PAY4),
+	IECM_PTT(50, IP, IPV4, NOF, IP_GRENAT, IPV4, NOF, ICMP, PAY4),
+
+	/* IPv4 --> GRE/NAT --> IPv6 */
+	IECM_PTT(51, IP, IPV4, NOF, IP_GRENAT, IPV6, FRG, NONE, PAY3),
+	IECM_PTT(52, IP, IPV4, NOF, IP_GRENAT, IPV6, NOF, NONE, PAY3),
+	IECM_PTT(53, IP, IPV4, NOF, IP_GRENAT, IPV6, NOF, UDP,  PAY4),
+	IECM_PTT_UNUSED_ENTRY(54),
+	IECM_PTT(55, IP, IPV4, NOF, IP_GRENAT, IPV6, NOF, TCP,  PAY4),
+	IECM_PTT(56, IP, IPV4, NOF, IP_GRENAT, IPV6, NOF, SCTP, PAY4),
+	IECM_PTT(57, IP, IPV4, NOF, IP_GRENAT, IPV6, NOF, ICMP, PAY4),
+
+	/* IPv4 --> GRE/NAT --> MAC */
+	IECM_PTT(58, IP, IPV4, NOF, IP_GRENAT_MAC, NONE, NOF, NONE, PAY3),
+
+	/* IPv4 --> GRE/NAT --> MAC --> IPv4 */
+	IECM_PTT(59, IP, IPV4, NOF, IP_GRENAT_MAC, IPV4, FRG, NONE, PAY3),
+	IECM_PTT(60, IP, IPV4, NOF, IP_GRENAT_MAC, IPV4, NOF, NONE, PAY3),
+	IECM_PTT(61, IP, IPV4, NOF, IP_GRENAT_MAC, IPV4, NOF, UDP,  PAY4),
+	IECM_PTT_UNUSED_ENTRY(62),
+	IECM_PTT(63, IP, IPV4, NOF, IP_GRENAT_MAC, IPV4, NOF, TCP,  PAY4),
+	IECM_PTT(64, IP, IPV4, NOF, IP_GRENAT_MAC, IPV4, NOF, SCTP, PAY4),
+	IECM_PTT(65, IP, IPV4, NOF, IP_GRENAT_MAC, IPV4, NOF, ICMP, PAY4),
+
+	/* IPv4 --> GRE/NAT -> MAC --> IPv6 */
+	IECM_PTT(66, IP, IPV4, NOF, IP_GRENAT_MAC, IPV6, FRG, NONE, PAY3),
+	IECM_PTT(67, IP, IPV4, NOF, IP_GRENAT_MAC, IPV6, NOF, NONE, PAY3),
+	IECM_PTT(68, IP, IPV4, NOF, IP_GRENAT_MAC, IPV6, NOF, UDP,  PAY4),
+	IECM_PTT_UNUSED_ENTRY(69),
+	IECM_PTT(70, IP, IPV4, NOF, IP_GRENAT_MAC, IPV6, NOF, TCP,  PAY4),
+	IECM_PTT(71, IP, IPV4, NOF, IP_GRENAT_MAC, IPV6, NOF, SCTP, PAY4),
+	IECM_PTT(72, IP, IPV4, NOF, IP_GRENAT_MAC, IPV6, NOF, ICMP, PAY4),
+
+	/* IPv4 --> GRE/NAT --> MAC/VLAN */
+	IECM_PTT(73, IP, IPV4, NOF, IP_GRENAT_MAC_VLAN, NONE, NOF, NONE, PAY3),
+
+	/* IPv4 ---> GRE/NAT -> MAC/VLAN --> IPv4 */
+	IECM_PTT(74, IP, IPV4, NOF, IP_GRENAT_MAC_VLAN, IPV4, FRG, NONE, PAY3),
+	IECM_PTT(75, IP, IPV4, NOF, IP_GRENAT_MAC_VLAN, IPV4, NOF, NONE, PAY3),
+	IECM_PTT(76, IP, IPV4, NOF, IP_GRENAT_MAC_VLAN, IPV4, NOF, UDP,  PAY4),
+	IECM_PTT_UNUSED_ENTRY(77),
+	IECM_PTT(78, IP, IPV4, NOF, IP_GRENAT_MAC_VLAN, IPV4, NOF, TCP,  PAY4),
+	IECM_PTT(79, IP, IPV4, NOF, IP_GRENAT_MAC_VLAN, IPV4, NOF, SCTP, PAY4),
+	IECM_PTT(80, IP, IPV4, NOF, IP_GRENAT_MAC_VLAN, IPV4, NOF, ICMP, PAY4),
+
+	/* IPv4 -> GRE/NAT -> MAC/VLAN --> IPv6 */
+	IECM_PTT(81, IP, IPV4, NOF, IP_GRENAT_MAC_VLAN, IPV6, FRG, NONE, PAY3),
+	IECM_PTT(82, IP, IPV4, NOF, IP_GRENAT_MAC_VLAN, IPV6, NOF, NONE, PAY3),
+	IECM_PTT(83, IP, IPV4, NOF, IP_GRENAT_MAC_VLAN, IPV6, NOF, UDP,  PAY4),
+	IECM_PTT_UNUSED_ENTRY(84),
+	IECM_PTT(85, IP, IPV4, NOF, IP_GRENAT_MAC_VLAN, IPV6, NOF, TCP,  PAY4),
+	IECM_PTT(86, IP, IPV4, NOF, IP_GRENAT_MAC_VLAN, IPV6, NOF, SCTP, PAY4),
+	IECM_PTT(87, IP, IPV4, NOF, IP_GRENAT_MAC_VLAN, IPV6, NOF, ICMP, PAY4),
+
+	/* Non Tunneled IPv6 */
+	IECM_PTT(88, IP, IPV6, FRG, NONE, NONE, NOF, NONE, PAY3),
+	IECM_PTT(89, IP, IPV6, NOF, NONE, NONE, NOF, NONE, PAY3),
+	IECM_PTT(90, IP, IPV6, NOF, NONE, NONE, NOF, UDP,  PAY4),
+	IECM_PTT_UNUSED_ENTRY(91),
+	IECM_PTT(92, IP, IPV6, NOF, NONE, NONE, NOF, TCP,  PAY4),
+	IECM_PTT(93, IP, IPV6, NOF, NONE, NONE, NOF, SCTP, PAY4),
+	IECM_PTT(94, IP, IPV6, NOF, NONE, NONE, NOF, ICMP, PAY4),
+
+	/* IPv6 --> IPv4 */
+	IECM_PTT(95,  IP, IPV6, NOF, IP_IP, IPV4, FRG, NONE, PAY3),
+	IECM_PTT(96,  IP, IPV6, NOF, IP_IP, IPV4, NOF, NONE, PAY3),
+	IECM_PTT(97,  IP, IPV6, NOF, IP_IP, IPV4, NOF, UDP,  PAY4),
+	IECM_PTT_UNUSED_ENTRY(98),
+	IECM_PTT(99,  IP, IPV6, NOF, IP_IP, IPV4, NOF, TCP,  PAY4),
+	IECM_PTT(100, IP, IPV6, NOF, IP_IP, IPV4, NOF, SCTP, PAY4),
+	IECM_PTT(101, IP, IPV6, NOF, IP_IP, IPV4, NOF, ICMP, PAY4),
+
+	/* IPv6 --> IPv6 */
+	IECM_PTT(102, IP, IPV6, NOF, IP_IP, IPV6, FRG, NONE, PAY3),
+	IECM_PTT(103, IP, IPV6, NOF, IP_IP, IPV6, NOF, NONE, PAY3),
+	IECM_PTT(104, IP, IPV6, NOF, IP_IP, IPV6, NOF, UDP,  PAY4),
+	IECM_PTT_UNUSED_ENTRY(105),
+	IECM_PTT(106, IP, IPV6, NOF, IP_IP, IPV6, NOF, TCP,  PAY4),
+	IECM_PTT(107, IP, IPV6, NOF, IP_IP, IPV6, NOF, SCTP, PAY4),
+	IECM_PTT(108, IP, IPV6, NOF, IP_IP, IPV6, NOF, ICMP, PAY4),
+
+	/* IPv6 --> GRE/NAT */
+	IECM_PTT(109, IP, IPV6, NOF, IP_GRENAT, NONE, NOF, NONE, PAY3),
+
+	/* IPv6 --> GRE/NAT -> IPv4 */
+	IECM_PTT(110, IP, IPV6, NOF, IP_GRENAT, IPV4, FRG, NONE, PAY3),
+	IECM_PTT(111, IP, IPV6, NOF, IP_GRENAT, IPV4, NOF, NONE, PAY3),
+	IECM_PTT(112, IP, IPV6, NOF, IP_GRENAT, IPV4, NOF, UDP,  PAY4),
+	IECM_PTT_UNUSED_ENTRY(113),
+	IECM_PTT(114, IP, IPV6, NOF, IP_GRENAT, IPV4, NOF, TCP,  PAY4),
+	IECM_PTT(115, IP, IPV6, NOF, IP_GRENAT, IPV4, NOF, SCTP, PAY4),
+	IECM_PTT(116, IP, IPV6, NOF, IP_GRENAT, IPV4, NOF, ICMP, PAY4),
+
+	/* IPv6 --> GRE/NAT -> IPv6 */
+	IECM_PTT(117, IP, IPV6, NOF, IP_GRENAT, IPV6, FRG, NONE, PAY3),
+	IECM_PTT(118, IP, IPV6, NOF, IP_GRENAT, IPV6, NOF, NONE, PAY3),
+	IECM_PTT(119, IP, IPV6, NOF, IP_GRENAT, IPV6, NOF, UDP,  PAY4),
+	IECM_PTT_UNUSED_ENTRY(120),
+	IECM_PTT(121, IP, IPV6, NOF, IP_GRENAT, IPV6, NOF, TCP,  PAY4),
+	IECM_PTT(122, IP, IPV6, NOF, IP_GRENAT, IPV6, NOF, SCTP, PAY4),
+	IECM_PTT(123, IP, IPV6, NOF, IP_GRENAT, IPV6, NOF, ICMP, PAY4),
+
+	/* IPv6 --> GRE/NAT -> MAC */
+	IECM_PTT(124, IP, IPV6, NOF, IP_GRENAT_MAC, NONE, NOF, NONE, PAY3),
+
+	/* IPv6 --> GRE/NAT -> MAC -> IPv4 */
+	IECM_PTT(125, IP, IPV6, NOF, IP_GRENAT_MAC, IPV4, FRG, NONE, PAY3),
+	IECM_PTT(126, IP, IPV6, NOF, IP_GRENAT_MAC, IPV4, NOF, NONE, PAY3),
+	IECM_PTT(127, IP, IPV6, NOF, IP_GRENAT_MAC, IPV4, NOF, UDP,  PAY4),
+	IECM_PTT_UNUSED_ENTRY(128),
+	IECM_PTT(129, IP, IPV6, NOF, IP_GRENAT_MAC, IPV4, NOF, TCP,  PAY4),
+	IECM_PTT(130, IP, IPV6, NOF, IP_GRENAT_MAC, IPV4, NOF, SCTP, PAY4),
+	IECM_PTT(131, IP, IPV6, NOF, IP_GRENAT_MAC, IPV4, NOF, ICMP, PAY4),
+
+	/* IPv6 --> GRE/NAT -> MAC -> IPv6 */
+	IECM_PTT(132, IP, IPV6, NOF, IP_GRENAT_MAC, IPV6, FRG, NONE, PAY3),
+	IECM_PTT(133, IP, IPV6, NOF, IP_GRENAT_MAC, IPV6, NOF, NONE, PAY3),
+	IECM_PTT(134, IP, IPV6, NOF, IP_GRENAT_MAC, IPV6, NOF, UDP,  PAY4),
+	IECM_PTT_UNUSED_ENTRY(135),
+	IECM_PTT(136, IP, IPV6, NOF, IP_GRENAT_MAC, IPV6, NOF, TCP,  PAY4),
+	IECM_PTT(137, IP, IPV6, NOF, IP_GRENAT_MAC, IPV6, NOF, SCTP, PAY4),
+	IECM_PTT(138, IP, IPV6, NOF, IP_GRENAT_MAC, IPV6, NOF, ICMP, PAY4),
+
+	/* IPv6 --> GRE/NAT -> MAC/VLAN */
+	IECM_PTT(139, IP, IPV6, NOF, IP_GRENAT_MAC_VLAN, NONE, NOF, NONE, PAY3),
+
+	/* IPv6 --> GRE/NAT -> MAC/VLAN --> IPv4 */
+	IECM_PTT(140, IP, IPV6, NOF, IP_GRENAT_MAC_VLAN, IPV4, FRG, NONE, PAY3),
+	IECM_PTT(141, IP, IPV6, NOF, IP_GRENAT_MAC_VLAN, IPV4, NOF, NONE, PAY3),
+	IECM_PTT(142, IP, IPV6, NOF, IP_GRENAT_MAC_VLAN, IPV4, NOF, UDP,  PAY4),
+	IECM_PTT_UNUSED_ENTRY(143),
+	IECM_PTT(144, IP, IPV6, NOF, IP_GRENAT_MAC_VLAN, IPV4, NOF, TCP,  PAY4),
+	IECM_PTT(145, IP, IPV6, NOF, IP_GRENAT_MAC_VLAN, IPV4, NOF, SCTP, PAY4),
+	IECM_PTT(146, IP, IPV6, NOF, IP_GRENAT_MAC_VLAN, IPV4, NOF, ICMP, PAY4),
+
+	/* IPv6 --> GRE/NAT -> MAC/VLAN --> IPv6 */
+	IECM_PTT(147, IP, IPV6, NOF, IP_GRENAT_MAC_VLAN, IPV6, FRG, NONE, PAY3),
+	IECM_PTT(148, IP, IPV6, NOF, IP_GRENAT_MAC_VLAN, IPV6, NOF, NONE, PAY3),
+	IECM_PTT(149, IP, IPV6, NOF, IP_GRENAT_MAC_VLAN, IPV6, NOF, UDP,  PAY4),
+	IECM_PTT_UNUSED_ENTRY(150),
+	IECM_PTT(151, IP, IPV6, NOF, IP_GRENAT_MAC_VLAN, IPV6, NOF, TCP,  PAY4),
+	IECM_PTT(152, IP, IPV6, NOF, IP_GRENAT_MAC_VLAN, IPV6, NOF, SCTP, PAY4),
+	IECM_PTT(153, IP, IPV6, NOF, IP_GRENAT_MAC_VLAN, IPV6, NOF, ICMP, PAY4),
+
+	/* rest of the entries are unused */
+};
+EXPORT_SYMBOL(iecm_ptype_lookup);
 
 /**
  * iecm_vport_init_num_qs - Initialize number of queues
diff --git a/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
index d8152e657e24..c4ae56897d1b 100644
--- a/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
+++ b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
@@ -859,6 +859,15 @@ static int iecm_recv_get_caps_msg(struct iecm_adapter *adapter)
 				sizeof(struct virtchnl2_get_capabilities));
 }
 
+/**
+ * iecm_vport_init_max_qs - Initialize max queues supported on this device
+ * @adapter: Driver specific private structure
+ */
+static void iecm_vport_init_max_qs(struct iecm_adapter *adapter)
+{
+	adapter->max_queue_limit = IECM_MAX_Q;
+}
+
 /**
  * iecm_get_reg_intr_vecs - Get vector queue register offset
  * @vport: virtual port structure
@@ -901,6 +910,199 @@ int iecm_get_reg_intr_vecs(struct iecm_vport *vport,
 }
 EXPORT_SYMBOL(iecm_get_reg_intr_vecs);
 
+/**
+ * iecm_vport_get_q_reg - Get the queue registers for the vport
+ * @reg_vals: register values needing to be set
+ * @num_regs: amount we expect to fill
+ * @q_type: queue model
+ * @chunks: queue regs received over mailbox
+ */
+static int
+iecm_vport_get_q_reg(u32 *reg_vals, int num_regs, u32 q_type,
+		     struct virtchnl2_queue_reg_chunks *chunks)
+{
+	u16 num_chunks = le16_to_cpu(chunks->num_chunks);
+	int reg_filled = 0, i;
+	u32 reg_val;
+	u16 num_q;
+
+	while (num_chunks) {
+		struct virtchnl2_queue_reg_chunk *chunk = &chunks->chunks[num_chunks - 1];
+
+		if (le32_to_cpu(chunk->type) == q_type) {
+			num_q = le32_to_cpu(chunk->num_queues);
+			reg_val = le64_to_cpu(chunk->qtail_reg_start);
+			for (i = 0; i < num_q; i++) {
+				if (reg_filled == num_regs)
+					break;
+				reg_vals[reg_filled++] = reg_val;
+				reg_val +=
+					le32_to_cpu(chunk->qtail_reg_spacing);
+			}
+		}
+		num_chunks--;
+	}
+
+	return reg_filled;
+}
+
+/**
+ * __iecm_queue_reg_init - initialize queue registers
+ * @vport: virtual port structure
+ * @reg_vals: registers we are initializing
+ * @num_regs: how many registers there are in total
+ * @q_type: queue model
+ *
+ * Return number of queues that are initialized
+ */
+static int
+__iecm_queue_reg_init(struct iecm_vport *vport, u32 *reg_vals,
+		      int num_regs, u32 q_type)
+{
+	struct iecm_hw *hw = &vport->adapter->hw;
+	struct iecm_queue *q;
+	int i, j, k = 0;
+
+	switch (q_type) {
+	case VIRTCHNL2_QUEUE_TYPE_TX:
+		for (i = 0; i < vport->num_txq_grp; i++) {
+			struct iecm_txq_group *tx_qgrp = &vport->txq_grps[i];
+
+			for (j = 0; j < tx_qgrp->num_txq; j++) {
+				if (k == num_regs)
+					break;
+
+				tx_qgrp->txqs[j]->tail =
+				  (__force u8 __iomem *)(hw->hw_addr +
+							 reg_vals[k]);
+				k++;
+			}
+		}
+		break;
+	case VIRTCHNL2_QUEUE_TYPE_RX:
+		for (i = 0; i < vport->num_rxq_grp; i++) {
+			struct iecm_rxq_group *rx_qgrp = &vport->rxq_grps[i];
+			int num_rxq = rx_qgrp->singleq.num_rxq;
+
+			for (j = 0; j < num_rxq; j++) {
+				if (k == num_regs)
+					break;
+
+				q = rx_qgrp->singleq.rxqs[j];
+				q->tail = (__force u8 __iomem *)(hw->hw_addr +
+								 reg_vals[k]);
+				k++;
+			}
+		}
+		break;
+	case VIRTCHNL2_QUEUE_TYPE_RX_BUFFER:
+		for (i = 0; i < vport->num_rxq_grp; i++) {
+			struct iecm_rxq_group *rx_qgrp = &vport->rxq_grps[i];
+
+			for (j = 0; j < vport->num_bufqs_per_qgrp; j++) {
+				if (k == num_regs)
+					break;
+
+				q = &rx_qgrp->splitq.bufq_sets[j].bufq;
+				q->tail = (__force u8 __iomem *)(hw->hw_addr +
+								 reg_vals[k]);
+				k++;
+			}
+		}
+		break;
+	default:
+		break;
+	}
+
+	return k;
+}
+
+/**
+ * iecm_queue_reg_init - initialize queue registers
+ * @vport: virtual port structure
+ *
+ * Return 0 on success, negative on failure
+ */
+static int iecm_queue_reg_init(struct iecm_vport *vport)
+{
+	struct virtchnl2_create_vport *vport_params;
+	struct virtchnl2_queue_reg_chunks *chunks;
+	int num_regs, ret = 0;
+	u32 *reg_vals;
+
+	/* We may never deal with more than 256 same type of queues */
+	reg_vals = kmalloc(sizeof(void *) * IECM_LARGE_MAX_Q,
+			   GFP_KERNEL);
+	if (!reg_vals)
+		return -ENOMEM;
+
+	if (vport->adapter->config_data.req_qs_chunks) {
+		struct virtchnl2_add_queues *vc_aq =
+		  (struct virtchnl2_add_queues *)
+		  vport->adapter->config_data.req_qs_chunks;
+		chunks = &vc_aq->chunks;
+	} else {
+		vport_params = (struct virtchnl2_create_vport *)
+			vport->adapter->vport_params_recvd[0];
+		chunks = &vport_params->chunks;
+	}
+
+	/* Initialize Tx queue tail register address */
+	num_regs = iecm_vport_get_q_reg(reg_vals, IECM_LARGE_MAX_Q,
+					VIRTCHNL2_QUEUE_TYPE_TX,
+					chunks);
+	if (num_regs < vport->num_txq) {
+		ret = -EINVAL;
+		goto free_reg_vals;
+	}
+
+	num_regs = __iecm_queue_reg_init(vport, reg_vals, num_regs,
+					 VIRTCHNL2_QUEUE_TYPE_TX);
+	if (num_regs < vport->num_txq) {
+		ret = -EINVAL;
+		goto free_reg_vals;
+	}
+
+	/* Initialize Rx/buffer queue tail register address based on Rx queue
+	 * model
+	 */
+	if (iecm_is_queue_model_split(vport->rxq_model)) {
+		num_regs = iecm_vport_get_q_reg(reg_vals, IECM_LARGE_MAX_Q,
+						VIRTCHNL2_QUEUE_TYPE_RX_BUFFER,
+						chunks);
+		if (num_regs < vport->num_bufq) {
+			ret = -EINVAL;
+			goto free_reg_vals;
+		}
+
+		num_regs = __iecm_queue_reg_init(vport, reg_vals, num_regs,
+						 VIRTCHNL2_QUEUE_TYPE_RX_BUFFER);
+		if (num_regs < vport->num_bufq) {
+			ret = -EINVAL;
+			goto free_reg_vals;
+		}
+	} else {
+		num_regs = iecm_vport_get_q_reg(reg_vals, IECM_LARGE_MAX_Q,
+						VIRTCHNL2_QUEUE_TYPE_RX,
+						chunks);
+		if (num_regs < vport->num_rxq) {
+			ret = -EINVAL;
+			goto free_reg_vals;
+		}
+
+		num_regs = __iecm_queue_reg_init(vport, reg_vals, num_regs,
+						 VIRTCHNL2_QUEUE_TYPE_RX);
+		if (num_regs < vport->num_rxq) {
+			ret = -EINVAL;
+			goto free_reg_vals;
+		}
+	}
+
+free_reg_vals:
+	kfree(reg_vals);
+	return ret;
+}
+
 /**
  * iecm_send_create_vport_msg - Send virtchnl create vport message
  * @adapter: Driver specific private structure
@@ -943,6 +1145,66 @@ static int iecm_send_create_vport_msg(struct iecm_adapter *adapter)
 				(u8 *)vport_msg);
 }
 
+/**
+ * iecm_check_descs - Verify we have the descriptor support required
+ * @vport: virtual port structure
+ * @rx_desc_ids: Rx descriptor ids to check
+ * @tx_desc_ids: Tx descriptor ids to check
+ * @rxq_model: Rx queue model
+ * @txq_model: Tx queue model
+ *
+ * Returns 0 on success, negative if we didn't get sufficient descriptor
+ * support.
+ */
+int iecm_check_descs(struct iecm_vport *vport, u64 rx_desc_ids,
+		     u64 tx_desc_ids, u16 rxq_model, u16 txq_model)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+
+	if (rxq_model == VIRTCHNL2_QUEUE_MODEL_SPLIT) {
+		if (!(rx_desc_ids & VIRTCHNL2_RXDID_1_FLEX_SPLITQ_M)) {
+			dev_err(&adapter->pdev->dev, "No supported RX descriptors provided");
+			return -EINVAL;
+		}
+	} else {
+		if (!(rx_desc_ids & VIRTCHNL2_RXDID_2_FLEX_SQ_NIC_M))
+			vport->base_rxd = true;
+	}
+
+	if (txq_model == VIRTCHNL2_QUEUE_MODEL_SPLIT) {
+#define MIN_SUPPORT_TXDID (\
+		VIRTCHNL2_TXDID_FLEX_FLOW_SCHED |\
+		VIRTCHNL2_TXDID_FLEX_TSO_CTX |\
+		VIRTCHNL2_TXDID_FLEX_DATA)
+		if ((tx_desc_ids & MIN_SUPPORT_TXDID) != MIN_SUPPORT_TXDID) {
+			dev_err(&adapter->pdev->dev, "Minimum TX descriptor support not provided");
+			return -EINVAL;
+		}
+	}
+	return 0;
+}
+EXPORT_SYMBOL(iecm_check_descs);
+
+/**
+ * iecm_get_supported_desc_ids - Get supported Rx and Tx descriptor ids
+ * @vport: virtual port structure
+ *
+ * Return 0 on success, error on failure
+ */
+static int iecm_get_supported_desc_ids(struct iecm_vport *vport)
+{
+	struct virtchnl2_create_vport *vport_msg;
+
+	vport_msg = (struct virtchnl2_create_vport *)
+			vport->adapter->vport_params_recvd[0];
+	vport_msg->rx_desc_ids = cpu_to_le64(VIRTCHNL2_RXDID_1_FLEX_SPLITQ_M);
+	vport_msg->tx_desc_ids = cpu_to_le64(MIN_SUPPORT_TXDID);
+
+	return iecm_check_descs(vport, le64_to_cpu(vport_msg->rx_desc_ids),
+				le64_to_cpu(vport_msg->tx_desc_ids),
+				vport->rxq_model, vport->txq_model);
+}
+
 /**
  * iecm_recv_create_vport_msg - Receive virtchnl create vport message
  * @adapter: Driver specific private structure
@@ -1333,6 +1595,9 @@ int iecm_send_config_rx_queues_msg(struct iecm_vport *vport)
 					bufq->rx_buf_stride;
 				qi[k].rx_buffer_low_watermark =
 					cpu_to_le16(bufq->rx_buffer_low_watermark);
+				if (iecm_is_feature_ena(vport, NETIF_F_GRO_HW))
+					qi[k].qflags |=
+						cpu_to_le16(VIRTCHNL2_RXQ_RSC);
 			}
 		}
 
@@ -1361,6 +1626,9 @@ int iecm_send_config_rx_queues_msg(struct iecm_vport *vport)
 					qi[k].hdr_buffer_size =
 						cpu_to_le16(rxq->rx_hbuf_size);
 				}
+				if (iecm_is_feature_ena(vport, NETIF_F_GRO_HW))
+					qi[k].qflags |=
+						cpu_to_le16(VIRTCHNL2_RXQ_RSC);
 			} else {
 				rxq = rx_qgrp->singleq.rxqs[j];
 			}
@@ -1968,6 +2236,765 @@ int iecm_send_add_queues_msg(struct iecm_vport *vport, u16 num_tx_q,
 	return err;
 }
 
+/**
+ * iecm_send_alloc_vectors_msg - Send virtchnl alloc vectors message
+ * @adapter: Driver specific private structure
+ * @num_vectors: number of vectors to be allocated
+ *
+ * Returns 0 on success, negative on failure.
+ */
+int iecm_send_alloc_vectors_msg(struct iecm_adapter *adapter, u16 num_vectors)
+{
+	struct virtchnl2_alloc_vectors *alloc_vec;
+	struct virtchnl2_alloc_vectors ac = {0};
+	int size, err;
+
+	ac.num_vectors = cpu_to_le16(num_vectors);
+
+	err = iecm_send_mb_msg(adapter, VIRTCHNL2_OP_ALLOC_VECTORS,
+			       sizeof(ac), (u8 *)&ac);
+	if (err)
+		return err;
+
+	err = iecm_wait_for_event(adapter, IECM_VC_ALLOC_VECTORS,
+				  IECM_VC_ALLOC_VECTORS_ERR);
+	if (err)
+		return err;
+
+	size = sizeof(struct virtchnl2_alloc_vectors) +
+		((num_vectors - 1) *
+		sizeof(struct virtchnl2_vector_chunk));
+
+	kfree(adapter->req_vec_chunks);
+	adapter->req_vec_chunks = NULL;
+	adapter->req_vec_chunks = kzalloc(size, GFP_KERNEL);
+	if (!adapter->req_vec_chunks) {
+		err = -ENOMEM;
+		goto error;
+	}
+	memcpy(adapter->req_vec_chunks, adapter->vc_msg, size);
+
+	alloc_vec = adapter->req_vec_chunks;
+	if (le16_to_cpu(alloc_vec->num_vectors) < num_vectors) {
+		kfree(adapter->req_vec_chunks);
+		adapter->req_vec_chunks = NULL;
+		err = -EINVAL;
+	}
+error:
+	clear_bit(__IECM_VC_MSG_PENDING, adapter->flags);
+	return err;
+}
+
+/**
+ * iecm_send_dealloc_vectors_msg - Send virtchnl de allocate vectors message
+ * @adapter: Driver specific private structure
+ *
+ * Returns 0 on success, negative on failure.
+ */
+int iecm_send_dealloc_vectors_msg(struct iecm_adapter *adapter)
+{
+	struct virtchnl2_vector_chunks *vcs;
+	struct virtchnl2_alloc_vectors *ac;
+	int buf_size, err;
+
+	ac = adapter->req_vec_chunks;
+	vcs = &ac->vchunks;
+
+	buf_size = sizeof(struct virtchnl2_vector_chunks) +
+			((le16_to_cpu(vcs->num_vchunks) - 1) *
+			sizeof(struct virtchnl2_vector_chunk));
+
+	err = iecm_send_mb_msg(adapter, VIRTCHNL2_OP_DEALLOC_VECTORS, buf_size,
+			       (u8 *)vcs);
+	if (err)
+		return err;
+	err = iecm_min_wait_for_event(adapter, IECM_VC_DEALLOC_VECTORS,
+				      IECM_VC_DEALLOC_VECTORS_ERR);
+	if (err)
+		return err;
+
+	kfree(adapter->req_vec_chunks);
+	adapter->req_vec_chunks = NULL;
+	return 0;
+}
+
+/**
+ * iecm_send_get_stats_msg - Send virtchnl get statistics message
+ * @vport: vport to get stats for
+ *
+ * Returns 0 on success, negative on failure.
+ */
+int iecm_send_get_stats_msg(struct iecm_vport *vport)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	struct virtchnl2_vport_stats *stats;
+	int err = 0;
+
+	stats = (struct virtchnl2_vport_stats *)adapter->vc_msg;
+
+	/* Don't send get_stats message if one is pending or the
+	 * link is down
+	 */
+	if (test_bit(IECM_VC_GET_STATS, adapter->vc_state) ||
+	    adapter->state <= __IECM_DOWN)
+		goto error;
+
+	stats->vport_id = cpu_to_le32(vport->vport_id);
+
+	err = iecm_send_mb_msg(adapter, VIRTCHNL2_OP_GET_STATS,
+			       sizeof(stats), (u8 *)&stats);
+	if (err)
+		goto error;
+
+	err = iecm_wait_for_event(adapter, IECM_VC_GET_STATS,
+				  IECM_VC_GET_STATS_ERR);
+	if (err)
+		goto error;
+
+	vport->netstats.rx_packets = le64_to_cpu(stats->rx_unicast) +
+				     le64_to_cpu(stats->rx_multicast) +
+				     le64_to_cpu(stats->rx_broadcast);
+	vport->netstats.tx_packets = le64_to_cpu(stats->tx_unicast) +
+				     le64_to_cpu(stats->tx_multicast) +
+				     le64_to_cpu(stats->tx_broadcast);
+	vport->netstats.rx_bytes = le64_to_cpu(stats->rx_bytes);
+	vport->netstats.tx_bytes = le64_to_cpu(stats->tx_bytes);
+	vport->netstats.tx_errors = le64_to_cpu(stats->tx_errors);
+	vport->netstats.rx_dropped = le64_to_cpu(stats->rx_discards);
+	vport->netstats.tx_dropped = le64_to_cpu(stats->tx_discards);
+	vport->port_stats.vport_stats = *stats;
+	clear_bit(__IECM_VC_MSG_PENDING, adapter->flags);
+error:
+	clear_bit(__IECM_MB_STATS_PENDING, vport->adapter->flags);
+
+	return err;
+}
+
+/**
+ * iecm_send_get_set_rss_hash_msg - Send set or get rss hash message
+ * @vport: virtual port data structure
+ * @get: flag to get or set rss hash
+ *
+ * Returns 0 on success, negative on failure.
+ */
+int iecm_send_get_set_rss_hash_msg(struct iecm_vport *vport, bool get)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	struct virtchnl2_rss_hash rh = {0};
+	int err;
+
+	rh.vport_id = cpu_to_le32(vport->vport_id);
+	rh.ptype_groups = cpu_to_le64(adapter->rss_data.rss_hash);
+
+	if (get) {
+		err = iecm_send_mb_msg(adapter, VIRTCHNL2_OP_GET_RSS_HASH,
+				       sizeof(rh), (u8 *)&rh);
+		if (err)
+			return err;
+
+		err = iecm_wait_for_event(adapter, IECM_VC_GET_RSS_HASH,
+					  IECM_VC_GET_RSS_HASH_ERR);
+		if (err)
+			return err;
+
+		memcpy(&rh, adapter->vc_msg, sizeof(rh));
+		adapter->rss_data.rss_hash = le64_to_cpu(rh.ptype_groups);
+		/* Leave the buffer clean for next message */
+		memset(adapter->vc_msg, 0, IECM_DFLT_MBX_BUF_SIZE);
+		clear_bit(__IECM_VC_MSG_PENDING, adapter->flags);
+
+		return 0;
+	}
+
+	err = iecm_send_mb_msg(adapter, VIRTCHNL2_OP_SET_RSS_HASH,
+			       sizeof(rh), (u8 *)&rh);
+	if (err)
+		return err;
+
+	return  iecm_wait_for_event(adapter, IECM_VC_SET_RSS_HASH,
+				    IECM_VC_SET_RSS_HASH_ERR);
+}
+
+/**
+ * iecm_send_get_set_rss_lut_msg - Send virtchnl get or set rss lut message
+ * @vport: virtual port data structure
+ * @get: flag to set or get rss look up table
+ *
+ * Returns 0 on success, negative on failure.
+ */
+int iecm_send_get_set_rss_lut_msg(struct iecm_vport *vport, bool get)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	struct virtchnl2_rss_lut *recv_rl;
+	struct virtchnl2_rss_lut *rl;
+	int buf_size, lut_buf_size;
+	int i, err = 0;
+
+	buf_size = sizeof(struct virtchnl2_rss_lut) +
+		       (sizeof(u32) * (adapter->rss_data.rss_lut_size - 1));
+	rl = kzalloc(buf_size, GFP_KERNEL);
+	if (!rl)
+		return -ENOMEM;
+
+	if (!get) {
+		rl->lut_entries = cpu_to_le16(adapter->rss_data.rss_lut_size);
+		for (i = 0; i < adapter->rss_data.rss_lut_size; i++)
+			rl->lut[i] = cpu_to_le32(adapter->rss_data.rss_lut[i]);
+	}
+	rl->vport_id = cpu_to_le32(vport->vport_id);
+
+	if (get) {
+		err = iecm_send_mb_msg(vport->adapter, VIRTCHNL2_OP_GET_RSS_LUT,
+				       buf_size, (u8 *)rl);
+		if (err)
+			goto error;
+
+		err = iecm_wait_for_event(adapter, IECM_VC_GET_RSS_LUT,
+					  IECM_VC_GET_RSS_LUT_ERR);
+		if (err)
+			goto error;
+
+		recv_rl = (struct virtchnl2_rss_lut *)adapter->vc_msg;
+		if (adapter->rss_data.rss_lut_size !=
+		    le16_to_cpu(recv_rl->lut_entries)) {
+			adapter->rss_data.rss_lut_size =
+				le16_to_cpu(recv_rl->lut_entries);
+			kfree(adapter->rss_data.rss_lut);
+
+			lut_buf_size = adapter->rss_data.rss_lut_size *
+					sizeof(u32);
+			adapter->rss_data.rss_lut = kzalloc(lut_buf_size,
+							    GFP_KERNEL);
+			if (!adapter->rss_data.rss_lut) {
+				adapter->rss_data.rss_lut_size = 0;
+				/* Leave the buffer clean */
+				memset(adapter->vc_msg, 0,
+				       IECM_DFLT_MBX_BUF_SIZE);
+				clear_bit(__IECM_VC_MSG_PENDING,
+					  adapter->flags);
+				err = -ENOMEM;
+				goto error;
+			}
+		}
+		memcpy(adapter->rss_data.rss_lut, adapter->vc_msg,
+		       adapter->rss_data.rss_lut_size);
+		/* Leave the buffer clean for next message */
+		memset(adapter->vc_msg, 0, IECM_DFLT_MBX_BUF_SIZE);
+		clear_bit(__IECM_VC_MSG_PENDING, adapter->flags);
+	} else {
+		err = iecm_send_mb_msg(adapter, VIRTCHNL2_OP_SET_RSS_LUT,
+				       buf_size, (u8 *)rl);
+		if (err)
+			goto error;
+
+		err = iecm_wait_for_event(adapter, IECM_VC_SET_RSS_LUT,
+					  IECM_VC_SET_RSS_LUT_ERR);
+	}
+error:
+	kfree(rl);
+	return err;
+}
+
+/**
+ * iecm_send_get_set_rss_key_msg - Send virtchnl get or set rss key message
+ * @vport: virtual port data structure
+ * @get: flag to set or get rss look up table
+ *
+ * Returns 0 on success, negative on failure
+ */
+int iecm_send_get_set_rss_key_msg(struct iecm_vport *vport, bool get)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	struct virtchnl2_rss_key *recv_rk;
+	struct virtchnl2_rss_key *rk;
+	int i, buf_size, err = 0;
+
+	buf_size = sizeof(struct virtchnl2_rss_key) +
+		       (sizeof(u8) * (adapter->rss_data.rss_key_size - 1));
+	rk = kzalloc(buf_size, GFP_KERNEL);
+	if (!rk)
+		return -ENOMEM;
+	rk->vport_id = cpu_to_le32(vport->vport_id);
+
+	if (get) {
+		err = iecm_send_mb_msg(adapter, VIRTCHNL2_OP_GET_RSS_KEY,
+				       buf_size, (u8 *)rk);
+		if (err)
+			goto error;
+
+		err = iecm_wait_for_event(adapter, IECM_VC_GET_RSS_KEY,
+					  IECM_VC_GET_RSS_KEY_ERR);
+		if (err)
+			goto error;
+
+		recv_rk = (struct virtchnl2_rss_key *)adapter->vc_msg;
+		if (adapter->rss_data.rss_key_size !=
+		    le16_to_cpu(recv_rk->key_len)) {
+			adapter->rss_data.rss_key_size =
+				min_t(u16, NETDEV_RSS_KEY_LEN,
+				      le16_to_cpu(recv_rk->key_len));
+			kfree(adapter->rss_data.rss_key);
+			adapter->rss_data.rss_key = kzalloc(adapter->rss_data.rss_key_size,
+							    GFP_KERNEL);
+			if (!adapter->rss_data.rss_key) {
+				adapter->rss_data.rss_key_size = 0;
+				/* Leave the buffer clean */
+				memset(adapter->vc_msg, 0,
+				       IECM_DFLT_MBX_BUF_SIZE);
+				clear_bit(__IECM_VC_MSG_PENDING,
+					  adapter->flags);
+				err = -ENOMEM;
+				goto error;
+			}
+		}
+		memcpy(adapter->rss_data.rss_key, adapter->vc_msg,
+		       adapter->rss_data.rss_key_size);
+		/* Leave the buffer clean for next message */
+		memset(adapter->vc_msg, 0, IECM_DFLT_MBX_BUF_SIZE);
+		clear_bit(__IECM_VC_MSG_PENDING, adapter->flags);
+	} else {
+		rk->key_len = cpu_to_le16(adapter->rss_data.rss_key_size);
+		for (i = 0; i < adapter->rss_data.rss_key_size; i++)
+			rk->key[i] = adapter->rss_data.rss_key[i];
+
+		err = iecm_send_mb_msg(adapter, VIRTCHNL2_OP_SET_RSS_KEY,
+				       buf_size, (u8 *)rk);
+		if (err)
+			goto error;
+
+		err = iecm_wait_for_event(adapter, IECM_VC_SET_RSS_KEY,
+					  IECM_VC_SET_RSS_KEY_ERR);
+	}
+error:
+	kfree(rk);
+	return err;
+}
+
+/**
+ * iecm_tpid_to_ethertype - transform from VLAN TPID to virtchnl ethertype
+ * @tpid: VLAN TPID (i.e. 0x8100, 0x88a8, etc.)
+ *
+ * Return virtchnl ethertype
+ */
+static u32 iecm_tpid_to_ethertype(u16 tpid)
+{
+	switch (tpid) {
+	case ETH_P_8021Q:
+		return VIRTCHNL_VLAN_ETHERTYPE_8100;
+	case ETH_P_8021AD:
+		return VIRTCHNL_VLAN_ETHERTYPE_88A8;
+	}
+
+	return 0;
+}
+
+/**
+ * iecm_set_vlan_offload_ethertype - set ethertype for offload message
+ * @adapter: adapter structure
+ * @msg: message structure used for updating offloads
+ * @offload_op: opcode used to determine which support structure to check
+ *
+ * Return 0 on success, negative on failure.
+ */
+static int
+iecm_set_vlan_offload_ethertype(struct iecm_adapter *adapter,
+				struct virtchnl_vlan_setting *msg,
+				enum virtchnl_ops offload_op)
+{
+	struct virtchnl_vlan_supported_caps *offload_support;
+	u16 tpid = adapter->config_data.vlan_ethertype;
+	u32 vc_ethertype;
+
+	vc_ethertype = iecm_tpid_to_ethertype(tpid);
+	/* reference the correct offload support structure */
+	switch (offload_op) {
+	case VIRTCHNL_OP_ENABLE_VLAN_STRIPPING_V2:
+	case VIRTCHNL_OP_DISABLE_VLAN_STRIPPING_V2:
+		offload_support =
+			&adapter->vlan_caps->offloads.stripping_support;
+		break;
+	case VIRTCHNL_OP_ENABLE_VLAN_INSERTION_V2:
+	case VIRTCHNL_OP_DISABLE_VLAN_INSERTION_V2:
+		offload_support =
+			&adapter->vlan_caps->offloads.insertion_support;
+		break;
+	default:
+		dev_err(&adapter->pdev->dev, "Invalid opcode %d for setting virtchnl ethertype to enable/disable VLAN offloads\n",
+			offload_op);
+		return -EINVAL;
+	}
+
+	/* make sure ethertype is supported and turning feature on/off
+	 * is allowed
+	 */
+	if ((offload_support->outer & vc_ethertype) &&
+	    (offload_support->outer & VIRTCHNL_VLAN_TOGGLE)) {
+		msg->outer_ethertype_setting = vc_ethertype;
+	} else if ((offload_support->inner & vc_ethertype) &&
+		   (offload_support->inner & VIRTCHNL_VLAN_TOGGLE)) {
+		msg->inner_ethertype_setting = vc_ethertype;
+	} else {
+		dev_err(&adapter->pdev->dev, "opcode %d unsupported for VLAN TPID 0x%04x\n",
+			offload_op, tpid);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/**
+ * iecm_send_strip_vlan_msg - Send enable/disable vlan stripping message
+ * @vport: vport structure
+ * @ena: enable or disable vlan stripping
+ *
+ * Returns 0 on success, negative on failure.
+ */
+static int iecm_send_strip_vlan_msg(struct iecm_vport *vport, bool ena)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	enum iecm_vport_vc_state vc, vc_err;
+	struct virtchnl_vlan_setting *msg;
+	enum virtchnl_ops vop;
+	int err, len;
+
+	len = sizeof(struct virtchnl_vlan_setting);
+	msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+
+	if (!msg)
+		return -ENOMEM;
+
+	msg->vport_id = vport->vport_id;
+	if (ena) {
+		vop = VIRTCHNL_OP_ENABLE_VLAN_STRIPPING_V2;
+		vc = IECM_VC_STRIPPING_ENA_VLAN_V2;
+		vc_err = IECM_VC_STRIPPING_ENA_VLAN_V2_ERR;
+	} else {
+		vop = VIRTCHNL_OP_DISABLE_VLAN_STRIPPING_V2;
+		vc = IECM_VC_STRIPPING_DIS_VLAN_V2;
+		vc_err = IECM_VC_STRIPPING_DIS_VLAN_V2_ERR;
+	}
+
+	err = iecm_set_vlan_offload_ethertype(adapter, msg, vop);
+	if (!err) {
+		err = iecm_send_mb_msg(adapter, vop, len, (u8 *)msg);
+		if (!err)
+			err = iecm_wait_for_event(adapter, vc, vc_err);
+	}
+
+	kfree(msg);
+
+	return err;
+}
+
+/**
+ * iecm_send_insert_vlan_msg - Send enable/disable vlan insertion message
+ * @vport: vport structure
+ * @ena: enable/disable vlan insertion
+ *
+ * Returns 0 on success, negative on failure.
+ */
+static int iecm_send_insert_vlan_msg(struct iecm_vport *vport, bool ena)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	enum iecm_vport_vc_state vc, vc_err;
+	struct virtchnl_vlan_setting *msg;
+	enum virtchnl_ops vop;
+	int err, len;
+
+	len = sizeof(struct virtchnl_vlan_setting);
+	msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+
+	if (!msg)
+		return -ENOMEM;
+
+	msg->vport_id = vport->vport_id;
+
+	if (ena) {
+		vop = VIRTCHNL_OP_ENABLE_VLAN_INSERTION_V2;
+		vc = IECM_VC_INSERTION_ENA_VLAN_V2;
+		vc_err = IECM_VC_INSERTION_ENA_VLAN_V2_ERR;
+	} else {
+		vop = VIRTCHNL_OP_DISABLE_VLAN_INSERTION_V2;
+		vc = IECM_VC_INSERTION_DIS_VLAN_V2;
+		vc_err = IECM_VC_INSERTION_DIS_VLAN_V2_ERR;
+	}
+
+	err = iecm_set_vlan_offload_ethertype(adapter, msg, vop);
+	if (!err) {
+		err = iecm_send_mb_msg(adapter, vop, len, (u8 *)msg);
+		if (!err)
+			err = iecm_wait_for_event(adapter, vc, vc_err);
+	}
+
+	kfree(msg);
+
+	return err;
+}
+
+/**
+ * iecm_fill_ptype_lookup - Fill L3 specific fields in ptype lookup table
+ * @ptype: ptype lookup table
+ * @pstate: state machine for ptype lookup table
+ * @ipv4: ipv4 or ipv6
+ * @frag: fragmentation allowed
+ *
+ */
+static void iecm_fill_ptype_lookup(struct iecm_rx_ptype_decoded *ptype,
+				   struct iecm_ptype_state *pstate,
+				   bool ipv4, bool frag)
+{
+	if (!pstate->outer_ip || !pstate->outer_frag) {
+		ptype->outer_ip = IECM_RX_PTYPE_OUTER_IP;
+		pstate->outer_ip = true;
+
+		if (ipv4)
+			ptype->outer_ip_ver = IECM_RX_PTYPE_OUTER_IPV4;
+		else
+			ptype->outer_ip_ver = IECM_RX_PTYPE_OUTER_IPV6;
+
+		if (frag) {
+			ptype->outer_frag = IECM_RX_PTYPE_FRAG;
+			pstate->outer_frag = true;
+		}
+	} else {
+		ptype->tunnel_type = IECM_RX_PTYPE_TUNNEL_IP_IP;
+		pstate->tunnel_state = IECM_PTYPE_TUNNEL_IP;
+
+		if (ipv4)
+			ptype->tunnel_end_prot =
+					IECM_RX_PTYPE_TUNNEL_END_IPV4;
+		else
+			ptype->tunnel_end_prot =
+					IECM_RX_PTYPE_TUNNEL_END_IPV6;
+
+		if (frag)
+			ptype->tunnel_end_frag = IECM_RX_PTYPE_FRAG;
+	}
+}
+
+/**
+ * iecm_send_get_rx_ptype_msg - Send virtchnl for ptype info
+ * @vport: virtual port data structure
+ *
+ * Returns 0 on success, negative on failure.
+ */
+int iecm_send_get_rx_ptype_msg(struct iecm_vport *vport)
+{
+	struct iecm_rx_ptype_decoded *ptype_lkup = vport->rx_ptype_lkup;
+	struct virtchnl2_get_ptype_info *get_ptype_info, *ptype_info;
+	int max_ptype, ptypes_recvd = 0, len, ptype_offset;
+	struct iecm_adapter *adapter = vport->adapter;
+	int err = 0, i, j, k = 0;
+
+	if (iecm_is_queue_model_split(vport->rxq_model))
+		max_ptype = IECM_RX_MAX_PTYPE;
+	else
+		max_ptype = IECM_RX_MAX_BASE_PTYPE;
+
+	for (i = 0; i < max_ptype; i++)
+		ptype_lkup[i] = iecm_ptype_lookup[0];
+
+	len = sizeof(struct virtchnl2_get_ptype_info);
+	get_ptype_info = kzalloc(len, GFP_KERNEL);
+	if (!get_ptype_info)
+		return -ENOMEM;
+
+	get_ptype_info->start_ptype_id = 0;
+	get_ptype_info->num_ptypes = cpu_to_le16(max_ptype);
+
+	err = iecm_send_mb_msg(adapter, VIRTCHNL2_OP_GET_PTYPE_INFO,
+			       len, (u8 *)get_ptype_info);
+	if (err)
+		goto get_ptype_rel;
+
+	while (ptypes_recvd < max_ptype) {
+		err = iecm_wait_for_event(adapter, IECM_VC_GET_PTYPE_INFO,
+					  IECM_VC_GET_PTYPE_INFO_ERR);
+		if (err)
+			goto get_ptype_rel;
+
+		len = IECM_DFLT_MBX_BUF_SIZE;
+		ptype_info = kzalloc(len, GFP_KERNEL);
+		if (!ptype_info) {
+			err = -ENOMEM;
+			goto clear_vc_flag;
+		}
+
+		memcpy(ptype_info, adapter->vc_msg, len);
+
+		ptypes_recvd += le16_to_cpu(ptype_info->num_ptypes);
+		if (ptypes_recvd > max_ptype) {
+			err = -EINVAL;
+			goto ptype_rel;
+		}
+
+		ptype_offset = sizeof(struct virtchnl2_get_ptype_info) -
+						sizeof(struct virtchnl2_ptype);
+
+		for (i = 0; i < le16_to_cpu(ptype_info->num_ptypes); i++) {
+			struct iecm_ptype_state pstate = { 0 };
+			struct virtchnl2_ptype *ptype;
+			u16 id;
+
+			ptype = (struct virtchnl2_ptype *)
+					((u8 *)ptype_info + ptype_offset);
+
+			ptype_offset += IECM_GET_PTYPE_SIZE(ptype);
+			if (ptype_offset > len) {
+				err = -EINVAL;
+				goto ptype_rel;
+			}
+
+			if (le16_to_cpu(ptype->ptype_id_10) == 0xFFFF)
+				goto ptype_rel;
+
+			if (iecm_is_queue_model_split(vport->rxq_model))
+				k = le16_to_cpu(ptype->ptype_id_10);
+			else
+				k = ptype->ptype_id_8;
+
+			if (ptype->proto_id_count)
+				ptype_lkup[k].known = 1;
+
+			for (j = 0; j < ptype->proto_id_count; j++) {
+				id = le16_to_cpu(ptype->proto_id[j]);
+				switch (id) {
+				case VIRTCHNL2_PROTO_HDR_GRE:
+					if (pstate.tunnel_state ==
+							IECM_PTYPE_TUNNEL_IP) {
+						ptype_lkup[k].tunnel_type =
+						IECM_RX_PTYPE_TUNNEL_IP_GRENAT;
+						pstate.tunnel_state |=
+						IECM_PTYPE_TUNNEL_IP_GRENAT;
+					}
+					break;
+				case VIRTCHNL2_PROTO_HDR_MAC:
+					ptype_lkup[k].outer_ip =
+						IECM_RX_PTYPE_OUTER_L2;
+					if (pstate.tunnel_state ==
+							IECM_TUN_IP_GRE) {
+						ptype_lkup[k].tunnel_type =
+						IECM_RX_PTYPE_TUNNEL_IP_GRENAT_MAC;
+						pstate.tunnel_state |=
+						IECM_PTYPE_TUNNEL_IP_GRENAT_MAC;
+					}
+					break;
+				case VIRTCHNL2_PROTO_HDR_VLAN:
+					if (pstate.tunnel_state ==
+							IECM_TUN_IP_GRE_MAC) {
+						ptype_lkup[k].tunnel_type =
+						IECM_RX_PTYPE_TUNNEL_IP_GRENAT_MAC_VLAN;
+						pstate.tunnel_state |=
+						IECM_PTYPE_TUNNEL_IP_GRENAT_MAC_VLAN;
+					}
+					break;
+				case VIRTCHNL2_PROTO_HDR_IPV4:
+					iecm_fill_ptype_lookup(&ptype_lkup[k],
+							       &pstate, true,
+							       false);
+					break;
+				case VIRTCHNL2_PROTO_HDR_IPV6:
+					iecm_fill_ptype_lookup(&ptype_lkup[k],
+							       &pstate, false,
+							       false);
+					break;
+				case VIRTCHNL2_PROTO_HDR_IPV4_FRAG:
+					iecm_fill_ptype_lookup(&ptype_lkup[k],
+							       &pstate, true,
+							       true);
+					break;
+				case VIRTCHNL2_PROTO_HDR_IPV6_FRAG:
+					iecm_fill_ptype_lookup(&ptype_lkup[k],
+							       &pstate, false,
+							       true);
+					break;
+				case VIRTCHNL2_PROTO_HDR_UDP:
+					ptype_lkup[k].inner_prot =
+					IECM_RX_PTYPE_INNER_PROT_UDP;
+					break;
+				case VIRTCHNL2_PROTO_HDR_TCP:
+					ptype_lkup[k].inner_prot =
+					IECM_RX_PTYPE_INNER_PROT_TCP;
+					break;
+				case VIRTCHNL2_PROTO_HDR_SCTP:
+					ptype_lkup[k].inner_prot =
+					IECM_RX_PTYPE_INNER_PROT_SCTP;
+					break;
+				case VIRTCHNL2_PROTO_HDR_ICMP:
+					ptype_lkup[k].inner_prot =
+					IECM_RX_PTYPE_INNER_PROT_ICMP;
+					break;
+				case VIRTCHNL2_PROTO_HDR_PAY:
+					ptype_lkup[k].payload_layer =
+						IECM_RX_PTYPE_PAYLOAD_LAYER_PAY2;
+					break;
+				case VIRTCHNL2_PROTO_HDR_ICMPV6:
+				case VIRTCHNL2_PROTO_HDR_IPV6_EH:
+				case VIRTCHNL2_PROTO_HDR_PRE_MAC:
+				case VIRTCHNL2_PROTO_HDR_POST_MAC:
+				case VIRTCHNL2_PROTO_HDR_ETHERTYPE:
+				case VIRTCHNL2_PROTO_HDR_SVLAN:
+				case VIRTCHNL2_PROTO_HDR_CVLAN:
+				case VIRTCHNL2_PROTO_HDR_MPLS:
+				case VIRTCHNL2_PROTO_HDR_MMPLS:
+				case VIRTCHNL2_PROTO_HDR_PTP:
+				case VIRTCHNL2_PROTO_HDR_CTRL:
+				case VIRTCHNL2_PROTO_HDR_LLDP:
+				case VIRTCHNL2_PROTO_HDR_ARP:
+				case VIRTCHNL2_PROTO_HDR_ECP:
+				case VIRTCHNL2_PROTO_HDR_EAPOL:
+				case VIRTCHNL2_PROTO_HDR_PPPOD:
+				case VIRTCHNL2_PROTO_HDR_PPPOE:
+				case VIRTCHNL2_PROTO_HDR_IGMP:
+				case VIRTCHNL2_PROTO_HDR_AH:
+				case VIRTCHNL2_PROTO_HDR_ESP:
+				case VIRTCHNL2_PROTO_HDR_IKE:
+				case VIRTCHNL2_PROTO_HDR_NATT_KEEP:
+				case VIRTCHNL2_PROTO_HDR_L2TPV2:
+				case VIRTCHNL2_PROTO_HDR_L2TPV2_CONTROL:
+				case VIRTCHNL2_PROTO_HDR_L2TPV3:
+				case VIRTCHNL2_PROTO_HDR_GTP:
+				case VIRTCHNL2_PROTO_HDR_GTP_EH:
+				case VIRTCHNL2_PROTO_HDR_GTPCV2:
+				case VIRTCHNL2_PROTO_HDR_GTPC_TEID:
+				case VIRTCHNL2_PROTO_HDR_GTPU:
+				case VIRTCHNL2_PROTO_HDR_GTPU_UL:
+				case VIRTCHNL2_PROTO_HDR_GTPU_DL:
+				case VIRTCHNL2_PROTO_HDR_ECPRI:
+				case VIRTCHNL2_PROTO_HDR_VRRP:
+				case VIRTCHNL2_PROTO_HDR_OSPF:
+				case VIRTCHNL2_PROTO_HDR_TUN:
+				case VIRTCHNL2_PROTO_HDR_NVGRE:
+				case VIRTCHNL2_PROTO_HDR_VXLAN:
+				case VIRTCHNL2_PROTO_HDR_VXLAN_GPE:
+				case VIRTCHNL2_PROTO_HDR_GENEVE:
+				case VIRTCHNL2_PROTO_HDR_NSH:
+				case VIRTCHNL2_PROTO_HDR_QUIC:
+				case VIRTCHNL2_PROTO_HDR_PFCP:
+				case VIRTCHNL2_PROTO_HDR_PFCP_NODE:
+				case VIRTCHNL2_PROTO_HDR_PFCP_SESSION:
+				case VIRTCHNL2_PROTO_HDR_RTP:
+				case VIRTCHNL2_PROTO_HDR_NO_PROTO:
+				default:
+					continue;
+				}
+			}
+		}
+		clear_bit(__IECM_VC_MSG_PENDING, adapter->flags);
+		kfree(ptype_info);
+	}
+	kfree(get_ptype_info);
+	return 0;
+
+ptype_rel:
+	kfree(ptype_info);
+clear_vc_flag:
+	clear_bit(__IECM_VC_MSG_PENDING, adapter->flags);
+get_ptype_rel:
+	kfree(get_ptype_info);
+	return err;
+}
+
 /**
  * iecm_find_ctlq - Given a type and id, find ctlq info
  * @hw: hardware struct
@@ -2478,6 +3505,25 @@ static int iecm_vport_queue_ids_init(struct iecm_vport *vport)
 	return 0;
 }
 
+/**
+ * iecm_vport_adjust_qs - Adjust to new requested queues
+ * @vport: virtual port data struct
+ *
+ * Renegotiate queues.  Returns 0 on success, negative on failure.
+ */
+void iecm_vport_adjust_qs(struct iecm_vport *vport)
+{
+	struct virtchnl2_create_vport vport_msg;
+
+	vport_msg.txq_model = cpu_to_le16(vport->txq_model);
+	vport_msg.rxq_model = cpu_to_le16(vport->rxq_model);
+	iecm_vport_calc_total_qs(vport->adapter, &vport_msg);
+
+	iecm_vport_init_num_qs(vport, &vport_msg);
+	iecm_vport_calc_num_q_groups(vport);
+	iecm_vport_calc_num_q_vec(vport);
+}
+
 /**
  * iecm_is_capability_ena - Default implementation of capability checking
  * @adapter: Private data struct
@@ -2522,6 +3568,117 @@ static u16 iecm_get_reserved_vectors(struct iecm_adapter *adapter)
 	return le16_to_cpu(caps->num_allocated_vectors);
 }
 
+/**
+ * iecm_get_max_tx_bufs - Max scatter-gather TX buffers
+ * @adapter: Private data struct
+ *
+ * Return maximum number of buffers that can be used in scatter-gather before
+ * they need to be linearized for hardware.
+ */
+static unsigned int iecm_get_max_tx_bufs(struct iecm_adapter *adapter)
+{
+	return ((struct virtchnl2_get_capabilities *)adapter->caps)->max_sg_bufs_per_tx_pkt;
+}
+
+/**
+ * iecm_add_del_vlans - Add or delete vlan filter
+ * @vport: vport structure
+ * @add: add or delete
+ *
+ * Request that the PF add one or more VLAN filters to our VSI.
+ */
+static void iecm_add_del_vlans(struct iecm_vport *vport, bool add)
+{
+	struct virtchnl_vlan_supported_caps *filtering_support;
+	struct iecm_adapter *adapter = vport->adapter;
+	struct virtchnl_vlan_filter_list_v2 *vvfl_v2;
+	int total_vlans = 0, num_vlans, i, len;
+	struct iecm_vlan_filter *f, *ftmp;
+	struct virtchnl_vlan *vlan;
+	enum virtchnl_ops vop;
+	int err = 0;
+
+	spin_lock_bh(&adapter->vlan_list_lock);
+
+	list_for_each_entry(f, &adapter->config_data.vlan_filter_list, list) {
+		if ((add && f->add) || (!add && f->remove))
+			total_vlans++;
+	}
+
+	if (!total_vlans) {
+		spin_unlock_bh(&adapter->vlan_list_lock);
+		return;
+	}
+
+	len = sizeof(struct virtchnl_vlan_filter_list_v2) +
+		(IECM_VLANS_PER_MSG * sizeof(struct virtchnl_vlan_filter));
+	vvfl_v2 = kzalloc(len, GFP_ATOMIC);
+
+	if (!vvfl_v2) {
+		err = -ENOMEM;
+		goto error;
+	}
+
+	if (add)
+		vop = VIRTCHNL_OP_ADD_VLAN_V2;
+	else
+		vop = VIRTCHNL_OP_DEL_VLAN_V2;
+
+	while (total_vlans) {
+		if (total_vlans > IECM_VLANS_PER_MSG)
+			num_vlans = IECM_VLANS_PER_MSG;
+		else
+			num_vlans = total_vlans;
+		total_vlans -= num_vlans;
+
+		len = sizeof(struct virtchnl_vlan_filter_list_v2) +
+			((num_vlans - 1) * sizeof(struct virtchnl_vlan_filter));
+		vvfl_v2->vport_id = vport->vport_id;
+		vvfl_v2->num_elements = num_vlans;
+		i = 0;
+		list_for_each_entry_safe(f, ftmp,
+					 &adapter->config_data.vlan_filter_list,
+					 list) {
+			filtering_support =
+			&adapter->vlan_caps->filtering.filtering_support;
+			if (add && f->add) {
+				/* give priority over outer if it's enabled */
+				if (filtering_support->outer)
+					vlan = &vvfl_v2->filters[i].outer;
+				else
+					vlan = &vvfl_v2->filters[i].inner;
+				vlan->tci = f->vlan.vid;
+				vlan->tpid = f->vlan.tpid;
+				i++;
+				f->add = false;
+			} else if (!add && f->remove) {
+				/* give priority over outer if it's enabled */
+				if (filtering_support->outer)
+					vlan = &vvfl_v2->filters[i].outer;
+				else
+					vlan = &vvfl_v2->filters[i].inner;
+				vlan->tci = f->vlan.vid;
+				vlan->tpid = f->vlan.tpid;
+				i++;
+				f->remove = false;
+			}
+			if (i == num_vlans)
+				break;
+		}
+		spin_unlock_bh(&adapter->vlan_list_lock);
+		iecm_send_mb_msg(adapter, vop, len, (u8 *)vvfl_v2);
+		spin_lock_bh(&adapter->vlan_list_lock);
+	}
+	spin_unlock_bh(&adapter->vlan_list_lock);
+	kfree(vvfl_v2);
+	return;
+error:
+	spin_unlock_bh(&adapter->vlan_list_lock);
+	if (err)
+		dev_err(&adapter->pdev->dev,
+			"Failed to add or del vlan filters %d", err);
+}
+
 /**
  * iecm_vc_ops_init - Initialize virtchnl common api
  * @adapter: Driver specific private structure
@@ -2548,21 +3705,21 @@ void iecm_vc_ops_init(struct iecm_adapter *adapter)
 	vc_ops->enable_vport = iecm_send_enable_vport_msg;
 	vc_ops->disable_vport = iecm_send_disable_vport_msg;
 	vc_ops->destroy_vport = iecm_send_destroy_vport_msg;
-	vc_ops->get_ptype = NULL;
-	vc_ops->get_set_rss_key = NULL;
-	vc_ops->get_set_rss_lut = NULL;
-	vc_ops->get_set_rss_hash = NULL;
-	vc_ops->adjust_qs = NULL;
-	vc_ops->add_del_vlans = NULL;
-	vc_ops->strip_vlan_msg = NULL;
-	vc_ops->insert_vlan_msg = NULL;
-	vc_ops->init_max_queues = NULL;
-	vc_ops->get_max_tx_bufs = NULL;
-	vc_ops->vportq_reg_init = NULL;
-	vc_ops->alloc_vectors = NULL;
-	vc_ops->dealloc_vectors = NULL;
-	vc_ops->get_supported_desc_ids = NULL;
-	vc_ops->get_stats_msg = NULL;
+	vc_ops->get_ptype = iecm_send_get_rx_ptype_msg;
+	vc_ops->get_set_rss_key = iecm_send_get_set_rss_key_msg;
+	vc_ops->get_set_rss_lut = iecm_send_get_set_rss_lut_msg;
+	vc_ops->get_set_rss_hash = iecm_send_get_set_rss_hash_msg;
+	vc_ops->adjust_qs = iecm_vport_adjust_qs;
+	vc_ops->add_del_vlans = iecm_add_del_vlans;
+	vc_ops->strip_vlan_msg = iecm_send_strip_vlan_msg;
+	vc_ops->insert_vlan_msg = iecm_send_insert_vlan_msg;
+	vc_ops->init_max_queues = iecm_vport_init_max_qs;
+	vc_ops->get_max_tx_bufs = iecm_get_max_tx_bufs;
+	vc_ops->vportq_reg_init = iecm_queue_reg_init;
+	vc_ops->alloc_vectors = iecm_send_alloc_vectors_msg;
+	vc_ops->dealloc_vectors = iecm_send_dealloc_vectors_msg;
+	vc_ops->get_supported_desc_ids = iecm_get_supported_desc_ids;
+	vc_ops->get_stats_msg = iecm_send_get_stats_msg;
 	vc_ops->recv_mbx_msg = NULL;
 }
 EXPORT_SYMBOL(iecm_vc_ops_init);
diff --git a/drivers/net/ethernet/intel/include/iecm.h b/drivers/net/ethernet/intel/include/iecm.h
index 8dd6272db7d3..d736db65da06 100644
--- a/drivers/net/ethernet/intel/include/iecm.h
+++ b/drivers/net/ethernet/intel/include/iecm.h
@@ -346,6 +346,7 @@ struct iecm_vport {
 	int num_rxq_grp;
 	struct iecm_rxq_group *rxq_grps;
 	u32 rxq_model;
+	struct iecm_rx_ptype_decoded rx_ptype_lkup[IECM_RX_MAX_PTYPE];
 
 	struct iecm_adapter *adapter;
 	struct net_device *netdev;
@@ -382,6 +383,30 @@ enum iecm_user_flags {
 	__IECM_USER_FLAGS_NBITS,
 };
 
+#define IECM_GET_PTYPE_SIZE(p) \
+	(sizeof(struct virtchnl2_ptype) + \
+	(((p)->proto_id_count ? ((p)->proto_id_count - 1) : 0) * sizeof(u16)))
+
+#define IECM_TUN_IP_GRE (\
+	IECM_PTYPE_TUNNEL_IP |\
+	IECM_PTYPE_TUNNEL_IP_GRENAT)
+
+#define IECM_TUN_IP_GRE_MAC (\
+	IECM_TUN_IP_GRE |\
+	IECM_PTYPE_TUNNEL_IP_GRENAT_MAC)
+
+enum iecm_tunnel_state {
+	IECM_PTYPE_TUNNEL_IP                    = BIT(0),
+	IECM_PTYPE_TUNNEL_IP_GRENAT             = BIT(1),
+	IECM_PTYPE_TUNNEL_IP_GRENAT_MAC         = BIT(2),
+	IECM_PTYPE_TUNNEL_IP_GRENAT_MAC_VLAN    = BIT(3),
+};
+
+struct iecm_ptype_state {
+	bool outer_ip;
+	bool outer_frag;
+	u8 tunnel_state;
+};
 /* User defined configuration values */
 struct iecm_user_config_data {
 	u32 num_req_tx_qs; /* user requested TX queues through ethtool */
@@ -534,6 +559,7 @@ int iecm_probe(struct pci_dev *pdev,
 	       const struct pci_device_id __always_unused *ent,
 	       struct iecm_adapter *adapter);
 void iecm_remove(struct pci_dev *pdev);
+void iecm_vport_adjust_qs(struct iecm_vport *vport);
 int iecm_init_dflt_mbx(struct iecm_adapter *adapter);
 void iecm_deinit_dflt_mbx(struct iecm_adapter *adapter);
 void iecm_vc_ops_init(struct iecm_adapter *adapter);
@@ -555,8 +581,15 @@ int iecm_send_config_rx_queues_msg(struct iecm_vport *vport);
 int iecm_send_enable_vport_msg(struct iecm_vport *vport);
 int iecm_send_disable_vport_msg(struct iecm_vport *vport);
 int iecm_send_destroy_vport_msg(struct iecm_vport *vport);
+int iecm_send_get_rx_ptype_msg(struct iecm_vport *vport);
+int iecm_send_get_set_rss_key_msg(struct iecm_vport *vport, bool get);
+int iecm_send_get_set_rss_lut_msg(struct iecm_vport *vport, bool get);
+int iecm_send_get_set_rss_hash_msg(struct iecm_vport *vport, bool get);
+int iecm_send_dealloc_vectors_msg(struct iecm_adapter *adapter);
+int iecm_send_alloc_vectors_msg(struct iecm_adapter *adapter, u16 num_vectors);
 int iecm_vport_params_buf_alloc(struct iecm_adapter *adapter);
 void iecm_vport_params_buf_rel(struct iecm_adapter *adapter);
+int iecm_send_get_stats_msg(struct iecm_vport *vport);
 int iecm_get_vec_ids(struct iecm_adapter *adapter,
 		     u16 *vecids, int num_vecids,
 		     struct virtchnl2_vector_chunks *chunks);
@@ -567,6 +600,9 @@ int iecm_send_mb_msg(struct iecm_adapter *adapter, enum virtchnl_ops op,
 void iecm_vport_set_hsplit(struct iecm_vport *vport, bool ena);
 int iecm_send_enable_channels_msg(struct iecm_vport *vport);
 int iecm_send_disable_channels_msg(struct iecm_vport *vport);
+bool iecm_is_feature_ena(struct iecm_vport *vport, netdev_features_t feature);
+int iecm_check_descs(struct iecm_vport *vport, u64 rx_desc_ids,
+		     u64 tx_desc_ids, u16 rxq_model, u16 txq_model);
 int iecm_set_msg_pending(struct iecm_adapter *adapter,
 			 struct iecm_ctlq_msg *ctlq_msg,
 			 enum iecm_vport_vc_state err_enum);
diff --git a/drivers/net/ethernet/intel/include/iecm_txrx.h b/drivers/net/ethernet/intel/include/iecm_txrx.h
index 448cae0bf6e7..9f3086bfe575 100644
--- a/drivers/net/ethernet/intel/include/iecm_txrx.h
+++ b/drivers/net/ethernet/intel/include/iecm_txrx.h
@@ -4,6 +4,9 @@
 #ifndef _IECM_TXRX_H_
 #define _IECM_TXRX_H_
 
+#include "virtchnl_lan_desc.h"
+#include <linux/indirect_call_wrapper.h>
+
 #define IECM_LARGE_MAX_Q			256
 #define IECM_MAX_Q				16
 /* Mailbox Queue */
@@ -77,9 +80,200 @@
 #define IECM_MAX_RXBUFFER			9728
 #define IECM_MAX_MTU		\
 	(IECM_MAX_RXBUFFER - IECM_PACKET_HDR_PAD)
-#define IECM_INT_NAME_STR_LEN	(IFNAMSIZ + 16)
+
+#define MAKEMASK(m, s)	((m) << (s))
+
+/* Checksum offload bits decoded from the receive descriptor. */
+struct iecm_rx_csum_decoded {
+	u8 l3l4p : 1;
+	u8 ipe : 1;
+	u8 eipe : 1;
+	u8 eudpe : 1;
+	u8 ipv6exadd : 1;
+	u8 l4e : 1;
+	u8 pprs : 1;
+	u8 nat : 1;
+	u8 rsc : 1;
+	u8 raw_csum_inv : 1;
+	u16 raw_csum;
+};
+
+struct iecm_rx_extracted {
+	unsigned int size;
+	u16 vlan_tag;
+	u16 rx_ptype;
+};
 
 #define IECM_TX_COMPLQ_CLEAN_BUDGET	256
+#define IECM_TX_MIN_LEN			17
+#define IECM_TX_DESCS_FOR_SKB_DATA_PTR	1
+#define IECM_TX_MAX_BUF			8
+#define IECM_TX_DESCS_PER_CACHE_LINE	4
+#define IECM_TX_DESCS_FOR_CTX		1
+/* TX descriptors needed, worst case */
+#define IECM_TX_DESC_NEEDED (MAX_SKB_FRAGS + IECM_TX_DESCS_FOR_CTX + \
+			     IECM_TX_DESCS_PER_CACHE_LINE + \
+			     IECM_TX_DESCS_FOR_SKB_DATA_PTR)
+
+/* The size limit for a transmit buffer in a descriptor is (16K - 1).
+ * In order to align with the read requests we will align the value to
+ * the nearest 4K which represents our maximum read request size.
+ */
+#define IECM_TX_MAX_READ_REQ_SIZE	4096
+#define IECM_TX_MAX_DESC_DATA		(16 * 1024 - 1)
+#define IECM_TX_MAX_DESC_DATA_ALIGNED \
+	(~(IECM_TX_MAX_READ_REQ_SIZE - 1) & IECM_TX_MAX_DESC_DATA)
+
+#define IECM_RX_DMA_ATTR \
+	(DMA_ATTR_SKIP_CPU_SYNC | DMA_ATTR_WEAK_ORDERING)
+#define IECM_RX_DESC(R, i)	\
+	(&(((union virtchnl2_rx_desc *)((R)->desc_ring))[i]))
+
+struct iecm_page_info {
+	dma_addr_t dma;
+	struct page *page;
+	unsigned int page_offset;
+	u16 pagecnt_bias;
+};
+
+struct iecm_rx_buf {
+#define IECM_RX_BUF_MAX_PAGES 2
+	struct iecm_page_info page_info[IECM_RX_BUF_MAX_PAGES];
+	u8 page_indx;
+	u16 buf_id;
+	u16 buf_size;
+	struct sk_buff *skb;
+};
+
+/* Packet type non-ip values */
+enum iecm_rx_ptype_l2 {
+	IECM_RX_PTYPE_L2_RESERVED	= 0,
+	IECM_RX_PTYPE_L2_MAC_PAY2	= 1,
+	IECM_RX_PTYPE_L2_TIMESYNC_PAY2	= 2,
+	IECM_RX_PTYPE_L2_FIP_PAY2	= 3,
+	IECM_RX_PTYPE_L2_OUI_PAY2	= 4,
+	IECM_RX_PTYPE_L2_MACCNTRL_PAY2	= 5,
+	IECM_RX_PTYPE_L2_LLDP_PAY2	= 6,
+	IECM_RX_PTYPE_L2_ECP_PAY2	= 7,
+	IECM_RX_PTYPE_L2_EVB_PAY2	= 8,
+	IECM_RX_PTYPE_L2_QCN_PAY2	= 9,
+	IECM_RX_PTYPE_L2_EAPOL_PAY2	= 10,
+	IECM_RX_PTYPE_L2_ARP		= 11,
+};
+
+enum iecm_rx_ptype_outer_ip {
+	IECM_RX_PTYPE_OUTER_L2	= 0,
+	IECM_RX_PTYPE_OUTER_IP	= 1,
+};
+
+enum iecm_rx_ptype_outer_ip_ver {
+	IECM_RX_PTYPE_OUTER_NONE	= 0,
+	IECM_RX_PTYPE_OUTER_IPV4	= 1,
+	IECM_RX_PTYPE_OUTER_IPV6	= 2,
+};
+
+enum iecm_rx_ptype_outer_fragmented {
+	IECM_RX_PTYPE_NOT_FRAG	= 0,
+	IECM_RX_PTYPE_FRAG	= 1,
+};
+
+enum iecm_rx_ptype_tunnel_type {
+	IECM_RX_PTYPE_TUNNEL_NONE		= 0,
+	IECM_RX_PTYPE_TUNNEL_IP_IP		= 1,
+	IECM_RX_PTYPE_TUNNEL_IP_GRENAT		= 2,
+	IECM_RX_PTYPE_TUNNEL_IP_GRENAT_MAC	= 3,
+	IECM_RX_PTYPE_TUNNEL_IP_GRENAT_MAC_VLAN	= 4,
+};
+
+enum iecm_rx_ptype_tunnel_end_prot {
+	IECM_RX_PTYPE_TUNNEL_END_NONE	= 0,
+	IECM_RX_PTYPE_TUNNEL_END_IPV4	= 1,
+	IECM_RX_PTYPE_TUNNEL_END_IPV6	= 2,
+};
+
+enum iecm_rx_ptype_inner_prot {
+	IECM_RX_PTYPE_INNER_PROT_NONE		= 0,
+	IECM_RX_PTYPE_INNER_PROT_UDP		= 1,
+	IECM_RX_PTYPE_INNER_PROT_TCP		= 2,
+	IECM_RX_PTYPE_INNER_PROT_SCTP		= 3,
+	IECM_RX_PTYPE_INNER_PROT_ICMP		= 4,
+	IECM_RX_PTYPE_INNER_PROT_TIMESYNC	= 5,
+};
+
+enum iecm_rx_ptype_payload_layer {
+	IECM_RX_PTYPE_PAYLOAD_LAYER_NONE	= 0,
+	IECM_RX_PTYPE_PAYLOAD_LAYER_PAY2	= 1,
+	IECM_RX_PTYPE_PAYLOAD_LAYER_PAY3	= 2,
+	IECM_RX_PTYPE_PAYLOAD_LAYER_PAY4	= 3,
+};
+
+struct iecm_rx_ptype_decoded {
+	u32 ptype:10;
+	u32 known:1;
+	u32 outer_ip:1;
+	u32 outer_ip_ver:2;
+	u32 outer_frag:1;
+	u32 tunnel_type:3;
+	u32 tunnel_end_prot:2;
+	u32 tunnel_end_frag:1;
+	u32 inner_prot:4;
+	u32 payload_layer:3;
+};
+
+enum iecm_rx_hsplit {
+	IECM_RX_NO_HDR_SPLIT = 0,
+	IECM_RX_HDR_SPLIT = 1,
+};
+
+/* The iecm_ptype_lkup table is used to convert from the 10-bit ptype in the
+ * hardware to a bit-field that can be used by SW to more easily determine the
+ * packet type.
+ *
+ * Macros are used to shorten the table lines and make this table human
+ * readable.
+ *
+ * We store the PTYPE in the top byte of the bit field - this is just so that
+ * we can check that the table doesn't have a row missing, as the index into
+ * the table should be the PTYPE.
+ *
+ * Typical work flow:
+ *
+ * IF NOT iecm_ptype_lkup[ptype].known
+ * THEN
+ *      Packet is unknown
+ * ELSE IF iecm_ptype_lkup[ptype].outer_ip == IECM_RX_PTYPE_OUTER_IP
+ *      Use the rest of the fields to look at the tunnels, inner protocols, etc
+ * ELSE
+ *      Use the enum iecm_rx_ptype_l2 to decode the packet type
+ * ENDIF
+ */
+/* macro to make the table lines short */
+#define IECM_PTT(PTYPE, OUTER_IP, OUTER_IP_VER, OUTER_FRAG, T, TE, TEF, I, PL)\
+	{	PTYPE, \
+		1, \
+		IECM_RX_PTYPE_OUTER_##OUTER_IP, \
+		IECM_RX_PTYPE_OUTER_##OUTER_IP_VER, \
+		IECM_RX_PTYPE_##OUTER_FRAG, \
+		IECM_RX_PTYPE_TUNNEL_##T, \
+		IECM_RX_PTYPE_TUNNEL_END_##TE, \
+		IECM_RX_PTYPE_##TEF, \
+		IECM_RX_PTYPE_INNER_PROT_##I, \
+		IECM_RX_PTYPE_PAYLOAD_LAYER_##PL }
+
+#define IECM_PTT_UNUSED_ENTRY(PTYPE) { PTYPE, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
+
+/* shorter macros makes the table fit but are terse */
+#define IECM_RX_PTYPE_NOF		IECM_RX_PTYPE_NOT_FRAG
+#define IECM_RX_PTYPE_FRG		IECM_RX_PTYPE_FRAG
+#define IECM_RX_PTYPE_INNER_PROT_TS	IECM_RX_PTYPE_INNER_PROT_TIMESYNC
+#define IECM_RX_SUPP_PTYPE		18
+#define IECM_RX_MAX_PTYPE		1024
+#define IECM_RX_MAX_BASE_PTYPE		256
+
+#define IECM_INT_NAME_STR_LEN	(IFNAMSIZ + 16)
+
+/* Lookup table mapping the HW PTYPE to the bit field for decoding */
+extern const struct iecm_rx_ptype_decoded iecm_ptype_lookup[IECM_RX_MAX_PTYPE];
 
 enum iecm_queue_flags_t {
 	__IECM_Q_GEN_CHK,
@@ -318,6 +512,4 @@ void iecm_vport_calc_total_qs(struct iecm_adapter *adapter,
 			      struct virtchnl2_create_vport *vport_msg);
 void iecm_vport_calc_num_q_groups(struct iecm_vport *vport);
 void iecm_vport_calc_num_q_vec(struct iecm_vport *vport);
-irqreturn_t
-iecm_vport_intr_clean_queues(int __always_unused irq, void *data);
 #endif /* !_IECM_TXRX_H_ */
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 08/19] iecm: add interrupts and configure netdev
  2022-01-28  0:09 [Intel-wired-lan] [PATCH net-next 00/19] Add iecm and idpf Alan Brady
                   ` (6 preceding siblings ...)
  2022-01-28  0:09 ` [Intel-wired-lan] [PATCH net-next 07/19] iecm: finish virtchnl messages Alan Brady
@ 2022-01-28  0:09 ` Alan Brady
  2022-01-28 13:34   ` Alexander Lobakin
  2022-01-28  0:09 ` [Intel-wired-lan] [PATCH net-next 09/19] iecm: alloc vport TX resources Alan Brady
                   ` (11 subsequent siblings)
  19 siblings, 1 reply; 89+ messages in thread
From: Alan Brady @ 2022-01-28  0:09 UTC (permalink / raw)
  To: intel-wired-lan

This finishes implementing init_task by adding everything we need to
configure the netdevice for the vport and setup its interrupts.

Signed-off-by: Phani Burra <phani.r.burra@intel.com>
Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
Signed-off-by: Alice Michael <alice.michael@intel.com>
Signed-off-by: Alan Brady <alan.brady@intel.com>
---
 drivers/net/ethernet/intel/iecm/iecm_lib.c    | 813 +++++++++++++++++-
 drivers/net/ethernet/intel/iecm/iecm_txrx.c   |  17 +
 .../net/ethernet/intel/iecm/iecm_virtchnl.c   | 165 ++++
 drivers/net/ethernet/intel/include/iecm.h     | 112 ++-
 .../net/ethernet/intel/include/iecm_txrx.h    |   2 +
 5 files changed, 1104 insertions(+), 5 deletions(-)

diff --git a/drivers/net/ethernet/intel/iecm/iecm_lib.c b/drivers/net/ethernet/intel/iecm/iecm_lib.c
index aab8ee40424e..255b04c25683 100644
--- a/drivers/net/ethernet/intel/iecm/iecm_lib.c
+++ b/drivers/net/ethernet/intel/iecm/iecm_lib.c
@@ -5,11 +5,35 @@
 
 #include "iecm.h"
 
+static const struct net_device_ops iecm_netdev_ops_splitq;
+static const struct net_device_ops iecm_netdev_ops_singleq;
+
 const char * const iecm_vport_vc_state_str[] = {
 	IECM_FOREACH_VPORT_VC_STATE(IECM_GEN_STRING)
 };
 EXPORT_SYMBOL(iecm_vport_vc_state_str);
 
+/**
+ * iecm_get_vport_index - Get the vport index
+ * @adapter: adapter structure to get the vports array
+ * @vport: vport pointer for which the index to find
+ */
+static int iecm_get_vport_index(struct iecm_adapter *adapter,
+				struct iecm_vport *vport)
+{
+	int i, err = -EINVAL;
+
+	if (!adapter->vports)
+		return err;
+
+	for (i = 0; i < adapter->num_alloc_vport; i++) {
+		if (adapter->vports[i] != vport)
+			continue;
+		return i;
+	}
+	return err;
+}
+
 /**
  * iecm_is_feature_ena - Determine if a particular feature is enabled
  * @vport: vport to check
@@ -29,6 +53,595 @@ bool iecm_is_feature_ena(struct iecm_vport *vport, netdev_features_t feature)
 	return ena;
 }
 
+/**
+ * iecm_is_vlan_cap_ena - Check if VLAN capability is enabled
+ * @adapter: pointer to adapter
+ * @vcaps: VLAN capability bit
+ *
+ * Returns true if VLAN capability is set, false otherwise
+ */
+static bool iecm_is_vlan_cap_ena(struct iecm_adapter *adapter,
+				 enum iecm_vlan_caps vcaps)
+{
+	if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS, VIRTCHNL2_CAP_VLAN)) {
+		struct virtchnl_vlan_supported_caps *offload;
+
+		if (!adapter->vlan_caps)
+			return false;
+
+		switch (vcaps) {
+		case IECM_CAP_VLAN_CTAG_INSERT:
+			offload =
+			&adapter->vlan_caps->offloads.insertion_support;
+			if ((offload->outer & IECM_VLAN_8100) == IECM_VLAN_8100 ||
+			    (offload->inner & IECM_VLAN_8100) == IECM_VLAN_8100)
+				return true;
+			break;
+		case IECM_CAP_VLAN_STAG_INSERT:
+			offload =
+			&adapter->vlan_caps->offloads.insertion_support;
+			if ((offload->outer & IECM_VLAN_88A8) == IECM_VLAN_88A8)
+				return true;
+			break;
+		case IECM_CAP_VLAN_CTAG_STRIP:
+			offload =
+			&adapter->vlan_caps->offloads.stripping_support;
+			if ((offload->outer & IECM_VLAN_8100) == IECM_VLAN_8100 ||
+			    (offload->inner & IECM_VLAN_8100) == IECM_VLAN_8100)
+				return true;
+			break;
+		case IECM_CAP_VLAN_STAG_STRIP:
+			offload =
+			&adapter->vlan_caps->offloads.stripping_support;
+			if ((offload->outer & IECM_VLAN_88A8) == IECM_VLAN_88A8)
+				return true;
+			break;
+		case IECM_CAP_VLAN_CTAG_ADD_DEL:
+			offload =
+			&adapter->vlan_caps->filtering.filtering_support;
+			if ((offload->outer & VIRTCHNL_VLAN_ETHERTYPE_8100) ||
+			    (offload->inner & VIRTCHNL_VLAN_ETHERTYPE_8100))
+				return true;
+			break;
+		case IECM_CAP_VLAN_STAG_ADD_DEL:
+			offload =
+			&adapter->vlan_caps->filtering.filtering_support;
+			if ((offload->outer & VIRTCHNL_VLAN_ETHERTYPE_88A8) ||
+			    (offload->inner & VIRTCHNL_VLAN_ETHERTYPE_88A8))
+				return true;
+			break;
+		default:
+			dev_err(&adapter->pdev->dev, "Invalid VLAN capability %d\n",
+				vcaps);
+			return false;
+		}
+	} else if (iecm_is_cap_ena(adapter, IECM_BASE_CAPS,
+				   VIRTCHNL2_CAP_VLAN)) {
+		switch (vcaps) {
+		case IECM_CAP_VLAN_CTAG_INSERT:
+		case IECM_CAP_VLAN_CTAG_STRIP:
+		case IECM_CAP_VLAN_CTAG_ADD_DEL:
+			return true;
+		default:
+			return false;
+		}
+	}
+
+	return false;
+}
+
+/**
+ * iecm_netdev_to_vport - get a vport handle from a netdev
+ * @netdev: network interface device structure
+ */
+struct iecm_vport *iecm_netdev_to_vport(struct net_device *netdev)
+{
+	struct iecm_netdev_priv *np = netdev_priv(netdev);
+
+	return np->vport;
+}
+
+/**
+ * iecm_mb_intr_rel_irq - Free the IRQ association with the OS
+ * @adapter: adapter structure
+ */
+static void iecm_mb_intr_rel_irq(struct iecm_adapter *adapter)
+{
+	int irq_num;
+
+	irq_num = adapter->msix_entries[0].vector;
+	free_irq(irq_num, adapter);
+}
+
+/**
+ * iecm_intr_rel - Release interrupt capabilities and free memory
+ * @adapter: adapter to disable interrupts on
+ */
+static void iecm_intr_rel(struct iecm_adapter *adapter)
+{
+	if (!adapter->msix_entries)
+		return;
+	clear_bit(__IECM_MB_INTR_MODE, adapter->flags);
+	clear_bit(__IECM_MB_INTR_TRIGGER, adapter->flags);
+	iecm_mb_intr_rel_irq(adapter);
+
+	pci_free_irq_vectors(adapter->pdev);
+	if (adapter->dev_ops.vc_ops.dealloc_vectors) {
+		int err;
+
+		err = adapter->dev_ops.vc_ops.dealloc_vectors(adapter);
+		if (err) {
+			dev_err(&adapter->pdev->dev,
+				"Failed to deallocate vectors: %d\n", err);
+		}
+	}
+	kfree(adapter->msix_entries);
+	adapter->msix_entries = NULL;
+	kfree(adapter->req_vec_chunks);
+	adapter->req_vec_chunks = NULL;
+}
+
+/**
+ * iecm_mb_intr_clean - Interrupt handler for the mailbox
+ * @irq: interrupt number
+ * @data: pointer to the adapter structure
+ */
+static irqreturn_t iecm_mb_intr_clean(int __always_unused irq, void *data)
+{
+	struct iecm_adapter *adapter = (struct iecm_adapter *)data;
+
+	set_bit(__IECM_MB_INTR_TRIGGER, adapter->flags);
+	queue_delayed_work(adapter->serv_wq, &adapter->serv_task,
+			   msecs_to_jiffies(0));
+	return IRQ_HANDLED;
+}
+
+/**
+ * iecm_mb_irq_enable - Enable MSIX interrupt for the mailbox
+ * @adapter: adapter to get the hardware address for register write
+ */
+static void iecm_mb_irq_enable(struct iecm_adapter *adapter)
+{
+	struct iecm_hw *hw = &adapter->hw;
+	struct iecm_intr_reg *intr = &adapter->mb_vector.intr_reg;
+	u32 val;
+
+	val = intr->dyn_ctl_intena_m | intr->dyn_ctl_itridx_m;
+	wr32(hw, intr->dyn_ctl, val);
+	wr32(hw, intr->icr_ena, intr->icr_ena_ctlq_m);
+}
+
+/**
+ * iecm_mb_intr_req_irq - Request irq for the mailbox interrupt
+ * @adapter: adapter structure to pass to the mailbox irq handler
+ */
+static int iecm_mb_intr_req_irq(struct iecm_adapter *adapter)
+{
+	struct iecm_q_vector *mb_vector = &adapter->mb_vector;
+	int irq_num, mb_vidx = 0, err;
+
+	irq_num = adapter->msix_entries[mb_vidx].vector;
+	snprintf(mb_vector->name, sizeof(mb_vector->name) - 1,
+		 "%s-%s-%d", dev_driver_string(&adapter->pdev->dev),
+		 "Mailbox", mb_vidx);
+	err = request_irq(irq_num, adapter->irq_mb_handler, 0,
+			  mb_vector->name, adapter);
+	if (err) {
+		dev_err(&adapter->pdev->dev,
+			"Request_irq for mailbox failed, error: %d\n", err);
+		return err;
+	}
+	set_bit(__IECM_MB_INTR_MODE, adapter->flags);
+	return 0;
+}
+
+/**
+ * iecm_get_mb_vec_id - Get vector index for mailbox
+ * @adapter: adapter structure to access the vector chunks
+ *
+ * The first vector id in the requested vector chunks from the CP is for
+ * the mailbox
+ */
+static void iecm_get_mb_vec_id(struct iecm_adapter *adapter)
+{
+	if (adapter->req_vec_chunks) {
+		struct virtchnl2_get_capabilities *caps;
+
+		caps = (struct virtchnl2_get_capabilities *)adapter->caps;
+		adapter->mb_vector.v_idx = le16_to_cpu(caps->mailbox_vector_id);
+	} else {
+		adapter->mb_vector.v_idx = 0;
+	}
+}
+
+/**
+ * iecm_mb_intr_init - Initialize the mailbox interrupt
+ * @adapter: adapter structure to store the mailbox vector
+ */
+static int iecm_mb_intr_init(struct iecm_adapter *adapter)
+{
+	adapter->dev_ops.reg_ops.mb_intr_reg_init(adapter);
+	adapter->irq_mb_handler = iecm_mb_intr_clean;
+	return iecm_mb_intr_req_irq(adapter);
+}
+
+/**
+ * iecm_intr_distribute - Distribute MSIX vectors
+ * @adapter: adapter structure to get the vports
+ * @pre_req: before or after msi request
+ *
+ * Distribute the MSIX vectors acquired from the OS to the vports based on the
+ * num of vectors requested by each vport
+ */
+static int
+iecm_intr_distribute(struct iecm_adapter *adapter, bool pre_req)
+{
+	struct iecm_vport *vport = adapter->vports[0];
+	int err = 0;
+
+	if (pre_req) {
+		u16 vecs_avail;
+
+		vecs_avail = iecm_get_reserved_vecs(adapter);
+		if (vecs_avail < IECM_MIN_VEC) {
+			return -EAGAIN;
+		} else if (vecs_avail == IECM_MIN_VEC) {
+			vport->num_q_vectors = IECM_MIN_Q_VEC;
+		} else {
+			vport->num_q_vectors = vecs_avail - IECM_NONQ_VEC -
+						IECM_MAX_RDMA_VEC;
+		}
+	} else {
+		if (adapter->num_msix_entries != adapter->num_req_msix)
+			vport->num_q_vectors = adapter->num_msix_entries -
+					       IECM_NONQ_VEC;
+	}
+
+	return err;
+}
+
+/**
+ * iecm_intr_req - Request interrupt capabilities
+ * @adapter: adapter to enable interrupts on
+ *
+ * Returns 0 on success, negative on failure
+ */
+static int iecm_intr_req(struct iecm_adapter *adapter)
+{
+	int min_vectors, max_vectors, err = 0;
+	int num_q_vecs, total_num_vecs;
+	u16 vecids[IECM_MAX_VECIDS];
+	unsigned int vector;
+	int v_actual;
+
+	err = iecm_intr_distribute(adapter, true);
+	if (err)
+		return err;
+
+	num_q_vecs = adapter->vports[0]->num_q_vectors;
+
+	total_num_vecs = num_q_vecs + IECM_NONQ_VEC;
+
+	if (adapter->dev_ops.vc_ops.alloc_vectors) {
+		err = adapter->dev_ops.vc_ops.alloc_vectors(adapter,
+							    num_q_vecs);
+		if (err) {
+			dev_err(&adapter->pdev->dev,
+				"Failed to allocate vectors: %d\n", err);
+			return -EAGAIN;
+		}
+	}
+
+	min_vectors = IECM_MIN_VEC;
+	max_vectors = total_num_vecs;
+	v_actual = pci_alloc_irq_vectors(adapter->pdev, min_vectors,
+					 max_vectors, PCI_IRQ_MSIX);
+	if (v_actual < 0) {
+		dev_err(&adapter->pdev->dev, "Failed to allocate MSIX vectors: %d\n",
+			v_actual);
+		if (adapter->dev_ops.vc_ops.dealloc_vectors)
+			adapter->dev_ops.vc_ops.dealloc_vectors(adapter);
+		return -EAGAIN;
+	}
+
+	adapter->msix_entries = kcalloc(v_actual, sizeof(struct msix_entry),
+					GFP_KERNEL);
+
+	if (!adapter->msix_entries) {
+		pci_free_irq_vectors(adapter->pdev);
+		if (adapter->dev_ops.vc_ops.dealloc_vectors)
+			adapter->dev_ops.vc_ops.dealloc_vectors(adapter);
+		return -ENOMEM;
+	}
+
+	iecm_get_mb_vec_id(adapter);
+
+	if (adapter->req_vec_chunks) {
+		struct virtchnl2_vector_chunks *vchunks;
+		struct virtchnl2_alloc_vectors *ac;
+
+		ac = adapter->req_vec_chunks;
+		vchunks = &ac->vchunks;
+
+		iecm_get_vec_ids(adapter, vecids, IECM_MAX_VECIDS, vchunks);
+	} else {
+		int i = 0;
+
+		for (i = 0; i < v_actual; i++)
+			vecids[i] = i;
+	}
+
+	for (vector = 0; vector < v_actual; vector++) {
+		adapter->msix_entries[vector].entry = vecids[vector];
+		adapter->msix_entries[vector].vector =
+			pci_irq_vector(adapter->pdev, vector);
+	}
+	adapter->num_msix_entries = v_actual;
+	adapter->num_req_msix = total_num_vecs;
+
+	iecm_intr_distribute(adapter, false);
+
+	err = iecm_mb_intr_init(adapter);
+	if (err)
+		goto intr_rel;
+	iecm_mb_irq_enable(adapter);
+	return err;
+
+intr_rel:
+	iecm_intr_rel(adapter);
+	return err;
+}
+
+/**
+ * iecm_find_mac_filter - Search filter list for specific mac filter
+ * @vport: main vport structure
+ * @macaddr: the MAC address
+ *
+ * Returns ptr to the filter object or NULL. Must be called while holding the
+ * mac_filter_list_lock.
+ **/
+static struct
+iecm_mac_filter *iecm_find_mac_filter(struct iecm_vport *vport,
+				      const u8 *macaddr)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	struct iecm_mac_filter *f;
+
+	if (!macaddr)
+		return NULL;
+
+	list_for_each_entry(f, &adapter->config_data.mac_filter_list, list) {
+		if (ether_addr_equal(macaddr, f->macaddr))
+			return f;
+	}
+	return NULL;
+}
+
+/**
+ * __iecm_add_mac_filter - Add mac filter helper function
+ * @vport: main vport struct
+ * @macaddr: address to add
+ *
+ * Takes mac_filter_list_lock spinlock to add new filter to list.
+ */
+static struct
+iecm_mac_filter *__iecm_add_mac_filter(struct iecm_vport *vport,
+				       const u8 *macaddr)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	struct iecm_mac_filter *f = NULL;
+
+	spin_lock_bh(&adapter->mac_filter_list_lock);
+	f = iecm_find_mac_filter(vport, macaddr);
+	if (!f) {
+		f = kzalloc(sizeof(*f), GFP_ATOMIC);
+		if (!f) {
+			dev_err(&adapter->pdev->dev, "Failed to allocate filter: %pM",
+				macaddr);
+			goto error;
+		}
+
+		ether_addr_copy(f->macaddr, macaddr);
+
+		list_add_tail(&f->list, &adapter->config_data.mac_filter_list);
+		f->add = true;
+	} else {
+		f->remove = false;
+	}
+error:
+	spin_unlock_bh(&adapter->mac_filter_list_lock);
+
+	return f;
+}
+
+/**
+ * iecm_add_mac_filter - Add a mac filter to the filter list
+ * @vport: main vport structure
+ * @macaddr: the MAC address
+ *
+ * Returns ptr to the filter or NULL on error. If interface is up, we'll also
+ * send the virtchnl message to tell hardware about the filter.
+ **/
+static struct iecm_mac_filter *iecm_add_mac_filter(struct iecm_vport *vport,
+						   const u8 *macaddr)
+{
+	struct iecm_mac_filter *f;
+
+	if (!macaddr)
+		return NULL;
+
+	f = __iecm_add_mac_filter(vport, macaddr);
+	if (!f)
+		return NULL;
+
+	if (vport->adapter->state == __IECM_UP)
+		iecm_add_del_ether_addrs(vport, true, false);
+
+	return f;
+}
+
+/**
+ * iecm_init_mac_addr - initialize mac address for vport
+ * @vport: main vport structure
+ * @netdev: pointer to netdev struct associated with this vport
+ */
+static int iecm_init_mac_addr(struct iecm_vport *vport,
+			      struct net_device *netdev)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+
+	if (!is_valid_ether_addr(vport->default_mac_addr)) {
+		if (!iecm_is_cap_ena(vport->adapter, IECM_OTHER_CAPS,
+				     VIRTCHNL2_CAP_MACFILTER)) {
+			dev_err(&adapter->pdev->dev,
+				"MAC address not provided and capability is not set\n");
+			return -EINVAL;
+		}
+
+		dev_info(&adapter->pdev->dev, "Invalid MAC address %pM, using random\n",
+			 vport->default_mac_addr);
+		eth_hw_addr_random(netdev);
+
+		if (!iecm_add_mac_filter(vport, netdev->dev_addr))
+			return -ENOMEM;
+
+		ether_addr_copy(vport->default_mac_addr, netdev->dev_addr);
+	} else {
+		dev_addr_mod(netdev, 0, vport->default_mac_addr, ETH_ALEN);
+		ether_addr_copy(netdev->perm_addr, vport->default_mac_addr);
+	}
+
+	return 0;
+}
+
+/**
+ * iecm_cfg_netdev - Allocate, configure and register a netdev
+ * @vport: main vport structure
+ *
+ * Returns 0 on success, negative value on failure.
+ */
+static int iecm_cfg_netdev(struct iecm_vport *vport)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	netdev_features_t dflt_features;
+	netdev_features_t offloads = 0;
+	struct iecm_netdev_priv *np;
+	struct net_device *netdev;
+	u16 max_q;
+	int err;
+
+	lockdep_assert_held(&adapter->sw_mutex);
+
+	/* It's possible we already have a netdev allocated and registered for
+	 * this vport
+	 */
+	if (adapter->netdevs[vport->idx]) {
+		netdev = adapter->netdevs[vport->idx];
+		np = netdev_priv(netdev);
+		np->vport = vport;
+		vport->netdev = netdev;
+
+		return iecm_init_mac_addr(vport, netdev);
+	}
+
+	max_q = adapter->max_queue_limit;
+
+	netdev = alloc_etherdev_mqs(sizeof(struct iecm_netdev_priv),
+				    max_q, max_q);
+	if (!netdev)
+		return -ENOMEM;
+	vport->netdev = netdev;
+	np = netdev_priv(netdev);
+	np->vport = vport;
+
+	err = iecm_init_mac_addr(vport, netdev);
+	if (err)
+		goto err;
+
+	/* assign netdev_ops */
+	if (iecm_is_queue_model_split(vport->txq_model))
+		netdev->netdev_ops = &iecm_netdev_ops_splitq;
+	else
+		netdev->netdev_ops = &iecm_netdev_ops_singleq;
+
+	/* setup watchdog timeout value to be 5 second */
+	netdev->watchdog_timeo = 5 * HZ;
+
+	/* configure default MTU size */
+	netdev->min_mtu = ETH_MIN_MTU;
+	netdev->max_mtu = vport->max_mtu;
+
+	dflt_features = NETIF_F_SG	|
+			NETIF_F_HIGHDMA;
+
+	if (iecm_is_cap_ena_all(adapter, IECM_RSS_CAPS, IECM_CAP_RSS))
+		dflt_features |= NETIF_F_RXHASH;
+	if (iecm_is_cap_ena_all(adapter, IECM_CSUM_CAPS, IECM_CAP_RX_CSUM_L4V4))
+		dflt_features |= NETIF_F_IP_CSUM;
+	if (iecm_is_cap_ena_all(adapter, IECM_CSUM_CAPS, IECM_CAP_RX_CSUM_L4V6))
+		dflt_features |= NETIF_F_IPV6_CSUM;
+	if (iecm_is_cap_ena(adapter, IECM_CSUM_CAPS, IECM_CAP_RX_CSUM))
+		dflt_features |= NETIF_F_RXCSUM;
+	if (iecm_is_cap_ena_all(adapter, IECM_CSUM_CAPS, IECM_CAP_SCTP_CSUM))
+		dflt_features |= NETIF_F_SCTP_CRC;
+
+	if (iecm_is_vlan_cap_ena(adapter, IECM_CAP_VLAN_CTAG_INSERT))
+		dflt_features |= IECM_F_HW_VLAN_CTAG_TX;
+	if (iecm_is_vlan_cap_ena(adapter, IECM_CAP_VLAN_CTAG_STRIP))
+		dflt_features |= IECM_F_HW_VLAN_CTAG_RX;
+	if (iecm_is_vlan_cap_ena(adapter, IECM_CAP_VLAN_CTAG_ADD_DEL))
+		dflt_features |= IECM_F_HW_VLAN_CTAG_FILTER;
+
+	if (iecm_is_vlan_cap_ena(adapter, IECM_CAP_VLAN_STAG_INSERT))
+		dflt_features |= NETIF_F_HW_VLAN_STAG_TX;
+	if (iecm_is_vlan_cap_ena(adapter, IECM_CAP_VLAN_STAG_STRIP))
+		dflt_features |= NETIF_F_HW_VLAN_STAG_RX;
+	if (iecm_is_vlan_cap_ena(adapter, IECM_CAP_VLAN_STAG_ADD_DEL))
+		dflt_features |= NETIF_F_HW_VLAN_STAG_FILTER;
+	/* Enable cloud filter if ADQ is supported */
+	if (iecm_is_cap_ena(adapter, IECM_BASE_CAPS, VIRTCHNL2_CAP_ADQ) ||
+	    iecm_is_cap_ena(adapter, IECM_OTHER_CAPS, VIRTCHNL2_CAP_ADQ))
+		dflt_features |= NETIF_F_HW_TC;
+	if (iecm_is_cap_ena(adapter, IECM_SEG_CAPS, VIRTCHNL2_CAP_SEG_IPV4_TCP))
+		dflt_features |= NETIF_F_TSO;
+	if (iecm_is_cap_ena(adapter, IECM_SEG_CAPS, VIRTCHNL2_CAP_SEG_IPV6_TCP))
+		dflt_features |= NETIF_F_TSO6;
+	if (iecm_is_cap_ena_all(adapter, IECM_SEG_CAPS,
+				VIRTCHNL2_CAP_SEG_IPV4_UDP |
+				VIRTCHNL2_CAP_SEG_IPV6_UDP))
+		dflt_features |= NETIF_F_GSO_UDP_L4;
+	if (iecm_is_cap_ena_all(adapter, IECM_RSC_CAPS, IECM_CAP_RSC))
+		offloads |= NETIF_F_GRO_HW;
+	netdev->features |= dflt_features;
+	netdev->hw_features |= dflt_features | offloads;
+	netdev->hw_enc_features |= dflt_features | offloads;
+
+	SET_NETDEV_DEV(netdev, &adapter->pdev->dev);
+
+	/* carrier off on init to avoid Tx hangs */
+	netif_carrier_off(netdev);
+
+	/* make sure transmit queues start off as stopped */
+	netif_tx_stop_all_queues(netdev);
+
+	/* register last */
+	err = register_netdev(netdev);
+	if (err)
+		goto err;
+
+	/* The vport can be arbitrarily released so we need to also track
+	 * netdevs in the adapter struct
+	 */
+	adapter->netdevs[vport->idx] = netdev;
+
+	return 0;
+err:
+	free_netdev(vport->netdev);
+	vport->netdev = NULL;
+
+	return err;
+}
+
 /**
  * iecm_cfg_hw - Initialize HW struct
  * @adapter: adapter to setup hw struct for
@@ -77,6 +690,24 @@ static int iecm_get_free_slot(void *array, int size, int curr)
 	return next;
 }
 
+/**
+ * iecm_decfg_netdev - Unregister the netdev
+ * @vport: vport for which netdev to be unregistred
+ */
+static void iecm_decfg_netdev(struct iecm_vport *vport)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+
+	if (!vport->netdev)
+		return;
+
+	unregister_netdev(vport->netdev);
+	free_netdev(vport->netdev);
+	vport->netdev = NULL;
+
+	adapter->netdevs[vport->idx] = NULL;
+}
+
 /**
  * iecm_vport_rel - Delete a vport and free its resources
  * @vport: the vport being removed
@@ -102,6 +733,8 @@ static void iecm_vport_rel_all(struct iecm_adapter *adapter)
 		if (!adapter->vports[i])
 			continue;
 
+		if (!test_bit(__IECM_HR_RESET_IN_PROG, adapter->flags))
+			iecm_decfg_netdev(adapter->vports[i]);
 		iecm_vport_rel(adapter->vports[i]);
 		adapter->vports[i] = NULL;
 		adapter->next_vport = 0;
@@ -151,7 +784,7 @@ iecm_vport_alloc(struct iecm_adapter *adapter, int vport_id)
 	adapter->num_alloc_vport++;
 
 	/* Setup default MSIX irq handler for the vport */
-	vport->irq_q_handler = NULL;
+	vport->irq_q_handler = iecm_vport_intr_clean_queues;
 	vport->q_vector_base = IECM_NONQ_VEC;
 
 	mutex_init(&vport->stop_mutex);
@@ -184,8 +817,94 @@ static void iecm_statistics_task(struct work_struct *work)
  *
  */
 static void iecm_service_task(struct work_struct *work)
+{
+	struct iecm_adapter *adapter = container_of(work,
+						    struct iecm_adapter,
+						    serv_task.work);
+
+	if (test_bit(__IECM_MB_INTR_MODE, adapter->flags)) {
+		if (test_and_clear_bit(__IECM_MB_INTR_TRIGGER,
+				       adapter->flags)) {
+			iecm_recv_mb_msg(adapter, VIRTCHNL_OP_UNKNOWN, NULL, 0);
+			iecm_mb_irq_enable(adapter);
+		}
+	} else {
+		iecm_recv_mb_msg(adapter, VIRTCHNL_OP_UNKNOWN, NULL, 0);
+	}
+
+	if (iecm_is_reset_detected(adapter) &&
+	    !iecm_is_reset_in_prog(adapter)) {
+		dev_info(&adapter->pdev->dev, "HW reset detected\n");
+		set_bit(__IECM_HR_FUNC_RESET, adapter->flags);
+		queue_delayed_work(adapter->vc_event_wq,
+				   &adapter->vc_event_task,
+				   msecs_to_jiffies(10));
+	}
+
+	queue_delayed_work(adapter->serv_wq, &adapter->serv_task,
+			   msecs_to_jiffies(300));
+}
+
+/* iecm_set_vlan_offload_features - set vlan offload features
+ * @netdev: netdev structure
+ * @prev_features: previously set features
+ * @features: current features received from user
+ *
+ * Returns 0 on success, error value on failure
+ */
+static int
+iecm_set_vlan_offload_features(struct net_device *netdev,
+			       netdev_features_t prev_features,
+			       netdev_features_t features)
+{
+	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
+	bool stripping_ena = true, insertion_ena = true;
+	struct iecm_virtchnl_ops *vc_ops;
+	u16 vlan_ethertype = 0;
+
+	vc_ops = &vport->adapter->dev_ops.vc_ops;
+	/* keep cases separate because one ethertype for offloads can be
+	 * disabled at the same time as another is disabled, so check for an
+	 * enabled ethertype first, then check for disabled. Default to
+	 * ETH_P_8021Q so an ethertype is specified if disabling insertion
+	 * and stripping.
+	 */
+	if (features & (NETIF_F_HW_VLAN_STAG_RX | NETIF_F_HW_VLAN_STAG_TX))
+		vlan_ethertype = ETH_P_8021AD;
+	else if (features & (NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_TX))
+		vlan_ethertype = ETH_P_8021Q;
+	else if (prev_features & (NETIF_F_HW_VLAN_STAG_RX |
+				  NETIF_F_HW_VLAN_STAG_TX))
+		vlan_ethertype = ETH_P_8021AD;
+	else if (prev_features & (NETIF_F_HW_VLAN_CTAG_RX |
+				  NETIF_F_HW_VLAN_CTAG_TX))
+		vlan_ethertype = ETH_P_8021Q;
+	else
+		vlan_ethertype = ETH_P_8021Q;
+
+	if (!(features & (NETIF_F_HW_VLAN_STAG_RX | NETIF_F_HW_VLAN_CTAG_RX)))
+		stripping_ena = false;
+	if (!(features & (NETIF_F_HW_VLAN_STAG_TX | NETIF_F_HW_VLAN_CTAG_TX)))
+		insertion_ena = false;
+
+	vport->adapter->config_data.vlan_ethertype = vlan_ethertype;
+
+	vc_ops->strip_vlan_msg(vport, stripping_ena);
+	if (vc_ops->insert_vlan_msg)
+		vc_ops->insert_vlan_msg(vport, insertion_ena);
+
+	return 0;
+}
+
+/**
+ * iecm_vport_open - Bring up a vport
+ * @vport: vport to bring up
+ * @alloc_res: allocate queue resources
+ */
+static int iecm_vport_open(struct iecm_vport *vport, bool alloc_res)
 {
 	/* stub */
+	return 0;
 }
 
 /**
@@ -206,6 +925,7 @@ static void iecm_init_task(struct work_struct *work)
 	struct iecm_vport *vport;
 	struct pci_dev *pdev;
 	int vport_id, err;
+	int index;
 
 	err = adapter->dev_ops.vc_ops.core_init(adapter, &vport_id);
 	if (err)
@@ -219,6 +939,65 @@ static void iecm_init_task(struct work_struct *work)
 			err);
 		return;
 	}
+	/* Start the service task before requesting vectors. This will ensure
+	 * vector information response from mailbox is handled
+	 */
+	queue_delayed_work(adapter->serv_wq, &adapter->serv_task,
+			   msecs_to_jiffies(5 * (pdev->devfn & 0x07)));
+	err = iecm_intr_req(adapter);
+	if (err) {
+		dev_err(&pdev->dev, "failed to enable interrupt vectors: %d\n",
+			err);
+		goto intr_req_err;
+	}
+	err = iecm_send_vlan_v2_caps_msg(adapter);
+	if (err)
+		goto vlan_v2_caps_failed;
+
+	err = adapter->dev_ops.vc_ops.get_supported_desc_ids(vport);
+	if (err) {
+		dev_err(&pdev->dev, "failed to get required descriptor ids\n");
+		goto rxdids_failed;
+	}
+
+	if (iecm_cfg_netdev(vport))
+		goto cfg_netdev_err;
+
+	if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS, VIRTCHNL2_CAP_VLAN) ||
+	    iecm_is_cap_ena(adapter, IECM_BASE_CAPS, VIRTCHNL2_CAP_VLAN)) {
+		err = iecm_set_vlan_offload_features(vport->netdev, 0,
+						     vport->netdev->features);
+		if (err)
+			goto cfg_netdev_err;
+	}
+
+	err = adapter->dev_ops.vc_ops.get_ptype(vport);
+	if (err)
+		goto cfg_netdev_err;
+	queue_delayed_work(adapter->stats_wq, &adapter->stats_task,
+			   msecs_to_jiffies(10 * (pdev->devfn & 0x07)));
+	set_bit(__IECM_VPORT_INIT_PROMISC, vport->flags);
+	/* Once state is put into DOWN, driver is ready for dev_open */
+	adapter->state = __IECM_DOWN;
+	if (test_and_clear_bit(__IECM_UP_REQUESTED, adapter->flags))
+		iecm_vport_open(vport, true);
+
+	/* Clear the reset flag unconditionally here in case we were in reset
+	 * and the link was down
+	 */
+	clear_bit(__IECM_HR_RESET_IN_PROG, vport->adapter->flags);
+
+	return;
+
+vlan_v2_caps_failed:
+rxdids_failed:
+cfg_netdev_err:
+	iecm_intr_rel(adapter);
+intr_req_err:
+	index = iecm_get_vport_index(adapter, vport);
+	if (index >= 0)
+		adapter->vports[index] = NULL;
+	iecm_vport_rel(vport);
 }
 
 /**
@@ -614,3 +1393,35 @@ void iecm_free_dma_mem(struct iecm_hw *hw, struct iecm_dma_mem *mem)
 	mem->va = NULL;
 	mem->pa = 0;
 }
+
+static const struct net_device_ops iecm_netdev_ops_splitq = {
+	.ndo_open = NULL,
+	.ndo_stop = NULL,
+	.ndo_start_xmit = NULL,
+	.ndo_set_rx_mode = NULL,
+	.ndo_validate_addr = eth_validate_addr,
+	.ndo_set_mac_address = NULL,
+	.ndo_change_mtu = NULL,
+	.ndo_get_stats64 = NULL,
+	.ndo_fix_features = NULL,
+	.ndo_set_features = NULL,
+	.ndo_vlan_rx_add_vid = NULL,
+	.ndo_vlan_rx_kill_vid = NULL,
+	.ndo_setup_tc = NULL,
+};
+
+static const struct net_device_ops iecm_netdev_ops_singleq = {
+	.ndo_open = NULL,
+	.ndo_stop = NULL,
+	.ndo_start_xmit = NULL,
+	.ndo_set_rx_mode = NULL,
+	.ndo_validate_addr = eth_validate_addr,
+	.ndo_set_mac_address = NULL,
+	.ndo_change_mtu = NULL,
+	.ndo_get_stats64 = NULL,
+	.ndo_fix_features = NULL,
+	.ndo_set_features = NULL,
+	.ndo_vlan_rx_add_vid = NULL,
+	.ndo_vlan_rx_kill_vid = NULL,
+	.ndo_setup_tc           = NULL,
+};
diff --git a/drivers/net/ethernet/intel/iecm/iecm_txrx.c b/drivers/net/ethernet/intel/iecm/iecm_txrx.c
index bd0cfd89bf03..bb7f5830cffb 100644
--- a/drivers/net/ethernet/intel/iecm/iecm_txrx.c
+++ b/drivers/net/ethernet/intel/iecm/iecm_txrx.c
@@ -218,6 +218,23 @@ const struct iecm_rx_ptype_decoded iecm_ptype_lookup[IECM_RX_MAX_PTYPE] = {
 };
 EXPORT_SYMBOL(iecm_ptype_lookup);
 
+/**
+ * iecm_vport_intr_clean_queues - MSIX mode Interrupt Handler
+ * @irq: interrupt number
+ * @data: pointer to a q_vector
+ *
+ */
+irqreturn_t
+iecm_vport_intr_clean_queues(int __always_unused irq, void *data)
+{
+	struct iecm_q_vector *q_vector = (struct iecm_q_vector *)data;
+
+	q_vector->total_events++;
+	napi_schedule(&q_vector->napi);
+
+	return IRQ_HANDLED;
+}
+
 /**
  * iecm_vport_init_num_qs - Initialize number of queues
  * @vport: vport to initialize queues
diff --git a/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
index c4ae56897d1b..b91716aeef6f 100644
--- a/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
+++ b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
@@ -2731,6 +2731,45 @@ static int iecm_send_insert_vlan_msg(struct iecm_vport *vport, bool ena)
 	return err;
 }
 
+/**
+ * iecm_send_vlan_v2_caps_msg - send virtchnl get offload VLAN V2 caps message
+ * @adapter: adapter info struct
+ *
+ * Returns 0 on success and if VLAN V1 capability is set`, negative on failure.
+ */
+int iecm_send_vlan_v2_caps_msg(struct iecm_adapter *adapter)
+{
+	int err = 0;
+
+	if (!iecm_is_cap_ena(adapter, IECM_OTHER_CAPS, VIRTCHNL2_CAP_VLAN))
+		return err;
+
+	err = iecm_send_mb_msg(adapter, VIRTCHNL_OP_GET_OFFLOAD_VLAN_V2_CAPS,
+			       0, NULL);
+	if (err)
+		return err;
+
+	err = iecm_min_wait_for_event(adapter, IECM_VC_OFFLOAD_VLAN_V2_CAPS,
+				      IECM_VC_OFFLOAD_VLAN_V2_CAPS_ERR);
+
+	if (err) {
+		dev_err(&adapter->pdev->dev, "Failed to recv get caps");
+		return err;
+	}
+
+	if (!adapter->vlan_caps) {
+		adapter->vlan_caps =
+		  kzalloc(sizeof(*adapter->vlan_caps), GFP_KERNEL);
+		if (!adapter->vlan_caps)
+			return -ENOMEM;
+	}
+
+	memcpy(adapter->vlan_caps,
+	       adapter->vc_msg, sizeof(*adapter->vlan_caps));
+
+	return err;
+}
+
 /**
  * iecm_fill_ptype_lookup - Fill L3 specific fields in ptype lookup table
  * @ptype: ptype lookup table
@@ -3580,6 +3619,132 @@ static unsigned int iecm_get_max_tx_bufs(struct iecm_adapter *adapter)
 	return ((struct virtchnl2_get_capabilities *)adapter->caps)->max_sg_bufs_per_tx_pkt;
 }
 
+/**
+ * iecm_add_del_ether_addrs
+ * @vport: virtual port data structure
+ * @add: Add or delete flag
+ * @async: Don't wait for return message
+ *
+ * Request that the PF add or delete one or more addresses to our filters.
+ **/
+void iecm_add_del_ether_addrs(struct iecm_vport *vport, bool add, bool async)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	struct virtchnl_ether_addr_list *veal = NULL;
+	int num_entries, num_msgs, total_filters = 0;
+	struct pci_dev *pdev = adapter->pdev;
+	enum iecm_vport_vc_state vc, vc_err;
+	struct virtchnl_ether_addr *eal;
+	struct iecm_mac_filter *f, *tmp;
+	int i = 0, k = 0, err = 0;
+	enum virtchnl_ops vop;
+
+	spin_lock_bh(&adapter->mac_filter_list_lock);
+
+	/* Find the number of newly added filters */
+	list_for_each_entry(f, &adapter->config_data.mac_filter_list, list) {
+		if (add && f->add)
+			total_filters++;
+		else if (!add && f->remove)
+			total_filters++;
+	}
+	if (!total_filters) {
+		spin_unlock_bh(&adapter->mac_filter_list_lock);
+		goto error;
+	}
+
+	/* Fill all the new filters into virtchannel message */
+	eal = kcalloc(total_filters, sizeof(struct virtchnl_ether_addr),
+		      GFP_ATOMIC);
+	if (!eal) {
+		err = -ENOMEM;
+		spin_unlock_bh(&adapter->mac_filter_list_lock);
+		goto error;
+	}
+	list_for_each_entry_safe(f, tmp, &adapter->config_data.mac_filter_list,
+				 list) {
+		if (add && f->add) {
+			ether_addr_copy(eal[i].addr, f->macaddr);
+			i++;
+			f->add = false;
+			if (i == total_filters)
+				break;
+		}
+		if (!add && f->remove) {
+			ether_addr_copy(eal[i].addr, f->macaddr);
+			i++;
+			list_del(&f->list);
+			kfree(f);
+			if (i == total_filters)
+				break;
+		}
+	}
+
+	spin_unlock_bh(&adapter->mac_filter_list_lock);
+
+	/* Chunk up the filters into multiple messages to avoid
+	 * sending a control queue message buffer that is too large
+	 */
+	if (total_filters < IECM_NUM_FILTERS_PER_MSG)
+		num_entries = total_filters;
+	else
+		num_entries = IECM_NUM_FILTERS_PER_MSG;
+
+	num_msgs = DIV_ROUND_UP(total_filters, IECM_NUM_FILTERS_PER_MSG);
+
+	for (i = 0, k = 0; i < num_msgs || num_entries; i++) {
+		int buf_size = sizeof(struct virtchnl_ether_addr_list) +
+			(sizeof(struct virtchnl_ether_addr) * num_entries);
+		if (!veal || num_entries != IECM_NUM_FILTERS_PER_MSG) {
+			kfree(veal);
+			veal = kzalloc(buf_size, GFP_KERNEL);
+			if (!veal) {
+				err = -ENOMEM;
+				goto list_prep_error;
+			}
+		} else {
+			memset(veal, 0, buf_size);
+		}
+
+		veal->vsi_id = vport->vport_id;
+		veal->num_elements = num_entries;
+		memcpy(veal->list, &eal[k],
+		       sizeof(struct virtchnl_ether_addr) * num_entries);
+
+		if (add) {
+			vop = VIRTCHNL_OP_ADD_ETH_ADDR;
+			vc = IECM_VC_ADD_ETH_ADDR;
+			vc_err = IECM_VC_ADD_ETH_ADDR_ERR;
+		} else  {
+			vop = VIRTCHNL_OP_DEL_ETH_ADDR;
+			vc = IECM_VC_DEL_ETH_ADDR;
+			vc_err = IECM_VC_DEL_ETH_ADDR_ERR;
+		}
+		err = iecm_send_mb_msg(vport->adapter, vop, buf_size,
+				       (u8 *)veal);
+		if (err)
+			goto mbx_error;
+
+		if (!async) {
+			err = iecm_wait_for_event(vport->adapter, vc, vc_err);
+			if (err)
+				goto mbx_error;
+		}
+
+		k += num_entries;
+		total_filters -= num_entries;
+		if (total_filters < IECM_NUM_FILTERS_PER_MSG)
+			num_entries = total_filters;
+	}
+mbx_error:
+	kfree(veal);
+list_prep_error:
+	kfree(eal);
+error:
+	if (err)
+		dev_err(&pdev->dev, "Failed to add or del mac filters %d", err);
+}
+
 /**
  * iecm_add_del_vlans - Add or delete vlan filter
  * @vport: vport structure
diff --git a/drivers/net/ethernet/intel/include/iecm.h b/drivers/net/ethernet/intel/include/iecm.h
index d736db65da06..b5bd73be2855 100644
--- a/drivers/net/ethernet/intel/include/iecm.h
+++ b/drivers/net/ethernet/intel/include/iecm.h
@@ -33,6 +33,11 @@
 #define IECM_MB_MAX_ERR			20
 #define IECM_NUM_CHUNKS_PER_MSG(a, b)	((IECM_DFLT_MBX_BUF_SIZE - (a)) / (b))
 
+/* 2K is the real maximum, but the driver should not be using more than the
+ * below limit
+ */
+#define IECM_MAX_VECIDS			256
+
 #define IECM_MAX_NUM_VPORTS		1
 
 /* available message levels */
@@ -135,6 +140,10 @@ enum iecm_cap_field {
 	IECM_CAP_FIELD_LAST,
 };
 
+struct iecm_netdev_priv {
+	struct iecm_vport *vport;
+};
+
 struct iecm_reset_reg {
 	u32 rstat;
 	u32 rstat_m;
@@ -450,6 +459,8 @@ struct iecm_adapter {
 	struct msix_entry *msix_entries;
 	struct virtchnl2_alloc_vectors *req_vec_chunks;
 	struct iecm_q_vector mb_vector;
+	/* handler for hard interrupt for mailbox*/
+	irqreturn_t (*irq_mb_handler)(int irq, void *data);
 
 	/* vport structs */
 	struct iecm_vport **vports;	/* vports created by the driver */
@@ -537,12 +548,88 @@ static inline bool __iecm_is_cap_ena(struct iecm_adapter *adapter, bool all,
 	return adapter->dev_ops.vc_ops.is_cap_ena(adapter, all, field, flag);
 }
 
-#define IECM_CAP_HSPLIT (\
-	VIRTCHNL2_CAP_RX_HSPLIT_AT_L2   |\
-	VIRTCHNL2_CAP_RX_HSPLIT_AT_L3   |\
-	VIRTCHNL2_CAP_RX_HSPLIT_AT_L4V4 |\
+/* enum used to distinguish vlan capabilities */
+enum iecm_vlan_caps {
+	IECM_CAP_VLAN_CTAG_INSERT,
+	IECM_CAP_VLAN_STAG_INSERT,
+	IECM_CAP_VLAN_CTAG_STRIP,
+	IECM_CAP_VLAN_STAG_STRIP,
+	IECM_CAP_VLAN_CTAG_ADD_DEL,
+	IECM_CAP_VLAN_STAG_ADD_DEL,
+	IECM_CAP_VLAN_LAST,
+};
+
+#define IECM_VLAN_8100 (VIRTCHNL_VLAN_TOGGLE | VIRTCHNL_VLAN_ETHERTYPE_8100)
+#define IECM_VLAN_88A8 (VIRTCHNL_VLAN_TOGGLE | VIRTCHNL_VLAN_ETHERTYPE_88A8)
+
+#define IECM_F_HW_VLAN_CTAG_TX NETIF_F_HW_VLAN_CTAG_TX
+
+#define IECM_F_HW_VLAN_CTAG_RX NETIF_F_HW_VLAN_CTAG_RX
+
+#define IECM_F_HW_VLAN_CTAG_FILTER NETIF_F_HW_VLAN_CTAG_FILTER
+
+#define IECM_CAP_RSS (\
+	VIRTCHNL2_CAP_RSS_IPV4_TCP	|\
+	VIRTCHNL2_CAP_RSS_IPV4_TCP	|\
+	VIRTCHNL2_CAP_RSS_IPV4_UDP	|\
+	VIRTCHNL2_CAP_RSS_IPV4_SCTP	|\
+	VIRTCHNL2_CAP_RSS_IPV4_OTHER	|\
+	VIRTCHNL2_CAP_RSS_IPV4_AH	|\
+	VIRTCHNL2_CAP_RSS_IPV4_ESP	|\
+	VIRTCHNL2_CAP_RSS_IPV4_AH_ESP	|\
+	VIRTCHNL2_CAP_RSS_IPV6_TCP	|\
+	VIRTCHNL2_CAP_RSS_IPV6_TCP	|\
+	VIRTCHNL2_CAP_RSS_IPV6_UDP	|\
+	VIRTCHNL2_CAP_RSS_IPV6_SCTP	|\
+	VIRTCHNL2_CAP_RSS_IPV6_OTHER	|\
+	VIRTCHNL2_CAP_RSS_IPV6_AH	|\
+	VIRTCHNL2_CAP_RSS_IPV6_ESP	|\
+	VIRTCHNL2_CAP_RSS_IPV6_AH_ESP)
+
+#define IECM_CAP_RSC (\
+	VIRTCHNL2_CAP_RSC_IPV4_TCP	|\
+	VIRTCHNL2_CAP_RSC_IPV4_SCTP	|\
+	VIRTCHNL2_CAP_RSC_IPV6_TCP	|\
+	VIRTCHNL2_CAP_RSC_IPV6_SCTP)
+
+#define IECM_CAP_HSPLIT	(\
+	VIRTCHNL2_CAP_RX_HSPLIT_AT_L2	|\
+	VIRTCHNL2_CAP_RX_HSPLIT_AT_L3	|\
+	VIRTCHNL2_CAP_RX_HSPLIT_AT_L4V4	|\
 	VIRTCHNL2_CAP_RX_HSPLIT_AT_L4V6)
 
+#define IECM_CAP_RX_CSUM_L4V4 (\
+	VIRTCHNL2_CAP_RX_CSUM_L4_IPV4_TCP	|\
+	VIRTCHNL2_CAP_RX_CSUM_L4_IPV4_UDP)
+
+#define IECM_CAP_RX_CSUM_L4V6 (\
+	VIRTCHNL2_CAP_RX_CSUM_L4_IPV6_TCP	|\
+	VIRTCHNL2_CAP_RX_CSUM_L4_IPV6_UDP)
+
+#define IECM_CAP_RX_CSUM (\
+	VIRTCHNL2_CAP_RX_CSUM_L3_IPV4		|\
+	VIRTCHNL2_CAP_RX_CSUM_L4_IPV4_TCP	|\
+	VIRTCHNL2_CAP_RX_CSUM_L4_IPV4_UDP	|\
+	VIRTCHNL2_CAP_RX_CSUM_L4_IPV4_SCTP	|\
+	VIRTCHNL2_CAP_RX_CSUM_L4_IPV6_TCP	|\
+	VIRTCHNL2_CAP_RX_CSUM_L4_IPV6_UDP	|\
+	VIRTCHNL2_CAP_RX_CSUM_L4_IPV6_SCTP)
+
+#define IECM_CAP_SCTP_CSUM (\
+	VIRTCHNL2_CAP_TX_CSUM_L4_IPV4_SCTP	|\
+	VIRTCHNL2_CAP_TX_CSUM_L4_IPV6_SCTP	|\
+	VIRTCHNL2_CAP_RX_CSUM_L4_IPV4_SCTP	|\
+	VIRTCHNL2_CAP_RX_CSUM_L4_IPV6_SCTP)
+
+/**
+ * iecm_get_reserved_vecs - Get reserved vectors
+ * @adapter: private data struct
+ */
+static inline u16 iecm_get_reserved_vecs(struct iecm_adapter *adapter)
+{
+	return adapter->dev_ops.vc_ops.get_reserved_vecs(adapter);
+}
+
 /**
  * iecm_is_reset_detected - check if we were reset at some point
  * @adapter: driver specific private structure
@@ -555,6 +642,20 @@ static inline bool iecm_is_reset_detected(struct iecm_adapter *adapter)
 		 adapter->hw.arq->reg.len_ena_mask);
 }
 
+/**
+ * iecm_is_reset_in_prog - check if reset is in progress
+ * @adapter: driver specific private structure
+ *
+ * Returns true if hard reset is in progress, false otherwise
+ */
+static inline bool iecm_is_reset_in_prog(struct iecm_adapter *adapter)
+{
+	return (test_bit(__IECM_HR_RESET_IN_PROG, adapter->flags) ||
+		test_bit(__IECM_HR_FUNC_RESET, adapter->flags) ||
+		test_bit(__IECM_HR_CORE_RESET, adapter->flags) ||
+		test_bit(__IECM_HR_DRV_LOAD, adapter->flags));
+}
+
 int iecm_probe(struct pci_dev *pdev,
 	       const struct pci_device_id __always_unused *ent,
 	       struct iecm_adapter *adapter);
@@ -576,6 +677,7 @@ int iecm_send_get_caps_msg(struct iecm_adapter *adapter);
 int iecm_send_delete_queues_msg(struct iecm_vport *vport);
 int iecm_send_add_queues_msg(struct iecm_vport *vport, u16 num_tx_q,
 			     u16 num_complq, u16 num_rx_q, u16 num_rx_bufq);
+int iecm_send_vlan_v2_caps_msg(struct iecm_adapter *adapter);
 int iecm_send_config_tx_queues_msg(struct iecm_vport *vport);
 int iecm_send_config_rx_queues_msg(struct iecm_vport *vport);
 int iecm_send_enable_vport_msg(struct iecm_vport *vport);
@@ -589,6 +691,7 @@ int iecm_send_dealloc_vectors_msg(struct iecm_adapter *adapter);
 int iecm_send_alloc_vectors_msg(struct iecm_adapter *adapter, u16 num_vectors);
 int iecm_vport_params_buf_alloc(struct iecm_adapter *adapter);
 void iecm_vport_params_buf_rel(struct iecm_adapter *adapter);
+struct iecm_vport *iecm_netdev_to_vport(struct net_device *netdev);
 int iecm_send_get_stats_msg(struct iecm_vport *vport);
 int iecm_get_vec_ids(struct iecm_adapter *adapter,
 		     u16 *vecids, int num_vecids,
@@ -598,6 +701,7 @@ int iecm_recv_mb_msg(struct iecm_adapter *adapter, enum virtchnl_ops op,
 int iecm_send_mb_msg(struct iecm_adapter *adapter, enum virtchnl_ops op,
 		     u16 msg_size, u8 *msg);
 void iecm_vport_set_hsplit(struct iecm_vport *vport, bool ena);
+void iecm_add_del_ether_addrs(struct iecm_vport *vport, bool add, bool async);
 int iecm_send_enable_channels_msg(struct iecm_vport *vport);
 int iecm_send_disable_channels_msg(struct iecm_vport *vport);
 bool iecm_is_feature_ena(struct iecm_vport *vport, netdev_features_t feature);
diff --git a/drivers/net/ethernet/intel/include/iecm_txrx.h b/drivers/net/ethernet/intel/include/iecm_txrx.h
index 9f3086bfe575..0aa1eac70e7c 100644
--- a/drivers/net/ethernet/intel/include/iecm_txrx.h
+++ b/drivers/net/ethernet/intel/include/iecm_txrx.h
@@ -512,4 +512,6 @@ void iecm_vport_calc_total_qs(struct iecm_adapter *adapter,
 			      struct virtchnl2_create_vport *vport_msg);
 void iecm_vport_calc_num_q_groups(struct iecm_vport *vport);
 void iecm_vport_calc_num_q_vec(struct iecm_vport *vport);
+irqreturn_t
+iecm_vport_intr_clean_queues(int __always_unused irq, void *data);
 #endif /* !_IECM_TXRX_H_ */
-- 
2.33.0



^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 09/19] iecm: alloc vport TX resources
  2022-01-28  0:09 [Intel-wired-lan] [PATCH net-next 00/19] Add iecm and idpf Alan Brady
                   ` (7 preceding siblings ...)
  2022-01-28  0:09 ` [Intel-wired-lan] [PATCH net-next 08/19] iecm: add interrupts and configure netdev Alan Brady
@ 2022-01-28  0:09 ` Alan Brady
  2022-02-02 23:45   ` Brady, Alan
  2022-01-28  0:10 ` [Intel-wired-lan] [PATCH net-next 10/19] iecm: alloc vport RX resources Alan Brady
                   ` (10 subsequent siblings)
  19 siblings, 1 reply; 89+ messages in thread
From: Alan Brady @ 2022-01-28  0:09 UTC (permalink / raw)
  To: intel-wired-lan

With init_task out of the way, we can start implementing open and data
path. During open we'll allocate queue resources for vport. This only
includes what's needed to get the TX resources. The next patch will get RX
resources.

The splitq model is unique in that it introduces the concept of "queue
groups" where, for TX, we have some number of descriptor queues being
serviced by one completion queue in a given group association. By
'splitting' a normal queue into two queues, one context is just handling
descriptors and one context handling buffers, we can more effeciently deal
with both and configure asymmetric setups (multiple descriptor queues to
one completion queue).

Signed-off-by: Phani Burra <phani.r.burra@intel.com>
Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
Signed-off-by: Alan Brady <alan.brady@intel.com>
---
 drivers/net/ethernet/intel/iecm/Makefile      |    1 +
 drivers/net/ethernet/intel/iecm/iecm_lib.c    |  369 ++++-
 .../ethernet/intel/iecm/iecm_singleq_txrx.c   |   29 +
 drivers/net/ethernet/intel/iecm/iecm_txrx.c   | 1282 ++++++++++++++++-
 .../net/ethernet/intel/iecm/iecm_virtchnl.c   |   29 +
 drivers/net/ethernet/intel/include/iecm.h     |   28 +
 .../ethernet/intel/include/iecm_lan_txrx.h    |  394 +++++
 .../net/ethernet/intel/include/iecm_txrx.h    |   96 ++
 8 files changed, 2214 insertions(+), 14 deletions(-)
 create mode 100644 drivers/net/ethernet/intel/iecm/iecm_singleq_txrx.c
 create mode 100644 drivers/net/ethernet/intel/include/iecm_lan_txrx.h

diff --git a/drivers/net/ethernet/intel/iecm/Makefile b/drivers/net/ethernet/intel/iecm/Makefile
index fcb49402334f..205d6f2b436a 100644
--- a/drivers/net/ethernet/intel/iecm/Makefile
+++ b/drivers/net/ethernet/intel/iecm/Makefile
@@ -14,6 +14,7 @@ iecm-y := \
 	iecm_lib.o \
 	iecm_virtchnl.o \
 	iecm_txrx.o \
+	iecm_singleq_txrx.o \
 	iecm_controlq.o \
 	iecm_controlq_setup.o \
 	iecm_main.o
diff --git a/drivers/net/ethernet/intel/iecm/iecm_lib.c b/drivers/net/ethernet/intel/iecm/iecm_lib.c
index 255b04c25683..037a0e06bb7b 100644
--- a/drivers/net/ethernet/intel/iecm/iecm_lib.c
+++ b/drivers/net/ethernet/intel/iecm/iecm_lib.c
@@ -480,6 +480,54 @@ static struct iecm_mac_filter *iecm_add_mac_filter(struct iecm_vport *vport,
 	return f;
 }
 
+/**
+ * iecm_set_all_filters - Re-add all MAC filters in list
+ * @vport: main vport struct
+ *
+ * Takes mac_filter_list_lock spinlock.  Sets add field to true for filters to
+ * resync filters back to HW.
+ */
+static void iecm_set_all_filters(struct iecm_vport *vport)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	struct iecm_mac_filter *f;
+
+	spin_lock_bh(&adapter->mac_filter_list_lock);
+	list_for_each_entry(f, &adapter->config_data.mac_filter_list, list) {
+		if (!f->remove)
+			f->add = true;
+	}
+	spin_unlock_bh(&adapter->mac_filter_list_lock);
+
+	iecm_add_del_ether_addrs(vport, true, false);
+}
+
+/**
+ * iecm_set_all_vlans - Re-add all VLANs in list
+ * @vport: main vport struct
+ *
+ * Takes vlan_list_lock spinlock.  Sets add field to true for vlan filters and
+ * resyncs vlans back to HW.
+ */
+static void iecm_set_all_vlans(struct iecm_vport *vport)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	struct iecm_vlan_filter *f;
+
+	spin_lock_bh(&adapter->vlan_list_lock);
+	list_for_each_entry(f, &adapter->config_data.vlan_filter_list, list) {
+		if (!f->remove)
+			f->add = true;
+	}
+	spin_unlock_bh(&adapter->vlan_list_lock);
+
+	/* Do both add and remove to make sure list is in sync in the case
+	 * filters were added and removed before up.
+	 */
+	adapter->dev_ops.vc_ops.add_del_vlans(vport, false);
+	adapter->dev_ops.vc_ops.add_del_vlans(vport, true);
+}
+
 /**
  * iecm_init_mac_addr - initialize mac address for vport
  * @vport: main vport structure
@@ -690,6 +738,63 @@ static int iecm_get_free_slot(void *array, int size, int curr)
 	return next;
 }
 
+/**
+ * iecm_vport_stop - Disable a vport
+ * @vport: vport to disable
+ */
+static void iecm_vport_stop(struct iecm_vport *vport)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+
+	mutex_lock(&vport->stop_mutex);
+	if (adapter->state <= __IECM_DOWN)
+		goto stop_unlock;
+
+	netif_tx_stop_all_queues(vport->netdev);
+	netif_carrier_off(vport->netdev);
+	netif_tx_disable(vport->netdev);
+
+	if (adapter->dev_ops.vc_ops.disable_vport)
+		adapter->dev_ops.vc_ops.disable_vport(vport);
+	adapter->dev_ops.vc_ops.disable_queues(vport);
+	adapter->dev_ops.vc_ops.irq_map_unmap(vport, false);
+	/* Normally we ask for queues in create_vport, but if we're changing
+	 * number of requested queues we do a delete then add instead of
+	 * deleting and reallocating the vport.
+	 */
+	if (test_and_clear_bit(__IECM_DEL_QUEUES,
+			       vport->adapter->flags))
+		iecm_send_delete_queues_msg(vport);
+
+	adapter->link_up = false;
+	iecm_vport_intr_deinit(vport);
+	iecm_vport_intr_rel(vport);
+	iecm_vport_queues_rel(vport);
+	adapter->state = __IECM_DOWN;
+
+stop_unlock:
+	mutex_unlock(&vport->stop_mutex);
+}
+
+/**
+ * iecm_stop - Disables a network interface
+ * @netdev: network interface device structure
+ *
+ * The stop entry point is called when an interface is de-activated by the OS,
+ * and the netdevice enters the DOWN state.  The hardware is still under the
+ * driver's control, but the netdev interface is disabled.
+ *
+ * Returns success only - not allowed to fail
+ */
+static int iecm_stop(struct net_device *netdev)
+{
+	struct iecm_netdev_priv *np = netdev_priv(netdev);
+
+	iecm_vport_stop(np->vport);
+
+	return 0;
+}
+
 /**
  * iecm_decfg_netdev - Unregister the netdev
  * @vport: vport for which netdev to be unregistred
@@ -714,6 +819,11 @@ static void iecm_decfg_netdev(struct iecm_vport *vport)
  */
 static void iecm_vport_rel(struct iecm_vport *vport)
 {
+	struct iecm_adapter *adapter = vport->adapter;
+
+	iecm_deinit_rss(vport);
+	if (adapter->dev_ops.vc_ops.destroy_vport)
+		adapter->dev_ops.vc_ops.destroy_vport(vport);
 	mutex_destroy(&vport->stop_mutex);
 	kfree(vport);
 }
@@ -733,6 +843,7 @@ static void iecm_vport_rel_all(struct iecm_adapter *adapter)
 		if (!adapter->vports[i])
 			continue;
 
+		iecm_vport_stop(adapter->vports[i]);
 		if (!test_bit(__IECM_HR_RESET_IN_PROG, adapter->flags))
 			iecm_decfg_netdev(adapter->vports[i]);
 		iecm_vport_rel(adapter->vports[i]);
@@ -782,6 +893,7 @@ iecm_vport_alloc(struct iecm_adapter *adapter, int vport_id)
 	vport->idx = adapter->next_vport;
 	vport->compln_clean_budget = IECM_TX_COMPLQ_CLEAN_BUDGET;
 	adapter->num_alloc_vport++;
+	adapter->dev_ops.vc_ops.vport_init(vport, vport_id);
 
 	/* Setup default MSIX irq handler for the vport */
 	vport->irq_q_handler = iecm_vport_intr_clean_queues;
@@ -845,6 +957,117 @@ static void iecm_service_task(struct work_struct *work)
 			   msecs_to_jiffies(300));
 }
 
+/**
+ * iecm_restore_vlans - Restore vlan filters/vlan stripping/insert config
+ * @vport: virtual port structure
+ */
+static void iecm_restore_vlans(struct iecm_vport *vport)
+{
+	if (iecm_is_feature_ena(vport, NETIF_F_HW_VLAN_CTAG_FILTER))
+		iecm_set_all_vlans(vport);
+}
+
+/**
+ * iecm_restore_features - Restore feature configs
+ * @vport: virtual port structure
+ */
+static void iecm_restore_features(struct iecm_vport *vport)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+
+	if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS, VIRTCHNL2_CAP_MACFILTER))
+		iecm_set_all_filters(vport);
+
+	if (iecm_is_cap_ena(adapter, IECM_BASE_CAPS, VIRTCHNL2_CAP_VLAN) ||
+	    iecm_is_cap_ena(adapter, IECM_OTHER_CAPS, VIRTCHNL2_CAP_VLAN))
+		iecm_restore_vlans(vport);
+
+	if ((iecm_is_user_flag_ena(adapter, __IECM_PROMISC_UC) ||
+	     iecm_is_user_flag_ena(adapter, __IECM_PROMISC_MC)) &&
+	    test_and_clear_bit(__IECM_VPORT_INIT_PROMISC, vport->flags)) {
+		if (iecm_set_promiscuous(adapter))
+			dev_info(&adapter->pdev->dev, "Failed to restore promiscuous settings\n");
+	}
+}
+
+/**
+ * iecm_set_real_num_queues - set number of queues for netdev
+ * @vport: virtual port structure
+ *
+ * Returns 0 on success, negative on failure.
+ */
+static int iecm_set_real_num_queues(struct iecm_vport *vport)
+{
+	int err;
+
+	/* If we're in normal up path, the stack already takes the rtnl_lock
+	 * for us, however, if we're doing up as a part of a hard reset, we'll
+	 * need to take the lock ourself before touching the netdev.
+	 */
+	if (test_bit(__IECM_HR_RESET_IN_PROG, vport->adapter->flags))
+		rtnl_lock();
+	err = netif_set_real_num_rx_queues(vport->netdev, vport->num_rxq);
+	if (err)
+		goto error;
+	err = netif_set_real_num_tx_queues(vport->netdev, vport->num_txq);
+error:
+	if (test_bit(__IECM_HR_RESET_IN_PROG, vport->adapter->flags))
+		rtnl_unlock();
+	return err;
+}
+
+/**
+ * iecm_up_complete - Complete interface up sequence
+ * @vport: virtual port strucutre
+ *
+ * Returns 0 on success, negative on failure.
+ */
+static int iecm_up_complete(struct iecm_vport *vport)
+{
+	int err;
+
+	err = iecm_set_real_num_queues(vport);
+	if (err)
+		return err;
+
+	if (vport->adapter->link_up && !netif_carrier_ok(vport->netdev)) {
+		netif_carrier_on(vport->netdev);
+		netif_tx_start_all_queues(vport->netdev);
+	}
+
+	vport->adapter->state = __IECM_UP;
+	return 0;
+}
+
+/**
+ * iecm_rx_init_buf_tail - Write initial buffer ring tail value
+ * @vport: virtual port struct
+ */
+static void iecm_rx_init_buf_tail(struct iecm_vport *vport)
+{
+	int i, j;
+
+	for (i = 0; i < vport->num_rxq_grp; i++) {
+		struct iecm_rxq_group *grp = &vport->rxq_grps[i];
+
+		if (iecm_is_queue_model_split(vport->rxq_model)) {
+			for (j = 0; j < vport->num_bufqs_per_qgrp; j++) {
+				struct iecm_queue *q =
+					&grp->splitq.bufq_sets[j].bufq;
+
+				writel(q->next_to_alloc, q->tail);
+			}
+		} else {
+			for (j = 0; j < grp->singleq.num_rxq; j++) {
+				struct iecm_queue *q =
+					grp->singleq.rxqs[j];
+
+				writel(q->next_to_alloc, q->tail);
+			}
+		}
+	}
+}
+
 /* iecm_set_vlan_offload_features - set vlan offload features
  * @netdev: netdev structure
  * @prev_features: previously set features
@@ -903,8 +1126,110 @@ iecm_set_vlan_offload_features(struct net_device *netdev,
  */
 static int iecm_vport_open(struct iecm_vport *vport, bool alloc_res)
 {
-	/* stub */
+	struct iecm_adapter *adapter = vport->adapter;
+	int err;
+
+	if (vport->adapter->state != __IECM_DOWN)
+		return -EBUSY;
+
+	/* we do not allow interface up just yet */
+	netif_carrier_off(vport->netdev);
+
+	if (alloc_res) {
+		err = iecm_vport_queues_alloc(vport);
+		if (err)
+			return err;
+	}
+
+	err = iecm_vport_intr_alloc(vport);
+	if (err) {
+		dev_err(&adapter->pdev->dev, "Call to interrupt alloc returned %d\n",
+			err);
+		goto unroll_queues_alloc;
+	}
+
+	err = adapter->dev_ops.vc_ops.vport_queue_ids_init(vport);
+	if (err) {
+		dev_err(&adapter->pdev->dev, "Call to queue ids init returned %d\n",
+			err);
+		goto unroll_intr_alloc;
+	}
+
+	err = adapter->dev_ops.vc_ops.vportq_reg_init(vport);
+	if (err) {
+		dev_err(&adapter->pdev->dev, "Call to queue reg init returned %d\n",
+			err);
+		goto unroll_intr_alloc;
+	}
+	iecm_rx_init_buf_tail(vport);
+
+	err = iecm_vport_intr_init(vport);
+	if (err) {
+		dev_err(&adapter->pdev->dev, "Call to vport interrupt init returned %d\n",
+			err);
+		goto unroll_intr_alloc;
+	}
+	err = adapter->dev_ops.vc_ops.config_queues(vport);
+	if (err) {
+		dev_err(&adapter->pdev->dev, "Failed to config queues\n");
+		goto unroll_config_queues;
+	}
+	err = adapter->dev_ops.vc_ops.irq_map_unmap(vport, true);
+	if (err) {
+		dev_err(&adapter->pdev->dev, "Call to irq_map_unmap returned %d\n",
+			err);
+		goto unroll_config_queues;
+	}
+	err = adapter->dev_ops.vc_ops.enable_queues(vport);
+	if (err) {
+		dev_err(&adapter->pdev->dev, "Failed to enable queues\n");
+		goto unroll_enable_queues;
+	}
+
+	if (adapter->dev_ops.vc_ops.enable_vport) {
+		err = adapter->dev_ops.vc_ops.enable_vport(vport);
+		if (err) {
+			dev_err(&adapter->pdev->dev, "Failed to enable vport\n");
+			err = -EAGAIN;
+			goto unroll_vport_enable;
+		}
+	}
+
+	iecm_restore_features(vport);
+
+	if (adapter->rss_data.rss_lut)
+		err = iecm_config_rss(vport);
+	else
+		err = iecm_init_rss(vport);
+	if (err) {
+		dev_err(&adapter->pdev->dev, "Failed to init RSS\n");
+		goto unroll_init_rss;
+	}
+	err = iecm_up_complete(vport);
+	if (err) {
+		dev_err(&adapter->pdev->dev, "Failed to complete up\n");
+		goto unroll_up_comp;
+	}
+
 	return 0;
+
+unroll_up_comp:
+	iecm_deinit_rss(vport);
+unroll_init_rss:
+	adapter->dev_ops.vc_ops.disable_vport(vport);
+unroll_vport_enable:
+	adapter->dev_ops.vc_ops.disable_queues(vport);
+unroll_enable_queues:
+	adapter->dev_ops.vc_ops.irq_map_unmap(vport, false);
+unroll_config_queues:
+	iecm_vport_intr_deinit(vport);
+unroll_intr_alloc:
+	iecm_vport_intr_rel(vport);
+unroll_queues_alloc:
+	if (alloc_res)
+		iecm_vport_queues_rel(vport);
+
+	return err;
 }
 
 /**
@@ -1060,6 +1385,8 @@ static int iecm_api_init(struct iecm_adapter *adapter)
  */
 static void iecm_deinit_task(struct iecm_adapter *adapter)
 {
+	int i;
+
 	set_bit(__IECM_REL_RES_IN_PROG, adapter->flags);
 	/* Wait until the init_task is done else this thread might release
 	 * the resources first and the other thread might end up in a bad state
@@ -1067,8 +1394,21 @@ static void iecm_deinit_task(struct iecm_adapter *adapter)
 	cancel_delayed_work_sync(&adapter->init_task);
 	iecm_vport_rel_all(adapter);
 
+	/* Set all bits as we dont know on which vc_state the vhnl_wq is
+	 * waiting on and wakeup the virtchnl workqueue even if it is waiting
+	 * for the response as we are going down
+	 */
+	for (i = 0; i < IECM_VC_NBITS; i++)
+		set_bit(i, adapter->vc_state);
+	wake_up(&adapter->vchnl_wq);
+
 	cancel_delayed_work_sync(&adapter->serv_task);
 	cancel_delayed_work_sync(&adapter->stats_task);
+	iecm_intr_rel(adapter);
+	/* Clear all the bits */
+	for (i = 0; i < IECM_VC_NBITS; i++)
+		clear_bit(i, adapter->vc_state);
+	clear_bit(__IECM_REL_RES_IN_PROG, adapter->flags);
 }
 
 /**
@@ -1371,6 +1711,25 @@ void iecm_remove(struct pci_dev *pdev)
 }
 EXPORT_SYMBOL(iecm_remove);
 
+/**
+ * iecm_open - Called when a network interface becomes active
+ * @netdev: network interface device structure
+ *
+ * The open entry point is called when a network interface is made
+ * active by the system (IFF_UP).  At this point all resources needed
+ * for transmit and receive operations are allocated, the interrupt
+ * handler is registered with the OS, the netdev watchdog is enabled,
+ * and the stack is notified that the interface is ready.
+ *
+ * Returns 0 on success, negative value on failure
+ */
+static int iecm_open(struct net_device *netdev)
+{
+	struct iecm_netdev_priv *np = netdev_priv(netdev);
+
+	return iecm_vport_open(np->vport, true);
+}
+
 void *iecm_alloc_dma_mem(struct iecm_hw *hw, struct iecm_dma_mem *mem, u64 size)
 {
 	struct iecm_adapter *adapter = (struct iecm_adapter *)hw->back;
@@ -1395,8 +1754,8 @@ void iecm_free_dma_mem(struct iecm_hw *hw, struct iecm_dma_mem *mem)
 }
 
 static const struct net_device_ops iecm_netdev_ops_splitq = {
-	.ndo_open = NULL,
-	.ndo_stop = NULL,
+	.ndo_open = iecm_open,
+	.ndo_stop = iecm_stop,
 	.ndo_start_xmit = NULL,
 	.ndo_set_rx_mode = NULL,
 	.ndo_validate_addr = eth_validate_addr,
@@ -1411,8 +1770,8 @@ static const struct net_device_ops iecm_netdev_ops_splitq = {
 };
 
 static const struct net_device_ops iecm_netdev_ops_singleq = {
-	.ndo_open = NULL,
-	.ndo_stop = NULL,
+	.ndo_open = iecm_open,
+	.ndo_stop = iecm_stop,
 	.ndo_start_xmit = NULL,
 	.ndo_set_rx_mode = NULL,
 	.ndo_validate_addr = eth_validate_addr,
diff --git a/drivers/net/ethernet/intel/iecm/iecm_singleq_txrx.c b/drivers/net/ethernet/intel/iecm/iecm_singleq_txrx.c
new file mode 100644
index 000000000000..d6c47cb84249
--- /dev/null
+++ b/drivers/net/ethernet/intel/iecm/iecm_singleq_txrx.c
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (C) 2019 Intel Corporation */
+
+#include "iecm.h"
+
+/**
+ * iecm_rx_singleq_buf_hw_alloc_all - Replace used receive buffers
+ * @rx_q: queue for which the hw buffers are allocated
+ * @cleaned_count: number of buffers to replace
+ *
+ * Returns false if all allocations were successful, true if any fail
+ */
+bool iecm_rx_singleq_buf_hw_alloc_all(struct iecm_queue *rx_q,
+				      u16 cleaned_count)
+{
+	/* stub */
+	return true;
+}
+
+/**
+ * iecm_vport_singleq_napi_poll - NAPI handler
+ * @napi: struct from which you get q_vector
+ * @budget: budget provided by stack
+ */
+int iecm_vport_singleq_napi_poll(struct napi_struct *napi, int budget)
+{
+	/* stub */
+	return 0;
+}
diff --git a/drivers/net/ethernet/intel/iecm/iecm_txrx.c b/drivers/net/ethernet/intel/iecm/iecm_txrx.c
index bb7f5830cffb..85e88a30370d 100644
--- a/drivers/net/ethernet/intel/iecm/iecm_txrx.c
+++ b/drivers/net/ethernet/intel/iecm/iecm_txrx.c
@@ -219,20 +219,318 @@ const struct iecm_rx_ptype_decoded iecm_ptype_lookup[IECM_RX_MAX_PTYPE] = {
 EXPORT_SYMBOL(iecm_ptype_lookup);
 
 /**
- * iecm_vport_intr_clean_queues - MSIX mode Interrupt Handler
- * @irq: interrupt number
- * @data: pointer to a q_vector
+ * iecm_tx_buf_rel - Release a Tx buffer
+ * @tx_q: the queue that owns the buffer
+ * @tx_buf: the buffer to free
+ */
+void iecm_tx_buf_rel(struct iecm_queue *tx_q, struct iecm_tx_buf *tx_buf)
+{
+	if (tx_buf->skb) {
+		if (dma_unmap_len(tx_buf, len))
+			dma_unmap_single(tx_q->dev,
+					 dma_unmap_addr(tx_buf, dma),
+					 dma_unmap_len(tx_buf, len),
+					 DMA_TO_DEVICE);
+		dev_kfree_skb_any(tx_buf->skb);
+	} else if (dma_unmap_len(tx_buf, len)) {
+		dma_unmap_page(tx_q->dev,
+			       dma_unmap_addr(tx_buf, dma),
+			       dma_unmap_len(tx_buf, len),
+			       DMA_TO_DEVICE);
+	}
+
+	tx_buf->next_to_watch = NULL;
+	tx_buf->skb = NULL;
+	dma_unmap_len_set(tx_buf, len, 0);
+}
+
+/**
+ * iecm_tx_buf_rel_all - Free any empty Tx buffers
+ * @txq: queue to be cleaned
+ */
+static void iecm_tx_buf_rel_all(struct iecm_queue *txq)
+{
+	u16 i;
+
+	/* Buffers already cleared, nothing to do */
+	if (!txq->tx_buf)
+		return;
+
+	/* Free all the Tx buffer sk_buffs */
+	for (i = 0; i < txq->desc_count; i++)
+		iecm_tx_buf_rel(txq, &txq->tx_buf[i]);
+
+	kfree(txq->tx_buf);
+	txq->tx_buf = NULL;
+
+	if (txq->buf_stack.bufs) {
+		for (i = 0; i < txq->buf_stack.size; i++) {
+			iecm_tx_buf_rel(txq, txq->buf_stack.bufs[i]);
+			kfree(txq->buf_stack.bufs[i]);
+		}
+		kfree(txq->buf_stack.bufs);
+		txq->buf_stack.bufs = NULL;
+	}
+}
+
+/**
+ * iecm_tx_desc_rel - Free Tx resources per queue
+ * @txq: Tx descriptor ring for a specific queue
+ * @bufq: buffer q or completion q
  *
+ * Free all transmit software resources
  */
-irqreturn_t
-iecm_vport_intr_clean_queues(int __always_unused irq, void *data)
+static void iecm_tx_desc_rel(struct iecm_queue *txq, bool bufq)
 {
-	struct iecm_q_vector *q_vector = (struct iecm_q_vector *)data;
+	if (bufq) {
+		iecm_tx_buf_rel_all(txq);
+		netdev_tx_reset_queue(netdev_get_tx_queue(txq->vport->netdev,
+							  txq->idx));
+	}
 
-	q_vector->total_events++;
-	napi_schedule(&q_vector->napi);
+	if (txq->desc_ring) {
+		dmam_free_coherent(txq->dev, txq->size,
+				   txq->desc_ring, txq->dma);
+		txq->desc_ring = NULL;
+		txq->next_to_alloc = 0;
+		txq->next_to_use = 0;
+		txq->next_to_clean = 0;
+	}
+}
 
-	return IRQ_HANDLED;
+/**
+ * iecm_tx_desc_rel_all - Free Tx Resources for All Queues
+ * @vport: virtual port structure
+ *
+ * Free all transmit software resources
+ */
+static void iecm_tx_desc_rel_all(struct iecm_vport *vport)
+{
+	int i, j;
+
+	if (!vport->txq_grps)
+		return;
+
+	for (i = 0; i < vport->num_txq_grp; i++) {
+		struct iecm_txq_group *txq_grp = &vport->txq_grps[i];
+
+		for (j = 0; j < txq_grp->num_txq; j++)
+			iecm_tx_desc_rel(txq_grp->txqs[j], true);
+		if (iecm_is_queue_model_split(vport->txq_model))
+			iecm_tx_desc_rel(txq_grp->complq, false);
+	}
+}
+
+/**
+ * iecm_tx_buf_alloc_all - Allocate memory for all buffer resources
+ * @tx_q: queue for which the buffers are allocated
+ *
+ * Returns 0 on success, negative on failure
+ */
+static int iecm_tx_buf_alloc_all(struct iecm_queue *tx_q)
+{
+	int buf_size;
+	int i = 0;
+
+	/* Allocate book keeping buffers only. Buffers to be supplied to HW
+	 * are allocated by kernel network stack and received as part of skb
+	 */
+	buf_size = sizeof(struct iecm_tx_buf) * tx_q->desc_count;
+	tx_q->tx_buf = kzalloc(buf_size, GFP_KERNEL);
+	if (!tx_q->tx_buf)
+		return -ENOMEM;
+
+	/* Initialize tx buf stack for out-of-order completions if
+	 * flow scheduling offload is enabled
+	 */
+	tx_q->buf_stack.bufs =
+		kcalloc(tx_q->desc_count, sizeof(struct iecm_tx_buf *),
+			GFP_KERNEL);
+	if (!tx_q->buf_stack.bufs)
+		return -ENOMEM;
+
+	tx_q->buf_stack.size = tx_q->desc_count;
+	tx_q->buf_stack.top = tx_q->desc_count;
+
+	for (i = 0; i < tx_q->desc_count; i++) {
+		tx_q->buf_stack.bufs[i] = kzalloc(sizeof(*tx_q->buf_stack.bufs[i]),
+						  GFP_KERNEL);
+		if (!tx_q->buf_stack.bufs[i])
+			return -ENOMEM;
+	}
+
+	return 0;
+}
+
+/**
+ * iecm_tx_desc_alloc - Allocate the Tx descriptors
+ * @tx_q: the tx ring to set up
+ * @bufq: buffer or completion queue
+ *
+ * Returns 0 on success, negative on failure
+ */
+static int iecm_tx_desc_alloc(struct iecm_queue *tx_q, bool bufq)
+{
+	struct device *dev = tx_q->dev;
+	int err = 0;
+
+	if (bufq) {
+		err = iecm_tx_buf_alloc_all(tx_q);
+		if (err)
+			goto err_alloc;
+		tx_q->size = tx_q->desc_count *
+				sizeof(struct iecm_base_tx_desc);
+	} else {
+		tx_q->size = tx_q->desc_count *
+				sizeof(struct iecm_splitq_tx_compl_desc);
+	}
+
+	/* Allocate descriptors also round up to nearest 4K */
+	tx_q->size = ALIGN(tx_q->size, 4096);
+	tx_q->desc_ring = dmam_alloc_coherent(dev, tx_q->size, &tx_q->dma,
+					      GFP_KERNEL);
+	if (!tx_q->desc_ring) {
+		dev_info(dev, "Unable to allocate memory for the Tx descriptor ring, size=%d\n",
+			 tx_q->size);
+		err = -ENOMEM;
+		goto err_alloc;
+	}
+
+	tx_q->next_to_alloc = 0;
+	tx_q->next_to_use = 0;
+	tx_q->next_to_clean = 0;
+	set_bit(__IECM_Q_GEN_CHK, tx_q->flags);
+
+err_alloc:
+	if (err)
+		iecm_tx_desc_rel(tx_q, bufq);
+	return err;
+}
+
+/**
+ * iecm_tx_desc_alloc_all - allocate all queues Tx resources
+ * @vport: virtual port private structure
+ *
+ * Returns 0 on success, negative on failure
+ */
+static int iecm_tx_desc_alloc_all(struct iecm_vport *vport)
+{
+	struct pci_dev *pdev = vport->adapter->pdev;
+	int err = 0;
+	int i, j;
+
+	/* Setup buffer queues. In single queue model buffer queues and
+	 * completion queues will be same
+	 */
+	for (i = 0; i < vport->num_txq_grp; i++) {
+		for (j = 0; j < vport->txq_grps[i].num_txq; j++) {
+			err = iecm_tx_desc_alloc(vport->txq_grps[i].txqs[j],
+						 true);
+			if (err) {
+				dev_err(&pdev->dev,
+					"Allocation for Tx Queue %u failed\n",
+					i);
+				goto err_out;
+			}
+		}
+
+		if (iecm_is_queue_model_split(vport->txq_model)) {
+			/* Setup completion queues */
+			err = iecm_tx_desc_alloc(vport->txq_grps[i].complq,
+						 false);
+			if (err) {
+				dev_err(&pdev->dev,
+					"Allocation for Tx Completion Queue %u failed\n",
+					i);
+				goto err_out;
+			}
+		}
+	}
+err_out:
+	if (err)
+		iecm_tx_desc_rel_all(vport);
+	return err;
+}
+
+/**
+ * iecm_txq_group_rel - Release all resources for txq groups
+ * @vport: vport to release txq groups on
+ */
+static void iecm_txq_group_rel(struct iecm_vport *vport)
+{
+	struct iecm_txq_group *txq_grp;
+	int i, j, num_txq;
+
+	if (vport->txq_grps) {
+		for (i = 0; i < vport->num_txq_grp; i++) {
+			txq_grp = &vport->txq_grps[i];
+			num_txq = txq_grp->num_txq;
+
+			for (j = 0; j < num_txq; j++) {
+				kfree(txq_grp->txqs[j]);
+				txq_grp->txqs[j] = NULL;
+			}
+			kfree(txq_grp->complq);
+			txq_grp->complq = NULL;
+		}
+		kfree(vport->txq_grps);
+		vport->txq_grps = NULL;
+	}
+}
+
+/**
+ * iecm_vport_queue_grp_rel_all - Release all queue groups
+ * @vport: vport to release queue groups for
+ */
+static void iecm_vport_queue_grp_rel_all(struct iecm_vport *vport)
+{
+	iecm_txq_group_rel(vport);
+}
+
+/**
+ * iecm_vport_queues_rel - Free memory for all queues
+ * @vport: virtual port
+ *
+ * Free the memory allocated for queues associated to a vport
+ */
+void iecm_vport_queues_rel(struct iecm_vport *vport)
+{
+	iecm_tx_desc_rel_all(vport);
+	iecm_vport_queue_grp_rel_all(vport);
+
+	kfree(vport->txqs);
+	vport->txqs = NULL;
+}
+
+/**
+ * iecm_vport_init_fast_path_txqs - Initialize fast path txq array
+ * @vport: vport to init txqs on
+ *
+ * We get a queue index from skb->queue_mapping and we need a fast way to
+ * dereference the queue from queue groups.  This allows us to quickly pull a
+ * txq based on a queue index.
+ *
+ * Returns 0 on success, negative on failure
+ */
+static int iecm_vport_init_fast_path_txqs(struct iecm_vport *vport)
+{
+	int i, j, k = 0;
+
+	vport->txqs = kcalloc(vport->num_txq, sizeof(struct iecm_queue *),
+			      GFP_KERNEL);
+
+	if (!vport->txqs)
+		return -ENOMEM;
+
+	for (i = 0; i < vport->num_txq_grp; i++) {
+		struct iecm_txq_group *tx_grp = &vport->txq_grps[i];
+
+		for (j = 0; j < tx_grp->num_txq; j++, k++) {
+			vport->txqs[k] = tx_grp->txqs[j];
+			vport->txqs[k]->idx = k;
+		}
+	}
+	return 0;
 }
 
 /**
@@ -382,6 +680,26 @@ void iecm_vport_calc_num_q_groups(struct iecm_vport *vport)
 }
 EXPORT_SYMBOL(iecm_vport_calc_num_q_groups);
 
+/**
+ * iecm_vport_calc_numq_per_grp - Calculate number of queues per group
+ * @vport: vport to calculate queues for
+ * @num_txq: int return parameter
+ * @num_rxq: int return parameter
+ */
+static void iecm_vport_calc_numq_per_grp(struct iecm_vport *vport,
+					 int *num_txq, int *num_rxq)
+{
+	if (iecm_is_queue_model_split(vport->txq_model))
+		*num_txq = IECM_DFLT_SPLITQ_TXQ_PER_GROUP;
+	else
+		*num_txq = vport->num_txq;
+
+	if (iecm_is_queue_model_split(vport->rxq_model))
+		*num_rxq = IECM_DFLT_SPLITQ_RXQ_PER_GROUP;
+	else
+		*num_rxq = vport->num_rxq;
+}
+
 /**
  * iecm_vport_calc_num_q_vec - Calculate total number of vectors required for
  * this vport
@@ -396,3 +714,949 @@ void iecm_vport_calc_num_q_vec(struct iecm_vport *vport)
 		vport->num_q_vectors = vport->num_txq;
 }
 EXPORT_SYMBOL(iecm_vport_calc_num_q_vec);
+
+/**
+ * iecm_set_vlan_tag_loc - set the tag location for a tx/rx queue
+ * @adapter: adapter structure
+ * @q: tx/rx queue to set tag location for
+ *
+ */
+static void iecm_set_vlan_tag_loc(struct iecm_adapter *adapter,
+				  struct iecm_queue *q)
+{
+	if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS, VIRTCHNL2_CAP_VLAN)) {
+		struct virtchnl_vlan_supported_caps *insertion_support;
+
+		insertion_support =
+				&adapter->vlan_caps->offloads.insertion_support;
+		if (insertion_support->outer) {
+			if (insertion_support->outer &
+			    VIRTCHNL_VLAN_TAG_LOCATION_L2TAG1)
+				set_bit(__IECM_Q_VLAN_TAG_LOC_L2TAG1,
+					q->flags);
+			else if (insertion_support->outer &
+				 VIRTCHNL_VLAN_TAG_LOCATION_L2TAG2)
+				set_bit(__IECM_Q_VLAN_TAG_LOC_L2TAG2,
+					q->flags);
+		} else if (insertion_support->inner) {
+			if (insertion_support->inner &
+			    VIRTCHNL_VLAN_TAG_LOCATION_L2TAG1)
+				set_bit(__IECM_Q_VLAN_TAG_LOC_L2TAG1,
+					q->flags);
+			else if (insertion_support->inner &
+				 VIRTCHNL_VLAN_TAG_LOCATION_L2TAG2)
+				set_bit(__IECM_Q_VLAN_TAG_LOC_L2TAG2,
+					q->flags);
+		}
+	} else if (iecm_is_cap_ena(adapter, IECM_BASE_CAPS,
+				   VIRTCHNL2_CAP_VLAN)) {
+		set_bit(__IECM_Q_VLAN_TAG_LOC_L2TAG1, q->flags);
+	}
+}
+
+/**
+ * iecm_txq_group_alloc - Allocate all txq group resources
+ * @vport: vport to allocate txq groups for
+ * @num_txq: number of txqs to allocate for each group
+ *
+ * Returns 0 on success, negative on failure
+ */
+static int iecm_txq_group_alloc(struct iecm_vport *vport, int num_txq)
+{
+	int err = 0, i;
+
+	vport->txq_grps = kcalloc(vport->num_txq_grp,
+				  sizeof(*vport->txq_grps), GFP_KERNEL);
+	if (!vport->txq_grps)
+		return -ENOMEM;
+
+	for (i = 0; i < vport->num_txq_grp; i++) {
+		struct iecm_txq_group *tx_qgrp = &vport->txq_grps[i];
+		int j;
+
+		tx_qgrp->vport = vport;
+		tx_qgrp->num_txq = num_txq;
+
+		for (j = 0; j < tx_qgrp->num_txq; j++) {
+			tx_qgrp->txqs[j] = kzalloc(sizeof(*tx_qgrp->txqs[j]),
+						   GFP_KERNEL);
+			if (!tx_qgrp->txqs[j]) {
+				err = -ENOMEM;
+				goto err_alloc;
+			}
+		}
+
+		for (j = 0; j < tx_qgrp->num_txq; j++) {
+			struct iecm_queue *q = tx_qgrp->txqs[j];
+
+			q->dev = &vport->adapter->pdev->dev;
+			q->desc_count = vport->txq_desc_count;
+			q->tx_max_bufs =
+				vport->adapter->dev_ops.vc_ops.get_max_tx_bufs(vport->adapter);
+			q->vport = vport;
+			q->txq_grp = tx_qgrp;
+			hash_init(q->sched_buf_hash);
+
+			if (!iecm_is_cap_ena(vport->adapter,
+					     IECM_OTHER_CAPS,
+					     VIRTCHNL2_CAP_SPLITQ_QSCHED))
+				set_bit(__IECM_Q_FLOW_SCH_EN, q->flags);
+			iecm_set_vlan_tag_loc(vport->adapter, q);
+		}
+
+		if (!iecm_is_queue_model_split(vport->txq_model))
+			continue;
+
+		tx_qgrp->complq = kcalloc(IECM_COMPLQ_PER_GROUP,
+					  sizeof(*tx_qgrp->complq),
+					  GFP_KERNEL);
+		if (!tx_qgrp->complq) {
+			err = -ENOMEM;
+			goto err_alloc;
+		}
+
+		tx_qgrp->complq->dev = &vport->adapter->pdev->dev;
+		tx_qgrp->complq->desc_count = vport->complq_desc_count;
+		tx_qgrp->complq->vport = vport;
+		tx_qgrp->complq->txq_grp = tx_qgrp;
+	}
+
+err_alloc:
+	if (err)
+		iecm_txq_group_rel(vport);
+	return err;
+}
+
+/**
+ * iecm_vport_queue_grp_alloc_all - Allocate all queue groups/resources
+ * @vport: vport with qgrps to allocate
+ *
+ * Returns 0 on success, negative on failure
+ */
+static int iecm_vport_queue_grp_alloc_all(struct iecm_vport *vport)
+{
+	int num_txq, num_rxq;
+	int err;
+
+	iecm_vport_calc_numq_per_grp(vport, &num_txq, &num_rxq);
+
+	err = iecm_txq_group_alloc(vport, num_txq);
+	if (err)
+		iecm_vport_queue_grp_rel_all(vport);
+	return err;
+}
+
+/**
+ * iecm_vport_queues_alloc - Allocate memory for all queues
+ * @vport: virtual port
+ *
+ * Allocate memory for queues associated with a vport.  Returns 0 on success,
+ * negative on failure.
+ */
+int iecm_vport_queues_alloc(struct iecm_vport *vport)
+{
+	int err;
+	int i;
+
+	err = iecm_vport_queue_grp_alloc_all(vport);
+	if (err)
+		goto err_out;
+
+	err = iecm_tx_desc_alloc_all(vport);
+	if (err)
+		goto err_out;
+
+	err = iecm_vport_init_fast_path_txqs(vport);
+	if (err)
+		goto err_out;
+
+	/* Initialize flow scheduling for queues that were requested
+	 * before the interface was brought up
+	 */
+	for (i = 0; i < vport->num_txq; i++) {
+		if (test_bit(i, vport->adapter->config_data.etf_qenable)) {
+			set_bit(__IECM_Q_FLOW_SCH_EN, vport->txqs[i]->flags);
+			set_bit(__IECM_Q_ETF_EN, vport->txqs[i]->flags);
+		}
+	}
+
+	return 0;
+err_out:
+	iecm_vport_queues_rel(vport);
+	return err;
+}
+
+/**
+ * iecm_vport_intr_clean_queues - MSIX mode Interrupt Handler
+ * @irq: interrupt number
+ * @data: pointer to a q_vector
+ *
+ */
+irqreturn_t
+iecm_vport_intr_clean_queues(int __always_unused irq, void *data)
+{
+	struct iecm_q_vector *q_vector = (struct iecm_q_vector *)data;
+
+	q_vector->total_events++;
+	napi_schedule(&q_vector->napi);
+
+	return IRQ_HANDLED;
+}
+
+/**
+ * iecm_vport_intr_napi_del_all - Unregister napi for all q_vectors in vport
+ * @vport: virtual port structure
+ *
+ */
+static void iecm_vport_intr_napi_del_all(struct iecm_vport *vport)
+{
+	u16 v_idx;
+
+	for (v_idx = 0; v_idx < vport->num_q_vectors; v_idx++) {
+		struct iecm_q_vector *q_vector = &vport->q_vectors[v_idx];
+
+		netif_napi_del(&q_vector->napi);
+	}
+}
+
+/**
+ * iecm_vport_intr_napi_dis_all - Disable NAPI for all q_vectors in the vport
+ * @vport: main vport structure
+ */
+static void iecm_vport_intr_napi_dis_all(struct iecm_vport *vport)
+{
+	int q_idx;
+
+	if (!vport->netdev)
+		return;
+
+	for (q_idx = 0; q_idx < vport->num_q_vectors; q_idx++) {
+		struct iecm_q_vector *q_vector = &vport->q_vectors[q_idx];
+
+		napi_disable(&q_vector->napi);
+	}
+}
+
+/**
+ * iecm_vport_intr_rel - Free memory allocated for interrupt vectors
+ * @vport: virtual port
+ *
+ * Free the memory allocated for interrupt vectors  associated to a vport
+ */
+void iecm_vport_intr_rel(struct iecm_vport *vport)
+{
+	int i, j, v_idx;
+
+	if (!vport->netdev)
+		return;
+
+	for (v_idx = 0; v_idx < vport->num_q_vectors; v_idx++) {
+		struct iecm_q_vector *q_vector = &vport->q_vectors[v_idx];
+
+		kfree(q_vector->bufq);
+		q_vector->bufq = NULL;
+		kfree(q_vector->tx);
+		q_vector->tx = NULL;
+		kfree(q_vector->rx);
+		q_vector->rx = NULL;
+	}
+
+	/* Clean up the mapping of queues to vectors */
+	for (i = 0; i < vport->num_rxq_grp; i++) {
+		struct iecm_rxq_group *rx_qgrp = &vport->rxq_grps[i];
+
+		if (iecm_is_queue_model_split(vport->rxq_model)) {
+			for (j = 0; j < rx_qgrp->splitq.num_rxq_sets; j++)
+				rx_qgrp->splitq.rxq_sets[j]->rxq.q_vector =
+									   NULL;
+		} else {
+			for (j = 0; j < rx_qgrp->singleq.num_rxq; j++)
+				rx_qgrp->singleq.rxqs[j]->q_vector = NULL;
+		}
+	}
+
+	if (iecm_is_queue_model_split(vport->txq_model)) {
+		for (i = 0; i < vport->num_txq_grp; i++)
+			vport->txq_grps[i].complq->q_vector = NULL;
+	} else {
+		for (i = 0; i < vport->num_txq_grp; i++) {
+			for (j = 0; j < vport->txq_grps[i].num_txq; j++)
+				vport->txq_grps[i].txqs[j]->q_vector = NULL;
+		}
+	}
+
+	kfree(vport->q_vectors);
+	vport->q_vectors = NULL;
+}
+
+/**
+ * iecm_vport_intr_rel_irq - Free the IRQ association with the OS
+ * @vport: main vport structure
+ */
+static void iecm_vport_intr_rel_irq(struct iecm_vport *vport)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	int vector;
+
+	for (vector = 0; vector < vport->num_q_vectors; vector++) {
+		struct iecm_q_vector *q_vector = &vport->q_vectors[vector];
+		int irq_num, vidx;
+
+		/* free only the irqs that were actually requested */
+		if (!q_vector)
+			continue;
+
+		vidx = vector + vport->q_vector_base;
+		irq_num = adapter->msix_entries[vidx].vector;
+
+		/* clear the affinity_mask in the IRQ descriptor */
+		irq_set_affinity_hint(irq_num, NULL);
+		free_irq(irq_num, q_vector);
+	}
+}
+
+/**
+ * iecm_vport_intr_dis_irq_all - Disable each interrupt
+ * @vport: main vport structure
+ */
+void iecm_vport_intr_dis_irq_all(struct iecm_vport *vport)
+{
+	struct iecm_q_vector *q_vector = vport->q_vectors;
+	struct iecm_hw *hw = &vport->adapter->hw;
+	int q_idx;
+
+	for (q_idx = 0; q_idx < vport->num_q_vectors; q_idx++)
+		wr32(hw, q_vector[q_idx].intr_reg.dyn_ctl, 0);
+}
+
+/**
+ * iecm_vport_intr_buildreg_itr - Enable default interrupt generation settings
+ * @q_vector: pointer to q_vector
+ * @type: itr index
+ * @itr: itr value
+ */
+static u32 iecm_vport_intr_buildreg_itr(struct iecm_q_vector *q_vector,
+					const int type, u16 itr)
+{
+	u32 itr_val;
+
+	itr &= IECM_ITR_MASK;
+	/* Don't clear PBA because that can cause lost interrupts that
+	 * came in while we were cleaning/polling
+	 */
+	itr_val = q_vector->intr_reg.dyn_ctl_intena_m |
+		  (type << q_vector->intr_reg.dyn_ctl_itridx_s) |
+		  (itr << (q_vector->intr_reg.dyn_ctl_intrvl_s - 1));
+
+	return itr_val;
+}
+
+/**
+ * iecm_net_dim - Update net DIM algorithm
+ * @q_vector: the vector associated with the interrupt
+ *
+ * Create a DIM sample and notify net_dim() so that it can possibly decide
+ * a new ITR value based on incoming packets, bytes, and interrupts.
+ *
+ * This function is a no-op if the queue is not configured to dynamic ITR.
+ */
+static void iecm_net_dim(struct iecm_q_vector *q_vector)
+{
+	if (IECM_ITR_IS_DYNAMIC(q_vector->tx_intr_mode)) {
+		struct dim_sample dim_sample = {};
+		u64 packets = 0, bytes = 0;
+		int i;
+
+		for (i = 0; i < q_vector->num_txq; i++) {
+			packets += q_vector->tx[i]->q_stats.tx.packets;
+			bytes += q_vector->tx[i]->q_stats.tx.bytes;
+		}
+
+		dim_update_sample(q_vector->total_events, packets, bytes,
+				  &dim_sample);
+		net_dim(&q_vector->tx_dim, dim_sample);
+	}
+
+	if (IECM_ITR_IS_DYNAMIC(q_vector->rx_intr_mode)) {
+		struct dim_sample dim_sample = {};
+		u64 packets = 0, bytes = 0;
+		int i;
+
+		for (i = 0; i < q_vector->num_rxq; i++) {
+			packets += q_vector->rx[i]->q_stats.rx.packets;
+			bytes += q_vector->rx[i]->q_stats.rx.bytes;
+		}
+
+		dim_update_sample(q_vector->total_events, packets, bytes,
+				  &dim_sample);
+		net_dim(&q_vector->rx_dim, dim_sample);
+	}
+}
+
+/**
+ * iecm_vport_intr_update_itr_ena_irq - Update itr and re-enable MSIX interrupt
+ * @q_vector: q_vector for which itr is being updated and interrupt enabled
+ *
+ * Update the net_dim() algorithm and re-enable the interrupt associated with
+ * this vector.
+ */
+void iecm_vport_intr_update_itr_ena_irq(struct iecm_q_vector *q_vector)
+{
+	struct iecm_hw *hw = &q_vector->vport->adapter->hw;
+	u32 intval;
+
+	/* net_dim() updates ITR out-of-band using a work item */
+	iecm_net_dim(q_vector);
+
+	intval = iecm_vport_intr_buildreg_itr(q_vector,
+					      VIRTCHNL2_ITR_IDX_NO_ITR, 0);
+
+	wr32(hw, q_vector->intr_reg.dyn_ctl, intval);
+}
+
+/**
+ * iecm_vport_intr_req_irq - get MSI-X vectors from the OS for the vport
+ * @vport: main vport structure
+ * @basename: name for the vector
+ */
+static int
+iecm_vport_intr_req_irq(struct iecm_vport *vport, char *basename)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	int vector, err, irq_num, vidx;
+
+	for (vector = 0; vector < vport->num_q_vectors; vector++) {
+		struct iecm_q_vector *q_vector = &vport->q_vectors[vector];
+
+		vidx = vector + vport->q_vector_base;
+		irq_num = adapter->msix_entries[vidx].vector;
+
+		snprintf(q_vector->name, sizeof(q_vector->name) - 1,
+			 "%s-%s-%d", basename, "TxRx", vidx);
+
+		err = request_irq(irq_num, vport->irq_q_handler, 0,
+				  q_vector->name, q_vector);
+		if (err) {
+			netdev_err(vport->netdev,
+				   "Request_irq failed, error: %d\n", err);
+			goto free_q_irqs;
+		}
+		/* assign the mask for this irq */
+		irq_set_affinity_hint(irq_num, &q_vector->affinity_mask);
+	}
+
+	return 0;
+
+free_q_irqs:
+	while (vector) {
+		vector--;
+		vidx = vector + vport->q_vector_base;
+		irq_num = adapter->msix_entries[vidx].vector;
+		free_irq(irq_num, &vport->q_vectors[vector]);
+	}
+	return err;
+}
+
+/**
+ * iecm_vport_intr_write_itr - Write ITR value to the ITR register
+ * @q_vector: q_vector structure
+ * @itr: Interrupt throttling rate
+ * @tx: Tx or Rx ITR
+ */
+void iecm_vport_intr_write_itr(struct iecm_q_vector *q_vector, u16 itr, bool tx)
+{
+	struct iecm_hw *hw = &q_vector->vport->adapter->hw;
+	struct iecm_intr_reg *intr_reg;
+
+	if (tx && !q_vector->tx)
+		return;
+	else if (!tx && !q_vector->rx)
+		return;
+
+	intr_reg = &q_vector->intr_reg;
+	wr32(hw, tx ? intr_reg->tx_itr : intr_reg->rx_itr,
+	     ITR_REG_ALIGN(itr) >> IECM_ITR_GRAN_S);
+}
+
+/**
+ * iecm_vport_intr_ena_irq_all - Enable IRQ for the given vport
+ * @vport: main vport structure
+ */
+void iecm_vport_intr_ena_irq_all(struct iecm_vport *vport)
+{
+	int q_idx;
+
+	for (q_idx = 0; q_idx < vport->num_q_vectors; q_idx++) {
+		struct iecm_q_vector *q_vector = &vport->q_vectors[q_idx];
+
+		if (q_vector->num_txq || q_vector->num_rxq) {
+			/* Write the default ITR values */
+			iecm_vport_intr_write_itr(q_vector,
+						  q_vector->rx_itr_value,
+						  false);
+			iecm_vport_intr_write_itr(q_vector,
+						  q_vector->tx_itr_value,
+						  true);
+			iecm_vport_intr_update_itr_ena_irq(q_vector);
+		}
+	}
+}
+
+/**
+ * iecm_vport_intr_deinit - Release all vector associations for the vport
+ * @vport: main vport structure
+ */
+void iecm_vport_intr_deinit(struct iecm_vport *vport)
+{
+	iecm_vport_intr_napi_dis_all(vport);
+	iecm_vport_intr_napi_del_all(vport);
+	iecm_vport_intr_dis_irq_all(vport);
+	iecm_vport_intr_rel_irq(vport);
+}
+
+/**
+ * iecm_tx_dim_work - Call back from the stack
+ * @work: work queue structure
+ */
+static void iecm_tx_dim_work(struct work_struct *work)
+{
+	struct iecm_q_vector *q_vector;
+	struct iecm_vport *vport;
+	struct dim *dim;
+	u16 itr;
+
+	dim = container_of(work, struct dim, work);
+	q_vector = container_of(dim, struct iecm_q_vector, tx_dim);
+	vport = q_vector->vport;
+
+	if (dim->profile_ix >= ARRAY_SIZE(vport->tx_itr_profile))
+		dim->profile_ix = ARRAY_SIZE(vport->tx_itr_profile) - 1;
+
+	/* look up the values in our local table */
+	itr = vport->tx_itr_profile[dim->profile_ix];
+
+	iecm_vport_intr_write_itr(q_vector, itr, true);
+
+	dim->state = DIM_START_MEASURE;
+}
+
+/**
+ * iecm_rx_dim_work - Call back from the stack
+ * @work: work queue structure
+ */
+static void iecm_rx_dim_work(struct work_struct *work)
+{
+	struct iecm_q_vector *q_vector;
+	struct iecm_vport *vport;
+	struct dim *dim;
+	u16 itr;
+
+	dim = container_of(work, struct dim, work);
+	q_vector = container_of(dim, struct iecm_q_vector, rx_dim);
+	vport = q_vector->vport;
+
+	if (dim->profile_ix >= ARRAY_SIZE(vport->rx_itr_profile))
+		dim->profile_ix = ARRAY_SIZE(vport->rx_itr_profile) - 1;
+
+	/* look up the values in our local table */
+	itr = vport->rx_itr_profile[dim->profile_ix];
+
+	iecm_vport_intr_write_itr(q_vector, itr, false);
+
+	dim->state = DIM_START_MEASURE;
+}
+
+/**
+ * iecm_vport_intr_napi_ena_all - Enable NAPI for all q_vectors in the vport
+ * @vport: main vport structure
+ */
+static void
+iecm_vport_intr_napi_ena_all(struct iecm_vport *vport)
+{
+	int q_idx;
+
+	if (!vport->netdev)
+		return;
+
+	for (q_idx = 0; q_idx < vport->num_q_vectors; q_idx++) {
+		struct iecm_q_vector *q_vector = &vport->q_vectors[q_idx];
+
+		INIT_WORK(&q_vector->tx_dim.work, iecm_tx_dim_work);
+		q_vector->tx_dim.mode = DIM_CQ_PERIOD_MODE_START_FROM_EQE;
+
+		INIT_WORK(&q_vector->rx_dim.work, iecm_rx_dim_work);
+		q_vector->rx_dim.mode = DIM_CQ_PERIOD_MODE_START_FROM_EQE;
+
+		napi_enable(&q_vector->napi);
+	}
+}
+
+/**
+ * iecm_vport_splitq_napi_poll - NAPI handler
+ * @napi: struct from which you get q_vector
+ * @budget: budget provided by stack
+ */
+static int iecm_vport_splitq_napi_poll(struct napi_struct *napi, int budget)
+{
+	/* stub */
+	return 0;
+}
+
+/**
+ * iecm_vport_intr_map_vector_to_qs - Map vectors to queues
+ * @vport: virtual port
+ *
+ * Mapping for vectors to queues
+ */
+static void iecm_vport_intr_map_vector_to_qs(struct iecm_vport *vport)
+{
+	int num_txq_grp = vport->num_txq_grp, bufq_vidx = 0;
+	int i, j, qv_idx = 0, num_rxq, num_txq, q_index;
+	struct iecm_rxq_group *rx_qgrp;
+	struct iecm_txq_group *tx_qgrp;
+	struct iecm_queue *q, *bufq;
+
+	for (i = 0; i < vport->num_rxq_grp; i++) {
+		rx_qgrp = &vport->rxq_grps[i];
+		if (iecm_is_queue_model_split(vport->rxq_model))
+			num_rxq = rx_qgrp->splitq.num_rxq_sets;
+		else
+			num_rxq = rx_qgrp->singleq.num_rxq;
+
+		for (j = 0; j < num_rxq; j++) {
+			if (qv_idx >= vport->num_q_vectors)
+				qv_idx = 0;
+
+			if (iecm_is_queue_model_split(vport->rxq_model))
+				q = &rx_qgrp->splitq.rxq_sets[j]->rxq;
+			else
+				q = rx_qgrp->singleq.rxqs[j];
+			q->q_vector = &vport->q_vectors[qv_idx];
+			q_index = q->q_vector->num_rxq;
+			q->q_vector->rx[q_index] = q;
+			q->q_vector->num_rxq++;
+			qv_idx++;
+		}
+
+		if (iecm_is_queue_model_split(vport->rxq_model)) {
+			for (j = 0; j < vport->num_bufqs_per_qgrp; j++) {
+				bufq = &rx_qgrp->splitq.bufq_sets[j].bufq;
+				bufq->q_vector = &vport->q_vectors[bufq_vidx];
+				q_index = bufq->q_vector->num_bufq;
+				bufq->q_vector->bufq[q_index] = bufq;
+				bufq->q_vector->num_bufq++;
+			}
+			if (++bufq_vidx >= vport->num_q_vectors)
+				bufq_vidx = 0;
+		}
+	}
+	qv_idx = 0;
+	for (i = 0; i < num_txq_grp; i++) {
+		tx_qgrp = &vport->txq_grps[i];
+		num_txq = tx_qgrp->num_txq;
+
+		if (iecm_is_queue_model_split(vport->txq_model)) {
+			if (qv_idx >= vport->num_q_vectors)
+				qv_idx = 0;
+
+			q = tx_qgrp->complq;
+			q->q_vector = &vport->q_vectors[qv_idx];
+			q_index = q->q_vector->num_txq;
+			q->q_vector->tx[q_index] = q;
+			q->q_vector->num_txq++;
+			qv_idx++;
+		} else {
+			for (j = 0; j < num_txq; j++) {
+				if (qv_idx >= vport->num_q_vectors)
+					qv_idx = 0;
+
+				q = tx_qgrp->txqs[j];
+				q->q_vector = &vport->q_vectors[qv_idx];
+				q_index = q->q_vector->num_txq;
+				q->q_vector->tx[q_index] = q;
+				q->q_vector->num_txq++;
+
+				qv_idx++;
+			}
+		}
+	}
+}
+
+/**
+ * iecm_vport_intr_init_vec_idx - Initialize the vector indexes
+ * @vport: virtual port
+ *
+ * Initialize vector indexes with values returened over mailbox
+ */
+static int iecm_vport_intr_init_vec_idx(struct iecm_vport *vport)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	struct iecm_q_vector *q_vector;
+	int i;
+
+	if (adapter->req_vec_chunks) {
+		struct virtchnl2_vector_chunks *vchunks;
+		struct virtchnl2_alloc_vectors *ac;
+		u16 vecids[IECM_MAX_VECIDS];
+		int num_ids;
+
+		ac = adapter->req_vec_chunks;
+		vchunks = &ac->vchunks;
+
+		num_ids = iecm_get_vec_ids(adapter, vecids, IECM_MAX_VECIDS,
+					   vchunks);
+
+		if (num_ids < adapter->num_msix_entries)
+			return -EFAULT;
+
+		for (i = 0; i < vport->num_q_vectors; i++) {
+			q_vector = &vport->q_vectors[i];
+			q_vector->v_idx = vecids[i + vport->q_vector_base];
+		}
+	} else {
+		for (i = 0; i < vport->num_q_vectors; i++) {
+			q_vector = &vport->q_vectors[i];
+			q_vector->v_idx = i + vport->q_vector_base;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * iecm_vport_intr_napi_add_all- Register napi handler for all qvectors
+ * @vport: virtual port structure
+ */
+static void iecm_vport_intr_napi_add_all(struct iecm_vport *vport)
+{
+	u16 v_idx;
+
+	for (v_idx = 0; v_idx < vport->num_q_vectors; v_idx++) {
+		struct iecm_q_vector *q_vector = &vport->q_vectors[v_idx];
+
+		if (vport->netdev) {
+			if (iecm_is_queue_model_split(vport->txq_model))
+				netif_napi_add(vport->netdev, &q_vector->napi,
+					       iecm_vport_splitq_napi_poll,
+					       NAPI_POLL_WEIGHT);
+			else
+				netif_napi_add(vport->netdev, &q_vector->napi,
+					       iecm_vport_singleq_napi_poll,
+					       NAPI_POLL_WEIGHT);
+		}
+
+		/* only set affinity_mask if the CPU is online */
+		if (cpu_online(v_idx))
+			cpumask_set_cpu(v_idx, &q_vector->affinity_mask);
+	}
+}
+
+/**
+ * iecm_vport_intr_alloc - Allocate memory for interrupt vectors
+ * @vport: virtual port
+ *
+ * We allocate one q_vector per queue interrupt. If allocation fails we
+ * return -ENOMEM.
+ */
+int iecm_vport_intr_alloc(struct iecm_vport *vport)
+{
+	int txqs_per_vector, rxqs_per_vector, bufqs_per_vector;
+	struct iecm_q_vector *q_vector;
+	int v_idx, err;
+
+	vport->q_vectors = kcalloc(vport->num_q_vectors,
+				   sizeof(struct iecm_q_vector), GFP_KERNEL);
+
+	if (!vport->q_vectors)
+		return -ENOMEM;
+
+	txqs_per_vector = DIV_ROUND_UP(vport->num_txq, vport->num_q_vectors);
+	rxqs_per_vector = DIV_ROUND_UP(vport->num_rxq, vport->num_q_vectors);
+	bufqs_per_vector = DIV_ROUND_UP(vport->num_bufqs_per_qgrp *
+					vport->num_rxq_grp,
+					vport->num_q_vectors);
+
+	for (v_idx = 0; v_idx < vport->num_q_vectors; v_idx++) {
+		q_vector = &vport->q_vectors[v_idx];
+		q_vector->vport = vport;
+
+		q_vector->tx_itr_value = IECM_ITR_TX_DEF;
+		q_vector->tx_intr_mode = IECM_ITR_DYNAMIC;
+		q_vector->tx_itr_idx = VIRTCHNL2_ITR_IDX_1;
+
+		q_vector->rx_itr_value = IECM_ITR_RX_DEF;
+		q_vector->rx_intr_mode = IECM_ITR_DYNAMIC;
+		q_vector->rx_itr_idx = VIRTCHNL2_ITR_IDX_0;
+
+		q_vector->tx = kcalloc(txqs_per_vector,
+				       sizeof(struct iecm_queue *),
+				       GFP_KERNEL);
+		if (!q_vector->tx) {
+			err = -ENOMEM;
+			goto error;
+		}
+
+		q_vector->rx = kcalloc(rxqs_per_vector,
+				       sizeof(struct iecm_queue *),
+				       GFP_KERNEL);
+		if (!q_vector->rx) {
+			err = -ENOMEM;
+			goto error;
+		}
+
+		if (iecm_is_queue_model_split(vport->rxq_model)) {
+			q_vector->bufq = kcalloc(bufqs_per_vector,
+						 sizeof(struct iecm_queue *),
+						 GFP_KERNEL);
+			if (!q_vector->bufq) {
+				err = -ENOMEM;
+				goto error;
+			}
+		}
+	}
+
+	return 0;
+
+error:
+	iecm_vport_intr_rel(vport);
+	return err;
+}
+
+/**
+ * iecm_vport_intr_init - Setup all vectors for the given vport
+ * @vport: virtual port
+ *
+ * Returns 0 on success or negative on failure
+ */
+int iecm_vport_intr_init(struct iecm_vport *vport)
+{
+	char int_name[IECM_INT_NAME_STR_LEN];
+	int err = 0;
+
+	err = iecm_vport_intr_init_vec_idx(vport);
+	if (err)
+		goto handle_err;
+
+	iecm_vport_intr_map_vector_to_qs(vport);
+	iecm_vport_intr_napi_add_all(vport);
+	iecm_vport_intr_napi_ena_all(vport);
+
+	err = vport->adapter->dev_ops.reg_ops.intr_reg_init(vport);
+	if (err)
+		goto unroll_vectors_alloc;
+
+	snprintf(int_name, sizeof(int_name) - 1, "%s-%s",
+		 dev_driver_string(&vport->adapter->pdev->dev),
+		 vport->netdev->name);
+
+	err = iecm_vport_intr_req_irq(vport, int_name);
+	if (err)
+		goto unroll_vectors_alloc;
+
+	iecm_vport_intr_ena_irq_all(vport);
+	goto handle_err;
+unroll_vectors_alloc:
+	iecm_vport_intr_napi_dis_all(vport);
+	iecm_vport_intr_napi_del_all(vport);
+handle_err:
+	return err;
+}
+
+/**
+ * iecm_config_rss - Prepare for RSS
+ * @vport: virtual port
+ *
+ * Return 0 on success, negative on failure
+ */
+int iecm_config_rss(struct iecm_vport *vport)
+{
+	int err;
+
+	err = vport->adapter->dev_ops.vc_ops.get_set_rss_key(vport, false);
+	if (!err)
+		err = vport->adapter->dev_ops.vc_ops.get_set_rss_lut(vport,
+								     false);
+
+	return err;
+}
+
+/**
+ * iecm_fill_dflt_rss_lut - Fill the indirection table with the default values
+ * @vport: virtual port structure
+ */
+void iecm_fill_dflt_rss_lut(struct iecm_vport *vport)
+{
+	u16 num_active_rxq = vport->num_rxq;
+	int i;
+
+	for (i = 0; i < vport->adapter->rss_data.rss_lut_size; i++)
+		vport->adapter->rss_data.rss_lut[i] = i % num_active_rxq;
+}
+
+/**
+ * iecm_init_rss - Prepare for RSS
+ * @vport: virtual port
+ *
+ * Return 0 on success, negative on failure
+ */
+int iecm_init_rss(struct iecm_vport *vport)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	u32 lut_size;
+
+	adapter->rss_data.rss_key = kzalloc(adapter->rss_data.rss_key_size,
+					    GFP_KERNEL);
+	if (!adapter->rss_data.rss_key)
+		return -ENOMEM;
+
+	lut_size = adapter->rss_data.rss_lut_size * sizeof(u32);
+	adapter->rss_data.rss_lut = kzalloc(lut_size, GFP_KERNEL);
+	if (!adapter->rss_data.rss_lut) {
+		kfree(adapter->rss_data.rss_key);
+		adapter->rss_data.rss_key = NULL;
+		return -ENOMEM;
+	}
+
+	/* Initialize default rss key */
+	netdev_rss_key_fill((void *)adapter->rss_data.rss_key,
+			    adapter->rss_data.rss_key_size);
+
+	/* Initialize default rss lut */
+	if (adapter->rss_data.rss_lut_size % vport->num_rxq) {
+		u32 dflt_qid;
+		int i;
+
+		/* Set all entries to a default RX queue if the algorithm below
+		 * won't fill all entries
+		 */
+		if (iecm_is_queue_model_split(vport->rxq_model))
+			dflt_qid =
+				vport->rxq_grps[0].splitq.rxq_sets[0]->rxq.q_id;
+		else
+			dflt_qid =
+				vport->rxq_grps[0].singleq.rxqs[0]->q_id;
+
+		for (i = 0; i < adapter->rss_data.rss_lut_size; i++)
+			adapter->rss_data.rss_lut[i] = dflt_qid;
+	}
+
+	/* Fill the default RSS lut values*/
+	iecm_fill_dflt_rss_lut(vport);
+
+	return iecm_config_rss(vport);
+}
+
+/**
+ * iecm_deinit_rss - Prepare for RSS
+ * @vport: virtual port
+ *
+ */
+void iecm_deinit_rss(struct iecm_vport *vport)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+
+	kfree(adapter->rss_data.rss_key);
+	adapter->rss_data.rss_key = NULL;
+	kfree(adapter->rss_data.rss_lut);
+	adapter->rss_data.rss_lut = NULL;
+}
diff --git a/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
index b91716aeef6f..919fb3958cf8 100644
--- a/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
+++ b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
@@ -3745,6 +3745,35 @@ void iecm_add_del_ether_addrs(struct iecm_vport *vport, bool add, bool async)
 		dev_err(&pdev->dev, "Failed to add or del mac filters %d", err);
 }
 
+/**
+ * iecm_set_promiscuous - set promiscuous and send message to mailbox
+ * @adapter: Driver specific private structure
+ *
+ * Request that the PF enable promiscuous mode for our VSI.  Message is sent
+ * asynchronously and won't wait for response.  Returns 0 on success, negative
+ * on failure;
+ */
+int iecm_set_promiscuous(struct iecm_adapter *adapter)
+{
+	struct iecm_vport *vport = adapter->vports[0];
+	struct virtchnl_promisc_info vpi;
+	u16 flags = 0;
+	int err = 0;
+
+	if (test_bit(__IECM_PROMISC_UC, adapter->config_data.user_flags))
+		flags |= FLAG_VF_UNICAST_PROMISC;
+	if (test_bit(__IECM_PROMISC_MC,
+		     adapter->config_data.user_flags))
+		flags |= FLAG_VF_MULTICAST_PROMISC;
+
+	vpi.vsi_id = vport->vport_id;
+	vpi.flags = flags;
+	err = iecm_send_mb_msg(adapter, VIRTCHNL_OP_CONFIG_PROMISCUOUS_MODE,
+			       sizeof(struct virtchnl_promisc_info),
+			       (u8 *)&vpi);
+	return err;
+}
+
 /**
  * iecm_add_del_vlans - Add or delete vlan filter
  * @vport: vport structure
diff --git a/drivers/net/ethernet/intel/include/iecm.h b/drivers/net/ethernet/intel/include/iecm.h
index b5bd73be2855..4304256f7010 100644
--- a/drivers/net/ethernet/intel/include/iecm.h
+++ b/drivers/net/ethernet/intel/include/iecm.h
@@ -13,6 +13,7 @@
 #include <linux/version.h>
 #include <linux/dim.h>
 
+#include "iecm_lan_txrx.h"
 #include "virtchnl_2.h"
 #include "iecm_txrx.h"
 #include "iecm_controlq.h"
@@ -621,6 +622,17 @@ enum iecm_vlan_caps {
 	VIRTCHNL2_CAP_RX_CSUM_L4_IPV4_SCTP	|\
 	VIRTCHNL2_CAP_RX_CSUM_L4_IPV6_SCTP)
 
+/**
+ * iecm_restore_features - Restore feature configs
+ * @adapter: driver specific private structure
+ * @flag: User settings flag to check
+ */
+static inline bool iecm_is_user_flag_ena(struct iecm_adapter *adapter,
+					 enum iecm_user_flags flag)
+{
+	return test_bit(flag, adapter->config_data.user_flags);
+}
+
 /**
  * iecm_get_reserved_vecs - Get reserved vectors
  * @adapter: private data struct
@@ -656,6 +668,19 @@ static inline bool iecm_is_reset_in_prog(struct iecm_adapter *adapter)
 		test_bit(__IECM_HR_DRV_LOAD, adapter->flags));
 }
 
+/**
+ * iecm_rx_offset - Return expected offset into page to access data
+ * @rx_q: queue we are requesting offset of
+ *
+ * Returns the offset value for queue into the data buffer.
+ */
+static inline unsigned int
+iecm_rx_offset(struct iecm_queue __maybe_unused *rx_q)
+{
+	/* could be non-zero if xdp is enabled */
+	return 0;
+}
+
 int iecm_probe(struct pci_dev *pdev,
 	       const struct pci_device_id __always_unused *ent,
 	       struct iecm_adapter *adapter);
@@ -702,6 +727,7 @@ int iecm_send_mb_msg(struct iecm_adapter *adapter, enum virtchnl_ops op,
 		     u16 msg_size, u8 *msg);
 void iecm_vport_set_hsplit(struct iecm_vport *vport, bool ena);
 void iecm_add_del_ether_addrs(struct iecm_vport *vport, bool add, bool async);
+int iecm_set_promiscuous(struct iecm_adapter *adapter);
 int iecm_send_enable_channels_msg(struct iecm_vport *vport);
 int iecm_send_disable_channels_msg(struct iecm_vport *vport);
 bool iecm_is_feature_ena(struct iecm_vport *vport, netdev_features_t feature);
@@ -710,5 +736,7 @@ int iecm_check_descs(struct iecm_vport *vport, u64 rx_desc_ids,
 int iecm_set_msg_pending(struct iecm_adapter *adapter,
 			 struct iecm_ctlq_msg *ctlq_msg,
 			 enum iecm_vport_vc_state err_enum);
+void iecm_vport_intr_write_itr(struct iecm_q_vector *q_vector,
+			       u16 itr, bool tx);
 int iecm_send_map_unmap_queue_vector_msg(struct iecm_vport *vport, bool map);
 #endif /* !_IECM_H_ */
diff --git a/drivers/net/ethernet/intel/include/iecm_lan_txrx.h b/drivers/net/ethernet/intel/include/iecm_lan_txrx.h
new file mode 100644
index 000000000000..967308036eba
--- /dev/null
+++ b/drivers/net/ethernet/intel/include/iecm_lan_txrx.h
@@ -0,0 +1,394 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (c) 2020, Intel Corporation. */
+
+#ifndef _IECM_LAN_TXRX_H_
+#define _IECM_LAN_TXRX_H_
+
+enum iecm_rss_hash {
+	/* Values 0 - 28 are reserved for future use */
+	IECM_HASH_INVALID		= 0,
+	IECM_HASH_NONF_UNICAST_IPV4_UDP	= 29,
+	IECM_HASH_NONF_MULTICAST_IPV4_UDP,
+	IECM_HASH_NONF_IPV4_UDP,
+	IECM_HASH_NONF_IPV4_TCP_SYN_NO_ACK,
+	IECM_HASH_NONF_IPV4_TCP,
+	IECM_HASH_NONF_IPV4_SCTP,
+	IECM_HASH_NONF_IPV4_OTHER,
+	IECM_HASH_FRAG_IPV4,
+	/* Values 37-38 are reserved */
+	IECM_HASH_NONF_UNICAST_IPV6_UDP	= 39,
+	IECM_HASH_NONF_MULTICAST_IPV6_UDP,
+	IECM_HASH_NONF_IPV6_UDP,
+	IECM_HASH_NONF_IPV6_TCP_SYN_NO_ACK,
+	IECM_HASH_NONF_IPV6_TCP,
+	IECM_HASH_NONF_IPV6_SCTP,
+	IECM_HASH_NONF_IPV6_OTHER,
+	IECM_HASH_FRAG_IPV6,
+	IECM_HASH_NONF_RSVD47,
+	IECM_HASH_NONF_FCOE_OX,
+	IECM_HASH_NONF_FCOE_RX,
+	IECM_HASH_NONF_FCOE_OTHER,
+	/* Values 51-62 are reserved */
+	IECM_HASH_L2_PAYLOAD		= 63,
+	IECM_HASH_MAX
+};
+
+/* Supported RSS offloads */
+#define IECM_DEFAULT_RSS_HASH ( \
+	BIT_ULL(IECM_HASH_NONF_IPV4_UDP) | \
+	BIT_ULL(IECM_HASH_NONF_IPV4_SCTP) | \
+	BIT_ULL(IECM_HASH_NONF_IPV4_TCP) | \
+	BIT_ULL(IECM_HASH_NONF_IPV4_OTHER) | \
+	BIT_ULL(IECM_HASH_FRAG_IPV4) | \
+	BIT_ULL(IECM_HASH_NONF_IPV6_UDP) | \
+	BIT_ULL(IECM_HASH_NONF_IPV6_TCP) | \
+	BIT_ULL(IECM_HASH_NONF_IPV6_SCTP) | \
+	BIT_ULL(IECM_HASH_NONF_IPV6_OTHER) | \
+	BIT_ULL(IECM_HASH_FRAG_IPV6) | \
+	BIT_ULL(IECM_HASH_L2_PAYLOAD))
+
+	/* TODO: Wrap belwo comment under internal flag
+	 * Below 6 pcktypes are not supported by FVL or older products
+	 * They are supported by FPK and future products
+	 */
+#define IECM_DEFAULT_RSS_HASH_EXPANDED (IECM_DEFAULT_RSS_HASH | \
+	BIT_ULL(IECM_HASH_NONF_IPV4_TCP_SYN_NO_ACK) | \
+	BIT_ULL(IECM_HASH_NONF_UNICAST_IPV4_UDP) | \
+	BIT_ULL(IECM_HASH_NONF_MULTICAST_IPV4_UDP) | \
+	BIT_ULL(IECM_HASH_NONF_IPV6_TCP_SYN_NO_ACK) | \
+	BIT_ULL(IECM_HASH_NONF_UNICAST_IPV6_UDP) | \
+	BIT_ULL(IECM_HASH_NONF_MULTICAST_IPV6_UDP))
+
+/* For iecm_splitq_base_tx_compl_desc */
+#define IECM_TXD_COMPLQ_GEN_S	15
+#define IECM_TXD_COMPLQ_GEN_M		BIT_ULL(IECM_TXD_COMPLQ_GEN_S)
+#define IECM_TXD_COMPLQ_COMPL_TYPE_S	11
+#define IECM_TXD_COMPLQ_COMPL_TYPE_M	\
+	MAKEMASK(0x7UL, IECM_TXD_COMPLQ_COMPL_TYPE_S)
+#define IECM_TXD_COMPLQ_QID_S	0
+#define IECM_TXD_COMPLQ_QID_M		MAKEMASK(0x3FFUL, IECM_TXD_COMPLQ_QID_S)
+
+/* For base mode TX descriptors */
+#define IECM_TXD_CTX_QW1_MSS_S		50
+#define IECM_TXD_CTX_QW1_MSS_M		\
+	MAKEMASK(0x3FFFULL, IECM_TXD_CTX_QW1_MSS_S)
+#define IECM_TXD_CTX_QW1_TSO_LEN_S	30
+#define IECM_TXD_CTX_QW1_TSO_LEN_M	\
+	MAKEMASK(0x3FFFFULL, IECM_TXD_CTX_QW1_TSO_LEN_S)
+#define IECM_TXD_CTX_QW1_CMD_S		4
+#define IECM_TXD_CTX_QW1_CMD_M		\
+	MAKEMASK(0xFFFUL, IECM_TXD_CTX_QW1_CMD_S)
+#define IECM_TXD_CTX_QW1_DTYPE_S	0
+#define IECM_TXD_CTX_QW1_DTYPE_M	\
+	MAKEMASK(0xFUL, IECM_TXD_CTX_QW1_DTYPE_S)
+#define IECM_TXD_QW1_L2TAG1_S		48
+#define IECM_TXD_QW1_L2TAG1_M		\
+	MAKEMASK(0xFFFFULL, IECM_TXD_QW1_L2TAG1_S)
+#define IECM_TXD_QW1_TX_BUF_SZ_S	34
+#define IECM_TXD_QW1_TX_BUF_SZ_M	\
+	MAKEMASK(0x3FFFULL, IECM_TXD_QW1_TX_BUF_SZ_S)
+#define IECM_TXD_QW1_OFFSET_S		16
+#define IECM_TXD_QW1_OFFSET_M		\
+	MAKEMASK(0x3FFFFULL, IECM_TXD_QW1_OFFSET_S)
+#define IECM_TXD_QW1_CMD_S		4
+#define IECM_TXD_QW1_CMD_M		MAKEMASK(0xFFFUL, IECM_TXD_QW1_CMD_S)
+#define IECM_TXD_QW1_DTYPE_S		0
+#define IECM_TXD_QW1_DTYPE_M		MAKEMASK(0xFUL, IECM_TXD_QW1_DTYPE_S)
+
+/* TX Completion Descriptor Completion Types */
+#define IECM_TXD_COMPLT_ITR_FLUSH	0
+#define IECM_TXD_COMPLT_RULE_MISS	1
+#define IECM_TXD_COMPLT_RS		2
+#define IECM_TXD_COMPLT_REINJECTED	3
+#define IECM_TXD_COMPLT_RE		4
+#define IECM_TXD_COMPLT_SW_MARKER	5
+
+enum iecm_tx_desc_dtype_value {
+	IECM_TX_DESC_DTYPE_DATA				= 0,
+	IECM_TX_DESC_DTYPE_CTX				= 1,
+	IECM_TX_DESC_DTYPE_REINJECT_CTX			= 2,
+	IECM_TX_DESC_DTYPE_FLEX_DATA			= 3,
+	IECM_TX_DESC_DTYPE_FLEX_CTX			= 4,
+	IECM_TX_DESC_DTYPE_FLEX_TSO_CTX			= 5,
+	IECM_TX_DESC_DTYPE_FLEX_TSYN_L2TAG1		= 6,
+	IECM_TX_DESC_DTYPE_FLEX_L2TAG1_L2TAG2		= 7,
+	IECM_TX_DESC_DTYPE_FLEX_TSO_L2TAG2_PARSTAG_CTX	= 8,
+	IECM_TX_DESC_DTYPE_FLEX_HOSTSPLIT_SA_TSO_CTX	= 9,
+	IECM_TX_DESC_DTYPE_FLEX_HOSTSPLIT_SA_CTX	= 10,
+	IECM_TX_DESC_DTYPE_FLEX_L2TAG2_CTX		= 11,
+	IECM_TX_DESC_DTYPE_FLEX_FLOW_SCHE		= 12,
+	IECM_TX_DESC_DTYPE_FLEX_HOSTSPLIT_TSO_CTX	= 13,
+	IECM_TX_DESC_DTYPE_FLEX_HOSTSPLIT_CTX		= 14,
+	/* DESC_DONE - HW has completed write-back of descriptor */
+	IECM_TX_DESC_DTYPE_DESC_DONE			= 15,
+};
+
+enum iecm_tx_ctx_desc_cmd_bits {
+	IECM_TX_CTX_DESC_TSO		= 0x01,
+	IECM_TX_CTX_DESC_TSYN		= 0x02,
+	IECM_TX_CTX_DESC_IL2TAG2	= 0x04,
+	IECM_TX_CTX_DESC_RSVD		= 0x08,
+	IECM_TX_CTX_DESC_SWTCH_NOTAG	= 0x00,
+	IECM_TX_CTX_DESC_SWTCH_UPLINK	= 0x10,
+	IECM_TX_CTX_DESC_SWTCH_LOCAL	= 0x20,
+	IECM_TX_CTX_DESC_SWTCH_VSI	= 0x30,
+	IECM_TX_CTX_DESC_FILT_AU_EN	= 0x40,
+	IECM_TX_CTX_DESC_FILT_AU_EVICT	= 0x80,
+	IECM_TX_CTX_DESC_RSVD1		= 0xF00
+};
+
+enum iecm_tx_desc_len_fields {
+	/* Note: These are predefined bit offsets */
+	IECM_TX_DESC_LEN_MACLEN_S	= 0, /* 7 BITS */
+	IECM_TX_DESC_LEN_IPLEN_S	= 7, /* 7 BITS */
+	IECM_TX_DESC_LEN_L4_LEN_S	= 14 /* 4 BITS */
+};
+
+enum iecm_tx_base_desc_cmd_bits {
+	IECM_TX_DESC_CMD_EOP			= 0x0001,
+	IECM_TX_DESC_CMD_RS			= 0x0002,
+	 /* only on VFs else RSVD */
+	IECM_TX_DESC_CMD_ICRC			= 0x0004,
+	IECM_TX_DESC_CMD_IL2TAG1		= 0x0008,
+	IECM_TX_DESC_CMD_RSVD1			= 0x0010,
+	IECM_TX_DESC_CMD_IIPT_NONIP		= 0x0000, /* 2 BITS */
+	IECM_TX_DESC_CMD_IIPT_IPV6		= 0x0020, /* 2 BITS */
+	IECM_TX_DESC_CMD_IIPT_IPV4		= 0x0040, /* 2 BITS */
+	IECM_TX_DESC_CMD_IIPT_IPV4_CSUM		= 0x0060, /* 2 BITS */
+	IECM_TX_DESC_CMD_RSVD2			= 0x0080,
+	IECM_TX_DESC_CMD_L4T_EOFT_UNK		= 0x0000, /* 2 BITS */
+	IECM_TX_DESC_CMD_L4T_EOFT_TCP		= 0x0100, /* 2 BITS */
+	IECM_TX_DESC_CMD_L4T_EOFT_SCTP		= 0x0200, /* 2 BITS */
+	IECM_TX_DESC_CMD_L4T_EOFT_UDP		= 0x0300, /* 2 BITS */
+	IECM_TX_DESC_CMD_RSVD3			= 0x0400,
+	IECM_TX_DESC_CMD_RSVD4			= 0x0800,
+};
+
+/* Transmit descriptors  */
+/* splitq tx buf, singleq tx buf and singleq compl desc */
+struct iecm_base_tx_desc {
+	__le64 buf_addr; /* Address of descriptor's data buf */
+	__le64 qw1; /* type_cmd_offset_bsz_l2tag1 */
+};/* read used with buffer queues*/
+
+struct iecm_splitq_tx_compl_desc {
+	/* qid=[10:0] comptype=[13:11] rsvd=[14] gen=[15] */
+	__le16 qid_comptype_gen;
+	union {
+		__le16 q_head; /* Queue head */
+		__le16 compl_tag; /* Completion tag */
+	} q_head_compl_tag;
+	u32 rsvd;
+
+};/* writeback used with completion queues*/
+
+/* Context descriptors */
+struct iecm_base_tx_ctx_desc {
+	struct {
+		__le32 rsvd0;
+		__le16 l2tag2;
+		__le16 rsvd1;
+	} qw0;
+	__le64 qw1; /* type_cmd_tlen_mss/rt_hint */
+};
+
+/* Common cmd field defines for all desc except Flex Flow Scheduler (0x0C) */
+enum iecm_tx_flex_desc_cmd_bits {
+	IECM_TX_FLEX_DESC_CMD_EOP			= 0x01,
+	IECM_TX_FLEX_DESC_CMD_RS			= 0x02,
+	IECM_TX_FLEX_DESC_CMD_RE			= 0x04,
+	IECM_TX_FLEX_DESC_CMD_IL2TAG1			= 0x08,
+	IECM_TX_FLEX_DESC_CMD_DUMMY			= 0x10,
+	IECM_TX_FLEX_DESC_CMD_CS_EN			= 0x20,
+	IECM_TX_FLEX_DESC_CMD_FILT_AU_EN		= 0x40,
+	IECM_TX_FLEX_DESC_CMD_FILT_AU_EVICT		= 0x80,
+};
+
+struct iecm_flex_tx_desc {
+	__le64 buf_addr;	/* Packet buffer address */
+	struct {
+		__le16 cmd_dtype;
+#define IECM_FLEX_TXD_QW1_DTYPE_S		0
+#define IECM_FLEX_TXD_QW1_DTYPE_M		\
+		MAKEMASK(0x1FUL, IECM_FLEX_TXD_QW1_DTYPE_S)
+#define IECM_FLEX_TXD_QW1_CMD_S		5
+#define IECM_FLEX_TXD_QW1_CMD_M		MAKEMASK(0x7FFUL, IECM_TXD_QW1_CMD_S)
+		union {
+			/* DTYPE = IECM_TX_DESC_DTYPE_FLEX_DATA_(0x03) */
+			u8 raw[4];
+
+			/* DTYPE = IECM_TX_DESC_DTYPE_FLEX_TSYN_L2TAG1 (0x06) */
+			struct {
+				__le16 l2tag1;
+				u8 flex;
+				u8 tsync;
+			} tsync;
+
+			/* DTYPE=IECM_TX_DESC_DTYPE_FLEX_L2TAG1_L2TAG2 (0x07) */
+			struct {
+				__le16 l2tag1;
+				__le16 l2tag2;
+			} l2tags;
+		} flex;
+		__le16 buf_size;
+	} qw1;
+};
+
+struct iecm_flex_tx_sched_desc {
+	__le64 buf_addr;	/* Packet buffer address */
+
+	/* DTYPE = IECM_TX_DESC_DTYPE_FLEX_FLOW_SCHE_16B (0x0C) */
+	struct {
+		u8 cmd_dtype;
+#define IECM_TXD_FLEX_FLOW_DTYPE_M	0x1F
+#define IECM_TXD_FLEX_FLOW_CMD_EOP	0x20
+#define IECM_TXD_FLEX_FLOW_CMD_CS_EN	0x40
+#define IECM_TXD_FLEX_FLOW_CMD_RE	0x80
+
+		u8 rsvd[3];
+
+		__le16 compl_tag;
+		__le16 rxr_bufsize;
+#define IECM_TXD_FLEX_FLOW_RXR		0x4000
+#define IECM_TXD_FLEX_FLOW_BUFSIZE_M	0x3FFF
+	} qw1;
+};
+
+/* Common cmd fields for all flex context descriptors
+ * Note: these defines already account for the 5 bit dtype in the cmd_dtype
+ * field
+ */
+enum iecm_tx_flex_ctx_desc_cmd_bits {
+	IECM_TX_FLEX_CTX_DESC_CMD_TSO			= 0x0020,
+	IECM_TX_FLEX_CTX_DESC_CMD_TSYN_EN		= 0x0040,
+	IECM_TX_FLEX_CTX_DESC_CMD_L2TAG2		= 0x0080,
+	IECM_TX_FLEX_CTX_DESC_CMD_SWTCH_UPLNK		= 0x0200, /* 2 bits */
+	IECM_TX_FLEX_CTX_DESC_CMD_SWTCH_LOCAL		= 0x0400, /* 2 bits */
+	IECM_TX_FLEX_CTX_DESC_CMD_SWTCH_TARGETVSI	= 0x0600, /* 2 bits */
+};
+
+/* Standard flex descriptor TSO context quad word */
+struct iecm_flex_tx_tso_ctx_qw {
+	__le32 flex_tlen;
+#define IECM_TXD_FLEX_CTX_TLEN_M	0x1FFFF
+#define IECM_TXD_FLEX_TSO_CTX_FLEX_S	24
+	__le16 mss_rt;
+#define IECM_TXD_FLEX_CTX_MSS_RT_M	0x3FFF
+	u8 hdr_len;
+	u8 flex;
+};
+
+union iecm_flex_tx_ctx_desc {
+	/* DTYPE = IECM_TX_DESC_DTYPE_FLEX_CTX (0x04) */
+	struct {
+		u8 qw0_flex[8];
+		struct {
+			__le16 cmd_dtype;
+			__le16 l2tag1;
+			u8 qw1_flex[4];
+		} qw1;
+	} gen;
+
+	/* DTYPE = IECM_TX_DESC_DTYPE_FLEX_TSO_CTX (0x05) */
+	struct {
+		struct iecm_flex_tx_tso_ctx_qw qw0;
+		struct {
+			__le16 cmd_dtype;
+			u8 flex[6];
+		} qw1;
+	} tso;
+
+	/* DTYPE = IECM_TX_DESC_DTYPE_FLEX_TSO_L2TAG2_PARSTAG_CTX (0x08) */
+	struct {
+		struct iecm_flex_tx_tso_ctx_qw qw0;
+		struct {
+			__le16 cmd_dtype;
+			__le16 l2tag2;
+			u8 flex0;
+			u8 ptag;
+			u8 flex1[2];
+		} qw1;
+	} tso_l2tag2_ptag;
+
+	/* DTYPE = IECM_TX_DESC_DTYPE_FLEX_L2TAG2_CTX (0x0B) */
+	struct {
+		u8 qw0_flex[8];
+		struct {
+			__le16 cmd_dtype;
+			__le16 l2tag2;
+			u8 flex[4];
+		} qw1;
+	} l2tag2;
+
+	/* DTYPE = IECM_TX_DESC_DTYPE_REINJECT_CTX (0x02) */
+	struct {
+		struct {
+			__le32 sa_domain;
+#define IECM_TXD_FLEX_CTX_SA_DOM_M	0xFFFF
+#define IECM_TXD_FLEX_CTX_SA_DOM_VAL	0x10000
+			__le32 sa_idx;
+#define IECM_TXD_FLEX_CTX_SAIDX_M	0x1FFFFF
+		} qw0;
+		struct {
+			__le16 cmd_dtype;
+			__le16 txr2comp;
+#define IECM_TXD_FLEX_CTX_TXR2COMP	0x1
+			__le16 miss_txq_comp_tag;
+			__le16 miss_txq_id;
+		} qw1;
+	} reinjection_pkt;
+};
+
+/* Host Split Context Descriptors */
+struct iecm_flex_tx_hs_ctx_desc {
+	union {
+		struct {
+			__le32 host_fnum_tlen;
+#define IECM_TXD_FLEX_CTX_TLEN_S	0
+#define IECM_TXD_FLEX_CTX_TLEN_M	0x1FFFF
+#define IECM_TXD_FLEX_CTX_FNUM_S	18
+#define IECM_TXD_FLEX_CTX_FNUM_M	0x7FF
+#define IECM_TXD_FLEX_CTX_HOST_S	29
+#define IECM_TXD_FLEX_CTX_HOST_M	0x7
+			__le16 ftype_mss_rt;
+#define IECM_TXD_FLEX_CTX_MSS_RT_0	0
+#define IECM_TXD_FLEX_CTX_MSS_RT_M	0x3FFF
+#define IECM_TXD_FLEX_CTX_FTYPE_S	14
+#define IECM_TXD_FLEX_CTX_FTYPE_VF	MAKEMASK(0x0, IECM_TXD_FLEX_CTX_FTYPE_S)
+#define IECM_TXD_FLEX_CTX_FTYPE_VDEV	MAKEMASK(0x1, IECM_TXD_FLEX_CTX_FTYPE_S)
+#define IECM_TXD_FLEX_CTX_FTYPE_PF	MAKEMASK(0x2, IECM_TXD_FLEX_CTX_FTYPE_S)
+			u8 hdr_len;
+			u8 ptag;
+		} tso;
+		struct {
+			u8 flex0[2];
+			__le16 host_fnum_ftype;
+			u8 flex1[3];
+			u8 ptag;
+		} no_tso;
+	} qw0;
+
+	__le64 qw1_cmd_dtype;
+#define IECM_TXD_FLEX_CTX_QW1_PASID_S		16
+#define IECM_TXD_FLEX_CTX_QW1_PASID_M		0xFFFFF
+#define IECM_TXD_FLEX_CTX_QW1_PASID_VALID_S	36
+#define IECM_TXD_FLEX_CTX_QW1_PASID_VALID	\
+		MAKEMASK(0x1, IECM_TXD_FLEX_CTX_PASID_VALID_S)
+#define IECM_TXD_FLEX_CTX_QW1_TPH_S		37
+#define IECM_TXD_FLEX_CTX_QW1_TPH \
+		MAKEMASK(0x1, IECM_TXD_FLEX_CTX_TPH_S)
+#define IECM_TXD_FLEX_CTX_QW1_PFNUM_S		38
+#define IECM_TXD_FLEX_CTX_QW1_PFNUM_M		0xF
+/* The following are only valid for DTYPE = 0x09 and DTYPE = 0x0A */
+#define IECM_TXD_FLEX_CTX_QW1_SAIDX_S		42
+#define IECM_TXD_FLEX_CTX_QW1_SAIDX_M		0x1FFFFF
+#define IECM_TXD_FLEX_CTX_QW1_SAIDX_VAL_S	63
+#define IECM_TXD_FLEX_CTX_QW1_SAIDX_VALID	\
+		MAKEMASK(0x1, IECM_TXD_FLEX_CTX_QW1_SAIDX_VAL_S)
+/* The following are only valid for DTYPE = 0x0D and DTYPE = 0x0E */
+#define IECM_TXD_FLEX_CTX_QW1_FLEX0_S		48
+#define IECM_TXD_FLEX_CTX_QW1_FLEX0_M		0xFF
+#define IECM_TXD_FLEX_CTX_QW1_FLEX1_S		56
+#define IECM_TXD_FLEX_CTX_QW1_FLEX1_M		0xFF
+};
+#endif /* _IECM_LAN_TXRX_H_ */
diff --git a/drivers/net/ethernet/intel/include/iecm_txrx.h b/drivers/net/ethernet/intel/include/iecm_txrx.h
index 0aa1eac70e7c..44c20f8a2039 100644
--- a/drivers/net/ethernet/intel/include/iecm_txrx.h
+++ b/drivers/net/ethernet/intel/include/iecm_txrx.h
@@ -81,8 +81,70 @@
 #define IECM_MAX_MTU		\
 	(IECM_MAX_RXBUFFER - IECM_PACKET_HDR_PAD)
 
+#define IECM_RX_BI_BUFID_S		0
+#define IECM_RX_BI_BUFID_M		MAKEMASK(0x7FFF, IECM_RX_BI_BUFID_S)
+#define IECM_RX_BI_GEN_S		15
+#define IECM_RX_BI_GEN_M		BIT(IECM_RX_BI_GEN_S)
+
+#define IECM_SINGLEQ_RX_BUF_DESC(R, i)	\
+	(&(((struct virtchnl2_singleq_rx_buf_desc *)((R)->desc_ring))[i]))
+#define IECM_SPLITQ_RX_BUF_DESC(R, i)	\
+	(&(((struct virtchnl2_splitq_rx_buf_desc *)((R)->desc_ring))[i]))
+#define IECM_SPLITQ_RX_BI_DESC(R, i)	\
+	(&(((u16 *)((R)->ring))[i]))
+
+#define IECM_BASE_TX_DESC(R, i)	\
+	(&(((struct iecm_base_tx_desc *)((R)->desc_ring))[i]))
+#define IECM_BASE_TX_CTX_DESC(R, i) \
+	(&(((struct iecm_base_tx_ctx_desc *)((R)->desc_ring))[i]))
+#define IECM_SPLITQ_TX_COMPLQ_DESC(R, i)	\
+	(&(((struct iecm_splitq_tx_compl_desc *)((R)->desc_ring))[i]))
+
+#define IECM_FLEX_TX_DESC(R, i)	\
+	(&(((union iecm_tx_flex_desc *)((R)->desc_ring))[i]))
+#define IECM_FLEX_TX_CTX_DESC(R, i)	\
+	(&(((union iecm_flex_tx_ctx_desc *)((R)->desc_ring))[i]))
+
+#define IECM_DESC_UNUSED(R)	\
+	((((R)->next_to_clean > (R)->next_to_use) ? 0 : (R)->desc_count) + \
+	(R)->next_to_clean - (R)->next_to_use - 1)
+
+#define IECM_TX_BUF_UNUSED(R)	((R)->buf_stack.top)
+
+#define IECM_TXD_LAST_DESC_CMD (IECM_TX_DESC_CMD_EOP | IECM_TX_DESC_CMD_RS)
+
 #define MAKEMASK(m, s)	((m) << (s))
 
+struct iecm_tx_buf {
+	struct hlist_node hlist;
+	void *next_to_watch;
+	union {
+		struct sk_buff *skb;
+		struct xdp_frame *xdpf;
+	};
+	unsigned int bytecount;
+	unsigned short gso_segs;
+#define IECM_TX_FLAGS_TSO			BIT(0)
+#define IECM_TX_FLAGS_VLAN_TAG			BIT(1)
+#define IECM_TX_FLAGS_HW_VLAN			BIT(2)
+#define IECM_TX_FLAGS_HW_OUTER_SINGLE_VLAN	BIT(3)
+#define IECM_TX_FLAGS_VLAN_SHIFT		16
+#define IECM_TX_FLAGS_VLAN_MASK			0xFFFF0000
+	u32 tx_flags;
+	DEFINE_DMA_UNMAP_ADDR(dma);
+	DEFINE_DMA_UNMAP_LEN(len);
+	u16 compl_tag;		/* Unique identifier for buffer; used to
+				 * compare with completion tag returned
+				 * in buffer completion event
+				 */
+};
+
+struct iecm_buf_lifo {
+	u16 top;
+	u16 size;
+	struct iecm_tx_buf **bufs;
+};
+
 /* Checksum offload bits decoded from the receive descriptor. */
 struct iecm_rx_csum_decoded {
 	u8 l3l4p : 1;
@@ -349,6 +411,16 @@ union iecm_queue_stats {
 	struct iecm_tx_queue_stats tx;
 };
 
+#define IECM_ITR_DYNAMIC	1
+#define IECM_ITR_MAX		0x1FE0
+#define IECM_ITR_20K		0x0032
+#define IECM_ITR_GRAN_S		1	/* Assume ITR granularity is 2us */
+#define IECM_ITR_MASK		0x1FFE	/* ITR register value alignment mask */
+#define ITR_REG_ALIGN(setting)	((setting) & IECM_ITR_MASK)
+#define IECM_ITR_IS_DYNAMIC(itr_mode) (itr_mode)
+#define IECM_ITR_TX_DEF		IECM_ITR_20K
+#define IECM_ITR_RX_DEF		IECM_ITR_20K
+
 /* queue associated with a vport */
 struct iecm_queue {
 	struct device *dev;		/* Used for DMA mapping */
@@ -414,6 +486,10 @@ struct iecm_queue {
 	dma_addr_t dma;			/* physical address of ring */
 	void *desc_ring;		/* Descriptor ring memory */
 
+	struct iecm_buf_lifo buf_stack; /* Stack of empty buffers to store
+					 * buffer info for out of order
+					 * buffer completions
+					 */
 	u16 tx_buf_key;			/* 16 bit unique "identifier" (index)
 					 * to be used as the completion tag when
 					 * queue is using flow based scheduling
@@ -505,13 +581,33 @@ struct iecm_txq_group {
 
 struct iecm_adapter;
 
+int iecm_vport_singleq_napi_poll(struct napi_struct *napi, int budget);
 void iecm_vport_init_num_qs(struct iecm_vport *vport,
 			    struct virtchnl2_create_vport *vport_msg);
 void iecm_vport_calc_num_q_desc(struct iecm_vport *vport);
 void iecm_vport_calc_total_qs(struct iecm_adapter *adapter,
 			      struct virtchnl2_create_vport *vport_msg);
 void iecm_vport_calc_num_q_groups(struct iecm_vport *vport);
+int iecm_vport_queues_alloc(struct iecm_vport *vport);
+void iecm_vport_queues_rel(struct iecm_vport *vport);
 void iecm_vport_calc_num_q_vec(struct iecm_vport *vport);
+void iecm_vport_intr_rel(struct iecm_vport *vport);
+int iecm_vport_intr_alloc(struct iecm_vport *vport);
+void iecm_vport_intr_dis_irq_all(struct iecm_vport *vport);
+void iecm_vport_intr_clear_dflt_itr(struct iecm_vport *vport);
+void iecm_vport_intr_update_itr_ena_irq(struct iecm_q_vector *q_vector);
+void iecm_vport_intr_deinit(struct iecm_vport *vport);
+int iecm_vport_intr_init(struct iecm_vport *vport);
 irqreturn_t
 iecm_vport_intr_clean_queues(int __always_unused irq, void *data);
+void iecm_vport_intr_ena_irq_all(struct iecm_vport *vport);
+int iecm_config_rss(struct iecm_vport *vport);
+void iecm_fill_dflt_rss_lut(struct iecm_vport *vport);
+int iecm_init_rss(struct iecm_vport *vport);
+void iecm_deinit_rss(struct iecm_vport *vport);
+bool iecm_init_rx_buf_hw_alloc(struct iecm_queue *rxq, struct iecm_rx_buf *buf);
+void iecm_rx_buf_hw_update(struct iecm_queue *rxq, u32 val);
+void iecm_tx_buf_rel(struct iecm_queue *tx_q, struct iecm_tx_buf *tx_buf);
+bool iecm_rx_singleq_buf_hw_alloc_all(struct iecm_queue *rxq,
+				      u16 cleaned_count);
 #endif /* !_IECM_TXRX_H_ */
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 10/19] iecm: alloc vport RX resources
  2022-01-28  0:09 [Intel-wired-lan] [PATCH net-next 00/19] Add iecm and idpf Alan Brady
                   ` (8 preceding siblings ...)
  2022-01-28  0:09 ` [Intel-wired-lan] [PATCH net-next 09/19] iecm: alloc vport TX resources Alan Brady
@ 2022-01-28  0:10 ` Alan Brady
  2022-01-28 14:16   ` Alexander Lobakin
  2022-01-28  0:10 ` [Intel-wired-lan] [PATCH net-next 11/19] iecm: add start_xmit and set_rx_mode Alan Brady
                   ` (9 subsequent siblings)
  19 siblings, 1 reply; 89+ messages in thread
From: Alan Brady @ 2022-01-28  0:10 UTC (permalink / raw)
  To: intel-wired-lan

This finishes what we need to do for open by adding RX resource
allocations.

As noted in the TX alloc patch, the splitq model is unique in introducing
the concept of queue groups, which also applies to RX, albeit in a slightly
different way. For RX we also split the queue between descriptor handling
and buffer handling. We have some number of RX completion queues associated
with up to two buffer queues in a given queue group. Once buffers are
cleaned and recycled, they're given the buffer queues which then posts the
buffers back to hardware. To enable this in a lockless way, there's also
the concept of 'refill queues' introduced. Recycled buffers are put onto
refill queues which is what the buffer queues clean to get buffers back.

Signed-off-by: Phani Burra <phani.r.burra@intel.com>
Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
Signed-off-by: Alan Brady <alan.brady@intel.com>
---
 drivers/net/ethernet/intel/iecm/iecm_txrx.c   | 769 ++++++++++++++++++
 .../net/ethernet/intel/include/iecm_txrx.h    |   7 +
 2 files changed, 776 insertions(+)

diff --git a/drivers/net/ethernet/intel/iecm/iecm_txrx.c b/drivers/net/ethernet/intel/iecm/iecm_txrx.c
index 85e88a30370d..fb6a61277b00 100644
--- a/drivers/net/ethernet/intel/iecm/iecm_txrx.c
+++ b/drivers/net/ethernet/intel/iecm/iecm_txrx.c
@@ -452,6 +452,545 @@ static int iecm_tx_desc_alloc_all(struct iecm_vport *vport)
 	return err;
 }
 
+/**
+ * iecm_rx_page_rel - Release an rx buffer page
+ * @rxq: the queue that owns the buffer
+ * @page_info: pointer to page metadata of page to be freed
+ */
+static void iecm_rx_page_rel(struct iecm_queue *rxq,
+			     struct iecm_page_info *page_info)
+{
+	if (!page_info->page)
+		return;
+
+	/* free resources associated with mapping */
+	dma_unmap_page_attrs(rxq->dev, page_info->dma, PAGE_SIZE,
+			     DMA_FROM_DEVICE, IECM_RX_DMA_ATTR);
+
+	__page_frag_cache_drain(page_info->page, page_info->pagecnt_bias);
+
+	page_info->page = NULL;
+	page_info->page_offset = 0;
+}
+
+/**
+ * iecm_rx_buf_rel - Release a rx buffer
+ * @rxq: the queue that owns the buffer
+ * @rx_buf: the buffer to free
+ */
+static void iecm_rx_buf_rel(struct iecm_queue *rxq,
+			    struct iecm_rx_buf *rx_buf)
+{
+	iecm_rx_page_rel(rxq, &rx_buf->page_info[0]);
+#if (PAGE_SIZE < 8192)
+	if (rx_buf->buf_size > IECM_RX_BUF_2048)
+		iecm_rx_page_rel(rxq, &rx_buf->page_info[1]);
+
+#endif
+	if (rx_buf->skb) {
+		dev_kfree_skb_any(rx_buf->skb);
+		rx_buf->skb = NULL;
+	}
+}
+
+/**
+ * iecm_rx_hdr_buf_rel_all - Release header buffer memory
+ * @rxq: queue to use
+ */
+static void iecm_rx_hdr_buf_rel_all(struct iecm_queue *rxq)
+{
+	struct iecm_hw *hw = &rxq->vport->adapter->hw;
+	int i;
+
+	if (!rxq)
+		return;
+
+	if (rxq->rx_buf.hdr_buf) {
+		for (i = 0; i < rxq->desc_count; i++) {
+			struct iecm_dma_mem *hbuf = rxq->rx_buf.hdr_buf[i];
+
+			if (hbuf) {
+				iecm_free_dma_mem(hw, hbuf);
+				kfree(hbuf);
+			}
+			rxq->rx_buf.hdr_buf[i] = NULL;
+		}
+		kfree(rxq->rx_buf.hdr_buf);
+		rxq->rx_buf.hdr_buf = NULL;
+	}
+
+	for (i = 0; i < rxq->hbuf_pages.nr_pages; i++)
+		iecm_rx_page_rel(rxq, &rxq->hbuf_pages.pages[i]);
+
+	kfree(rxq->hbuf_pages.pages);
+}
+
+/**
+ * iecm_rx_buf_rel_all - Free all Rx buffer resources for a queue
+ * @rxq: queue to be cleaned
+ */
+static void iecm_rx_buf_rel_all(struct iecm_queue *rxq)
+{
+	u16 i;
+
+	/* queue already cleared, nothing to do */
+	if (!rxq->rx_buf.buf)
+		return;
+
+	/* Free all the bufs allocated and given to hw on Rx queue */
+	for (i = 0; i < rxq->desc_count; i++)
+		iecm_rx_buf_rel(rxq, &rxq->rx_buf.buf[i]);
+	if (rxq->rx_hsplit_en)
+		iecm_rx_hdr_buf_rel_all(rxq);
+
+	kfree(rxq->rx_buf.buf);
+	rxq->rx_buf.buf = NULL;
+	kfree(rxq->rx_buf.hdr_buf);
+	rxq->rx_buf.hdr_buf = NULL;
+}
+
+/**
+ * iecm_rx_desc_rel - Free a specific Rx q resources
+ * @rxq: queue to clean the resources from
+ * @bufq: buffer q or completion q
+ * @q_model: single or split q model
+ *
+ * Free a specific rx queue resources
+ */
+static void iecm_rx_desc_rel(struct iecm_queue *rxq, bool bufq, s32 q_model)
+{
+	if (!rxq)
+		return;
+
+	if (!bufq && iecm_is_queue_model_split(q_model) && rxq->skb) {
+		dev_kfree_skb_any(rxq->skb);
+		rxq->skb = NULL;
+	}
+
+	if (bufq || !iecm_is_queue_model_split(q_model))
+		iecm_rx_buf_rel_all(rxq);
+
+	if (rxq->desc_ring) {
+		dmam_free_coherent(rxq->dev, rxq->size,
+				   rxq->desc_ring, rxq->dma);
+		rxq->desc_ring = NULL;
+		rxq->next_to_alloc = 0;
+		rxq->next_to_clean = 0;
+		rxq->next_to_use = 0;
+	}
+}
+
+/**
+ * iecm_rx_desc_rel_all - Free Rx Resources for All Queues
+ * @vport: virtual port structure
+ *
+ * Free all rx queues resources
+ */
+static void iecm_rx_desc_rel_all(struct iecm_vport *vport)
+{
+	struct iecm_rxq_group *rx_qgrp;
+	struct iecm_queue *q;
+	int i, j, num_rxq;
+
+	if (!vport->rxq_grps)
+		return;
+
+	for (i = 0; i < vport->num_rxq_grp; i++) {
+		rx_qgrp = &vport->rxq_grps[i];
+
+		if (iecm_is_queue_model_split(vport->rxq_model)) {
+			num_rxq = rx_qgrp->splitq.num_rxq_sets;
+			for (j = 0; j < num_rxq; j++) {
+				q = &rx_qgrp->splitq.rxq_sets[j]->rxq;
+				iecm_rx_desc_rel(q, false,
+						 vport->rxq_model);
+			}
+
+			if (!rx_qgrp->splitq.bufq_sets)
+				continue;
+			for (j = 0; j < vport->num_bufqs_per_qgrp; j++) {
+				struct iecm_bufq_set *bufq_set =
+					&rx_qgrp->splitq.bufq_sets[j];
+
+				q = &bufq_set->bufq;
+				iecm_rx_desc_rel(q, true, vport->rxq_model);
+			}
+		} else {
+			for (j = 0; j < rx_qgrp->singleq.num_rxq; j++) {
+				q = rx_qgrp->singleq.rxqs[j];
+				iecm_rx_desc_rel(q, false,
+						 vport->rxq_model);
+			}
+		}
+	}
+}
+
+/**
+ * iecm_rx_buf_hw_update - Store the new tail and head values
+ * @rxq: queue to bump
+ * @val: new head index
+ */
+void iecm_rx_buf_hw_update(struct iecm_queue *rxq, u32 val)
+{
+	rxq->next_to_use = val;
+
+	if (unlikely(!rxq->tail))
+		return;
+	/* writel has an implicit memory barrier */
+	writel(val, rxq->tail);
+}
+
+/**
+ * iecm_alloc_page - allocate page to back RX buffer
+ * @rxbufq: pointer to queue struct
+ * @page_info: pointer to page metadata struct
+ */
+static int
+iecm_alloc_page(struct iecm_queue *rxbufq, struct iecm_page_info *page_info)
+{
+	/* alloc new page for storage */
+	page_info->page = alloc_page(GFP_ATOMIC | __GFP_NOWARN);
+	if (unlikely(!page_info->page))
+		return -ENOMEM;
+
+	/* map page for use */
+	page_info->dma = dma_map_page_attrs(rxbufq->dev, page_info->page,
+					    0, PAGE_SIZE, DMA_FROM_DEVICE,
+					    IECM_RX_DMA_ATTR);
+
+	/* if mapping failed free memory back to system since
+	 * there isn't much point in holding memory we can't use
+	 */
+	if (dma_mapping_error(rxbufq->dev, page_info->dma)) {
+		__free_pages(page_info->page, 0);
+		return -ENOMEM;
+	}
+
+	page_info->page_offset = 0;
+
+	/* initialize pagecnt_bias to claim we fully own page */
+	page_ref_add(page_info->page, USHRT_MAX - 1);
+	page_info->pagecnt_bias = USHRT_MAX;
+
+	return 0;
+}
+
+/**
+ * iecm_init_rx_buf_hw_alloc - allocate initial RX buffer pages
+ * @rxbufq: ring to use; equivalent to rxq when operating in singleq mode
+ * @buf: rx_buffer struct to modify
+ *
+ * Returns true if the page was successfully allocated or
+ * reused.
+ */
+bool iecm_init_rx_buf_hw_alloc(struct iecm_queue *rxbufq, struct iecm_rx_buf *buf)
+{
+	if (iecm_alloc_page(rxbufq, &buf->page_info[0]))
+		return false;
+
+#if (PAGE_SIZE < 8192)
+	if (rxbufq->rx_buf_size > IECM_RX_BUF_2048)
+		if (iecm_alloc_page(rxbufq, &buf->page_info[1]))
+			return false;
+#endif
+
+	buf->page_indx = 0;
+	buf->buf_size = rxbufq->rx_buf_size;
+
+	return true;
+}
+
+/**
+ * iecm_rx_hdr_buf_alloc_all - Allocate memory for header buffers
+ * @rxq: ring to use
+ *
+ * Returns 0 on success, negative on failure.
+ */
+static int iecm_rx_hdr_buf_alloc_all(struct iecm_queue *rxq)
+{
+	struct iecm_page_info *page_info;
+	int nr_pages, offset;
+	int i, j = 0;
+
+	rxq->rx_buf.hdr_buf = kcalloc(rxq->desc_count,
+				      sizeof(struct iecm_dma_mem *),
+				      GFP_KERNEL);
+	if (!rxq->rx_buf.hdr_buf)
+		return -ENOMEM;
+
+	for (i = 0; i < rxq->desc_count; i++) {
+		rxq->rx_buf.hdr_buf[i] = kcalloc(1,
+						 sizeof(struct iecm_dma_mem),
+						 GFP_KERNEL);
+		if (!rxq->rx_buf.hdr_buf[i])
+			goto unroll_buf_alloc;
+	}
+
+	/* Determine the number of pages necessary to back the total number of header buffers */
+	nr_pages = (rxq->desc_count * rxq->rx_hbuf_size) / PAGE_SIZE;
+	rxq->hbuf_pages.pages = kcalloc(nr_pages,
+					sizeof(struct iecm_page_info),
+					GFP_KERNEL);
+	if (!rxq->hbuf_pages.pages)
+		goto unroll_buf_alloc;
+
+	rxq->hbuf_pages.nr_pages = nr_pages;
+	for (i = 0; i < nr_pages; i++) {
+		if (iecm_alloc_page(rxq, &rxq->hbuf_pages.pages[i]))
+			goto unroll_buf_alloc;
+	}
+
+	page_info = &rxq->hbuf_pages.pages[0];
+	for (i = 0, offset = 0; i < rxq->desc_count; i++, offset += rxq->rx_hbuf_size) {
+		struct iecm_dma_mem *hbuf = rxq->rx_buf.hdr_buf[i];
+
+		/* Move to next page */
+		if (offset >= PAGE_SIZE) {
+			offset = 0;
+			page_info = &rxq->hbuf_pages.pages[++j];
+		}
+
+		hbuf->va = page_address(page_info->page) + offset;
+		hbuf->pa = page_info->dma + offset;
+		hbuf->size = rxq->rx_hbuf_size;
+	}
+
+	return 0;
+unroll_buf_alloc:
+	iecm_rx_hdr_buf_rel_all(rxq);
+	return -ENOMEM;
+}
+
+/**
+ * iecm_rx_buf_hw_alloc_all - Allocate receive buffers
+ * @rxbufq: queue for which the hw buffers are allocated; equivalent to rxq
+ * when operating in singleq mode
+ * @alloc_count: number of buffers to allocate
+ *
+ * Returns false if all allocations were successful, true if any fail
+ */
+static bool
+iecm_rx_buf_hw_alloc_all(struct iecm_queue *rxbufq, u16 alloc_count)
+{
+	u16 nta = rxbufq->next_to_alloc;
+	struct iecm_rx_buf *buf;
+
+	if (!alloc_count)
+		return false;
+
+	buf = &rxbufq->rx_buf.buf[nta];
+
+	do {
+		if (!iecm_init_rx_buf_hw_alloc(rxbufq, buf))
+			break;
+
+		buf++;
+		nta++;
+		if (unlikely(nta == rxbufq->desc_count)) {
+			buf = rxbufq->rx_buf.buf;
+			nta = 0;
+		}
+
+		alloc_count--;
+	} while (alloc_count);
+
+	return !!alloc_count;
+}
+
+/**
+ * iecm_rx_post_buf_desc - Post buffer to bufq descriptor ring
+ * @bufq: buffer queue to post to
+ * @buf_id: buffer id to post
+ */
+static void iecm_rx_post_buf_desc(struct iecm_queue *bufq, u16 buf_id)
+{
+	struct virtchnl2_splitq_rx_buf_desc *splitq_rx_desc = NULL;
+	struct iecm_page_info *page_info;
+	u16 nta = bufq->next_to_alloc;
+	struct iecm_rx_buf *buf;
+
+	splitq_rx_desc = IECM_SPLITQ_RX_BUF_DESC(bufq, nta);
+	buf = &bufq->rx_buf.buf[buf_id];
+	page_info = &buf->page_info[buf->page_indx];
+	if (bufq->rx_hsplit_en)
+		splitq_rx_desc->hdr_addr = cpu_to_le64(bufq->rx_buf.hdr_buf[buf_id]->pa);
+
+	splitq_rx_desc->pkt_addr = cpu_to_le64(page_info->dma +
+					       page_info->page_offset);
+	splitq_rx_desc->qword0.buf_id = cpu_to_le16(buf_id);
+
+	nta++;
+	if (unlikely(nta == bufq->desc_count))
+		nta = 0;
+	bufq->next_to_alloc = nta;
+}
+
+/**
+ * iecm_rx_post_init_bufs - Post initial buffers to bufq
+ * @bufq: buffer queue to post working set to
+ * @working_set: number of buffers to put in working set
+ */
+static void iecm_rx_post_init_bufs(struct iecm_queue *bufq,
+				   u16 working_set)
+{
+	int i;
+
+	for (i = 0; i < working_set; i++)
+		iecm_rx_post_buf_desc(bufq, i);
+
+	iecm_rx_buf_hw_update(bufq, bufq->next_to_alloc & ~(bufq->rx_buf_stride - 1));
+}
+
+/**
+ * iecm_rx_buf_alloc_all - Allocate memory for all buffer resources
+ * @rxbufq: queue for which the buffers are allocated; equivalent to
+ * rxq when operating in singleq mode
+ *
+ * Returns 0 on success, negative on failure
+ */
+static int iecm_rx_buf_alloc_all(struct iecm_queue *rxbufq)
+{
+	int err = 0;
+
+	/* Allocate book keeping buffers */
+	rxbufq->rx_buf.buf = kcalloc(rxbufq->desc_count,
+				     sizeof(struct iecm_rx_buf), GFP_KERNEL);
+	if (!rxbufq->rx_buf.buf) {
+		err = -ENOMEM;
+		goto rx_buf_alloc_all_out;
+	}
+
+	if (rxbufq->rx_hsplit_en) {
+		err = iecm_rx_hdr_buf_alloc_all(rxbufq);
+		if (err)
+			goto rx_buf_alloc_all_out;
+	}
+
+	/* Allocate buffers to be given to HW.	 */
+	if (iecm_is_queue_model_split(rxbufq->vport->rxq_model)) {
+		if (iecm_rx_buf_hw_alloc_all(rxbufq, rxbufq->desc_count - 1))
+			err = -ENOMEM;
+	} else {
+		if (iecm_rx_singleq_buf_hw_alloc_all(rxbufq, rxbufq->desc_count - 1))
+			err = -ENOMEM;
+	}
+
+rx_buf_alloc_all_out:
+	if (err)
+		iecm_rx_buf_rel_all(rxbufq);
+	return err;
+}
+
+/**
+ * iecm_rx_desc_alloc - Allocate queue Rx resources
+ * @rxq: Rx queue for which the resources are setup
+ * @bufq: buffer or completion queue
+ * @q_model: single or split queue model
+ *
+ * Returns 0 on success, negative on failure
+ */
+static int iecm_rx_desc_alloc(struct iecm_queue *rxq, bool bufq, s32 q_model)
+{
+	struct device *dev = rxq->dev;
+
+	/* As both single and split descriptors are 32 byte, memory size
+	 * will be same for all three singleq_base rx, buf., splitq_base
+	 * rx. So pick anyone of them for size
+	 */
+	if (bufq) {
+		rxq->size = rxq->desc_count *
+			sizeof(struct virtchnl2_splitq_rx_buf_desc);
+	} else {
+		rxq->size = rxq->desc_count *
+			sizeof(union virtchnl2_rx_desc);
+	}
+
+	/* Allocate descriptors and also round up to nearest 4K */
+	rxq->size = ALIGN(rxq->size, 4096);
+	rxq->desc_ring = dmam_alloc_coherent(dev, rxq->size,
+					     &rxq->dma, GFP_KERNEL);
+	if (!rxq->desc_ring) {
+		dev_info(dev, "Unable to allocate memory for the Rx descriptor ring, size=%d\n",
+			 rxq->size);
+		return -ENOMEM;
+	}
+
+	rxq->next_to_alloc = 0;
+	rxq->next_to_clean = 0;
+	rxq->next_to_use = 0;
+	set_bit(__IECM_Q_GEN_CHK, rxq->flags);
+
+	/* Allocate buffers for a rx queue if the q_model is single OR if it
+	 * is a buffer queue in split queue model
+	 */
+	if (bufq || !iecm_is_queue_model_split(q_model)) {
+		int err = 0;
+
+		err = iecm_rx_buf_alloc_all(rxq);
+		if (err) {
+			iecm_rx_desc_rel(rxq, bufq, q_model);
+			return err;
+		}
+	}
+	return 0;
+}
+
+/**
+ * iecm_rx_desc_alloc_all - allocate all RX queues resources
+ * @vport: virtual port structure
+ *
+ * Returns 0 on success, negative on failure
+ */
+static int iecm_rx_desc_alloc_all(struct iecm_vport *vport)
+{
+	struct device *dev = &vport->adapter->pdev->dev;
+	struct iecm_rxq_group *rx_qgrp;
+	int i, j, num_rxq, working_set;
+	struct iecm_queue *q;
+	int err = 0;
+
+	for (i = 0; i < vport->num_rxq_grp; i++) {
+		rx_qgrp = &vport->rxq_grps[i];
+		if (iecm_is_queue_model_split(vport->rxq_model))
+			num_rxq = rx_qgrp->splitq.num_rxq_sets;
+		else
+			num_rxq = rx_qgrp->singleq.num_rxq;
+
+		for (j = 0; j < num_rxq; j++) {
+			if (iecm_is_queue_model_split(vport->rxq_model))
+				q = &rx_qgrp->splitq.rxq_sets[j]->rxq;
+			else
+				q = rx_qgrp->singleq.rxqs[j];
+			err = iecm_rx_desc_alloc(q, false, vport->rxq_model);
+			if (err) {
+				dev_err(dev, "Memory allocation for Rx Queue %u failed\n",
+					i);
+				goto err_out;
+			}
+		}
+
+		if (iecm_is_queue_model_split(vport->rxq_model)) {
+			for (j = 0; j < vport->num_bufqs_per_qgrp; j++) {
+				q = &rx_qgrp->splitq.bufq_sets[j].bufq;
+				err = iecm_rx_desc_alloc(q, true,
+							 vport->rxq_model);
+				if (err) {
+					dev_err(dev, "Memory allocation for Rx Buffer Queue %u failed\n",
+						i);
+					goto err_out;
+				}
+
+				working_set = IECM_RX_BUFQ_WORKING_SET(q);
+				iecm_rx_post_init_bufs(q, working_set);
+			}
+		}
+	}
+err_out:
+	if (err)
+		iecm_rx_desc_rel_all(vport);
+	return err;
+}
+
 /**
  * iecm_txq_group_rel - Release all resources for txq groups
  * @vport: vport to release txq groups on
@@ -478,6 +1017,61 @@ static void iecm_txq_group_rel(struct iecm_vport *vport)
 	}
 }
 
+/**
+ * iecm_rxq_sw_queue_rel - Release software queue resources
+ * @rx_qgrp: rx queue group with software queues
+ */
+static void iecm_rxq_sw_queue_rel(struct iecm_rxq_group *rx_qgrp)
+{
+	int i, j;
+
+	for (i = 0; i < rx_qgrp->vport->num_bufqs_per_qgrp; i++) {
+		struct iecm_bufq_set *bufq_set = &rx_qgrp->splitq.bufq_sets[i];
+
+		for (j = 0; j < bufq_set->num_refillqs; j++) {
+			kfree(bufq_set->refillqs[j].ring);
+			bufq_set->refillqs[j].ring = NULL;
+		}
+		kfree(bufq_set->refillqs);
+		bufq_set->refillqs = NULL;
+	}
+}
+
+/**
+ * iecm_rxq_group_rel - Release all resources for rxq groups
+ * @vport: vport to release rxq groups on
+ */
+static void iecm_rxq_group_rel(struct iecm_vport *vport)
+{
+	if (vport->rxq_grps) {
+		int i;
+
+		for (i = 0; i < vport->num_rxq_grp; i++) {
+			struct iecm_rxq_group *rx_qgrp = &vport->rxq_grps[i];
+			int j, num_rxq;
+
+			if (iecm_is_queue_model_split(vport->rxq_model)) {
+				num_rxq = rx_qgrp->splitq.num_rxq_sets;
+				for (j = 0; j < num_rxq; j++) {
+					kfree(rx_qgrp->splitq.rxq_sets[j]);
+					rx_qgrp->splitq.rxq_sets[j] = NULL;
+				}
+				iecm_rxq_sw_queue_rel(rx_qgrp);
+				kfree(rx_qgrp->splitq.bufq_sets);
+				rx_qgrp->splitq.bufq_sets = NULL;
+			} else {
+				num_rxq = rx_qgrp->singleq.num_rxq;
+				for (j = 0; j < num_rxq; j++) {
+					kfree(rx_qgrp->singleq.rxqs[j]);
+					rx_qgrp->singleq.rxqs[j] = NULL;
+				}
+			}
+		}
+		kfree(vport->rxq_grps);
+		vport->rxq_grps = NULL;
+	}
+}
+
 /**
  * iecm_vport_queue_grp_rel_all - Release all queue groups
  * @vport: vport to release queue groups for
@@ -485,6 +1079,7 @@ static void iecm_txq_group_rel(struct iecm_vport *vport)
 static void iecm_vport_queue_grp_rel_all(struct iecm_vport *vport)
 {
 	iecm_txq_group_rel(vport);
+	iecm_rxq_group_rel(vport);
 }
 
 /**
@@ -496,6 +1091,7 @@ static void iecm_vport_queue_grp_rel_all(struct iecm_vport *vport)
 void iecm_vport_queues_rel(struct iecm_vport *vport)
 {
 	iecm_tx_desc_rel_all(vport);
+	iecm_rx_desc_rel_all(vport);
 	iecm_vport_queue_grp_rel_all(vport);
 
 	kfree(vport->txqs);
@@ -715,6 +1311,24 @@ void iecm_vport_calc_num_q_vec(struct iecm_vport *vport)
 }
 EXPORT_SYMBOL(iecm_vport_calc_num_q_vec);
 
+/**
+ * iecm_rxq_set_descids - set the descids supported by this queue
+ * @vport: virtual port data structure
+ * @q: rx queue for which descids are set
+ *
+ */
+static void iecm_rxq_set_descids(struct iecm_vport *vport, struct iecm_queue *q)
+{
+	if (vport->rxq_model == VIRTCHNL2_QUEUE_MODEL_SPLIT) {
+		q->rxdids = VIRTCHNL2_RXDID_1_FLEX_SPLITQ_M;
+	} else {
+		if (vport->base_rxd)
+			q->rxdids = VIRTCHNL2_RXDID_1_32B_BASE_M;
+		else
+			q->rxdids = VIRTCHNL2_RXDID_2_FLEX_SQ_NIC_M;
+	}
+}
+
 /**
  * iecm_set_vlan_tag_loc - set the tag location for a tx/rx queue
  * @adapter: adapter structure
@@ -827,6 +1441,152 @@ static int iecm_txq_group_alloc(struct iecm_vport *vport, int num_txq)
 	return err;
 }
 
+/**
+ * iecm_rxq_group_alloc - Allocate all rxq group resources
+ * @vport: vport to allocate rxq groups for
+ * @num_rxq: number of rxqs to allocate for each group
+ *
+ * Returns 0 on success, negative on failure
+ */
+static int iecm_rxq_group_alloc(struct iecm_vport *vport, int num_rxq)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	struct iecm_queue *q;
+	int i, k, err = 0;
+
+	vport->rxq_grps = kcalloc(vport->num_rxq_grp,
+				  sizeof(struct iecm_rxq_group), GFP_KERNEL);
+	if (!vport->rxq_grps)
+		return -ENOMEM;
+
+	for (i = 0; i < vport->num_rxq_grp; i++) {
+		struct iecm_rxq_group *rx_qgrp = &vport->rxq_grps[i];
+		int j;
+
+		rx_qgrp->vport = vport;
+		if (iecm_is_queue_model_split(vport->rxq_model)) {
+			rx_qgrp->splitq.num_rxq_sets = num_rxq;
+
+			for (j = 0; j < num_rxq; j++) {
+				rx_qgrp->splitq.rxq_sets[j] =
+					kzalloc(sizeof(struct iecm_rxq_set),
+						GFP_KERNEL);
+				if (!rx_qgrp->splitq.rxq_sets[j]) {
+					err = -ENOMEM;
+					goto err_alloc;
+				}
+			}
+
+			rx_qgrp->splitq.bufq_sets = kcalloc(vport->num_bufqs_per_qgrp,
+							    sizeof(struct iecm_bufq_set),
+							    GFP_KERNEL);
+			if (!rx_qgrp->splitq.bufq_sets) {
+				err = -ENOMEM;
+				goto err_alloc;
+			}
+
+			for (j = 0; j < vport->num_bufqs_per_qgrp; j++) {
+				struct iecm_bufq_set *bufq_set =
+					&rx_qgrp->splitq.bufq_sets[j];
+				int swq_size = sizeof(struct iecm_sw_queue);
+
+				q = &rx_qgrp->splitq.bufq_sets[j].bufq;
+				q->dev = &adapter->pdev->dev;
+				q->desc_count = vport->bufq_desc_count[j];
+				q->vport = vport;
+				q->rxq_grp = rx_qgrp;
+				q->idx = j;
+				q->rx_buf_size = vport->bufq_size[j];
+				q->rx_buffer_low_watermark = IECM_LOW_WATERMARK;
+				q->rx_buf_stride = IECM_RX_BUF_STRIDE;
+
+				if (test_bit(__IECM_PRIV_FLAGS_HDR_SPLIT,
+					     adapter->config_data.user_flags)) {
+					q->rx_hsplit_en = true;
+					q->rx_hbuf_size = IECM_HDR_BUF_SIZE;
+				}
+
+				bufq_set->num_refillqs = num_rxq;
+				bufq_set->refillqs = kcalloc(num_rxq,
+							     swq_size,
+							     GFP_KERNEL);
+				if (!bufq_set->refillqs) {
+					err = -ENOMEM;
+					goto err_alloc;
+				}
+				for (k = 0; k < bufq_set->num_refillqs; k++) {
+					struct iecm_sw_queue *refillq =
+						&bufq_set->refillqs[k];
+
+					refillq->dev =
+						&vport->adapter->pdev->dev;
+					refillq->buf_size = q->rx_buf_size;
+					refillq->desc_count =
+						vport->bufq_desc_count[j];
+					set_bit(__IECM_Q_GEN_CHK,
+						refillq->flags);
+					set_bit(__IECM_RFLQ_GEN_CHK,
+						refillq->flags);
+					refillq->ring = kcalloc(refillq->desc_count,
+								sizeof(u16),
+								GFP_KERNEL);
+					if (!refillq->ring) {
+						err = -ENOMEM;
+						goto err_alloc;
+					}
+				}
+			}
+		} else {
+			rx_qgrp->singleq.num_rxq = num_rxq;
+			for (j = 0; j < num_rxq; j++) {
+				rx_qgrp->singleq.rxqs[j] =
+					kzalloc(sizeof(*rx_qgrp->singleq.rxqs[j]), GFP_KERNEL);
+				if (!rx_qgrp->singleq.rxqs[j]) {
+					err = -ENOMEM;
+					goto err_alloc;
+				}
+			}
+		}
+
+		for (j = 0; j < num_rxq; j++) {
+			if (iecm_is_queue_model_split(vport->rxq_model)) {
+				q = &rx_qgrp->splitq.rxq_sets[j]->rxq;
+				rx_qgrp->splitq.rxq_sets[j]->refillq0 =
+				      &rx_qgrp->splitq.bufq_sets[0].refillqs[j];
+				rx_qgrp->splitq.rxq_sets[j]->refillq1 =
+				      &rx_qgrp->splitq.bufq_sets[1].refillqs[j];
+
+				if (test_bit(__IECM_PRIV_FLAGS_HDR_SPLIT,
+					     adapter->config_data.user_flags)) {
+					q->rx_hsplit_en = true;
+					q->rx_hbuf_size = IECM_HDR_BUF_SIZE;
+				}
+			} else {
+				q = rx_qgrp->singleq.rxqs[j];
+			}
+			q->dev = &adapter->pdev->dev;
+			q->desc_count = vport->rxq_desc_count;
+			q->vport = vport;
+			q->rxq_grp = rx_qgrp;
+			q->idx = (i * num_rxq) + j;
+			/* In splitq mode, RXQ buffer size should be
+			 * set to that of the first buffer queue
+			 * associated with this RXQ
+			 */
+			q->rx_buf_size = vport->bufq_size[0];
+			q->rx_buffer_low_watermark = IECM_LOW_WATERMARK;
+			q->rx_max_pkt_size = vport->netdev->mtu +
+							IECM_PACKET_HDR_PAD;
+			iecm_rxq_set_descids(vport, q);
+			iecm_set_vlan_tag_loc(adapter, q);
+		}
+	}
+err_alloc:
+	if (err)
+		iecm_rxq_group_rel(vport);
+	return err;
+}
+
 /**
  * iecm_vport_queue_grp_alloc_all - Allocate all queue groups/resources
  * @vport: vport with qgrps to allocate
@@ -841,6 +1601,11 @@ static int iecm_vport_queue_grp_alloc_all(struct iecm_vport *vport)
 	iecm_vport_calc_numq_per_grp(vport, &num_txq, &num_rxq);
 
 	err = iecm_txq_group_alloc(vport, num_txq);
+	if (err)
+		goto err_out;
+
+	err = iecm_rxq_group_alloc(vport, num_rxq);
+err_out:
 	if (err)
 		iecm_vport_queue_grp_rel_all(vport);
 	return err;
@@ -866,6 +1631,10 @@ int iecm_vport_queues_alloc(struct iecm_vport *vport)
 	if (err)
 		goto err_out;
 
+	err = iecm_rx_desc_alloc_all(vport);
+	if (err)
+		goto err_out;
+
 	err = iecm_vport_init_fast_path_txqs(vport);
 	if (err)
 		goto err_out;
diff --git a/drivers/net/ethernet/intel/include/iecm_txrx.h b/drivers/net/ethernet/intel/include/iecm_txrx.h
index 44c20f8a2039..5e29148938fb 100644
--- a/drivers/net/ethernet/intel/include/iecm_txrx.h
+++ b/drivers/net/ethernet/intel/include/iecm_txrx.h
@@ -198,6 +198,11 @@ struct iecm_page_info {
 	u16 pagecnt_bias;
 };
 
+struct iecm_rx_hdr_buf_pages {
+	u32 nr_pages;
+	struct iecm_page_info *pages;
+};
+
 struct iecm_rx_buf {
 #define IECM_RX_BUF_MAX_PAGES 2
 	struct iecm_page_info page_info[IECM_RX_BUF_MAX_PAGES];
@@ -498,6 +503,8 @@ struct iecm_queue {
 					 * with scatter-gather
 					 */
 	DECLARE_HASHTABLE(sched_buf_hash, 12);
+
+	struct iecm_rx_hdr_buf_pages hbuf_pages;
 } ____cacheline_internodealigned_in_smp;
 
 /* Software queues are used in splitq mode to manage buffers between rxq
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 11/19] iecm: add start_xmit and set_rx_mode
  2022-01-28  0:09 [Intel-wired-lan] [PATCH net-next 00/19] Add iecm and idpf Alan Brady
                   ` (9 preceding siblings ...)
  2022-01-28  0:10 ` [Intel-wired-lan] [PATCH net-next 10/19] iecm: alloc vport RX resources Alan Brady
@ 2022-01-28  0:10 ` Alan Brady
  2022-01-28 16:35   ` Alexander Lobakin
  2022-01-28  0:10 ` [Intel-wired-lan] [PATCH net-next 12/19] iecm: finish netdev_ops Alan Brady
                   ` (8 subsequent siblings)
  19 siblings, 1 reply; 89+ messages in thread
From: Alan Brady @ 2022-01-28  0:10 UTC (permalink / raw)
  To: intel-wired-lan

With open and stop done, this continues down the netdev_ops struct to add
start_xmit and set_rx_mode callbacks. The rest of the data path will be
added after netdev_ops are done.

Signed-off-by: Phani Burra <phani.r.burra@intel.com>
Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
Signed-off-by: Alan Brady <alan.brady@intel.com>
---
 drivers/net/ethernet/intel/iecm/iecm_lib.c    | 247 ++++++-
 drivers/net/ethernet/intel/iecm/iecm_txrx.c   | 667 ++++++++++++++++++
 drivers/net/ethernet/intel/include/iecm.h     |   1 +
 .../net/ethernet/intel/include/iecm_txrx.h    |  60 ++
 4 files changed, 970 insertions(+), 5 deletions(-)

diff --git a/drivers/net/ethernet/intel/iecm/iecm_lib.c b/drivers/net/ethernet/intel/iecm/iecm_lib.c
index 037a0e06bb7b..003057f48f0c 100644
--- a/drivers/net/ethernet/intel/iecm/iecm_lib.c
+++ b/drivers/net/ethernet/intel/iecm/iecm_lib.c
@@ -141,6 +141,17 @@ struct iecm_vport *iecm_netdev_to_vport(struct net_device *netdev)
 	return np->vport;
 }
 
+/**
+ * iecm_netdev_to_adapter - get an adapter handle from a netdev
+ * @netdev: network interface device structure
+ */
+struct iecm_adapter *iecm_netdev_to_adapter(struct net_device *netdev)
+{
+	struct iecm_netdev_priv *np = netdev_priv(netdev);
+
+	return np->vport->adapter;
+}
+
 /**
  * iecm_mb_intr_rel_irq - Free the IRQ association with the OS
  * @adapter: adapter structure
@@ -417,6 +428,61 @@ iecm_mac_filter *iecm_find_mac_filter(struct iecm_vport *vport,
 	return NULL;
 }
 
+/**
+ * __iecm_del_mac_filter - Delete MAC filter helper
+ * @vport: main vport struct
+ * @macaddr: address to delete
+ *
+ * Takes mac_filter_list_lock spinlock to set remove field for filter in list.
+ */
+static struct
+iecm_mac_filter *__iecm_del_mac_filter(struct iecm_vport *vport,
+				       const u8 *macaddr)
+{
+	struct iecm_mac_filter *f;
+
+	spin_lock_bh(&vport->adapter->mac_filter_list_lock);
+	f = iecm_find_mac_filter(vport, macaddr);
+	if (f) {
+		/* If filter was never synced to HW we can just delete it here,
+		 * otherwise mark for removal.
+		 */
+		if (f->add) {
+			list_del(&f->list);
+			kfree(f);
+			f = NULL;
+		} else {
+			f->remove = true;
+		}
+	}
+	spin_unlock_bh(&vport->adapter->mac_filter_list_lock);
+
+	return f;
+}
+
+/**
+ * iecm_del_mac_filter - Delete a MAC filter from the filter list
+ * @vport: main vport structure
+ * @macaddr: the MAC address
+ *
+ * Removes filter from list and if interface is up, tells hardware about the
+ * removed filter.
+ **/
+static void iecm_del_mac_filter(struct iecm_vport *vport, const u8 *macaddr)
+{
+	struct iecm_mac_filter *f;
+
+	if (!macaddr)
+		return;
+
+	f = __iecm_del_mac_filter(vport, macaddr);
+	if (!f)
+		return;
+
+	if (vport->adapter->state == __IECM_UP)
+		iecm_add_del_ether_addrs(vport, false, false);
+}
+
 /**
  * __iecm_add_mac_filter - Add mac filter helper function
  * @vport: main vport struct
@@ -1711,6 +1777,134 @@ void iecm_remove(struct pci_dev *pdev)
 }
 EXPORT_SYMBOL(iecm_remove);
 
+/**
+ * iecm_addr_sync - Callback for dev_(mc|uc)_sync to add address
+ * @netdev: the netdevice
+ * @addr: address to add
+ *
+ * Called by __dev_(mc|uc)_sync when an address needs to be added. We call
+ * __dev_(uc|mc)_sync from .set_rx_mode. Kernel takes addr_list_lock spinlock
+ * meaning we cannot sleep in this context. Due to this, we have to add the
+ * filter and send the virtchnl message asynchronously without waiting for the
+ * response from the other side. We won't know whether or not the operation
+ * actually succeeded until we get the message back.  Returns 0 on success,
+ * negative on failure.
+ */
+static int iecm_addr_sync(struct net_device *netdev, const u8 *addr)
+{
+	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
+
+	if (__iecm_add_mac_filter(vport, addr)) {
+		if (vport->adapter->state == __IECM_UP) {
+			set_bit(__IECM_ADD_ETH_REQ, vport->adapter->flags);
+			iecm_add_del_ether_addrs(vport, true, true);
+		}
+		return 0;
+	}
+
+	return -ENOMEM;
+}
+
+/**
+ * iecm_addr_unsync - Callback for dev_(mc|uc)_sync to remove address
+ * @netdev: the netdevice
+ * @addr: address to add
+ *
+ * Called by __dev_(mc|uc)_sync when an address needs to be added. We call
+ * __dev_(uc|mc)_sync from .set_rx_mode. Kernel takes addr_list_lock spinlock
+ * meaning we cannot sleep in this context. Due to this we have to delete the
+ * filter and send the virtchnl message asychronously without waiting for the
+ * return from the other side.  We won't know whether or not the operation
+ * actually succeeded until we get the message back. Returns 0 on success,
+ * negative on failure.
+ */
+static int iecm_addr_unsync(struct net_device *netdev, const u8 *addr)
+{
+	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
+
+	/* Under some circumstances, we might receive a request to delete
+	 * our own device address from our uc list. Because we store the
+	 * device address in the VSI's MAC/VLAN filter list, we need to ignore
+	 * such requests and not delete our device address from this list.
+	 */
+	if (ether_addr_equal(addr, netdev->dev_addr))
+		return 0;
+
+	if (__iecm_del_mac_filter(vport, addr)) {
+		if (vport->adapter->state == __IECM_UP) {
+			set_bit(__IECM_DEL_ETH_REQ, vport->adapter->flags);
+			iecm_add_del_ether_addrs(vport, false, true);
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * iecm_set_rx_mode - NDO callback to set the netdev filters
+ * @netdev: network interface device structure
+ *
+ * Stack takes addr_list_lock spinlock before calling our .set_rx_mode.  We
+ * cannot sleep in this context.
+ */
+static void iecm_set_rx_mode(struct net_device *netdev)
+{
+	struct iecm_adapter *adapter = iecm_netdev_to_adapter(netdev);
+
+	if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS, VIRTCHNL2_CAP_MACFILTER)) {
+		__dev_uc_sync(netdev, iecm_addr_sync, iecm_addr_unsync);
+		__dev_mc_sync(netdev, iecm_addr_sync, iecm_addr_unsync);
+	}
+
+	if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS, VIRTCHNL2_CAP_PROMISC)) {
+		bool changed = false;
+
+		/* IFF_PROMISC enables both unicast and multicast promiscuous,
+		 * while IFF_ALLMULTI only enables multicast such that:
+		 *
+		 * promisc  + allmulti		= unicast | multicast
+		 * promisc  + !allmulti		= unicast | multicast
+		 * !promisc + allmulti		= multicast
+		 */
+		if ((netdev->flags & IFF_PROMISC) &&
+		    !test_and_set_bit(__IECM_PROMISC_UC,
+				      adapter->config_data.user_flags)) {
+			changed = true;
+			dev_info(&adapter->pdev->dev, "Entering promiscuous mode\n");
+			if (!test_and_set_bit(__IECM_PROMISC_MC,
+					      adapter->flags))
+				dev_info(&adapter->pdev->dev, "Entering multicast promiscuous mode\n");
+		}
+		if (!(netdev->flags & IFF_PROMISC) &&
+		    test_and_clear_bit(__IECM_PROMISC_UC,
+				       adapter->config_data.user_flags)) {
+			changed = true;
+			dev_info(&adapter->pdev->dev, "Leaving promiscuous mode\n");
+		}
+		if (netdev->flags & IFF_ALLMULTI &&
+		    !test_and_set_bit(__IECM_PROMISC_MC,
+				      adapter->config_data.user_flags)) {
+			changed = true;
+			dev_info(&adapter->pdev->dev, "Entering multicast promiscuous mode\n");
+		}
+		if (!(netdev->flags & (IFF_ALLMULTI | IFF_PROMISC)) &&
+		    test_and_clear_bit(__IECM_PROMISC_MC,
+				       adapter->config_data.user_flags)) {
+			changed = true;
+			dev_info(&adapter->pdev->dev, "Leaving multicast promiscuous mode\n");
+		}
+
+		if (changed) {
+			int err = iecm_set_promiscuous(adapter);
+
+			if (err) {
+				dev_info(&adapter->pdev->dev, "Failed to set promiscuous mode: %d\n",
+					 err);
+			}
+		}
+	}
+}
+
 /**
  * iecm_open - Called when a network interface becomes active
  * @netdev: network interface device structure
@@ -1730,6 +1924,49 @@ static int iecm_open(struct net_device *netdev)
 	return iecm_vport_open(np->vport, true);
 }
 
+/**
+ * iecm_set_mac - NDO callback to set port mac address
+ * @netdev: network interface device structure
+ * @p: pointer to an address structure
+ *
+ * Returns 0 on success, negative on failure
+ **/
+static int iecm_set_mac(struct net_device *netdev, void *p)
+{
+	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
+	struct iecm_mac_filter *f;
+	struct sockaddr *addr = p;
+
+	if (!iecm_is_cap_ena(vport->adapter, IECM_OTHER_CAPS,
+			     VIRTCHNL2_CAP_MACFILTER)) {
+		dev_info(&vport->adapter->pdev->dev, "Setting MAC address is not supported\n");
+		return -EOPNOTSUPP;
+	}
+
+	if (!is_valid_ether_addr(addr->sa_data)) {
+		dev_info(&vport->adapter->pdev->dev, "Invalid MAC address: %pM\n",
+			 addr->sa_data);
+		return -EADDRNOTAVAIL;
+	}
+
+	if (ether_addr_equal(netdev->dev_addr, addr->sa_data))
+		return 0;
+
+	/* Delete the current filter */
+	if (is_valid_ether_addr(vport->default_mac_addr))
+		iecm_del_mac_filter(vport, vport->default_mac_addr);
+
+	/* Add new filter */
+	f = iecm_add_mac_filter(vport, addr->sa_data);
+
+	if (f) {
+		ether_addr_copy(vport->default_mac_addr, addr->sa_data);
+		dev_addr_mod(netdev, 0, addr->sa_data, ETH_ALEN);
+	}
+
+	return f ? 0 : -ENOMEM;
+}
+
 void *iecm_alloc_dma_mem(struct iecm_hw *hw, struct iecm_dma_mem *mem, u64 size)
 {
 	struct iecm_adapter *adapter = (struct iecm_adapter *)hw->back;
@@ -1756,10 +1993,10 @@ void iecm_free_dma_mem(struct iecm_hw *hw, struct iecm_dma_mem *mem)
 static const struct net_device_ops iecm_netdev_ops_splitq = {
 	.ndo_open = iecm_open,
 	.ndo_stop = iecm_stop,
-	.ndo_start_xmit = NULL,
-	.ndo_set_rx_mode = NULL,
+	.ndo_start_xmit = iecm_tx_splitq_start,
+	.ndo_set_rx_mode = iecm_set_rx_mode,
 	.ndo_validate_addr = eth_validate_addr,
-	.ndo_set_mac_address = NULL,
+	.ndo_set_mac_address = iecm_set_mac,
 	.ndo_change_mtu = NULL,
 	.ndo_get_stats64 = NULL,
 	.ndo_fix_features = NULL,
@@ -1773,9 +2010,9 @@ static const struct net_device_ops iecm_netdev_ops_singleq = {
 	.ndo_open = iecm_open,
 	.ndo_stop = iecm_stop,
 	.ndo_start_xmit = NULL,
-	.ndo_set_rx_mode = NULL,
+	.ndo_set_rx_mode = iecm_set_rx_mode,
 	.ndo_validate_addr = eth_validate_addr,
-	.ndo_set_mac_address = NULL,
+	.ndo_set_mac_address = iecm_set_mac,
 	.ndo_change_mtu = NULL,
 	.ndo_get_stats64 = NULL,
 	.ndo_fix_features = NULL,
diff --git a/drivers/net/ethernet/intel/iecm/iecm_txrx.c b/drivers/net/ethernet/intel/iecm/iecm_txrx.c
index fb6a61277b00..ef5fe659389b 100644
--- a/drivers/net/ethernet/intel/iecm/iecm_txrx.c
+++ b/drivers/net/ethernet/intel/iecm/iecm_txrx.c
@@ -1655,6 +1655,673 @@ int iecm_vport_queues_alloc(struct iecm_vport *vport)
 	return err;
 }
 
+/**
+ * iecm_tx_splitq_build_ctb - populate command tag and size for queue
+ * based scheduling descriptors
+ * @desc: descriptor to populate
+ * @parms: pointer to tx params struct
+ * @td_cmd: command to be filled in desc
+ * @size: size of buffer
+ */
+void
+iecm_tx_splitq_build_ctb(union iecm_tx_flex_desc *desc,
+			 struct iecm_tx_splitq_params *parms,
+			 u16 td_cmd, u16 size)
+{
+	desc->q.qw1.cmd_dtype =
+		cpu_to_le16(parms->dtype & IECM_FLEX_TXD_QW1_DTYPE_M);
+	desc->q.qw1.cmd_dtype |=
+		cpu_to_le16((td_cmd << IECM_FLEX_TXD_QW1_CMD_S) &
+			    IECM_FLEX_TXD_QW1_CMD_M);
+	desc->q.qw1.buf_size = cpu_to_le16((u16)size);
+	desc->q.qw1.flex.l2tags.l2tag1 = cpu_to_le16(parms->td_tag);
+}
+
+/**
+ * iecm_tx_splitq_build_flow_desc - populate command tag and size for flow
+ * scheduling descriptors
+ * @desc: descriptor to populate
+ * @parms: pointer to tx params struct
+ * @td_cmd: command to be filled in desc
+ * @size: size of buffer
+ */
+void
+iecm_tx_splitq_build_flow_desc(union iecm_tx_flex_desc *desc,
+			       struct iecm_tx_splitq_params *parms,
+			       u16 td_cmd, u16 size)
+{
+	desc->flow.qw1.cmd_dtype = (u16)parms->dtype | td_cmd;
+	desc->flow.qw1.rxr_bufsize = cpu_to_le16((u16)size);
+	desc->flow.qw1.compl_tag = cpu_to_le16(parms->compl_tag);
+}
+
+/**
+ * iecm_tx_buf_avail - Stop Tx if no enough book keeping buffers are available
+ * @tx_q: the queue to be checked
+ *
+ * Return -EBUSY if Tx queue stop is needed, else 0
+ */
+static int iecm_tx_buf_avail(struct iecm_queue *tx_q)
+{
+	/* If We have less than a quarter of the total desc_count left
+	 * stop the queue to wait for more completions
+	 */
+	if (unlikely(IECM_TX_BUF_UNUSED(tx_q) < tx_q->desc_count >> 2)) {
+		netif_stop_subqueue(tx_q->vport->netdev, tx_q->idx);
+		return -EBUSY;
+	}
+	return 0;
+}
+
+/**
+ * __iecm_tx_maybe_stop - 2nd level check for Tx stop conditions
+ * @tx_q: the queue to be checked
+ * @size: the size buffer we want to assure is available
+ *
+ * Returns -EBUSY if a stop is needed, else 0
+ */
+static int
+__iecm_tx_maybe_stop(struct iecm_queue *tx_q, unsigned int size)
+{
+	netif_stop_subqueue(tx_q->vport->netdev, tx_q->idx);
+
+	/* Memory barrier before checking head and tail */
+	smp_mb();
+
+	/* Check again in a case another CPU has just made room available. */
+	if (likely(IECM_DESC_UNUSED(tx_q) < size))
+		return -EBUSY;
+
+	/* A reprieve! - use start_subqueue because it doesn't call schedule */
+	netif_start_subqueue(tx_q->vport->netdev, tx_q->idx);
+
+	return 0;
+}
+
+/**
+ * iecm_tx_maybe_stop - 1st level check for Tx stop conditions
+ * @tx_q: the queue to be checked
+ * @size: number of descriptors we want to assure is available
+ *
+ * Returns 0 if stop is not needed
+ */
+int iecm_tx_maybe_stop(struct iecm_queue *tx_q, unsigned int size)
+{
+	if (likely(IECM_DESC_UNUSED(tx_q) >= size))
+		return 0;
+
+	return __iecm_tx_maybe_stop(tx_q, size);
+}
+
+/**
+ * iecm_tx_buf_hw_update - Store the new tail value
+ * @tx_q: queue to bump
+ * @val: new tail index
+ * @xmit_more: more skb's pending
+ *
+ * The naming here is special in that 'hw' signals that this function is about
+ * to do a register write to update our queue status. We know this can only
+ * mean tail here as HW should be owning head for TX.
+ */
+void iecm_tx_buf_hw_update(struct iecm_queue *tx_q, u32 val,
+			   bool xmit_more)
+{
+	struct netdev_queue *nq;
+
+	nq = netdev_get_tx_queue(tx_q->vport->netdev, tx_q->idx);
+	tx_q->next_to_use = val;
+
+	iecm_tx_maybe_stop(tx_q, IECM_TX_DESC_NEEDED);
+
+	/* Force memory writes to complete before letting h/w
+	 * know there are new descriptors to fetch.  (Only
+	 * applicable for weak-ordered memory model archs,
+	 * such as IA-64).
+	 */
+	wmb();
+
+	/* notify HW of packet */
+	if (netif_xmit_stopped(nq) || !xmit_more)
+		writel(val, tx_q->tail);
+}
+
+/**
+ * iecm_size_to_txd_count - Get the number of descriptors needed for Tx
+ * @size: transmit request size in bytes
+ *
+ * Due to hardware alignment restrictions (4K alignment), we need to assume
+ * that we can have no more than 12K of data per descriptor, even though each
+ * descriptor can take up to 16K - 1 bytes of aligned memory.
+ */
+unsigned int iecm_size_to_txd_count(unsigned int size)
+{
+	return (size / 12288) + IECM_TX_DESCS_FOR_SKB_DATA_PTR;
+}
+
+/**
+ * iecm_tx_desc_count_required - calculate number of Tx descriptors needed
+ * @skb: send buffer
+ *
+ * Returns number of data descriptors needed for this skb.
+ */
+unsigned int iecm_tx_desc_count_required(struct sk_buff *skb)
+{
+	const skb_frag_t *frag = &skb_shinfo(skb)->frags[0];
+	unsigned int nr_frags = skb_shinfo(skb)->nr_frags;
+	unsigned int count = 0, size = skb_headlen(skb);
+
+	for (;;) {
+		count += iecm_size_to_txd_count(size);
+
+		if (!nr_frags--)
+			break;
+
+		size = skb_frag_size(frag++);
+	}
+
+	return count;
+}
+
+/**
+ * iecm_tx_splitq_map - Build the Tx flex descriptor
+ * @tx_q: queue to send buffer on
+ * @parms: pointer to splitq params struct
+ * @first: first buffer info buffer to use
+ *
+ * This function loops over the skb data pointed to by *first
+ * and gets a physical address for each memory location and programs
+ * it and the length into the transmit flex descriptor.
+ */
+static void
+iecm_tx_splitq_map(struct iecm_queue *tx_q,
+		   struct iecm_tx_splitq_params *parms,
+		   struct iecm_tx_buf *first)
+{
+	union iecm_tx_flex_desc *tx_desc;
+	unsigned int data_len, size;
+	struct iecm_tx_buf *tx_buf;
+	u16 i = tx_q->next_to_use;
+	struct netdev_queue *nq;
+	struct sk_buff *skb;
+	skb_frag_t *frag;
+	u16 td_cmd = 0;
+	dma_addr_t dma;
+
+	skb = first->skb;
+
+	td_cmd = parms->offload.td_cmd;
+	parms->compl_tag = tx_q->tx_buf_key;
+
+	data_len = skb->data_len;
+	size = skb_headlen(skb);
+
+	tx_desc = IECM_FLEX_TX_DESC(tx_q, i);
+
+	dma = dma_map_single(tx_q->dev, skb->data, size, DMA_TO_DEVICE);
+
+	tx_buf = first;
+
+	for (frag = &skb_shinfo(skb)->frags[0];; frag++) {
+		unsigned int max_data = IECM_TX_MAX_DESC_DATA_ALIGNED;
+
+		if (dma_mapping_error(tx_q->dev, dma))
+			goto dma_error;
+
+		/* record length, and DMA address */
+		dma_unmap_len_set(tx_buf, len, size);
+		dma_unmap_addr_set(tx_buf, dma, dma);
+
+		/* align size to end of page */
+		max_data += -dma & (IECM_TX_MAX_READ_REQ_SIZE - 1);
+
+		/* buf_addr is in same location for both desc types */
+		tx_desc->q.buf_addr = cpu_to_le64(dma);
+
+		/* account for data chunks larger than the hardware
+		 * can handle
+		 */
+		while (unlikely(size > IECM_TX_MAX_DESC_DATA)) {
+			parms->splitq_build_ctb(tx_desc, parms, td_cmd,
+						max_data);
+
+			tx_desc++;
+			i++;
+
+			if (i == tx_q->desc_count) {
+				tx_desc = IECM_FLEX_TX_DESC(tx_q, 0);
+				i = 0;
+			}
+
+			dma += max_data;
+			size -= max_data;
+
+			max_data = IECM_TX_MAX_DESC_DATA_ALIGNED;
+			/* buf_addr is in same location for both desc types */
+			tx_desc->q.buf_addr = cpu_to_le64(dma);
+		}
+
+		if (likely(!data_len))
+			break;
+		parms->splitq_build_ctb(tx_desc, parms, td_cmd, size);
+		tx_desc++;
+		i++;
+
+		if (i == tx_q->desc_count) {
+			tx_desc = IECM_FLEX_TX_DESC(tx_q, 0);
+			i = 0;
+		}
+
+		size = skb_frag_size(frag);
+		data_len -= size;
+
+		dma = skb_frag_dma_map(tx_q->dev, frag, 0, size,
+				       DMA_TO_DEVICE);
+
+		tx_buf->compl_tag = parms->compl_tag;
+		tx_buf = &tx_q->tx_buf[i];
+	}
+
+	/* record bytecount for BQL */
+	nq = netdev_get_tx_queue(tx_q->vport->netdev, tx_q->idx);
+	netdev_tx_sent_queue(nq, first->bytecount);
+
+	/* record SW timestamp if HW timestamp is not available */
+	skb_tx_timestamp(first->skb);
+
+	/* write last descriptor with RS and EOP bits */
+	td_cmd |= parms->eop_cmd;
+	parms->splitq_build_ctb(tx_desc, parms, td_cmd, size);
+	i++;
+	if (i == tx_q->desc_count)
+		i = 0;
+
+	/* set next_to_watch value indicating a packet is present */
+	first->next_to_watch = tx_desc;
+	tx_buf->compl_tag = parms->compl_tag++;
+
+	iecm_tx_buf_hw_update(tx_q, i, netdev_xmit_more());
+
+	/* Update TXQ Completion Tag key for next buffer */
+	tx_q->tx_buf_key = parms->compl_tag;
+
+	return;
+
+dma_error:
+	/* clear dma mappings for failed tx_buf map */
+	for (;;) {
+		tx_buf = &tx_q->tx_buf[i];
+		iecm_tx_buf_rel(tx_q, tx_buf);
+		if (tx_buf == first)
+			break;
+		if (i == 0)
+			i = tx_q->desc_count;
+		i--;
+	}
+
+	tx_q->next_to_use = i;
+}
+
+/**
+ * iecm_tx_prepare_vlan_flags - prepare generic vlan tagging for HW
+ * @tx_q: txq to find the tag location
+ * @first: pointer to struct iecm_tx_buf
+ * @skb: skb being xmitted
+ */
+void iecm_tx_prepare_vlan_flags(struct iecm_queue *tx_q,
+				struct iecm_tx_buf *first,
+				struct sk_buff *skb)
+{
+	struct iecm_vport *vport = tx_q->vport;
+	u32 tx_flags = 0;
+
+	/* Stack sets protocol to 8021q when offload is disabled so SW can take
+	 * any necessary steps to handle it.  We don't need to do anything,
+	 * just set protocol to encapsulated type.
+	 */
+	if (skb->protocol == htons(ETH_P_8021Q) &&
+	    !iecm_is_feature_ena(vport, NETIF_F_HW_VLAN_CTAG_RX)) {
+		skb->protocol = vlan_get_protocol(skb);
+		return;
+	}
+
+	if (!skb_vlan_tag_present(skb))
+		return;
+
+	tx_flags |= skb_vlan_tag_get(skb) << IECM_TX_FLAGS_VLAN_SHIFT;
+	tx_flags |= IECM_TX_FLAGS_VLAN_TAG;
+	if (test_bit(__IECM_Q_VLAN_TAG_LOC_L2TAG2, tx_q->flags))
+		tx_flags |= IECM_TX_FLAGS_HW_OUTER_SINGLE_VLAN;
+	else if (test_bit(__IECM_Q_VLAN_TAG_LOC_L2TAG1, tx_q->flags))
+		tx_flags |= IECM_TX_FLAGS_HW_VLAN;
+	else
+		dev_dbg(&vport->adapter->pdev->dev, "Unsupported Tx VLAN tag location requested\n");
+
+	first->tx_flags |= tx_flags;
+}
+
+/**
+ * iecm_tso - computes mss and TSO length to prepare for TSO
+ * @first: pointer to struct iecm_tx_buf
+ * @off: pointer to struct that holds offload parameters
+ *
+ * Returns error (negative) if TSO doesn't apply to the given skb,
+ * 0 otherwise.
+ *
+ * Note: this function can be used in the splitq and singleq paths
+ */
+int iecm_tso(struct iecm_tx_buf *first, struct iecm_tx_offload_params *off)
+{
+	struct sk_buff *skb = first->skb;
+	union {
+		struct iphdr *v4;
+		struct ipv6hdr *v6;
+		unsigned char *hdr;
+	} ip;
+	union {
+		struct tcphdr *tcp;
+		struct udphdr *udp;
+		unsigned char *hdr;
+	} l4;
+	u32 paylen, l4_start;
+	int err;
+
+	if (!skb_is_gso(skb))
+		return 0;
+
+	err = skb_cow_head(skb, 0);
+	if (err < 0)
+		return err;
+
+	ip.hdr = skb_network_header(skb);
+	l4.hdr = skb_transport_header(skb);
+
+	/* initialize outer IP header fields */
+	if (ip.v4->version == 4) {
+		ip.v4->tot_len = 0;
+		ip.v4->check = 0;
+	} else if (ip.v4->version == 6) {
+		ip.v6->payload_len = 0;
+	} else {
+		return -EINVAL;
+	}
+
+	l4_start = skb_transport_offset(skb);
+
+	/* remove payload length from checksum */
+	paylen = skb->len - l4_start;
+
+	switch (skb_shinfo(skb)->gso_type) {
+	case SKB_GSO_TCPV4:
+	case SKB_GSO_TCPV6:
+		csum_replace_by_diff(&l4.tcp->check,
+				     (__force __wsum)htonl(paylen));
+
+		/* compute length of segmentation header */
+		off->tso_hdr_len = tcp_hdrlen(skb) + l4_start;
+		break;
+	case SKB_GSO_UDP_L4:
+		csum_replace_by_diff(&l4.udp->check,
+				     (__force __wsum)htonl(paylen));
+		/* compute length of segmentation header */
+		off->tso_hdr_len = sizeof(struct udphdr) + l4_start;
+		l4.udp->len =
+			htons(skb_shinfo(skb)->gso_size +
+			      sizeof(struct udphdr));
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	off->tso_len = skb->len - off->tso_hdr_len;
+	off->mss = skb_shinfo(skb)->gso_size;
+
+	/* update gso_segs and bytecount */
+	first->gso_segs = skb_shinfo(skb)->gso_segs;
+	first->bytecount = qdisc_skb_cb(skb)->pkt_len;
+
+	first->tx_flags |= IECM_TX_FLAGS_TSO;
+
+	return 0;
+}
+
+/**
+ * __iecm_chk_linearize - Check skb is not using too many buffers
+ * @skb: send buffer
+ * @max_bufs: maximum number of buffers
+ *
+ * For TSO we need to count the TSO header and segment payload separately.  As
+ * such we need to check cases where we have max_bufs-1 fragments or more as we
+ * can potentially require max_bufs+1 DMA transactions, 1 for the TSO header, 1
+ * for the segment payload in the first descriptor, and another max_buf-1 for
+ * the fragments.
+ */
+static bool __iecm_chk_linearize(struct sk_buff *skb, unsigned int max_bufs)
+{
+	const skb_frag_t *frag, *stale;
+	int nr_frags, sum;
+
+	/* no need to check if number of frags is less than max_bufs - 1 */
+	nr_frags = skb_shinfo(skb)->nr_frags;
+	if (nr_frags < (max_bufs - 1))
+		return false;
+
+	/* We need to walk through the list and validate that each group
+	 * of max_bufs-2 fragments totals@least gso_size.
+	 */
+	nr_frags -= max_bufs - 2;
+	frag = &skb_shinfo(skb)->frags[0];
+
+	/* Initialize size to the negative value of gso_size minus 1.  We use
+	 * this as the worst case scenario in which the frag ahead of us only
+	 * provides one byte which is why we are limited to max_bufs-2
+	 * descriptors for a single transmit as the header and previous
+	 * fragment are already consuming 2 descriptors.
+	 */
+	sum = 1 - skb_shinfo(skb)->gso_size;
+
+	/* Add size of frags 0 through 4 to create our initial sum */
+	sum += skb_frag_size(frag++);
+	sum += skb_frag_size(frag++);
+	sum += skb_frag_size(frag++);
+	sum += skb_frag_size(frag++);
+	sum += skb_frag_size(frag++);
+
+	/* Walk through fragments adding latest fragment, testing it, and
+	 * then removing stale fragments from the sum.
+	 */
+	for (stale = &skb_shinfo(skb)->frags[0];; stale++) {
+		int stale_size = skb_frag_size(stale);
+
+		sum += skb_frag_size(frag++);
+
+		/* The stale fragment may present us with a smaller
+		 * descriptor than the actual fragment size. To account
+		 * for that we need to remove all the data on the front and
+		 * figure out what the remainder would be in the last
+		 * descriptor associated with the fragment.
+		 */
+		if (stale_size > IECM_TX_MAX_DESC_DATA) {
+			int align_pad = -(skb_frag_off(stale)) &
+					(IECM_TX_MAX_READ_REQ_SIZE - 1);
+
+			sum -= align_pad;
+			stale_size -= align_pad;
+
+			do {
+				sum -= IECM_TX_MAX_DESC_DATA_ALIGNED;
+				stale_size -= IECM_TX_MAX_DESC_DATA_ALIGNED;
+			} while (stale_size > IECM_TX_MAX_DESC_DATA);
+		}
+
+		/* if sum is negative we failed to make sufficient progress */
+		if (sum < 0)
+			return true;
+
+		if (!nr_frags--)
+			break;
+
+		sum -= stale_size;
+	}
+
+	return false;
+}
+
+/**
+ * iecm_chk_linearize - Check if skb exceeds max descriptors per packet
+ * @skb: send buffer
+ * @max_bufs: maximum scatter gather buffers for single packet
+ * @count: number of buffers this packet needs
+ *
+ * Make sure we don't exceed maximum scatter gather buffers for a single
+ * packet. We have to do some special checking around the boundary (max_bufs-1)
+ * if TSO is on since we need count the TSO header and payload separately.
+ * E.g.: a packet with 7 fragments can require 9 DMA transactions; 1 for TSO
+ * header, 1 for segment payload, and then 7 for the fragments.
+ */
+bool iecm_chk_linearize(struct sk_buff *skb, unsigned int max_bufs,
+			unsigned int count)
+{
+	if (likely(count < max_bufs))
+		return false;
+	if (skb_is_gso(skb))
+		return __iecm_chk_linearize(skb, max_bufs);
+
+	return count != max_bufs;
+}
+
+/**
+ * iecm_tx_splitq_frame - Sends buffer on Tx ring using flex descriptors
+ * @skb: send buffer
+ * @tx_q: queue to send buffer on
+ *
+ * Returns NETDEV_TX_OK if sent, else an error code
+ */
+static netdev_tx_t
+iecm_tx_splitq_frame(struct sk_buff *skb, struct iecm_queue *tx_q)
+{
+	struct iecm_tx_splitq_params tx_parms = {
+		NULL, (enum iecm_tx_desc_dtype_value)0, 0, {0}, {0}
+	};
+	struct iecm_tx_buf *first;
+	unsigned int count;
+
+	count = iecm_tx_desc_count_required(skb);
+	if (iecm_chk_linearize(skb, tx_q->tx_max_bufs, count)) {
+		if (__skb_linearize(skb)) {
+			dev_kfree_skb_any(skb);
+			return NETDEV_TX_OK;
+		}
+		count = iecm_size_to_txd_count(skb->len);
+		tx_q->vport->port_stats.tx_linearize++;
+	}
+
+	if (iecm_tx_maybe_stop(tx_q, count + IECM_TX_DESCS_PER_CACHE_LINE +
+			       IECM_TX_DESCS_FOR_CTX)) {
+		return NETDEV_TX_BUSY;
+	}
+
+	/* Also check for available book keeping buffers */
+	if (iecm_tx_buf_avail(tx_q))
+		return NETDEV_TX_BUSY;
+
+	/* record the location of the first descriptor for this packet */
+	first = &tx_q->tx_buf[tx_q->next_to_use];
+	first->skb = skb;
+	first->bytecount = max_t(unsigned int, skb->len, ETH_ZLEN);
+	first->gso_segs = 1;
+	first->tx_flags = 0;
+
+	iecm_tx_prepare_vlan_flags(tx_q, first, skb);
+
+	if (iecm_tso(first, &tx_parms.offload) < 0) {
+		/* If tso returns an error, drop the packet */
+		dev_kfree_skb_any(skb);
+		return NETDEV_TX_OK;
+	}
+
+	if (first->tx_flags & IECM_TX_FLAGS_TSO) {
+		/* If tso is needed, set up context desc */
+		union iecm_flex_tx_ctx_desc *ctx_desc;
+		int i = tx_q->next_to_use;
+
+		/* grab the next descriptor */
+		ctx_desc = IECM_FLEX_TX_CTX_DESC(tx_q, i);
+		i++;
+		tx_q->next_to_use = (i < tx_q->desc_count) ? i : 0;
+
+		ctx_desc->tso.qw1.cmd_dtype =
+				cpu_to_le16(IECM_TX_DESC_DTYPE_FLEX_TSO_CTX |
+					    IECM_TX_FLEX_CTX_DESC_CMD_TSO);
+		ctx_desc->tso.qw0.flex_tlen =
+				cpu_to_le32(tx_parms.offload.tso_len &
+					    IECM_TXD_FLEX_CTX_TLEN_M);
+		ctx_desc->tso.qw0.mss_rt =
+				cpu_to_le16(tx_parms.offload.mss &
+					    IECM_TXD_FLEX_CTX_MSS_RT_M);
+		ctx_desc->tso.qw0.hdr_len = tx_parms.offload.tso_hdr_len;
+
+		u64_stats_update_begin(&tx_q->stats_sync);
+		tx_q->q_stats.tx.lso_pkts++;
+		u64_stats_update_end(&tx_q->stats_sync);
+	}
+
+	if (test_bit(__IECM_Q_FLOW_SCH_EN, tx_q->flags)) {
+		tx_parms.dtype = IECM_TX_DESC_DTYPE_FLEX_FLOW_SCHE;
+		tx_parms.splitq_build_ctb = iecm_tx_splitq_build_flow_desc;
+		tx_parms.eop_cmd =
+			IECM_TXD_FLEX_FLOW_CMD_EOP | IECM_TXD_FLEX_FLOW_CMD_RE;
+
+		if (skb->ip_summed == CHECKSUM_PARTIAL)
+			tx_parms.offload.td_cmd |= IECM_TXD_FLEX_FLOW_CMD_CS_EN;
+
+	} else {
+		tx_parms.dtype = IECM_TX_DESC_DTYPE_FLEX_L2TAG1_L2TAG2;
+		tx_parms.splitq_build_ctb = iecm_tx_splitq_build_ctb;
+		tx_parms.eop_cmd = IECM_TXD_LAST_DESC_CMD;
+
+		if (skb->ip_summed == CHECKSUM_PARTIAL)
+			tx_parms.offload.td_cmd |= IECM_TX_FLEX_DESC_CMD_CS_EN;
+
+		/* VLAN Offload can only be used with queue based scheduling */
+		if (first->tx_flags & IECM_TX_FLAGS_VLAN_TAG) {
+			tx_parms.offload.td_cmd |= (u64)IECM_TX_FLEX_DESC_CMD_IL2TAG1;
+			tx_parms.td_tag = (first->tx_flags & IECM_TX_FLAGS_VLAN_MASK) >>
+					  IECM_TX_FLAGS_VLAN_SHIFT;
+		}
+	}
+
+	iecm_tx_splitq_map(tx_q, &tx_parms, first);
+
+	return NETDEV_TX_OK;
+}
+
+/**
+ * iecm_tx_splitq_start - Selects the right Tx queue to send buffer
+ * @skb: send buffer
+ * @netdev: network interface device structure
+ *
+ * Returns NETDEV_TX_OK if sent, else an error code
+ */
+netdev_tx_t iecm_tx_splitq_start(struct sk_buff *skb,
+				 struct net_device *netdev)
+{
+	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
+	struct iecm_queue *tx_q;
+
+	if (skb->queue_mapping >= vport->num_txq)
+		return -EINVAL;
+
+	tx_q = vport->txqs[skb->queue_mapping];
+
+	/* hardware can't handle really short frames, hardware padding works
+	 * beyond this point
+	 */
+	if (skb_put_padto(skb, IECM_TX_MIN_LEN))
+		return NETDEV_TX_OK;
+
+	return iecm_tx_splitq_frame(skb, tx_q);
+}
+
 /**
  * iecm_vport_intr_clean_queues - MSIX mode Interrupt Handler
  * @irq: interrupt number
diff --git a/drivers/net/ethernet/intel/include/iecm.h b/drivers/net/ethernet/intel/include/iecm.h
index 4304256f7010..f6f9884c10c2 100644
--- a/drivers/net/ethernet/intel/include/iecm.h
+++ b/drivers/net/ethernet/intel/include/iecm.h
@@ -717,6 +717,7 @@ int iecm_send_alloc_vectors_msg(struct iecm_adapter *adapter, u16 num_vectors);
 int iecm_vport_params_buf_alloc(struct iecm_adapter *adapter);
 void iecm_vport_params_buf_rel(struct iecm_adapter *adapter);
 struct iecm_vport *iecm_netdev_to_vport(struct net_device *netdev);
+struct iecm_adapter *iecm_netdev_to_adapter(struct net_device *netdev);
 int iecm_send_get_stats_msg(struct iecm_vport *vport);
 int iecm_get_vec_ids(struct iecm_adapter *adapter,
 		     u16 *vecids, int num_vecids,
diff --git a/drivers/net/ethernet/intel/include/iecm_txrx.h b/drivers/net/ethernet/intel/include/iecm_txrx.h
index 5e29148938fb..26e480343876 100644
--- a/drivers/net/ethernet/intel/include/iecm_txrx.h
+++ b/drivers/net/ethernet/intel/include/iecm_txrx.h
@@ -115,6 +115,11 @@
 
 #define MAKEMASK(m, s)	((m) << (s))
 
+union iecm_tx_flex_desc {
+	struct iecm_flex_tx_desc q; /* queue based scheduling */
+	struct iecm_flex_tx_sched_desc flow; /* flow based scheduling */
+};
+
 struct iecm_tx_buf {
 	struct hlist_node hlist;
 	void *next_to_watch;
@@ -145,6 +150,37 @@ struct iecm_buf_lifo {
 	struct iecm_tx_buf **bufs;
 };
 
+struct iecm_tx_offload_params {
+	u16 td_cmd;	/* command field to be inserted into descriptor */
+	u32 tso_len;	/* total length of payload to segment */
+	u16 mss;
+	u8 tso_hdr_len;	/* length of headers to be duplicated */
+
+	/* Flow scheduling offload timestamp, formatting as hw expects it */
+	/* timestamp = bits[0:22], overflow = bit[23] */
+	u8 desc_ts[3];
+
+	/* For legacy offloads */
+	u32 hdr_offsets;
+};
+
+struct iecm_tx_splitq_params {
+	/* Descriptor build function pointer */
+	void (*splitq_build_ctb)(union iecm_tx_flex_desc *desc,
+				 struct iecm_tx_splitq_params *params,
+				 u16 td_cmd, u16 size);
+
+	/* General descriptor info */
+	enum iecm_tx_desc_dtype_value dtype;
+	u16 eop_cmd;
+	union {
+		u16 compl_tag; /* only relevant for flow scheduling */
+		u16 td_tag; /* only relevant for queue scheduling */
+	};
+
+	struct iecm_tx_offload_params offload;
+};
+
 /* Checksum offload bits decoded from the receive descriptor. */
 struct iecm_rx_csum_decoded {
 	u8 l3l4p : 1;
@@ -588,6 +624,12 @@ struct iecm_txq_group {
 
 struct iecm_adapter;
 
+void iecm_tx_splitq_build_ctb(union iecm_tx_flex_desc *desc,
+			      struct iecm_tx_splitq_params *parms,
+			      u16 td_cmd, u16 size);
+void iecm_tx_splitq_build_flow_desc(union iecm_tx_flex_desc *desc,
+				    struct iecm_tx_splitq_params *parms,
+				    u16 td_cmd, u16 size);
 int iecm_vport_singleq_napi_poll(struct napi_struct *napi, int budget);
 void iecm_vport_init_num_qs(struct iecm_vport *vport,
 			    struct virtchnl2_create_vport *vport_msg);
@@ -614,7 +656,25 @@ int iecm_init_rss(struct iecm_vport *vport);
 void iecm_deinit_rss(struct iecm_vport *vport);
 bool iecm_init_rx_buf_hw_alloc(struct iecm_queue *rxq, struct iecm_rx_buf *buf);
 void iecm_rx_buf_hw_update(struct iecm_queue *rxq, u32 val);
+void iecm_tx_buf_hw_update(struct iecm_queue *tx_q, u32 val,
+			   bool xmit_more);
 void iecm_tx_buf_rel(struct iecm_queue *tx_q, struct iecm_tx_buf *tx_buf);
+unsigned int iecm_size_to_txd_count(unsigned int size);
+unsigned int iecm_tx_desc_count_required(struct sk_buff *skb);
+bool iecm_chk_linearize(struct sk_buff *skb, unsigned int max_bufs,
+			unsigned int count);
+int iecm_tx_maybe_stop(struct iecm_queue *tx_q, unsigned int size);
+void iecm_tx_timeout(struct net_device *netdev,
+		     unsigned int __always_unused txqueue);
+netdev_tx_t iecm_tx_splitq_start(struct sk_buff *skb,
+				 struct net_device *netdev);
+netdev_tx_t iecm_tx_singleq_start(struct sk_buff *skb,
+				  struct net_device *netdev);
 bool iecm_rx_singleq_buf_hw_alloc_all(struct iecm_queue *rxq,
 				      u16 cleaned_count);
+int iecm_tso(struct iecm_tx_buf *first, struct iecm_tx_offload_params *off);
+void iecm_tx_prepare_vlan_flags(struct iecm_queue *tx_q,
+				struct iecm_tx_buf *first,
+				struct sk_buff *skb);
+
 #endif /* !_IECM_TXRX_H_ */
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 12/19] iecm: finish netdev_ops
  2022-01-28  0:09 [Intel-wired-lan] [PATCH net-next 00/19] Add iecm and idpf Alan Brady
                   ` (10 preceding siblings ...)
  2022-01-28  0:10 ` [Intel-wired-lan] [PATCH net-next 11/19] iecm: add start_xmit and set_rx_mode Alan Brady
@ 2022-01-28  0:10 ` Alan Brady
  2022-01-28 17:06   ` Alexander Lobakin
  2022-01-28  0:10 ` [Intel-wired-lan] [PATCH net-next 13/19] iecm: implement splitq napi_poll Alan Brady
                   ` (7 subsequent siblings)
  19 siblings, 1 reply; 89+ messages in thread
From: Alan Brady @ 2022-01-28  0:10 UTC (permalink / raw)
  To: intel-wired-lan

This fills out the remaining NDO callbacks. Once netdev_ops are there, the
rest of the patches will fill out the data path and advanced features.

Signed-off-by: Phani Burra <phani.r.burra@intel.com>
Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
Signed-off-by: Alan Brady <alan.brady@intel.com>
---
 drivers/net/ethernet/intel/iecm/iecm_lib.c    | 742 +++++++++++++++++-
 drivers/net/ethernet/intel/iecm/iecm_txrx.c   |  15 +
 .../net/ethernet/intel/iecm/iecm_virtchnl.c   |  63 ++
 drivers/net/ethernet/intel/include/iecm.h     |  14 +
 .../net/ethernet/intel/include/iecm_txrx.h    |   2 +
 5 files changed, 822 insertions(+), 14 deletions(-)

diff --git a/drivers/net/ethernet/intel/iecm/iecm_lib.c b/drivers/net/ethernet/intel/iecm/iecm_lib.c
index 003057f48f0c..cc82e665dfaf 100644
--- a/drivers/net/ethernet/intel/iecm/iecm_lib.c
+++ b/drivers/net/ethernet/intel/iecm/iecm_lib.c
@@ -568,6 +568,147 @@ static void iecm_set_all_filters(struct iecm_vport *vport)
 	iecm_add_del_ether_addrs(vport, true, false);
 }
 
+/**
+ * iecm_find_vlan - Search filter list for specific vlan filter
+ * @vport: vport structure
+ * @vlan: vlan tag
+ *
+ * Returns ptr to the filter object or NULL. Must be called while holding the
+ * vlan_list_lock.
+ */
+static struct
+iecm_vlan_filter *iecm_find_vlan(struct iecm_vport *vport,
+				 struct iecm_vlan *vlan)
+{
+	struct iecm_vlan_filter *f;
+
+	list_for_each_entry(f, &vport->adapter->config_data.vlan_filter_list,
+			    list) {
+		if (vlan->vid == f->vlan.vid && vlan->tpid == f->vlan.tpid)
+			return f;
+	}
+	return NULL;
+}
+
+/**
+ * iecm_add_vlan - Add a vlan filter to the list
+ * @vport: vport structure
+ * @vlan: VLAN tag
+ *
+ * Returns ptr to the filter object or NULL when no memory available.
+ */
+static struct
+iecm_vlan_filter *iecm_add_vlan(struct iecm_vport *vport,
+				struct iecm_vlan *vlan)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	struct iecm_vlan_filter *f = NULL;
+
+	spin_lock_bh(&adapter->vlan_list_lock);
+
+	f = iecm_find_vlan(vport, vlan);
+	if (!f) {
+		f = kzalloc(sizeof(*f), GFP_ATOMIC);
+		if (!f)
+			goto error;
+
+		f->vlan.vid = vlan->vid;
+		f->vlan.tpid = vlan->tpid;
+
+		list_add_tail(&f->list, &adapter->config_data.vlan_filter_list);
+		f->add = true;
+	}
+
+error:
+	spin_unlock_bh(&adapter->vlan_list_lock);
+	return f;
+}
+
+/**
+ * iecm_del_vlan - Remove a vlan filter from the list
+ * @vport: vport structure
+ * @vlan: VLAN tag
+ */
+static void iecm_del_vlan(struct iecm_vport *vport, struct iecm_vlan *vlan)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	struct iecm_vlan_filter *f;
+
+	spin_lock_bh(&adapter->vlan_list_lock);
+
+	f = iecm_find_vlan(vport, vlan);
+	if (f)
+		f->remove = true;
+
+	spin_unlock_bh(&adapter->vlan_list_lock);
+}
+
+/**
+ * iecm_vlan_rx_add_vid - Add a VLAN filter to the device
+ * @netdev: network device struct
+ * @proto: unused protocol data
+ * @vid: VLAN tag
+ *
+ * Returns 0 on success
+ */
+static int iecm_vlan_rx_add_vid(struct net_device *netdev,
+				__always_unused __be16 proto, u16 vid)
+{
+	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
+	struct iecm_adapter *adapter = vport->adapter;
+	struct iecm_vlan vlan;
+
+	vlan = IECM_VLAN(vid, be16_to_cpu(proto));
+	if (!iecm_is_feature_ena(vport, NETIF_F_HW_VLAN_CTAG_FILTER))
+		return -EINVAL;
+
+	iecm_add_vlan(vport, &vlan);
+
+	if (adapter->state == __IECM_UP)
+		adapter->dev_ops.vc_ops.add_del_vlans(vport, true);
+
+	return 0;
+}
+
+/**
+ * iecm_vlan_rx_kill_vid - Remove a VLAN filter from the device
+ * @netdev: network device struct
+ * @proto: unused protocol data
+ * @vid: VLAN tag
+ *
+ * Returns 0 on success
+ */
+static int iecm_vlan_rx_kill_vid(struct net_device *netdev,
+				 __always_unused __be16 proto, u16 vid)
+{
+	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
+	struct iecm_adapter *adapter = vport->adapter;
+	struct iecm_vlan_filter *f, *ftmp;
+	struct iecm_vlan vlan;
+
+	vlan = IECM_VLAN(vid, be16_to_cpu(proto));
+	if (!iecm_is_feature_ena(vport, NETIF_F_HW_VLAN_CTAG_FILTER))
+		return -EINVAL;
+
+	if (vport->adapter->state == __IECM_UP) {
+		iecm_del_vlan(vport, &vlan);
+		adapter->dev_ops.vc_ops.add_del_vlans(vport, false);
+	}
+	/* It is safe to delete entry from the list now */
+	spin_lock_bh(&adapter->vlan_list_lock);
+	list_for_each_entry_safe(f, ftmp,
+				 &adapter->config_data.vlan_filter_list,
+				 list) {
+		if (f->vlan.vid == vlan.vid && f->vlan.tpid == vlan.tpid) {
+			list_del(&f->list);
+			kfree(f);
+		}
+	}
+	spin_unlock_bh(&adapter->vlan_list_lock);
+
+	return 0;
+}
+
 /**
  * iecm_set_all_vlans - Re-add all VLANs in list
  * @vport: main vport struct
@@ -804,6 +945,27 @@ static int iecm_get_free_slot(void *array, int size, int curr)
 	return next;
 }
 
+/**
+ * iecm_remove_vlan_filters - Remove all vlan filters
+ * @vport: vport structure
+ */
+static void iecm_remove_vlan_filters(struct iecm_vport *vport)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	struct iecm_user_config_data *config_data;
+
+	config_data = &adapter->config_data;
+	if (!list_empty(&config_data->vlan_filter_list)) {
+		struct iecm_vlan_filter *f;
+
+		spin_lock_bh(&adapter->vlan_list_lock);
+		list_for_each_entry(f, &config_data->vlan_filter_list, list)
+			f->remove = true;
+		spin_unlock_bh(&adapter->vlan_list_lock);
+		adapter->dev_ops.vc_ops.add_del_vlans(vport, false);
+	}
+}
+
 /**
  * iecm_vport_stop - Disable a vport
  * @vport: vport to disable
@@ -831,6 +993,8 @@ static void iecm_vport_stop(struct iecm_vport *vport)
 	if (test_and_clear_bit(__IECM_DEL_QUEUES,
 			       vport->adapter->flags))
 		iecm_send_delete_queues_msg(vport);
+	if (!test_bit(__IECM_REL_RES_IN_PROG, adapter->flags))
+		iecm_remove_vlan_filters(vport);
 
 	adapter->link_up = false;
 	iecm_vport_intr_deinit(vport);
@@ -1581,6 +1745,147 @@ static void iecm_vc_event_task(struct work_struct *work)
 	}
 }
 
+/**
+ * iecm_initiate_soft_reset - Initiate a software reset
+ * @vport: virtual port data struct
+ * @reset_cause: reason for the soft reset
+ *
+ * Soft reset only reallocs vport queue resources. Returns 0 on success,
+ * negative on failure.
+ */
+int iecm_initiate_soft_reset(struct iecm_vport *vport,
+			     enum iecm_flags reset_cause)
+{
+	enum iecm_state current_state = vport->adapter->state;
+	struct iecm_adapter *adapter = vport->adapter;
+	struct iecm_vport *new_vport;
+	int err = 0, i;
+
+	/* make sure we do not end up in initiating multiple resets */
+	mutex_lock(&adapter->reset_lock);
+
+	/* If the system is low on memory, we can end up in bad state if we
+	 * free all the memory for queue resources and try to allocate them
+	 * again. Instead, we can pre-allocate the new resources before doing
+	 * anything and bailing if the alloc fails.
+	 *
+	 * Make a clone of the existing vport to mimic its current configuration,
+	 * then modify the new structure with any requested changes. Once the
+	 * allocation of the new resources is done, stop the existing vport and
+	 * copy the configuration to the main vport. If an error occurred, the
+	 * existing vport will be untouched.
+	 *
+	 */
+	new_vport = kzalloc(sizeof(*vport), GFP_KERNEL);
+	if (!new_vport) {
+		mutex_unlock(&adapter->reset_lock);
+		return -ENOMEM;
+	}
+	memcpy(new_vport, vport, sizeof(*vport));
+
+	/* Adjust resource parameters prior to reallocating resources */
+	switch (reset_cause) {
+	case __IECM_SR_Q_CHANGE:
+		adapter->dev_ops.vc_ops.adjust_qs(new_vport);
+		break;
+	case __IECM_SR_Q_DESC_CHANGE:
+		/* Update queue parameters before allocating resources */
+		iecm_vport_calc_num_q_desc(new_vport);
+		break;
+	case __IECM_SR_Q_SCH_CHANGE:
+	case __IECM_SR_MTU_CHANGE:
+	case __IECM_SR_RSC_CHANGE:
+	case __IECM_SR_HSPLIT_CHANGE:
+		break;
+	default:
+		dev_err(&adapter->pdev->dev, "Unhandled soft reset cause\n");
+		err = -EINVAL;
+		goto err_default;
+	}
+
+	err = iecm_vport_queues_alloc(new_vport);
+	if (err)
+		goto err_default;
+
+	if (adapter->virt_ver_maj == VIRTCHNL_VERSION_MAJOR_2) {
+		if (current_state <= __IECM_DOWN) {
+			adapter->dev_ops.vc_ops.delete_queues(vport);
+		} else {
+			set_bit(__IECM_DEL_QUEUES, adapter->flags);
+			iecm_vport_stop(vport);
+		}
+
+		iecm_deinit_rss(vport);
+		err = adapter->dev_ops.vc_ops.add_queues(new_vport, new_vport->num_txq,
+							 new_vport->num_complq,
+							 new_vport->num_rxq,
+							 new_vport->num_bufq);
+		if (err)
+			goto err_reset;
+	} else {
+		iecm_vport_stop(vport);
+	}
+
+	memcpy(vport, new_vport, sizeof(*vport));
+	/* Since iecm_vport_queues_alloc was called with new_port, the queue
+	 * back pointers are currently pointing to the local new_vport. Reset
+	 * the backpointers to the original vport here
+	 */
+	for (i = 0; i < vport->num_txq_grp; i++) {
+		struct iecm_txq_group *tx_qgrp = &vport->txq_grps[i];
+		int j;
+
+		tx_qgrp->vport = vport;
+		for (j = 0; j < tx_qgrp->num_txq; j++)
+			tx_qgrp->txqs[j]->vport = vport;
+
+		if (iecm_is_queue_model_split(vport->txq_model))
+			tx_qgrp->complq->vport = vport;
+	}
+
+	for (i = 0; i < vport->num_rxq_grp; i++) {
+		struct iecm_rxq_group *rx_qgrp = &vport->rxq_grps[i];
+		struct iecm_queue *q;
+		int j, num_rxq;
+
+		rx_qgrp->vport = vport;
+		for (j = 0; j < vport->num_bufqs_per_qgrp; j++)
+			rx_qgrp->splitq.bufq_sets[j].bufq.vport = vport;
+
+		if (iecm_is_queue_model_split(vport->rxq_model))
+			num_rxq = rx_qgrp->splitq.num_rxq_sets;
+		else
+			num_rxq = rx_qgrp->singleq.num_rxq;
+
+		for (j = 0; j < num_rxq; j++) {
+			if (iecm_is_queue_model_split(vport->rxq_model))
+				q = &rx_qgrp->splitq.rxq_sets[j]->rxq;
+			else
+				q = rx_qgrp->singleq.rxqs[j];
+			q->vport = vport;
+		}
+	}
+
+	/* Post resource allocation reset */
+	if (reset_cause == __IECM_SR_Q_CHANGE) {
+		iecm_intr_rel(adapter);
+		iecm_intr_req(adapter);
+	}
+
+	kfree(new_vport);
+
+	if (current_state == __IECM_UP)
+		err = iecm_vport_open(vport, false);
+	mutex_unlock(&adapter->reset_lock);
+	return err;
+err_reset:
+	iecm_vport_queues_rel(vport);
+err_default:
+	kfree(new_vport);
+	mutex_unlock(&adapter->reset_lock);
+	return err;
+}
+
 /**
  * iecm_probe - Device initialization routine
  * @pdev: PCI device information struct
@@ -1905,6 +2210,47 @@ static void iecm_set_rx_mode(struct net_device *netdev)
 	}
 }
 
+/**
+ * iecm_set_features - set the netdev feature flags
+ * @netdev: ptr to the netdev being adjusted
+ * @features: the feature set that the stack is suggesting
+ */
+static int iecm_set_features(struct net_device *netdev,
+			     netdev_features_t features)
+{
+	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
+	struct iecm_adapter *adapter = vport->adapter;
+	int err = 0;
+
+	if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS, VIRTCHNL2_CAP_VLAN) ||
+	    iecm_is_cap_ena(adapter, IECM_BASE_CAPS, VIRTCHNL2_CAP_VLAN)) {
+		err = iecm_set_vlan_offload_features(netdev, netdev->features,
+						     features);
+		if (err)
+			return err;
+	}
+
+	if ((netdev->features ^ features) & NETIF_F_GRO_HW) {
+		netdev->features ^= NETIF_F_GRO_HW;
+		err = iecm_initiate_soft_reset(vport, __IECM_SR_RSC_CHANGE);
+	}
+
+	return err;
+}
+
+/**
+ * iecm_fix_features - fix up the netdev feature bits
+ * @netdev: our net device
+ * @features: desired feature bits
+ *
+ * Returns fixed-up features bits
+ */
+static netdev_features_t iecm_fix_features(struct net_device *netdev,
+					   netdev_features_t features)
+{
+	return features;
+}
+
 /**
  * iecm_open - Called when a network interface becomes active
  * @netdev: network interface device structure
@@ -1924,6 +2270,374 @@ static int iecm_open(struct net_device *netdev)
 	return iecm_vport_open(np->vport, true);
 }
 
+/**
+ * iecm_change_mtu - NDO callback to change the MTU
+ * @netdev: network interface device structure
+ * @new_mtu: new value for maximum frame size
+ *
+ * Returns 0 on success, negative on failure
+ */
+static int iecm_change_mtu(struct net_device *netdev, int new_mtu)
+{
+	struct iecm_vport *vport =  iecm_netdev_to_vport(netdev);
+
+	netdev->mtu = new_mtu;
+
+	return iecm_initiate_soft_reset(vport, __IECM_SR_MTU_CHANGE);
+}
+
+static int iecm_offload_txtime(struct iecm_vport *vport,
+			       struct tc_etf_qopt_offload *qopt)
+{
+	return -EOPNOTSUPP;
+}
+
+/**
+ * iecm_validate_tx_bandwidth - validate the max Tx bandwidth
+ * @vport: vport structure
+ * @max_tx_rate: max Tx bandwidth for a tc
+ **/
+static int iecm_validate_tx_bandwidth(struct iecm_vport *vport,
+				      u64 max_tx_rate)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	int speed = 0, ret = 0;
+
+	if (adapter->link_speed_mbps) {
+		if (adapter->link_speed_mbps < U32_MAX) {
+			speed = adapter->link_speed_mbps;
+			goto validate_bw;
+		} else {
+			dev_err(&adapter->pdev->dev, "Unknown link speed\n");
+			return -EINVAL;
+		}
+	}
+
+	switch (adapter->link_speed) {
+	case VIRTCHNL_LINK_SPEED_40GB:
+		speed = SPEED_40000;
+		break;
+	case VIRTCHNL_LINK_SPEED_25GB:
+		speed = SPEED_25000;
+		break;
+	case VIRTCHNL_LINK_SPEED_20GB:
+		speed = SPEED_20000;
+		break;
+	case VIRTCHNL_LINK_SPEED_10GB:
+		speed = SPEED_10000;
+		break;
+	case VIRTCHNL_LINK_SPEED_5GB:
+		speed = SPEED_5000;
+		break;
+	case VIRTCHNL_LINK_SPEED_2_5GB:
+		speed = SPEED_2500;
+		break;
+	case VIRTCHNL_LINK_SPEED_1GB:
+		speed = SPEED_1000;
+		break;
+	case VIRTCHNL_LINK_SPEED_100MB:
+		speed = SPEED_100;
+		break;
+	default:
+		break;
+	}
+
+validate_bw:
+	if (max_tx_rate > speed) {
+		dev_err(&adapter->pdev->dev, "Invalid tx rate specified\n");
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+/**
+ * iecm_validate_ch_config - validate queue mapping info
+ * @vport: vport structure
+ * @mqprio_qopt: queue parameters
+ * @max_tc_allowed: MAX TC allowed, it could be 4 or 16 depends.
+ *
+ * This function validates if the configuration provided by the user to
+ * configure queue channels is valid or not.
+ *
+ * Returns 0 on a valid config and negative on invalid config.
+ **/
+static int iecm_validate_ch_config(struct iecm_vport *vport,
+				   struct tc_mqprio_qopt_offload *mqprio_qopt,
+				   u8 max_tc_allowed)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	u32 tc, qcount, non_power_2_qcount = 0;
+	u64 total_max_rate = 0;
+	int i, num_qs = 0;
+
+	if (mqprio_qopt->qopt.num_tc > max_tc_allowed ||
+	    mqprio_qopt->qopt.num_tc < 1)
+		return -EINVAL;
+
+	/* For ADQ there are few rules on queue allocation for each TC
+	 *     1. Number of queues for TC0 should always be a power of 2
+	 *     2. Number of queues for rest of TCs can be non-power of 2
+	 *     3. If the previous TC has non-power of 2 queues, then all the
+	 *        following TCs should be either
+	 *        a. same number of queues as that of the previous non-power
+	 *           of 2 or
+	 *        b. less than previous non-power of 2 and power of 2
+	 *        ex: 1 at 0 2 at 1 3 at 3 4 at 6 - Invalid
+	 *            1 at 0 2 at 1 3 at 3 3 at 6 - Valid
+	 *            1 at 0 2 at 1 3 at 3 2 at 6 - Valid
+	 *            1 at 0 2 at 1 3 at 3 1@6 - Valid
+	 */
+	for (tc = 0; tc < mqprio_qopt->qopt.num_tc; tc++) {
+		qcount = mqprio_qopt->qopt.count[tc];
+
+		/* case 1. check for first TC to be always power of 2 in ADQ */
+		if (!tc && !is_power_of_2(qcount)) {
+			dev_err(&adapter->pdev->dev,
+				"TC0:qcount[%d] must be a power of 2\n",
+				qcount);
+			return -EINVAL;
+		}
+		/* case 2 & 3, check for non-power of 2 number of queues */
+		if (tc && non_power_2_qcount) {
+			if (qcount > non_power_2_qcount) {
+				dev_err(&adapter->pdev->dev,
+					"TC%d has %d qcount cannot be > non_power_of_2 qcount [%d]\n",
+					tc, qcount, non_power_2_qcount);
+				return -EINVAL;
+			} else if (qcount < non_power_2_qcount) {
+				/* it must be power of 2, otherwise fail */
+				if (!is_power_of_2(qcount)) {
+					dev_err(&adapter->pdev->dev,
+						"TC%d has %d qcount must be a power of 2 < non_power_of_2 qcount [%d]\n",
+						tc, qcount, non_power_2_qcount);
+					return -EINVAL;
+				}
+			}
+		} else if (tc && !is_power_of_2(qcount)) {
+			/* this is the first TC to have a non-power of 2 queue
+			 * count and the code is going to enter this section
+			 * only once. The qcount for this TC will serve as
+			 * our reference/guide to allocate number of queues
+			 * for all the further TCs as per section a. and b. in
+			 * case 3 mentioned above.
+			 */
+			non_power_2_qcount = qcount;
+			dev_dbg(&adapter->pdev->dev,
+				"TC%d:count[%d] non power of 2\n", tc,
+				qcount);
+			}
+	}
+
+	for (i = 0; i <= mqprio_qopt->qopt.num_tc - 1; i++) {
+		u64 tx_rate = 0;
+
+		if (!mqprio_qopt->qopt.count[i] ||
+		    mqprio_qopt->qopt.offset[i] != num_qs)
+			return -EINVAL;
+		if (mqprio_qopt->min_rate[i]) {
+			dev_err(&adapter->pdev->dev,
+				"Invalid min tx rate (greater than 0) specified\n");
+			return -EINVAL;
+		}
+		/*convert to Mbps */
+		tx_rate = div_u64(mqprio_qopt->max_rate[i], IECM_MBPS_DIVISOR);
+		total_max_rate += tx_rate;
+		num_qs += mqprio_qopt->qopt.count[i];
+	}
+	/* Comparing with num_txq as num_txq and num_rxq are equal for single
+	 * queue model
+	 */
+	if (num_qs > vport->num_txq) {
+		dev_err(&adapter->pdev->dev,
+			"Cannot support requested number of queues\n");
+		return -EINVAL;
+	}
+	/* no point in validating TX bandwidth rate limit if the user hasn't
+	 * specified any rate limit for any TCs, so validate only if it's set.
+	 */
+	if (total_max_rate)
+		return iecm_validate_tx_bandwidth(vport, total_max_rate);
+	else
+		return 0;
+}
+
+/**
+ * __iecm_setup_tc - configure multiple traffic classes
+ * @vport: vport structure
+ * @type_data: tc offload data
+ *
+ * This function processes the config information provided by the
+ * user to configure traffic classes/queue channels and packages the
+ * information to request the PF to setup traffic classes.
+ *
+ * Returns 0 on success.
+ **/
+static int __iecm_setup_tc(struct iecm_vport *vport, void *type_data)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	struct tc_mqprio_qopt_offload *mqprio_qopt;
+	struct net_device *netdev = vport->netdev;
+	struct iecm_channel_config *ch_config;
+	u8 num_tc = 0, total_qs = 0;
+	int ret = 0;
+	u8 max_tc_allowed;
+	u64 max_tx_rate;
+	u16 mode;
+
+	mqprio_qopt = (struct tc_mqprio_qopt_offload *)type_data;
+	ch_config = &adapter->config_data.ch_config;
+	num_tc = mqprio_qopt->qopt.num_tc;
+	mode = mqprio_qopt->mode;
+
+	/* delete queue_channel */
+	if (!mqprio_qopt->qopt.hw) {
+		if (ch_config->tc_running) {
+			/* reset the tc configuration */
+			netdev_reset_tc(netdev);
+			ch_config->num_tc = 0;
+			netif_tx_stop_all_queues(netdev);
+			netif_tx_disable(netdev);
+			ret = iecm_send_disable_channels_msg(vport);
+			netif_tx_start_all_queues(netdev);
+			if (!test_bit(__IECM_REL_RES_IN_PROG, adapter->flags) &&
+			    !ret) {
+				ch_config->tc_running = false;
+				set_bit(__IECM_HR_FUNC_RESET, adapter->flags);
+				queue_delayed_work(adapter->vc_event_wq,
+						   &adapter->vc_event_task,
+						   msecs_to_jiffies(10));
+			}
+			return ret;
+		} else {
+			return -EINVAL;
+		}
+	}
+
+	if (mode == TC_MQPRIO_MODE_CHANNEL) {
+		int i, netdev_tc = 0;
+
+		if (!iecm_is_cap_ena(adapter, IECM_BASE_CAPS,
+				     VIRTCHNL2_CAP_ADQ) &&
+		    !iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
+					     VIRTCHNL2_CAP_ADQ)) {
+			dev_info(&adapter->pdev->dev, "ADQ not supported\n");
+			return -EOPNOTSUPP;
+		}
+
+		if (ch_config->tc_running) {
+			dev_info(&adapter->pdev->dev, "TC configuration already exists\n");
+			return -EINVAL;
+		}
+
+		/* If negotiated capability between VF and PF indicated that
+		 * ADQ_V2 is enabled, means it's OK to allow max_tc
+		 * to be 16. This is needed to handle the case where iAVF
+		 * is newer but PF is older or different generation
+		 */
+		if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS, VIRTCHNL2_CAP_ADQ))
+			max_tc_allowed = VIRTCHNL_MAX_ADQ_V2_CHANNELS;
+		else
+			max_tc_allowed = VIRTCHNL_MAX_ADQ_CHANNELS;
+
+		ret = iecm_validate_ch_config(vport, mqprio_qopt,
+					      max_tc_allowed);
+		if (ret)
+			return ret;
+		/* Return if same TC config is requested */
+		if (ch_config->num_tc == num_tc)
+			return 0;
+		ch_config->num_tc = num_tc;
+
+		for (i = 0; i < max_tc_allowed; i++) {
+			if (i < num_tc) {
+				ch_config->ch_info[i].count =
+					mqprio_qopt->qopt.count[i];
+				ch_config->ch_info[i].offset =
+					mqprio_qopt->qopt.offset[i];
+				total_qs += mqprio_qopt->qopt.count[i];
+				max_tx_rate = mqprio_qopt->max_rate[i];
+				/* convert to Mbps */
+				max_tx_rate = div_u64(max_tx_rate,
+						      IECM_MBPS_DIVISOR);
+				ch_config->ch_info[i].max_tx_rate =
+								max_tx_rate;
+			} else {
+				ch_config->ch_info[i].count = 1;
+				ch_config->ch_info[i].offset = 0;
+			}
+		}
+
+		/* Store queue info based on TC so that, VF gets configured
+		 * with correct number of queues when VF completes ADQ config
+		 * flow
+		 */
+		ch_config->total_qs = total_qs;
+
+		netif_tx_stop_all_queues(netdev);
+		netif_tx_disable(netdev);
+		ret = iecm_send_enable_channels_msg(vport);
+		if (ret)
+			return ret;
+		netdev_reset_tc(netdev);
+		/* Report the tc mapping up the stack */
+		netdev_set_num_tc(netdev, num_tc);
+		for (i = 0; i < max_tc_allowed; i++) {
+			u16 qcount = mqprio_qopt->qopt.count[i];
+			u16 qoffset = mqprio_qopt->qopt.offset[i];
+
+			if (i < num_tc)
+				netdev_set_tc_queue(netdev, netdev_tc++, qcount,
+						    qoffset);
+		}
+		/* Start all queues */
+		netif_tx_start_all_queues(netdev);
+		ch_config->tc_running = true;
+		set_bit(__IECM_HR_FUNC_RESET, adapter->flags);
+		queue_delayed_work(adapter->vc_event_wq,
+				   &adapter->vc_event_task,
+				   msecs_to_jiffies(10));
+	}
+	return ret;
+}
+
+/**
+ * iecm_setup_tc - ndo callback to setup up TC schedulers
+ * @netdev: pointer to net_device struct
+ * @type: TC type
+ * @type_data: TC type specific data
+ */
+static int iecm_setup_tc(struct net_device *netdev, enum tc_setup_type type,
+			 void *type_data)
+{
+	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
+	struct iecm_adapter *adapter = vport->adapter;
+	int err = 0;
+
+	switch (type) {
+	case TC_SETUP_QDISC_ETF:
+		if (iecm_is_queue_model_split(vport->txq_model))
+			err =
+			iecm_offload_txtime(vport,
+					    (struct tc_etf_qopt_offload *)
+					     type_data);
+		break;
+	case TC_SETUP_BLOCK:
+		break;
+	case TC_SETUP_QDISC_MQPRIO:
+		if (iecm_is_cap_ena(adapter, IECM_BASE_CAPS,
+				    VIRTCHNL2_CAP_ADQ) ||
+		    iecm_is_cap_ena(adapter, IECM_OTHER_CAPS, VIRTCHNL2_CAP_ADQ))
+			__iecm_setup_tc(vport, type_data);
+		break;
+	default:
+		err = -EOPNOTSUPP;
+		break;
+	}
+
+	return err;
+}
+
 /**
  * iecm_set_mac - NDO callback to set port mac address
  * @netdev: network interface device structure
@@ -1997,13 +2711,13 @@ static const struct net_device_ops iecm_netdev_ops_splitq = {
 	.ndo_set_rx_mode = iecm_set_rx_mode,
 	.ndo_validate_addr = eth_validate_addr,
 	.ndo_set_mac_address = iecm_set_mac,
-	.ndo_change_mtu = NULL,
-	.ndo_get_stats64 = NULL,
-	.ndo_fix_features = NULL,
-	.ndo_set_features = NULL,
-	.ndo_vlan_rx_add_vid = NULL,
-	.ndo_vlan_rx_kill_vid = NULL,
-	.ndo_setup_tc = NULL,
+	.ndo_change_mtu = iecm_change_mtu,
+	.ndo_get_stats64 = iecm_get_stats64,
+	.ndo_fix_features = iecm_fix_features,
+	.ndo_set_features = iecm_set_features,
+	.ndo_vlan_rx_add_vid = iecm_vlan_rx_add_vid,
+	.ndo_vlan_rx_kill_vid = iecm_vlan_rx_kill_vid,
+	.ndo_setup_tc = iecm_setup_tc,
 };
 
 static const struct net_device_ops iecm_netdev_ops_singleq = {
@@ -2013,11 +2727,11 @@ static const struct net_device_ops iecm_netdev_ops_singleq = {
 	.ndo_set_rx_mode = iecm_set_rx_mode,
 	.ndo_validate_addr = eth_validate_addr,
 	.ndo_set_mac_address = iecm_set_mac,
-	.ndo_change_mtu = NULL,
-	.ndo_get_stats64 = NULL,
-	.ndo_fix_features = NULL,
-	.ndo_set_features = NULL,
-	.ndo_vlan_rx_add_vid = NULL,
-	.ndo_vlan_rx_kill_vid = NULL,
-	.ndo_setup_tc           = NULL,
+	.ndo_change_mtu = iecm_change_mtu,
+	.ndo_get_stats64 = iecm_get_stats64,
+	.ndo_fix_features = iecm_fix_features,
+	.ndo_set_features = iecm_set_features,
+	.ndo_vlan_rx_add_vid = iecm_vlan_rx_add_vid,
+	.ndo_vlan_rx_kill_vid = iecm_vlan_rx_kill_vid,
+	.ndo_setup_tc = iecm_setup_tc,
 };
diff --git a/drivers/net/ethernet/intel/iecm/iecm_txrx.c b/drivers/net/ethernet/intel/iecm/iecm_txrx.c
index ef5fe659389b..4b9288e1c254 100644
--- a/drivers/net/ethernet/intel/iecm/iecm_txrx.c
+++ b/drivers/net/ethernet/intel/iecm/iecm_txrx.c
@@ -218,6 +218,21 @@ const struct iecm_rx_ptype_decoded iecm_ptype_lookup[IECM_RX_MAX_PTYPE] = {
 };
 EXPORT_SYMBOL(iecm_ptype_lookup);
 
+/**
+ * iecm_get_stats64 - get statistics for network device structure
+ * @netdev: network interface device structure
+ * @stats: main device statistics structure
+ */
+void iecm_get_stats64(struct net_device *netdev,
+		      struct rtnl_link_stats64 *stats)
+{
+	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
+
+	set_bit(__IECM_MB_STATS_PENDING, vport->adapter->flags);
+
+	*stats = vport->netstats;
+}
+
 /**
  * iecm_tx_buf_rel - Release a Tx buffer
  * @tx_q: the queue that owns the buffer
diff --git a/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
index 919fb3958cf8..f2516343c199 100644
--- a/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
+++ b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
@@ -2731,6 +2731,69 @@ static int iecm_send_insert_vlan_msg(struct iecm_vport *vport, bool ena)
 	return err;
 }
 
+/**
+ * iecm_send_enable_channels_msg - Send enable channels message
+ * @vport: vport structure
+ *
+ * Request the PF/CP to enable channels as specified by the user via tc tool.
+ * Returns 0 on success, negative on failure.
+ **/
+int iecm_send_enable_channels_msg(struct iecm_vport *vport)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	struct iecm_channel_config *ch_config;
+	struct virtchnl_tc_info *vti = NULL;
+	int i, err;
+	u16 len;
+
+	ch_config = &adapter->config_data.ch_config;
+	len = ((ch_config->num_tc - 1) * sizeof(struct virtchnl_channel_info)) +
+		sizeof(struct virtchnl_tc_info);
+
+	vti = kzalloc(len, GFP_KERNEL);
+	if (!vti)
+		return -ENOMEM;
+	vti->num_tc = ch_config->num_tc;
+	for (i = 0; i < vti->num_tc; i++) {
+		vti->list[i].count = ch_config->ch_info[i].count;
+		vti->list[i].offset = ch_config->ch_info[i].offset;
+		vti->list[i].pad = 0;
+		vti->list[i].max_tx_rate = ch_config->ch_info[i].max_tx_rate;
+	}
+
+	err = iecm_send_mb_msg(adapter, VIRTCHNL_OP_ENABLE_CHANNELS, len,
+			       (u8 *)vti);
+	if (err)
+		goto error;
+
+	err = iecm_wait_for_event(adapter, IECM_VC_ENA_CHANNELS,
+				  IECM_VC_ENA_CHANNELS_ERR);
+error:
+	kfree(vti);
+	return err;
+}
+
+/**
+ * iecm_send_disable_channels_msg - Send disable channels message
+ * @vport: vport structure to disable channels on
+ *
+ * Returns 0 on success, negative on failure.
+ */
+int iecm_send_disable_channels_msg(struct iecm_vport *vport)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	int err;
+
+	err = iecm_send_mb_msg(adapter, VIRTCHNL_OP_DISABLE_CHANNELS,
+			       0, NULL);
+	if (err)
+		return err;
+
+	err = iecm_min_wait_for_event(adapter, IECM_VC_DIS_CHANNELS,
+				      IECM_VC_DIS_CHANNELS_ERR);
+	return err;
+}
+
 /**
  * iecm_send_vlan_v2_caps_msg - send virtchnl get offload VLAN V2 caps message
  * @adapter: adapter info struct
diff --git a/drivers/net/ethernet/intel/include/iecm.h b/drivers/net/ethernet/intel/include/iecm.h
index f6f9884c10c2..a655e797f457 100644
--- a/drivers/net/ethernet/intel/include/iecm.h
+++ b/drivers/net/ethernet/intel/include/iecm.h
@@ -4,6 +4,8 @@
 #ifndef _IECM_H_
 #define _IECM_H_
 
+#include <net/pkt_sched.h>
+#include <net/pkt_cls.h>
 #include <linux/aer.h>
 #include <linux/pci.h>
 #include <linux/netdevice.h>
@@ -44,6 +46,8 @@
 /* available message levels */
 #define IECM_AVAIL_NETIF_M (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK)
 
+#define IECM_MBPS_DIVISOR		125000 /* divisor to convert to Mbps */
+
 #define IECM_VIRTCHNL_VERSION_MAJOR VIRTCHNL_VERSION_MAJOR_2
 #define IECM_VIRTCHNL_VERSION_MINOR VIRTCHNL_VERSION_MINOR_0
 
@@ -393,6 +397,13 @@ enum iecm_user_flags {
 	__IECM_USER_FLAGS_NBITS,
 };
 
+struct iecm_channel_config {
+	struct virtchnl_channel_info ch_info[VIRTCHNL_MAX_ADQ_V2_CHANNELS];
+	bool tc_running;
+	u8 total_qs;
+	u8 num_tc;
+};
+
 #define IECM_GET_PTYPE_SIZE(p) \
 	(sizeof(struct virtchnl2_ptype) + \
 	(((p)->proto_id_count ? ((p)->proto_id_count - 1) : 0) * sizeof(u16)))
@@ -430,6 +441,7 @@ struct iecm_user_config_data {
 	struct list_head mac_filter_list;
 	struct list_head vlan_filter_list;
 	struct list_head adv_rss_list;
+	struct iecm_channel_config ch_config;
 };
 
 struct iecm_rss_data {
@@ -703,6 +715,8 @@ int iecm_send_delete_queues_msg(struct iecm_vport *vport);
 int iecm_send_add_queues_msg(struct iecm_vport *vport, u16 num_tx_q,
 			     u16 num_complq, u16 num_rx_q, u16 num_rx_bufq);
 int iecm_send_vlan_v2_caps_msg(struct iecm_adapter *adapter);
+int iecm_initiate_soft_reset(struct iecm_vport *vport,
+			     enum iecm_flags reset_cause);
 int iecm_send_config_tx_queues_msg(struct iecm_vport *vport);
 int iecm_send_config_rx_queues_msg(struct iecm_vport *vport);
 int iecm_send_enable_vport_msg(struct iecm_vport *vport);
diff --git a/drivers/net/ethernet/intel/include/iecm_txrx.h b/drivers/net/ethernet/intel/include/iecm_txrx.h
index 26e480343876..7ec742fd4c6b 100644
--- a/drivers/net/ethernet/intel/include/iecm_txrx.h
+++ b/drivers/net/ethernet/intel/include/iecm_txrx.h
@@ -672,6 +672,8 @@ netdev_tx_t iecm_tx_singleq_start(struct sk_buff *skb,
 				  struct net_device *netdev);
 bool iecm_rx_singleq_buf_hw_alloc_all(struct iecm_queue *rxq,
 				      u16 cleaned_count);
+void iecm_get_stats64(struct net_device *netdev,
+		      struct rtnl_link_stats64 *stats);
 int iecm_tso(struct iecm_tx_buf *first, struct iecm_tx_offload_params *off);
 void iecm_tx_prepare_vlan_flags(struct iecm_queue *tx_q,
 				struct iecm_tx_buf *first,
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 13/19] iecm: implement splitq napi_poll
  2022-01-28  0:09 [Intel-wired-lan] [PATCH net-next 00/19] Add iecm and idpf Alan Brady
                   ` (11 preceding siblings ...)
  2022-01-28  0:10 ` [Intel-wired-lan] [PATCH net-next 12/19] iecm: finish netdev_ops Alan Brady
@ 2022-01-28  0:10 ` Alan Brady
  2022-01-28  5:21     ` kernel test robot
  2022-01-28 17:38   ` Alexander Lobakin
  2022-01-28  0:10 ` [Intel-wired-lan] [PATCH net-next 14/19] iecm: implement singleq napi_poll Alan Brady
                   ` (6 subsequent siblings)
  19 siblings, 2 replies; 89+ messages in thread
From: Alan Brady @ 2022-01-28  0:10 UTC (permalink / raw)
  To: intel-wired-lan

This adds everything we need to actually receive packets and process spent
buffers using the splitq model. This contrasts to more traditional queueing
models by essentially splitting a normal queue of descriptors and mapped
buffers into separate queues. This allows us to deal with both more
efficiently and also allows us to implement asymmetric queuing setups where
you have multiple completion queues associated with a single buffer queue.

Signed-off-by: Phani Burra <phani.r.burra@intel.com>
Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
Signed-off-by: Alan Brady <alan.brady@intel.com>
---
 drivers/net/ethernet/intel/iecm/iecm_txrx.c   | 1468 ++++++++++++++++-
 drivers/net/ethernet/intel/include/iecm.h     |    4 +
 .../net/ethernet/intel/include/iecm_txrx.h    |   20 +
 3 files changed, 1490 insertions(+), 2 deletions(-)

diff --git a/drivers/net/ethernet/intel/iecm/iecm_txrx.c b/drivers/net/ethernet/intel/iecm/iecm_txrx.c
index 4b9288e1c254..85a82b58525a 100644
--- a/drivers/net/ethernet/intel/iecm/iecm_txrx.c
+++ b/drivers/net/ethernet/intel/iecm/iecm_txrx.c
@@ -218,6 +218,36 @@ const struct iecm_rx_ptype_decoded iecm_ptype_lookup[IECM_RX_MAX_PTYPE] = {
 };
 EXPORT_SYMBOL(iecm_ptype_lookup);
 
+/**
+ * iecm_buf_lifo_push - push a buffer pointer onto stack
+ * @stack: pointer to stack struct
+ * @buf: pointer to buf to push
+ *
+ * Returns 0 on success, negative on failure
+ **/
+static int iecm_buf_lifo_push(struct iecm_buf_lifo *stack,
+			      struct iecm_tx_buf *buf)
+{
+	if (stack->top == stack->size)
+		return -ENOSPC;
+
+	stack->bufs[stack->top++] = buf;
+
+	return 0;
+}
+
+/**
+ * iecm_buf_lifo_pop - pop a buffer pointer from stack
+ * @stack: pointer to stack struct
+ **/
+static struct iecm_tx_buf *iecm_buf_lifo_pop(struct iecm_buf_lifo *stack)
+{
+	if (!stack->top)
+		return NULL;
+
+	return stack->bufs[--stack->top];
+}
+
 /**
  * iecm_get_stats64 - get statistics for network device structure
  * @netdev: network interface device structure
@@ -812,6 +842,30 @@ iecm_rx_buf_hw_alloc_all(struct iecm_queue *rxbufq, u16 alloc_count)
 	return !!alloc_count;
 }
 
+/**
+ * iecm_rx_post_buf_refill - Post buffer id to refill queue
+ * @refillq: refill queue to post to
+ * @buf_id: buffer id to post
+ */
+void iecm_rx_post_buf_refill(struct iecm_sw_queue *refillq, u16 buf_id)
+{
+	u16 nta = refillq->next_to_alloc;
+	u16 *bi;
+
+	bi = IECM_SPLITQ_RX_BI_DESC(refillq, nta);
+	/* store the buffer ID and the SW maintained GEN bit to the refillq */
+	*bi = ((buf_id << IECM_RX_BI_BUFID_S) & IECM_RX_BI_BUFID_M) |
+	      (!!(test_bit(__IECM_Q_GEN_CHK, refillq->flags)) <<
+	       IECM_RX_BI_GEN_S);
+
+	nta++;
+	if (unlikely(nta == refillq->desc_count)) {
+		nta = 0;
+		change_bit(__IECM_Q_GEN_CHK, refillq->flags);
+	}
+	refillq->next_to_alloc = nta;
+}
+
 /**
  * iecm_rx_post_buf_desc - Post buffer to bufq descriptor ring
  * @bufq: buffer queue to post to
@@ -1670,6 +1724,398 @@ int iecm_vport_queues_alloc(struct iecm_vport *vport)
 	return err;
 }
 
+/**
+ * iecm_tx_find_q - Find the tx q based on q id
+ * @vport: the vport we care about
+ * @q_id: Id of the queue
+ *
+ * Returns queue ptr if found else returns NULL
+ */
+static struct iecm_queue *
+iecm_tx_find_q(struct iecm_vport *vport, int q_id)
+{
+	int i;
+
+	for (i = 0; i < vport->num_txq; i++) {
+		struct iecm_queue *tx_q = vport->txqs[i];
+
+		if (tx_q->q_id == q_id)
+			return tx_q;
+	}
+
+	return NULL;
+}
+
+/**
+ * iecm_tx_handle_sw_marker - Handle queue marker packet
+ * @tx_q: tx queue to handle software marker
+ */
+static void iecm_tx_handle_sw_marker(struct iecm_queue *tx_q)
+{
+	struct iecm_vport *vport = tx_q->vport;
+	bool drain_complete = true;
+	int i;
+
+	clear_bit(__IECM_Q_SW_MARKER, tx_q->flags);
+	/* Hardware must write marker packets to all queues associated with
+	 * completion queues. So check if all queues received marker packets
+	 */
+	for (i = 0; i < vport->num_txq; i++) {
+		if (test_bit(__IECM_Q_SW_MARKER, vport->txqs[i]->flags))
+			drain_complete = false;
+	}
+	if (drain_complete) {
+		set_bit(__IECM_SW_MARKER, vport->adapter->flags);
+		wake_up(&vport->adapter->sw_marker_wq);
+	}
+}
+
+/**
+ * iecm_tx_splitq_clean_buf - Clean TX buffer resources
+ * @tx_q: tx queue to clean buffer from
+ * @tx_buf: buffer to be cleaned
+ * @napi_budget: Used to determine if we are in netpoll
+ */
+static void
+iecm_tx_splitq_clean_buf(struct iecm_queue *tx_q, struct iecm_tx_buf *tx_buf,
+			 int napi_budget)
+{
+	/* unmap skb header data */
+	dma_unmap_single(tx_q->dev,
+			 dma_unmap_addr(tx_buf, dma),
+			 dma_unmap_len(tx_buf, len),
+			 DMA_TO_DEVICE);
+
+	napi_consume_skb(tx_buf->skb, napi_budget);
+
+	/* clear tx_buf data */
+	tx_buf->skb = NULL;
+	dma_unmap_len_set(tx_buf, len, 0);
+}
+
+/**
+ * iecm_stash_flow_sch_buffers - store buffere parameter info to be freed at a
+ * later time (only relevant for flow scheduling mode)
+ * @txq: Tx queue to clean
+ * @tx_buf: buffer to store
+ */
+static int
+iecm_stash_flow_sch_buffers(struct iecm_queue *txq, struct iecm_tx_buf *tx_buf)
+{
+	struct iecm_adapter *adapter = txq->vport->adapter;
+	struct iecm_tx_buf *shadow_buf;
+
+	shadow_buf = iecm_buf_lifo_pop(&txq->buf_stack);
+	if (!shadow_buf) {
+		dev_err(&adapter->pdev->dev,
+			"No out-of-order TX buffers left!\n");
+		return -ENOMEM;
+	}
+
+	/* Store buffer params in shadow buffer */
+	shadow_buf->skb = tx_buf->skb;
+	shadow_buf->bytecount = tx_buf->bytecount;
+	shadow_buf->gso_segs = tx_buf->gso_segs;
+	dma_unmap_addr_set(shadow_buf, dma, dma_unmap_addr(tx_buf, dma));
+	dma_unmap_len_set(shadow_buf, len, dma_unmap_len(tx_buf, len));
+	shadow_buf->compl_tag = tx_buf->compl_tag;
+
+	/* Add buffer to buf_hash table to be freed
+	 * later
+	 */
+	hash_add(txq->sched_buf_hash, &shadow_buf->hlist,
+		 shadow_buf->compl_tag);
+
+	memset(tx_buf, 0, sizeof(struct iecm_tx_buf));
+
+	return 0;
+}
+
+/**
+ * iecm_tx_splitq_clean - Reclaim resources from buffer queue
+ * @tx_q: Tx queue to clean
+ * @end: queue index until which it should be cleaned
+ * @napi_budget: Used to determine if we are in netpoll
+ * @descs_only: true if queue is using flow-based scheduling and should
+ * not clean buffers at this time
+ *
+ * Cleans the queue descriptor ring. If the queue is using queue-based
+ * scheduling, the buffers will be cleaned as well and this function will
+ * return the number of bytes/packets cleaned. If the queue is using flow-based
+ * scheduling, only the descriptors are cleaned at this time. Separate packet
+ * completion events will be reported on the completion queue, and the buffers
+ * will be cleaned separately. The stats returned from this function when using
+ * flow-based scheduling are irrelevant.
+ */
+static struct iecm_tx_queue_stats
+iecm_tx_splitq_clean(struct iecm_queue *tx_q, u16 end, int napi_budget,
+		     bool descs_only)
+{
+	union iecm_tx_flex_desc *next_pending_desc = NULL;
+	struct iecm_tx_queue_stats cleaned_stats = {0};
+	union iecm_tx_flex_desc *tx_desc;
+	s16 ntc = tx_q->next_to_clean;
+	struct iecm_tx_buf *tx_buf;
+	unsigned short gso_segs = 0;
+	unsigned int bytecount = 0;
+	struct netdev_queue *nq;
+
+	tx_desc = IECM_FLEX_TX_DESC(tx_q, ntc);
+	next_pending_desc = IECM_FLEX_TX_DESC(tx_q, end);
+	tx_buf = &tx_q->tx_buf[ntc];
+	ntc -= tx_q->desc_count;
+
+	while (tx_desc != next_pending_desc) {
+		union iecm_tx_flex_desc *eop_desc =
+			(union iecm_tx_flex_desc *)tx_buf->next_to_watch;
+
+		/* clear next_to_watch to prevent false hangs */
+		tx_buf->next_to_watch = NULL;
+
+		bytecount += tx_buf->bytecount;
+		gso_segs += tx_buf->gso_segs;
+
+		if (descs_only) {
+			if (iecm_stash_flow_sch_buffers(tx_q, tx_buf))
+				goto tx_splitq_clean_out;
+
+			while (tx_desc != eop_desc) {
+				tx_buf++;
+				tx_desc++;
+				ntc++;
+				if (unlikely(!ntc)) {
+					ntc -= tx_q->desc_count;
+					tx_buf = tx_q->tx_buf;
+					tx_desc = IECM_FLEX_TX_DESC(tx_q, 0);
+				}
+
+				if (dma_unmap_len(tx_buf, len)) {
+					if (iecm_stash_flow_sch_buffers(tx_q,
+									tx_buf))
+						goto tx_splitq_clean_out;
+				}
+			}
+		} else {
+			/* update the statistics for this packet */
+			cleaned_stats.bytes += tx_buf->bytecount;
+			cleaned_stats.packets += tx_buf->gso_segs;
+
+			iecm_tx_splitq_clean_buf(tx_q, tx_buf, napi_budget);
+
+			/* unmap remaining buffers */
+			while (tx_desc != eop_desc) {
+				tx_buf++;
+				tx_desc++;
+				ntc++;
+				if (unlikely(!ntc)) {
+					ntc -= tx_q->desc_count;
+					tx_buf = tx_q->tx_buf;
+					tx_desc = IECM_FLEX_TX_DESC(tx_q, 0);
+				}
+
+				/* unmap any remaining paged data */
+				if (dma_unmap_len(tx_buf, len)) {
+					dma_unmap_page(tx_q->dev,
+						       dma_unmap_addr(tx_buf, dma),
+						       dma_unmap_len(tx_buf, len),
+						       DMA_TO_DEVICE);
+					dma_unmap_len_set(tx_buf, len, 0);
+				}
+			}
+		}
+
+		tx_buf++;
+		tx_desc++;
+		ntc++;
+		if (unlikely(!ntc)) {
+			ntc -= tx_q->desc_count;
+			tx_buf = tx_q->tx_buf;
+			tx_desc = IECM_FLEX_TX_DESC(tx_q, 0);
+		}
+	}
+
+tx_splitq_clean_out:
+	ntc += tx_q->desc_count;
+	tx_q->next_to_clean = ntc;
+
+	nq = netdev_get_tx_queue(tx_q->vport->netdev, tx_q->idx);
+	netdev_tx_completed_queue(nq, gso_segs, bytecount);
+
+	return cleaned_stats;
+}
+
+/**
+ * iecm_tx_clean_flow_sch_bufs - clean bufs that were stored for
+ * out of order completions
+ * @txq: queue to clean
+ * @compl_tag: completion tag of packet to clean (from completion descriptor)
+ * @budget: Used to determine if we are in netpoll
+ */
+static struct iecm_tx_queue_stats
+iecm_tx_clean_flow_sch_bufs(struct iecm_queue *txq, u16 compl_tag,
+			    int budget)
+{
+	struct iecm_tx_queue_stats cleaned_stats = {0};
+	struct hlist_node *tmp_buf = NULL;
+	struct iecm_tx_buf *tx_buf = NULL;
+
+	/* Buffer completion */
+	hash_for_each_possible_safe(txq->sched_buf_hash, tx_buf, tmp_buf,
+				    hlist, compl_tag) {
+		if (tx_buf->compl_tag != compl_tag)
+			continue;
+
+		if (likely(tx_buf->skb)) {
+			/* update the statistics for this packet */
+			cleaned_stats.bytes += tx_buf->bytecount;
+			cleaned_stats.packets += tx_buf->gso_segs;
+
+			iecm_tx_splitq_clean_buf(txq, tx_buf, budget);
+		} else if (dma_unmap_len(tx_buf, len)) {
+			dma_unmap_page(txq->dev,
+				       dma_unmap_addr(tx_buf, dma),
+				       dma_unmap_len(tx_buf, len),
+				       DMA_TO_DEVICE);
+			dma_unmap_len_set(tx_buf, len, 0);
+		}
+		/* Push shadow buf back onto stack */
+		iecm_buf_lifo_push(&txq->buf_stack, tx_buf);
+
+		hash_del(&tx_buf->hlist);
+	}
+
+	return cleaned_stats;
+}
+
+/**
+ * iecm_tx_clean_complq - Reclaim resources on completion queue
+ * @complq: Tx ring to clean
+ * @budget: Used to determine if we are in netpoll
+ *
+ * Returns true if there's any budget left (e.g. the clean is finished)
+ */
+static bool
+iecm_tx_clean_complq(struct iecm_queue *complq, int budget)
+{
+	struct iecm_splitq_tx_compl_desc *tx_desc;
+	struct iecm_vport *vport = complq->vport;
+	s16 ntc = complq->next_to_clean;
+	bool clean_completed = false;
+	unsigned int complq_budget;
+
+	complq_budget = vport->compln_clean_budget;
+	tx_desc = IECM_SPLITQ_TX_COMPLQ_DESC(complq, ntc);
+	ntc -= complq->desc_count;
+
+	do {
+		struct iecm_tx_queue_stats cleaned_stats = {0};
+		struct iecm_queue *tx_q;
+		u16 compl_tag, hw_head;
+		int tx_qid;
+		u8 ctype;	/* completion type */
+		u16 gen;
+
+		/* if the descriptor isn't done, no work yet to do */
+		gen = (le16_to_cpu(tx_desc->qid_comptype_gen) &
+		      IECM_TXD_COMPLQ_GEN_M) >> IECM_TXD_COMPLQ_GEN_S;
+		if (test_bit(__IECM_Q_GEN_CHK, complq->flags) != gen)
+			break;
+
+		/* Find necessary info of TX queue to clean buffers */
+		tx_qid = (le16_to_cpu(tx_desc->qid_comptype_gen) &
+			 IECM_TXD_COMPLQ_QID_M) >> IECM_TXD_COMPLQ_QID_S;
+		tx_q = iecm_tx_find_q(vport, tx_qid);
+		if (!tx_q) {
+			dev_err(&complq->vport->adapter->pdev->dev,
+				"TxQ #%d not found\n", tx_qid);
+			goto fetch_next_desc;
+		}
+
+		/* Determine completion type */
+		ctype = (le16_to_cpu(tx_desc->qid_comptype_gen) &
+			IECM_TXD_COMPLQ_COMPL_TYPE_M) >>
+			IECM_TXD_COMPLQ_COMPL_TYPE_S;
+		switch (ctype) {
+		case IECM_TXD_COMPLT_RE:
+			hw_head = le16_to_cpu(tx_desc->q_head_compl_tag.q_head);
+
+			cleaned_stats = iecm_tx_splitq_clean(tx_q, hw_head,
+							     budget, true);
+			break;
+		case IECM_TXD_COMPLT_RS:
+			if (test_bit(__IECM_Q_FLOW_SCH_EN, tx_q->flags)) {
+				compl_tag =
+				le16_to_cpu(tx_desc->q_head_compl_tag.compl_tag);
+
+				cleaned_stats =
+					iecm_tx_clean_flow_sch_bufs(tx_q,
+								    compl_tag,
+								    budget);
+			} else {
+				hw_head =
+				le16_to_cpu(tx_desc->q_head_compl_tag.q_head);
+
+				cleaned_stats = iecm_tx_splitq_clean(tx_q,
+								     hw_head,
+								     budget,
+								     false);
+			}
+
+			break;
+		case IECM_TXD_COMPLT_SW_MARKER:
+			iecm_tx_handle_sw_marker(tx_q);
+			break;
+		default:
+			dev_err(&tx_q->vport->adapter->pdev->dev,
+				"Unknown TX completion type: %d\n",
+				ctype);
+			goto fetch_next_desc;
+		}
+
+		u64_stats_update_begin(&tx_q->stats_sync);
+		tx_q->q_stats.tx.packets += cleaned_stats.packets;
+		tx_q->q_stats.tx.bytes += cleaned_stats.bytes;
+		u64_stats_update_end(&tx_q->stats_sync);
+
+		if (unlikely(cleaned_stats.packets &&
+			     netif_carrier_ok(tx_q->vport->netdev) &&
+			     (IECM_DESC_UNUSED(tx_q) >= IECM_TX_WAKE_THRESH) &&
+			     (IECM_TX_BUF_UNUSED(tx_q) >= tx_q->desc_count >> 2))) {
+			/* Make sure any other threads stopping queue after
+			 * this see new next_to_clean.
+			 */
+			smp_mb();
+			if (tx_q->vport->adapter->state == __IECM_UP &&
+			    __netif_subqueue_stopped(tx_q->vport->netdev,
+						     tx_q->idx)) {
+				netif_wake_subqueue(tx_q->vport->netdev,
+						    tx_q->idx);
+			}
+		}
+
+fetch_next_desc:
+		tx_desc++;
+		ntc++;
+		if (unlikely(!ntc)) {
+			ntc -= complq->desc_count;
+			tx_desc = IECM_SPLITQ_TX_COMPLQ_DESC(complq, 0);
+			change_bit(__IECM_Q_GEN_CHK, complq->flags);
+		}
+
+		prefetch(tx_desc);
+
+		/* update budget accounting */
+		complq_budget--;
+	} while (likely(complq_budget));
+
+	ntc += complq->desc_count;
+	complq->next_to_clean = ntc;
+
+	clean_completed = !!complq_budget;
+
+	return clean_completed;
+}
+
 /**
  * iecm_tx_splitq_build_ctb - populate command tag and size for queue
  * based scheduling descriptors
@@ -2337,6 +2783,940 @@ netdev_tx_t iecm_tx_splitq_start(struct sk_buff *skb,
 	return iecm_tx_splitq_frame(skb, tx_q);
 }
 
+/**
+ * iecm_ptype_to_htype - get a hash type
+ * @decoded: Decoded Rx packet type related fields
+ *
+ * Returns appropriate hash type (such as PKT_HASH_TYPE_L2/L3/L4) to be used by
+ * skb_set_hash based on PTYPE as parsed by HW Rx pipeline and is part of
+ * Rx desc.
+ */
+enum
+pkt_hash_types iecm_ptype_to_htype(struct iecm_rx_ptype_decoded *decoded)
+{
+	if (!decoded->known)
+		return PKT_HASH_TYPE_NONE;
+	if (decoded->payload_layer == IECM_RX_PTYPE_PAYLOAD_LAYER_PAY2 &&
+	    decoded->inner_prot)
+		return PKT_HASH_TYPE_L4;
+	if (decoded->payload_layer == IECM_RX_PTYPE_PAYLOAD_LAYER_PAY2 &&
+	    decoded->outer_ip)
+		return PKT_HASH_TYPE_L3;
+	if (decoded->outer_ip == IECM_RX_PTYPE_OUTER_L2)
+		return PKT_HASH_TYPE_L2;
+
+	return PKT_HASH_TYPE_NONE;
+}
+
+/**
+ * iecm_rx_hash - set the hash value in the skb
+ * @rxq: Rx descriptor ring packet is being transacted on
+ * @skb: pointer to current skb being populated
+ * @rx_desc: Receive descriptor
+ * @decoded: Decoded Rx packet type related fields
+ */
+static void
+iecm_rx_hash(struct iecm_queue *rxq, struct sk_buff *skb,
+	     struct virtchnl2_rx_flex_desc_adv_nic_3 *rx_desc,
+	     struct iecm_rx_ptype_decoded *decoded)
+{
+	u32 hash;
+
+	if (!iecm_is_feature_ena(rxq->vport, NETIF_F_RXHASH))
+		return;
+
+	hash = le16_to_cpu(rx_desc->hash1) |
+	       (rx_desc->ff2_mirrid_hash2.hash2 << 16) |
+	       (rx_desc->hash3 << 24);
+
+	skb_set_hash(skb, hash, iecm_ptype_to_htype(decoded));
+}
+
+/**
+ * iecm_rx_csum - Indicate in skb if checksum is good
+ * @rxq: Rx descriptor ring packet is being transacted on
+ * @skb: pointer to current skb being populated
+ * @csum_bits: checksum fields extracted from the descriptor
+ * @decoded: Decoded Rx packet type related fields
+ *
+ * skb->protocol must be set before this function is called
+ */
+static void iecm_rx_csum(struct iecm_queue *rxq, struct sk_buff *skb,
+			 struct iecm_rx_csum_decoded *csum_bits,
+			 struct iecm_rx_ptype_decoded *decoded)
+{
+	bool ipv4, ipv6;
+
+	/* Start with CHECKSUM_NONE and by default csum_level = 0 */
+	skb->ip_summed = CHECKSUM_NONE;
+
+	/* check if Rx checksum is enabled */
+	if (!iecm_is_feature_ena(rxq->vport, NETIF_F_RXCSUM))
+		return;
+
+	/* check if HW has decoded the packet and checksum */
+	if (!(csum_bits->l3l4p))
+		return;
+
+	ipv4 = (decoded->outer_ip == IECM_RX_PTYPE_OUTER_IP) &&
+	       (decoded->outer_ip_ver == IECM_RX_PTYPE_OUTER_IPV4);
+	ipv6 = (decoded->outer_ip == IECM_RX_PTYPE_OUTER_IP) &&
+	       (decoded->outer_ip_ver == IECM_RX_PTYPE_OUTER_IPV6);
+
+	if (ipv4 && (csum_bits->ipe || csum_bits->eipe))
+		goto checksum_fail;
+
+	if (ipv6 && csum_bits->ipv6exadd)
+		return;
+
+	/* HW checksum will be invalid if vlan stripping is not enabled and
+	 * packet has an outer vlan tag. raw_csum_inv will also not be set
+	 * even though it's invalid.
+	 */
+	if (skb_vlan_tag_present(skb))
+		return;
+
+	/* check for L4 errors and handle packets that were not able to be
+	 * checksummed
+	 */
+	if (csum_bits->l4e)
+		goto checksum_fail;
+
+	/* Only report checksum unnecessary for ICMP, TCP, UDP, or SCTP */
+	switch (decoded->inner_prot) {
+	case IECM_RX_PTYPE_INNER_PROT_ICMP:
+	case IECM_RX_PTYPE_INNER_PROT_TCP:
+	case IECM_RX_PTYPE_INNER_PROT_UDP:
+	case IECM_RX_PTYPE_INNER_PROT_SCTP:
+		skb->ip_summed = CHECKSUM_UNNECESSARY;
+	default:
+		break;
+	}
+	return;
+
+checksum_fail:
+	rxq->vport->port_stats.rx_hw_csum_err++;
+}
+
+/**
+ * iecm_rx_splitq_extract_csum_bits - Extract checksum bits from descriptor
+ * @rx_desc: receive descriptor
+ * @csum: structure to extract checksum fields
+ *
+ **/
+static void
+iecm_rx_splitq_extract_csum_bits(struct virtchnl2_rx_flex_desc_adv_nic_3 *rx_desc,
+				 struct iecm_rx_csum_decoded *csum)
+{
+	u8 qword0, qword1;
+
+	qword0 = rx_desc->status_err0_qw0;
+	qword1 = rx_desc->status_err0_qw1;
+
+	csum->ipe = !!(qword1 &
+		       BIT(VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_XSUM_IPE_S));
+	csum->eipe = !!(qword1 &
+			BIT(VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_XSUM_EIPE_S));
+	csum->l4e = !!(qword1 &
+		       BIT(VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_XSUM_L4E_S));
+	csum->l3l4p = !!(qword1 &
+			 BIT(VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_L3L4P_S));
+	csum->ipv6exadd =
+			!!(qword0 &
+			   BIT(VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_IPV6EXADD_S));
+	csum->rsc = !!(le16_to_cpu(rx_desc->hdrlen_flags) &
+		       VIRTCHNL2_RX_FLEX_DESC_ADV_RSC_M);
+	csum->raw_csum_inv = !!(le16_to_cpu(rx_desc->ptype_err_fflags0) &
+				BIT(VIRTCHNL2_RX_FLEX_DESC_ADV_FF0_S));
+	csum->raw_csum = le16_to_cpu(rx_desc->misc.raw_cs);
+	csum->pprs = 0;
+}
+
+/**
+ * iecm_rx_rsc - Set the RSC fields in the skb
+ * @rxq : Rx descriptor ring packet is being transacted on
+ * @skb : pointer to current skb being populated
+ * @rx_desc: Receive descriptor
+ * @decoded: Decoded Rx packet type related fields
+ *
+ * Return 0 on success and error code on failure
+ *
+ * Populate the skb fields with the total number of RSC segments, RSC payload
+ * length and packet type.
+ */
+static int iecm_rx_rsc(struct iecm_queue *rxq, struct sk_buff *skb,
+		       struct virtchnl2_rx_flex_desc_adv_nic_3 *rx_desc,
+		       struct iecm_rx_ptype_decoded *decoded)
+{
+	u16 rsc_segments, rsc_payload_len;
+	struct tcphdr *tcph;
+	bool ipv4, ipv6;
+
+	if (!decoded->outer_ip)
+		return -EINVAL;
+
+	rsc_payload_len = le16_to_cpu(rx_desc->misc.rscseglen);
+	if (!rsc_payload_len)
+		return -EINVAL;
+
+	ipv4 = (decoded->outer_ip == IECM_RX_PTYPE_OUTER_IP) &&
+		(decoded->outer_ip_ver == IECM_RX_PTYPE_OUTER_IPV4);
+	ipv6 = (decoded->outer_ip == IECM_RX_PTYPE_OUTER_IP) &&
+		(decoded->outer_ip_ver == IECM_RX_PTYPE_OUTER_IPV6);
+
+	if (!(ipv4 ^ ipv6))
+		return -EINVAL;
+
+	rsc_segments = DIV_ROUND_UP(skb->data_len, rsc_payload_len);
+
+	NAPI_GRO_CB(skb)->count = rsc_segments;
+	skb_shinfo(skb)->gso_size = rsc_payload_len;
+
+	skb_reset_network_header(skb);
+
+	if (ipv4) {
+		struct iphdr *ipv4h = ip_hdr(skb);
+
+		skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4;
+
+		/* Reset and set transport header offset in skb */
+		skb_set_transport_header(skb, sizeof(struct iphdr));
+		tcph = tcp_hdr(skb);
+
+		/* Compute the TCP pseudo header checksum*/
+		tcph->check =
+			~tcp_v4_check(skb->len - skb_transport_offset(skb),
+				      ipv4h->saddr, ipv4h->daddr, 0);
+	} else {
+		struct ipv6hdr *ipv6h = ipv6_hdr(skb);
+
+		skb_shinfo(skb)->gso_type = SKB_GSO_TCPV6;
+		skb_set_transport_header(skb, sizeof(struct ipv6hdr));
+		tcph = tcp_hdr(skb);
+		tcph->check =
+			~tcp_v6_check(skb->len - skb_transport_offset(skb),
+				      &ipv6h->saddr, &ipv6h->daddr, 0);
+	}
+
+	tcp_gro_complete(skb);
+
+	u64_stats_update_begin(&rxq->stats_sync);
+	rxq->q_stats.rx.rsc_pkts++;
+	u64_stats_update_end(&rxq->stats_sync);
+
+	return 0;
+}
+
+/**
+ * iecm_rx_process_skb_fields - Populate skb header fields from Rx descriptor
+ * @rxq: Rx descriptor ring packet is being transacted on
+ * @skb: pointer to current skb being populated
+ * @rx_desc: Receive descriptor
+ *
+ * This function checks the ring, descriptor, and packet information in
+ * order to populate the hash, checksum, VLAN, protocol, and
+ * other fields within the skb.
+ */
+int
+iecm_rx_process_skb_fields(struct iecm_queue *rxq, struct sk_buff *skb,
+			   struct virtchnl2_rx_flex_desc_adv_nic_3 *rx_desc)
+{
+	struct iecm_rx_ptype_decoded decoded;
+	struct iecm_rx_csum_decoded csum_bits;
+	u16 rx_ptype;
+	int err = 0;
+
+	rx_ptype = le16_to_cpu(rx_desc->ptype_err_fflags0) &
+				VIRTCHNL2_RX_FLEX_DESC_ADV_PTYPE_M;
+
+	decoded = rxq->vport->rx_ptype_lkup[rx_ptype];
+	if (!decoded.known)
+		return -EINVAL;
+
+	/* modifies the skb - consumes the enet header */
+	skb->protocol = eth_type_trans(skb, rxq->vport->netdev);
+	iecm_rx_splitq_extract_csum_bits(rx_desc, &csum_bits);
+	iecm_rx_csum(rxq, skb, &csum_bits, &decoded);
+	/* process RSS/hash */
+	iecm_rx_hash(rxq, skb, rx_desc, &decoded);
+
+	if (csum_bits.rsc)
+		err = iecm_rx_rsc(rxq, skb, rx_desc, &decoded);
+
+	return err;
+}
+
+/**
+ * iecm_rx_skb - Send a completed packet up the stack
+ * @rxq: Rx ring in play
+ * @skb: packet to send up
+ * @vlan_tag: packet vlan tag
+ *
+ * This function sends the completed packet (via. skb) up the stack using
+ * gro receive functions
+ */
+void iecm_rx_skb(struct iecm_queue *rxq, struct sk_buff *skb, u16 vlan_tag)
+{
+	/* Adding HW VLAN tag to skb must occur after processing csum */
+	if (vlan_tag & VLAN_VID_MASK)
+		__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan_tag);
+
+	napi_gro_receive(&rxq->q_vector->napi, skb);
+}
+
+/**
+ * iecm_rx_page_is_reserved - check if reuse is possible
+ * @page: page struct to check
+ */
+static bool iecm_rx_page_is_reserved(struct page *page)
+{
+	return (page_to_nid(page) != numa_mem_id()) || page_is_pfmemalloc(page);
+}
+
+/**
+ * iecm_rx_buf_adjust_pg - Prepare rx buffer for reuse
+ * @rx_buf: Rx buffer to adjust
+ * @size: Size of adjustment
+ *
+ * Update the offset within page so that rx buf will be ready to be reused.
+ * For systems with PAGE_SIZE < 8192 this function will flip the page offset
+ * so the second half of page assigned to rx buffer will be used, otherwise
+ * the offset is moved by the @size bytes
+ */
+void
+iecm_rx_buf_adjust_pg(struct iecm_rx_buf *rx_buf, unsigned int size)
+{
+	struct iecm_page_info *page_info = &rx_buf->page_info[rx_buf->page_indx];
+
+#if (PAGE_SIZE < 8192)
+	if (rx_buf->buf_size > IECM_RX_BUF_2048)
+		/* flip to second page */
+		rx_buf->page_indx = !rx_buf->page_indx;
+	else
+		/* flip page offset to other buffer */
+		page_info->page_offset ^= size;
+#else
+	/* move offset up to the next cache line */
+	page_info->page_offset += size;
+#endif
+}
+
+/**
+ * iecm_rx_can_reuse_page - Determine if page can be reused for another rx
+ * @rx_buf: buffer containing the page
+ *
+ * If page is reusable, we have a green light for calling iecm_reuse_rx_page,
+ * which will assign the current buffer to the buffer that next_to_alloc is
+ * pointing to; otherwise, the dma mapping needs to be destroyed and
+ * page freed
+ */
+bool iecm_rx_can_reuse_page(struct iecm_rx_buf *rx_buf)
+{
+	struct iecm_page_info *page_info = &rx_buf->page_info[rx_buf->page_indx];
+
+#if (PAGE_SIZE >= 8192)
+	unsigned int last_offset = PAGE_SIZE - rx_buf->buf_size;
+#endif /* PAGE_SIZE < 8192) */
+	unsigned int pagecnt_bias = page_info->pagecnt_bias;
+	struct page *page = page_info->page;
+
+	/* avoid re-using remote pages */
+	if (unlikely(iecm_rx_page_is_reserved(page)))
+		return false;
+
+#if (PAGE_SIZE < 8192)
+	/* if we are only owner of page we can reuse it */
+	if (unlikely((page_count(page) - pagecnt_bias) > 1))
+		return false;
+#else
+	if (rx_buf->page_offset > last_offset)
+		return false;
+#endif /* PAGE_SIZE < 8192) */
+
+	/* If we have drained the page fragment pool we need to update
+	 * the pagecnt_bias and page count so that we fully restock the
+	 * number of references the driver holds.
+	 */
+	if (unlikely(pagecnt_bias == 1)) {
+		page_ref_add(page, USHRT_MAX - 1);
+		page_info->pagecnt_bias = USHRT_MAX;
+	}
+
+	return true;
+}
+
+/**
+ * iecm_rx_add_frag - Add contents of Rx buffer to sk_buff as a frag
+ * @rx_buf: buffer containing page to add
+ * @skb: sk_buff to place the data into
+ * @size: packet length from rx_desc
+ *
+ * This function will add the data contained in rx_buf->page to the skb.
+ * It will just attach the page as a frag to the skb.
+ * The function will then update the page offset.
+ */
+void iecm_rx_add_frag(struct iecm_rx_buf *rx_buf, struct sk_buff *skb,
+		      unsigned int size)
+{
+	struct iecm_page_info *page_info = &rx_buf->page_info[rx_buf->page_indx];
+
+#if (PAGE_SIZE >= 8192)
+	unsigned int truesize = SKB_DATA_ALIGN(size);
+#else
+	unsigned int truesize = rx_buf->buf_size;
+#endif
+
+	skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page_info->page,
+			page_info->page_offset, size, truesize);
+
+	iecm_rx_buf_adjust_pg(rx_buf, truesize);
+}
+
+/**
+ * iecm_rx_get_buf_page - Fetch Rx buffer page and synchronize data for use
+ * @dev: device struct
+ * @rx_buf: Rx buf to fetch page for
+ * @size: size of buffer to add to skb
+ *
+ * This function will pull an Rx buffer page from the ring and synchronize it
+ * for use by the CPU.
+ */
+static void
+iecm_rx_get_buf_page(struct device *dev, struct iecm_rx_buf *rx_buf,
+		     const unsigned int size)
+{
+	struct iecm_page_info *page_info = &rx_buf->page_info[rx_buf->page_indx];
+
+	prefetch(page_info->page);
+
+	/* we are reusing so sync this buffer for CPU use */
+	dma_sync_single_range_for_cpu(dev, page_info->dma,
+				      page_info->page_offset, size,
+				      DMA_FROM_DEVICE);
+
+	/* We have pulled a buffer for use, so decrement pagecnt_bias */
+	page_info->pagecnt_bias--;
+}
+
+/**
+ * iecm_rx_construct_skb - Allocate skb and populate it
+ * @rxq: Rx descriptor queue
+ * @rx_buf: Rx buffer to pull data from
+ * @size: the length of the packet
+ *
+ * This function allocates an skb. It then populates it with the page
+ * data from the current receive descriptor, taking care to set up the
+ * skb correctly.
+ */
+struct sk_buff *
+iecm_rx_construct_skb(struct iecm_queue *rxq, struct iecm_rx_buf *rx_buf,
+		      unsigned int size)
+{
+	struct iecm_page_info *page_info = &rx_buf->page_info[rx_buf->page_indx];
+
+	void *va = page_address(page_info->page) + page_info->page_offset;
+	unsigned int headlen;
+	struct sk_buff *skb;
+
+	/* prefetch first cache line of first page */
+	prefetch(va);
+#if L1_CACHE_BYTES < 128
+	prefetch((u8 *)va + L1_CACHE_BYTES);
+#endif /* L1_CACHE_BYTES */
+	/* allocate a skb to store the frags */
+	skb = __napi_alloc_skb(&rxq->q_vector->napi, IECM_RX_HDR_SIZE,
+			       GFP_ATOMIC | __GFP_NOWARN);
+	if (unlikely(!skb))
+		return NULL;
+
+	skb_record_rx_queue(skb, rxq->idx);
+
+	/* Determine available headroom for copy */
+	headlen = size;
+	if (headlen > IECM_RX_HDR_SIZE)
+		headlen = eth_get_headlen(skb->dev, va, IECM_RX_HDR_SIZE);
+
+	/* align pull length to size of long to optimize memcpy performance */
+	memcpy(__skb_put(skb, headlen), va, ALIGN(headlen, sizeof(long)));
+
+	/* if we exhaust the linear part then add what is left as a frag */
+	size -= headlen;
+	if (size) {
+#if (PAGE_SIZE >= 8192)
+		unsigned int truesize = SKB_DATA_ALIGN(size);
+#else
+		unsigned int truesize = rx_buf->buf_size;
+#endif
+		skb_add_rx_frag(skb, 0, page_info->page,
+				page_info->page_offset + headlen, size,
+				truesize);
+		/* buffer is used by skb, update page_offset */
+		iecm_rx_buf_adjust_pg(rx_buf, truesize);
+
+	} else {
+		/* buffer is unused, reset bias back to rx_buf; data was copied
+		 * onto skb's linear part so there's no need for adjusting
+		 * page offset and we can reuse this buffer as-is
+		 */
+		page_info->pagecnt_bias++;
+	}
+
+	return skb;
+}
+
+/**
+ * iecm_rx_hdr_construct_skb - Allocate skb and populate it from header buffer
+ * @rxq: Rx descriptor queue
+ * @hdr_buf: Rx buffer to pull data from
+ * @size: the length of the packet
+ *
+ * This function allocates an skb. It then populates it with the page data from
+ * the current receive descriptor, taking care to set up the skb correctly.
+ * This specifcally uses a header buffer to start building the skb.
+ */
+static struct sk_buff *
+iecm_rx_hdr_construct_skb(struct iecm_queue *rxq, struct iecm_dma_mem *hdr_buf,
+			  unsigned int size)
+{
+	struct sk_buff *skb;
+
+	/* allocate a skb to store the frags */
+	skb = __napi_alloc_skb(&rxq->q_vector->napi, IECM_RX_HDR_SIZE,
+			       GFP_ATOMIC | __GFP_NOWARN);
+	if (unlikely(!skb))
+		return NULL;
+
+	skb_record_rx_queue(skb, rxq->idx);
+
+	memcpy(__skb_put(skb, size), hdr_buf->va, size);
+
+	return skb;
+}
+
+/**
+ * iecm_rx_splitq_test_staterr - tests bits in Rx descriptor
+ * status and error fields
+ * @stat_err_field: field from descriptor to test bits in
+ * @stat_err_bits: value to mask
+ *
+ */
+bool
+iecm_rx_splitq_test_staterr(u8 stat_err_field, const u8 stat_err_bits)
+{
+	return !!(stat_err_field & stat_err_bits);
+}
+
+/**
+ * iecm_rx_splitq_extract_vlan_tag - Extract vlan tag from the descriptor
+ * @desc: Rx flex descriptor
+ * @rxq: rxq to check the vlan flags
+ * @vlan_tag: vlan tag to fill in
+ *
+ * Return true if error bit is set in the descriptor, else return false and
+ * store the vlan_tag in the variable passed in the function parameters
+ */
+bool iecm_rx_splitq_extract_vlan_tag(struct virtchnl2_rx_flex_desc_adv_nic_3 *desc,
+				     struct iecm_queue *rxq, u16 *vlan_tag)
+{
+	u8 stat_err0_qw0, stat_err_bits, stat_err1;
+
+	stat_err_bits = BIT(VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_RXE_S);
+	stat_err0_qw0 = desc->status_err0_qw0;
+	if (unlikely(iecm_rx_splitq_test_staterr(stat_err0_qw0, stat_err_bits)))
+		return true;
+
+	stat_err1 = desc->status_err1;
+
+	if (stat_err0_qw0 & BIT(VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_L2TAG1P_S) &&
+	    test_bit(__IECM_Q_VLAN_TAG_LOC_L2TAG1, rxq->flags))
+		*vlan_tag = le16_to_cpu(desc->l2tag1);
+	if (stat_err1 & BIT(VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS1_L2TAG2P_S) &&
+	    test_bit(__IECM_Q_VLAN_TAG_LOC_L2TAG2, rxq->flags))
+		*vlan_tag = le16_to_cpu(desc->l2tag2);
+
+	return false;
+}
+
+/**
+ * iecm_rx_splitq_is_non_eop - process handling of non-EOP buffers
+ * @rx_desc: Rx descriptor for current buffer
+ *
+ * If the buffer is an EOP buffer, this function exits returning false,
+ * otherwise return true indicating that this is in fact a non-EOP buffer.
+ */
+static bool
+iecm_rx_splitq_is_non_eop(struct virtchnl2_rx_flex_desc_adv_nic_3 *rx_desc)
+{
+	/* if we are the last buffer then there is nothing else to do */
+#define IECM_RXD_EOF BIT(VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_EOF_S)
+	if (likely(iecm_rx_splitq_test_staterr(rx_desc->status_err0_qw1,
+					       IECM_RXD_EOF)))
+		return false;
+
+	return true;
+}
+
+/**
+ * iecm_rx_splitq_recycle_buf - Attempt to recycle or realloc buffer
+ * @rxbufq: receive queue
+ * @rx_buf: Rx buffer to pull data from
+ *
+ * This function will clean up the contents of the rx_buf. It will either
+ * recycle the buffer or unmap it and free the associated resources. The buffer
+ * will then be placed on a refillq where it will later be reclaimed by the
+ * corresponding bufq.
+ *
+ * This works based on page flipping. If we assume e.g., a 4k page, it will be
+ * divided into two 2k buffers. We post the first half to hardware and, after
+ * using it, flip to second half of the page with iecm_adjust_pg_offset and
+ * post that to hardware. The third time through we'll flip back to first half
+ * of page and check if stack is still using it, if not we can reuse the buffer
+ * as is, otherwise we'll drain it and get a new page.
+ */
+static void iecm_rx_splitq_recycle_buf(struct iecm_queue *rxbufq,
+				       struct iecm_rx_buf *rx_buf)
+{
+	struct iecm_page_info *page_info = &rx_buf->page_info[rx_buf->page_indx];
+
+	if (!iecm_rx_can_reuse_page(rx_buf)) {
+		/* we are not reusing the buffer so unmap it */
+		dma_unmap_page_attrs(rxbufq->dev, page_info->dma, PAGE_SIZE,
+				     DMA_FROM_DEVICE, IECM_RX_DMA_ATTR);
+		__page_frag_cache_drain(page_info->page,
+					page_info->pagecnt_bias);
+
+		/* clear contents of buffer_info */
+		page_info->page = NULL;
+		rx_buf->skb = NULL;
+
+		/* It's possible the alloc can fail here but there's not much
+		 * we can do, bufq will have to try and realloc to fill the
+		 * hole.
+		 */
+		iecm_alloc_page(rxbufq, page_info);
+	}
+
+	/* We sync the memory back to hardware now to do as much work in this
+	 * context as feasible.  Hardware won't actually know about the buffer
+	 * until it's reclaimed off the refillq and put back into the bufq.
+	 */
+	if (likely(page_info->page)) {
+		dma_sync_single_range_for_device(rxbufq->dev, page_info->dma,
+						 page_info->page_offset,
+						 rxbufq->rx_buf_size,
+						 DMA_FROM_DEVICE);
+	}
+}
+
+/**
+ * iecm_rx_bump_ntc - Bump and wrap q->next_to_clean value
+ * @q: queue to bump
+ */
+void iecm_rx_bump_ntc(struct iecm_queue *q)
+{
+	u16 ntc = q->next_to_clean + 1;
+	/* fetch, update, and store next to clean */
+	if (ntc < q->desc_count) {
+		q->next_to_clean = ntc;
+	} else {
+		q->next_to_clean = 0;
+		change_bit(__IECM_Q_GEN_CHK, q->flags);
+	}
+}
+
+/**
+ * iecm_rx_splitq_clean - Clean completed descriptors from Rx queue
+ * @rxq: Rx descriptor queue to retrieve receive buffer queue
+ * @budget: Total limit on number of packets to process
+ *
+ * This function provides a "bounce buffer" approach to Rx interrupt
+ * processing. The advantage to this is that on systems that have
+ * expensive overhead for IOMMU access this provides a means of avoiding
+ * it by maintaining the mapping of the page to the system.
+ *
+ * Returns amount of work completed
+ */
+static int iecm_rx_splitq_clean(struct iecm_queue *rxq, int budget)
+{
+	int total_rx_bytes = 0, total_rx_pkts = 0;
+	struct iecm_queue *rx_bufq = NULL;
+	struct sk_buff *skb = rxq->skb;
+	bool failure = false;
+
+	/* Process Rx packets bounded by budget */
+	while (likely(total_rx_pkts < budget)) {
+		struct virtchnl2_rx_flex_desc_adv_nic_3 *splitq_flex_rx_desc;
+		struct iecm_sw_queue *refillq = NULL;
+		struct iecm_dma_mem *hdr_buf = NULL;
+		struct iecm_rxq_set *rxq_set = NULL;
+		struct iecm_rx_buf *rx_buf = NULL;
+		u16 gen_id, buf_id, vlan_tag = 0;
+		union virtchnl2_rx_desc *rx_desc;
+		unsigned int pkt_len = 0;
+		unsigned int hdr_len = 0;
+		 /* Header buffer overflow only valid for header split */
+		bool hbo = false;
+		int bufq_id;
+
+		/* get the Rx desc from Rx queue based on 'next_to_clean' */
+		rx_desc = IECM_RX_DESC(rxq, rxq->next_to_clean);
+		splitq_flex_rx_desc = (struct virtchnl2_rx_flex_desc_adv_nic_3 *)rx_desc;
+
+		/* This memory barrier is needed to keep us from reading
+		 * any other fields out of the rx_desc
+		 */
+		dma_rmb();
+
+		/* if the descriptor isn't done, no work yet to do */
+		gen_id = le16_to_cpu(splitq_flex_rx_desc->pktlen_gen_bufq_id);
+		gen_id = (gen_id & VIRTCHNL2_RX_FLEX_DESC_ADV_GEN_M) >>
+						VIRTCHNL2_RX_FLEX_DESC_ADV_GEN_S;
+		if (test_bit(__IECM_Q_GEN_CHK, rxq->flags) != gen_id)
+			break;
+
+		if ((splitq_flex_rx_desc->rxdid_ucast &
+		    VIRTCHNL2_RX_FLEX_DESC_ADV_RXDID_M) != VIRTCHNL2_RXDID_1_FLEX_SPLITQ) {
+			iecm_rx_bump_ntc(rxq);
+			rxq->vport->port_stats.rx_bad_descs++;
+			continue;
+		}
+
+		pkt_len = le16_to_cpu(splitq_flex_rx_desc->pktlen_gen_bufq_id) &
+			  VIRTCHNL2_RX_FLEX_DESC_ADV_LEN_PBUF_M;
+
+		hbo = splitq_flex_rx_desc->status_err0_qw1 &
+		      BIT(VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_HBO_S);
+
+		if (unlikely(hbo)) {
+			rxq->vport->port_stats.rx_hsplit_hbo++;
+			goto bypass_hsplit;
+		}
+
+		hdr_len =
+			le16_to_cpu(splitq_flex_rx_desc->hdrlen_flags) &
+			VIRTCHNL2_RX_FLEX_DESC_ADV_LEN_HDR_M;
+
+bypass_hsplit:
+		bufq_id = le16_to_cpu(splitq_flex_rx_desc->pktlen_gen_bufq_id);
+		bufq_id = (bufq_id & VIRTCHNL2_RX_FLEX_DESC_ADV_BUFQ_ID_M) >>
+			  VIRTCHNL2_RX_FLEX_DESC_ADV_BUFQ_ID_S;
+
+		rxq_set = container_of(rxq, struct iecm_rxq_set, rxq);
+		if (!bufq_id)
+			refillq = rxq_set->refillq0;
+		else
+			refillq = rxq_set->refillq1;
+
+		/* retrieve buffer from the rxq */
+		rx_bufq = &rxq->rxq_grp->splitq.bufq_sets[bufq_id].bufq;
+
+		buf_id = le16_to_cpu(splitq_flex_rx_desc->buf_id);
+
+		if (pkt_len) {
+			rx_buf = &rx_bufq->rx_buf.buf[buf_id];
+			iecm_rx_get_buf_page(rx_bufq->dev, rx_buf, pkt_len);
+		}
+
+		if (hdr_len) {
+			hdr_buf = rx_bufq->rx_buf.hdr_buf[buf_id];
+
+			dma_sync_single_for_cpu(rxq->dev, hdr_buf->pa, hdr_buf->size,
+						DMA_FROM_DEVICE);
+
+			skb = iecm_rx_hdr_construct_skb(rxq, hdr_buf, hdr_len);
+			rxq->vport->port_stats.rx_hsplit++;
+		}
+
+		if (pkt_len) {
+			if (skb)
+				iecm_rx_add_frag(rx_buf, skb, pkt_len);
+			else
+				skb = iecm_rx_construct_skb(rxq, rx_buf,
+							    pkt_len);
+		}
+
+		/* exit if we failed to retrieve a buffer */
+		if (!skb) {
+			/* If we fetched a buffer, but didn't use it
+			 * undo pagecnt_bias decrement
+			 */
+			if (rx_buf)
+				rx_buf->page_info[rx_buf->page_indx].pagecnt_bias++;
+			break;
+		}
+
+		if (rx_buf)
+			iecm_rx_splitq_recycle_buf(rx_bufq, rx_buf);
+		iecm_rx_post_buf_refill(refillq, buf_id);
+
+		iecm_rx_bump_ntc(rxq);
+		/* skip if it is non EOP desc */
+		if (iecm_rx_splitq_is_non_eop(splitq_flex_rx_desc))
+			continue;
+
+		/* extract vlan tag from the descriptor */
+		if (unlikely(iecm_rx_splitq_extract_vlan_tag(splitq_flex_rx_desc,
+							     rxq, &vlan_tag))) {
+			dev_kfree_skb_any(skb);
+			skb = NULL;
+			continue;
+		}
+
+		/* pad skb if needed (to make valid ethernet frame) */
+		if (eth_skb_pad(skb)) {
+			skb = NULL;
+			continue;
+		}
+
+		/* probably a little skewed due to removing CRC */
+		total_rx_bytes += skb->len;
+
+		/* protocol */
+		if (unlikely(iecm_rx_process_skb_fields(rxq, skb,
+							splitq_flex_rx_desc))) {
+			dev_kfree_skb_any(skb);
+			skb = NULL;
+			continue;
+		}
+
+		/* send completed skb up the stack */
+		iecm_rx_skb(rxq, skb, vlan_tag);
+		skb = NULL;
+
+		/* update budget accounting */
+		total_rx_pkts++;
+	}
+	rxq->skb = skb;
+	u64_stats_update_begin(&rxq->stats_sync);
+	rxq->q_stats.rx.packets += total_rx_pkts;
+	rxq->q_stats.rx.bytes += total_rx_bytes;
+	u64_stats_update_end(&rxq->stats_sync);
+
+	/* guarantee a trip back through this routine if there was a failure */
+	return unlikely(failure) ? budget : total_rx_pkts;
+}
+
+/**
+ * iecm_rx_update_bufq_desc - Update buffer queue descriptor
+ * @bufq: Pointer to the buffer queue
+ * @desc: Refill queue descriptor
+ * @buf_desc: Buffer queue descriptor
+ *
+ * Return 0 on success and negative on failure.
+ */
+static int iecm_rx_update_bufq_desc(struct iecm_queue *bufq, u16 *desc,
+				    struct virtchnl2_splitq_rx_buf_desc *buf_desc)
+{
+	struct iecm_page_info *page_info;
+	struct iecm_rx_buf *buf;
+	u16 buf_id;
+
+	buf_id = ((*desc) & IECM_RX_BI_BUFID_M) >> IECM_RX_BI_BUFID_S;
+
+	buf = &bufq->rx_buf.buf[buf_id];
+	page_info = &buf->page_info[buf->page_indx];
+
+	/* It's possible page alloc failed during rxq clean, try to
+	 * recover here.
+	 */
+	if (unlikely(!page_info->page)) {
+		if (iecm_alloc_page(bufq, page_info))
+			return -ENOMEM;
+	}
+	buf_desc->pkt_addr = cpu_to_le64(page_info->dma + page_info->page_offset);
+	buf_desc->qword0.buf_id = cpu_to_le16(buf_id);
+
+	if (bufq->rx_hsplit_en) {
+		struct iecm_dma_mem *hdr_buf = bufq->rx_buf.hdr_buf[buf_id];
+
+		buf_desc->hdr_addr = cpu_to_le64(hdr_buf->pa);
+		dma_sync_single_for_device(bufq->dev, hdr_buf->pa,
+					   hdr_buf->size, DMA_FROM_DEVICE);
+	}
+
+	return 0;
+}
+
+/**
+ * iecm_rx_clean_refillq - Clean refill queue buffers
+ * @bufq: buffer queue to post buffers back to
+ * @refillq: refill queue to clean
+ *
+ * This function takes care of the buffer refill management
+ */
+static void iecm_rx_clean_refillq(struct iecm_queue *bufq,
+				  struct iecm_sw_queue *refillq)
+{
+	struct virtchnl2_splitq_rx_buf_desc *buf_desc;
+	u16 refillq_ntc = refillq->next_to_clean;
+	u16 bufq_nta = bufq->next_to_alloc;
+	u16 *refill_desc;
+	int cleaned = 0;
+	u16 gen;
+
+	refill_desc = IECM_SPLITQ_RX_BI_DESC(refillq, refillq_ntc);
+	buf_desc = IECM_SPLITQ_RX_BUF_DESC(bufq, bufq_nta);
+
+	/* make sure we stop at ring wrap in the unlikely case ring is full */
+	while (likely(cleaned < refillq->desc_count)) {
+		bool failure;
+
+		gen = ((*refill_desc) & IECM_RX_BI_GEN_M) >> IECM_RX_BI_GEN_S;
+		if (test_bit(__IECM_RFLQ_GEN_CHK, refillq->flags) != gen)
+			break;
+
+		failure = iecm_rx_update_bufq_desc(bufq, refill_desc,
+						   buf_desc);
+		if (failure)
+			break;
+
+		refillq_ntc++;
+		refill_desc++;
+		bufq_nta++;
+		buf_desc++;
+		cleaned++;
+
+		if (unlikely(refillq_ntc == refillq->desc_count)) {
+			change_bit(__IECM_RFLQ_GEN_CHK, refillq->flags);
+			refill_desc = IECM_SPLITQ_RX_BI_DESC(refillq, 0);
+			refillq_ntc = 0;
+		}
+		if (unlikely(bufq_nta == bufq->desc_count)) {
+			buf_desc = IECM_SPLITQ_RX_BUF_DESC(bufq, 0);
+			bufq_nta = 0;
+		}
+	}
+
+	if (cleaned) {
+		/* only update hardware tail in strides */
+		if (((bufq->next_to_use <= bufq_nta ? 0 : bufq->desc_count) +
+		    bufq_nta - bufq->next_to_use) >= bufq->rx_buf_stride)
+			iecm_rx_buf_hw_update(bufq, bufq_nta & ~(bufq->rx_buf_stride - 1));
+
+		/* update next to alloc since we have filled the ring */
+		refillq->next_to_clean = refillq_ntc;
+		bufq->next_to_alloc = bufq_nta;
+	}
+}
+
+/**
+ * iecm_rx_clean_refillq_all - Clean all refill queues
+ * @bufq: bufq with refillqs to clean
+ *
+ * Iterates through all refill queues assigned to the buffer queue assigned to
+ * this vector.  Returns true if clean is complete within budget, false
+ * otherwise.
+ */
+static void iecm_rx_clean_refillq_all(struct iecm_queue *bufq)
+{
+	struct iecm_bufq_set *bufq_set;
+	int i = 0;
+
+	bufq_set = container_of(bufq, struct iecm_bufq_set, bufq);
+	for (i = 0; i < bufq_set->num_refillqs; i++)
+		iecm_rx_clean_refillq(bufq, &bufq_set->refillqs[i]);
+}
+
 /**
  * iecm_vport_intr_clean_queues - MSIX mode Interrupt Handler
  * @irq: interrupt number
@@ -2742,6 +4122,64 @@ iecm_vport_intr_napi_ena_all(struct iecm_vport *vport)
 	}
 }
 
+/**
+ * iecm_tx_splitq_clean_all- Clean completetion queues
+ * @q_vec: queue vector
+ * @budget: Used to determine if we are in netpoll
+ *
+ * Returns false if clean is not complete else returns true
+ */
+static bool
+iecm_tx_splitq_clean_all(struct iecm_q_vector *q_vec, int budget)
+{
+	bool clean_complete = true;
+	int i, budget_per_q;
+
+	budget_per_q = max(budget / q_vec->num_txq, 1);
+	for (i = 0; i < q_vec->num_txq; i++) {
+		if (!iecm_tx_clean_complq(q_vec->tx[i], budget_per_q))
+			clean_complete = false;
+	}
+	return clean_complete;
+}
+
+/**
+ * iecm_rx_splitq_clean_all- Clean completetion queues
+ * @q_vec: queue vector
+ * @budget: Used to determine if we are in netpoll
+ * @cleaned: returns number of packets cleaned
+ *
+ * Returns false if clean is not complete else returns true
+ */
+static bool
+iecm_rx_splitq_clean_all(struct iecm_q_vector *q_vec, int budget,
+			 int *cleaned)
+{
+	bool clean_complete = true;
+	int pkts_cleaned = 0;
+	int i, budget_per_q;
+
+	budget_per_q = max(budget / q_vec->num_rxq, 1);
+	for (i = 0; i < q_vec->num_rxq; i++) {
+		struct iecm_queue *rxq = q_vec->rx[i];
+		int pkts_cleaned_per_q;
+
+		pkts_cleaned_per_q = iecm_rx_splitq_clean(rxq, budget_per_q);
+		/* if we clean as many as budgeted, we must not
+		 * be done
+		 */
+		if (pkts_cleaned_per_q >= budget_per_q)
+			clean_complete = false;
+		pkts_cleaned += pkts_cleaned_per_q;
+	}
+	*cleaned = pkts_cleaned;
+
+	for (i = 0; i < q_vec->num_bufq; i++)
+		iecm_rx_clean_refillq_all(q_vec->bufq[i]);
+
+	return clean_complete;
+}
+
 /**
  * iecm_vport_splitq_napi_poll - NAPI handler
  * @napi: struct from which you get q_vector
@@ -2749,8 +4187,34 @@ iecm_vport_intr_napi_ena_all(struct iecm_vport *vport)
  */
 static int iecm_vport_splitq_napi_poll(struct napi_struct *napi, int budget)
 {
-	/* stub */
-	return 0;
+	struct iecm_q_vector *q_vector =
+				container_of(napi, struct iecm_q_vector, napi);
+	bool clean_complete;
+	int work_done = 0;
+
+	clean_complete = iecm_tx_splitq_clean_all(q_vector, budget);
+
+	/* Handle case where we are called by netpoll with a budget of 0 */
+	if (budget <= 0)
+		return budget;
+
+	/* We attempt to distribute budget to each Rx queue fairly, but don't
+	 * allow the budget to go below 1 because that would exit polling early.
+	 */
+	clean_complete |= iecm_rx_splitq_clean_all(q_vector, budget,
+						   &work_done);
+
+	/* If work not completed, return budget and polling will return */
+	if (!clean_complete)
+		return budget;
+
+	/* Exit the polling mode, but don't re-enable interrupts if stack might
+	 * poll us due to busy-polling
+	 */
+	if (likely(napi_complete_done(napi, work_done)))
+		iecm_vport_intr_update_itr_ena_irq(q_vector);
+
+	return min_t(int, work_done, budget - 1);
 }
 
 /**
diff --git a/drivers/net/ethernet/intel/include/iecm.h b/drivers/net/ethernet/intel/include/iecm.h
index a655e797f457..3cf2a2f0cb0f 100644
--- a/drivers/net/ethernet/intel/include/iecm.h
+++ b/drivers/net/ethernet/intel/include/iecm.h
@@ -12,6 +12,10 @@
 #include <linux/etherdevice.h>
 #include <linux/ethtool.h>
 #include <net/tcp.h>
+#include <net/ip6_checksum.h>
+#include <net/ipv6.h>
+#include <net/sch_generic.h>
+#include <net/gro.h>
 #include <linux/version.h>
 #include <linux/dim.h>
 
diff --git a/drivers/net/ethernet/intel/include/iecm_txrx.h b/drivers/net/ethernet/intel/include/iecm_txrx.h
index 7ec742fd4c6b..086b0efc989a 100644
--- a/drivers/net/ethernet/intel/include/iecm_txrx.h
+++ b/drivers/net/ethernet/intel/include/iecm_txrx.h
@@ -638,6 +638,7 @@ void iecm_vport_calc_total_qs(struct iecm_adapter *adapter,
 			      struct virtchnl2_create_vport *vport_msg);
 void iecm_vport_calc_num_q_groups(struct iecm_vport *vport);
 int iecm_vport_queues_alloc(struct iecm_vport *vport);
+void iecm_rx_post_buf_refill(struct iecm_sw_queue *refillq, u16 buf_id);
 void iecm_vport_queues_rel(struct iecm_vport *vport);
 void iecm_vport_calc_num_q_vec(struct iecm_vport *vport);
 void iecm_vport_intr_rel(struct iecm_vport *vport);
@@ -650,14 +651,33 @@ int iecm_vport_intr_init(struct iecm_vport *vport);
 irqreturn_t
 iecm_vport_intr_clean_queues(int __always_unused irq, void *data);
 void iecm_vport_intr_ena_irq_all(struct iecm_vport *vport);
+enum
+pkt_hash_types iecm_ptype_to_htype(struct iecm_rx_ptype_decoded *decoded);
 int iecm_config_rss(struct iecm_vport *vport);
 void iecm_fill_dflt_rss_lut(struct iecm_vport *vport);
 int iecm_init_rss(struct iecm_vport *vport);
 void iecm_deinit_rss(struct iecm_vport *vport);
+bool iecm_rx_can_reuse_page(struct iecm_rx_buf *rx_buf);
+void iecm_rx_buf_adjust_pg(struct iecm_rx_buf *rx_buf, unsigned int size);
+void iecm_rx_add_frag(struct iecm_rx_buf *rx_buf, struct sk_buff *skb,
+		      unsigned int size);
+struct sk_buff *iecm_rx_construct_skb(struct iecm_queue *rxq,
+				      struct iecm_rx_buf *rx_buf,
+				      unsigned int size);
+void iecm_rx_skb(struct iecm_queue *rxq, struct sk_buff *skb, u16 vlan_tag);
 bool iecm_init_rx_buf_hw_alloc(struct iecm_queue *rxq, struct iecm_rx_buf *buf);
 void iecm_rx_buf_hw_update(struct iecm_queue *rxq, u32 val);
 void iecm_tx_buf_hw_update(struct iecm_queue *tx_q, u32 val,
 			   bool xmit_more);
+void iecm_rx_splitq_put_bufs(struct iecm_queue *rx_bufq,
+			     struct iecm_rx_buf *hdr_buf,
+			     struct iecm_rx_buf *rx_buf);
+bool iecm_rx_splitq_test_staterr(u8 stat_err_field, const u8 stat_err_bits);
+int iecm_rx_process_skb_fields(struct iecm_queue *rxq, struct sk_buff *skb,
+			       struct virtchnl2_rx_flex_desc_adv_nic_3 *rx_desc);
+bool iecm_rx_splitq_extract_vlan_tag(struct virtchnl2_rx_flex_desc_adv_nic_3 *desc,
+				     struct iecm_queue *rxq, u16 *vlan_tag);
+void iecm_rx_bump_ntc(struct iecm_queue *q);
 void iecm_tx_buf_rel(struct iecm_queue *tx_q, struct iecm_tx_buf *tx_buf);
 unsigned int iecm_size_to_txd_count(unsigned int size);
 unsigned int iecm_tx_desc_count_required(struct sk_buff *skb);
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 14/19] iecm: implement singleq napi_poll
  2022-01-28  0:09 [Intel-wired-lan] [PATCH net-next 00/19] Add iecm and idpf Alan Brady
                   ` (12 preceding siblings ...)
  2022-01-28  0:10 ` [Intel-wired-lan] [PATCH net-next 13/19] iecm: implement splitq napi_poll Alan Brady
@ 2022-01-28  0:10 ` Alan Brady
  2022-01-28 17:57   ` Alexander Lobakin
  2022-01-28  0:10 ` [Intel-wired-lan] [PATCH net-next 15/19] iecm: implement ethtool callbacks Alan Brady
                   ` (5 subsequent siblings)
  19 siblings, 1 reply; 89+ messages in thread
From: Alan Brady @ 2022-01-28  0:10 UTC (permalink / raw)
  To: intel-wired-lan

This adds everything we do the more traditional singleq model data path.

Signed-off-by: Phani Burra <phani.r.burra@intel.com>
Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
Signed-off-by: Alan Brady <alan.brady@intel.com>
---
 drivers/net/ethernet/intel/iecm/iecm_lib.c    |    2 +-
 .../ethernet/intel/iecm/iecm_singleq_txrx.c   | 1208 ++++++++++++++++-
 drivers/net/ethernet/intel/include/iecm.h     |    1 +
 .../net/ethernet/intel/include/iecm_txrx.h    |   31 +
 4 files changed, 1237 insertions(+), 5 deletions(-)

diff --git a/drivers/net/ethernet/intel/iecm/iecm_lib.c b/drivers/net/ethernet/intel/iecm/iecm_lib.c
index cc82e665dfaf..cbde65f1c523 100644
--- a/drivers/net/ethernet/intel/iecm/iecm_lib.c
+++ b/drivers/net/ethernet/intel/iecm/iecm_lib.c
@@ -2723,7 +2723,7 @@ static const struct net_device_ops iecm_netdev_ops_splitq = {
 static const struct net_device_ops iecm_netdev_ops_singleq = {
 	.ndo_open = iecm_open,
 	.ndo_stop = iecm_stop,
-	.ndo_start_xmit = NULL,
+	.ndo_start_xmit = iecm_tx_singleq_start,
 	.ndo_set_rx_mode = iecm_set_rx_mode,
 	.ndo_validate_addr = eth_validate_addr,
 	.ndo_set_mac_address = iecm_set_mac,
diff --git a/drivers/net/ethernet/intel/iecm/iecm_singleq_txrx.c b/drivers/net/ethernet/intel/iecm/iecm_singleq_txrx.c
index d6c47cb84249..7bfec79e6afc 100644
--- a/drivers/net/ethernet/intel/iecm/iecm_singleq_txrx.c
+++ b/drivers/net/ethernet/intel/iecm/iecm_singleq_txrx.c
@@ -3,6 +3,779 @@
 
 #include "iecm.h"
 
+/**
+ * iecm_tx_singleq_csum - Enable tx checksum offloads
+ * @first: pointer to first descriptor
+ * @off: pointer to struct that holds offload parameters
+ *
+ * Returns 0 or error (negative) if checksum offload
+ */
+static
+int iecm_tx_singleq_csum(struct iecm_tx_buf *first,
+			 struct iecm_tx_offload_params *off)
+{
+	u32 l4_len = 0, l3_len = 0, l2_len = 0;
+	struct sk_buff *skb = first->skb;
+	union {
+		struct iphdr *v4;
+		struct ipv6hdr *v6;
+		unsigned char *hdr;
+	} ip;
+	union {
+		struct tcphdr *tcp;
+		unsigned char *hdr;
+	} l4;
+	__be16 frag_off, protocol;
+	unsigned char *exthdr;
+	u32 offset, cmd = 0;
+	u8 l4_proto = 0;
+
+	if (skb->ip_summed != CHECKSUM_PARTIAL)
+		return 0;
+
+	if (skb->encapsulation)
+		return -1;
+
+	ip.hdr = skb_network_header(skb);
+	l4.hdr = skb_transport_header(skb);
+
+	/* compute outer L2 header size */
+	l2_len = ip.hdr - skb->data;
+	offset = (l2_len / 2) << IECM_TX_DESC_LEN_MACLEN_S;
+
+	/* Enable IP checksum offloads */
+	protocol = vlan_get_protocol(skb);
+	if (protocol == htons(ETH_P_IP)) {
+		l4_proto = ip.v4->protocol;
+		/* the stack computes the IP header already, the only time we
+		 * need the hardware to recompute it is in the case of TSO.
+		 */
+		if (first->tx_flags & IECM_TX_FLAGS_TSO)
+			cmd |= IECM_TX_DESC_CMD_IIPT_IPV4_CSUM;
+		else
+			cmd |= IECM_TX_DESC_CMD_IIPT_IPV4;
+
+	} else if (protocol == htons(ETH_P_IPV6)) {
+		cmd |= IECM_TX_DESC_CMD_IIPT_IPV6;
+		exthdr = ip.hdr + sizeof(struct ipv6hdr);
+		l4_proto = ip.v6->nexthdr;
+		if (l4.hdr != exthdr)
+			ipv6_skip_exthdr(skb, exthdr - skb->data, &l4_proto,
+					 &frag_off);
+	} else {
+		return -1;
+	}
+
+	/* compute inner L3 header size */
+	l3_len = l4.hdr - ip.hdr;
+	offset |= (l3_len / 4) << IECM_TX_DESC_LEN_IPLEN_S;
+
+	/* Enable L4 checksum offloads */
+	switch (l4_proto) {
+	case IPPROTO_TCP:
+		/* enable checksum offloads */
+		cmd |= IECM_TX_DESC_CMD_L4T_EOFT_TCP;
+		l4_len = l4.tcp->doff;
+		offset |= l4_len << IECM_TX_DESC_LEN_L4_LEN_S;
+		break;
+	case IPPROTO_UDP:
+		/* enable UDP checksum offload */
+		cmd |= IECM_TX_DESC_CMD_L4T_EOFT_UDP;
+		l4_len = (sizeof(struct udphdr) >> 2);
+		offset |= l4_len << IECM_TX_DESC_LEN_L4_LEN_S;
+		break;
+	case IPPROTO_SCTP:
+		/* enable SCTP checksum offload */
+		cmd |= IECM_TX_DESC_CMD_L4T_EOFT_SCTP;
+		l4_len = sizeof(struct sctphdr) >> 2;
+		offset |= l4_len << IECM_TX_DESC_LEN_L4_LEN_S;
+		break;
+
+	default:
+		if (first->tx_flags & IECM_TX_FLAGS_TSO)
+			return -1;
+		skb_checksum_help(skb);
+		return 0;
+	}
+
+	off->td_cmd |= cmd;
+	off->hdr_offsets |= offset;
+	return 1;
+}
+
+/**
+ * iecm_tx_singleq_map - Build the Tx base descriptor
+ * @tx_q: queue to send buffer on
+ * @first: first buffer info buffer to use
+ * @offloads: pointer to struct that holds offload parameters
+ *
+ * This function loops over the skb data pointed to by *first
+ * and gets a physical address for each memory location and programs
+ * it and the length into the transmit base mode descriptor.
+ */
+static void
+iecm_tx_singleq_map(struct iecm_queue *tx_q, struct iecm_tx_buf *first,
+		    struct iecm_tx_offload_params *offloads)
+{
+	u32 offsets = offloads->hdr_offsets;
+	struct iecm_base_tx_desc *tx_desc;
+	u64 td_cmd = offloads->td_cmd;
+	unsigned int data_len, size;
+	struct iecm_tx_buf *tx_buf;
+	u16 i = tx_q->next_to_use;
+	struct netdev_queue *nq;
+	struct sk_buff *skb;
+	skb_frag_t *frag;
+	dma_addr_t dma;
+	u64 td_tag = 0;
+
+	skb = first->skb;
+
+	data_len = skb->data_len;
+	size = skb_headlen(skb);
+
+	tx_desc = IECM_BASE_TX_DESC(tx_q, i);
+
+	if (first->tx_flags & IECM_TX_FLAGS_VLAN_TAG) {
+		td_cmd |= (u64)IECM_TX_DESC_CMD_IL2TAG1;
+		td_tag = (first->tx_flags & IECM_TX_FLAGS_VLAN_MASK) >>
+			 IECM_TX_FLAGS_VLAN_SHIFT;
+	}
+
+	dma = dma_map_single(tx_q->dev, skb->data, size, DMA_TO_DEVICE);
+
+	tx_buf = first;
+
+	/* write each descriptor with CRC bit */
+	if (tx_q->vport->adapter->dev_ops.crc_enable)
+		tx_q->vport->adapter->dev_ops.crc_enable(&td_cmd);
+
+	for (frag = &skb_shinfo(skb)->frags[0];; frag++) {
+		unsigned int max_data = IECM_TX_MAX_DESC_DATA_ALIGNED;
+
+		if (dma_mapping_error(tx_q->dev, dma))
+			goto dma_error;
+
+		/* record length, and DMA address */
+		dma_unmap_len_set(tx_buf, len, size);
+		dma_unmap_addr_set(tx_buf, dma, dma);
+
+		/* align size to end of page */
+		max_data += -dma & (IECM_TX_MAX_READ_REQ_SIZE - 1);
+		tx_desc->buf_addr = cpu_to_le64(dma);
+
+		/* account for data chunks larger than the hardware
+		 * can handle
+		 */
+		while (unlikely(size > IECM_TX_MAX_DESC_DATA)) {
+			tx_desc->qw1 = iecm_tx_singleq_build_ctob(td_cmd,
+								  offsets,
+								  max_data,
+								  td_tag);
+			tx_desc++;
+			i++;
+
+			if (i == tx_q->desc_count) {
+				tx_desc = IECM_BASE_TX_DESC(tx_q, 0);
+				i = 0;
+			}
+
+			dma += max_data;
+			size -= max_data;
+
+			max_data = IECM_TX_MAX_DESC_DATA_ALIGNED;
+			tx_desc->buf_addr = cpu_to_le64(dma);
+		}
+
+		if (likely(!data_len))
+			break;
+		tx_desc->qw1 = iecm_tx_singleq_build_ctob(td_cmd, offsets,
+							  size, td_tag);
+		tx_desc++;
+		i++;
+
+		if (i == tx_q->desc_count) {
+			tx_desc = IECM_BASE_TX_DESC(tx_q, 0);
+			i = 0;
+		}
+
+		size = skb_frag_size(frag);
+		data_len -= size;
+
+		dma = skb_frag_dma_map(tx_q->dev, frag, 0, size,
+				       DMA_TO_DEVICE);
+
+		tx_buf = &tx_q->tx_buf[i];
+	}
+
+	/* record bytecount for BQL */
+	nq = netdev_get_tx_queue(tx_q->vport->netdev, tx_q->idx);
+	netdev_tx_sent_queue(nq, first->bytecount);
+
+	/* record SW timestamp if HW timestamp is not available */
+	skb_tx_timestamp(first->skb);
+
+	/* write last descriptor with RS and EOP bits */
+	td_cmd |= (u64)(IECM_TX_DESC_CMD_EOP | IECM_TX_DESC_CMD_RS);
+
+	tx_desc->qw1 = iecm_tx_singleq_build_ctob(td_cmd, offsets, size, td_tag);
+
+	i++;
+	if (i == tx_q->desc_count)
+		i = 0;
+
+	/* set next_to_watch value indicating a packet is present */
+	first->next_to_watch = tx_desc;
+
+	iecm_tx_buf_hw_update(tx_q, i, netdev_xmit_more());
+
+	return;
+
+dma_error:
+	/* clear dma mappings for failed tx_buf map */
+	for (;;) {
+		tx_buf = &tx_q->tx_buf[i];
+		iecm_tx_buf_rel(tx_q, tx_buf);
+		if (tx_buf == first)
+			break;
+		if (i == 0)
+			i = tx_q->desc_count;
+		i--;
+	}
+
+	tx_q->next_to_use = i;
+}
+
+/**
+ * iecm_tx_singleq_frame - Sends buffer on Tx ring using base descriptors
+ * @skb: send buffer
+ * @tx_q: queue to send buffer on
+ *
+ * Returns NETDEV_TX_OK if sent, else an error code
+ */
+static netdev_tx_t
+iecm_tx_singleq_frame(struct sk_buff *skb, struct iecm_queue *tx_q)
+{
+	struct iecm_tx_offload_params offload = {0};
+	struct iecm_tx_buf *first;
+	unsigned int count;
+	int csum, tso;
+
+	count = iecm_tx_desc_count_required(skb);
+
+	if (iecm_chk_linearize(skb, tx_q->tx_max_bufs, count)) {
+		if (__skb_linearize(skb)) {
+			dev_kfree_skb_any(skb);
+			return NETDEV_TX_OK;
+		}
+		count = iecm_size_to_txd_count(skb->len);
+		tx_q->vport->port_stats.tx_linearize++;
+	}
+
+	if (iecm_tx_maybe_stop(tx_q, count + IECM_TX_DESCS_PER_CACHE_LINE +
+			       IECM_TX_DESCS_FOR_CTX)) {
+		return NETDEV_TX_BUSY;
+	}
+
+	/* record the location of the first descriptor for this packet */
+	first = &tx_q->tx_buf[tx_q->next_to_use];
+	first->skb = skb;
+	first->bytecount = max_t(unsigned int, skb->len, ETH_ZLEN);
+	first->gso_segs = 1;
+	first->tx_flags = 0;
+
+	iecm_tx_prepare_vlan_flags(tx_q, first, skb);
+
+	tso = iecm_tso(first, &offload);
+	if (tso < 0)
+		goto out_drop;
+
+	csum = iecm_tx_singleq_csum(first, &offload);
+	if (csum < 0)
+		goto out_drop;
+
+	if (first->tx_flags & IECM_TX_FLAGS_TSO) {
+		struct iecm_base_tx_ctx_desc *ctx_desc;
+		int i = tx_q->next_to_use;
+		u64 qw1 = (u64)IECM_TX_DESC_DTYPE_CTX |
+			       IECM_TX_CTX_DESC_TSO << IECM_TXD_CTX_QW1_CMD_S;
+
+		/* grab the next descriptor */
+		ctx_desc = IECM_BASE_TX_CTX_DESC(tx_q, i);
+		i++;
+		tx_q->next_to_use = (i < tx_q->desc_count) ? i : 0;
+
+		qw1 |= ((u64)offload.tso_len << IECM_TXD_CTX_QW1_TSO_LEN_S) &
+			IECM_TXD_CTX_QW1_TSO_LEN_M;
+
+		qw1 |= ((u64)offload.mss << IECM_TXD_CTX_QW1_MSS_S) &
+			IECM_TXD_CTX_QW1_MSS_M;
+
+		ctx_desc->qw0.rsvd0 = cpu_to_le32(0);
+		ctx_desc->qw0.l2tag2 = cpu_to_le16(0);
+		ctx_desc->qw0.rsvd1 = cpu_to_le16(0);
+		ctx_desc->qw1 = cpu_to_le64(qw1);
+	}
+
+	iecm_tx_singleq_map(tx_q, first, &offload);
+
+	return NETDEV_TX_OK;
+
+out_drop:
+	dev_kfree_skb_any(skb);
+	return NETDEV_TX_OK;
+}
+
+/**
+ * iecm_tx_singleq_start - Selects the right Tx queue to send buffer
+ * @skb: send buffer
+ * @netdev: network interface device structure
+ *
+ * Returns NETDEV_TX_OK if sent, else an error code
+ */
+netdev_tx_t iecm_tx_singleq_start(struct sk_buff *skb,
+				  struct net_device *netdev)
+{
+	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
+	struct iecm_queue *tx_q;
+
+	if (test_bit(__IECM_HR_RESET_IN_PROG, vport->adapter->flags))
+		return NETDEV_TX_BUSY;
+
+	tx_q = vport->txqs[skb->queue_mapping];
+
+	/* hardware can't handle really short frames, hardware padding works
+	 * beyond this point
+	 */
+	if (skb_put_padto(skb, IECM_TX_MIN_LEN))
+		return NETDEV_TX_OK;
+
+	return iecm_tx_singleq_frame(skb, tx_q);
+}
+
+/**
+ * iecm_tx_singleq_clean - Reclaim resources from queue
+ * @tx_q: Tx queue to clean
+ * @napi_budget: Used to determine if we are in netpoll
+ *
+ */
+static bool iecm_tx_singleq_clean(struct iecm_queue *tx_q, int napi_budget)
+{
+	unsigned int budget = tx_q->vport->compln_clean_budget;
+	unsigned int total_bytes = 0, total_pkts = 0;
+	struct iecm_base_tx_desc *tx_desc;
+	s16 ntc = tx_q->next_to_clean;
+	struct iecm_tx_buf *tx_buf;
+	struct netdev_queue *nq;
+
+	tx_desc = IECM_BASE_TX_DESC(tx_q, ntc);
+	tx_buf = &tx_q->tx_buf[ntc];
+	ntc -= tx_q->desc_count;
+
+	do {
+		struct iecm_base_tx_desc *eop_desc =
+			(struct iecm_base_tx_desc *)tx_buf->next_to_watch;
+
+		/* if next_to_watch is not set then no work pending */
+		if (!eop_desc)
+			break;
+
+		/* prevent any other reads prior to eop_desc */
+		smp_rmb();
+
+		/* if the descriptor isn't done, no work yet to do */
+		if (!(eop_desc->qw1 &
+		      cpu_to_le64(IECM_TX_DESC_DTYPE_DESC_DONE)))
+			break;
+
+		/* clear next_to_watch to prevent false hangs */
+		tx_buf->next_to_watch = NULL;
+
+		/* update the statistics for this packet */
+		total_bytes += tx_buf->bytecount;
+		total_pkts += tx_buf->gso_segs;
+
+		/* free the skb */
+		napi_consume_skb(tx_buf->skb, napi_budget);
+
+		/* unmap skb header data */
+		dma_unmap_single(tx_q->dev,
+				 dma_unmap_addr(tx_buf, dma),
+				 dma_unmap_len(tx_buf, len),
+				 DMA_TO_DEVICE);
+
+		/* clear tx_buf data */
+		tx_buf->skb = NULL;
+		dma_unmap_len_set(tx_buf, len, 0);
+
+		/* unmap remaining buffers */
+		while (tx_desc != eop_desc) {
+			tx_buf++;
+			tx_desc++;
+			ntc++;
+			if (unlikely(!ntc)) {
+				ntc -= tx_q->desc_count;
+				tx_buf = tx_q->tx_buf;
+				tx_desc = IECM_BASE_TX_DESC(tx_q, 0);
+			}
+
+			/* unmap any remaining paged data */
+			if (dma_unmap_len(tx_buf, len)) {
+				dma_unmap_page(tx_q->dev,
+					       dma_unmap_addr(tx_buf, dma),
+					       dma_unmap_len(tx_buf, len),
+					       DMA_TO_DEVICE);
+				dma_unmap_len_set(tx_buf, len, 0);
+			}
+		}
+
+		tx_buf++;
+		tx_desc++;
+		ntc++;
+		if (unlikely(!ntc)) {
+			ntc -= tx_q->desc_count;
+			tx_buf = tx_q->tx_buf;
+			tx_desc = IECM_BASE_TX_DESC(tx_q, 0);
+		}
+		/* update budget */
+		budget--;
+	} while (likely(budget));
+
+	ntc += tx_q->desc_count;
+	tx_q->next_to_clean = ntc;
+
+	u64_stats_update_begin(&tx_q->stats_sync);
+	tx_q->q_stats.tx.packets += total_pkts;
+	tx_q->q_stats.tx.bytes += total_bytes;
+	u64_stats_update_end(&tx_q->stats_sync);
+
+	nq = netdev_get_tx_queue(tx_q->vport->netdev, tx_q->idx);
+	netdev_tx_completed_queue(nq, total_pkts, total_bytes);
+
+	if (unlikely(total_pkts && netif_carrier_ok(tx_q->vport->netdev) &&
+		     (IECM_DESC_UNUSED(tx_q) >= IECM_TX_WAKE_THRESH))) {
+		/* Make sure any other threads stopping queue after this see
+		 * new next_to_clean.
+		 */
+		smp_mb();
+		if (__netif_subqueue_stopped(tx_q->vport->netdev, tx_q->idx) &&
+		    tx_q->vport->adapter->state == __IECM_UP)
+			netif_wake_subqueue(tx_q->vport->netdev, tx_q->idx);
+	}
+
+	return !!budget;
+}
+
+/**
+ * iecm_tx_singleq_clean_all - Clean all Tx queues
+ * @q_vec: queue vector
+ * @budget: Used to determine if we are in netpoll
+ *
+ * Returns false if clean is not complete else returns true
+ */
+static bool
+iecm_tx_singleq_clean_all(struct iecm_q_vector *q_vec, int budget)
+{
+	bool clean_complete = true;
+	int i, budget_per_q;
+
+	budget_per_q = max(budget / q_vec->num_txq, 1);
+	for (i = 0; i < q_vec->num_txq; i++)
+		clean_complete = iecm_tx_singleq_clean(q_vec->tx[i], budget_per_q);
+
+	return clean_complete;
+}
+
+/**
+ * iecm_rx_singleq_test_staterr - tests bits in Rx descriptor
+ * status and error fields
+ * @rx_desc: pointer to receive descriptor (in le64 format)
+ * @stat_err_bits: value to mask
+ *
+ * This function does some fast chicanery in order to return the
+ * value of the mask which is really only used for boolean tests.
+ * The status_error_ptype_len doesn't need to be shifted because it begins
+ * at offset zero.
+ */
+bool
+iecm_rx_singleq_test_staterr(union virtchnl2_rx_desc *rx_desc,
+			     const u64 stat_err_bits)
+{
+	return !!(rx_desc->base_wb.qword1.status_error_ptype_len &
+		  cpu_to_le64(stat_err_bits));
+}
+
+/**
+ * iecm_rx_singleq_is_non_eop - process handling of non-EOP buffers
+ * @rxq: Rx ring being processed
+ * @rx_desc: Rx descriptor for current buffer
+ * @skb: Current socket buffer containing buffer in progress
+ */
+bool iecm_rx_singleq_is_non_eop(struct iecm_queue *rxq,
+				union virtchnl2_rx_desc *rx_desc,
+				struct sk_buff *skb)
+{
+	/* if we are the last buffer then there is nothing else to do */
+#define IECM_RXD_EOF BIT(VIRTCHNL2_RX_BASE_DESC_STATUS_EOF_S)
+	if (likely(iecm_rx_singleq_test_staterr(rx_desc, IECM_RXD_EOF)))
+		return false;
+
+	/* place skb in next buffer to be received */
+	rxq->rx_buf.buf[rxq->next_to_clean].skb = skb;
+
+	return true;
+}
+
+/**
+ * iecm_rx_singleq_csum - Indicate in skb if checksum is good
+ * @rxq: Rx descriptor ring packet is being transacted on
+ * @skb: skb currently being received and modified
+ * @csum_bits: descriptor csum bits
+ * @ptype: the packet type decoded by hardware
+ *
+ * skb->protocol must be set before this function is called
+ */
+static void iecm_rx_singleq_csum(struct iecm_queue *rxq, struct sk_buff *skb,
+				 struct iecm_rx_csum_decoded *csum_bits,
+				 u16 ptype)
+{
+	struct iecm_rx_ptype_decoded decoded;
+	bool ipv4, ipv6;
+
+	/* Start with CHECKSUM_NONE and by default csum_level = 0 */
+	skb->ip_summed = CHECKSUM_NONE;
+	skb_checksum_none_assert(skb);
+
+	/* check if Rx checksum is enabled */
+	if (!(rxq->vport->netdev->features & NETIF_F_RXCSUM))
+		return;
+
+	/* check if HW has decoded the packet and checksum */
+	if (!(csum_bits->l3l4p))
+		return;
+
+	decoded = rxq->vport->rx_ptype_lkup[ptype];
+	if (!(decoded.known && decoded.outer_ip))
+		return;
+
+	ipv4 = (decoded.outer_ip == IECM_RX_PTYPE_OUTER_IP) &&
+	       (decoded.outer_ip_ver == IECM_RX_PTYPE_OUTER_IPV4);
+	ipv6 = (decoded.outer_ip == IECM_RX_PTYPE_OUTER_IP) &&
+	       (decoded.outer_ip_ver == IECM_RX_PTYPE_OUTER_IPV6);
+
+	if (ipv4 && (csum_bits->ipe || csum_bits->eipe))
+		goto checksum_fail;
+	else if (ipv6 && (csum_bits->ipv6exadd))
+		goto checksum_fail;
+
+	/* check for L4 errors and handle packets that were not able to be
+	 * checksummed due to arrival speed
+	 */
+	if (csum_bits->l4e)
+		goto checksum_fail;
+
+	if (csum_bits->nat && csum_bits->eudpe)
+		goto checksum_fail;
+
+	/* Handle packets that were not able to be checksummed due to arrival
+	 * speed, in this case the stack can compute the csum.
+	 */
+	if (csum_bits->pprs)
+		return;
+
+	/* If there is an outer header present that might contain a checksum
+	 * we need to bump the checksum level by 1 to reflect the fact that
+	 * we are indicating we validated the inner checksum.
+	 */
+	if (decoded.tunnel_type >= IECM_RX_PTYPE_TUNNEL_IP_GRENAT)
+		skb->csum_level = 1;
+
+	/* Only report checksum unnecessary for ICMP, TCP, UDP, or SCTP */
+	switch (decoded.inner_prot) {
+	case IECM_RX_PTYPE_INNER_PROT_ICMP:
+	case IECM_RX_PTYPE_INNER_PROT_TCP:
+	case IECM_RX_PTYPE_INNER_PROT_UDP:
+	case IECM_RX_PTYPE_INNER_PROT_SCTP:
+		skb->ip_summed = CHECKSUM_UNNECESSARY;
+	default:
+		break;
+	}
+	return;
+
+checksum_fail:
+	rxq->vport->port_stats.rx_hw_csum_err++;
+}
+
+/**
+ * iecm_rx_singleq_base_csum - Indicate in skb if hw indicated a good cksum
+ * @rx_q: Rx completion queue
+ * @skb: skb currently being received and modified
+ * @rx_desc: the receive descriptor
+ * @ptype: Rx packet type
+ *
+ * This function only operates on the VIRTCHNL2_RXDID_1_32B_BASE_M legacy 32byte
+ * descriptor writeback format.
+ **/
+static void
+iecm_rx_singleq_base_csum(struct iecm_queue *rx_q, struct sk_buff *skb,
+			  union virtchnl2_rx_desc *rx_desc, u16 ptype)
+{
+	struct iecm_rx_csum_decoded csum_bits;
+	u32 rx_error, rx_status;
+	u64 qword;
+
+	qword = le64_to_cpu(rx_desc->base_wb.qword1.status_error_ptype_len);
+
+	rx_status = ((qword & VIRTCHNL2_RX_BASE_DESC_QW1_STATUS_M) >>
+					VIRTCHNL2_RX_BASE_DESC_QW1_STATUS_S);
+	rx_error = ((qword & VIRTCHNL2_RX_BASE_DESC_QW1_ERROR_M) >>
+					VIRTCHNL2_RX_BASE_DESC_QW1_ERROR_S);
+
+	csum_bits.ipe = !!(rx_error & BIT(VIRTCHNL2_RX_BASE_DESC_ERROR_IPE_S));
+	csum_bits.eipe = !!(rx_error & BIT(VIRTCHNL2_RX_BASE_DESC_ERROR_EIPE_S));
+	csum_bits.l4e = !!(rx_error & BIT(VIRTCHNL2_RX_BASE_DESC_ERROR_L4E_S));
+	csum_bits.pprs = !!(rx_error & BIT(VIRTCHNL2_RX_BASE_DESC_ERROR_PPRS_S));
+	csum_bits.l3l4p = !!(rx_status &
+			     BIT(VIRTCHNL2_RX_BASE_DESC_STATUS_L3L4P_S));
+	csum_bits.ipv6exadd = !!(rx_status &
+				 BIT(VIRTCHNL2_RX_BASE_DESC_STATUS_IPV6EXADD_S));
+	csum_bits.nat = 0;
+	csum_bits.eudpe = 0;
+
+	iecm_rx_singleq_csum(rx_q, skb, &csum_bits, ptype);
+}
+
+/**
+ * iecm_rx_singleq_flex_csum - Indicate in skb if hw indicated a good cksum
+ * @rx_q: Rx completion queue
+ * @skb: skb currently being received and modified
+ * @rx_desc: the receive descriptor
+ * @ptype: Rx packet type
+ *
+ * This function only operates on the VIRTCHNL2_RXDID_2_FLEX_SQ_NIC flexible
+ * descriptor writeback format.
+ **/
+static void
+iecm_rx_singleq_flex_csum(struct iecm_queue *rx_q, struct sk_buff *skb,
+			  union virtchnl2_rx_desc *rx_desc, u16 ptype)
+{
+	struct iecm_rx_csum_decoded csum_bits;
+	u16 rx_status0, rx_status1;
+
+	rx_status0 = le16_to_cpu(rx_desc->flex_nic_wb.status_error0);
+	rx_status1 = le16_to_cpu(rx_desc->flex_nic_wb.status_error1);
+
+	csum_bits.ipe = !!(rx_status0 &
+			   BIT(VIRTCHNL2_RX_FLEX_DESC_STATUS0_XSUM_IPE_S));
+	csum_bits.eipe = !!(rx_status0 &
+			    BIT(VIRTCHNL2_RX_FLEX_DESC_STATUS0_XSUM_EIPE_S));
+	csum_bits.l4e = !!(rx_status0 &
+			   BIT(VIRTCHNL2_RX_FLEX_DESC_STATUS0_XSUM_L4E_S));
+	csum_bits.eudpe = !!(rx_status0 &
+			     BIT(VIRTCHNL2_RX_FLEX_DESC_STATUS0_XSUM_EUDPE_S));
+	csum_bits.l3l4p = !!(rx_status0 &
+			     BIT(VIRTCHNL2_RX_FLEX_DESC_STATUS0_L3L4P_S));
+	csum_bits.ipv6exadd =
+			!!(rx_status0 &
+			   BIT(VIRTCHNL2_RX_FLEX_DESC_STATUS0_IPV6EXADD_S));
+	csum_bits.nat = !!(rx_status1 &
+			   BIT(VIRTCHNL2_RX_FLEX_DESC_STATUS1_NAT_S));
+	csum_bits.pprs = 0;
+
+	iecm_rx_singleq_csum(rx_q, skb, &csum_bits, ptype);
+}
+
+/**
+ * iecm_rx_singleq_base_hash - set the hash value in the skb
+ * @rx_q: Rx completion queue
+ * @skb: skb currently being received and modified
+ * @rx_desc: specific descriptor
+ * @decoded: Decoded Rx packet type related fields
+ *
+ * This function only operates on the VIRTCHNL2_RXDID_1_32B_BASE_M legacy 32byte
+ * descriptor writeback format.
+ **/
+static void
+iecm_rx_singleq_base_hash(struct iecm_queue *rx_q, struct sk_buff *skb,
+			  union virtchnl2_rx_desc *rx_desc,
+			  struct iecm_rx_ptype_decoded *decoded)
+{
+	const __le64 rss_mask =
+		cpu_to_le64((u64)VIRTCHNL2_RX_BASE_DESC_FLTSTAT_RSS_HASH <<
+				VIRTCHNL2_RX_BASE_DESC_STATUS_FLTSTAT_S);
+	u32 hash;
+
+	if (!(rx_q->vport->netdev->features & NETIF_F_RXHASH))
+		return;
+
+	if ((rx_desc->base_wb.qword1.status_error_ptype_len & rss_mask) ==
+								rss_mask) {
+		hash = le32_to_cpu(rx_desc->base_wb.qword0.hi_dword.rss);
+		skb_set_hash(skb, hash, iecm_ptype_to_htype(decoded));
+	}
+}
+
+/**
+ * iecm_rx_singleq_flex_hash - set the hash value in the skb
+ * @rx_q: Rx completion queue
+ * @skb: skb currently being received and modified
+ * @rx_desc: specific descriptor
+ * @decoded: Decoded Rx packet type related fields
+ *
+ * This function only operates on the VIRTCHNL2_RXDID_2_FLEX_SQ_NIC flexible
+ * descriptor writeback format.
+ **/
+static void
+iecm_rx_singleq_flex_hash(struct iecm_queue *rx_q, struct sk_buff *skb,
+			  union virtchnl2_rx_desc *rx_desc,
+			  struct iecm_rx_ptype_decoded *decoded)
+{
+	__le16 status0;
+
+	if (!(rx_q->vport->netdev->features & NETIF_F_RXHASH))
+		return;
+
+	status0 = rx_desc->flex_nic_wb.status_error0;
+	if (status0 &
+		cpu_to_le16(BIT(VIRTCHNL2_RX_FLEX_DESC_STATUS0_RSS_VALID_S))) {
+		u32 hash = le32_to_cpu(rx_desc->flex_nic_wb.rss_hash);
+
+		skb_set_hash(skb, hash, iecm_ptype_to_htype(decoded));
+	}
+}
+
+/**
+ * iecm_rx_singleq_process_skb_fields - Populate skb header fields from Rx
+ * descriptor
+ * @rx_q: Rx descriptor ring packet is being transacted on
+ * @skb: pointer to current skb being populated
+ * @rx_desc: descriptor for skb
+ * @ptype: packet type
+ *
+ * This function checks the ring, descriptor, and packet information in
+ * order to populate the hash, checksum, VLAN, protocol, and
+ * other fields within the skb.
+ */
+void
+iecm_rx_singleq_process_skb_fields(struct iecm_queue *rx_q, struct sk_buff *skb,
+				   union virtchnl2_rx_desc *rx_desc,
+				   u16 ptype)
+{
+	struct iecm_rx_ptype_decoded decoded =
+					rx_q->vport->rx_ptype_lkup[ptype];
+
+	/* modifies the skb - consumes the enet header */
+	skb->protocol = eth_type_trans(skb, rx_q->vport->netdev);
+
+	if (rx_q->rxdids == VIRTCHNL2_RXDID_1_32B_BASE_M) {
+		iecm_rx_singleq_base_hash(rx_q, skb, rx_desc, &decoded);
+		iecm_rx_singleq_base_csum(rx_q, skb, rx_desc, ptype);
+	} else {
+		iecm_rx_singleq_flex_hash(rx_q, skb, rx_desc, &decoded);
+		iecm_rx_singleq_flex_csum(rx_q, skb, rx_desc, ptype);
+	}
+}
+
 /**
  * iecm_rx_singleq_buf_hw_alloc_all - Replace used receive buffers
  * @rx_q: queue for which the hw buffers are allocated
@@ -13,8 +786,410 @@
 bool iecm_rx_singleq_buf_hw_alloc_all(struct iecm_queue *rx_q,
 				      u16 cleaned_count)
 {
-	/* stub */
-	return true;
+	struct virtchnl2_singleq_rx_buf_desc *singleq_rx_desc = NULL;
+	struct iecm_page_info *page_info;
+	u16 nta = rx_q->next_to_alloc;
+	struct iecm_rx_buf *buf;
+
+	/* do nothing if no valid netdev defined */
+	if (!rx_q->vport->netdev || !cleaned_count)
+		return false;
+
+	singleq_rx_desc = IECM_SINGLEQ_RX_BUF_DESC(rx_q, nta);
+	buf = &rx_q->rx_buf.buf[nta];
+	page_info = &buf->page_info[buf->page_indx];
+
+	do {
+		if (unlikely(!page_info->page)) {
+			if (!iecm_init_rx_buf_hw_alloc(rx_q, buf))
+				break;
+		}
+
+		/* Refresh the desc even if buffer_addrs didn't change
+		 * because each write-back erases this info.
+		 */
+		singleq_rx_desc->pkt_addr =
+			cpu_to_le64(page_info->dma + page_info->page_offset);
+		singleq_rx_desc->hdr_addr = 0;
+		singleq_rx_desc++;
+
+		buf++;
+		nta++;
+		if (unlikely(nta == rx_q->desc_count)) {
+			singleq_rx_desc = IECM_SINGLEQ_RX_BUF_DESC(rx_q, 0);
+			buf = rx_q->rx_buf.buf;
+			nta = 0;
+		}
+
+		page_info = &buf->page_info[buf->page_indx];
+
+		cleaned_count--;
+	} while (cleaned_count);
+
+	if (rx_q->next_to_alloc != nta) {
+		iecm_rx_buf_hw_update(rx_q, nta);
+		rx_q->next_to_alloc = nta;
+	}
+
+	return !!cleaned_count;
+}
+
+/**
+ * iecm_rx_reuse_page - Put recycled buffer back onto ring
+ * @rxq: Rx descriptor ring to store buffers on
+ * @old_buf: donor buffer to have page reused
+ */
+static void iecm_rx_reuse_page(struct iecm_queue *rxq,
+			       struct iecm_rx_buf *old_buf)
+{
+	u16 ntu = rxq->next_to_use;
+	struct iecm_rx_buf *new_buf;
+
+	new_buf = &rxq->rx_buf.buf[ntu];
+
+	/* Transfer page from old buffer to new buffer.  Move each member
+	 * individually to avoid possible store forwarding stalls and
+	 * unnecessary copy of skb.
+	 */
+	new_buf->page_info[new_buf->page_indx].dma =
+				old_buf->page_info[old_buf->page_indx].dma;
+	new_buf->page_info[new_buf->page_indx].page =
+				old_buf->page_info[old_buf->page_indx].page;
+	new_buf->page_info[new_buf->page_indx].page_offset =
+			old_buf->page_info[old_buf->page_indx].page_offset;
+	new_buf->page_info[new_buf->page_indx].pagecnt_bias =
+			old_buf->page_info[old_buf->page_indx].pagecnt_bias;
+}
+
+/**
+ * iecm_rx_singleq_recycle_buf - Clean up used buffer and either recycle or free
+ * @rxq: Rx descriptor queue to transact packets on
+ * @rx_buf: Rx buffer to pull data from
+ *
+ * This function will clean up the contents of the rx_buf. It will either
+ * recycle the buffer or unmap it and free the associated resources.
+ *
+ * Returns true if the buffer is reused, false if the buffer is freed.
+ */
+static bool iecm_rx_singleq_recycle_buf(struct iecm_queue *rxq,
+					struct iecm_rx_buf *rx_buf)
+{
+	struct iecm_page_info *page_info =
+			&rx_buf->page_info[rx_buf->page_indx];
+
+	bool recycled = false;
+
+	if (iecm_rx_can_reuse_page(rx_buf)) {
+		/* hand second half of page back to the queue */
+		iecm_rx_reuse_page(rxq, rx_buf);
+		recycled = true;
+	} else {
+		/* we are not reusing the buffer so unmap it */
+		dma_unmap_page_attrs(rxq->dev, page_info->dma, PAGE_SIZE,
+				     DMA_FROM_DEVICE, IECM_RX_DMA_ATTR);
+		__page_frag_cache_drain(page_info->page,
+					page_info->pagecnt_bias);
+	}
+
+	/* clear contents of buffer_info */
+	page_info->page = NULL;
+	rx_buf->skb = NULL;
+
+	return recycled;
+}
+
+/**
+ * iecm_rx_singleq_put_buf - Wrapper function to clean and recycle buffers
+ * @rxq: Rx descriptor queue to transact packets on
+ * @rx_buf: Rx buffer to pull data from
+ *
+ * This function will update the next_to_use/next_to_alloc if the current
+ * buffer is recycled.
+ */
+static void iecm_rx_singleq_put_buf(struct iecm_queue *rxq,
+				    struct iecm_rx_buf *rx_buf)
+{
+	u16 ntu = rxq->next_to_use;
+	bool recycled = false;
+
+	recycled = iecm_rx_singleq_recycle_buf(rxq, rx_buf);
+
+	/* update, and store next to alloc if the buffer was recycled */
+	if (recycled) {
+		ntu++;
+		rxq->next_to_use = (ntu < rxq->desc_count) ? ntu : 0;
+	}
+}
+
+/**
+ * iecm_rx_singleq_bump_ntc - Bump and wrap q->next_to_clean value
+ * @rxq: queue to bump
+ */
+void iecm_rx_singleq_bump_ntc(struct iecm_queue *rxq)
+{
+	u16 ntc = rxq->next_to_clean + 1;
+	/* fetch, update, and store next to clean */
+	if (ntc < rxq->desc_count)
+		rxq->next_to_clean = ntc;
+	else
+		rxq->next_to_clean = 0;
+}
+
+/**
+ * iecm_rx_singleq_get_buf_page - Fetch Rx buffer page and synchronize data
+ * @dev: device struct
+ * @rx_buf: Rx buf to fetch page for
+ * @size: size of buffer to add to skb
+ *
+ * This function will pull an Rx buffer page from the ring and synchronize it
+ * for use by the CPU.
+ */
+static struct sk_buff *
+iecm_rx_singleq_get_buf_page(struct device *dev, struct iecm_rx_buf *rx_buf,
+			     const unsigned int size)
+{
+	struct iecm_page_info *page_info;
+
+	page_info = &rx_buf->page_info[rx_buf->page_indx];
+	prefetch(page_info->page);
+
+	/* we are reusing so sync this buffer for CPU use */
+	dma_sync_single_range_for_cpu(dev, page_info->dma,
+				      page_info->page_offset, size,
+				      DMA_FROM_DEVICE);
+
+	/* We have pulled a buffer for use, so decrement pagecnt_bias */
+	page_info->pagecnt_bias--;
+
+	return rx_buf->skb;
+}
+
+/**
+ * iecm_rx_singleq_extract_base_fields - Extract fields from the Rx descriptor
+ * @rx_q: Rx descriptor queue
+ * @rx_desc: the descriptor to process
+ * @fields: storage for extracted values
+ *
+ * Decode the Rx descriptor and extract relevant information including the
+ * size, VLAN tag, and Rx packet type.
+ *
+ * This function only operates on the VIRTCHNL2_RXDID_1_32B_BASE_M legacy 32byte
+ * descriptor writeback format.
+ */
+static inline void
+iecm_rx_singleq_extract_base_fields(struct iecm_queue *rx_q,
+				    union virtchnl2_rx_desc *rx_desc,
+				    struct iecm_rx_extracted *fields)
+{
+	u64 qw2_status, qword;
+
+	qword = le64_to_cpu(rx_desc->base_wb.qword1.status_error_ptype_len);
+	qw2_status = le16_to_cpu(rx_desc->base_wb.qword2.ext_status);
+
+	fields->size = (qword & VIRTCHNL2_RX_BASE_DESC_QW1_LEN_PBUF_M) >>
+					VIRTCHNL2_RX_BASE_DESC_QW1_LEN_PBUF_S;
+	fields->rx_ptype = (qword & VIRTCHNL2_RX_BASE_DESC_QW1_PTYPE_M) >>
+					VIRTCHNL2_RX_BASE_DESC_QW1_PTYPE_S;
+
+	if (qword & BIT(VIRTCHNL2_RX_BASE_DESC_STATUS_L2TAG1P_S) &&
+	    test_bit(__IECM_Q_VLAN_TAG_LOC_L2TAG1, rx_q->flags))
+		fields->vlan_tag =
+			le16_to_cpu(rx_desc->base_wb.qword0.lo_dword.l2tag1);
+	if (qw2_status & BIT(VIRTCHNL2_RX_BASE_DESC_EXT_STATUS_L2TAG2P_S) &&
+	    test_bit(__IECM_Q_VLAN_TAG_LOC_L2TAG2, rx_q->flags))
+		fields->vlan_tag =
+			le16_to_cpu(rx_desc->base_wb.qword2.l2tag2_1);
+}
+
+/**
+ * iecm_rx_singleq_extract_flex_fields - Extract fields from the Rx descriptor
+ * @rx_q: Rx descriptor queue
+ * @rx_desc: the descriptor to process
+ * @fields: storage for extracted values
+ *
+ * Decode the Rx descriptor and extract relevant information including the
+ * size, VLAN tag, and Rx packet type.
+ *
+ * This function only operates on the VIRTCHNL2_RXDID_2_FLEX_SQ_NIC flexible
+ * descriptor writeback format.
+ */
+static inline void
+iecm_rx_singleq_extract_flex_fields(struct iecm_queue *rx_q,
+				    union virtchnl2_rx_desc *rx_desc,
+				    struct iecm_rx_extracted *fields)
+{
+	__le16 status0, status1;
+
+	fields->size = le16_to_cpu(rx_desc->flex_nic_wb.pkt_len) &
+					VIRTCHNL2_RX_FLEX_DESC_PKT_LEN_M;
+	fields->rx_ptype = le16_to_cpu(rx_desc->flex_nic_wb.ptype_flex_flags0) &
+					VIRTCHNL2_RX_FLEX_DESC_PTYPE_M;
+
+	status0 = rx_desc->flex_nic_wb.status_error0;
+	if (status0 & cpu_to_le16(BIT(VIRTCHNL2_RX_FLEX_DESC_STATUS0_L2TAG1P_S)) &&
+	    test_bit(__IECM_Q_VLAN_TAG_LOC_L2TAG1, rx_q->flags))
+		fields->vlan_tag = le16_to_cpu(rx_desc->flex_nic_wb.l2tag1);
+
+	status1 = rx_desc->flex_nic_wb.status_error1;
+	if (status1 & cpu_to_le16(BIT(VIRTCHNL2_RX_FLEX_DESC_STATUS1_L2TAG2P_S)) &&
+	    test_bit(__IECM_Q_VLAN_TAG_LOC_L2TAG2, rx_q->flags))
+		fields->vlan_tag = le16_to_cpu(rx_desc->flex_nic_wb.l2tag2_2nd);
+}
+
+/**
+ * iecm_rx_singleq_extract_fields - Extract fields from the Rx descriptor
+ * @rx_q: Rx descriptor queue
+ * @rx_desc: the descriptor to process
+ * @fields: storage for extracted values
+ *
+ */
+void
+iecm_rx_singleq_extract_fields(struct iecm_queue *rx_q,
+			       union virtchnl2_rx_desc *rx_desc,
+			       struct iecm_rx_extracted *fields)
+{
+	if (rx_q->rxdids == VIRTCHNL2_RXDID_1_32B_BASE_M)
+		iecm_rx_singleq_extract_base_fields(rx_q, rx_desc, fields);
+	else
+		iecm_rx_singleq_extract_flex_fields(rx_q, rx_desc, fields);
+}
+
+/**
+ * iecm_rx_singleq_clean - Reclaim resources after receive completes
+ * @rx_q: rx queue to clean
+ * @budget: Total limit on number of packets to process
+ *
+ * Returns true if there's any budget left (e.g. the clean is finished)
+ */
+static int iecm_rx_singleq_clean(struct iecm_queue *rx_q, int budget)
+{
+	unsigned int total_rx_bytes = 0, total_rx_pkts = 0;
+	u16 cleaned_count = 0;
+	bool failure = false;
+
+	/* Process Rx packets bounded by budget */
+	while (likely(total_rx_pkts < (unsigned int)budget)) {
+		struct iecm_rx_extracted fields = {};
+		union virtchnl2_rx_desc *rx_desc;
+		struct sk_buff *skb = NULL;
+		struct iecm_rx_buf *rx_buf;
+
+		/* get the Rx desc from Rx queue based on 'next_to_clean' */
+		rx_desc = IECM_RX_DESC(rx_q, rx_q->next_to_clean);
+
+		/* This memory barrier is needed to keep us from reading
+		 * any other fields out of the rx_desc
+		 */
+		dma_rmb();
+
+		/* status_error_ptype_len will always be zero for unused
+		 * descriptors because it's cleared in cleanup, and overlaps
+		 * with hdr_addr which is always zero because packet split
+		 * isn't used, if the hardware wrote DD then the length will be
+		 * non-zero
+		 */
+#define IECM_RXD_DD BIT(VIRTCHNL2_RX_BASE_DESC_STATUS_DD_S)
+		if (!iecm_rx_singleq_test_staterr(rx_desc,
+						  IECM_RXD_DD))
+			break;
+		iecm_rx_singleq_extract_fields(rx_q, rx_desc, &fields);
+
+		if (!fields.size)
+			break;
+
+		rx_buf = &rx_q->rx_buf.buf[rx_q->next_to_clean];
+		skb = iecm_rx_singleq_get_buf_page(rx_q->dev, rx_buf,
+						   fields.size);
+
+		if (skb)
+			iecm_rx_add_frag(rx_buf, skb, fields.size);
+		else
+			skb = iecm_rx_construct_skb(rx_q, rx_buf, fields.size);
+
+		/* exit if we failed to retrieve a buffer */
+		if (!skb) {
+			rx_buf->page_info[rx_buf->page_indx].pagecnt_bias++;
+			break;
+		}
+
+		iecm_rx_singleq_put_buf(rx_q, rx_buf);
+		iecm_rx_singleq_bump_ntc(rx_q);
+
+		cleaned_count++;
+
+		/* skip if it is non EOP desc */
+		if (iecm_rx_singleq_is_non_eop(rx_q, rx_desc,
+					       skb))
+			continue;
+
+#define IECM_RXD_ERR_S BIT(VIRTCHNL2_RX_BASE_DESC_QW1_ERROR_S)
+		if (unlikely(iecm_rx_singleq_test_staterr(rx_desc,
+							  IECM_RXD_ERR_S))) {
+			dev_kfree_skb_any(skb);
+			skb = NULL;
+			continue;
+		}
+
+		/* pad skb if needed (to make valid ethernet frame) */
+		if (eth_skb_pad(skb)) {
+			skb = NULL;
+			continue;
+		}
+
+		/* probably a little skewed due to removing CRC */
+		total_rx_bytes += skb->len;
+
+		/* protocol */
+		iecm_rx_singleq_process_skb_fields(rx_q, skb,
+						   rx_desc, fields.rx_ptype);
+
+		/* send completed skb up the stack */
+		iecm_rx_skb(rx_q, skb, fields.vlan_tag);
+
+		/* update budget accounting */
+		total_rx_pkts++;
+	}
+	if (cleaned_count)
+		failure = iecm_rx_singleq_buf_hw_alloc_all(rx_q, cleaned_count);
+
+	u64_stats_update_begin(&rx_q->stats_sync);
+	rx_q->q_stats.rx.packets += total_rx_pkts;
+	rx_q->q_stats.rx.bytes += total_rx_bytes;
+	u64_stats_update_end(&rx_q->stats_sync);
+
+	/* guarantee a trip back through this routine if there was a failure */
+	return failure ? budget : (int)total_rx_pkts;
+}
+
+/**
+ * iecm_rx_singleq_clean_all - Clean all Rx queues
+ * @q_vec: queue vector
+ * @budget: Used to determine if we are in netpoll
+ * @cleaned: returns number of packets cleaned
+ *
+ * Returns false if clean is not complete else returns true
+ */
+static bool
+iecm_rx_singleq_clean_all(struct iecm_q_vector *q_vec, int budget,
+			  int *cleaned)
+{
+	bool clean_complete = true;
+	int budget_per_q, i;
+
+	budget_per_q = max(budget / q_vec->num_rxq, 1);
+	for (i = 0; i < q_vec->num_rxq; i++) {
+		struct iecm_queue *rxq = q_vec->rx[i];
+		int pkts_cleaned_per_q;
+
+		pkts_cleaned_per_q = iecm_rx_singleq_clean(rxq, budget_per_q);
+
+		/* if we clean as many as budgeted, we must not be done */
+		if (pkts_cleaned_per_q >= budget_per_q)
+			clean_complete = false;
+		*cleaned += pkts_cleaned_per_q;
+	}
+
+	return clean_complete;
 }
 
 /**
@@ -24,6 +1199,31 @@ bool iecm_rx_singleq_buf_hw_alloc_all(struct iecm_queue *rx_q,
  */
 int iecm_vport_singleq_napi_poll(struct napi_struct *napi, int budget)
 {
-	/* stub */
-	return 0;
+	struct iecm_q_vector *q_vector =
+				container_of(napi, struct iecm_q_vector, napi);
+	bool clean_complete;
+	int work_done = 0;
+
+	clean_complete = iecm_tx_singleq_clean_all(q_vector, budget);
+
+	/* Handle case where we are called by netpoll with a budget of 0 */
+	if (budget <= 0)
+		return budget;
+
+	/* We attempt to distribute budget to each Rx queue fairly, but don't
+	 * allow the budget to go below 1 because that would exit polling early.
+	 */
+	clean_complete |= iecm_rx_singleq_clean_all(q_vector, budget,
+						    &work_done);
+
+	/* If work not completed, return budget and polling will return */
+	if (!clean_complete)
+		return budget;
+
+	/* Exit the polling mode, but don't re-enable interrupts if stack might
+	 * poll us due to busy-polling
+	 */
+	if (likely(napi_complete_done(napi, work_done)))
+		iecm_vport_intr_update_itr_ena_irq(q_vector);
+	return min_t(int, work_done, budget - 1);
 }
diff --git a/drivers/net/ethernet/intel/include/iecm.h b/drivers/net/ethernet/intel/include/iecm.h
index 3cf2a2f0cb0f..97c9935b832d 100644
--- a/drivers/net/ethernet/intel/include/iecm.h
+++ b/drivers/net/ethernet/intel/include/iecm.h
@@ -8,6 +8,7 @@
 #include <net/pkt_cls.h>
 #include <linux/aer.h>
 #include <linux/pci.h>
+#include <linux/sctp.h>
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/ethtool.h>
diff --git a/drivers/net/ethernet/intel/include/iecm_txrx.h b/drivers/net/ethernet/intel/include/iecm_txrx.h
index 086b0efc989a..4dba4f52be98 100644
--- a/drivers/net/ethernet/intel/include/iecm_txrx.h
+++ b/drivers/net/ethernet/intel/include/iecm_txrx.h
@@ -624,6 +624,26 @@ struct iecm_txq_group {
 
 struct iecm_adapter;
 
+/**
+ * iecm_tx_singleq_build_ctob - populate command tag offset and size
+ * @td_cmd: Command to be filled in desc
+ * @td_offset: Offset to be filled in desc
+ * @size: Size of the buffer
+ * @td_tag: vlan tag to be filled
+ *
+ * Returns the 64 bit value populated with the input parameters
+ */
+static inline __le64
+iecm_tx_singleq_build_ctob(u64 td_cmd, u64 td_offset, unsigned int size,
+			   u64 td_tag)
+{
+	return cpu_to_le64(IECM_TX_DESC_DTYPE_DATA |
+			   (td_cmd    << IECM_TXD_QW1_CMD_S) |
+			   (td_offset << IECM_TXD_QW1_OFFSET_S) |
+			   ((u64)size << IECM_TXD_QW1_TX_BUF_SZ_S) |
+			   (td_tag    << IECM_TXD_QW1_L2TAG1_S));
+}
+
 void iecm_tx_splitq_build_ctb(union iecm_tx_flex_desc *desc,
 			      struct iecm_tx_splitq_params *parms,
 			      u16 td_cmd, u16 size);
@@ -673,8 +693,19 @@ void iecm_rx_splitq_put_bufs(struct iecm_queue *rx_bufq,
 			     struct iecm_rx_buf *hdr_buf,
 			     struct iecm_rx_buf *rx_buf);
 bool iecm_rx_splitq_test_staterr(u8 stat_err_field, const u8 stat_err_bits);
+bool iecm_rx_singleq_test_staterr(union virtchnl2_rx_desc *rx_desc,
+				  const u64 stat_err_bits);
 int iecm_rx_process_skb_fields(struct iecm_queue *rxq, struct sk_buff *skb,
 			       struct virtchnl2_rx_flex_desc_adv_nic_3 *rx_desc);
+void iecm_rx_singleq_process_skb_fields(struct iecm_queue *rx_q, struct sk_buff *skb,
+					union virtchnl2_rx_desc *rx_desc, u16 ptype);
+void iecm_rx_singleq_extract_fields(struct iecm_queue *rx_q,
+				    union virtchnl2_rx_desc *rx_desc,
+				    struct iecm_rx_extracted *fields);
+void iecm_rx_singleq_bump_ntc(struct iecm_queue *q);
+bool iecm_rx_singleq_is_non_eop(struct iecm_queue *rxq,
+				union virtchnl2_rx_desc *rx_desc,
+				struct sk_buff *skb);
 bool iecm_rx_splitq_extract_vlan_tag(struct virtchnl2_rx_flex_desc_adv_nic_3 *desc,
 				     struct iecm_queue *rxq, u16 *vlan_tag);
 void iecm_rx_bump_ntc(struct iecm_queue *q);
-- 
2.33.0



^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 15/19] iecm: implement ethtool callbacks
  2022-01-28  0:09 [Intel-wired-lan] [PATCH net-next 00/19] Add iecm and idpf Alan Brady
                   ` (13 preceding siblings ...)
  2022-01-28  0:10 ` [Intel-wired-lan] [PATCH net-next 14/19] iecm: implement singleq napi_poll Alan Brady
@ 2022-01-28  0:10 ` Alan Brady
  2022-01-28 18:13   ` Alexander Lobakin
  2022-01-28  0:10 ` [Intel-wired-lan] [PATCH net-next 16/19] iecm: implement flow director Alan Brady
                   ` (4 subsequent siblings)
  19 siblings, 1 reply; 89+ messages in thread
From: Alan Brady @ 2022-01-28  0:10 UTC (permalink / raw)
  To: intel-wired-lan

This does everything needed to handle ethtool ops minus a few stubs for
cloud filters and other advanced features which will be added in later in
this series.

Signed-off-by: Phani Burra <phani.r.burra@intel.com>
Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
Signed-off-by: Alan Brady <alan.brady@intel.com>
---
 drivers/net/ethernet/intel/iecm/Makefile      |    1 +
 .../net/ethernet/intel/iecm/iecm_ethtool.c    | 1325 +++++++++++++++++
 drivers/net/ethernet/intel/iecm/iecm_lib.c    |   11 +-
 drivers/net/ethernet/intel/include/iecm.h     |    1 +
 4 files changed, 1337 insertions(+), 1 deletion(-)
 create mode 100644 drivers/net/ethernet/intel/iecm/iecm_ethtool.c

diff --git a/drivers/net/ethernet/intel/iecm/Makefile b/drivers/net/ethernet/intel/iecm/Makefile
index 205d6f2b436a..fe2ed403d35c 100644
--- a/drivers/net/ethernet/intel/iecm/Makefile
+++ b/drivers/net/ethernet/intel/iecm/Makefile
@@ -15,6 +15,7 @@ iecm-y := \
 	iecm_virtchnl.o \
 	iecm_txrx.o \
 	iecm_singleq_txrx.o \
+	iecm_ethtool.o \
 	iecm_controlq.o \
 	iecm_controlq_setup.o \
 	iecm_main.o
diff --git a/drivers/net/ethernet/intel/iecm/iecm_ethtool.c b/drivers/net/ethernet/intel/iecm/iecm_ethtool.c
new file mode 100644
index 000000000000..32d905fb1bb6
--- /dev/null
+++ b/drivers/net/ethernet/intel/iecm/iecm_ethtool.c
@@ -0,0 +1,1325 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (C) 2019 Intel Corporation */
+
+#include "iecm.h"
+
+/**
+ * iecm_get_rxnfc - command to get RX flow classification rules
+ * @netdev: network interface device structure
+ * @cmd: ethtool rxnfc command
+ * @rule_locs: pointer to store rule locations
+ *
+ * Returns Success if the command is supported.
+ */
+static int iecm_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd,
+			  u32 __always_unused *rule_locs)
+{
+	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
+	int ret = -EOPNOTSUPP;
+
+	switch (cmd->cmd) {
+	case ETHTOOL_GRXRINGS:
+		cmd->data = vport->num_rxq;
+		ret = 0;
+		break;
+	case ETHTOOL_GRXCLSRLCNT:
+		/* stub */
+		ret = 0;
+		break;
+	case ETHTOOL_GRXCLSRULE:
+		/* stub */
+		break;
+	case ETHTOOL_GRXCLSRLALL:
+		/* stub */
+		break;
+	case ETHTOOL_GRXFH:
+		/* stub */
+		break;
+	default:
+		break;
+	}
+
+	return ret;
+}
+
+/**
+ * iecm_set_rxnfc - command to set Rx flow rules.
+ * @netdev: network interface device structure
+ * @cmd: ethtool rxnfc command
+ *
+ * Returns 0 for success and negative values for errors
+ */
+static int iecm_set_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd)
+{
+	int ret = -EOPNOTSUPP;
+
+	switch (cmd->cmd) {
+	case ETHTOOL_SRXCLSRLINS:
+		/* stub */
+		break;
+	case ETHTOOL_SRXCLSRLDEL:
+		/* stub */
+		break;
+	case ETHTOOL_SRXFH:
+		/* stub */
+		break;
+	default:
+		break;
+	}
+
+	return ret;
+}
+
+/**
+ * iecm_get_rxfh_key_size - get the RSS hash key size
+ * @netdev: network interface device structure
+ *
+ * Returns the table size.
+ */
+static u32 iecm_get_rxfh_key_size(struct net_device *netdev)
+{
+	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
+
+	if (!iecm_is_cap_ena_all(vport->adapter, IECM_RSS_CAPS, IECM_CAP_RSS)) {
+		dev_info(&vport->adapter->pdev->dev, "RSS is not supported on this device\n");
+		return 0;
+	}
+
+	return vport->adapter->rss_data.rss_key_size;
+}
+
+/**
+ * iecm_get_rxfh_indir_size - get the rx flow hash indirection table size
+ * @netdev: network interface device structure
+ *
+ * Returns the table size.
+ */
+static u32 iecm_get_rxfh_indir_size(struct net_device *netdev)
+{
+	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
+
+	if (!iecm_is_cap_ena_all(vport->adapter, IECM_RSS_CAPS, IECM_CAP_RSS)) {
+		dev_info(&vport->adapter->pdev->dev, "RSS is not supported on this device\n");
+		return 0;
+	}
+
+	return vport->adapter->rss_data.rss_lut_size;
+}
+
+/**
+ * iecm_get_rxfh - get the rx flow hash indirection table
+ * @netdev: network interface device structure
+ * @indir: indirection table
+ * @key: hash key
+ * @hfunc: hash function in use
+ *
+ * Reads the indirection table directly from the hardware. Always returns 0.
+ */
+static int iecm_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key,
+			 u8 *hfunc)
+{
+	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
+	struct iecm_adapter *adapter;
+	u16 i;
+
+	adapter = vport->adapter;
+
+	if (!iecm_is_cap_ena_all(adapter, IECM_RSS_CAPS, IECM_CAP_RSS)) {
+		dev_info(&vport->adapter->pdev->dev, "RSS is not supported on this device\n");
+		return 0;
+	}
+
+	if (adapter->state != __IECM_UP)
+		return 0;
+
+	if (hfunc)
+		*hfunc = ETH_RSS_HASH_TOP;
+
+	if (key)
+		memcpy(key, adapter->rss_data.rss_key,
+		       adapter->rss_data.rss_key_size);
+
+	if (indir)
+		/* Each 32 bits pointed by 'indir' is stored with a lut entry */
+		for (i = 0; i < adapter->rss_data.rss_lut_size; i++)
+			indir[i] = adapter->rss_data.rss_lut[i];
+
+	return 0;
+}
+
+/**
+ * iecm_set_rxfh - set the rx flow hash indirection table
+ * @netdev: network interface device structure
+ * @indir: indirection table
+ * @key: hash key
+ * @hfunc: hash function to use
+ *
+ * Returns -EINVAL if the table specifies an invalid queue id, otherwise
+ * returns 0 after programming the table.
+ */
+static int iecm_set_rxfh(struct net_device *netdev, const u32 *indir,
+			 const u8 *key, const u8 hfunc)
+{
+	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
+	struct iecm_adapter *adapter;
+	u16 lut;
+
+	adapter = vport->adapter;
+
+	if (!iecm_is_cap_ena_all(adapter, IECM_RSS_CAPS, IECM_CAP_RSS)) {
+		dev_info(&adapter->pdev->dev, "RSS is not supported on this device\n");
+		return 0;
+	}
+	if (adapter->state != __IECM_UP)
+		return 0;
+	if (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP)
+		return -EOPNOTSUPP;
+
+	if (key)
+		memcpy(adapter->rss_data.rss_key, key,
+		       adapter->rss_data.rss_key_size);
+
+	if (indir) {
+		for (lut = 0; lut < adapter->rss_data.rss_lut_size; lut++)
+			adapter->rss_data.rss_lut[lut] = indir[lut];
+	}
+
+	return iecm_config_rss(vport);
+}
+
+/**
+ * iecm_get_channels: get the number of channels supported by the device
+ * @netdev: network interface device structure
+ * @ch: channel information structure
+ *
+ * Report maximum of TX and RX. Report one extra channel to match our MailBox
+ * Queue.
+ */
+static void iecm_get_channels(struct net_device *netdev,
+			      struct ethtool_channels *ch)
+{
+	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
+	unsigned int combined;
+	int num_txq, num_rxq;
+
+	num_txq = vport->adapter->config_data.num_req_tx_qs;
+	num_rxq = vport->adapter->config_data.num_req_rx_qs;
+
+	combined = min(num_txq, num_rxq);
+
+	/* Report maximum channels */
+	ch->max_combined = vport->adapter->max_queue_limit;
+
+	/* For now we've only enabled combined queues but will be enabling
+	 * asymmetric queues after splitq model is fleshed out more.
+	 */
+	ch->max_rx = 0;
+	ch->max_tx = 0;
+
+	ch->max_other = IECM_MAX_NONQ;
+	ch->other_count = IECM_MAX_NONQ;
+
+	ch->combined_count = combined;
+	ch->rx_count = num_rxq - combined;
+	ch->tx_count = num_txq - combined;
+}
+
+/**
+ * iecm_set_channels: set the new channel count
+ * @netdev: network interface device structure
+ * @ch: channel information structure
+ *
+ * Negotiate a new number of channels with CP. Returns 0 on success, negative
+ * on failure.
+ */
+static int iecm_set_channels(struct net_device *netdev,
+			     struct ethtool_channels *ch)
+{
+	unsigned int num_req_tx_q = ch->combined_count + ch->tx_count;
+	unsigned int num_req_rx_q = ch->combined_count + ch->rx_count;
+	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
+	struct iecm_adapter *adapter = vport->adapter;
+	int num_txq, num_rxq, err;
+
+	if (num_req_tx_q == 0 || num_req_rx_q == 0)
+		return -EINVAL;
+
+	num_txq = vport->adapter->config_data.num_req_tx_qs;
+	num_rxq = vport->adapter->config_data.num_req_rx_qs;
+
+	if (num_req_tx_q == num_txq && num_req_rx_q == num_rxq)
+		return 0;
+
+	vport->adapter->config_data.num_req_tx_qs = num_req_tx_q;
+	vport->adapter->config_data.num_req_rx_qs = num_req_rx_q;
+
+	if (adapter->virt_ver_maj < VIRTCHNL_VERSION_MAJOR_2) {
+		err = adapter->dev_ops.vc_ops.add_queues(vport,
+							 num_req_tx_q, 0,
+							 num_req_rx_q, 0);
+	} else {
+		err = iecm_initiate_soft_reset(vport, __IECM_SR_Q_CHANGE);
+	}
+
+	if (err) {
+		/* roll back queue change */
+		vport->adapter->config_data.num_req_tx_qs = num_txq;
+		vport->adapter->config_data.num_req_rx_qs = num_rxq;
+	}
+
+	return err;
+}
+
+/**
+ * iecm_get_ringparam - Get ring parameters
+ * @netdev: network interface device structure
+ * @ring: ethtool ringparam structure
+ * @kring: unused
+ * @ext_ack: unused
+ *
+ * Returns current ring parameters. TX and RX rings are reported separately,
+ * but the number of rings is not reported.
+ */
+static void iecm_get_ringparam(struct net_device *netdev,
+			       struct ethtool_ringparam *ring,
+			       __always_unused struct kernel_ethtool_ringparam *kring,
+			       __always_unused struct netlink_ext_ack *ext_ack)
+{
+	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
+
+	ring->rx_max_pending = IECM_MAX_RXQ_DESC;
+	ring->tx_max_pending = IECM_MAX_TXQ_DESC;
+	ring->rx_pending = vport->rxq_desc_count;
+	ring->tx_pending = vport->txq_desc_count;
+}
+
+/**
+ * iecm_set_ringparam - Set ring parameters
+ * @netdev: network interface device structure
+ * @ring: ethtool ringparam structure
+ * @kring: unused
+ * @ext_ack: unused
+ *
+ * Sets ring parameters. TX and RX rings are controlled separately, but the
+ * number of rings is not specified, so all rings get the same settings.
+ */
+static int iecm_set_ringparam(struct net_device *netdev,
+			      struct ethtool_ringparam *ring,
+			      __always_unused struct kernel_ethtool_ringparam *kring,
+			      __always_unused struct netlink_ext_ack *ext_ack)
+{
+	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
+	u32 new_rx_count, new_tx_count;
+	int i;
+
+	if (iecm_is_queue_model_split(vport->rxq_model))
+		/* When in splitq mode, any one RXQ needs to contain enough
+		 * descriptors to service the 2 buffer queues associated with
+		 * it. Since each buffer queue must be a multiple of 32, the
+		 * RXQ then needs to be a multiple of 64 to be divided into
+		 * multiples of 32 for each buffer queue
+		 */
+		new_rx_count = ALIGN(ring->rx_pending,
+				     IECM_REQ_SPLITQ_RXQ_DESC_MULTIPLE);
+	else
+		new_rx_count = ALIGN(ring->rx_pending, IECM_REQ_DESC_MULTIPLE);
+
+	new_tx_count = ALIGN(ring->tx_pending, IECM_REQ_DESC_MULTIPLE);
+
+	/* if nothing to do return success */
+	if (new_tx_count == vport->txq_desc_count &&
+	    new_rx_count == vport->rxq_desc_count)
+		return 0;
+
+	vport->adapter->config_data.num_req_txq_desc = new_tx_count;
+	vport->adapter->config_data.num_req_rxq_desc = new_rx_count;
+
+	/* Since we adjusted the RX completion queue count, the RX buffer queue
+	 * descriptor count needs to be adjusted as well
+	 */
+	for (i = 0; i < vport->num_bufqs_per_qgrp; i++)
+		vport->bufq_desc_count[i] =
+			IECM_RX_BUFQ_DESC_COUNT(new_rx_count,
+						vport->num_bufqs_per_qgrp);
+
+	return iecm_initiate_soft_reset(vport, __IECM_SR_Q_DESC_CHANGE);
+}
+
+/**
+ * struct iecm_stats - definition for an ethtool statistic
+ * @stat_string: statistic name to display in ethtool -S output
+ * @sizeof_stat: the sizeof() the stat, must be no greater than sizeof(u64)
+ * @stat_offset: offsetof() the stat from a base pointer
+ *
+ * This structure defines a statistic to be added to the ethtool stats buffer.
+ * It defines a statistic as offset from a common base pointer. Stats should
+ * be defined in constant arrays using the IECM_STAT macro, with every element
+ * of the array using the same _type for calculating the sizeof_stat and
+ * stat_offset.
+ *
+ * The @sizeof_stat is expected to be sizeof(u8), sizeof(u16), sizeof(u32) or
+ * sizeof(u64). Other sizes are not expected and will produce a WARN_ONCE from
+ * the iecm_add_ethtool_stat() helper function.
+ *
+ * The @stat_string is interpreted as a format string, allowing formatted
+ * values to be inserted while looping over multiple structures for a given
+ * statistics array. Thus, every statistic string in an array should have the
+ * same type and number of format specifiers, to be formatted by variadic
+ * arguments to the iecm_add_stat_string() helper function.
+ */
+struct iecm_stats {
+	char stat_string[ETH_GSTRING_LEN];
+	int sizeof_stat;
+	int stat_offset;
+};
+
+/* Helper macro to define an iecm_stat structure with proper size and type.
+ * Use this when defining constant statistics arrays. Note that @_type expects
+ * only a type name and is used multiple times.
+ */
+#define IECM_STAT(_type, _name, _stat) { \
+	.stat_string = _name, \
+	.sizeof_stat = sizeof_field(_type, _stat), \
+	.stat_offset = offsetof(_type, _stat) \
+}
+
+/* Helper macro for defining some statistics related to queues */
+#define IECM_QUEUE_STAT(_name, _stat) \
+	IECM_STAT(struct iecm_queue, _name, _stat)
+
+/* Stats associated with a Tx queue */
+static const struct iecm_stats iecm_gstrings_tx_queue_stats[] = {
+	IECM_QUEUE_STAT("packets", q_stats.tx.packets),
+	IECM_QUEUE_STAT("bytes", q_stats.tx.bytes),
+	IECM_QUEUE_STAT("lso_pkts", q_stats.tx.lso_pkts),
+};
+
+/* Stats associated with an Rx queue */
+static const struct iecm_stats iecm_gstrings_rx_queue_stats[] = {
+	IECM_QUEUE_STAT("packets", q_stats.rx.packets),
+	IECM_QUEUE_STAT("bytes", q_stats.rx.bytes),
+	IECM_QUEUE_STAT("rsc_pkts", q_stats.rx.rsc_pkts),
+};
+
+#define IECM_TX_QUEUE_STATS_LEN		ARRAY_SIZE(iecm_gstrings_tx_queue_stats)
+#define IECM_RX_QUEUE_STATS_LEN		ARRAY_SIZE(iecm_gstrings_rx_queue_stats)
+
+#define IECM_PORT_STAT(_name, _stat) \
+	IECM_STAT(struct iecm_vport,  _name, _stat)
+
+static const struct iecm_stats iecm_gstrings_port_stats[] = {
+	IECM_PORT_STAT("port-rx-csum_errors", port_stats.rx_hw_csum_err),
+	IECM_PORT_STAT("port-rx-hsplit", port_stats.rx_hsplit),
+	IECM_PORT_STAT("port-rx-hsplit_hbo", port_stats.rx_hsplit_hbo),
+	IECM_PORT_STAT("tx-linearized_pkts", port_stats.tx_linearize),
+	IECM_PORT_STAT("rx-bad_descs", port_stats.rx_bad_descs),
+	IECM_PORT_STAT("port-rx_bytes", port_stats.eth_stats.rx_bytes),
+	IECM_PORT_STAT("port-rx-unicast_pkts", port_stats.eth_stats.rx_unicast),
+	IECM_PORT_STAT("port-rx-multicast_pkts", port_stats.eth_stats.rx_multicast),
+	IECM_PORT_STAT("port-rx-broadcast_pkts", port_stats.eth_stats.rx_broadcast),
+	IECM_PORT_STAT("port-rx-unknown_protocol", port_stats.eth_stats.rx_unknown_protocol),
+	IECM_PORT_STAT("port-tx_bytes", port_stats.eth_stats.tx_bytes),
+	IECM_PORT_STAT("port-tx-unicast_pkts", port_stats.eth_stats.tx_unicast),
+	IECM_PORT_STAT("port-tx-multicast_pkts", port_stats.eth_stats.tx_multicast),
+	IECM_PORT_STAT("port-tx-broadcast_pkts", port_stats.eth_stats.tx_broadcast),
+	IECM_PORT_STAT("port-tx_errors", port_stats.eth_stats.tx_errors),
+};
+
+#define IECM_PORT_STATS_LEN ARRAY_SIZE(iecm_gstrings_port_stats)
+
+struct iecm_priv_flags {
+	char flag_string[ETH_GSTRING_LEN];
+	bool read_only;
+	u32 bitno;
+};
+
+#define IECM_PRIV_FLAG(_name, _bitno, _read_only) { \
+	.read_only = _read_only, \
+	.flag_string = _name, \
+	.bitno = _bitno, \
+}
+
+static const struct iecm_priv_flags iecm_gstrings_priv_flags[] = {
+	IECM_PRIV_FLAG("header-split", __IECM_PRIV_FLAGS_HDR_SPLIT, 0),
+};
+
+#define IECM_PRIV_FLAGS_STR_LEN ARRAY_SIZE(iecm_gstrings_priv_flags)
+
+/**
+ * __iecm_add_qstat_strings - copy stat strings into ethtool buffer
+ * @p: ethtool supplied buffer
+ * @stats: stat definitions array
+ * @size: size of the stats array
+ * @type: stat type
+ * @idx: stat index
+ *
+ * Format and copy the strings described by stats into the buffer pointed at
+ * by p.
+ */
+static void __iecm_add_qstat_strings(u8 **p, const struct iecm_stats stats[],
+				     const unsigned int size, const char *type,
+				     unsigned int idx)
+{
+	unsigned int i;
+
+	for (i = 0; i < size; i++) {
+		snprintf((char *)*p, ETH_GSTRING_LEN,
+			 "%2s-%u.%.17s", type, idx, stats[i].stat_string);
+		*p += ETH_GSTRING_LEN;
+	}
+}
+
+/**
+ * iecm_add_qstat_strings - Copy queue stat strings into ethtool buffer
+ * @p: ethtool supplied buffer
+ * @stats: stat definitions array
+ * @type: stat type
+ * @idx: stat idx
+ *
+ * Format and copy the strings described by the const static stats value into
+ * the buffer pointed@by p.
+ *
+ * The parameter @stats is evaluated twice, so parameters with side effects
+ * should be avoided. Additionally, stats must be an array such that
+ * ARRAY_SIZE can be called on it.
+ */
+#define iecm_add_qstat_strings(p, stats, type, idx) \
+	__iecm_add_qstat_strings(p, stats, ARRAY_SIZE(stats), type, idx)
+
+/**
+ * iecm_add_port_stat_strings - Copy port stat strings into ethtool buffer
+ * @p: ethtool buffer
+ * @stats: struct to copy from
+ */
+static void iecm_add_port_stat_strings(u8 **p, const struct iecm_stats stats[])
+{
+	const unsigned int size = IECM_PORT_STATS_LEN;
+	unsigned int i;
+
+	for (i = 0; i < size; i++) {
+		snprintf((char *)*p, ETH_GSTRING_LEN, "%.32s",
+			 stats[i].stat_string);
+		*p += ETH_GSTRING_LEN;
+	}
+}
+
+/**
+ * iecm_get_stat_strings - Get stat strings
+ * @netdev: network interface device structure
+ * @data: buffer for string data
+ *
+ * Builds the statistics string table
+ */
+static void iecm_get_stat_strings(struct net_device *netdev, u8 *data)
+{
+	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
+	unsigned int i;
+	u16 max_q;
+
+	iecm_add_port_stat_strings(&data, iecm_gstrings_port_stats);
+
+	/* It's critical that we always report a constant number of strings and
+	 * that the strings are reported in the same order regardless of how
+	 * many queues are actually in use.
+	 */
+	max_q = vport->adapter->max_queue_limit;
+
+	for (i = 0; i < max_q; i++)
+		iecm_add_qstat_strings(&data, iecm_gstrings_tx_queue_stats,
+				       "tx", i);
+	for (i = 0; i < max_q; i++)
+		iecm_add_qstat_strings(&data, iecm_gstrings_rx_queue_stats,
+				       "rx", i);
+}
+
+/**
+ * iecm_get_priv_flag_strings - Get private flag strings
+ * @netdev: network interface device structure
+ * @data: buffer for string data
+ *
+ * Builds the private flags string table
+ */
+static void iecm_get_priv_flag_strings(struct net_device *netdev, u8 *data)
+{
+	unsigned int i;
+
+	for (i = 0; i < IECM_PRIV_FLAGS_STR_LEN; i++) {
+		snprintf((char *)data, ETH_GSTRING_LEN, "%s",
+			 iecm_gstrings_priv_flags[i].flag_string);
+		data += ETH_GSTRING_LEN;
+	}
+}
+
+/**
+ * iecm_get_priv_flags - report device private flags
+ * @dev: network interface device structure
+ *
+ * The get string set count and the string set should be matched for each
+ * flag returned.  Add new strings for each flag to the iecm_gstrings_priv_flags
+ * array.
+ *
+ * Returns a u32 bitmap of flags.
+ **/
+static u32 iecm_get_priv_flags(struct net_device *dev)
+{
+	struct iecm_netdev_priv *np = netdev_priv(dev);
+	struct iecm_user_config_data *user_data;
+	struct iecm_vport *vport = np->vport;
+	u32 i, ret_flags = 0;
+
+	user_data = &vport->adapter->config_data;
+
+	for (i = 0; i < IECM_PRIV_FLAGS_STR_LEN; i++) {
+		const struct iecm_priv_flags *priv_flag;
+
+		priv_flag = &iecm_gstrings_priv_flags[i];
+
+		if (test_bit(priv_flag->bitno, user_data->user_flags))
+			ret_flags |= BIT(i);
+	}
+
+	return ret_flags;
+}
+
+/**
+ * iecm_set_priv_flags - set private flags
+ * @dev: network interface device structure
+ * @flags: bit flags to be set
+ **/
+static int iecm_set_priv_flags(struct net_device *dev, u32 flags)
+{
+	struct iecm_netdev_priv *np = netdev_priv(dev);
+	DECLARE_BITMAP(change_flags, __IECM_USER_FLAGS_NBITS);
+	DECLARE_BITMAP(orig_flags, __IECM_USER_FLAGS_NBITS);
+	struct iecm_user_config_data *user_data;
+	struct iecm_vport *vport = np->vport;
+	bool is_reset_needed;
+	int err = 0;
+	u32 i;
+
+	if (flags > BIT(IECM_PRIV_FLAGS_STR_LEN))
+		return -EINVAL;
+
+	user_data = &vport->adapter->config_data;
+
+	bitmap_copy(orig_flags, user_data->user_flags, __IECM_USER_FLAGS_NBITS);
+
+	for (i = 0; i < IECM_PRIV_FLAGS_STR_LEN; i++) {
+		const struct iecm_priv_flags *priv_flag;
+
+		priv_flag = &iecm_gstrings_priv_flags[i];
+
+		if (flags & BIT(i)) {
+			/* If this is a read-only flag, it can't be changed */
+			if (priv_flag->read_only)
+				return -EOPNOTSUPP;
+
+			set_bit(priv_flag->bitno, user_data->user_flags);
+		} else {
+			clear_bit(priv_flag->bitno, user_data->user_flags);
+		}
+	}
+
+	bitmap_xor(change_flags, user_data->user_flags,
+		   orig_flags, __IECM_USER_FLAGS_NBITS);
+
+	is_reset_needed =
+		!!(test_bit(__IECM_PRIV_FLAGS_HDR_SPLIT, change_flags));
+
+	/* Issue reset to cause things to take effect, as additional bits
+	 * are added we will need to create a mask of bits requiring reset
+	 */
+	if (is_reset_needed)
+		err = iecm_initiate_soft_reset(vport, __IECM_SR_HSPLIT_CHANGE);
+
+	return err;
+}
+
+/**
+ * iecm_get_strings - Get string set
+ * @netdev: network interface device structure
+ * @sset: id of string set
+ * @data: buffer for string data
+ *
+ * Builds string tables for various string sets
+ */
+static void iecm_get_strings(struct net_device *netdev, u32 sset, u8 *data)
+{
+	switch (sset) {
+	case ETH_SS_STATS:
+		iecm_get_stat_strings(netdev, data);
+		break;
+	case ETH_SS_PRIV_FLAGS:
+		iecm_get_priv_flag_strings(netdev, data);
+		break;
+	default:
+		break;
+	}
+}
+
+/**
+ * iecm_get_sset_count - Get length of string set
+ * @netdev: network interface device structure
+ * @sset: id of string set
+ *
+ * Reports size of various string tables.
+ */
+static
+int iecm_get_sset_count(struct net_device *netdev, int sset)
+{
+	if (sset == ETH_SS_STATS) {
+		struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
+		u16 max_q;
+
+		/* This size reported back here *must* be constant throughout
+		 * the lifecycle of the netdevice, i.e. we must report the
+		 * maximum length even for queues that don't technically exist.
+		 * This is due to the fact that this userspace API uses three
+		 * separate ioctl calls to get stats data but has no way to
+		 * communicate back to userspace when that size has changed,
+		 * which can typically happen as a result of changing number of
+		 * queues. If the number/order of stats change in the middle of
+		 * this call chain it will lead to userspace crashing/accessing
+		 * bad data through buffer under/overflow.
+		 */
+		max_q = vport->adapter->max_queue_limit;
+
+		return IECM_PORT_STATS_LEN +
+		       (IECM_TX_QUEUE_STATS_LEN * max_q) +
+			(IECM_RX_QUEUE_STATS_LEN * max_q);
+	} else if (sset == ETH_SS_PRIV_FLAGS) {
+		return IECM_PRIV_FLAGS_STR_LEN;
+	} else {
+		return -EINVAL;
+	}
+}
+
+/**
+ * iecm_add_one_ethtool_stat - copy the stat into the supplied buffer
+ * @data: location to store the stat value
+ * @pstat: old stat pointer to copy from
+ * @stat: the stat definition
+ *
+ * Copies the stat data defined by the pointer and stat structure pair into
+ * the memory supplied as data. Used to implement iecm_add_ethtool_stats and
+ * iecm_add_queue_stats. If the pointer is null, data will be zero'd.
+ */
+static void
+iecm_add_one_ethtool_stat(u64 *data, void *pstat,
+			  const struct iecm_stats *stat)
+{
+	char *p;
+
+	if (!pstat) {
+		/* Ensure that the ethtool data buffer is zero'd for any stats
+		 * which don't have a valid pointer.
+		 */
+		*data = 0;
+		return;
+	}
+
+	p = (char *)pstat + stat->stat_offset;
+	switch (stat->sizeof_stat) {
+	case sizeof(u64):
+		*data = *((u64 *)p);
+		break;
+	case sizeof(u32):
+		*data = *((u32 *)p);
+		break;
+	case sizeof(u16):
+		*data = *((u16 *)p);
+		break;
+	case sizeof(u8):
+		*data = *((u8 *)p);
+		break;
+	default:
+		WARN_ONCE(1, "unexpected stat size for %s",
+			  stat->stat_string);
+		*data = 0;
+	}
+}
+
+/**
+ * iecm_add_queue_stats - copy queue statistics into supplied buffer
+ * @data: ethtool stats buffer
+ * @q: the queue to copy
+ *
+ * Queue statistics must be copied while protected by
+ * u64_stats_fetch_begin_irq, so we can't directly use iecm_add_ethtool_stats.
+ * Assumes that queue stats are defined in iecm_gstrings_queue_stats. If the
+ * queue pointer is null, zero out the queue stat values and update the data
+ * pointer. Otherwise safely copy the stats from the queue into the supplied
+ * buffer and update the data pointer when finished.
+ *
+ * This function expects to be called while under rcu_read_lock().
+ */
+static void
+iecm_add_queue_stats(u64 **data, struct iecm_queue *q)
+{
+	const struct iecm_stats *stats;
+	unsigned int start;
+	unsigned int size;
+	unsigned int i;
+
+	if (q->q_type == VIRTCHNL2_QUEUE_TYPE_RX) {
+		size = IECM_RX_QUEUE_STATS_LEN;
+		stats = iecm_gstrings_rx_queue_stats;
+	} else {
+		size = IECM_TX_QUEUE_STATS_LEN;
+		stats = iecm_gstrings_tx_queue_stats;
+	}
+
+	/* To avoid invalid statistics values, ensure that we keep retrying
+	 * the copy until we get a consistent value according to
+	 * u64_stats_fetch_retry_irq. But first, make sure our queue is
+	 * non-null before attempting to access its syncp.
+	 */
+	do {
+		start = u64_stats_fetch_begin_irq(&q->stats_sync);
+		for (i = 0; i < size; i++)
+			iecm_add_one_ethtool_stat(&(*data)[i], q, &stats[i]);
+	} while (u64_stats_fetch_retry_irq(&q->stats_sync, start));
+
+	/* Once we successfully copy the stats in, update the data pointer */
+	*data += size;
+}
+
+/**
+ * iecm_add_empty_queue_stats - Add stats for a non-existent queue
+ * @data: pointer to data buffer
+ * @qtype: type of data queue
+ *
+ * We must report a constant length of stats back to userspace regardless of
+ * how many queues are actually in use because stats collection happens over
+ * three separate ioctls and there's no way to notify userspace the size
+ * changed between those calls. This adds empty to data to the stats since we
+ * don't have a real queue to refer to for this stats slot.
+ */
+static void
+iecm_add_empty_queue_stats(u64 **data, u16 qtype)
+{
+	unsigned int i;
+	int stats_len;
+
+	if (qtype == VIRTCHNL2_QUEUE_TYPE_RX)
+		stats_len = IECM_RX_QUEUE_STATS_LEN;
+	else
+		stats_len = IECM_TX_QUEUE_STATS_LEN;
+
+	for (i = 0; i < stats_len; i++)
+		(*data)[i] = 0;
+	*data += stats_len;
+}
+
+/**
+ * iecm_add_port_stats - Copy port stats into ethtool buffer
+ * @vport: virtual port struct
+ * @data: ethtool buffer to copy into
+ */
+static void iecm_add_port_stats(struct iecm_vport *vport, u64 **data)
+{
+	unsigned int size = IECM_PORT_STATS_LEN;
+	unsigned int start;
+	unsigned int i;
+
+	/* To avoid invalid statistics values, ensure that we keep retrying
+	 * the copy until we get a consistent value according to
+	 * u64_stats_fetch_retry_irq.
+	 */
+	do {
+		start = u64_stats_fetch_begin_irq(&vport->port_stats.stats_sync);
+		for (i = 0; i < size; i++) {
+			iecm_add_one_ethtool_stat(&(*data)[i], vport,
+						  &iecm_gstrings_port_stats[i]);
+		}
+	} while (u64_stats_fetch_retry_irq(&vport->port_stats.stats_sync, start));
+
+	*data += size;
+}
+
+/**
+ * iecm_get_ethtool_stats - report device statistics
+ * @netdev: network interface device structure
+ * @stats: ethtool statistics structure
+ * @data: pointer to data buffer
+ *
+ * All statistics are added to the data buffer as an array of u64.
+ */
+static void iecm_get_ethtool_stats(struct net_device *netdev,
+				   struct ethtool_stats __always_unused *stats,
+				   u64 *data)
+{
+	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
+	unsigned int total = 0;
+	unsigned int i, j;
+	u16 max_q, qtype;
+
+	if (vport->adapter->state != __IECM_UP)
+		return;
+
+	set_bit(__IECM_MB_STATS_PENDING, vport->adapter->flags);
+
+	max_q = vport->adapter->max_queue_limit;
+
+	rcu_read_lock();
+
+	iecm_add_port_stats(vport, &data);
+
+	for (i = 0; i < vport->num_txq_grp; i++) {
+		struct iecm_txq_group *txq_grp = &vport->txq_grps[i];
+
+		qtype = VIRTCHNL2_QUEUE_TYPE_TX;
+
+		for (j = 0; j < txq_grp->num_txq; j++, total++) {
+			struct iecm_queue *txq = txq_grp->txqs[j];
+
+			if (!txq)
+				iecm_add_empty_queue_stats(&data, qtype);
+			else
+				iecm_add_queue_stats(&data, txq);
+		}
+	}
+	/* It is critical we provide a constant number of stats back to
+	 * userspace regardless of how many queues are actually in use because
+	 * there is no way to inform userspace the size has changed between
+	 * ioctl calls. This will fill in any missing stats with zero.
+	 */
+	for (; total < max_q; total++)
+		iecm_add_empty_queue_stats(&data, VIRTCHNL2_QUEUE_TYPE_TX);
+	total = 0;
+
+	for (i = 0; i < vport->num_rxq_grp; i++) {
+		struct iecm_rxq_group *rxq_grp = &vport->rxq_grps[i];
+		int num_rxq;
+
+		qtype = VIRTCHNL2_QUEUE_TYPE_RX;
+
+		if (iecm_is_queue_model_split(vport->rxq_model))
+			num_rxq = rxq_grp->splitq.num_rxq_sets;
+		else
+			num_rxq = rxq_grp->singleq.num_rxq;
+
+		for (j = 0; j < num_rxq; j++, total++) {
+			struct iecm_queue *rxq;
+
+			if (iecm_is_queue_model_split(vport->rxq_model))
+				rxq = &rxq_grp->splitq.rxq_sets[j]->rxq;
+			else
+				rxq = rxq_grp->singleq.rxqs[j];
+			if (!rxq)
+				iecm_add_empty_queue_stats(&data, qtype);
+			else
+				iecm_add_queue_stats(&data, rxq);
+		}
+	}
+	for (; total < max_q; total++)
+		iecm_add_empty_queue_stats(&data, VIRTCHNL2_QUEUE_TYPE_RX);
+	rcu_read_unlock();
+}
+
+/**
+ * iecm_find_rxq - find rxq from q index
+ * @vport: virtual port associated to queue
+ * @q_num: q index used to find queue
+ *
+ * returns pointer to rx queue
+ */
+static struct iecm_queue *
+iecm_find_rxq(struct iecm_vport *vport, int q_num)
+{
+	struct iecm_queue *rxq;
+
+	if (iecm_is_queue_model_split(vport->rxq_model)) {
+		int q_grp, q_idx;
+
+		q_grp = q_num / IECM_DFLT_SPLITQ_RXQ_PER_GROUP;
+		q_idx = q_num % IECM_DFLT_SPLITQ_RXQ_PER_GROUP;
+
+		rxq = &vport->rxq_grps[q_grp].splitq.rxq_sets[q_idx]->rxq;
+	} else {
+		rxq = vport->rxq_grps->singleq.rxqs[q_num];
+	}
+
+	return rxq;
+}
+
+/**
+ * iecm_find_txq - find txq from q index
+ * @vport: virtual port associated to queue
+ * @q_num: q index used to find queue
+ *
+ * returns pointer to tx queue
+ */
+static struct iecm_queue *
+iecm_find_txq(struct iecm_vport *vport, int q_num)
+{
+	struct iecm_queue *txq;
+
+	if (iecm_is_queue_model_split(vport->txq_model)) {
+		int q_grp = q_num / IECM_DFLT_SPLITQ_TXQ_PER_GROUP;
+
+		txq = vport->txq_grps[q_grp].complq;
+	} else {
+		txq = vport->txqs[q_num];
+	}
+
+	return txq;
+}
+
+/**
+ * __iecm_get_q_coalesce - get ITR values for specific queue
+ * @ec: ethtool structure to fill with driver's coalesce settings
+ * @q: quuee of Rx or Tx
+ */
+static void
+__iecm_get_q_coalesce(struct ethtool_coalesce *ec, struct iecm_queue *q)
+{
+	if (q->q_type == VIRTCHNL2_QUEUE_TYPE_RX) {
+		ec->use_adaptive_rx_coalesce =
+				IECM_ITR_IS_DYNAMIC(q->q_vector->rx_intr_mode);
+		ec->rx_coalesce_usecs = q->q_vector->rx_itr_value;
+	} else {
+		ec->use_adaptive_tx_coalesce =
+				IECM_ITR_IS_DYNAMIC(q->q_vector->tx_intr_mode);
+		ec->tx_coalesce_usecs = q->q_vector->tx_itr_value;
+	}
+}
+
+/**
+ * iecm_get_q_coalesce - get ITR values for specific queue
+ * @netdev: pointer to the netdev associated with this query
+ * @ec: coalesce settings to program the device with
+ * @q_num: update ITR/INTRL (coalesce) settings for this queue number/index
+ *
+ * Return 0 on success, and negative on failure
+ */
+static int
+iecm_get_q_coalesce(struct net_device *netdev, struct ethtool_coalesce *ec,
+		    u32 q_num)
+{
+	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
+
+	if (vport->adapter->state != __IECM_UP)
+		return 0;
+
+	if (q_num >= vport->num_rxq && q_num >= vport->num_txq)
+		return -EINVAL;
+
+	if (q_num < vport->num_rxq) {
+		struct iecm_queue *rxq = iecm_find_rxq(vport, q_num);
+
+		__iecm_get_q_coalesce(ec, rxq);
+	}
+
+	if (q_num < vport->num_txq) {
+		struct iecm_queue *txq = iecm_find_txq(vport, q_num);
+
+		__iecm_get_q_coalesce(ec, txq);
+	}
+
+	return 0;
+}
+
+/**
+ * iecm_get_coalesce - get ITR values as requested by user
+ * @netdev: pointer to the netdev associated with this query
+ * @ec: coalesce settings to be filled
+ * @kec: unused
+ * @extack: unused
+ *
+ * Return 0 on success, and negative on failure
+ */
+static int
+iecm_get_coalesce(struct net_device *netdev, struct ethtool_coalesce *ec,
+		  struct kernel_ethtool_coalesce __always_unused *kec,
+		  struct netlink_ext_ack __always_unused *extack)
+{
+	/* Return coalesce based on queue number zero */
+	return iecm_get_q_coalesce(netdev, ec, 0);
+}
+
+/**
+ * iecm_get_per_q_coalesce - get ITR values as requested by user
+ * @netdev: pointer to the netdev associated with this query
+ * @q_num: queue for which the itr values has to retrieved
+ * @ec: coalesce settings to be filled
+ *
+ * Return 0 on success, and negative on failure
+ */
+
+static int
+iecm_get_per_q_coalesce(struct net_device *netdev, u32 q_num,
+			struct ethtool_coalesce *ec)
+{
+	return iecm_get_q_coalesce(netdev, ec, q_num);
+}
+
+/**
+ * __iecm_set_q_coalesce - set ITR values for specific queue
+ * @ec: ethtool structure from user to update ITR settings
+ * @q: queue for which itr values has to be set
+ *
+ * Returns 0 on success, negative otherwise.
+ */
+static int
+__iecm_set_q_coalesce(struct ethtool_coalesce *ec, struct iecm_queue *q)
+{
+	u32 use_adaptive_coalesce, coalesce_usecs;
+	struct iecm_q_vector *qv = q->q_vector;
+	struct iecm_vport *vport;
+	bool is_dim_ena = false;
+
+	vport = q->vport;
+	if (q->q_type == VIRTCHNL2_QUEUE_TYPE_RX) {
+		is_dim_ena = IECM_ITR_IS_DYNAMIC(qv->rx_intr_mode);
+		use_adaptive_coalesce = ec->use_adaptive_rx_coalesce;
+		coalesce_usecs = ec->rx_coalesce_usecs;
+	} else {
+		is_dim_ena = IECM_ITR_IS_DYNAMIC(qv->tx_intr_mode);
+		use_adaptive_coalesce = ec->use_adaptive_tx_coalesce;
+		coalesce_usecs = ec->tx_coalesce_usecs;
+	}
+
+	if (coalesce_usecs && use_adaptive_coalesce) {
+		netdev_info(vport->netdev, "Cannot set coalesce usecs if adaptive enabled\n");
+		return -EINVAL;
+	}
+
+	if (is_dim_ena && use_adaptive_coalesce)
+		return 0;
+
+	if (coalesce_usecs > IECM_ITR_MAX) {
+		netdev_info(vport->netdev,
+			    "Invalid value, %d-usecs range is 0-%d\n",
+			    coalesce_usecs, IECM_ITR_MAX);
+		return -EINVAL;
+	}
+
+	if (coalesce_usecs % 2 != 0) {
+		coalesce_usecs = coalesce_usecs & 0xFFFFFFFE;
+		netdev_info(vport->netdev, "HW only supports even ITR values, ITR rounded to %d\n",
+			    coalesce_usecs);
+	}
+
+	if (q->q_type == VIRTCHNL2_QUEUE_TYPE_RX) {
+		qv->rx_itr_value = coalesce_usecs;
+		if (use_adaptive_coalesce) {
+			qv->rx_intr_mode = IECM_ITR_DYNAMIC;
+		} else {
+			qv->rx_intr_mode = !IECM_ITR_DYNAMIC;
+			iecm_vport_intr_write_itr(qv, qv->rx_itr_value,
+						  false);
+		}
+	} else {
+		qv->tx_itr_value = coalesce_usecs;
+		if (use_adaptive_coalesce) {
+			qv->tx_intr_mode = IECM_ITR_DYNAMIC;
+		} else {
+			qv->tx_intr_mode = !IECM_ITR_DYNAMIC;
+			iecm_vport_intr_write_itr(qv, qv->tx_itr_value, true);
+		}
+	}
+	/* Update of static/dynamic itr will be taken care when interrupt is
+	 * fired
+	 */
+	return 0;
+}
+
+/**
+ * iecm_set_q_coalesce - set ITR values for specific queue
+ * @vport: vport associated to the queue that need updating
+ * @ec: coalesce settings to program the device with
+ * @q_num: update ITR/INTRL (coalesce) settings for this queue number/index
+ * @is_rxq: is queue type rx
+ *
+ * Return 0 on success, and negative on failure
+ */
+static int
+iecm_set_q_coalesce(struct iecm_vport *vport, struct ethtool_coalesce *ec,
+		    int q_num, bool is_rxq)
+{
+	struct iecm_queue *q;
+
+	if (is_rxq)
+		q = iecm_find_rxq(vport, q_num);
+	else
+		q = iecm_find_txq(vport, q_num);
+
+	if (q && __iecm_set_q_coalesce(ec, q))
+		return -EINVAL;
+
+	return 0;
+}
+
+/**
+ * iecm_set_coalesce - set ITR values as requested by user
+ * @netdev: pointer to the netdev associated with this query
+ * @ec: coalesce settings to program the device with
+ * @kec: unused
+ * @extack: unused
+ *
+ * Return 0 on success, and negative on failure
+ */
+static int
+iecm_set_coalesce(struct net_device *netdev, struct ethtool_coalesce *ec,
+		  struct kernel_ethtool_coalesce __always_unused *kec,
+		  struct netlink_ext_ack __always_unused *extack)
+{
+	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
+	int i, err;
+
+	if (vport->adapter->state != __IECM_UP)
+		return 0;
+
+	for (i = 0; i < vport->num_txq; i++) {
+		err = iecm_set_q_coalesce(vport, ec, i, false);
+		if (err)
+			return err;
+	}
+
+	for (i = 0; i < vport->num_rxq; i++) {
+		err = iecm_set_q_coalesce(vport, ec, i, true);
+		if (err)
+			return err;
+	}
+	return 0;
+}
+
+/**
+ * iecm_set_per_q_coalesce - set ITR values as requested by user
+ * @netdev: pointer to the netdev associated with this query
+ * @q_num: queue for which the itr values has to be set
+ * @ec: coalesce settings to program the device with
+ *
+ * Return 0 on success, and negative on failure
+ */
+static int
+iecm_set_per_q_coalesce(struct net_device *netdev, u32 q_num,
+			struct ethtool_coalesce *ec)
+{
+	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
+	int err;
+
+	err = iecm_set_q_coalesce(vport, ec, q_num, false);
+	if (!err)
+		err = iecm_set_q_coalesce(vport, ec, q_num, true);
+
+	return err;
+}
+
+/**
+ * iecm_get_msglevel - Get debug message level
+ * @netdev: network interface device structure
+ *
+ * Returns current debug message level.
+ */
+static u32 iecm_get_msglevel(struct net_device *netdev)
+{
+	struct iecm_adapter *adapter = iecm_netdev_to_adapter(netdev);
+
+	return adapter->msg_enable;
+}
+
+/**
+ * iecm_set_msglevel - Set debug message level
+ * @netdev: network interface device structure
+ * @data: message level
+ *
+ * Set current debug message level. Higher values cause the driver to
+ * be noisier.
+ */
+static void iecm_set_msglevel(struct net_device *netdev, u32 data)
+{
+	struct iecm_adapter *adapter = iecm_netdev_to_adapter(netdev);
+
+	adapter->msg_enable = data;
+}
+
+/**
+ * iecm_get_link_ksettings - Get Link Speed and Duplex settings
+ * @netdev: network interface device structure
+ * @cmd: ethtool command
+ *
+ * Reports speed/duplex settings.
+ **/
+static int iecm_get_link_ksettings(struct net_device *netdev,
+				   struct ethtool_link_ksettings *cmd)
+{
+	struct iecm_netdev_priv *np = netdev_priv(netdev);
+	struct iecm_adapter *adapter = np->vport->adapter;
+
+	ethtool_link_ksettings_zero_link_mode(cmd, supported);
+	cmd->base.autoneg = AUTONEG_DISABLE;
+	cmd->base.port = PORT_NONE;
+	cmd->base.duplex = DUPLEX_FULL;
+
+	if (adapter->link_speed_mbps) {
+		cmd->base.speed = adapter->link_speed_mbps;
+		return 0;
+	}
+
+	/* Set speed and duplex */
+	switch (adapter->link_speed) {
+	case VIRTCHNL_LINK_SPEED_40GB:
+		cmd->base.speed = SPEED_40000;
+		break;
+	case VIRTCHNL_LINK_SPEED_25GB:
+		cmd->base.speed = SPEED_25000;
+		break;
+	case VIRTCHNL_LINK_SPEED_20GB:
+		cmd->base.speed = SPEED_20000;
+		break;
+	case VIRTCHNL_LINK_SPEED_10GB:
+		cmd->base.speed = SPEED_10000;
+		break;
+	case VIRTCHNL_LINK_SPEED_1GB:
+		cmd->base.speed = SPEED_1000;
+		break;
+	case VIRTCHNL_LINK_SPEED_100MB:
+		cmd->base.speed = SPEED_100;
+		break;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static const struct ethtool_ops iecm_ethtool_ops = {
+	.supported_coalesce_params = ETHTOOL_COALESCE_USECS |
+				     ETHTOOL_COALESCE_USE_ADAPTIVE,
+	.get_msglevel		= iecm_get_msglevel,
+	.set_msglevel		= iecm_set_msglevel,
+	.get_coalesce		= iecm_get_coalesce,
+	.set_coalesce		= iecm_set_coalesce,
+	.get_per_queue_coalesce = iecm_get_per_q_coalesce,
+	.set_per_queue_coalesce = iecm_set_per_q_coalesce,
+	.get_ethtool_stats	= iecm_get_ethtool_stats,
+	.get_strings		= iecm_get_strings,
+	.get_sset_count		= iecm_get_sset_count,
+	.get_priv_flags		= iecm_get_priv_flags,
+	.set_priv_flags		= iecm_set_priv_flags,
+	.get_rxnfc		= iecm_get_rxnfc,
+	.set_rxnfc		= iecm_set_rxnfc,
+	.get_rxfh_key_size	= iecm_get_rxfh_key_size,
+	.get_rxfh_indir_size	= iecm_get_rxfh_indir_size,
+	.get_rxfh		= iecm_get_rxfh,
+	.set_rxfh		= iecm_set_rxfh,
+	.get_channels		= iecm_get_channels,
+	.set_channels		= iecm_set_channels,
+	.get_ringparam		= iecm_get_ringparam,
+	.set_ringparam		= iecm_set_ringparam,
+	.get_link_ksettings	= iecm_get_link_ksettings,
+};
+
+/**
+ * iecm_set_ethtool_ops - Initialize ethtool ops struct
+ * @netdev: network interface device structure
+ *
+ * Sets ethtool ops struct in our netdev so that ethtool can call
+ * our functions.
+ */
+void iecm_set_ethtool_ops(struct net_device *netdev)
+{
+	netdev->ethtool_ops = &iecm_ethtool_ops;
+}
diff --git a/drivers/net/ethernet/intel/iecm/iecm_lib.c b/drivers/net/ethernet/intel/iecm/iecm_lib.c
index cbde65f1c523..c5900723b018 100644
--- a/drivers/net/ethernet/intel/iecm/iecm_lib.c
+++ b/drivers/net/ethernet/intel/iecm/iecm_lib.c
@@ -871,6 +871,7 @@ static int iecm_cfg_netdev(struct iecm_vport *vport)
 	netdev->hw_features |= dflt_features | offloads;
 	netdev->hw_enc_features |= dflt_features | offloads;
 
+	iecm_set_ethtool_ops(netdev);
 	SET_NETDEV_DEV(netdev, &adapter->pdev->dev);
 
 	/* carrier off on init to avoid Tx hangs */
@@ -1150,7 +1151,15 @@ iecm_vport_alloc(struct iecm_adapter *adapter, int vport_id)
  */
 static void iecm_statistics_task(struct work_struct *work)
 {
-	/* stub */
+	struct iecm_adapter *adapter = container_of(work,
+						    struct iecm_adapter,
+						    stats_task.work);
+	if (test_bit(__IECM_MB_STATS_PENDING, adapter->flags) &&
+	    !test_bit(__IECM_HR_RESET_IN_PROG, adapter->flags))
+		adapter->dev_ops.vc_ops.get_stats_msg(adapter->vports[0]);
+
+	queue_delayed_work(adapter->stats_wq, &adapter->stats_task,
+			   msecs_to_jiffies(1000));
 }
 
 /**
diff --git a/drivers/net/ethernet/intel/include/iecm.h b/drivers/net/ethernet/intel/include/iecm.h
index 97c9935b832d..d118da1ea8cd 100644
--- a/drivers/net/ethernet/intel/include/iecm.h
+++ b/drivers/net/ethernet/intel/include/iecm.h
@@ -745,6 +745,7 @@ int iecm_recv_mb_msg(struct iecm_adapter *adapter, enum virtchnl_ops op,
 		     void *msg, int msg_size);
 int iecm_send_mb_msg(struct iecm_adapter *adapter, enum virtchnl_ops op,
 		     u16 msg_size, u8 *msg);
+void iecm_set_ethtool_ops(struct net_device *netdev);
 void iecm_vport_set_hsplit(struct iecm_vport *vport, bool ena);
 void iecm_add_del_ether_addrs(struct iecm_vport *vport, bool add, bool async);
 int iecm_set_promiscuous(struct iecm_adapter *adapter);
-- 
2.33.0



^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 16/19] iecm: implement flow director
  2022-01-28  0:09 [Intel-wired-lan] [PATCH net-next 00/19] Add iecm and idpf Alan Brady
                   ` (14 preceding siblings ...)
  2022-01-28  0:10 ` [Intel-wired-lan] [PATCH net-next 15/19] iecm: implement ethtool callbacks Alan Brady
@ 2022-01-28  0:10 ` Alan Brady
  2022-01-28 19:04   ` Alexander Lobakin
  2022-01-28  0:10 ` [Intel-wired-lan] [PATCH net-next 17/19] iecm: implement cloud filters Alan Brady
                   ` (3 subsequent siblings)
  19 siblings, 1 reply; 89+ messages in thread
From: Alan Brady @ 2022-01-28  0:10 UTC (permalink / raw)
  To: intel-wired-lan

From: Haiyue Wang <haiyue.wang@intel.com>

This adds everthing needed to do flow director commands.

Signed-off-by: Haiyue Wang <haiyue.wang@intel.com>
---
 .../net/ethernet/intel/iecm/iecm_ethtool.c    |   17 +-
 drivers/net/ethernet/intel/iecm/iecm_lib.c    | 1528 ++++++++++++++++-
 .../net/ethernet/intel/iecm/iecm_virtchnl.c   |  119 ++
 drivers/net/ethernet/intel/include/iecm.h     |  112 ++
 4 files changed, 1770 insertions(+), 6 deletions(-)

diff --git a/drivers/net/ethernet/intel/iecm/iecm_ethtool.c b/drivers/net/ethernet/intel/iecm/iecm_ethtool.c
index 32d905fb1bb6..7e252b25e02d 100644
--- a/drivers/net/ethernet/intel/iecm/iecm_ethtool.c
+++ b/drivers/net/ethernet/intel/iecm/iecm_ethtool.c
@@ -15,6 +15,7 @@ static int iecm_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd,
 			  u32 __always_unused *rule_locs)
 {
 	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
+	struct iecm_adapter *adapter = vport->adapter;
 	int ret = -EOPNOTSUPP;
 
 	switch (cmd->cmd) {
@@ -23,14 +24,19 @@ static int iecm_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd,
 		ret = 0;
 		break;
 	case ETHTOOL_GRXCLSRLCNT:
-		/* stub */
+		if (!iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
+				     VIRTCHNL2_CAP_FDIR))
+			break;
+		cmd->rule_cnt =
+			adapter->config_data.fdir_config.num_active_filters;
+		cmd->data = IECM_MAX_FDIR_FILTERS;
 		ret = 0;
 		break;
 	case ETHTOOL_GRXCLSRULE:
-		/* stub */
+		ret = iecm_get_fdir_fltr_entry(vport, cmd);
 		break;
 	case ETHTOOL_GRXCLSRLALL:
-		/* stub */
+		ret = iecm_get_fdir_fltr_ids(vport, cmd, (u32 *)rule_locs);
 		break;
 	case ETHTOOL_GRXFH:
 		/* stub */
@@ -51,14 +57,15 @@ static int iecm_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd,
  */
 static int iecm_set_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd)
 {
+	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
 	int ret = -EOPNOTSUPP;
 
 	switch (cmd->cmd) {
 	case ETHTOOL_SRXCLSRLINS:
-		/* stub */
+		ret = iecm_add_fdir_fltr(vport, cmd);
 		break;
 	case ETHTOOL_SRXCLSRLDEL:
-		/* stub */
+		ret = iecm_del_fdir_fltr(vport, cmd);
 		break;
 	case ETHTOOL_SRXFH:
 		/* stub */
diff --git a/drivers/net/ethernet/intel/iecm/iecm_lib.c b/drivers/net/ethernet/intel/iecm/iecm_lib.c
index c5900723b018..35c0cbc42ebe 100644
--- a/drivers/net/ethernet/intel/iecm/iecm_lib.c
+++ b/drivers/net/ethernet/intel/iecm/iecm_lib.c
@@ -967,6 +967,56 @@ static void iecm_remove_vlan_filters(struct iecm_vport *vport)
 	}
 }
 
+/**
+ * iecm_remove_fdir_filters - Remove all Flow Director filters
+ * @vport: vport structure
+ */
+static void iecm_remove_fdir_filters(struct iecm_vport *vport)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	struct iecm_fdir_fltr_config *fdir_config;
+
+	if (!iecm_is_cap_ena(adapter, IECM_OTHER_CAPS, VIRTCHNL2_CAP_FDIR))
+		return;
+
+	fdir_config = &adapter->config_data.fdir_config;
+	if (!list_empty(&fdir_config->fdir_fltr_list)) {
+		struct iecm_fdir_fltr *fdir;
+
+		spin_lock_bh(&adapter->fdir_fltr_list_lock);
+		list_for_each_entry(fdir, &fdir_config->fdir_fltr_list, list) {
+			fdir->remove = true;
+		}
+		spin_unlock_bh(&adapter->fdir_fltr_list_lock);
+		iecm_send_del_fdir_filter_msg(vport);
+	}
+}
+
+/**
+ * iecm_del_all_fdir_filters - delete all Flow Director filters
+ * @vport: vport structure
+ *
+ * This function will loop through the list of Flow Director filters and
+ * deletes them.
+ **/
+static void iecm_del_all_fdir_filters(struct iecm_vport *vport)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	struct iecm_fdir_fltr_config *fdir_config;
+	struct iecm_fdir_fltr *fdir, *fdir_tmp;
+
+	fdir_config = &adapter->config_data.fdir_config;
+
+	spin_lock_bh(&adapter->fdir_fltr_list_lock);
+	list_for_each_entry_safe(fdir, fdir_tmp, &fdir_config->fdir_fltr_list,
+				 list) {
+		list_del(&fdir->list);
+		kfree(fdir);
+	}
+	fdir_config->num_active_filters = 0;
+	spin_unlock_bh(&adapter->fdir_fltr_list_lock);
+}
+
 /**
  * iecm_vport_stop - Disable a vport
  * @vport: vport to disable
@@ -997,6 +1047,8 @@ static void iecm_vport_stop(struct iecm_vport *vport)
 	if (!test_bit(__IECM_REL_RES_IN_PROG, adapter->flags))
 		iecm_remove_vlan_filters(vport);
 
+	iecm_remove_fdir_filters(vport);
+
 	adapter->link_up = false;
 	iecm_vport_intr_deinit(vport);
 	iecm_vport_intr_rel(vport);
@@ -1206,6 +1258,28 @@ static void iecm_restore_vlans(struct iecm_vport *vport)
 		iecm_set_all_vlans(vport);
 }
 
+/**
+ * iecm_restore_fdir_filters - Restore all Flow Director filters
+ * @vport: vport structure
+ */
+static void iecm_restore_fdir_filters(struct iecm_vport *vport)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	struct iecm_fdir_fltr_config *fdir_config;
+
+	fdir_config = &adapter->config_data.fdir_config;
+	if (!list_empty(&fdir_config->fdir_fltr_list)) {
+		struct iecm_fdir_fltr *fdir;
+
+		spin_lock_bh(&adapter->fdir_fltr_list_lock);
+		list_for_each_entry(fdir, &fdir_config->fdir_fltr_list, list) {
+			fdir->add = true;
+		}
+		spin_unlock_bh(&adapter->fdir_fltr_list_lock);
+		iecm_send_add_fdir_filter_msg(vport);
+	}
+}
+
 /**
  * iecm_restore_features - Restore feature configs
  * @vport: virtual port structure
@@ -1227,6 +1301,9 @@ static void iecm_restore_features(struct iecm_vport *vport)
 		if (iecm_set_promiscuous(adapter))
 			dev_info(&adapter->pdev->dev, "Failed to restore promiscuous settings\n");
 	}
+
+	if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS, VIRTCHNL2_CAP_FDIR))
+		iecm_restore_fdir_filters(vport);
 }
 
 /**
@@ -2014,6 +2091,7 @@ int iecm_probe(struct pci_dev *pdev,
 	INIT_LIST_HEAD(&adapter->config_data.mac_filter_list);
 	INIT_LIST_HEAD(&adapter->config_data.vlan_filter_list);
 	INIT_LIST_HEAD(&adapter->config_data.adv_rss_list);
+	INIT_LIST_HEAD(&adapter->config_data.fdir_config.fdir_fltr_list);
 
 	INIT_DELAYED_WORK(&adapter->stats_task, iecm_statistics_task);
 	INIT_DELAYED_WORK(&adapter->serv_task, iecm_service_task);
@@ -2052,7 +2130,17 @@ EXPORT_SYMBOL(iecm_probe);
  */
 static void iecm_del_user_cfg_data(struct iecm_adapter *adapter)
 {
-	/* stub */
+	int i;
+
+	if (!adapter->vports)
+		return;
+
+	for (i = 0; i < adapter->num_alloc_vport; i++) {
+		if (!adapter->vports[i])
+			continue;
+
+		iecm_del_all_fdir_filters(adapter->vports[i]);
+	}
 }
 
 /**
@@ -2647,6 +2735,1444 @@ static int iecm_setup_tc(struct net_device *netdev, enum tc_setup_type type,
 	return err;
 }
 
+/**
+ * iecm_pkt_udp_no_pay_len - the length of UDP packet without payload
+ * @fltr: Flow Director filter data structure
+ */
+static u16 iecm_pkt_udp_no_pay_len(struct iecm_fdir_fltr *fltr)
+{
+	return sizeof(struct ethhdr) +
+		(fltr->ip_ver == 4 ? sizeof(struct iphdr)
+				   : sizeof(struct ipv6hdr)) +
+		sizeof(struct udphdr);
+}
+
+/**
+ * iecm_fill_fdir_gtpu_hdr - fill the GTP-U protocol header
+ * @fltr: Flow Director filter data structure
+ * @proto_hdrs: Flow Director protocol headers data structure
+ *
+ * Returns 0 if the GTP-U protocol header is set successfully
+ */
+static int
+iecm_fill_fdir_gtpu_hdr(struct iecm_fdir_fltr *fltr,
+			struct virtchnl_proto_hdrs *proto_hdrs)
+{
+	struct virtchnl_proto_hdr *uhdr = &proto_hdrs->proto_hdr[proto_hdrs->count - 1];
+	struct virtchnl_proto_hdr *ghdr = &proto_hdrs->proto_hdr[proto_hdrs->count++];
+	struct virtchnl_proto_hdr *ehdr = NULL;
+	u16 adj_offs, hdr_offs;
+	int i;
+
+	VIRTCHNL_SET_PROTO_HDR_TYPE(ghdr, GTPU_IP);
+
+	adj_offs = iecm_pkt_udp_no_pay_len(fltr);
+
+	for (i = 0; i < fltr->flex_cnt; i++) {
+#define IECM_GTPU_HDR_TEID_OFFS0	4
+#define IECM_GTPU_HDR_TEID_OFFS1	6
+#define IECM_GTPU_HDR_N_PDU_AND_NEXT_EXTHDR_OFFS	10
+#define IECM_GTPU_HDR_NEXT_EXTHDR_TYPE_MASK		0x00FF /* skip N_PDU */
+/* PDU Session Container Extension Header (PSC) */
+#define IECM_GTPU_PSC_EXTHDR_TYPE			0x85
+#define IECM_GTPU_HDR_PSC_PDU_TYPE_AND_QFI_OFFS		13
+#define IECM_GTPU_HDR_PSC_PDU_QFI_MASK			0x3F /* skip Type */
+#define IECM_GTPU_EH_QFI_IDX				1
+
+		if (fltr->flex_words[i].offset < adj_offs)
+			return -EINVAL;
+
+		hdr_offs = fltr->flex_words[i].offset - adj_offs;
+
+		switch (hdr_offs) {
+		case IECM_GTPU_HDR_TEID_OFFS0:
+		case IECM_GTPU_HDR_TEID_OFFS1: {
+			__be16 *pay_word = (__force __be16 *)ghdr->buffer;
+
+			pay_word[hdr_offs >> 1] =
+					htons(fltr->flex_words[i].word);
+			VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(ghdr, GTPU_IP, TEID);
+			}
+			break;
+		case IECM_GTPU_HDR_N_PDU_AND_NEXT_EXTHDR_OFFS:
+			if ((fltr->flex_words[i].word &
+			     IECM_GTPU_HDR_NEXT_EXTHDR_TYPE_MASK) !=
+						IECM_GTPU_PSC_EXTHDR_TYPE)
+				return -EOPNOTSUPP;
+			if (!ehdr)
+				ehdr =
+				   &proto_hdrs->proto_hdr[proto_hdrs->count++];
+			VIRTCHNL_SET_PROTO_HDR_TYPE(ehdr, GTPU_EH);
+			break;
+		case IECM_GTPU_HDR_PSC_PDU_TYPE_AND_QFI_OFFS:
+			if (!ehdr)
+				return -EINVAL;
+			ehdr->buffer[IECM_GTPU_EH_QFI_IDX] =
+					fltr->flex_words[i].word &
+						IECM_GTPU_HDR_PSC_PDU_QFI_MASK;
+			VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(ehdr, GTPU_EH, QFI);
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+
+	/* The PF ignores the UDP header fields */
+	uhdr->field_selector = 0;
+
+	return 0;
+}
+
+/**
+ * iecm_fill_fdir_pfcp_hdr - fill the PFCP protocol header
+ * @fltr: Flow Director filter data structure
+ * @proto_hdrs: Flow Director protocol headers data structure
+ *
+ * Returns 0 if the PFCP protocol header is set successfully
+ */
+static int
+iecm_fill_fdir_pfcp_hdr(struct iecm_fdir_fltr *fltr,
+			struct virtchnl_proto_hdrs *proto_hdrs)
+{
+	struct virtchnl_proto_hdr *uhdr =
+				&proto_hdrs->proto_hdr[proto_hdrs->count - 1];
+	struct virtchnl_proto_hdr *hdr =
+				&proto_hdrs->proto_hdr[proto_hdrs->count++];
+	u16 adj_offs, hdr_offs;
+	int i;
+
+	VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, PFCP);
+
+	adj_offs = iecm_pkt_udp_no_pay_len(fltr);
+
+	for (i = 0; i < fltr->flex_cnt; i++) {
+#define IECM_PFCP_HDR_SFIELD_AND_MSG_TYPE_OFFS	0
+		if (fltr->flex_words[i].offset < adj_offs)
+			return -EINVAL;
+
+		hdr_offs = fltr->flex_words[i].offset - adj_offs;
+
+		switch (hdr_offs) {
+		case IECM_PFCP_HDR_SFIELD_AND_MSG_TYPE_OFFS:
+			hdr->buffer[0] = (fltr->flex_words[i].word >> 8) & 0xff;
+			VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, PFCP, S_FIELD);
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+
+	/* The PF ignores the UDP header fields */
+	uhdr->field_selector = 0;
+
+	return 0;
+}
+
+/**
+ * iecm_fill_fdir_nat_t_esp_hdr - fill the NAT-T-ESP protocol header
+ * @fltr: Flow Director filter data structure
+ * @proto_hdrs: Flow Director protocol headers data structure
+ *
+ * Returns 0 if the NAT-T-ESP protocol header is set successfully
+ */
+static int
+iecm_fill_fdir_nat_t_esp_hdr(struct iecm_fdir_fltr *fltr,
+			     struct virtchnl_proto_hdrs *proto_hdrs)
+{
+	struct virtchnl_proto_hdr *uhdr =
+				&proto_hdrs->proto_hdr[proto_hdrs->count - 1];
+	struct virtchnl_proto_hdr *hdr =
+				&proto_hdrs->proto_hdr[proto_hdrs->count++];
+	u16 adj_offs, hdr_offs;
+	u32 spi = 0;
+	int i;
+
+	VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, ESP);
+
+	adj_offs = iecm_pkt_udp_no_pay_len(fltr);
+
+	for (i = 0; i < fltr->flex_cnt; i++) {
+#define IECM_NAT_T_ESP_SPI_OFFS0	0
+#define IECM_NAT_T_ESP_SPI_OFFS1	2
+		if (fltr->flex_words[i].offset < adj_offs)
+			return -EINVAL;
+
+		hdr_offs = fltr->flex_words[i].offset - adj_offs;
+
+		switch (hdr_offs) {
+		case IECM_NAT_T_ESP_SPI_OFFS0:
+			spi |= fltr->flex_words[i].word << 16;
+			break;
+		case IECM_NAT_T_ESP_SPI_OFFS1:
+			spi |= fltr->flex_words[i].word;
+			break;
+		default:
+			return -EINVAL;
+		}
+	}
+
+	/* Not support IKE Header Format with SPI 0 */
+	if (!spi)
+		return -EOPNOTSUPP;
+
+	*(__force __be32 *)hdr->buffer = htonl(spi);
+	VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, ESP, SPI);
+
+	/* The PF ignores the UDP header fields */
+	uhdr->field_selector = 0;
+
+	return 0;
+}
+
+/**
+ * iecm_fill_fdir_udp_flex_pay_hdr - fill the UDP payload header
+ * @fltr: Flow Director filter data structure
+ * @proto_hdrs: Flow Director protocol headers data structure
+ *
+ * Returns 0 if the UDP payload defined protocol header is set successfully
+ */
+static int
+iecm_fill_fdir_udp_flex_pay_hdr(struct iecm_fdir_fltr *fltr,
+				struct virtchnl_proto_hdrs *proto_hdrs)
+{
+#define IECM_GTPU_PORT		2152
+#define IECM_NAT_T_ESP_PORT	4500
+#define IECM_PFCP_PORT		8805
+	int err;
+
+	switch (ntohs(fltr->ip_data.dst_port)) {
+	case IECM_GTPU_PORT:
+		err = iecm_fill_fdir_gtpu_hdr(fltr, proto_hdrs);
+		break;
+	case IECM_NAT_T_ESP_PORT:
+		err = iecm_fill_fdir_nat_t_esp_hdr(fltr, proto_hdrs);
+		break;
+	case IECM_PFCP_PORT:
+		err = iecm_fill_fdir_pfcp_hdr(fltr, proto_hdrs);
+		break;
+	default:
+		err = -EOPNOTSUPP;
+		break;
+	}
+
+	return err;
+}
+
+/**
+ * iecm_fill_fdir_ip4_hdr - fill the IPv4 protocol header
+ * @fltr: Flow Director filter data structure
+ * @proto_hdrs: Flow Director protocol headers data structure
+ *
+ * Returns 0 if the IPv4 protocol header is set successfully
+ */
+static int iecm_fill_fdir_ip4_hdr(struct iecm_fdir_fltr *fltr,
+				  struct virtchnl_proto_hdrs *proto_hdrs)
+{
+	struct virtchnl_proto_hdr *hdr =
+				&proto_hdrs->proto_hdr[proto_hdrs->count++];
+	struct iphdr *iph = (struct iphdr *)hdr->buffer;
+
+	VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, IPV4);
+
+	if (fltr->ip_mask.tos == U8_MAX) {
+		iph->tos = fltr->ip_data.tos;
+		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, IPV4, DSCP);
+	}
+
+	if (fltr->ip_mask.proto == U8_MAX) {
+		iph->protocol = fltr->ip_data.proto;
+		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, IPV4, PROT);
+	}
+
+	if (fltr->ip_mask.v4_addrs.src_ip == htonl(U32_MAX)) {
+		iph->saddr = fltr->ip_data.v4_addrs.src_ip;
+		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, IPV4, SRC);
+	}
+
+	if (fltr->ip_mask.v4_addrs.dst_ip == htonl(U32_MAX)) {
+		iph->daddr = fltr->ip_data.v4_addrs.dst_ip;
+		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, IPV4, DST);
+	}
+
+	fltr->ip_ver = 4;
+
+	return 0;
+}
+
+/**
+ * iecm_fill_fdir_ip6_hdr - fill the IPv6 protocol header
+ * @fltr: Flow Director filter data structure
+ * @proto_hdrs: Flow Director protocol headers data structure
+ *
+ * Returns 0 if the IPv6 protocol header is set successfully
+ */
+static int
+iecm_fill_fdir_ip6_hdr(struct iecm_fdir_fltr *fltr,
+		       struct virtchnl_proto_hdrs *proto_hdrs)
+{
+	static const struct in6_addr ipv6_addr_full_mask = {
+		.in6_u = {
+			.u6_addr8 = {
+				0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+				0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+			}
+		}
+	};
+	struct virtchnl_proto_hdr *hdr =
+				&proto_hdrs->proto_hdr[proto_hdrs->count++];
+	struct ipv6hdr *iph = (struct ipv6hdr *)hdr->buffer;
+
+	VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, IPV6);
+
+	if (fltr->ip_mask.tclass == U8_MAX) {
+		iph->priority = (fltr->ip_data.tclass >> 4) & 0xF;
+		iph->flow_lbl[0] = (fltr->ip_data.tclass << 4) & 0xF0;
+		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, IPV6, TC);
+	}
+
+	if (fltr->ip_mask.proto == U8_MAX) {
+		iph->nexthdr = fltr->ip_data.proto;
+		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, IPV6, PROT);
+	}
+
+	if (!memcmp(&fltr->ip_mask.v6_addrs.src_ip, &ipv6_addr_full_mask,
+		    sizeof(struct in6_addr))) {
+		memcpy(&iph->saddr, &fltr->ip_data.v6_addrs.src_ip,
+		       sizeof(struct in6_addr));
+		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, IPV6, SRC);
+	}
+
+	if (!memcmp(&fltr->ip_mask.v6_addrs.dst_ip, &ipv6_addr_full_mask,
+		    sizeof(struct in6_addr))) {
+		memcpy(&iph->daddr, &fltr->ip_data.v6_addrs.dst_ip,
+		       sizeof(struct in6_addr));
+		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, IPV6, DST);
+	}
+
+	fltr->ip_ver = 6;
+
+	return 0;
+}
+
+/**
+ * iecm_fill_fdir_tcp_hdr - fill the TCP protocol header
+ * @fltr: Flow Director filter data structure
+ * @proto_hdrs: Flow Director protocol headers data structure
+ *
+ * Returns 0 if the TCP protocol header is set successfully
+ */
+static int
+iecm_fill_fdir_tcp_hdr(struct iecm_fdir_fltr *fltr,
+		       struct virtchnl_proto_hdrs *proto_hdrs)
+{
+	struct virtchnl_proto_hdr *hdr =
+				&proto_hdrs->proto_hdr[proto_hdrs->count++];
+	struct tcphdr *tcph = (struct tcphdr *)hdr->buffer;
+
+	VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, TCP);
+
+	if (fltr->ip_mask.src_port == htons(U16_MAX)) {
+		tcph->source = fltr->ip_data.src_port;
+		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, TCP, SRC_PORT);
+	}
+
+	if (fltr->ip_mask.dst_port == htons(U16_MAX)) {
+		tcph->dest = fltr->ip_data.dst_port;
+		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, TCP, DST_PORT);
+	}
+
+	return 0;
+}
+
+/**
+ * iecm_fill_fdir_udp_hdr - fill the UDP protocol header
+ * @fltr: Flow Director filter data structure
+ * @proto_hdrs: Flow Director protocol headers data structure
+ *
+ * Returns 0 if the UDP protocol header is set successfully
+ */
+static int
+iecm_fill_fdir_udp_hdr(struct iecm_fdir_fltr *fltr,
+		       struct virtchnl_proto_hdrs *proto_hdrs)
+{
+	struct virtchnl_proto_hdr *hdr =
+				&proto_hdrs->proto_hdr[proto_hdrs->count++];
+	struct udphdr *udph = (struct udphdr *)hdr->buffer;
+
+	VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, UDP);
+
+	if (fltr->ip_mask.src_port == htons(U16_MAX)) {
+		udph->source = fltr->ip_data.src_port;
+		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, UDP, SRC_PORT);
+	}
+
+	if (fltr->ip_mask.dst_port == htons(U16_MAX)) {
+		udph->dest = fltr->ip_data.dst_port;
+		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, UDP, DST_PORT);
+	}
+
+	if (!fltr->flex_cnt)
+		return 0;
+
+	return iecm_fill_fdir_udp_flex_pay_hdr(fltr, proto_hdrs);
+}
+
+/**
+ * iecm_fill_fdir_sctp_hdr - fill the SCTP protocol header
+ * @fltr: Flow Director filter data structure
+ * @proto_hdrs: Flow Director protocol headers data structure
+ *
+ * Returns 0 if the SCTP protocol header is set successfully
+ */
+static int
+iecm_fill_fdir_sctp_hdr(struct iecm_fdir_fltr *fltr,
+			struct virtchnl_proto_hdrs *proto_hdrs)
+{
+	struct virtchnl_proto_hdr *hdr =
+				&proto_hdrs->proto_hdr[proto_hdrs->count++];
+	struct sctphdr *sctph = (struct sctphdr *)hdr->buffer;
+
+	VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, SCTP);
+
+	if (fltr->ip_mask.src_port == htons(U16_MAX)) {
+		sctph->source = fltr->ip_data.src_port;
+		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, SCTP, SRC_PORT);
+	}
+
+	if (fltr->ip_mask.dst_port == htons(U16_MAX)) {
+		sctph->dest = fltr->ip_data.dst_port;
+		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, SCTP, DST_PORT);
+	}
+
+	return 0;
+}
+
+/**
+ * iecm_fill_fdir_ah_hdr - fill the AH protocol header
+ * @fltr: Flow Director filter data structure
+ * @proto_hdrs: Flow Director protocol headers data structure
+ *
+ * Returns 0 if the AH protocol header is set successfully
+ */
+static int
+iecm_fill_fdir_ah_hdr(struct iecm_fdir_fltr *fltr,
+		      struct virtchnl_proto_hdrs *proto_hdrs)
+{
+	struct virtchnl_proto_hdr *hdr =
+				&proto_hdrs->proto_hdr[proto_hdrs->count++];
+	struct ip_auth_hdr *ah = (struct ip_auth_hdr *)hdr->buffer;
+
+	VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, AH);
+
+	if (fltr->ip_mask.spi == htonl(U32_MAX)) {
+		ah->spi = fltr->ip_data.spi;
+		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, AH, SPI);
+	}
+
+	return 0;
+}
+
+/**
+ * iecm_fill_fdir_esp_hdr - fill the ESP protocol header
+ * @fltr: Flow Director filter data structure
+ * @proto_hdrs: Flow Director protocol headers data structure
+ *
+ * Returns 0 if the ESP protocol header is set successfully
+ */
+static int
+iecm_fill_fdir_esp_hdr(struct iecm_fdir_fltr *fltr,
+		       struct virtchnl_proto_hdrs *proto_hdrs)
+{
+	struct virtchnl_proto_hdr *hdr =
+				&proto_hdrs->proto_hdr[proto_hdrs->count++];
+	struct ip_esp_hdr *esph = (struct ip_esp_hdr *)hdr->buffer;
+
+	VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, ESP);
+
+	if (fltr->ip_mask.spi == htonl(U32_MAX)) {
+		esph->spi = fltr->ip_data.spi;
+		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, ESP, SPI);
+	}
+
+	return 0;
+}
+
+/**
+ * iecm_fill_fdir_l4_hdr - fill the L4 protocol header
+ * @fltr: Flow Director filter data structure
+ * @proto_hdrs: Flow Director protocol headers data structure
+ *
+ * Returns 0 if the L4 protocol header is set successfully
+ */
+static int
+iecm_fill_fdir_l4_hdr(struct iecm_fdir_fltr *fltr,
+		      struct virtchnl_proto_hdrs *proto_hdrs)
+{
+	struct virtchnl_proto_hdr *hdr;
+	__be32 *l4_4_data;
+
+	if (!fltr->ip_mask.proto) /* IPv4/IPv6 header only */
+		return 0;
+
+	hdr = &proto_hdrs->proto_hdr[proto_hdrs->count++];
+	l4_4_data = (__force __be32 *)hdr->buffer;
+
+	/* L2TPv3 over IP with 'Session ID' */
+	if (fltr->ip_data.proto == IPPROTO_L2TP &&
+	    fltr->ip_mask.l4_header == htonl(U32_MAX)) {
+		VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, L2TPV3);
+		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, L2TPV3, SESS_ID);
+
+		*l4_4_data = fltr->ip_data.l4_header;
+	} else {
+		return -EOPNOTSUPP;
+	}
+
+	return 0;
+}
+
+/**
+ * iecm_fill_fdir_eth_hdr - fill the Ethernet protocol header
+ * @fltr: Flow Director filter data structure
+ * @proto_hdrs: Flow Director protocol headers data structure
+ *
+ * Returns 0 if the Ethernet protocol header is set successfully
+ */
+static int
+iecm_fill_fdir_eth_hdr(struct iecm_fdir_fltr *fltr,
+		       struct virtchnl_proto_hdrs *proto_hdrs)
+{
+	struct virtchnl_proto_hdr *hdr =
+				&proto_hdrs->proto_hdr[proto_hdrs->count++];
+	struct ethhdr *ehdr = (struct ethhdr *)hdr->buffer;
+
+	VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, ETH);
+
+	if (fltr->eth_mask.etype == htons(U16_MAX)) {
+		if (fltr->eth_data.etype == htons(ETH_P_IP) ||
+		    fltr->eth_data.etype == htons(ETH_P_IPV6))
+			return -EOPNOTSUPP;
+
+		ehdr->h_proto = fltr->eth_data.etype;
+		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, ETH, ETHERTYPE);
+	}
+
+	return 0;
+}
+
+/**
+ * iecm_fill_fdir_add_msg - fill the Flow Director filter into virtchnl message
+ * @vport: vport structure
+ * @fltr: Flow Director filter data structure
+ *
+ * Returns 0 if the add Flow Director virtchnl message is filled successfully
+ */
+static int
+iecm_fill_fdir_add_msg(struct iecm_vport *vport, struct iecm_fdir_fltr *fltr)
+{
+	struct virtchnl_fdir_add *vc_msg = &fltr->vc_add_msg;
+	struct virtchnl_proto_hdrs *proto_hdrs;
+	int err;
+
+	proto_hdrs = &vc_msg->rule_cfg.proto_hdrs;
+
+	err = iecm_fill_fdir_eth_hdr(fltr, proto_hdrs); /* L2 always exists */
+	if (err)
+		return err;
+
+	switch (fltr->flow_type) {
+	case IECM_FDIR_FLOW_IPV4_TCP:
+		err = iecm_fill_fdir_ip4_hdr(fltr, proto_hdrs) |
+		      iecm_fill_fdir_tcp_hdr(fltr, proto_hdrs);
+		break;
+	case IECM_FDIR_FLOW_IPV4_UDP:
+		err = iecm_fill_fdir_ip4_hdr(fltr, proto_hdrs) |
+		      iecm_fill_fdir_udp_hdr(fltr, proto_hdrs);
+		break;
+	case IECM_FDIR_FLOW_IPV4_SCTP:
+		err = iecm_fill_fdir_ip4_hdr(fltr, proto_hdrs) |
+		      iecm_fill_fdir_sctp_hdr(fltr, proto_hdrs);
+		break;
+	case IECM_FDIR_FLOW_IPV4_AH:
+		err = iecm_fill_fdir_ip4_hdr(fltr, proto_hdrs) |
+		      iecm_fill_fdir_ah_hdr(fltr, proto_hdrs);
+		break;
+	case IECM_FDIR_FLOW_IPV4_ESP:
+		err = iecm_fill_fdir_ip4_hdr(fltr, proto_hdrs) |
+		      iecm_fill_fdir_esp_hdr(fltr, proto_hdrs);
+		break;
+	case IECM_FDIR_FLOW_IPV4_OTHER:
+		err = iecm_fill_fdir_ip4_hdr(fltr, proto_hdrs) |
+		      iecm_fill_fdir_l4_hdr(fltr, proto_hdrs);
+		break;
+	case IECM_FDIR_FLOW_IPV6_TCP:
+		err = iecm_fill_fdir_ip6_hdr(fltr, proto_hdrs) |
+		      iecm_fill_fdir_tcp_hdr(fltr, proto_hdrs);
+		break;
+	case IECM_FDIR_FLOW_IPV6_UDP:
+		err = iecm_fill_fdir_ip6_hdr(fltr, proto_hdrs) |
+		      iecm_fill_fdir_udp_hdr(fltr, proto_hdrs);
+		break;
+	case IECM_FDIR_FLOW_IPV6_SCTP:
+		err = iecm_fill_fdir_ip6_hdr(fltr, proto_hdrs) |
+		      iecm_fill_fdir_sctp_hdr(fltr, proto_hdrs);
+		break;
+	case IECM_FDIR_FLOW_IPV6_AH:
+		err = iecm_fill_fdir_ip6_hdr(fltr, proto_hdrs) |
+		      iecm_fill_fdir_ah_hdr(fltr, proto_hdrs);
+		break;
+	case IECM_FDIR_FLOW_IPV6_ESP:
+		err = iecm_fill_fdir_ip6_hdr(fltr, proto_hdrs) |
+		      iecm_fill_fdir_esp_hdr(fltr, proto_hdrs);
+		break;
+	case IECM_FDIR_FLOW_IPV6_OTHER:
+		err = iecm_fill_fdir_ip6_hdr(fltr, proto_hdrs) |
+		      iecm_fill_fdir_l4_hdr(fltr, proto_hdrs);
+		break;
+	case IECM_FDIR_FLOW_NON_IP_L2:
+		break;
+	default:
+		err = -EINVAL;
+		break;
+	}
+
+	if (err)
+		return err;
+
+	vc_msg->vsi_id = vport->vport_id;
+	vc_msg->rule_cfg.action_set.count = 1;
+	vc_msg->rule_cfg.action_set.actions[0].type = fltr->action;
+	vc_msg->rule_cfg.action_set.actions[0].act_conf.queue.index =
+								fltr->q_index;
+
+	return 0;
+}
+
+/**
+ * iecm_fdir_flow_proto_name - get the flow protocol name
+ * @flow_type: Flow Director filter flow type
+ **/
+static const char *
+iecm_fdir_flow_proto_name(enum iecm_fdir_flow_type flow_type)
+{
+	switch (flow_type) {
+	case IECM_FDIR_FLOW_IPV4_TCP:
+	case IECM_FDIR_FLOW_IPV6_TCP:
+		return "TCP";
+	case IECM_FDIR_FLOW_IPV4_UDP:
+	case IECM_FDIR_FLOW_IPV6_UDP:
+		return "UDP";
+	case IECM_FDIR_FLOW_IPV4_SCTP:
+	case IECM_FDIR_FLOW_IPV6_SCTP:
+		return "SCTP";
+	case IECM_FDIR_FLOW_IPV4_AH:
+	case IECM_FDIR_FLOW_IPV6_AH:
+		return "AH";
+	case IECM_FDIR_FLOW_IPV4_ESP:
+	case IECM_FDIR_FLOW_IPV6_ESP:
+		return "ESP";
+	case IECM_FDIR_FLOW_IPV4_OTHER:
+	case IECM_FDIR_FLOW_IPV6_OTHER:
+		return "Other";
+	case IECM_FDIR_FLOW_NON_IP_L2:
+		return "Ethernet";
+	default:
+		return NULL;
+	}
+}
+
+/**
+ * iecm_dump_fdir_fltr
+ * @vport: vport structure
+ * @fltr: Flow Director filter to print
+ *
+ * Print the Flow Director filter
+ **/
+static void
+iecm_dump_fdir_fltr(struct iecm_vport *vport, struct iecm_fdir_fltr *fltr)
+{
+	const char *proto = iecm_fdir_flow_proto_name(fltr->flow_type);
+	struct device *dev = &vport->adapter->pdev->dev;
+
+	if (!proto)
+		return;
+
+	switch (fltr->flow_type) {
+	case IECM_FDIR_FLOW_IPV4_TCP:
+	case IECM_FDIR_FLOW_IPV4_UDP:
+	case IECM_FDIR_FLOW_IPV4_SCTP:
+		dev_info(dev, "Rule ID: %u dst_ip: %pI4 src_ip %pI4 %s: dst_port %hu src_port %hu\n",
+			 fltr->loc,
+			 &fltr->ip_data.v4_addrs.dst_ip,
+			 &fltr->ip_data.v4_addrs.src_ip,
+			 proto,
+			 ntohs(fltr->ip_data.dst_port),
+			 ntohs(fltr->ip_data.src_port));
+		break;
+	case IECM_FDIR_FLOW_IPV4_AH:
+	case IECM_FDIR_FLOW_IPV4_ESP:
+		dev_info(dev, "Rule ID: %u dst_ip: %pI4 src_ip %pI4 %s: SPI %u\n",
+			 fltr->loc,
+			 &fltr->ip_data.v4_addrs.dst_ip,
+			 &fltr->ip_data.v4_addrs.src_ip,
+			 proto,
+			 ntohl(fltr->ip_data.spi));
+		break;
+	case IECM_FDIR_FLOW_IPV4_OTHER:
+		dev_info(dev, "Rule ID: %u dst_ip: %pI4 src_ip %pI4 proto: %u L4_bytes: 0x%x\n",
+			 fltr->loc,
+			 &fltr->ip_data.v4_addrs.dst_ip,
+			 &fltr->ip_data.v4_addrs.src_ip,
+			 fltr->ip_data.proto,
+			 ntohl(fltr->ip_data.l4_header));
+		break;
+	case IECM_FDIR_FLOW_IPV6_TCP:
+	case IECM_FDIR_FLOW_IPV6_UDP:
+	case IECM_FDIR_FLOW_IPV6_SCTP:
+		dev_info(dev, "Rule ID: %u dst_ip: %pI6 src_ip %pI6 %s: dst_port %hu src_port %hu\n",
+			 fltr->loc,
+			 &fltr->ip_data.v6_addrs.dst_ip,
+			 &fltr->ip_data.v6_addrs.src_ip,
+			 proto,
+			 ntohs(fltr->ip_data.dst_port),
+			 ntohs(fltr->ip_data.src_port));
+		break;
+	case IECM_FDIR_FLOW_IPV6_AH:
+	case IECM_FDIR_FLOW_IPV6_ESP:
+		dev_info(dev, "Rule ID: %u dst_ip: %pI6 src_ip %pI6 %s: SPI %u\n",
+			 fltr->loc,
+			 &fltr->ip_data.v6_addrs.dst_ip,
+			 &fltr->ip_data.v6_addrs.src_ip,
+			 proto,
+			 ntohl(fltr->ip_data.spi));
+		break;
+	case IECM_FDIR_FLOW_IPV6_OTHER:
+		dev_info(dev, "Rule ID: %u dst_ip: %pI6 src_ip %pI6 proto: %u L4_bytes: 0x%x\n",
+			 fltr->loc,
+			 &fltr->ip_data.v6_addrs.dst_ip,
+			 &fltr->ip_data.v6_addrs.src_ip,
+			 fltr->ip_data.proto,
+			 ntohl(fltr->ip_data.l4_header));
+		break;
+	case IECM_FDIR_FLOW_NON_IP_L2:
+		dev_info(dev, "Rule ID: %u eth_type: 0x%x\n",
+			 fltr->loc,
+			 ntohs(fltr->eth_data.etype));
+		break;
+	default:
+		break;
+	}
+}
+
+/**
+ * iecm_fdir_is_dup_fltr - test if filter is already in list
+ * @adapter: board private structure
+ * @fltr: Flow Director filter data structure
+ *
+ * Returns true if the filter is found in the list
+ */
+static bool
+iecm_fdir_is_dup_fltr(struct iecm_adapter *adapter,
+		      struct iecm_fdir_fltr *fltr)
+{
+	struct iecm_fdir_fltr_config *fdir_config;
+	struct iecm_fdir_fltr *tmp;
+
+	fdir_config = &adapter->config_data.fdir_config;
+	list_for_each_entry(tmp, &fdir_config->fdir_fltr_list, list) {
+		if (tmp->flow_type != fltr->flow_type)
+			continue;
+
+		if (!memcmp(&tmp->eth_data, &fltr->eth_data,
+			    sizeof(fltr->eth_data)) &&
+		    !memcmp(&tmp->ip_data, &fltr->ip_data,
+			    sizeof(fltr->ip_data)) &&
+		    !memcmp(&tmp->ext_data, &fltr->ext_data,
+			    sizeof(fltr->ext_data)))
+			return true;
+	}
+
+	return false;
+}
+
+/**
+ * iecm_find_fdir_fltr_by_loc - find filter with location
+ * @adapter: board private structure
+ * @loc: location to find.
+ *
+ * Returns pointer to Flow Director filter if found or null
+ */
+static struct iecm_fdir_fltr *
+iecm_find_fdir_fltr_by_loc(struct iecm_adapter *adapter, u32 loc)
+{
+	struct iecm_fdir_fltr_config *fdir_config;
+	struct iecm_fdir_fltr *rule;
+
+	fdir_config = &adapter->config_data.fdir_config;
+	list_for_each_entry(rule, &fdir_config->fdir_fltr_list, list)
+		if (rule->loc == loc)
+			return rule;
+
+	return NULL;
+}
+
+/**
+ * iecm_fdir_list_add_fltr - add a new node to the flow director filter list
+ * @adapter: board private structure
+ * @fltr: filter node to add to structure
+ */
+static void
+iecm_fdir_list_add_fltr(struct iecm_adapter *adapter,
+			struct iecm_fdir_fltr *fltr)
+{
+	struct iecm_fdir_fltr *rule, *parent = NULL;
+	struct iecm_fdir_fltr_config *fdir_config;
+
+	fdir_config = &adapter->config_data.fdir_config;
+
+	spin_lock_bh(&adapter->fdir_fltr_list_lock);
+	list_for_each_entry(rule, &fdir_config->fdir_fltr_list, list) {
+		if (rule->loc >= fltr->loc)
+			break;
+		parent = rule;
+	}
+
+	if (parent)
+		list_add(&fltr->list, &parent->list);
+	else
+		list_add(&fltr->list, &fdir_config->fdir_fltr_list);
+	fltr->add = true;
+	spin_unlock_bh(&adapter->fdir_fltr_list_lock);
+}
+
+/**
+ * iecm_fltr_to_ethtool_flow - convert filter type values to ethtool
+ * flow type values
+ * @flow: filter type to be converted
+ *
+ * Returns the corresponding ethtool flow type.
+ */
+static int iecm_fltr_to_ethtool_flow(enum iecm_fdir_flow_type flow)
+{
+	switch (flow) {
+	case IECM_FDIR_FLOW_IPV4_TCP:
+		return TCP_V4_FLOW;
+	case IECM_FDIR_FLOW_IPV4_UDP:
+		return UDP_V4_FLOW;
+	case IECM_FDIR_FLOW_IPV4_SCTP:
+		return SCTP_V4_FLOW;
+	case IECM_FDIR_FLOW_IPV4_AH:
+		return AH_V4_FLOW;
+	case IECM_FDIR_FLOW_IPV4_ESP:
+		return ESP_V4_FLOW;
+	case IECM_FDIR_FLOW_IPV4_OTHER:
+		return IPV4_USER_FLOW;
+	case IECM_FDIR_FLOW_IPV6_TCP:
+		return TCP_V6_FLOW;
+	case IECM_FDIR_FLOW_IPV6_UDP:
+		return UDP_V6_FLOW;
+	case IECM_FDIR_FLOW_IPV6_SCTP:
+		return SCTP_V6_FLOW;
+	case IECM_FDIR_FLOW_IPV6_AH:
+		return AH_V6_FLOW;
+	case IECM_FDIR_FLOW_IPV6_ESP:
+		return ESP_V6_FLOW;
+	case IECM_FDIR_FLOW_IPV6_OTHER:
+		return IPV6_USER_FLOW;
+	case IECM_FDIR_FLOW_NON_IP_L2:
+		return ETHER_FLOW;
+	default:
+		/* 0 is undefined ethtool flow */
+		return 0;
+	}
+}
+
+/**
+ * iecm_ethtool_flow_to_fltr - convert ethtool flow type to filter enum
+ * @eth: Ethtool flow type to be converted
+ *
+ * Returns flow enum
+ */
+static enum iecm_fdir_flow_type iecm_ethtool_flow_to_fltr(int eth)
+{
+	switch (eth) {
+	case TCP_V4_FLOW:
+		return IECM_FDIR_FLOW_IPV4_TCP;
+	case UDP_V4_FLOW:
+		return IECM_FDIR_FLOW_IPV4_UDP;
+	case SCTP_V4_FLOW:
+		return IECM_FDIR_FLOW_IPV4_SCTP;
+	case AH_V4_FLOW:
+		return IECM_FDIR_FLOW_IPV4_AH;
+	case ESP_V4_FLOW:
+		return IECM_FDIR_FLOW_IPV4_ESP;
+	case IPV4_USER_FLOW:
+		return IECM_FDIR_FLOW_IPV4_OTHER;
+	case TCP_V6_FLOW:
+		return IECM_FDIR_FLOW_IPV6_TCP;
+	case UDP_V6_FLOW:
+		return IECM_FDIR_FLOW_IPV6_UDP;
+	case SCTP_V6_FLOW:
+		return IECM_FDIR_FLOW_IPV6_SCTP;
+	case AH_V6_FLOW:
+		return IECM_FDIR_FLOW_IPV6_AH;
+	case ESP_V6_FLOW:
+		return IECM_FDIR_FLOW_IPV6_ESP;
+	case IPV6_USER_FLOW:
+		return IECM_FDIR_FLOW_IPV6_OTHER;
+	case ETHER_FLOW:
+		return IECM_FDIR_FLOW_NON_IP_L2;
+	default:
+		return IECM_FDIR_FLOW_NONE;
+	}
+}
+
+/**
+ * iecm_is_mask_valid - check mask field set
+ * @mask: full mask to check
+ * @field: field for which mask should be valid
+ *
+ * If the mask is fully set return true. If it is not valid for field return
+ * false.
+ */
+static bool iecm_is_mask_valid(u64 mask, u64 field)
+{
+	return (mask & field) == field;
+}
+
+/**
+ * iecm_parse_rx_flow_user_data - deconstruct user-defined data
+ * @fsp: pointer to ethtool Rx flow specification
+ * @fltr: pointer to Flow Director filter for userdef data storage
+ *
+ * Returns 0 on success, negative error value on failure
+ */
+static int
+iecm_parse_rx_flow_user_data(struct ethtool_rx_flow_spec *fsp,
+			     struct iecm_fdir_fltr *fltr)
+{
+	struct iecm_flex_word *flex;
+	int i, cnt = 0;
+
+	if (!(fsp->flow_type & FLOW_EXT))
+		return 0;
+
+	for (i = 0; i < IECM_FLEX_WORD_NUM; i++) {
+#define IECM_USERDEF_FLEX_WORD_M	GENMASK(15, 0)
+#define IECM_USERDEF_FLEX_OFFS_S	16
+#define IECM_USERDEF_FLEX_OFFS_M	GENMASK(31, IECM_USERDEF_FLEX_OFFS_S)
+#define IECM_USERDEF_FLEX_FLTR_M	GENMASK(31, 0)
+		u32 value = be32_to_cpu(fsp->h_ext.data[i]);
+		u32 mask = be32_to_cpu(fsp->m_ext.data[i]);
+
+		if (!value || !mask)
+			continue;
+
+		if (!iecm_is_mask_valid(mask, IECM_USERDEF_FLEX_FLTR_M))
+			return -EINVAL;
+
+		/* 504 is the maximum value for offsets, and offset is measured
+		 * from the start of the MAC address.
+		 */
+#define IECM_USERDEF_FLEX_MAX_OFFS_VAL 504
+		flex = &fltr->flex_words[cnt++];
+		flex->word = value & IECM_USERDEF_FLEX_WORD_M;
+		flex->offset = (value & IECM_USERDEF_FLEX_OFFS_M) >>
+			     IECM_USERDEF_FLEX_OFFS_S;
+		if (flex->offset > IECM_USERDEF_FLEX_MAX_OFFS_VAL)
+			return -EINVAL;
+	}
+
+	fltr->flex_cnt = cnt;
+
+	return 0;
+}
+
+/**
+ * iecm_fill_rx_flow_ext_data - fill the additional data
+ * @fsp: pointer to ethtool Rx flow specification
+ * @fltr: pointer to Flow Director filter to get additional data
+ */
+static void
+iecm_fill_rx_flow_ext_data(struct ethtool_rx_flow_spec *fsp,
+			   struct iecm_fdir_fltr *fltr)
+{
+	if (!fltr->ext_mask.usr_def[0] && !fltr->ext_mask.usr_def[1])
+		return;
+
+	fsp->flow_type |= FLOW_EXT;
+
+	memcpy(fsp->h_ext.data, fltr->ext_data.usr_def,
+	       sizeof(fsp->h_ext.data));
+	memcpy(fsp->m_ext.data, fltr->ext_mask.usr_def,
+	       sizeof(fsp->m_ext.data));
+}
+
+/**
+ * iecm_get_fdir_fltr_entry - fill ethtool structure with Flow Director
+ * filter data
+ * @vport: vport structure
+ * @cmd: ethtool command data structure to receive the filter data
+ *
+ * Returns 0 as expected for success by ethtool
+ */
+int
+iecm_get_fdir_fltr_entry(struct iecm_vport *vport, struct ethtool_rxnfc *cmd)
+{
+	struct ethtool_rx_flow_spec *fsp =
+					(struct ethtool_rx_flow_spec *)&cmd->fs;
+	struct iecm_adapter *adapter = vport->adapter;
+	struct iecm_fdir_fltr *rule;
+	int ret = 0;
+
+	if (adapter->state != __IECM_UP)
+		return -EIO;
+
+	if (!iecm_is_cap_ena(adapter, IECM_OTHER_CAPS, VIRTCHNL2_CAP_FDIR))
+		return -EOPNOTSUPP;
+
+	spin_lock_bh(&adapter->fdir_fltr_list_lock);
+
+	rule = iecm_find_fdir_fltr_by_loc(adapter, fsp->location);
+	if (!rule) {
+		ret = -EINVAL;
+		goto release_lock;
+	}
+
+	fsp->flow_type = iecm_fltr_to_ethtool_flow(rule->flow_type);
+
+	memset(&fsp->m_u, 0, sizeof(fsp->m_u));
+	memset(&fsp->m_ext, 0, sizeof(fsp->m_ext));
+
+	switch (fsp->flow_type) {
+	case TCP_V4_FLOW:
+	case UDP_V4_FLOW:
+	case SCTP_V4_FLOW:
+		fsp->h_u.tcp_ip4_spec.ip4src = rule->ip_data.v4_addrs.src_ip;
+		fsp->h_u.tcp_ip4_spec.ip4dst = rule->ip_data.v4_addrs.dst_ip;
+		fsp->h_u.tcp_ip4_spec.psrc = rule->ip_data.src_port;
+		fsp->h_u.tcp_ip4_spec.pdst = rule->ip_data.dst_port;
+		fsp->h_u.tcp_ip4_spec.tos = rule->ip_data.tos;
+		fsp->m_u.tcp_ip4_spec.ip4src = rule->ip_mask.v4_addrs.src_ip;
+		fsp->m_u.tcp_ip4_spec.ip4dst = rule->ip_mask.v4_addrs.dst_ip;
+		fsp->m_u.tcp_ip4_spec.psrc = rule->ip_mask.src_port;
+		fsp->m_u.tcp_ip4_spec.pdst = rule->ip_mask.dst_port;
+		fsp->m_u.tcp_ip4_spec.tos = rule->ip_mask.tos;
+		break;
+	case AH_V4_FLOW:
+	case ESP_V4_FLOW:
+		fsp->h_u.ah_ip4_spec.ip4src = rule->ip_data.v4_addrs.src_ip;
+		fsp->h_u.ah_ip4_spec.ip4dst = rule->ip_data.v4_addrs.dst_ip;
+		fsp->h_u.ah_ip4_spec.spi = rule->ip_data.spi;
+		fsp->h_u.ah_ip4_spec.tos = rule->ip_data.tos;
+		fsp->m_u.ah_ip4_spec.ip4src = rule->ip_mask.v4_addrs.src_ip;
+		fsp->m_u.ah_ip4_spec.ip4dst = rule->ip_mask.v4_addrs.dst_ip;
+		fsp->m_u.ah_ip4_spec.spi = rule->ip_mask.spi;
+		fsp->m_u.ah_ip4_spec.tos = rule->ip_mask.tos;
+		break;
+	case IPV4_USER_FLOW:
+		fsp->h_u.usr_ip4_spec.ip4src = rule->ip_data.v4_addrs.src_ip;
+		fsp->h_u.usr_ip4_spec.ip4dst = rule->ip_data.v4_addrs.dst_ip;
+		fsp->h_u.usr_ip4_spec.l4_4_bytes = rule->ip_data.l4_header;
+		fsp->h_u.usr_ip4_spec.tos = rule->ip_data.tos;
+		fsp->h_u.usr_ip4_spec.ip_ver = ETH_RX_NFC_IP4;
+		fsp->h_u.usr_ip4_spec.proto = rule->ip_data.proto;
+		fsp->m_u.usr_ip4_spec.ip4src = rule->ip_mask.v4_addrs.src_ip;
+		fsp->m_u.usr_ip4_spec.ip4dst = rule->ip_mask.v4_addrs.dst_ip;
+		fsp->m_u.usr_ip4_spec.l4_4_bytes = rule->ip_mask.l4_header;
+		fsp->m_u.usr_ip4_spec.tos = rule->ip_mask.tos;
+		fsp->m_u.usr_ip4_spec.ip_ver = 0xFF;
+		fsp->m_u.usr_ip4_spec.proto = rule->ip_mask.proto;
+		break;
+	case TCP_V6_FLOW:
+	case UDP_V6_FLOW:
+	case SCTP_V6_FLOW:
+		memcpy(fsp->h_u.usr_ip6_spec.ip6src,
+		       &rule->ip_data.v6_addrs.src_ip, sizeof(struct in6_addr));
+		memcpy(fsp->h_u.usr_ip6_spec.ip6dst,
+		       &rule->ip_data.v6_addrs.dst_ip, sizeof(struct in6_addr));
+		fsp->h_u.tcp_ip6_spec.psrc = rule->ip_data.src_port;
+		fsp->h_u.tcp_ip6_spec.pdst = rule->ip_data.dst_port;
+		fsp->h_u.tcp_ip6_spec.tclass = rule->ip_data.tclass;
+		memcpy(fsp->m_u.usr_ip6_spec.ip6src,
+		       &rule->ip_mask.v6_addrs.src_ip, sizeof(struct in6_addr));
+		memcpy(fsp->m_u.usr_ip6_spec.ip6dst,
+		       &rule->ip_mask.v6_addrs.dst_ip, sizeof(struct in6_addr));
+		fsp->m_u.tcp_ip6_spec.psrc = rule->ip_mask.src_port;
+		fsp->m_u.tcp_ip6_spec.pdst = rule->ip_mask.dst_port;
+		fsp->m_u.tcp_ip6_spec.tclass = rule->ip_mask.tclass;
+		break;
+	case AH_V6_FLOW:
+	case ESP_V6_FLOW:
+		memcpy(fsp->h_u.ah_ip6_spec.ip6src,
+		       &rule->ip_data.v6_addrs.src_ip, sizeof(struct in6_addr));
+		memcpy(fsp->h_u.ah_ip6_spec.ip6dst,
+		       &rule->ip_data.v6_addrs.dst_ip, sizeof(struct in6_addr));
+		fsp->h_u.ah_ip6_spec.spi = rule->ip_data.spi;
+		fsp->h_u.ah_ip6_spec.tclass = rule->ip_data.tclass;
+		memcpy(fsp->m_u.ah_ip6_spec.ip6src,
+		       &rule->ip_mask.v6_addrs.src_ip, sizeof(struct in6_addr));
+		memcpy(fsp->m_u.ah_ip6_spec.ip6dst,
+		       &rule->ip_mask.v6_addrs.dst_ip, sizeof(struct in6_addr));
+		fsp->m_u.ah_ip6_spec.spi = rule->ip_mask.spi;
+		fsp->m_u.ah_ip6_spec.tclass = rule->ip_mask.tclass;
+		break;
+	case IPV6_USER_FLOW:
+		memcpy(fsp->h_u.usr_ip6_spec.ip6src,
+		       &rule->ip_data.v6_addrs.src_ip, sizeof(struct in6_addr));
+		memcpy(fsp->h_u.usr_ip6_spec.ip6dst,
+		       &rule->ip_data.v6_addrs.dst_ip, sizeof(struct in6_addr));
+		fsp->h_u.usr_ip6_spec.l4_4_bytes = rule->ip_data.l4_header;
+		fsp->h_u.usr_ip6_spec.tclass = rule->ip_data.tclass;
+		fsp->h_u.usr_ip6_spec.l4_proto = rule->ip_data.proto;
+		memcpy(fsp->m_u.usr_ip6_spec.ip6src,
+		       &rule->ip_mask.v6_addrs.src_ip, sizeof(struct in6_addr));
+		memcpy(fsp->m_u.usr_ip6_spec.ip6dst,
+		       &rule->ip_mask.v6_addrs.dst_ip, sizeof(struct in6_addr));
+		fsp->m_u.usr_ip6_spec.l4_4_bytes = rule->ip_mask.l4_header;
+		fsp->m_u.usr_ip6_spec.tclass = rule->ip_mask.tclass;
+		fsp->m_u.usr_ip6_spec.l4_proto = rule->ip_mask.proto;
+		break;
+	case ETHER_FLOW:
+		fsp->h_u.ether_spec.h_proto = rule->eth_data.etype;
+		fsp->m_u.ether_spec.h_proto = rule->eth_mask.etype;
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	iecm_fill_rx_flow_ext_data(fsp, rule);
+
+	if (rule->action == VIRTCHNL_ACTION_DROP)
+		fsp->ring_cookie = RX_CLS_FLOW_DISC;
+	else
+		fsp->ring_cookie = rule->q_index;
+
+release_lock:
+	spin_unlock_bh(&adapter->fdir_fltr_list_lock);
+	return ret;
+}
+
+/**
+ * iecm_get_fdir_fltr_ids - fill buffer with filter IDs of active filters
+ * @vport: vport structure
+ * @cmd: ethtool command data structure
+ * @rule_locs: ethtool array passed in from OS to receive filter IDs
+ *
+ * Returns 0 as expected for success by ethtool
+ */
+int
+iecm_get_fdir_fltr_ids(struct iecm_vport *vport, struct ethtool_rxnfc *cmd,
+		       u32 *rule_locs)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	struct iecm_fdir_fltr_config *fdir_config;
+	struct iecm_fdir_fltr *fltr;
+	unsigned int cnt = 0;
+	int ret = 0;
+
+	if (adapter->state != __IECM_UP)
+		return -EIO;
+
+	if (!iecm_is_cap_ena(adapter, IECM_OTHER_CAPS, VIRTCHNL2_CAP_FDIR))
+		return -EOPNOTSUPP;
+
+	cmd->data = IECM_MAX_FDIR_FILTERS;
+
+	fdir_config = &adapter->config_data.fdir_config;
+
+	spin_lock_bh(&adapter->fdir_fltr_list_lock);
+
+	list_for_each_entry(fltr, &fdir_config->fdir_fltr_list, list) {
+		if (cnt == cmd->rule_cnt) {
+			ret = -EMSGSIZE;
+			goto release_lock;
+		}
+		rule_locs[cnt] = fltr->loc;
+		cnt++;
+	}
+
+release_lock:
+	spin_unlock_bh(&adapter->fdir_fltr_list_lock);
+	if (!ret)
+		cmd->rule_cnt = cnt;
+
+	return ret;
+}
+
+/**
+ * iecm_add_fdir_fltr_info - Set the input set for Flow Director filter
+ * @vport: vport structure
+ * @fsp: pointer to ethtool Rx flow specification
+ * @fltr: filter structure
+ */
+static int
+iecm_add_fdir_fltr_info(struct iecm_vport *vport,
+			struct ethtool_rx_flow_spec *fsp,
+			struct iecm_fdir_fltr *fltr)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	u32 flow_type, q_index = 0;
+	enum virtchnl_action act;
+	int err;
+
+	if (fsp->ring_cookie == RX_CLS_FLOW_DISC) {
+		act = VIRTCHNL_ACTION_DROP;
+	} else {
+		q_index = fsp->ring_cookie;
+		if (q_index >= vport->num_rxq)
+			return -EINVAL;
+
+		act = VIRTCHNL_ACTION_QUEUE;
+	}
+
+	fltr->action = act;
+	fltr->loc = fsp->location;
+	fltr->q_index = q_index;
+
+	if (fsp->flow_type & FLOW_EXT) {
+		memcpy(fltr->ext_data.usr_def, fsp->h_ext.data,
+		       sizeof(fltr->ext_data.usr_def));
+		memcpy(fltr->ext_mask.usr_def, fsp->m_ext.data,
+		       sizeof(fltr->ext_mask.usr_def));
+	}
+
+	flow_type = fsp->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT | FLOW_RSS);
+	fltr->flow_type = iecm_ethtool_flow_to_fltr(flow_type);
+
+	switch (flow_type) {
+	case TCP_V4_FLOW:
+	case UDP_V4_FLOW:
+	case SCTP_V4_FLOW:
+		fltr->ip_data.v4_addrs.src_ip = fsp->h_u.tcp_ip4_spec.ip4src;
+		fltr->ip_data.v4_addrs.dst_ip = fsp->h_u.tcp_ip4_spec.ip4dst;
+		fltr->ip_data.src_port = fsp->h_u.tcp_ip4_spec.psrc;
+		fltr->ip_data.dst_port = fsp->h_u.tcp_ip4_spec.pdst;
+		fltr->ip_data.tos = fsp->h_u.tcp_ip4_spec.tos;
+		fltr->ip_mask.v4_addrs.src_ip = fsp->m_u.tcp_ip4_spec.ip4src;
+		fltr->ip_mask.v4_addrs.dst_ip = fsp->m_u.tcp_ip4_spec.ip4dst;
+		fltr->ip_mask.src_port = fsp->m_u.tcp_ip4_spec.psrc;
+		fltr->ip_mask.dst_port = fsp->m_u.tcp_ip4_spec.pdst;
+		fltr->ip_mask.tos = fsp->m_u.tcp_ip4_spec.tos;
+		break;
+	case AH_V4_FLOW:
+	case ESP_V4_FLOW:
+		fltr->ip_data.v4_addrs.src_ip = fsp->h_u.ah_ip4_spec.ip4src;
+		fltr->ip_data.v4_addrs.dst_ip = fsp->h_u.ah_ip4_spec.ip4dst;
+		fltr->ip_data.spi = fsp->h_u.ah_ip4_spec.spi;
+		fltr->ip_data.tos = fsp->h_u.ah_ip4_spec.tos;
+		fltr->ip_mask.v4_addrs.src_ip = fsp->m_u.ah_ip4_spec.ip4src;
+		fltr->ip_mask.v4_addrs.dst_ip = fsp->m_u.ah_ip4_spec.ip4dst;
+		fltr->ip_mask.spi = fsp->m_u.ah_ip4_spec.spi;
+		fltr->ip_mask.tos = fsp->m_u.ah_ip4_spec.tos;
+		break;
+	case IPV4_USER_FLOW:
+		fltr->ip_data.v4_addrs.src_ip = fsp->h_u.usr_ip4_spec.ip4src;
+		fltr->ip_data.v4_addrs.dst_ip = fsp->h_u.usr_ip4_spec.ip4dst;
+		fltr->ip_data.l4_header = fsp->h_u.usr_ip4_spec.l4_4_bytes;
+		fltr->ip_data.tos = fsp->h_u.usr_ip4_spec.tos;
+		fltr->ip_data.proto = fsp->h_u.usr_ip4_spec.proto;
+		fltr->ip_mask.v4_addrs.src_ip = fsp->m_u.usr_ip4_spec.ip4src;
+		fltr->ip_mask.v4_addrs.dst_ip = fsp->m_u.usr_ip4_spec.ip4dst;
+		fltr->ip_mask.l4_header = fsp->m_u.usr_ip4_spec.l4_4_bytes;
+		fltr->ip_mask.tos = fsp->m_u.usr_ip4_spec.tos;
+		fltr->ip_mask.proto = fsp->m_u.usr_ip4_spec.proto;
+		break;
+	case TCP_V6_FLOW:
+	case UDP_V6_FLOW:
+	case SCTP_V6_FLOW:
+		memcpy(&fltr->ip_data.v6_addrs.src_ip,
+		       fsp->h_u.usr_ip6_spec.ip6src, sizeof(struct in6_addr));
+		memcpy(&fltr->ip_data.v6_addrs.dst_ip,
+		       fsp->h_u.usr_ip6_spec.ip6dst, sizeof(struct in6_addr));
+		fltr->ip_data.src_port = fsp->h_u.tcp_ip6_spec.psrc;
+		fltr->ip_data.dst_port = fsp->h_u.tcp_ip6_spec.pdst;
+		fltr->ip_data.tclass = fsp->h_u.tcp_ip6_spec.tclass;
+		memcpy(&fltr->ip_mask.v6_addrs.src_ip,
+		       fsp->m_u.usr_ip6_spec.ip6src, sizeof(struct in6_addr));
+		memcpy(&fltr->ip_mask.v6_addrs.dst_ip,
+		       fsp->m_u.usr_ip6_spec.ip6dst, sizeof(struct in6_addr));
+		fltr->ip_mask.src_port = fsp->m_u.tcp_ip6_spec.psrc;
+		fltr->ip_mask.dst_port = fsp->m_u.tcp_ip6_spec.pdst;
+		fltr->ip_mask.tclass = fsp->m_u.tcp_ip6_spec.tclass;
+		break;
+	case AH_V6_FLOW:
+	case ESP_V6_FLOW:
+		memcpy(&fltr->ip_data.v6_addrs.src_ip,
+		       fsp->h_u.ah_ip6_spec.ip6src, sizeof(struct in6_addr));
+		memcpy(&fltr->ip_data.v6_addrs.dst_ip,
+		       fsp->h_u.ah_ip6_spec.ip6dst, sizeof(struct in6_addr));
+		fltr->ip_data.spi = fsp->h_u.ah_ip6_spec.spi;
+		fltr->ip_data.tclass = fsp->h_u.ah_ip6_spec.tclass;
+		memcpy(&fltr->ip_mask.v6_addrs.src_ip,
+		       fsp->m_u.ah_ip6_spec.ip6src, sizeof(struct in6_addr));
+		memcpy(&fltr->ip_mask.v6_addrs.dst_ip,
+		       fsp->m_u.ah_ip6_spec.ip6dst, sizeof(struct in6_addr));
+		fltr->ip_mask.spi = fsp->m_u.ah_ip6_spec.spi;
+		fltr->ip_mask.tclass = fsp->m_u.ah_ip6_spec.tclass;
+		break;
+	case IPV6_USER_FLOW:
+		memcpy(&fltr->ip_data.v6_addrs.src_ip,
+		       fsp->h_u.usr_ip6_spec.ip6src, sizeof(struct in6_addr));
+		memcpy(&fltr->ip_data.v6_addrs.dst_ip,
+		       fsp->h_u.usr_ip6_spec.ip6dst, sizeof(struct in6_addr));
+		fltr->ip_data.l4_header = fsp->h_u.usr_ip6_spec.l4_4_bytes;
+		fltr->ip_data.tclass = fsp->h_u.usr_ip6_spec.tclass;
+		fltr->ip_data.proto = fsp->h_u.usr_ip6_spec.l4_proto;
+		memcpy(&fltr->ip_mask.v6_addrs.src_ip,
+		       fsp->m_u.usr_ip6_spec.ip6src, sizeof(struct in6_addr));
+		memcpy(&fltr->ip_mask.v6_addrs.dst_ip,
+		       fsp->m_u.usr_ip6_spec.ip6dst, sizeof(struct in6_addr));
+		fltr->ip_mask.l4_header = fsp->m_u.usr_ip6_spec.l4_4_bytes;
+		fltr->ip_mask.tclass = fsp->m_u.usr_ip6_spec.tclass;
+		fltr->ip_mask.proto = fsp->m_u.usr_ip6_spec.l4_proto;
+		break;
+	case ETHER_FLOW:
+		fltr->eth_data.etype = fsp->h_u.ether_spec.h_proto;
+		fltr->eth_mask.etype = fsp->m_u.ether_spec.h_proto;
+		break;
+	default:
+		/* not doing un-parsed flow types */
+		return -EINVAL;
+	}
+
+	if (iecm_fdir_is_dup_fltr(adapter, fltr))
+		return -EEXIST;
+
+	err = iecm_parse_rx_flow_user_data(fsp, fltr);
+	if (err)
+		return err;
+
+	return iecm_fill_fdir_add_msg(vport, fltr);
+}
+
+/**
+ * iecm_add_fdir_fltr - add Flow Director filter
+ * @vport: vport structure
+ * @cmd: command to add Flow Director filter
+ *
+ * Returns 0 on success and negative values for failure
+ */
+int iecm_add_fdir_fltr(struct iecm_vport *vport, struct ethtool_rxnfc *cmd)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	struct ethtool_rx_flow_spec *fsp = &cmd->fs;
+	struct iecm_fdir_fltr_config *fdir_config;
+	struct iecm_fdir_fltr *fltr;
+	int err;
+
+	if (adapter->state != __IECM_UP)
+		return -EIO;
+
+	if (!iecm_is_cap_ena(adapter, IECM_OTHER_CAPS, VIRTCHNL2_CAP_FDIR))
+		return -EOPNOTSUPP;
+
+	if (fsp->flow_type & FLOW_MAC_EXT)
+		return -EINVAL;
+
+	fdir_config = &adapter->config_data.fdir_config;
+	if (fdir_config->num_active_filters >= IECM_MAX_FDIR_FILTERS) {
+		dev_err(&adapter->pdev->dev,
+			"Unable to add Flow Director filter because vport reached the limit of max allowed filters (%u)\n",
+			IECM_MAX_FDIR_FILTERS);
+		return -ENOSPC;
+	}
+
+	spin_lock_bh(&adapter->fdir_fltr_list_lock);
+	fltr = iecm_find_fdir_fltr_by_loc(adapter, fsp->location);
+	if (fltr) {
+		fltr->remove = false;
+		dev_err(&adapter->pdev->dev, "Failed to add Flow Director filter, it already exists\n");
+		spin_unlock_bh(&adapter->fdir_fltr_list_lock);
+		return -EEXIST;
+	}
+	spin_unlock_bh(&adapter->fdir_fltr_list_lock);
+
+	fltr = kzalloc(sizeof(*fltr), GFP_KERNEL);
+	if (!fltr)
+		return -ENOMEM;
+
+	err = iecm_add_fdir_fltr_info(vport, fsp, fltr);
+	if (err)
+		goto error;
+
+	iecm_fdir_list_add_fltr(adapter, fltr);
+	err = iecm_send_add_fdir_filter_msg(vport);
+	if (!err) {
+		fdir_config->num_active_filters++;
+	} else {
+		spin_lock_bh(&adapter->fdir_fltr_list_lock);
+		list_del(&fltr->list);
+		spin_unlock_bh(&adapter->fdir_fltr_list_lock);
+	}
+
+error:
+	if (!err) {
+		dev_info(&adapter->pdev->dev, "Flow Director filter with location %u is added\n",
+			 fsp->location);
+	} else {
+		dev_info(&adapter->pdev->dev, "Failed to add Flow Director filter\n");
+		iecm_dump_fdir_fltr(vport, fltr);
+		kfree(fltr);
+	}
+
+	return err;
+}
+
+/**
+ * iecm_del_fdir_fltr - delete Flow Director filter
+ * @vport: vport structure
+ * @cmd: command to delete Flow Director filter
+ *
+ * Returns 0 on success and negative values for failure
+ */
+int iecm_del_fdir_fltr(struct iecm_vport *vport, struct ethtool_rxnfc *cmd)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	struct ethtool_rx_flow_spec *fsp = &cmd->fs;
+	struct iecm_fdir_fltr_config *fdir_config;
+	struct iecm_fdir_fltr *fltr = NULL;
+	int err;
+
+	if (adapter->state != __IECM_UP)
+		return -EIO;
+
+	if (!iecm_is_cap_ena(adapter, IECM_OTHER_CAPS, VIRTCHNL2_CAP_FDIR))
+		return -EOPNOTSUPP;
+
+	fdir_config = &adapter->config_data.fdir_config;
+	spin_lock_bh(&adapter->fdir_fltr_list_lock);
+	fltr = iecm_find_fdir_fltr_by_loc(adapter, fsp->location);
+	if (fltr) {
+		fltr->remove = true;
+		fdir_config->num_active_filters--;
+	}
+	spin_unlock_bh(&adapter->fdir_fltr_list_lock);
+
+	err = iecm_send_del_fdir_filter_msg(vport);
+	if (err)
+		dev_err(&adapter->pdev->dev, "Failed to del Flow Director filter\n");
+
+	/* If the above fails, still delete the filter from the list because
+	 * either HW thinks it doesn't exist or we have a bad filter somehow
+	 * and it doesn't do us any good to continue hanging on to it.
+	 */
+	spin_lock_bh(&adapter->fdir_fltr_list_lock);
+	fltr = iecm_find_fdir_fltr_by_loc(adapter, fsp->location);
+	/* It can happen that asynchronously the filter has already been
+	 * removed from the list, make sure it's still there under spinlock
+	 * before deleting it.
+	 */
+	if (fltr) {
+		list_del(&fltr->list);
+		kfree(fltr);
+	}
+	spin_unlock_bh(&adapter->fdir_fltr_list_lock);
+
+	return err;
+}
+
 /**
  * iecm_set_mac - NDO callback to set port mac address
  * @netdev: network interface device structure
diff --git a/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
index f2516343c199..5601846b4674 100644
--- a/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
+++ b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
@@ -2731,6 +2731,125 @@ static int iecm_send_insert_vlan_msg(struct iecm_vport *vport, bool ena)
 	return err;
 }
 
+/**
+ * iecm_send_add_fdir_filter_msg: Send add Flow Director filter message
+ * @vport: vport structure
+ *
+ * Request the CP/PF to add Flow Director as specified by the user via
+ * ethtool
+ *
+ * Return 0 on success, negative on failure
+ **/
+int iecm_send_add_fdir_filter_msg(struct iecm_vport *vport)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	struct iecm_fdir_fltr_config *fdir_config;
+	struct iecm_fdir_fltr *fdir;
+	struct virtchnl_fdir_add *f;
+	int len, err = 0;
+
+	fdir_config = &adapter->config_data.fdir_config;
+	len = sizeof(struct virtchnl_fdir_add);
+	/* kzalloc required because otherwise stack is over 2k */
+	f = kzalloc(len, GFP_KERNEL);
+	if (!f)
+		return -ENOMEM;
+
+	while (true) {
+		bool process_fltr = false;
+
+		/* Only add a single Flow Director per call */
+		spin_lock_bh(&adapter->fdir_fltr_list_lock);
+		list_for_each_entry(fdir, &fdir_config->fdir_fltr_list, list) {
+			if (fdir->add) {
+				fdir->add = false;
+				process_fltr = true;
+				memcpy(f, &fdir->vc_add_msg, len);
+				break;
+			}
+		}
+		spin_unlock_bh(&adapter->fdir_fltr_list_lock);
+
+		if (!process_fltr)
+			break;
+
+		err = iecm_send_mb_msg(adapter, VIRTCHNL_OP_ADD_FDIR_FILTER,
+				       len, (u8 *)f);
+		if (err)
+			break;
+
+		err = iecm_wait_for_event(adapter, IECM_VC_ADD_FDIR_FILTER,
+					  IECM_VC_ADD_FDIR_FILTER_ERR);
+		if (err)
+			break;
+
+		memcpy(f, adapter->vc_msg, len);
+		if (f->status == VIRTCHNL_FDIR_SUCCESS) {
+			fdir->flow_id = f->flow_id;
+		} else {
+			err = -EIO;
+			break;
+		}
+		clear_bit(__IECM_VC_MSG_PENDING, adapter->flags);
+	}
+
+	clear_bit(__IECM_VC_MSG_PENDING, adapter->flags);
+	kfree(f);
+	return err;
+}
+
+/**
+ * iecm_send_del_fdir_filter_msg: Send del Flow Director filter message
+ * @vport: vport structure
+ *
+ * Request the CP/PF to del Flow Director as specified by the user via
+ * ethtool
+ *
+ * Return 0 on success, negative on failure
+ **/
+int iecm_send_del_fdir_filter_msg(struct iecm_vport *vport)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	struct iecm_fdir_fltr_config *fdir_config;
+	struct iecm_fdir_fltr *fdir;
+	struct virtchnl_fdir_del f;
+	int err = 0;
+
+	fdir_config = &adapter->config_data.fdir_config;
+
+	while (true) {
+		bool process_fltr = false;
+
+		/* Only del a single Flow Director filter per call */
+		spin_lock_bh(&adapter->fdir_fltr_list_lock);
+		list_for_each_entry(fdir, &fdir_config->fdir_fltr_list, list) {
+			if (fdir->remove) {
+				process_fltr = true;
+				fdir->remove = false;
+				f.vsi_id = fdir->vc_add_msg.vsi_id;
+				f.flow_id = fdir->flow_id;
+				break;
+			}
+		}
+		spin_unlock_bh(&adapter->fdir_fltr_list_lock);
+
+		if (!process_fltr)
+			break;
+
+		err = iecm_send_mb_msg(adapter, VIRTCHNL_OP_DEL_FDIR_FILTER,
+				       sizeof(struct virtchnl_fdir_del), (u8 *)&f);
+		if (err)
+			break;
+
+		err = iecm_wait_for_event(adapter, IECM_VC_DEL_FDIR_FILTER,
+					  IECM_VC_DEL_FDIR_FILTER_ERR);
+		clear_bit(__IECM_VC_MSG_PENDING, adapter->flags);
+	}
+
+	clear_bit(__IECM_VC_MSG_PENDING, adapter->flags);
+	return err;
+}
+
 /**
  * iecm_send_enable_channels_msg - Send enable channels message
  * @vport: vport structure
diff --git a/drivers/net/ethernet/intel/include/iecm.h b/drivers/net/ethernet/intel/include/iecm.h
index d118da1ea8cd..b0785684cc63 100644
--- a/drivers/net/ethernet/intel/include/iecm.h
+++ b/drivers/net/ethernet/intel/include/iecm.h
@@ -12,6 +12,7 @@
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/ethtool.h>
+#include <linux/l2tp.h>
 #include <net/tcp.h>
 #include <net/ip6_checksum.h>
 #include <net/ipv6.h>
@@ -409,6 +410,108 @@ struct iecm_channel_config {
 	u8 num_tc;
 };
 
+enum iecm_fdir_flow_type {
+	/* NONE - used for undef/error */
+	IECM_FDIR_FLOW_NONE = 0,
+	IECM_FDIR_FLOW_IPV4_TCP,
+	IECM_FDIR_FLOW_IPV4_UDP,
+	IECM_FDIR_FLOW_IPV4_SCTP,
+	IECM_FDIR_FLOW_IPV4_AH,
+	IECM_FDIR_FLOW_IPV4_ESP,
+	IECM_FDIR_FLOW_IPV4_OTHER,
+	IECM_FDIR_FLOW_IPV6_TCP,
+	IECM_FDIR_FLOW_IPV6_UDP,
+	IECM_FDIR_FLOW_IPV6_SCTP,
+	IECM_FDIR_FLOW_IPV6_AH,
+	IECM_FDIR_FLOW_IPV6_ESP,
+	IECM_FDIR_FLOW_IPV6_OTHER,
+	IECM_FDIR_FLOW_NON_IP_L2,
+	/* MAX - this must be last and add anything new just above it */
+	IECM_FDIR_FLOW_PTYPE_MAX,
+};
+
+/* Must not exceed the array element number of '__be32 data[2]' in the ethtool
+ * 'struct ethtool_rx_flow_spec.m_ext.data[2]' to express the flex-byte (word).
+ */
+#define IECM_FLEX_WORD_NUM	2
+
+struct iecm_flex_word {
+	u16 offset;
+	u16 word;
+};
+
+struct iecm_ipv4_addrs {
+	__be32 src_ip;
+	__be32 dst_ip;
+};
+
+struct iecm_ipv6_addrs {
+	struct in6_addr src_ip;
+	struct in6_addr dst_ip;
+};
+
+struct iecm_fdir_eth {
+	__be16 etype;
+};
+
+struct iecm_fdir_ip {
+	union {
+		struct iecm_ipv4_addrs v4_addrs;
+		struct iecm_ipv6_addrs v6_addrs;
+	};
+	__be16 src_port;
+	__be16 dst_port;
+	__be32 l4_header;	/* first 4 bytes of the layer 4 header */
+	__be32 spi;		/* security parameter index for AH/ESP */
+	union {
+		u8 tos;
+		u8 tclass;
+	};
+	u8 proto;
+};
+
+struct iecm_fdir_extra {
+	u32 usr_def[IECM_FLEX_WORD_NUM];
+};
+
+/* bookkeeping of Flow Director filters */
+struct iecm_fdir_fltr {
+	struct list_head list;
+
+	enum iecm_fdir_flow_type flow_type;
+
+	struct iecm_fdir_eth eth_data;
+	struct iecm_fdir_eth eth_mask;
+
+	struct iecm_fdir_ip ip_data;
+	struct iecm_fdir_ip ip_mask;
+
+	struct iecm_fdir_extra ext_data;
+	struct iecm_fdir_extra ext_mask;
+
+	enum virtchnl_action action;
+
+	/* flex byte filter data */
+	u8 ip_ver; /* used to adjust the flex offset, 4 : IPv4, 6 : IPv6 */
+	u8 flex_cnt;
+	struct iecm_flex_word flex_words[IECM_FLEX_WORD_NUM];
+
+	u32 flow_id;
+
+	u32 loc;	/* Rule location inside the flow table */
+	u32 q_index;
+
+	struct virtchnl_fdir_add vc_add_msg;
+	bool remove;	/* Flow Director filter needs to be deleted */
+	bool add;	/* Flow Director filter needs to be added */
+};
+
+struct iecm_fdir_fltr_config {
+	struct list_head fdir_fltr_list;
+#define IECM_MAX_FDIR_FILTERS	128	/* max allowed Flow Director filters */
+	u16 num_active_filters;
+};
+
 #define IECM_GET_PTYPE_SIZE(p) \
 	(sizeof(struct virtchnl2_ptype) + \
 	(((p)->proto_id_count ? ((p)->proto_id_count - 1) : 0) * sizeof(u16)))
@@ -446,6 +549,7 @@ struct iecm_user_config_data {
 	struct list_head mac_filter_list;
 	struct list_head vlan_filter_list;
 	struct list_head adv_rss_list;
+	struct iecm_fdir_fltr_config fdir_config;
 	struct iecm_channel_config ch_config;
 };
 
@@ -749,6 +853,14 @@ void iecm_set_ethtool_ops(struct net_device *netdev);
 void iecm_vport_set_hsplit(struct iecm_vport *vport, bool ena);
 void iecm_add_del_ether_addrs(struct iecm_vport *vport, bool add, bool async);
 int iecm_set_promiscuous(struct iecm_adapter *adapter);
+int iecm_send_add_fdir_filter_msg(struct iecm_vport *vport);
+int iecm_send_del_fdir_filter_msg(struct iecm_vport *vport);
+int iecm_get_fdir_fltr_entry(struct iecm_vport *vport,
+			     struct ethtool_rxnfc *cmd);
+int iecm_get_fdir_fltr_ids(struct iecm_vport *vport, struct ethtool_rxnfc *cmd,
+			   u32 *rule_locs);
+int iecm_add_fdir_fltr(struct iecm_vport *vport, struct ethtool_rxnfc *cmd);
+int iecm_del_fdir_fltr(struct iecm_vport *vport, struct ethtool_rxnfc *cmd);
 int iecm_send_enable_channels_msg(struct iecm_vport *vport);
 int iecm_send_disable_channels_msg(struct iecm_vport *vport);
 bool iecm_is_feature_ena(struct iecm_vport *vport, netdev_features_t feature);
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 17/19] iecm: implement cloud filters
  2022-01-28  0:09 [Intel-wired-lan] [PATCH net-next 00/19] Add iecm and idpf Alan Brady
                   ` (15 preceding siblings ...)
  2022-01-28  0:10 ` [Intel-wired-lan] [PATCH net-next 16/19] iecm: implement flow director Alan Brady
@ 2022-01-28  0:10 ` Alan Brady
  2022-01-28 19:38   ` Alexander Lobakin
  2022-01-28  0:10 ` [Intel-wired-lan] [PATCH net-next 18/19] iecm: add advanced rss Alan Brady
                   ` (2 subsequent siblings)
  19 siblings, 1 reply; 89+ messages in thread
From: Alan Brady @ 2022-01-28  0:10 UTC (permalink / raw)
  To: intel-wired-lan

This gives iecm the ability to deal with cloud filters and other traffic
classes.

Signed-off-by: Phani Burra <phani.r.burra@intel.com>
Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
Signed-off-by: Alan Brady <alan.brady@intel.com>
---
 drivers/net/ethernet/intel/iecm/iecm_lib.c    | 900 +++++++++++++++++-
 .../net/ethernet/intel/iecm/iecm_virtchnl.c   |  68 ++
 drivers/net/ethernet/intel/include/iecm.h     |  25 +
 3 files changed, 992 insertions(+), 1 deletion(-)

diff --git a/drivers/net/ethernet/intel/iecm/iecm_lib.c b/drivers/net/ethernet/intel/iecm/iecm_lib.c
index 35c0cbc42ebe..d11413cb438c 100644
--- a/drivers/net/ethernet/intel/iecm/iecm_lib.c
+++ b/drivers/net/ethernet/intel/iecm/iecm_lib.c
@@ -43,9 +43,16 @@ static int iecm_get_vport_index(struct iecm_adapter *adapter,
  */
 bool iecm_is_feature_ena(struct iecm_vport *vport, netdev_features_t feature)
 {
+	struct iecm_channel_config *ch_config;
 	bool ena;
 
 	switch (feature) {
+	case NETIF_F_HW_TC:
+		ch_config = &vport->adapter->config_data.ch_config;
+		ena = (vport->netdev->features & feature) &&
+			(ch_config->num_tc > IECM_START_CHNL_TC) &&
+			(ch_config->tc_running);
+		break;
 	default:
 		ena = vport->netdev->features & feature;
 		break;
@@ -53,6 +60,23 @@ bool iecm_is_feature_ena(struct iecm_vport *vport, netdev_features_t feature)
 	return ena;
 }
 
+/**
+ * iecm_is_adq_v2_ena - Determine whether ADQ V2 is enabled
+ * @vport: virtual port struct
+ *
+ * This function returns true based on negotiated capability ADQ_V2
+ * if set and ADQ enabled
+ */
+static bool iecm_is_adq_v2_ena(struct iecm_vport *vport)
+{
+	/* iecm_is_feature_ena tells if the netdev flag is set and adq is
+	 * enabled
+	 */
+	return (iecm_is_feature_ena(vport, NETIF_F_HW_TC) &&
+		iecm_is_cap_ena(vport->adapter, IECM_OTHER_CAPS,
+				VIRTCHNL2_CAP_ADQ));
+}
+
 /**
  * iecm_is_vlan_cap_ena - Check if VLAN capability is enabled
  * @adapter: pointer to adapter
@@ -946,6 +970,28 @@ static int iecm_get_free_slot(void *array, int size, int curr)
 	return next;
 }
 
+/**
+ * iecm_remove_cloud_filters - Remove all cloud filters
+ * @vport: vport structure
+ */
+static void iecm_remove_cloud_filters(struct iecm_vport *vport)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	struct iecm_cloud_filter_config *cf_config;
+
+	cf_config = &adapter->config_data.cf_config;
+	if (!list_empty(&cf_config->cloud_filter_list)) {
+		struct iecm_cloud_filter *cf;
+
+		spin_lock_bh(&adapter->cloud_filter_list_lock);
+		list_for_each_entry(cf, &cf_config->cloud_filter_list, list) {
+			cf->remove = true;
+		}
+		spin_unlock_bh(&adapter->cloud_filter_list_lock);
+		iecm_send_add_del_cloud_filter_msg(vport, false);
+	}
+}
+
 /**
  * iecm_remove_vlan_filters - Remove all vlan filters
  * @vport: vport structure
@@ -1044,8 +1090,14 @@ static void iecm_vport_stop(struct iecm_vport *vport)
 	if (test_and_clear_bit(__IECM_DEL_QUEUES,
 			       vport->adapter->flags))
 		iecm_send_delete_queues_msg(vport);
-	if (!test_bit(__IECM_REL_RES_IN_PROG, adapter->flags))
+	/* In function reset/rmmod path we call unregister_netdev which
+	 * internally calls delete cloud filters. We remove cloud filters only
+	 * when the interface goes down
+	 */
+	if (!test_bit(__IECM_REL_RES_IN_PROG, adapter->flags)) {
+		iecm_remove_cloud_filters(vport);
 		iecm_remove_vlan_filters(vport);
+	}
 
 	iecm_remove_fdir_filters(vport);
 
@@ -1258,6 +1310,28 @@ static void iecm_restore_vlans(struct iecm_vport *vport)
 		iecm_set_all_vlans(vport);
 }
 
+/**
+ * iecm_restore_cloud_filters - Restore cloud filters
+ * @vport: vport structure
+ */
+static void iecm_restore_cloud_filters(struct iecm_vport *vport)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	struct iecm_cloud_filter_config *cf_config;
+
+	cf_config = &adapter->config_data.cf_config;
+	if (!list_empty(&cf_config->cloud_filter_list)) {
+		struct iecm_cloud_filter *cf;
+
+		spin_lock_bh(&adapter->cloud_filter_list_lock);
+		list_for_each_entry(cf, &cf_config->cloud_filter_list, list) {
+			cf->add = true;
+		}
+		spin_unlock_bh(&adapter->cloud_filter_list_lock);
+		iecm_send_add_del_cloud_filter_msg(vport, true);
+	}
+}
+
 /**
  * iecm_restore_fdir_filters - Restore all Flow Director filters
  * @vport: vport structure
@@ -1302,6 +1376,10 @@ static void iecm_restore_features(struct iecm_vport *vport)
 			dev_info(&adapter->pdev->dev, "Failed to restore promiscuous settings\n");
 	}
 
+	/* Restore cloud filters if ADQ is enabled */
+	if (iecm_is_feature_ena(vport, NETIF_F_HW_TC))
+		iecm_restore_cloud_filters(vport);
+
 	if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS, VIRTCHNL2_CAP_FDIR))
 		iecm_restore_fdir_filters(vport);
 }
@@ -2088,6 +2166,8 @@ int iecm_probe(struct pci_dev *pdev,
 	spin_lock_init(&adapter->vlan_list_lock);
 	spin_lock_init(&adapter->adv_rss_list_lock);
 	spin_lock_init(&adapter->fdir_fltr_list_lock);
+	INIT_LIST_HEAD(&adapter->config_data.cf_config.cloud_filter_list);
+	INIT_LIST_HEAD(&adapter->config_data.cf_config.block_cb_list);
 	INIT_LIST_HEAD(&adapter->config_data.mac_filter_list);
 	INIT_LIST_HEAD(&adapter->config_data.vlan_filter_list);
 	INIT_LIST_HEAD(&adapter->config_data.adv_rss_list);
@@ -2389,6 +2469,810 @@ static int iecm_offload_txtime(struct iecm_vport *vport,
 	return -EOPNOTSUPP;
 }
 
+/**
+ * iecm_is_vlan_tc_filter_allowed - allowed to add tc-filter using VLAN
+ * @vport: vport structure
+ * @vlan: VLAN to verify
+ *
+ * Using specified "vlan" ID, there must be active VLAN filter in VF's
+ * MAC-VLAN filter list.
+ */
+static bool
+iecm_is_vlan_tc_filter_allowed(struct iecm_vport *vport,
+			       struct iecm_vlan *vlan)
+{
+	struct iecm_vlan_filter *f;
+	bool allowed;
+
+	spin_lock_bh(&vport->adapter->vlan_list_lock);
+	f = iecm_find_vlan(vport, vlan);
+	allowed = (f && !f->add && !f->remove);
+	spin_unlock_bh(&vport->adapter->vlan_list_lock);
+	return allowed;
+}
+
+/**
+ * iecm_is_mac_tc_filter_allowed - allowed to add tc-filter using MAC addr
+ * @vport: vport structure
+ * @macaddr: MAC address
+ *
+ * Using specified MAC address, there must be active MAC filter in
+ * MAC filter list.
+ */
+static bool
+iecm_is_mac_tc_filter_allowed(struct iecm_vport *vport, const u8 *macaddr)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	struct iecm_mac_filter *f;
+	bool allowed;
+
+	spin_lock_bh(&adapter->mac_filter_list_lock);
+	f = iecm_find_mac_filter(vport, macaddr);
+	allowed = (f && !f->add && !f->remove);
+	spin_unlock_bh(&adapter->mac_filter_list_lock);
+	return allowed;
+}
+
+/**
+ * iecm_parse_keyid - Parse keyid
+ * @rule: Flow rule structure
+ * @field_flags: Cloud filter flags
+ */
+static void  iecm_parse_keyid(struct flow_rule *rule, u8 *field_flags)
+{
+	struct flow_match_enc_keyid match;
+
+	flow_rule_match_enc_keyid(rule, &match);
+
+	if (match.mask->keyid != 0)
+		*field_flags |= IECM_CLOUD_FIELD_TEN_ID;
+}
+
+/**
+ * iecm_parse_flow_type - Parse flow type based on L2 and L3 protocols
+ * @vport: vport structure
+ * @rule: rule from user
+ * @cf: Structure for the virtchnl filter
+ * @filter: Structure for the cloud filter
+ *
+ * Return 0 on success, negative on failure
+ */
+static int
+iecm_parse_flow_type(struct iecm_vport *vport,
+		     struct flow_rule *rule, struct virtchnl_filter *cf,
+		     struct iecm_cloud_filter *filter)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	enum virtchnl_flow_type flow_type;
+	struct flow_match_basic match;
+	u16 n_proto_mask = 0;
+	u16 n_proto_key = 0;
+	u16 n_proto = 0;
+	u8 ip_proto = 0;
+
+	flow_rule_match_basic(rule, &match);
+
+	n_proto_key = ntohs(match.key->n_proto);
+	n_proto_mask = ntohs(match.mask->n_proto);
+
+	if (n_proto_key == ETH_P_ALL) {
+		n_proto_key = 0;
+		n_proto_mask = 0;
+	}
+	n_proto = n_proto_key & n_proto_mask;
+	if (n_proto != ETH_P_IP && n_proto != ETH_P_IPV6)
+		return -EINVAL;
+
+	if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS, VIRTCHNL2_CAP_ADQ)) {
+		if (match.key->ip_proto != IPPROTO_TCP &&
+		    match.key->ip_proto != IPPROTO_UDP) {
+			dev_err(&adapter->pdev->dev,
+				"Only TCP or UDP transport is supported\n");
+			return -EINVAL;
+		}
+	} else if (match.key->ip_proto != IPPROTO_TCP) {
+		dev_err(&adapter->pdev->dev,
+			"Only TCP transport is supported\n");
+		return -EINVAL;
+	}
+	ip_proto = match.key->ip_proto;
+
+	/* determine VIRTCHNL flow_type based on L3 and L4 protocol */
+	if (n_proto == ETH_P_IP)
+		flow_type = (ip_proto == IPPROTO_TCP) ?
+			     VIRTCHNL_TCP_V4_FLOW :
+			     VIRTCHNL_UDP_V4_FLOW;
+	else
+		flow_type = (ip_proto == IPPROTO_TCP) ?
+			     VIRTCHNL_TCP_V6_FLOW :
+			     VIRTCHNL_UDP_V6_FLOW;
+	cf->flow_type = flow_type;
+	filter->f.flow_type = flow_type;
+
+	return 0;
+}
+
+/**
+ * iecm_parse_ether_header - Parse ethernet header fields
+ * @vport: vport structure
+ * @field_flags: Cloud filter flags
+ * @d_spec: Virtchnl structure for L4 specs
+ * @m_spec: Virtchnl structure for L4 specs
+ * @rule: Flow rule structure
+ *
+ * Return 0 on success, negative on failure
+ */
+static int
+iecm_parse_ether_header(struct iecm_vport *vport, u8 *field_flags,
+			struct virtchnl_l4_spec *d_spec,
+			struct virtchnl_l4_spec *m_spec,
+			struct flow_rule *rule)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	struct flow_match_eth_addrs match;
+	bool adv_adq_ena;
+
+	adv_adq_ena = iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
+				      VIRTCHNL2_CAP_ADQ);
+
+	flow_rule_match_eth_addrs(rule, &match);
+
+	/* use is_broadcast and is_zero to check for all 0xf or 0 */
+	if (!is_zero_ether_addr(match.mask->dst)) {
+		if (adv_adq_ena || is_broadcast_ether_addr(match.mask->dst)) {
+			*field_flags |= IECM_CLOUD_FIELD_OMAC;
+		} else {
+			dev_err(&adapter->pdev->dev, "Bad ether dest mask %pM\n",
+				match.mask->dst);
+			return -EINVAL;
+		}
+	}
+
+	if (!is_zero_ether_addr(match.mask->src)) {
+		if (adv_adq_ena || is_broadcast_ether_addr(match.mask->src)) {
+			*field_flags |= IECM_CLOUD_FIELD_IMAC;
+		} else {
+			dev_err(&adapter->pdev->dev, "Bad ether src mask %pM\n",
+				match.mask->src);
+			return -EINVAL;
+		}
+	}
+
+	if (!is_zero_ether_addr(match.key->dst)) {
+		if (!iecm_is_mac_tc_filter_allowed(adapter->vports[0],
+						   match.key->dst)) {
+			dev_err(&adapter->pdev->dev,
+				"Dest MAC %pM doesn't belong to this device\n",
+				match.key->dst);
+			return -EINVAL;
+		}
+
+		if (is_valid_ether_addr(match.key->dst) ||
+		    is_multicast_ether_addr(match.key->dst)) {
+			/* set the mask if a valid dst_mac address */
+			if (adv_adq_ena)
+				ether_addr_copy(m_spec->dst_mac,
+						match.mask->dst);
+			else
+				eth_broadcast_addr(m_spec->dst_mac);
+			ether_addr_copy(d_spec->dst_mac,
+					match.key->dst);
+		}
+	}
+
+	if (!is_zero_ether_addr(match.key->src))
+		if (is_valid_ether_addr(match.key->src) ||
+		    is_multicast_ether_addr(match.key->src)) {
+			/* set the mask if a valid src_mac address */
+			if (adv_adq_ena) {
+				ether_addr_copy(m_spec->src_mac,
+						match.mask->src);
+			} else {
+				eth_broadcast_addr(m_spec->src_mac);
+			}
+			ether_addr_copy(d_spec->src_mac,
+					match.key->src);
+		}
+	return 0;
+}
+
+/**
+ * iecm_parse_vlan_header - Parse vlan header fields
+ * @vport: vport structure
+ * @field_flags: Cloud filter flags
+ * @d_spec: Virtchnl structure for L4 specs
+ * @m_spec: Virtchnl structure for L4 specs
+ * @rule: Flow rule structure
+ *
+ * Return 0 on success, negative on failure
+ */
+static int
+iecm_parse_vlan_header(struct iecm_vport *vport, u8 *field_flags,
+		       struct virtchnl_l4_spec *d_spec,
+		       struct virtchnl_l4_spec *m_spec,
+		       struct flow_rule *rule)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	struct flow_match_vlan match;
+
+	flow_rule_match_vlan(rule, &match);
+	if (match.mask->vlan_id) {
+		u16 vid = match.key->vlan_id & VLAN_VID_MASK;
+		struct iecm_vlan vlan;
+
+		vlan = IECM_VLAN(vid, ETH_P_8021Q);
+
+		if (match.mask->vlan_id != VLAN_VID_MASK) {
+			dev_err(&adapter->pdev->dev, "Bad vlan mask %u\n",
+				match.mask->vlan_id);
+			return -EINVAL;
+		}
+		if (!iecm_is_vlan_tc_filter_allowed(vport, &vlan)) {
+			dev_err(&adapter->pdev->dev,
+				"VLAN %u doesn't belong to this VF\n",
+				vid);
+			return -EINVAL;
+		}
+		*field_flags |= IECM_CLOUD_FIELD_IVLAN;
+		m_spec->vlan_id = cpu_to_be16(match.mask->vlan_id);
+		d_spec->vlan_id = cpu_to_be16(match.key->vlan_id);
+	}
+	return 0;
+}
+
+/**
+ * iecm_parse_ipv4_header - Parse ipv4 header fields
+ * @vport: vport structure
+ * @field_flags: Cloud filter flags
+ * @d_spec: Virtchnl structure for L4 specs
+ * @m_spec: Virtchnl structure for L4 specs
+ * @rule: Flow rule structure
+ *
+ * Return 0 on success, negative on failure
+ */
+static int
+iecm_parse_ipv4_header(struct iecm_vport *vport, u8 *field_flags,
+		       struct virtchnl_l4_spec *d_spec,
+		       struct virtchnl_l4_spec *m_spec,
+		       struct flow_rule *rule)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	struct flow_match_ipv4_addrs match;
+	bool adv_adq_ena;
+
+	adv_adq_ena = iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
+				      VIRTCHNL2_CAP_ADQ);
+
+	flow_rule_match_ipv4_addrs(rule, &match);
+
+	if (*field_flags & IECM_CLOUD_FIELD_TEN_ID) {
+		dev_info(&adapter->pdev->dev,
+			 "Tenant id not allowed for ip filter\n");
+		return -EINVAL;
+	}
+
+	if (match.mask->dst) {
+		if (adv_adq_ena || match.mask->dst == cpu_to_be32(0xffffffff)) {
+			*field_flags |= IECM_CLOUD_FIELD_IIP;
+		} else {
+			dev_err(&adapter->pdev->dev, "Bad ip dst mask 0x%08x\n",
+				be32_to_cpu(match.mask->dst));
+			return -EINVAL;
+		}
+	}
+
+	if (match.mask->src) {
+		if (adv_adq_ena || match.mask->src == cpu_to_be32(0xffffffff)) {
+			*field_flags |= IECM_CLOUD_FIELD_IIP;
+		} else {
+			dev_err(&adapter->pdev->dev, "Bad ip src mask 0x%08x\n",
+				be32_to_cpu(match.mask->dst));
+			return -EINVAL;
+		}
+	}
+
+	if (match.key->dst) {
+		if (adv_adq_ena)
+			m_spec->dst_ip[0] = match.mask->dst;
+		else
+			m_spec->dst_ip[0] = cpu_to_be32(0xffffffff);
+		d_spec->dst_ip[0] = match.key->dst;
+	}
+
+	if (match.key->src) {
+		if (adv_adq_ena)
+			m_spec->src_ip[0] = match.mask->src;
+		else
+			m_spec->src_ip[0] = cpu_to_be32(0xffffffff);
+		d_spec->src_ip[0] = match.key->src;
+	}
+	return 0;
+}
+
+/**
+ * iecm_parse_ipv6_header - Parse ipv6 header fields
+ * @vport: vport structure
+ * @field_flags: Cloud filter flags
+ * @d_spec: Virtchnl structure for L4 specs
+ * @m_spec: Virtchnl structure for L4 specs
+ * @rule: Flow rule structure
+ *
+ * Return 0 on success, negative on failure
+ */
+static int
+iecm_parse_ipv6_header(struct iecm_vport *vport, u8 *field_flags,
+		       struct virtchnl_l4_spec *d_spec,
+		       struct virtchnl_l4_spec *m_spec,
+		       struct flow_rule *rule)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	struct flow_match_ipv6_addrs match;
+	int i;
+
+	flow_rule_match_ipv6_addrs(rule, &match);
+
+	/* validate mask, make sure it is not IPV6_ADDR_ANY */
+	if (ipv6_addr_any(&match.mask->dst)) {
+		dev_err(&adapter->pdev->dev, "Bad ipv6 dst mask 0x%02x\n",
+			IPV6_ADDR_ANY);
+		return -EINVAL;
+	}
+
+	/* src and dest IPv6 address should not be LOOPBACK
+	 * (0:0:0:0:0:0:0:1) which can be represented as ::1
+	 */
+	if (ipv6_addr_loopback(&match.key->dst) ||
+	    ipv6_addr_loopback(&match.key->src)) {
+		dev_err(&adapter->pdev->dev,
+			"ipv6 addr should not be loopback\n");
+		return -EINVAL;
+	}
+
+	if (!ipv6_addr_any(&match.mask->dst) ||
+	    !ipv6_addr_any(&match.mask->src))
+		*field_flags |= IECM_CLOUD_FIELD_IIP;
+
+	/* copy dest IPv6 mask and address */
+	if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS, VIRTCHNL2_CAP_ADQ)) {
+		memcpy(&m_spec->dst_ip, &match.mask->dst.s6_addr32,
+		       sizeof(m_spec->dst_ip));
+	} else {
+		for (i = 0; i < 4; i++)
+			m_spec->dst_ip[i] = cpu_to_be32(0xffffffff);
+	}
+	memcpy(&d_spec->dst_ip, &match.key->dst.s6_addr32,
+	       sizeof(d_spec->dst_ip));
+
+	/* copy source IPv6 mask and address */
+	if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS, VIRTCHNL2_CAP_ADQ)) {
+		memcpy(&m_spec->src_ip, &match.mask->src.s6_addr32,
+		       sizeof(m_spec->src_ip));
+	} else {
+		for (i = 0; i < 4; i++)
+			m_spec->src_ip[i] = cpu_to_be32(0xffffffff);
+	}
+	memcpy(&d_spec->src_ip, &match.key->src.s6_addr32,
+	       sizeof(d_spec->src_ip));
+
+	return 0;
+}
+
+/**
+ * iecm_parse_l4_header - Parse l4 header fields
+ * @vport: vport structure
+ * @d_spec: Virtchnl structure for L4 specs
+ * @m_spec: Virtchnl structure for L4 specs
+ * @rule: Flow rule structure
+ *
+ * Return 0 on success, negative on failure
+ */
+static int
+iecm_parse_l4_header(struct iecm_vport *vport,
+		     struct virtchnl_l4_spec *d_spec,
+		     struct virtchnl_l4_spec *m_spec,
+		     struct flow_rule *rule)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	struct flow_match_ports match;
+
+	flow_rule_match_ports(rule, &match);
+
+	if (match.key->dst) {
+		if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
+				    VIRTCHNL2_CAP_ADQ) ||
+		    match.mask->dst == cpu_to_be16(0xffff)) {
+			m_spec->dst_port = match.mask->dst;
+			d_spec->dst_port = match.key->dst;
+		} else {
+			dev_err(&adapter->pdev->dev, "Bad dst port mask %u\n",
+				be16_to_cpu(match.mask->dst));
+			return -EINVAL;
+		}
+	}
+
+	if (match.key->src) {
+		if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
+				    VIRTCHNL2_CAP_ADQ) ||
+		    match.mask->src == cpu_to_be16(0xffff)) {
+			m_spec->src_port = match.mask->src;
+			d_spec->src_port = match.key->src;
+		} else {
+			dev_err(&adapter->pdev->dev, "Bad src port mask %u\n",
+				be16_to_cpu(match.mask->src));
+			return -EINVAL;
+		}
+	}
+	return 0;
+}
+
+/**
+ * iecm_parse_cls_flower - Parse tc flower filters provided by kernel
+ * @vport: vport structure
+ * @f: pointer to struct flow_cls_offload
+ * @filter: pointer to cloud filter structure
+ */
+static int iecm_parse_cls_flower(struct iecm_vport *vport,
+				 struct flow_cls_offload *f,
+				 struct iecm_cloud_filter *filter)
+{
+	struct flow_rule *rule = flow_cls_offload_flow_rule(f);
+	struct iecm_adapter *adapter = vport->adapter;
+	struct virtchnl_l4_spec *d_spec, *m_spec;
+	struct virtchnl_filter *cf = &filter->f;
+	struct flow_dissector *dissector;
+	u8 field_flags = 0;
+	u16 addr_type = 0;
+	int err;
+
+	dissector = rule->match.dissector;
+	if (dissector->used_keys &
+	    ~(BIT(FLOW_DISSECTOR_KEY_CONTROL) |
+	      BIT(FLOW_DISSECTOR_KEY_BASIC) |
+	      BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS) |
+	      BIT(FLOW_DISSECTOR_KEY_VLAN) |
+	      BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS) |
+	      BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS) |
+	      BIT(FLOW_DISSECTOR_KEY_ENC_KEYID) |
+	      BIT(FLOW_DISSECTOR_KEY_PORTS))) {
+		dev_err(&adapter->pdev->dev, "Unsupported key used: 0x%x\n",
+			dissector->used_keys);
+		return -EOPNOTSUPP;
+	}
+	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_KEYID))
+		iecm_parse_keyid(rule, &field_flags);
+
+	/* even though following code refers as "tcp_sec", it is not
+	 * just for TCP but a generic struct representing
+	 * L2, L3 + L4 fields if specified
+	 */
+	m_spec = &cf->mask.tcp_spec;
+	d_spec = &cf->data.tcp_spec;
+
+	/* determine flow type, TCP/UDP_V4[6]_FLOW based on
+	 * L2 proto (aka ETH proto) and L3 proto (aka IP_PROTO)
+	 */
+	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {
+		err = iecm_parse_flow_type(vport, rule, cf, filter);
+		if (err)
+			return err;
+	}
+
+	/* process Ethernet header fields */
+	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
+		err = iecm_parse_ether_header(vport, &field_flags,
+					      d_spec, m_spec, rule);
+		if (err)
+			return err;
+	}
+
+	/* process VLAN header for single VLAN (type could be S/C-tag) */
+	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) {
+		err = iecm_parse_vlan_header(vport, &field_flags,
+					     d_spec, m_spec, rule);
+		if (err)
+			return err;
+	}
+
+	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CONTROL)) {
+		struct flow_match_control match;
+
+		flow_rule_match_control(rule, &match);
+		addr_type = match.key->addr_type;
+	}
+
+	/* process IPv4 header */
+	if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) {
+		err = iecm_parse_ipv4_header(vport, &field_flags,
+					     d_spec, m_spec, rule);
+		if (err)
+			return err;
+	}
+
+	/* process IPv6 header */
+	if (addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) {
+		err = iecm_parse_ipv6_header(vport, &field_flags,
+					     d_spec, m_spec, rule);
+		if (err)
+			return err;
+	}
+
+	/* process L4 header, supported L4 protocols are TCP and UDP */
+	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) {
+		err = iecm_parse_l4_header(vport, d_spec, m_spec, rule);
+		if (err)
+			return err;
+	}
+	cf->field_flags = field_flags;
+
+	return 0;
+}
+
+/**
+ * iecm_handle_tclass - Forward to a traffic class on the device
+ * @vport: vport structure
+ * @tc: traffic class index on the device
+ * @filter: pointer to cloud filter structure
+ *
+ * Return 0 on success, negative on failure
+ */
+static int iecm_handle_tclass(struct iecm_vport *vport, int tc,
+			      struct iecm_cloud_filter *filter)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+
+	if (tc == 0)
+		return 0;
+	if ((!iecm_is_adq_v2_ena(vport)) &&
+	    !filter->f.data.tcp_spec.dst_port) {
+		dev_err(&adapter->pdev->dev,
+			"Specify destination port to redirect to traffic class other than TC0\n");
+		return -EINVAL;
+	}
+	/* redirect to a traffic class on the same device */
+	filter->f.action = VIRTCHNL_ACTION_TC_REDIRECT;
+	filter->f.action_meta = tc;
+	return 0;
+}
+
+/* iecm_find_cf - Find the cloud filter in the list
+ * @vport: vport structure
+ * @cookie: filter specific cookie
+ *
+ * Returns pointer to the filter object or NULL. Must be called while holding
+ * cloud_filter_list_lock.
+ */
+static struct iecm_cloud_filter *iecm_find_cf(struct iecm_vport *vport,
+					      unsigned long *cookie)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	struct iecm_cloud_filter *filter = NULL;
+
+	if (!cookie)
+		return NULL;
+
+	list_for_each_entry(filter,
+			    &adapter->config_data.cf_config.cloud_filter_list,
+			    list) {
+		if (!memcmp(cookie, &filter->cookie, sizeof(filter->cookie)))
+			return filter;
+	}
+	return NULL;
+}
+
+/**
+ * iecm_configure_clsflower - Add tc flower filters
+ * @vport: vport structure
+ * @cls_flower: Pointer to struct flow_cls_offload
+ *
+ * Return 0 on success, negative on failure
+ */
+static int iecm_configure_clsflower(struct iecm_vport *vport,
+				    struct flow_cls_offload *cls_flower)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	struct iecm_user_config_data *config_data;
+	struct iecm_cloud_filter *filter = NULL;
+	int err;
+	int tc;
+
+	config_data = &adapter->config_data;
+	tc = tc_classid_to_hwtc(vport->netdev, cls_flower->classid);
+	if (tc < 0) {
+		dev_err(&adapter->pdev->dev, "Invalid traffic class\n");
+		return -EINVAL;
+	}
+
+#define IECM_MAX_CLOUD_ADQ_FILTERS	128
+
+	if (config_data->cf_config.num_cloud_filters >=
+						IECM_MAX_CLOUD_ADQ_FILTERS) {
+		dev_err(&adapter->pdev->dev,
+			"Unable to add filter (action is forward to TC), reached max allowed filters (%u)\n",
+			IECM_MAX_CLOUD_ADQ_FILTERS);
+		return -ENOSPC;
+	}
+
+	/* bail out here if filter already exists */
+	spin_lock_bh(&adapter->cloud_filter_list_lock);
+	filter = iecm_find_cf(vport, &cls_flower->cookie);
+	if (filter) {
+		filter->remove = false;
+		dev_err(&adapter->pdev->dev, "Failed to add TC Flower filter, it already exists\n");
+		spin_unlock_bh(&adapter->cloud_filter_list_lock);
+		return -EEXIST;
+	}
+	spin_unlock_bh(&adapter->cloud_filter_list_lock);
+
+	filter = kzalloc(sizeof(*filter), GFP_KERNEL);
+	if (!filter)
+		return -ENOMEM;
+
+	filter->cookie = cls_flower->cookie;
+
+	/* set the mask to all zeroes to begin with */
+	memset(&filter->f.mask.tcp_spec, 0, sizeof(struct virtchnl_l4_spec));
+
+	/* start out with flow type and eth type IPv4 to begin with */
+	filter->f.flow_type = VIRTCHNL_TCP_V4_FLOW;
+	err = iecm_parse_cls_flower(vport, cls_flower, filter);
+	if (err)
+		goto error;
+
+	err = iecm_handle_tclass(vport, tc, filter);
+	if (err)
+		goto error;
+
+	/* add filter to the list */
+	spin_lock_bh(&adapter->cloud_filter_list_lock);
+	list_add_tail(&filter->list, &config_data->cf_config.cloud_filter_list);
+	filter->add = true;
+	spin_unlock_bh(&adapter->cloud_filter_list_lock);
+	err = iecm_send_add_del_cloud_filter_msg(vport, true);
+	spin_lock_bh(&adapter->cloud_filter_list_lock);
+	/* We have to find it again in case another thread has already
+	 * deleted and kfreed it.
+	 */
+	filter = iecm_find_cf(vport, &cls_flower->cookie);
+	if (filter && err) {
+		list_del(&filter->list);
+		spin_unlock_bh(&adapter->cloud_filter_list_lock);
+		goto error;
+	}
+	spin_unlock_bh(&adapter->cloud_filter_list_lock);
+
+	config_data->cf_config.num_cloud_filters++;
+error:
+	if (err)
+		kfree(filter);
+	return err;
+}
+
+/**
+ * iecm_delete_clsflower - Remove tc flower filters
+ * @vport: vport structure
+ * @cls_flower: Pointer to struct flow_cls_offload
+ *
+ * Return 0 on success, negative on failure
+ */
+static int iecm_delete_clsflower(struct iecm_vport *vport,
+				 struct flow_cls_offload *cls_flower)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	struct iecm_cloud_filter *filter = NULL;
+	int err = 0;
+
+	spin_lock_bh(&adapter->cloud_filter_list_lock);
+	filter = iecm_find_cf(vport, &cls_flower->cookie);
+	if (filter) {
+		filter->remove = true;
+		adapter->config_data.cf_config.num_cloud_filters--;
+	} else if (adapter->config_data.cf_config.num_cloud_filters) {
+		/* "num_cloud_filters" can become zero if egress qdisc is
+		 * detached as per design, driver deletes related filters
+		 * when qdisc is detached to avoid stale filters, hence
+		 * num_cloud_filters can become zero. But since netdev
+		 * layer doesn't know that filters are deleted by driver
+		 * implictly when egress qdisc is deleted, it sees filters
+		 * being present and "in_hw". User can request delete
+		 * of specific filter of detach ingress qdisc - in either of
+		 * those operation, filter(s) won't be found in driver cache,
+		 * hence instead of returning, let this function return SUCCESS
+		 * Returning of err as -EINVAL is only applicable when
+		 * unable to find filter and num_cloud_filters is non-zero
+		 */
+		err = -EINVAL;
+	}
+	spin_unlock_bh(&adapter->cloud_filter_list_lock);
+
+	if (filter) {
+		err = iecm_send_add_del_cloud_filter_msg(vport, false);
+		spin_lock_bh(&adapter->cloud_filter_list_lock);
+		/* It can happen that asynchronously the filter was already
+		 * deleted from the list. Make sure it's still there and marked
+		 * for remove under spinlock before actually trying to delete
+		 * from list.
+		 */
+		filter = iecm_find_cf(vport, &cls_flower->cookie);
+		if (filter) {
+			list_del(&filter->list);
+			kfree(filter);
+		}
+		spin_unlock_bh(&adapter->cloud_filter_list_lock);
+	}
+	return err;
+}
+
+/**
+ * iecm_setup_tc_cls_flower - flower classifier offloads
+ * @vport: vport structure
+ * @cls_flower: pointer to struct flow_cls_offload
+ *
+ * Return 0 on success, negative on failure
+ */
+static int iecm_setup_tc_cls_flower(struct iecm_vport *vport,
+				    struct flow_cls_offload *cls_flower)
+{
+	if (cls_flower->common.chain_index)
+		return -EOPNOTSUPP;
+
+	switch (cls_flower->command) {
+	case FLOW_CLS_REPLACE:
+		return iecm_configure_clsflower(vport, cls_flower);
+	case FLOW_CLS_DESTROY:
+		return iecm_delete_clsflower(vport, cls_flower);
+	case FLOW_CLS_STATS:
+		return -EOPNOTSUPP;
+	default:
+		return -EINVAL;
+	}
+}
+
+/**
+ * iecm_setup_tc_block_cb - block callback for tc
+ * @type: type of offload
+ * @type_data: offload data
+ * @cb_priv: Private adapter structure
+ *
+ * This function is the block callback for traffic classes
+ * Return 0 on success, negative on failure
+ **/
+static int iecm_setup_tc_block_cb(enum tc_setup_type type, void *type_data,
+				  void *cb_priv)
+{
+	switch (type) {
+	case TC_SETUP_CLSFLOWER:
+		return iecm_setup_tc_cls_flower((struct iecm_vport *)cb_priv,
+						(struct flow_cls_offload *)
+						 type_data);
+	default:
+		return -EOPNOTSUPP;
+	}
+}
+
+/**
+ * iecm_del_all_cloud_filters - delete all cloud filters on the traffic classes
+ * @vport: vport structure
+ *
+ * This function will loop through the list of cloud filters and deletes them.
+ **/
+static void iecm_del_all_cloud_filters(struct iecm_vport *vport)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	struct iecm_cloud_filter_config *cf_config;
+	struct iecm_cloud_filter *cf, *cftmp;
+
+	cf_config = &adapter->config_data.cf_config;
+	spin_lock_bh(&adapter->cloud_filter_list_lock);
+	list_for_each_entry_safe(cf, cftmp,
+				 &cf_config->cloud_filter_list,
+				 list) {
+		list_del(&cf->list);
+		kfree(cf);
+		cf_config->num_cloud_filters--;
+	}
+	spin_unlock_bh(&adapter->cloud_filter_list_lock);
+}
+
 /**
  * iecm_validate_tx_bandwidth - validate the max Tx bandwidth
  * @vport: vport structure
@@ -2596,6 +3480,7 @@ static int __iecm_setup_tc(struct iecm_vport *vport, void *type_data)
 			netif_tx_stop_all_queues(netdev);
 			netif_tx_disable(netdev);
 			ret = iecm_send_disable_channels_msg(vport);
+			iecm_del_all_cloud_filters(vport);
 			netif_tx_start_all_queues(netdev);
 			if (!test_bit(__IECM_REL_RES_IN_PROG, adapter->flags) &&
 			    !ret) {
@@ -2709,8 +3594,10 @@ static int iecm_setup_tc(struct net_device *netdev, enum tc_setup_type type,
 {
 	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
 	struct iecm_adapter *adapter = vport->adapter;
+	struct iecm_cloud_filter_config *cf_config;
 	int err = 0;
 
+	cf_config = &adapter->config_data.cf_config;
 	switch (type) {
 	case TC_SETUP_QDISC_ETF:
 		if (iecm_is_queue_model_split(vport->txq_model))
@@ -2720,6 +3607,17 @@ static int iecm_setup_tc(struct net_device *netdev, enum tc_setup_type type,
 					     type_data);
 		break;
 	case TC_SETUP_BLOCK:
+		if (iecm_is_cap_ena(adapter, IECM_BASE_CAPS,
+				    VIRTCHNL2_CAP_ADQ) ||
+		    iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
+				    VIRTCHNL2_CAP_ADQ)) {
+			err =
+			flow_block_cb_setup_simple((struct flow_block_offload *)
+						    type_data,
+						   &cf_config->block_cb_list,
+						   iecm_setup_tc_block_cb,
+						   vport, vport, true);
+		}
 		break;
 	case TC_SETUP_QDISC_MQPRIO:
 		if (iecm_is_cap_ena(adapter, IECM_BASE_CAPS,
diff --git a/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
index 5601846b4674..94af45c36866 100644
--- a/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
+++ b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
@@ -2731,6 +2731,74 @@ static int iecm_send_insert_vlan_msg(struct iecm_vport *vport, bool ena)
 	return err;
 }
 
+/**
+ * iecm_send_add_del_cloud_filter_msg: Send add/del cloud filter message
+ * @vport: vport structure
+ * @add: True to add, false to delete cloud filter
+ *
+ * Request the CP/PF to add/del cloud filters as specified by the user via
+ * tc tool
+ *
+ * Return 0 on success, negative on failure
+ **/
+int iecm_send_add_del_cloud_filter_msg(struct iecm_vport *vport, bool add)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	struct iecm_cloud_filter_config *cf_config;
+	struct iecm_cloud_filter *cf;
+	struct virtchnl_filter f;
+	int len = 0, err = 0;
+
+	cf_config = &adapter->config_data.cf_config;
+
+	while (true) {
+		bool process_fltr = false;
+
+		spin_lock_bh(&adapter->cloud_filter_list_lock);
+		list_for_each_entry(cf, &cf_config->cloud_filter_list, list) {
+			if (add && cf->add) {
+				process_fltr = true;
+				cf->add = false;
+				f = cf->f;
+				break;
+			} else if (!add && cf->remove) {
+				process_fltr = true;
+				cf->remove = false;
+				f = cf->f;
+				break;
+			}
+		}
+		spin_unlock_bh(&adapter->cloud_filter_list_lock);
+
+		/* Don't send mailbox message when there are no filters to add/del */
+		if (!process_fltr)
+			goto error;
+
+		if (add) {
+			err = iecm_send_mb_msg(adapter, VIRTCHNL_OP_ADD_CLOUD_FILTER,
+					       len, (u8 *)&f);
+			if (err)
+				goto error;
+
+			err = iecm_wait_for_event(adapter, IECM_VC_ADD_CLOUD_FILTER,
+						  IECM_VC_ADD_CLOUD_FILTER_ERR);
+		} else {
+			err = iecm_send_mb_msg(adapter, VIRTCHNL_OP_DEL_CLOUD_FILTER,
+					       len, (u8 *)&f);
+			if (err)
+				goto error;
+
+			err =
+			iecm_min_wait_for_event(adapter, IECM_VC_DEL_CLOUD_FILTER,
+						IECM_VC_DEL_CLOUD_FILTER_ERR);
+		}
+		if (err)
+			break;
+	}
+error:
+	return err;
+}
+
 /**
  * iecm_send_add_fdir_filter_msg: Send add Flow Director filter message
  * @vport: vport structure
diff --git a/drivers/net/ethernet/intel/include/iecm.h b/drivers/net/ethernet/intel/include/iecm.h
index b0785684cc63..0aab41cf982c 100644
--- a/drivers/net/ethernet/intel/include/iecm.h
+++ b/drivers/net/ethernet/intel/include/iecm.h
@@ -403,6 +403,28 @@ enum iecm_user_flags {
 	__IECM_USER_FLAGS_NBITS,
 };
 
+#define IECM_CLOUD_FIELD_OMAC		BIT(0)
+#define IECM_CLOUD_FIELD_IMAC		BIT(1)
+#define IECM_CLOUD_FIELD_IVLAN		BIT(2)
+#define IECM_CLOUD_FIELD_TEN_ID		BIT(3)
+#define IECM_CLOUD_FIELD_IIP		BIT(4)
+
+#define IECM_START_CHNL_TC		1
+
+struct iecm_cloud_filter {
+	struct list_head list;
+	struct virtchnl_filter f;
+	unsigned long cookie;
+	bool remove;		/* filter needs to be deleted */
+	bool add;		/* filter needs to be added */
+};
+
+struct iecm_cloud_filter_config {
+	struct list_head block_cb_list;		/* need to pass this to stack */
+	struct list_head cloud_filter_list;
+	u16 num_cloud_filters;
+};
+
 struct iecm_channel_config {
 	struct virtchnl_channel_info ch_info[VIRTCHNL_MAX_ADQ_V2_CHANNELS];
 	bool tc_running;
@@ -536,6 +558,7 @@ struct iecm_ptype_state {
 	bool outer_frag;
 	u8 tunnel_state;
 };
+
 /* User defined configuration values */
 struct iecm_user_config_data {
 	u32 num_req_tx_qs; /* user requested TX queues through ethtool */
@@ -550,6 +573,7 @@ struct iecm_user_config_data {
 	struct list_head vlan_filter_list;
 	struct list_head adv_rss_list;
 	struct iecm_fdir_fltr_config fdir_config;
+	struct iecm_cloud_filter_config cf_config;
 	struct iecm_channel_config ch_config;
 };
 
@@ -853,6 +877,7 @@ void iecm_set_ethtool_ops(struct net_device *netdev);
 void iecm_vport_set_hsplit(struct iecm_vport *vport, bool ena);
 void iecm_add_del_ether_addrs(struct iecm_vport *vport, bool add, bool async);
 int iecm_set_promiscuous(struct iecm_adapter *adapter);
+int iecm_send_add_del_cloud_filter_msg(struct iecm_vport *vport, bool add);
 int iecm_send_add_fdir_filter_msg(struct iecm_vport *vport);
 int iecm_send_del_fdir_filter_msg(struct iecm_vport *vport);
 int iecm_get_fdir_fltr_entry(struct iecm_vport *vport,
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 18/19] iecm: add advanced rss
  2022-01-28  0:09 [Intel-wired-lan] [PATCH net-next 00/19] Add iecm and idpf Alan Brady
                   ` (16 preceding siblings ...)
  2022-01-28  0:10 ` [Intel-wired-lan] [PATCH net-next 17/19] iecm: implement cloud filters Alan Brady
@ 2022-01-28  0:10 ` Alan Brady
  2022-01-28 19:53   ` Alexander Lobakin
  2022-01-28  0:10 ` [Intel-wired-lan] [PATCH net-next 19/19] idpf: introduce idpf driver Alan Brady
  2022-02-04 12:05 ` [Intel-wired-lan] [PATCH net-next 00/19] Add iecm and idpf Alexander Lobakin
  19 siblings, 1 reply; 89+ messages in thread
From: Alan Brady @ 2022-01-28  0:10 UTC (permalink / raw)
  To: intel-wired-lan

From: Haiyue Wang <haiyue.wang@intel.com>

Continuing with advanced features this implements what's needed to do
advanced rss.

Signed-off-by: Haiyue Wang <haiyue.wang@intel.com>
---
 drivers/net/ethernet/intel/iecm/iecm_lib.c    | 547 ++++++++++++++++++
 .../net/ethernet/intel/iecm/iecm_virtchnl.c   |  71 +++
 drivers/net/ethernet/intel/include/iecm.h     |  73 +++
 3 files changed, 691 insertions(+)

diff --git a/drivers/net/ethernet/intel/iecm/iecm_lib.c b/drivers/net/ethernet/intel/iecm/iecm_lib.c
index d11413cb438c..baa1e312652a 100644
--- a/drivers/net/ethernet/intel/iecm/iecm_lib.c
+++ b/drivers/net/ethernet/intel/iecm/iecm_lib.c
@@ -1013,6 +1013,52 @@ static void iecm_remove_vlan_filters(struct iecm_vport *vport)
 	}
 }
 
+/**
+ * iecm_remove_adv_rss_cfgs - Remove all RSS configuration
+ * @vport: vport structure
+ */
+static void iecm_remove_adv_rss_cfgs(struct iecm_vport *vport)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+
+	if (!iecm_is_cap_ena(adapter, IECM_OTHER_CAPS, VIRTCHNL2_CAP_ADV_RSS))
+		return;
+
+	if (!list_empty(&adapter->config_data.adv_rss_list)) {
+		struct iecm_adv_rss *rss;
+
+		spin_lock_bh(&adapter->adv_rss_list_lock);
+		list_for_each_entry(rss, &adapter->config_data.adv_rss_list,
+				    list) {
+			rss->remove = true;
+		}
+		spin_unlock_bh(&adapter->adv_rss_list_lock);
+		iecm_send_add_del_adv_rss_cfg_msg(vport, false);
+	}
+}
+
+/**
+ * iecm_del_all_adv_rss_cfgs - delete all RSS configuration
+ * @vport: vport structure
+ *
+ * This function will loop through the list of RSS configuration and deletes
+ * them.
+ **/
+static void iecm_del_all_adv_rss_cfgs(struct iecm_vport *vport)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	struct iecm_adv_rss *rss, *rss_tmp;
+
+	spin_lock_bh(&adapter->adv_rss_list_lock);
+	list_for_each_entry_safe(rss, rss_tmp,
+				 &adapter->config_data.adv_rss_list,
+				 list) {
+		list_del(&rss->list);
+		kfree(rss);
+	}
+	spin_unlock_bh(&adapter->adv_rss_list_lock);
+}
+
 /**
  * iecm_remove_fdir_filters - Remove all Flow Director filters
  * @vport: vport structure
@@ -1099,6 +1145,7 @@ static void iecm_vport_stop(struct iecm_vport *vport)
 		iecm_remove_vlan_filters(vport);
 	}
 
+	iecm_remove_adv_rss_cfgs(vport);
 	iecm_remove_fdir_filters(vport);
 
 	adapter->link_up = false;
@@ -1332,6 +1379,27 @@ static void iecm_restore_cloud_filters(struct iecm_vport *vport)
 	}
 }
 
+/**
+ * iecm_restore_adv_rss_cfgs - Restore all RSS configuration
+ * @vport: vport structure
+ */
+static void iecm_restore_adv_rss_cfgs(struct iecm_vport *vport)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+
+	if (!list_empty(&adapter->config_data.adv_rss_list)) {
+		struct iecm_adv_rss *rss;
+
+		spin_lock_bh(&adapter->adv_rss_list_lock);
+		list_for_each_entry(rss, &adapter->config_data.adv_rss_list,
+				    list) {
+			rss->add = true;
+		}
+		spin_unlock_bh(&adapter->adv_rss_list_lock);
+		iecm_send_add_del_adv_rss_cfg_msg(vport, true);
+	}
+}
+
 /**
  * iecm_restore_fdir_filters - Restore all Flow Director filters
  * @vport: vport structure
@@ -1380,6 +1448,9 @@ static void iecm_restore_features(struct iecm_vport *vport)
 	if (iecm_is_feature_ena(vport, NETIF_F_HW_TC))
 		iecm_restore_cloud_filters(vport);
 
+	if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS, VIRTCHNL2_CAP_ADV_RSS))
+		iecm_restore_adv_rss_cfgs(vport);
+
 	if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS, VIRTCHNL2_CAP_FDIR))
 		iecm_restore_fdir_filters(vport);
 }
@@ -2219,6 +2290,7 @@ static void iecm_del_user_cfg_data(struct iecm_adapter *adapter)
 		if (!adapter->vports[i])
 			continue;
 
+		iecm_del_all_adv_rss_cfgs(adapter->vports[i]);
 		iecm_del_all_fdir_filters(adapter->vports[i]);
 	}
 }
@@ -3633,6 +3705,481 @@ static int iecm_setup_tc(struct net_device *netdev, enum tc_setup_type type,
 	return err;
 }
 
+/**
+ * iecm_fill_adv_rss_ip4_hdr - fill the IPv4 RSS protocol header
+ * @hdr: the virtchnl message protocol header data structure
+ * @hash_flds: the RSS configuration protocol hash fields
+ */
+static void
+iecm_fill_adv_rss_ip4_hdr(struct virtchnl_proto_hdr *hdr, u64 hash_flds)
+{
+	VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, IPV4);
+
+	if (hash_flds & IECM_ADV_RSS_HASH_FLD_IPV4_SA)
+		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, IPV4, SRC);
+
+	if (hash_flds & IECM_ADV_RSS_HASH_FLD_IPV4_DA)
+		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, IPV4, DST);
+}
+
+/**
+ * iecm_fill_adv_rss_ip6_hdr - fill the IPv6 RSS protocol header
+ * @hdr: the virtchnl message protocol header data structure
+ * @hash_flds: the RSS configuration protocol hash fields
+ */
+static void
+iecm_fill_adv_rss_ip6_hdr(struct virtchnl_proto_hdr *hdr, u64 hash_flds)
+{
+	VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, IPV6);
+
+	if (hash_flds & IECM_ADV_RSS_HASH_FLD_IPV6_SA)
+		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, IPV6, SRC);
+
+	if (hash_flds & IECM_ADV_RSS_HASH_FLD_IPV6_DA)
+		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, IPV6, DST);
+}
+
+/**
+ * iecm_fill_adv_rss_tcp_hdr - fill the TCP RSS protocol header
+ * @hdr: the virtchnl message protocol header data structure
+ * @hash_flds: the RSS configuration protocol hash fields
+ */
+static void
+iecm_fill_adv_rss_tcp_hdr(struct virtchnl_proto_hdr *hdr, u64 hash_flds)
+{
+	VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, TCP);
+
+	if (hash_flds & IECM_ADV_RSS_HASH_FLD_TCP_SRC_PORT)
+		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, TCP, SRC_PORT);
+
+	if (hash_flds & IECM_ADV_RSS_HASH_FLD_TCP_DST_PORT)
+		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, TCP, DST_PORT);
+}
+
+/**
+ * iecm_fill_adv_rss_udp_hdr - fill the UDP RSS protocol header
+ * @hdr: the virtchnl message protocol header data structure
+ * @hash_flds: the RSS configuration protocol hash fields
+ */
+static void
+iecm_fill_adv_rss_udp_hdr(struct virtchnl_proto_hdr *hdr, u64 hash_flds)
+{
+	VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, UDP);
+
+	if (hash_flds & IECM_ADV_RSS_HASH_FLD_UDP_SRC_PORT)
+		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, UDP, SRC_PORT);
+
+	if (hash_flds & IECM_ADV_RSS_HASH_FLD_UDP_DST_PORT)
+		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, UDP, DST_PORT);
+}
+
+/**
+ * iecm_fill_adv_rss_sctp_hdr - fill the SCTP RSS protocol header
+ * @hdr: the virtchnl message protocol header data structure
+ * @hash_flds: the RSS configuration protocol hash fields
+ */
+static void
+iecm_fill_adv_rss_sctp_hdr(struct virtchnl_proto_hdr *hdr, s64 hash_flds)
+{
+	VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, SCTP);
+
+	if (hash_flds & IECM_ADV_RSS_HASH_FLD_SCTP_SRC_PORT)
+		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, SCTP, SRC_PORT);
+
+	if (hash_flds & IECM_ADV_RSS_HASH_FLD_SCTP_DST_PORT)
+		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, SCTP, DST_PORT);
+}
+
+/**
+ * iecm_fill_adv_rss_cfg_msg - fill the RSS configuration into virtchnl message
+ * @rss_cfg: the virtchnl message to be filled with RSS configuration setting
+ * @packet_hdrs: the RSS configuration protocol header types
+ * @hash_flds: the RSS configuration protocol hash fields
+ *
+ * Returns 0 if the RSS configuration virtchnl message is filled successfully
+ */
+static int
+iecm_fill_adv_rss_cfg_msg(struct virtchnl_rss_cfg *rss_cfg,
+			  u32 packet_hdrs, u64 hash_flds)
+{
+	struct virtchnl_proto_hdrs *proto_hdrs = &rss_cfg->proto_hdrs;
+	struct virtchnl_proto_hdr *hdr;
+
+	rss_cfg->rss_algorithm = VIRTCHNL_RSS_ALG_TOEPLITZ_ASYMMETRIC;
+
+	proto_hdrs->tunnel_level = 0;	/* always outer layer */
+
+	hdr = &proto_hdrs->proto_hdr[proto_hdrs->count++];
+	switch (packet_hdrs & IECM_ADV_RSS_FLOW_SEG_HDR_L3) {
+	case IECM_ADV_RSS_FLOW_SEG_HDR_IPV4:
+		iecm_fill_adv_rss_ip4_hdr(hdr, hash_flds);
+		break;
+	case IECM_ADV_RSS_FLOW_SEG_HDR_IPV6:
+		iecm_fill_adv_rss_ip6_hdr(hdr, hash_flds);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	hdr = &proto_hdrs->proto_hdr[proto_hdrs->count++];
+	switch (packet_hdrs & IECM_ADV_RSS_FLOW_SEG_HDR_L4) {
+	case IECM_ADV_RSS_FLOW_SEG_HDR_TCP:
+		iecm_fill_adv_rss_tcp_hdr(hdr, hash_flds);
+		break;
+	case IECM_ADV_RSS_FLOW_SEG_HDR_UDP:
+		iecm_fill_adv_rss_udp_hdr(hdr, hash_flds);
+		break;
+	case IECM_ADV_RSS_FLOW_SEG_HDR_SCTP:
+		iecm_fill_adv_rss_sctp_hdr(hdr, hash_flds);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/**
+ * iecm_find_adv_rss_cfg_by_hdrs - find RSS configuration with header type
+ * @vport: vport structure
+ * @packet_hdrs: protocol header type to find.
+ *
+ * Returns pointer to advance RSS configuration if found or null
+ */
+static struct iecm_adv_rss *
+iecm_find_adv_rss_cfg_by_hdrs(struct iecm_vport *vport, u32 packet_hdrs)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	struct iecm_adv_rss *rss;
+
+	list_for_each_entry(rss, &adapter->config_data.adv_rss_list, list)
+		if (rss->packet_hdrs == packet_hdrs)
+			return rss;
+
+	return NULL;
+}
+
+/**
+ * iecm_dump_adv_rss_cfg_info
+ * @vport: vport structure
+ * @packet_hdrs: The protocol headers for RSS configuration
+ * @hash_flds: The protocol hash fields for RSS configuration
+ * @prefix: the prefix string description to dump the RSS
+ * @postfix: the postfix string description to dump the RSS
+ *
+ * Dump the advance RSS configuration
+ **/
+static void
+iecm_dump_adv_rss_cfg_info(struct iecm_vport *vport,
+			   u32 packet_hdrs, u64 hash_flds,
+			   const char *prefix, const char *postfix)
+{
+	static char hash_opt[300];
+	const char *proto;
+
+	if (packet_hdrs & IECM_ADV_RSS_FLOW_SEG_HDR_TCP)
+		proto = "TCP";
+	else if (packet_hdrs & IECM_ADV_RSS_FLOW_SEG_HDR_UDP)
+		proto = "UDP";
+	else if (packet_hdrs & IECM_ADV_RSS_FLOW_SEG_HDR_SCTP)
+		proto = "SCTP";
+	else
+		return;
+
+	memset(hash_opt, 0, sizeof(hash_opt));
+
+	strcat(hash_opt, proto);
+	if (packet_hdrs & IECM_ADV_RSS_FLOW_SEG_HDR_IPV4)
+		strcat(hash_opt, "v4 ");
+	else
+		strcat(hash_opt, "v6 ");
+
+	if (hash_flds & (IECM_ADV_RSS_HASH_FLD_IPV4_SA |
+			 IECM_ADV_RSS_HASH_FLD_IPV6_SA))
+		strcat(hash_opt, "[IP SA] ");
+	if (hash_flds & (IECM_ADV_RSS_HASH_FLD_IPV4_DA |
+			 IECM_ADV_RSS_HASH_FLD_IPV6_DA))
+		strcat(hash_opt, "[IP DA] ");
+	if (hash_flds & (IECM_ADV_RSS_HASH_FLD_TCP_SRC_PORT |
+			 IECM_ADV_RSS_HASH_FLD_UDP_SRC_PORT |
+			 IECM_ADV_RSS_HASH_FLD_SCTP_SRC_PORT))
+		strcat(hash_opt, "[src port] ");
+	if (hash_flds & (IECM_ADV_RSS_HASH_FLD_TCP_DST_PORT |
+			 IECM_ADV_RSS_HASH_FLD_UDP_DST_PORT |
+			 IECM_ADV_RSS_HASH_FLD_SCTP_DST_PORT))
+		strcat(hash_opt, "[dst port] ");
+
+	if (!prefix)
+		prefix = "";
+
+	if (!postfix)
+		postfix = "";
+
+	dev_info(&vport->adapter->pdev->dev, "%s %s %s\n",
+		 prefix, hash_opt, postfix);
+}
+
+/**
+ * iecm_adv_rss_parse_hdrs - parses headers from RSS hash input
+ * @cmd: ethtool rxnfc command
+ *
+ * This function parses the rxnfc command and returns intended
+ * header types for RSS configuration
+ */
+static u32 iecm_adv_rss_parse_hdrs(struct ethtool_rxnfc *cmd)
+{
+	u32 hdrs = IECM_ADV_RSS_FLOW_SEG_HDR_NONE;
+
+	switch (cmd->flow_type) {
+	case TCP_V4_FLOW:
+		hdrs |= IECM_ADV_RSS_FLOW_SEG_HDR_TCP |
+			IECM_ADV_RSS_FLOW_SEG_HDR_IPV4;
+		break;
+	case UDP_V4_FLOW:
+		hdrs |= IECM_ADV_RSS_FLOW_SEG_HDR_UDP |
+			IECM_ADV_RSS_FLOW_SEG_HDR_IPV4;
+		break;
+	case SCTP_V4_FLOW:
+		hdrs |= IECM_ADV_RSS_FLOW_SEG_HDR_SCTP |
+			IECM_ADV_RSS_FLOW_SEG_HDR_IPV4;
+		break;
+	case TCP_V6_FLOW:
+		hdrs |= IECM_ADV_RSS_FLOW_SEG_HDR_TCP |
+			IECM_ADV_RSS_FLOW_SEG_HDR_IPV6;
+		break;
+	case UDP_V6_FLOW:
+		hdrs |= IECM_ADV_RSS_FLOW_SEG_HDR_UDP |
+			IECM_ADV_RSS_FLOW_SEG_HDR_IPV6;
+		break;
+	case SCTP_V6_FLOW:
+		hdrs |= IECM_ADV_RSS_FLOW_SEG_HDR_SCTP |
+			IECM_ADV_RSS_FLOW_SEG_HDR_IPV6;
+		break;
+	default:
+		break;
+	}
+
+	return hdrs;
+}
+
+/**
+ * iecm_adv_rss_parse_hash_flds - parses hash fields from RSS hash input
+ * @cmd: ethtool rxnfc command
+ *
+ * This function parses the rxnfc command and returns intended hash fields for
+ * RSS configuration
+ */
+static u64 iecm_adv_rss_parse_hash_flds(struct ethtool_rxnfc *cmd)
+{
+	u64 hfld = IECM_ADV_RSS_HASH_INVALID;
+
+	if (cmd->data & RXH_IP_SRC || cmd->data & RXH_IP_DST) {
+		switch (cmd->flow_type) {
+		case TCP_V4_FLOW:
+		case UDP_V4_FLOW:
+		case SCTP_V4_FLOW:
+			if (cmd->data & RXH_IP_SRC)
+				hfld |= IECM_ADV_RSS_HASH_FLD_IPV4_SA;
+			if (cmd->data & RXH_IP_DST)
+				hfld |= IECM_ADV_RSS_HASH_FLD_IPV4_DA;
+			break;
+		case TCP_V6_FLOW:
+		case UDP_V6_FLOW:
+		case SCTP_V6_FLOW:
+			if (cmd->data & RXH_IP_SRC)
+				hfld |= IECM_ADV_RSS_HASH_FLD_IPV6_SA;
+			if (cmd->data & RXH_IP_DST)
+				hfld |= IECM_ADV_RSS_HASH_FLD_IPV6_DA;
+			break;
+		default:
+			break;
+		}
+	}
+
+	if (cmd->data & RXH_L4_B_0_1 || cmd->data & RXH_L4_B_2_3) {
+		switch (cmd->flow_type) {
+		case TCP_V4_FLOW:
+		case TCP_V6_FLOW:
+			if (cmd->data & RXH_L4_B_0_1)
+				hfld |= IECM_ADV_RSS_HASH_FLD_TCP_SRC_PORT;
+			if (cmd->data & RXH_L4_B_2_3)
+				hfld |= IECM_ADV_RSS_HASH_FLD_TCP_DST_PORT;
+			break;
+		case UDP_V4_FLOW:
+		case UDP_V6_FLOW:
+			if (cmd->data & RXH_L4_B_0_1)
+				hfld |= IECM_ADV_RSS_HASH_FLD_UDP_SRC_PORT;
+			if (cmd->data & RXH_L4_B_2_3)
+				hfld |= IECM_ADV_RSS_HASH_FLD_UDP_DST_PORT;
+			break;
+		case SCTP_V4_FLOW:
+		case SCTP_V6_FLOW:
+			if (cmd->data & RXH_L4_B_0_1)
+				hfld |= IECM_ADV_RSS_HASH_FLD_SCTP_SRC_PORT;
+			if (cmd->data & RXH_L4_B_2_3)
+				hfld |= IECM_ADV_RSS_HASH_FLD_SCTP_DST_PORT;
+			break;
+		default:
+			break;
+		}
+	}
+
+	return hfld;
+}
+
+/**
+ * iecm_set_adv_rss_hash_opt - Enable/Disable flow types for RSS hash
+ * @vport: vport structure
+ * @cmd: ethtool rxnfc command
+ *
+ * Returns Success if the flow input set is supported.
+ */
+int
+iecm_set_adv_rss_hash_opt(struct iecm_vport *vport, struct ethtool_rxnfc *cmd)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	struct iecm_adv_rss *rss, *rss_new;
+	u64 hash_flds;
+	u32 hdrs;
+	int err;
+
+	if (adapter->state != __IECM_UP)
+		return -EIO;
+
+	if (!iecm_is_cap_ena(adapter, IECM_OTHER_CAPS, VIRTCHNL2_CAP_ADV_RSS))
+		return -EOPNOTSUPP;
+
+	hdrs = iecm_adv_rss_parse_hdrs(cmd);
+	if (hdrs == IECM_ADV_RSS_FLOW_SEG_HDR_NONE)
+		return -EINVAL;
+
+	hash_flds = iecm_adv_rss_parse_hash_flds(cmd);
+	if (hash_flds == IECM_ADV_RSS_HASH_INVALID)
+		return -EINVAL;
+
+	rss_new = kzalloc(sizeof(*rss_new), GFP_KERNEL);
+	if (!rss_new)
+		return -ENOMEM;
+
+	/* Since this can fail, do it now to avoid dirtying the list, we'll
+	 * copy it from rss_new if it turns out we're updating an existing
+	 * filter instead of adding a new one.
+	 */
+	if (iecm_fill_adv_rss_cfg_msg(&rss_new->cfg_msg, hdrs, hash_flds)) {
+		kfree(rss_new);
+		return -EINVAL;
+	}
+
+	iecm_dump_adv_rss_cfg_info(vport, hdrs, hash_flds,
+				   "Input set change for", "is pending");
+
+	spin_lock_bh(&adapter->adv_rss_list_lock);
+	rss = iecm_find_adv_rss_cfg_by_hdrs(vport, hdrs);
+	if (rss) {
+		if (rss->hash_flds != hash_flds) {
+			rss->remove = false;
+			memcpy(&rss->cfg_msg, &rss_new->cfg_msg,
+			       sizeof(rss_new->cfg_msg));
+			kfree(rss_new);
+		} else {
+			kfree(rss_new);
+			spin_unlock_bh(&adapter->adv_rss_list_lock);
+			return -EEXIST;
+		}
+	} else {
+		rss = rss_new;
+		rss->packet_hdrs = hdrs;
+		list_add_tail(&rss->list, &adapter->config_data.adv_rss_list);
+	}
+	rss->add = true;
+	rss->hash_flds = hash_flds;
+	spin_unlock_bh(&adapter->adv_rss_list_lock);
+
+	err = iecm_send_add_del_adv_rss_cfg_msg(vport, true);
+	if (err) {
+		spin_lock_bh(&adapter->adv_rss_list_lock);
+		/* We have to find it again to make sure another thread hasn't
+		 * already deleted and kfreed it.
+		 */
+		rss = iecm_find_adv_rss_cfg_by_hdrs(vport, hdrs);
+		if (rss) {
+			list_del(&rss->list);
+			kfree(rss);
+		}
+		spin_unlock_bh(&adapter->adv_rss_list_lock);
+	}
+
+	if (!err)
+		iecm_dump_adv_rss_cfg_info(vport, hdrs, hash_flds,
+					   "Input set change for",
+					   "successful");
+	else
+		iecm_dump_adv_rss_cfg_info(vport, hdrs, hash_flds,
+					   "Failed to change the input set for",
+					   NULL);
+
+	return err;
+}
+
+/**
+ * iecm_get_adv_rss_hash_opt - Retrieve hash fields for a given flow-type
+ * @vport: vport structure
+ * @cmd: ethtool rxnfc command
+ *
+ * Returns Success if the flow input set is supported.
+ */
+int
+iecm_get_adv_rss_hash_opt(struct iecm_vport *vport, struct ethtool_rxnfc *cmd)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	struct iecm_adv_rss *rss;
+	u64 hash_flds;
+	u32 hdrs;
+
+	if (adapter->state != __IECM_UP)
+		return -EIO;
+
+	if (!iecm_is_cap_ena(adapter, IECM_OTHER_CAPS, VIRTCHNL2_CAP_ADV_RSS))
+		return -EOPNOTSUPP;
+
+	cmd->data = 0;
+
+	hdrs = iecm_adv_rss_parse_hdrs(cmd);
+	if (hdrs == IECM_ADV_RSS_FLOW_SEG_HDR_NONE)
+		return -EINVAL;
+
+	spin_lock_bh(&adapter->adv_rss_list_lock);
+	rss = iecm_find_adv_rss_cfg_by_hdrs(vport, hdrs);
+	if (rss)
+		hash_flds = rss->hash_flds;
+	else
+		hash_flds = IECM_ADV_RSS_HASH_INVALID;
+	spin_unlock_bh(&adapter->adv_rss_list_lock);
+
+	if (hash_flds == IECM_ADV_RSS_HASH_INVALID)
+		return -EINVAL;
+
+	if (hash_flds & (IECM_ADV_RSS_HASH_FLD_IPV4_SA |
+			 IECM_ADV_RSS_HASH_FLD_IPV6_SA))
+		cmd->data |= (u64)RXH_IP_SRC;
+
+	if (hash_flds & (IECM_ADV_RSS_HASH_FLD_IPV4_DA |
+			 IECM_ADV_RSS_HASH_FLD_IPV6_DA))
+		cmd->data |= (u64)RXH_IP_DST;
+
+	if (hash_flds & (IECM_ADV_RSS_HASH_FLD_TCP_SRC_PORT |
+			 IECM_ADV_RSS_HASH_FLD_UDP_SRC_PORT |
+			 IECM_ADV_RSS_HASH_FLD_SCTP_SRC_PORT))
+		cmd->data |= (u64)RXH_L4_B_0_1;
+
+	if (hash_flds & (IECM_ADV_RSS_HASH_FLD_TCP_DST_PORT |
+			 IECM_ADV_RSS_HASH_FLD_UDP_DST_PORT |
+			 IECM_ADV_RSS_HASH_FLD_SCTP_DST_PORT))
+		cmd->data |= (u64)RXH_L4_B_2_3;
+
+	return 0;
+}
+
 /**
  * iecm_pkt_udp_no_pay_len - the length of UDP packet without payload
  * @fltr: Flow Director filter data structure
diff --git a/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
index 94af45c36866..c05baf12515c 100644
--- a/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
+++ b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
@@ -2799,6 +2799,77 @@ int iecm_send_add_del_cloud_filter_msg(struct iecm_vport *vport, bool add)
 	return err;
 }
 
+/**
+ * iecm_send_add_del_adv_rss_cfg_msg: Send add/del RSS configuration message
+ * @vport: vport structure
+ * @add: True to add, false to delete RSS configuration
+ *
+ * Request the CP/PF to add/del RSS configuration as specified by the user via
+ * ethtool
+ *
+ * Return 0 on success, negative on failure
+ **/
+int iecm_send_add_del_adv_rss_cfg_msg(struct iecm_vport *vport, bool add)
+{
+	struct iecm_adapter *adapter = vport->adapter;
+	struct virtchnl_rss_cfg *rss_cfg;
+	struct iecm_adv_rss *rss;
+	int len, err = -ENXIO;
+
+	len = sizeof(struct virtchnl_rss_cfg);
+	rss_cfg = kzalloc(len, GFP_KERNEL);
+	if (!rss_cfg)
+		return -ENOMEM;
+
+	while (true) {
+		bool process_rss = false;
+
+		spin_lock_bh(&adapter->adv_rss_list_lock);
+		list_for_each_entry(rss, &adapter->config_data.adv_rss_list, list) {
+			if (add && rss->add) {
+				/* Only add needs print the RSS information */
+				process_rss = true;
+				rss->add = false;
+				memcpy(rss_cfg, &rss->cfg_msg, len);
+				break;
+			} else if (!add && rss->remove) {
+				process_rss = true;
+				rss->remove = false;
+				memcpy(rss_cfg, &rss->cfg_msg, len);
+				break;
+			}
+		}
+		spin_unlock_bh(&adapter->adv_rss_list_lock);
+
+		/* Don't send mailbox message when there are no RSS to add/del */
+		if (!process_rss)
+			break;
+
+		if (add) {
+			err = iecm_send_mb_msg(adapter, VIRTCHNL_OP_ADD_RSS_CFG,
+					       len, (u8 *)rss_cfg);
+			if (err)
+				break;
+
+			err = iecm_wait_for_event(adapter, IECM_VC_ADD_RSS_CFG,
+						  IECM_VC_ADD_RSS_CFG_ERR);
+		} else {
+			err = iecm_send_mb_msg(adapter, VIRTCHNL_OP_DEL_RSS_CFG,
+					       len, (u8 *)rss_cfg);
+			if (err)
+				break;
+
+			err = iecm_min_wait_for_event(adapter, IECM_VC_DEL_RSS_CFG,
+						      IECM_VC_DEL_RSS_CFG_ERR);
+		}
+		if (err)
+			break;
+	}
+
+	kfree(rss_cfg);
+	return err;
+}
+
 /**
  * iecm_send_add_fdir_filter_msg: Send add Flow Director filter message
  * @vport: vport structure
diff --git a/drivers/net/ethernet/intel/include/iecm.h b/drivers/net/ethernet/intel/include/iecm.h
index 0aab41cf982c..c7be8c88f9b3 100644
--- a/drivers/net/ethernet/intel/include/iecm.h
+++ b/drivers/net/ethernet/intel/include/iecm.h
@@ -432,6 +432,74 @@ struct iecm_channel_config {
 	u8 num_tc;
 };
 
+enum iecm_adv_rss_flow_seg_hdr {
+	IECM_ADV_RSS_FLOW_SEG_HDR_NONE	= 0x00000000,
+	IECM_ADV_RSS_FLOW_SEG_HDR_IPV4	= 0x00000001,
+	IECM_ADV_RSS_FLOW_SEG_HDR_IPV6	= 0x00000002,
+	IECM_ADV_RSS_FLOW_SEG_HDR_TCP	= 0x00000004,
+	IECM_ADV_RSS_FLOW_SEG_HDR_UDP	= 0x00000008,
+	IECM_ADV_RSS_FLOW_SEG_HDR_SCTP	= 0x00000010,
+};
+
+#define IECM_ADV_RSS_FLOW_SEG_HDR_L3		\
+	(IECM_ADV_RSS_FLOW_SEG_HDR_IPV4	|	\
+	 IECM_ADV_RSS_FLOW_SEG_HDR_IPV6)
+
+#define IECM_ADV_RSS_FLOW_SEG_HDR_L4		\
+	(IECM_ADV_RSS_FLOW_SEG_HDR_TCP |	\
+	 IECM_ADV_RSS_FLOW_SEG_HDR_UDP |	\
+	 IECM_ADV_RSS_FLOW_SEG_HDR_SCTP)
+
+enum iecm_adv_rss_flow_field {
+	/* L3 */
+	IECM_ADV_RSS_FLOW_FIELD_IDX_IPV4_SA,
+	IECM_ADV_RSS_FLOW_FIELD_IDX_IPV4_DA,
+	IECM_ADV_RSS_FLOW_FIELD_IDX_IPV6_SA,
+	IECM_ADV_RSS_FLOW_FIELD_IDX_IPV6_DA,
+	/* L4 */
+	IECM_ADV_RSS_FLOW_FIELD_IDX_TCP_SRC_PORT,
+	IECM_ADV_RSS_FLOW_FIELD_IDX_TCP_DST_PORT,
+	IECM_ADV_RSS_FLOW_FIELD_IDX_UDP_SRC_PORT,
+	IECM_ADV_RSS_FLOW_FIELD_IDX_UDP_DST_PORT,
+	IECM_ADV_RSS_FLOW_FIELD_IDX_SCTP_SRC_PORT,
+	IECM_ADV_RSS_FLOW_FIELD_IDX_SCTP_DST_PORT,
+
+	/* The total number of enums must not exceed 64 */
+	IECM_ADV_RSS_FLOW_FIELD_IDX_MAX
+};
+
+#define IECM_ADV_RSS_HASH_INVALID	0
+#define IECM_ADV_RSS_HASH_FLD_IPV4_SA	\
+	BIT_ULL(IECM_ADV_RSS_FLOW_FIELD_IDX_IPV4_SA)
+#define IECM_ADV_RSS_HASH_FLD_IPV6_SA	\
+	BIT_ULL(IECM_ADV_RSS_FLOW_FIELD_IDX_IPV6_SA)
+#define IECM_ADV_RSS_HASH_FLD_IPV4_DA	\
+	BIT_ULL(IECM_ADV_RSS_FLOW_FIELD_IDX_IPV4_DA)
+#define IECM_ADV_RSS_HASH_FLD_IPV6_DA	\
+	BIT_ULL(IECM_ADV_RSS_FLOW_FIELD_IDX_IPV6_DA)
+#define IECM_ADV_RSS_HASH_FLD_TCP_SRC_PORT	\
+	BIT_ULL(IECM_ADV_RSS_FLOW_FIELD_IDX_TCP_SRC_PORT)
+#define IECM_ADV_RSS_HASH_FLD_TCP_DST_PORT	\
+	BIT_ULL(IECM_ADV_RSS_FLOW_FIELD_IDX_TCP_DST_PORT)
+#define IECM_ADV_RSS_HASH_FLD_UDP_SRC_PORT	\
+	BIT_ULL(IECM_ADV_RSS_FLOW_FIELD_IDX_UDP_SRC_PORT)
+#define IECM_ADV_RSS_HASH_FLD_UDP_DST_PORT	\
+	BIT_ULL(IECM_ADV_RSS_FLOW_FIELD_IDX_UDP_DST_PORT)
+#define IECM_ADV_RSS_HASH_FLD_SCTP_SRC_PORT	\
+	BIT_ULL(IECM_ADV_RSS_FLOW_FIELD_IDX_SCTP_SRC_PORT)
+#define IECM_ADV_RSS_HASH_FLD_SCTP_DST_PORT	\
+	BIT_ULL(IECM_ADV_RSS_FLOW_FIELD_IDX_SCTP_DST_PORT)
+
+/* bookkeeping of advanced RSS configuration */
+struct iecm_adv_rss {
+	struct list_head list;
+	u32 packet_hdrs;
+	u64 hash_flds;
+	struct virtchnl_rss_cfg cfg_msg;
+	bool remove;	/* RSS filter needs to be deleted */
+	bool add;	/* RSS filter needs to be added */
+};
+
 enum iecm_fdir_flow_type {
 	/* NONE - used for undef/error */
 	IECM_FDIR_FLOW_NONE = 0,
@@ -878,6 +946,11 @@ void iecm_vport_set_hsplit(struct iecm_vport *vport, bool ena);
 void iecm_add_del_ether_addrs(struct iecm_vport *vport, bool add, bool async);
 int iecm_set_promiscuous(struct iecm_adapter *adapter);
 int iecm_send_add_del_cloud_filter_msg(struct iecm_vport *vport, bool add);
+int iecm_send_add_del_adv_rss_cfg_msg(struct iecm_vport *vport, bool add);
+int iecm_set_adv_rss_hash_opt(struct iecm_vport *vport,
+			      struct ethtool_rxnfc *cmd);
+int iecm_get_adv_rss_hash_opt(struct iecm_vport *vport,
+			      struct ethtool_rxnfc *cmd);
 int iecm_send_add_fdir_filter_msg(struct iecm_vport *vport);
 int iecm_send_del_fdir_filter_msg(struct iecm_vport *vport);
 int iecm_get_fdir_fltr_entry(struct iecm_vport *vport,
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 19/19] idpf: introduce idpf driver
  2022-01-28  0:09 [Intel-wired-lan] [PATCH net-next 00/19] Add iecm and idpf Alan Brady
                   ` (17 preceding siblings ...)
  2022-01-28  0:10 ` [Intel-wired-lan] [PATCH net-next 18/19] iecm: add advanced rss Alan Brady
@ 2022-01-28  0:10 ` Alan Brady
  2022-01-28 20:08   ` Alexander Lobakin
  2022-02-04 12:05 ` [Intel-wired-lan] [PATCH net-next 00/19] Add iecm and idpf Alexander Lobakin
  19 siblings, 1 reply; 89+ messages in thread
From: Alan Brady @ 2022-01-28  0:10 UTC (permalink / raw)
  To: intel-wired-lan

This adds the idpf driver which uses the iecm module to provide common
functionality. Device specific behavior and registers are defined here and
handed off to iecm which takes over the rest of the flow.

Signed-off-by: Phani Burra <phani.r.burra@intel.com>
Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
Signed-off-by: Alan Brady <alan.brady@intel.com>
---
 .../device_drivers/ethernet/intel/idpf.rst    |  47 ++++++
 drivers/net/ethernet/intel/Kconfig            |  16 ++
 drivers/net/ethernet/intel/Makefile           |   1 +
 drivers/net/ethernet/intel/idpf/Makefile      |  15 ++
 drivers/net/ethernet/intel/idpf/idpf_dev.h    |  17 +++
 drivers/net/ethernet/intel/idpf/idpf_devids.h |  10 ++
 drivers/net/ethernet/intel/idpf/idpf_main.c   | 140 ++++++++++++++++++
 drivers/net/ethernet/intel/idpf/idpf_reg.c    | 130 ++++++++++++++++
 .../ethernet/intel/include/iecm_lan_pf_regs.h | 131 ++++++++++++++++
 9 files changed, 507 insertions(+)
 create mode 100644 Documentation/networking/device_drivers/ethernet/intel/idpf.rst
 create mode 100644 drivers/net/ethernet/intel/idpf/Makefile
 create mode 100644 drivers/net/ethernet/intel/idpf/idpf_dev.h
 create mode 100644 drivers/net/ethernet/intel/idpf/idpf_devids.h
 create mode 100644 drivers/net/ethernet/intel/idpf/idpf_main.c
 create mode 100644 drivers/net/ethernet/intel/idpf/idpf_reg.c
 create mode 100644 drivers/net/ethernet/intel/include/iecm_lan_pf_regs.h

diff --git a/Documentation/networking/device_drivers/ethernet/intel/idpf.rst b/Documentation/networking/device_drivers/ethernet/intel/idpf.rst
new file mode 100644
index 000000000000..973fa9613428
--- /dev/null
+++ b/Documentation/networking/device_drivers/ethernet/intel/idpf.rst
@@ -0,0 +1,47 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+==================================================================
+Linux Base Driver for the Intel(R) Smart Network Adapter Family Series
+==================================================================
+
+Intel idpf Linux driver.
+Copyright(c) 2020 Intel Corporation.
+
+Contents
+========
+
+- Enabling the driver
+- Support
+
+The driver in this release supports Intel's Smart Network Adapter Family Series
+of products. For more information, visit Intel's support page at
+https://support.intel.com.
+
+Enabling the driver
+===================
+The driver is enabled via the standard kernel configuration system,
+using the make command::
+
+  make oldconfig/menuconfig/etc.
+
+The driver is located in the menu structure at:
+
+  -> Device Drivers
+    -> Network device support (NETDEVICES [=y])
+      -> Ethernet driver support
+        -> Intel devices
+          -> Intel(R) Smart Network Adapter Family Series Support
+
+Support
+=======
+For general information, go to the Intel support website at:
+
+https://www.intel.com/support/
+
+or the Intel Wired Networking project hosted by Sourceforge at:
+
+https://sourceforge.net/projects/e1000
+
+If an issue is identified with the released source code on a supported kernel
+with a supported adapter, email the specific information related to the issue
+to e1000-devel at lists.sf.net.
diff --git a/drivers/net/ethernet/intel/Kconfig b/drivers/net/ethernet/intel/Kconfig
index 754dc7677ad5..93c8883c22ad 100644
--- a/drivers/net/ethernet/intel/Kconfig
+++ b/drivers/net/ethernet/intel/Kconfig
@@ -387,4 +387,20 @@ config IECM
       To compile this as a module, choose M here. The module will be called
       iecm.
 
+config IDPF
+	tristate "Intel(R) Data Plane Function Support"
+	default n
+	depends on IECM
+	help
+	  For more information on how to identify your adapter, go
+	  to the Adapter & Driver ID Guide that can be located at:
+
+	  <http://support.intel.com>
+
+	  More specific information on configuring the driver is in
+	  <file:Documentation/networking/device_drivers/ethernet/intel/idpf.rst>.
+
+	  To compile this driver as a module, choose M here. The module
+	  will be called idpf.
+
 endif # NET_VENDOR_INTEL
diff --git a/drivers/net/ethernet/intel/Makefile b/drivers/net/ethernet/intel/Makefile
index c9eba9cc5087..3786c2269f3d 100644
--- a/drivers/net/ethernet/intel/Makefile
+++ b/drivers/net/ethernet/intel/Makefile
@@ -17,3 +17,4 @@ obj-$(CONFIG_IAVF) += iavf/
 obj-$(CONFIG_FM10K) += fm10k/
 obj-$(CONFIG_ICE) += ice/
 obj-$(CONFIG_IECM) += iecm/
+obj-$(CONFIG_IDPF) += idpf/
diff --git a/drivers/net/ethernet/intel/idpf/Makefile b/drivers/net/ethernet/intel/idpf/Makefile
new file mode 100644
index 000000000000..85846620bc9f
--- /dev/null
+++ b/drivers/net/ethernet/intel/idpf/Makefile
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0-only
+# Copyright (C) 2019 Intel Corporation
+
+#
+# Makefile for the Intel(R) Data Plane Function Linux Driver
+#
+
+obj-$(CONFIG_IDPF) += idpf.o
+
+ccflags-y += -I$(srctree)/drivers/net/ethernet/intel/include \
+			 -I$(srctree)/include/linux/avf
+
+idpf-y := \
+	idpf_main.o \
+	idpf_reg.o
diff --git a/drivers/net/ethernet/intel/idpf/idpf_dev.h b/drivers/net/ethernet/intel/idpf/idpf_dev.h
new file mode 100644
index 000000000000..dc146161f884
--- /dev/null
+++ b/drivers/net/ethernet/intel/idpf/idpf_dev.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (C) 2019 Intel Corporation */
+
+#ifndef _IDPF_DEV_H_
+#define _IDPF_DEV_H_
+
+#include "iecm.h"
+
+int idpf_intr_reg_init(struct iecm_vport *vport);
+void idpf_mb_intr_reg_init(struct iecm_adapter *adapter);
+void idpf_reset_reg_init(struct iecm_reset_reg *reset_reg);
+void idpf_trigger_reset(struct iecm_adapter *adapter,
+			enum iecm_flags trig_cause);
+void idpf_vportq_reg_init(struct iecm_vport *vport);
+void idpf_ctlq_reg_init(struct iecm_ctlq_create_info *cq);
+
+#endif /* _IDPF_DEV_H_ */
diff --git a/drivers/net/ethernet/intel/idpf/idpf_devids.h b/drivers/net/ethernet/intel/idpf/idpf_devids.h
new file mode 100644
index 000000000000..7bf8eb64b76a
--- /dev/null
+++ b/drivers/net/ethernet/intel/idpf/idpf_devids.h
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (C) 2019 Intel Corporation */
+
+#ifndef _IDPF_DEVIDS_H_
+#define _IDPF_DEVIDS_H_
+
+/* Device IDs */
+#define IDPF_DEV_ID_PF			0x1452
+
+#endif /* _IDPF_DEVIDS_H_ */
diff --git a/drivers/net/ethernet/intel/idpf/idpf_main.c b/drivers/net/ethernet/intel/idpf/idpf_main.c
new file mode 100644
index 000000000000..da5e668beabf
--- /dev/null
+++ b/drivers/net/ethernet/intel/idpf/idpf_main.c
@@ -0,0 +1,140 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (C) 2019 Intel Corporation */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include "idpf_dev.h"
+#include "idpf_devids.h"
+
+#define DRV_SUMMARY	"Intel(R) Data Plane Function Linux Driver"
+static const char idpf_driver_string[] = DRV_SUMMARY;
+static const char idpf_copyright[] = "Copyright (c) 2020, Intel Corporation.";
+
+MODULE_DESCRIPTION(DRV_SUMMARY);
+MODULE_LICENSE("GPL");
+
+/**
+ * idpf_reg_ops_init - Initialize register API function pointers
+ * @adapter: Driver specific private structure
+ */
+static void idpf_reg_ops_init(struct iecm_adapter *adapter)
+{
+	adapter->dev_ops.reg_ops.ctlq_reg_init = idpf_ctlq_reg_init;
+	adapter->dev_ops.reg_ops.intr_reg_init = idpf_intr_reg_init;
+	adapter->dev_ops.reg_ops.mb_intr_reg_init = idpf_mb_intr_reg_init;
+	adapter->dev_ops.reg_ops.reset_reg_init = idpf_reset_reg_init;
+	adapter->dev_ops.reg_ops.trigger_reset = idpf_trigger_reset;
+}
+
+/**
+ * idpf_probe - Device initialization routine
+ * @pdev: PCI device information struct
+ * @ent: entry in idpf_pci_tbl
+ *
+ * Returns 0 on success, negative on failure
+ */
+static int idpf_probe(struct pci_dev *pdev,
+		      const struct pci_device_id __always_unused *ent)
+{
+	struct iecm_adapter *adapter = NULL;
+	int err;
+
+	adapter = kzalloc(sizeof(*adapter), GFP_KERNEL);
+	if (!adapter)
+		return -ENOMEM;
+
+	adapter->dev_ops.reg_ops_init = idpf_reg_ops_init;
+	set_bit(__IECM_REQ_TX_SPLITQ, adapter->flags);
+	set_bit(__IECM_REQ_RX_SPLITQ, adapter->flags);
+
+	err = iecm_probe(pdev, ent, adapter);
+	if (err)
+		kfree(adapter);
+
+	return err;
+}
+
+/**
+ * idpf_remove - Device removal routine
+ * @pdev: PCI device information struct
+ */
+static void idpf_remove(struct pci_dev *pdev)
+{
+	struct iecm_adapter *adapter = pci_get_drvdata(pdev);
+
+	if (!adapter)
+		return;
+
+	iecm_remove(pdev);
+	pci_set_drvdata(pdev, NULL);
+	kfree(adapter);
+}
+
+/**
+ * idpf_shutdown - PCI callback for shutting down device
+ * @pdev: PCI device information struct
+ */
+static void idpf_shutdown(struct pci_dev *pdev)
+{
+	idpf_remove(pdev);
+
+	if (system_state == SYSTEM_POWER_OFF)
+		pci_set_power_state(pdev, PCI_D3hot);
+}
+
+/* idpf_pci_tbl - PCI Dev iapf ID Table
+ *
+ * Wildcard entries (PCI_ANY_ID) should come last
+ * Last entry must be all 0s
+ *
+ * { Vendor ID, Device ID, SubVendor ID, SubDevice ID,
+ *   Class, Class Mask, private data (not used) }
+ */
+static const struct pci_device_id idpf_pci_tbl[] = {
+	{ PCI_VDEVICE(INTEL, IDPF_DEV_ID_PF), 0 },
+	/* required last entry */
+	{ 0, }
+};
+MODULE_DEVICE_TABLE(pci, idpf_pci_tbl);
+
+static struct pci_driver idpf_driver = {
+	.name = KBUILD_MODNAME,
+	.id_table = idpf_pci_tbl,
+	.probe = idpf_probe,
+	.remove = idpf_remove,
+	.shutdown = idpf_shutdown,
+};
+
+/**
+ * idpf_module_init - Driver registration routine
+ *
+ * idpf_module_init is the first routine called when the driver is
+ * loaded. All it does is register with the PCI subsystem.
+ */
+static int __init idpf_module_init(void)
+{
+	int status;
+
+	pr_info("%s - version %d\n", idpf_driver_string, LINUX_VERSION_CODE);
+	pr_info("%s\n", idpf_copyright);
+
+	status = pci_register_driver(&idpf_driver);
+	if (status)
+		pr_err("failed to register pci driver, err %d\n", status);
+
+	return status;
+}
+module_init(idpf_module_init);
+
+/**
+ * idpf_module_exit - Driver exit cleanup routine
+ *
+ * idpf_module_exit is called just before the driver is removed
+ * from memory.
+ */
+static void __exit idpf_module_exit(void)
+{
+	pci_unregister_driver(&idpf_driver);
+	pr_info("module unloaded\n");
+}
+module_exit(idpf_module_exit);
diff --git a/drivers/net/ethernet/intel/idpf/idpf_reg.c b/drivers/net/ethernet/intel/idpf/idpf_reg.c
new file mode 100644
index 000000000000..d0ea6c495c62
--- /dev/null
+++ b/drivers/net/ethernet/intel/idpf/idpf_reg.c
@@ -0,0 +1,130 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright (C) 2019 Intel Corporation */
+
+#include "idpf_dev.h"
+#include "iecm_lan_pf_regs.h"
+
+/**
+ * idpf_ctlq_reg_init - initialize default mailbox registers
+ * @cq: pointer to the array of create control queues
+ */
+void idpf_ctlq_reg_init(struct iecm_ctlq_create_info *cq)
+{
+	int i;
+
+#define NUM_Q 2
+	for (i = 0; i < NUM_Q; i++) {
+		struct iecm_ctlq_create_info *ccq = cq + i;
+
+		switch (ccq->type) {
+		case IECM_CTLQ_TYPE_MAILBOX_TX:
+			/* set head and tail registers in our local struct */
+			ccq->reg.head = PF_FW_ATQH;
+			ccq->reg.tail = PF_FW_ATQT;
+			ccq->reg.len = PF_FW_ATQLEN;
+			ccq->reg.bah = PF_FW_ATQBAH;
+			ccq->reg.bal = PF_FW_ATQBAL;
+			ccq->reg.len_mask = PF_FW_ATQLEN_ATQLEN_M;
+			ccq->reg.len_ena_mask = PF_FW_ATQLEN_ATQENABLE_M;
+			ccq->reg.head_mask = PF_FW_ATQH_ATQH_M;
+			break;
+		case IECM_CTLQ_TYPE_MAILBOX_RX:
+			/* set head and tail registers in our local struct */
+			ccq->reg.head = PF_FW_ARQH;
+			ccq->reg.tail = PF_FW_ARQT;
+			ccq->reg.len = PF_FW_ARQLEN;
+			ccq->reg.bah = PF_FW_ARQBAH;
+			ccq->reg.bal = PF_FW_ARQBAL;
+			ccq->reg.len_mask = PF_FW_ARQLEN_ARQLEN_M;
+			ccq->reg.len_ena_mask = PF_FW_ARQLEN_ARQENABLE_M;
+			ccq->reg.head_mask = PF_FW_ARQH_ARQH_M;
+			break;
+		default:
+			break;
+		}
+	}
+}
+
+/**
+ * idpf_mb_intr_reg_init - Initialize mailbox interrupt register
+ * @adapter: adapter structure
+ */
+void idpf_mb_intr_reg_init(struct iecm_adapter *adapter)
+{
+	struct iecm_intr_reg *intr = &adapter->mb_vector.intr_reg;
+	struct virtchnl2_get_capabilities *caps;
+
+	caps = (struct virtchnl2_get_capabilities *)adapter->caps;
+	intr->dyn_ctl = le32_to_cpu(caps->mailbox_dyn_ctl);
+	intr->dyn_ctl_intena_m = PF_GLINT_DYN_CTL_INTENA_M;
+	intr->dyn_ctl_itridx_m = PF_GLINT_DYN_CTL_ITR_INDX_M;
+	intr->icr_ena = PF_INT_DIR_OICR_ENA;
+	intr->icr_ena_ctlq_m = PF_INT_DIR_OICR_ENA_M;
+}
+
+/**
+ * idpf_intr_reg_init - Initialize interrupt registers
+ * @vport: virtual port structure
+ */
+int idpf_intr_reg_init(struct iecm_vport *vport)
+{
+	int num_vecs = vport->num_q_vectors;
+	struct iecm_vec_regs *reg_vals;
+	int num_regs, i, err = 0;
+
+	reg_vals = kmalloc(sizeof(void *) * IECM_LARGE_MAX_Q,
+			   GFP_KERNEL);
+	if (!reg_vals)
+		return -ENOMEM;
+
+	num_regs = iecm_get_reg_intr_vecs(vport, reg_vals, num_vecs);
+	if (num_regs != num_vecs) {
+		err = -EINVAL;
+		goto free_reg_vals;
+	}
+
+	for (i = 0; i < num_regs; i++) {
+		struct iecm_q_vector *q_vector = &vport->q_vectors[i];
+		struct iecm_intr_reg *intr = &q_vector->intr_reg;
+
+		intr->dyn_ctl = reg_vals[i].dyn_ctl_reg;
+		intr->dyn_ctl_clrpba_m = PF_GLINT_DYN_CTL_CLEARPBA_M;
+		intr->dyn_ctl_intena_m = PF_GLINT_DYN_CTL_INTENA_M;
+		intr->dyn_ctl_itridx_s = PF_GLINT_DYN_CTL_ITR_INDX_S;
+		intr->dyn_ctl_intrvl_s = PF_GLINT_DYN_CTL_INTERVAL_S;
+
+		intr->rx_itr = PF_GLINT_ITR_V2(VIRTCHNL2_ITR_IDX_0,
+					       reg_vals[i].itrn_reg);
+		intr->tx_itr = PF_GLINT_ITR_V2(VIRTCHNL2_ITR_IDX_1,
+					       reg_vals[i].itrn_reg);
+	}
+
+free_reg_vals:
+	kfree(reg_vals);
+	return err;
+}
+
+/**
+ * idpf_reset_reg_init - Initialize reset registers
+ * @reset_reg: struct to be filled in with reset registers
+ */
+void idpf_reset_reg_init(struct iecm_reset_reg *reset_reg)
+{
+	reset_reg->rstat = PFGEN_RSTAT;
+	reset_reg->rstat_m = PFGEN_RSTAT_PFR_STATE_M;
+}
+
+/**
+ * idpf_trigger_reset - trigger reset
+ * @adapter: Driver specific private structure
+ * @trig_cause: Reason to trigger a reset
+ */
+void idpf_trigger_reset(struct iecm_adapter *adapter,
+			enum iecm_flags __always_unused trig_cause)
+{
+	u32 reset_reg;
+
+	reset_reg = rd32(&adapter->hw, PFGEN_CTRL);
+	wr32(&adapter->hw, PFGEN_CTRL, (reset_reg | PFGEN_CTRL_PFSWR));
+}
+
diff --git a/drivers/net/ethernet/intel/include/iecm_lan_pf_regs.h b/drivers/net/ethernet/intel/include/iecm_lan_pf_regs.h
new file mode 100644
index 000000000000..52ffe5c4a7ca
--- /dev/null
+++ b/drivers/net/ethernet/intel/include/iecm_lan_pf_regs.h
@@ -0,0 +1,131 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (c) 2020, Intel Corporation. */
+
+#ifndef _IECM_LAN_PF_REGS_H_
+#define _IECM_LAN_PF_REGS_H_
+
+/* Receive queues */
+#define PF_QRX_BASE			0x00000000
+#define PF_QRX_TAIL(_QRX)		(PF_QRX_BASE + (((_QRX) * 0x1000)))
+#define PF_QRX_BUFFQ_BASE		0x03000000
+#define PF_QRX_BUFFQ_TAIL(_QRX)		(PF_QRX_BUFFQ_BASE + (((_QRX) * 0x1000)))
+
+/* Transmit queues */
+#define PF_QTX_BASE			0x05000000
+#define PF_QTX_COMM_DBELL(_DBQM)	(PF_QTX_BASE + ((_DBQM) * 0x1000))
+
+/* Control(PF Mailbox) Queue */
+#define PF_FW_BASE			0x08400000
+
+#define PF_FW_ARQBAL			(PF_FW_BASE)
+#define PF_FW_ARQBAH			(PF_FW_BASE + 0x4)
+#define PF_FW_ARQLEN			(PF_FW_BASE + 0x8)
+#define PF_FW_ARQLEN_ARQLEN_S		0
+#define PF_FW_ARQLEN_ARQLEN_M		MAKEMASK(0x1FFF, PF_FW_ARQLEN_ARQLEN_S)
+#define PF_FW_ARQLEN_ARQVFE_S		28
+#define PF_FW_ARQLEN_ARQVFE_M		BIT(PF_FW_ARQLEN_ARQVFE_S)
+#define PF_FW_ARQLEN_ARQOVFL_S		29
+#define PF_FW_ARQLEN_ARQOVFL_M		BIT(PF_FW_ARQLEN_ARQOVFL_S)
+#define PF_FW_ARQLEN_ARQCRIT_S		30
+#define PF_FW_ARQLEN_ARQCRIT_M		BIT(PF_FW_ARQLEN_ARQCRIT_S)
+#define PF_FW_ARQLEN_ARQENABLE_S	31
+#define PF_FW_ARQLEN_ARQENABLE_M	BIT(PF_FW_ARQLEN_ARQENABLE_S)
+#define PF_FW_ARQH			(PF_FW_BASE + 0xC)
+#define PF_FW_ARQH_ARQH_S		0
+#define PF_FW_ARQH_ARQH_M		MAKEMASK(0x1FFF, PF_FW_ARQH_ARQH_S)
+#define PF_FW_ARQT			(PF_FW_BASE + 0x10)
+
+#define PF_FW_ATQBAL			(PF_FW_BASE + 0x14)
+#define PF_FW_ATQBAH			(PF_FW_BASE + 0x18)
+#define PF_FW_ATQLEN			(PF_FW_BASE + 0x1C)
+#define PF_FW_ATQLEN_ATQLEN_S		0
+#define PF_FW_ATQLEN_ATQLEN_M		MAKEMASK(0x3FF, PF_FW_ATQLEN_ATQLEN_S)
+#define PF_FW_ATQLEN_ATQVFE_S		28
+#define PF_FW_ATQLEN_ATQVFE_M		BIT(PF_FW_ATQLEN_ATQVFE_S)
+#define PF_FW_ATQLEN_ATQOVFL_S		29
+#define PF_FW_ATQLEN_ATQOVFL_M		BIT(PF_FW_ATQLEN_ATQOVFL_S)
+#define PF_FW_ATQLEN_ATQCRIT_S		30
+#define PF_FW_ATQLEN_ATQCRIT_M		BIT(PF_FW_ATQLEN_ATQCRIT_S)
+#define PF_FW_ATQLEN_ATQENABLE_S	31
+#define PF_FW_ATQLEN_ATQENABLE_M	BIT(PF_FW_ATQLEN_ATQENABLE_S)
+#define PF_FW_ATQH			(PF_FW_BASE + 0x20)
+#define PF_FW_ATQH_ATQH_S		0
+#define PF_FW_ATQH_ATQH_M		MAKEMASK(0x3FF, PF_FW_ATQH_ATQH_S)
+#define PF_FW_ATQT			(PF_FW_BASE + 0x24)
+
+/* Interrupts */
+#define PF_GLINT_BASE			0x08900000
+#define PF_GLINT_DYN_CTL(_INT)		(PF_GLINT_BASE + ((_INT) * 0x1000))
+#define PF_GLINT_DYN_CTL_INTENA_S	0
+#define PF_GLINT_DYN_CTL_INTENA_M	BIT(PF_GLINT_DYN_CTL_INTENA_S)
+#define PF_GLINT_DYN_CTL_CLEARPBA_S	1
+#define PF_GLINT_DYN_CTL_CLEARPBA_M	BIT(PF_GLINT_DYN_CTL_CLEARPBA_S)
+#define PF_GLINT_DYN_CTL_SWINT_TRIG_S	2
+#define PF_GLINT_DYN_CTL_SWINT_TRIG_M	BIT(PF_GLINT_DYN_CTL_SWINT_TRIG_S)
+#define PF_GLINT_DYN_CTL_ITR_INDX_S	3
+#define PF_GLINT_DYN_CTL_ITR_INDX_M	MAKEMASK(0x3, PF_GLINT_DYN_CTL_ITR_INDX_S)
+#define PF_GLINT_DYN_CTL_INTERVAL_S	5
+#define PF_GLINT_DYN_CTL_INTERVAL_M	BIT(PF_GLINT_DYN_CTL_INTERVAL_S)
+#define PF_GLINT_DYN_CTL_SW_ITR_INDX_ENA_S	24
+#define PF_GLINT_DYN_CTL_SW_ITR_INDX_ENA_M BIT(PF_GLINT_DYN_CTL_SW_ITR_INDX_ENA_S)
+#define PF_GLINT_DYN_CTL_SW_ITR_INDX_S	25
+#define PF_GLINT_DYN_CTL_SW_ITR_INDX_M	BIT(PF_GLINT_DYN_CTL_SW_ITR_INDX_S)
+#define PF_GLINT_DYN_CTL_WB_ON_ITR_S	30
+#define PF_GLINT_DYN_CTL_WB_ON_ITR_M	BIT(PF_GLINT_DYN_CTL_WB_ON_ITR_S)
+#define PF_GLINT_DYN_CTL_INTENA_MSK_S	31
+#define PF_GLINT_DYN_CTL_INTENA_MSK_M	BIT(PF_GLINT_DYN_CTL_INTENA_MSK_S)
+#define PF_GLINT_ITR_V2(_i, _reg_start) (((_i) * 4) + (_reg_start))
+#define PF_GLINT_ITR(_i, _INT) (PF_GLINT_BASE + (((_i) + 1) * 4) + ((_INT) * 0x1000))
+#define PF_GLINT_ITR_MAX_INDEX		2
+#define PF_GLINT_ITR_INTERVAL_S		0
+#define PF_GLINT_ITR_INTERVAL_M		MAKEMASK(0xFFF, PF_GLINT_ITR_INTERVAL_S)
+
+/* Timesync registers */
+#define PF_TIMESYNC_BASE		0x08404000
+#define PF_GLTSYN_CMD_SYNC		(PF_TIMESYNC_BASE)
+#define PF_GLTSYN_CMD_SYNC_EXEC_CMD_S	0
+#define PF_GLTSYN_CMD_SYNC_EXEC_CMD_M	MAKEMASK(0x3, PF_GLTSYN_CMD_SYNC_EXEC_CMD_S)
+#define PF_GLTSYN_CMD_SYNC_SHTIME_EN_S	2
+#define PF_GLTSYN_CMD_SYNC_SHTIME_EN_M	BIT(PF_GLTSYN_CMD_SYNC_SHTIME_EN_S)
+#define PF_GLTSYN_SHTIME_0		(PF_TIMESYNC_BASE + 0x4)
+#define PF_GLTSYN_SHTIME_L		(PF_TIMESYNC_BASE + 0x8)
+#define PF_GLTSYN_SHTIME_H		(PF_TIMESYNC_BASE + 0xC)
+#define PF_GLTSYN_ART_L			(PF_TIMESYNC_BASE + 0x10)
+#define PF_GLTSYN_ART_H			(PF_TIMESYNC_BASE + 0x14)
+
+/* Generic registers */
+#define PF_INT_DIR_OICR_ENA		0x08406000
+#define PF_INT_DIR_OICR_ENA_S		0
+#define PF_INT_DIR_OICR_ENA_M	MAKEMASK(0xFFFFFFFF, PF_INT_DIR_OICR_ENA_S)
+#define PF_INT_DIR_OICR			0x08406004
+#define PF_INT_DIR_OICR_TSYN_EVNT	0
+#define PF_INT_DIR_OICR_PHY_TS_0	BIT(1)
+#define PF_INT_DIR_OICR_PHY_TS_1	BIT(2)
+#define PF_INT_DIR_OICR_CAUSE		0x08406008
+#define PF_INT_DIR_OICR_CAUSE_CAUSE_S	0
+#define PF_INT_DIR_OICR_CAUSE_CAUSE_M	MAKEMASK(0xFFFFFFFF, PF_INT_DIR_OICR_CAUSE_CAUSE_S)
+#define PF_INT_PBA_CLEAR		0x0840600C
+
+#define PF_FUNC_RID			0x08406010
+#define PF_FUNC_RID_FUNCTION_NUMBER_S	0
+#define PF_FUNC_RID_FUNCTION_NUMBER_M	MAKEMASK(0x7, PF_FUNC_RID_FUNCTION_NUMBER_S)
+#define PF_FUNC_RID_DEVICE_NUMBER_S	3
+#define PF_FUNC_RID_DEVICE_NUMBER_M	MAKEMASK(0x1F, PF_FUNC_RID_DEVICE_NUMBER_S)
+#define PF_FUNC_RID_BUS_NUMBER_S	8
+#define PF_FUNC_RID_BUS_NUMBER_M	MAKEMASK(0xFF, PF_FUNC_RID_BUS_NUMBER_S)
+
+/* Reset registers */
+#define PFGEN_RTRIG			0x08407000
+#define PFGEN_RTRIG_CORER_S		0
+#define PFGEN_RTRIG_CORER_M		BIT(0)
+#define PFGEN_RTRIG_LINKR_S		1
+#define PFGEN_RTRIG_LINKR_M		BIT(1)
+#define PFGEN_RTRIG_IMCR_S		2
+#define PFGEN_RTRIG_IMCR_M		BIT(2)
+#define PFGEN_RSTAT			0x08407008 /* PFR Status */
+#define PFGEN_RSTAT_PFR_STATE_S		0
+#define PFGEN_RSTAT_PFR_STATE_M		MAKEMASK(0x3, PFGEN_RSTAT_PFR_STATE_S)
+#define PFGEN_CTRL			0x0840700C
+#define PFGEN_CTRL_PFSWR		BIT(0)
+
+#endif
-- 
2.33.0


^ permalink raw reply related	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 05/19] iecm: add vport alloc and virtchnl messages
  2022-01-28  0:09 ` [Intel-wired-lan] [PATCH net-next 05/19] iecm: add vport alloc and virtchnl messages Alan Brady
@ 2022-01-28  4:19     ` kernel test robot
  2022-01-28 12:32   ` Alexander Lobakin
  1 sibling, 0 replies; 89+ messages in thread
From: kernel test robot @ 2022-01-28  4:19 UTC (permalink / raw)
  To: intel-wired-lan

Hi Alan,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on net-next/master]

url:    https://github.com/0day-ci/linux/commits/Alan-Brady/Add-iecm-and-idpf/20220128-085513
base:   https://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next.git e2cf07654efb0fd7bbcb475c6f74be7b5755a8fd
config: i386-allyesconfig (https://download.01.org/0day-ci/archive/20220128/202201281200.CI9u45bS-lkp at intel.com/config)
compiler: gcc-9 (Debian 9.3.0-22) 9.3.0
reproduce (this is a W=1 build):
        # https://github.com/0day-ci/linux/commit/1233d9631b312eea5aebbce63590e27f9993bacc
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Alan-Brady/Add-iecm-and-idpf/20220128-085513
        git checkout 1233d9631b312eea5aebbce63590e27f9993bacc
        # save the config file to linux build tree
        mkdir build_dir
        make W=1 O=build_dir ARCH=i386 SHELL=/bin/bash drivers/net/ethernet/intel/iecm/

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

   drivers/net/ethernet/intel/iecm/iecm_virtchnl.c: In function 'iecm_vport_queue_ids_init':
>> drivers/net/ethernet/intel/iecm/iecm_virtchnl.c:1396:1: warning: the frame size of 1052 bytes is larger than 1024 bytes [-Wframe-larger-than=]
    1396 | }
         | ^


vim +1396 drivers/net/ethernet/intel/iecm/iecm_virtchnl.c

  1322	
  1323	/**
  1324	 * iecm_vport_queue_ids_init - Initialize queue ids from Mailbox parameters
  1325	 * @vport: virtual port for which the queues ids are initialized
  1326	 *
  1327	 * Will initialize all queue ids with ids received as mailbox parameters.
  1328	 * Returns 0 on success, negative if all the queues are not initialized.
  1329	 */
  1330	static int iecm_vport_queue_ids_init(struct iecm_vport *vport)
  1331	{
  1332		struct virtchnl2_create_vport *vport_params;
  1333		struct virtchnl2_queue_reg_chunks *chunks;
  1334		/* We may never deal with more than 256 same type of queues */
  1335	#define IECM_MAX_QIDS	256
  1336		u32 qids[IECM_MAX_QIDS];
  1337		int num_ids;
  1338		u16 q_type;
  1339	
  1340		if (vport->adapter->config_data.req_qs_chunks) {
  1341			struct virtchnl2_add_queues *vc_aq =
  1342				(struct virtchnl2_add_queues *)
  1343				vport->adapter->config_data.req_qs_chunks;
  1344			chunks = &vc_aq->chunks;
  1345		} else {
  1346			vport_params = (struct virtchnl2_create_vport *)
  1347					vport->adapter->vport_params_recvd[0];
  1348			chunks = &vport_params->chunks;
  1349		}
  1350	
  1351		num_ids = iecm_vport_get_queue_ids(qids, IECM_MAX_QIDS,
  1352						   VIRTCHNL2_QUEUE_TYPE_TX,
  1353						   chunks);
  1354		if (num_ids != vport->num_txq)
  1355			return -EINVAL;
  1356		num_ids = __iecm_vport_queue_ids_init(vport, qids, num_ids,
  1357						      VIRTCHNL2_QUEUE_TYPE_TX);
  1358		if (num_ids != vport->num_txq)
  1359			return -EINVAL;
  1360		num_ids = iecm_vport_get_queue_ids(qids, IECM_MAX_QIDS,
  1361						   VIRTCHNL2_QUEUE_TYPE_RX,
  1362						   chunks);
  1363		if (num_ids != vport->num_rxq)
  1364			return -EINVAL;
  1365		num_ids = __iecm_vport_queue_ids_init(vport, qids, num_ids,
  1366						      VIRTCHNL2_QUEUE_TYPE_RX);
  1367		if (num_ids != vport->num_rxq)
  1368			return -EINVAL;
  1369	
  1370		if (iecm_is_queue_model_split(vport->txq_model)) {
  1371			q_type = VIRTCHNL2_QUEUE_TYPE_TX_COMPLETION;
  1372			num_ids = iecm_vport_get_queue_ids(qids, IECM_MAX_QIDS, q_type,
  1373							   chunks);
  1374			if (num_ids != vport->num_complq)
  1375				return -EINVAL;
  1376			num_ids = __iecm_vport_queue_ids_init(vport, qids,
  1377							      num_ids,
  1378							      q_type);
  1379			if (num_ids != vport->num_complq)
  1380				return -EINVAL;
  1381		}
  1382	
  1383		if (iecm_is_queue_model_split(vport->rxq_model)) {
  1384			q_type = VIRTCHNL2_QUEUE_TYPE_RX_BUFFER;
  1385			num_ids = iecm_vport_get_queue_ids(qids, IECM_MAX_QIDS, q_type,
  1386							   chunks);
  1387			if (num_ids != vport->num_bufq)
  1388				return -EINVAL;
  1389			num_ids = __iecm_vport_queue_ids_init(vport, qids, num_ids,
  1390							      q_type);
  1391			if (num_ids != vport->num_bufq)
  1392				return -EINVAL;
  1393		}
  1394	
  1395		return 0;
> 1396	}
  1397	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all at lists.01.org

^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [Intel-wired-lan] [PATCH net-next 05/19] iecm: add vport alloc and virtchnl messages
@ 2022-01-28  4:19     ` kernel test robot
  0 siblings, 0 replies; 89+ messages in thread
From: kernel test robot @ 2022-01-28  4:19 UTC (permalink / raw)
  To: kbuild-all

[-- Attachment #1: Type: text/plain, Size: 4551 bytes --]

Hi Alan,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on net-next/master]

url:    https://github.com/0day-ci/linux/commits/Alan-Brady/Add-iecm-and-idpf/20220128-085513
base:   https://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next.git e2cf07654efb0fd7bbcb475c6f74be7b5755a8fd
config: i386-allyesconfig (https://download.01.org/0day-ci/archive/20220128/202201281200.CI9u45bS-lkp(a)intel.com/config)
compiler: gcc-9 (Debian 9.3.0-22) 9.3.0
reproduce (this is a W=1 build):
        # https://github.com/0day-ci/linux/commit/1233d9631b312eea5aebbce63590e27f9993bacc
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Alan-Brady/Add-iecm-and-idpf/20220128-085513
        git checkout 1233d9631b312eea5aebbce63590e27f9993bacc
        # save the config file to linux build tree
        mkdir build_dir
        make W=1 O=build_dir ARCH=i386 SHELL=/bin/bash drivers/net/ethernet/intel/iecm/

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

   drivers/net/ethernet/intel/iecm/iecm_virtchnl.c: In function 'iecm_vport_queue_ids_init':
>> drivers/net/ethernet/intel/iecm/iecm_virtchnl.c:1396:1: warning: the frame size of 1052 bytes is larger than 1024 bytes [-Wframe-larger-than=]
    1396 | }
         | ^


vim +1396 drivers/net/ethernet/intel/iecm/iecm_virtchnl.c

  1322	
  1323	/**
  1324	 * iecm_vport_queue_ids_init - Initialize queue ids from Mailbox parameters
  1325	 * @vport: virtual port for which the queues ids are initialized
  1326	 *
  1327	 * Will initialize all queue ids with ids received as mailbox parameters.
  1328	 * Returns 0 on success, negative if all the queues are not initialized.
  1329	 */
  1330	static int iecm_vport_queue_ids_init(struct iecm_vport *vport)
  1331	{
  1332		struct virtchnl2_create_vport *vport_params;
  1333		struct virtchnl2_queue_reg_chunks *chunks;
  1334		/* We may never deal with more than 256 same type of queues */
  1335	#define IECM_MAX_QIDS	256
  1336		u32 qids[IECM_MAX_QIDS];
  1337		int num_ids;
  1338		u16 q_type;
  1339	
  1340		if (vport->adapter->config_data.req_qs_chunks) {
  1341			struct virtchnl2_add_queues *vc_aq =
  1342				(struct virtchnl2_add_queues *)
  1343				vport->adapter->config_data.req_qs_chunks;
  1344			chunks = &vc_aq->chunks;
  1345		} else {
  1346			vport_params = (struct virtchnl2_create_vport *)
  1347					vport->adapter->vport_params_recvd[0];
  1348			chunks = &vport_params->chunks;
  1349		}
  1350	
  1351		num_ids = iecm_vport_get_queue_ids(qids, IECM_MAX_QIDS,
  1352						   VIRTCHNL2_QUEUE_TYPE_TX,
  1353						   chunks);
  1354		if (num_ids != vport->num_txq)
  1355			return -EINVAL;
  1356		num_ids = __iecm_vport_queue_ids_init(vport, qids, num_ids,
  1357						      VIRTCHNL2_QUEUE_TYPE_TX);
  1358		if (num_ids != vport->num_txq)
  1359			return -EINVAL;
  1360		num_ids = iecm_vport_get_queue_ids(qids, IECM_MAX_QIDS,
  1361						   VIRTCHNL2_QUEUE_TYPE_RX,
  1362						   chunks);
  1363		if (num_ids != vport->num_rxq)
  1364			return -EINVAL;
  1365		num_ids = __iecm_vport_queue_ids_init(vport, qids, num_ids,
  1366						      VIRTCHNL2_QUEUE_TYPE_RX);
  1367		if (num_ids != vport->num_rxq)
  1368			return -EINVAL;
  1369	
  1370		if (iecm_is_queue_model_split(vport->txq_model)) {
  1371			q_type = VIRTCHNL2_QUEUE_TYPE_TX_COMPLETION;
  1372			num_ids = iecm_vport_get_queue_ids(qids, IECM_MAX_QIDS, q_type,
  1373							   chunks);
  1374			if (num_ids != vport->num_complq)
  1375				return -EINVAL;
  1376			num_ids = __iecm_vport_queue_ids_init(vport, qids,
  1377							      num_ids,
  1378							      q_type);
  1379			if (num_ids != vport->num_complq)
  1380				return -EINVAL;
  1381		}
  1382	
  1383		if (iecm_is_queue_model_split(vport->rxq_model)) {
  1384			q_type = VIRTCHNL2_QUEUE_TYPE_RX_BUFFER;
  1385			num_ids = iecm_vport_get_queue_ids(qids, IECM_MAX_QIDS, q_type,
  1386							   chunks);
  1387			if (num_ids != vport->num_bufq)
  1388				return -EINVAL;
  1389			num_ids = __iecm_vport_queue_ids_init(vport, qids, num_ids,
  1390							      q_type);
  1391			if (num_ids != vport->num_bufq)
  1392				return -EINVAL;
  1393		}
  1394	
  1395		return 0;
> 1396	}
  1397	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org

^ permalink raw reply	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 13/19] iecm: implement splitq napi_poll
  2022-01-28  0:10 ` [Intel-wired-lan] [PATCH net-next 13/19] iecm: implement splitq napi_poll Alan Brady
@ 2022-01-28  5:21     ` kernel test robot
  2022-01-28 17:38   ` Alexander Lobakin
  1 sibling, 0 replies; 89+ messages in thread
From: kernel test robot @ 2022-01-28  5:21 UTC (permalink / raw)
  To: intel-wired-lan

Hi Alan,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on net-next/master]

url:    https://github.com/0day-ci/linux/commits/Alan-Brady/Add-iecm-and-idpf/20220128-085513
base:   https://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next.git e2cf07654efb0fd7bbcb475c6f74be7b5755a8fd
config: arc-allyesconfig (https://download.01.org/0day-ci/archive/20220128/202201281316.ZdiaZw6q-lkp at intel.com/config)
compiler: arceb-elf-gcc (GCC) 11.2.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/0day-ci/linux/commit/8e9b2451747f81363327cf5a4e07aaf88af52397
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Alan-Brady/Add-iecm-and-idpf/20220128-085513
        git checkout 8e9b2451747f81363327cf5a4e07aaf88af52397
        # save the config file to linux build tree
        mkdir build_dir
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0 make.cross O=build_dir ARCH=arc SHELL=/bin/bash

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

   drivers/net/ethernet/intel/iecm/iecm_txrx.c: In function 'iecm_rx_can_reuse_page':
>> drivers/net/ethernet/intel/iecm/iecm_txrx.c:3132:19: error: 'struct iecm_rx_buf' has no member named 'page_offset'
    3132 |         if (rx_buf->page_offset > last_offset)
         |                   ^~


vim +3132 drivers/net/ethernet/intel/iecm/iecm_txrx.c

  3103	
  3104	/**
  3105	 * iecm_rx_can_reuse_page - Determine if page can be reused for another rx
  3106	 * @rx_buf: buffer containing the page
  3107	 *
  3108	 * If page is reusable, we have a green light for calling iecm_reuse_rx_page,
  3109	 * which will assign the current buffer to the buffer that next_to_alloc is
  3110	 * pointing to; otherwise, the dma mapping needs to be destroyed and
  3111	 * page freed
  3112	 */
  3113	bool iecm_rx_can_reuse_page(struct iecm_rx_buf *rx_buf)
  3114	{
  3115		struct iecm_page_info *page_info = &rx_buf->page_info[rx_buf->page_indx];
  3116	
  3117	#if (PAGE_SIZE >= 8192)
  3118		unsigned int last_offset = PAGE_SIZE - rx_buf->buf_size;
  3119	#endif /* PAGE_SIZE < 8192) */
  3120		unsigned int pagecnt_bias = page_info->pagecnt_bias;
  3121		struct page *page = page_info->page;
  3122	
  3123		/* avoid re-using remote pages */
  3124		if (unlikely(iecm_rx_page_is_reserved(page)))
  3125			return false;
  3126	
  3127	#if (PAGE_SIZE < 8192)
  3128		/* if we are only owner of page we can reuse it */
  3129		if (unlikely((page_count(page) - pagecnt_bias) > 1))
  3130			return false;
  3131	#else
> 3132		if (rx_buf->page_offset > last_offset)
  3133			return false;
  3134	#endif /* PAGE_SIZE < 8192) */
  3135	
  3136		/* If we have drained the page fragment pool we need to update
  3137		 * the pagecnt_bias and page count so that we fully restock the
  3138		 * number of references the driver holds.
  3139		 */
  3140		if (unlikely(pagecnt_bias == 1)) {
  3141			page_ref_add(page, USHRT_MAX - 1);
  3142			page_info->pagecnt_bias = USHRT_MAX;
  3143		}
  3144	
  3145		return true;
  3146	}
  3147	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all at lists.01.org

^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [Intel-wired-lan] [PATCH net-next 13/19] iecm: implement splitq napi_poll
@ 2022-01-28  5:21     ` kernel test robot
  0 siblings, 0 replies; 89+ messages in thread
From: kernel test robot @ 2022-01-28  5:21 UTC (permalink / raw)
  To: kbuild-all

[-- Attachment #1: Type: text/plain, Size: 3503 bytes --]

Hi Alan,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on net-next/master]

url:    https://github.com/0day-ci/linux/commits/Alan-Brady/Add-iecm-and-idpf/20220128-085513
base:   https://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next.git e2cf07654efb0fd7bbcb475c6f74be7b5755a8fd
config: arc-allyesconfig (https://download.01.org/0day-ci/archive/20220128/202201281316.ZdiaZw6q-lkp(a)intel.com/config)
compiler: arceb-elf-gcc (GCC) 11.2.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/0day-ci/linux/commit/8e9b2451747f81363327cf5a4e07aaf88af52397
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Alan-Brady/Add-iecm-and-idpf/20220128-085513
        git checkout 8e9b2451747f81363327cf5a4e07aaf88af52397
        # save the config file to linux build tree
        mkdir build_dir
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0 make.cross O=build_dir ARCH=arc SHELL=/bin/bash

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

   drivers/net/ethernet/intel/iecm/iecm_txrx.c: In function 'iecm_rx_can_reuse_page':
>> drivers/net/ethernet/intel/iecm/iecm_txrx.c:3132:19: error: 'struct iecm_rx_buf' has no member named 'page_offset'
    3132 |         if (rx_buf->page_offset > last_offset)
         |                   ^~


vim +3132 drivers/net/ethernet/intel/iecm/iecm_txrx.c

  3103	
  3104	/**
  3105	 * iecm_rx_can_reuse_page - Determine if page can be reused for another rx
  3106	 * @rx_buf: buffer containing the page
  3107	 *
  3108	 * If page is reusable, we have a green light for calling iecm_reuse_rx_page,
  3109	 * which will assign the current buffer to the buffer that next_to_alloc is
  3110	 * pointing to; otherwise, the dma mapping needs to be destroyed and
  3111	 * page freed
  3112	 */
  3113	bool iecm_rx_can_reuse_page(struct iecm_rx_buf *rx_buf)
  3114	{
  3115		struct iecm_page_info *page_info = &rx_buf->page_info[rx_buf->page_indx];
  3116	
  3117	#if (PAGE_SIZE >= 8192)
  3118		unsigned int last_offset = PAGE_SIZE - rx_buf->buf_size;
  3119	#endif /* PAGE_SIZE < 8192) */
  3120		unsigned int pagecnt_bias = page_info->pagecnt_bias;
  3121		struct page *page = page_info->page;
  3122	
  3123		/* avoid re-using remote pages */
  3124		if (unlikely(iecm_rx_page_is_reserved(page)))
  3125			return false;
  3126	
  3127	#if (PAGE_SIZE < 8192)
  3128		/* if we are only owner of page we can reuse it */
  3129		if (unlikely((page_count(page) - pagecnt_bias) > 1))
  3130			return false;
  3131	#else
> 3132		if (rx_buf->page_offset > last_offset)
  3133			return false;
  3134	#endif /* PAGE_SIZE < 8192) */
  3135	
  3136		/* If we have drained the page fragment pool we need to update
  3137		 * the pagecnt_bias and page count so that we fully restock the
  3138		 * number of references the driver holds.
  3139		 */
  3140		if (unlikely(pagecnt_bias == 1)) {
  3141			page_ref_add(page, USHRT_MAX - 1);
  3142			page_info->pagecnt_bias = USHRT_MAX;
  3143		}
  3144	
  3145		return true;
  3146	}
  3147	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org

^ permalink raw reply	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 02/19] iecm: add basic module init and documentation
  2022-01-28  0:09 ` [Intel-wired-lan] [PATCH net-next 02/19] iecm: add basic module init and documentation Alan Brady
@ 2022-01-28 11:56   ` Alexander Lobakin
  2022-02-02 22:15     ` Brady, Alan
  2022-02-01 19:44   ` Shannon Nelson
  1 sibling, 1 reply; 89+ messages in thread
From: Alexander Lobakin @ 2022-01-28 11:56 UTC (permalink / raw)
  To: intel-wired-lan

From: Alan Brady <alan.brady@intel.com>
Date: Thu, 27 Jan 2022 16:09:52 -0800

> This adds the basics needed to make a kernel module and documentation
> needed to use iecm module.
> 
> Signed-off-by: Alan Brady <alan.brady@intel.com>
> Signed-off-by: Phani Burra <phani.r.burra@intel.com>
> Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
> Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
> Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
> ---
>  .../device_drivers/ethernet/intel/iecm.rst    | 93 +++++++++++++++++++
>  drivers/net/ethernet/intel/Kconfig            | 15 +++
>  drivers/net/ethernet/intel/Makefile           |  1 +
>  drivers/net/ethernet/intel/iecm/Makefile      | 13 +++
>  drivers/net/ethernet/intel/iecm/iecm_main.c   | 40 ++++++++
>  drivers/net/ethernet/intel/include/iecm.h     | 10 ++
>  6 files changed, 172 insertions(+)
>  create mode 100644 Documentation/networking/device_drivers/ethernet/intel/iecm.rst
>  create mode 100644 drivers/net/ethernet/intel/iecm/Makefile
>  create mode 100644 drivers/net/ethernet/intel/iecm/iecm_main.c
>  create mode 100644 drivers/net/ethernet/intel/include/iecm.h
> 
> diff --git a/Documentation/networking/device_drivers/ethernet/intel/iecm.rst b/Documentation/networking/device_drivers/ethernet/intel/iecm.rst
> new file mode 100644
> index 000000000000..5634e3e65c74
> --- /dev/null
> +++ b/Documentation/networking/device_drivers/ethernet/intel/iecm.rst
> @@ -0,0 +1,93 @@
> +.. SPDX-License-Identifier: GPL-2.0
> +
> +========================
> +Intel Ethernet Common Module
> +========================
> +
> +The Intel Ethernet Common Module is meant to serve as an abstraction layer
> +between device specific implementation details and common net device driver
> +flows. This library provides several function hooks which allow a device driver
> +to specify register addresses, control queue communications, and other device
> +specific functionality.  Some functions are required to be implemented while
> +others have a default implementation that is used when none is supplied by the
> +device driver.  Doing this, a device driver can be written to take advantage
> +of existing code while also giving the flexibility to implement device specific
> +features.
> +
> +The common use case for this library is for a network device driver that wants
> +specify its own device specific details but also leverage the more common
> +code flows found in network device drivers.
> +
> +Sections in this document:
> +	Entry Point
> +	Exit Point
> +	Register Operations API
> +	Virtchnl Operations API
> +
> +Entry Point
> +~~~~~~~~~~~
> +The primary entry point to the library is the iecm_probe function.  Prior to
> +calling this, device drivers must have allocated an iecm_adapter struct and
> +initialized it with the required API functions.  The adapter struct, along with
> +the pci_dev struct and the pci_device_id struct, is provided to iecm_probe
> +which finalizes device initialization and prepares the device for open.
> +
> +The iecm_dev_ops struct within the iecm_adapter struct is the primary vehicle
> +for passing information from device drivers to the common module.  A dependent
> +module must define and assign a reg_ops_init function which will assign the
> +respective function pointers to initialize register values (see iecm_reg_ops
> +struct).  These are required to be provided by the dependent device driver as
> +no suitable default can be assumed for register addresses.
> +
> +The vc_ops_init function pointer and the related iecm_virtchnl_ops struct are
> +optional and should only be necessary for device drivers which use a different
> +method/timing for communicating across a mailbox to the hardware.  Within iecm
> +is a default interface provided in the case where one is not provided by the
> +device driver.
> +
> +Exit Point
> +~~~~~~~~~~
> +When the device driver is being prepared to be removed through the pci_driver
> +remove callback, it is required for the device driver to call iecm_remove with
> +the pci_dev struct provided.  This is required to ensure all resources are
> +properly freed and returned to the operating system.
> +
> +Register Operations API
> +~~~~~~~~~~~~~~~~~~~~~~~
> +iecm_reg_ops contains three different function pointers relating to initializing
> +registers for the specific net device using the library.
> +
> +ctlq_reg_init relates specifically to setting up registers related to control
> +queue/mailbox communications.  Registers that should be defined include: head,
> +tail, len, bah, bal, len_mask, len_ena_mask, and head_mask.
> +
> +vportq_reg_init relates to setting up queue registers.  The tail registers to
> +be assigned to the iecm_queue struct for each RX/TX queue.
> +
> +intr_reg_init relates to any registers needed to setup interrupts.  These
> +include registers needed to enable the interrupt and change ITR settings.
> +
> +If the initialization function finds that one or more required function
> +pointers were not provided, an error will be issued and the device will be
> +inoperable.
> +
> +
> +Virtchnl Operations API
> +~~~~~~~~~~~~~~~~~~~~~~~
> +The virtchnl is a conduit between driver and hardware that allows device
> +drivers to send and receive control messages to/from hardware.  This is
> +optional to be specified as there is a general interface that can be assumed
> +when using this library.  However, if a device deviates in some way to
> +communicate across the mailbox correctly, this interface is provided to allow
> +that.
> +
> +If vc_ops_init is set in the dev_ops field of the iecm_adapter struct, then it
> +is assumed the device driver is using providing it's own interface to do
> +virtchnl communications.  While providing vc_ops_init is optional, if it is
> +provided, it is required that the device driver provide function pointers for
> +those functions in vc_ops, with exception for the enable_vport, disable_vport,
> +and destroy_vport functions which may not be required for all devices.
> +
> +If the initialization function finds that vc_ops_init was defined but one or
> +more required function pointers were not provided, an error will be issued and
> +the device will be inoperable.
> diff --git a/drivers/net/ethernet/intel/Kconfig b/drivers/net/ethernet/intel/Kconfig
> index 3facb55b7161..754dc7677ad5 100644
> --- a/drivers/net/ethernet/intel/Kconfig
> +++ b/drivers/net/ethernet/intel/Kconfig
> @@ -372,4 +372,19 @@ config IGC
>  	  To compile this driver as a module, choose M here. The module
>  	  will be called igc.
>  
> +config IECM
> +	tristate "Intel(R) Ethernet Common Module Support"
> +	default n
> +	depends on PCI_MSI
> +	select DIMLIB
> +	help
> +      This supplies needed functions to device specific device drivers

One Tab + two spaces instead of 6 spaces. And one Tab is 8 cols in
the kernel.

> +      implementing common module.
> +
> +	  More specific information on configuring the driver is in
> +	  <file:Documentation/networking/device_drivers/ethernet/intel/iecm.rst>.
> +
> +      To compile this as a module, choose M here. The module will be called
> +      iecm.
> +
>  endif # NET_VENDOR_INTEL
> diff --git a/drivers/net/ethernet/intel/Makefile b/drivers/net/ethernet/intel/Makefile
> index 3075290063f6..c9eba9cc5087 100644
> --- a/drivers/net/ethernet/intel/Makefile
> +++ b/drivers/net/ethernet/intel/Makefile
> @@ -16,3 +16,4 @@ obj-$(CONFIG_IXGB) += ixgb/
>  obj-$(CONFIG_IAVF) += iavf/
>  obj-$(CONFIG_FM10K) += fm10k/
>  obj-$(CONFIG_ICE) += ice/
> +obj-$(CONFIG_IECM) += iecm/
> diff --git a/drivers/net/ethernet/intel/iecm/Makefile b/drivers/net/ethernet/intel/iecm/Makefile
> new file mode 100644
> index 000000000000..d2d087ac71e9
> --- /dev/null
> +++ b/drivers/net/ethernet/intel/iecm/Makefile
> @@ -0,0 +1,13 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +# Copyright (C) 2019 Intel Corporation
> +
> +#
> +# Makefile for the Intel(R) Data Plane Function Linux Driver
> +#
> +
> +obj-$(CONFIG_IECM) += iecm.o
> +
> +ccflags-y += -I$(srctree)/drivers/net/ethernet/intel/include

Common includes are usually being added to include/linux/. Files
outside include directories are usually considered "private", i.e.
used only inside a particular folder.

> +
> +iecm-y := \
> +	iecm_main.o
> diff --git a/drivers/net/ethernet/intel/iecm/iecm_main.c b/drivers/net/ethernet/intel/iecm/iecm_main.c
> new file mode 100644
> index 000000000000..7c09403c6918
> --- /dev/null
> +++ b/drivers/net/ethernet/intel/iecm/iecm_main.c
> @@ -0,0 +1,40 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/* Copyright (C) 2019 Intel Corporation */
> +
> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> +
> +#include "iecm.h"

Quotes are used for local includes. For includes from the search
directories please use <>.

> +
> +#define DRV_SUMMARY	"Intel(R) Ethernet Common Module"
> +static const char iecm_driver_string[] = DRV_SUMMARY;
> +static const char iecm_copyright[] = "Copyright (c) 2020, Intel Corporation.";
> +
> +MODULE_DESCRIPTION(DRV_SUMMARY);
> +MODULE_LICENSE("GPL v2");

"GPL v2" is a deprecated identifier and is not recommended for new
modules. Just "GPL" is enough and means exactly the same.

> +
> +/**
> + * iecm_module_init - Driver registration routine
> + *
> + * iecm_module_init is the first routine called when the driver is
> + * loaded. All it does is register with the PCI subsystem.
> + */
> +static int __init iecm_module_init(void)
> +{
> +	pr_info("%s - version %d\n", iecm_driver_string, LINUX_VERSION_CODE);
> +	pr_info("%s\n", iecm_copyright);
> +
> +	return 0;
> +}
> +module_init(iecm_module_init);
> +
> +/**
> + * iecm_module_exit - Driver exit cleanup routine
> + *
> + * iecm_module_exit is called just before the driver is removed
> + * from memory.
> + */
> +static void __exit iecm_module_exit(void)
> +{
> +	pr_info("module unloaded\n");
> +}
> +module_exit(iecm_module_exit);
> diff --git a/drivers/net/ethernet/intel/include/iecm.h b/drivers/net/ethernet/intel/include/iecm.h
> new file mode 100644
> index 000000000000..f66f0d7db8e7
> --- /dev/null
> +++ b/drivers/net/ethernet/intel/include/iecm.h
> @@ -0,0 +1,10 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/* Copyright (C) 2019 Intel Corporation */
> +
> +#ifndef _IECM_H_
> +#define _IECM_H_
> +
> +#include <linux/etherdevice.h>
> +#include <linux/version.h>
> +
> +#endif /* !_IECM_H_ */
> -- 
> 2.33.0

Thanks,
Al

^ permalink raw reply	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 04/19] iecm: add api_init and controlq init
  2022-01-28  0:09 ` [Intel-wired-lan] [PATCH net-next 04/19] iecm: add api_init and controlq init Alan Brady
@ 2022-01-28 12:09   ` Alexander Lobakin
  2022-02-02 22:16     ` Brady, Alan
  2022-02-01 21:26   ` Shannon Nelson
  1 sibling, 1 reply; 89+ messages in thread
From: Alexander Lobakin @ 2022-01-28 12:09 UTC (permalink / raw)
  To: intel-wired-lan

From: Alan Brady <alan.brady@intel.com>
Date: Thu, 27 Jan 2022 16:09:54 -0800

> Initializing device registers is offloaded into function pointers given
> to iecm from the dependent device driver for a given device, as offsets
> can vary wildly. This also adds everything needed to setup and use a
> controlq which uses some of those registers.
> 
> At the end of probe we kicked off a hard reset and this implements what's
> needed to handle that reset and continue init.
> 
> Signed-off-by: Phani Burra <phani.r.burra@intel.com>
> Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
> Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
> Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
> Signed-off-by: Alan Brady <alan.brady@intel.com>
> ---
>  drivers/net/ethernet/intel/iecm/Makefile      |   3 +
>  .../net/ethernet/intel/iecm/iecm_controlq.c   | 649 ++++++++++++++++++
>  .../ethernet/intel/iecm/iecm_controlq_setup.c | 175 +++++
>  drivers/net/ethernet/intel/iecm/iecm_lib.c    | 191 +++++-
>  .../net/ethernet/intel/iecm/iecm_virtchnl.c   | 172 +++++
>  drivers/net/ethernet/intel/include/iecm.h     |  52 ++
>  .../ethernet/intel/include/iecm_controlq.h    | 117 ++++
>  .../intel/include/iecm_controlq_api.h         | 185 +++++
>  drivers/net/ethernet/intel/include/iecm_mem.h |  20 +
>  9 files changed, 1563 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/net/ethernet/intel/iecm/iecm_controlq.c
>  create mode 100644 drivers/net/ethernet/intel/iecm/iecm_controlq_setup.c
>  create mode 100644 drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
>  create mode 100644 drivers/net/ethernet/intel/include/iecm_controlq.h
>  create mode 100644 drivers/net/ethernet/intel/include/iecm_controlq_api.h
>  create mode 100644 drivers/net/ethernet/intel/include/iecm_mem.h
> 

--- 8< ---

> diff --git a/drivers/net/ethernet/intel/include/iecm_controlq_api.h b/drivers/net/ethernet/intel/include/iecm_controlq_api.h
> new file mode 100644
> index 000000000000..5f624f005d33
> --- /dev/null
> +++ b/drivers/net/ethernet/intel/include/iecm_controlq_api.h
> @@ -0,0 +1,185 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/* Copyright (c) 2020, Intel Corporation. */
> +
> +#ifndef _IECM_CONTROLQ_API_H_
> +#define _IECM_CONTROLQ_API_H_
> +
> +#include "iecm_mem.h"
> +
> +struct iecm_hw;
> +
> +/* Used for queue init, response and events */
> +enum iecm_ctlq_type {
> +	IECM_CTLQ_TYPE_MAILBOX_TX	= 0,
> +	IECM_CTLQ_TYPE_MAILBOX_RX	= 1,
> +	IECM_CTLQ_TYPE_CONFIG_TX	= 2,
> +	IECM_CTLQ_TYPE_CONFIG_RX	= 3,
> +	IECM_CTLQ_TYPE_EVENT_RX		= 4,
> +	IECM_CTLQ_TYPE_RDMA_TX		= 5,
> +	IECM_CTLQ_TYPE_RDMA_RX		= 6,
> +	IECM_CTLQ_TYPE_RDMA_COMPL	= 7
> +};
> +
> +/* Generic Control Queue Structures */
> +struct iecm_ctlq_reg {
> +	/* used for queue tracking */
> +	u32 head;
> +	u32 tail;
> +	/* Below applies only to default mb (if present) */
> +	u32 len;
> +	u32 bah;
> +	u32 bal;
> +	u32 len_mask;
> +	u32 len_ena_mask;
> +	u32 head_mask;
> +};
> +
> +/* Generic queue msg structure */
> +struct iecm_ctlq_msg {
> +	u16 vmvf_type; /* represents the source of the message on recv */
> +#define IECM_VMVF_TYPE_VF 0
> +#define IECM_VMVF_TYPE_VM 1
> +#define IECM_VMVF_TYPE_PF 2
> +	u16 opcode;
> +	u16 data_len;	/* data_len = 0 when no payload is attached */
> +	union {
> +		u16 func_id;	/* when sending a message */
> +		u16 status;	/* when receiving a message */
> +	};
> +	union {
> +		struct {
> +			u32 chnl_retval;
> +			u32 chnl_opcode;
> +		} mbx;
> +	} cookie;

One field union? If it will be expanded later, please unionize it
only then.

> +	union {
> +#define IECM_DIRECT_CTX_SIZE	16
> +#define IECM_INDIRECT_CTX_SIZE	8
> +		/* 16 bytes of context can be provided or 8 bytes of context
> +		 * plus the address of a DMA buffer
> +		 */
> +		u8 direct[IECM_DIRECT_CTX_SIZE];
> +		struct {
> +			u8 context[IECM_INDIRECT_CTX_SIZE];
> +			struct iecm_dma_mem *payload;
> +		} indirect;
> +	} ctx;
> +};
> +
> +/* Generic queue info structures */
> +/* MB, CONFIG and EVENT q do not have extended info */
> +struct iecm_ctlq_create_info {
> +	enum iecm_ctlq_type type;
> +	int id; /* absolute queue offset passed as input
> +		 * -1 for default mailbox if present
> +		 */
> +	u16 len; /* Queue length passed as input */
> +	u16 buf_size; /* buffer size passed as input */
> +	u64 base_address; /* output, HPA of the Queue start  */
> +	struct iecm_ctlq_reg reg; /* registers accessed by ctlqs */
> +
> +	int ext_info_size;
> +	void *ext_info; /* Specific to q type */
> +};
> +
> +/* Control Queue information */
> +struct iecm_ctlq_info {
> +	struct list_head cq_list;
> +
> +	enum iecm_ctlq_type cq_type;
> +	int q_id;
> +	/* control queue lock */
> +	struct mutex cq_lock;
> +
> +	/* used for interrupt processing */
> +	u16 next_to_use;
> +	u16 next_to_clean;
> +	u16 next_to_post;		/* starting descriptor to post buffers
> +					 * to after recev
> +					 */
> +
> +	struct iecm_dma_mem desc_ring;	/* descriptor ring memory
> +					 * iecm_dma_mem is defined in OSdep.h
> +					 */
> +	union {
> +		struct iecm_dma_mem **rx_buff;
> +		struct iecm_ctlq_msg **tx_msg;
> +	} bi;
> +
> +	u16 buf_size;			/* queue buffer size */
> +	u16 ring_size;			/* Number of descriptors */
> +	struct iecm_ctlq_reg reg;	/* registers accessed by ctlqs */
> +};
> +
> +/* PF/VF mailbox commands */
> +enum iecm_mbx_opc {
> +	/* iecm_mbq_opc_send_msg_to_pf:
> +	 *	usage: used by PF or VF to send a message to its CPF
> +	 *	target: RX queue and function ID of parent PF taken from HW
> +	 */
> +	iecm_mbq_opc_send_msg_to_pf		= 0x0801,
> +
> +	/* iecm_mbq_opc_send_msg_to_vf:
> +	 *	usage: used by PF to send message to a VF
> +	 *	target: VF control queue ID must be specified in descriptor
> +	 */
> +	iecm_mbq_opc_send_msg_to_vf		= 0x0802,
> +
> +	/* iecm_mbq_opc_send_msg_to_peer_pf:
> +	 *	usage: used by any function to send message to any peer PF
> +	 *	target: RX queue and host of parent PF taken from HW
> +	 */
> +	iecm_mbq_opc_send_msg_to_peer_pf	= 0x0803,
> +
> +	/* iecm_mbq_opc_send_msg_to_peer_drv:
> +	 *	usage: used by any function to send message to any peer driver
> +	 *	target: RX queue and target host must be specific in descriptor
> +	 */
> +	iecm_mbq_opc_send_msg_to_peer_drv	= 0x0804,
> +};
> +
> +/* API support for control queue management */
> +
> +/* Will init all required q including default mb.  "q_info" is an array of
> + * create_info structs equal to the number of control queues to be created.
> + */
> +int iecm_ctlq_init(struct iecm_hw *hw, u8 num_q,
> +		   struct iecm_ctlq_create_info *q_info);
> +
> +/* Allocate and initialize a single control queue, which will be added to the
> + * control queue list; returns a handle to the created control queue
> + */
> +int iecm_ctlq_add(struct iecm_hw *hw,
> +		  struct iecm_ctlq_create_info *qinfo,
> +		  struct iecm_ctlq_info **cq);
> +
> +/* Deinitialize and deallocate a single control queue */
> +void iecm_ctlq_remove(struct iecm_hw *hw,
> +		      struct iecm_ctlq_info *cq);
> +
> +/* Sends messages to HW and will also free the buffer*/
> +int iecm_ctlq_send(struct iecm_hw *hw,
> +		   struct iecm_ctlq_info *cq,
> +		   u16 num_q_msg,
> +		   struct iecm_ctlq_msg q_msg[]);
> +
> +/* Receives messages and called by interrupt handler/polling
> + * initiated by app/process. Also caller is supposed to free the buffers
> + */
> +int iecm_ctlq_recv(struct iecm_ctlq_info *cq, u16 *num_q_msg,
> +		   struct iecm_ctlq_msg *q_msg);
> +
> +/* Reclaims send descriptors on HW write back */
> +int iecm_ctlq_clean_sq(struct iecm_ctlq_info *cq, u16 *clean_count,
> +		       struct iecm_ctlq_msg *msg_status[]);
> +
> +/* Indicate RX buffers are done being processed */
> +int iecm_ctlq_post_rx_buffs(struct iecm_hw *hw,
> +			    struct iecm_ctlq_info *cq,
> +			    u16 *buff_count,
> +			    struct iecm_dma_mem **buffs);
> +
> +/* Will destroy all q including the default mb */
> +int iecm_ctlq_deinit(struct iecm_hw *hw);
> +
> +#endif /* _IECM_CONTROLQ_API_H_ */

--- 8< ---

> -- 
> 2.33.0

Thanks,
Al

^ permalink raw reply	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 05/19] iecm: add vport alloc and virtchnl messages
  2022-01-28  0:09 ` [Intel-wired-lan] [PATCH net-next 05/19] iecm: add vport alloc and virtchnl messages Alan Brady
  2022-01-28  4:19     ` kernel test robot
@ 2022-01-28 12:32   ` Alexander Lobakin
  2022-02-02 22:21     ` Brady, Alan
  1 sibling, 1 reply; 89+ messages in thread
From: Alexander Lobakin @ 2022-01-28 12:32 UTC (permalink / raw)
  To: intel-wired-lan

From: Alan Brady <alan.brady@intel.com>
Date: Thu, 27 Jan 2022 16:09:55 -0800

> After handling hard reset, we end up in init task. This starts by
> allocating and setting up a vport. To do that we need implement virtchnl
> messages.
> 
> The virtchnl messages are also offloaded into function pointers so that a
> device driver may override them. Here a default implementation is provided
> for devices using virtchnl 2.0 but there exists the flexibility add
> virtchnl 1.1 support through function pointers.
> 
> Signed-off-by: Phani Burra <phani.r.burra@intel.com>
> Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
> Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
> Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
> Signed-off-by: Alan Brady <alan.brady@intel.com>
> ---
>  drivers/net/ethernet/intel/iecm/Makefile      |    4 +-
>  drivers/net/ethernet/intel/iecm/iecm_lib.c    |  167 ++-
>  drivers/net/ethernet/intel/iecm/iecm_txrx.c   |   22 +
>  .../net/ethernet/intel/iecm/iecm_virtchnl.c   | 1299 +++++++++++++++++
>  drivers/net/ethernet/intel/include/iecm.h     |  316 +++-
>  .../net/ethernet/intel/include/iecm_txrx.h    |   94 ++
>  6 files changed, 1898 insertions(+), 4 deletions(-)
>  create mode 100644 drivers/net/ethernet/intel/iecm/iecm_txrx.c
> 
> diff --git a/drivers/net/ethernet/intel/iecm/Makefile b/drivers/net/ethernet/intel/iecm/Makefile
> index db8fecb075a6..fcb49402334f 100644
> --- a/drivers/net/ethernet/intel/iecm/Makefile
> +++ b/drivers/net/ethernet/intel/iecm/Makefile
> @@ -7,11 +7,13 @@
>  
>  obj-$(CONFIG_IECM) += iecm.o
>  
> -ccflags-y += -I$(srctree)/drivers/net/ethernet/intel/include
> +ccflags-y += -I$(srctree)/drivers/net/ethernet/intel/include \
> +	     -I$(srctree)/include/linux/avf
>  
>  iecm-y := \
>  	iecm_lib.o \
>  	iecm_virtchnl.o \
> +	iecm_txrx.o \
>  	iecm_controlq.o \
>  	iecm_controlq_setup.o \
>  	iecm_main.o
> diff --git a/drivers/net/ethernet/intel/iecm/iecm_lib.c b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> index 64cdbce2c842..e2e523f0700e 100644
> --- a/drivers/net/ethernet/intel/iecm/iecm_lib.c
> +++ b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> @@ -5,6 +5,11 @@
>  
>  #include "iecm.h"
>  
> +const char * const iecm_vport_vc_state_str[] = {
> +	IECM_FOREACH_VPORT_VC_STATE(IECM_GEN_STRING)
> +};
> +EXPORT_SYMBOL(iecm_vport_vc_state_str);
> +
>  /**
>   * iecm_cfg_hw - Initialize HW struct
>   * @adapter: adapter to setup hw struct for
> @@ -24,6 +29,113 @@ static int iecm_cfg_hw(struct iecm_adapter *adapter)
>  	return 0;
>  }
>  
> +/**
> + * iecm_get_free_slot - get the next non-NULL location index in array
> + * @array: array to search
> + * @size: size of the array
> + * @curr: last known occupied index to be used as a search hint
> + *
> + * void * is being used to keep the functionality generic. This lets us use this
> + * function on any array of pointers.
> + */
> +static int iecm_get_free_slot(void *array, int size, int curr)
> +{
> +	int **tmp_array = (int **)array;
> +	int next;
> +
> +	if (curr < (size - 1) && !tmp_array[curr + 1]) {

Redundant braces around `size - 1`.

> +		next = curr + 1;
> +	} else {
> +		int i = 0;
> +
> +		while ((i < size) && (tmp_array[i]))
> +			i++;
> +		if (i == size)
> +			next = IECM_NO_FREE_SLOT;
> +		else
> +			next = i;
> +	}

One indent level is redundant here. First condition is an oneliner:

	if (curr < (size - 1) && !tmp_array[curr + 1]) {
		return curr + 1;

	while ((i < size) && (tmp_array[i])) {
		...

> +	return next;
> +}
> +
> +/**
> + * iecm_vport_rel - Delete a vport and free its resources
> + * @vport: the vport being removed
> + */
> +static void iecm_vport_rel(struct iecm_vport *vport)
> +{
> +	mutex_destroy(&vport->stop_mutex);
> +	kfree(vport);
> +}
> +
> +/**
> + * iecm_vport_rel_all - Delete all vports
> + * @adapter: adapter from which all vports are being removed
> + */
> +static void iecm_vport_rel_all(struct iecm_adapter *adapter)
> +{
> +	int i;
> +
> +	if (!adapter->vports)
> +		return;
> +
> +	for (i = 0; i < adapter->num_alloc_vport; i++) {
> +		if (!adapter->vports[i])
> +			continue;
> +
> +		iecm_vport_rel(adapter->vports[i]);
> +		adapter->vports[i] = NULL;
> +		adapter->next_vport = 0;
> +	}
> +	adapter->num_alloc_vport = 0;
> +}
> +
> +/**
> + * iecm_vport_alloc - Allocates the next available struct vport in the adapter
> + * @adapter: board private structure
> + * @vport_id: vport identifier
> + *
> + * returns a pointer to a vport on success, NULL on failure.
> + */
> +static struct iecm_vport *
> +iecm_vport_alloc(struct iecm_adapter *adapter, int vport_id)
> +{
> +	struct iecm_vport *vport = NULL;
> +
> +	if (adapter->next_vport == IECM_NO_FREE_SLOT)
> +		return vport;
> +
> +	/* Need to protect the allocation of the vports at the adapter level */
> +	mutex_lock(&adapter->sw_mutex);
> +
> +	vport = kzalloc(sizeof(*vport), GFP_KERNEL);
> +	if (!vport)
> +		goto unlock_adapter;
> +
> +	vport->adapter = adapter;
> +	vport->idx = adapter->next_vport;
> +	vport->compln_clean_budget = IECM_TX_COMPLQ_CLEAN_BUDGET;
> +	adapter->num_alloc_vport++;
> +
> +	/* Setup default MSIX irq handler for the vport */
> +	vport->irq_q_handler = iecm_vport_intr_clean_queues;
> +	vport->q_vector_base = IECM_NONQ_VEC;
> +
> +	mutex_init(&vport->stop_mutex);
> +
> +	/* fill vport slot in the adapter struct */
> +	adapter->vports[adapter->next_vport] = vport;
> +
> +	/* prepare adapter->next_vport for next use */
> +	adapter->next_vport = iecm_get_free_slot(adapter->vports,
> +						 adapter->num_alloc_vport,
> +						 adapter->next_vport);
> +
> +unlock_adapter:
> +	mutex_unlock(&adapter->sw_mutex);
> +	return vport;
> +}
> +
>  /**
>   * iecm_statistics_task - Delayed task to get statistics over mailbox
>   * @work: work_struct handle to our data
> @@ -55,7 +167,25 @@ static void iecm_service_task(struct work_struct *work)
>   */
>  static void iecm_init_task(struct work_struct *work)
>  {
> -	/* stub */
> +	struct iecm_adapter *adapter = container_of(work,
> +						    struct iecm_adapter,
> +						    init_task.work);
> +	struct iecm_vport *vport;
> +	struct pci_dev *pdev;
> +	int vport_id, err;
> +
> +	err = adapter->dev_ops.vc_ops.core_init(adapter, &vport_id);
> +	if (err)
> +		return;
> +
> +	pdev = adapter->pdev;
> +	vport = iecm_vport_alloc(adapter, vport_id);
> +	if (!vport) {
> +		err = -EFAULT;
> +		dev_err(&pdev->dev, "failed to allocate vport: %d\n",
> +			err);
> +		return;
> +	}
>  }
>  
>  /**
> @@ -81,6 +211,31 @@ static int iecm_api_init(struct iecm_adapter *adapter)
>  		return -EINVAL;
>  	}
>  
> +	if (adapter->dev_ops.vc_ops_init) {
> +		struct iecm_virtchnl_ops *vc_ops;
> +
> +		adapter->dev_ops.vc_ops_init(adapter);
> +		vc_ops = &adapter->dev_ops.vc_ops;
> +		if (!(vc_ops->core_init &&
> +		      vc_ops->vport_init &&
> +		      vc_ops->vport_queue_ids_init &&
> +		      vc_ops->get_caps &&
> +		      vc_ops->config_queues &&
> +		      vc_ops->enable_queues &&
> +		      vc_ops->disable_queues &&
> +		      vc_ops->irq_map_unmap &&
> +		      vc_ops->get_set_rss_lut &&
> +		      vc_ops->get_set_rss_hash &&
> +		      vc_ops->adjust_qs &&
> +		      vc_ops->get_ptype &&
> +		      vc_ops->init_max_queues)) {

if (!op1 ||
    !op2 ||
    !opn) would look more natural and more readable here.

> +			dev_err(&pdev->dev, "Invalid device, missing one or more virtchnl functions\n");
> +			return -EINVAL;
> +		}
> +	} else {
> +		iecm_vc_ops_init(adapter);
> +	}

'else' path is a two-liner allowing to save one indent level:

	if (!adapter->dev_ops.vc_ops_init) {
		iecm_vc_ops_init(adapter);
		return 0;
	}

	adapter->dev_ops.vc_ops_init(adapter);
	vc_ops = &adapter->dev_ops.vc_ops;
	if (!(vc_ops->core_init &&
	...

> +
>  	return 0;
>  }
>  
> @@ -93,7 +248,15 @@ static int iecm_api_init(struct iecm_adapter *adapter)
>   */
>  static void iecm_deinit_task(struct iecm_adapter *adapter)
>  {
> -	/* stub */
> +	set_bit(__IECM_REL_RES_IN_PROG, adapter->flags);
> +	/* Wait until the init_task is done else this thread might release
> +	 * the resources first and the other thread might end up in a bad state
> +	 */
> +	cancel_delayed_work_sync(&adapter->init_task);
> +	iecm_vport_rel_all(adapter);
> +
> +	cancel_delayed_work_sync(&adapter->serv_task);
> +	cancel_delayed_work_sync(&adapter->stats_task);
>  }
>  
>  /**
> diff --git a/drivers/net/ethernet/intel/iecm/iecm_txrx.c b/drivers/net/ethernet/intel/iecm/iecm_txrx.c
> new file mode 100644
> index 000000000000..2f5c16a28266
> --- /dev/null
> +++ b/drivers/net/ethernet/intel/iecm/iecm_txrx.c
> @@ -0,0 +1,22 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/* Copyright (C) 2019 Intel Corporation */
> +
> +#include "iecm.h"
> +
> +/**
> + * iecm_vport_intr_clean_queues - MSIX mode Interrupt Handler
> + * @irq: interrupt number
> + * @data: pointer to a q_vector
> + *
> + */
> +irqreturn_t
> +iecm_vport_intr_clean_queues(int __always_unused irq, void *data)
> +{
> +	struct iecm_q_vector *q_vector = (struct iecm_q_vector *)data;
> +
> +	q_vector->total_events++;
> +	napi_schedule(&q_vector->napi);
> +
> +	return IRQ_HANDLED;
> +}
> +
> diff --git a/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> index b8f54b8c700a..aae06064d706 100644
> --- a/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> +++ b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> @@ -3,6 +3,74 @@
>  
>  #include "iecm.h"
>  
> +/**
> + * iecm_recv_event_msg - Receive virtchnl event message
> + * @vport: virtual port structure
> + *
> + * Receive virtchnl event message
> + */
> +static void iecm_recv_event_msg(struct iecm_vport *vport)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct virtchnl_pf_event *vpe;
> +	struct virtchnl2_event *v2e;
> +	bool link_status;
> +	u32 event;
> +
> +	if (adapter->virt_ver_maj < VIRTCHNL_VERSION_MAJOR_2) {
> +		vpe = (struct virtchnl_pf_event *)adapter->vc_msg;
> +		event = vpe->event;
> +	} else {
> +		v2e = (struct virtchnl2_event *)adapter->vc_msg;
> +		event = le32_to_cpu(v2e->event);
> +	}
> +
> +	switch (event) {
> +	case VIRTCHNL2_EVENT_LINK_CHANGE:
> +		if (adapter->virt_ver_maj < VIRTCHNL_VERSION_MAJOR_2) {
> +			if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
> +					    VIRTCHNL2_CAP_LINK_SPEED)) {
> +				adapter->link_speed_mbps =
> +				vpe->event_data.link_event_adv.link_speed;
> +				link_status =
> +				vpe->event_data.link_event_adv.link_status;
> +			} else {
> +				adapter->link_speed =
> +				vpe->event_data.link_event.link_speed;
> +				link_status =
> +				vpe->event_data.link_event.link_status;
> +			}
> +		} else {
> +			adapter->link_speed_mbps = le32_to_cpu(v2e->link_speed);
> +			link_status = v2e->link_status;
> +		}
> +		if (adapter->link_up != link_status) {
> +			adapter->link_up = link_status;
> +			if (adapter->state == __IECM_UP) {
> +				if (adapter->link_up) {
> +					netif_tx_start_all_queues(vport->netdev);
> +					netif_carrier_on(vport->netdev);
> +				} else {
> +					netif_tx_stop_all_queues(vport->netdev);
> +					netif_carrier_off(vport->netdev);
> +				}
> +			}
> +		}
> +		break;
> +	case VIRTCHNL_EVENT_RESET_IMPENDING:
> +		set_bit(__IECM_HR_CORE_RESET, adapter->flags);
> +		queue_delayed_work(adapter->vc_event_wq,
> +				   &adapter->vc_event_task,
> +				   msecs_to_jiffies(10));
> +		break;
> +	default:
> +		dev_err(&vport->adapter->pdev->dev,
> +			"Unknown event %d from PF\n", event);
> +		break;
> +	}
> +	clear_bit(__IECM_VC_MSG_PENDING, adapter->flags);
> +}
> +
>  /**
>   * iecm_mb_clean - Reclaim the send mailbox queue entries
>   * @adapter: Driver specific private structure
> @@ -39,6 +107,865 @@ static int iecm_mb_clean(struct iecm_adapter *adapter)
>  	return err;
>  }
>  
> +/**
> + * iecm_send_mb_msg - Send message over mailbox
> + * @adapter: Driver specific private structure
> + * @op: virtchnl opcode
> + * @msg_size: size of the payload
> + * @msg: pointer to buffer holding the payload
> + *
> + * Will prepare the control queue message and initiates the send api
> + *
> + * Returns 0 on success, negative on failure
> + */
> +int iecm_send_mb_msg(struct iecm_adapter *adapter, enum virtchnl_ops op,
> +		     u16 msg_size, u8 *msg)
> +{
> +	struct iecm_ctlq_msg *ctlq_msg;
> +	struct iecm_dma_mem *dma_mem;
> +	int err = 0;
> +
> +	if (iecm_is_reset_detected(adapter))
> +		return -EBUSY;
> +
> +	err = iecm_mb_clean(adapter);
> +	if (err)
> +		return err;
> +
> +	ctlq_msg = kzalloc(sizeof(*ctlq_msg), GFP_KERNEL);
> +	if (!ctlq_msg)
> +		return -ENOMEM;
> +
> +	dma_mem = kzalloc(sizeof(*dma_mem), GFP_KERNEL);
> +	if (!dma_mem) {
> +		err = -ENOMEM;
> +		goto dma_mem_error;
> +	}
> +
> +	memset(ctlq_msg, 0, sizeof(struct iecm_ctlq_msg));
> +	ctlq_msg->opcode = iecm_mbq_opc_send_msg_to_pf;
> +	ctlq_msg->func_id = 0;
> +	ctlq_msg->data_len = msg_size;
> +	ctlq_msg->cookie.mbx.chnl_opcode = op;
> +	ctlq_msg->cookie.mbx.chnl_retval = VIRTCHNL_STATUS_SUCCESS;
> +	dma_mem->size = IECM_DFLT_MBX_BUF_SIZE;
> +	dma_mem->va = dmam_alloc_coherent(&adapter->pdev->dev, dma_mem->size,
> +					  &dma_mem->pa, GFP_KERNEL);
> +	if (!dma_mem->va) {
> +		err = -ENOMEM;
> +		goto dma_alloc_error;
> +	}
> +	memcpy(dma_mem->va, msg, msg_size);
> +	ctlq_msg->ctx.indirect.payload = dma_mem;
> +
> +	err = iecm_ctlq_send(&adapter->hw, adapter->hw.asq, 1, ctlq_msg);
> +	if (err)
> +		goto send_error;
> +
> +	return 0;
> +send_error:
> +	dmam_free_coherent(&adapter->pdev->dev, dma_mem->size, dma_mem->va,
> +			   dma_mem->pa);
> +dma_alloc_error:
> +	kfree(dma_mem);
> +dma_mem_error:
> +	kfree(ctlq_msg);
> +	return err;
> +}
> +EXPORT_SYMBOL(iecm_send_mb_msg);
> +
> +/**
> + * iecm_set_msg_pending_bit - Wait for clear and set msg pending
> + * @adapter: driver specific private structure
> + *
> + * If clear sets msg pending bit, otherwise waits for it to clear before
> + * setting it again. Returns 0 on success, negative on failure.
> + */
> +static int iecm_set_msg_pending_bit(struct iecm_adapter *adapter)
> +{
> +	unsigned int retries = 100;
> +
> +	/* If msg pending bit already set, there's a message waiting to be
> +	 * parsed and we must wait for it to be cleared before copying a new
> +	 * message into the vc_msg buffer or else we'll stomp all over the
> +	 * previous message.
> +	 */
> +	while (retries) {
> +		if (!test_and_set_bit(__IECM_VC_MSG_PENDING,
> +				      adapter->flags))
> +			break;
> +		msleep(20);
> +		retries--;
> +	}
> +	return retries ? 0 : -ETIMEDOUT;
> +}
> +
> +/**
> + * iecm_set_msg_pending - Wait for msg pending bit and copy msg to buf
> + * @adapter: driver specific private structure
> + * @ctlq_msg: msg to copy from
> + * @err_enum: err bit to set on error
> + *
> + * Copies payload from ctlq_msg into vc_msg buf in adapter and sets msg pending
> + * bit. Returns 0 on success, negative on failure.
> + */
> +int iecm_set_msg_pending(struct iecm_adapter *adapter,
> +			 struct iecm_ctlq_msg *ctlq_msg,
> +			 enum iecm_vport_vc_state err_enum)
> +{
> +	if (ctlq_msg->cookie.mbx.chnl_retval) {
> +		set_bit(err_enum, adapter->vc_state);
> +		return -EINVAL;
> +	}
> +
> +	if (iecm_set_msg_pending_bit(adapter)) {
> +		set_bit(err_enum, adapter->vc_state);
> +		dev_info(&adapter->pdev->dev, "Timed out setting msg pending\n");
> +		return -ETIMEDOUT;
> +	}
> +
> +	memcpy(adapter->vc_msg, ctlq_msg->ctx.indirect.payload->va,
> +	       min_t(int, ctlq_msg->ctx.indirect.payload->size,
> +		     IECM_DFLT_MBX_BUF_SIZE));
> +	return 0;
> +}
> +EXPORT_SYMBOL(iecm_set_msg_pending);
> +
> +/**
> + * iecm_recv_mb_msg - Receive message over mailbox
> + * @adapter: Driver specific private structure
> + * @op: virtchannel operation code
> + * @msg: Received message holding buffer
> + * @msg_size: message size
> + *
> + * Will receive control queue message and posts the receive buffer. Returns 0
> + * on success and negative on failure.
> + */
> +int iecm_recv_mb_msg(struct iecm_adapter *adapter, enum virtchnl_ops op,
> +		     void *msg, int msg_size)
> +{
> +	struct iecm_ctlq_msg ctlq_msg;
> +	struct iecm_dma_mem *dma_mem;
> +	struct iecm_vport *vport;
> +	bool work_done = false;
> +	int num_retry = 2000;
> +	int payload_size = 0;
> +	u16 num_q_msg;
> +	int err = 0;
> +
> +	vport = adapter->vports[0];
> +	while (1) {
> +		/* Try to get one message */
> +		num_q_msg = 1;
> +		dma_mem = NULL;
> +		err = iecm_ctlq_recv(adapter->hw.arq, &num_q_msg, &ctlq_msg);
> +		/* If no message then decide if we have to retry based on
> +		 * opcode
> +		 */
> +		if (err || !num_q_msg) {
> +			/* Increasing num_retry to consider the delayed
> +			 * responses because of large number of VF's mailbox
> +			 * messages. If the mailbox message is received from
> +			 * the other side, we come out of the sleep cycle
> +			 * immediately else we wait for more time.
> +			 */
> +			if (op && num_retry-- &&
> +			    !test_bit(__IECM_REL_RES_IN_PROG, adapter->flags)) {
> +				msleep(20);
> +				continue;
> +			} else {
> +				break;
> +			}

Since if-condition always ends with a 'continue', 'else' is
reduntant here.

> +		}
> +
> +		/* If we are here a message is received. Check if we are looking
> +		 * for a specific message based on opcode. If it is different
> +		 * ignore and post buffers
> +		 */
> +		if (op && ctlq_msg.cookie.mbx.chnl_opcode != op)
> +			goto post_buffs;
> +
> +		if (ctlq_msg.data_len)
> +			payload_size = ctlq_msg.ctx.indirect.payload->size;
> +
> +		/* All conditions are met. Either a message requested is
> +		 * received or we received a message to be processed
> +		 */
> +		switch (ctlq_msg.cookie.mbx.chnl_opcode) {
> +		case VIRTCHNL_OP_VERSION:
> +		case VIRTCHNL2_OP_GET_CAPS:
> +		case VIRTCHNL2_OP_CREATE_VPORT:
> +			if (ctlq_msg.cookie.mbx.chnl_retval) {
> +				dev_info(&adapter->pdev->dev, "Failure initializing, vc op: %u retval: %u\n",
> +					 ctlq_msg.cookie.mbx.chnl_opcode,
> +					 ctlq_msg.cookie.mbx.chnl_retval);
> +				err = -EBADMSG;
> +			} else if (msg) {
> +				memcpy(msg, ctlq_msg.ctx.indirect.payload->va,
> +				       min_t(int,
> +					     payload_size, msg_size));
> +			}
> +			work_done = true;
> +			break;
> +		case VIRTCHNL2_OP_ENABLE_VPORT:
> +			if (ctlq_msg.cookie.mbx.chnl_retval)
> +				set_bit(IECM_VC_ENA_VPORT_ERR,
> +					adapter->vc_state);
> +			set_bit(IECM_VC_ENA_VPORT, adapter->vc_state);
> +			wake_up(&adapter->vchnl_wq);
> +			break;
> +		case VIRTCHNL2_OP_DISABLE_VPORT:
> +			if (ctlq_msg.cookie.mbx.chnl_retval)
> +				set_bit(IECM_VC_DIS_VPORT_ERR,
> +					adapter->vc_state);
> +			set_bit(IECM_VC_DIS_VPORT, adapter->vc_state);
> +			wake_up(&adapter->vchnl_wq);
> +			break;
> +		case VIRTCHNL2_OP_DESTROY_VPORT:
> +			if (ctlq_msg.cookie.mbx.chnl_retval)
> +				set_bit(IECM_VC_DESTROY_VPORT_ERR,
> +					adapter->vc_state);
> +			set_bit(IECM_VC_DESTROY_VPORT, adapter->vc_state);
> +			wake_up(&adapter->vchnl_wq);
> +			break;
> +		case VIRTCHNL2_OP_CONFIG_TX_QUEUES:
> +			if (ctlq_msg.cookie.mbx.chnl_retval)
> +				set_bit(IECM_VC_CONFIG_TXQ_ERR,
> +					adapter->vc_state);
> +			set_bit(IECM_VC_CONFIG_TXQ, adapter->vc_state);
> +			wake_up(&adapter->vchnl_wq);
> +			break;
> +		case VIRTCHNL2_OP_CONFIG_RX_QUEUES:
> +			if (ctlq_msg.cookie.mbx.chnl_retval)
> +				set_bit(IECM_VC_CONFIG_RXQ_ERR,
> +					adapter->vc_state);
> +			set_bit(IECM_VC_CONFIG_RXQ, adapter->vc_state);
> +			wake_up(&adapter->vchnl_wq);
> +			break;
> +		case VIRTCHNL2_OP_ENABLE_QUEUES:
> +			if (ctlq_msg.cookie.mbx.chnl_retval)
> +				set_bit(IECM_VC_ENA_QUEUES_ERR,
> +					adapter->vc_state);
> +			set_bit(IECM_VC_ENA_QUEUES, adapter->vc_state);
> +			wake_up(&adapter->vchnl_wq);
> +			break;
> +		case VIRTCHNL2_OP_DISABLE_QUEUES:
> +			if (ctlq_msg.cookie.mbx.chnl_retval)
> +				set_bit(IECM_VC_DIS_QUEUES_ERR,
> +					adapter->vc_state);
> +			set_bit(IECM_VC_DIS_QUEUES, adapter->vc_state);
> +			wake_up(&adapter->vchnl_wq);
> +			break;
> +		case VIRTCHNL2_OP_ADD_QUEUES:
> +			iecm_set_msg_pending(adapter, &ctlq_msg,
> +					     IECM_VC_ADD_QUEUES_ERR);
> +			set_bit(IECM_VC_ADD_QUEUES, adapter->vc_state);
> +			wake_up(&adapter->vchnl_wq);
> +			break;
> +		case VIRTCHNL2_OP_DEL_QUEUES:
> +			if (ctlq_msg.cookie.mbx.chnl_retval)
> +				set_bit(IECM_VC_DEL_QUEUES_ERR,
> +					adapter->vc_state);
> +			set_bit(IECM_VC_DEL_QUEUES, adapter->vc_state);
> +			wake_up(&adapter->vchnl_wq);
> +			break;
> +		case VIRTCHNL2_OP_MAP_QUEUE_VECTOR:
> +			if (ctlq_msg.cookie.mbx.chnl_retval)
> +				set_bit(IECM_VC_MAP_IRQ_ERR,
> +					adapter->vc_state);
> +			set_bit(IECM_VC_MAP_IRQ, adapter->vc_state);
> +			wake_up(&adapter->vchnl_wq);
> +			break;
> +		case VIRTCHNL2_OP_UNMAP_QUEUE_VECTOR:
> +			if (ctlq_msg.cookie.mbx.chnl_retval)
> +				set_bit(IECM_VC_UNMAP_IRQ_ERR,
> +					adapter->vc_state);
> +			set_bit(IECM_VC_UNMAP_IRQ, adapter->vc_state);
> +			wake_up(&adapter->vchnl_wq);
> +			break;
> +		case VIRTCHNL2_OP_GET_STATS:
> +			iecm_set_msg_pending(adapter, &ctlq_msg,
> +					     IECM_VC_GET_STATS_ERR);
> +			set_bit(IECM_VC_GET_STATS, adapter->vc_state);
> +			wake_up(&adapter->vchnl_wq);
> +			break;
> +		case VIRTCHNL2_OP_GET_RSS_HASH:
> +			iecm_set_msg_pending(adapter, &ctlq_msg,
> +					     IECM_VC_GET_RSS_HASH_ERR);
> +			set_bit(IECM_VC_GET_RSS_HASH, adapter->vc_state);
> +			wake_up(&adapter->vchnl_wq);
> +			break;
> +		case VIRTCHNL2_OP_SET_RSS_HASH:
> +			if (ctlq_msg.cookie.mbx.chnl_retval)
> +				set_bit(IECM_VC_SET_RSS_HASH_ERR,
> +					adapter->vc_state);
> +			set_bit(IECM_VC_SET_RSS_HASH, adapter->vc_state);
> +			wake_up(&adapter->vchnl_wq);
> +			break;
> +		case VIRTCHNL2_OP_GET_RSS_LUT:
> +			iecm_set_msg_pending(adapter, &ctlq_msg,
> +					     IECM_VC_GET_RSS_LUT_ERR);
> +			set_bit(IECM_VC_GET_RSS_LUT, adapter->vc_state);
> +			wake_up(&adapter->vchnl_wq);
> +			break;
> +		case VIRTCHNL2_OP_SET_RSS_LUT:
> +			if (ctlq_msg.cookie.mbx.chnl_retval)
> +				set_bit(IECM_VC_SET_RSS_LUT_ERR,
> +					adapter->vc_state);
> +			set_bit(IECM_VC_SET_RSS_LUT, adapter->vc_state);
> +			wake_up(&adapter->vchnl_wq);
> +			break;
> +		case VIRTCHNL2_OP_GET_RSS_KEY:
> +			iecm_set_msg_pending(adapter, &ctlq_msg,
> +					     IECM_VC_GET_RSS_KEY_ERR);
> +			set_bit(IECM_VC_GET_RSS_KEY, adapter->vc_state);
> +			wake_up(&adapter->vchnl_wq);
> +			break;
> +		case VIRTCHNL2_OP_SET_RSS_KEY:
> +			if (ctlq_msg.cookie.mbx.chnl_retval)
> +				set_bit(IECM_VC_SET_RSS_KEY_ERR,
> +					adapter->vc_state);
> +			set_bit(IECM_VC_SET_RSS_KEY, adapter->vc_state);
> +			wake_up(&adapter->vchnl_wq);
> +			break;
> +		case VIRTCHNL2_OP_ALLOC_VECTORS:
> +			iecm_set_msg_pending(adapter, &ctlq_msg,
> +					     IECM_VC_ALLOC_VECTORS_ERR);
> +			set_bit(IECM_VC_ALLOC_VECTORS, adapter->vc_state);
> +			wake_up(&adapter->vchnl_wq);
> +			break;
> +		case VIRTCHNL2_OP_DEALLOC_VECTORS:
> +			if (ctlq_msg.cookie.mbx.chnl_retval)
> +				set_bit(IECM_VC_DEALLOC_VECTORS_ERR,
> +					adapter->vc_state);
> +			set_bit(IECM_VC_DEALLOC_VECTORS, adapter->vc_state);
> +			wake_up(&adapter->vchnl_wq);
> +			break;
> +		case VIRTCHNL2_OP_GET_PTYPE_INFO:
> +			iecm_set_msg_pending(adapter, &ctlq_msg,
> +					     IECM_VC_GET_PTYPE_INFO_ERR);
> +			set_bit(IECM_VC_GET_PTYPE_INFO, adapter->vc_state);
> +			wake_up(&adapter->vchnl_wq);
> +			break;
> +		case VIRTCHNL2_OP_EVENT:
> +		case VIRTCHNL_OP_EVENT:
> +			if (iecm_set_msg_pending_bit(adapter)) {
> +				dev_info(&adapter->pdev->dev, "Timed out setting msg pending\n");
> +			} else {
> +				memcpy(adapter->vc_msg,
> +				       ctlq_msg.ctx.indirect.payload->va,
> +				       min_t(int, payload_size,
> +					     IECM_DFLT_MBX_BUF_SIZE));
> +				iecm_recv_event_msg(vport);
> +			}
> +			break;
> +		case VIRTCHNL_OP_ADD_ETH_ADDR:
> +			if (test_and_clear_bit(__IECM_ADD_ETH_REQ, adapter->flags)) {
> +				/* Message was sent asynchronously. We don't
> +				 * normally print errors here, instead
> +				 * preferring to handle errors in the function
> +				 * calling wait_for_event. However, we will
> +				 * have lost the context in which we sent the
> +				 * message if asynchronous. We can't really do
> +				 * anything about at it this point, but we
> +				 * should at a minimum indicate that it looks
> +				 * like something went wrong. Also don't bother
> +				 * setting ERR bit or waking vchnl_wq since no
> +				 * one will be waiting to read the async
> +				 * message.
> +				 */
> +				if (ctlq_msg.cookie.mbx.chnl_retval) {
> +					dev_err(&adapter->pdev->dev, "Failed to add MAC address: %d\n",
> +						ctlq_msg.cookie.mbx.chnl_retval);
> +				}
> +				break;
> +			}
> +			if (ctlq_msg.cookie.mbx.chnl_retval) {
> +				set_bit(IECM_VC_ADD_ETH_ADDR_ERR,
> +					adapter->vc_state);
> +			}
> +			set_bit(IECM_VC_ADD_ETH_ADDR, adapter->vc_state);
> +			wake_up(&adapter->vchnl_wq);
> +			break;
> +		case VIRTCHNL_OP_DEL_ETH_ADDR:
> +			if (test_and_clear_bit(__IECM_DEL_ETH_REQ, adapter->flags)) {
> +				/* Message was sent asynchronously. We don't
> +				 * normally print errors here, instead
> +				 * preferring to handle errors in the function
> +				 * calling wait_for_event. However, we will
> +				 * have lost the context in which we sent the
> +				 * message if asynchronous. We can't really do
> +				 * anything about at it this point, but we
> +				 * should at a minimum indicate that it looks
> +				 * like something went wrong. Also don't bother
> +				 * setting ERR bit or waking vchnl_wq since no
> +				 * one will be waiting to read the async
> +				 * message.
> +				 */
> +				if (ctlq_msg.cookie.mbx.chnl_retval) {
> +					dev_err(&adapter->pdev->dev, "Failed to delete MAC address: %d\n",
> +						ctlq_msg.cookie.mbx.chnl_retval);
> +				}
> +				break;
> +			}
> +			if (ctlq_msg.cookie.mbx.chnl_retval) {
> +				set_bit(IECM_VC_DEL_ETH_ADDR_ERR,
> +					adapter->vc_state);
> +			}
> +			set_bit(IECM_VC_DEL_ETH_ADDR, adapter->vc_state);
> +			wake_up(&adapter->vchnl_wq);
> +			break;
> +		case VIRTCHNL_OP_GET_OFFLOAD_VLAN_V2_CAPS:
> +			if (ctlq_msg.cookie.mbx.chnl_retval) {
> +				set_bit(IECM_VC_OFFLOAD_VLAN_V2_CAPS_ERR, adapter->vc_state);
> +			} else {
> +				memcpy(adapter->vc_msg,
> +				       ctlq_msg.ctx.indirect.payload->va,
> +				       min_t(int, payload_size,
> +					     IECM_DFLT_MBX_BUF_SIZE));
> +			}
> +			set_bit(IECM_VC_OFFLOAD_VLAN_V2_CAPS, adapter->vc_state);
> +			wake_up(&adapter->vchnl_wq);
> +			break;
> +		case VIRTCHNL_OP_ADD_VLAN_V2:
> +			if (ctlq_msg.cookie.mbx.chnl_retval) {
> +				dev_err(&adapter->pdev->dev, "Failed to add vlan filter: %d\n",
> +					ctlq_msg.cookie.mbx.chnl_retval);
> +			}
> +			break;
> +		case VIRTCHNL_OP_DEL_VLAN_V2:
> +			if (ctlq_msg.cookie.mbx.chnl_retval) {
> +				dev_err(&adapter->pdev->dev, "Failed to delete vlan filter: %d\n",
> +					ctlq_msg.cookie.mbx.chnl_retval);
> +			}
> +			break;
> +		case VIRTCHNL_OP_ENABLE_VLAN_INSERTION_V2:
> +			if (ctlq_msg.cookie.mbx.chnl_retval) {
> +				set_bit(IECM_VC_INSERTION_ENA_VLAN_V2_ERR,
> +					adapter->vc_state);
> +			}
> +			set_bit(IECM_VC_INSERTION_ENA_VLAN_V2,
> +				adapter->vc_state);
> +			wake_up(&adapter->vchnl_wq);
> +			break;
> +		case VIRTCHNL_OP_DISABLE_VLAN_INSERTION_V2:
> +			if (ctlq_msg.cookie.mbx.chnl_retval) {
> +				set_bit(IECM_VC_INSERTION_DIS_VLAN_V2_ERR,
> +					adapter->vc_state);
> +			}
> +			set_bit(IECM_VC_INSERTION_DIS_VLAN_V2,
> +				adapter->vc_state);
> +			wake_up(&adapter->vchnl_wq);
> +			break;
> +		case VIRTCHNL_OP_ENABLE_VLAN_STRIPPING_V2:
> +			if (ctlq_msg.cookie.mbx.chnl_retval) {
> +				set_bit(IECM_VC_STRIPPING_ENA_VLAN_V2_ERR,
> +					adapter->vc_state);
> +			}
> +			set_bit(IECM_VC_STRIPPING_ENA_VLAN_V2,
> +				adapter->vc_state);
> +			wake_up(&adapter->vchnl_wq);
> +			break;
> +		case VIRTCHNL_OP_DISABLE_VLAN_STRIPPING_V2:
> +			if (ctlq_msg.cookie.mbx.chnl_retval) {
> +				set_bit(IECM_VC_STRIPPING_DIS_VLAN_V2_ERR,
> +					adapter->vc_state);
> +			}
> +			set_bit(IECM_VC_STRIPPING_DIS_VLAN_V2,
> +				adapter->vc_state);
> +			wake_up(&adapter->vchnl_wq);
> +			break;
> +		case VIRTCHNL_OP_CONFIG_PROMISCUOUS_MODE:
> +			/* This message can only be sent asynchronously. As
> +			 * such we'll have lost the context in which it was
> +			 * called and thus can only really report if it looks
> +			 * like an error occurred. Don't bother setting ERR bit
> +			 * or waking chnl_wq since no will be waiting to
> +			 * reading message.
> +			 */
> +			if (ctlq_msg.cookie.mbx.chnl_retval) {
> +				dev_err(&adapter->pdev->dev, "Failed to set promiscuous mode: %d\n",
> +					ctlq_msg.cookie.mbx.chnl_retval);
> +			}
> +			break;
> +		case VIRTCHNL_OP_ADD_CLOUD_FILTER:
> +			if (ctlq_msg.cookie.mbx.chnl_retval) {
> +				dev_err(&adapter->pdev->dev, "Failed to add cloud filter: %d\n",
> +					ctlq_msg.cookie.mbx.chnl_retval);
> +				set_bit(IECM_VC_ADD_CLOUD_FILTER_ERR,
> +					adapter->vc_state);
> +			}
> +			set_bit(IECM_VC_ADD_CLOUD_FILTER, adapter->vc_state);
> +			wake_up(&adapter->vchnl_wq);
> +			break;
> +		case VIRTCHNL_OP_DEL_CLOUD_FILTER:
> +			if (ctlq_msg.cookie.mbx.chnl_retval) {
> +				dev_err(&adapter->pdev->dev, "Failed to delete cloud filter: %d\n",
> +					ctlq_msg.cookie.mbx.chnl_retval);
> +				set_bit(IECM_VC_DEL_CLOUD_FILTER_ERR,
> +					adapter->vc_state);
> +			}
> +			set_bit(IECM_VC_DEL_CLOUD_FILTER, adapter->vc_state);
> +			wake_up(&adapter->vchnl_wq);
> +			break;
> +		case VIRTCHNL_OP_ADD_RSS_CFG:
> +			if (ctlq_msg.cookie.mbx.chnl_retval) {
> +				dev_err(&adapter->pdev->dev, "Failed to add RSS configuration: %d\n",
> +					ctlq_msg.cookie.mbx.chnl_retval);
> +				set_bit(IECM_VC_ADD_RSS_CFG_ERR,
> +					adapter->vc_state);
> +			}
> +			set_bit(IECM_VC_ADD_RSS_CFG, adapter->vc_state);
> +			wake_up(&adapter->vchnl_wq);
> +			break;
> +		case VIRTCHNL_OP_DEL_RSS_CFG:
> +			if (ctlq_msg.cookie.mbx.chnl_retval) {
> +				dev_err(&adapter->pdev->dev, "Failed to delete RSS configuration: %d\n",
> +					ctlq_msg.cookie.mbx.chnl_retval);
> +				set_bit(IECM_VC_DEL_RSS_CFG_ERR,
> +					adapter->vc_state);
> +			}
> +			set_bit(IECM_VC_DEL_RSS_CFG, adapter->vc_state);
> +			wake_up(&adapter->vchnl_wq);
> +			break;
> +		case VIRTCHNL_OP_ADD_FDIR_FILTER:
> +			iecm_set_msg_pending(adapter, &ctlq_msg,
> +					     IECM_VC_ADD_FDIR_FILTER_ERR);
> +			set_bit(IECM_VC_ADD_FDIR_FILTER, adapter->vc_state);
> +			wake_up(&adapter->vchnl_wq);
> +			break;
> +		case VIRTCHNL_OP_DEL_FDIR_FILTER:
> +			iecm_set_msg_pending(adapter, &ctlq_msg,
> +					     IECM_VC_DEL_FDIR_FILTER_ERR);
> +			set_bit(IECM_VC_DEL_FDIR_FILTER, adapter->vc_state);
> +			wake_up(&adapter->vchnl_wq);
> +			break;
> +		case VIRTCHNL_OP_ENABLE_CHANNELS:
> +			if (ctlq_msg.cookie.mbx.chnl_retval) {
> +				dev_err(&adapter->pdev->dev, "Failed to enable channels: %d\n",
> +					ctlq_msg.cookie.mbx.chnl_retval);
> +				set_bit(IECM_VC_ENA_CHANNELS_ERR,
> +					adapter->vc_state);
> +			}
> +			set_bit(IECM_VC_ENA_CHANNELS, adapter->vc_state);
> +			wake_up(&adapter->vchnl_wq);
> +			break;
> +		case VIRTCHNL_OP_DISABLE_CHANNELS:
> +			if (ctlq_msg.cookie.mbx.chnl_retval) {
> +				dev_err(&adapter->pdev->dev, "Failed to disable channels: %d\n",
> +					ctlq_msg.cookie.mbx.chnl_retval);
> +				set_bit(IECM_VC_DIS_CHANNELS_ERR,
> +					adapter->vc_state);
> +			}
> +			set_bit(IECM_VC_DIS_CHANNELS, adapter->vc_state);
> +			wake_up(&adapter->vchnl_wq);
> +			break;
> +		default:
> +			if (adapter->dev_ops.vc_ops.recv_mbx_msg)
> +				err =
> +				adapter->dev_ops.vc_ops.recv_mbx_msg(adapter,
> +								   msg,
> +								   msg_size,
> +								   &ctlq_msg,
> +								   &work_done);
> +			else
> +				dev_warn(&adapter->pdev->dev,
> +					 "Unhandled virtchnl response %d\n",
> +					 ctlq_msg.cookie.mbx.chnl_opcode);
> +			break;
> +		} /* switch v_opcode */
> +post_buffs:
> +		if (ctlq_msg.data_len)
> +			dma_mem = ctlq_msg.ctx.indirect.payload;
> +		else
> +			num_q_msg = 0;
> +
> +		err = iecm_ctlq_post_rx_buffs(&adapter->hw, adapter->hw.arq,
> +					      &num_q_msg, &dma_mem);
> +		/* If post failed clear the only buffer we supplied */
> +		if (err && dma_mem)
> +			dmam_free_coherent(&adapter->pdev->dev, dma_mem->size,
> +					   dma_mem->va, dma_mem->pa);
> +		/* Applies only if we are looking for a specific opcode */
> +		if (work_done)
> +			break;
> +	}
> +
> +	return err;
> +}
> +EXPORT_SYMBOL(iecm_recv_mb_msg);
> +
> +/**
> + * iecm_send_ver_msg - send virtchnl version message
> + * @adapter: Driver specific private structure
> + *
> + * Send virtchnl version message.  Returns 0 on success, negative on failure.
> + */
> +static int iecm_send_ver_msg(struct iecm_adapter *adapter)
> +{
> +	struct virtchnl_version_info vvi;
> +
> +	if (adapter->virt_ver_maj) {
> +		vvi.major = adapter->virt_ver_maj;
> +		vvi.minor = adapter->virt_ver_min;
> +	} else {
> +		vvi.major = IECM_VIRTCHNL_VERSION_MAJOR;
> +		vvi.minor = IECM_VIRTCHNL_VERSION_MINOR;
> +	}
> +
> +	return iecm_send_mb_msg(adapter, VIRTCHNL_OP_VERSION, sizeof(vvi),
> +				(u8 *)&vvi);
> +}
> +
> +/**
> + * iecm_recv_ver_msg - Receive virtchnl version message
> + * @adapter: Driver specific private structure
> + *
> + * Receive virtchnl version message. Returns 0 on success, -EAGAIN if we need
> + * to send version message again, otherwise negative on failure.
> + */
> +static int iecm_recv_ver_msg(struct iecm_adapter *adapter)
> +{
> +	struct virtchnl_version_info vvi;
> +	int err = 0;
> +
> +	err = iecm_recv_mb_msg(adapter, VIRTCHNL_OP_VERSION, &vvi, sizeof(vvi));
> +	if (err)
> +		return err;
> +
> +	if (vvi.major > IECM_VIRTCHNL_VERSION_MAJOR) {
> +		dev_warn(&adapter->pdev->dev, "Virtchnl major version greater than supported\n");
> +		return -EINVAL;
> +	}
> +	if (vvi.major == IECM_VIRTCHNL_VERSION_MAJOR &&
> +	    vvi.minor > IECM_VIRTCHNL_VERSION_MINOR)
> +		dev_warn(&adapter->pdev->dev, "Virtchnl minor version not matched\n");
> +
> +	/* If we have a mismatch, resend version to update receiver on what
> +	 * version we will use.
> +	 */
> +	if (!adapter->virt_ver_maj &&
> +	    vvi.major != IECM_VIRTCHNL_VERSION_MAJOR &&
> +	    vvi.minor != IECM_VIRTCHNL_VERSION_MINOR)
> +		err = -EAGAIN;
> +
> +	adapter->virt_ver_maj = vvi.major;
> +	adapter->virt_ver_min = vvi.minor;
> +
> +	return err;
> +}
> +
> +/**
> + * iecm_send_get_caps_msg - Send virtchnl get capabilities message
> + * @adapter: Driver specific private structure
> + *
> + * Send virtchl get capabilities message. Returns 0 on success, negative on
> + * failure.
> + */
> +int iecm_send_get_caps_msg(struct iecm_adapter *adapter)
> +{
> +	struct virtchnl2_get_capabilities caps = {0};
> +	int buf_size;
> +
> +	buf_size = sizeof(struct virtchnl2_get_capabilities);
> +	adapter->caps = kzalloc(buf_size, GFP_KERNEL);
> +	if (!adapter->caps)
> +		return -ENOMEM;
> +
> +	caps.csum_caps =
> +		cpu_to_le32(VIRTCHNL2_CAP_TX_CSUM_L3_IPV4	|
> +			    VIRTCHNL2_CAP_TX_CSUM_L4_IPV4_TCP	|
> +			    VIRTCHNL2_CAP_TX_CSUM_L4_IPV4_UDP	|
> +			    VIRTCHNL2_CAP_TX_CSUM_L4_IPV4_SCTP	|
> +			    VIRTCHNL2_CAP_TX_CSUM_L4_IPV6_TCP	|
> +			    VIRTCHNL2_CAP_TX_CSUM_L4_IPV6_UDP	|
> +			    VIRTCHNL2_CAP_TX_CSUM_L4_IPV6_SCTP	|
> +			    VIRTCHNL2_CAP_TX_CSUM_GENERIC	|
> +			    VIRTCHNL2_CAP_RX_CSUM_L3_IPV4	|
> +			    VIRTCHNL2_CAP_RX_CSUM_L4_IPV4_TCP	|
> +			    VIRTCHNL2_CAP_RX_CSUM_L4_IPV4_UDP	|
> +			    VIRTCHNL2_CAP_RX_CSUM_L4_IPV4_SCTP	|
> +			    VIRTCHNL2_CAP_RX_CSUM_L4_IPV6_TCP	|
> +			    VIRTCHNL2_CAP_RX_CSUM_L4_IPV6_UDP	|
> +			    VIRTCHNL2_CAP_RX_CSUM_L4_IPV6_SCTP	|
> +			    VIRTCHNL2_CAP_RX_CSUM_GENERIC);
> +
> +	caps.seg_caps =
> +		cpu_to_le32(VIRTCHNL2_CAP_SEG_IPV4_TCP		|
> +			    VIRTCHNL2_CAP_SEG_IPV4_UDP		|
> +			    VIRTCHNL2_CAP_SEG_IPV4_SCTP		|
> +			    VIRTCHNL2_CAP_SEG_IPV6_TCP		|
> +			    VIRTCHNL2_CAP_SEG_IPV6_UDP		|
> +			    VIRTCHNL2_CAP_SEG_IPV6_SCTP		|
> +			    VIRTCHNL2_CAP_SEG_GENERIC);
> +
> +	caps.rss_caps =
> +		cpu_to_le64(VIRTCHNL2_CAP_RSS_IPV4_TCP		|
> +			    VIRTCHNL2_CAP_RSS_IPV4_UDP		|
> +			    VIRTCHNL2_CAP_RSS_IPV4_SCTP		|
> +			    VIRTCHNL2_CAP_RSS_IPV4_OTHER	|
> +			    VIRTCHNL2_CAP_RSS_IPV6_TCP		|
> +			    VIRTCHNL2_CAP_RSS_IPV6_UDP		|
> +			    VIRTCHNL2_CAP_RSS_IPV6_SCTP		|
> +			    VIRTCHNL2_CAP_RSS_IPV6_OTHER	|
> +			    VIRTCHNL2_CAP_RSS_IPV4_AH		|
> +			    VIRTCHNL2_CAP_RSS_IPV4_ESP		|
> +			    VIRTCHNL2_CAP_RSS_IPV4_AH_ESP	|
> +			    VIRTCHNL2_CAP_RSS_IPV6_AH		|
> +			    VIRTCHNL2_CAP_RSS_IPV6_ESP		|
> +			    VIRTCHNL2_CAP_RSS_IPV6_AH_ESP);
> +
> +	caps.hsplit_caps =
> +		cpu_to_le32(VIRTCHNL2_CAP_RX_HSPLIT_AT_L2	|
> +			    VIRTCHNL2_CAP_RX_HSPLIT_AT_L3	|
> +			    VIRTCHNL2_CAP_RX_HSPLIT_AT_L4V4	|
> +			    VIRTCHNL2_CAP_RX_HSPLIT_AT_L4V6);
> +
> +	caps.rsc_caps =
> +		cpu_to_le32(VIRTCHNL2_CAP_RSC_IPV4_TCP		|
> +			    VIRTCHNL2_CAP_RSC_IPV4_SCTP		|
> +			    VIRTCHNL2_CAP_RSC_IPV6_TCP		|
> +			    VIRTCHNL2_CAP_RSC_IPV6_SCTP);
> +
> +	caps.other_caps =
> +		cpu_to_le64(VIRTCHNL2_CAP_RDMA			|
> +			    VIRTCHNL2_CAP_SRIOV			|
> +			    VIRTCHNL2_CAP_MACFILTER		|
> +			    VIRTCHNL2_CAP_FLOW_DIRECTOR		|
> +			    VIRTCHNL2_CAP_SPLITQ_QSCHED		|
> +			    VIRTCHNL2_CAP_CRC			|
> +			    VIRTCHNL2_CAP_ADQ			|
> +			    VIRTCHNL2_CAP_WB_ON_ITR		|
> +			    VIRTCHNL2_CAP_PROMISC		|
> +			    VIRTCHNL2_CAP_INLINE_IPSEC		|
> +			    VIRTCHNL2_CAP_VLAN			|
> +			    VIRTCHNL2_CAP_RX_FLEX_DESC);
> +
> +	return iecm_send_mb_msg(adapter, VIRTCHNL2_OP_GET_CAPS, sizeof(caps),
> +				(u8 *)&caps);
> +}
> +EXPORT_SYMBOL(iecm_send_get_caps_msg);
> +
> +/**
> + * iecm_recv_get_caps_msg - Receive virtchnl get capabilities message
> + * @adapter: Driver specific private structure
> + *
> + * Receive virtchnl get capabilities message.  Returns 0 on succes, negative on
> + * failure.
> + */
> +static int iecm_recv_get_caps_msg(struct iecm_adapter *adapter)
> +{
> +	return iecm_recv_mb_msg(adapter, VIRTCHNL2_OP_GET_CAPS, adapter->caps,
> +				sizeof(struct virtchnl2_get_capabilities));
> +}
> +
> +/**
> + * iecm_send_create_vport_msg - Send virtchnl create vport message
> + * @adapter: Driver specific private structure
> + *
> + * send virtchnl creae vport message
> + *
> + * Returns 0 on success, negative on failure
> + */
> +static int iecm_send_create_vport_msg(struct iecm_adapter *adapter)
> +{
> +	/* stub */
> +	return 0;
> +}
> +
> +/**
> + * iecm_recv_create_vport_msg - Receive virtchnl create vport message
> + * @adapter: Driver specific private structure
> + * @vport_id: Virtual port identifier
> + *
> + * Receive virtchnl create vport message.  Returns 0 on success, negative on
> + * failure.
> + */
> +static int iecm_recv_create_vport_msg(struct iecm_adapter *adapter,
> +				      int *vport_id)
> +{
> +	/* stub */
> +	return 0;
> +}
> +
> +/**
> + * __iecm_wait_for_event - wrapper function for wait on virtchannel response
> + * @adapter: Driver private data structure
> + * @state: check on state upon timeout
> + * @err_check: check if this specific error bit is set
> + * @timeout: Max time to wait
> + *
> + * Checks if state is set upon expiry of timeout.  Returns 0 on success,
> + * negative on failure.
> + */
> +static int __iecm_wait_for_event(struct iecm_adapter *adapter,
> +				 enum iecm_vport_vc_state state,
> +				 enum iecm_vport_vc_state err_check,
> +				 int timeout)
> +{
> +	int event;
> +
> +	event = wait_event_timeout(adapter->vchnl_wq,
> +				   test_and_clear_bit(state, adapter->vc_state),
> +				   msecs_to_jiffies(timeout));
> +	if (event) {
> +		if (test_and_clear_bit(err_check, adapter->vc_state)) {
> +			dev_err(&adapter->pdev->dev, "VC response error %s\n",
> +				iecm_vport_vc_state_str[err_check]);
> +			return -EINVAL;
> +		}
> +		return 0;
> +	}
> +
> +	/* Timeout occurred */
> +	dev_err(&adapter->pdev->dev, "VC timeout, state = %s\n",
> +		iecm_vport_vc_state_str[state]);
> +	return -ETIMEDOUT;
> +}
> +
> +/**
> + * iecm_min_wait_for_event - wait for virtchannel response
> + * @adapter: Driver private data structure
> + * @state: check on state upon timeout
> + * @err_check: check if this specific error bit is set
> + *
> + * Returns 0 on success, negative on failure.
> + */
> +int iecm_min_wait_for_event(struct iecm_adapter *adapter,
> +			    enum iecm_vport_vc_state state,
> +			    enum iecm_vport_vc_state err_check)
> +{
> +	int timeout = 2000;
> +
> +	return __iecm_wait_for_event(adapter, state, err_check, timeout);
> +}
> +EXPORT_SYMBOL(iecm_min_wait_for_event);
> +
> +/**
> + * iecm_wait_for_event - wait for virtchannel response
> + * @adapter: Driver private data structure
> + * @state: check on state upon timeout after 500ms
> + * @err_check: check if this specific error bit is set
> + *
> + * Returns 0 on success, negative on failure.
> + */
> +int iecm_wait_for_event(struct iecm_adapter *adapter,
> +			enum iecm_vport_vc_state state,
> +			enum iecm_vport_vc_state err_check)
> +{
> +	/* Increasing the timeout in __IECM_INIT_SW flow to consider large
> +	 * number of VF's mailbox message responses. When a message is received
> +	 * on mailbox, this thread is wake up by the iecm_recv_mb_msg before the
> +	 * timeout expires. Only in the error case i.e. if no message is
> +	 * received on mailbox, we wait for the complete timeout which is
> +	 * less likely to happen.
> +	 */
> +	int timeout = 60000;

If you make a definition from it, it would be easier to find it
later in case of adjustments needed.

> +
> +	return __iecm_wait_for_event(adapter, state, err_check, timeout);
> +}
> +EXPORT_SYMBOL(iecm_wait_for_event);
> +
>  /**
>   * iecm_find_ctlq - Given a type and id, find ctlq info
>   * @hw: hardware struct
> @@ -170,3 +1097,375 @@ void iecm_vport_params_buf_rel(struct iecm_adapter *adapter)
>  	kfree(adapter->caps);
>  	kfree(adapter->config_data.req_qs_chunks);
>  }
> +
> +/**
> + * iecm_vc_core_init - Initialize mailbox and get resources
> + * @adapter: Driver specific private structure
> + * @vport_id: Virtual port identifier
> + *
> + * Will check if HW is ready with reset complete. Initializes the mailbox and
> + * communicate with master to get all the default vport parameters. Returns 0
> + * on success, -EAGAIN function will get called again, otherwise negative on
> + * failure.
> + */
> +int iecm_vc_core_init(struct iecm_adapter *adapter, int *vport_id)
> +{
> +	int err;
> +
> +	switch (adapter->state) {
> +	case __IECM_STARTUP:
> +		if (iecm_send_ver_msg(adapter))
> +			goto init_failed;
> +		adapter->state = __IECM_VER_CHECK;
> +		goto restart;
> +	case __IECM_VER_CHECK:
> +		err = iecm_recv_ver_msg(adapter);
> +		if (err == -EAGAIN) {
> +			adapter->state = __IECM_STARTUP;
> +			goto restart;
> +		} else if (err) {
> +			goto init_failed;
> +		}
> +		adapter->state = __IECM_GET_CAPS;
> +		if (adapter->dev_ops.vc_ops.get_caps(adapter))
> +			goto init_failed;
> +		goto restart;
> +	case __IECM_GET_CAPS:
> +		if (iecm_recv_get_caps_msg(adapter))
> +			goto init_failed;
> +		if (iecm_send_create_vport_msg(adapter))
> +			goto init_failed;
> +		adapter->state = __IECM_GET_DFLT_VPORT_PARAMS;
> +		goto restart;
> +	case __IECM_GET_DFLT_VPORT_PARAMS:
> +		if (iecm_recv_create_vport_msg(adapter, vport_id))
> +			goto init_failed;
> +		adapter->state = __IECM_INIT_SW;
> +		break;
> +	case __IECM_INIT_SW:
> +		break;
> +	default:
> +		dev_err(&adapter->pdev->dev, "Device is in bad state: %d\n",
> +			adapter->state);
> +		goto init_failed;
> +	}
> +
> +	return 0;
> +restart:
> +	queue_delayed_work(adapter->init_wq, &adapter->init_task,
> +			   msecs_to_jiffies(30));
> +	/* Not an error. Using try again to continue with state machine */
> +	return -EAGAIN;
> +init_failed:
> +	if (++adapter->mb_wait_count > IECM_MB_MAX_ERR) {
> +		dev_err(&adapter->pdev->dev, "Failed to establish mailbox communications with hardware\n");
> +		return -EFAULT;
> +	}
> +	adapter->state = __IECM_STARTUP;
> +	/* If it reaches here, it is possible that mailbox queue initialization
> +	 * register writes might not have taken effect. Retry to initialize
> +	 * the mailbox again
> +	 */
> +	iecm_deinit_dflt_mbx(adapter);
> +	set_bit(__IECM_HR_DRV_LOAD, adapter->flags);
> +	queue_delayed_work(adapter->vc_event_wq, &adapter->vc_event_task,
> +			   msecs_to_jiffies(20));
> +	return -EAGAIN;
> +}
> +EXPORT_SYMBOL(iecm_vc_core_init);
> +
> +/**
> + * iecm_vport_init - Initialize virtual port
> + * @vport: virtual port to be initialized
> + * @vport_id: Unique identification number of vport
> + *
> + * Will initialize vport with the info received through MB earlier
> + */
> +static void iecm_vport_init(struct iecm_vport *vport,
> +			    __always_unused int vport_id)
> +{
> +	struct virtchnl2_create_vport *vport_msg;
> +	u16 rx_itr[] = {2, 8, 32, 96, 128};
> +	u16 tx_itr[] = {2, 8, 64, 128, 256};
> +
> +	vport_msg = (struct virtchnl2_create_vport *)
> +				vport->adapter->vport_params_recvd[0];
> +	vport->txq_model = le16_to_cpu(vport_msg->txq_model);
> +	vport->rxq_model = le16_to_cpu(vport_msg->rxq_model);
> +	vport->vport_type = le16_to_cpu(vport_msg->vport_type);
> +	vport->vport_id = le32_to_cpu(vport_msg->vport_id);
> +	vport->adapter->rss_data.rss_key_size =
> +				min_t(u16, NETDEV_RSS_KEY_LEN,
> +				      le16_to_cpu(vport_msg->rss_key_size));
> +	vport->adapter->rss_data.rss_lut_size =
> +				le16_to_cpu(vport_msg->rss_lut_size);
> +	ether_addr_copy(vport->default_mac_addr, vport_msg->default_mac_addr);
> +	vport->max_mtu = IECM_MAX_MTU;
> +
> +	if (iecm_is_queue_model_split(vport->rxq_model)) {
> +		vport->num_bufqs_per_qgrp = IECM_MAX_BUFQS_PER_RXQ_GRP;
> +		/* Bufq[0] default buffer size is 4K
> +		 * Bufq[1] default buffer size is 2K
> +		 */
> +		vport->bufq_size[0] = IECM_RX_BUF_4096;
> +		vport->bufq_size[1] = IECM_RX_BUF_2048;
> +	} else {
> +		vport->num_bufqs_per_qgrp = 0;
> +		vport->bufq_size[0] = IECM_RX_BUF_2048;
> +	}
> +
> +	/*Initialize Tx and Rx profiles for Dynamic Interrupt Moderation */
> +	memcpy(vport->rx_itr_profile, rx_itr, IECM_DIM_PROFILE_SLOTS);
> +	memcpy(vport->tx_itr_profile, tx_itr, IECM_DIM_PROFILE_SLOTS);
> +}
> +
> +/**
> + * iecm_get_vec_ids - Initialize vector id from Mailbox parameters
> + * @adapter: adapter structure to get the mailbox vector id
> + * @vecids: Array of vector ids
> + * @num_vecids: number of vector ids
> + * @chunks: vector ids received over mailbox
> + *
> + * Will initialize the mailbox vector id which is received from the
> + * get capabilities and data queue vector ids with ids received as
> + * mailbox parameters.
> + * Returns number of ids filled
> + */
> +int iecm_get_vec_ids(struct iecm_adapter *adapter,
> +		     u16 *vecids, int num_vecids,
> +		     struct virtchnl2_vector_chunks *chunks)
> +{
> +	u16 num_chunks = le16_to_cpu(chunks->num_vchunks);
> +	u16 start_vecid, num_vec;
> +	int num_vecid_filled = 0;
> +	int i, j;
> +
> +	vecids[num_vecid_filled] = adapter->mb_vector.v_idx;
> +	num_vecid_filled++;
> +
> +	for (j = 0; j < num_chunks; j++) {
> +		struct virtchnl2_vector_chunk *chunk = &chunks->vchunks[j];
> +
> +		num_vec = le16_to_cpu(chunk->num_vectors);
> +		start_vecid = le16_to_cpu(chunk->start_vector_id);
> +		for (i = 0; i < num_vec; i++) {
> +			if ((num_vecid_filled + i) < num_vecids) {
> +				vecids[num_vecid_filled + i] = start_vecid;
> +				start_vecid++;
> +			} else {
> +				break;
> +			}
> +		}
> +		num_vecid_filled = num_vecid_filled + i;
> +	}
> +
> +	return num_vecid_filled;
> +}
> +
> +/**
> + * iecm_vport_get_queue_ids - Initialize queue id from Mailbox parameters
> + * @qids: Array of queue ids
> + * @num_qids: number of queue ids
> + * @q_type: queue model
> + * @chunks: queue ids received over mailbox
> + *
> + * Will initialize all queue ids with ids received as mailbox parameters
> + * Returns number of ids filled
> + */
> +static int
> +iecm_vport_get_queue_ids(u32 *qids, int num_qids, u16 q_type,
> +			 struct virtchnl2_queue_reg_chunks *chunks)
> +{
> +	u16 num_chunks = le16_to_cpu(chunks->num_chunks);
> +	u32 num_q_id_filled = 0, i;
> +	u32 start_q_id, num_q;
> +
> +	while (num_chunks) {
> +		struct virtchnl2_queue_reg_chunk *chunk = &chunks->chunks[num_chunks - 1];
> +
> +		if (le32_to_cpu(chunk->type) == q_type) {
> +			num_q = le32_to_cpu(chunk->num_queues);
> +			start_q_id = le32_to_cpu(chunk->start_queue_id);
> +			for (i = 0; i < num_q; i++) {
> +				if ((num_q_id_filled + i) < num_qids) {
> +					qids[num_q_id_filled + i] = start_q_id;
> +					start_q_id++;
> +				} else {
> +					break;
> +				}
> +			}
> +			num_q_id_filled = num_q_id_filled + i;
> +		}
> +		num_chunks--;
> +	}
> +
> +	return num_q_id_filled;
> +}
> +
> +/**
> + * __iecm_vport_queue_ids_init - Initialize queue ids from Mailbox parameters
> + * @vport: virtual port for which the queues ids are initialized
> + * @qids: queue ids
> + * @num_qids: number of queue ids
> + * @q_type: type of queue
> + *
> + * Will initialize all queue ids with ids received as mailbox
> + * parameters. Returns number of queue ids initialized.
> + */
> +static int
> +__iecm_vport_queue_ids_init(struct iecm_vport *vport, u32 *qids,
> +			    int num_qids, u32 q_type)
> +{
> +	/* stub */
> +	return 0;
> +}
> +
> +/**
> + * iecm_vport_queue_ids_init - Initialize queue ids from Mailbox parameters
> + * @vport: virtual port for which the queues ids are initialized
> + *
> + * Will initialize all queue ids with ids received as mailbox parameters.
> + * Returns 0 on success, negative if all the queues are not initialized.
> + */
> +static int iecm_vport_queue_ids_init(struct iecm_vport *vport)
> +{
> +	struct virtchnl2_create_vport *vport_params;
> +	struct virtchnl2_queue_reg_chunks *chunks;
> +	/* We may never deal with more than 256 same type of queues */
> +#define IECM_MAX_QIDS	256
> +	u32 qids[IECM_MAX_QIDS];
> +	int num_ids;
> +	u16 q_type;
> +
> +	if (vport->adapter->config_data.req_qs_chunks) {
> +		struct virtchnl2_add_queues *vc_aq =
> +			(struct virtchnl2_add_queues *)
> +			vport->adapter->config_data.req_qs_chunks;
> +		chunks = &vc_aq->chunks;
> +	} else {
> +		vport_params = (struct virtchnl2_create_vport *)
> +				vport->adapter->vport_params_recvd[0];
> +		chunks = &vport_params->chunks;
> +	}
> +
> +	num_ids = iecm_vport_get_queue_ids(qids, IECM_MAX_QIDS,
> +					   VIRTCHNL2_QUEUE_TYPE_TX,
> +					   chunks);
> +	if (num_ids != vport->num_txq)
> +		return -EINVAL;
> +	num_ids = __iecm_vport_queue_ids_init(vport, qids, num_ids,
> +					      VIRTCHNL2_QUEUE_TYPE_TX);
> +	if (num_ids != vport->num_txq)
> +		return -EINVAL;
> +	num_ids = iecm_vport_get_queue_ids(qids, IECM_MAX_QIDS,
> +					   VIRTCHNL2_QUEUE_TYPE_RX,
> +					   chunks);
> +	if (num_ids != vport->num_rxq)
> +		return -EINVAL;
> +	num_ids = __iecm_vport_queue_ids_init(vport, qids, num_ids,
> +					      VIRTCHNL2_QUEUE_TYPE_RX);
> +	if (num_ids != vport->num_rxq)
> +		return -EINVAL;
> +
> +	if (iecm_is_queue_model_split(vport->txq_model)) {
> +		q_type = VIRTCHNL2_QUEUE_TYPE_TX_COMPLETION;
> +		num_ids = iecm_vport_get_queue_ids(qids, IECM_MAX_QIDS, q_type,
> +						   chunks);
> +		if (num_ids != vport->num_complq)
> +			return -EINVAL;
> +		num_ids = __iecm_vport_queue_ids_init(vport, qids,
> +						      num_ids,
> +						      q_type);
> +		if (num_ids != vport->num_complq)
> +			return -EINVAL;
> +	}
> +
> +	if (iecm_is_queue_model_split(vport->rxq_model)) {
> +		q_type = VIRTCHNL2_QUEUE_TYPE_RX_BUFFER;
> +		num_ids = iecm_vport_get_queue_ids(qids, IECM_MAX_QIDS, q_type,
> +						   chunks);
> +		if (num_ids != vport->num_bufq)
> +			return -EINVAL;
> +		num_ids = __iecm_vport_queue_ids_init(vport, qids, num_ids,
> +						      q_type);
> +		if (num_ids != vport->num_bufq)
> +			return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * iecm_is_capability_ena - Default implementation of capability checking
> + * @adapter: Private data struct
> + * @all: all or one flag
> + * @field: caps field to check for flags
> + * @flag: flag to check
> + *
> + * Return true if all capabilities are supported, false otherwise
> + */
> +static bool iecm_is_capability_ena(struct iecm_adapter *adapter, bool all,
> +				   enum iecm_cap_field field, u64 flag)
> +{
> +	u8 *caps = (u8 *)adapter->caps;
> +	u32 *cap_field;
> +
> +	if (field == IECM_BASE_CAPS)
> +		return false;
> +	if (field >= IECM_CAP_FIELD_LAST) {
> +		dev_err(&adapter->pdev->dev, "Bad capability field: %d\n",
> +			field);
> +		return false;
> +	}
> +	cap_field = (u32 *)(caps + field);
> +
> +	if (all)
> +		return (*cap_field & flag) == flag;
> +	else
> +		return !!(*cap_field & flag);
> +}
> +
> +/**
> + * iecm_vc_ops_init - Initialize virtchnl common api
> + * @adapter: Driver specific private structure
> + *
> + * Initialize the function pointers with the extended feature set functions
> + * as APF will deal only with new set of opcodes.
> + */
> +void iecm_vc_ops_init(struct iecm_adapter *adapter)
> +{
> +	struct iecm_virtchnl_ops *vc_ops = &adapter->dev_ops.vc_ops;
> +
> +	vc_ops->core_init = iecm_vc_core_init;
> +	vc_ops->vport_init = iecm_vport_init;
> +	vc_ops->vport_queue_ids_init = iecm_vport_queue_ids_init;
> +	vc_ops->get_caps = iecm_send_get_caps_msg;
> +	vc_ops->is_cap_ena = iecm_is_capability_ena;
> +	vc_ops->get_reserved_vecs = NULL;
> +	vc_ops->config_queues = NULL;
> +	vc_ops->enable_queues = NULL;
> +	vc_ops->disable_queues = NULL;
> +	vc_ops->add_queues = NULL;
> +	vc_ops->delete_queues = NULL;
> +	vc_ops->irq_map_unmap = NULL;
> +	vc_ops->enable_vport = NULL;
> +	vc_ops->disable_vport = NULL;
> +	vc_ops->destroy_vport = NULL;
> +	vc_ops->get_ptype = NULL;
> +	vc_ops->get_set_rss_key = NULL;
> +	vc_ops->get_set_rss_lut = NULL;
> +	vc_ops->get_set_rss_hash = NULL;
> +	vc_ops->adjust_qs = NULL;
> +	vc_ops->add_del_vlans = NULL;
> +	vc_ops->strip_vlan_msg = NULL;
> +	vc_ops->insert_vlan_msg = NULL;
> +	vc_ops->init_max_queues = NULL;
> +	vc_ops->get_max_tx_bufs = NULL;
> +	vc_ops->vportq_reg_init = NULL;
> +	vc_ops->alloc_vectors = NULL;
> +	vc_ops->dealloc_vectors = NULL;
> +	vc_ops->get_supported_desc_ids = NULL;
> +	vc_ops->get_stats_msg = NULL;
> +	vc_ops->recv_mbx_msg = NULL;
> +}
> +EXPORT_SYMBOL(iecm_vc_ops_init);
> diff --git a/drivers/net/ethernet/intel/include/iecm.h b/drivers/net/ethernet/intel/include/iecm.h
> index ca9029224e06..994664dfe419 100644
> --- a/drivers/net/ethernet/intel/include/iecm.h
> +++ b/drivers/net/ethernet/intel/include/iecm.h
> @@ -7,10 +7,13 @@
>  #include <linux/aer.h>
>  #include <linux/pci.h>
>  #include <linux/netdevice.h>
> +#include <linux/etherdevice.h>
>  #include <linux/ethtool.h>
> +#include <net/tcp.h>
>  #include <linux/version.h>
>  #include <linux/dim.h>
>  
> +#include "virtchnl_2.h"
>  #include "iecm_txrx.h"
>  #include "iecm_controlq.h"
>  
> @@ -35,10 +38,34 @@
>  /* available message levels */
>  #define IECM_AVAIL_NETIF_M (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK)
>  
> +#define IECM_VIRTCHNL_VERSION_MAJOR VIRTCHNL_VERSION_MAJOR_2
> +#define IECM_VIRTCHNL_VERSION_MINOR VIRTCHNL_VERSION_MINOR_0
> +
>  /* Forward declaration */
>  struct iecm_adapter;
>  struct iecm_vport;
>  
> +struct iecm_mac_filter {
> +	struct list_head list;
> +	u8 macaddr[ETH_ALEN];
> +	bool remove;		/* filter needs to be removed */
> +	bool add;		/* filter needs to be added */
> +};
> +
> +#define IECM_VLAN(vid, tpid) ((struct iecm_vlan){ vid, tpid })
> +
> +struct iecm_vlan {
> +	u16 vid;
> +	u16 tpid;
> +};
> +
> +struct iecm_vlan_filter {
> +	struct list_head list;
> +	struct iecm_vlan vlan;
> +	bool remove;		/* filter needs to be removed */
> +	bool add;		/* filter needs to be added */
> +};
> +
>  enum iecm_state {
>  	__IECM_STARTUP,
>  	__IECM_VER_CHECK,
> @@ -90,6 +117,24 @@ enum iecm_flags {
>  	__IECM_FLAGS_NBITS,
>  };
>  
> +/* enum used to distinquish which capability field to check */
> +enum iecm_cap_field {
> +	IECM_BASE_CAPS		= -1,
> +	IECM_CSUM_CAPS		= offsetof(struct virtchnl2_get_capabilities,
> +					   csum_caps),
> +	IECM_SEG_CAPS		= offsetof(struct virtchnl2_get_capabilities,
> +					   seg_caps),
> +	IECM_RSS_CAPS		= offsetof(struct virtchnl2_get_capabilities,
> +					   rss_caps),
> +	IECM_HSPLIT_CAPS	= offsetof(struct virtchnl2_get_capabilities,
> +					   hsplit_caps),
> +	IECM_RSC_CAPS		= offsetof(struct virtchnl2_get_capabilities,
> +					   rsc_caps),
> +	IECM_OTHER_CAPS		= offsetof(struct virtchnl2_get_capabilities,
> +					   other_caps),
> +	IECM_CAP_FIELD_LAST,
> +};
> +
>  struct iecm_reset_reg {
>  	u32 rstat;
>  	u32 rstat_m;
> @@ -105,14 +150,229 @@ struct iecm_reg_ops {
>  			      enum iecm_flags trig_cause);
>  };
>  
> +struct iecm_virtchnl_ops {
> +	int (*core_init)(struct iecm_adapter *adapter, int *vport_id);
> +	void (*vport_init)(struct iecm_vport *vport, int vport_id);
> +	int (*vport_queue_ids_init)(struct iecm_vport *vport);
> +	int (*get_caps)(struct iecm_adapter *adapter);
> +	int (*config_queues)(struct iecm_vport *vport);
> +	int (*enable_queues)(struct iecm_vport *vport);
> +	int (*disable_queues)(struct iecm_vport *vport);
> +	int (*add_queues)(struct iecm_vport *vport, u16 num_tx_q,
> +			  u16 num_complq, u16 num_rx_q,
> +			  u16 num_rx_bufq);
> +	int (*delete_queues)(struct iecm_vport *vport);
> +	int (*irq_map_unmap)(struct iecm_vport *vport, bool map);
> +	int (*enable_vport)(struct iecm_vport *vport);
> +	int (*disable_vport)(struct iecm_vport *vport);
> +	int (*destroy_vport)(struct iecm_vport *vport);
> +	int (*get_ptype)(struct iecm_vport *vport);
> +	int (*get_set_rss_key)(struct iecm_vport *vport, bool get);
> +	int (*get_set_rss_lut)(struct iecm_vport *vport, bool get);
> +	int (*get_set_rss_hash)(struct iecm_vport *vport, bool get);
> +	void (*adjust_qs)(struct iecm_vport *vport);
> +	int (*recv_mbx_msg)(struct iecm_adapter *adapter,
> +			    void *msg, int msg_size,
> +			    struct iecm_ctlq_msg *ctlq_msg, bool *work_done);
> +	bool (*is_cap_ena)(struct iecm_adapter *adapter, bool all,
> +			   enum iecm_cap_field field, u64 flag);
> +	u16 (*get_reserved_vecs)(struct iecm_adapter *adapter);
> +	void (*add_del_vlans)(struct iecm_vport *vport, bool add);
> +	int (*strip_vlan_msg)(struct iecm_vport *vport, bool ena);
> +	int (*insert_vlan_msg)(struct iecm_vport *vport, bool ena);
> +	void (*init_max_queues)(struct iecm_adapter *adapter);
> +	unsigned int (*get_max_tx_bufs)(struct iecm_adapter *adapter);
> +	int (*vportq_reg_init)(struct iecm_vport *vport);
> +	int (*alloc_vectors)(struct iecm_adapter *adapter, u16 num_vectors);
> +	int (*dealloc_vectors)(struct iecm_adapter *adapter);
> +	int (*get_supported_desc_ids)(struct iecm_vport *vport);
> +	int (*get_stats_msg)(struct iecm_vport *vport);
> +};
> +
>  struct iecm_dev_ops {
>  	void (*reg_ops_init)(struct iecm_adapter *adapter);
> +	void (*vc_ops_init)(struct iecm_adapter *adapter);
>  	void (*crc_enable)(u64 *td_cmd);
>  	struct iecm_reg_ops reg_ops;
> +	struct iecm_virtchnl_ops vc_ops;
> +};
> +
> +/* These macros allow us to generate an enum and a matching char * array of
> + * stringified enums that are always in sync. Checkpatch issues a bogus warning
> + * about this being a complex macro; but it's wrong, these are never used as a
> + * statement and instead only used to define the enum and array.
> + */
> +#define IECM_FOREACH_VPORT_VC_STATE(STATE)	\
> +	STATE(IECM_VC_ENA_VPORT)		\
> +	STATE(IECM_VC_ENA_VPORT_ERR)		\
> +	STATE(IECM_VC_DIS_VPORT)		\
> +	STATE(IECM_VC_DIS_VPORT_ERR)		\
> +	STATE(IECM_VC_DESTROY_VPORT)		\
> +	STATE(IECM_VC_DESTROY_VPORT_ERR)	\
> +	STATE(IECM_VC_CONFIG_TXQ)		\
> +	STATE(IECM_VC_CONFIG_TXQ_ERR)		\
> +	STATE(IECM_VC_CONFIG_RXQ)		\
> +	STATE(IECM_VC_CONFIG_RXQ_ERR)		\
> +	STATE(IECM_VC_CONFIG_Q)			\
> +	STATE(IECM_VC_CONFIG_Q_ERR)		\
> +	STATE(IECM_VC_ENA_QUEUES)		\
> +	STATE(IECM_VC_ENA_QUEUES_ERR)		\
> +	STATE(IECM_VC_DIS_QUEUES)		\
> +	STATE(IECM_VC_DIS_QUEUES_ERR)		\
> +	STATE(IECM_VC_ENA_CHANNELS)		\
> +	STATE(IECM_VC_ENA_CHANNELS_ERR)		\
> +	STATE(IECM_VC_DIS_CHANNELS)		\
> +	STATE(IECM_VC_DIS_CHANNELS_ERR)		\
> +	STATE(IECM_VC_MAP_IRQ)			\
> +	STATE(IECM_VC_MAP_IRQ_ERR)		\
> +	STATE(IECM_VC_UNMAP_IRQ)		\
> +	STATE(IECM_VC_UNMAP_IRQ_ERR)		\
> +	STATE(IECM_VC_ADD_QUEUES)		\
> +	STATE(IECM_VC_ADD_QUEUES_ERR)		\
> +	STATE(IECM_VC_DEL_QUEUES)		\
> +	STATE(IECM_VC_REQUEST_QUEUES)		\
> +	STATE(IECM_VC_REQUEST_QUEUES_ERR)	\
> +	STATE(IECM_VC_DEL_QUEUES_ERR)		\
> +	STATE(IECM_VC_ALLOC_VECTORS)		\
> +	STATE(IECM_VC_ALLOC_VECTORS_ERR)	\
> +	STATE(IECM_VC_DEALLOC_VECTORS)		\
> +	STATE(IECM_VC_DEALLOC_VECTORS_ERR)	\
> +	STATE(IECM_VC_SET_SRIOV_VFS)		\
> +	STATE(IECM_VC_SET_SRIOV_VFS_ERR)	\
> +	STATE(IECM_VC_GET_RSS_HASH)		\
> +	STATE(IECM_VC_GET_RSS_HASH_ERR)		\
> +	STATE(IECM_VC_SET_RSS_HASH)		\
> +	STATE(IECM_VC_SET_RSS_HASH_ERR)		\
> +	STATE(IECM_VC_GET_RSS_LUT)		\
> +	STATE(IECM_VC_GET_RSS_LUT_ERR)		\
> +	STATE(IECM_VC_SET_RSS_LUT)		\
> +	STATE(IECM_VC_SET_RSS_LUT_ERR)		\
> +	STATE(IECM_VC_GET_RSS_KEY)		\
> +	STATE(IECM_VC_GET_RSS_KEY_ERR)		\
> +	STATE(IECM_VC_SET_RSS_KEY)		\
> +	STATE(IECM_VC_SET_RSS_KEY_ERR)		\
> +	STATE(IECM_VC_GET_STATS)		\
> +	STATE(IECM_VC_GET_STATS_ERR)		\
> +	STATE(IECM_VC_ENA_STRIP_VLAN_TAG)	\
> +	STATE(IECM_VC_ENA_STRIP_VLAN_TAG_ERR)	\
> +	STATE(IECM_VC_DIS_STRIP_VLAN_TAG)	\
> +	STATE(IECM_VC_DIS_STRIP_VLAN_TAG_ERR)	\
> +	STATE(IECM_VC_IWARP_IRQ_MAP)		\
> +	STATE(IECM_VC_IWARP_IRQ_MAP_ERR)	\
> +	STATE(IECM_VC_ADD_ETH_ADDR)		\
> +	STATE(IECM_VC_ADD_ETH_ADDR_ERR)		\
> +	STATE(IECM_VC_DEL_ETH_ADDR)		\
> +	STATE(IECM_VC_DEL_ETH_ADDR_ERR)		\
> +	STATE(IECM_VC_PROMISC)			\
> +	STATE(IECM_VC_ADD_CLOUD_FILTER)		\
> +	STATE(IECM_VC_ADD_CLOUD_FILTER_ERR)	\
> +	STATE(IECM_VC_DEL_CLOUD_FILTER)		\
> +	STATE(IECM_VC_DEL_CLOUD_FILTER_ERR)	\
> +	STATE(IECM_VC_ADD_RSS_CFG)		\
> +	STATE(IECM_VC_ADD_RSS_CFG_ERR)		\
> +	STATE(IECM_VC_DEL_RSS_CFG)		\
> +	STATE(IECM_VC_DEL_RSS_CFG_ERR)		\
> +	STATE(IECM_VC_ADD_FDIR_FILTER)		\
> +	STATE(IECM_VC_ADD_FDIR_FILTER_ERR)	\
> +	STATE(IECM_VC_DEL_FDIR_FILTER)		\
> +	STATE(IECM_VC_DEL_FDIR_FILTER_ERR)	\
> +	STATE(IECM_VC_OFFLOAD_VLAN_V2_CAPS)	\
> +	STATE(IECM_VC_OFFLOAD_VLAN_V2_CAPS_ERR)	\
> +	STATE(IECM_VC_INSERTION_ENA_VLAN_V2)	\
> +	STATE(IECM_VC_INSERTION_ENA_VLAN_V2_ERR)\
> +	STATE(IECM_VC_INSERTION_DIS_VLAN_V2)	\
> +	STATE(IECM_VC_INSERTION_DIS_VLAN_V2_ERR)\
> +	STATE(IECM_VC_STRIPPING_ENA_VLAN_V2)	\
> +	STATE(IECM_VC_STRIPPING_ENA_VLAN_V2_ERR)\
> +	STATE(IECM_VC_STRIPPING_DIS_VLAN_V2)	\
> +	STATE(IECM_VC_STRIPPING_DIS_VLAN_V2_ERR)\
> +	STATE(IECM_VC_GET_SUPPORTED_RXDIDS)	\
> +	STATE(IECM_VC_GET_SUPPORTED_RXDIDS_ERR)	\
> +	STATE(IECM_VC_GET_PTYPE_INFO)		\
> +	STATE(IECM_VC_GET_PTYPE_INFO_ERR)	\
> +	STATE(IECM_VC_NBITS)
> +
> +#define IECM_GEN_ENUM(ENUM) ENUM,
> +#define IECM_GEN_STRING(STRING) #STRING,
> +
> +enum iecm_vport_vc_state {
> +	IECM_FOREACH_VPORT_VC_STATE(IECM_GEN_ENUM)
> +};
> +
> +extern const char * const iecm_vport_vc_state_str[];
> +
> +enum iecm_vport_flags {
> +	__IECM_VPORT_INIT_PROMISC,
> +	__IECM_VPORT_FLAGS_NBITS,
> +};
> +
> +struct iecm_port_stats {
> +	struct u64_stats_sync stats_sync;
> +	u64 rx_hw_csum_err;
> +	u64 rx_hsplit;
> +	u64 rx_hsplit_hbo;
> +	u64 tx_linearize;
> +	u64 rx_bad_descs;
> +	struct virtchnl2_vport_stats vport_stats;
> +	struct virtchnl_eth_stats eth_stats;
>  };
>  
> -/* stub */
>  struct iecm_vport {
> +	/* TX */
> +	int num_txq;
> +	int num_complq;
> +	/* It makes more sense for descriptor count to be part of only idpf
> +	 * queue structure. But when user changes the count via ethtool, driver
> +	 * has to store that value somewhere other than queue structure as the
> +	 * queues will be freed and allocated again.
> +	 */
> +	int txq_desc_count;
> +	int complq_desc_count;
> +	int compln_clean_budget;
> +	int num_txq_grp;
> +	struct iecm_txq_group *txq_grps;
> +	u32 txq_model;
> +	/* Used only in hotpath to get to the right queue very fast */
> +	struct iecm_queue **txqs;
> +	DECLARE_BITMAP(flags, __IECM_VPORT_FLAGS_NBITS);
> +
> +	/* RX */
> +	int num_rxq;
> +	int num_bufq;
> +	int rxq_desc_count;
> +	u8 num_bufqs_per_qgrp;
> +	int bufq_desc_count[IECM_MAX_BUFQS_PER_RXQ_GRP];
> +	u32 bufq_size[IECM_MAX_BUFQS_PER_RXQ_GRP];
> +	int num_rxq_grp;
> +	struct iecm_rxq_group *rxq_grps;
> +	u32 rxq_model;
> +
> +	struct iecm_adapter *adapter;
> +	struct net_device *netdev;
> +	u16 vport_type;
> +	u16 vport_id;
> +	u16 idx;		 /* software index in adapter vports struct */
> +	bool base_rxd;

Bools are not really advised to use inside structures since they may
have different sizes and alignments. There's a 2-byte hole here, so
u8 or u16 for base_rxd would do the job.

> +
> +	/* handler for hard interrupt */
> +	irqreturn_t (*irq_q_handler)(int irq, void *data);
> +	struct iecm_q_vector *q_vectors;	/* q vector array */
> +	u16 num_q_vectors;
> +	u16 q_vector_base;
> +	u16 max_mtu;
> +	u8 default_mac_addr[ETH_ALEN];
> +	u16 qset_handle;
> +	/* ITR profiles for the DIM algorithm */
> +#define IECM_DIM_PROFILE_SLOTS	5
> +	u16 rx_itr_profile[IECM_DIM_PROFILE_SLOTS];
> +	u16 tx_itr_profile[IECM_DIM_PROFILE_SLOTS];
> +	struct rtnl_link_stats64 netstats;
> +	struct iecm_port_stats port_stats;
> +
> +	/* lock to protect against multiple stop threads, which can happen when
> +	 * the driver is in a namespace in a system that is being shutdown
> +	 */
> +	struct mutex stop_mutex;
>  };
>  
>  enum iecm_user_flags {
> @@ -164,6 +424,7 @@ struct iecm_adapter {
>  	u16 num_msix_entries;
>  	struct msix_entry *msix_entries;
>  	struct virtchnl2_alloc_vectors *req_vec_chunks;
> +	struct iecm_q_vector mb_vector;
>  
>  	/* vport structs */
>  	struct iecm_vport **vports;	/* vports created by the driver */
> @@ -190,6 +451,8 @@ struct iecm_adapter {
>  
>  	wait_queue_head_t vchnl_wq;
>  	wait_queue_head_t sw_marker_wq;
> +	DECLARE_BITMAP(vc_state, IECM_VC_NBITS);
> +	char vc_msg[IECM_DFLT_MBX_BUF_SIZE];
>  	struct iecm_rss_data rss_data;
>  	struct iecm_dev_ops dev_ops;
>  	s32 link_speed;
> @@ -215,6 +478,38 @@ struct iecm_adapter {
>  	spinlock_t fdir_fltr_list_lock;
>  };
>  
> +/**
> + * iecm_is_queue_model_split - check if queue model is split
> + * @q_model: queue model single or split
> + *
> + * Returns true if queue model is split else false
> + */
> +static inline int iecm_is_queue_model_split(u16 q_model)
> +{
> +	return (q_model == VIRTCHNL2_QUEUE_MODEL_SPLIT);
> +}
> +
> +#define iecm_is_cap_ena(adapter, field, flag) \
> +	__iecm_is_cap_ena(adapter, false, field, flag)
> +#define iecm_is_cap_ena_all(adapter, field, flag) \
> +	__iecm_is_cap_ena(adapter, true, field, flag)
> +/**
> + * __iecm_is_cap_ena - Determine if HW capability is supported
> + * @adapter: private data struct
> + * @all: all or one flag
> + * @field: cap field to check
> + * @flag: Feature flag to check
> + *
> + * iecm_is_cap_ena_all is used to check if all the capability bits are set
> + * ('AND' operation) where as iecm_is_cap_ena is used to check if
> + * any one of the capability bits is set ('OR' operation)
> + */
> +static inline bool __iecm_is_cap_ena(struct iecm_adapter *adapter, bool all,
> +				     enum iecm_cap_field field, u64 flag)
> +{
> +	return adapter->dev_ops.vc_ops.is_cap_ena(adapter, all, field, flag);
> +}
> +
>  /**
>   * iecm_is_reset_detected - check if we were reset at some point
>   * @adapter: driver specific private structure
> @@ -233,6 +528,25 @@ int iecm_probe(struct pci_dev *pdev,
>  void iecm_remove(struct pci_dev *pdev);
>  int iecm_init_dflt_mbx(struct iecm_adapter *adapter);
>  void iecm_deinit_dflt_mbx(struct iecm_adapter *adapter);
> +void iecm_vc_ops_init(struct iecm_adapter *adapter);
> +int iecm_vc_core_init(struct iecm_adapter *adapter, int *vport_id);
> +int iecm_wait_for_event(struct iecm_adapter *adapter,
> +			enum iecm_vport_vc_state state,
> +			enum iecm_vport_vc_state err_check);
> +int iecm_min_wait_for_event(struct iecm_adapter *adapter,
> +			    enum iecm_vport_vc_state state,
> +			    enum iecm_vport_vc_state err_check);
> +int iecm_send_get_caps_msg(struct iecm_adapter *adapter);
>  int iecm_vport_params_buf_alloc(struct iecm_adapter *adapter);
>  void iecm_vport_params_buf_rel(struct iecm_adapter *adapter);
> +int iecm_get_vec_ids(struct iecm_adapter *adapter,
> +		     u16 *vecids, int num_vecids,
> +		     struct virtchnl2_vector_chunks *chunks);
> +int iecm_recv_mb_msg(struct iecm_adapter *adapter, enum virtchnl_ops op,
> +		     void *msg, int msg_size);
> +int iecm_send_mb_msg(struct iecm_adapter *adapter, enum virtchnl_ops op,
> +		     u16 msg_size, u8 *msg);
> +int iecm_set_msg_pending(struct iecm_adapter *adapter,
> +			 struct iecm_ctlq_msg *ctlq_msg,
> +			 enum iecm_vport_vc_state err_enum);
>  #endif /* !_IECM_H_ */
> diff --git a/drivers/net/ethernet/intel/include/iecm_txrx.h b/drivers/net/ethernet/intel/include/iecm_txrx.h
> index 602d3b3b19dd..e1348011c991 100644
> --- a/drivers/net/ethernet/intel/include/iecm_txrx.h
> +++ b/drivers/net/ethernet/intel/include/iecm_txrx.h
> @@ -30,4 +30,98 @@
>  #define IECM_DFLT_SPLITQ_RX_Q_GROUPS		4
>  #define IECM_DFLT_SPLITQ_TXQ_PER_GROUP		1
>  #define IECM_DFLT_SPLITQ_RXQ_PER_GROUP		1
> +
> +/* Default vector sharing */
> +#define IECM_NONQ_VEC		1
> +#define IECM_MAX_Q_VEC		4 /* For Tx Completion queue and Rx queue */
> +#define IECM_MIN_Q_VEC		1
> +#define IECM_MAX_RDMA_VEC	2 /* To share with RDMA */
> +#define IECM_MIN_RDMA_VEC	1 /* Minimum vectors to be shared with RDMA */
> +#define IECM_MIN_VEC		3 /* One for mailbox, one for data queues, one
> +				   * for RDMA
> +				   */
> +
> +#define IECM_DFLT_TX_Q_DESC_COUNT		512
> +#define IECM_DFLT_TX_COMPLQ_DESC_COUNT		512
> +#define IECM_DFLT_RX_Q_DESC_COUNT		512
> +/* IMPORTANT: We absolutely _cannot_ have more buffers in the system than a
> + * given RX completion queue has descriptors. This includes _ALL_ buffer
> + * queues. E.g.: If you have two buffer queues of 512 descriptors and buffers,
> + * you have a total of 1024 buffers so your RX queue _must_ have at least that
> + * many descriptors. This macro divides a given number of RX descriptors by
> + * number of buffer queues to calculate how many descriptors each buffer queue
> + * can have without overrunning the RX queue.
> + *
> + * If you give hardware more buffers than completion descriptors what will
> + * happen is that if hardware gets a chance to post more than ring wrap of
> + * descriptors before SW gets an interrupt and overwrites SW head, the gen bit
> + * in the descriptor will be wrong. Any overwritten descriptors' buffers will
> + * be gone forever and SW has no reasonable way to tell that this has happened.
> + * From SW perspective, when we finally get an interrupt, it looks like we're
> + * still waiting for descriptor to be done, stalling forever.
> + */
> +#define IECM_RX_BUFQ_DESC_COUNT(RXD, NUM_BUFQ)	((RXD) / (NUM_BUFQ))
> +
> +#define IECM_RX_BUFQ_WORKING_SET(R)		((R)->desc_count - 1)
> +#define IECM_RX_BUFQ_NON_WORKING_SET(R)		((R)->desc_count - \
> +						 IECM_RX_BUFQ_WORKING_SET(R))
> +
> +#define IECM_RX_HDR_SIZE			256
> +#define IECM_RX_BUF_2048			2048
> +#define IECM_RX_BUF_4096			4096
> +#define IECM_RX_BUF_STRIDE			64
> +#define IECM_LOW_WATERMARK			64
> +#define IECM_HDR_BUF_SIZE			256
> +#define IECM_PACKET_HDR_PAD	\
> +	(ETH_HLEN + ETH_FCS_LEN + (VLAN_HLEN * 2))
> +#define IECM_MAX_RXBUFFER			9728
> +#define IECM_MAX_MTU		\
> +	(IECM_MAX_RXBUFFER - IECM_PACKET_HDR_PAD)
> +#define IECM_INT_NAME_STR_LEN	(IFNAMSIZ + 16)
> +
> +#define IECM_TX_COMPLQ_CLEAN_BUDGET	256
> +
> +struct iecm_intr_reg {
> +	u32 dyn_ctl;
> +	u32 dyn_ctl_intena_m;
> +	u32 dyn_ctl_clrpba_m;
> +	u32 dyn_ctl_itridx_s;
> +	u32 dyn_ctl_itridx_m;
> +	u32 dyn_ctl_intrvl_s;
> +	u32 rx_itr;
> +	u32 tx_itr;
> +	u32 icr_ena;
> +	u32 icr_ena_ctlq_m;
> +};
> +
> +struct iecm_q_vector {
> +	struct iecm_vport *vport;
> +	cpumask_t affinity_mask;
> +	struct napi_struct napi;
> +	u16 v_idx;		/* index in the vport->q_vector array */
> +	struct iecm_intr_reg intr_reg;
> +
> +	int num_txq;
> +	struct iecm_queue **tx;
> +	struct dim tx_dim;	/* data for net_dim algorithm */
> +	u16 tx_itr_value;
> +	bool tx_intr_mode;

Same here.

> +	u32 tx_itr_idx;
> +
> +	int num_rxq;
> +	struct iecm_queue **rx;
> +	struct dim rx_dim;	/* data for net_dim algorithm */
> +	u16 rx_itr_value;
> +	bool rx_intr_mode;

...here as well, and most likely in more places I overlooked.

> +	u32 rx_itr_idx;
> +
> +	int num_bufq;
> +	struct iecm_queue **bufq;
> +
> +	u16 total_events;       /* net_dim(): number of interrupts processed */
> +	char name[IECM_INT_NAME_STR_LEN];
> +};
> +
> +irqreturn_t
> +iecm_vport_intr_clean_queues(int __always_unused irq, void *data);
>  #endif /* !_IECM_TXRX_H_ */
> -- 
> 2.33.0

Thanks,
Al

^ permalink raw reply	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 05/19] iecm: add vport alloc and virtchnl messages
  2022-01-28  4:19     ` kernel test robot
@ 2022-01-28 12:39       ` Alexander Lobakin
  -1 siblings, 0 replies; 89+ messages in thread
From: Alexander Lobakin @ 2022-01-28 12:39 UTC (permalink / raw)
  To: intel-wired-lan

From: kernel test robot <lkp@intel.com>
Date: Fri, 28 Jan 2022 12:19:10 +0800

> Hi Alan,
> 
> Thank you for the patch! Perhaps something to improve:
> 
> [auto build test WARNING on net-next/master]
> 
> url:    https://github.com/0day-ci/linux/commits/Alan-Brady/Add-iecm-and-idpf/20220128-085513
> base:   https://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next.git e2cf07654efb0fd7bbcb475c6f74be7b5755a8fd
> config: i386-allyesconfig (https://download.01.org/0day-ci/archive/20220128/202201281200.CI9u45bS-lkp at intel.com/config)
> compiler: gcc-9 (Debian 9.3.0-22) 9.3.0
> reproduce (this is a W=1 build):
>         # https://github.com/0day-ci/linux/commit/1233d9631b312eea5aebbce63590e27f9993bacc
>         git remote add linux-review https://github.com/0day-ci/linux
>         git fetch --no-tags linux-review Alan-Brady/Add-iecm-and-idpf/20220128-085513
>         git checkout 1233d9631b312eea5aebbce63590e27f9993bacc
>         # save the config file to linux build tree
>         mkdir build_dir
>         make W=1 O=build_dir ARCH=i386 SHELL=/bin/bash drivers/net/ethernet/intel/iecm/
> 
> If you fix the issue, kindly add following tag as appropriate
> Reported-by: kernel test robot <lkp@intel.com>
> 
> All warnings (new ones prefixed by >>):
> 
>    drivers/net/ethernet/intel/iecm/iecm_virtchnl.c: In function 'iecm_vport_queue_ids_init':
> >> drivers/net/ethernet/intel/iecm/iecm_virtchnl.c:1396:1: warning: the frame size of 1052 bytes is larger than 1024 bytes [-Wframe-larger-than=]
>     1396 | }
>          | ^
> 
> 
> vim +1396 drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> 
>   1322	
>   1323	/**
>   1324	 * iecm_vport_queue_ids_init - Initialize queue ids from Mailbox parameters
>   1325	 * @vport: virtual port for which the queues ids are initialized
>   1326	 *
>   1327	 * Will initialize all queue ids with ids received as mailbox parameters.
>   1328	 * Returns 0 on success, negative if all the queues are not initialized.
>   1329	 */
>   1330	static int iecm_vport_queue_ids_init(struct iecm_vport *vport)
>   1331	{
>   1332		struct virtchnl2_create_vport *vport_params;
>   1333		struct virtchnl2_queue_reg_chunks *chunks;
>   1334		/* We may never deal with more than 256 same type of queues */
>   1335	#define IECM_MAX_QIDS	256
>   1336		u32 qids[IECM_MAX_QIDS];

I'll elaborate on this warning a bit: you declare 4 * 256 = 1 Kb
array directly on the stack here.

1 Kb is a limit inside the kernel, it won't cause any issues since
stacks on x86 are huge, but raises such warnings and we should
listen to them, especially given that compile tests are now being
done with -Werror.

Just use kzalloc(array_size(IECM_MAX_QIDS, sizeof(u32))) + kfree()
here to avoid that.

>   1337		int num_ids;
>   1338		u16 q_type;
>   1339	
>   1340		if (vport->adapter->config_data.req_qs_chunks) {
>   1341			struct virtchnl2_add_queues *vc_aq =
>   1342				(struct virtchnl2_add_queues *)
>   1343				vport->adapter->config_data.req_qs_chunks;
>   1344			chunks = &vc_aq->chunks;
>   1345		} else {
>   1346			vport_params = (struct virtchnl2_create_vport *)
>   1347					vport->adapter->vport_params_recvd[0];
>   1348			chunks = &vport_params->chunks;
>   1349		}
>   1350	
>   1351		num_ids = iecm_vport_get_queue_ids(qids, IECM_MAX_QIDS,
>   1352						   VIRTCHNL2_QUEUE_TYPE_TX,
>   1353						   chunks);
>   1354		if (num_ids != vport->num_txq)
>   1355			return -EINVAL;
>   1356		num_ids = __iecm_vport_queue_ids_init(vport, qids, num_ids,
>   1357						      VIRTCHNL2_QUEUE_TYPE_TX);
>   1358		if (num_ids != vport->num_txq)
>   1359			return -EINVAL;
>   1360		num_ids = iecm_vport_get_queue_ids(qids, IECM_MAX_QIDS,
>   1361						   VIRTCHNL2_QUEUE_TYPE_RX,
>   1362						   chunks);
>   1363		if (num_ids != vport->num_rxq)
>   1364			return -EINVAL;
>   1365		num_ids = __iecm_vport_queue_ids_init(vport, qids, num_ids,
>   1366						      VIRTCHNL2_QUEUE_TYPE_RX);
>   1367		if (num_ids != vport->num_rxq)
>   1368			return -EINVAL;
>   1369	
>   1370		if (iecm_is_queue_model_split(vport->txq_model)) {
>   1371			q_type = VIRTCHNL2_QUEUE_TYPE_TX_COMPLETION;
>   1372			num_ids = iecm_vport_get_queue_ids(qids, IECM_MAX_QIDS, q_type,
>   1373							   chunks);
>   1374			if (num_ids != vport->num_complq)
>   1375				return -EINVAL;
>   1376			num_ids = __iecm_vport_queue_ids_init(vport, qids,
>   1377							      num_ids,
>   1378							      q_type);
>   1379			if (num_ids != vport->num_complq)
>   1380				return -EINVAL;
>   1381		}
>   1382	
>   1383		if (iecm_is_queue_model_split(vport->rxq_model)) {
>   1384			q_type = VIRTCHNL2_QUEUE_TYPE_RX_BUFFER;
>   1385			num_ids = iecm_vport_get_queue_ids(qids, IECM_MAX_QIDS, q_type,
>   1386							   chunks);
>   1387			if (num_ids != vport->num_bufq)
>   1388				return -EINVAL;
>   1389			num_ids = __iecm_vport_queue_ids_init(vport, qids, num_ids,
>   1390							      q_type);
>   1391			if (num_ids != vport->num_bufq)
>   1392				return -EINVAL;
>   1393		}
>   1394	
>   1395		return 0;
> > 1396	}
>   1397	
> 
> ---
> 0-DAY CI Kernel Test Service, Intel Corporation
> https://lists.01.org/hyperkitty/list/kbuild-all at lists.01.org

Thanks,
Al

^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [Intel-wired-lan] [PATCH net-next 05/19] iecm: add vport alloc and virtchnl messages
@ 2022-01-28 12:39       ` Alexander Lobakin
  0 siblings, 0 replies; 89+ messages in thread
From: Alexander Lobakin @ 2022-01-28 12:39 UTC (permalink / raw)
  To: kbuild-all

[-- Attachment #1: Type: text/plain, Size: 5289 bytes --]

From: kernel test robot <lkp@intel.com>
Date: Fri, 28 Jan 2022 12:19:10 +0800

> Hi Alan,
> 
> Thank you for the patch! Perhaps something to improve:
> 
> [auto build test WARNING on net-next/master]
> 
> url:    https://github.com/0day-ci/linux/commits/Alan-Brady/Add-iecm-and-idpf/20220128-085513
> base:   https://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next.git e2cf07654efb0fd7bbcb475c6f74be7b5755a8fd
> config: i386-allyesconfig (https://download.01.org/0day-ci/archive/20220128/202201281200.CI9u45bS-lkp(a)intel.com/config)
> compiler: gcc-9 (Debian 9.3.0-22) 9.3.0
> reproduce (this is a W=1 build):
>         # https://github.com/0day-ci/linux/commit/1233d9631b312eea5aebbce63590e27f9993bacc
>         git remote add linux-review https://github.com/0day-ci/linux
>         git fetch --no-tags linux-review Alan-Brady/Add-iecm-and-idpf/20220128-085513
>         git checkout 1233d9631b312eea5aebbce63590e27f9993bacc
>         # save the config file to linux build tree
>         mkdir build_dir
>         make W=1 O=build_dir ARCH=i386 SHELL=/bin/bash drivers/net/ethernet/intel/iecm/
> 
> If you fix the issue, kindly add following tag as appropriate
> Reported-by: kernel test robot <lkp@intel.com>
> 
> All warnings (new ones prefixed by >>):
> 
>    drivers/net/ethernet/intel/iecm/iecm_virtchnl.c: In function 'iecm_vport_queue_ids_init':
> >> drivers/net/ethernet/intel/iecm/iecm_virtchnl.c:1396:1: warning: the frame size of 1052 bytes is larger than 1024 bytes [-Wframe-larger-than=]
>     1396 | }
>          | ^
> 
> 
> vim +1396 drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> 
>   1322	
>   1323	/**
>   1324	 * iecm_vport_queue_ids_init - Initialize queue ids from Mailbox parameters
>   1325	 * @vport: virtual port for which the queues ids are initialized
>   1326	 *
>   1327	 * Will initialize all queue ids with ids received as mailbox parameters.
>   1328	 * Returns 0 on success, negative if all the queues are not initialized.
>   1329	 */
>   1330	static int iecm_vport_queue_ids_init(struct iecm_vport *vport)
>   1331	{
>   1332		struct virtchnl2_create_vport *vport_params;
>   1333		struct virtchnl2_queue_reg_chunks *chunks;
>   1334		/* We may never deal with more than 256 same type of queues */
>   1335	#define IECM_MAX_QIDS	256
>   1336		u32 qids[IECM_MAX_QIDS];

I'll elaborate on this warning a bit: you declare 4 * 256 = 1 Kb
array directly on the stack here.

1 Kb is a limit inside the kernel, it won't cause any issues since
stacks on x86 are huge, but raises such warnings and we should
listen to them, especially given that compile tests are now being
done with -Werror.

Just use kzalloc(array_size(IECM_MAX_QIDS, sizeof(u32))) + kfree()
here to avoid that.

>   1337		int num_ids;
>   1338		u16 q_type;
>   1339	
>   1340		if (vport->adapter->config_data.req_qs_chunks) {
>   1341			struct virtchnl2_add_queues *vc_aq =
>   1342				(struct virtchnl2_add_queues *)
>   1343				vport->adapter->config_data.req_qs_chunks;
>   1344			chunks = &vc_aq->chunks;
>   1345		} else {
>   1346			vport_params = (struct virtchnl2_create_vport *)
>   1347					vport->adapter->vport_params_recvd[0];
>   1348			chunks = &vport_params->chunks;
>   1349		}
>   1350	
>   1351		num_ids = iecm_vport_get_queue_ids(qids, IECM_MAX_QIDS,
>   1352						   VIRTCHNL2_QUEUE_TYPE_TX,
>   1353						   chunks);
>   1354		if (num_ids != vport->num_txq)
>   1355			return -EINVAL;
>   1356		num_ids = __iecm_vport_queue_ids_init(vport, qids, num_ids,
>   1357						      VIRTCHNL2_QUEUE_TYPE_TX);
>   1358		if (num_ids != vport->num_txq)
>   1359			return -EINVAL;
>   1360		num_ids = iecm_vport_get_queue_ids(qids, IECM_MAX_QIDS,
>   1361						   VIRTCHNL2_QUEUE_TYPE_RX,
>   1362						   chunks);
>   1363		if (num_ids != vport->num_rxq)
>   1364			return -EINVAL;
>   1365		num_ids = __iecm_vport_queue_ids_init(vport, qids, num_ids,
>   1366						      VIRTCHNL2_QUEUE_TYPE_RX);
>   1367		if (num_ids != vport->num_rxq)
>   1368			return -EINVAL;
>   1369	
>   1370		if (iecm_is_queue_model_split(vport->txq_model)) {
>   1371			q_type = VIRTCHNL2_QUEUE_TYPE_TX_COMPLETION;
>   1372			num_ids = iecm_vport_get_queue_ids(qids, IECM_MAX_QIDS, q_type,
>   1373							   chunks);
>   1374			if (num_ids != vport->num_complq)
>   1375				return -EINVAL;
>   1376			num_ids = __iecm_vport_queue_ids_init(vport, qids,
>   1377							      num_ids,
>   1378							      q_type);
>   1379			if (num_ids != vport->num_complq)
>   1380				return -EINVAL;
>   1381		}
>   1382	
>   1383		if (iecm_is_queue_model_split(vport->rxq_model)) {
>   1384			q_type = VIRTCHNL2_QUEUE_TYPE_RX_BUFFER;
>   1385			num_ids = iecm_vport_get_queue_ids(qids, IECM_MAX_QIDS, q_type,
>   1386							   chunks);
>   1387			if (num_ids != vport->num_bufq)
>   1388				return -EINVAL;
>   1389			num_ids = __iecm_vport_queue_ids_init(vport, qids, num_ids,
>   1390							      q_type);
>   1391			if (num_ids != vport->num_bufq)
>   1392				return -EINVAL;
>   1393		}
>   1394	
>   1395		return 0;
> > 1396	}
>   1397	
> 
> ---
> 0-DAY CI Kernel Test Service, Intel Corporation
> https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org

Thanks,
Al

^ permalink raw reply	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 06/19] iecm: add virtchnl messages for queues
  2022-01-28  0:09 ` [Intel-wired-lan] [PATCH net-next 06/19] iecm: add virtchnl messages for queues Alan Brady
@ 2022-01-28 13:03   ` Alexander Lobakin
  2022-02-02 22:48     ` Brady, Alan
  0 siblings, 1 reply; 89+ messages in thread
From: Alexander Lobakin @ 2022-01-28 13:03 UTC (permalink / raw)
  To: intel-wired-lan

From: Alan Brady <alan.brady@intel.com>
Date: Thu, 27 Jan 2022 16:09:56 -0800

> This continues adding virtchnl messages. This largely relates to adding
> messages needed to negotiate and setup traffic queues.
> 
> Signed-off-by: Phani Burra <phani.r.burra@intel.com>
> Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
> Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
> Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
> Signed-off-by: Alice Michael <alice.michael@intel.com>
> Signed-off-by: Alan Brady <alan.brady@intel.com>
> ---
>  drivers/net/ethernet/intel/iecm/iecm_lib.c    |   14 +
>  drivers/net/ethernet/intel/iecm/iecm_txrx.c   |  161 +++
>  .../net/ethernet/intel/iecm/iecm_virtchnl.c   | 1127 ++++++++++++++++-
>  drivers/net/ethernet/intel/include/iecm.h     |   22 +
>  .../net/ethernet/intel/include/iecm_txrx.h    |  196 +++
>  5 files changed, 1505 insertions(+), 15 deletions(-)
> 
> diff --git a/drivers/net/ethernet/intel/iecm/iecm_lib.c b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> index e2e523f0700e..4e9cc7f2d138 100644
> --- a/drivers/net/ethernet/intel/iecm/iecm_lib.c
> +++ b/drivers/net/ethernet/intel/iecm/iecm_lib.c

--- 8< ---

> +void iecm_vport_calc_num_q_desc(struct iecm_vport *vport)
> +{
> +	int num_req_txq_desc = vport->adapter->config_data.num_req_txq_desc;
> +	int num_req_rxq_desc = vport->adapter->config_data.num_req_rxq_desc;
> +	int num_bufqs = vport->num_bufqs_per_qgrp;
> +	int i = 0;
> +
> +	vport->complq_desc_count = 0;
> +	if (num_req_txq_desc) {
> +		vport->txq_desc_count = num_req_txq_desc;
> +		if (iecm_is_queue_model_split(vport->txq_model)) {
> +			vport->complq_desc_count = num_req_txq_desc;
> +			if (vport->complq_desc_count < IECM_MIN_TXQ_COMPLQ_DESC)
> +				vport->complq_desc_count =
> +					IECM_MIN_TXQ_COMPLQ_DESC;
> +		}
> +	} else {
> +		vport->txq_desc_count =
> +			IECM_DFLT_TX_Q_DESC_COUNT;
> +		if (iecm_is_queue_model_split(vport->txq_model)) {
> +			vport->complq_desc_count =
> +				IECM_DFLT_TX_COMPLQ_DESC_COUNT;
> +		}

Braces are redundant here since the path is a one-liner.

> +	}
> +
> +	if (num_req_rxq_desc)
> +		vport->rxq_desc_count = num_req_rxq_desc;
> +	else
> +		vport->rxq_desc_count = IECM_DFLT_RX_Q_DESC_COUNT;
> +
> +	for (i = 0; i < num_bufqs; i++) {
> +		if (!vport->bufq_desc_count[i])
> +			vport->bufq_desc_count[i] =
> +				IECM_RX_BUFQ_DESC_COUNT(vport->rxq_desc_count,
> +							num_bufqs);

		if (vport->bufq_desc_count[i])
			continue;

		vport-> ...

-1 indent level with that.

> +	}
> +}
> +EXPORT_SYMBOL(iecm_vport_calc_num_q_desc);
> +
> +/**
> + * iecm_vport_calc_total_qs - Calculate total number of queues
> + * @adapter: private data struct
> + * @vport_msg: message to fill with data
> + */
> +void iecm_vport_calc_total_qs(struct iecm_adapter *adapter,
> +			      struct virtchnl2_create_vport *vport_msg)
> +{
> +	unsigned int num_req_tx_qs = adapter->config_data.num_req_tx_qs;
> +	unsigned int num_req_rx_qs = adapter->config_data.num_req_rx_qs;
> +	int dflt_splitq_txq_grps, dflt_singleq_txqs;
> +	int dflt_splitq_rxq_grps, dflt_singleq_rxqs;
> +	int num_txq_grps, num_rxq_grps;
> +	int num_cpus;
> +	u16 max_q;
> +
> +	/* Restrict num of queues to cpus online as a default configuration to
> +	 * give best performance. User can always override to a max number
> +	 * of queues via ethtool.
> +	 */
> +	num_cpus = num_online_cpus();
> +	max_q = adapter->max_queue_limit;
> +
> +	dflt_splitq_txq_grps = min_t(int, max_q, num_cpus);
> +	dflt_singleq_txqs = min_t(int, max_q, num_cpus);
> +	dflt_splitq_rxq_grps = min_t(int, max_q, num_cpus);
> +	dflt_singleq_rxqs = min_t(int, max_q, num_cpus);
> +
> +	if (iecm_is_queue_model_split(le16_to_cpu(vport_msg->txq_model))) {
> +		num_txq_grps = num_req_tx_qs ? num_req_tx_qs : dflt_splitq_txq_grps;
> +		vport_msg->num_tx_complq = cpu_to_le16(num_txq_grps *
> +						       IECM_COMPLQ_PER_GROUP);
> +		vport_msg->num_tx_q = cpu_to_le16(num_txq_grps *
> +						  IECM_DFLT_SPLITQ_TXQ_PER_GROUP);
> +	} else {
> +		num_txq_grps = IECM_DFLT_SINGLEQ_TX_Q_GROUPS;
> +		vport_msg->num_tx_q =
> +				cpu_to_le16(num_txq_grps *
> +					    (num_req_tx_qs ? num_req_tx_qs :
> +					    dflt_singleq_txqs));
> +		vport_msg->num_tx_complq = 0;
> +	}
> +	if (iecm_is_queue_model_split(le16_to_cpu(vport_msg->rxq_model))) {
> +		num_rxq_grps = num_req_rx_qs ? num_req_rx_qs : dflt_splitq_rxq_grps;
> +		vport_msg->num_rx_bufq =
> +					cpu_to_le16(num_rxq_grps *
> +						    IECM_MAX_BUFQS_PER_RXQ_GRP);
> +
> +		vport_msg->num_rx_q = cpu_to_le16(num_rxq_grps *
> +						  IECM_DFLT_SPLITQ_RXQ_PER_GROUP);
> +	} else {
> +		num_rxq_grps = IECM_DFLT_SINGLEQ_RX_Q_GROUPS;
> +		vport_msg->num_rx_bufq = 0;
> +		vport_msg->num_rx_q =
> +				cpu_to_le16(num_rxq_grps *
> +					    (num_req_rx_qs ? num_req_rx_qs :
> +					    dflt_singleq_rxqs));
> +	}
> +}
> +
> +/**
> + * iecm_vport_calc_num_q_groups - Calculate number of queue groups
> + * @vport: vport to calculate q groups for
> + */
> +void iecm_vport_calc_num_q_groups(struct iecm_vport *vport)
> +{
> +	if (iecm_is_queue_model_split(vport->txq_model))
> +		vport->num_txq_grp = vport->num_txq;
> +	else
> +		vport->num_txq_grp = IECM_DFLT_SINGLEQ_TX_Q_GROUPS;
> +
> +	if (iecm_is_queue_model_split(vport->rxq_model))
> +		vport->num_rxq_grp = vport->num_rxq;
> +	else
> +		vport->num_rxq_grp = IECM_DFLT_SINGLEQ_RX_Q_GROUPS;
> +}
> +EXPORT_SYMBOL(iecm_vport_calc_num_q_groups);
> +
> +/**
> + * iecm_vport_calc_num_q_vec - Calculate total number of vectors required for
> + * this vport
> + * @vport: virtual port
> + *
> + */
> +void iecm_vport_calc_num_q_vec(struct iecm_vport *vport)
> +{
> +	if (iecm_is_queue_model_split(vport->txq_model))
> +		vport->num_q_vectors = vport->num_txq_grp;
> +	else
> +		vport->num_q_vectors = vport->num_txq;
> +}
> +EXPORT_SYMBOL(iecm_vport_calc_num_q_vec);
> diff --git a/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> index aae06064d706..d8152e657e24 100644
> --- a/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> +++ b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> @@ -859,6 +859,48 @@ static int iecm_recv_get_caps_msg(struct iecm_adapter *adapter)
>  				sizeof(struct virtchnl2_get_capabilities));
>  }
>  
> +/**
> + * iecm_get_reg_intr_vecs - Get vector queue register offset
> + * @vport: virtual port structure
> + * @reg_vals: Register offsets to store in
> + * @num_vecs: Number of vector registers
> + *
> + * Returns number of regsiters that got populated
> + */
> +int iecm_get_reg_intr_vecs(struct iecm_vport *vport,
> +			   struct iecm_vec_regs *reg_vals, int num_vecs)
> +{
> +	struct virtchnl2_vector_chunks *chunks;
> +	struct iecm_vec_regs reg_val;
> +	u16 num_vchunks, num_vec;
> +	int num_regs = 0, i, j;
> +
> +	chunks = &vport->adapter->req_vec_chunks->vchunks;
> +	num_vchunks = le16_to_cpu(chunks->num_vchunks);
> +
> +	for (j = 0; j < num_vchunks; j++) {
> +		struct virtchnl2_vector_chunk *chunk = &chunks->vchunks[j];
> +
> +		num_vec = le16_to_cpu(chunk->num_vectors);
> +		reg_val.dyn_ctl_reg = le32_to_cpu(chunk->dynctl_reg_start);
> +		reg_val.itrn_reg = le32_to_cpu(chunk->itrn_reg_start);
> +		for (i = 0; i < num_vec; i++) {
> +			if (num_regs == num_vecs)
> +				break;
> +			reg_vals[i].dyn_ctl_reg = reg_val.dyn_ctl_reg;
> +			reg_vals[i].itrn_reg = reg_val.itrn_reg;
> +			reg_val.dyn_ctl_reg +=
> +				le32_to_cpu(chunk->dynctl_reg_spacing);
> +			reg_val.itrn_reg +=
> +				le32_to_cpu(chunk->itrn_reg_spacing);
> +			num_regs++;
> +		}
> +	}
> +
> +	return num_regs;
> +}
> +EXPORT_SYMBOL(iecm_get_reg_intr_vecs);
> +
>  /**
>   * iecm_send_create_vport_msg - Send virtchnl create vport message
>   * @adapter: Driver specific private structure
> @@ -869,8 +911,36 @@ static int iecm_recv_get_caps_msg(struct iecm_adapter *adapter)
>   */
>  static int iecm_send_create_vport_msg(struct iecm_adapter *adapter)
>  {
> -	/* stub */
> -	return 0;
> +	struct virtchnl2_create_vport *vport_msg;
> +	int buf_size;
> +
> +	buf_size = sizeof(struct virtchnl2_create_vport);
> +	if (!adapter->vport_params_reqd[0]) {
> +		adapter->vport_params_reqd[0] = kzalloc(buf_size, GFP_KERNEL);
> +		if (!adapter->vport_params_reqd[0])
> +			return -ENOMEM;
> +	}
> +
> +	vport_msg = (struct virtchnl2_create_vport *)
> +			adapter->vport_params_reqd[0];
> +	vport_msg->vport_type = cpu_to_le16(VIRTCHNL2_VPORT_TYPE_DEFAULT);
> +
> +	if (test_bit(__IECM_REQ_TX_SPLITQ, adapter->flags))
> +		vport_msg->txq_model = cpu_to_le16(VIRTCHNL2_QUEUE_MODEL_SPLIT);
> +	else
> +		vport_msg->txq_model = cpu_to_le16(VIRTCHNL2_QUEUE_MODEL_SINGLE);
> +
> +	if (test_bit(__IECM_REQ_RX_SPLITQ, adapter->flags))
> +		vport_msg->rxq_model = cpu_to_le16(VIRTCHNL2_QUEUE_MODEL_SPLIT);
> +	else
> +		vport_msg->rxq_model = cpu_to_le16(VIRTCHNL2_QUEUE_MODEL_SINGLE);
> +
> +	adapter->dev_ops.vc_ops.init_max_queues(adapter);
> +
> +	iecm_vport_calc_total_qs(adapter, vport_msg);
> +
> +	return iecm_send_mb_msg(adapter, VIRTCHNL2_OP_CREATE_VPORT, buf_size,
> +				(u8 *)vport_msg);
>  }
>  
>  /**
> @@ -884,7 +954,25 @@ static int iecm_send_create_vport_msg(struct iecm_adapter *adapter)
>  static int iecm_recv_create_vport_msg(struct iecm_adapter *adapter,
>  				      int *vport_id)
>  {
> -	/* stub */
> +	struct virtchnl2_create_vport *vport_msg;
> +	int err;
> +
> +	if (!adapter->vport_params_recvd[0]) {
> +		adapter->vport_params_recvd[0] = kzalloc(IECM_DFLT_MBX_BUF_SIZE,
> +							 GFP_KERNEL);
> +		if (!adapter->vport_params_recvd[0])
> +			return -ENOMEM;
> +	}
> +
> +	vport_msg = (struct virtchnl2_create_vport *)
> +			adapter->vport_params_recvd[0];
> +
> +	err = iecm_recv_mb_msg(adapter, VIRTCHNL2_OP_CREATE_VPORT, vport_msg,
> +			       IECM_DFLT_MBX_BUF_SIZE);
> +	if (err)
> +		return err;
> +
> +	*vport_id = le32_to_cpu(vport_msg->vport_id);
>  	return 0;
>  }
>  
> @@ -966,6 +1054,920 @@ int iecm_wait_for_event(struct iecm_adapter *adapter,
>  }
>  EXPORT_SYMBOL(iecm_wait_for_event);
>  
> +/**
> + * iecm_wait_for_marker_event - wait for software marker response
> + * @vport: virtual port data structure
> + *
> + * Returns 0 success, negative on failure.
> + **/
> +static int iecm_wait_for_marker_event(struct iecm_vport *vport)
> +{
> +	int event = 0;
> +	int i;
> +
> +	for (i = 0; i < vport->num_txq; i++)
> +		set_bit(__IECM_Q_SW_MARKER, vport->txqs[i]->flags);
> +
> +	event = wait_event_timeout(vport->adapter->sw_marker_wq,
> +				   test_and_clear_bit(__IECM_SW_MARKER,
> +						      vport->adapter->flags),
> +				   msecs_to_jiffies(500));
> +	if (event)
> +		return 0;
> +	return -ETIMEDOUT;
> +}
> +
> +/**
> + * iecm_send_destroy_vport_msg - Send virtchnl destroy vport message
> + * @vport: virtual port data structure
> + *
> + * Send virtchnl destroy vport message.  Returns 0 on success, negative on
> + * failure.
> + */
> +int iecm_send_destroy_vport_msg(struct iecm_vport *vport)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct virtchnl2_vport v_id;
> +	int err;
> +
> +	v_id.vport_id = cpu_to_le32(vport->vport_id);
> +
> +	err = iecm_send_mb_msg(adapter, VIRTCHNL2_OP_DESTROY_VPORT,
> +			       sizeof(v_id), (u8 *)&v_id);
> +	if (err)
> +		return err;
> +
> +	return iecm_min_wait_for_event(adapter, IECM_VC_DESTROY_VPORT,
> +				       IECM_VC_DESTROY_VPORT_ERR);
> +}
> +
> +/**
> + * iecm_send_enable_vport_msg - Send virtchnl enable vport message
> + * @vport: virtual port data structure
> + *
> + * Send enable vport virtchnl message.  Returns 0 on success, negative on
> + * failure.
> + */
> +int iecm_send_enable_vport_msg(struct iecm_vport *vport)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct virtchnl2_vport v_id;
> +	int err;
> +
> +	v_id.vport_id = cpu_to_le32(vport->vport_id);
> +
> +	err = iecm_send_mb_msg(adapter, VIRTCHNL2_OP_ENABLE_VPORT,
> +			       sizeof(v_id), (u8 *)&v_id);
> +	if (err)
> +		return err;
> +
> +	return iecm_wait_for_event(adapter, IECM_VC_ENA_VPORT,
> +				   IECM_VC_ENA_VPORT_ERR);
> +}
> +
> +/**
> + * iecm_send_disable_vport_msg - Send virtchnl disable vport message
> + * @vport: virtual port data structure
> + *
> + * Send disable vport virtchnl message.  Returns 0 on success, negative on
> + * failure.
> + */
> +int iecm_send_disable_vport_msg(struct iecm_vport *vport)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct virtchnl2_vport v_id;
> +	int err;
> +
> +	v_id.vport_id = cpu_to_le32(vport->vport_id);
> +
> +	err = iecm_send_mb_msg(adapter, VIRTCHNL2_OP_DISABLE_VPORT,
> +			       sizeof(v_id), (u8 *)&v_id);
> +	if (err)
> +		return err;
> +
> +	return iecm_min_wait_for_event(adapter, IECM_VC_DIS_VPORT,
> +				       IECM_VC_DIS_VPORT_ERR);
> +}
> +
> +/**
> + * iecm_send_config_tx_queues_msg - Send virtchnl config tx queues message
> + * @vport: virtual port data structure
> + *
> + * Send config tx queues virtchnl message. Returns 0 on success, negative on
> + * failure.
> + */
> +int iecm_send_config_tx_queues_msg(struct iecm_vport *vport)
> +{
> +	struct virtchnl2_config_tx_queues *ctq = NULL;
> +	int config_data_size, chunk_size, buf_size = 0;
> +	int totqs, num_msgs, num_chunks;
> +	struct virtchnl2_txq_info *qi;
> +	int err = 0, i, k = 0;
> +	bool alloc = false;
> +
> +	totqs = vport->num_txq + vport->num_complq;
> +	qi = kcalloc(totqs, sizeof(struct virtchnl2_txq_info), GFP_KERNEL);
> +	if (!qi)
> +		return -ENOMEM;
> +
> +	/* Populate the queue info buffer with all queue context info */
> +	for (i = 0; i < vport->num_txq_grp; i++) {
> +		struct iecm_txq_group *tx_qgrp = &vport->txq_grps[i];
> +		int j;
> +
> +		for (j = 0; j < tx_qgrp->num_txq; j++, k++) {
> +			qi[k].queue_id =
> +				cpu_to_le32(tx_qgrp->txqs[j]->q_id);
> +			qi[k].model =
> +				cpu_to_le16(vport->txq_model);
> +			qi[k].type =
> +				cpu_to_le32(tx_qgrp->txqs[j]->q_type);
> +			qi[k].ring_len =
> +				cpu_to_le16(tx_qgrp->txqs[j]->desc_count);
> +			qi[k].dma_ring_addr =
> +				cpu_to_le64(tx_qgrp->txqs[j]->dma);
> +			if (iecm_is_queue_model_split(vport->txq_model)) {
> +				struct iecm_queue *q = tx_qgrp->txqs[j];
> +
> +				qi[k].tx_compl_queue_id =
> +					cpu_to_le16(tx_qgrp->complq->q_id);
> +
> +				if (test_bit(__IECM_Q_FLOW_SCH_EN, q->flags))
> +					qi[k].sched_mode =
> +					cpu_to_le16(VIRTCHNL2_TXQ_SCHED_MODE_FLOW);
> +				else
> +					qi[k].sched_mode =
> +					cpu_to_le16(VIRTCHNL2_TXQ_SCHED_MODE_QUEUE);
> +			} else {
> +				qi[k].sched_mode =
> +					cpu_to_le16(VIRTCHNL2_TXQ_SCHED_MODE_QUEUE);
> +			}
> +		}
> +
> +		if (iecm_is_queue_model_split(vport->txq_model)) {
> +			qi[k].queue_id =
> +				cpu_to_le32(tx_qgrp->complq->q_id);
> +			qi[k].model =
> +				cpu_to_le16(vport->txq_model);
> +			qi[k].type =
> +				cpu_to_le32(tx_qgrp->complq->q_type);
> +			qi[k].ring_len =
> +				cpu_to_le16(tx_qgrp->complq->desc_count);
> +			qi[k].dma_ring_addr =
> +				cpu_to_le64(tx_qgrp->complq->dma);
> +			k++;
> +		}
> +	}
> +
> +	/* Make sure accounting agrees */
> +	if (k != totqs) {
> +		err = -EINVAL;
> +		goto error;
> +	}
> +
> +	/* Chunk up the queue contexts into multiple messages to avoid
> +	 * sending a control queue message buffer that is too large
> +	 */
> +	config_data_size = sizeof(struct virtchnl2_config_tx_queues);
> +	chunk_size = sizeof(struct virtchnl2_txq_info);
> +
> +	num_chunks = IECM_NUM_CHUNKS_PER_MSG(config_data_size, chunk_size) + 1;
> +	if (totqs < num_chunks)
> +		num_chunks = totqs;
> +
> +	num_msgs = totqs / num_chunks;
> +	if (totqs % num_chunks)
> +		num_msgs++;
> +
> +	for (i = 0, k = 0; i < num_msgs; i++) {
> +		if (!ctq || alloc) {
> +			buf_size = (chunk_size * (num_chunks - 1)) +
> +					config_data_size;
> +			kfree(ctq);
> +			ctq = kzalloc(buf_size, GFP_KERNEL);
> +			if (!ctq) {
> +				err = -ENOMEM;
> +				goto error;
> +			}
> +		} else {
> +			memset(ctq, 0, buf_size);
> +		}
> +
> +		ctq->vport_id = cpu_to_le32(vport->vport_id);
> +		ctq->num_qinfo = cpu_to_le16(num_chunks);
> +		memcpy(ctq->qinfo, &qi[k], chunk_size * num_chunks);
> +
> +		err = iecm_send_mb_msg(vport->adapter,
> +				       VIRTCHNL2_OP_CONFIG_TX_QUEUES,
> +				       buf_size, (u8 *)ctq);
> +		if (err)
> +			goto mbx_error;
> +
> +		err = iecm_wait_for_event(vport->adapter, IECM_VC_CONFIG_TXQ,
> +					  IECM_VC_CONFIG_TXQ_ERR);
> +		if (err)
> +			goto mbx_error;
> +
> +		k += num_chunks;
> +		totqs -= num_chunks;
> +		if (totqs < num_chunks) {
> +			num_chunks = totqs;
> +			alloc = true;
> +		}
> +	}
> +
> +mbx_error:
> +	kfree(ctq);
> +error:
> +	kfree(qi);
> +	return err;
> +}
> +
> +/**
> + * iecm_send_config_rx_queues_msg - Send virtchnl config rx queues message
> + * @vport: virtual port data structure
> + *
> + * Send config rx queues virtchnl message.  Returns 0 on success, negative on
> + * failure.
> + */
> +int iecm_send_config_rx_queues_msg(struct iecm_vport *vport)
> +{
> +	struct virtchnl2_config_rx_queues *crq = NULL;
> +	int config_data_size, chunk_size, buf_size = 0;
> +	int totqs, num_msgs, num_chunks;
> +	struct virtchnl2_rxq_info *qi;
> +	int err = 0, i, k = 0;
> +	bool alloc = false;
> +
> +	totqs = vport->num_rxq + vport->num_bufq;
> +	qi = kcalloc(totqs, sizeof(struct virtchnl2_rxq_info), GFP_KERNEL);
> +	if (!qi)
> +		return -ENOMEM;
> +
> +	/* Populate the queue info buffer with all queue context info */
> +	for (i = 0; i < vport->num_rxq_grp; i++) {
> +		struct iecm_rxq_group *rx_qgrp = &vport->rxq_grps[i];
> +		int num_rxq;
> +		int j;
> +
> +		if (iecm_is_queue_model_split(vport->rxq_model)) {
> +			for (j = 0; j < vport->num_bufqs_per_qgrp; j++, k++) {
> +				struct iecm_queue *bufq =
> +					&rx_qgrp->splitq.bufq_sets[j].bufq;
> +
> +				qi[k].queue_id =
> +					cpu_to_le32(bufq->q_id);
> +				qi[k].model =
> +					cpu_to_le16(vport->rxq_model);
> +				qi[k].type =
> +					cpu_to_le32(bufq->q_type);
> +				qi[k].desc_ids =
> +					cpu_to_le64(VIRTCHNL2_RXDID_1_FLEX_SPLITQ_M);
> +				qi[k].ring_len =
> +					cpu_to_le16(bufq->desc_count);
> +				qi[k].dma_ring_addr =
> +					cpu_to_le64(bufq->dma);
> +				qi[k].data_buffer_size =
> +					cpu_to_le32(bufq->rx_buf_size);
> +				qi[k].buffer_notif_stride =
> +					bufq->rx_buf_stride;
> +				qi[k].rx_buffer_low_watermark =
> +					cpu_to_le16(bufq->rx_buffer_low_watermark);
> +			}
> +		}

		if (iecm_is_queue_model_split(vport->rxq_model))
			goto here;

-1 indent level for the for-loop.
Braces for 'if' are not needed since the for-loop has their own.

> +
> +		if (iecm_is_queue_model_split(vport->rxq_model))
> +			num_rxq = rx_qgrp->splitq.num_rxq_sets;
> +		else
> +			num_rxq = rx_qgrp->singleq.num_rxq;
> +
> +		for (j = 0; j < num_rxq; j++, k++) {
> +			struct iecm_queue *rxq;
> +
> +			if (iecm_is_queue_model_split(vport->rxq_model)) {
> +				rxq = &rx_qgrp->splitq.rxq_sets[j]->rxq;
> +				qi[k].rx_bufq1_id =
> +				  cpu_to_le16(rxq->rxq_grp->splitq.bufq_sets[0].bufq.q_id);
> +				qi[k].rx_bufq2_id =
> +				  cpu_to_le16(rxq->rxq_grp->splitq.bufq_sets[1].bufq.q_id);
> +				qi[k].hdr_buffer_size =
> +					cpu_to_le16(rxq->rx_hbuf_size);
> +				qi[k].rx_buffer_low_watermark =
> +					cpu_to_le16(rxq->rx_buffer_low_watermark);
> +
> +				if (rxq->rx_hsplit_en) {
> +					qi[k].qflags =
> +						cpu_to_le16(VIRTCHNL2_RXQ_HDR_SPLIT);
> +					qi[k].hdr_buffer_size =
> +						cpu_to_le16(rxq->rx_hbuf_size);
> +				}
> +			} else {
> +				rxq = rx_qgrp->singleq.rxqs[j];
> +			}

Same here, but with rxq = ... + goto.

> +
> +			qi[k].queue_id =
> +				cpu_to_le32(rxq->q_id);
> +			qi[k].model =
> +				cpu_to_le16(vport->rxq_model);
> +			qi[k].type =
> +				cpu_to_le32(rxq->q_type);
> +			qi[k].ring_len =
> +				cpu_to_le16(rxq->desc_count);
> +			qi[k].dma_ring_addr =
> +				cpu_to_le64(rxq->dma);
> +			qi[k].max_pkt_size =
> +				cpu_to_le32(rxq->rx_max_pkt_size);
> +			qi[k].data_buffer_size =
> +				cpu_to_le32(rxq->rx_buf_size);
> +			qi[k].qflags |=
> +				cpu_to_le16(VIRTCHNL2_RX_DESC_SIZE_32BYTE);
> +			qi[k].desc_ids =
> +				cpu_to_le64(rxq->rxdids);
> +		}
> +	}
> +
> +	/* Make sure accounting agrees */
> +	if (k != totqs) {
> +		err = -EINVAL;
> +		goto error;
> +	}
> +
> +	/* Chunk up the queue contexts into multiple messages to avoid
> +	 * sending a control queue message buffer that is too large
> +	 */
> +	config_data_size = sizeof(struct virtchnl2_config_rx_queues);
> +	chunk_size = sizeof(struct virtchnl2_rxq_info);
> +
> +	num_chunks = IECM_NUM_CHUNKS_PER_MSG(config_data_size, chunk_size) + 1;
> +	if (totqs < num_chunks)
> +		num_chunks = totqs;
> +
> +	num_msgs = totqs / num_chunks;
> +	if (totqs % num_chunks)
> +		num_msgs++;
> +
> +	for (i = 0, k = 0; i < num_msgs; i++) {
> +		if (!crq || alloc) {
> +			buf_size = (chunk_size * (num_chunks - 1)) +
> +					config_data_size;
> +			kfree(crq);
> +			crq = kzalloc(buf_size, GFP_KERNEL);
> +			if (!crq) {
> +				err = -ENOMEM;
> +				goto error;
> +			}
> +		} else {
> +			memset(crq, 0, buf_size);
> +		}
> +
> +		crq->vport_id = cpu_to_le32(vport->vport_id);
> +		crq->num_qinfo = cpu_to_le16(num_chunks);
> +		memcpy(crq->qinfo, &qi[k], chunk_size * num_chunks);
> +
> +		err = iecm_send_mb_msg(vport->adapter,
> +				       VIRTCHNL2_OP_CONFIG_RX_QUEUES,
> +				       buf_size, (u8 *)crq);
> +		if (err)
> +			goto mbx_error;
> +
> +		err = iecm_wait_for_event(vport->adapter, IECM_VC_CONFIG_RXQ,
> +					  IECM_VC_CONFIG_RXQ_ERR);
> +		if (err)
> +			goto mbx_error;
> +
> +		k += num_chunks;
> +		totqs -= num_chunks;
> +		if (totqs < num_chunks) {
> +			num_chunks = totqs;
> +			alloc = true;
> +		}
> +	}
> +
> +mbx_error:
> +	kfree(crq);
> +error:
> +	kfree(qi);
> +	return err;
> +}
> +
> +/**
> + * iecm_send_ena_dis_queues_msg - Send virtchnl enable or disable
> + * queues message
> + * @vport: virtual port data structure
> + * @vc_op: virtchnl op code to send
> + *
> + * Send enable or disable queues virtchnl message. Returns 0 on success,
> + * negative on failure.
> + */
> +static int iecm_send_ena_dis_queues_msg(struct iecm_vport *vport,
> +					enum virtchnl_ops vc_op)
> +{
> +	int num_msgs, num_chunks, config_data_size, chunk_size;
> +	int num_txq, num_rxq, num_q, buf_size, err = 0;
> +	struct virtchnl2_del_ena_dis_queues *eq = NULL;
> +	struct virtchnl2_queue_chunk *qc;
> +	bool alloc = false;
> +	int i, j, k = 0;
> +
> +	/* validate virtchnl op */
> +	switch (vc_op) {
> +	case VIRTCHNL2_OP_ENABLE_QUEUES:
> +	case VIRTCHNL2_OP_DISABLE_QUEUES:
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	num_txq = vport->num_txq + vport->num_complq;
> +	num_rxq = vport->num_rxq + vport->num_bufq;
> +	num_q = num_txq + num_rxq;
> +	buf_size = sizeof(struct virtchnl2_queue_chunk) * (num_q);
> +	qc = kzalloc(buf_size, GFP_KERNEL);
> +	if (!qc)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < vport->num_txq_grp; i++) {
> +		struct iecm_txq_group *tx_qgrp = &vport->txq_grps[i];
> +
> +		for (j = 0; j < tx_qgrp->num_txq; j++, k++) {
> +			qc[k].type = cpu_to_le32(tx_qgrp->txqs[j]->q_type);
> +			qc[k].start_queue_id =
> +					cpu_to_le32(tx_qgrp->txqs[j]->q_id);
> +			qc[k].num_queues = cpu_to_le32(1);
> +		}
> +	}
> +	if (vport->num_txq != k) {
> +		err = -EINVAL;
> +		goto error;
> +	}
> +
> +	if (iecm_is_queue_model_split(vport->txq_model)) {
> +		for (i = 0; i < vport->num_txq_grp; i++, k++) {
> +			struct iecm_txq_group *tx_qgrp = &vport->txq_grps[i];
> +
> +			qc[k].type = cpu_to_le32(tx_qgrp->complq->q_type);
> +			qc[k].start_queue_id =
> +					cpu_to_le32(tx_qgrp->complq->q_id);
> +			qc[k].num_queues = cpu_to_le32(1);
> +		}
> +		if (vport->num_complq != (k - vport->num_txq)) {
> +			err = -EINVAL;
> +			goto error;
> +		}
> +	}

...and here.

> +
> +	for (i = 0; i < vport->num_rxq_grp; i++) {
> +		struct iecm_rxq_group *rx_qgrp = &vport->rxq_grps[i];
> +
> +		if (iecm_is_queue_model_split(vport->rxq_model))
> +			num_rxq = rx_qgrp->splitq.num_rxq_sets;
> +		else
> +			num_rxq = rx_qgrp->singleq.num_rxq;
> +
> +		for (j = 0; j < num_rxq; j++, k++) {
> +			if (iecm_is_queue_model_split(vport->rxq_model)) {
> +				qc[k].start_queue_id =
> +				cpu_to_le32(rx_qgrp->splitq.rxq_sets[j]->rxq.q_id);
> +				qc[k].type =
> +				cpu_to_le32(rx_qgrp->splitq.rxq_sets[j]->rxq.q_type);
> +			} else {
> +				qc[k].start_queue_id =
> +				cpu_to_le32(rx_qgrp->singleq.rxqs[j]->q_id);
> +				qc[k].type =
> +				cpu_to_le32(rx_qgrp->singleq.rxqs[j]->q_type);
> +			}
> +			qc[k].num_queues = cpu_to_le32(1);
> +		}
> +	}
> +	if (vport->num_rxq != k - (vport->num_txq + vport->num_complq)) {
> +		err = -EINVAL;
> +		goto error;
> +	}
> +
> +	if (iecm_is_queue_model_split(vport->rxq_model)) {
> +		for (i = 0; i < vport->num_rxq_grp; i++) {
> +			struct iecm_rxq_group *rx_qgrp = &vport->rxq_grps[i];
> +
> +			for (j = 0; j < vport->num_bufqs_per_qgrp; j++, k++) {
> +				struct iecm_queue *q = &rx_qgrp->splitq.bufq_sets[j].bufq;
> +
> +				qc[k].type = cpu_to_le32(q->q_type);
> +				qc[k].start_queue_id = cpu_to_le32(q->q_id);
> +				qc[k].num_queues = cpu_to_le32(1);
> +			}
> +		}
> +		if (vport->num_bufq != k - (vport->num_txq +
> +					       vport->num_complq +
> +					       vport->num_rxq)) {
> +			err = -EINVAL;
> +			goto error;
> +		}
> +	}

...and here.

> +
> +	/* Chunk up the queue info into multiple messages */
> +	config_data_size = sizeof(struct virtchnl2_del_ena_dis_queues);
> +	chunk_size = sizeof(struct virtchnl2_queue_chunk);
> +
> +	num_chunks = IECM_NUM_CHUNKS_PER_MSG(config_data_size, chunk_size) + 1;
> +	if (num_q < num_chunks)
> +		num_chunks = num_q;
> +
> +	num_msgs = num_q / num_chunks;
> +	if (num_q % num_chunks)
> +		num_msgs++;
> +
> +	for (i = 0, k = 0; i < num_msgs; i++) {
> +		if (!eq || alloc) {
> +			buf_size = (chunk_size * (num_chunks - 1)) +
> +					config_data_size;
> +			kfree(eq);
> +			eq = kzalloc(buf_size, GFP_KERNEL);
> +			if (!eq) {
> +				err = -ENOMEM;
> +				goto error;
> +			}
> +		} else {
> +			memset(eq, 0, buf_size);
> +		}
> +		eq->vport_id = cpu_to_le32(vport->vport_id);
> +		eq->chunks.num_chunks = cpu_to_le16(num_chunks);
> +		memcpy(eq->chunks.chunks, &qc[k], chunk_size * num_chunks);
> +
> +		err = iecm_send_mb_msg(vport->adapter, vc_op, buf_size,
> +				       (u8 *)eq);
> +		if (err)
> +			goto mbx_error;
> +		k += num_chunks;
> +		num_q -= num_chunks;
> +		if (num_q < num_chunks) {
> +			num_chunks = num_q;
> +			alloc = true;
> +		}
> +	}
> +mbx_error:
> +	kfree(eq);
> +error:
> +	kfree(qc);
> +	return err;
> +}
> +
> +/**
> + * iecm_send_map_unmap_queue_vector_msg - Send virtchnl map or unmap queue
> + * vector message
> + * @vport: virtual port data structure
> + * @map: true for map and false for unmap
> + *
> + * Send map or unmap queue vector virtchnl message.  Returns 0 on success,
> + * negative on failure.
> + */
> +int iecm_send_map_unmap_queue_vector_msg(struct iecm_vport *vport, bool map)
> +{
> +	int num_msgs, num_chunks, config_data_size, chunk_size;
> +	struct virtchnl2_queue_vector_maps *vqvm = NULL;
> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct virtchnl2_queue_vector *vqv;
> +	int buf_size, num_q, err = 0;
> +	bool alloc = false;
> +	int i, j, k = 0;
> +
> +	num_q = vport->num_txq + vport->num_rxq;
> +
> +	buf_size = sizeof(struct virtchnl2_queue_vector) * num_q;
> +	vqv = kzalloc(buf_size, GFP_KERNEL);
> +	if (!vqv)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < vport->num_txq_grp; i++) {
> +		struct iecm_txq_group *tx_qgrp = &vport->txq_grps[i];
> +
> +		for (j = 0; j < tx_qgrp->num_txq; j++, k++) {
> +			vqv[k].queue_type = cpu_to_le32(tx_qgrp->txqs[j]->q_type);
> +			vqv[k].queue_id = cpu_to_le32(tx_qgrp->txqs[j]->q_id);
> +
> +			if (iecm_is_queue_model_split(vport->txq_model)) {
> +				vqv[k].vector_id =
> +				cpu_to_le16(tx_qgrp->complq->q_vector->v_idx);
> +				vqv[k].itr_idx =
> +				cpu_to_le32(tx_qgrp->complq->q_vector->tx_itr_idx);
> +			} else {
> +				vqv[k].vector_id =
> +				cpu_to_le16(tx_qgrp->txqs[j]->q_vector->v_idx);
> +				vqv[k].itr_idx =
> +				cpu_to_le32(tx_qgrp->txqs[j]->q_vector->tx_itr_idx);
> +			}
> +		}
> +	}
> +
> +	if (vport->num_txq != k) {
> +		err = -EINVAL;
> +		goto error;
> +	}
> +
> +	for (i = 0; i < vport->num_rxq_grp; i++) {
> +		struct iecm_rxq_group *rx_qgrp = &vport->rxq_grps[i];
> +		int num_rxq;
> +
> +		if (iecm_is_queue_model_split(vport->rxq_model))
> +			num_rxq = rx_qgrp->splitq.num_rxq_sets;
> +		else
> +			num_rxq = rx_qgrp->singleq.num_rxq;
> +
> +		for (j = 0; j < num_rxq; j++, k++) {
> +			struct iecm_queue *rxq;
> +
> +			if (iecm_is_queue_model_split(vport->rxq_model))
> +				rxq = &rx_qgrp->splitq.rxq_sets[j]->rxq;
> +			else
> +				rxq = rx_qgrp->singleq.rxqs[j];
> +
> +			vqv[k].queue_type = cpu_to_le32(rxq->q_type);
> +			vqv[k].queue_id = cpu_to_le32(rxq->q_id);
> +			vqv[k].vector_id = cpu_to_le16(rxq->q_vector->v_idx);
> +			vqv[k].itr_idx = cpu_to_le32(rxq->q_vector->rx_itr_idx);
> +		}
> +	}
> +
> +	if (iecm_is_queue_model_split(vport->txq_model)) {
> +		if (vport->num_rxq != k - vport->num_complq) {

	if (iecm_is_queue_model_split() && vport->num_rxq != ...) {

> +			err = -EINVAL;
> +			goto error;
> +		}
> +	} else {

Don't forget to convert this then into `!split + ...`, either with
'else' or not.

> +		if (vport->num_rxq != k - vport->num_txq) {
> +			err = -EINVAL;
> +			goto error;
> +		}
> +	}
> +
> +	/* Chunk up the vector info into multiple messages */
> +	config_data_size = sizeof(struct virtchnl2_queue_vector_maps);
> +	chunk_size = sizeof(struct virtchnl2_queue_vector);
> +
> +	num_chunks = IECM_NUM_CHUNKS_PER_MSG(config_data_size, chunk_size) + 1;
> +	if (num_q < num_chunks)
> +		num_chunks = num_q;
> +
> +	num_msgs = num_q / num_chunks;
> +	if (num_q % num_chunks)
> +		num_msgs++;
> +
> +	for (i = 0, k = 0; i < num_msgs; i++) {
> +		if (!vqvm || alloc) {
> +			buf_size = (chunk_size * (num_chunks - 1)) +
> +					config_data_size;
> +			kfree(vqvm);
> +			vqvm = kzalloc(buf_size, GFP_KERNEL);
> +			if (!vqvm) {
> +				err = -ENOMEM;
> +				goto error;
> +			}
> +		} else {
> +			memset(vqvm, 0, buf_size);
> +		}
> +		vqvm->vport_id = cpu_to_le32(vport->vport_id);
> +		vqvm->num_qv_maps = cpu_to_le16(num_chunks);
> +		memcpy(vqvm->qv_maps, &vqv[k], chunk_size * num_chunks);
> +
> +		if (map) {
> +			err = iecm_send_mb_msg(adapter,
> +					       VIRTCHNL2_OP_MAP_QUEUE_VECTOR,
> +					       buf_size, (u8 *)vqvm);
> +			if (!err)
> +				err = iecm_wait_for_event(adapter,
> +							  IECM_VC_MAP_IRQ,
> +							  IECM_VC_MAP_IRQ_ERR);
> +		} else {
> +			err = iecm_send_mb_msg(adapter,
> +					       VIRTCHNL2_OP_UNMAP_QUEUE_VECTOR,
> +					       buf_size, (u8 *)vqvm);
> +			if (!err)
> +				err =
> +				iecm_min_wait_for_event(adapter,
> +							IECM_VC_UNMAP_IRQ,
> +							IECM_VC_UNMAP_IRQ_ERR);
> +		}
> +		if (err)
> +			goto mbx_error;
> +
> +		k += num_chunks;
> +		num_q -= num_chunks;
> +		if (num_q < num_chunks) {
> +			num_chunks = num_q;
> +			alloc = true;
> +		}
> +	}
> +mbx_error:
> +	kfree(vqvm);
> +error:
> +	kfree(vqv);
> +	return err;
> +}
> +EXPORT_SYMBOL(iecm_send_map_unmap_queue_vector_msg);
> +
> +/**
> + * iecm_send_enable_queues_msg - send enable queues virtchnl message
> + * @vport: Virtual port private data structure
> + *
> + * Will send enable queues virtchnl message.  Returns 0 on success, negative on
> + * failure.
> + */
> +static int iecm_send_enable_queues_msg(struct iecm_vport *vport)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	int err;
> +
> +	err = iecm_send_ena_dis_queues_msg(vport,
> +					   VIRTCHNL2_OP_ENABLE_QUEUES);
> +	if (err)
> +		return err;
> +
> +	return iecm_wait_for_event(adapter, IECM_VC_ENA_QUEUES,
> +				   IECM_VC_ENA_QUEUES_ERR);
> +}
> +
> +/**
> + * iecm_send_disable_queues_msg - send disable queues virtchnl message
> + * @vport: Virtual port private data structure
> + *
> + * Will send disable queues virtchnl message.  Returns 0 on success, negative
> + * on failure.
> + */
> +static int iecm_send_disable_queues_msg(struct iecm_vport *vport)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	int err;
> +
> +	err = iecm_send_ena_dis_queues_msg(vport,
> +					   VIRTCHNL2_OP_DISABLE_QUEUES);
> +	if (err)
> +		return err;
> +
> +	err = iecm_min_wait_for_event(adapter, IECM_VC_DIS_QUEUES,
> +				      IECM_VC_DIS_QUEUES_ERR);
> +	if (err)
> +		return err;
> +
> +	return iecm_wait_for_marker_event(vport);
> +}
> +
> +/**
> + * iecm_convert_reg_to_queue_chunks - Copy queue chunk information to the right
> + * structure
> + * @dchunks: Destination chunks to store data to
> + * @schunks: Source chunks to copy data from
> + * @num_chunks: number of chunks to copy
> + */
> +static void
> +iecm_convert_reg_to_queue_chunks(struct virtchnl2_queue_chunk *dchunks,
> +				 struct virtchnl2_queue_reg_chunk *schunks,
> +				 u16 num_chunks)
> +{
> +	u16 i;
> +
> +	for (i = 0; i < num_chunks; i++) {
> +		dchunks[i].type = schunks[i].type;
> +		dchunks[i].start_queue_id = schunks[i].start_queue_id;
> +		dchunks[i].num_queues = schunks[i].num_queues;
> +	}
> +}
> +
> +/**
> + * iecm_send_delete_queues_msg - send delete queues virtchnl message
> + * @vport: Virtual port private data structure
> + *
> + * Will send delete queues virtchnl message. Return 0 on success, negative on
> + * failure.
> + */
> +int iecm_send_delete_queues_msg(struct iecm_vport *vport)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct virtchnl2_create_vport *vport_params;
> +	struct virtchnl2_queue_reg_chunks *chunks;
> +	struct virtchnl2_del_ena_dis_queues *eq;
> +	int buf_size, err;
> +	u16 num_chunks;
> +
> +	if (vport->adapter->config_data.req_qs_chunks) {
> +		struct virtchnl2_add_queues *vc_aq =
> +			(struct virtchnl2_add_queues *)
> +			vport->adapter->config_data.req_qs_chunks;
> +		chunks = &vc_aq->chunks;
> +	} else {
> +		vport_params = (struct virtchnl2_create_vport *)
> +				vport->adapter->vport_params_recvd[0];
> +		 chunks = &vport_params->chunks;
> +	}
> +
> +	num_chunks = le16_to_cpu(chunks->num_chunks);
> +	buf_size = sizeof(struct virtchnl2_del_ena_dis_queues) +
> +			  (sizeof(struct virtchnl2_queue_chunk) *
> +			  (num_chunks - 1));
> +
> +	eq = kzalloc(buf_size, GFP_KERNEL);
> +	if (!eq)
> +		return -ENOMEM;
> +
> +	eq->vport_id = cpu_to_le32(vport->vport_id);
> +	eq->chunks.num_chunks = cpu_to_le16(num_chunks);
> +
> +	iecm_convert_reg_to_queue_chunks(eq->chunks.chunks, chunks->chunks,
> +					 num_chunks);
> +
> +	err = iecm_send_mb_msg(vport->adapter, VIRTCHNL2_OP_DEL_QUEUES,
> +			       buf_size, (u8 *)eq);
> +	if (err)
> +		goto error;
> +
> +	err = iecm_min_wait_for_event(adapter, IECM_VC_DEL_QUEUES,
> +				      IECM_VC_DEL_QUEUES_ERR);
> +error:
> +	kfree(eq);
> +	return err;
> +}
> +
> +/**
> + * iecm_send_config_queues_msg - Send config queues virtchnl message
> + * @vport: Virtual port private data structure
> + *
> + * Will send config queues virtchnl message. Returns 0 on success, negative on
> + * failure.
> + */
> +static int iecm_send_config_queues_msg(struct iecm_vport *vport)
> +{
> +	int err;
> +
> +	err = iecm_send_config_tx_queues_msg(vport);
> +	if (err)
> +		return err;
> +
> +	return iecm_send_config_rx_queues_msg(vport);
> +}
> +
> +/**
> + * iecm_send_add_queues_msg - Send virtchnl add queues message
> + * @vport: Virtual port private data structure
> + * @num_tx_q: number of transmit queues
> + * @num_complq: number of transmit completion queues
> + * @num_rx_q: number of receive queues
> + * @num_rx_bufq: number of receive buffer queues
> + *
> + * Returns 0 on success, negative on failure.
> + */
> +int iecm_send_add_queues_msg(struct iecm_vport *vport, u16 num_tx_q,
> +			     u16 num_complq, u16 num_rx_q, u16 num_rx_bufq)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct virtchnl2_add_queues aq = {0};
> +	struct virtchnl2_add_queues *vc_msg;
> +	int size, err;
> +
> +	vc_msg = (struct virtchnl2_add_queues *)adapter->vc_msg;
> +
> +	aq.vport_id = cpu_to_le32(vport->vport_id);
> +	aq.num_tx_q = cpu_to_le16(num_tx_q);
> +	aq.num_tx_complq = cpu_to_le16(num_complq);
> +	aq.num_rx_q = cpu_to_le16(num_rx_q);
> +	aq.num_rx_bufq = cpu_to_le16(num_rx_bufq);
> +
> +	err = iecm_send_mb_msg(adapter,
> +			       VIRTCHNL2_OP_ADD_QUEUES,
> +			       sizeof(struct virtchnl2_add_queues), (u8 *)&aq);
> +	if (err)
> +		return err;
> +
> +	err = iecm_wait_for_event(adapter, IECM_VC_ADD_QUEUES,
> +				  IECM_VC_ADD_QUEUES_ERR);
> +	if (err)
> +		return err;
> +
> +	kfree(adapter->config_data.req_qs_chunks);
> +	adapter->config_data.req_qs_chunks = NULL;
> +
> +	/* compare vc_msg num queues with vport num queues */
> +	if (le16_to_cpu(vc_msg->num_tx_q) != num_tx_q ||
> +	    le16_to_cpu(vc_msg->num_rx_q) != num_rx_q ||
> +	    le16_to_cpu(vc_msg->num_tx_complq) != num_complq ||
> +	    le16_to_cpu(vc_msg->num_rx_bufq) != num_rx_bufq) {
> +		err = -EINVAL;
> +		goto error;
> +	}
> +
> +	size = sizeof(struct virtchnl2_add_queues) +
> +			((le16_to_cpu(vc_msg->chunks.num_chunks) - 1) *
> +			sizeof(struct virtchnl2_queue_reg_chunk));
> +	adapter->config_data.req_qs_chunks =
> +		kzalloc(size, GFP_KERNEL);
> +	if (!adapter->config_data.req_qs_chunks) {
> +		err = -ENOMEM;
> +		goto error;
> +	}
> +	memcpy(adapter->config_data.req_qs_chunks,
> +	       adapter->vc_msg, size);
> +error:
> +	clear_bit(__IECM_VC_MSG_PENDING, adapter->flags);
> +	return err;
> +}
> +
>  /**
>   * iecm_find_ctlq - Given a type and id, find ctlq info
>   * @hw: hardware struct
> @@ -1217,6 +2219,13 @@ static void iecm_vport_init(struct iecm_vport *vport,
>  	/*Initialize Tx and Rx profiles for Dynamic Interrupt Moderation */
>  	memcpy(vport->rx_itr_profile, rx_itr, IECM_DIM_PROFILE_SLOTS);
>  	memcpy(vport->tx_itr_profile, tx_itr, IECM_DIM_PROFILE_SLOTS);
> +
> +	iecm_vport_set_hsplit(vport, true);
> +
> +	iecm_vport_init_num_qs(vport, vport_msg);
> +	iecm_vport_calc_num_q_desc(vport);
> +	iecm_vport_calc_num_q_groups(vport);
> +	iecm_vport_calc_num_q_vec(vport);
>  }
>  
>  /**
> @@ -1316,8 +2325,82 @@ static int
>  __iecm_vport_queue_ids_init(struct iecm_vport *vport, u32 *qids,
>  			    int num_qids, u32 q_type)
>  {
> -	/* stub */
> -	return 0;
> +	struct iecm_queue *q;
> +	int i, j, k = 0;
> +
> +	switch (q_type) {
> +	case VIRTCHNL2_QUEUE_TYPE_TX:
> +		for (i = 0; i < vport->num_txq_grp; i++) {
> +			struct iecm_txq_group *tx_qgrp = &vport->txq_grps[i];
> +
> +			for (j = 0; j < tx_qgrp->num_txq; j++) {
> +				if (k < num_qids) {
> +					tx_qgrp->txqs[j]->q_id = qids[k];
> +					tx_qgrp->txqs[j]->q_type =
> +						VIRTCHNL2_QUEUE_TYPE_TX;
> +					k++;
> +				} else {
> +					break;
> +				}
> +			}
> +		}
> +		break;
> +	case VIRTCHNL2_QUEUE_TYPE_RX:
> +		for (i = 0; i < vport->num_rxq_grp; i++) {
> +			struct iecm_rxq_group *rx_qgrp = &vport->rxq_grps[i];
> +			int num_rxq;
> +
> +			if (iecm_is_queue_model_split(vport->rxq_model))
> +				num_rxq = rx_qgrp->splitq.num_rxq_sets;
> +			else
> +				num_rxq = rx_qgrp->singleq.num_rxq;
> +
> +			for (j = 0; j < num_rxq && k < num_qids; j++, k++) {
> +				if (iecm_is_queue_model_split(vport->rxq_model))
> +					q = &rx_qgrp->splitq.rxq_sets[j]->rxq;
> +				else
> +					q = rx_qgrp->singleq.rxqs[j];
> +				q->q_id = qids[k];
> +				q->q_type = VIRTCHNL2_QUEUE_TYPE_RX;
> +			}
> +		}
> +		break;
> +	case VIRTCHNL2_QUEUE_TYPE_TX_COMPLETION:
> +		for (i = 0; i < vport->num_txq_grp; i++) {
> +			struct iecm_txq_group *tx_qgrp = &vport->txq_grps[i];
> +
> +			if (k < num_qids) {
> +				tx_qgrp->complq->q_id = qids[k];
> +				tx_qgrp->complq->q_type =
> +					VIRTCHNL2_QUEUE_TYPE_TX_COMPLETION;
> +				k++;
> +			} else {
> +				break;
> +			}
> +		}
> +		break;
> +	case VIRTCHNL2_QUEUE_TYPE_RX_BUFFER:
> +		for (i = 0; i < vport->num_rxq_grp; i++) {
> +			struct iecm_rxq_group *rx_qgrp = &vport->rxq_grps[i];
> +
> +			for (j = 0; j < vport->num_bufqs_per_qgrp; j++) {
> +				if (k < num_qids) {
> +					q = &rx_qgrp->splitq.bufq_sets[j].bufq;
> +					q->q_id = qids[k];
> +					q->q_type =
> +						VIRTCHNL2_QUEUE_TYPE_RX_BUFFER;
> +					k++;
> +				} else {
> +					break;
> +				}
> +			}
> +		}
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	return k;
>  }
>  
>  /**
> @@ -1425,6 +2508,20 @@ static bool iecm_is_capability_ena(struct iecm_adapter *adapter, bool all,
>  		return !!(*cap_field & flag);
>  }
>  
> +/**
> + * iecm_get_reserved_vectors - Default implementation to get reserved vectors
> + * @adapter: Private data struct
> + *
> + * Return number of vectors reserved
> + */
> +static u16 iecm_get_reserved_vectors(struct iecm_adapter *adapter)
> +{
> +	struct virtchnl2_get_capabilities *caps;
> +
> +	caps = (struct virtchnl2_get_capabilities *)adapter->caps;
> +	return le16_to_cpu(caps->num_allocated_vectors);
> +}
> +
>  /**
>   * iecm_vc_ops_init - Initialize virtchnl common api
>   * @adapter: Driver specific private structure
> @@ -1441,16 +2538,16 @@ void iecm_vc_ops_init(struct iecm_adapter *adapter)
>  	vc_ops->vport_queue_ids_init = iecm_vport_queue_ids_init;
>  	vc_ops->get_caps = iecm_send_get_caps_msg;
>  	vc_ops->is_cap_ena = iecm_is_capability_ena;
> -	vc_ops->get_reserved_vecs = NULL;
> -	vc_ops->config_queues = NULL;
> -	vc_ops->enable_queues = NULL;
> -	vc_ops->disable_queues = NULL;
> -	vc_ops->add_queues = NULL;
> -	vc_ops->delete_queues = NULL;
> -	vc_ops->irq_map_unmap = NULL;
> -	vc_ops->enable_vport = NULL;
> -	vc_ops->disable_vport = NULL;
> -	vc_ops->destroy_vport = NULL;
> +	vc_ops->get_reserved_vecs = iecm_get_reserved_vectors;
> +	vc_ops->config_queues = iecm_send_config_queues_msg;
> +	vc_ops->enable_queues = iecm_send_enable_queues_msg;
> +	vc_ops->disable_queues = iecm_send_disable_queues_msg;
> +	vc_ops->add_queues = iecm_send_add_queues_msg;
> +	vc_ops->delete_queues = iecm_send_delete_queues_msg;
> +	vc_ops->irq_map_unmap = iecm_send_map_unmap_queue_vector_msg;
> +	vc_ops->enable_vport = iecm_send_enable_vport_msg;
> +	vc_ops->disable_vport = iecm_send_disable_vport_msg;
> +	vc_ops->destroy_vport = iecm_send_destroy_vport_msg;
>  	vc_ops->get_ptype = NULL;
>  	vc_ops->get_set_rss_key = NULL;
>  	vc_ops->get_set_rss_lut = NULL;

Forgot to mention earlier, any reason to not declare this ops as
static const and just assign a pointer then? I don't see any
alternations here to e.g. fill callbacks with different functions
or so.

> diff --git a/drivers/net/ethernet/intel/include/iecm.h b/drivers/net/ethernet/intel/include/iecm.h
> index 994664dfe419..8dd6272db7d3 100644
> --- a/drivers/net/ethernet/intel/include/iecm.h
> +++ b/drivers/net/ethernet/intel/include/iecm.h
> @@ -432,6 +432,8 @@ struct iecm_adapter {
>  	u16 num_alloc_vport;
>  	u16 next_vport;		/* Next free slot in pf->vport[] - 0-based! */
>  
> +	u16 max_queue_limit;	/* Max number of queues user can request */
> +
>  	struct delayed_work init_task; /* delayed init task */
>  	struct workqueue_struct *init_wq;
>  	u32 mb_wait_count;
> @@ -510,6 +512,12 @@ static inline bool __iecm_is_cap_ena(struct iecm_adapter *adapter, bool all,
>  	return adapter->dev_ops.vc_ops.is_cap_ena(adapter, all, field, flag);
>  }
>  
> +#define IECM_CAP_HSPLIT (\
> +	VIRTCHNL2_CAP_RX_HSPLIT_AT_L2   |\
> +	VIRTCHNL2_CAP_RX_HSPLIT_AT_L3   |\
> +	VIRTCHNL2_CAP_RX_HSPLIT_AT_L4V4 |\
> +	VIRTCHNL2_CAP_RX_HSPLIT_AT_L4V6)
> +
>  /**
>   * iecm_is_reset_detected - check if we were reset at some point
>   * @adapter: driver specific private structure
> @@ -530,6 +538,8 @@ int iecm_init_dflt_mbx(struct iecm_adapter *adapter);
>  void iecm_deinit_dflt_mbx(struct iecm_adapter *adapter);
>  void iecm_vc_ops_init(struct iecm_adapter *adapter);
>  int iecm_vc_core_init(struct iecm_adapter *adapter, int *vport_id);
> +int iecm_get_reg_intr_vecs(struct iecm_vport *vport,
> +			   struct iecm_vec_regs *reg_vals, int num_vecs);
>  int iecm_wait_for_event(struct iecm_adapter *adapter,
>  			enum iecm_vport_vc_state state,
>  			enum iecm_vport_vc_state err_check);
> @@ -537,6 +547,14 @@ int iecm_min_wait_for_event(struct iecm_adapter *adapter,
>  			    enum iecm_vport_vc_state state,
>  			    enum iecm_vport_vc_state err_check);
>  int iecm_send_get_caps_msg(struct iecm_adapter *adapter);
> +int iecm_send_delete_queues_msg(struct iecm_vport *vport);
> +int iecm_send_add_queues_msg(struct iecm_vport *vport, u16 num_tx_q,
> +			     u16 num_complq, u16 num_rx_q, u16 num_rx_bufq);
> +int iecm_send_config_tx_queues_msg(struct iecm_vport *vport);
> +int iecm_send_config_rx_queues_msg(struct iecm_vport *vport);
> +int iecm_send_enable_vport_msg(struct iecm_vport *vport);
> +int iecm_send_disable_vport_msg(struct iecm_vport *vport);
> +int iecm_send_destroy_vport_msg(struct iecm_vport *vport);
>  int iecm_vport_params_buf_alloc(struct iecm_adapter *adapter);
>  void iecm_vport_params_buf_rel(struct iecm_adapter *adapter);
>  int iecm_get_vec_ids(struct iecm_adapter *adapter,
> @@ -546,7 +564,11 @@ int iecm_recv_mb_msg(struct iecm_adapter *adapter, enum virtchnl_ops op,
>  		     void *msg, int msg_size);
>  int iecm_send_mb_msg(struct iecm_adapter *adapter, enum virtchnl_ops op,
>  		     u16 msg_size, u8 *msg);
> +void iecm_vport_set_hsplit(struct iecm_vport *vport, bool ena);
> +int iecm_send_enable_channels_msg(struct iecm_vport *vport);
> +int iecm_send_disable_channels_msg(struct iecm_vport *vport);
>  int iecm_set_msg_pending(struct iecm_adapter *adapter,
>  			 struct iecm_ctlq_msg *ctlq_msg,
>  			 enum iecm_vport_vc_state err_enum);
> +int iecm_send_map_unmap_queue_vector_msg(struct iecm_vport *vport, bool map);
>  #endif /* !_IECM_H_ */
> diff --git a/drivers/net/ethernet/intel/include/iecm_txrx.h b/drivers/net/ethernet/intel/include/iecm_txrx.h
> index e1348011c991..448cae0bf6e7 100644
> --- a/drivers/net/ethernet/intel/include/iecm_txrx.h
> +++ b/drivers/net/ethernet/intel/include/iecm_txrx.h
> @@ -81,6 +81,22 @@
>  
>  #define IECM_TX_COMPLQ_CLEAN_BUDGET	256
>  
> +enum iecm_queue_flags_t {
> +	__IECM_Q_GEN_CHK,
> +	__IECM_RFLQ_GEN_CHK,
> +	__IECM_Q_FLOW_SCH_EN,
> +	__IECM_Q_ETF_EN,
> +	__IECM_Q_SW_MARKER,
> +	__IECM_Q_VLAN_TAG_LOC_L2TAG1,
> +	__IECM_Q_VLAN_TAG_LOC_L2TAG2,
> +	__IECM_Q_FLAGS_NBITS,
> +};
> +
> +struct iecm_vec_regs {
> +	u32 dyn_ctl_reg;
> +	u32 itrn_reg;
> +};
> +
>  struct iecm_intr_reg {
>  	u32 dyn_ctl;
>  	u32 dyn_ctl_intena_m;
> @@ -122,6 +138,186 @@ struct iecm_q_vector {
>  	char name[IECM_INT_NAME_STR_LEN];
>  };
>  
> +struct iecm_rx_queue_stats {
> +	u64 packets;
> +	u64 bytes;
> +	u64 rsc_pkts;
> +};
> +
> +struct iecm_tx_queue_stats {
> +	u64 packets;
> +	u64 bytes;
> +	u64 lso_pkts;
> +};
> +
> +union iecm_queue_stats {
> +	struct iecm_rx_queue_stats rx;
> +	struct iecm_tx_queue_stats tx;
> +};
> +
> +/* queue associated with a vport */
> +struct iecm_queue {
> +	struct device *dev;		/* Used for DMA mapping */
> +	struct iecm_vport *vport;	/* Backreference to associated vport */
> +	union {
> +		struct iecm_txq_group *txq_grp;
> +		struct iecm_rxq_group *rxq_grp;
> +	};
> +	/* bufq: Used as group id, either 0 or 1, on clean Buf Q uses this
> +	 *       index to determine which group of refill queues to clean.
> +	 *       Bufqs are use in splitq only.
> +	 * txq: Index to map between Tx Q group and hot path Tx ptrs stored in
> +	 *      vport.  Used in both single Q/split Q
> +	 * rxq: Index to total rxq across groups, used for skb reporting
> +	 */
> +	u16 idx;
> +	/* Used for both Q models single and split. In split Q model relevant
> +	 * only to Tx Q and Rx Q
> +	 */
> +	u8 __iomem *tail;
> +	/* Used in both single and split Q.  In single Q, Tx Q uses tx_buf and
> +	 * Rx Q uses rx_buf.  In split Q, Tx Q uses tx_buf, Rx Q uses skb, and
> +	 * Buf Q uses rx_buf.
> +	 */
> +	union {
> +		struct iecm_tx_buf *tx_buf;
> +		struct {
> +			struct iecm_rx_buf *buf;
> +			struct iecm_dma_mem **hdr_buf;
> +		} rx_buf;
> +		struct sk_buff *skb;
> +	};
> +	u16 q_type;
> +	/* Queue id(Tx/Tx compl/Rx/Bufq) */
> +	u32 q_id;
> +	u16 desc_count;		/* Number of descriptors */
> +
> +	/* Relevant in both split & single Tx Q & Buf Q*/
> +	u16 next_to_use;
> +	/* In split q model only relevant for Tx Compl Q and Rx Q */
> +	u16 next_to_clean;	/* used in interrupt processing */
> +	/* Used only for Rx. In split Q model only relevant to Rx Q */
> +	u16 next_to_alloc;
> +	/* Generation bit check stored, as HW flips the bit at Queue end */
> +	DECLARE_BITMAP(flags, __IECM_Q_FLAGS_NBITS);
> +
> +	union iecm_queue_stats q_stats;
> +	struct u64_stats_sync stats_sync;
> +
> +	bool rx_hsplit_en;
> +
> +	u16 rx_hbuf_size;	/* Header buffer size */
> +	u16 rx_buf_size;
> +	u16 rx_max_pkt_size;
> +	u16 rx_buf_stride;
> +	u8 rx_buffer_low_watermark;
> +	u64 rxdids;
> +	/* Used for both Q models single and split. In split Q model relavant
> +	 * only to Tx compl Q and Rx compl Q
> +	 */
> +	struct iecm_q_vector *q_vector;	/* Backreference to associated vector */
> +	unsigned int size;		/* length of descriptor ring in bytes */
> +	dma_addr_t dma;			/* physical address of ring */
> +	void *desc_ring;		/* Descriptor ring memory */
> +
> +	u16 tx_buf_key;			/* 16 bit unique "identifier" (index)
> +					 * to be used as the completion tag when
> +					 * queue is using flow based scheduling
> +					 */
> +	u16 tx_max_bufs;		/* Max buffers that can be transmitted
> +					 * with scatter-gather
> +					 */
> +	DECLARE_HASHTABLE(sched_buf_hash, 12);
> +} ____cacheline_internodealigned_in_smp;
> +
> +/* Software queues are used in splitq mode to manage buffers between rxq
> + * producer and the bufq consumer.  These are required in order to maintain a
> + * lockless buffer management system and are strictly software only constructs.
> + */
> +struct iecm_sw_queue {
> +	u16 next_to_clean ____cacheline_aligned_in_smp;
> +	u16 next_to_alloc ____cacheline_aligned_in_smp;
> +	u16 next_to_use ____cacheline_aligned_in_smp;
> +	DECLARE_BITMAP(flags, __IECM_Q_FLAGS_NBITS)
> +		____cacheline_aligned_in_smp;
> +	u16 *ring ____cacheline_aligned_in_smp;

This will result in this part being FIVE cachelines long for
3 * 2 + 8 + 8 = 22 bytes, i.e. 320 bytes for 22!
Just making the entire structure cacheline-aligned after its
declaration is enough, these ones are not even an overkill,
it's an overslaughter.

> +	u16 desc_count;
> +	u16 buf_size;
> +	struct device *dev;
> +} ____cacheline_internodealigned_in_smp;
> +
> +/* Splitq only.  iecm_rxq_set associates an rxq with at an array of refillqs.
> + * Each rxq needs a refillq to return used buffers back to the respective bufq.
> + * Bufqs then clean these refillqs for buffers to give to hardware.
> + */
> +struct iecm_rxq_set {
> +	struct iecm_queue rxq;
> +	/* refillqs assoc with bufqX mapped to this rxq */
> +	struct iecm_sw_queue *refillq0;
> +	struct iecm_sw_queue *refillq1;
> +};
> +
> +/* Splitq only.  iecm_bufq_set associates a bufq to an array of refillqs.
> + * In this bufq_set, there will be one refillq for each rxq in this rxq_group.
> + * Used buffers received by rxqs will be put on refillqs which bufqs will
> + * clean to return new buffers back to hardware.
> + *
> + * Buffers needed by some number of rxqs associated in this rxq_group are
> + * managed by at most two bufqs (depending on performance configuration).
> + */
> +struct iecm_bufq_set {
> +	struct iecm_queue bufq;
> +	/* This is always equal to num_rxq_sets in iecm_rxq_group */
> +	int num_refillqs;
> +	struct iecm_sw_queue *refillqs;
> +};
> +
> +/* In singleq mode, an rxq_group is simply an array of rxqs.  In splitq, a
> + * rxq_group contains all the rxqs, bufqs and refillqs needed to
> + * manage buffers in splitq mode.
> + */
> +struct iecm_rxq_group {
> +	struct iecm_vport *vport; /* back pointer */
> +
> +	union {
> +		struct {
> +			int num_rxq;
> +			/* store queue pointers */
> +			struct iecm_queue *rxqs[IECM_LARGE_MAX_Q];
> +		} singleq;
> +		struct {
> +			int num_rxq_sets;
> +			/* store queue pointers */
> +			struct iecm_rxq_set *rxq_sets[IECM_LARGE_MAX_Q];
> +			struct iecm_bufq_set *bufq_sets;
> +		} splitq;
> +	};
> +};
> +
> +/* Between singleq and splitq, a txq_group is largely the same except for the
> + * complq.  In splitq a single complq is responsible for handling completions
> + * for some number of txqs associated in this txq_group.
> + */
> +struct iecm_txq_group {
> +	struct iecm_vport *vport; /* back pointer */
> +
> +	int num_txq;
> +	/* store queue pointers */
> +	struct iecm_queue *txqs[IECM_LARGE_MAX_Q];
> +
> +	/* splitq only */
> +	struct iecm_queue *complq;
> +};
> +
> +struct iecm_adapter;
> +
> +void iecm_vport_init_num_qs(struct iecm_vport *vport,
> +			    struct virtchnl2_create_vport *vport_msg);
> +void iecm_vport_calc_num_q_desc(struct iecm_vport *vport);
> +void iecm_vport_calc_total_qs(struct iecm_adapter *adapter,
> +			      struct virtchnl2_create_vport *vport_msg);
> +void iecm_vport_calc_num_q_groups(struct iecm_vport *vport);
> +void iecm_vport_calc_num_q_vec(struct iecm_vport *vport);
>  irqreturn_t
>  iecm_vport_intr_clean_queues(int __always_unused irq, void *data);
>  #endif /* !_IECM_TXRX_H_ */
> -- 
> 2.33.0

Thanks,
Al

^ permalink raw reply	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 07/19] iecm: finish virtchnl messages
  2022-01-28  0:09 ` [Intel-wired-lan] [PATCH net-next 07/19] iecm: finish virtchnl messages Alan Brady
@ 2022-01-28 13:19   ` Alexander Lobakin
  2022-02-02 23:06     ` Brady, Alan
  0 siblings, 1 reply; 89+ messages in thread
From: Alexander Lobakin @ 2022-01-28 13:19 UTC (permalink / raw)
  To: intel-wired-lan

From: Alan Brady <alan.brady@intel.com>
Date: Thu, 27 Jan 2022 16:09:57 -0800

> This adds the rest of the needed virtchnl messages mostly related to
> negotiating ptypes and initializing queue registers.
> 
> Signed-off-by: Phani Burra <phani.r.burra@intel.com>
> Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
> Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
> Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
> Signed-off-by: Alice Michael <alice.michael@intel.com>
> Signed-off-by: Alan Brady <alan.brady@intel.com>
> ---
>  drivers/net/ethernet/intel/iecm/iecm_lib.c    |   21 +-
>  drivers/net/ethernet/intel/iecm/iecm_txrx.c   |  226 +++-
>  .../net/ethernet/intel/iecm/iecm_virtchnl.c   | 1187 ++++++++++++++++-
>  drivers/net/ethernet/intel/include/iecm.h     |   36 +
>  .../net/ethernet/intel/include/iecm_txrx.h    |  198 ++-
>  5 files changed, 1635 insertions(+), 33 deletions(-)
> 
> diff --git a/drivers/net/ethernet/intel/iecm/iecm_lib.c b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> index 4e9cc7f2d138..aab8ee40424e 100644
> --- a/drivers/net/ethernet/intel/iecm/iecm_lib.c
> +++ b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> @@ -10,6 +10,25 @@ const char * const iecm_vport_vc_state_str[] = {
>  };
>  EXPORT_SYMBOL(iecm_vport_vc_state_str);
>  
> +/**
> + * iecm_is_feature_ena - Determine if a particular feature is enabled
> + * @vport: vport to check
> + * @feature: netdev flag to check
> + *
> + * Returns true or false if a particular feature is enabled.
> + */
> +bool iecm_is_feature_ena(struct iecm_vport *vport, netdev_features_t feature)
> +{
> +	bool ena;
> +
> +	switch (feature) {
> +	default:
> +		ena = vport->netdev->features & feature;
> +		break;
> +	}
> +	return ena;
> +}

This makes absolutely no sense, please rewrite to

	return vport->netdev->features & feature;

If it will be expanded later, convert it to a switch-case only then.

> +
>  /**
>   * iecm_cfg_hw - Initialize HW struct
>   * @adapter: adapter to setup hw struct for
> @@ -132,7 +151,7 @@ iecm_vport_alloc(struct iecm_adapter *adapter, int vport_id)
>  	adapter->num_alloc_vport++;
>  
>  	/* Setup default MSIX irq handler for the vport */
> -	vport->irq_q_handler = iecm_vport_intr_clean_queues;
> +	vport->irq_q_handler = NULL;
>  	vport->q_vector_base = IECM_NONQ_VEC;
>  
>  	mutex_init(&vport->stop_mutex);
> diff --git a/drivers/net/ethernet/intel/iecm/iecm_txrx.c b/drivers/net/ethernet/intel/iecm/iecm_txrx.c
> index 2dfb0be002e3..bd0cfd89bf03 100644
> --- a/drivers/net/ethernet/intel/iecm/iecm_txrx.c
> +++ b/drivers/net/ethernet/intel/iecm/iecm_txrx.c
> @@ -3,22 +3,220 @@
>  
>  #include "iecm.h"
>  
> -/**
> - * iecm_vport_intr_clean_queues - MSIX mode Interrupt Handler
> - * @irq: interrupt number
> - * @data: pointer to a q_vector
> - *
> - */
> -irqreturn_t
> -iecm_vport_intr_clean_queues(int __always_unused irq, void *data)
> -{
> -	struct iecm_q_vector *q_vector = (struct iecm_q_vector *)data;
> +const struct iecm_rx_ptype_decoded iecm_ptype_lookup[IECM_RX_MAX_PTYPE] = {
> +	/* ptype indices are dynamic and package dependent. Indices represented
> +	 * in this lookup table are for reference and will be replaced by the
> +	 * values which CP sends. Also these values are static for older
> +	 * versions of virtchnl and if VIRTCHNL2_CAP_PTYPE is not set in
> +	 * virtchnl2_get_capabilities.
> +	 */
> +	/* L2 Packet types */
> +	IECM_PTT_UNUSED_ENTRY(0),
> +	IECM_PTT(1,  L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY2),
> +	IECM_PTT(2,  L2, NONE, NOF, NONE, NONE, NOF, TS,   PAY2),
> +	IECM_PTT(3,  L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY2),
> +	IECM_PTT_UNUSED_ENTRY(4),
> +	IECM_PTT_UNUSED_ENTRY(5),
> +	IECM_PTT(6,  L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY2),
> +	IECM_PTT(7,  L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY2),
> +	IECM_PTT_UNUSED_ENTRY(8),
> +	IECM_PTT_UNUSED_ENTRY(9),
> +	IECM_PTT(10, L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY2),
> +	IECM_PTT(11, L2, NONE, NOF, NONE, NONE, NOF, NONE, NONE),
> +	IECM_PTT(12, L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY3),
> +	IECM_PTT(13, L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY3),
> +	IECM_PTT(14, L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY3),
> +	IECM_PTT(15, L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY3),
> +	IECM_PTT(16, L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY3),
> +	IECM_PTT(17, L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY3),
> +	IECM_PTT(18, L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY3),
> +	IECM_PTT(19, L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY3),
> +	IECM_PTT(20, L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY3),
> +	IECM_PTT(21, L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY3),
>  
> -	q_vector->total_events++;
> -	napi_schedule(&q_vector->napi);
> +	/* Non Tunneled IPv4 */
> +	IECM_PTT(22, IP, IPV4, FRG, NONE, NONE, NOF, NONE, PAY3),
> +	IECM_PTT(23, IP, IPV4, NOF, NONE, NONE, NOF, NONE, PAY3),
> +	IECM_PTT(24, IP, IPV4, NOF, NONE, NONE, NOF, UDP,  PAY4),
> +	IECM_PTT_UNUSED_ENTRY(25),
> +	IECM_PTT(26, IP, IPV4, NOF, NONE, NONE, NOF, TCP,  PAY4),
> +	IECM_PTT(27, IP, IPV4, NOF, NONE, NONE, NOF, SCTP, PAY4),
> +	IECM_PTT(28, IP, IPV4, NOF, NONE, NONE, NOF, ICMP, PAY4),
>  
> -	return IRQ_HANDLED;
> -}
> +	/* IPv4 --> IPv4 */
> +	IECM_PTT(29, IP, IPV4, NOF, IP_IP, IPV4, FRG, NONE, PAY3),
> +	IECM_PTT(30, IP, IPV4, NOF, IP_IP, IPV4, NOF, NONE, PAY3),
> +	IECM_PTT(31, IP, IPV4, NOF, IP_IP, IPV4, NOF, UDP,  PAY4),
> +	IECM_PTT_UNUSED_ENTRY(32),
> +	IECM_PTT(33, IP, IPV4, NOF, IP_IP, IPV4, NOF, TCP,  PAY4),
> +	IECM_PTT(34, IP, IPV4, NOF, IP_IP, IPV4, NOF, SCTP, PAY4),
> +	IECM_PTT(35, IP, IPV4, NOF, IP_IP, IPV4, NOF, ICMP, PAY4),
> +
> +	/* IPv4 --> IPv6 */
> +	IECM_PTT(36, IP, IPV4, NOF, IP_IP, IPV6, FRG, NONE, PAY3),
> +	IECM_PTT(37, IP, IPV4, NOF, IP_IP, IPV6, NOF, NONE, PAY3),
> +	IECM_PTT(38, IP, IPV4, NOF, IP_IP, IPV6, NOF, UDP,  PAY4),
> +	IECM_PTT_UNUSED_ENTRY(39),
> +	IECM_PTT(40, IP, IPV4, NOF, IP_IP, IPV6, NOF, TCP,  PAY4),
> +	IECM_PTT(41, IP, IPV4, NOF, IP_IP, IPV6, NOF, SCTP, PAY4),
> +	IECM_PTT(42, IP, IPV4, NOF, IP_IP, IPV6, NOF, ICMP, PAY4),
> +
> +	/* IPv4 --> GRE/NAT */
> +	IECM_PTT(43, IP, IPV4, NOF, IP_GRENAT, NONE, NOF, NONE, PAY3),
> +
> +	/* IPv4 --> GRE/NAT --> IPv4 */
> +	IECM_PTT(44, IP, IPV4, NOF, IP_GRENAT, IPV4, FRG, NONE, PAY3),
> +	IECM_PTT(45, IP, IPV4, NOF, IP_GRENAT, IPV4, NOF, NONE, PAY3),
> +	IECM_PTT(46, IP, IPV4, NOF, IP_GRENAT, IPV4, NOF, UDP,  PAY4),
> +	IECM_PTT_UNUSED_ENTRY(47),
> +	IECM_PTT(48, IP, IPV4, NOF, IP_GRENAT, IPV4, NOF, TCP,  PAY4),
> +	IECM_PTT(49, IP, IPV4, NOF, IP_GRENAT, IPV4, NOF, SCTP, PAY4),
> +	IECM_PTT(50, IP, IPV4, NOF, IP_GRENAT, IPV4, NOF, ICMP, PAY4),
> +
> +	/* IPv4 --> GRE/NAT --> IPv6 */
> +	IECM_PTT(51, IP, IPV4, NOF, IP_GRENAT, IPV6, FRG, NONE, PAY3),
> +	IECM_PTT(52, IP, IPV4, NOF, IP_GRENAT, IPV6, NOF, NONE, PAY3),
> +	IECM_PTT(53, IP, IPV4, NOF, IP_GRENAT, IPV6, NOF, UDP,  PAY4),
> +	IECM_PTT_UNUSED_ENTRY(54),
> +	IECM_PTT(55, IP, IPV4, NOF, IP_GRENAT, IPV6, NOF, TCP,  PAY4),
> +	IECM_PTT(56, IP, IPV4, NOF, IP_GRENAT, IPV6, NOF, SCTP, PAY4),
> +	IECM_PTT(57, IP, IPV4, NOF, IP_GRENAT, IPV6, NOF, ICMP, PAY4),
> +
> +	/* IPv4 --> GRE/NAT --> MAC */
> +	IECM_PTT(58, IP, IPV4, NOF, IP_GRENAT_MAC, NONE, NOF, NONE, PAY3),
> +
> +	/* IPv4 --> GRE/NAT --> MAC --> IPv4 */
> +	IECM_PTT(59, IP, IPV4, NOF, IP_GRENAT_MAC, IPV4, FRG, NONE, PAY3),
> +	IECM_PTT(60, IP, IPV4, NOF, IP_GRENAT_MAC, IPV4, NOF, NONE, PAY3),
> +	IECM_PTT(61, IP, IPV4, NOF, IP_GRENAT_MAC, IPV4, NOF, UDP,  PAY4),
> +	IECM_PTT_UNUSED_ENTRY(62),
> +	IECM_PTT(63, IP, IPV4, NOF, IP_GRENAT_MAC, IPV4, NOF, TCP,  PAY4),
> +	IECM_PTT(64, IP, IPV4, NOF, IP_GRENAT_MAC, IPV4, NOF, SCTP, PAY4),
> +	IECM_PTT(65, IP, IPV4, NOF, IP_GRENAT_MAC, IPV4, NOF, ICMP, PAY4),
> +
> +	/* IPv4 --> GRE/NAT -> MAC --> IPv6 */
> +	IECM_PTT(66, IP, IPV4, NOF, IP_GRENAT_MAC, IPV6, FRG, NONE, PAY3),
> +	IECM_PTT(67, IP, IPV4, NOF, IP_GRENAT_MAC, IPV6, NOF, NONE, PAY3),
> +	IECM_PTT(68, IP, IPV4, NOF, IP_GRENAT_MAC, IPV6, NOF, UDP,  PAY4),
> +	IECM_PTT_UNUSED_ENTRY(69),
> +	IECM_PTT(70, IP, IPV4, NOF, IP_GRENAT_MAC, IPV6, NOF, TCP,  PAY4),
> +	IECM_PTT(71, IP, IPV4, NOF, IP_GRENAT_MAC, IPV6, NOF, SCTP, PAY4),
> +	IECM_PTT(72, IP, IPV4, NOF, IP_GRENAT_MAC, IPV6, NOF, ICMP, PAY4),
> +
> +	/* IPv4 --> GRE/NAT --> MAC/VLAN */
> +	IECM_PTT(73, IP, IPV4, NOF, IP_GRENAT_MAC_VLAN, NONE, NOF, NONE, PAY3),
> +
> +	/* IPv4 ---> GRE/NAT -> MAC/VLAN --> IPv4 */
> +	IECM_PTT(74, IP, IPV4, NOF, IP_GRENAT_MAC_VLAN, IPV4, FRG, NONE, PAY3),
> +	IECM_PTT(75, IP, IPV4, NOF, IP_GRENAT_MAC_VLAN, IPV4, NOF, NONE, PAY3),
> +	IECM_PTT(76, IP, IPV4, NOF, IP_GRENAT_MAC_VLAN, IPV4, NOF, UDP,  PAY4),
> +	IECM_PTT_UNUSED_ENTRY(77),
> +	IECM_PTT(78, IP, IPV4, NOF, IP_GRENAT_MAC_VLAN, IPV4, NOF, TCP,  PAY4),
> +	IECM_PTT(79, IP, IPV4, NOF, IP_GRENAT_MAC_VLAN, IPV4, NOF, SCTP, PAY4),
> +	IECM_PTT(80, IP, IPV4, NOF, IP_GRENAT_MAC_VLAN, IPV4, NOF, ICMP, PAY4),
> +
> +	/* IPv4 -> GRE/NAT -> MAC/VLAN --> IPv6 */
> +	IECM_PTT(81, IP, IPV4, NOF, IP_GRENAT_MAC_VLAN, IPV6, FRG, NONE, PAY3),
> +	IECM_PTT(82, IP, IPV4, NOF, IP_GRENAT_MAC_VLAN, IPV6, NOF, NONE, PAY3),
> +	IECM_PTT(83, IP, IPV4, NOF, IP_GRENAT_MAC_VLAN, IPV6, NOF, UDP,  PAY4),
> +	IECM_PTT_UNUSED_ENTRY(84),
> +	IECM_PTT(85, IP, IPV4, NOF, IP_GRENAT_MAC_VLAN, IPV6, NOF, TCP,  PAY4),
> +	IECM_PTT(86, IP, IPV4, NOF, IP_GRENAT_MAC_VLAN, IPV6, NOF, SCTP, PAY4),
> +	IECM_PTT(87, IP, IPV4, NOF, IP_GRENAT_MAC_VLAN, IPV6, NOF, ICMP, PAY4),
> +
> +	/* Non Tunneled IPv6 */
> +	IECM_PTT(88, IP, IPV6, FRG, NONE, NONE, NOF, NONE, PAY3),
> +	IECM_PTT(89, IP, IPV6, NOF, NONE, NONE, NOF, NONE, PAY3),
> +	IECM_PTT(90, IP, IPV6, NOF, NONE, NONE, NOF, UDP,  PAY4),
> +	IECM_PTT_UNUSED_ENTRY(91),
> +	IECM_PTT(92, IP, IPV6, NOF, NONE, NONE, NOF, TCP,  PAY4),
> +	IECM_PTT(93, IP, IPV6, NOF, NONE, NONE, NOF, SCTP, PAY4),
> +	IECM_PTT(94, IP, IPV6, NOF, NONE, NONE, NOF, ICMP, PAY4),
> +
> +	/* IPv6 --> IPv4 */
> +	IECM_PTT(95,  IP, IPV6, NOF, IP_IP, IPV4, FRG, NONE, PAY3),
> +	IECM_PTT(96,  IP, IPV6, NOF, IP_IP, IPV4, NOF, NONE, PAY3),
> +	IECM_PTT(97,  IP, IPV6, NOF, IP_IP, IPV4, NOF, UDP,  PAY4),
> +	IECM_PTT_UNUSED_ENTRY(98),
> +	IECM_PTT(99,  IP, IPV6, NOF, IP_IP, IPV4, NOF, TCP,  PAY4),
> +	IECM_PTT(100, IP, IPV6, NOF, IP_IP, IPV4, NOF, SCTP, PAY4),
> +	IECM_PTT(101, IP, IPV6, NOF, IP_IP, IPV4, NOF, ICMP, PAY4),
> +
> +	/* IPv6 --> IPv6 */
> +	IECM_PTT(102, IP, IPV6, NOF, IP_IP, IPV6, FRG, NONE, PAY3),
> +	IECM_PTT(103, IP, IPV6, NOF, IP_IP, IPV6, NOF, NONE, PAY3),
> +	IECM_PTT(104, IP, IPV6, NOF, IP_IP, IPV6, NOF, UDP,  PAY4),
> +	IECM_PTT_UNUSED_ENTRY(105),
> +	IECM_PTT(106, IP, IPV6, NOF, IP_IP, IPV6, NOF, TCP,  PAY4),
> +	IECM_PTT(107, IP, IPV6, NOF, IP_IP, IPV6, NOF, SCTP, PAY4),
> +	IECM_PTT(108, IP, IPV6, NOF, IP_IP, IPV6, NOF, ICMP, PAY4),
> +
> +	/* IPv6 --> GRE/NAT */
> +	IECM_PTT(109, IP, IPV6, NOF, IP_GRENAT, NONE, NOF, NONE, PAY3),
> +
> +	/* IPv6 --> GRE/NAT -> IPv4 */
> +	IECM_PTT(110, IP, IPV6, NOF, IP_GRENAT, IPV4, FRG, NONE, PAY3),
> +	IECM_PTT(111, IP, IPV6, NOF, IP_GRENAT, IPV4, NOF, NONE, PAY3),
> +	IECM_PTT(112, IP, IPV6, NOF, IP_GRENAT, IPV4, NOF, UDP,  PAY4),
> +	IECM_PTT_UNUSED_ENTRY(113),
> +	IECM_PTT(114, IP, IPV6, NOF, IP_GRENAT, IPV4, NOF, TCP,  PAY4),
> +	IECM_PTT(115, IP, IPV6, NOF, IP_GRENAT, IPV4, NOF, SCTP, PAY4),
> +	IECM_PTT(116, IP, IPV6, NOF, IP_GRENAT, IPV4, NOF, ICMP, PAY4),
> +
> +	/* IPv6 --> GRE/NAT -> IPv6 */
> +	IECM_PTT(117, IP, IPV6, NOF, IP_GRENAT, IPV6, FRG, NONE, PAY3),
> +	IECM_PTT(118, IP, IPV6, NOF, IP_GRENAT, IPV6, NOF, NONE, PAY3),
> +	IECM_PTT(119, IP, IPV6, NOF, IP_GRENAT, IPV6, NOF, UDP,  PAY4),
> +	IECM_PTT_UNUSED_ENTRY(120),
> +	IECM_PTT(121, IP, IPV6, NOF, IP_GRENAT, IPV6, NOF, TCP,  PAY4),
> +	IECM_PTT(122, IP, IPV6, NOF, IP_GRENAT, IPV6, NOF, SCTP, PAY4),
> +	IECM_PTT(123, IP, IPV6, NOF, IP_GRENAT, IPV6, NOF, ICMP, PAY4),
> +
> +	/* IPv6 --> GRE/NAT -> MAC */
> +	IECM_PTT(124, IP, IPV6, NOF, IP_GRENAT_MAC, NONE, NOF, NONE, PAY3),
> +
> +	/* IPv6 --> GRE/NAT -> MAC -> IPv4 */
> +	IECM_PTT(125, IP, IPV6, NOF, IP_GRENAT_MAC, IPV4, FRG, NONE, PAY3),
> +	IECM_PTT(126, IP, IPV6, NOF, IP_GRENAT_MAC, IPV4, NOF, NONE, PAY3),
> +	IECM_PTT(127, IP, IPV6, NOF, IP_GRENAT_MAC, IPV4, NOF, UDP,  PAY4),
> +	IECM_PTT_UNUSED_ENTRY(128),
> +	IECM_PTT(129, IP, IPV6, NOF, IP_GRENAT_MAC, IPV4, NOF, TCP,  PAY4),
> +	IECM_PTT(130, IP, IPV6, NOF, IP_GRENAT_MAC, IPV4, NOF, SCTP, PAY4),
> +	IECM_PTT(131, IP, IPV6, NOF, IP_GRENAT_MAC, IPV4, NOF, ICMP, PAY4),
> +
> +	/* IPv6 --> GRE/NAT -> MAC -> IPv6 */
> +	IECM_PTT(132, IP, IPV6, NOF, IP_GRENAT_MAC, IPV6, FRG, NONE, PAY3),
> +	IECM_PTT(133, IP, IPV6, NOF, IP_GRENAT_MAC, IPV6, NOF, NONE, PAY3),
> +	IECM_PTT(134, IP, IPV6, NOF, IP_GRENAT_MAC, IPV6, NOF, UDP,  PAY4),
> +	IECM_PTT_UNUSED_ENTRY(135),
> +	IECM_PTT(136, IP, IPV6, NOF, IP_GRENAT_MAC, IPV6, NOF, TCP,  PAY4),
> +	IECM_PTT(137, IP, IPV6, NOF, IP_GRENAT_MAC, IPV6, NOF, SCTP, PAY4),
> +	IECM_PTT(138, IP, IPV6, NOF, IP_GRENAT_MAC, IPV6, NOF, ICMP, PAY4),
> +
> +	/* IPv6 --> GRE/NAT -> MAC/VLAN */
> +	IECM_PTT(139, IP, IPV6, NOF, IP_GRENAT_MAC_VLAN, NONE, NOF, NONE, PAY3),
> +
> +	/* IPv6 --> GRE/NAT -> MAC/VLAN --> IPv4 */
> +	IECM_PTT(140, IP, IPV6, NOF, IP_GRENAT_MAC_VLAN, IPV4, FRG, NONE, PAY3),
> +	IECM_PTT(141, IP, IPV6, NOF, IP_GRENAT_MAC_VLAN, IPV4, NOF, NONE, PAY3),
> +	IECM_PTT(142, IP, IPV6, NOF, IP_GRENAT_MAC_VLAN, IPV4, NOF, UDP,  PAY4),
> +	IECM_PTT_UNUSED_ENTRY(143),
> +	IECM_PTT(144, IP, IPV6, NOF, IP_GRENAT_MAC_VLAN, IPV4, NOF, TCP,  PAY4),
> +	IECM_PTT(145, IP, IPV6, NOF, IP_GRENAT_MAC_VLAN, IPV4, NOF, SCTP, PAY4),
> +	IECM_PTT(146, IP, IPV6, NOF, IP_GRENAT_MAC_VLAN, IPV4, NOF, ICMP, PAY4),
> +
> +	/* IPv6 --> GRE/NAT -> MAC/VLAN --> IPv6 */
> +	IECM_PTT(147, IP, IPV6, NOF, IP_GRENAT_MAC_VLAN, IPV6, FRG, NONE, PAY3),
> +	IECM_PTT(148, IP, IPV6, NOF, IP_GRENAT_MAC_VLAN, IPV6, NOF, NONE, PAY3),
> +	IECM_PTT(149, IP, IPV6, NOF, IP_GRENAT_MAC_VLAN, IPV6, NOF, UDP,  PAY4),
> +	IECM_PTT_UNUSED_ENTRY(150),
> +	IECM_PTT(151, IP, IPV6, NOF, IP_GRENAT_MAC_VLAN, IPV6, NOF, TCP,  PAY4),
> +	IECM_PTT(152, IP, IPV6, NOF, IP_GRENAT_MAC_VLAN, IPV6, NOF, SCTP, PAY4),
> +	IECM_PTT(153, IP, IPV6, NOF, IP_GRENAT_MAC_VLAN, IPV6, NOF, ICMP, PAY4),
> +
> +	/* rest of the entries are unused */
> +};
> +EXPORT_SYMBOL(iecm_ptype_lookup);
>  
>  /**
>   * iecm_vport_init_num_qs - Initialize number of queues
> diff --git a/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> index d8152e657e24..c4ae56897d1b 100644
> --- a/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> +++ b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> @@ -859,6 +859,15 @@ static int iecm_recv_get_caps_msg(struct iecm_adapter *adapter)
>  				sizeof(struct virtchnl2_get_capabilities));
>  }
>  
> +/**
> + * iecm_vport_init_max_qs - Initialize max queues supported on this device
> + * @adapter: Driver specific private structure
> + */
> +static void iecm_vport_init_max_qs(struct iecm_adapter *adapter)
> +{
> +	adapter->max_queue_limit = IECM_MAX_Q;
> +}
> +
>  /**
>   * iecm_get_reg_intr_vecs - Get vector queue register offset
>   * @vport: virtual port structure
> @@ -901,6 +910,199 @@ int iecm_get_reg_intr_vecs(struct iecm_vport *vport,
>  }
>  EXPORT_SYMBOL(iecm_get_reg_intr_vecs);
>  
> +/**
> + * iecm_vport_get_q_reg - Get the queue registers for the vport
> + * @reg_vals: register values needing to be set
> + * @num_regs: amount we expect to fill
> + * @q_type: queue model
> + * @chunks: queue regs received over mailbox
> + */
> +static int
> +iecm_vport_get_q_reg(u32 *reg_vals, int num_regs, u32 q_type,
> +		     struct virtchnl2_queue_reg_chunks *chunks)
> +{
> +	u16 num_chunks = le16_to_cpu(chunks->num_chunks);
> +	int reg_filled = 0, i;
> +	u32 reg_val;
> +	u16 num_q;
> +
> +	while (num_chunks) {
> +		struct virtchnl2_queue_reg_chunk *chunk = &chunks->chunks[num_chunks - 1];
> +
> +		if (le32_to_cpu(chunk->type) == q_type) {
> +			num_q = le32_to_cpu(chunk->num_queues);
> +			reg_val = le64_to_cpu(chunk->qtail_reg_start);
> +			for (i = 0; i < num_q; i++) {
> +				if (reg_filled == num_regs)
> +					break;
> +				reg_vals[reg_filled++] = reg_val;
> +				reg_val +=
> +					le32_to_cpu(chunk->qtail_reg_spacing);
> +			}
> +		}
> +		num_chunks--;
> +	}

	while (num_chunks--) {
		struct ... = ... [num_chunks];

		if (le32_to_cpu(chunk->type) != q_type)
			continue;

		...
	}

-1 indent level, -complexity.

> +
> +	return reg_filled;
> +}
> +
> +/**
> + * __iecm_queue_reg_init - initialize queue registers
> + * @vport: virtual port structure
> + * @reg_vals: registers we are initializing
> + * @num_regs: how many registers there are in total
> + * @q_type: queue model
> + *
> + * Return number of queues that are initialized
> + */
> +static int
> +__iecm_queue_reg_init(struct iecm_vport *vport, u32 *reg_vals,
> +		      int num_regs, u32 q_type)
> +{
> +	struct iecm_hw *hw = &vport->adapter->hw;
> +	struct iecm_queue *q;
> +	int i, j, k = 0;
> +
> +	switch (q_type) {
> +	case VIRTCHNL2_QUEUE_TYPE_TX:
> +		for (i = 0; i < vport->num_txq_grp; i++) {
> +			struct iecm_txq_group *tx_qgrp = &vport->txq_grps[i];
> +
> +			for (j = 0; j < tx_qgrp->num_txq; j++) {
> +				if (k == num_regs)
> +					break;
> +
> +				tx_qgrp->txqs[j]->tail =
> +				  (__force u8 __iomem *)(hw->hw_addr +
> +							 reg_vals[k]);
> +				k++;
> +			}
> +		}
> +		break;
> +	case VIRTCHNL2_QUEUE_TYPE_RX:
> +		for (i = 0; i < vport->num_rxq_grp; i++) {
> +			struct iecm_rxq_group *rx_qgrp = &vport->rxq_grps[i];
> +			int num_rxq = rx_qgrp->singleq.num_rxq;
> +
> +			for (j = 0; j < num_rxq; j++) {
> +				if (k == num_regs)
> +					break;
> +
> +				q = rx_qgrp->singleq.rxqs[j];
> +				q->tail = (__force u8 __iomem *)(hw->hw_addr +
> +								 reg_vals[k]);
> +				k++;
> +			}
> +		}
> +		break;
> +	case VIRTCHNL2_QUEUE_TYPE_RX_BUFFER:
> +		for (i = 0; i < vport->num_rxq_grp; i++) {
> +			struct iecm_rxq_group *rx_qgrp = &vport->rxq_grps[i];
> +
> +			for (j = 0; j < vport->num_bufqs_per_qgrp; j++) {
> +				if (k == num_regs)
> +					break;
> +
> +				q = &rx_qgrp->splitq.bufq_sets[j].bufq;
> +				q->tail = (__force u8 __iomem *)(hw->hw_addr +
> +								 reg_vals[k]);
> +				k++;
> +			}
> +		}
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	return k;
> +}
> +
> +/**
> + * iecm_queue_reg_init - initialize queue registers
> + * @vport: virtual port structure
> + *
> + * Return 0 on success, negative on failure
> + */
> +static int iecm_queue_reg_init(struct iecm_vport *vport)
> +{
> +	struct virtchnl2_create_vport *vport_params;
> +	struct virtchnl2_queue_reg_chunks *chunks;
> +	int num_regs, ret = 0;
> +	u32 *reg_vals;
> +
> +	/* We may never deal with more than 256 same type of queues */
> +	reg_vals = kmalloc(sizeof(void *) * IECM_LARGE_MAX_Q,
> +			   GFP_KERNEL);
> +	if (!reg_vals)
> +		return -ENOMEM;
> +
> +	if (vport->adapter->config_data.req_qs_chunks) {
> +		struct virtchnl2_add_queues *vc_aq =
> +		  (struct virtchnl2_add_queues *)
> +		  vport->adapter->config_data.req_qs_chunks;
> +		chunks = &vc_aq->chunks;
> +	} else {
> +		vport_params = (struct virtchnl2_create_vport *)
> +			vport->adapter->vport_params_recvd[0];
> +		chunks = &vport_params->chunks;
> +	}
> +
> +	/* Initialize Tx queue tail register address */
> +	num_regs = iecm_vport_get_q_reg(reg_vals, IECM_LARGE_MAX_Q,
> +					VIRTCHNL2_QUEUE_TYPE_TX,
> +					chunks);
> +	if (num_regs < vport->num_txq) {
> +		ret = -EINVAL;
> +		goto free_reg_vals;
> +	}
> +
> +	num_regs = __iecm_queue_reg_init(vport, reg_vals, num_regs,
> +					 VIRTCHNL2_QUEUE_TYPE_TX);
> +	if (num_regs < vport->num_txq) {
> +		ret = -EINVAL;
> +		goto free_reg_vals;
> +	}
> +
> +	/* Initialize Rx/buffer queue tail register address based on Rx queue
> +	 * model
> +	 */
> +	if (iecm_is_queue_model_split(vport->rxq_model)) {
> +		num_regs = iecm_vport_get_q_reg(reg_vals, IECM_LARGE_MAX_Q,
> +						VIRTCHNL2_QUEUE_TYPE_RX_BUFFER,
> +						chunks);
> +		if (num_regs < vport->num_bufq) {
> +			ret = -EINVAL;
> +			goto free_reg_vals;
> +		}
> +
> +		num_regs = __iecm_queue_reg_init(vport, reg_vals, num_regs,
> +						 VIRTCHNL2_QUEUE_TYPE_RX_BUFFER);
> +		if (num_regs < vport->num_bufq) {
> +			ret = -EINVAL;
> +			goto free_reg_vals;
> +		}
> +	} else {
> +		num_regs = iecm_vport_get_q_reg(reg_vals, IECM_LARGE_MAX_Q,
> +						VIRTCHNL2_QUEUE_TYPE_RX,
> +						chunks);
> +		if (num_regs < vport->num_rxq) {
> +			ret = -EINVAL;
> +			goto free_reg_vals;
> +		}
> +
> +		num_regs = __iecm_queue_reg_init(vport, reg_vals, num_regs,
> +						 VIRTCHNL2_QUEUE_TYPE_RX);
> +		if (num_regs < vport->num_rxq) {
> +			ret = -EINVAL;
> +			goto free_reg_vals;
> +		}
> +	}
> +
> +free_reg_vals:
> +	kfree(reg_vals);
> +	return ret;
> +}
> +
>  /**
>   * iecm_send_create_vport_msg - Send virtchnl create vport message
>   * @adapter: Driver specific private structure
> @@ -943,6 +1145,66 @@ static int iecm_send_create_vport_msg(struct iecm_adapter *adapter)
>  				(u8 *)vport_msg);
>  }
>  
> +/**
> + * iecm_check_descs - Verify we have the descriptor support required
> + * @vport: virtual port structure
> + * @rx_desc_ids: Rx descriptor ids to check
> + * @tx_desc_ids: Tx descriptor ids to check
> + * @rxq_model: Rx queue model
> + * @txq_model: Tx queue model
> + *
> + * Returns 0 on success, negative if we didn't get sufficient descriptor
> + * support.
> + */
> +int iecm_check_descs(struct iecm_vport *vport, u64 rx_desc_ids,
> +		     u64 tx_desc_ids, u16 rxq_model, u16 txq_model)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +
> +	if (rxq_model == VIRTCHNL2_QUEUE_MODEL_SPLIT) {
> +		if (!(rx_desc_ids & VIRTCHNL2_RXDID_1_FLEX_SPLITQ_M)) {
> +			dev_err(&adapter->pdev->dev, "No supported RX descriptors provided");
> +			return -EINVAL;
> +		}
> +	} else {
> +		if (!(rx_desc_ids & VIRTCHNL2_RXDID_2_FLEX_SQ_NIC_M))
> +			vport->base_rxd = true;
> +	}
> +
> +	if (txq_model == VIRTCHNL2_QUEUE_MODEL_SPLIT) {
> +#define MIN_SUPPORT_TXDID (\
> +		VIRTCHNL2_TXDID_FLEX_FLOW_SCHED |\
> +		VIRTCHNL2_TXDID_FLEX_TSO_CTX |\
> +		VIRTCHNL2_TXDID_FLEX_DATA)
> +		if ((tx_desc_ids & MIN_SUPPORT_TXDID) != MIN_SUPPORT_TXDID) {
> +			dev_err(&adapter->pdev->dev, "Minimum TX descriptor support not provided");
> +			return -EINVAL;
> +		}
> +	}
> +	return 0;
> +}
> +EXPORT_SYMBOL(iecm_check_descs);
> +
> +/**
> + * iecm_get_supported_desc_ids - Get supported Rx and Tx descriptor ids
> + * @vport: virtual port structure
> + *
> + * Return 0 on success, error on failure
> + */
> +static int iecm_get_supported_desc_ids(struct iecm_vport *vport)
> +{
> +	struct virtchnl2_create_vport *vport_msg;
> +
> +	vport_msg = (struct virtchnl2_create_vport *)
> +			vport->adapter->vport_params_recvd[0];
> +	vport_msg->rx_desc_ids = cpu_to_le64(VIRTCHNL2_RXDID_1_FLEX_SPLITQ_M);
> +	vport_msg->tx_desc_ids = cpu_to_le64(MIN_SUPPORT_TXDID);
> +
> +	return iecm_check_descs(vport, le64_to_cpu(vport_msg->rx_desc_ids),
> +				le64_to_cpu(vport_msg->tx_desc_ids),
> +				vport->rxq_model, vport->txq_model);
> +}
> +
>  /**
>   * iecm_recv_create_vport_msg - Receive virtchnl create vport message
>   * @adapter: Driver specific private structure
> @@ -1333,6 +1595,9 @@ int iecm_send_config_rx_queues_msg(struct iecm_vport *vport)
>  					bufq->rx_buf_stride;
>  				qi[k].rx_buffer_low_watermark =
>  					cpu_to_le16(bufq->rx_buffer_low_watermark);
> +				if (iecm_is_feature_ena(vport, NETIF_F_GRO_HW))
> +					qi[k].qflags |=
> +						cpu_to_le16(VIRTCHNL2_RXQ_RSC);
>  			}
>  		}
>  
> @@ -1361,6 +1626,9 @@ int iecm_send_config_rx_queues_msg(struct iecm_vport *vport)
>  					qi[k].hdr_buffer_size =
>  						cpu_to_le16(rxq->rx_hbuf_size);
>  				}
> +				if (iecm_is_feature_ena(vport, NETIF_F_GRO_HW))
> +					qi[k].qflags |=
> +						cpu_to_le16(VIRTCHNL2_RXQ_RSC);
>  			} else {
>  				rxq = rx_qgrp->singleq.rxqs[j];
>  			}
> @@ -1968,6 +2236,765 @@ int iecm_send_add_queues_msg(struct iecm_vport *vport, u16 num_tx_q,
>  	return err;
>  }
>  
> +/**
> + * iecm_send_alloc_vectors_msg - Send virtchnl alloc vectors message
> + * @adapter: Driver specific private structure
> + * @num_vectors: number of vectors to be allocated
> + *
> + * Returns 0 on success, negative on failure.
> + */
> +int iecm_send_alloc_vectors_msg(struct iecm_adapter *adapter, u16 num_vectors)
> +{
> +	struct virtchnl2_alloc_vectors *alloc_vec;
> +	struct virtchnl2_alloc_vectors ac = {0};
> +	int size, err;
> +
> +	ac.num_vectors = cpu_to_le16(num_vectors);
> +
> +	err = iecm_send_mb_msg(adapter, VIRTCHNL2_OP_ALLOC_VECTORS,
> +			       sizeof(ac), (u8 *)&ac);
> +	if (err)
> +		return err;
> +
> +	err = iecm_wait_for_event(adapter, IECM_VC_ALLOC_VECTORS,
> +				  IECM_VC_ALLOC_VECTORS_ERR);
> +	if (err)
> +		return err;
> +
> +	size = sizeof(struct virtchnl2_alloc_vectors) +
> +		((num_vectors - 1) *
> +		sizeof(struct virtchnl2_vector_chunk));
> +
> +	kfree(adapter->req_vec_chunks);
> +	adapter->req_vec_chunks = NULL;
> +	adapter->req_vec_chunks = kzalloc(size, GFP_KERNEL);
> +	if (!adapter->req_vec_chunks) {
> +		err = -ENOMEM;
> +		goto error;
> +	}
> +	memcpy(adapter->req_vec_chunks, adapter->vc_msg, size);
> +
> +	alloc_vec = adapter->req_vec_chunks;
> +	if (le16_to_cpu(alloc_vec->num_vectors) < num_vectors) {
> +		kfree(adapter->req_vec_chunks);
> +		adapter->req_vec_chunks = NULL;
> +		err = -EINVAL;
> +	}
> +error:
> +	clear_bit(__IECM_VC_MSG_PENDING, adapter->flags);
> +	return err;
> +}
> +
> +/**
> + * iecm_send_dealloc_vectors_msg - Send virtchnl de allocate vectors message
> + * @adapter: Driver specific private structure
> + *
> + * Returns 0 on success, negative on failure.
> + */
> +int iecm_send_dealloc_vectors_msg(struct iecm_adapter *adapter)
> +{
> +	struct virtchnl2_vector_chunks *vcs;
> +	struct virtchnl2_alloc_vectors *ac;
> +	int buf_size, err;
> +
> +	ac = adapter->req_vec_chunks;
> +	vcs = &ac->vchunks;
> +
> +	buf_size = sizeof(struct virtchnl2_vector_chunks) +
> +			((le16_to_cpu(vcs->num_vchunks) - 1) *
> +			sizeof(struct virtchnl2_vector_chunk));
> +
> +	err = iecm_send_mb_msg(adapter, VIRTCHNL2_OP_DEALLOC_VECTORS, buf_size,
> +			       (u8 *)vcs);
> +	if (err)
> +		return err;
> +	err = iecm_min_wait_for_event(adapter, IECM_VC_DEALLOC_VECTORS,
> +				      IECM_VC_DEALLOC_VECTORS_ERR);
> +	if (err)
> +		return err;
> +
> +	kfree(adapter->req_vec_chunks);
> +	adapter->req_vec_chunks = NULL;
> +	return 0;
> +}
> +
> +/**
> + * iecm_send_get_stats_msg - Send virtchnl get statistics message
> + * @vport: vport to get stats for
> + *
> + * Returns 0 on success, negative on failure.
> + */
> +int iecm_send_get_stats_msg(struct iecm_vport *vport)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct virtchnl2_vport_stats *stats;
> +	int err = 0;
> +
> +	stats = (struct virtchnl2_vport_stats *)adapter->vc_msg;
> +
> +	/* Don't send get_stats message if one is pending or the
> +	 * link is down
> +	 */
> +	if (test_bit(IECM_VC_GET_STATS, adapter->vc_state) ||
> +	    adapter->state <= __IECM_DOWN)
> +		goto error;
> +
> +	stats->vport_id = cpu_to_le32(vport->vport_id);
> +
> +	err = iecm_send_mb_msg(adapter, VIRTCHNL2_OP_GET_STATS,
> +			       sizeof(stats), (u8 *)&stats);
> +	if (err)
> +		goto error;
> +
> +	err = iecm_wait_for_event(adapter, IECM_VC_GET_STATS,
> +				  IECM_VC_GET_STATS_ERR);
> +	if (err)
> +		goto error;
> +
> +	vport->netstats.rx_packets = le64_to_cpu(stats->rx_unicast) +
> +				     le64_to_cpu(stats->rx_multicast) +
> +				     le64_to_cpu(stats->rx_broadcast);
> +	vport->netstats.tx_packets = le64_to_cpu(stats->tx_unicast) +
> +				     le64_to_cpu(stats->tx_multicast) +
> +				     le64_to_cpu(stats->tx_broadcast);
> +	vport->netstats.rx_bytes = le64_to_cpu(stats->rx_bytes);
> +	vport->netstats.tx_bytes = le64_to_cpu(stats->tx_bytes);
> +	vport->netstats.tx_errors = le64_to_cpu(stats->tx_errors);
> +	vport->netstats.rx_dropped = le64_to_cpu(stats->rx_discards);
> +	vport->netstats.tx_dropped = le64_to_cpu(stats->tx_discards);
> +	vport->port_stats.vport_stats = *stats;
> +	clear_bit(__IECM_VC_MSG_PENDING, adapter->flags);
> +error:
> +	clear_bit(__IECM_MB_STATS_PENDING, vport->adapter->flags);
> +
> +	return err;
> +}
> +
> +/**
> + * iecm_send_get_set_rss_hash_msg - Send set or get rss hash message
> + * @vport: virtual port data structure
> + * @get: flag to get or set rss hash
> + *
> + * Returns 0 on success, negative on failure.
> + */
> +int iecm_send_get_set_rss_hash_msg(struct iecm_vport *vport, bool get)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct virtchnl2_rss_hash rh = {0};
> +	int err;
> +
> +	rh.vport_id = cpu_to_le32(vport->vport_id);
> +	rh.ptype_groups = cpu_to_le64(adapter->rss_data.rss_hash);
> +
> +	if (get) {
> +		err = iecm_send_mb_msg(adapter, VIRTCHNL2_OP_GET_RSS_HASH,
> +				       sizeof(rh), (u8 *)&rh);
> +		if (err)
> +			return err;
> +
> +		err = iecm_wait_for_event(adapter, IECM_VC_GET_RSS_HASH,
> +					  IECM_VC_GET_RSS_HASH_ERR);
> +		if (err)
> +			return err;
> +
> +		memcpy(&rh, adapter->vc_msg, sizeof(rh));
> +		adapter->rss_data.rss_hash = le64_to_cpu(rh.ptype_groups);
> +		/* Leave the buffer clean for next message */
> +		memset(adapter->vc_msg, 0, IECM_DFLT_MBX_BUF_SIZE);
> +		clear_bit(__IECM_VC_MSG_PENDING, adapter->flags);
> +
> +		return 0;
> +	}
> +
> +	err = iecm_send_mb_msg(adapter, VIRTCHNL2_OP_SET_RSS_HASH,
> +			       sizeof(rh), (u8 *)&rh);
> +	if (err)
> +		return err;
> +
> +	return  iecm_wait_for_event(adapter, IECM_VC_SET_RSS_HASH,
> +				    IECM_VC_SET_RSS_HASH_ERR);
> +}
> +
> +/**
> + * iecm_send_get_set_rss_lut_msg - Send virtchnl get or set rss lut message
> + * @vport: virtual port data structure
> + * @get: flag to set or get rss look up table
> + *
> + * Returns 0 on success, negative on failure.
> + */
> +int iecm_send_get_set_rss_lut_msg(struct iecm_vport *vport, bool get)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct virtchnl2_rss_lut *recv_rl;
> +	struct virtchnl2_rss_lut *rl;
> +	int buf_size, lut_buf_size;
> +	int i, err = 0;
> +
> +	buf_size = sizeof(struct virtchnl2_rss_lut) +
> +		       (sizeof(u32) * (adapter->rss_data.rss_lut_size - 1));
> +	rl = kzalloc(buf_size, GFP_KERNEL);
> +	if (!rl)
> +		return -ENOMEM;
> +
> +	if (!get) {
> +		rl->lut_entries = cpu_to_le16(adapter->rss_data.rss_lut_size);
> +		for (i = 0; i < adapter->rss_data.rss_lut_size; i++)
> +			rl->lut[i] = cpu_to_le32(adapter->rss_data.rss_lut[i]);
> +	}
> +	rl->vport_id = cpu_to_le32(vport->vport_id);
> +
> +	if (get) {
> +		err = iecm_send_mb_msg(vport->adapter, VIRTCHNL2_OP_GET_RSS_LUT,
> +				       buf_size, (u8 *)rl);
> +		if (err)
> +			goto error;
> +
> +		err = iecm_wait_for_event(adapter, IECM_VC_GET_RSS_LUT,
> +					  IECM_VC_GET_RSS_LUT_ERR);
> +		if (err)
> +			goto error;
> +
> +		recv_rl = (struct virtchnl2_rss_lut *)adapter->vc_msg;
> +		if (adapter->rss_data.rss_lut_size !=
> +		    le16_to_cpu(recv_rl->lut_entries)) {
> +			adapter->rss_data.rss_lut_size =
> +				le16_to_cpu(recv_rl->lut_entries);
> +			kfree(adapter->rss_data.rss_lut);
> +
> +			lut_buf_size = adapter->rss_data.rss_lut_size *
> +					sizeof(u32);
> +			adapter->rss_data.rss_lut = kzalloc(lut_buf_size,
> +							    GFP_KERNEL);
> +			if (!adapter->rss_data.rss_lut) {
> +				adapter->rss_data.rss_lut_size = 0;
> +				/* Leave the buffer clean */
> +				memset(adapter->vc_msg, 0,
> +				       IECM_DFLT_MBX_BUF_SIZE);
> +				clear_bit(__IECM_VC_MSG_PENDING,
> +					  adapter->flags);
> +				err = -ENOMEM;
> +				goto error;
> +			}
> +		}
> +		memcpy(adapter->rss_data.rss_lut, adapter->vc_msg,
> +		       adapter->rss_data.rss_lut_size);
> +		/* Leave the buffer clean for next message */
> +		memset(adapter->vc_msg, 0, IECM_DFLT_MBX_BUF_SIZE);
> +		clear_bit(__IECM_VC_MSG_PENDING, adapter->flags);
> +	} else {
> +		err = iecm_send_mb_msg(adapter, VIRTCHNL2_OP_SET_RSS_LUT,
> +				       buf_size, (u8 *)rl);
> +		if (err)
> +			goto error;
> +
> +		err = iecm_wait_for_event(adapter, IECM_VC_SET_RSS_LUT,
> +					  IECM_VC_SET_RSS_LUT_ERR);
> +	}
> +error:
> +	kfree(rl);
> +	return err;
> +}
> +
> +/**
> + * iecm_send_get_set_rss_key_msg - Send virtchnl get or set rss key message
> + * @vport: virtual port data structure
> + * @get: flag to set or get rss look up table
> + *
> + * Returns 0 on success, negative on failure
> + */
> +int iecm_send_get_set_rss_key_msg(struct iecm_vport *vport, bool get)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct virtchnl2_rss_key *recv_rk;
> +	struct virtchnl2_rss_key *rk;
> +	int i, buf_size, err = 0;
> +
> +	buf_size = sizeof(struct virtchnl2_rss_key) +
> +		       (sizeof(u8) * (adapter->rss_data.rss_key_size - 1));
> +	rk = kzalloc(buf_size, GFP_KERNEL);
> +	if (!rk)
> +		return -ENOMEM;
> +	rk->vport_id = cpu_to_le32(vport->vport_id);
> +
> +	if (get) {
> +		err = iecm_send_mb_msg(adapter, VIRTCHNL2_OP_GET_RSS_KEY,
> +				       buf_size, (u8 *)rk);
> +		if (err)
> +			goto error;
> +
> +		err = iecm_wait_for_event(adapter, IECM_VC_GET_RSS_KEY,
> +					  IECM_VC_GET_RSS_KEY_ERR);
> +		if (err)
> +			goto error;
> +
> +		recv_rk = (struct virtchnl2_rss_key *)adapter->vc_msg;
> +		if (adapter->rss_data.rss_key_size !=
> +		    le16_to_cpu(recv_rk->key_len)) {
> +			adapter->rss_data.rss_key_size =
> +				min_t(u16, NETDEV_RSS_KEY_LEN,
> +				      le16_to_cpu(recv_rk->key_len));
> +			kfree(adapter->rss_data.rss_key);
> +			adapter->rss_data.rss_key = kzalloc(adapter->rss_data.rss_key_size,
> +							    GFP_KERNEL);
> +			if (!adapter->rss_data.rss_key) {
> +				adapter->rss_data.rss_key_size = 0;
> +				/* Leave the buffer clean */
> +				memset(adapter->vc_msg, 0,
> +				       IECM_DFLT_MBX_BUF_SIZE);
> +				clear_bit(__IECM_VC_MSG_PENDING,
> +					  adapter->flags);
> +				err = -ENOMEM;
> +				goto error;
> +			}
> +		}
> +		memcpy(adapter->rss_data.rss_key, adapter->vc_msg,
> +		       adapter->rss_data.rss_key_size);
> +		/* Leave the buffer clean for next message */
> +		memset(adapter->vc_msg, 0, IECM_DFLT_MBX_BUF_SIZE);
> +		clear_bit(__IECM_VC_MSG_PENDING, adapter->flags);
> +	} else {
> +		rk->key_len = cpu_to_le16(adapter->rss_data.rss_key_size);
> +		for (i = 0; i < adapter->rss_data.rss_key_size; i++)
> +			rk->key[i] = adapter->rss_data.rss_key[i];
> +
> +		err = iecm_send_mb_msg(adapter, VIRTCHNL2_OP_SET_RSS_KEY,
> +				       buf_size, (u8 *)rk);
> +		if (err)
> +			goto error;
> +
> +		err = iecm_wait_for_event(adapter, IECM_VC_SET_RSS_KEY,
> +					  IECM_VC_SET_RSS_KEY_ERR);
> +	}
> +error:
> +	kfree(rk);
> +	return err;
> +}
> +
> +/**
> + * iecm_tpid_to_ethertype - transform from VLAN TPID to virtchnl ethertype
> + * @tpid: VLAN TPID (i.e. 0x8100, 0x88a8, etc.)
> + *
> + * Return virtchnl ethertype
> + */
> +static u32 iecm_tpid_to_ethertype(u16 tpid)
> +{
> +	switch (tpid) {
> +	case ETH_P_8021Q:
> +		return VIRTCHNL_VLAN_ETHERTYPE_8100;
> +	case ETH_P_8021AD:
> +		return VIRTCHNL_VLAN_ETHERTYPE_88A8;
> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * iecm_set_vlan_offload_ethertype - set ethertype for offload message
> + * @adapter: adapter structure
> + * @msg: message structure used for updating offloads
> + * @offload_op: opcode used to determine which support structure to check
> + *
> + * Return 0 on success, negative on failure.
> + */
> +static int
> +iecm_set_vlan_offload_ethertype(struct iecm_adapter *adapter,
> +				struct virtchnl_vlan_setting *msg,
> +				enum virtchnl_ops offload_op)
> +{
> +	struct virtchnl_vlan_supported_caps *offload_support;
> +	u16 tpid = adapter->config_data.vlan_ethertype;
> +	u32 vc_ethertype;
> +
> +	vc_ethertype = iecm_tpid_to_ethertype(tpid);
> +	/* reference the correct offload support structure */
> +	switch (offload_op) {
> +	case VIRTCHNL_OP_ENABLE_VLAN_STRIPPING_V2:
> +	case VIRTCHNL_OP_DISABLE_VLAN_STRIPPING_V2:
> +		offload_support =
> +			&adapter->vlan_caps->offloads.stripping_support;
> +		break;
> +	case VIRTCHNL_OP_ENABLE_VLAN_INSERTION_V2:
> +	case VIRTCHNL_OP_DISABLE_VLAN_INSERTION_V2:
> +		offload_support =
> +			&adapter->vlan_caps->offloads.insertion_support;
> +		break;
> +	default:
> +		dev_err(&adapter->pdev->dev, "Invalid opcode %d for setting virtchnl ethertype to enable/disable VLAN offloads\n",
> +			offload_op);
> +		return -EINVAL;
> +	}
> +
> +	/* make sure ethertype is supported and turning feature on/off
> +	 * is allowed
> +	 */
> +	if ((offload_support->outer & vc_ethertype) &&
> +	    (offload_support->outer & VIRTCHNL_VLAN_TOGGLE)) {
> +		msg->outer_ethertype_setting = vc_ethertype;
> +	} else if ((offload_support->inner & vc_ethertype) &&
> +		   (offload_support->inner & VIRTCHNL_VLAN_TOGGLE)) {
> +		msg->inner_ethertype_setting = vc_ethertype;
> +	} else {
> +		dev_err(&adapter->pdev->dev, "opcode %d unsupported for VLAN TPID 0x%04x\n",
> +			offload_op, tpid);
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * iecm_send_strip_vlan_msg - Send enable/disable vlan stripping message
> + * @vport: vport structure
> + * @ena: enable or disable vlan stripping
> + *
> + * Returns 0 on success, negative on failure.
> + */
> +static int iecm_send_strip_vlan_msg(struct iecm_vport *vport, bool ena)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	enum iecm_vport_vc_state vc, vc_err;
> +	struct virtchnl_vlan_setting *msg;
> +	enum virtchnl_ops vop;
> +	int err, len;
> +
> +	len = sizeof(struct virtchnl_vlan_setting);
> +	msg = kzalloc(sizeof(*msg), GFP_KERNEL);
> +
> +	if (!msg)
> +		return -ENOMEM;
> +
> +	msg->vport_id = vport->vport_id;
> +	if (ena) {
> +		vop = VIRTCHNL_OP_ENABLE_VLAN_STRIPPING_V2;
> +		vc = IECM_VC_STRIPPING_ENA_VLAN_V2;
> +		vc_err = IECM_VC_STRIPPING_ENA_VLAN_V2_ERR;
> +	} else {
> +		vop = VIRTCHNL_OP_DISABLE_VLAN_STRIPPING_V2;
> +		vc = IECM_VC_STRIPPING_DIS_VLAN_V2;
> +		vc_err = IECM_VC_STRIPPING_DIS_VLAN_V2_ERR;
> +	}
> +
> +	err = iecm_set_vlan_offload_ethertype(adapter, msg, vop);
> +	if (!err) {
> +		err = iecm_send_mb_msg(adapter, vop, len, (u8 *)msg);
> +		if (!err)
> +			err = iecm_wait_for_event(adapter, vc, vc_err);
> +	}
> +
> +	kfree(msg);
> +
> +	return err;
> +}
> +
> +/**
> + * iecm_send_insert_vlan_msg - Send enable/disable vlan insertion message
> + * @vport: vport structure
> + * @ena: enable/disable vlan insertion
> + *
> + * Returns 0 on success, negative on failure.
> + */
> +static int iecm_send_insert_vlan_msg(struct iecm_vport *vport, bool ena)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	enum iecm_vport_vc_state vc, vc_err;
> +	struct virtchnl_vlan_setting *msg;
> +	enum virtchnl_ops vop;
> +	int err, len;
> +
> +	len = sizeof(struct virtchnl_vlan_setting);
> +	msg = kzalloc(sizeof(*msg), GFP_KERNEL);
> +
> +	if (!msg)
> +		return -ENOMEM;
> +
> +	msg->vport_id = vport->vport_id;
> +
> +	if (ena) {
> +		vop = VIRTCHNL_OP_ENABLE_VLAN_INSERTION_V2;
> +		vc = IECM_VC_INSERTION_ENA_VLAN_V2;
> +		vc_err = IECM_VC_INSERTION_ENA_VLAN_V2_ERR;
> +	} else {
> +		vop = VIRTCHNL_OP_DISABLE_VLAN_INSERTION_V2;
> +		vc = IECM_VC_INSERTION_DIS_VLAN_V2;
> +		vc_err = IECM_VC_INSERTION_DIS_VLAN_V2_ERR;
> +	}
> +
> +	err = iecm_set_vlan_offload_ethertype(adapter, msg, vop);
> +	if (!err) {
> +		err = iecm_send_mb_msg(adapter, vop, len, (u8 *)msg);
> +		if (!err)
> +			err = iecm_wait_for_event(adapter, vc, vc_err);
> +	}
> +
> +	kfree(msg);
> +
> +	return err;
> +}
> +
> +/**
> + * iecm_fill_ptype_lookup - Fill L3 specific fields in ptype lookup table
> + * @ptype: ptype lookup table
> + * @pstate: state machine for ptype lookup table
> + * @ipv4: ipv4 or ipv6
> + * @frag: fragmentation allowed
> + *
> + */
> +static void iecm_fill_ptype_lookup(struct iecm_rx_ptype_decoded *ptype,
> +				   struct iecm_ptype_state *pstate,
> +				   bool ipv4, bool frag)
> +{
> +	if (!pstate->outer_ip || !pstate->outer_frag) {
> +		ptype->outer_ip = IECM_RX_PTYPE_OUTER_IP;
> +		pstate->outer_ip = true;
> +
> +		if (ipv4)
> +			ptype->outer_ip_ver = IECM_RX_PTYPE_OUTER_IPV4;
> +		else
> +			ptype->outer_ip_ver = IECM_RX_PTYPE_OUTER_IPV6;
> +
> +		if (frag) {
> +			ptype->outer_frag = IECM_RX_PTYPE_FRAG;
> +			pstate->outer_frag = true;
> +		}
> +	} else {
> +		ptype->tunnel_type = IECM_RX_PTYPE_TUNNEL_IP_IP;
> +		pstate->tunnel_state = IECM_PTYPE_TUNNEL_IP;
> +
> +		if (ipv4)
> +			ptype->tunnel_end_prot =
> +					IECM_RX_PTYPE_TUNNEL_END_IPV4;
> +		else
> +			ptype->tunnel_end_prot =
> +					IECM_RX_PTYPE_TUNNEL_END_IPV6;
> +
> +		if (frag)
> +			ptype->tunnel_end_frag = IECM_RX_PTYPE_FRAG;
> +	}
> +}
> +
> +/**
> + * iecm_send_get_rx_ptype_msg - Send virtchnl for ptype info
> + * @vport: virtual port data structure
> + *
> + * Returns 0 on success, negative on failure.
> + */
> +int iecm_send_get_rx_ptype_msg(struct iecm_vport *vport)
> +{
> +	struct iecm_rx_ptype_decoded *ptype_lkup = vport->rx_ptype_lkup;
> +	struct virtchnl2_get_ptype_info *get_ptype_info, *ptype_info;
> +	int max_ptype, ptypes_recvd = 0, len, ptype_offset;
> +	struct iecm_adapter *adapter = vport->adapter;
> +	int err = 0, i, j, k = 0;
> +
> +	if (iecm_is_queue_model_split(vport->rxq_model))
> +		max_ptype = IECM_RX_MAX_PTYPE;
> +	else
> +		max_ptype = IECM_RX_MAX_BASE_PTYPE;
> +
> +	for (i = 0; i < max_ptype; i++)
> +		ptype_lkup[i] = iecm_ptype_lookup[0];
> +
> +	len = sizeof(struct virtchnl2_get_ptype_info);
> +	get_ptype_info = kzalloc(len, GFP_KERNEL);
> +	if (!get_ptype_info)
> +		return -ENOMEM;
> +
> +	get_ptype_info->start_ptype_id = 0;
> +	get_ptype_info->num_ptypes = cpu_to_le16(max_ptype);
> +
> +	err = iecm_send_mb_msg(adapter, VIRTCHNL2_OP_GET_PTYPE_INFO,
> +			       len, (u8 *)get_ptype_info);
> +	if (err)
> +		goto get_ptype_rel;
> +
> +	while (ptypes_recvd < max_ptype) {
> +		err = iecm_wait_for_event(adapter, IECM_VC_GET_PTYPE_INFO,
> +					  IECM_VC_GET_PTYPE_INFO_ERR);
> +		if (err)
> +			goto get_ptype_rel;
> +
> +		len = IECM_DFLT_MBX_BUF_SIZE;
> +		ptype_info = kzalloc(len, GFP_KERNEL);
> +		if (!ptype_info) {
> +			err = -ENOMEM;
> +			goto clear_vc_flag;
> +		}
> +
> +		memcpy(ptype_info, adapter->vc_msg, len);
> +
> +		ptypes_recvd += le16_to_cpu(ptype_info->num_ptypes);
> +		if (ptypes_recvd > max_ptype) {
> +			err = -EINVAL;
> +			goto ptype_rel;
> +		}
> +
> +		ptype_offset = sizeof(struct virtchnl2_get_ptype_info) -
> +						sizeof(struct virtchnl2_ptype);
> +
> +		for (i = 0; i < le16_to_cpu(ptype_info->num_ptypes); i++) {
> +			struct iecm_ptype_state pstate = { 0 };
> +			struct virtchnl2_ptype *ptype;
> +			u16 id;
> +
> +			ptype = (struct virtchnl2_ptype *)
> +					((u8 *)ptype_info + ptype_offset);
> +
> +			ptype_offset += IECM_GET_PTYPE_SIZE(ptype);
> +			if (ptype_offset > len) {
> +				err = -EINVAL;
> +				goto ptype_rel;
> +			}
> +
> +			if (le16_to_cpu(ptype->ptype_id_10) == 0xFFFF)
> +				goto ptype_rel;
> +
> +			if (iecm_is_queue_model_split(vport->rxq_model))
> +				k = le16_to_cpu(ptype->ptype_id_10);
> +			else
> +				k = ptype->ptype_id_8;
> +
> +			if (ptype->proto_id_count)
> +				ptype_lkup[k].known = 1;
> +
> +			for (j = 0; j < ptype->proto_id_count; j++) {
> +				id = le16_to_cpu(ptype->proto_id[j]);
> +				switch (id) {
> +				case VIRTCHNL2_PROTO_HDR_GRE:
> +					if (pstate.tunnel_state ==
> +							IECM_PTYPE_TUNNEL_IP) {
> +						ptype_lkup[k].tunnel_type =
> +						IECM_RX_PTYPE_TUNNEL_IP_GRENAT;
> +						pstate.tunnel_state |=
> +						IECM_PTYPE_TUNNEL_IP_GRENAT;
> +					}
> +					break;
> +				case VIRTCHNL2_PROTO_HDR_MAC:
> +					ptype_lkup[k].outer_ip =
> +						IECM_RX_PTYPE_OUTER_L2;
> +					if (pstate.tunnel_state ==
> +							IECM_TUN_IP_GRE) {
> +						ptype_lkup[k].tunnel_type =
> +						IECM_RX_PTYPE_TUNNEL_IP_GRENAT_MAC;
> +						pstate.tunnel_state |=
> +						IECM_PTYPE_TUNNEL_IP_GRENAT_MAC;
> +					}
> +					break;
> +				case VIRTCHNL2_PROTO_HDR_VLAN:
> +					if (pstate.tunnel_state ==
> +							IECM_TUN_IP_GRE_MAC) {
> +						ptype_lkup[k].tunnel_type =
> +						IECM_RX_PTYPE_TUNNEL_IP_GRENAT_MAC_VLAN;
> +						pstate.tunnel_state |=
> +						IECM_PTYPE_TUNNEL_IP_GRENAT_MAC_VLAN;
> +					}
> +					break;
> +				case VIRTCHNL2_PROTO_HDR_IPV4:
> +					iecm_fill_ptype_lookup(&ptype_lkup[k],
> +							       &pstate, true,
> +							       false);
> +					break;
> +				case VIRTCHNL2_PROTO_HDR_IPV6:
> +					iecm_fill_ptype_lookup(&ptype_lkup[k],
> +							       &pstate, false,
> +							       false);
> +					break;
> +				case VIRTCHNL2_PROTO_HDR_IPV4_FRAG:
> +					iecm_fill_ptype_lookup(&ptype_lkup[k],
> +							       &pstate, true,
> +							       true);
> +					break;
> +				case VIRTCHNL2_PROTO_HDR_IPV6_FRAG:
> +					iecm_fill_ptype_lookup(&ptype_lkup[k],
> +							       &pstate, false,
> +							       true);
> +					break;
> +				case VIRTCHNL2_PROTO_HDR_UDP:
> +					ptype_lkup[k].inner_prot =
> +					IECM_RX_PTYPE_INNER_PROT_UDP;
> +					break;
> +				case VIRTCHNL2_PROTO_HDR_TCP:
> +					ptype_lkup[k].inner_prot =
> +					IECM_RX_PTYPE_INNER_PROT_TCP;
> +					break;
> +				case VIRTCHNL2_PROTO_HDR_SCTP:
> +					ptype_lkup[k].inner_prot =
> +					IECM_RX_PTYPE_INNER_PROT_SCTP;
> +					break;
> +				case VIRTCHNL2_PROTO_HDR_ICMP:
> +					ptype_lkup[k].inner_prot =
> +					IECM_RX_PTYPE_INNER_PROT_ICMP;
> +					break;
> +				case VIRTCHNL2_PROTO_HDR_PAY:
> +					ptype_lkup[k].payload_layer =
> +						IECM_RX_PTYPE_PAYLOAD_LAYER_PAY2;
> +					break;
> +				case VIRTCHNL2_PROTO_HDR_ICMPV6:
> +				case VIRTCHNL2_PROTO_HDR_IPV6_EH:
> +				case VIRTCHNL2_PROTO_HDR_PRE_MAC:
> +				case VIRTCHNL2_PROTO_HDR_POST_MAC:
> +				case VIRTCHNL2_PROTO_HDR_ETHERTYPE:
> +				case VIRTCHNL2_PROTO_HDR_SVLAN:
> +				case VIRTCHNL2_PROTO_HDR_CVLAN:
> +				case VIRTCHNL2_PROTO_HDR_MPLS:
> +				case VIRTCHNL2_PROTO_HDR_MMPLS:
> +				case VIRTCHNL2_PROTO_HDR_PTP:
> +				case VIRTCHNL2_PROTO_HDR_CTRL:
> +				case VIRTCHNL2_PROTO_HDR_LLDP:
> +				case VIRTCHNL2_PROTO_HDR_ARP:
> +				case VIRTCHNL2_PROTO_HDR_ECP:
> +				case VIRTCHNL2_PROTO_HDR_EAPOL:
> +				case VIRTCHNL2_PROTO_HDR_PPPOD:
> +				case VIRTCHNL2_PROTO_HDR_PPPOE:
> +				case VIRTCHNL2_PROTO_HDR_IGMP:
> +				case VIRTCHNL2_PROTO_HDR_AH:
> +				case VIRTCHNL2_PROTO_HDR_ESP:
> +				case VIRTCHNL2_PROTO_HDR_IKE:
> +				case VIRTCHNL2_PROTO_HDR_NATT_KEEP:
> +				case VIRTCHNL2_PROTO_HDR_L2TPV2:
> +				case VIRTCHNL2_PROTO_HDR_L2TPV2_CONTROL:
> +				case VIRTCHNL2_PROTO_HDR_L2TPV3:
> +				case VIRTCHNL2_PROTO_HDR_GTP:
> +				case VIRTCHNL2_PROTO_HDR_GTP_EH:
> +				case VIRTCHNL2_PROTO_HDR_GTPCV2:
> +				case VIRTCHNL2_PROTO_HDR_GTPC_TEID:
> +				case VIRTCHNL2_PROTO_HDR_GTPU:
> +				case VIRTCHNL2_PROTO_HDR_GTPU_UL:
> +				case VIRTCHNL2_PROTO_HDR_GTPU_DL:
> +				case VIRTCHNL2_PROTO_HDR_ECPRI:
> +				case VIRTCHNL2_PROTO_HDR_VRRP:
> +				case VIRTCHNL2_PROTO_HDR_OSPF:
> +				case VIRTCHNL2_PROTO_HDR_TUN:
> +				case VIRTCHNL2_PROTO_HDR_NVGRE:
> +				case VIRTCHNL2_PROTO_HDR_VXLAN:
> +				case VIRTCHNL2_PROTO_HDR_VXLAN_GPE:
> +				case VIRTCHNL2_PROTO_HDR_GENEVE:
> +				case VIRTCHNL2_PROTO_HDR_NSH:
> +				case VIRTCHNL2_PROTO_HDR_QUIC:
> +				case VIRTCHNL2_PROTO_HDR_PFCP:
> +				case VIRTCHNL2_PROTO_HDR_PFCP_NODE:
> +				case VIRTCHNL2_PROTO_HDR_PFCP_SESSION:
> +				case VIRTCHNL2_PROTO_HDR_RTP:
> +				case VIRTCHNL2_PROTO_HDR_NO_PROTO:
> +				default:
> +					continue;
> +				}
> +			}
> +		}
> +		clear_bit(__IECM_VC_MSG_PENDING, adapter->flags);
> +		kfree(ptype_info);
> +	}
> +	kfree(get_ptype_info);
> +	return 0;
> +
> +ptype_rel:
> +	kfree(ptype_info);
> +clear_vc_flag:
> +	clear_bit(__IECM_VC_MSG_PENDING, adapter->flags);
> +get_ptype_rel:
> +	kfree(get_ptype_info);
> +	return err;
> +}
> +
>  /**
>   * iecm_find_ctlq - Given a type and id, find ctlq info
>   * @hw: hardware struct
> @@ -2478,6 +3505,25 @@ static int iecm_vport_queue_ids_init(struct iecm_vport *vport)
>  	return 0;
>  }
>  
> +/**
> + * iecm_vport_adjust_qs - Adjust to new requested queues
> + * @vport: virtual port data struct
> + *
> + * Renegotiate queues.  Returns 0 on success, negative on failure.
> + */
> +void iecm_vport_adjust_qs(struct iecm_vport *vport)
> +{
> +	struct virtchnl2_create_vport vport_msg;
> +
> +	vport_msg.txq_model = cpu_to_le16(vport->txq_model);
> +	vport_msg.rxq_model = cpu_to_le16(vport->rxq_model);
> +	iecm_vport_calc_total_qs(vport->adapter, &vport_msg);
> +
> +	iecm_vport_init_num_qs(vport, &vport_msg);
> +	iecm_vport_calc_num_q_groups(vport);
> +	iecm_vport_calc_num_q_vec(vport);
> +}
> +
>  /**
>   * iecm_is_capability_ena - Default implementation of capability checking
>   * @adapter: Private data struct
> @@ -2522,6 +3568,117 @@ static u16 iecm_get_reserved_vectors(struct iecm_adapter *adapter)
>  	return le16_to_cpu(caps->num_allocated_vectors);
>  }
>  
> +/**
> + * iecm_get_max_tx_bufs - Max scatter-gather TX buffers
> + * @adapter: Private data struct
> + *
> + * Return maximum number of buffers that can be used in scatter-gather before
> + * they need to be linearized for hardware.
> + */
> +static unsigned int iecm_get_max_tx_bufs(struct iecm_adapter *adapter)
> +{
> +	return ((struct virtchnl2_get_capabilities *)adapter->caps)->max_sg_bufs_per_tx_pkt;

93-cols line here, you can introduce some interm variables to reduce
the burden.

> +}
> +
> +/**
> + * iecm_add_del_vlans - Add or delete vlan filter
> + * @vport: vport structure
> + * @add: add or delete
> + *
> + * Request that the PF add one or more VLAN filters to our VSI.
> + */
> +static void iecm_add_del_vlans(struct iecm_vport *vport, bool add)
> +{
> +	struct virtchnl_vlan_supported_caps *filtering_support;
> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct virtchnl_vlan_filter_list_v2 *vvfl_v2;
> +	int total_vlans = 0, num_vlans, i, len;
> +	struct iecm_vlan_filter *f, *ftmp;
> +	struct virtchnl_vlan *vlan;
> +	enum virtchnl_ops vop;
> +	int err = 0;
> +
> +	spin_lock_bh(&adapter->vlan_list_lock);
> +
> +	list_for_each_entry(f, &adapter->config_data.vlan_filter_list, list) {
> +		if ((add && f->add) || (!add && f->remove))
> +			total_vlans++;
> +	}

Braces are redundant, if-something is one command from the outer view.

> +
> +	if (!total_vlans) {
> +		spin_unlock_bh(&adapter->vlan_list_lock);
> +		return;
> +	}
> +
> +	len = sizeof(struct virtchnl_vlan_filter_list_v2) +
> +		(IECM_VLANS_PER_MSG * sizeof(struct virtchnl_vlan_filter));
> +	vvfl_v2 = kzalloc(len, GFP_ATOMIC);
> +
> +	if (!vvfl_v2) {
> +		err = -ENOMEM;
> +		goto error;
> +	}
> +
> +	if (add)
> +		vop = VIRTCHNL_OP_ADD_VLAN_V2;
> +	else
> +		vop = VIRTCHNL_OP_DEL_VLAN_V2;
> +
> +	while (total_vlans) {
> +		if (total_vlans > IECM_VLANS_PER_MSG)
> +			num_vlans = IECM_VLANS_PER_MSG;
> +		else
> +			num_vlans = total_vlans;
> +		total_vlans -= num_vlans;
> +
> +		len = sizeof(struct virtchnl_vlan_filter_list_v2) +
> +			((num_vlans - 1) * sizeof(struct virtchnl_vlan_filter));
> +		vvfl_v2->vport_id = vport->vport_id;
> +		vvfl_v2->num_elements = num_vlans;
> +		i = 0;
> +		list_for_each_entry_safe(f, ftmp,
> +					 &adapter->config_data.vlan_filter_list,
> +					 list) {
> +			filtering_support =
> +			&adapter->vlan_caps->filtering.filtering_support;
> +			if (add && f->add) {
> +				/* give priority over outer if it's enabled */
> +				if (filtering_support->outer)
> +					vlan = &vvfl_v2->filters[i].outer;
> +				else
> +					vlan = &vvfl_v2->filters[i].inner;
> +				vlan->tci = f->vlan.vid;
> +				vlan->tpid = f->vlan.tpid;
> +				i++;
> +				f->add = false;
> +			} else if (!add && f->remove) {
> +				/* give priority over outer if it's enabled */
> +				if (filtering_support->outer)
> +					vlan = &vvfl_v2->filters[i].outer;
> +				else
> +					vlan = &vvfl_v2->filters[i].inner;
> +				vlan->tci = f->vlan.vid;
> +				vlan->tpid = f->vlan.tpid;
> +				i++;
> +				f->remove = false;
> +			}
> +			if (i == num_vlans)
> +				break;
> +		}
> +		spin_unlock_bh(&adapter->vlan_list_lock);
> +		iecm_send_mb_msg(adapter, vop, len, (u8 *)vvfl_v2);
> +		spin_lock_bh(&adapter->vlan_list_lock);
> +	}
> +	spin_unlock_bh(&adapter->vlan_list_lock);
> +	kfree(vvfl_v2);
> +	return;
> +error:
> +	spin_unlock_bh(&adapter->vlan_list_lock);
> +	if (err)
> +		dev_err(&adapter->pdev->dev,
> +			"Failed to add or del vlan filters %d", err);
> +}
> +
>  /**
>   * iecm_vc_ops_init - Initialize virtchnl common api
>   * @adapter: Driver specific private structure
> @@ -2548,21 +3705,21 @@ void iecm_vc_ops_init(struct iecm_adapter *adapter)
>  	vc_ops->enable_vport = iecm_send_enable_vport_msg;
>  	vc_ops->disable_vport = iecm_send_disable_vport_msg;
>  	vc_ops->destroy_vport = iecm_send_destroy_vport_msg;
> -	vc_ops->get_ptype = NULL;
> -	vc_ops->get_set_rss_key = NULL;
> -	vc_ops->get_set_rss_lut = NULL;
> -	vc_ops->get_set_rss_hash = NULL;
> -	vc_ops->adjust_qs = NULL;
> -	vc_ops->add_del_vlans = NULL;
> -	vc_ops->strip_vlan_msg = NULL;
> -	vc_ops->insert_vlan_msg = NULL;
> -	vc_ops->init_max_queues = NULL;
> -	vc_ops->get_max_tx_bufs = NULL;
> -	vc_ops->vportq_reg_init = NULL;
> -	vc_ops->alloc_vectors = NULL;
> -	vc_ops->dealloc_vectors = NULL;
> -	vc_ops->get_supported_desc_ids = NULL;
> -	vc_ops->get_stats_msg = NULL;
> +	vc_ops->get_ptype = iecm_send_get_rx_ptype_msg;
> +	vc_ops->get_set_rss_key = iecm_send_get_set_rss_key_msg;
> +	vc_ops->get_set_rss_lut = iecm_send_get_set_rss_lut_msg;
> +	vc_ops->get_set_rss_hash = iecm_send_get_set_rss_hash_msg;
> +	vc_ops->adjust_qs = iecm_vport_adjust_qs;
> +	vc_ops->add_del_vlans = iecm_add_del_vlans;
> +	vc_ops->strip_vlan_msg = iecm_send_strip_vlan_msg;
> +	vc_ops->insert_vlan_msg = iecm_send_insert_vlan_msg;
> +	vc_ops->init_max_queues = iecm_vport_init_max_qs;
> +	vc_ops->get_max_tx_bufs = iecm_get_max_tx_bufs;
> +	vc_ops->vportq_reg_init = iecm_queue_reg_init;
> +	vc_ops->alloc_vectors = iecm_send_alloc_vectors_msg;
> +	vc_ops->dealloc_vectors = iecm_send_dealloc_vectors_msg;
> +	vc_ops->get_supported_desc_ids = iecm_get_supported_desc_ids;
> +	vc_ops->get_stats_msg = iecm_send_get_stats_msg;
>  	vc_ops->recv_mbx_msg = NULL;
>  }
>  EXPORT_SYMBOL(iecm_vc_ops_init);
> diff --git a/drivers/net/ethernet/intel/include/iecm.h b/drivers/net/ethernet/intel/include/iecm.h
> index 8dd6272db7d3..d736db65da06 100644
> --- a/drivers/net/ethernet/intel/include/iecm.h
> +++ b/drivers/net/ethernet/intel/include/iecm.h
> @@ -346,6 +346,7 @@ struct iecm_vport {
>  	int num_rxq_grp;
>  	struct iecm_rxq_group *rxq_grps;
>  	u32 rxq_model;
> +	struct iecm_rx_ptype_decoded rx_ptype_lkup[IECM_RX_MAX_PTYPE];
>  
>  	struct iecm_adapter *adapter;
>  	struct net_device *netdev;
> @@ -382,6 +383,30 @@ enum iecm_user_flags {
>  	__IECM_USER_FLAGS_NBITS,
>  };
>  
> +#define IECM_GET_PTYPE_SIZE(p) \
> +	(sizeof(struct virtchnl2_ptype) + \
> +	(((p)->proto_id_count ? ((p)->proto_id_count - 1) : 0) * sizeof(u16)))
> +
> +#define IECM_TUN_IP_GRE (\
> +	IECM_PTYPE_TUNNEL_IP |\
> +	IECM_PTYPE_TUNNEL_IP_GRENAT)
> +
> +#define IECM_TUN_IP_GRE_MAC (\
> +	IECM_TUN_IP_GRE |\
> +	IECM_PTYPE_TUNNEL_IP_GRENAT_MAC)
> +
> +enum iecm_tunnel_state {
> +	IECM_PTYPE_TUNNEL_IP                    = BIT(0),
> +	IECM_PTYPE_TUNNEL_IP_GRENAT             = BIT(1),
> +	IECM_PTYPE_TUNNEL_IP_GRENAT_MAC         = BIT(2),
> +	IECM_PTYPE_TUNNEL_IP_GRENAT_MAC_VLAN    = BIT(3),
> +};
> +
> +struct iecm_ptype_state {
> +	bool outer_ip;
> +	bool outer_frag;
> +	u8 tunnel_state;
> +};
>  /* User defined configuration values */
>  struct iecm_user_config_data {
>  	u32 num_req_tx_qs; /* user requested TX queues through ethtool */
> @@ -534,6 +559,7 @@ int iecm_probe(struct pci_dev *pdev,
>  	       const struct pci_device_id __always_unused *ent,
>  	       struct iecm_adapter *adapter);
>  void iecm_remove(struct pci_dev *pdev);
> +void iecm_vport_adjust_qs(struct iecm_vport *vport);
>  int iecm_init_dflt_mbx(struct iecm_adapter *adapter);
>  void iecm_deinit_dflt_mbx(struct iecm_adapter *adapter);
>  void iecm_vc_ops_init(struct iecm_adapter *adapter);
> @@ -555,8 +581,15 @@ int iecm_send_config_rx_queues_msg(struct iecm_vport *vport);
>  int iecm_send_enable_vport_msg(struct iecm_vport *vport);
>  int iecm_send_disable_vport_msg(struct iecm_vport *vport);
>  int iecm_send_destroy_vport_msg(struct iecm_vport *vport);
> +int iecm_send_get_rx_ptype_msg(struct iecm_vport *vport);
> +int iecm_send_get_set_rss_key_msg(struct iecm_vport *vport, bool get);
> +int iecm_send_get_set_rss_lut_msg(struct iecm_vport *vport, bool get);
> +int iecm_send_get_set_rss_hash_msg(struct iecm_vport *vport, bool get);
> +int iecm_send_dealloc_vectors_msg(struct iecm_adapter *adapter);
> +int iecm_send_alloc_vectors_msg(struct iecm_adapter *adapter, u16 num_vectors);
>  int iecm_vport_params_buf_alloc(struct iecm_adapter *adapter);
>  void iecm_vport_params_buf_rel(struct iecm_adapter *adapter);
> +int iecm_send_get_stats_msg(struct iecm_vport *vport);
>  int iecm_get_vec_ids(struct iecm_adapter *adapter,
>  		     u16 *vecids, int num_vecids,
>  		     struct virtchnl2_vector_chunks *chunks);
> @@ -567,6 +600,9 @@ int iecm_send_mb_msg(struct iecm_adapter *adapter, enum virtchnl_ops op,
>  void iecm_vport_set_hsplit(struct iecm_vport *vport, bool ena);
>  int iecm_send_enable_channels_msg(struct iecm_vport *vport);
>  int iecm_send_disable_channels_msg(struct iecm_vport *vport);
> +bool iecm_is_feature_ena(struct iecm_vport *vport, netdev_features_t feature);
> +int iecm_check_descs(struct iecm_vport *vport, u64 rx_desc_ids,
> +		     u64 tx_desc_ids, u16 rxq_model, u16 txq_model);
>  int iecm_set_msg_pending(struct iecm_adapter *adapter,
>  			 struct iecm_ctlq_msg *ctlq_msg,
>  			 enum iecm_vport_vc_state err_enum);
> diff --git a/drivers/net/ethernet/intel/include/iecm_txrx.h b/drivers/net/ethernet/intel/include/iecm_txrx.h
> index 448cae0bf6e7..9f3086bfe575 100644
> --- a/drivers/net/ethernet/intel/include/iecm_txrx.h
> +++ b/drivers/net/ethernet/intel/include/iecm_txrx.h
> @@ -4,6 +4,9 @@
>  #ifndef _IECM_TXRX_H_
>  #define _IECM_TXRX_H_
>  
> +#include "virtchnl_lan_desc.h"
> +#include <linux/indirect_call_wrapper.h>
> +
>  #define IECM_LARGE_MAX_Q			256
>  #define IECM_MAX_Q				16
>  /* Mailbox Queue */
> @@ -77,9 +80,200 @@
>  #define IECM_MAX_RXBUFFER			9728
>  #define IECM_MAX_MTU		\
>  	(IECM_MAX_RXBUFFER - IECM_PACKET_HDR_PAD)
> -#define IECM_INT_NAME_STR_LEN	(IFNAMSIZ + 16)
> +
> +#define MAKEMASK(m, s)	((m) << (s))
> +
> +/* Checksum offload bits decoded from the receive descriptor. */
> +struct iecm_rx_csum_decoded {
> +	u8 l3l4p : 1;
> +	u8 ipe : 1;
> +	u8 eipe : 1;
> +	u8 eudpe : 1;
> +	u8 ipv6exadd : 1;
> +	u8 l4e : 1;
> +	u8 pprs : 1;
> +	u8 nat : 1;
> +	u8 rsc : 1;
> +	u8 raw_csum_inv : 1;
> +	u16 raw_csum;
> +};
> +
> +struct iecm_rx_extracted {
> +	unsigned int size;
> +	u16 vlan_tag;
> +	u16 rx_ptype;
> +};
>  
>  #define IECM_TX_COMPLQ_CLEAN_BUDGET	256
> +#define IECM_TX_MIN_LEN			17
> +#define IECM_TX_DESCS_FOR_SKB_DATA_PTR	1
> +#define IECM_TX_MAX_BUF			8
> +#define IECM_TX_DESCS_PER_CACHE_LINE	4
> +#define IECM_TX_DESCS_FOR_CTX		1
> +/* TX descriptors needed, worst case */
> +#define IECM_TX_DESC_NEEDED (MAX_SKB_FRAGS + IECM_TX_DESCS_FOR_CTX + \
> +			     IECM_TX_DESCS_PER_CACHE_LINE + \
> +			     IECM_TX_DESCS_FOR_SKB_DATA_PTR)
> +
> +/* The size limit for a transmit buffer in a descriptor is (16K - 1).
> + * In order to align with the read requests we will align the value to
> + * the nearest 4K which represents our maximum read request size.
> + */
> +#define IECM_TX_MAX_READ_REQ_SIZE	4096
> +#define IECM_TX_MAX_DESC_DATA		(16 * 1024 - 1)
> +#define IECM_TX_MAX_DESC_DATA_ALIGNED \
> +	(~(IECM_TX_MAX_READ_REQ_SIZE - 1) & IECM_TX_MAX_DESC_DATA)
> +
> +#define IECM_RX_DMA_ATTR \
> +	(DMA_ATTR_SKIP_CPU_SYNC | DMA_ATTR_WEAK_ORDERING)
> +#define IECM_RX_DESC(R, i)	\
> +	(&(((union virtchnl2_rx_desc *)((R)->desc_ring))[i]))
> +
> +struct iecm_page_info {
> +	dma_addr_t dma;
> +	struct page *page;
> +	unsigned int page_offset;
> +	u16 pagecnt_bias;
> +};
> +
> +struct iecm_rx_buf {
> +#define IECM_RX_BUF_MAX_PAGES 2
> +	struct iecm_page_info page_info[IECM_RX_BUF_MAX_PAGES];

As I said previously, it will most likely be rejected upstream. They
will either suggest using compounds or page_pool (it uses compounds
for non-zero-order pages) or maybe introduce folio support to the
networking stack or so, but not such stuff.

> +	u8 page_indx;
> +	u16 buf_id;
> +	u16 buf_size;
> +	struct sk_buff *skb;
> +};
> +
> +/* Packet type non-ip values */
> +enum iecm_rx_ptype_l2 {
> +	IECM_RX_PTYPE_L2_RESERVED	= 0,
> +	IECM_RX_PTYPE_L2_MAC_PAY2	= 1,
> +	IECM_RX_PTYPE_L2_TIMESYNC_PAY2	= 2,
> +	IECM_RX_PTYPE_L2_FIP_PAY2	= 3,
> +	IECM_RX_PTYPE_L2_OUI_PAY2	= 4,
> +	IECM_RX_PTYPE_L2_MACCNTRL_PAY2	= 5,
> +	IECM_RX_PTYPE_L2_LLDP_PAY2	= 6,
> +	IECM_RX_PTYPE_L2_ECP_PAY2	= 7,
> +	IECM_RX_PTYPE_L2_EVB_PAY2	= 8,
> +	IECM_RX_PTYPE_L2_QCN_PAY2	= 9,
> +	IECM_RX_PTYPE_L2_EAPOL_PAY2	= 10,
> +	IECM_RX_PTYPE_L2_ARP		= 11,
> +};
> +
> +enum iecm_rx_ptype_outer_ip {
> +	IECM_RX_PTYPE_OUTER_L2	= 0,
> +	IECM_RX_PTYPE_OUTER_IP	= 1,
> +};
> +
> +enum iecm_rx_ptype_outer_ip_ver {
> +	IECM_RX_PTYPE_OUTER_NONE	= 0,
> +	IECM_RX_PTYPE_OUTER_IPV4	= 1,
> +	IECM_RX_PTYPE_OUTER_IPV6	= 2,
> +};
> +
> +enum iecm_rx_ptype_outer_fragmented {
> +	IECM_RX_PTYPE_NOT_FRAG	= 0,
> +	IECM_RX_PTYPE_FRAG	= 1,
> +};
> +
> +enum iecm_rx_ptype_tunnel_type {
> +	IECM_RX_PTYPE_TUNNEL_NONE		= 0,
> +	IECM_RX_PTYPE_TUNNEL_IP_IP		= 1,
> +	IECM_RX_PTYPE_TUNNEL_IP_GRENAT		= 2,
> +	IECM_RX_PTYPE_TUNNEL_IP_GRENAT_MAC	= 3,
> +	IECM_RX_PTYPE_TUNNEL_IP_GRENAT_MAC_VLAN	= 4,
> +};
> +
> +enum iecm_rx_ptype_tunnel_end_prot {
> +	IECM_RX_PTYPE_TUNNEL_END_NONE	= 0,
> +	IECM_RX_PTYPE_TUNNEL_END_IPV4	= 1,
> +	IECM_RX_PTYPE_TUNNEL_END_IPV6	= 2,
> +};
> +
> +enum iecm_rx_ptype_inner_prot {
> +	IECM_RX_PTYPE_INNER_PROT_NONE		= 0,
> +	IECM_RX_PTYPE_INNER_PROT_UDP		= 1,
> +	IECM_RX_PTYPE_INNER_PROT_TCP		= 2,
> +	IECM_RX_PTYPE_INNER_PROT_SCTP		= 3,
> +	IECM_RX_PTYPE_INNER_PROT_ICMP		= 4,
> +	IECM_RX_PTYPE_INNER_PROT_TIMESYNC	= 5,
> +};
> +
> +enum iecm_rx_ptype_payload_layer {
> +	IECM_RX_PTYPE_PAYLOAD_LAYER_NONE	= 0,
> +	IECM_RX_PTYPE_PAYLOAD_LAYER_PAY2	= 1,
> +	IECM_RX_PTYPE_PAYLOAD_LAYER_PAY3	= 2,
> +	IECM_RX_PTYPE_PAYLOAD_LAYER_PAY4	= 3,
> +};
> +
> +struct iecm_rx_ptype_decoded {
> +	u32 ptype:10;
> +	u32 known:1;
> +	u32 outer_ip:1;
> +	u32 outer_ip_ver:2;
> +	u32 outer_frag:1;
> +	u32 tunnel_type:3;
> +	u32 tunnel_end_prot:2;
> +	u32 tunnel_end_frag:1;
> +	u32 inner_prot:4;
> +	u32 payload_layer:3;
> +};
> +
> +enum iecm_rx_hsplit {
> +	IECM_RX_NO_HDR_SPLIT = 0,
> +	IECM_RX_HDR_SPLIT = 1,
> +};
> +
> +/* The iecm_ptype_lkup table is used to convert from the 10-bit ptype in the
> + * hardware to a bit-field that can be used by SW to more easily determine the
> + * packet type.
> + *
> + * Macros are used to shorten the table lines and make this table human
> + * readable.
> + *
> + * We store the PTYPE in the top byte of the bit field - this is just so that
> + * we can check that the table doesn't have a row missing, as the index into
> + * the table should be the PTYPE.
> + *
> + * Typical work flow:
> + *
> + * IF NOT iecm_ptype_lkup[ptype].known
> + * THEN
> + *      Packet is unknown
> + * ELSE IF iecm_ptype_lkup[ptype].outer_ip == IECM_RX_PTYPE_OUTER_IP
> + *      Use the rest of the fields to look at the tunnels, inner protocols, etc
> + * ELSE
> + *      Use the enum iecm_rx_ptype_l2 to decode the packet type
> + * ENDIF
> + */
> +/* macro to make the table lines short */
> +#define IECM_PTT(PTYPE, OUTER_IP, OUTER_IP_VER, OUTER_FRAG, T, TE, TEF, I, PL)\
> +	{	PTYPE, \
> +		1, \
> +		IECM_RX_PTYPE_OUTER_##OUTER_IP, \
> +		IECM_RX_PTYPE_OUTER_##OUTER_IP_VER, \
> +		IECM_RX_PTYPE_##OUTER_FRAG, \
> +		IECM_RX_PTYPE_TUNNEL_##T, \
> +		IECM_RX_PTYPE_TUNNEL_END_##TE, \
> +		IECM_RX_PTYPE_##TEF, \
> +		IECM_RX_PTYPE_INNER_PROT_##I, \
> +		IECM_RX_PTYPE_PAYLOAD_LAYER_##PL }
> +
> +#define IECM_PTT_UNUSED_ENTRY(PTYPE) { PTYPE, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
> +
> +/* shorter macros makes the table fit but are terse */
> +#define IECM_RX_PTYPE_NOF		IECM_RX_PTYPE_NOT_FRAG
> +#define IECM_RX_PTYPE_FRG		IECM_RX_PTYPE_FRAG
> +#define IECM_RX_PTYPE_INNER_PROT_TS	IECM_RX_PTYPE_INNER_PROT_TIMESYNC
> +#define IECM_RX_SUPP_PTYPE		18
> +#define IECM_RX_MAX_PTYPE		1024
> +#define IECM_RX_MAX_BASE_PTYPE		256
> +
> +#define IECM_INT_NAME_STR_LEN	(IFNAMSIZ + 16)
> +
> +/* Lookup table mapping the HW PTYPE to the bit field for decoding */
> +extern const struct iecm_rx_ptype_decoded iecm_ptype_lookup[IECM_RX_MAX_PTYPE];
>  
>  enum iecm_queue_flags_t {
>  	__IECM_Q_GEN_CHK,
> @@ -318,6 +512,4 @@ void iecm_vport_calc_total_qs(struct iecm_adapter *adapter,
>  			      struct virtchnl2_create_vport *vport_msg);
>  void iecm_vport_calc_num_q_groups(struct iecm_vport *vport);
>  void iecm_vport_calc_num_q_vec(struct iecm_vport *vport);
> -irqreturn_t
> -iecm_vport_intr_clean_queues(int __always_unused irq, void *data);
>  #endif /* !_IECM_TXRX_H_ */
> -- 
> 2.33.0

Thanks,
Al

^ permalink raw reply	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 08/19] iecm: add interrupts and configure netdev
  2022-01-28  0:09 ` [Intel-wired-lan] [PATCH net-next 08/19] iecm: add interrupts and configure netdev Alan Brady
@ 2022-01-28 13:34   ` Alexander Lobakin
  2022-02-02 23:17     ` Brady, Alan
  0 siblings, 1 reply; 89+ messages in thread
From: Alexander Lobakin @ 2022-01-28 13:34 UTC (permalink / raw)
  To: intel-wired-lan

From: Alan Brady <alan.brady@intel.com>
Date: Thu, 27 Jan 2022 16:09:58 -0800

> This finishes implementing init_task by adding everything we need to
> configure the netdevice for the vport and setup its interrupts.
> 
> Signed-off-by: Phani Burra <phani.r.burra@intel.com>
> Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
> Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
> Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
> Signed-off-by: Alice Michael <alice.michael@intel.com>
> Signed-off-by: Alan Brady <alan.brady@intel.com>
> ---
>  drivers/net/ethernet/intel/iecm/iecm_lib.c    | 813 +++++++++++++++++-
>  drivers/net/ethernet/intel/iecm/iecm_txrx.c   |  17 +
>  .../net/ethernet/intel/iecm/iecm_virtchnl.c   | 165 ++++
>  drivers/net/ethernet/intel/include/iecm.h     | 112 ++-
>  .../net/ethernet/intel/include/iecm_txrx.h    |   2 +
>  5 files changed, 1104 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/net/ethernet/intel/iecm/iecm_lib.c b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> index aab8ee40424e..255b04c25683 100644
> --- a/drivers/net/ethernet/intel/iecm/iecm_lib.c
> +++ b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> @@ -5,11 +5,35 @@
>  
>  #include "iecm.h"
>  
> +static const struct net_device_ops iecm_netdev_ops_splitq;
> +static const struct net_device_ops iecm_netdev_ops_singleq;
> +
>  const char * const iecm_vport_vc_state_str[] = {
>  	IECM_FOREACH_VPORT_VC_STATE(IECM_GEN_STRING)
>  };
>  EXPORT_SYMBOL(iecm_vport_vc_state_str);
>  
> +/**
> + * iecm_get_vport_index - Get the vport index
> + * @adapter: adapter structure to get the vports array
> + * @vport: vport pointer for which the index to find
> + */
> +static int iecm_get_vport_index(struct iecm_adapter *adapter,
> +				struct iecm_vport *vport)
> +{
> +	int i, err = -EINVAL;
> +
> +	if (!adapter->vports)
> +		return err;
> +
> +	for (i = 0; i < adapter->num_alloc_vport; i++) {
> +		if (adapter->vports[i] != vport)
> +			continue;
> +		return i;
> +	}
> +	return err;
> +}
> +
>  /**
>   * iecm_is_feature_ena - Determine if a particular feature is enabled
>   * @vport: vport to check
> @@ -29,6 +53,595 @@ bool iecm_is_feature_ena(struct iecm_vport *vport, netdev_features_t feature)
>  	return ena;
>  }
>  
> +/**
> + * iecm_is_vlan_cap_ena - Check if VLAN capability is enabled
> + * @adapter: pointer to adapter
> + * @vcaps: VLAN capability bit
> + *
> + * Returns true if VLAN capability is set, false otherwise
> + */
> +static bool iecm_is_vlan_cap_ena(struct iecm_adapter *adapter,
> +				 enum iecm_vlan_caps vcaps)
> +{
> +	if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS, VIRTCHNL2_CAP_VLAN)) {
> +		struct virtchnl_vlan_supported_caps *offload;
> +
> +		if (!adapter->vlan_caps)
> +			return false;
> +
> +		switch (vcaps) {
> +		case IECM_CAP_VLAN_CTAG_INSERT:
> +			offload =
> +			&adapter->vlan_caps->offloads.insertion_support;
> +			if ((offload->outer & IECM_VLAN_8100) == IECM_VLAN_8100 ||
> +			    (offload->inner & IECM_VLAN_8100) == IECM_VLAN_8100)
> +				return true;
> +			break;
> +		case IECM_CAP_VLAN_STAG_INSERT:
> +			offload =
> +			&adapter->vlan_caps->offloads.insertion_support;
> +			if ((offload->outer & IECM_VLAN_88A8) == IECM_VLAN_88A8)
> +				return true;
> +			break;
> +		case IECM_CAP_VLAN_CTAG_STRIP:
> +			offload =
> +			&adapter->vlan_caps->offloads.stripping_support;
> +			if ((offload->outer & IECM_VLAN_8100) == IECM_VLAN_8100 ||
> +			    (offload->inner & IECM_VLAN_8100) == IECM_VLAN_8100)
> +				return true;
> +			break;
> +		case IECM_CAP_VLAN_STAG_STRIP:
> +			offload =
> +			&adapter->vlan_caps->offloads.stripping_support;
> +			if ((offload->outer & IECM_VLAN_88A8) == IECM_VLAN_88A8)
> +				return true;
> +			break;
> +		case IECM_CAP_VLAN_CTAG_ADD_DEL:
> +			offload =
> +			&adapter->vlan_caps->filtering.filtering_support;
> +			if ((offload->outer & VIRTCHNL_VLAN_ETHERTYPE_8100) ||
> +			    (offload->inner & VIRTCHNL_VLAN_ETHERTYPE_8100))
> +				return true;
> +			break;
> +		case IECM_CAP_VLAN_STAG_ADD_DEL:
> +			offload =
> +			&adapter->vlan_caps->filtering.filtering_support;
> +			if ((offload->outer & VIRTCHNL_VLAN_ETHERTYPE_88A8) ||
> +			    (offload->inner & VIRTCHNL_VLAN_ETHERTYPE_88A8))
> +				return true;
> +			break;
> +		default:
> +			dev_err(&adapter->pdev->dev, "Invalid VLAN capability %d\n",
> +				vcaps);
> +			return false;
> +		}
> +	} else if (iecm_is_cap_ena(adapter, IECM_BASE_CAPS,
> +				   VIRTCHNL2_CAP_VLAN)) {
> +		switch (vcaps) {
> +		case IECM_CAP_VLAN_CTAG_INSERT:
> +		case IECM_CAP_VLAN_CTAG_STRIP:
> +		case IECM_CAP_VLAN_CTAG_ADD_DEL:
> +			return true;
> +		default:
> +			return false;
> +		}
> +	}
> +
> +	return false;
> +}
> +
> +/**
> + * iecm_netdev_to_vport - get a vport handle from a netdev
> + * @netdev: network interface device structure
> + */
> +struct iecm_vport *iecm_netdev_to_vport(struct net_device *netdev)
> +{
> +	struct iecm_netdev_priv *np = netdev_priv(netdev);
> +
> +	return np->vport;
> +}
> +
> +/**
> + * iecm_mb_intr_rel_irq - Free the IRQ association with the OS
> + * @adapter: adapter structure
> + */
> +static void iecm_mb_intr_rel_irq(struct iecm_adapter *adapter)
> +{
> +	int irq_num;
> +
> +	irq_num = adapter->msix_entries[0].vector;
> +	free_irq(irq_num, adapter);
> +}
> +
> +/**
> + * iecm_intr_rel - Release interrupt capabilities and free memory
> + * @adapter: adapter to disable interrupts on
> + */
> +static void iecm_intr_rel(struct iecm_adapter *adapter)
> +{
> +	if (!adapter->msix_entries)
> +		return;
> +	clear_bit(__IECM_MB_INTR_MODE, adapter->flags);
> +	clear_bit(__IECM_MB_INTR_TRIGGER, adapter->flags);
> +	iecm_mb_intr_rel_irq(adapter);
> +
> +	pci_free_irq_vectors(adapter->pdev);
> +	if (adapter->dev_ops.vc_ops.dealloc_vectors) {
> +		int err;
> +
> +		err = adapter->dev_ops.vc_ops.dealloc_vectors(adapter);
> +		if (err) {
> +			dev_err(&adapter->pdev->dev,
> +				"Failed to deallocate vectors: %d\n", err);
> +		}
> +	}
> +	kfree(adapter->msix_entries);
> +	adapter->msix_entries = NULL;
> +	kfree(adapter->req_vec_chunks);
> +	adapter->req_vec_chunks = NULL;
> +}
> +
> +/**
> + * iecm_mb_intr_clean - Interrupt handler for the mailbox
> + * @irq: interrupt number
> + * @data: pointer to the adapter structure
> + */
> +static irqreturn_t iecm_mb_intr_clean(int __always_unused irq, void *data)
> +{
> +	struct iecm_adapter *adapter = (struct iecm_adapter *)data;
> +
> +	set_bit(__IECM_MB_INTR_TRIGGER, adapter->flags);
> +	queue_delayed_work(adapter->serv_wq, &adapter->serv_task,
> +			   msecs_to_jiffies(0));
> +	return IRQ_HANDLED;
> +}
> +
> +/**
> + * iecm_mb_irq_enable - Enable MSIX interrupt for the mailbox
> + * @adapter: adapter to get the hardware address for register write
> + */
> +static void iecm_mb_irq_enable(struct iecm_adapter *adapter)
> +{
> +	struct iecm_hw *hw = &adapter->hw;
> +	struct iecm_intr_reg *intr = &adapter->mb_vector.intr_reg;

Please don't break Reverse Christmas Tree declaration style. *intr
doesn't depend on *hw.

> +	u32 val;
> +
> +	val = intr->dyn_ctl_intena_m | intr->dyn_ctl_itridx_m;
> +	wr32(hw, intr->dyn_ctl, val);
> +	wr32(hw, intr->icr_ena, intr->icr_ena_ctlq_m);
> +}
> +
> +/**
> + * iecm_mb_intr_req_irq - Request irq for the mailbox interrupt
> + * @adapter: adapter structure to pass to the mailbox irq handler
> + */
> +static int iecm_mb_intr_req_irq(struct iecm_adapter *adapter)
> +{
> +	struct iecm_q_vector *mb_vector = &adapter->mb_vector;
> +	int irq_num, mb_vidx = 0, err;
> +
> +	irq_num = adapter->msix_entries[mb_vidx].vector;
> +	snprintf(mb_vector->name, sizeof(mb_vector->name) - 1,
> +		 "%s-%s-%d", dev_driver_string(&adapter->pdev->dev),
> +		 "Mailbox", mb_vidx);
> +	err = request_irq(irq_num, adapter->irq_mb_handler, 0,
> +			  mb_vector->name, adapter);
> +	if (err) {
> +		dev_err(&adapter->pdev->dev,
> +			"Request_irq for mailbox failed, error: %d\n", err);
> +		return err;
> +	}
> +	set_bit(__IECM_MB_INTR_MODE, adapter->flags);
> +	return 0;
> +}
> +
> +/**
> + * iecm_get_mb_vec_id - Get vector index for mailbox
> + * @adapter: adapter structure to access the vector chunks
> + *
> + * The first vector id in the requested vector chunks from the CP is for
> + * the mailbox
> + */
> +static void iecm_get_mb_vec_id(struct iecm_adapter *adapter)
> +{
> +	if (adapter->req_vec_chunks) {
> +		struct virtchnl2_get_capabilities *caps;
> +
> +		caps = (struct virtchnl2_get_capabilities *)adapter->caps;
> +		adapter->mb_vector.v_idx = le16_to_cpu(caps->mailbox_vector_id);
> +	} else {
> +		adapter->mb_vector.v_idx = 0;
> +	}
> +}
> +
> +/**
> + * iecm_mb_intr_init - Initialize the mailbox interrupt
> + * @adapter: adapter structure to store the mailbox vector
> + */
> +static int iecm_mb_intr_init(struct iecm_adapter *adapter)
> +{
> +	adapter->dev_ops.reg_ops.mb_intr_reg_init(adapter);
> +	adapter->irq_mb_handler = iecm_mb_intr_clean;
> +	return iecm_mb_intr_req_irq(adapter);
> +}
> +
> +/**
> + * iecm_intr_distribute - Distribute MSIX vectors
> + * @adapter: adapter structure to get the vports
> + * @pre_req: before or after msi request
> + *
> + * Distribute the MSIX vectors acquired from the OS to the vports based on the
> + * num of vectors requested by each vport
> + */
> +static int
> +iecm_intr_distribute(struct iecm_adapter *adapter, bool pre_req)
> +{
> +	struct iecm_vport *vport = adapter->vports[0];
> +	int err = 0;
> +
> +	if (pre_req) {
> +		u16 vecs_avail;
> +
> +		vecs_avail = iecm_get_reserved_vecs(adapter);
> +		if (vecs_avail < IECM_MIN_VEC) {
> +			return -EAGAIN;
> +		} else if (vecs_avail == IECM_MIN_VEC) {
> +			vport->num_q_vectors = IECM_MIN_Q_VEC;
> +		} else {
> +			vport->num_q_vectors = vecs_avail - IECM_NONQ_VEC -
> +						IECM_MAX_RDMA_VEC;
> +		}
> +	} else {
> +		if (adapter->num_msix_entries != adapter->num_req_msix)
> +			vport->num_q_vectors = adapter->num_msix_entries -
> +					       IECM_NONQ_VEC;
> +	}
> +
> +	return err;
> +}
> +
> +/**
> + * iecm_intr_req - Request interrupt capabilities
> + * @adapter: adapter to enable interrupts on
> + *
> + * Returns 0 on success, negative on failure
> + */
> +static int iecm_intr_req(struct iecm_adapter *adapter)
> +{
> +	int min_vectors, max_vectors, err = 0;
> +	int num_q_vecs, total_num_vecs;
> +	u16 vecids[IECM_MAX_VECIDS];
> +	unsigned int vector;
> +	int v_actual;
> +
> +	err = iecm_intr_distribute(adapter, true);
> +	if (err)
> +		return err;
> +
> +	num_q_vecs = adapter->vports[0]->num_q_vectors;
> +
> +	total_num_vecs = num_q_vecs + IECM_NONQ_VEC;
> +
> +	if (adapter->dev_ops.vc_ops.alloc_vectors) {
> +		err = adapter->dev_ops.vc_ops.alloc_vectors(adapter,
> +							    num_q_vecs);
> +		if (err) {
> +			dev_err(&adapter->pdev->dev,
> +				"Failed to allocate vectors: %d\n", err);
> +			return -EAGAIN;
> +		}
> +	}
> +
> +	min_vectors = IECM_MIN_VEC;
> +	max_vectors = total_num_vecs;
> +	v_actual = pci_alloc_irq_vectors(adapter->pdev, min_vectors,
> +					 max_vectors, PCI_IRQ_MSIX);
> +	if (v_actual < 0) {
> +		dev_err(&adapter->pdev->dev, "Failed to allocate MSIX vectors: %d\n",
> +			v_actual);
> +		if (adapter->dev_ops.vc_ops.dealloc_vectors)
> +			adapter->dev_ops.vc_ops.dealloc_vectors(adapter);
> +		return -EAGAIN;
> +	}
> +
> +	adapter->msix_entries = kcalloc(v_actual, sizeof(struct msix_entry),
> +					GFP_KERNEL);
> +
> +	if (!adapter->msix_entries) {
> +		pci_free_irq_vectors(adapter->pdev);
> +		if (adapter->dev_ops.vc_ops.dealloc_vectors)
> +			adapter->dev_ops.vc_ops.dealloc_vectors(adapter);
> +		return -ENOMEM;
> +	}
> +
> +	iecm_get_mb_vec_id(adapter);
> +
> +	if (adapter->req_vec_chunks) {
> +		struct virtchnl2_vector_chunks *vchunks;
> +		struct virtchnl2_alloc_vectors *ac;
> +
> +		ac = adapter->req_vec_chunks;
> +		vchunks = &ac->vchunks;
> +
> +		iecm_get_vec_ids(adapter, vecids, IECM_MAX_VECIDS, vchunks);
> +	} else {
> +		int i = 0;
> +
> +		for (i = 0; i < v_actual; i++)
> +			vecids[i] = i;
> +	}
> +
> +	for (vector = 0; vector < v_actual; vector++) {
> +		adapter->msix_entries[vector].entry = vecids[vector];
> +		adapter->msix_entries[vector].vector =
> +			pci_irq_vector(adapter->pdev, vector);
> +	}
> +	adapter->num_msix_entries = v_actual;
> +	adapter->num_req_msix = total_num_vecs;
> +
> +	iecm_intr_distribute(adapter, false);
> +
> +	err = iecm_mb_intr_init(adapter);
> +	if (err)
> +		goto intr_rel;
> +	iecm_mb_irq_enable(adapter);
> +	return err;
> +
> +intr_rel:
> +	iecm_intr_rel(adapter);
> +	return err;
> +}
> +
> +/**
> + * iecm_find_mac_filter - Search filter list for specific mac filter
> + * @vport: main vport structure
> + * @macaddr: the MAC address
> + *
> + * Returns ptr to the filter object or NULL. Must be called while holding the
> + * mac_filter_list_lock.
> + **/
> +static struct
> +iecm_mac_filter *iecm_find_mac_filter(struct iecm_vport *vport,
> +				      const u8 *macaddr)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct iecm_mac_filter *f;
> +
> +	if (!macaddr)
> +		return NULL;
> +
> +	list_for_each_entry(f, &adapter->config_data.mac_filter_list, list) {
> +		if (ether_addr_equal(macaddr, f->macaddr))
> +			return f;
> +	}

Excessive braces again.

> +	return NULL;
> +}
> +
> +/**
> + * __iecm_add_mac_filter - Add mac filter helper function
> + * @vport: main vport struct
> + * @macaddr: address to add
> + *
> + * Takes mac_filter_list_lock spinlock to add new filter to list.
> + */
> +static struct
> +iecm_mac_filter *__iecm_add_mac_filter(struct iecm_vport *vport,
> +				       const u8 *macaddr)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct iecm_mac_filter *f = NULL;
> +
> +	spin_lock_bh(&adapter->mac_filter_list_lock);
> +	f = iecm_find_mac_filter(vport, macaddr);
> +	if (!f) {
> +		f = kzalloc(sizeof(*f), GFP_ATOMIC);
> +		if (!f) {
> +			dev_err(&adapter->pdev->dev, "Failed to allocate filter: %pM",
> +				macaddr);
> +			goto error;
> +		}
> +
> +		ether_addr_copy(f->macaddr, macaddr);
> +
> +		list_add_tail(&f->list, &adapter->config_data.mac_filter_list);
> +		f->add = true;
> +	} else {
> +		f->remove = false;
> +	}

	if (f) {
		f->remove = false;
		goto error; /* It's better to rename it */
	}

	f = kzalloc(...

-1 indent level.

> +error:
> +	spin_unlock_bh(&adapter->mac_filter_list_lock);
> +
> +	return f;
> +}
> +
> +/**
> + * iecm_add_mac_filter - Add a mac filter to the filter list
> + * @vport: main vport structure
> + * @macaddr: the MAC address
> + *
> + * Returns ptr to the filter or NULL on error. If interface is up, we'll also
> + * send the virtchnl message to tell hardware about the filter.
> + **/
> +static struct iecm_mac_filter *iecm_add_mac_filter(struct iecm_vport *vport,
> +						   const u8 *macaddr)
> +{
> +	struct iecm_mac_filter *f;
> +
> +	if (!macaddr)
> +		return NULL;
> +
> +	f = __iecm_add_mac_filter(vport, macaddr);
> +	if (!f)
> +		return NULL;
> +
> +	if (vport->adapter->state == __IECM_UP)
> +		iecm_add_del_ether_addrs(vport, true, false);
> +
> +	return f;
> +}
> +
> +/**
> + * iecm_init_mac_addr - initialize mac address for vport
> + * @vport: main vport structure
> + * @netdev: pointer to netdev struct associated with this vport
> + */
> +static int iecm_init_mac_addr(struct iecm_vport *vport,
> +			      struct net_device *netdev)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +
> +	if (!is_valid_ether_addr(vport->default_mac_addr)) {
> +		if (!iecm_is_cap_ena(vport->adapter, IECM_OTHER_CAPS,
> +				     VIRTCHNL2_CAP_MACFILTER)) {
> +			dev_err(&adapter->pdev->dev,
> +				"MAC address not provided and capability is not set\n");
> +			return -EINVAL;
> +		}
> +
> +		dev_info(&adapter->pdev->dev, "Invalid MAC address %pM, using random\n",
> +			 vport->default_mac_addr);
> +		eth_hw_addr_random(netdev);
> +
> +		if (!iecm_add_mac_filter(vport, netdev->dev_addr))
> +			return -ENOMEM;
> +
> +		ether_addr_copy(vport->default_mac_addr, netdev->dev_addr);
> +	} else {
> +		dev_addr_mod(netdev, 0, vport->default_mac_addr, ETH_ALEN);
> +		ether_addr_copy(netdev->perm_addr, vport->default_mac_addr);
> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * iecm_cfg_netdev - Allocate, configure and register a netdev
> + * @vport: main vport structure
> + *
> + * Returns 0 on success, negative value on failure.
> + */
> +static int iecm_cfg_netdev(struct iecm_vport *vport)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	netdev_features_t dflt_features;
> +	netdev_features_t offloads = 0;
> +	struct iecm_netdev_priv *np;
> +	struct net_device *netdev;
> +	u16 max_q;
> +	int err;
> +
> +	lockdep_assert_held(&adapter->sw_mutex);
> +
> +	/* It's possible we already have a netdev allocated and registered for
> +	 * this vport
> +	 */
> +	if (adapter->netdevs[vport->idx]) {
> +		netdev = adapter->netdevs[vport->idx];
> +		np = netdev_priv(netdev);
> +		np->vport = vport;
> +		vport->netdev = netdev;
> +
> +		return iecm_init_mac_addr(vport, netdev);
> +	}
> +
> +	max_q = adapter->max_queue_limit;
> +
> +	netdev = alloc_etherdev_mqs(sizeof(struct iecm_netdev_priv),
> +				    max_q, max_q);
> +	if (!netdev)
> +		return -ENOMEM;
> +	vport->netdev = netdev;
> +	np = netdev_priv(netdev);
> +	np->vport = vport;
> +
> +	err = iecm_init_mac_addr(vport, netdev);
> +	if (err)
> +		goto err;
> +
> +	/* assign netdev_ops */
> +	if (iecm_is_queue_model_split(vport->txq_model))
> +		netdev->netdev_ops = &iecm_netdev_ops_splitq;
> +	else
> +		netdev->netdev_ops = &iecm_netdev_ops_singleq;
> +
> +	/* setup watchdog timeout value to be 5 second */
> +	netdev->watchdog_timeo = 5 * HZ;
> +
> +	/* configure default MTU size */
> +	netdev->min_mtu = ETH_MIN_MTU;
> +	netdev->max_mtu = vport->max_mtu;
> +
> +	dflt_features = NETIF_F_SG	|
> +			NETIF_F_HIGHDMA;
> +
> +	if (iecm_is_cap_ena_all(adapter, IECM_RSS_CAPS, IECM_CAP_RSS))
> +		dflt_features |= NETIF_F_RXHASH;
> +	if (iecm_is_cap_ena_all(adapter, IECM_CSUM_CAPS, IECM_CAP_RX_CSUM_L4V4))
> +		dflt_features |= NETIF_F_IP_CSUM;
> +	if (iecm_is_cap_ena_all(adapter, IECM_CSUM_CAPS, IECM_CAP_RX_CSUM_L4V6))
> +		dflt_features |= NETIF_F_IPV6_CSUM;
> +	if (iecm_is_cap_ena(adapter, IECM_CSUM_CAPS, IECM_CAP_RX_CSUM))
> +		dflt_features |= NETIF_F_RXCSUM;
> +	if (iecm_is_cap_ena_all(adapter, IECM_CSUM_CAPS, IECM_CAP_SCTP_CSUM))
> +		dflt_features |= NETIF_F_SCTP_CRC;
> +
> +	if (iecm_is_vlan_cap_ena(adapter, IECM_CAP_VLAN_CTAG_INSERT))
> +		dflt_features |= IECM_F_HW_VLAN_CTAG_TX;
> +	if (iecm_is_vlan_cap_ena(adapter, IECM_CAP_VLAN_CTAG_STRIP))
> +		dflt_features |= IECM_F_HW_VLAN_CTAG_RX;
> +	if (iecm_is_vlan_cap_ena(adapter, IECM_CAP_VLAN_CTAG_ADD_DEL))
> +		dflt_features |= IECM_F_HW_VLAN_CTAG_FILTER;
> +
> +	if (iecm_is_vlan_cap_ena(adapter, IECM_CAP_VLAN_STAG_INSERT))
> +		dflt_features |= NETIF_F_HW_VLAN_STAG_TX;
> +	if (iecm_is_vlan_cap_ena(adapter, IECM_CAP_VLAN_STAG_STRIP))
> +		dflt_features |= NETIF_F_HW_VLAN_STAG_RX;
> +	if (iecm_is_vlan_cap_ena(adapter, IECM_CAP_VLAN_STAG_ADD_DEL))
> +		dflt_features |= NETIF_F_HW_VLAN_STAG_FILTER;
> +	/* Enable cloud filter if ADQ is supported */
> +	if (iecm_is_cap_ena(adapter, IECM_BASE_CAPS, VIRTCHNL2_CAP_ADQ) ||
> +	    iecm_is_cap_ena(adapter, IECM_OTHER_CAPS, VIRTCHNL2_CAP_ADQ))
> +		dflt_features |= NETIF_F_HW_TC;
> +	if (iecm_is_cap_ena(adapter, IECM_SEG_CAPS, VIRTCHNL2_CAP_SEG_IPV4_TCP))
> +		dflt_features |= NETIF_F_TSO;
> +	if (iecm_is_cap_ena(adapter, IECM_SEG_CAPS, VIRTCHNL2_CAP_SEG_IPV6_TCP))
> +		dflt_features |= NETIF_F_TSO6;
> +	if (iecm_is_cap_ena_all(adapter, IECM_SEG_CAPS,
> +				VIRTCHNL2_CAP_SEG_IPV4_UDP |
> +				VIRTCHNL2_CAP_SEG_IPV6_UDP))
> +		dflt_features |= NETIF_F_GSO_UDP_L4;
> +	if (iecm_is_cap_ena_all(adapter, IECM_RSC_CAPS, IECM_CAP_RSC))
> +		offloads |= NETIF_F_GRO_HW;

I see `offloads` being used only here, |= -> =.

> +	netdev->features |= dflt_features;
> +	netdev->hw_features |= dflt_features | offloads;
> +	netdev->hw_enc_features |= dflt_features | offloads;
> +
> +	SET_NETDEV_DEV(netdev, &adapter->pdev->dev);
> +
> +	/* carrier off on init to avoid Tx hangs */
> +	netif_carrier_off(netdev);
> +
> +	/* make sure transmit queues start off as stopped */
> +	netif_tx_stop_all_queues(netdev);
> +
> +	/* register last */
> +	err = register_netdev(netdev);
> +	if (err)
> +		goto err;
> +
> +	/* The vport can be arbitrarily released so we need to also track
> +	 * netdevs in the adapter struct
> +	 */
> +	adapter->netdevs[vport->idx] = netdev;
> +
> +	return 0;
> +err:
> +	free_netdev(vport->netdev);
> +	vport->netdev = NULL;
> +
> +	return err;
> +}
> +
>  /**
>   * iecm_cfg_hw - Initialize HW struct
>   * @adapter: adapter to setup hw struct for
> @@ -77,6 +690,24 @@ static int iecm_get_free_slot(void *array, int size, int curr)
>  	return next;
>  }
>  
> +/**
> + * iecm_decfg_netdev - Unregister the netdev
> + * @vport: vport for which netdev to be unregistred
> + */
> +static void iecm_decfg_netdev(struct iecm_vport *vport)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +
> +	if (!vport->netdev)
> +		return;
> +
> +	unregister_netdev(vport->netdev);
> +	free_netdev(vport->netdev);
> +	vport->netdev = NULL;
> +
> +	adapter->netdevs[vport->idx] = NULL;
> +}
> +
>  /**
>   * iecm_vport_rel - Delete a vport and free its resources
>   * @vport: the vport being removed
> @@ -102,6 +733,8 @@ static void iecm_vport_rel_all(struct iecm_adapter *adapter)
>  		if (!adapter->vports[i])
>  			continue;
>  
> +		if (!test_bit(__IECM_HR_RESET_IN_PROG, adapter->flags))
> +			iecm_decfg_netdev(adapter->vports[i]);
>  		iecm_vport_rel(adapter->vports[i]);
>  		adapter->vports[i] = NULL;
>  		adapter->next_vport = 0;
> @@ -151,7 +784,7 @@ iecm_vport_alloc(struct iecm_adapter *adapter, int vport_id)
>  	adapter->num_alloc_vport++;
>  
>  	/* Setup default MSIX irq handler for the vport */
> -	vport->irq_q_handler = NULL;
> +	vport->irq_q_handler = iecm_vport_intr_clean_queues;
>  	vport->q_vector_base = IECM_NONQ_VEC;
>  
>  	mutex_init(&vport->stop_mutex);
> @@ -184,8 +817,94 @@ static void iecm_statistics_task(struct work_struct *work)
>   *
>   */
>  static void iecm_service_task(struct work_struct *work)
> +{
> +	struct iecm_adapter *adapter = container_of(work,
> +						    struct iecm_adapter,
> +						    serv_task.work);

	struct iecm_adapter *adapter;

	adapter = container_of(work, typeof(*adapter), serv_task.work);

Same line count, but more elegant with no line wraps.

> +
> +	if (test_bit(__IECM_MB_INTR_MODE, adapter->flags)) {
> +		if (test_and_clear_bit(__IECM_MB_INTR_TRIGGER,
> +				       adapter->flags)) {
> +			iecm_recv_mb_msg(adapter, VIRTCHNL_OP_UNKNOWN, NULL, 0);
> +			iecm_mb_irq_enable(adapter);
> +		}
> +	} else {
> +		iecm_recv_mb_msg(adapter, VIRTCHNL_OP_UNKNOWN, NULL, 0);
> +	}
> +
> +	if (iecm_is_reset_detected(adapter) &&
> +	    !iecm_is_reset_in_prog(adapter)) {
> +		dev_info(&adapter->pdev->dev, "HW reset detected\n");
> +		set_bit(__IECM_HR_FUNC_RESET, adapter->flags);
> +		queue_delayed_work(adapter->vc_event_wq,
> +				   &adapter->vc_event_task,
> +				   msecs_to_jiffies(10));
> +	}
> +
> +	queue_delayed_work(adapter->serv_wq, &adapter->serv_task,
> +			   msecs_to_jiffies(300));
> +}
> +
> +/* iecm_set_vlan_offload_features - set vlan offload features
> + * @netdev: netdev structure
> + * @prev_features: previously set features
> + * @features: current features received from user
> + *
> + * Returns 0 on success, error value on failure
> + */
> +static int
> +iecm_set_vlan_offload_features(struct net_device *netdev,
> +			       netdev_features_t prev_features,
> +			       netdev_features_t features)
> +{
> +	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
> +	bool stripping_ena = true, insertion_ena = true;
> +	struct iecm_virtchnl_ops *vc_ops;
> +	u16 vlan_ethertype = 0;
> +
> +	vc_ops = &vport->adapter->dev_ops.vc_ops;
> +	/* keep cases separate because one ethertype for offloads can be
> +	 * disabled at the same time as another is disabled, so check for an
> +	 * enabled ethertype first, then check for disabled. Default to
> +	 * ETH_P_8021Q so an ethertype is specified if disabling insertion
> +	 * and stripping.
> +	 */
> +	if (features & (NETIF_F_HW_VLAN_STAG_RX | NETIF_F_HW_VLAN_STAG_TX))
> +		vlan_ethertype = ETH_P_8021AD;
> +	else if (features & (NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_TX))
> +		vlan_ethertype = ETH_P_8021Q;
> +	else if (prev_features & (NETIF_F_HW_VLAN_STAG_RX |
> +				  NETIF_F_HW_VLAN_STAG_TX))
> +		vlan_ethertype = ETH_P_8021AD;
> +	else if (prev_features & (NETIF_F_HW_VLAN_CTAG_RX |
> +				  NETIF_F_HW_VLAN_CTAG_TX))
> +		vlan_ethertype = ETH_P_8021Q;
> +	else
> +		vlan_ethertype = ETH_P_8021Q;
> +
> +	if (!(features & (NETIF_F_HW_VLAN_STAG_RX | NETIF_F_HW_VLAN_CTAG_RX)))
> +		stripping_ena = false;
> +	if (!(features & (NETIF_F_HW_VLAN_STAG_TX | NETIF_F_HW_VLAN_CTAG_TX)))
> +		insertion_ena = false;
> +
> +	vport->adapter->config_data.vlan_ethertype = vlan_ethertype;
> +
> +	vc_ops->strip_vlan_msg(vport, stripping_ena);
> +	if (vc_ops->insert_vlan_msg)
> +		vc_ops->insert_vlan_msg(vport, insertion_ena);
> +
> +	return 0;
> +}
> +
> +/**
> + * iecm_vport_open - Bring up a vport
> + * @vport: vport to bring up
> + * @alloc_res: allocate queue resources
> + */
> +static int iecm_vport_open(struct iecm_vport *vport, bool alloc_res)
>  {
>  	/* stub */
> +	return 0;
>  }
>  
>  /**
> @@ -206,6 +925,7 @@ static void iecm_init_task(struct work_struct *work)
>  	struct iecm_vport *vport;
>  	struct pci_dev *pdev;
>  	int vport_id, err;
> +	int index;
>  
>  	err = adapter->dev_ops.vc_ops.core_init(adapter, &vport_id);
>  	if (err)
> @@ -219,6 +939,65 @@ static void iecm_init_task(struct work_struct *work)
>  			err);
>  		return;
>  	}
> +	/* Start the service task before requesting vectors. This will ensure
> +	 * vector information response from mailbox is handled
> +	 */
> +	queue_delayed_work(adapter->serv_wq, &adapter->serv_task,
> +			   msecs_to_jiffies(5 * (pdev->devfn & 0x07)));
> +	err = iecm_intr_req(adapter);
> +	if (err) {
> +		dev_err(&pdev->dev, "failed to enable interrupt vectors: %d\n",
> +			err);
> +		goto intr_req_err;
> +	}
> +	err = iecm_send_vlan_v2_caps_msg(adapter);
> +	if (err)
> +		goto vlan_v2_caps_failed;
> +
> +	err = adapter->dev_ops.vc_ops.get_supported_desc_ids(vport);
> +	if (err) {
> +		dev_err(&pdev->dev, "failed to get required descriptor ids\n");
> +		goto rxdids_failed;
> +	}
> +
> +	if (iecm_cfg_netdev(vport))
> +		goto cfg_netdev_err;
> +
> +	if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS, VIRTCHNL2_CAP_VLAN) ||
> +	    iecm_is_cap_ena(adapter, IECM_BASE_CAPS, VIRTCHNL2_CAP_VLAN)) {
> +		err = iecm_set_vlan_offload_features(vport->netdev, 0,
> +						     vport->netdev->features);
> +		if (err)
> +			goto cfg_netdev_err;
> +	}
> +
> +	err = adapter->dev_ops.vc_ops.get_ptype(vport);
> +	if (err)
> +		goto cfg_netdev_err;
> +	queue_delayed_work(adapter->stats_wq, &adapter->stats_task,
> +			   msecs_to_jiffies(10 * (pdev->devfn & 0x07)));
> +	set_bit(__IECM_VPORT_INIT_PROMISC, vport->flags);
> +	/* Once state is put into DOWN, driver is ready for dev_open */
> +	adapter->state = __IECM_DOWN;
> +	if (test_and_clear_bit(__IECM_UP_REQUESTED, adapter->flags))
> +		iecm_vport_open(vport, true);
> +
> +	/* Clear the reset flag unconditionally here in case we were in reset
> +	 * and the link was down
> +	 */
> +	clear_bit(__IECM_HR_RESET_IN_PROG, vport->adapter->flags);
> +
> +	return;
> +
> +vlan_v2_caps_failed:
> +rxdids_failed:
> +cfg_netdev_err:

No reason to declare 3 labels at the same point.

> +	iecm_intr_rel(adapter);
> +intr_req_err:
> +	index = iecm_get_vport_index(adapter, vport);
> +	if (index >= 0)
> +		adapter->vports[index] = NULL;
> +	iecm_vport_rel(vport);
>  }
>  
>  /**
> @@ -614,3 +1393,35 @@ void iecm_free_dma_mem(struct iecm_hw *hw, struct iecm_dma_mem *mem)
>  	mem->va = NULL;
>  	mem->pa = 0;
>  }
> +
> +static const struct net_device_ops iecm_netdev_ops_splitq = {
> +	.ndo_open = NULL,
> +	.ndo_stop = NULL,
> +	.ndo_start_xmit = NULL,
> +	.ndo_set_rx_mode = NULL,
> +	.ndo_validate_addr = eth_validate_addr,

eth_validate_addr() (or ether_addr_valid()) gets run by default when
no .ndo_validate_addr is specified. It's redundant to declare it
here.

> +	.ndo_set_mac_address = NULL,
> +	.ndo_change_mtu = NULL,
> +	.ndo_get_stats64 = NULL,
> +	.ndo_fix_features = NULL,
> +	.ndo_set_features = NULL,
> +	.ndo_vlan_rx_add_vid = NULL,
> +	.ndo_vlan_rx_kill_vid = NULL,
> +	.ndo_setup_tc = NULL,

Non-initialized members get zeroed by default, NULLing them is
excessive.

> +};
> +
> +static const struct net_device_ops iecm_netdev_ops_singleq = {
> +	.ndo_open = NULL,
> +	.ndo_stop = NULL,
> +	.ndo_start_xmit = NULL,
> +	.ndo_set_rx_mode = NULL,
> +	.ndo_validate_addr = eth_validate_addr,
> +	.ndo_set_mac_address = NULL,
> +	.ndo_change_mtu = NULL,
> +	.ndo_get_stats64 = NULL,
> +	.ndo_fix_features = NULL,
> +	.ndo_set_features = NULL,
> +	.ndo_vlan_rx_add_vid = NULL,
> +	.ndo_vlan_rx_kill_vid = NULL,
> +	.ndo_setup_tc           = NULL,

Same 2 previous points.

.ndo_setup_tc assignment indentation is weird. Either align all the
initializers with tabs or don't align at all (both are valid cases).

> +};
> diff --git a/drivers/net/ethernet/intel/iecm/iecm_txrx.c b/drivers/net/ethernet/intel/iecm/iecm_txrx.c
> index bd0cfd89bf03..bb7f5830cffb 100644
> --- a/drivers/net/ethernet/intel/iecm/iecm_txrx.c
> +++ b/drivers/net/ethernet/intel/iecm/iecm_txrx.c
> @@ -218,6 +218,23 @@ const struct iecm_rx_ptype_decoded iecm_ptype_lookup[IECM_RX_MAX_PTYPE] = {
>  };
>  EXPORT_SYMBOL(iecm_ptype_lookup);
>  
> +/**
> + * iecm_vport_intr_clean_queues - MSIX mode Interrupt Handler
> + * @irq: interrupt number
> + * @data: pointer to a q_vector
> + *
> + */
> +irqreturn_t
> +iecm_vport_intr_clean_queues(int __always_unused irq, void *data)
> +{
> +	struct iecm_q_vector *q_vector = (struct iecm_q_vector *)data;
> +
> +	q_vector->total_events++;
> +	napi_schedule(&q_vector->napi);
> +
> +	return IRQ_HANDLED;
> +}
> +
>  /**
>   * iecm_vport_init_num_qs - Initialize number of queues
>   * @vport: vport to initialize queues
> diff --git a/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> index c4ae56897d1b..b91716aeef6f 100644
> --- a/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> +++ b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> @@ -2731,6 +2731,45 @@ static int iecm_send_insert_vlan_msg(struct iecm_vport *vport, bool ena)
>  	return err;
>  }
>  
> +/**
> + * iecm_send_vlan_v2_caps_msg - send virtchnl get offload VLAN V2 caps message
> + * @adapter: adapter info struct
> + *
> + * Returns 0 on success and if VLAN V1 capability is set`, negative on failure.
> + */
> +int iecm_send_vlan_v2_caps_msg(struct iecm_adapter *adapter)
> +{
> +	int err = 0;
> +
> +	if (!iecm_is_cap_ena(adapter, IECM_OTHER_CAPS, VIRTCHNL2_CAP_VLAN))
> +		return err;
> +
> +	err = iecm_send_mb_msg(adapter, VIRTCHNL_OP_GET_OFFLOAD_VLAN_V2_CAPS,
> +			       0, NULL);
> +	if (err)
> +		return err;
> +
> +	err = iecm_min_wait_for_event(adapter, IECM_VC_OFFLOAD_VLAN_V2_CAPS,
> +				      IECM_VC_OFFLOAD_VLAN_V2_CAPS_ERR);
> +
> +	if (err) {
> +		dev_err(&adapter->pdev->dev, "Failed to recv get caps");
> +		return err;
> +	}
> +
> +	if (!adapter->vlan_caps) {
> +		adapter->vlan_caps =
> +		  kzalloc(sizeof(*adapter->vlan_caps), GFP_KERNEL);
> +		if (!adapter->vlan_caps)
> +			return -ENOMEM;
> +	}
> +
> +	memcpy(adapter->vlan_caps,
> +	       adapter->vc_msg, sizeof(*adapter->vlan_caps));
> +
> +	return err;
> +}
> +
>  /**
>   * iecm_fill_ptype_lookup - Fill L3 specific fields in ptype lookup table
>   * @ptype: ptype lookup table
> @@ -3580,6 +3619,132 @@ static unsigned int iecm_get_max_tx_bufs(struct iecm_adapter *adapter)
>  	return ((struct virtchnl2_get_capabilities *)adapter->caps)->max_sg_bufs_per_tx_pkt;
>  }
>  
> +/**
> + * iecm_add_del_ether_addrs
> + * @vport: virtual port data structure
> + * @add: Add or delete flag
> + * @async: Don't wait for return message
> + *
> + * Request that the PF add or delete one or more addresses to our filters.
> + **/
> +void iecm_add_del_ether_addrs(struct iecm_vport *vport, bool add, bool async)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct virtchnl_ether_addr_list *veal = NULL;
> +	int num_entries, num_msgs, total_filters = 0;
> +	struct pci_dev *pdev = adapter->pdev;
> +	enum iecm_vport_vc_state vc, vc_err;
> +	struct virtchnl_ether_addr *eal;
> +	struct iecm_mac_filter *f, *tmp;
> +	int i = 0, k = 0, err = 0;
> +	enum virtchnl_ops vop;
> +
> +	spin_lock_bh(&adapter->mac_filter_list_lock);
> +
> +	/* Find the number of newly added filters */
> +	list_for_each_entry(f, &adapter->config_data.mac_filter_list, list) {
> +		if (add && f->add)
> +			total_filters++;
> +		else if (!add && f->remove)
> +			total_filters++;
> +	}
> +	if (!total_filters) {
> +		spin_unlock_bh(&adapter->mac_filter_list_lock);
> +		goto error;
> +	}
> +
> +	/* Fill all the new filters into virtchannel message */
> +	eal = kcalloc(total_filters, sizeof(struct virtchnl_ether_addr),
> +		      GFP_ATOMIC);
> +	if (!eal) {
> +		err = -ENOMEM;
> +		spin_unlock_bh(&adapter->mac_filter_list_lock);
> +		goto error;
> +	}
> +	list_for_each_entry_safe(f, tmp, &adapter->config_data.mac_filter_list,
> +				 list) {
> +		if (add && f->add) {
> +			ether_addr_copy(eal[i].addr, f->macaddr);
> +			i++;
> +			f->add = false;
> +			if (i == total_filters)
> +				break;
> +		}
> +		if (!add && f->remove) {
> +			ether_addr_copy(eal[i].addr, f->macaddr);
> +			i++;
> +			list_del(&f->list);
> +			kfree(f);
> +			if (i == total_filters)
> +				break;
> +		}
> +	}
> +
> +	spin_unlock_bh(&adapter->mac_filter_list_lock);
> +
> +	/* Chunk up the filters into multiple messages to avoid
> +	 * sending a control queue message buffer that is too large
> +	 */
> +	if (total_filters < IECM_NUM_FILTERS_PER_MSG)
> +		num_entries = total_filters;
> +	else
> +		num_entries = IECM_NUM_FILTERS_PER_MSG;
> +
> +	num_msgs = DIV_ROUND_UP(total_filters, IECM_NUM_FILTERS_PER_MSG);
> +
> +	for (i = 0, k = 0; i < num_msgs || num_entries; i++) {
> +		int buf_size = sizeof(struct virtchnl_ether_addr_list) +
> +			(sizeof(struct virtchnl_ether_addr) * num_entries);
> +		if (!veal || num_entries != IECM_NUM_FILTERS_PER_MSG) {
> +			kfree(veal);
> +			veal = kzalloc(buf_size, GFP_KERNEL);
> +			if (!veal) {
> +				err = -ENOMEM;
> +				goto list_prep_error;
> +			}
> +		} else {
> +			memset(veal, 0, buf_size);
> +		}
> +
> +		veal->vsi_id = vport->vport_id;
> +		veal->num_elements = num_entries;
> +		memcpy(veal->list, &eal[k],
> +		       sizeof(struct virtchnl_ether_addr) * num_entries);
> +
> +		if (add) {
> +			vop = VIRTCHNL_OP_ADD_ETH_ADDR;
> +			vc = IECM_VC_ADD_ETH_ADDR;
> +			vc_err = IECM_VC_ADD_ETH_ADDR_ERR;
> +		} else  {
> +			vop = VIRTCHNL_OP_DEL_ETH_ADDR;
> +			vc = IECM_VC_DEL_ETH_ADDR;
> +			vc_err = IECM_VC_DEL_ETH_ADDR_ERR;
> +		}
> +		err = iecm_send_mb_msg(vport->adapter, vop, buf_size,
> +				       (u8 *)veal);
> +		if (err)
> +			goto mbx_error;
> +
> +		if (!async) {
> +			err = iecm_wait_for_event(vport->adapter, vc, vc_err);
> +			if (err)
> +				goto mbx_error;
> +		}
> +
> +		k += num_entries;
> +		total_filters -= num_entries;
> +		if (total_filters < IECM_NUM_FILTERS_PER_MSG)
> +			num_entries = total_filters;
> +	}
> +mbx_error:
> +	kfree(veal);
> +list_prep_error:
> +	kfree(eal);
> +error:
> +	if (err)
> +		dev_err(&pdev->dev, "Failed to add or del mac filters %d", err);
> +}
> +
>  /**
>   * iecm_add_del_vlans - Add or delete vlan filter
>   * @vport: vport structure
> diff --git a/drivers/net/ethernet/intel/include/iecm.h b/drivers/net/ethernet/intel/include/iecm.h
> index d736db65da06..b5bd73be2855 100644
> --- a/drivers/net/ethernet/intel/include/iecm.h
> +++ b/drivers/net/ethernet/intel/include/iecm.h
> @@ -33,6 +33,11 @@
>  #define IECM_MB_MAX_ERR			20
>  #define IECM_NUM_CHUNKS_PER_MSG(a, b)	((IECM_DFLT_MBX_BUF_SIZE - (a)) / (b))
>  
> +/* 2K is the real maximum, but the driver should not be using more than the
> + * below limit
> + */
> +#define IECM_MAX_VECIDS			256
> +
>  #define IECM_MAX_NUM_VPORTS		1
>  
>  /* available message levels */
> @@ -135,6 +140,10 @@ enum iecm_cap_field {
>  	IECM_CAP_FIELD_LAST,
>  };
>  
> +struct iecm_netdev_priv {
> +	struct iecm_vport *vport;
> +};
> +
>  struct iecm_reset_reg {
>  	u32 rstat;
>  	u32 rstat_m;
> @@ -450,6 +459,8 @@ struct iecm_adapter {
>  	struct msix_entry *msix_entries;
>  	struct virtchnl2_alloc_vectors *req_vec_chunks;
>  	struct iecm_q_vector mb_vector;
> +	/* handler for hard interrupt for mailbox*/
> +	irqreturn_t (*irq_mb_handler)(int irq, void *data);
>  
>  	/* vport structs */
>  	struct iecm_vport **vports;	/* vports created by the driver */
> @@ -537,12 +548,88 @@ static inline bool __iecm_is_cap_ena(struct iecm_adapter *adapter, bool all,
>  	return adapter->dev_ops.vc_ops.is_cap_ena(adapter, all, field, flag);
>  }
>  
> -#define IECM_CAP_HSPLIT (\
> -	VIRTCHNL2_CAP_RX_HSPLIT_AT_L2   |\
> -	VIRTCHNL2_CAP_RX_HSPLIT_AT_L3   |\
> -	VIRTCHNL2_CAP_RX_HSPLIT_AT_L4V4 |\
> +/* enum used to distinguish vlan capabilities */
> +enum iecm_vlan_caps {
> +	IECM_CAP_VLAN_CTAG_INSERT,
> +	IECM_CAP_VLAN_STAG_INSERT,
> +	IECM_CAP_VLAN_CTAG_STRIP,
> +	IECM_CAP_VLAN_STAG_STRIP,
> +	IECM_CAP_VLAN_CTAG_ADD_DEL,
> +	IECM_CAP_VLAN_STAG_ADD_DEL,
> +	IECM_CAP_VLAN_LAST,
> +};
> +
> +#define IECM_VLAN_8100 (VIRTCHNL_VLAN_TOGGLE | VIRTCHNL_VLAN_ETHERTYPE_8100)
> +#define IECM_VLAN_88A8 (VIRTCHNL_VLAN_TOGGLE | VIRTCHNL_VLAN_ETHERTYPE_88A8)
> +
> +#define IECM_F_HW_VLAN_CTAG_TX NETIF_F_HW_VLAN_CTAG_TX
> +
> +#define IECM_F_HW_VLAN_CTAG_RX NETIF_F_HW_VLAN_CTAG_RX
> +
> +#define IECM_F_HW_VLAN_CTAG_FILTER NETIF_F_HW_VLAN_CTAG_FILTER
> +
> +#define IECM_CAP_RSS (\
> +	VIRTCHNL2_CAP_RSS_IPV4_TCP	|\
> +	VIRTCHNL2_CAP_RSS_IPV4_TCP	|\
> +	VIRTCHNL2_CAP_RSS_IPV4_UDP	|\
> +	VIRTCHNL2_CAP_RSS_IPV4_SCTP	|\
> +	VIRTCHNL2_CAP_RSS_IPV4_OTHER	|\
> +	VIRTCHNL2_CAP_RSS_IPV4_AH	|\
> +	VIRTCHNL2_CAP_RSS_IPV4_ESP	|\
> +	VIRTCHNL2_CAP_RSS_IPV4_AH_ESP	|\
> +	VIRTCHNL2_CAP_RSS_IPV6_TCP	|\
> +	VIRTCHNL2_CAP_RSS_IPV6_TCP	|\
> +	VIRTCHNL2_CAP_RSS_IPV6_UDP	|\
> +	VIRTCHNL2_CAP_RSS_IPV6_SCTP	|\
> +	VIRTCHNL2_CAP_RSS_IPV6_OTHER	|\
> +	VIRTCHNL2_CAP_RSS_IPV6_AH	|\
> +	VIRTCHNL2_CAP_RSS_IPV6_ESP	|\
> +	VIRTCHNL2_CAP_RSS_IPV6_AH_ESP)
> +
> +#define IECM_CAP_RSC (\
> +	VIRTCHNL2_CAP_RSC_IPV4_TCP	|\
> +	VIRTCHNL2_CAP_RSC_IPV4_SCTP	|\
> +	VIRTCHNL2_CAP_RSC_IPV6_TCP	|\
> +	VIRTCHNL2_CAP_RSC_IPV6_SCTP)
> +
> +#define IECM_CAP_HSPLIT	(\
> +	VIRTCHNL2_CAP_RX_HSPLIT_AT_L2	|\
> +	VIRTCHNL2_CAP_RX_HSPLIT_AT_L3	|\
> +	VIRTCHNL2_CAP_RX_HSPLIT_AT_L4V4	|\
>  	VIRTCHNL2_CAP_RX_HSPLIT_AT_L4V6)
>  
> +#define IECM_CAP_RX_CSUM_L4V4 (\
> +	VIRTCHNL2_CAP_RX_CSUM_L4_IPV4_TCP	|\
> +	VIRTCHNL2_CAP_RX_CSUM_L4_IPV4_UDP)
> +
> +#define IECM_CAP_RX_CSUM_L4V6 (\
> +	VIRTCHNL2_CAP_RX_CSUM_L4_IPV6_TCP	|\
> +	VIRTCHNL2_CAP_RX_CSUM_L4_IPV6_UDP)
> +
> +#define IECM_CAP_RX_CSUM (\
> +	VIRTCHNL2_CAP_RX_CSUM_L3_IPV4		|\
> +	VIRTCHNL2_CAP_RX_CSUM_L4_IPV4_TCP	|\
> +	VIRTCHNL2_CAP_RX_CSUM_L4_IPV4_UDP	|\
> +	VIRTCHNL2_CAP_RX_CSUM_L4_IPV4_SCTP	|\
> +	VIRTCHNL2_CAP_RX_CSUM_L4_IPV6_TCP	|\
> +	VIRTCHNL2_CAP_RX_CSUM_L4_IPV6_UDP	|\
> +	VIRTCHNL2_CAP_RX_CSUM_L4_IPV6_SCTP)
> +
> +#define IECM_CAP_SCTP_CSUM (\
> +	VIRTCHNL2_CAP_TX_CSUM_L4_IPV4_SCTP	|\
> +	VIRTCHNL2_CAP_TX_CSUM_L4_IPV6_SCTP	|\
> +	VIRTCHNL2_CAP_RX_CSUM_L4_IPV4_SCTP	|\
> +	VIRTCHNL2_CAP_RX_CSUM_L4_IPV6_SCTP)
> +
> +/**
> + * iecm_get_reserved_vecs - Get reserved vectors
> + * @adapter: private data struct
> + */
> +static inline u16 iecm_get_reserved_vecs(struct iecm_adapter *adapter)
> +{
> +	return adapter->dev_ops.vc_ops.get_reserved_vecs(adapter);
> +}
> +
>  /**
>   * iecm_is_reset_detected - check if we were reset at some point
>   * @adapter: driver specific private structure
> @@ -555,6 +642,20 @@ static inline bool iecm_is_reset_detected(struct iecm_adapter *adapter)
>  		 adapter->hw.arq->reg.len_ena_mask);
>  }
>  
> +/**
> + * iecm_is_reset_in_prog - check if reset is in progress
> + * @adapter: driver specific private structure
> + *
> + * Returns true if hard reset is in progress, false otherwise
> + */
> +static inline bool iecm_is_reset_in_prog(struct iecm_adapter *adapter)
> +{
> +	return (test_bit(__IECM_HR_RESET_IN_PROG, adapter->flags) ||
> +		test_bit(__IECM_HR_FUNC_RESET, adapter->flags) ||
> +		test_bit(__IECM_HR_CORE_RESET, adapter->flags) ||
> +		test_bit(__IECM_HR_DRV_LOAD, adapter->flags));
> +}
> +
>  int iecm_probe(struct pci_dev *pdev,
>  	       const struct pci_device_id __always_unused *ent,
>  	       struct iecm_adapter *adapter);
> @@ -576,6 +677,7 @@ int iecm_send_get_caps_msg(struct iecm_adapter *adapter);
>  int iecm_send_delete_queues_msg(struct iecm_vport *vport);
>  int iecm_send_add_queues_msg(struct iecm_vport *vport, u16 num_tx_q,
>  			     u16 num_complq, u16 num_rx_q, u16 num_rx_bufq);
> +int iecm_send_vlan_v2_caps_msg(struct iecm_adapter *adapter);
>  int iecm_send_config_tx_queues_msg(struct iecm_vport *vport);
>  int iecm_send_config_rx_queues_msg(struct iecm_vport *vport);
>  int iecm_send_enable_vport_msg(struct iecm_vport *vport);
> @@ -589,6 +691,7 @@ int iecm_send_dealloc_vectors_msg(struct iecm_adapter *adapter);
>  int iecm_send_alloc_vectors_msg(struct iecm_adapter *adapter, u16 num_vectors);
>  int iecm_vport_params_buf_alloc(struct iecm_adapter *adapter);
>  void iecm_vport_params_buf_rel(struct iecm_adapter *adapter);
> +struct iecm_vport *iecm_netdev_to_vport(struct net_device *netdev);
>  int iecm_send_get_stats_msg(struct iecm_vport *vport);
>  int iecm_get_vec_ids(struct iecm_adapter *adapter,
>  		     u16 *vecids, int num_vecids,
> @@ -598,6 +701,7 @@ int iecm_recv_mb_msg(struct iecm_adapter *adapter, enum virtchnl_ops op,
>  int iecm_send_mb_msg(struct iecm_adapter *adapter, enum virtchnl_ops op,
>  		     u16 msg_size, u8 *msg);
>  void iecm_vport_set_hsplit(struct iecm_vport *vport, bool ena);
> +void iecm_add_del_ether_addrs(struct iecm_vport *vport, bool add, bool async);
>  int iecm_send_enable_channels_msg(struct iecm_vport *vport);
>  int iecm_send_disable_channels_msg(struct iecm_vport *vport);
>  bool iecm_is_feature_ena(struct iecm_vport *vport, netdev_features_t feature);
> diff --git a/drivers/net/ethernet/intel/include/iecm_txrx.h b/drivers/net/ethernet/intel/include/iecm_txrx.h
> index 9f3086bfe575..0aa1eac70e7c 100644
> --- a/drivers/net/ethernet/intel/include/iecm_txrx.h
> +++ b/drivers/net/ethernet/intel/include/iecm_txrx.h
> @@ -512,4 +512,6 @@ void iecm_vport_calc_total_qs(struct iecm_adapter *adapter,
>  			      struct virtchnl2_create_vport *vport_msg);
>  void iecm_vport_calc_num_q_groups(struct iecm_vport *vport);
>  void iecm_vport_calc_num_q_vec(struct iecm_vport *vport);
> +irqreturn_t
> +iecm_vport_intr_clean_queues(int __always_unused irq, void *data);
>  #endif /* !_IECM_TXRX_H_ */
> -- 
> 2.33.0

Thanks,
Al


^ permalink raw reply	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 10/19] iecm: alloc vport RX resources
  2022-01-28  0:10 ` [Intel-wired-lan] [PATCH net-next 10/19] iecm: alloc vport RX resources Alan Brady
@ 2022-01-28 14:16   ` Alexander Lobakin
  2022-02-03  0:13     ` Brady, Alan
  0 siblings, 1 reply; 89+ messages in thread
From: Alexander Lobakin @ 2022-01-28 14:16 UTC (permalink / raw)
  To: intel-wired-lan

From: Alan Brady <alan.brady@intel.com>
Date: Thu, 27 Jan 2022 16:10:00 -0800

> This finishes what we need to do for open by adding RX resource
> allocations.
> 
> As noted in the TX alloc patch, the splitq model is unique in introducing
> the concept of queue groups, which also applies to RX, albeit in a slightly
> different way. For RX we also split the queue between descriptor handling
> and buffer handling. We have some number of RX completion queues associated
> with up to two buffer queues in a given queue group. Once buffers are
> cleaned and recycled, they're given the buffer queues which then posts the
> buffers back to hardware. To enable this in a lockless way, there's also
> the concept of 'refill queues' introduced. Recycled buffers are put onto
> refill queues which is what the buffer queues clean to get buffers back.
> 
> Signed-off-by: Phani Burra <phani.r.burra@intel.com>
> Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
> Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
> Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
> Signed-off-by: Alan Brady <alan.brady@intel.com>
> ---
>  drivers/net/ethernet/intel/iecm/iecm_txrx.c   | 769 ++++++++++++++++++
>  .../net/ethernet/intel/include/iecm_txrx.h    |   7 +
>  2 files changed, 776 insertions(+)
> 
> diff --git a/drivers/net/ethernet/intel/iecm/iecm_txrx.c b/drivers/net/ethernet/intel/iecm/iecm_txrx.c
> index 85e88a30370d..fb6a61277b00 100644
> --- a/drivers/net/ethernet/intel/iecm/iecm_txrx.c
> +++ b/drivers/net/ethernet/intel/iecm/iecm_txrx.c
> @@ -452,6 +452,545 @@ static int iecm_tx_desc_alloc_all(struct iecm_vport *vport)
>  	return err;
>  }
>  
> +/**
> + * iecm_rx_page_rel - Release an rx buffer page
> + * @rxq: the queue that owns the buffer
> + * @page_info: pointer to page metadata of page to be freed
> + */
> +static void iecm_rx_page_rel(struct iecm_queue *rxq,
> +			     struct iecm_page_info *page_info)
> +{
> +	if (!page_info->page)
> +		return;
> +
> +	/* free resources associated with mapping */
> +	dma_unmap_page_attrs(rxq->dev, page_info->dma, PAGE_SIZE,
> +			     DMA_FROM_DEVICE, IECM_RX_DMA_ATTR);
> +
> +	__page_frag_cache_drain(page_info->page, page_info->pagecnt_bias);
> +
> +	page_info->page = NULL;
> +	page_info->page_offset = 0;
> +}
> +
> +/**
> + * iecm_rx_buf_rel - Release a rx buffer
> + * @rxq: the queue that owns the buffer
> + * @rx_buf: the buffer to free
> + */
> +static void iecm_rx_buf_rel(struct iecm_queue *rxq,
> +			    struct iecm_rx_buf *rx_buf)
> +{
> +	iecm_rx_page_rel(rxq, &rx_buf->page_info[0]);
> +#if (PAGE_SIZE < 8192)
> +	if (rx_buf->buf_size > IECM_RX_BUF_2048)
> +		iecm_rx_page_rel(rxq, &rx_buf->page_info[1]);
> +
> +#endif

PAGE_SIZE is always defined, thus can be embedded in an if
statement.

	if (PAGE_SIZE < 8192 && rx_buf ...)

will produce the same code, but without ifdeffery.

> +	if (rx_buf->skb) {
> +		dev_kfree_skb_any(rx_buf->skb);
> +		rx_buf->skb = NULL;
> +	}
> +}
> +
> +/**
> + * iecm_rx_hdr_buf_rel_all - Release header buffer memory
> + * @rxq: queue to use
> + */
> +static void iecm_rx_hdr_buf_rel_all(struct iecm_queue *rxq)
> +{
> +	struct iecm_hw *hw = &rxq->vport->adapter->hw;
> +	int i;
> +
> +	if (!rxq)
> +		return;
> +
> +	if (rxq->rx_buf.hdr_buf) {
> +		for (i = 0; i < rxq->desc_count; i++) {
> +			struct iecm_dma_mem *hbuf = rxq->rx_buf.hdr_buf[i];
> +
> +			if (hbuf) {
> +				iecm_free_dma_mem(hw, hbuf);
> +				kfree(hbuf);
> +			}
> +			rxq->rx_buf.hdr_buf[i] = NULL;
> +		}
> +		kfree(rxq->rx_buf.hdr_buf);
> +		rxq->rx_buf.hdr_buf = NULL;
> +	}

	if (!hdr_buf)
		goto release_pages;

-1 indent level.

> +
> +	for (i = 0; i < rxq->hbuf_pages.nr_pages; i++)
> +		iecm_rx_page_rel(rxq, &rxq->hbuf_pages.pages[i]);
> +
> +	kfree(rxq->hbuf_pages.pages);
> +}
> +
> +/**
> + * iecm_rx_buf_rel_all - Free all Rx buffer resources for a queue
> + * @rxq: queue to be cleaned
> + */
> +static void iecm_rx_buf_rel_all(struct iecm_queue *rxq)
> +{
> +	u16 i;
> +
> +	/* queue already cleared, nothing to do */
> +	if (!rxq->rx_buf.buf)
> +		return;
> +
> +	/* Free all the bufs allocated and given to hw on Rx queue */
> +	for (i = 0; i < rxq->desc_count; i++)
> +		iecm_rx_buf_rel(rxq, &rxq->rx_buf.buf[i]);
> +	if (rxq->rx_hsplit_en)
> +		iecm_rx_hdr_buf_rel_all(rxq);
> +
> +	kfree(rxq->rx_buf.buf);
> +	rxq->rx_buf.buf = NULL;
> +	kfree(rxq->rx_buf.hdr_buf);
> +	rxq->rx_buf.hdr_buf = NULL;
> +}
> +
> +/**
> + * iecm_rx_desc_rel - Free a specific Rx q resources
> + * @rxq: queue to clean the resources from
> + * @bufq: buffer q or completion q
> + * @q_model: single or split q model
> + *
> + * Free a specific rx queue resources
> + */
> +static void iecm_rx_desc_rel(struct iecm_queue *rxq, bool bufq, s32 q_model)
> +{
> +	if (!rxq)
> +		return;
> +
> +	if (!bufq && iecm_is_queue_model_split(q_model) && rxq->skb) {
> +		dev_kfree_skb_any(rxq->skb);
> +		rxq->skb = NULL;
> +	}
> +
> +	if (bufq || !iecm_is_queue_model_split(q_model))
> +		iecm_rx_buf_rel_all(rxq);
> +
> +	if (rxq->desc_ring) {
> +		dmam_free_coherent(rxq->dev, rxq->size,
> +				   rxq->desc_ring, rxq->dma);
> +		rxq->desc_ring = NULL;
> +		rxq->next_to_alloc = 0;
> +		rxq->next_to_clean = 0;
> +		rxq->next_to_use = 0;
> +	}

	if (!desc_ring)
		return;

Same.

> +}
> +
> +/**
> + * iecm_rx_desc_rel_all - Free Rx Resources for All Queues
> + * @vport: virtual port structure
> + *
> + * Free all rx queues resources
> + */
> +static void iecm_rx_desc_rel_all(struct iecm_vport *vport)
> +{
> +	struct iecm_rxq_group *rx_qgrp;
> +	struct iecm_queue *q;
> +	int i, j, num_rxq;
> +
> +	if (!vport->rxq_grps)
> +		return;
> +
> +	for (i = 0; i < vport->num_rxq_grp; i++) {
> +		rx_qgrp = &vport->rxq_grps[i];
> +
> +		if (iecm_is_queue_model_split(vport->rxq_model)) {
> +			num_rxq = rx_qgrp->splitq.num_rxq_sets;
> +			for (j = 0; j < num_rxq; j++) {
> +				q = &rx_qgrp->splitq.rxq_sets[j]->rxq;
> +				iecm_rx_desc_rel(q, false,
> +						 vport->rxq_model);
> +			}
> +
> +			if (!rx_qgrp->splitq.bufq_sets)
> +				continue;
> +			for (j = 0; j < vport->num_bufqs_per_qgrp; j++) {
> +				struct iecm_bufq_set *bufq_set =
> +					&rx_qgrp->splitq.bufq_sets[j];
> +
> +				q = &bufq_set->bufq;
> +				iecm_rx_desc_rel(q, true, vport->rxq_model);
> +			}
> +		} else {
> +			for (j = 0; j < rx_qgrp->singleq.num_rxq; j++) {
> +				q = rx_qgrp->singleq.rxqs[j];
> +				iecm_rx_desc_rel(q, false,
> +						 vport->rxq_model);
> +			}
> +		}
> +	}
> +}
> +
> +/**
> + * iecm_rx_buf_hw_update - Store the new tail and head values
> + * @rxq: queue to bump
> + * @val: new head index
> + */
> +void iecm_rx_buf_hw_update(struct iecm_queue *rxq, u32 val)
> +{
> +	rxq->next_to_use = val;
> +
> +	if (unlikely(!rxq->tail))
> +		return;
> +	/* writel has an implicit memory barrier */
> +	writel(val, rxq->tail);
> +}
> +
> +/**
> + * iecm_alloc_page - allocate page to back RX buffer
> + * @rxbufq: pointer to queue struct
> + * @page_info: pointer to page metadata struct
> + */
> +static int
> +iecm_alloc_page(struct iecm_queue *rxbufq, struct iecm_page_info *page_info)
> +{
> +	/* alloc new page for storage */
> +	page_info->page = alloc_page(GFP_ATOMIC | __GFP_NOWARN);
> +	if (unlikely(!page_info->page))
> +		return -ENOMEM;
> +
> +	/* map page for use */
> +	page_info->dma = dma_map_page_attrs(rxbufq->dev, page_info->page,
> +					    0, PAGE_SIZE, DMA_FROM_DEVICE,
> +					    IECM_RX_DMA_ATTR);
> +
> +	/* if mapping failed free memory back to system since
> +	 * there isn't much point in holding memory we can't use
> +	 */
> +	if (dma_mapping_error(rxbufq->dev, page_info->dma)) {
> +		__free_pages(page_info->page, 0);
> +		return -ENOMEM;
> +	}
> +
> +	page_info->page_offset = 0;
> +
> +	/* initialize pagecnt_bias to claim we fully own page */
> +	page_ref_add(page_info->page, USHRT_MAX - 1);

Too many references to page_info->page, could be optimized by
placing *page onstack and then assigning page_info->page later.

> +	page_info->pagecnt_bias = USHRT_MAX;
> +
> +	return 0;
> +}
> +
> +/**
> + * iecm_init_rx_buf_hw_alloc - allocate initial RX buffer pages
> + * @rxbufq: ring to use; equivalent to rxq when operating in singleq mode
> + * @buf: rx_buffer struct to modify
> + *
> + * Returns true if the page was successfully allocated or
> + * reused.
> + */
> +bool iecm_init_rx_buf_hw_alloc(struct iecm_queue *rxbufq, struct iecm_rx_buf *buf)
> +{
> +	if (iecm_alloc_page(rxbufq, &buf->page_info[0]))
> +		return false;
> +
> +#if (PAGE_SIZE < 8192)
> +	if (rxbufq->rx_buf_size > IECM_RX_BUF_2048)
> +		if (iecm_alloc_page(rxbufq, &buf->page_info[1]))
> +			return false;
> +#endif

Same here with embedding the check.

> +
> +	buf->page_indx = 0;
> +	buf->buf_size = rxbufq->rx_buf_size;
> +
> +	return true;
> +}
> +
> +/**
> + * iecm_rx_hdr_buf_alloc_all - Allocate memory for header buffers
> + * @rxq: ring to use
> + *
> + * Returns 0 on success, negative on failure.
> + */
> +static int iecm_rx_hdr_buf_alloc_all(struct iecm_queue *rxq)
> +{
> +	struct iecm_page_info *page_info;
> +	int nr_pages, offset;
> +	int i, j = 0;
> +
> +	rxq->rx_buf.hdr_buf = kcalloc(rxq->desc_count,
> +				      sizeof(struct iecm_dma_mem *),
> +				      GFP_KERNEL);
> +	if (!rxq->rx_buf.hdr_buf)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < rxq->desc_count; i++) {
> +		rxq->rx_buf.hdr_buf[i] = kcalloc(1,
> +						 sizeof(struct iecm_dma_mem),
> +						 GFP_KERNEL);
> +		if (!rxq->rx_buf.hdr_buf[i])
> +			goto unroll_buf_alloc;
> +	}
> +
> +	/* Determine the number of pages necessary to back the total number of header buffers */
> +	nr_pages = (rxq->desc_count * rxq->rx_hbuf_size) / PAGE_SIZE;
> +	rxq->hbuf_pages.pages = kcalloc(nr_pages,
> +					sizeof(struct iecm_page_info),
> +					GFP_KERNEL);
> +	if (!rxq->hbuf_pages.pages)
> +		goto unroll_buf_alloc;
> +
> +	rxq->hbuf_pages.nr_pages = nr_pages;
> +	for (i = 0; i < nr_pages; i++) {
> +		if (iecm_alloc_page(rxq, &rxq->hbuf_pages.pages[i]))

And here you allocate pages with GFP_ATOMIC in process context.
Atomic allocations must not be used if the function may sleep.
Please add gfp_t gfp argument to iecm_alloc_page() and use
GFP_KERNEL here (and GFP_ATOMIC on buffer refill hotpath).

> +			goto unroll_buf_alloc;
> +	}
> +
> +	page_info = &rxq->hbuf_pages.pages[0];
> +	for (i = 0, offset = 0; i < rxq->desc_count; i++, offset += rxq->rx_hbuf_size) {
> +		struct iecm_dma_mem *hbuf = rxq->rx_buf.hdr_buf[i];
> +
> +		/* Move to next page */
> +		if (offset >= PAGE_SIZE) {
> +			offset = 0;
> +			page_info = &rxq->hbuf_pages.pages[++j];
> +		}
> +
> +		hbuf->va = page_address(page_info->page) + offset;
> +		hbuf->pa = page_info->dma + offset;
> +		hbuf->size = rxq->rx_hbuf_size;
> +	}
> +
> +	return 0;
> +unroll_buf_alloc:
> +	iecm_rx_hdr_buf_rel_all(rxq);
> +	return -ENOMEM;
> +}
> +
> +/**
> + * iecm_rx_buf_hw_alloc_all - Allocate receive buffers
> + * @rxbufq: queue for which the hw buffers are allocated; equivalent to rxq
> + * when operating in singleq mode
> + * @alloc_count: number of buffers to allocate
> + *
> + * Returns false if all allocations were successful, true if any fail
> + */
> +static bool
> +iecm_rx_buf_hw_alloc_all(struct iecm_queue *rxbufq, u16 alloc_count)
> +{
> +	u16 nta = rxbufq->next_to_alloc;
> +	struct iecm_rx_buf *buf;
> +
> +	if (!alloc_count)
> +		return false;
> +
> +	buf = &rxbufq->rx_buf.buf[nta];
> +
> +	do {
> +		if (!iecm_init_rx_buf_hw_alloc(rxbufq, buf))
> +			break;
> +
> +		buf++;
> +		nta++;
> +		if (unlikely(nta == rxbufq->desc_count)) {

		if (unlikely(++nta == ...)) { /* Just in one line */

> +			buf = rxbufq->rx_buf.buf;
> +			nta = 0;
> +		}
> +
> +		alloc_count--;
> +	} while (alloc_count);

	} while (alloc_count--); /* Just in one line */

> +
> +	return !!alloc_count;
> +}
> +
> +/**
> + * iecm_rx_post_buf_desc - Post buffer to bufq descriptor ring
> + * @bufq: buffer queue to post to
> + * @buf_id: buffer id to post
> + */
> +static void iecm_rx_post_buf_desc(struct iecm_queue *bufq, u16 buf_id)
> +{
> +	struct virtchnl2_splitq_rx_buf_desc *splitq_rx_desc = NULL;
> +	struct iecm_page_info *page_info;
> +	u16 nta = bufq->next_to_alloc;
> +	struct iecm_rx_buf *buf;
> +
> +	splitq_rx_desc = IECM_SPLITQ_RX_BUF_DESC(bufq, nta);
> +	buf = &bufq->rx_buf.buf[buf_id];
> +	page_info = &buf->page_info[buf->page_indx];
> +	if (bufq->rx_hsplit_en)
> +		splitq_rx_desc->hdr_addr = cpu_to_le64(bufq->rx_buf.hdr_buf[buf_id]->pa);

90-cols line.

> +
> +	splitq_rx_desc->pkt_addr = cpu_to_le64(page_info->dma +
> +					       page_info->page_offset);
> +	splitq_rx_desc->qword0.buf_id = cpu_to_le16(buf_id);
> +
> +	nta++;
> +	if (unlikely(nta == bufq->desc_count))
> +		nta = 0;

Post-increment can be embedded into a condition check (with
converting into a pre-increment obviously).

> +	bufq->next_to_alloc = nta;
> +}
> +
> +/**
> + * iecm_rx_post_init_bufs - Post initial buffers to bufq
> + * @bufq: buffer queue to post working set to
> + * @working_set: number of buffers to put in working set
> + */
> +static void iecm_rx_post_init_bufs(struct iecm_queue *bufq,
> +				   u16 working_set)
> +{
> +	int i;
> +
> +	for (i = 0; i < working_set; i++)
> +		iecm_rx_post_buf_desc(bufq, i);
> +
> +	iecm_rx_buf_hw_update(bufq, bufq->next_to_alloc & ~(bufq->rx_buf_stride - 1));

87-cols line.
Please test all your patches with `checkpatch --strict --codespell`.

> +}
> +
> +/**
> + * iecm_rx_buf_alloc_all - Allocate memory for all buffer resources
> + * @rxbufq: queue for which the buffers are allocated; equivalent to
> + * rxq when operating in singleq mode
> + *
> + * Returns 0 on success, negative on failure
> + */
> +static int iecm_rx_buf_alloc_all(struct iecm_queue *rxbufq)
> +{
> +	int err = 0;
> +
> +	/* Allocate book keeping buffers */
> +	rxbufq->rx_buf.buf = kcalloc(rxbufq->desc_count,
> +				     sizeof(struct iecm_rx_buf), GFP_KERNEL);
> +	if (!rxbufq->rx_buf.buf) {
> +		err = -ENOMEM;
> +		goto rx_buf_alloc_all_out;
> +	}
> +
> +	if (rxbufq->rx_hsplit_en) {
> +		err = iecm_rx_hdr_buf_alloc_all(rxbufq);
> +		if (err)
> +			goto rx_buf_alloc_all_out;
> +	}
> +
> +	/* Allocate buffers to be given to HW.	 */
> +	if (iecm_is_queue_model_split(rxbufq->vport->rxq_model)) {
> +		if (iecm_rx_buf_hw_alloc_all(rxbufq, rxbufq->desc_count - 1))
> +			err = -ENOMEM;
> +	} else {
> +		if (iecm_rx_singleq_buf_hw_alloc_all(rxbufq, rxbufq->desc_count - 1))
> +			err = -ENOMEM;
> +	}
> +
> +rx_buf_alloc_all_out:
> +	if (err)
> +		iecm_rx_buf_rel_all(rxbufq);
> +	return err;
> +}
> +
> +/**
> + * iecm_rx_desc_alloc - Allocate queue Rx resources
> + * @rxq: Rx queue for which the resources are setup
> + * @bufq: buffer or completion queue
> + * @q_model: single or split queue model
> + *
> + * Returns 0 on success, negative on failure
> + */
> +static int iecm_rx_desc_alloc(struct iecm_queue *rxq, bool bufq, s32 q_model)
> +{
> +	struct device *dev = rxq->dev;
> +
> +	/* As both single and split descriptors are 32 byte, memory size
> +	 * will be same for all three singleq_base rx, buf., splitq_base
> +	 * rx. So pick anyone of them for size
> +	 */
> +	if (bufq) {
> +		rxq->size = rxq->desc_count *
> +			sizeof(struct virtchnl2_splitq_rx_buf_desc);
> +	} else {
> +		rxq->size = rxq->desc_count *
> +			sizeof(union virtchnl2_rx_desc);
> +	}

Oneliners, braces are unneeded.

For counting the array sizes it's required to use array_size():

	rxq->size = array_size(rxq->desc_count, sizeof(...));
	if (unlikely(rxq->size == -EOVERFLOW))
		/* Error path */

There are more such places in the code, I couldn't catch them all.

> +
> +	/* Allocate descriptors and also round up to nearest 4K */
> +	rxq->size = ALIGN(rxq->size, 4096);

4096 = SZ_4K, no need to open-code.

> +	rxq->desc_ring = dmam_alloc_coherent(dev, rxq->size,
> +					     &rxq->dma, GFP_KERNEL);
> +	if (!rxq->desc_ring) {
> +		dev_info(dev, "Unable to allocate memory for the Rx descriptor ring, size=%d\n",
> +			 rxq->size);
> +		return -ENOMEM;
> +	}
> +
> +	rxq->next_to_alloc = 0;
> +	rxq->next_to_clean = 0;
> +	rxq->next_to_use = 0;

You allocate rxq with kzalloc() (or derivative) IIRC, 'z'-versions
zero the memory before returning. These initializers are redundant.

> +	set_bit(__IECM_Q_GEN_CHK, rxq->flags);
> +
> +	/* Allocate buffers for a rx queue if the q_model is single OR if it
> +	 * is a buffer queue in split queue model
> +	 */
> +	if (bufq || !iecm_is_queue_model_split(q_model)) {
> +		int err = 0;
> +
> +		err = iecm_rx_buf_alloc_all(rxq);
> +		if (err) {
> +			iecm_rx_desc_rel(rxq, bufq, q_model);
> +			return err;
> +		}
> +	}

	if (inverse_the_condition_above)
		return 0;

	err = ...

-1 indent level.

> +	return 0;
> +}
> +
> +/**
> + * iecm_rx_desc_alloc_all - allocate all RX queues resources
> + * @vport: virtual port structure
> + *
> + * Returns 0 on success, negative on failure
> + */
> +static int iecm_rx_desc_alloc_all(struct iecm_vport *vport)
> +{
> +	struct device *dev = &vport->adapter->pdev->dev;
> +	struct iecm_rxq_group *rx_qgrp;
> +	int i, j, num_rxq, working_set;
> +	struct iecm_queue *q;
> +	int err = 0;
> +
> +	for (i = 0; i < vport->num_rxq_grp; i++) {
> +		rx_qgrp = &vport->rxq_grps[i];
> +		if (iecm_is_queue_model_split(vport->rxq_model))
> +			num_rxq = rx_qgrp->splitq.num_rxq_sets;
> +		else
> +			num_rxq = rx_qgrp->singleq.num_rxq;
> +
> +		for (j = 0; j < num_rxq; j++) {
> +			if (iecm_is_queue_model_split(vport->rxq_model))
> +				q = &rx_qgrp->splitq.rxq_sets[j]->rxq;
> +			else
> +				q = rx_qgrp->singleq.rxqs[j];
> +			err = iecm_rx_desc_alloc(q, false, vport->rxq_model);
> +			if (err) {
> +				dev_err(dev, "Memory allocation for Rx Queue %u failed\n",
> +					i);
> +				goto err_out;
> +			}
> +		}
> +
> +		if (iecm_is_queue_model_split(vport->rxq_model)) {
> +			for (j = 0; j < vport->num_bufqs_per_qgrp; j++) {
> +				q = &rx_qgrp->splitq.bufq_sets[j].bufq;
> +				err = iecm_rx_desc_alloc(q, true,
> +							 vport->rxq_model);
> +				if (err) {
> +					dev_err(dev, "Memory allocation for Rx Buffer Queue %u failed\n",
> +						i);
> +					goto err_out;
> +				}
> +
> +				working_set = IECM_RX_BUFQ_WORKING_SET(q);
> +				iecm_rx_post_init_bufs(q, working_set);
> +			}
> +		}
> +	}
> +err_out:
> +	if (err)
> +		iecm_rx_desc_rel_all(vport);
> +	return err;
> +}
> +
>  /**
>   * iecm_txq_group_rel - Release all resources for txq groups
>   * @vport: vport to release txq groups on
> @@ -478,6 +1017,61 @@ static void iecm_txq_group_rel(struct iecm_vport *vport)
>  	}
>  }
>  
> +/**
> + * iecm_rxq_sw_queue_rel - Release software queue resources
> + * @rx_qgrp: rx queue group with software queues
> + */
> +static void iecm_rxq_sw_queue_rel(struct iecm_rxq_group *rx_qgrp)
> +{
> +	int i, j;
> +
> +	for (i = 0; i < rx_qgrp->vport->num_bufqs_per_qgrp; i++) {
> +		struct iecm_bufq_set *bufq_set = &rx_qgrp->splitq.bufq_sets[i];
> +
> +		for (j = 0; j < bufq_set->num_refillqs; j++) {
> +			kfree(bufq_set->refillqs[j].ring);
> +			bufq_set->refillqs[j].ring = NULL;
> +		}
> +		kfree(bufq_set->refillqs);
> +		bufq_set->refillqs = NULL;
> +	}
> +}
> +
> +/**
> + * iecm_rxq_group_rel - Release all resources for rxq groups
> + * @vport: vport to release rxq groups on
> + */
> +static void iecm_rxq_group_rel(struct iecm_vport *vport)
> +{
> +	if (vport->rxq_grps) {
> +		int i;
> +
> +		for (i = 0; i < vport->num_rxq_grp; i++) {
> +			struct iecm_rxq_group *rx_qgrp = &vport->rxq_grps[i];
> +			int j, num_rxq;
> +
> +			if (iecm_is_queue_model_split(vport->rxq_model)) {
> +				num_rxq = rx_qgrp->splitq.num_rxq_sets;
> +				for (j = 0; j < num_rxq; j++) {
> +					kfree(rx_qgrp->splitq.rxq_sets[j]);
> +					rx_qgrp->splitq.rxq_sets[j] = NULL;
> +				}
> +				iecm_rxq_sw_queue_rel(rx_qgrp);
> +				kfree(rx_qgrp->splitq.bufq_sets);
> +				rx_qgrp->splitq.bufq_sets = NULL;
> +			} else {
> +				num_rxq = rx_qgrp->singleq.num_rxq;
> +				for (j = 0; j < num_rxq; j++) {
> +					kfree(rx_qgrp->singleq.rxqs[j]);
> +					rx_qgrp->singleq.rxqs[j] = NULL;
> +				}
> +			}
> +		}
> +		kfree(vport->rxq_grps);
> +		vport->rxq_grps = NULL;
> +	}

	if (!rxq_grps)
		return;

-1 lvl.

> +}
> +
>  /**
>   * iecm_vport_queue_grp_rel_all - Release all queue groups
>   * @vport: vport to release queue groups for
> @@ -485,6 +1079,7 @@ static void iecm_txq_group_rel(struct iecm_vport *vport)
>  static void iecm_vport_queue_grp_rel_all(struct iecm_vport *vport)
>  {
>  	iecm_txq_group_rel(vport);
> +	iecm_rxq_group_rel(vport);
>  }
>  
>  /**
> @@ -496,6 +1091,7 @@ static void iecm_vport_queue_grp_rel_all(struct iecm_vport *vport)
>  void iecm_vport_queues_rel(struct iecm_vport *vport)
>  {
>  	iecm_tx_desc_rel_all(vport);
> +	iecm_rx_desc_rel_all(vport);
>  	iecm_vport_queue_grp_rel_all(vport);
>  
>  	kfree(vport->txqs);
> @@ -715,6 +1311,24 @@ void iecm_vport_calc_num_q_vec(struct iecm_vport *vport)
>  }
>  EXPORT_SYMBOL(iecm_vport_calc_num_q_vec);
>  
> +/**
> + * iecm_rxq_set_descids - set the descids supported by this queue
> + * @vport: virtual port data structure
> + * @q: rx queue for which descids are set
> + *
> + */
> +static void iecm_rxq_set_descids(struct iecm_vport *vport, struct iecm_queue *q)
> +{
> +	if (vport->rxq_model == VIRTCHNL2_QUEUE_MODEL_SPLIT) {
> +		q->rxdids = VIRTCHNL2_RXDID_1_FLEX_SPLITQ_M;
> +	} else {
> +		if (vport->base_rxd)
> +			q->rxdids = VIRTCHNL2_RXDID_1_32B_BASE_M;
> +		else
> +			q->rxdids = VIRTCHNL2_RXDID_2_FLEX_SQ_NIC_M;
> +	}
> +}
> +
>  /**
>   * iecm_set_vlan_tag_loc - set the tag location for a tx/rx queue
>   * @adapter: adapter structure
> @@ -827,6 +1441,152 @@ static int iecm_txq_group_alloc(struct iecm_vport *vport, int num_txq)
>  	return err;
>  }
>  
> +/**
> + * iecm_rxq_group_alloc - Allocate all rxq group resources
> + * @vport: vport to allocate rxq groups for
> + * @num_rxq: number of rxqs to allocate for each group
> + *
> + * Returns 0 on success, negative on failure
> + */
> +static int iecm_rxq_group_alloc(struct iecm_vport *vport, int num_rxq)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct iecm_queue *q;
> +	int i, k, err = 0;
> +
> +	vport->rxq_grps = kcalloc(vport->num_rxq_grp,
> +				  sizeof(struct iecm_rxq_group), GFP_KERNEL);
> +	if (!vport->rxq_grps)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < vport->num_rxq_grp; i++) {
> +		struct iecm_rxq_group *rx_qgrp = &vport->rxq_grps[i];
> +		int j;
> +
> +		rx_qgrp->vport = vport;
> +		if (iecm_is_queue_model_split(vport->rxq_model)) {
> +			rx_qgrp->splitq.num_rxq_sets = num_rxq;
> +
> +			for (j = 0; j < num_rxq; j++) {
> +				rx_qgrp->splitq.rxq_sets[j] =
> +					kzalloc(sizeof(struct iecm_rxq_set),
> +						GFP_KERNEL);
> +				if (!rx_qgrp->splitq.rxq_sets[j]) {
> +					err = -ENOMEM;
> +					goto err_alloc;
> +				}
> +			}
> +
> +			rx_qgrp->splitq.bufq_sets = kcalloc(vport->num_bufqs_per_qgrp,
> +							    sizeof(struct iecm_bufq_set),
> +							    GFP_KERNEL);
> +			if (!rx_qgrp->splitq.bufq_sets) {
> +				err = -ENOMEM;
> +				goto err_alloc;
> +			}
> +
> +			for (j = 0; j < vport->num_bufqs_per_qgrp; j++) {
> +				struct iecm_bufq_set *bufq_set =
> +					&rx_qgrp->splitq.bufq_sets[j];
> +				int swq_size = sizeof(struct iecm_sw_queue);
> +
> +				q = &rx_qgrp->splitq.bufq_sets[j].bufq;
> +				q->dev = &adapter->pdev->dev;
> +				q->desc_count = vport->bufq_desc_count[j];
> +				q->vport = vport;
> +				q->rxq_grp = rx_qgrp;
> +				q->idx = j;
> +				q->rx_buf_size = vport->bufq_size[j];
> +				q->rx_buffer_low_watermark = IECM_LOW_WATERMARK;
> +				q->rx_buf_stride = IECM_RX_BUF_STRIDE;
> +
> +				if (test_bit(__IECM_PRIV_FLAGS_HDR_SPLIT,
> +					     adapter->config_data.user_flags)) {
> +					q->rx_hsplit_en = true;
> +					q->rx_hbuf_size = IECM_HDR_BUF_SIZE;
> +				}
> +
> +				bufq_set->num_refillqs = num_rxq;
> +				bufq_set->refillqs = kcalloc(num_rxq,
> +							     swq_size,
> +							     GFP_KERNEL);
> +				if (!bufq_set->refillqs) {
> +					err = -ENOMEM;
> +					goto err_alloc;
> +				}
> +				for (k = 0; k < bufq_set->num_refillqs; k++) {
> +					struct iecm_sw_queue *refillq =
> +						&bufq_set->refillqs[k];
> +
> +					refillq->dev =
> +						&vport->adapter->pdev->dev;
> +					refillq->buf_size = q->rx_buf_size;
> +					refillq->desc_count =
> +						vport->bufq_desc_count[j];
> +					set_bit(__IECM_Q_GEN_CHK,
> +						refillq->flags);
> +					set_bit(__IECM_RFLQ_GEN_CHK,
> +						refillq->flags);
> +					refillq->ring = kcalloc(refillq->desc_count,
> +								sizeof(u16),
> +								GFP_KERNEL);
> +					if (!refillq->ring) {
> +						err = -ENOMEM;
> +						goto err_alloc;
> +					}
> +				}
> +			}
> +		} else {
> +			rx_qgrp->singleq.num_rxq = num_rxq;
> +			for (j = 0; j < num_rxq; j++) {
> +				rx_qgrp->singleq.rxqs[j] =
> +					kzalloc(sizeof(*rx_qgrp->singleq.rxqs[j]), GFP_KERNEL);
> +				if (!rx_qgrp->singleq.rxqs[j]) {
> +					err = -ENOMEM;
> +					goto err_alloc;
> +				}
> +			}
> +		}
> +
> +		for (j = 0; j < num_rxq; j++) {
> +			if (iecm_is_queue_model_split(vport->rxq_model)) {
> +				q = &rx_qgrp->splitq.rxq_sets[j]->rxq;
> +				rx_qgrp->splitq.rxq_sets[j]->refillq0 =
> +				      &rx_qgrp->splitq.bufq_sets[0].refillqs[j];
> +				rx_qgrp->splitq.rxq_sets[j]->refillq1 =
> +				      &rx_qgrp->splitq.bufq_sets[1].refillqs[j];
> +
> +				if (test_bit(__IECM_PRIV_FLAGS_HDR_SPLIT,
> +					     adapter->config_data.user_flags)) {
> +					q->rx_hsplit_en = true;
> +					q->rx_hbuf_size = IECM_HDR_BUF_SIZE;
> +				}
> +			} else {
> +				q = rx_qgrp->singleq.rxqs[j];
> +			}
> +			q->dev = &adapter->pdev->dev;
> +			q->desc_count = vport->rxq_desc_count;
> +			q->vport = vport;
> +			q->rxq_grp = rx_qgrp;
> +			q->idx = (i * num_rxq) + j;
> +			/* In splitq mode, RXQ buffer size should be
> +			 * set to that of the first buffer queue
> +			 * associated with this RXQ
> +			 */
> +			q->rx_buf_size = vport->bufq_size[0];
> +			q->rx_buffer_low_watermark = IECM_LOW_WATERMARK;
> +			q->rx_max_pkt_size = vport->netdev->mtu +
> +							IECM_PACKET_HDR_PAD;
> +			iecm_rxq_set_descids(vport, q);
> +			iecm_set_vlan_tag_loc(adapter, q);
> +		}
> +	}
> +err_alloc:
> +	if (err)
> +		iecm_rxq_group_rel(vport);
> +	return err;
> +}
> +
>  /**
>   * iecm_vport_queue_grp_alloc_all - Allocate all queue groups/resources
>   * @vport: vport with qgrps to allocate
> @@ -841,6 +1601,11 @@ static int iecm_vport_queue_grp_alloc_all(struct iecm_vport *vport)
>  	iecm_vport_calc_numq_per_grp(vport, &num_txq, &num_rxq);
>  
>  	err = iecm_txq_group_alloc(vport, num_txq);
> +	if (err)
> +		goto err_out;
> +
> +	err = iecm_rxq_group_alloc(vport, num_rxq);
> +err_out:
>  	if (err)
>  		iecm_vport_queue_grp_rel_all(vport);
>  	return err;
> @@ -866,6 +1631,10 @@ int iecm_vport_queues_alloc(struct iecm_vport *vport)
>  	if (err)
>  		goto err_out;
>  
> +	err = iecm_rx_desc_alloc_all(vport);
> +	if (err)
> +		goto err_out;
> +
>  	err = iecm_vport_init_fast_path_txqs(vport);
>  	if (err)
>  		goto err_out;
> diff --git a/drivers/net/ethernet/intel/include/iecm_txrx.h b/drivers/net/ethernet/intel/include/iecm_txrx.h
> index 44c20f8a2039..5e29148938fb 100644
> --- a/drivers/net/ethernet/intel/include/iecm_txrx.h
> +++ b/drivers/net/ethernet/intel/include/iecm_txrx.h
> @@ -198,6 +198,11 @@ struct iecm_page_info {
>  	u16 pagecnt_bias;
>  };
>  
> +struct iecm_rx_hdr_buf_pages {
> +	u32 nr_pages;
> +	struct iecm_page_info *pages;

Place the pointer at the beginning to avoid gaps and alignment
issues.

> +};
> +
>  struct iecm_rx_buf {
>  #define IECM_RX_BUF_MAX_PAGES 2
>  	struct iecm_page_info page_info[IECM_RX_BUF_MAX_PAGES];
> @@ -498,6 +503,8 @@ struct iecm_queue {
>  					 * with scatter-gather
>  					 */
>  	DECLARE_HASHTABLE(sched_buf_hash, 12);
> +
> +	struct iecm_rx_hdr_buf_pages hbuf_pages;
>  } ____cacheline_internodealigned_in_smp;
>  
>  /* Software queues are used in splitq mode to manage buffers between rxq
> -- 
> 2.33.0

Thanks,
Al

^ permalink raw reply	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 11/19] iecm: add start_xmit and set_rx_mode
  2022-01-28  0:10 ` [Intel-wired-lan] [PATCH net-next 11/19] iecm: add start_xmit and set_rx_mode Alan Brady
@ 2022-01-28 16:35   ` Alexander Lobakin
  0 siblings, 0 replies; 89+ messages in thread
From: Alexander Lobakin @ 2022-01-28 16:35 UTC (permalink / raw)
  To: intel-wired-lan

From: Alan Brady <alan.brady@intel.com>
Date: Thu, 27 Jan 2022 16:10:01 -0800

> With open and stop done, this continues down the netdev_ops struct to add
> start_xmit and set_rx_mode callbacks. The rest of the data path will be
> added after netdev_ops are done.
> 
> Signed-off-by: Phani Burra <phani.r.burra@intel.com>
> Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
> Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
> Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
> Signed-off-by: Alan Brady <alan.brady@intel.com>
> ---
>  drivers/net/ethernet/intel/iecm/iecm_lib.c    | 247 ++++++-
>  drivers/net/ethernet/intel/iecm/iecm_txrx.c   | 667 ++++++++++++++++++
>  drivers/net/ethernet/intel/include/iecm.h     |   1 +
>  .../net/ethernet/intel/include/iecm_txrx.h    |  60 ++
>  4 files changed, 970 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/net/ethernet/intel/iecm/iecm_lib.c b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> index 037a0e06bb7b..003057f48f0c 100644
> --- a/drivers/net/ethernet/intel/iecm/iecm_lib.c
> +++ b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> @@ -141,6 +141,17 @@ struct iecm_vport *iecm_netdev_to_vport(struct net_device *netdev)
>  	return np->vport;
>  }
>  
> +/**
> + * iecm_netdev_to_adapter - get an adapter handle from a netdev
> + * @netdev: network interface device structure
> + */
> +struct iecm_adapter *iecm_netdev_to_adapter(struct net_device *netdev)
> +{
> +	struct iecm_netdev_priv *np = netdev_priv(netdev);
> +
> +	return np->vport->adapter;
> +}
> +
>  /**
>   * iecm_mb_intr_rel_irq - Free the IRQ association with the OS
>   * @adapter: adapter structure
> @@ -417,6 +428,61 @@ iecm_mac_filter *iecm_find_mac_filter(struct iecm_vport *vport,
>  	return NULL;
>  }
>  
> +/**
> + * __iecm_del_mac_filter - Delete MAC filter helper
> + * @vport: main vport struct
> + * @macaddr: address to delete
> + *
> + * Takes mac_filter_list_lock spinlock to set remove field for filter in list.
> + */
> +static struct
> +iecm_mac_filter *__iecm_del_mac_filter(struct iecm_vport *vport,
> +				       const u8 *macaddr)
> +{
> +	struct iecm_mac_filter *f;
> +
> +	spin_lock_bh(&vport->adapter->mac_filter_list_lock);
> +	f = iecm_find_mac_filter(vport, macaddr);
> +	if (f) {
> +		/* If filter was never synced to HW we can just delete it here,
> +		 * otherwise mark for removal.
> +		 */
> +		if (f->add) {
> +			list_del(&f->list);
> +			kfree(f);
> +			f = NULL;
> +		} else {
> +			f->remove = true;
> +		}
> +	}

	if (!f)
		goto unlock;

-1 level.

> +	spin_unlock_bh(&vport->adapter->mac_filter_list_lock);
> +
> +	return f;
> +}
> +
> +/**
> + * iecm_del_mac_filter - Delete a MAC filter from the filter list
> + * @vport: main vport structure
> + * @macaddr: the MAC address
> + *
> + * Removes filter from list and if interface is up, tells hardware about the
> + * removed filter.
> + **/
> +static void iecm_del_mac_filter(struct iecm_vport *vport, const u8 *macaddr)
> +{
> +	struct iecm_mac_filter *f;
> +
> +	if (!macaddr)
> +		return;
> +
> +	f = __iecm_del_mac_filter(vport, macaddr);
> +	if (!f)
> +		return;
> +
> +	if (vport->adapter->state == __IECM_UP)
> +		iecm_add_del_ether_addrs(vport, false, false);
> +}
> +
>  /**
>   * __iecm_add_mac_filter - Add mac filter helper function
>   * @vport: main vport struct
> @@ -1711,6 +1777,134 @@ void iecm_remove(struct pci_dev *pdev)
>  }
>  EXPORT_SYMBOL(iecm_remove);
>  
> +/**
> + * iecm_addr_sync - Callback for dev_(mc|uc)_sync to add address
> + * @netdev: the netdevice
> + * @addr: address to add
> + *
> + * Called by __dev_(mc|uc)_sync when an address needs to be added. We call
> + * __dev_(uc|mc)_sync from .set_rx_mode. Kernel takes addr_list_lock spinlock
> + * meaning we cannot sleep in this context. Due to this, we have to add the
> + * filter and send the virtchnl message asynchronously without waiting for the
> + * response from the other side. We won't know whether or not the operation
> + * actually succeeded until we get the message back.  Returns 0 on success,
> + * negative on failure.
> + */
> +static int iecm_addr_sync(struct net_device *netdev, const u8 *addr)
> +{
> +	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
> +
> +	if (__iecm_add_mac_filter(vport, addr)) {
> +		if (vport->adapter->state == __IECM_UP) {
> +			set_bit(__IECM_ADD_ETH_REQ, vport->adapter->flags);
> +			iecm_add_del_ether_addrs(vport, true, true);
> +		}
> +		return 0;
> +	}

	if (!add_filter())
		return -ENOMEM;

	if (state != __IECM_UP)
		return 0;

	set_bit(...

-2 (!) levels.

> +
> +	return -ENOMEM;
> +}
> +
> +/**
> + * iecm_addr_unsync - Callback for dev_(mc|uc)_sync to remove address
> + * @netdev: the netdevice
> + * @addr: address to add
> + *
> + * Called by __dev_(mc|uc)_sync when an address needs to be added. We call
> + * __dev_(uc|mc)_sync from .set_rx_mode. Kernel takes addr_list_lock spinlock
> + * meaning we cannot sleep in this context. Due to this we have to delete the
> + * filter and send the virtchnl message asychronously without waiting for the
> + * return from the other side.  We won't know whether or not the operation
> + * actually succeeded until we get the message back. Returns 0 on success,
> + * negative on failure.
> + */
> +static int iecm_addr_unsync(struct net_device *netdev, const u8 *addr)
> +{
> +	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
> +
> +	/* Under some circumstances, we might receive a request to delete
> +	 * our own device address from our uc list. Because we store the
> +	 * device address in the VSI's MAC/VLAN filter list, we need to ignore
> +	 * such requests and not delete our device address from this list.
> +	 */
> +	if (ether_addr_equal(addr, netdev->dev_addr))
> +		return 0;
> +
> +	if (__iecm_del_mac_filter(vport, addr)) {
> +		if (vport->adapter->state == __IECM_UP) {
> +			set_bit(__IECM_DEL_ETH_REQ, vport->adapter->flags);
> +			iecm_add_del_ether_addrs(vport, false, true);
> +		}
> +	}

Very same here.

> +
> +	return 0;
> +}
> +
> +/**
> + * iecm_set_rx_mode - NDO callback to set the netdev filters
> + * @netdev: network interface device structure
> + *
> + * Stack takes addr_list_lock spinlock before calling our .set_rx_mode.  We
> + * cannot sleep in this context.
> + */
> +static void iecm_set_rx_mode(struct net_device *netdev)
> +{
> +	struct iecm_adapter *adapter = iecm_netdev_to_adapter(netdev);
> +
> +	if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS, VIRTCHNL2_CAP_MACFILTER)) {
> +		__dev_uc_sync(netdev, iecm_addr_sync, iecm_addr_unsync);
> +		__dev_mc_sync(netdev, iecm_addr_sync, iecm_addr_unsync);
> +	}
> +
> +	if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS, VIRTCHNL2_CAP_PROMISC)) {
> +		bool changed = false;
> +
> +		/* IFF_PROMISC enables both unicast and multicast promiscuous,
> +		 * while IFF_ALLMULTI only enables multicast such that:
> +		 *
> +		 * promisc  + allmulti		= unicast | multicast
> +		 * promisc  + !allmulti		= unicast | multicast
> +		 * !promisc + allmulti		= multicast
> +		 */
> +		if ((netdev->flags & IFF_PROMISC) &&
> +		    !test_and_set_bit(__IECM_PROMISC_UC,
> +				      adapter->config_data.user_flags)) {
> +			changed = true;
> +			dev_info(&adapter->pdev->dev, "Entering promiscuous mode\n");
> +			if (!test_and_set_bit(__IECM_PROMISC_MC,
> +					      adapter->flags))
> +				dev_info(&adapter->pdev->dev, "Entering multicast promiscuous mode\n");
> +		}
> +		if (!(netdev->flags & IFF_PROMISC) &&
> +		    test_and_clear_bit(__IECM_PROMISC_UC,
> +				       adapter->config_data.user_flags)) {
> +			changed = true;
> +			dev_info(&adapter->pdev->dev, "Leaving promiscuous mode\n");
> +		}
> +		if (netdev->flags & IFF_ALLMULTI &&
> +		    !test_and_set_bit(__IECM_PROMISC_MC,
> +				      adapter->config_data.user_flags)) {
> +			changed = true;
> +			dev_info(&adapter->pdev->dev, "Entering multicast promiscuous mode\n");
> +		}
> +		if (!(netdev->flags & (IFF_ALLMULTI | IFF_PROMISC)) &&
> +		    test_and_clear_bit(__IECM_PROMISC_MC,
> +				       adapter->config_data.user_flags)) {
> +			changed = true;
> +			dev_info(&adapter->pdev->dev, "Leaving multicast promiscuous mode\n");
> +		}
> +
> +		if (changed) {
> +			int err = iecm_set_promiscuous(adapter);
> +
> +			if (err) {
> +				dev_info(&adapter->pdev->dev, "Failed to set promiscuous mode: %d\n",
> +					 err);
> +			}
> +		}
> +	}

	if (!cap_ena())
		return;

-1 level for a big pile of code.

> +}
> +
>  /**
>   * iecm_open - Called when a network interface becomes active
>   * @netdev: network interface device structure
> @@ -1730,6 +1924,49 @@ static int iecm_open(struct net_device *netdev)
>  	return iecm_vport_open(np->vport, true);
>  }
>  
> +/**
> + * iecm_set_mac - NDO callback to set port mac address
> + * @netdev: network interface device structure
> + * @p: pointer to an address structure
> + *
> + * Returns 0 on success, negative on failure
> + **/
> +static int iecm_set_mac(struct net_device *netdev, void *p)
> +{
> +	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
> +	struct iecm_mac_filter *f;
> +	struct sockaddr *addr = p;
> +
> +	if (!iecm_is_cap_ena(vport->adapter, IECM_OTHER_CAPS,
> +			     VIRTCHNL2_CAP_MACFILTER)) {
> +		dev_info(&vport->adapter->pdev->dev, "Setting MAC address is not supported\n");
> +		return -EOPNOTSUPP;
> +	}
> +
> +	if (!is_valid_ether_addr(addr->sa_data)) {
> +		dev_info(&vport->adapter->pdev->dev, "Invalid MAC address: %pM\n",
> +			 addr->sa_data);
> +		return -EADDRNOTAVAIL;
> +	}
> +
> +	if (ether_addr_equal(netdev->dev_addr, addr->sa_data))
> +		return 0;
> +
> +	/* Delete the current filter */
> +	if (is_valid_ether_addr(vport->default_mac_addr))
> +		iecm_del_mac_filter(vport, vport->default_mac_addr);
> +
> +	/* Add new filter */
> +	f = iecm_add_mac_filter(vport, addr->sa_data);
> +
> +	if (f) {
> +		ether_addr_copy(vport->default_mac_addr, addr->sa_data);
> +		dev_addr_mod(netdev, 0, addr->sa_data, ETH_ALEN);
> +	}
> +
> +	return f ? 0 : -ENOMEM;

	if (!f)
		return -ENOMEM;

	ether_addr(...
	dev_addr(...

	return 0;

Some unneeded complexity here.

> +}
> +
>  void *iecm_alloc_dma_mem(struct iecm_hw *hw, struct iecm_dma_mem *mem, u64 size)
>  {
>  	struct iecm_adapter *adapter = (struct iecm_adapter *)hw->back;
> @@ -1756,10 +1993,10 @@ void iecm_free_dma_mem(struct iecm_hw *hw, struct iecm_dma_mem *mem)
>  static const struct net_device_ops iecm_netdev_ops_splitq = {
>  	.ndo_open = iecm_open,
>  	.ndo_stop = iecm_stop,
> -	.ndo_start_xmit = NULL,
> -	.ndo_set_rx_mode = NULL,
> +	.ndo_start_xmit = iecm_tx_splitq_start,
> +	.ndo_set_rx_mode = iecm_set_rx_mode,
>  	.ndo_validate_addr = eth_validate_addr,
> -	.ndo_set_mac_address = NULL,
> +	.ndo_set_mac_address = iecm_set_mac,
>  	.ndo_change_mtu = NULL,
>  	.ndo_get_stats64 = NULL,
>  	.ndo_fix_features = NULL,
> @@ -1773,9 +2010,9 @@ static const struct net_device_ops iecm_netdev_ops_singleq = {
>  	.ndo_open = iecm_open,
>  	.ndo_stop = iecm_stop,
>  	.ndo_start_xmit = NULL,
> -	.ndo_set_rx_mode = NULL,
> +	.ndo_set_rx_mode = iecm_set_rx_mode,
>  	.ndo_validate_addr = eth_validate_addr,
> -	.ndo_set_mac_address = NULL,
> +	.ndo_set_mac_address = iecm_set_mac,
>  	.ndo_change_mtu = NULL,
>  	.ndo_get_stats64 = NULL,
>  	.ndo_fix_features = NULL,
> diff --git a/drivers/net/ethernet/intel/iecm/iecm_txrx.c b/drivers/net/ethernet/intel/iecm/iecm_txrx.c
> index fb6a61277b00..ef5fe659389b 100644
> --- a/drivers/net/ethernet/intel/iecm/iecm_txrx.c
> +++ b/drivers/net/ethernet/intel/iecm/iecm_txrx.c
> @@ -1655,6 +1655,673 @@ int iecm_vport_queues_alloc(struct iecm_vport *vport)
>  	return err;
>  }
>  
> +/**
> + * iecm_tx_splitq_build_ctb - populate command tag and size for queue
> + * based scheduling descriptors
> + * @desc: descriptor to populate
> + * @parms: pointer to tx params struct
> + * @td_cmd: command to be filled in desc
> + * @size: size of buffer
> + */
> +void
> +iecm_tx_splitq_build_ctb(union iecm_tx_flex_desc *desc,
> +			 struct iecm_tx_splitq_params *parms,
> +			 u16 td_cmd, u16 size)
> +{
> +	desc->q.qw1.cmd_dtype =
> +		cpu_to_le16(parms->dtype & IECM_FLEX_TXD_QW1_DTYPE_M);
> +	desc->q.qw1.cmd_dtype |=
> +		cpu_to_le16((td_cmd << IECM_FLEX_TXD_QW1_CMD_S) &
> +			    IECM_FLEX_TXD_QW1_CMD_M);
> +	desc->q.qw1.buf_size = cpu_to_le16((u16)size);
> +	desc->q.qw1.flex.l2tags.l2tag1 = cpu_to_le16(parms->td_tag);
> +}
> +
> +/**
> + * iecm_tx_splitq_build_flow_desc - populate command tag and size for flow
> + * scheduling descriptors
> + * @desc: descriptor to populate
> + * @parms: pointer to tx params struct
> + * @td_cmd: command to be filled in desc
> + * @size: size of buffer
> + */
> +void
> +iecm_tx_splitq_build_flow_desc(union iecm_tx_flex_desc *desc,
> +			       struct iecm_tx_splitq_params *parms,
> +			       u16 td_cmd, u16 size)
> +{
> +	desc->flow.qw1.cmd_dtype = (u16)parms->dtype | td_cmd;
> +	desc->flow.qw1.rxr_bufsize = cpu_to_le16((u16)size);
> +	desc->flow.qw1.compl_tag = cpu_to_le16(parms->compl_tag);
> +}
> +
> +/**
> + * iecm_tx_buf_avail - Stop Tx if no enough book keeping buffers are available
> + * @tx_q: the queue to be checked
> + *
> + * Return -EBUSY if Tx queue stop is needed, else 0
> + */
> +static int iecm_tx_buf_avail(struct iecm_queue *tx_q)
> +{
> +	/* If We have less than a quarter of the total desc_count left
> +	 * stop the queue to wait for more completions
> +	 */
> +	if (unlikely(IECM_TX_BUF_UNUSED(tx_q) < tx_q->desc_count >> 2)) {
> +		netif_stop_subqueue(tx_q->vport->netdev, tx_q->idx);
> +		return -EBUSY;
> +	}
> +	return 0;

	if (likely(...))
		return 0;

What can be easier than this.
I also suggest to

#define iecm_tx_buf_threshold(q)	((q)->desc_count >> 2)

in order to be able to easily find and change this in the future.

> +}
> +
> +/**
> + * __iecm_tx_maybe_stop - 2nd level check for Tx stop conditions
> + * @tx_q: the queue to be checked
> + * @size: the size buffer we want to assure is available
> + *
> + * Returns -EBUSY if a stop is needed, else 0
> + */
> +static int
> +__iecm_tx_maybe_stop(struct iecm_queue *tx_q, unsigned int size)
> +{
> +	netif_stop_subqueue(tx_q->vport->netdev, tx_q->idx);
> +
> +	/* Memory barrier before checking head and tail */
> +	smp_mb();
> +
> +	/* Check again in a case another CPU has just made room available. */
> +	if (likely(IECM_DESC_UNUSED(tx_q) < size))
> +		return -EBUSY;
> +
> +	/* A reprieve! - use start_subqueue because it doesn't call schedule */
> +	netif_start_subqueue(tx_q->vport->netdev, tx_q->idx);
> +
> +	return 0;
> +}
> +
> +/**
> + * iecm_tx_maybe_stop - 1st level check for Tx stop conditions
> + * @tx_q: the queue to be checked
> + * @size: number of descriptors we want to assure is available
> + *
> + * Returns 0 if stop is not needed
> + */
> +int iecm_tx_maybe_stop(struct iecm_queue *tx_q, unsigned int size)
> +{
> +	if (likely(IECM_DESC_UNUSED(tx_q) >= size))
> +		return 0;
> +
> +	return __iecm_tx_maybe_stop(tx_q, size);
> +}
> +
> +/**
> + * iecm_tx_buf_hw_update - Store the new tail value
> + * @tx_q: queue to bump
> + * @val: new tail index
> + * @xmit_more: more skb's pending
> + *
> + * The naming here is special in that 'hw' signals that this function is about
> + * to do a register write to update our queue status. We know this can only
> + * mean tail here as HW should be owning head for TX.
> + */
> +void iecm_tx_buf_hw_update(struct iecm_queue *tx_q, u32 val,
> +			   bool xmit_more)
> +{
> +	struct netdev_queue *nq;
> +
> +	nq = netdev_get_tx_queue(tx_q->vport->netdev, tx_q->idx);
> +	tx_q->next_to_use = val;
> +
> +	iecm_tx_maybe_stop(tx_q, IECM_TX_DESC_NEEDED);
> +
> +	/* Force memory writes to complete before letting h/w
> +	 * know there are new descriptors to fetch.  (Only
> +	 * applicable for weak-ordered memory model archs,
> +	 * such as IA-64).
> +	 */
> +	wmb();
> +
> +	/* notify HW of packet */
> +	if (netif_xmit_stopped(nq) || !xmit_more)
> +		writel(val, tx_q->tail);
> +}
> +
> +/**
> + * iecm_size_to_txd_count - Get the number of descriptors needed for Tx
> + * @size: transmit request size in bytes
> + *
> + * Due to hardware alignment restrictions (4K alignment), we need to assume
> + * that we can have no more than 12K of data per descriptor, even though each
> + * descriptor can take up to 16K - 1 bytes of aligned memory.
> + */
> +unsigned int iecm_size_to_txd_count(unsigned int size)

It seems to be used only in one file. Static then.
If it will be used in other files later, I'd suggest to make it
non-static only then, but in general this should be a static inline
as it's small and simple, but is being called from very
perf-critical paths. Uninlining two math operations is a huge
overkill.

> +{
> +	return (size / 12288) + IECM_TX_DESCS_FOR_SKB_DATA_PTR;

0. Braces around `size / 12288` are not needed.

1. skb_headlen(skb) can hypothetically be == 0, as well as some
skb_frag_size(frag). In this case, the function will return 1,
although we don't need a descriptor for no data.
It is a rough mistake, it didn't harm only because zero-sized skb
segs are not common (zero-sized heads are getting more used tho).

So,

	return size ? size / 12288 + overhead : 0;

2. The logics here is very obscure.
At first, I thought that it's a mistake to naturally-divide as
we want it to be rounded-up. But then I realized that this
FOR_SKB_DATA_PTR means 1 initial descriptor, thus rounding down.
So, with pt. 1 in mind, it would look like

	return DIV_ROUND_UP(size, 12288);

For size == 0 it returns 0,
for size == 1 -> 1,
for 12288 -> 1,
for 12289 -> 2,
...

Oh wait. Your original function returns 1 for 12287 and 2 for 12288.
Is that intentional? Does one descriptor fit 12287 or 12288 bytes?
If 12287, then you should do DIV_ROUND_UP(size, 12287).
If 12288, then use the code 10 lines above, and then it's a mistake
in your code which returns 2 desc for 12288.

If I got the meaning of FOR_SKB_DATA_PTR wrong, then please explain.
For now it looks like it's not a really fitting name, it sounds like
it defines the overhead for one skb, not each fragment (and you call
this function for each).

3. Please define 12288 somewhere and move the comment there. I also
highly recommend to define it as

ALIGN_DOWN(SZ_16K - 1, SZ_4K)

This explicitly tells the reader that:
 - a descriptor can take up to 16K - 1;
 - we have the requirement of aligning everything to 4K;
 - as 16K doesn't fit into (16K - 1) boundary, we align it down.

With this construction, I could get it even with no commentary, but
for now 12288 looks like something magic or just random.
Version from e1000 with mul byt 85 and shift by 20 looks even more
cryptic. And 32-bit division on any 32-bit arch is atomic and fast.

I've just found that it's actually defined as
IECM_TX_MAX_DESC_DATA_ALIGNED (open-coded instead of ALIGN_DOWN()).
Please do it a make-up and use here instead of open-coding again and
again.

With all those taken into account, this code looks optimal to me
(keep in mind that this should be moved to a header file):

/*
 * Comment about hardware reqs here
 */
#define IECM_TX_MAX_DESC_DATA_ALIGNED	ALIGN_DOWN(SZ_16K - 1, SZ_4K)

...

static inline u32 iecm_size_to_txd_count(u32 size)
{
	return DIV_ROUND_UP(size, IECM_TX_MAX_DESC_DATA_ALIGNED);
}

If something changes in the hardware, you'd only need to adjust the
IECM_TX_MAX_BUFFER_LEN and the comment above it, and that's all.

Summarizing, it seems like this hasn't been tested on corner cases,
e.g. with skb which has its headlen or one of the frag's len 0, 1,
12287, 12288 and 12289, maybe even hasn't been tested with some
frag (or head) larger than 12288 at all (usually it's no larger
than PAGE_SIZE, i.e. SZ_4K on x86).
You can craft an arbitrary packet using raw sockets or write a
simple self-test in the iecm or idpf module which will create a
new skb with your own params right in the kernel (your module)
and send it to the wire.

> +}
> +
> +/**
> + * iecm_tx_desc_count_required - calculate number of Tx descriptors needed
> + * @skb: send buffer
> + *
> + * Returns number of data descriptors needed for this skb.
> + */
> +unsigned int iecm_tx_desc_count_required(struct sk_buff *skb)

You only read the skb fields and ptrs, it should be const.

> +{
> +	const skb_frag_t *frag = &skb_shinfo(skb)->frags[0];
> +	unsigned int nr_frags = skb_shinfo(skb)->nr_frags;
> +	unsigned int count = 0, size = skb_headlen(skb);
> +
> +	for (;;) {
> +		count += iecm_size_to_txd_count(size);
> +
> +		if (!nr_frags--)
> +			break;
> +
> +		size = skb_frag_size(frag++);
> +	}
> +
> +	return count;
> +}

	const skb_frag_t *frags = skb_shinfo(skb)->frags;
	u32 i, count = 0;

	for (i = 0; i = skb_shinfo(skb)->nr_frags; i++)
		count += iecm_size_to_txd_count(skb_frag_size(frags + i));

	return count + iecm_size_to_txd_count(skb_headlen(skb));

Two times smaller, two times more readable and I bet the object code
will be more optimized.

> +
> +/**
> + * iecm_tx_splitq_map - Build the Tx flex descriptor
> + * @tx_q: queue to send buffer on
> + * @parms: pointer to splitq params struct
> + * @first: first buffer info buffer to use
> + *
> + * This function loops over the skb data pointed to by *first
> + * and gets a physical address for each memory location and programs
> + * it and the length into the transmit flex descriptor.
> + */
> +static void
> +iecm_tx_splitq_map(struct iecm_queue *tx_q,
> +		   struct iecm_tx_splitq_params *parms,
> +		   struct iecm_tx_buf *first)
> +{
> +	union iecm_tx_flex_desc *tx_desc;
> +	unsigned int data_len, size;
> +	struct iecm_tx_buf *tx_buf;
> +	u16 i = tx_q->next_to_use;
> +	struct netdev_queue *nq;
> +	struct sk_buff *skb;
> +	skb_frag_t *frag;
> +	u16 td_cmd = 0;
> +	dma_addr_t dma;
> +
> +	skb = first->skb;
> +
> +	td_cmd = parms->offload.td_cmd;
> +	parms->compl_tag = tx_q->tx_buf_key;
> +
> +	data_len = skb->data_len;
> +	size = skb_headlen(skb);
> +
> +	tx_desc = IECM_FLEX_TX_DESC(tx_q, i);
> +
> +	dma = dma_map_single(tx_q->dev, skb->data, size, DMA_TO_DEVICE);
> +
> +	tx_buf = first;
> +
> +	for (frag = &skb_shinfo(skb)->frags[0];; frag++) {
> +		unsigned int max_data = IECM_TX_MAX_DESC_DATA_ALIGNED;
> +
> +		if (dma_mapping_error(tx_q->dev, dma))
> +			goto dma_error;
> +
> +		/* record length, and DMA address */
> +		dma_unmap_len_set(tx_buf, len, size);
> +		dma_unmap_addr_set(tx_buf, dma, dma);
> +
> +		/* align size to end of page */
> +		max_data += -dma & (IECM_TX_MAX_READ_REQ_SIZE - 1);

Super-cryptic. What is going on here?
dma_addr_t is unsigned and its bit-width can vary a lot depending
on the platform, playing with signedness is playing with fire here.
max_data is unsigned.
My test program gave me:

dma:         1
-dma & 4095: 4095
max_data:    16383

max_data is initially set to 12288 which is claimed to be the
maximum Tx desc buffer size. And now you increase it to SZ_16K - 1.
Either your comment above is incorrect, or this logics is.

dma:         4096
-dma & 4095: 0
max_data:    12288

Ok, maybe I got it, this cryptic -dma & 4095 is basically the
distance from the address to the next HW boundary:

rem = 4096 - dma & (4096 - 1);

While your code looks more compact, it at least needs some comments
on why you do that.
Still no idea what max_data is. For both dma == 0 and dma == 4096 it
equals to 12288.

> +
> +		/* buf_addr is in same location for both desc types */
> +		tx_desc->q.buf_addr = cpu_to_le64(dma);
> +
> +		/* account for data chunks larger than the hardware
> +		 * can handle
> +		 */
> +		while (unlikely(size > IECM_TX_MAX_DESC_DATA)) {

In the function which counts the number of descs needed you check
head and frag length against 12288, and here's a check against
16383.
I either don't get something basic, or this is another mistake.

> +			parms->splitq_build_ctb(tx_desc, parms, td_cmd,
> +						max_data);

And now you add indirect calls to hotpath, which on x86_64 is
expensive as hell with retpolines.

I checked your previous patch where you set this callback, and
there's absolutely no reason for introducing a callback here.
It can be set to one of two functions already defined in the same
module. So basically it's just a boolean 0/1 or direct bitmap test.
At least it could be declared with INDIRECT_CALLABLE_DECLARE() +
INDIRECT_CALL_2() to convert it into if-else on x86 with retpolines.
But indirect call wrappers are made in mind that the actual callback
might be something different from 1st or 2nd function, and in this
case it can not.
So it's a pure if-else put into a static inline for convenience.

> +
> +			tx_desc++;
> +			i++;
> +
> +			if (i == tx_q->desc_count) {
> +				tx_desc = IECM_FLEX_TX_DESC(tx_q, 0);
> +				i = 0;
> +			}
> +
> +			dma += max_data;
> +			size -= max_data;
> +
> +			max_data = IECM_TX_MAX_DESC_DATA_ALIGNED;
> +			/* buf_addr is in same location for both desc types */
> +			tx_desc->q.buf_addr = cpu_to_le64(dma);
> +		}
> +
> +		if (likely(!data_len))

GSO packets can have up to 16 frags (re. that this driver doesn't
support fraglists) and is turned on by default. Driver advertises
support for both TSO and UDP L4 GSO, which means that such skbs
will be a relatively common thing.
And each of them will hit the unlikely condition branch 15 times.
Like one indirect call per each segment was not enough to kill
the performance.

> +			break;
> +		parms->splitq_build_ctb(tx_desc, parms, td_cmd, size);
> +		tx_desc++;
> +		i++;
> +
> +		if (i == tx_q->desc_count) {
> +			tx_desc = IECM_FLEX_TX_DESC(tx_q, 0);
> +			i = 0;
> +		}
> +
> +		size = skb_frag_size(frag);
> +		data_len -= size;
> +
> +		dma = skb_frag_dma_map(tx_q->dev, frag, 0, size,
> +				       DMA_TO_DEVICE);
> +
> +		tx_buf->compl_tag = parms->compl_tag;
> +		tx_buf = &tx_q->tx_buf[i];
> +	}
> +
> +	/* record bytecount for BQL */
> +	nq = netdev_get_tx_queue(tx_q->vport->netdev, tx_q->idx);
> +	netdev_tx_sent_queue(nq, first->bytecount);
> +
> +	/* record SW timestamp if HW timestamp is not available */
> +	skb_tx_timestamp(first->skb);
> +
> +	/* write last descriptor with RS and EOP bits */
> +	td_cmd |= parms->eop_cmd;
> +	parms->splitq_build_ctb(tx_desc, parms, td_cmd, size);
> +	i++;
> +	if (i == tx_q->desc_count)
> +		i = 0;
> +
> +	/* set next_to_watch value indicating a packet is present */
> +	first->next_to_watch = tx_desc;
> +	tx_buf->compl_tag = parms->compl_tag++;
> +
> +	iecm_tx_buf_hw_update(tx_q, i, netdev_xmit_more());
> +
> +	/* Update TXQ Completion Tag key for next buffer */
> +	tx_q->tx_buf_key = parms->compl_tag;
> +
> +	return;
> +
> +dma_error:
> +	/* clear dma mappings for failed tx_buf map */
> +	for (;;) {
> +		tx_buf = &tx_q->tx_buf[i];
> +		iecm_tx_buf_rel(tx_q, tx_buf);
> +		if (tx_buf == first)
> +			break;
> +		if (i == 0)
> +			i = tx_q->desc_count;
> +		i--;
> +	}
> +
> +	tx_q->next_to_use = i;
> +}
> +
> +/**
> + * iecm_tx_prepare_vlan_flags - prepare generic vlan tagging for HW
> + * @tx_q: txq to find the tag location
> + * @first: pointer to struct iecm_tx_buf
> + * @skb: skb being xmitted
> + */
> +void iecm_tx_prepare_vlan_flags(struct iecm_queue *tx_q,
> +				struct iecm_tx_buf *first,
> +				struct sk_buff *skb)
> +{
> +	struct iecm_vport *vport = tx_q->vport;
> +	u32 tx_flags = 0;
> +
> +	/* Stack sets protocol to 8021q when offload is disabled so SW can take
> +	 * any necessary steps to handle it.  We don't need to do anything,
> +	 * just set protocol to encapsulated type.
> +	 */
> +	if (skb->protocol == htons(ETH_P_8021Q) &&
> +	    !iecm_is_feature_ena(vport, NETIF_F_HW_VLAN_CTAG_RX)) {
> +		skb->protocol = vlan_get_protocol(skb);
> +		return;
> +	}
> +
> +	if (!skb_vlan_tag_present(skb))
> +		return;
> +
> +	tx_flags |= skb_vlan_tag_get(skb) << IECM_TX_FLAGS_VLAN_SHIFT;
> +	tx_flags |= IECM_TX_FLAGS_VLAN_TAG;
> +	if (test_bit(__IECM_Q_VLAN_TAG_LOC_L2TAG2, tx_q->flags))
> +		tx_flags |= IECM_TX_FLAGS_HW_OUTER_SINGLE_VLAN;
> +	else if (test_bit(__IECM_Q_VLAN_TAG_LOC_L2TAG1, tx_q->flags))
> +		tx_flags |= IECM_TX_FLAGS_HW_VLAN;
> +	else
> +		dev_dbg(&vport->adapter->pdev->dev, "Unsupported Tx VLAN tag location requested\n");
> +
> +	first->tx_flags |= tx_flags;
> +}
> +
> +/**
> + * iecm_tso - computes mss and TSO length to prepare for TSO
> + * @first: pointer to struct iecm_tx_buf
> + * @off: pointer to struct that holds offload parameters
> + *
> + * Returns error (negative) if TSO doesn't apply to the given skb,
> + * 0 otherwise.
> + *
> + * Note: this function can be used in the splitq and singleq paths
> + */
> +int iecm_tso(struct iecm_tx_buf *first, struct iecm_tx_offload_params *off)
> +{
> +	struct sk_buff *skb = first->skb;
> +	union {
> +		struct iphdr *v4;
> +		struct ipv6hdr *v6;
> +		unsigned char *hdr;
> +	} ip;
> +	union {
> +		struct tcphdr *tcp;
> +		struct udphdr *udp;
> +		unsigned char *hdr;
> +	} l4;
> +	u32 paylen, l4_start;
> +	int err;
> +
> +	if (!skb_is_gso(skb))
> +		return 0;
> +
> +	err = skb_cow_head(skb, 0);
> +	if (err < 0)
> +		return err;
> +
> +	ip.hdr = skb_network_header(skb);
> +	l4.hdr = skb_transport_header(skb);
> +
> +	/* initialize outer IP header fields */
> +	if (ip.v4->version == 4) {
> +		ip.v4->tot_len = 0;
> +		ip.v4->check = 0;
> +	} else if (ip.v4->version == 6) {
> +		ip.v6->payload_len = 0;
> +	} else {
> +		return -EINVAL;
> +	}
> +
> +	l4_start = skb_transport_offset(skb);
> +
> +	/* remove payload length from checksum */
> +	paylen = skb->len - l4_start;
> +
> +	switch (skb_shinfo(skb)->gso_type) {
> +	case SKB_GSO_TCPV4:
> +	case SKB_GSO_TCPV6:
> +		csum_replace_by_diff(&l4.tcp->check,
> +				     (__force __wsum)htonl(paylen));
> +
> +		/* compute length of segmentation header */
> +		off->tso_hdr_len = tcp_hdrlen(skb) + l4_start;
> +		break;
> +	case SKB_GSO_UDP_L4:
> +		csum_replace_by_diff(&l4.udp->check,
> +				     (__force __wsum)htonl(paylen));
> +		/* compute length of segmentation header */
> +		off->tso_hdr_len = sizeof(struct udphdr) + l4_start;
> +		l4.udp->len =
> +			htons(skb_shinfo(skb)->gso_size +
> +			      sizeof(struct udphdr));
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	off->tso_len = skb->len - off->tso_hdr_len;
> +	off->mss = skb_shinfo(skb)->gso_size;
> +
> +	/* update gso_segs and bytecount */
> +	first->gso_segs = skb_shinfo(skb)->gso_segs;
> +	first->bytecount = qdisc_skb_cb(skb)->pkt_len;
> +
> +	first->tx_flags |= IECM_TX_FLAGS_TSO;
> +
> +	return 0;
> +}
> +
> +/**
> + * __iecm_chk_linearize - Check skb is not using too many buffers
> + * @skb: send buffer
> + * @max_bufs: maximum number of buffers
> + *
> + * For TSO we need to count the TSO header and segment payload separately.  As
> + * such we need to check cases where we have max_bufs-1 fragments or more as we
> + * can potentially require max_bufs+1 DMA transactions, 1 for the TSO header, 1
> + * for the segment payload in the first descriptor, and another max_buf-1 for
> + * the fragments.
> + */
> +static bool __iecm_chk_linearize(struct sk_buff *skb, unsigned int max_bufs)
> +{
> +	const skb_frag_t *frag, *stale;
> +	int nr_frags, sum;
> +
> +	/* no need to check if number of frags is less than max_bufs - 1 */
> +	nr_frags = skb_shinfo(skb)->nr_frags;
> +	if (nr_frags < (max_bufs - 1))
> +		return false;
> +
> +	/* We need to walk through the list and validate that each group
> +	 * of max_bufs-2 fragments totals at least gso_size.
> +	 */
> +	nr_frags -= max_bufs - 2;
> +	frag = &skb_shinfo(skb)->frags[0];
> +
> +	/* Initialize size to the negative value of gso_size minus 1.  We use
> +	 * this as the worst case scenario in which the frag ahead of us only
> +	 * provides one byte which is why we are limited to max_bufs-2
> +	 * descriptors for a single transmit as the header and previous
> +	 * fragment are already consuming 2 descriptors.
> +	 */
> +	sum = 1 - skb_shinfo(skb)->gso_size;
> +
> +	/* Add size of frags 0 through 4 to create our initial sum */
> +	sum += skb_frag_size(frag++);
> +	sum += skb_frag_size(frag++);
> +	sum += skb_frag_size(frag++);
> +	sum += skb_frag_size(frag++);
> +	sum += skb_frag_size(frag++);

There can be junk values if shinfo->nr_frags < 5. It's not a rare
case to have a small GSO packet.
It should check for shinfo->nr_frags first.

> +
> +	/* Walk through fragments adding latest fragment, testing it, and
> +	 * then removing stale fragments from the sum.
> +	 */
> +	for (stale = &skb_shinfo(skb)->frags[0];; stale++) {
> +		int stale_size = skb_frag_size(stale);
> +
> +		sum += skb_frag_size(frag++);
> +
> +		/* The stale fragment may present us with a smaller
> +		 * descriptor than the actual fragment size. To account
> +		 * for that we need to remove all the data on the front and
> +		 * figure out what the remainder would be in the last
> +		 * descriptor associated with the fragment.
> +		 */
> +		if (stale_size > IECM_TX_MAX_DESC_DATA) {
> +			int align_pad = -(skb_frag_off(stale)) &
> +					(IECM_TX_MAX_READ_REQ_SIZE - 1);

Open-coded stuff again. Please define this operation once and use
the definition.

> +
> +			sum -= align_pad;
> +			stale_size -= align_pad;
> +
> +			do {
> +				sum -= IECM_TX_MAX_DESC_DATA_ALIGNED;
> +				stale_size -= IECM_TX_MAX_DESC_DATA_ALIGNED;
> +			} while (stale_size > IECM_TX_MAX_DESC_DATA);
> +		}
> +
> +		/* if sum is negative we failed to make sufficient progress */
> +		if (sum < 0)
> +			return true;
> +
> +		if (!nr_frags--)
> +			break;
> +
> +		sum -= stale_size;
> +	}
> +
> +	return false;
> +}
> +
> +/**
> + * iecm_chk_linearize - Check if skb exceeds max descriptors per packet
> + * @skb: send buffer
> + * @max_bufs: maximum scatter gather buffers for single packet
> + * @count: number of buffers this packet needs
> + *
> + * Make sure we don't exceed maximum scatter gather buffers for a single
> + * packet. We have to do some special checking around the boundary (max_bufs-1)
> + * if TSO is on since we need count the TSO header and payload separately.
> + * E.g.: a packet with 7 fragments can require 9 DMA transactions; 1 for TSO
> + * header, 1 for segment payload, and then 7 for the fragments.
> + */
> +bool iecm_chk_linearize(struct sk_buff *skb, unsigned int max_bufs,
> +			unsigned int count)
> +{
> +	if (likely(count < max_bufs))
> +		return false;
> +	if (skb_is_gso(skb))
> +		return __iecm_chk_linearize(skb, max_bufs);
> +
> +	return count != max_bufs;
> +}
> +
> +/**
> + * iecm_tx_splitq_frame - Sends buffer on Tx ring using flex descriptors
> + * @skb: send buffer
> + * @tx_q: queue to send buffer on
> + *
> + * Returns NETDEV_TX_OK if sent, else an error code
> + */
> +static netdev_tx_t
> +iecm_tx_splitq_frame(struct sk_buff *skb, struct iecm_queue *tx_q)
> +{
> +	struct iecm_tx_splitq_params tx_parms = {
> +		NULL, (enum iecm_tx_desc_dtype_value)0, 0, {0}, {0}
> +	};
> +	struct iecm_tx_buf *first;
> +	unsigned int count;
> +
> +	count = iecm_tx_desc_count_required(skb);
> +	if (iecm_chk_linearize(skb, tx_q->tx_max_bufs, count)) {
> +		if (__skb_linearize(skb)) {
> +			dev_kfree_skb_any(skb);
> +			return NETDEV_TX_OK;
> +		}
> +		count = iecm_size_to_txd_count(skb->len);
> +		tx_q->vport->port_stats.tx_linearize++;

Why is this counter not protected with u64_stats?
Also, please use u64_stats_t type for statistics. It provides
tearing protection for 64-bit plaforms. Using plain u64 is
discouraged.

> +	}
> +
> +	if (iecm_tx_maybe_stop(tx_q, count + IECM_TX_DESCS_PER_CACHE_LINE +
> +			       IECM_TX_DESCS_FOR_CTX)) {
> +		return NETDEV_TX_BUSY;
> +	}
> +
> +	/* Also check for available book keeping buffers */
> +	if (iecm_tx_buf_avail(tx_q))
> +		return NETDEV_TX_BUSY;
> +
> +	/* record the location of the first descriptor for this packet */
> +	first = &tx_q->tx_buf[tx_q->next_to_use];
> +	first->skb = skb;
> +	first->bytecount = max_t(unsigned int, skb->len, ETH_ZLEN);
> +	first->gso_segs = 1;
> +	first->tx_flags = 0;
> +
> +	iecm_tx_prepare_vlan_flags(tx_q, first, skb);
> +
> +	if (iecm_tso(first, &tx_parms.offload) < 0) {
> +		/* If tso returns an error, drop the packet */
> +		dev_kfree_skb_any(skb);
> +		return NETDEV_TX_OK;
> +	}
> +
> +	if (first->tx_flags & IECM_TX_FLAGS_TSO) {
> +		/* If tso is needed, set up context desc */
> +		union iecm_flex_tx_ctx_desc *ctx_desc;
> +		int i = tx_q->next_to_use;
> +
> +		/* grab the next descriptor */
> +		ctx_desc = IECM_FLEX_TX_CTX_DESC(tx_q, i);
> +		i++;
> +		tx_q->next_to_use = (i < tx_q->desc_count) ? i : 0;
> +
> +		ctx_desc->tso.qw1.cmd_dtype =
> +				cpu_to_le16(IECM_TX_DESC_DTYPE_FLEX_TSO_CTX |
> +					    IECM_TX_FLEX_CTX_DESC_CMD_TSO);
> +		ctx_desc->tso.qw0.flex_tlen =
> +				cpu_to_le32(tx_parms.offload.tso_len &
> +					    IECM_TXD_FLEX_CTX_TLEN_M);
> +		ctx_desc->tso.qw0.mss_rt =
> +				cpu_to_le16(tx_parms.offload.mss &
> +					    IECM_TXD_FLEX_CTX_MSS_RT_M);
> +		ctx_desc->tso.qw0.hdr_len = tx_parms.offload.tso_hdr_len;
> +
> +		u64_stats_update_begin(&tx_q->stats_sync);
> +		tx_q->q_stats.tx.lso_pkts++;
> +		u64_stats_update_end(&tx_q->stats_sync);

And this is protected...

> +	}
> +
> +	if (test_bit(__IECM_Q_FLOW_SCH_EN, tx_q->flags)) {
> +		tx_parms.dtype = IECM_TX_DESC_DTYPE_FLEX_FLOW_SCHE;
> +		tx_parms.splitq_build_ctb = iecm_tx_splitq_build_flow_desc;

Here's what I was talking about. There's absolutely no need to
create indirect call overhead just because of one if-else.
Just replace this callback with `u8 flow : 1;` and assign 0 or 1
here in this function. Use a static inline to call the appropriate
function depending on the value of `flow`.

> +		tx_parms.eop_cmd =
> +			IECM_TXD_FLEX_FLOW_CMD_EOP | IECM_TXD_FLEX_FLOW_CMD_RE;
> +
> +		if (skb->ip_summed == CHECKSUM_PARTIAL)
> +			tx_parms.offload.td_cmd |= IECM_TXD_FLEX_FLOW_CMD_CS_EN;
> +
> +	} else {
> +		tx_parms.dtype = IECM_TX_DESC_DTYPE_FLEX_L2TAG1_L2TAG2;
> +		tx_parms.splitq_build_ctb = iecm_tx_splitq_build_ctb;
> +		tx_parms.eop_cmd = IECM_TXD_LAST_DESC_CMD;
> +
> +		if (skb->ip_summed == CHECKSUM_PARTIAL)
> +			tx_parms.offload.td_cmd |= IECM_TX_FLEX_DESC_CMD_CS_EN;
> +
> +		/* VLAN Offload can only be used with queue based scheduling */
> +		if (first->tx_flags & IECM_TX_FLAGS_VLAN_TAG) {
> +			tx_parms.offload.td_cmd |= (u64)IECM_TX_FLEX_DESC_CMD_IL2TAG1;
> +			tx_parms.td_tag = (first->tx_flags & IECM_TX_FLAGS_VLAN_MASK) >>
> +					  IECM_TX_FLAGS_VLAN_SHIFT;
> +		}
> +	}
> +
> +	iecm_tx_splitq_map(tx_q, &tx_parms, first);
> +
> +	return NETDEV_TX_OK;
> +}
> +
> +/**
> + * iecm_tx_splitq_start - Selects the right Tx queue to send buffer
> + * @skb: send buffer
> + * @netdev: network interface device structure
> + *
> + * Returns NETDEV_TX_OK if sent, else an error code
> + */
> +netdev_tx_t iecm_tx_splitq_start(struct sk_buff *skb,
> +				 struct net_device *netdev)
> +{
> +	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
> +	struct iecm_queue *tx_q;
> +
> +	if (skb->queue_mapping >= vport->num_txq)
> +		return -EINVAL;
> +
> +	tx_q = vport->txqs[skb->queue_mapping];
> +
> +	/* hardware can't handle really short frames, hardware padding works
> +	 * beyond this point
> +	 */
> +	if (skb_put_padto(skb, IECM_TX_MIN_LEN))
> +		return NETDEV_TX_OK;
> +
> +	return iecm_tx_splitq_frame(skb, tx_q);
> +}
> +
>  /**
>   * iecm_vport_intr_clean_queues - MSIX mode Interrupt Handler
>   * @irq: interrupt number
> diff --git a/drivers/net/ethernet/intel/include/iecm.h b/drivers/net/ethernet/intel/include/iecm.h
> index 4304256f7010..f6f9884c10c2 100644
> --- a/drivers/net/ethernet/intel/include/iecm.h
> +++ b/drivers/net/ethernet/intel/include/iecm.h
> @@ -717,6 +717,7 @@ int iecm_send_alloc_vectors_msg(struct iecm_adapter *adapter, u16 num_vectors);
>  int iecm_vport_params_buf_alloc(struct iecm_adapter *adapter);
>  void iecm_vport_params_buf_rel(struct iecm_adapter *adapter);
>  struct iecm_vport *iecm_netdev_to_vport(struct net_device *netdev);
> +struct iecm_adapter *iecm_netdev_to_adapter(struct net_device *netdev);
>  int iecm_send_get_stats_msg(struct iecm_vport *vport);
>  int iecm_get_vec_ids(struct iecm_adapter *adapter,
>  		     u16 *vecids, int num_vecids,
> diff --git a/drivers/net/ethernet/intel/include/iecm_txrx.h b/drivers/net/ethernet/intel/include/iecm_txrx.h
> index 5e29148938fb..26e480343876 100644
> --- a/drivers/net/ethernet/intel/include/iecm_txrx.h
> +++ b/drivers/net/ethernet/intel/include/iecm_txrx.h
> @@ -115,6 +115,11 @@
>  
>  #define MAKEMASK(m, s)	((m) << (s))
>  
> +union iecm_tx_flex_desc {
> +	struct iecm_flex_tx_desc q; /* queue based scheduling */
> +	struct iecm_flex_tx_sched_desc flow; /* flow based scheduling */
> +};
> +
>  struct iecm_tx_buf {
>  	struct hlist_node hlist;
>  	void *next_to_watch;
> @@ -145,6 +150,37 @@ struct iecm_buf_lifo {
>  	struct iecm_tx_buf **bufs;
>  };
>  
> +struct iecm_tx_offload_params {
> +	u16 td_cmd;	/* command field to be inserted into descriptor */
> +	u32 tso_len;	/* total length of payload to segment */
> +	u16 mss;
> +	u8 tso_hdr_len;	/* length of headers to be duplicated */
> +
> +	/* Flow scheduling offload timestamp, formatting as hw expects it */
> +	/* timestamp = bits[0:22], overflow = bit[23] */
> +	u8 desc_ts[3];
> +
> +	/* For legacy offloads */
> +	u32 hdr_offsets;
> +};
> +
> +struct iecm_tx_splitq_params {
> +	/* Descriptor build function pointer */
> +	void (*splitq_build_ctb)(union iecm_tx_flex_desc *desc,
> +				 struct iecm_tx_splitq_params *params,
> +				 u16 td_cmd, u16 size);

	^^^^^^^^^^^^^^^^^^^^^^^^
	This one.

> +
> +	/* General descriptor info */
> +	enum iecm_tx_desc_dtype_value dtype;
> +	u16 eop_cmd;
> +	union {
> +		u16 compl_tag; /* only relevant for flow scheduling */
> +		u16 td_tag; /* only relevant for queue scheduling */
> +	};
> +
> +	struct iecm_tx_offload_params offload;
> +};
> +
>  /* Checksum offload bits decoded from the receive descriptor. */
>  struct iecm_rx_csum_decoded {
>  	u8 l3l4p : 1;
> @@ -588,6 +624,12 @@ struct iecm_txq_group {
>  
>  struct iecm_adapter;
>  
> +void iecm_tx_splitq_build_ctb(union iecm_tx_flex_desc *desc,
> +			      struct iecm_tx_splitq_params *parms,
> +			      u16 td_cmd, u16 size);
> +void iecm_tx_splitq_build_flow_desc(union iecm_tx_flex_desc *desc,
> +				    struct iecm_tx_splitq_params *parms,
> +				    u16 td_cmd, u16 size);
>  int iecm_vport_singleq_napi_poll(struct napi_struct *napi, int budget);
>  void iecm_vport_init_num_qs(struct iecm_vport *vport,
>  			    struct virtchnl2_create_vport *vport_msg);
> @@ -614,7 +656,25 @@ int iecm_init_rss(struct iecm_vport *vport);
>  void iecm_deinit_rss(struct iecm_vport *vport);
>  bool iecm_init_rx_buf_hw_alloc(struct iecm_queue *rxq, struct iecm_rx_buf *buf);
>  void iecm_rx_buf_hw_update(struct iecm_queue *rxq, u32 val);
> +void iecm_tx_buf_hw_update(struct iecm_queue *tx_q, u32 val,
> +			   bool xmit_more);
>  void iecm_tx_buf_rel(struct iecm_queue *tx_q, struct iecm_tx_buf *tx_buf);
> +unsigned int iecm_size_to_txd_count(unsigned int size);
> +unsigned int iecm_tx_desc_count_required(struct sk_buff *skb);
> +bool iecm_chk_linearize(struct sk_buff *skb, unsigned int max_bufs,
> +			unsigned int count);
> +int iecm_tx_maybe_stop(struct iecm_queue *tx_q, unsigned int size);
> +void iecm_tx_timeout(struct net_device *netdev,
> +		     unsigned int __always_unused txqueue);
> +netdev_tx_t iecm_tx_splitq_start(struct sk_buff *skb,
> +				 struct net_device *netdev);
> +netdev_tx_t iecm_tx_singleq_start(struct sk_buff *skb,
> +				  struct net_device *netdev);
>  bool iecm_rx_singleq_buf_hw_alloc_all(struct iecm_queue *rxq,
>  				      u16 cleaned_count);
> +int iecm_tso(struct iecm_tx_buf *first, struct iecm_tx_offload_params *off);
> +void iecm_tx_prepare_vlan_flags(struct iecm_queue *tx_q,
> +				struct iecm_tx_buf *first,
> +				struct sk_buff *skb);
> +

At least some of these functions can be made static. Please
double-check.

>  #endif /* !_IECM_TXRX_H_ */
> -- 
> 2.33.0

Thanks,
Al


^ permalink raw reply	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 12/19] iecm: finish netdev_ops
  2022-01-28  0:10 ` [Intel-wired-lan] [PATCH net-next 12/19] iecm: finish netdev_ops Alan Brady
@ 2022-01-28 17:06   ` Alexander Lobakin
  0 siblings, 0 replies; 89+ messages in thread
From: Alexander Lobakin @ 2022-01-28 17:06 UTC (permalink / raw)
  To: intel-wired-lan

From: Alan Brady <alan.brady@intel.com>
Date: Thu, 27 Jan 2022 16:10:02 -0800

> This fills out the remaining NDO callbacks. Once netdev_ops are there, the
> rest of the patches will fill out the data path and advanced features.
> 
> Signed-off-by: Phani Burra <phani.r.burra@intel.com>
> Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
> Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
> Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
> Signed-off-by: Alan Brady <alan.brady@intel.com>
> ---
>  drivers/net/ethernet/intel/iecm/iecm_lib.c    | 742 +++++++++++++++++-
>  drivers/net/ethernet/intel/iecm/iecm_txrx.c   |  15 +
>  .../net/ethernet/intel/iecm/iecm_virtchnl.c   |  63 ++
>  drivers/net/ethernet/intel/include/iecm.h     |  14 +
>  .../net/ethernet/intel/include/iecm_txrx.h    |   2 +
>  5 files changed, 822 insertions(+), 14 deletions(-)
> 
> diff --git a/drivers/net/ethernet/intel/iecm/iecm_lib.c b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> index 003057f48f0c..cc82e665dfaf 100644
> --- a/drivers/net/ethernet/intel/iecm/iecm_lib.c
> +++ b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> @@ -568,6 +568,147 @@ static void iecm_set_all_filters(struct iecm_vport *vport)
>  	iecm_add_del_ether_addrs(vport, true, false);
>  }
>  
> +/**
> + * iecm_find_vlan - Search filter list for specific vlan filter
> + * @vport: vport structure
> + * @vlan: vlan tag
> + *
> + * Returns ptr to the filter object or NULL. Must be called while holding the
> + * vlan_list_lock.
> + */
> +static struct
> +iecm_vlan_filter *iecm_find_vlan(struct iecm_vport *vport,
> +				 struct iecm_vlan *vlan)

Both are read-only here, thus const.

> +{
> +	struct iecm_vlan_filter *f;

Read-only, const.

> +
> +	list_for_each_entry(f, &vport->adapter->config_data.vlan_filter_list,
> +			    list) {
> +		if (vlan->vid == f->vlan.vid && vlan->tpid == f->vlan.tpid)
> +			return f;
> +	}

Braces are redundant.

> +	return NULL;
> +}
> +
> +/**
> + * iecm_add_vlan - Add a vlan filter to the list
> + * @vport: vport structure
> + * @vlan: VLAN tag
> + *
> + * Returns ptr to the filter object or NULL when no memory available.
> + */
> +static struct
> +iecm_vlan_filter *iecm_add_vlan(struct iecm_vport *vport,
> +				struct iecm_vlan *vlan)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct iecm_vlan_filter *f = NULL;
> +
> +	spin_lock_bh(&adapter->vlan_list_lock);
> +
> +	f = iecm_find_vlan(vport, vlan);
> +	if (!f) {
> +		f = kzalloc(sizeof(*f), GFP_ATOMIC);
> +		if (!f)
> +			goto error;
> +
> +		f->vlan.vid = vlan->vid;
> +		f->vlan.tpid = vlan->tpid;
> +
> +		list_add_tail(&f->list, &adapter->config_data.vlan_filter_list);
> +		f->add = true;
> +	}

	if (f)
		goto error; /* It's better to rename the label */

	f = kzalloc(...

> +
> +error:
> +	spin_unlock_bh(&adapter->vlan_list_lock);
> +	return f;
> +}
> +
> +/**
> + * iecm_del_vlan - Remove a vlan filter from the list
> + * @vport: vport structure
> + * @vlan: VLAN tag
> + */
> +static void iecm_del_vlan(struct iecm_vport *vport, struct iecm_vlan *vlan)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct iecm_vlan_filter *f;
> +
> +	spin_lock_bh(&adapter->vlan_list_lock);
> +
> +	f = iecm_find_vlan(vport, vlan);
> +	if (f)
> +		f->remove = true;
> +
> +	spin_unlock_bh(&adapter->vlan_list_lock);
> +}
> +
> +/**
> + * iecm_vlan_rx_add_vid - Add a VLAN filter to the device
> + * @netdev: network device struct
> + * @proto: unused protocol data
> + * @vid: VLAN tag
> + *
> + * Returns 0 on success
> + */
> +static int iecm_vlan_rx_add_vid(struct net_device *netdev,
> +				__always_unused __be16 proto, u16 vid)
> +{
> +	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct iecm_vlan vlan;
> +
> +	vlan = IECM_VLAN(vid, be16_to_cpu(proto));
> +	if (!iecm_is_feature_ena(vport, NETIF_F_HW_VLAN_CTAG_FILTER))
> +		return -EINVAL;
> +
> +	iecm_add_vlan(vport, &vlan);
> +
> +	if (adapter->state == __IECM_UP)
> +		adapter->dev_ops.vc_ops.add_del_vlans(vport, true);
> +
> +	return 0;
> +}
> +
> +/**
> + * iecm_vlan_rx_kill_vid - Remove a VLAN filter from the device
> + * @netdev: network device struct
> + * @proto: unused protocol data
> + * @vid: VLAN tag
> + *
> + * Returns 0 on success
> + */
> +static int iecm_vlan_rx_kill_vid(struct net_device *netdev,
> +				 __always_unused __be16 proto, u16 vid)
> +{
> +	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct iecm_vlan_filter *f, *ftmp;
> +	struct iecm_vlan vlan;
> +
> +	vlan = IECM_VLAN(vid, be16_to_cpu(proto));
> +	if (!iecm_is_feature_ena(vport, NETIF_F_HW_VLAN_CTAG_FILTER))
> +		return -EINVAL;
> +
> +	if (vport->adapter->state == __IECM_UP) {
> +		iecm_del_vlan(vport, &vlan);
> +		adapter->dev_ops.vc_ops.add_del_vlans(vport, false);
> +	}
> +	/* It is safe to delete entry from the list now */
> +	spin_lock_bh(&adapter->vlan_list_lock);
> +	list_for_each_entry_safe(f, ftmp,
> +				 &adapter->config_data.vlan_filter_list,
> +				 list) {
> +		if (f->vlan.vid == vlan.vid && f->vlan.tpid == vlan.tpid) {
> +			list_del(&f->list);
> +			kfree(f);
> +		}
> +	}

Braces.

> +	spin_unlock_bh(&adapter->vlan_list_lock);
> +
> +	return 0;
> +}
> +
>  /**
>   * iecm_set_all_vlans - Re-add all VLANs in list
>   * @vport: main vport struct
> @@ -804,6 +945,27 @@ static int iecm_get_free_slot(void *array, int size, int curr)
>  	return next;
>  }
>  
> +/**
> + * iecm_remove_vlan_filters - Remove all vlan filters
> + * @vport: vport structure
> + */
> +static void iecm_remove_vlan_filters(struct iecm_vport *vport)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct iecm_user_config_data *config_data;
> +
> +	config_data = &adapter->config_data;
> +	if (!list_empty(&config_data->vlan_filter_list)) {
> +		struct iecm_vlan_filter *f;
> +
> +		spin_lock_bh(&adapter->vlan_list_lock);
> +		list_for_each_entry(f, &config_data->vlan_filter_list, list)
> +			f->remove = true;
> +		spin_unlock_bh(&adapter->vlan_list_lock);
> +		adapter->dev_ops.vc_ops.add_del_vlans(vport, false);
> +	}
> +}
> +
>  /**
>   * iecm_vport_stop - Disable a vport
>   * @vport: vport to disable
> @@ -831,6 +993,8 @@ static void iecm_vport_stop(struct iecm_vport *vport)
>  	if (test_and_clear_bit(__IECM_DEL_QUEUES,
>  			       vport->adapter->flags))
>  		iecm_send_delete_queues_msg(vport);
> +	if (!test_bit(__IECM_REL_RES_IN_PROG, adapter->flags))
> +		iecm_remove_vlan_filters(vport);
>  
>  	adapter->link_up = false;
>  	iecm_vport_intr_deinit(vport);
> @@ -1581,6 +1745,147 @@ static void iecm_vc_event_task(struct work_struct *work)
>  	}
>  }
>  
> +/**
> + * iecm_initiate_soft_reset - Initiate a software reset
> + * @vport: virtual port data struct
> + * @reset_cause: reason for the soft reset
> + *
> + * Soft reset only reallocs vport queue resources. Returns 0 on success,
> + * negative on failure.
> + */
> +int iecm_initiate_soft_reset(struct iecm_vport *vport,
> +			     enum iecm_flags reset_cause)
> +{
> +	enum iecm_state current_state = vport->adapter->state;
> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct iecm_vport *new_vport;
> +	int err = 0, i;
> +
> +	/* make sure we do not end up in initiating multiple resets */
> +	mutex_lock(&adapter->reset_lock);
> +
> +	/* If the system is low on memory, we can end up in bad state if we
> +	 * free all the memory for queue resources and try to allocate them
> +	 * again. Instead, we can pre-allocate the new resources before doing
> +	 * anything and bailing if the alloc fails.
> +	 *
> +	 * Make a clone of the existing vport to mimic its current configuration,
> +	 * then modify the new structure with any requested changes. Once the
> +	 * allocation of the new resources is done, stop the existing vport and
> +	 * copy the configuration to the main vport. If an error occurred, the
> +	 * existing vport will be untouched.
> +	 *
> +	 */
> +	new_vport = kzalloc(sizeof(*vport), GFP_KERNEL);
> +	if (!new_vport) {
> +		mutex_unlock(&adapter->reset_lock);
> +		return -ENOMEM;
> +	}
> +	memcpy(new_vport, vport, sizeof(*vport));
> +
> +	/* Adjust resource parameters prior to reallocating resources */
> +	switch (reset_cause) {
> +	case __IECM_SR_Q_CHANGE:
> +		adapter->dev_ops.vc_ops.adjust_qs(new_vport);
> +		break;
> +	case __IECM_SR_Q_DESC_CHANGE:
> +		/* Update queue parameters before allocating resources */
> +		iecm_vport_calc_num_q_desc(new_vport);
> +		break;
> +	case __IECM_SR_Q_SCH_CHANGE:
> +	case __IECM_SR_MTU_CHANGE:
> +	case __IECM_SR_RSC_CHANGE:
> +	case __IECM_SR_HSPLIT_CHANGE:
> +		break;
> +	default:
> +		dev_err(&adapter->pdev->dev, "Unhandled soft reset cause\n");
> +		err = -EINVAL;
> +		goto err_default;
> +	}
> +
> +	err = iecm_vport_queues_alloc(new_vport);
> +	if (err)
> +		goto err_default;
> +
> +	if (adapter->virt_ver_maj == VIRTCHNL_VERSION_MAJOR_2) {
> +		if (current_state <= __IECM_DOWN) {
> +			adapter->dev_ops.vc_ops.delete_queues(vport);
> +		} else {
> +			set_bit(__IECM_DEL_QUEUES, adapter->flags);
> +			iecm_vport_stop(vport);
> +		}
> +
> +		iecm_deinit_rss(vport);
> +		err = adapter->dev_ops.vc_ops.add_queues(new_vport, new_vport->num_txq,
> +							 new_vport->num_complq,
> +							 new_vport->num_rxq,
> +							 new_vport->num_bufq);
> +		if (err)
> +			goto err_reset;
> +	} else {
> +		iecm_vport_stop(vport);
> +	}

	if (maj != MAJOR_2) {
		iecm_vport_stop(vport);
		goto init;
	}

	...

> +
> +	memcpy(vport, new_vport, sizeof(*vport));
> +	/* Since iecm_vport_queues_alloc was called with new_port, the queue
> +	 * back pointers are currently pointing to the local new_vport. Reset
> +	 * the backpointers to the original vport here
> +	 */
> +	for (i = 0; i < vport->num_txq_grp; i++) {
> +		struct iecm_txq_group *tx_qgrp = &vport->txq_grps[i];
> +		int j;
> +
> +		tx_qgrp->vport = vport;
> +		for (j = 0; j < tx_qgrp->num_txq; j++)
> +			tx_qgrp->txqs[j]->vport = vport;
> +
> +		if (iecm_is_queue_model_split(vport->txq_model))
> +			tx_qgrp->complq->vport = vport;
> +	}
> +
> +	for (i = 0; i < vport->num_rxq_grp; i++) {
> +		struct iecm_rxq_group *rx_qgrp = &vport->rxq_grps[i];
> +		struct iecm_queue *q;
> +		int j, num_rxq;
> +
> +		rx_qgrp->vport = vport;
> +		for (j = 0; j < vport->num_bufqs_per_qgrp; j++)
> +			rx_qgrp->splitq.bufq_sets[j].bufq.vport = vport;
> +
> +		if (iecm_is_queue_model_split(vport->rxq_model))
> +			num_rxq = rx_qgrp->splitq.num_rxq_sets;
> +		else
> +			num_rxq = rx_qgrp->singleq.num_rxq;
> +
> +		for (j = 0; j < num_rxq; j++) {
> +			if (iecm_is_queue_model_split(vport->rxq_model))
> +				q = &rx_qgrp->splitq.rxq_sets[j]->rxq;
> +			else
> +				q = rx_qgrp->singleq.rxqs[j];
> +			q->vport = vport;
> +		}
> +	}
> +
> +	/* Post resource allocation reset */
> +	if (reset_cause == __IECM_SR_Q_CHANGE) {
> +		iecm_intr_rel(adapter);
> +		iecm_intr_req(adapter);
> +	}
> +
> +	kfree(new_vport);
> +
> +	if (current_state == __IECM_UP)
> +		err = iecm_vport_open(vport, false);
> +	mutex_unlock(&adapter->reset_lock);
> +	return err;
> +err_reset:
> +	iecm_vport_queues_rel(vport);
> +err_default:
> +	kfree(new_vport);
> +	mutex_unlock(&adapter->reset_lock);
> +	return err;
> +}
> +
>  /**
>   * iecm_probe - Device initialization routine
>   * @pdev: PCI device information struct
> @@ -1905,6 +2210,47 @@ static void iecm_set_rx_mode(struct net_device *netdev)
>  	}
>  }
>  
> +/**
> + * iecm_set_features - set the netdev feature flags
> + * @netdev: ptr to the netdev being adjusted
> + * @features: the feature set that the stack is suggesting
> + */
> +static int iecm_set_features(struct net_device *netdev,
> +			     netdev_features_t features)
> +{
> +	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
> +	struct iecm_adapter *adapter = vport->adapter;
> +	int err = 0;
> +
> +	if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS, VIRTCHNL2_CAP_VLAN) ||
> +	    iecm_is_cap_ena(adapter, IECM_BASE_CAPS, VIRTCHNL2_CAP_VLAN)) {
> +		err = iecm_set_vlan_offload_features(netdev, netdev->features,
> +						     features);
> +		if (err)
> +			return err;
> +	}
> +
> +	if ((netdev->features ^ features) & NETIF_F_GRO_HW) {
> +		netdev->features ^= NETIF_F_GRO_HW;
> +		err = iecm_initiate_soft_reset(vport, __IECM_SR_RSC_CHANGE);
> +	}
> +
> +	return err;
> +}
> +
> +/**
> + * iecm_fix_features - fix up the netdev feature bits
> + * @netdev: our net device
> + * @features: desired feature bits
> + *
> + * Returns fixed-up features bits
> + */
> +static netdev_features_t iecm_fix_features(struct net_device *netdev,
> +					   netdev_features_t features)
> +{
> +	return features;
> +}

This adds +1 redundant indirect call to hotpath. fix_features is
fully optional.

> +
>  /**
>   * iecm_open - Called when a network interface becomes active
>   * @netdev: network interface device structure
> @@ -1924,6 +2270,374 @@ static int iecm_open(struct net_device *netdev)
>  	return iecm_vport_open(np->vport, true);
>  }
>  
> +/**
> + * iecm_change_mtu - NDO callback to change the MTU
> + * @netdev: network interface device structure
> + * @new_mtu: new value for maximum frame size
> + *
> + * Returns 0 on success, negative on failure
> + */
> +static int iecm_change_mtu(struct net_device *netdev, int new_mtu)
> +{
> +	struct iecm_vport *vport =  iecm_netdev_to_vport(netdev);
> +
> +	netdev->mtu = new_mtu;
> +
> +	return iecm_initiate_soft_reset(vport, __IECM_SR_MTU_CHANGE);
> +}
> +
> +static int iecm_offload_txtime(struct iecm_vport *vport,
> +			       struct tc_etf_qopt_offload *qopt)
> +{
> +	return -EOPNOTSUPP;
> +}

Pointless function. If it will be expanded later, introduce it only
then.
Otherwise, code becomes a mess.

> +
> +/**
> + * iecm_validate_tx_bandwidth - validate the max Tx bandwidth
> + * @vport: vport structure
> + * @max_tx_rate: max Tx bandwidth for a tc
> + **/
> +static int iecm_validate_tx_bandwidth(struct iecm_vport *vport,
> +				      u64 max_tx_rate)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	int speed = 0, ret = 0;
> +
> +	if (adapter->link_speed_mbps) {
> +		if (adapter->link_speed_mbps < U32_MAX) {
> +			speed = adapter->link_speed_mbps;
> +			goto validate_bw;
> +		} else {
> +			dev_err(&adapter->pdev->dev, "Unknown link speed\n");
> +			return -EINVAL;
> +		}
> +	}

	switch (adapter->link_speed_mbps) {
	case 1 ... U32_MAX - 1:
		speed = adapter->link_speed_mbps;
		goto validate_bw;
	case U32_MAX:
		dev_err();
		return -EINVAL;
	case 0:
	default:
		break;
	}

or

	if (link_speed_mbps < U32_MAX)
		set;
	else if (link_speed_mbps) /* This means it's U32_MAX */
		error;
> +
> +	switch (adapter->link_speed) {
> +	case VIRTCHNL_LINK_SPEED_40GB:
> +		speed = SPEED_40000;
> +		break;
> +	case VIRTCHNL_LINK_SPEED_25GB:
> +		speed = SPEED_25000;
> +		break;
> +	case VIRTCHNL_LINK_SPEED_20GB:
> +		speed = SPEED_20000;
> +		break;
> +	case VIRTCHNL_LINK_SPEED_10GB:
> +		speed = SPEED_10000;
> +		break;
> +	case VIRTCHNL_LINK_SPEED_5GB:
> +		speed = SPEED_5000;
> +		break;
> +	case VIRTCHNL_LINK_SPEED_2_5GB:
> +		speed = SPEED_2500;
> +		break;
> +	case VIRTCHNL_LINK_SPEED_1GB:
> +		speed = SPEED_1000;
> +		break;
> +	case VIRTCHNL_LINK_SPEED_100MB:
> +		speed = SPEED_100;
> +		break;

All these can be converted to an array and compressed using macros.

> +	default:
> +		break;
> +	}
> +
> +validate_bw:
> +	if (max_tx_rate > speed) {
> +		dev_err(&adapter->pdev->dev, "Invalid tx rate specified\n");
> +		ret = -EINVAL;
> +	}
> +
> +	return ret;
> +}
> +
> +/**
> + * iecm_validate_ch_config - validate queue mapping info
> + * @vport: vport structure
> + * @mqprio_qopt: queue parameters
> + * @max_tc_allowed: MAX TC allowed, it could be 4 or 16 depends.
> + *
> + * This function validates if the configuration provided by the user to
> + * configure queue channels is valid or not.
> + *
> + * Returns 0 on a valid config and negative on invalid config.
> + **/
> +static int iecm_validate_ch_config(struct iecm_vport *vport,
> +				   struct tc_mqprio_qopt_offload *mqprio_qopt,
> +				   u8 max_tc_allowed)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	u32 tc, qcount, non_power_2_qcount = 0;
> +	u64 total_max_rate = 0;
> +	int i, num_qs = 0;
> +
> +	if (mqprio_qopt->qopt.num_tc > max_tc_allowed ||
> +	    mqprio_qopt->qopt.num_tc < 1)
> +		return -EINVAL;
> +
> +	/* For ADQ there are few rules on queue allocation for each TC
> +	 *     1. Number of queues for TC0 should always be a power of 2
> +	 *     2. Number of queues for rest of TCs can be non-power of 2
> +	 *     3. If the previous TC has non-power of 2 queues, then all the
> +	 *        following TCs should be either
> +	 *        a. same number of queues as that of the previous non-power
> +	 *           of 2 or
> +	 *        b. less than previous non-power of 2 and power of 2
> +	 *        ex: 1 at 0 2 at 1 3 at 3 4 at 6 - Invalid
> +	 *            1 at 0 2 at 1 3 at 3 3 at 6 - Valid
> +	 *            1 at 0 2 at 1 3 at 3 2 at 6 - Valid
> +	 *            1 at 0 2 at 1 3 at 3 1 at 6 - Valid
> +	 */
> +	for (tc = 0; tc < mqprio_qopt->qopt.num_tc; tc++) {
> +		qcount = mqprio_qopt->qopt.count[tc];
> +
> +		/* case 1. check for first TC to be always power of 2 in ADQ */
> +		if (!tc && !is_power_of_2(qcount)) {
> +			dev_err(&adapter->pdev->dev,
> +				"TC0:qcount[%d] must be a power of 2\n",
> +				qcount);
> +			return -EINVAL;
> +		}
> +		/* case 2 & 3, check for non-power of 2 number of queues */
> +		if (tc && non_power_2_qcount) {
> +			if (qcount > non_power_2_qcount) {
> +				dev_err(&adapter->pdev->dev,
> +					"TC%d has %d qcount cannot be > non_power_of_2 qcount [%d]\n",
> +					tc, qcount, non_power_2_qcount);
> +				return -EINVAL;
> +			} else if (qcount < non_power_2_qcount) {
> +				/* it must be power of 2, otherwise fail */
> +				if (!is_power_of_2(qcount)) {
> +					dev_err(&adapter->pdev->dev,
> +						"TC%d has %d qcount must be a power of 2 < non_power_of_2 qcount [%d]\n",
> +						tc, qcount, non_power_2_qcount);
> +					return -EINVAL;
> +				}
> +			}
> +		} else if (tc && !is_power_of_2(qcount)) {
> +			/* this is the first TC to have a non-power of 2 queue
> +			 * count and the code is going to enter this section
> +			 * only once. The qcount for this TC will serve as
> +			 * our reference/guide to allocate number of queues
> +			 * for all the further TCs as per section a. and b. in
> +			 * case 3 mentioned above.
> +			 */
> +			non_power_2_qcount = qcount;
> +			dev_dbg(&adapter->pdev->dev,
> +				"TC%d:count[%d] non power of 2\n", tc,
> +				qcount);
> +			}
> +	}
> +
> +	for (i = 0; i <= mqprio_qopt->qopt.num_tc - 1; i++) {
> +		u64 tx_rate = 0;
> +
> +		if (!mqprio_qopt->qopt.count[i] ||
> +		    mqprio_qopt->qopt.offset[i] != num_qs)
> +			return -EINVAL;
> +		if (mqprio_qopt->min_rate[i]) {
> +			dev_err(&adapter->pdev->dev,
> +				"Invalid min tx rate (greater than 0) specified\n");
> +			return -EINVAL;
> +		}
> +		/*convert to Mbps */
> +		tx_rate = div_u64(mqprio_qopt->max_rate[i], IECM_MBPS_DIVISOR);
> +		total_max_rate += tx_rate;
> +		num_qs += mqprio_qopt->qopt.count[i];
> +	}
> +	/* Comparing with num_txq as num_txq and num_rxq are equal for single
> +	 * queue model
> +	 */
> +	if (num_qs > vport->num_txq) {
> +		dev_err(&adapter->pdev->dev,
> +			"Cannot support requested number of queues\n");
> +		return -EINVAL;
> +	}
> +	/* no point in validating TX bandwidth rate limit if the user hasn't
> +	 * specified any rate limit for any TCs, so validate only if it's set.
> +	 */
> +	if (total_max_rate)
> +		return iecm_validate_tx_bandwidth(vport, total_max_rate);
> +	else
> +		return 0;

`else` before `return` is redundant.
I'd invert the condition at all:

	if (!total_max_rate)
		return 0;

	return iecm_ ...

It's more natural to read and follow the flow.

> +}
> +
> +/**
> + * __iecm_setup_tc - configure multiple traffic classes
> + * @vport: vport structure
> + * @type_data: tc offload data
> + *
> + * This function processes the config information provided by the
> + * user to configure traffic classes/queue channels and packages the
> + * information to request the PF to setup traffic classes.
> + *
> + * Returns 0 on success.
> + **/
> +static int __iecm_setup_tc(struct iecm_vport *vport, void *type_data)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct tc_mqprio_qopt_offload *mqprio_qopt;
> +	struct net_device *netdev = vport->netdev;
> +	struct iecm_channel_config *ch_config;
> +	u8 num_tc = 0, total_qs = 0;
> +	int ret = 0;

Please don't break RCT style.

> +	u8 max_tc_allowed;
> +	u64 max_tx_rate;
> +	u16 mode;
> +
> +	mqprio_qopt = (struct tc_mqprio_qopt_offload *)type_data;
> +	ch_config = &adapter->config_data.ch_config;
> +	num_tc = mqprio_qopt->qopt.num_tc;
> +	mode = mqprio_qopt->mode;
> +
> +	/* delete queue_channel */
> +	if (!mqprio_qopt->qopt.hw) {
> +		if (ch_config->tc_running) {
> +			/* reset the tc configuration */
> +			netdev_reset_tc(netdev);
> +			ch_config->num_tc = 0;
> +			netif_tx_stop_all_queues(netdev);
> +			netif_tx_disable(netdev);
> +			ret = iecm_send_disable_channels_msg(vport);
> +			netif_tx_start_all_queues(netdev);
> +			if (!test_bit(__IECM_REL_RES_IN_PROG, adapter->flags) &&
> +			    !ret) {
> +				ch_config->tc_running = false;
> +				set_bit(__IECM_HR_FUNC_RESET, adapter->flags);
> +				queue_delayed_work(adapter->vc_event_wq,
> +						   &adapter->vc_event_task,
> +						   msecs_to_jiffies(10));
> +			}
> +			return ret;
> +		} else {
> +			return -EINVAL;
> +		}
> +	}

You can save 2 indent levels by inverting the conditions (and adding
one `goto`).

> +
> +	if (mode == TC_MQPRIO_MODE_CHANNEL) {
> +		int i, netdev_tc = 0;
> +
> +		if (!iecm_is_cap_ena(adapter, IECM_BASE_CAPS,
> +				     VIRTCHNL2_CAP_ADQ) &&
> +		    !iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
> +					     VIRTCHNL2_CAP_ADQ)) {
> +			dev_info(&adapter->pdev->dev, "ADQ not supported\n");
> +			return -EOPNOTSUPP;
> +		}
> +
> +		if (ch_config->tc_running) {
> +			dev_info(&adapter->pdev->dev, "TC configuration already exists\n");
> +			return -EINVAL;
> +		}
> +
> +		/* If negotiated capability between VF and PF indicated that
> +		 * ADQ_V2 is enabled, means it's OK to allow max_tc
> +		 * to be 16. This is needed to handle the case where iAVF
> +		 * is newer but PF is older or different generation
> +		 */
> +		if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS, VIRTCHNL2_CAP_ADQ))
> +			max_tc_allowed = VIRTCHNL_MAX_ADQ_V2_CHANNELS;
> +		else
> +			max_tc_allowed = VIRTCHNL_MAX_ADQ_CHANNELS;
> +
> +		ret = iecm_validate_ch_config(vport, mqprio_qopt,
> +					      max_tc_allowed);
> +		if (ret)
> +			return ret;
> +		/* Return if same TC config is requested */
> +		if (ch_config->num_tc == num_tc)
> +			return 0;
> +		ch_config->num_tc = num_tc;
> +
> +		for (i = 0; i < max_tc_allowed; i++) {
> +			if (i < num_tc) {
> +				ch_config->ch_info[i].count =
> +					mqprio_qopt->qopt.count[i];
> +				ch_config->ch_info[i].offset =
> +					mqprio_qopt->qopt.offset[i];
> +				total_qs += mqprio_qopt->qopt.count[i];
> +				max_tx_rate = mqprio_qopt->max_rate[i];
> +				/* convert to Mbps */
> +				max_tx_rate = div_u64(max_tx_rate,
> +						      IECM_MBPS_DIVISOR);
> +				ch_config->ch_info[i].max_tx_rate =
> +								max_tx_rate;
> +			} else {
> +				ch_config->ch_info[i].count = 1;
> +				ch_config->ch_info[i].offset = 0;
> +			}
> +		}
> +
> +		/* Store queue info based on TC so that, VF gets configured
> +		 * with correct number of queues when VF completes ADQ config
> +		 * flow
> +		 */
> +		ch_config->total_qs = total_qs;
> +
> +		netif_tx_stop_all_queues(netdev);
> +		netif_tx_disable(netdev);
> +		ret = iecm_send_enable_channels_msg(vport);
> +		if (ret)
> +			return ret;
> +		netdev_reset_tc(netdev);
> +		/* Report the tc mapping up the stack */
> +		netdev_set_num_tc(netdev, num_tc);
> +		for (i = 0; i < max_tc_allowed; i++) {
> +			u16 qcount = mqprio_qopt->qopt.count[i];
> +			u16 qoffset = mqprio_qopt->qopt.offset[i];
> +
> +			if (i < num_tc)
> +				netdev_set_tc_queue(netdev, netdev_tc++, qcount,
> +						    qoffset);
> +		}
> +		/* Start all queues */
> +		netif_tx_start_all_queues(netdev);
> +		ch_config->tc_running = true;
> +		set_bit(__IECM_HR_FUNC_RESET, adapter->flags);
> +		queue_delayed_work(adapter->vc_event_wq,
> +				   &adapter->vc_event_task,
> +				   msecs_to_jiffies(10));
> +	}
> +	return ret;
> +}
> +
> +/**
> + * iecm_setup_tc - ndo callback to setup up TC schedulers
> + * @netdev: pointer to net_device struct
> + * @type: TC type
> + * @type_data: TC type specific data
> + */
> +static int iecm_setup_tc(struct net_device *netdev, enum tc_setup_type type,
> +			 void *type_data)
> +{
> +	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
> +	struct iecm_adapter *adapter = vport->adapter;
> +	int err = 0;
> +
> +	switch (type) {
> +	case TC_SETUP_QDISC_ETF:
> +		if (iecm_is_queue_model_split(vport->txq_model))
> +			err =
> +			iecm_offload_txtime(vport,
> +					    (struct tc_etf_qopt_offload *)
> +					     type_data);
> +		break;

So if we're using singleq model, it will do nothing and return 0.
The kernel will think we've set things up, and we haven't.

> +	case TC_SETUP_BLOCK:
> +		break;

Same here.

> +	case TC_SETUP_QDISC_MQPRIO:
> +		if (iecm_is_cap_ena(adapter, IECM_BASE_CAPS,
> +				    VIRTCHNL2_CAP_ADQ) ||
> +		    iecm_is_cap_ena(adapter, IECM_OTHER_CAPS, VIRTCHNL2_CAP_ADQ))
> +			__iecm_setup_tc(vport, type_data);
> +		break;

And here if we don't have caps.

> +	default:
> +		err = -EOPNOTSUPP;
> +		break;
> +	}
> +
> +	return err;
> +}
> +
>  /**
>   * iecm_set_mac - NDO callback to set port mac address
>   * @netdev: network interface device structure
> @@ -1997,13 +2711,13 @@ static const struct net_device_ops iecm_netdev_ops_splitq = {
>  	.ndo_set_rx_mode = iecm_set_rx_mode,
>  	.ndo_validate_addr = eth_validate_addr,
>  	.ndo_set_mac_address = iecm_set_mac,
> -	.ndo_change_mtu = NULL,
> -	.ndo_get_stats64 = NULL,
> -	.ndo_fix_features = NULL,
> -	.ndo_set_features = NULL,
> -	.ndo_vlan_rx_add_vid = NULL,
> -	.ndo_vlan_rx_kill_vid = NULL,
> -	.ndo_setup_tc = NULL,
> +	.ndo_change_mtu = iecm_change_mtu,
> +	.ndo_get_stats64 = iecm_get_stats64,
> +	.ndo_fix_features = iecm_fix_features,
> +	.ndo_set_features = iecm_set_features,
> +	.ndo_vlan_rx_add_vid = iecm_vlan_rx_add_vid,
> +	.ndo_vlan_rx_kill_vid = iecm_vlan_rx_kill_vid,
> +	.ndo_setup_tc = iecm_setup_tc,
>  };
>  
>  static const struct net_device_ops iecm_netdev_ops_singleq = {
> @@ -2013,11 +2727,11 @@ static const struct net_device_ops iecm_netdev_ops_singleq = {
>  	.ndo_set_rx_mode = iecm_set_rx_mode,
>  	.ndo_validate_addr = eth_validate_addr,
>  	.ndo_set_mac_address = iecm_set_mac,
> -	.ndo_change_mtu = NULL,
> -	.ndo_get_stats64 = NULL,
> -	.ndo_fix_features = NULL,
> -	.ndo_set_features = NULL,
> -	.ndo_vlan_rx_add_vid = NULL,
> -	.ndo_vlan_rx_kill_vid = NULL,
> -	.ndo_setup_tc           = NULL,
> +	.ndo_change_mtu = iecm_change_mtu,
> +	.ndo_get_stats64 = iecm_get_stats64,
> +	.ndo_fix_features = iecm_fix_features,
> +	.ndo_set_features = iecm_set_features,
> +	.ndo_vlan_rx_add_vid = iecm_vlan_rx_add_vid,
> +	.ndo_vlan_rx_kill_vid = iecm_vlan_rx_kill_vid,
> +	.ndo_setup_tc = iecm_setup_tc,
>  };
> diff --git a/drivers/net/ethernet/intel/iecm/iecm_txrx.c b/drivers/net/ethernet/intel/iecm/iecm_txrx.c
> index ef5fe659389b..4b9288e1c254 100644
> --- a/drivers/net/ethernet/intel/iecm/iecm_txrx.c
> +++ b/drivers/net/ethernet/intel/iecm/iecm_txrx.c
> @@ -218,6 +218,21 @@ const struct iecm_rx_ptype_decoded iecm_ptype_lookup[IECM_RX_MAX_PTYPE] = {
>  };
>  EXPORT_SYMBOL(iecm_ptype_lookup);
>  
> +/**
> + * iecm_get_stats64 - get statistics for network device structure
> + * @netdev: network interface device structure
> + * @stats: main device statistics structure
> + */
> +void iecm_get_stats64(struct net_device *netdev,
> +		      struct rtnl_link_stats64 *stats)
> +{
> +	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
> +
> +	set_bit(__IECM_MB_STATS_PENDING, vport->adapter->flags);
> +
> +	*stats = vport->netstats;

This structure is 192 bytes long, I'd suggest using memcpy(), esp.
given that it's not on hotpath at all.

> +}
> +
>  /**
>   * iecm_tx_buf_rel - Release a Tx buffer
>   * @tx_q: the queue that owns the buffer
> diff --git a/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> index 919fb3958cf8..f2516343c199 100644
> --- a/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> +++ b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> @@ -2731,6 +2731,69 @@ static int iecm_send_insert_vlan_msg(struct iecm_vport *vport, bool ena)
>  	return err;
>  }
>  
> +/**
> + * iecm_send_enable_channels_msg - Send enable channels message
> + * @vport: vport structure
> + *
> + * Request the PF/CP to enable channels as specified by the user via tc tool.
> + * Returns 0 on success, negative on failure.
> + **/
> +int iecm_send_enable_channels_msg(struct iecm_vport *vport)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct iecm_channel_config *ch_config;
> +	struct virtchnl_tc_info *vti = NULL;
> +	int i, err;
> +	u16 len;
> +
> +	ch_config = &adapter->config_data.ch_config;
> +	len = ((ch_config->num_tc - 1) * sizeof(struct virtchnl_channel_info)) +
> +		sizeof(struct virtchnl_tc_info);

	len = struct_size(vti, list, ch_config->num_tc - 1);

You can embed it directly in kzalloc() below.
Also please check if num_tc > 1.

> +
> +	vti = kzalloc(len, GFP_KERNEL);
> +	if (!vti)
> +		return -ENOMEM;
> +	vti->num_tc = ch_config->num_tc;
> +	for (i = 0; i < vti->num_tc; i++) {
> +		vti->list[i].count = ch_config->ch_info[i].count;
> +		vti->list[i].offset = ch_config->ch_info[i].offset;
> +		vti->list[i].pad = 0;
> +		vti->list[i].max_tx_rate = ch_config->ch_info[i].max_tx_rate;
> +	}
> +
> +	err = iecm_send_mb_msg(adapter, VIRTCHNL_OP_ENABLE_CHANNELS, len,
> +			       (u8 *)vti);
> +	if (err)
> +		goto error;
> +
> +	err = iecm_wait_for_event(adapter, IECM_VC_ENA_CHANNELS,
> +				  IECM_VC_ENA_CHANNELS_ERR);
> +error:
> +	kfree(vti);
> +	return err;
> +}
> +
> +/**
> + * iecm_send_disable_channels_msg - Send disable channels message
> + * @vport: vport structure to disable channels on
> + *
> + * Returns 0 on success, negative on failure.
> + */
> +int iecm_send_disable_channels_msg(struct iecm_vport *vport)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	int err;
> +
> +	err = iecm_send_mb_msg(adapter, VIRTCHNL_OP_DISABLE_CHANNELS,
> +			       0, NULL);
> +	if (err)
> +		return err;
> +
> +	err = iecm_min_wait_for_event(adapter, IECM_VC_DIS_CHANNELS,
> +				      IECM_VC_DIS_CHANNELS_ERR);
> +	return err;

Just return iecm_min_wait ..., this is redundant and either
checkpatch or coccinelle will protest.

> +}
> +
>  /**
>   * iecm_send_vlan_v2_caps_msg - send virtchnl get offload VLAN V2 caps message
>   * @adapter: adapter info struct
> diff --git a/drivers/net/ethernet/intel/include/iecm.h b/drivers/net/ethernet/intel/include/iecm.h
> index f6f9884c10c2..a655e797f457 100644
> --- a/drivers/net/ethernet/intel/include/iecm.h
> +++ b/drivers/net/ethernet/intel/include/iecm.h
> @@ -4,6 +4,8 @@
>  #ifndef _IECM_H_
>  #define _IECM_H_
>  
> +#include <net/pkt_sched.h>
> +#include <net/pkt_cls.h>
>  #include <linux/aer.h>
>  #include <linux/pci.h>
>  #include <linux/netdevice.h>
> @@ -44,6 +46,8 @@
>  /* available message levels */
>  #define IECM_AVAIL_NETIF_M (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK)
>  
> +#define IECM_MBPS_DIVISOR		125000 /* divisor to convert to Mbps */

Isn't this defined somewhere already?

> +
>  #define IECM_VIRTCHNL_VERSION_MAJOR VIRTCHNL_VERSION_MAJOR_2
>  #define IECM_VIRTCHNL_VERSION_MINOR VIRTCHNL_VERSION_MINOR_0
>  
> @@ -393,6 +397,13 @@ enum iecm_user_flags {
>  	__IECM_USER_FLAGS_NBITS,
>  };
>  
> +struct iecm_channel_config {
> +	struct virtchnl_channel_info ch_info[VIRTCHNL_MAX_ADQ_V2_CHANNELS];
> +	bool tc_running;

	u8 tc_running:1;

> +	u8 total_qs;
> +	u8 num_tc;
> +};
> +
>  #define IECM_GET_PTYPE_SIZE(p) \
>  	(sizeof(struct virtchnl2_ptype) + \
>  	(((p)->proto_id_count ? ((p)->proto_id_count - 1) : 0) * sizeof(u16)))
> @@ -430,6 +441,7 @@ struct iecm_user_config_data {
>  	struct list_head mac_filter_list;
>  	struct list_head vlan_filter_list;
>  	struct list_head adv_rss_list;
> +	struct iecm_channel_config ch_config;
>  };
>  
>  struct iecm_rss_data {
> @@ -703,6 +715,8 @@ int iecm_send_delete_queues_msg(struct iecm_vport *vport);
>  int iecm_send_add_queues_msg(struct iecm_vport *vport, u16 num_tx_q,
>  			     u16 num_complq, u16 num_rx_q, u16 num_rx_bufq);
>  int iecm_send_vlan_v2_caps_msg(struct iecm_adapter *adapter);
> +int iecm_initiate_soft_reset(struct iecm_vport *vport,
> +			     enum iecm_flags reset_cause);
>  int iecm_send_config_tx_queues_msg(struct iecm_vport *vport);
>  int iecm_send_config_rx_queues_msg(struct iecm_vport *vport);
>  int iecm_send_enable_vport_msg(struct iecm_vport *vport);
> diff --git a/drivers/net/ethernet/intel/include/iecm_txrx.h b/drivers/net/ethernet/intel/include/iecm_txrx.h
> index 26e480343876..7ec742fd4c6b 100644
> --- a/drivers/net/ethernet/intel/include/iecm_txrx.h
> +++ b/drivers/net/ethernet/intel/include/iecm_txrx.h
> @@ -672,6 +672,8 @@ netdev_tx_t iecm_tx_singleq_start(struct sk_buff *skb,
>  				  struct net_device *netdev);
>  bool iecm_rx_singleq_buf_hw_alloc_all(struct iecm_queue *rxq,
>  				      u16 cleaned_count);
> +void iecm_get_stats64(struct net_device *netdev,
> +		      struct rtnl_link_stats64 *stats);
>  int iecm_tso(struct iecm_tx_buf *first, struct iecm_tx_offload_params *off);
>  void iecm_tx_prepare_vlan_flags(struct iecm_queue *tx_q,
>  				struct iecm_tx_buf *first,
> -- 
> 2.33.0

Thanks,
Al

^ permalink raw reply	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 13/19] iecm: implement splitq napi_poll
  2022-01-28  0:10 ` [Intel-wired-lan] [PATCH net-next 13/19] iecm: implement splitq napi_poll Alan Brady
  2022-01-28  5:21     ` kernel test robot
@ 2022-01-28 17:38   ` Alexander Lobakin
  2022-02-03  1:07     ` Brady, Alan
  1 sibling, 1 reply; 89+ messages in thread
From: Alexander Lobakin @ 2022-01-28 17:38 UTC (permalink / raw)
  To: intel-wired-lan

From: Alan Brady <alan.brady@intel.com>
Date: Thu, 27 Jan 2022 16:10:03 -0800

> This adds everything we need to actually receive packets and process spent
> buffers using the splitq model. This contrasts to more traditional queueing
> models by essentially splitting a normal queue of descriptors and mapped
> buffers into separate queues. This allows us to deal with both more
> efficiently and also allows us to implement asymmetric queuing setups where
> you have multiple completion queues associated with a single buffer queue.
> 
> Signed-off-by: Phani Burra <phani.r.burra@intel.com>
> Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
> Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
> Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
> Signed-off-by: Alan Brady <alan.brady@intel.com>
> ---
>  drivers/net/ethernet/intel/iecm/iecm_txrx.c   | 1468 ++++++++++++++++-
>  drivers/net/ethernet/intel/include/iecm.h     |    4 +
>  .../net/ethernet/intel/include/iecm_txrx.h    |   20 +
>  3 files changed, 1490 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/net/ethernet/intel/iecm/iecm_txrx.c b/drivers/net/ethernet/intel/iecm/iecm_txrx.c
> index 4b9288e1c254..85a82b58525a 100644
> --- a/drivers/net/ethernet/intel/iecm/iecm_txrx.c
> +++ b/drivers/net/ethernet/intel/iecm/iecm_txrx.c
> @@ -218,6 +218,36 @@ const struct iecm_rx_ptype_decoded iecm_ptype_lookup[IECM_RX_MAX_PTYPE] = {
>  };
>  EXPORT_SYMBOL(iecm_ptype_lookup);
>  
> +/**
> + * iecm_buf_lifo_push - push a buffer pointer onto stack
> + * @stack: pointer to stack struct
> + * @buf: pointer to buf to push
> + *
> + * Returns 0 on success, negative on failure
> + **/
> +static int iecm_buf_lifo_push(struct iecm_buf_lifo *stack,
> +			      struct iecm_tx_buf *buf)
> +{
> +	if (stack->top == stack->size)

How frequent is this? A candidate for unlikely() maybe?

> +		return -ENOSPC;
> +
> +	stack->bufs[stack->top++] = buf;
> +
> +	return 0;
> +}
> +
> +/**
> + * iecm_buf_lifo_pop - pop a buffer pointer from stack
> + * @stack: pointer to stack struct
> + **/
> +static struct iecm_tx_buf *iecm_buf_lifo_pop(struct iecm_buf_lifo *stack)
> +{
> +	if (!stack->top)
> +		return NULL;
> +
> +	return stack->bufs[--stack->top];
> +}
> +
>  /**
>   * iecm_get_stats64 - get statistics for network device structure
>   * @netdev: network interface device structure
> @@ -812,6 +842,30 @@ iecm_rx_buf_hw_alloc_all(struct iecm_queue *rxbufq, u16 alloc_count)
>  	return !!alloc_count;
>  }
>  
> +/**
> + * iecm_rx_post_buf_refill - Post buffer id to refill queue
> + * @refillq: refill queue to post to
> + * @buf_id: buffer id to post
> + */
> +void iecm_rx_post_buf_refill(struct iecm_sw_queue *refillq, u16 buf_id)
> +{
> +	u16 nta = refillq->next_to_alloc;
> +	u16 *bi;
> +
> +	bi = IECM_SPLITQ_RX_BI_DESC(refillq, nta);
> +	/* store the buffer ID and the SW maintained GEN bit to the refillq */
> +	*bi = ((buf_id << IECM_RX_BI_BUFID_S) & IECM_RX_BI_BUFID_M) |
> +	      (!!(test_bit(__IECM_Q_GEN_CHK, refillq->flags)) <<
> +	       IECM_RX_BI_GEN_S);

Please use FIELD_GET() and FIELD_PREP() for masks. This won't pass
the maintainers.

> +
> +	nta++;
> +	if (unlikely(nta == refillq->desc_count)) {

Could be compressed into one line.

> +		nta = 0;
> +		change_bit(__IECM_Q_GEN_CHK, refillq->flags);
> +	}
> +	refillq->next_to_alloc = nta;
> +}
> +
>  /**
>   * iecm_rx_post_buf_desc - Post buffer to bufq descriptor ring
>   * @bufq: buffer queue to post to
> @@ -1670,6 +1724,398 @@ int iecm_vport_queues_alloc(struct iecm_vport *vport)
>  	return err;
>  }
>  
> +/**
> + * iecm_tx_find_q - Find the tx q based on q id
> + * @vport: the vport we care about
> + * @q_id: Id of the queue
> + *
> + * Returns queue ptr if found else returns NULL
> + */
> +static struct iecm_queue *
> +iecm_tx_find_q(struct iecm_vport *vport, int q_id)
> +{
> +	int i;
> +
> +	for (i = 0; i < vport->num_txq; i++) {
> +		struct iecm_queue *tx_q = vport->txqs[i];
> +
> +		if (tx_q->q_id == q_id)
> +			return tx_q;
> +	}

	for (i = ...)
		if (vport->txqs[i].q_id == q_id)
			return tx_q;

No need to create a variable.

> +
> +	return NULL;
> +}
> +
> +/**
> + * iecm_tx_handle_sw_marker - Handle queue marker packet
> + * @tx_q: tx queue to handle software marker
> + */
> +static void iecm_tx_handle_sw_marker(struct iecm_queue *tx_q)
> +{
> +	struct iecm_vport *vport = tx_q->vport;
> +	bool drain_complete = true;
> +	int i;
> +
> +	clear_bit(__IECM_Q_SW_MARKER, tx_q->flags);
> +	/* Hardware must write marker packets to all queues associated with
> +	 * completion queues. So check if all queues received marker packets
> +	 */
> +	for (i = 0; i < vport->num_txq; i++) {
> +		if (test_bit(__IECM_Q_SW_MARKER, vport->txqs[i]->flags))
> +			drain_complete = false;
> +	}
> +	if (drain_complete) {
> +		set_bit(__IECM_SW_MARKER, vport->adapter->flags);
> +		wake_up(&vport->adapter->sw_marker_wq);
> +	}
> +}
> +
> +/**
> + * iecm_tx_splitq_clean_buf - Clean TX buffer resources
> + * @tx_q: tx queue to clean buffer from
> + * @tx_buf: buffer to be cleaned
> + * @napi_budget: Used to determine if we are in netpoll
> + */
> +static void
> +iecm_tx_splitq_clean_buf(struct iecm_queue *tx_q, struct iecm_tx_buf *tx_buf,
> +			 int napi_budget)
> +{
> +	/* unmap skb header data */
> +	dma_unmap_single(tx_q->dev,
> +			 dma_unmap_addr(tx_buf, dma),

These two lines can fit into 79 without wrapping.

> +			 dma_unmap_len(tx_buf, len),
> +			 DMA_TO_DEVICE);

These two as well.

> +
> +	napi_consume_skb(tx_buf->skb, napi_budget);
> +
> +	/* clear tx_buf data */
> +	tx_buf->skb = NULL;
> +	dma_unmap_len_set(tx_buf, len, 0);
> +}
> +
> +/**
> + * iecm_stash_flow_sch_buffers - store buffere parameter info to be freed at a
> + * later time (only relevant for flow scheduling mode)
> + * @txq: Tx queue to clean
> + * @tx_buf: buffer to store
> + */
> +static int
> +iecm_stash_flow_sch_buffers(struct iecm_queue *txq, struct iecm_tx_buf *tx_buf)
> +{
> +	struct iecm_adapter *adapter = txq->vport->adapter;
> +	struct iecm_tx_buf *shadow_buf;
> +
> +	shadow_buf = iecm_buf_lifo_pop(&txq->buf_stack);
> +	if (!shadow_buf) {
> +		dev_err(&adapter->pdev->dev,
> +			"No out-of-order TX buffers left!\n");
> +		return -ENOMEM;
> +	}
> +
> +	/* Store buffer params in shadow buffer */
> +	shadow_buf->skb = tx_buf->skb;
> +	shadow_buf->bytecount = tx_buf->bytecount;
> +	shadow_buf->gso_segs = tx_buf->gso_segs;
> +	dma_unmap_addr_set(shadow_buf, dma, dma_unmap_addr(tx_buf, dma));
> +	dma_unmap_len_set(shadow_buf, len, dma_unmap_len(tx_buf, len));
> +	shadow_buf->compl_tag = tx_buf->compl_tag;
> +
> +	/* Add buffer to buf_hash table to be freed
> +	 * later
> +	 */
> +	hash_add(txq->sched_buf_hash, &shadow_buf->hlist,
> +		 shadow_buf->compl_tag);
> +
> +	memset(tx_buf, 0, sizeof(struct iecm_tx_buf));
> +
> +	return 0;
> +}
> +
> +/**
> + * iecm_tx_splitq_clean - Reclaim resources from buffer queue
> + * @tx_q: Tx queue to clean
> + * @end: queue index until which it should be cleaned
> + * @napi_budget: Used to determine if we are in netpoll
> + * @descs_only: true if queue is using flow-based scheduling and should
> + * not clean buffers at this time
> + *
> + * Cleans the queue descriptor ring. If the queue is using queue-based
> + * scheduling, the buffers will be cleaned as well and this function will
> + * return the number of bytes/packets cleaned. If the queue is using flow-based
> + * scheduling, only the descriptors are cleaned at this time. Separate packet
> + * completion events will be reported on the completion queue, and the buffers
> + * will be cleaned separately. The stats returned from this function when using
> + * flow-based scheduling are irrelevant.
> + */
> +static struct iecm_tx_queue_stats
> +iecm_tx_splitq_clean(struct iecm_queue *tx_q, u16 end, int napi_budget,
> +		     bool descs_only)
> +{
> +	union iecm_tx_flex_desc *next_pending_desc = NULL;
> +	struct iecm_tx_queue_stats cleaned_stats = {0};
> +	union iecm_tx_flex_desc *tx_desc;
> +	s16 ntc = tx_q->next_to_clean;
> +	struct iecm_tx_buf *tx_buf;
> +	unsigned short gso_segs = 0;
> +	unsigned int bytecount = 0;
> +	struct netdev_queue *nq;
> +
> +	tx_desc = IECM_FLEX_TX_DESC(tx_q, ntc);
> +	next_pending_desc = IECM_FLEX_TX_DESC(tx_q, end);
> +	tx_buf = &tx_q->tx_buf[ntc];
> +	ntc -= tx_q->desc_count;
> +
> +	while (tx_desc != next_pending_desc) {
> +		union iecm_tx_flex_desc *eop_desc =
> +			(union iecm_tx_flex_desc *)tx_buf->next_to_watch;
> +
> +		/* clear next_to_watch to prevent false hangs */
> +		tx_buf->next_to_watch = NULL;
> +
> +		bytecount += tx_buf->bytecount;
> +		gso_segs += tx_buf->gso_segs;
> +
> +		if (descs_only) {
> +			if (iecm_stash_flow_sch_buffers(tx_q, tx_buf))
> +				goto tx_splitq_clean_out;
> +
> +			while (tx_desc != eop_desc) {
> +				tx_buf++;
> +				tx_desc++;
> +				ntc++;
> +				if (unlikely(!ntc)) {
> +					ntc -= tx_q->desc_count;
> +					tx_buf = tx_q->tx_buf;
> +					tx_desc = IECM_FLEX_TX_DESC(tx_q, 0);
> +				}
> +
> +				if (dma_unmap_len(tx_buf, len)) {
> +					if (iecm_stash_flow_sch_buffers(tx_q,
> +									tx_buf))
> +						goto tx_splitq_clean_out;
> +				}

Redundant braces, redundant nested ifs.

Also, it's actually an error, since dma_unmap_len() always equals 0
for architectures which don't select NEED_DMA_MAP_STATE. So this
should either check for any other signs of mapped buffer or field
`len` should be present and defined with correct values
unconditionally.

> +			}
> +		} else {
> +			/* update the statistics for this packet */
> +			cleaned_stats.bytes += tx_buf->bytecount;
> +			cleaned_stats.packets += tx_buf->gso_segs;
> +
> +			iecm_tx_splitq_clean_buf(tx_q, tx_buf, napi_budget);
> +
> +			/* unmap remaining buffers */
> +			while (tx_desc != eop_desc) {
> +				tx_buf++;
> +				tx_desc++;
> +				ntc++;
> +				if (unlikely(!ntc)) {
> +					ntc -= tx_q->desc_count;
> +					tx_buf = tx_q->tx_buf;
> +					tx_desc = IECM_FLEX_TX_DESC(tx_q, 0);
> +				}
> +
> +				/* unmap any remaining paged data */
> +				if (dma_unmap_len(tx_buf, len)) {

Same here.

> +					dma_unmap_page(tx_q->dev,
> +						       dma_unmap_addr(tx_buf, dma),
> +						       dma_unmap_len(tx_buf, len),
> +						       DMA_TO_DEVICE);
> +					dma_unmap_len_set(tx_buf, len, 0);
> +				}
> +			}
> +		}
> +
> +		tx_buf++;
> +		tx_desc++;
> +		ntc++;
> +		if (unlikely(!ntc)) {
> +			ntc -= tx_q->desc_count;
> +			tx_buf = tx_q->tx_buf;
> +			tx_desc = IECM_FLEX_TX_DESC(tx_q, 0);
> +		}
> +	}
> +
> +tx_splitq_clean_out:
> +	ntc += tx_q->desc_count;
> +	tx_q->next_to_clean = ntc;
> +
> +	nq = netdev_get_tx_queue(tx_q->vport->netdev, tx_q->idx);
> +	netdev_tx_completed_queue(nq, gso_segs, bytecount);
> +
> +	return cleaned_stats;
> +}
> +
> +/**
> + * iecm_tx_clean_flow_sch_bufs - clean bufs that were stored for
> + * out of order completions
> + * @txq: queue to clean
> + * @compl_tag: completion tag of packet to clean (from completion descriptor)
> + * @budget: Used to determine if we are in netpoll
> + */
> +static struct iecm_tx_queue_stats
> +iecm_tx_clean_flow_sch_bufs(struct iecm_queue *txq, u16 compl_tag,
> +			    int budget)
> +{
> +	struct iecm_tx_queue_stats cleaned_stats = {0};
> +	struct hlist_node *tmp_buf = NULL;
> +	struct iecm_tx_buf *tx_buf = NULL;
> +
> +	/* Buffer completion */
> +	hash_for_each_possible_safe(txq->sched_buf_hash, tx_buf, tmp_buf,
> +				    hlist, compl_tag) {
> +		if (tx_buf->compl_tag != compl_tag)
> +			continue;
> +
> +		if (likely(tx_buf->skb)) {
> +			/* update the statistics for this packet */
> +			cleaned_stats.bytes += tx_buf->bytecount;
> +			cleaned_stats.packets += tx_buf->gso_segs;
> +
> +			iecm_tx_splitq_clean_buf(txq, tx_buf, budget);
> +		} else if (dma_unmap_len(tx_buf, len)) {

Here as well.

> +			dma_unmap_page(txq->dev,
> +				       dma_unmap_addr(tx_buf, dma),
> +				       dma_unmap_len(tx_buf, len),
> +				       DMA_TO_DEVICE);
> +			dma_unmap_len_set(tx_buf, len, 0);
> +		}
> +		/* Push shadow buf back onto stack */
> +		iecm_buf_lifo_push(&txq->buf_stack, tx_buf);
> +
> +		hash_del(&tx_buf->hlist);
> +	}
> +
> +	return cleaned_stats;
> +}
> +
> +/**
> + * iecm_tx_clean_complq - Reclaim resources on completion queue
> + * @complq: Tx ring to clean
> + * @budget: Used to determine if we are in netpoll
> + *
> + * Returns true if there's any budget left (e.g. the clean is finished)
> + */
> +static bool
> +iecm_tx_clean_complq(struct iecm_queue *complq, int budget)
> +{
> +	struct iecm_splitq_tx_compl_desc *tx_desc;
> +	struct iecm_vport *vport = complq->vport;
> +	s16 ntc = complq->next_to_clean;
> +	bool clean_completed = false;
> +	unsigned int complq_budget;
> +
> +	complq_budget = vport->compln_clean_budget;
> +	tx_desc = IECM_SPLITQ_TX_COMPLQ_DESC(complq, ntc);
> +	ntc -= complq->desc_count;
> +
> +	do {
> +		struct iecm_tx_queue_stats cleaned_stats = {0};
> +		struct iecm_queue *tx_q;
> +		u16 compl_tag, hw_head;
> +		int tx_qid;
> +		u8 ctype;	/* completion type */
> +		u16 gen;
> +
> +		/* if the descriptor isn't done, no work yet to do */
> +		gen = (le16_to_cpu(tx_desc->qid_comptype_gen) &
> +		      IECM_TXD_COMPLQ_GEN_M) >> IECM_TXD_COMPLQ_GEN_S;

Same stuff about FIELD_{GET,PREP}().

> +		if (test_bit(__IECM_Q_GEN_CHK, complq->flags) != gen)
> +			break;
> +
> +		/* Find necessary info of TX queue to clean buffers */
> +		tx_qid = (le16_to_cpu(tx_desc->qid_comptype_gen) &
> +			 IECM_TXD_COMPLQ_QID_M) >> IECM_TXD_COMPLQ_QID_S;

Here as well, and in other places I missed.

> +		tx_q = iecm_tx_find_q(vport, tx_qid);
> +		if (!tx_q) {
> +			dev_err(&complq->vport->adapter->pdev->dev,
> +				"TxQ #%d not found\n", tx_qid);
> +			goto fetch_next_desc;
> +		}
> +
> +		/* Determine completion type */
> +		ctype = (le16_to_cpu(tx_desc->qid_comptype_gen) &
> +			IECM_TXD_COMPLQ_COMPL_TYPE_M) >>
> +			IECM_TXD_COMPLQ_COMPL_TYPE_S;
> +		switch (ctype) {
> +		case IECM_TXD_COMPLT_RE:
> +			hw_head = le16_to_cpu(tx_desc->q_head_compl_tag.q_head);
> +
> +			cleaned_stats = iecm_tx_splitq_clean(tx_q, hw_head,
> +							     budget, true);
> +			break;
> +		case IECM_TXD_COMPLT_RS:
> +			if (test_bit(__IECM_Q_FLOW_SCH_EN, tx_q->flags)) {
> +				compl_tag =
> +				le16_to_cpu(tx_desc->q_head_compl_tag.compl_tag);
> +
> +				cleaned_stats =
> +					iecm_tx_clean_flow_sch_bufs(tx_q,
> +								    compl_tag,
> +								    budget);
> +			} else {
> +				hw_head =
> +				le16_to_cpu(tx_desc->q_head_compl_tag.q_head);
> +
> +				cleaned_stats = iecm_tx_splitq_clean(tx_q,
> +								     hw_head,
> +								     budget,
> +								     false);
> +			}
> +
> +			break;
> +		case IECM_TXD_COMPLT_SW_MARKER:
> +			iecm_tx_handle_sw_marker(tx_q);
> +			break;
> +		default:
> +			dev_err(&tx_q->vport->adapter->pdev->dev,
> +				"Unknown TX completion type: %d\n",
> +				ctype);
> +			goto fetch_next_desc;
> +		}
> +
> +		u64_stats_update_begin(&tx_q->stats_sync);
> +		tx_q->q_stats.tx.packets += cleaned_stats.packets;
> +		tx_q->q_stats.tx.bytes += cleaned_stats.bytes;
> +		u64_stats_update_end(&tx_q->stats_sync);
> +
> +		if (unlikely(cleaned_stats.packets &&
> +			     netif_carrier_ok(tx_q->vport->netdev) &&
> +			     (IECM_DESC_UNUSED(tx_q) >= IECM_TX_WAKE_THRESH) &&
> +			     (IECM_TX_BUF_UNUSED(tx_q) >= tx_q->desc_count >> 2))) {
> +			/* Make sure any other threads stopping queue after
> +			 * this see new next_to_clean.
> +			 */
> +			smp_mb();
> +			if (tx_q->vport->adapter->state == __IECM_UP &&
> +			    __netif_subqueue_stopped(tx_q->vport->netdev,
> +						     tx_q->idx)) {
> +				netif_wake_subqueue(tx_q->vport->netdev,
> +						    tx_q->idx);
> +			}
> +		}
> +
> +fetch_next_desc:
> +		tx_desc++;
> +		ntc++;
> +		if (unlikely(!ntc)) {
> +			ntc -= complq->desc_count;
> +			tx_desc = IECM_SPLITQ_TX_COMPLQ_DESC(complq, 0);
> +			change_bit(__IECM_Q_GEN_CHK, complq->flags);
> +		}
> +
> +		prefetch(tx_desc);
> +
> +		/* update budget accounting */
> +		complq_budget--;
> +	} while (likely(complq_budget));
> +
> +	ntc += complq->desc_count;
> +	complq->next_to_clean = ntc;
> +
> +	clean_completed = !!complq_budget;
> +
> +	return clean_completed;
> +}
> +
>  /**
>   * iecm_tx_splitq_build_ctb - populate command tag and size for queue
>   * based scheduling descriptors
> @@ -2337,6 +2783,940 @@ netdev_tx_t iecm_tx_splitq_start(struct sk_buff *skb,
>  	return iecm_tx_splitq_frame(skb, tx_q);
>  }
>  
> +/**
> + * iecm_ptype_to_htype - get a hash type
> + * @decoded: Decoded Rx packet type related fields
> + *
> + * Returns appropriate hash type (such as PKT_HASH_TYPE_L2/L3/L4) to be used by
> + * skb_set_hash based on PTYPE as parsed by HW Rx pipeline and is part of
> + * Rx desc.
> + */
> +enum
> +pkt_hash_types iecm_ptype_to_htype(struct iecm_rx_ptype_decoded *decoded)
> +{
> +	if (!decoded->known)
> +		return PKT_HASH_TYPE_NONE;
> +	if (decoded->payload_layer == IECM_RX_PTYPE_PAYLOAD_LAYER_PAY2 &&
> +	    decoded->inner_prot)
> +		return PKT_HASH_TYPE_L4;
> +	if (decoded->payload_layer == IECM_RX_PTYPE_PAYLOAD_LAYER_PAY2 &&
> +	    decoded->outer_ip)
> +		return PKT_HASH_TYPE_L3;
> +	if (decoded->outer_ip == IECM_RX_PTYPE_OUTER_L2)
> +		return PKT_HASH_TYPE_L2;
> +
> +	return PKT_HASH_TYPE_NONE;
> +}
> +
> +/**
> + * iecm_rx_hash - set the hash value in the skb
> + * @rxq: Rx descriptor ring packet is being transacted on
> + * @skb: pointer to current skb being populated
> + * @rx_desc: Receive descriptor
> + * @decoded: Decoded Rx packet type related fields
> + */
> +static void
> +iecm_rx_hash(struct iecm_queue *rxq, struct sk_buff *skb,
> +	     struct virtchnl2_rx_flex_desc_adv_nic_3 *rx_desc,
> +	     struct iecm_rx_ptype_decoded *decoded)
> +{
> +	u32 hash;
> +
> +	if (!iecm_is_feature_ena(rxq->vport, NETIF_F_RXHASH))
> +		return;
> +
> +	hash = le16_to_cpu(rx_desc->hash1) |
> +	       (rx_desc->ff2_mirrid_hash2.hash2 << 16) |
> +	       (rx_desc->hash3 << 24);
> +
> +	skb_set_hash(skb, hash, iecm_ptype_to_htype(decoded));
> +}
> +
> +/**
> + * iecm_rx_csum - Indicate in skb if checksum is good
> + * @rxq: Rx descriptor ring packet is being transacted on
> + * @skb: pointer to current skb being populated
> + * @csum_bits: checksum fields extracted from the descriptor
> + * @decoded: Decoded Rx packet type related fields
> + *
> + * skb->protocol must be set before this function is called
> + */
> +static void iecm_rx_csum(struct iecm_queue *rxq, struct sk_buff *skb,
> +			 struct iecm_rx_csum_decoded *csum_bits,
> +			 struct iecm_rx_ptype_decoded *decoded)
> +{
> +	bool ipv4, ipv6;
> +
> +	/* Start with CHECKSUM_NONE and by default csum_level = 0 */
> +	skb->ip_summed = CHECKSUM_NONE;
> +
> +	/* check if Rx checksum is enabled */
> +	if (!iecm_is_feature_ena(rxq->vport, NETIF_F_RXCSUM))
> +		return;
> +
> +	/* check if HW has decoded the packet and checksum */
> +	if (!(csum_bits->l3l4p))
> +		return;
> +
> +	ipv4 = (decoded->outer_ip == IECM_RX_PTYPE_OUTER_IP) &&
> +	       (decoded->outer_ip_ver == IECM_RX_PTYPE_OUTER_IPV4);
> +	ipv6 = (decoded->outer_ip == IECM_RX_PTYPE_OUTER_IP) &&
> +	       (decoded->outer_ip_ver == IECM_RX_PTYPE_OUTER_IPV6);
> +
> +	if (ipv4 && (csum_bits->ipe || csum_bits->eipe))
> +		goto checksum_fail;
> +
> +	if (ipv6 && csum_bits->ipv6exadd)
> +		return;
> +
> +	/* HW checksum will be invalid if vlan stripping is not enabled and
> +	 * packet has an outer vlan tag. raw_csum_inv will also not be set
> +	 * even though it's invalid.
> +	 */
> +	if (skb_vlan_tag_present(skb))
> +		return;
> +
> +	/* check for L4 errors and handle packets that were not able to be
> +	 * checksummed
> +	 */
> +	if (csum_bits->l4e)
> +		goto checksum_fail;
> +
> +	/* Only report checksum unnecessary for ICMP, TCP, UDP, or SCTP */
> +	switch (decoded->inner_prot) {
> +	case IECM_RX_PTYPE_INNER_PROT_ICMP:
> +	case IECM_RX_PTYPE_INNER_PROT_TCP:
> +	case IECM_RX_PTYPE_INNER_PROT_UDP:
> +	case IECM_RX_PTYPE_INNER_PROT_SCTP:
> +		skb->ip_summed = CHECKSUM_UNNECESSARY;
> +	default:
> +		break;
> +	}
> +	return;
> +
> +checksum_fail:
> +	rxq->vport->port_stats.rx_hw_csum_err++;
> +}
> +
> +/**
> + * iecm_rx_splitq_extract_csum_bits - Extract checksum bits from descriptor
> + * @rx_desc: receive descriptor
> + * @csum: structure to extract checksum fields
> + *
> + **/
> +static void
> +iecm_rx_splitq_extract_csum_bits(struct virtchnl2_rx_flex_desc_adv_nic_3 *rx_desc,
> +				 struct iecm_rx_csum_decoded *csum)
> +{
> +	u8 qword0, qword1;
> +
> +	qword0 = rx_desc->status_err0_qw0;
> +	qword1 = rx_desc->status_err0_qw1;
> +
> +	csum->ipe = !!(qword1 &
> +		       BIT(VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_XSUM_IPE_S));
> +	csum->eipe = !!(qword1 &
> +			BIT(VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_XSUM_EIPE_S));
> +	csum->l4e = !!(qword1 &
> +		       BIT(VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_XSUM_L4E_S));
> +	csum->l3l4p = !!(qword1 &
> +			 BIT(VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_L3L4P_S));
> +	csum->ipv6exadd =
> +			!!(qword0 &
> +			   BIT(VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_IPV6EXADD_S));
> +	csum->rsc = !!(le16_to_cpu(rx_desc->hdrlen_flags) &
> +		       VIRTCHNL2_RX_FLEX_DESC_ADV_RSC_M);
> +	csum->raw_csum_inv = !!(le16_to_cpu(rx_desc->ptype_err_fflags0) &
> +				BIT(VIRTCHNL2_RX_FLEX_DESC_ADV_FF0_S));
> +	csum->raw_csum = le16_to_cpu(rx_desc->misc.raw_cs);
> +	csum->pprs = 0;
> +}
> +
> +/**
> + * iecm_rx_rsc - Set the RSC fields in the skb
> + * @rxq : Rx descriptor ring packet is being transacted on
> + * @skb : pointer to current skb being populated
> + * @rx_desc: Receive descriptor
> + * @decoded: Decoded Rx packet type related fields
> + *
> + * Return 0 on success and error code on failure
> + *
> + * Populate the skb fields with the total number of RSC segments, RSC payload
> + * length and packet type.
> + */
> +static int iecm_rx_rsc(struct iecm_queue *rxq, struct sk_buff *skb,
> +		       struct virtchnl2_rx_flex_desc_adv_nic_3 *rx_desc,
> +		       struct iecm_rx_ptype_decoded *decoded)
> +{
> +	u16 rsc_segments, rsc_payload_len;
> +	struct tcphdr *tcph;
> +	bool ipv4, ipv6;
> +
> +	if (!decoded->outer_ip)
> +		return -EINVAL;
> +
> +	rsc_payload_len = le16_to_cpu(rx_desc->misc.rscseglen);
> +	if (!rsc_payload_len)
> +		return -EINVAL;
> +
> +	ipv4 = (decoded->outer_ip == IECM_RX_PTYPE_OUTER_IP) &&
> +		(decoded->outer_ip_ver == IECM_RX_PTYPE_OUTER_IPV4);
> +	ipv6 = (decoded->outer_ip == IECM_RX_PTYPE_OUTER_IP) &&
> +		(decoded->outer_ip_ver == IECM_RX_PTYPE_OUTER_IPV6);
> +
> +	if (!(ipv4 ^ ipv6))
> +		return -EINVAL;
> +
> +	rsc_segments = DIV_ROUND_UP(skb->data_len, rsc_payload_len);
> +
> +	NAPI_GRO_CB(skb)->count = rsc_segments;
> +	skb_shinfo(skb)->gso_size = rsc_payload_len;
> +
> +	skb_reset_network_header(skb);
> +
> +	if (ipv4) {
> +		struct iphdr *ipv4h = ip_hdr(skb);
> +
> +		skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4;
> +
> +		/* Reset and set transport header offset in skb */
> +		skb_set_transport_header(skb, sizeof(struct iphdr));
> +		tcph = tcp_hdr(skb);
> +
> +		/* Compute the TCP pseudo header checksum*/
> +		tcph->check =
> +			~tcp_v4_check(skb->len - skb_transport_offset(skb),
> +				      ipv4h->saddr, ipv4h->daddr, 0);
> +	} else {
> +		struct ipv6hdr *ipv6h = ipv6_hdr(skb);
> +
> +		skb_shinfo(skb)->gso_type = SKB_GSO_TCPV6;
> +		skb_set_transport_header(skb, sizeof(struct ipv6hdr));
> +		tcph = tcp_hdr(skb);
> +		tcph->check =
> +			~tcp_v6_check(skb->len - skb_transport_offset(skb),
> +				      &ipv6h->saddr, &ipv6h->daddr, 0);
> +	}
> +
> +	tcp_gro_complete(skb);
> +
> +	u64_stats_update_begin(&rxq->stats_sync);
> +	rxq->q_stats.rx.rsc_pkts++;
> +	u64_stats_update_end(&rxq->stats_sync);
> +
> +	return 0;
> +}
> +
> +/**
> + * iecm_rx_process_skb_fields - Populate skb header fields from Rx descriptor
> + * @rxq: Rx descriptor ring packet is being transacted on
> + * @skb: pointer to current skb being populated
> + * @rx_desc: Receive descriptor
> + *
> + * This function checks the ring, descriptor, and packet information in
> + * order to populate the hash, checksum, VLAN, protocol, and
> + * other fields within the skb.
> + */
> +int
> +iecm_rx_process_skb_fields(struct iecm_queue *rxq, struct sk_buff *skb,
> +			   struct virtchnl2_rx_flex_desc_adv_nic_3 *rx_desc)
> +{
> +	struct iecm_rx_ptype_decoded decoded;
> +	struct iecm_rx_csum_decoded csum_bits;

Please follow RCT.

> +	u16 rx_ptype;
> +	int err = 0;
> +
> +	rx_ptype = le16_to_cpu(rx_desc->ptype_err_fflags0) &
> +				VIRTCHNL2_RX_FLEX_DESC_ADV_PTYPE_M;
> +
> +	decoded = rxq->vport->rx_ptype_lkup[rx_ptype];
> +	if (!decoded.known)
> +		return -EINVAL;
> +
> +	/* modifies the skb - consumes the enet header */
> +	skb->protocol = eth_type_trans(skb, rxq->vport->netdev);

eth_type_trans() should generally be called *right* before
napi_gro_receive() to still have caches warm.

> +	iecm_rx_splitq_extract_csum_bits(rx_desc, &csum_bits);
> +	iecm_rx_csum(rxq, skb, &csum_bits, &decoded);
> +	/* process RSS/hash */
> +	iecm_rx_hash(rxq, skb, rx_desc, &decoded);
> +
> +	if (csum_bits.rsc)
> +		err = iecm_rx_rsc(rxq, skb, rx_desc, &decoded);
> +
> +	return err;
> +}
> +
> +/**
> + * iecm_rx_skb - Send a completed packet up the stack
> + * @rxq: Rx ring in play
> + * @skb: packet to send up
> + * @vlan_tag: packet vlan tag
> + *
> + * This function sends the completed packet (via. skb) up the stack using
> + * gro receive functions
> + */
> +void iecm_rx_skb(struct iecm_queue *rxq, struct sk_buff *skb, u16 vlan_tag)
> +{
> +	/* Adding HW VLAN tag to skb must occur after processing csum */
> +	if (vlan_tag & VLAN_VID_MASK)
> +		__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan_tag);

Why can't this be placed into process_skb_fields()? This function
becomes redundant then, and you could call napi_gro_receive()
directly instead.

> +
> +	napi_gro_receive(&rxq->q_vector->napi, skb);
> +}
> +
> +/**
> + * iecm_rx_page_is_reserved - check if reuse is possible
> + * @page: page struct to check
> + */
> +static bool iecm_rx_page_is_reserved(struct page *page)
> +{
> +	return (page_to_nid(page) != numa_mem_id()) || page_is_pfmemalloc(page);
> +}

Please check generic dev_page_is_reusable(), it's almost the same
(a bit more optimized).

> +
> +/**
> + * iecm_rx_buf_adjust_pg - Prepare rx buffer for reuse
> + * @rx_buf: Rx buffer to adjust
> + * @size: Size of adjustment
> + *
> + * Update the offset within page so that rx buf will be ready to be reused.
> + * For systems with PAGE_SIZE < 8192 this function will flip the page offset
> + * so the second half of page assigned to rx buffer will be used, otherwise
> + * the offset is moved by the @size bytes
> + */
> +void
> +iecm_rx_buf_adjust_pg(struct iecm_rx_buf *rx_buf, unsigned int size)
> +{
> +	struct iecm_page_info *page_info = &rx_buf->page_info[rx_buf->page_indx];
> +
> +#if (PAGE_SIZE < 8192)
> +	if (rx_buf->buf_size > IECM_RX_BUF_2048)
> +		/* flip to second page */
> +		rx_buf->page_indx = !rx_buf->page_indx;
> +	else
> +		/* flip page offset to other buffer */
> +		page_info->page_offset ^= size;
> +#else
> +	/* move offset up to the next cache line */
> +	page_info->page_offset += size;
> +#endif

Again, no need for ifdeffery, PAGE_SIZE check can be placed into
an if statement.

> +}
> +
> +/**
> + * iecm_rx_can_reuse_page - Determine if page can be reused for another rx
> + * @rx_buf: buffer containing the page
> + *
> + * If page is reusable, we have a green light for calling iecm_reuse_rx_page,
> + * which will assign the current buffer to the buffer that next_to_alloc is
> + * pointing to; otherwise, the dma mapping needs to be destroyed and
> + * page freed
> + */
> +bool iecm_rx_can_reuse_page(struct iecm_rx_buf *rx_buf)
> +{
> +	struct iecm_page_info *page_info = &rx_buf->page_info[rx_buf->page_indx];
> +
> +#if (PAGE_SIZE >= 8192)
> +	unsigned int last_offset = PAGE_SIZE - rx_buf->buf_size;
> +#endif /* PAGE_SIZE < 8192) */
> +	unsigned int pagecnt_bias = page_info->pagecnt_bias;
> +	struct page *page = page_info->page;
> +
> +	/* avoid re-using remote pages */
> +	if (unlikely(iecm_rx_page_is_reserved(page)))
> +		return false;
> +
> +#if (PAGE_SIZE < 8192)
> +	/* if we are only owner of page we can reuse it */
> +	if (unlikely((page_count(page) - pagecnt_bias) > 1))
> +		return false;
> +#else
> +	if (rx_buf->page_offset > last_offset)
> +		return false;
> +#endif /* PAGE_SIZE < 8192) */

Same here 2 times.

> +
> +	/* If we have drained the page fragment pool we need to update
> +	 * the pagecnt_bias and page count so that we fully restock the
> +	 * number of references the driver holds.
> +	 */
> +	if (unlikely(pagecnt_bias == 1)) {

With 1532 byte frames, this condition will be hit 50% of times. It's
definitely not a good place for unlkely().

> +		page_ref_add(page, USHRT_MAX - 1);
> +		page_info->pagecnt_bias = USHRT_MAX;
> +	}
> +
> +	return true;
> +}
> +
> +/**
> + * iecm_rx_add_frag - Add contents of Rx buffer to sk_buff as a frag
> + * @rx_buf: buffer containing page to add
> + * @skb: sk_buff to place the data into
> + * @size: packet length from rx_desc
> + *
> + * This function will add the data contained in rx_buf->page to the skb.
> + * It will just attach the page as a frag to the skb.
> + * The function will then update the page offset.
> + */
> +void iecm_rx_add_frag(struct iecm_rx_buf *rx_buf, struct sk_buff *skb,
> +		      unsigned int size)
> +{
> +	struct iecm_page_info *page_info = &rx_buf->page_info[rx_buf->page_indx];
> +
> +#if (PAGE_SIZE >= 8192)
> +	unsigned int truesize = SKB_DATA_ALIGN(size);
> +#else
> +	unsigned int truesize = rx_buf->buf_size;
> +#endif

Same.

> +
> +	skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page_info->page,
> +			page_info->page_offset, size, truesize);
> +
> +	iecm_rx_buf_adjust_pg(rx_buf, truesize);
> +}
> +
> +/**
> + * iecm_rx_get_buf_page - Fetch Rx buffer page and synchronize data for use
> + * @dev: device struct
> + * @rx_buf: Rx buf to fetch page for
> + * @size: size of buffer to add to skb
> + *
> + * This function will pull an Rx buffer page from the ring and synchronize it
> + * for use by the CPU.
> + */
> +static void
> +iecm_rx_get_buf_page(struct device *dev, struct iecm_rx_buf *rx_buf,
> +		     const unsigned int size)
> +{
> +	struct iecm_page_info *page_info = &rx_buf->page_info[rx_buf->page_indx];
> +
> +	prefetch(page_info->page);
> +
> +	/* we are reusing so sync this buffer for CPU use */
> +	dma_sync_single_range_for_cpu(dev, page_info->dma,
> +				      page_info->page_offset, size,
> +				      DMA_FROM_DEVICE);
> +
> +	/* We have pulled a buffer for use, so decrement pagecnt_bias */
> +	page_info->pagecnt_bias--;
> +}
> +
> +/**
> + * iecm_rx_construct_skb - Allocate skb and populate it
> + * @rxq: Rx descriptor queue
> + * @rx_buf: Rx buffer to pull data from
> + * @size: the length of the packet
> + *
> + * This function allocates an skb. It then populates it with the page
> + * data from the current receive descriptor, taking care to set up the
> + * skb correctly.
> + */
> +struct sk_buff *
> +iecm_rx_construct_skb(struct iecm_queue *rxq, struct iecm_rx_buf *rx_buf,
> +		      unsigned int size)
> +{
> +	struct iecm_page_info *page_info = &rx_buf->page_info[rx_buf->page_indx];
> +
> +	void *va = page_address(page_info->page) + page_info->page_offset;
> +	unsigned int headlen;
> +	struct sk_buff *skb;
> +
> +	/* prefetch first cache line of first page */
> +	prefetch(va);
> +#if L1_CACHE_BYTES < 128
> +	prefetch((u8 *)va + L1_CACHE_BYTES);
> +#endif /* L1_CACHE_BYTES */

Here's open-coded net_prefetch().

> +	/* allocate a skb to store the frags */
> +	skb = __napi_alloc_skb(&rxq->q_vector->napi, IECM_RX_HDR_SIZE,
> +			       GFP_ATOMIC | __GFP_NOWARN);
> +	if (unlikely(!skb))
> +		return NULL;
> +
> +	skb_record_rx_queue(skb, rxq->idx);
> +
> +	/* Determine available headroom for copy */
> +	headlen = size;
> +	if (headlen > IECM_RX_HDR_SIZE)
> +		headlen = eth_get_headlen(skb->dev, va, IECM_RX_HDR_SIZE);
> +
> +	/* align pull length to size of long to optimize memcpy performance */
> +	memcpy(__skb_put(skb, headlen), va, ALIGN(headlen, sizeof(long)));
> +
> +	/* if we exhaust the linear part then add what is left as a frag */
> +	size -= headlen;
> +	if (size) {
> +#if (PAGE_SIZE >= 8192)
> +		unsigned int truesize = SKB_DATA_ALIGN(size);
> +#else
> +		unsigned int truesize = rx_buf->buf_size;
> +#endif

Again.

> +		skb_add_rx_frag(skb, 0, page_info->page,
> +				page_info->page_offset + headlen, size,
> +				truesize);
> +		/* buffer is used by skb, update page_offset */
> +		iecm_rx_buf_adjust_pg(rx_buf, truesize);
> +
> +	} else {
> +		/* buffer is unused, reset bias back to rx_buf; data was copied
> +		 * onto skb's linear part so there's no need for adjusting
> +		 * page offset and we can reuse this buffer as-is
> +		 */
> +		page_info->pagecnt_bias++;
> +	}
> +
> +	return skb;
> +}
> +
> +/**
> + * iecm_rx_hdr_construct_skb - Allocate skb and populate it from header buffer
> + * @rxq: Rx descriptor queue
> + * @hdr_buf: Rx buffer to pull data from
> + * @size: the length of the packet
> + *
> + * This function allocates an skb. It then populates it with the page data from
> + * the current receive descriptor, taking care to set up the skb correctly.
> + * This specifcally uses a header buffer to start building the skb.
> + */
> +static struct sk_buff *
> +iecm_rx_hdr_construct_skb(struct iecm_queue *rxq, struct iecm_dma_mem *hdr_buf,
> +			  unsigned int size)
> +{
> +	struct sk_buff *skb;
> +
> +	/* allocate a skb to store the frags */
> +	skb = __napi_alloc_skb(&rxq->q_vector->napi, IECM_RX_HDR_SIZE,
> +			       GFP_ATOMIC | __GFP_NOWARN);
> +	if (unlikely(!skb))
> +		return NULL;
> +
> +	skb_record_rx_queue(skb, rxq->idx);
> +
> +	memcpy(__skb_put(skb, size), hdr_buf->va, size);
> +
> +	return skb;
> +}
> +
> +/**
> + * iecm_rx_splitq_test_staterr - tests bits in Rx descriptor
> + * status and error fields
> + * @stat_err_field: field from descriptor to test bits in
> + * @stat_err_bits: value to mask
> + *
> + */
> +bool
> +iecm_rx_splitq_test_staterr(u8 stat_err_field, const u8 stat_err_bits)
> +{
> +	return !!(stat_err_field & stat_err_bits);
> +}
> +
> +/**
> + * iecm_rx_splitq_extract_vlan_tag - Extract vlan tag from the descriptor
> + * @desc: Rx flex descriptor
> + * @rxq: rxq to check the vlan flags
> + * @vlan_tag: vlan tag to fill in
> + *
> + * Return true if error bit is set in the descriptor, else return false and
> + * store the vlan_tag in the variable passed in the function parameters
> + */
> +bool iecm_rx_splitq_extract_vlan_tag(struct virtchnl2_rx_flex_desc_adv_nic_3 *desc,
> +				     struct iecm_queue *rxq, u16 *vlan_tag)
> +{
> +	u8 stat_err0_qw0, stat_err_bits, stat_err1;
> +
> +	stat_err_bits = BIT(VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_RXE_S);
> +	stat_err0_qw0 = desc->status_err0_qw0;
> +	if (unlikely(iecm_rx_splitq_test_staterr(stat_err0_qw0, stat_err_bits)))
> +		return true;
> +
> +	stat_err1 = desc->status_err1;
> +
> +	if (stat_err0_qw0 & BIT(VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_L2TAG1P_S) &&
> +	    test_bit(__IECM_Q_VLAN_TAG_LOC_L2TAG1, rxq->flags))
> +		*vlan_tag = le16_to_cpu(desc->l2tag1);
> +	if (stat_err1 & BIT(VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS1_L2TAG2P_S) &&
> +	    test_bit(__IECM_Q_VLAN_TAG_LOC_L2TAG2, rxq->flags))
> +		*vlan_tag = le16_to_cpu(desc->l2tag2);
> +
> +	return false;
> +}
> +
> +/**
> + * iecm_rx_splitq_is_non_eop - process handling of non-EOP buffers
> + * @rx_desc: Rx descriptor for current buffer
> + *
> + * If the buffer is an EOP buffer, this function exits returning false,
> + * otherwise return true indicating that this is in fact a non-EOP buffer.
> + */
> +static bool
> +iecm_rx_splitq_is_non_eop(struct virtchnl2_rx_flex_desc_adv_nic_3 *rx_desc)
> +{
> +	/* if we are the last buffer then there is nothing else to do */
> +#define IECM_RXD_EOF BIT(VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_EOF_S)

This is a wrong place to define anything, it can be easily
overlooked.

> +	if (likely(iecm_rx_splitq_test_staterr(rx_desc->status_err0_qw1,
> +					       IECM_RXD_EOF)))
> +		return false;
> +
> +	return true;

This can be converted to an one-liner

	return likely(...);

> +}
> +
> +/**
> + * iecm_rx_splitq_recycle_buf - Attempt to recycle or realloc buffer
> + * @rxbufq: receive queue
> + * @rx_buf: Rx buffer to pull data from
> + *
> + * This function will clean up the contents of the rx_buf. It will either
> + * recycle the buffer or unmap it and free the associated resources. The buffer
> + * will then be placed on a refillq where it will later be reclaimed by the
> + * corresponding bufq.
> + *
> + * This works based on page flipping. If we assume e.g., a 4k page, it will be
> + * divided into two 2k buffers. We post the first half to hardware and, after
> + * using it, flip to second half of the page with iecm_adjust_pg_offset and
> + * post that to hardware. The third time through we'll flip back to first half
> + * of page and check if stack is still using it, if not we can reuse the buffer
> + * as is, otherwise we'll drain it and get a new page.
> + */
> +static void iecm_rx_splitq_recycle_buf(struct iecm_queue *rxbufq,
> +				       struct iecm_rx_buf *rx_buf)
> +{
> +	struct iecm_page_info *page_info = &rx_buf->page_info[rx_buf->page_indx];
> +
> +	if (!iecm_rx_can_reuse_page(rx_buf)) {
> +		/* we are not reusing the buffer so unmap it */
> +		dma_unmap_page_attrs(rxbufq->dev, page_info->dma, PAGE_SIZE,
> +				     DMA_FROM_DEVICE, IECM_RX_DMA_ATTR);
> +		__page_frag_cache_drain(page_info->page,
> +					page_info->pagecnt_bias);
> +
> +		/* clear contents of buffer_info */
> +		page_info->page = NULL;
> +		rx_buf->skb = NULL;
> +
> +		/* It's possible the alloc can fail here but there's not much
> +		 * we can do, bufq will have to try and realloc to fill the
> +		 * hole.
> +		 */
> +		iecm_alloc_page(rxbufq, page_info);
> +	}
> +
> +	/* We sync the memory back to hardware now to do as much work in this
> +	 * context as feasible.  Hardware won't actually know about the buffer
> +	 * until it's reclaimed off the refillq and put back into the bufq.
> +	 */
> +	if (likely(page_info->page)) {
> +		dma_sync_single_range_for_device(rxbufq->dev, page_info->dma,
> +						 page_info->page_offset,
> +						 rxbufq->rx_buf_size,
> +						 DMA_FROM_DEVICE);
> +	}

One-liner, braces are redundant.

> +}

Here you set page_info->page to NULL in the first condition branch
and check for it in the second.
So it's equivalent to

	if (iecm_rx_can_reuse_page(rx_buf)) {
		dma_sync_single_range();
		return;
	}

	/* we are not reusing the buffer ... */
	...

but saves 1 indent level and generally more readable.

> +
> +/**
> + * iecm_rx_bump_ntc - Bump and wrap q->next_to_clean value
> + * @q: queue to bump
> + */
> +void iecm_rx_bump_ntc(struct iecm_queue *q)
> +{
> +	u16 ntc = q->next_to_clean + 1;

There should be an empty line (between variable declarations and
function body). checkpatch should've mentioned this.

> +	/* fetch, update, and store next to clean */
> +	if (ntc < q->desc_count) {
> +		q->next_to_clean = ntc;
> +	} else {
> +		q->next_to_clean = 0;
> +		change_bit(__IECM_Q_GEN_CHK, q->flags);
> +	}
> +}
> +
> +/**
> + * iecm_rx_splitq_clean - Clean completed descriptors from Rx queue
> + * @rxq: Rx descriptor queue to retrieve receive buffer queue
> + * @budget: Total limit on number of packets to process
> + *
> + * This function provides a "bounce buffer" approach to Rx interrupt
> + * processing. The advantage to this is that on systems that have
> + * expensive overhead for IOMMU access this provides a means of avoiding
> + * it by maintaining the mapping of the page to the system.
> + *
> + * Returns amount of work completed
> + */
> +static int iecm_rx_splitq_clean(struct iecm_queue *rxq, int budget)
> +{
> +	int total_rx_bytes = 0, total_rx_pkts = 0;
> +	struct iecm_queue *rx_bufq = NULL;
> +	struct sk_buff *skb = rxq->skb;
> +	bool failure = false;
> +
> +	/* Process Rx packets bounded by budget */
> +	while (likely(total_rx_pkts < budget)) {
> +		struct virtchnl2_rx_flex_desc_adv_nic_3 *splitq_flex_rx_desc;
> +		struct iecm_sw_queue *refillq = NULL;
> +		struct iecm_dma_mem *hdr_buf = NULL;
> +		struct iecm_rxq_set *rxq_set = NULL;
> +		struct iecm_rx_buf *rx_buf = NULL;
> +		u16 gen_id, buf_id, vlan_tag = 0;
> +		union virtchnl2_rx_desc *rx_desc;
> +		unsigned int pkt_len = 0;
> +		unsigned int hdr_len = 0;
> +		 /* Header buffer overflow only valid for header split */
> +		bool hbo = false;
> +		int bufq_id;
> +
> +		/* get the Rx desc from Rx queue based on 'next_to_clean' */
> +		rx_desc = IECM_RX_DESC(rxq, rxq->next_to_clean);
> +		splitq_flex_rx_desc = (struct virtchnl2_rx_flex_desc_adv_nic_3 *)rx_desc;
> +
> +		/* This memory barrier is needed to keep us from reading
> +		 * any other fields out of the rx_desc
> +		 */
> +		dma_rmb();
> +
> +		/* if the descriptor isn't done, no work yet to do */
> +		gen_id = le16_to_cpu(splitq_flex_rx_desc->pktlen_gen_bufq_id);
> +		gen_id = (gen_id & VIRTCHNL2_RX_FLEX_DESC_ADV_GEN_M) >>
> +						VIRTCHNL2_RX_FLEX_DESC_ADV_GEN_S;
> +		if (test_bit(__IECM_Q_GEN_CHK, rxq->flags) != gen_id)
> +			break;
> +
> +		if ((splitq_flex_rx_desc->rxdid_ucast &
> +		    VIRTCHNL2_RX_FLEX_DESC_ADV_RXDID_M) != VIRTCHNL2_RXDID_1_FLEX_SPLITQ) {
> +			iecm_rx_bump_ntc(rxq);
> +			rxq->vport->port_stats.rx_bad_descs++;
> +			continue;
> +		}
> +
> +		pkt_len = le16_to_cpu(splitq_flex_rx_desc->pktlen_gen_bufq_id) &
> +			  VIRTCHNL2_RX_FLEX_DESC_ADV_LEN_PBUF_M;
> +
> +		hbo = splitq_flex_rx_desc->status_err0_qw1 &
> +		      BIT(VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_HBO_S);
> +
> +		if (unlikely(hbo)) {
> +			rxq->vport->port_stats.rx_hsplit_hbo++;
> +			goto bypass_hsplit;
> +		}
> +
> +		hdr_len =
> +			le16_to_cpu(splitq_flex_rx_desc->hdrlen_flags) &
> +			VIRTCHNL2_RX_FLEX_DESC_ADV_LEN_HDR_M;
> +
> +bypass_hsplit:
> +		bufq_id = le16_to_cpu(splitq_flex_rx_desc->pktlen_gen_bufq_id);
> +		bufq_id = (bufq_id & VIRTCHNL2_RX_FLEX_DESC_ADV_BUFQ_ID_M) >>
> +			  VIRTCHNL2_RX_FLEX_DESC_ADV_BUFQ_ID_S;
> +
> +		rxq_set = container_of(rxq, struct iecm_rxq_set, rxq);
> +		if (!bufq_id)
> +			refillq = rxq_set->refillq0;
> +		else
> +			refillq = rxq_set->refillq1;
> +
> +		/* retrieve buffer from the rxq */
> +		rx_bufq = &rxq->rxq_grp->splitq.bufq_sets[bufq_id].bufq;
> +
> +		buf_id = le16_to_cpu(splitq_flex_rx_desc->buf_id);
> +
> +		if (pkt_len) {
> +			rx_buf = &rx_bufq->rx_buf.buf[buf_id];
> +			iecm_rx_get_buf_page(rx_bufq->dev, rx_buf, pkt_len);
> +		}
> +
> +		if (hdr_len) {
> +			hdr_buf = rx_bufq->rx_buf.hdr_buf[buf_id];
> +
> +			dma_sync_single_for_cpu(rxq->dev, hdr_buf->pa, hdr_buf->size,
> +						DMA_FROM_DEVICE);
> +
> +			skb = iecm_rx_hdr_construct_skb(rxq, hdr_buf, hdr_len);
> +			rxq->vport->port_stats.rx_hsplit++;
> +		}
> +
> +		if (pkt_len) {
> +			if (skb)
> +				iecm_rx_add_frag(rx_buf, skb, pkt_len);
> +			else
> +				skb = iecm_rx_construct_skb(rxq, rx_buf,
> +							    pkt_len);
> +		}
> +
> +		/* exit if we failed to retrieve a buffer */
> +		if (!skb) {
> +			/* If we fetched a buffer, but didn't use it
> +			 * undo pagecnt_bias decrement
> +			 */
> +			if (rx_buf)
> +				rx_buf->page_info[rx_buf->page_indx].pagecnt_bias++;

Line of 85 cols here.

> +			break;
> +		}
> +
> +		if (rx_buf)
> +			iecm_rx_splitq_recycle_buf(rx_bufq, rx_buf);
> +		iecm_rx_post_buf_refill(refillq, buf_id);
> +
> +		iecm_rx_bump_ntc(rxq);
> +		/* skip if it is non EOP desc */
> +		if (iecm_rx_splitq_is_non_eop(splitq_flex_rx_desc))
> +			continue;
> +
> +		/* extract vlan tag from the descriptor */
> +		if (unlikely(iecm_rx_splitq_extract_vlan_tag(splitq_flex_rx_desc,
> +							     rxq, &vlan_tag))) {

81 and 81 respectively.

> +			dev_kfree_skb_any(skb);
> +			skb = NULL;
> +			continue;
> +		}
> +
> +		/* pad skb if needed (to make valid ethernet frame) */
> +		if (eth_skb_pad(skb)) {
> +			skb = NULL;
> +			continue;
> +		}
> +
> +		/* probably a little skewed due to removing CRC */
> +		total_rx_bytes += skb->len;
> +
> +		/* protocol */
> +		if (unlikely(iecm_rx_process_skb_fields(rxq, skb,
> +							splitq_flex_rx_desc))) {

You can define variables with shorter names to avoid this.

> +			dev_kfree_skb_any(skb);
> +			skb = NULL;
> +			continue;
> +		}
> +
> +		/* send completed skb up the stack */
> +		iecm_rx_skb(rxq, skb, vlan_tag);
> +		skb = NULL;
> +
> +		/* update budget accounting */
> +		total_rx_pkts++;
> +	}
> +	rxq->skb = skb;
> +	u64_stats_update_begin(&rxq->stats_sync);
> +	rxq->q_stats.rx.packets += total_rx_pkts;
> +	rxq->q_stats.rx.bytes += total_rx_bytes;
> +	u64_stats_update_end(&rxq->stats_sync);
> +
> +	/* guarantee a trip back through this routine if there was a failure */
> +	return unlikely(failure) ? budget : total_rx_pkts;
> +}
> +
> +/**
> + * iecm_rx_update_bufq_desc - Update buffer queue descriptor
> + * @bufq: Pointer to the buffer queue
> + * @desc: Refill queue descriptor
> + * @buf_desc: Buffer queue descriptor
> + *
> + * Return 0 on success and negative on failure.
> + */
> +static int iecm_rx_update_bufq_desc(struct iecm_queue *bufq, u16 *desc,
> +				    struct virtchnl2_splitq_rx_buf_desc *buf_desc)
> +{
> +	struct iecm_page_info *page_info;
> +	struct iecm_rx_buf *buf;
> +	u16 buf_id;
> +
> +	buf_id = ((*desc) & IECM_RX_BI_BUFID_M) >> IECM_RX_BI_BUFID_S;

FIELD_GET().

> +
> +	buf = &bufq->rx_buf.buf[buf_id];
> +	page_info = &buf->page_info[buf->page_indx];
> +
> +	/* It's possible page alloc failed during rxq clean, try to
> +	 * recover here.
> +	 */
> +	if (unlikely(!page_info->page)) {
> +		if (iecm_alloc_page(bufq, page_info))
> +			return -ENOMEM;
> +	}

if (1 && 2).

> +	buf_desc->pkt_addr = cpu_to_le64(page_info->dma + page_info->page_offset);
> +	buf_desc->qword0.buf_id = cpu_to_le16(buf_id);
> +
> +	if (bufq->rx_hsplit_en) {
> +		struct iecm_dma_mem *hdr_buf = bufq->rx_buf.hdr_buf[buf_id];
> +
> +		buf_desc->hdr_addr = cpu_to_le64(hdr_buf->pa);
> +		dma_sync_single_for_device(bufq->dev, hdr_buf->pa,
> +					   hdr_buf->size, DMA_FROM_DEVICE);
> +	}

	if (!hsplit_en)
		return 0;

	hdr_buf = ...

> +
> +	return 0;
> +}
> +
> +/**
> + * iecm_rx_clean_refillq - Clean refill queue buffers
> + * @bufq: buffer queue to post buffers back to
> + * @refillq: refill queue to clean
> + *
> + * This function takes care of the buffer refill management
> + */
> +static void iecm_rx_clean_refillq(struct iecm_queue *bufq,
> +				  struct iecm_sw_queue *refillq)
> +{
> +	struct virtchnl2_splitq_rx_buf_desc *buf_desc;
> +	u16 refillq_ntc = refillq->next_to_clean;
> +	u16 bufq_nta = bufq->next_to_alloc;
> +	u16 *refill_desc;
> +	int cleaned = 0;
> +	u16 gen;
> +
> +	refill_desc = IECM_SPLITQ_RX_BI_DESC(refillq, refillq_ntc);
> +	buf_desc = IECM_SPLITQ_RX_BUF_DESC(bufq, bufq_nta);
> +
> +	/* make sure we stop at ring wrap in the unlikely case ring is full */
> +	while (likely(cleaned < refillq->desc_count)) {
> +		bool failure;
> +
> +		gen = ((*refill_desc) & IECM_RX_BI_GEN_M) >> IECM_RX_BI_GEN_S;

FIELD_GET().

> +		if (test_bit(__IECM_RFLQ_GEN_CHK, refillq->flags) != gen)
> +			break;
> +
> +		failure = iecm_rx_update_bufq_desc(bufq, refill_desc,
> +						   buf_desc);
> +		if (failure)
> +			break;
> +
> +		refillq_ntc++;
> +		refill_desc++;
> +		bufq_nta++;
> +		buf_desc++;
> +		cleaned++;
> +
> +		if (unlikely(refillq_ntc == refillq->desc_count)) {
> +			change_bit(__IECM_RFLQ_GEN_CHK, refillq->flags);
> +			refill_desc = IECM_SPLITQ_RX_BI_DESC(refillq, 0);
> +			refillq_ntc = 0;
> +		}
> +		if (unlikely(bufq_nta == bufq->desc_count)) {
> +			buf_desc = IECM_SPLITQ_RX_BUF_DESC(bufq, 0);
> +			bufq_nta = 0;
> +		}
> +	}
> +
> +	if (cleaned) {
> +		/* only update hardware tail in strides */
> +		if (((bufq->next_to_use <= bufq_nta ? 0 : bufq->desc_count) +
> +		    bufq_nta - bufq->next_to_use) >= bufq->rx_buf_stride)
> +			iecm_rx_buf_hw_update(bufq, bufq_nta & ~(bufq->rx_buf_stride - 1));

92 chars.

> +
> +		/* update next to alloc since we have filled the ring */
> +		refillq->next_to_clean = refillq_ntc;
> +		bufq->next_to_alloc = bufq_nta;
> +	}
> +}
> +
> +/**
> + * iecm_rx_clean_refillq_all - Clean all refill queues
> + * @bufq: bufq with refillqs to clean
> + *
> + * Iterates through all refill queues assigned to the buffer queue assigned to
> + * this vector.  Returns true if clean is complete within budget, false
> + * otherwise.
> + */
> +static void iecm_rx_clean_refillq_all(struct iecm_queue *bufq)
> +{
> +	struct iecm_bufq_set *bufq_set;
> +	int i = 0;
> +
> +	bufq_set = container_of(bufq, struct iecm_bufq_set, bufq);
> +	for (i = 0; i < bufq_set->num_refillqs; i++)
> +		iecm_rx_clean_refillq(bufq, &bufq_set->refillqs[i]);
> +}
> +
>  /**
>   * iecm_vport_intr_clean_queues - MSIX mode Interrupt Handler
>   * @irq: interrupt number
> @@ -2742,6 +4122,64 @@ iecm_vport_intr_napi_ena_all(struct iecm_vport *vport)
>  	}
>  }
>  
> +/**
> + * iecm_tx_splitq_clean_all- Clean completetion queues
> + * @q_vec: queue vector
> + * @budget: Used to determine if we are in netpoll
> + *
> + * Returns false if clean is not complete else returns true
> + */
> +static bool
> +iecm_tx_splitq_clean_all(struct iecm_q_vector *q_vec, int budget)
> +{
> +	bool clean_complete = true;
> +	int i, budget_per_q;
> +
> +	budget_per_q = max(budget / q_vec->num_txq, 1);
> +	for (i = 0; i < q_vec->num_txq; i++) {
> +		if (!iecm_tx_clean_complq(q_vec->tx[i], budget_per_q))
> +			clean_complete = false;
> +	}
> +	return clean_complete;
> +}
> +
> +/**
> + * iecm_rx_splitq_clean_all- Clean completetion queues
> + * @q_vec: queue vector
> + * @budget: Used to determine if we are in netpoll
> + * @cleaned: returns number of packets cleaned
> + *
> + * Returns false if clean is not complete else returns true
> + */
> +static bool
> +iecm_rx_splitq_clean_all(struct iecm_q_vector *q_vec, int budget,
> +			 int *cleaned)
> +{
> +	bool clean_complete = true;
> +	int pkts_cleaned = 0;
> +	int i, budget_per_q;
> +
> +	budget_per_q = max(budget / q_vec->num_rxq, 1);
> +	for (i = 0; i < q_vec->num_rxq; i++) {
> +		struct iecm_queue *rxq = q_vec->rx[i];
> +		int pkts_cleaned_per_q;
> +
> +		pkts_cleaned_per_q = iecm_rx_splitq_clean(rxq, budget_per_q);
> +		/* if we clean as many as budgeted, we must not
> +		 * be done
> +		 */
> +		if (pkts_cleaned_per_q >= budget_per_q)
> +			clean_complete = false;
> +		pkts_cleaned += pkts_cleaned_per_q;
> +	}
> +	*cleaned = pkts_cleaned;
> +
> +	for (i = 0; i < q_vec->num_bufq; i++)
> +		iecm_rx_clean_refillq_all(q_vec->bufq[i]);
> +
> +	return clean_complete;
> +}
> +
>  /**
>   * iecm_vport_splitq_napi_poll - NAPI handler
>   * @napi: struct from which you get q_vector
> @@ -2749,8 +4187,34 @@ iecm_vport_intr_napi_ena_all(struct iecm_vport *vport)
>   */
>  static int iecm_vport_splitq_napi_poll(struct napi_struct *napi, int budget)
>  {
> -	/* stub */
> -	return 0;
> +	struct iecm_q_vector *q_vector =
> +				container_of(napi, struct iecm_q_vector, napi);

You can assign it later (before clean_complete assignment) to avoid
this.

> +	bool clean_complete;
> +	int work_done = 0;
> +
> +	clean_complete = iecm_tx_splitq_clean_all(q_vector, budget);
> +
> +	/* Handle case where we are called by netpoll with a budget of 0 */
> +	if (budget <= 0)
> +		return budget;
> +
> +	/* We attempt to distribute budget to each Rx queue fairly, but don't
> +	 * allow the budget to go below 1 because that would exit polling early.
> +	 */
> +	clean_complete |= iecm_rx_splitq_clean_all(q_vector, budget,
> +						   &work_done);
> +
> +	/* If work not completed, return budget and polling will return */
> +	if (!clean_complete)
> +		return budget;
> +
> +	/* Exit the polling mode, but don't re-enable interrupts if stack might
> +	 * poll us due to busy-polling
> +	 */
> +	if (likely(napi_complete_done(napi, work_done)))
> +		iecm_vport_intr_update_itr_ena_irq(q_vector);
> +
> +	return min_t(int, work_done, budget - 1);
>  }
>  
>  /**
> diff --git a/drivers/net/ethernet/intel/include/iecm.h b/drivers/net/ethernet/intel/include/iecm.h
> index a655e797f457..3cf2a2f0cb0f 100644
> --- a/drivers/net/ethernet/intel/include/iecm.h
> +++ b/drivers/net/ethernet/intel/include/iecm.h
> @@ -12,6 +12,10 @@
>  #include <linux/etherdevice.h>
>  #include <linux/ethtool.h>
>  #include <net/tcp.h>
> +#include <net/ip6_checksum.h>
> +#include <net/ipv6.h>
> +#include <net/sch_generic.h>
> +#include <net/gro.h>
>  #include <linux/version.h>
>  #include <linux/dim.h>
>  
> diff --git a/drivers/net/ethernet/intel/include/iecm_txrx.h b/drivers/net/ethernet/intel/include/iecm_txrx.h
> index 7ec742fd4c6b..086b0efc989a 100644
> --- a/drivers/net/ethernet/intel/include/iecm_txrx.h
> +++ b/drivers/net/ethernet/intel/include/iecm_txrx.h
> @@ -638,6 +638,7 @@ void iecm_vport_calc_total_qs(struct iecm_adapter *adapter,
>  			      struct virtchnl2_create_vport *vport_msg);
>  void iecm_vport_calc_num_q_groups(struct iecm_vport *vport);
>  int iecm_vport_queues_alloc(struct iecm_vport *vport);
> +void iecm_rx_post_buf_refill(struct iecm_sw_queue *refillq, u16 buf_id);
>  void iecm_vport_queues_rel(struct iecm_vport *vport);
>  void iecm_vport_calc_num_q_vec(struct iecm_vport *vport);
>  void iecm_vport_intr_rel(struct iecm_vport *vport);
> @@ -650,14 +651,33 @@ int iecm_vport_intr_init(struct iecm_vport *vport);
>  irqreturn_t
>  iecm_vport_intr_clean_queues(int __always_unused irq, void *data);
>  void iecm_vport_intr_ena_irq_all(struct iecm_vport *vport);
> +enum
> +pkt_hash_types iecm_ptype_to_htype(struct iecm_rx_ptype_decoded *decoded);
>  int iecm_config_rss(struct iecm_vport *vport);
>  void iecm_fill_dflt_rss_lut(struct iecm_vport *vport);
>  int iecm_init_rss(struct iecm_vport *vport);
>  void iecm_deinit_rss(struct iecm_vport *vport);
> +bool iecm_rx_can_reuse_page(struct iecm_rx_buf *rx_buf);
> +void iecm_rx_buf_adjust_pg(struct iecm_rx_buf *rx_buf, unsigned int size);
> +void iecm_rx_add_frag(struct iecm_rx_buf *rx_buf, struct sk_buff *skb,
> +		      unsigned int size);
> +struct sk_buff *iecm_rx_construct_skb(struct iecm_queue *rxq,
> +				      struct iecm_rx_buf *rx_buf,
> +				      unsigned int size);
> +void iecm_rx_skb(struct iecm_queue *rxq, struct sk_buff *skb, u16 vlan_tag);
>  bool iecm_init_rx_buf_hw_alloc(struct iecm_queue *rxq, struct iecm_rx_buf *buf);
>  void iecm_rx_buf_hw_update(struct iecm_queue *rxq, u32 val);
>  void iecm_tx_buf_hw_update(struct iecm_queue *tx_q, u32 val,
>  			   bool xmit_more);
> +void iecm_rx_splitq_put_bufs(struct iecm_queue *rx_bufq,
> +			     struct iecm_rx_buf *hdr_buf,
> +			     struct iecm_rx_buf *rx_buf);
> +bool iecm_rx_splitq_test_staterr(u8 stat_err_field, const u8 stat_err_bits);
> +int iecm_rx_process_skb_fields(struct iecm_queue *rxq, struct sk_buff *skb,
> +			       struct virtchnl2_rx_flex_desc_adv_nic_3 *rx_desc);
> +bool iecm_rx_splitq_extract_vlan_tag(struct virtchnl2_rx_flex_desc_adv_nic_3 *desc,
> +				     struct iecm_queue *rxq, u16 *vlan_tag);
> +void iecm_rx_bump_ntc(struct iecm_queue *q);
>  void iecm_tx_buf_rel(struct iecm_queue *tx_q, struct iecm_tx_buf *tx_buf);
>  unsigned int iecm_size_to_txd_count(unsigned int size);
>  unsigned int iecm_tx_desc_count_required(struct sk_buff *skb);
> -- 
> 2.33.0

Thanks,
Al

^ permalink raw reply	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 13/19] iecm: implement splitq napi_poll
  2022-01-28  5:21     ` kernel test robot
@ 2022-01-28 17:44       ` Alexander Lobakin
  -1 siblings, 0 replies; 89+ messages in thread
From: Alexander Lobakin @ 2022-01-28 17:44 UTC (permalink / raw)
  To: intel-wired-lan

From: kernel test robot <lkp@intel.com>
Date: Fri, 28 Jan 2022 13:21:42 +0800

> Hi Alan,
> 
> Thank you for the patch! Yet something to improve:
> 
> [auto build test ERROR on net-next/master]
> 
> url:    https://github.com/0day-ci/linux/commits/Alan-Brady/Add-iecm-and-idpf/20220128-085513
> base:   https://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next.git e2cf07654efb0fd7bbcb475c6f74be7b5755a8fd
> config: arc-allyesconfig (https://download.01.org/0day-ci/archive/20220128/202201281316.ZdiaZw6q-lkp at intel.com/config)
> compiler: arceb-elf-gcc (GCC) 11.2.0
> reproduce (this is a W=1 build):
>         wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
>         chmod +x ~/bin/make.cross
>         # https://github.com/0day-ci/linux/commit/8e9b2451747f81363327cf5a4e07aaf88af52397
>         git remote add linux-review https://github.com/0day-ci/linux
>         git fetch --no-tags linux-review Alan-Brady/Add-iecm-and-idpf/20220128-085513
>         git checkout 8e9b2451747f81363327cf5a4e07aaf88af52397
>         # save the config file to linux build tree
>         mkdir build_dir
>         COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0 make.cross O=build_dir ARCH=arc SHELL=/bin/bash
> 
> If you fix the issue, kindly add following tag as appropriate
> Reported-by: kernel test robot <lkp@intel.com>
> 
> All errors (new ones prefixed by >>):
> 
>    drivers/net/ethernet/intel/iecm/iecm_txrx.c: In function 'iecm_rx_can_reuse_page':
> >> drivers/net/ethernet/intel/iecm/iecm_txrx.c:3132:19: error: 'struct iecm_rx_buf' has no member named 'page_offset'
>     3132 |         if (rx_buf->page_offset > last_offset)
>          |                   ^~

So these series wasn't even compile-tested properly.
x86 has only 4k pages, but lots of other architectures have much
more. If ARC is not relevant enough to MEV, then I say that
ARM64 has 4-64 Kb pages, and PowerPC64 has up to 256 Kb.

> 
> 
> vim +3132 drivers/net/ethernet/intel/iecm/iecm_txrx.c
> 
>   3103	
>   3104	/**
>   3105	 * iecm_rx_can_reuse_page - Determine if page can be reused for another rx
>   3106	 * @rx_buf: buffer containing the page
>   3107	 *
>   3108	 * If page is reusable, we have a green light for calling iecm_reuse_rx_page,
>   3109	 * which will assign the current buffer to the buffer that next_to_alloc is
>   3110	 * pointing to; otherwise, the dma mapping needs to be destroyed and
>   3111	 * page freed
>   3112	 */
>   3113	bool iecm_rx_can_reuse_page(struct iecm_rx_buf *rx_buf)
>   3114	{
>   3115		struct iecm_page_info *page_info = &rx_buf->page_info[rx_buf->page_indx];
>   3116	
>   3117	#if (PAGE_SIZE >= 8192)
>   3118		unsigned int last_offset = PAGE_SIZE - rx_buf->buf_size;
>   3119	#endif /* PAGE_SIZE < 8192) */
>   3120		unsigned int pagecnt_bias = page_info->pagecnt_bias;
>   3121		struct page *page = page_info->page;
>   3122	
>   3123		/* avoid re-using remote pages */
>   3124		if (unlikely(iecm_rx_page_is_reserved(page)))
>   3125			return false;
>   3126	
>   3127	#if (PAGE_SIZE < 8192)
>   3128		/* if we are only owner of page we can reuse it */
>   3129		if (unlikely((page_count(page) - pagecnt_bias) > 1))
>   3130			return false;
>   3131	#else
> > 3132		if (rx_buf->page_offset > last_offset)
>   3133			return false;
>   3134	#endif /* PAGE_SIZE < 8192) */
>   3135	
>   3136		/* If we have drained the page fragment pool we need to update
>   3137		 * the pagecnt_bias and page count so that we fully restock the
>   3138		 * number of references the driver holds.
>   3139		 */
>   3140		if (unlikely(pagecnt_bias == 1)) {
>   3141			page_ref_add(page, USHRT_MAX - 1);
>   3142			page_info->pagecnt_bias = USHRT_MAX;
>   3143		}
>   3144	
>   3145		return true;
>   3146	}
>   3147	
> 
> ---
> 0-DAY CI Kernel Test Service, Intel Corporation
> https://lists.01.org/hyperkitty/list/kbuild-all at lists.01.org

Al

^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [Intel-wired-lan] [PATCH net-next 13/19] iecm: implement splitq napi_poll
@ 2022-01-28 17:44       ` Alexander Lobakin
  0 siblings, 0 replies; 89+ messages in thread
From: Alexander Lobakin @ 2022-01-28 17:44 UTC (permalink / raw)
  To: kbuild-all

[-- Attachment #1: Type: text/plain, Size: 4000 bytes --]

From: kernel test robot <lkp@intel.com>
Date: Fri, 28 Jan 2022 13:21:42 +0800

> Hi Alan,
> 
> Thank you for the patch! Yet something to improve:
> 
> [auto build test ERROR on net-next/master]
> 
> url:    https://github.com/0day-ci/linux/commits/Alan-Brady/Add-iecm-and-idpf/20220128-085513
> base:   https://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next.git e2cf07654efb0fd7bbcb475c6f74be7b5755a8fd
> config: arc-allyesconfig (https://download.01.org/0day-ci/archive/20220128/202201281316.ZdiaZw6q-lkp(a)intel.com/config)
> compiler: arceb-elf-gcc (GCC) 11.2.0
> reproduce (this is a W=1 build):
>         wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
>         chmod +x ~/bin/make.cross
>         # https://github.com/0day-ci/linux/commit/8e9b2451747f81363327cf5a4e07aaf88af52397
>         git remote add linux-review https://github.com/0day-ci/linux
>         git fetch --no-tags linux-review Alan-Brady/Add-iecm-and-idpf/20220128-085513
>         git checkout 8e9b2451747f81363327cf5a4e07aaf88af52397
>         # save the config file to linux build tree
>         mkdir build_dir
>         COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0 make.cross O=build_dir ARCH=arc SHELL=/bin/bash
> 
> If you fix the issue, kindly add following tag as appropriate
> Reported-by: kernel test robot <lkp@intel.com>
> 
> All errors (new ones prefixed by >>):
> 
>    drivers/net/ethernet/intel/iecm/iecm_txrx.c: In function 'iecm_rx_can_reuse_page':
> >> drivers/net/ethernet/intel/iecm/iecm_txrx.c:3132:19: error: 'struct iecm_rx_buf' has no member named 'page_offset'
>     3132 |         if (rx_buf->page_offset > last_offset)
>          |                   ^~

So these series wasn't even compile-tested properly.
x86 has only 4k pages, but lots of other architectures have much
more. If ARC is not relevant enough to MEV, then I say that
ARM64 has 4-64 Kb pages, and PowerPC64 has up to 256 Kb.

> 
> 
> vim +3132 drivers/net/ethernet/intel/iecm/iecm_txrx.c
> 
>   3103	
>   3104	/**
>   3105	 * iecm_rx_can_reuse_page - Determine if page can be reused for another rx
>   3106	 * @rx_buf: buffer containing the page
>   3107	 *
>   3108	 * If page is reusable, we have a green light for calling iecm_reuse_rx_page,
>   3109	 * which will assign the current buffer to the buffer that next_to_alloc is
>   3110	 * pointing to; otherwise, the dma mapping needs to be destroyed and
>   3111	 * page freed
>   3112	 */
>   3113	bool iecm_rx_can_reuse_page(struct iecm_rx_buf *rx_buf)
>   3114	{
>   3115		struct iecm_page_info *page_info = &rx_buf->page_info[rx_buf->page_indx];
>   3116	
>   3117	#if (PAGE_SIZE >= 8192)
>   3118		unsigned int last_offset = PAGE_SIZE - rx_buf->buf_size;
>   3119	#endif /* PAGE_SIZE < 8192) */
>   3120		unsigned int pagecnt_bias = page_info->pagecnt_bias;
>   3121		struct page *page = page_info->page;
>   3122	
>   3123		/* avoid re-using remote pages */
>   3124		if (unlikely(iecm_rx_page_is_reserved(page)))
>   3125			return false;
>   3126	
>   3127	#if (PAGE_SIZE < 8192)
>   3128		/* if we are only owner of page we can reuse it */
>   3129		if (unlikely((page_count(page) - pagecnt_bias) > 1))
>   3130			return false;
>   3131	#else
> > 3132		if (rx_buf->page_offset > last_offset)
>   3133			return false;
>   3134	#endif /* PAGE_SIZE < 8192) */
>   3135	
>   3136		/* If we have drained the page fragment pool we need to update
>   3137		 * the pagecnt_bias and page count so that we fully restock the
>   3138		 * number of references the driver holds.
>   3139		 */
>   3140		if (unlikely(pagecnt_bias == 1)) {
>   3141			page_ref_add(page, USHRT_MAX - 1);
>   3142			page_info->pagecnt_bias = USHRT_MAX;
>   3143		}
>   3144	
>   3145		return true;
>   3146	}
>   3147	
> 
> ---
> 0-DAY CI Kernel Test Service, Intel Corporation
> https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org

Al

^ permalink raw reply	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 14/19] iecm: implement singleq napi_poll
  2022-01-28  0:10 ` [Intel-wired-lan] [PATCH net-next 14/19] iecm: implement singleq napi_poll Alan Brady
@ 2022-01-28 17:57   ` Alexander Lobakin
  2022-02-03  1:45     ` Brady, Alan
  0 siblings, 1 reply; 89+ messages in thread
From: Alexander Lobakin @ 2022-01-28 17:57 UTC (permalink / raw)
  To: intel-wired-lan

> From: Alan Brady <alan.brady@intel.com>
> Date: Thu, 27 Jan 2022 16:10:04 -0800
> 
> This adds everything we do the more traditional singleq model data path.
> 
> Signed-off-by: Phani Burra <phani.r.burra@intel.com>
> Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
> Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
> Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
> Signed-off-by: Alan Brady <alan.brady@intel.com>
> ---
>  drivers/net/ethernet/intel/iecm/iecm_lib.c    |    2 +-
>  .../ethernet/intel/iecm/iecm_singleq_txrx.c   | 1208 ++++++++++++++++-
>  drivers/net/ethernet/intel/include/iecm.h     |    1 +
>  .../net/ethernet/intel/include/iecm_txrx.h    |   31 +
>  4 files changed, 1237 insertions(+), 5 deletions(-)
> 
> diff --git a/drivers/net/ethernet/intel/iecm/iecm_lib.c b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> index cc82e665dfaf..cbde65f1c523 100644
> --- a/drivers/net/ethernet/intel/iecm/iecm_lib.c
> +++ b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> @@ -2723,7 +2723,7 @@ static const struct net_device_ops iecm_netdev_ops_splitq = {
>  static const struct net_device_ops iecm_netdev_ops_singleq = {
>  	.ndo_open = iecm_open,
>  	.ndo_stop = iecm_stop,
> -	.ndo_start_xmit = NULL,
> +	.ndo_start_xmit = iecm_tx_singleq_start,
>  	.ndo_set_rx_mode = iecm_set_rx_mode,
>  	.ndo_validate_addr = eth_validate_addr,
>  	.ndo_set_mac_address = iecm_set_mac,
> diff --git a/drivers/net/ethernet/intel/iecm/iecm_singleq_txrx.c b/drivers/net/ethernet/intel/iecm/iecm_singleq_txrx.c
> index d6c47cb84249..7bfec79e6afc 100644
> --- a/drivers/net/ethernet/intel/iecm/iecm_singleq_txrx.c
> +++ b/drivers/net/ethernet/intel/iecm/iecm_singleq_txrx.c
> @@ -3,6 +3,779 @@
>  
>  #include "iecm.h"
>  
> +/**
> + * iecm_tx_singleq_csum - Enable tx checksum offloads
> + * @first: pointer to first descriptor
> + * @off: pointer to struct that holds offload parameters
> + *
> + * Returns 0 or error (negative) if checksum offload
> + */
> +static
> +int iecm_tx_singleq_csum(struct iecm_tx_buf *first,
> +			 struct iecm_tx_offload_params *off)
> +{
> +	u32 l4_len = 0, l3_len = 0, l2_len = 0;
> +	struct sk_buff *skb = first->skb;
> +	union {
> +		struct iphdr *v4;
> +		struct ipv6hdr *v6;
> +		unsigned char *hdr;
> +	} ip;
> +	union {
> +		struct tcphdr *tcp;
> +		unsigned char *hdr;
> +	} l4;
> +	__be16 frag_off, protocol;
> +	unsigned char *exthdr;
> +	u32 offset, cmd = 0;
> +	u8 l4_proto = 0;
> +
> +	if (skb->ip_summed != CHECKSUM_PARTIAL)
> +		return 0;
> +
> +	if (skb->encapsulation)
> +		return -1;
> +
> +	ip.hdr = skb_network_header(skb);
> +	l4.hdr = skb_transport_header(skb);
> +
> +	/* compute outer L2 header size */
> +	l2_len = ip.hdr - skb->data;
> +	offset = (l2_len / 2) << IECM_TX_DESC_LEN_MACLEN_S;
> +
> +	/* Enable IP checksum offloads */
> +	protocol = vlan_get_protocol(skb);
> +	if (protocol == htons(ETH_P_IP)) {
> +		l4_proto = ip.v4->protocol;
> +		/* the stack computes the IP header already, the only time we
> +		 * need the hardware to recompute it is in the case of TSO.
> +		 */
> +		if (first->tx_flags & IECM_TX_FLAGS_TSO)
> +			cmd |= IECM_TX_DESC_CMD_IIPT_IPV4_CSUM;
> +		else
> +			cmd |= IECM_TX_DESC_CMD_IIPT_IPV4;
> +
> +	} else if (protocol == htons(ETH_P_IPV6)) {
> +		cmd |= IECM_TX_DESC_CMD_IIPT_IPV6;
> +		exthdr = ip.hdr + sizeof(struct ipv6hdr);
> +		l4_proto = ip.v6->nexthdr;
> +		if (l4.hdr != exthdr)
> +			ipv6_skip_exthdr(skb, exthdr - skb->data, &l4_proto,
> +					 &frag_off);
> +	} else {
> +		return -1;
> +	}
> +
> +	/* compute inner L3 header size */
> +	l3_len = l4.hdr - ip.hdr;
> +	offset |= (l3_len / 4) << IECM_TX_DESC_LEN_IPLEN_S;
> +
> +	/* Enable L4 checksum offloads */
> +	switch (l4_proto) {
> +	case IPPROTO_TCP:
> +		/* enable checksum offloads */
> +		cmd |= IECM_TX_DESC_CMD_L4T_EOFT_TCP;
> +		l4_len = l4.tcp->doff;
> +		offset |= l4_len << IECM_TX_DESC_LEN_L4_LEN_S;
> +		break;
> +	case IPPROTO_UDP:
> +		/* enable UDP checksum offload */
> +		cmd |= IECM_TX_DESC_CMD_L4T_EOFT_UDP;
> +		l4_len = (sizeof(struct udphdr) >> 2);
> +		offset |= l4_len << IECM_TX_DESC_LEN_L4_LEN_S;
> +		break;
> +	case IPPROTO_SCTP:
> +		/* enable SCTP checksum offload */
> +		cmd |= IECM_TX_DESC_CMD_L4T_EOFT_SCTP;
> +		l4_len = sizeof(struct sctphdr) >> 2;
> +		offset |= l4_len << IECM_TX_DESC_LEN_L4_LEN_S;
> +		break;
> +
> +	default:
> +		if (first->tx_flags & IECM_TX_FLAGS_TSO)
> +			return -1;
> +		skb_checksum_help(skb);
> +		return 0;
> +	}
> +
> +	off->td_cmd |= cmd;
> +	off->hdr_offsets |= offset;
> +	return 1;
> +}
> +
> +/**
> + * iecm_tx_singleq_map - Build the Tx base descriptor
> + * @tx_q: queue to send buffer on
> + * @first: first buffer info buffer to use
> + * @offloads: pointer to struct that holds offload parameters
> + *
> + * This function loops over the skb data pointed to by *first
> + * and gets a physical address for each memory location and programs
> + * it and the length into the transmit base mode descriptor.
> + */
> +static void
> +iecm_tx_singleq_map(struct iecm_queue *tx_q, struct iecm_tx_buf *first,
> +		    struct iecm_tx_offload_params *offloads)
> +{
> +	u32 offsets = offloads->hdr_offsets;
> +	struct iecm_base_tx_desc *tx_desc;
> +	u64 td_cmd = offloads->td_cmd;
> +	unsigned int data_len, size;
> +	struct iecm_tx_buf *tx_buf;
> +	u16 i = tx_q->next_to_use;
> +	struct netdev_queue *nq;
> +	struct sk_buff *skb;
> +	skb_frag_t *frag;
> +	dma_addr_t dma;
> +	u64 td_tag = 0;
> +
> +	skb = first->skb;
> +
> +	data_len = skb->data_len;
> +	size = skb_headlen(skb);
> +
> +	tx_desc = IECM_BASE_TX_DESC(tx_q, i);
> +
> +	if (first->tx_flags & IECM_TX_FLAGS_VLAN_TAG) {
> +		td_cmd |= (u64)IECM_TX_DESC_CMD_IL2TAG1;
> +		td_tag = (first->tx_flags & IECM_TX_FLAGS_VLAN_MASK) >>
> +			 IECM_TX_FLAGS_VLAN_SHIFT;
> +	}
> +
> +	dma = dma_map_single(tx_q->dev, skb->data, size, DMA_TO_DEVICE);
> +
> +	tx_buf = first;
> +
> +	/* write each descriptor with CRC bit */
> +	if (tx_q->vport->adapter->dev_ops.crc_enable)
> +		tx_q->vport->adapter->dev_ops.crc_enable(&td_cmd);
> +
> +	for (frag = &skb_shinfo(skb)->frags[0];; frag++) {
> +		unsigned int max_data = IECM_TX_MAX_DESC_DATA_ALIGNED;
> +
> +		if (dma_mapping_error(tx_q->dev, dma))
> +			goto dma_error;
> +
> +		/* record length, and DMA address */
> +		dma_unmap_len_set(tx_buf, len, size);
> +		dma_unmap_addr_set(tx_buf, dma, dma);
> +
> +		/* align size to end of page */
> +		max_data += -dma & (IECM_TX_MAX_READ_REQ_SIZE - 1);

Here applies the same I said for splitq before, this code is
counter-intuitive.

> +		tx_desc->buf_addr = cpu_to_le64(dma);
> +
> +		/* account for data chunks larger than the hardware
> +		 * can handle
> +		 */
> +		while (unlikely(size > IECM_TX_MAX_DESC_DATA)) {
> +			tx_desc->qw1 = iecm_tx_singleq_build_ctob(td_cmd,
> +								  offsets,
> +								  max_data,
> +								  td_tag);
> +			tx_desc++;
> +			i++;
> +
> +			if (i == tx_q->desc_count) {
> +				tx_desc = IECM_BASE_TX_DESC(tx_q, 0);
> +				i = 0;
> +			}
> +
> +			dma += max_data;
> +			size -= max_data;
> +
> +			max_data = IECM_TX_MAX_DESC_DATA_ALIGNED;
> +			tx_desc->buf_addr = cpu_to_le64(dma);
> +		}
> +
> +		if (likely(!data_len))
> +			break;
> +		tx_desc->qw1 = iecm_tx_singleq_build_ctob(td_cmd, offsets,
> +							  size, td_tag);
> +		tx_desc++;
> +		i++;
> +
> +		if (i == tx_q->desc_count) {
> +			tx_desc = IECM_BASE_TX_DESC(tx_q, 0);
> +			i = 0;
> +		}
> +
> +		size = skb_frag_size(frag);
> +		data_len -= size;
> +
> +		dma = skb_frag_dma_map(tx_q->dev, frag, 0, size,
> +				       DMA_TO_DEVICE);
> +
> +		tx_buf = &tx_q->tx_buf[i];
> +	}
> +
> +	/* record bytecount for BQL */
> +	nq = netdev_get_tx_queue(tx_q->vport->netdev, tx_q->idx);
> +	netdev_tx_sent_queue(nq, first->bytecount);
> +
> +	/* record SW timestamp if HW timestamp is not available */
> +	skb_tx_timestamp(first->skb);
> +
> +	/* write last descriptor with RS and EOP bits */
> +	td_cmd |= (u64)(IECM_TX_DESC_CMD_EOP | IECM_TX_DESC_CMD_RS);
> +
> +	tx_desc->qw1 = iecm_tx_singleq_build_ctob(td_cmd, offsets, size, td_tag);
> +
> +	i++;
> +	if (i == tx_q->desc_count)
> +		i = 0;
> +
> +	/* set next_to_watch value indicating a packet is present */
> +	first->next_to_watch = tx_desc;
> +
> +	iecm_tx_buf_hw_update(tx_q, i, netdev_xmit_more());
> +
> +	return;
> +
> +dma_error:
> +	/* clear dma mappings for failed tx_buf map */
> +	for (;;) {
> +		tx_buf = &tx_q->tx_buf[i];
> +		iecm_tx_buf_rel(tx_q, tx_buf);
> +		if (tx_buf == first)
> +			break;
> +		if (i == 0)
> +			i = tx_q->desc_count;
> +		i--;
> +	}
> +
> +	tx_q->next_to_use = i;
> +}
> +
> +/**
> + * iecm_tx_singleq_frame - Sends buffer on Tx ring using base descriptors
> + * @skb: send buffer
> + * @tx_q: queue to send buffer on
> + *
> + * Returns NETDEV_TX_OK if sent, else an error code
> + */
> +static netdev_tx_t
> +iecm_tx_singleq_frame(struct sk_buff *skb, struct iecm_queue *tx_q)
> +{
> +	struct iecm_tx_offload_params offload = {0};
> +	struct iecm_tx_buf *first;
> +	unsigned int count;
> +	int csum, tso;
> +
> +	count = iecm_tx_desc_count_required(skb);
> +
> +	if (iecm_chk_linearize(skb, tx_q->tx_max_bufs, count)) {
> +		if (__skb_linearize(skb)) {
> +			dev_kfree_skb_any(skb);
> +			return NETDEV_TX_OK;
> +		}
> +		count = iecm_size_to_txd_count(skb->len);
> +		tx_q->vport->port_stats.tx_linearize++;
> +	}
> +
> +	if (iecm_tx_maybe_stop(tx_q, count + IECM_TX_DESCS_PER_CACHE_LINE +
> +			       IECM_TX_DESCS_FOR_CTX)) {
> +		return NETDEV_TX_BUSY;
> +	}
> +
> +	/* record the location of the first descriptor for this packet */
> +	first = &tx_q->tx_buf[tx_q->next_to_use];
> +	first->skb = skb;
> +	first->bytecount = max_t(unsigned int, skb->len, ETH_ZLEN);
> +	first->gso_segs = 1;
> +	first->tx_flags = 0;
> +
> +	iecm_tx_prepare_vlan_flags(tx_q, first, skb);
> +
> +	tso = iecm_tso(first, &offload);
> +	if (tso < 0)
> +		goto out_drop;
> +
> +	csum = iecm_tx_singleq_csum(first, &offload);
> +	if (csum < 0)
> +		goto out_drop;
> +
> +	if (first->tx_flags & IECM_TX_FLAGS_TSO) {
> +		struct iecm_base_tx_ctx_desc *ctx_desc;
> +		int i = tx_q->next_to_use;
> +		u64 qw1 = (u64)IECM_TX_DESC_DTYPE_CTX |
> +			       IECM_TX_CTX_DESC_TSO << IECM_TXD_CTX_QW1_CMD_S;
> +
> +		/* grab the next descriptor */
> +		ctx_desc = IECM_BASE_TX_CTX_DESC(tx_q, i);
> +		i++;
> +		tx_q->next_to_use = (i < tx_q->desc_count) ? i : 0;
> +
> +		qw1 |= ((u64)offload.tso_len << IECM_TXD_CTX_QW1_TSO_LEN_S) &
> +			IECM_TXD_CTX_QW1_TSO_LEN_M;
> +
> +		qw1 |= ((u64)offload.mss << IECM_TXD_CTX_QW1_MSS_S) &
> +			IECM_TXD_CTX_QW1_MSS_M;
> +
> +		ctx_desc->qw0.rsvd0 = cpu_to_le32(0);
> +		ctx_desc->qw0.l2tag2 = cpu_to_le16(0);
> +		ctx_desc->qw0.rsvd1 = cpu_to_le16(0);
> +		ctx_desc->qw1 = cpu_to_le64(qw1);
> +	}
> +
> +	iecm_tx_singleq_map(tx_q, first, &offload);
> +
> +	return NETDEV_TX_OK;
> +
> +out_drop:
> +	dev_kfree_skb_any(skb);
> +	return NETDEV_TX_OK;
> +}
> +
> +/**
> + * iecm_tx_singleq_start - Selects the right Tx queue to send buffer
> + * @skb: send buffer
> + * @netdev: network interface device structure
> + *
> + * Returns NETDEV_TX_OK if sent, else an error code
> + */
> +netdev_tx_t iecm_tx_singleq_start(struct sk_buff *skb,
> +				  struct net_device *netdev)
> +{
> +	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
> +	struct iecm_queue *tx_q;
> +
> +	if (test_bit(__IECM_HR_RESET_IN_PROG, vport->adapter->flags))
> +		return NETDEV_TX_BUSY;
> +
> +	tx_q = vport->txqs[skb->queue_mapping];
> +
> +	/* hardware can't handle really short frames, hardware padding works
> +	 * beyond this point
> +	 */
> +	if (skb_put_padto(skb, IECM_TX_MIN_LEN))
> +		return NETDEV_TX_OK;
> +
> +	return iecm_tx_singleq_frame(skb, tx_q);
> +}
> +
> +/**
> + * iecm_tx_singleq_clean - Reclaim resources from queue
> + * @tx_q: Tx queue to clean
> + * @napi_budget: Used to determine if we are in netpoll
> + *
> + */
> +static bool iecm_tx_singleq_clean(struct iecm_queue *tx_q, int napi_budget)
> +{
> +	unsigned int budget = tx_q->vport->compln_clean_budget;
> +	unsigned int total_bytes = 0, total_pkts = 0;
> +	struct iecm_base_tx_desc *tx_desc;
> +	s16 ntc = tx_q->next_to_clean;
> +	struct iecm_tx_buf *tx_buf;
> +	struct netdev_queue *nq;
> +
> +	tx_desc = IECM_BASE_TX_DESC(tx_q, ntc);
> +	tx_buf = &tx_q->tx_buf[ntc];
> +	ntc -= tx_q->desc_count;
> +
> +	do {
> +		struct iecm_base_tx_desc *eop_desc =
> +			(struct iecm_base_tx_desc *)tx_buf->next_to_watch;

		struct iecm_base_tx_desc *eop_desc;

		eop_desc = (typeof(*eop_desc))tx_buf->next_to_watch;

> +
> +		/* if next_to_watch is not set then no work pending */
> +		if (!eop_desc)
> +			break;
> +
> +		/* prevent any other reads prior to eop_desc */
> +		smp_rmb();
> +
> +		/* if the descriptor isn't done, no work yet to do */
> +		if (!(eop_desc->qw1 &
> +		      cpu_to_le64(IECM_TX_DESC_DTYPE_DESC_DONE)))
> +			break;
> +
> +		/* clear next_to_watch to prevent false hangs */
> +		tx_buf->next_to_watch = NULL;
> +
> +		/* update the statistics for this packet */
> +		total_bytes += tx_buf->bytecount;
> +		total_pkts += tx_buf->gso_segs;
> +
> +		/* free the skb */
> +		napi_consume_skb(tx_buf->skb, napi_budget);
> +
> +		/* unmap skb header data */
> +		dma_unmap_single(tx_q->dev,
> +				 dma_unmap_addr(tx_buf, dma),
> +				 dma_unmap_len(tx_buf, len),
> +				 DMA_TO_DEVICE);
> +
> +		/* clear tx_buf data */
> +		tx_buf->skb = NULL;
> +		dma_unmap_len_set(tx_buf, len, 0);
> +
> +		/* unmap remaining buffers */
> +		while (tx_desc != eop_desc) {
> +			tx_buf++;
> +			tx_desc++;
> +			ntc++;
> +			if (unlikely(!ntc)) {
> +				ntc -= tx_q->desc_count;
> +				tx_buf = tx_q->tx_buf;
> +				tx_desc = IECM_BASE_TX_DESC(tx_q, 0);
> +			}
> +
> +			/* unmap any remaining paged data */
> +			if (dma_unmap_len(tx_buf, len)) {

Here applies the same I said for splitq about dma_unmap_len().

> +				dma_unmap_page(tx_q->dev,
> +					       dma_unmap_addr(tx_buf, dma),
> +					       dma_unmap_len(tx_buf, len),
> +					       DMA_TO_DEVICE);
> +				dma_unmap_len_set(tx_buf, len, 0);
> +			}
> +		}
> +
> +		tx_buf++;
> +		tx_desc++;
> +		ntc++;
> +		if (unlikely(!ntc)) {
> +			ntc -= tx_q->desc_count;
> +			tx_buf = tx_q->tx_buf;
> +			tx_desc = IECM_BASE_TX_DESC(tx_q, 0);
> +		}
> +		/* update budget */
> +		budget--;
> +	} while (likely(budget));
> +
> +	ntc += tx_q->desc_count;
> +	tx_q->next_to_clean = ntc;
> +
> +	u64_stats_update_begin(&tx_q->stats_sync);
> +	tx_q->q_stats.tx.packets += total_pkts;
> +	tx_q->q_stats.tx.bytes += total_bytes;
> +	u64_stats_update_end(&tx_q->stats_sync);
> +
> +	nq = netdev_get_tx_queue(tx_q->vport->netdev, tx_q->idx);
> +	netdev_tx_completed_queue(nq, total_pkts, total_bytes);
> +
> +	if (unlikely(total_pkts && netif_carrier_ok(tx_q->vport->netdev) &&
> +		     (IECM_DESC_UNUSED(tx_q) >= IECM_TX_WAKE_THRESH))) {
> +		/* Make sure any other threads stopping queue after this see
> +		 * new next_to_clean.
> +		 */
> +		smp_mb();
> +		if (__netif_subqueue_stopped(tx_q->vport->netdev, tx_q->idx) &&
> +		    tx_q->vport->adapter->state == __IECM_UP)
> +			netif_wake_subqueue(tx_q->vport->netdev, tx_q->idx);
> +	}
> +
> +	return !!budget;
> +}
> +
> +/**
> + * iecm_tx_singleq_clean_all - Clean all Tx queues
> + * @q_vec: queue vector
> + * @budget: Used to determine if we are in netpoll
> + *
> + * Returns false if clean is not complete else returns true
> + */
> +static bool
> +iecm_tx_singleq_clean_all(struct iecm_q_vector *q_vec, int budget)
> +{
> +	bool clean_complete = true;
> +	int i, budget_per_q;
> +
> +	budget_per_q = max(budget / q_vec->num_txq, 1);
> +	for (i = 0; i < q_vec->num_txq; i++)
> +		clean_complete = iecm_tx_singleq_clean(q_vec->tx[i], budget_per_q);

84 cols per line.

> +
> +	return clean_complete;
> +}
> +
> +/**
> + * iecm_rx_singleq_test_staterr - tests bits in Rx descriptor
> + * status and error fields
> + * @rx_desc: pointer to receive descriptor (in le64 format)
> + * @stat_err_bits: value to mask
> + *
> + * This function does some fast chicanery in order to return the
> + * value of the mask which is really only used for boolean tests.
> + * The status_error_ptype_len doesn't need to be shifted because it begins
> + * at offset zero.
> + */
> +bool
> +iecm_rx_singleq_test_staterr(union virtchnl2_rx_desc *rx_desc,
> +			     const u64 stat_err_bits)
> +{
> +	return !!(rx_desc->base_wb.qword1.status_error_ptype_len &
> +		  cpu_to_le64(stat_err_bits));
> +}
> +
> +/**
> + * iecm_rx_singleq_is_non_eop - process handling of non-EOP buffers
> + * @rxq: Rx ring being processed
> + * @rx_desc: Rx descriptor for current buffer
> + * @skb: Current socket buffer containing buffer in progress
> + */
> +bool iecm_rx_singleq_is_non_eop(struct iecm_queue *rxq,
> +				union virtchnl2_rx_desc *rx_desc,
> +				struct sk_buff *skb)
> +{
> +	/* if we are the last buffer then there is nothing else to do */
> +#define IECM_RXD_EOF BIT(VIRTCHNL2_RX_BASE_DESC_STATUS_EOF_S)

Not a good place for a definition, makes reading difficult.

> +	if (likely(iecm_rx_singleq_test_staterr(rx_desc, IECM_RXD_EOF)))
> +		return false;
> +
> +	/* place skb in next buffer to be received */
> +	rxq->rx_buf.buf[rxq->next_to_clean].skb = skb;
> +
> +	return true;
> +}
> +
> +/**
> + * iecm_rx_singleq_csum - Indicate in skb if checksum is good
> + * @rxq: Rx descriptor ring packet is being transacted on
> + * @skb: skb currently being received and modified
> + * @csum_bits: descriptor csum bits
> + * @ptype: the packet type decoded by hardware
> + *
> + * skb->protocol must be set before this function is called
> + */
> +static void iecm_rx_singleq_csum(struct iecm_queue *rxq, struct sk_buff *skb,
> +				 struct iecm_rx_csum_decoded *csum_bits,
> +				 u16 ptype)
> +{
> +	struct iecm_rx_ptype_decoded decoded;
> +	bool ipv4, ipv6;
> +
> +	/* Start with CHECKSUM_NONE and by default csum_level = 0 */
> +	skb->ip_summed = CHECKSUM_NONE;
> +	skb_checksum_none_assert(skb);
> +
> +	/* check if Rx checksum is enabled */
> +	if (!(rxq->vport->netdev->features & NETIF_F_RXCSUM))
> +		return;
> +
> +	/* check if HW has decoded the packet and checksum */
> +	if (!(csum_bits->l3l4p))
> +		return;
> +
> +	decoded = rxq->vport->rx_ptype_lkup[ptype];
> +	if (!(decoded.known && decoded.outer_ip))
> +		return;
> +
> +	ipv4 = (decoded.outer_ip == IECM_RX_PTYPE_OUTER_IP) &&
> +	       (decoded.outer_ip_ver == IECM_RX_PTYPE_OUTER_IPV4);
> +	ipv6 = (decoded.outer_ip == IECM_RX_PTYPE_OUTER_IP) &&
> +	       (decoded.outer_ip_ver == IECM_RX_PTYPE_OUTER_IPV6);
> +
> +	if (ipv4 && (csum_bits->ipe || csum_bits->eipe))
> +		goto checksum_fail;
> +	else if (ipv6 && (csum_bits->ipv6exadd))
> +		goto checksum_fail;
> +
> +	/* check for L4 errors and handle packets that were not able to be
> +	 * checksummed due to arrival speed
> +	 */
> +	if (csum_bits->l4e)
> +		goto checksum_fail;
> +
> +	if (csum_bits->nat && csum_bits->eudpe)
> +		goto checksum_fail;
> +
> +	/* Handle packets that were not able to be checksummed due to arrival
> +	 * speed, in this case the stack can compute the csum.
> +	 */
> +	if (csum_bits->pprs)
> +		return;
> +
> +	/* If there is an outer header present that might contain a checksum
> +	 * we need to bump the checksum level by 1 to reflect the fact that
> +	 * we are indicating we validated the inner checksum.
> +	 */
> +	if (decoded.tunnel_type >= IECM_RX_PTYPE_TUNNEL_IP_GRENAT)
> +		skb->csum_level = 1;
> +
> +	/* Only report checksum unnecessary for ICMP, TCP, UDP, or SCTP */
> +	switch (decoded.inner_prot) {
> +	case IECM_RX_PTYPE_INNER_PROT_ICMP:
> +	case IECM_RX_PTYPE_INNER_PROT_TCP:
> +	case IECM_RX_PTYPE_INNER_PROT_UDP:
> +	case IECM_RX_PTYPE_INNER_PROT_SCTP:
> +		skb->ip_summed = CHECKSUM_UNNECESSARY;
> +	default:
> +		break;
> +	}
> +	return;
> +
> +checksum_fail:
> +	rxq->vport->port_stats.rx_hw_csum_err++;
> +}
> +
> +/**
> + * iecm_rx_singleq_base_csum - Indicate in skb if hw indicated a good cksum
> + * @rx_q: Rx completion queue
> + * @skb: skb currently being received and modified
> + * @rx_desc: the receive descriptor
> + * @ptype: Rx packet type
> + *
> + * This function only operates on the VIRTCHNL2_RXDID_1_32B_BASE_M legacy 32byte
> + * descriptor writeback format.
> + **/
> +static void
> +iecm_rx_singleq_base_csum(struct iecm_queue *rx_q, struct sk_buff *skb,
> +			  union virtchnl2_rx_desc *rx_desc, u16 ptype)
> +{
> +	struct iecm_rx_csum_decoded csum_bits;
> +	u32 rx_error, rx_status;
> +	u64 qword;
> +
> +	qword = le64_to_cpu(rx_desc->base_wb.qword1.status_error_ptype_len);
> +
> +	rx_status = ((qword & VIRTCHNL2_RX_BASE_DESC_QW1_STATUS_M) >>
> +					VIRTCHNL2_RX_BASE_DESC_QW1_STATUS_S);
> +	rx_error = ((qword & VIRTCHNL2_RX_BASE_DESC_QW1_ERROR_M) >>
> +					VIRTCHNL2_RX_BASE_DESC_QW1_ERROR_S);
> +
> +	csum_bits.ipe = !!(rx_error & BIT(VIRTCHNL2_RX_BASE_DESC_ERROR_IPE_S));
> +	csum_bits.eipe = !!(rx_error & BIT(VIRTCHNL2_RX_BASE_DESC_ERROR_EIPE_S));
> +	csum_bits.l4e = !!(rx_error & BIT(VIRTCHNL2_RX_BASE_DESC_ERROR_L4E_S));
> +	csum_bits.pprs = !!(rx_error & BIT(VIRTCHNL2_RX_BASE_DESC_ERROR_PPRS_S));
> +	csum_bits.l3l4p = !!(rx_status &
> +			     BIT(VIRTCHNL2_RX_BASE_DESC_STATUS_L3L4P_S));
> +	csum_bits.ipv6exadd = !!(rx_status &
> +				 BIT(VIRTCHNL2_RX_BASE_DESC_STATUS_IPV6EXADD_S));
> +	csum_bits.nat = 0;
> +	csum_bits.eudpe = 0;
> +
> +	iecm_rx_singleq_csum(rx_q, skb, &csum_bits, ptype);
> +}
> +
> +/**
> + * iecm_rx_singleq_flex_csum - Indicate in skb if hw indicated a good cksum
> + * @rx_q: Rx completion queue
> + * @skb: skb currently being received and modified
> + * @rx_desc: the receive descriptor
> + * @ptype: Rx packet type
> + *
> + * This function only operates on the VIRTCHNL2_RXDID_2_FLEX_SQ_NIC flexible
> + * descriptor writeback format.
> + **/
> +static void
> +iecm_rx_singleq_flex_csum(struct iecm_queue *rx_q, struct sk_buff *skb,
> +			  union virtchnl2_rx_desc *rx_desc, u16 ptype)
> +{
> +	struct iecm_rx_csum_decoded csum_bits;
> +	u16 rx_status0, rx_status1;
> +
> +	rx_status0 = le16_to_cpu(rx_desc->flex_nic_wb.status_error0);
> +	rx_status1 = le16_to_cpu(rx_desc->flex_nic_wb.status_error1);
> +
> +	csum_bits.ipe = !!(rx_status0 &
> +			   BIT(VIRTCHNL2_RX_FLEX_DESC_STATUS0_XSUM_IPE_S));
> +	csum_bits.eipe = !!(rx_status0 &
> +			    BIT(VIRTCHNL2_RX_FLEX_DESC_STATUS0_XSUM_EIPE_S));
> +	csum_bits.l4e = !!(rx_status0 &
> +			   BIT(VIRTCHNL2_RX_FLEX_DESC_STATUS0_XSUM_L4E_S));
> +	csum_bits.eudpe = !!(rx_status0 &
> +			     BIT(VIRTCHNL2_RX_FLEX_DESC_STATUS0_XSUM_EUDPE_S));
> +	csum_bits.l3l4p = !!(rx_status0 &
> +			     BIT(VIRTCHNL2_RX_FLEX_DESC_STATUS0_L3L4P_S));
> +	csum_bits.ipv6exadd =
> +			!!(rx_status0 &
> +			   BIT(VIRTCHNL2_RX_FLEX_DESC_STATUS0_IPV6EXADD_S));
> +	csum_bits.nat = !!(rx_status1 &
> +			   BIT(VIRTCHNL2_RX_FLEX_DESC_STATUS1_NAT_S));
> +	csum_bits.pprs = 0;
> +
> +	iecm_rx_singleq_csum(rx_q, skb, &csum_bits, ptype);
> +}
> +
> +/**
> + * iecm_rx_singleq_base_hash - set the hash value in the skb
> + * @rx_q: Rx completion queue
> + * @skb: skb currently being received and modified
> + * @rx_desc: specific descriptor
> + * @decoded: Decoded Rx packet type related fields
> + *
> + * This function only operates on the VIRTCHNL2_RXDID_1_32B_BASE_M legacy 32byte
> + * descriptor writeback format.
> + **/
> +static void
> +iecm_rx_singleq_base_hash(struct iecm_queue *rx_q, struct sk_buff *skb,
> +			  union virtchnl2_rx_desc *rx_desc,
> +			  struct iecm_rx_ptype_decoded *decoded)
> +{
> +	const __le64 rss_mask =
> +		cpu_to_le64((u64)VIRTCHNL2_RX_BASE_DESC_FLTSTAT_RSS_HASH <<

You can define _RSS_HASH with a 'ull' suffix to avoid casting.
You can define a shorthand for this, like _RSS_HASH_LE to avoid
these wraps.

> +				VIRTCHNL2_RX_BASE_DESC_STATUS_FLTSTAT_S);
> +	u32 hash;
> +
> +	if (!(rx_q->vport->netdev->features & NETIF_F_RXHASH))
> +		return;
> +
> +	if ((rx_desc->base_wb.qword1.status_error_ptype_len & rss_mask) ==
> +								rss_mask) {

This is wrong indentation, just align it to the first opening brace.

> +		hash = le32_to_cpu(rx_desc->base_wb.qword0.hi_dword.rss);
> +		skb_set_hash(skb, hash, iecm_ptype_to_htype(decoded));
> +	}
> +}
> +
> +/**
> + * iecm_rx_singleq_flex_hash - set the hash value in the skb
> + * @rx_q: Rx completion queue
> + * @skb: skb currently being received and modified
> + * @rx_desc: specific descriptor
> + * @decoded: Decoded Rx packet type related fields
> + *
> + * This function only operates on the VIRTCHNL2_RXDID_2_FLEX_SQ_NIC flexible
> + * descriptor writeback format.
> + **/
> +static void
> +iecm_rx_singleq_flex_hash(struct iecm_queue *rx_q, struct sk_buff *skb,
> +			  union virtchnl2_rx_desc *rx_desc,
> +			  struct iecm_rx_ptype_decoded *decoded)
> +{
> +	__le16 status0;
> +
> +	if (!(rx_q->vport->netdev->features & NETIF_F_RXHASH))
> +		return;
> +
> +	status0 = rx_desc->flex_nic_wb.status_error0;
> +	if (status0 &
> +		cpu_to_le16(BIT(VIRTCHNL2_RX_FLEX_DESC_STATUS0_RSS_VALID_S))) {
> +		u32 hash = le32_to_cpu(rx_desc->flex_nic_wb.rss_hash);
> +
> +		skb_set_hash(skb, hash, iecm_ptype_to_htype(decoded));
> +	}

	if (!status)
		return;

-1 indent level.

> +}
> +
> +/**
> + * iecm_rx_singleq_process_skb_fields - Populate skb header fields from Rx
> + * descriptor
> + * @rx_q: Rx descriptor ring packet is being transacted on
> + * @skb: pointer to current skb being populated
> + * @rx_desc: descriptor for skb
> + * @ptype: packet type
> + *
> + * This function checks the ring, descriptor, and packet information in
> + * order to populate the hash, checksum, VLAN, protocol, and
> + * other fields within the skb.
> + */
> +void
> +iecm_rx_singleq_process_skb_fields(struct iecm_queue *rx_q, struct sk_buff *skb,
> +				   union virtchnl2_rx_desc *rx_desc,
> +				   u16 ptype)
> +{
> +	struct iecm_rx_ptype_decoded decoded =
> +					rx_q->vport->rx_ptype_lkup[ptype];

Declare, then assign to avoid wraps.

> +
> +	/* modifies the skb - consumes the enet header */
> +	skb->protocol = eth_type_trans(skb, rx_q->vport->netdev);

Here applies the same I said for splitq, eth_type_trans() should be
right before napi_gro_receive().

> +
> +	if (rx_q->rxdids == VIRTCHNL2_RXDID_1_32B_BASE_M) {
> +		iecm_rx_singleq_base_hash(rx_q, skb, rx_desc, &decoded);
> +		iecm_rx_singleq_base_csum(rx_q, skb, rx_desc, ptype);
> +	} else {
> +		iecm_rx_singleq_flex_hash(rx_q, skb, rx_desc, &decoded);
> +		iecm_rx_singleq_flex_csum(rx_q, skb, rx_desc, ptype);
> +	}
> +}
> +
>  /**
>   * iecm_rx_singleq_buf_hw_alloc_all - Replace used receive buffers
>   * @rx_q: queue for which the hw buffers are allocated
> @@ -13,8 +786,410 @@
>  bool iecm_rx_singleq_buf_hw_alloc_all(struct iecm_queue *rx_q,
>  				      u16 cleaned_count)
>  {
> -	/* stub */
> -	return true;
> +	struct virtchnl2_singleq_rx_buf_desc *singleq_rx_desc = NULL;
> +	struct iecm_page_info *page_info;
> +	u16 nta = rx_q->next_to_alloc;
> +	struct iecm_rx_buf *buf;
> +
> +	/* do nothing if no valid netdev defined */
> +	if (!rx_q->vport->netdev || !cleaned_count)
> +		return false;
> +
> +	singleq_rx_desc = IECM_SINGLEQ_RX_BUF_DESC(rx_q, nta);
> +	buf = &rx_q->rx_buf.buf[nta];
> +	page_info = &buf->page_info[buf->page_indx];
> +
> +	do {
> +		if (unlikely(!page_info->page)) {
> +			if (!iecm_init_rx_buf_hw_alloc(rx_q, buf))
> +				break;
> +		}

	if (1 && 2)

> +
> +		/* Refresh the desc even if buffer_addrs didn't change
> +		 * because each write-back erases this info.
> +		 */
> +		singleq_rx_desc->pkt_addr =
> +			cpu_to_le64(page_info->dma + page_info->page_offset);
> +		singleq_rx_desc->hdr_addr = 0;
> +		singleq_rx_desc++;
> +
> +		buf++;
> +		nta++;
> +		if (unlikely(nta == rx_q->desc_count)) {

Can be squashed into ++nta == count.

> +			singleq_rx_desc = IECM_SINGLEQ_RX_BUF_DESC(rx_q, 0);
> +			buf = rx_q->rx_buf.buf;
> +			nta = 0;
> +		}
> +
> +		page_info = &buf->page_info[buf->page_indx];
> +
> +		cleaned_count--;
> +	} while (cleaned_count);

And these into `while (--cleaned_count);`

> +
> +	if (rx_q->next_to_alloc != nta) {
> +		iecm_rx_buf_hw_update(rx_q, nta);
> +		rx_q->next_to_alloc = nta;
> +	}
> +
> +	return !!cleaned_count;
> +}
> +
> +/**
> + * iecm_rx_reuse_page - Put recycled buffer back onto ring
> + * @rxq: Rx descriptor ring to store buffers on
> + * @old_buf: donor buffer to have page reused
> + */
> +static void iecm_rx_reuse_page(struct iecm_queue *rxq,
> +			       struct iecm_rx_buf *old_buf)
> +{
> +	u16 ntu = rxq->next_to_use;
> +	struct iecm_rx_buf *new_buf;

RCTree.

> +
> +	new_buf = &rxq->rx_buf.buf[ntu];
> +
> +	/* Transfer page from old buffer to new buffer.  Move each member
> +	 * individually to avoid possible store forwarding stalls and
> +	 * unnecessary copy of skb.
> +	 */
> +	new_buf->page_info[new_buf->page_indx].dma =
> +				old_buf->page_info[old_buf->page_indx].dma;
> +	new_buf->page_info[new_buf->page_indx].page =
> +				old_buf->page_info[old_buf->page_indx].page;
> +	new_buf->page_info[new_buf->page_indx].page_offset =
> +			old_buf->page_info[old_buf->page_indx].page_offset;
> +	new_buf->page_info[new_buf->page_indx].pagecnt_bias =
> +			old_buf->page_info[old_buf->page_indx].pagecnt_bias;
> +}
> +
> +/**
> + * iecm_rx_singleq_recycle_buf - Clean up used buffer and either recycle or free
> + * @rxq: Rx descriptor queue to transact packets on
> + * @rx_buf: Rx buffer to pull data from
> + *
> + * This function will clean up the contents of the rx_buf. It will either
> + * recycle the buffer or unmap it and free the associated resources.
> + *
> + * Returns true if the buffer is reused, false if the buffer is freed.
> + */
> +static bool iecm_rx_singleq_recycle_buf(struct iecm_queue *rxq,
> +					struct iecm_rx_buf *rx_buf)
> +{
> +	struct iecm_page_info *page_info =
> +			&rx_buf->page_info[rx_buf->page_indx];

Declare, then assign to avoid wraps.

> +

No empty lines between declarations.

> +	bool recycled = false;
> +
> +	if (iecm_rx_can_reuse_page(rx_buf)) {
> +		/* hand second half of page back to the queue */
> +		iecm_rx_reuse_page(rxq, rx_buf);
> +		recycled = true;
> +	} else {
> +		/* we are not reusing the buffer so unmap it */
> +		dma_unmap_page_attrs(rxq->dev, page_info->dma, PAGE_SIZE,
> +				     DMA_FROM_DEVICE, IECM_RX_DMA_ATTR);
> +		__page_frag_cache_drain(page_info->page,
> +					page_info->pagecnt_bias);
> +	}
> +
> +	/* clear contents of buffer_info */
> +	page_info->page = NULL;
> +	rx_buf->skb = NULL;
> +
> +	return recycled;
> +}
> +
> +/**
> + * iecm_rx_singleq_put_buf - Wrapper function to clean and recycle buffers
> + * @rxq: Rx descriptor queue to transact packets on
> + * @rx_buf: Rx buffer to pull data from
> + *
> + * This function will update the next_to_use/next_to_alloc if the current
> + * buffer is recycled.
> + */
> +static void iecm_rx_singleq_put_buf(struct iecm_queue *rxq,
> +				    struct iecm_rx_buf *rx_buf)
> +{
> +	u16 ntu = rxq->next_to_use;
> +	bool recycled = false;
> +
> +	recycled = iecm_rx_singleq_recycle_buf(rxq, rx_buf);
> +
> +	/* update, and store next to alloc if the buffer was recycled */
> +	if (recycled) {
> +		ntu++;
> +		rxq->next_to_use = (ntu < rxq->desc_count) ? ntu : 0;
> +	}
> +}
> +
> +/**
> + * iecm_rx_singleq_bump_ntc - Bump and wrap q->next_to_clean value
> + * @rxq: queue to bump
> + */
> +void iecm_rx_singleq_bump_ntc(struct iecm_queue *rxq)
> +{
> +	u16 ntc = rxq->next_to_clean + 1;
> +	/* fetch, update, and store next to clean */
> +	if (ntc < rxq->desc_count)
> +		rxq->next_to_clean = ntc;
> +	else
> +		rxq->next_to_clean = 0;
> +}
> +
> +/**
> + * iecm_rx_singleq_get_buf_page - Fetch Rx buffer page and synchronize data
> + * @dev: device struct
> + * @rx_buf: Rx buf to fetch page for
> + * @size: size of buffer to add to skb
> + *
> + * This function will pull an Rx buffer page from the ring and synchronize it
> + * for use by the CPU.
> + */
> +static struct sk_buff *
> +iecm_rx_singleq_get_buf_page(struct device *dev, struct iecm_rx_buf *rx_buf,
> +			     const unsigned int size)
> +{
> +	struct iecm_page_info *page_info;
> +
> +	page_info = &rx_buf->page_info[rx_buf->page_indx];
> +	prefetch(page_info->page);
> +
> +	/* we are reusing so sync this buffer for CPU use */
> +	dma_sync_single_range_for_cpu(dev, page_info->dma,
> +				      page_info->page_offset, size,
> +				      DMA_FROM_DEVICE);
> +
> +	/* We have pulled a buffer for use, so decrement pagecnt_bias */
> +	page_info->pagecnt_bias--;
> +
> +	return rx_buf->skb;
> +}
> +
> +/**
> + * iecm_rx_singleq_extract_base_fields - Extract fields from the Rx descriptor
> + * @rx_q: Rx descriptor queue
> + * @rx_desc: the descriptor to process
> + * @fields: storage for extracted values
> + *
> + * Decode the Rx descriptor and extract relevant information including the
> + * size, VLAN tag, and Rx packet type.
> + *
> + * This function only operates on the VIRTCHNL2_RXDID_1_32B_BASE_M legacy 32byte
> + * descriptor writeback format.
> + */
> +static inline void
> +iecm_rx_singleq_extract_base_fields(struct iecm_queue *rx_q,
> +				    union virtchnl2_rx_desc *rx_desc,
> +				    struct iecm_rx_extracted *fields)
> +{
> +	u64 qw2_status, qword;
> +
> +	qword = le64_to_cpu(rx_desc->base_wb.qword1.status_error_ptype_len);
> +	qw2_status = le16_to_cpu(rx_desc->base_wb.qword2.ext_status);
> +
> +	fields->size = (qword & VIRTCHNL2_RX_BASE_DESC_QW1_LEN_PBUF_M) >>
> +					VIRTCHNL2_RX_BASE_DESC_QW1_LEN_PBUF_S;
> +	fields->rx_ptype = (qword & VIRTCHNL2_RX_BASE_DESC_QW1_PTYPE_M) >>
> +					VIRTCHNL2_RX_BASE_DESC_QW1_PTYPE_S;
> +
> +	if (qword & BIT(VIRTCHNL2_RX_BASE_DESC_STATUS_L2TAG1P_S) &&
> +	    test_bit(__IECM_Q_VLAN_TAG_LOC_L2TAG1, rx_q->flags))
> +		fields->vlan_tag =
> +			le16_to_cpu(rx_desc->base_wb.qword0.lo_dword.l2tag1);
> +	if (qw2_status & BIT(VIRTCHNL2_RX_BASE_DESC_EXT_STATUS_L2TAG2P_S) &&
> +	    test_bit(__IECM_Q_VLAN_TAG_LOC_L2TAG2, rx_q->flags))
> +		fields->vlan_tag =
> +			le16_to_cpu(rx_desc->base_wb.qword2.l2tag2_1);
> +}
> +
> +/**
> + * iecm_rx_singleq_extract_flex_fields - Extract fields from the Rx descriptor
> + * @rx_q: Rx descriptor queue
> + * @rx_desc: the descriptor to process
> + * @fields: storage for extracted values
> + *
> + * Decode the Rx descriptor and extract relevant information including the
> + * size, VLAN tag, and Rx packet type.
> + *
> + * This function only operates on the VIRTCHNL2_RXDID_2_FLEX_SQ_NIC flexible
> + * descriptor writeback format.
> + */
> +static inline void
> +iecm_rx_singleq_extract_flex_fields(struct iecm_queue *rx_q,
> +				    union virtchnl2_rx_desc *rx_desc,
> +				    struct iecm_rx_extracted *fields)
> +{
> +	__le16 status0, status1;
> +
> +	fields->size = le16_to_cpu(rx_desc->flex_nic_wb.pkt_len) &
> +					VIRTCHNL2_RX_FLEX_DESC_PKT_LEN_M;
> +	fields->rx_ptype = le16_to_cpu(rx_desc->flex_nic_wb.ptype_flex_flags0) &
> +					VIRTCHNL2_RX_FLEX_DESC_PTYPE_M;
> +
> +	status0 = rx_desc->flex_nic_wb.status_error0;
> +	if (status0 & cpu_to_le16(BIT(VIRTCHNL2_RX_FLEX_DESC_STATUS0_L2TAG1P_S)) &&
> +	    test_bit(__IECM_Q_VLAN_TAG_LOC_L2TAG1, rx_q->flags))
> +		fields->vlan_tag = le16_to_cpu(rx_desc->flex_nic_wb.l2tag1);
> +
> +	status1 = rx_desc->flex_nic_wb.status_error1;
> +	if (status1 & cpu_to_le16(BIT(VIRTCHNL2_RX_FLEX_DESC_STATUS1_L2TAG2P_S)) &&
> +	    test_bit(__IECM_Q_VLAN_TAG_LOC_L2TAG2, rx_q->flags))
> +		fields->vlan_tag = le16_to_cpu(rx_desc->flex_nic_wb.l2tag2_2nd);
> +}
> +
> +/**
> + * iecm_rx_singleq_extract_fields - Extract fields from the Rx descriptor
> + * @rx_q: Rx descriptor queue
> + * @rx_desc: the descriptor to process
> + * @fields: storage for extracted values
> + *
> + */
> +void
> +iecm_rx_singleq_extract_fields(struct iecm_queue *rx_q,
> +			       union virtchnl2_rx_desc *rx_desc,
> +			       struct iecm_rx_extracted *fields)
> +{
> +	if (rx_q->rxdids == VIRTCHNL2_RXDID_1_32B_BASE_M)
> +		iecm_rx_singleq_extract_base_fields(rx_q, rx_desc, fields);
> +	else
> +		iecm_rx_singleq_extract_flex_fields(rx_q, rx_desc, fields);
> +}
> +
> +/**
> + * iecm_rx_singleq_clean - Reclaim resources after receive completes
> + * @rx_q: rx queue to clean
> + * @budget: Total limit on number of packets to process
> + *
> + * Returns true if there's any budget left (e.g. the clean is finished)
> + */
> +static int iecm_rx_singleq_clean(struct iecm_queue *rx_q, int budget)
> +{
> +	unsigned int total_rx_bytes = 0, total_rx_pkts = 0;
> +	u16 cleaned_count = 0;
> +	bool failure = false;
> +
> +	/* Process Rx packets bounded by budget */
> +	while (likely(total_rx_pkts < (unsigned int)budget)) {
> +		struct iecm_rx_extracted fields = {};
> +		union virtchnl2_rx_desc *rx_desc;
> +		struct sk_buff *skb = NULL;
> +		struct iecm_rx_buf *rx_buf;
> +
> +		/* get the Rx desc from Rx queue based on 'next_to_clean' */
> +		rx_desc = IECM_RX_DESC(rx_q, rx_q->next_to_clean);
> +
> +		/* This memory barrier is needed to keep us from reading
> +		 * any other fields out of the rx_desc
> +		 */
> +		dma_rmb();
> +
> +		/* status_error_ptype_len will always be zero for unused
> +		 * descriptors because it's cleared in cleanup, and overlaps
> +		 * with hdr_addr which is always zero because packet split
> +		 * isn't used, if the hardware wrote DD then the length will be
> +		 * non-zero
> +		 */
> +#define IECM_RXD_DD BIT(VIRTCHNL2_RX_BASE_DESC_STATUS_DD_S)

Should be moved from here as well.

> +		if (!iecm_rx_singleq_test_staterr(rx_desc,
> +						  IECM_RXD_DD))
> +			break;
> +		iecm_rx_singleq_extract_fields(rx_q, rx_desc, &fields);
> +
> +		if (!fields.size)
> +			break;
> +
> +		rx_buf = &rx_q->rx_buf.buf[rx_q->next_to_clean];
> +		skb = iecm_rx_singleq_get_buf_page(rx_q->dev, rx_buf,
> +						   fields.size);
> +
> +		if (skb)
> +			iecm_rx_add_frag(rx_buf, skb, fields.size);
> +		else
> +			skb = iecm_rx_construct_skb(rx_q, rx_buf, fields.size);
> +
> +		/* exit if we failed to retrieve a buffer */
> +		if (!skb) {
> +			rx_buf->page_info[rx_buf->page_indx].pagecnt_bias++;
> +			break;
> +		}
> +
> +		iecm_rx_singleq_put_buf(rx_q, rx_buf);
> +		iecm_rx_singleq_bump_ntc(rx_q);
> +
> +		cleaned_count++;
> +
> +		/* skip if it is non EOP desc */
> +		if (iecm_rx_singleq_is_non_eop(rx_q, rx_desc,
> +					       skb))

skb fits into previous line.

> +			continue;
> +
> +#define IECM_RXD_ERR_S BIT(VIRTCHNL2_RX_BASE_DESC_QW1_ERROR_S)

Same.

> +		if (unlikely(iecm_rx_singleq_test_staterr(rx_desc,
> +							  IECM_RXD_ERR_S))) {
> +			dev_kfree_skb_any(skb);
> +			skb = NULL;
> +			continue;
> +		}
> +
> +		/* pad skb if needed (to make valid ethernet frame) */
> +		if (eth_skb_pad(skb)) {
> +			skb = NULL;
> +			continue;
> +		}
> +
> +		/* probably a little skewed due to removing CRC */
> +		total_rx_bytes += skb->len;
> +
> +		/* protocol */
> +		iecm_rx_singleq_process_skb_fields(rx_q, skb,
> +						   rx_desc, fields.rx_ptype);
> +
> +		/* send completed skb up the stack */
> +		iecm_rx_skb(rx_q, skb, fields.vlan_tag);
> +
> +		/* update budget accounting */
> +		total_rx_pkts++;
> +	}
> +	if (cleaned_count)
> +		failure = iecm_rx_singleq_buf_hw_alloc_all(rx_q, cleaned_count);
> +
> +	u64_stats_update_begin(&rx_q->stats_sync);
> +	rx_q->q_stats.rx.packets += total_rx_pkts;
> +	rx_q->q_stats.rx.bytes += total_rx_bytes;
> +	u64_stats_update_end(&rx_q->stats_sync);
> +
> +	/* guarantee a trip back through this routine if there was a failure */
> +	return failure ? budget : (int)total_rx_pkts;
> +}
> +
> +/**
> + * iecm_rx_singleq_clean_all - Clean all Rx queues
> + * @q_vec: queue vector
> + * @budget: Used to determine if we are in netpoll
> + * @cleaned: returns number of packets cleaned
> + *
> + * Returns false if clean is not complete else returns true
> + */
> +static bool
> +iecm_rx_singleq_clean_all(struct iecm_q_vector *q_vec, int budget,
> +			  int *cleaned)
> +{
> +	bool clean_complete = true;
> +	int budget_per_q, i;
> +
> +	budget_per_q = max(budget / q_vec->num_rxq, 1);
> +	for (i = 0; i < q_vec->num_rxq; i++) {
> +		struct iecm_queue *rxq = q_vec->rx[i];
> +		int pkts_cleaned_per_q;
> +
> +		pkts_cleaned_per_q = iecm_rx_singleq_clean(rxq, budget_per_q);
> +
> +		/* if we clean as many as budgeted, we must not be done */
> +		if (pkts_cleaned_per_q >= budget_per_q)
> +			clean_complete = false;
> +		*cleaned += pkts_cleaned_per_q;
> +	}
> +
> +	return clean_complete;
>  }
>  
>  /**
> @@ -24,6 +1199,31 @@ bool iecm_rx_singleq_buf_hw_alloc_all(struct iecm_queue *rx_q,
>   */
>  int iecm_vport_singleq_napi_poll(struct napi_struct *napi, int budget)
>  {
> -	/* stub */
> -	return 0;
> +	struct iecm_q_vector *q_vector =
> +				container_of(napi, struct iecm_q_vector, napi);

Declare + assign to avoid.

> +	bool clean_complete;
> +	int work_done = 0;
> +
> +	clean_complete = iecm_tx_singleq_clean_all(q_vector, budget);
> +
> +	/* Handle case where we are called by netpoll with a budget of 0 */
> +	if (budget <= 0)
> +		return budget;
> +
> +	/* We attempt to distribute budget to each Rx queue fairly, but don't
> +	 * allow the budget to go below 1 because that would exit polling early.
> +	 */
> +	clean_complete |= iecm_rx_singleq_clean_all(q_vector, budget,
> +						    &work_done);
> +
> +	/* If work not completed, return budget and polling will return */
> +	if (!clean_complete)
> +		return budget;
> +
> +	/* Exit the polling mode, but don't re-enable interrupts if stack might
> +	 * poll us due to busy-polling
> +	 */
> +	if (likely(napi_complete_done(napi, work_done)))
> +		iecm_vport_intr_update_itr_ena_irq(q_vector);
> +	return min_t(int, work_done, budget - 1);
>  }
> diff --git a/drivers/net/ethernet/intel/include/iecm.h b/drivers/net/ethernet/intel/include/iecm.h
> index 3cf2a2f0cb0f..97c9935b832d 100644
> --- a/drivers/net/ethernet/intel/include/iecm.h
> +++ b/drivers/net/ethernet/intel/include/iecm.h
> @@ -8,6 +8,7 @@
>  #include <net/pkt_cls.h>
>  #include <linux/aer.h>
>  #include <linux/pci.h>
> +#include <linux/sctp.h>
>  #include <linux/netdevice.h>
>  #include <linux/etherdevice.h>
>  #include <linux/ethtool.h>
> diff --git a/drivers/net/ethernet/intel/include/iecm_txrx.h b/drivers/net/ethernet/intel/include/iecm_txrx.h
> index 086b0efc989a..4dba4f52be98 100644
> --- a/drivers/net/ethernet/intel/include/iecm_txrx.h
> +++ b/drivers/net/ethernet/intel/include/iecm_txrx.h
> @@ -624,6 +624,26 @@ struct iecm_txq_group {
>  
>  struct iecm_adapter;
>  
> +/**
> + * iecm_tx_singleq_build_ctob - populate command tag offset and size
> + * @td_cmd: Command to be filled in desc
> + * @td_offset: Offset to be filled in desc
> + * @size: Size of the buffer
> + * @td_tag: vlan tag to be filled
> + *
> + * Returns the 64 bit value populated with the input parameters
> + */
> +static inline __le64
> +iecm_tx_singleq_build_ctob(u64 td_cmd, u64 td_offset, unsigned int size,
> +			   u64 td_tag)
> +{
> +	return cpu_to_le64(IECM_TX_DESC_DTYPE_DATA |
> +			   (td_cmd    << IECM_TXD_QW1_CMD_S) |
> +			   (td_offset << IECM_TXD_QW1_OFFSET_S) |
> +			   ((u64)size << IECM_TXD_QW1_TX_BUF_SZ_S) |
> +			   (td_tag    << IECM_TXD_QW1_L2TAG1_S));
> +}
> +
>  void iecm_tx_splitq_build_ctb(union iecm_tx_flex_desc *desc,
>  			      struct iecm_tx_splitq_params *parms,
>  			      u16 td_cmd, u16 size);
> @@ -673,8 +693,19 @@ void iecm_rx_splitq_put_bufs(struct iecm_queue *rx_bufq,
>  			     struct iecm_rx_buf *hdr_buf,
>  			     struct iecm_rx_buf *rx_buf);
>  bool iecm_rx_splitq_test_staterr(u8 stat_err_field, const u8 stat_err_bits);
> +bool iecm_rx_singleq_test_staterr(union virtchnl2_rx_desc *rx_desc,
> +				  const u64 stat_err_bits);
>  int iecm_rx_process_skb_fields(struct iecm_queue *rxq, struct sk_buff *skb,
>  			       struct virtchnl2_rx_flex_desc_adv_nic_3 *rx_desc);
> +void iecm_rx_singleq_process_skb_fields(struct iecm_queue *rx_q, struct sk_buff *skb,
> +					union virtchnl2_rx_desc *rx_desc, u16 ptype);

Break the line after `void` to avoid (huh) going past 79.

> +void iecm_rx_singleq_extract_fields(struct iecm_queue *rx_q,
> +				    union virtchnl2_rx_desc *rx_desc,
> +				    struct iecm_rx_extracted *fields);
> +void iecm_rx_singleq_bump_ntc(struct iecm_queue *q);
> +bool iecm_rx_singleq_is_non_eop(struct iecm_queue *rxq,
> +				union virtchnl2_rx_desc *rx_desc,
> +				struct sk_buff *skb);
>  bool iecm_rx_splitq_extract_vlan_tag(struct virtchnl2_rx_flex_desc_adv_nic_3 *desc,
>  				     struct iecm_queue *rxq, u16 *vlan_tag);
>  void iecm_rx_bump_ntc(struct iecm_queue *q);
> -- 
> 2.33.0

Thanks,
Al


^ permalink raw reply	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 15/19] iecm: implement ethtool callbacks
  2022-01-28  0:10 ` [Intel-wired-lan] [PATCH net-next 15/19] iecm: implement ethtool callbacks Alan Brady
@ 2022-01-28 18:13   ` Alexander Lobakin
  2022-02-03  2:13     ` Brady, Alan
  0 siblings, 1 reply; 89+ messages in thread
From: Alexander Lobakin @ 2022-01-28 18:13 UTC (permalink / raw)
  To: intel-wired-lan

From: Alan Brady <alan.brady@intel.com>
Date: Thu, 27 Jan 2022 16:10:05 -0800

> This does everything needed to handle ethtool ops minus a few stubs for
> cloud filters and other advanced features which will be added in later in
> this series.
> 
> Signed-off-by: Phani Burra <phani.r.burra@intel.com>
> Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
> Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
> Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
> Signed-off-by: Alan Brady <alan.brady@intel.com>
> ---
>  drivers/net/ethernet/intel/iecm/Makefile      |    1 +
>  .../net/ethernet/intel/iecm/iecm_ethtool.c    | 1325 +++++++++++++++++
>  drivers/net/ethernet/intel/iecm/iecm_lib.c    |   11 +-
>  drivers/net/ethernet/intel/include/iecm.h     |    1 +
>  4 files changed, 1337 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/net/ethernet/intel/iecm/iecm_ethtool.c
> 
> diff --git a/drivers/net/ethernet/intel/iecm/Makefile b/drivers/net/ethernet/intel/iecm/Makefile
> index 205d6f2b436a..fe2ed403d35c 100644
> --- a/drivers/net/ethernet/intel/iecm/Makefile
> +++ b/drivers/net/ethernet/intel/iecm/Makefile
> @@ -15,6 +15,7 @@ iecm-y := \
>  	iecm_virtchnl.o \
>  	iecm_txrx.o \
>  	iecm_singleq_txrx.o \
> +	iecm_ethtool.o \
>  	iecm_controlq.o \
>  	iecm_controlq_setup.o \
>  	iecm_main.o
> diff --git a/drivers/net/ethernet/intel/iecm/iecm_ethtool.c b/drivers/net/ethernet/intel/iecm/iecm_ethtool.c
> new file mode 100644
> index 000000000000..32d905fb1bb6
> --- /dev/null
> +++ b/drivers/net/ethernet/intel/iecm/iecm_ethtool.c
> @@ -0,0 +1,1325 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/* Copyright (C) 2019 Intel Corporation */
> +
> +#include "iecm.h"
> +
> +/**
> + * iecm_get_rxnfc - command to get RX flow classification rules
> + * @netdev: network interface device structure
> + * @cmd: ethtool rxnfc command
> + * @rule_locs: pointer to store rule locations
> + *
> + * Returns Success if the command is supported.
> + */
> +static int iecm_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd,
> +			  u32 __always_unused *rule_locs)

Kernel Kbuild system tell compilers to not complain on unused
function arguments.
It's pointless to add __always_unused here.

> +{
> +	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
> +	int ret = -EOPNOTSUPP;
> +
> +	switch (cmd->cmd) {
> +	case ETHTOOL_GRXRINGS:
> +		cmd->data = vport->num_rxq;
> +		ret = 0;
> +		break;
> +	case ETHTOOL_GRXCLSRLCNT:
> +		/* stub */
> +		ret = 0;
> +		break;
> +	case ETHTOOL_GRXCLSRULE:
> +		/* stub */
> +		break;
> +	case ETHTOOL_GRXCLSRLALL:
> +		/* stub */
> +		break;
> +	case ETHTOOL_GRXFH:
> +		/* stub */
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	return ret;
> +}
> +
> +/**
> + * iecm_set_rxnfc - command to set Rx flow rules.
> + * @netdev: network interface device structure
> + * @cmd: ethtool rxnfc command
> + *
> + * Returns 0 for success and negative values for errors
> + */
> +static int iecm_set_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd)
> +{
> +	int ret = -EOPNOTSUPP;
> +
> +	switch (cmd->cmd) {
> +	case ETHTOOL_SRXCLSRLINS:
> +		/* stub */
> +		break;
> +	case ETHTOOL_SRXCLSRLDEL:
> +		/* stub */
> +		break;
> +	case ETHTOOL_SRXFH:
> +		/* stub */
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	return ret;
> +}
> +
> +/**
> + * iecm_get_rxfh_key_size - get the RSS hash key size
> + * @netdev: network interface device structure
> + *
> + * Returns the table size.
> + */
> +static u32 iecm_get_rxfh_key_size(struct net_device *netdev)
> +{
> +	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
> +
> +	if (!iecm_is_cap_ena_all(vport->adapter, IECM_RSS_CAPS, IECM_CAP_RSS)) {
> +		dev_info(&vport->adapter->pdev->dev, "RSS is not supported on this device\n");
> +		return 0;
> +	}
> +
> +	return vport->adapter->rss_data.rss_key_size;

If you invert the condition, it would take less lines.

> +}
> +
> +/**
> + * iecm_get_rxfh_indir_size - get the rx flow hash indirection table size
> + * @netdev: network interface device structure
> + *
> + * Returns the table size.
> + */
> +static u32 iecm_get_rxfh_indir_size(struct net_device *netdev)
> +{
> +	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
> +
> +	if (!iecm_is_cap_ena_all(vport->adapter, IECM_RSS_CAPS, IECM_CAP_RSS)) {
> +		dev_info(&vport->adapter->pdev->dev, "RSS is not supported on this device\n");
> +		return 0;
> +	}
> +
> +	return vport->adapter->rss_data.rss_lut_size;

Same here.

> +}
> +
> +/**
> + * iecm_get_rxfh - get the rx flow hash indirection table
> + * @netdev: network interface device structure
> + * @indir: indirection table
> + * @key: hash key
> + * @hfunc: hash function in use
> + *
> + * Reads the indirection table directly from the hardware. Always returns 0.
> + */
> +static int iecm_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key,
> +			 u8 *hfunc)
> +{
> +	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
> +	struct iecm_adapter *adapter;
> +	u16 i;
> +
> +	adapter = vport->adapter;
> +
> +	if (!iecm_is_cap_ena_all(adapter, IECM_RSS_CAPS, IECM_CAP_RSS)) {
> +		dev_info(&vport->adapter->pdev->dev, "RSS is not supported on this device\n");
> +		return 0;
> +	}
> +
> +	if (adapter->state != __IECM_UP)
> +		return 0;
> +
> +	if (hfunc)
> +		*hfunc = ETH_RSS_HASH_TOP;
> +
> +	if (key)
> +		memcpy(key, adapter->rss_data.rss_key,
> +		       adapter->rss_data.rss_key_size);
> +
> +	if (indir)
> +		/* Each 32 bits pointed by 'indir' is stored with a lut entry */
> +		for (i = 0; i < adapter->rss_data.rss_lut_size; i++)
> +			indir[i] = adapter->rss_data.rss_lut[i];
> +
> +	return 0;
> +}
> +
> +/**
> + * iecm_set_rxfh - set the rx flow hash indirection table
> + * @netdev: network interface device structure
> + * @indir: indirection table
> + * @key: hash key
> + * @hfunc: hash function to use
> + *
> + * Returns -EINVAL if the table specifies an invalid queue id, otherwise
> + * returns 0 after programming the table.
> + */
> +static int iecm_set_rxfh(struct net_device *netdev, const u32 *indir,
> +			 const u8 *key, const u8 hfunc)
> +{
> +	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
> +	struct iecm_adapter *adapter;
> +	u16 lut;
> +
> +	adapter = vport->adapter;
> +
> +	if (!iecm_is_cap_ena_all(adapter, IECM_RSS_CAPS, IECM_CAP_RSS)) {
> +		dev_info(&adapter->pdev->dev, "RSS is not supported on this device\n");
> +		return 0;
> +	}
> +	if (adapter->state != __IECM_UP)
> +		return 0;
> +	if (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP)
> +		return -EOPNOTSUPP;
> +
> +	if (key)
> +		memcpy(adapter->rss_data.rss_key, key,
> +		       adapter->rss_data.rss_key_size);
> +
> +	if (indir) {
> +		for (lut = 0; lut < adapter->rss_data.rss_lut_size; lut++)
> +			adapter->rss_data.rss_lut[lut] = indir[lut];
> +	}
> +
> +	return iecm_config_rss(vport);
> +}
> +
> +/**
> + * iecm_get_channels: get the number of channels supported by the device
> + * @netdev: network interface device structure
> + * @ch: channel information structure
> + *
> + * Report maximum of TX and RX. Report one extra channel to match our MailBox
> + * Queue.
> + */
> +static void iecm_get_channels(struct net_device *netdev,
> +			      struct ethtool_channels *ch)
> +{
> +	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
> +	unsigned int combined;
> +	int num_txq, num_rxq;
> +
> +	num_txq = vport->adapter->config_data.num_req_tx_qs;
> +	num_rxq = vport->adapter->config_data.num_req_rx_qs;
> +
> +	combined = min(num_txq, num_rxq);
> +
> +	/* Report maximum channels */
> +	ch->max_combined = vport->adapter->max_queue_limit;
> +
> +	/* For now we've only enabled combined queues but will be enabling
> +	 * asymmetric queues after splitq model is fleshed out more.
> +	 */
> +	ch->max_rx = 0;
> +	ch->max_tx = 0;
> +
> +	ch->max_other = IECM_MAX_NONQ;
> +	ch->other_count = IECM_MAX_NONQ;
> +
> +	ch->combined_count = combined;
> +	ch->rx_count = num_rxq - combined;
> +	ch->tx_count = num_txq - combined;
> +}
> +
> +/**
> + * iecm_set_channels: set the new channel count
> + * @netdev: network interface device structure
> + * @ch: channel information structure
> + *
> + * Negotiate a new number of channels with CP. Returns 0 on success, negative
> + * on failure.
> + */
> +static int iecm_set_channels(struct net_device *netdev,
> +			     struct ethtool_channels *ch)
> +{
> +	unsigned int num_req_tx_q = ch->combined_count + ch->tx_count;
> +	unsigned int num_req_rx_q = ch->combined_count + ch->rx_count;
> +	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
> +	struct iecm_adapter *adapter = vport->adapter;
> +	int num_txq, num_rxq, err;
> +
> +	if (num_req_tx_q == 0 || num_req_rx_q == 0)
> +		return -EINVAL;
> +
> +	num_txq = vport->adapter->config_data.num_req_tx_qs;
> +	num_rxq = vport->adapter->config_data.num_req_rx_qs;
> +
> +	if (num_req_tx_q == num_txq && num_req_rx_q == num_rxq)
> +		return 0;
> +
> +	vport->adapter->config_data.num_req_tx_qs = num_req_tx_q;
> +	vport->adapter->config_data.num_req_rx_qs = num_req_rx_q;
> +
> +	if (adapter->virt_ver_maj < VIRTCHNL_VERSION_MAJOR_2) {
> +		err = adapter->dev_ops.vc_ops.add_queues(vport,
> +							 num_req_tx_q, 0,
> +							 num_req_rx_q, 0);
> +	} else {
> +		err = iecm_initiate_soft_reset(vport, __IECM_SR_Q_CHANGE);
> +	}

One-liners, no need for braces.

> +
> +	if (err) {
> +		/* roll back queue change */
> +		vport->adapter->config_data.num_req_tx_qs = num_txq;
> +		vport->adapter->config_data.num_req_rx_qs = num_rxq;
> +	}
> +
> +	return err;
> +}
> +
> +/**
> + * iecm_get_ringparam - Get ring parameters
> + * @netdev: network interface device structure
> + * @ring: ethtool ringparam structure
> + * @kring: unused
> + * @ext_ack: unused
> + *
> + * Returns current ring parameters. TX and RX rings are reported separately,
> + * but the number of rings is not reported.
> + */
> +static void iecm_get_ringparam(struct net_device *netdev,
> +			       struct ethtool_ringparam *ring,
> +			       __always_unused struct kernel_ethtool_ringparam *kring,
> +			       __always_unused struct netlink_ext_ack *ext_ack)

Same here for __always_unused. +lines longer than 79.

> +{
> +	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
> +
> +	ring->rx_max_pending = IECM_MAX_RXQ_DESC;
> +	ring->tx_max_pending = IECM_MAX_TXQ_DESC;
> +	ring->rx_pending = vport->rxq_desc_count;
> +	ring->tx_pending = vport->txq_desc_count;
> +}
> +
> +/**
> + * iecm_set_ringparam - Set ring parameters
> + * @netdev: network interface device structure
> + * @ring: ethtool ringparam structure
> + * @kring: unused
> + * @ext_ack: unused
> + *
> + * Sets ring parameters. TX and RX rings are controlled separately, but the
> + * number of rings is not specified, so all rings get the same settings.
> + */
> +static int iecm_set_ringparam(struct net_device *netdev,
> +			      struct ethtool_ringparam *ring,
> +			      __always_unused struct kernel_ethtool_ringparam *kring,
> +			      __always_unused struct netlink_ext_ack *ext_ack)

Same.

> +{
> +	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
> +	u32 new_rx_count, new_tx_count;
> +	int i;
> +
> +	if (iecm_is_queue_model_split(vport->rxq_model))
> +		/* When in splitq mode, any one RXQ needs to contain enough
> +		 * descriptors to service the 2 buffer queues associated with
> +		 * it. Since each buffer queue must be a multiple of 32, the
> +		 * RXQ then needs to be a multiple of 64 to be divided into
> +		 * multiples of 32 for each buffer queue
> +		 */
> +		new_rx_count = ALIGN(ring->rx_pending,
> +				     IECM_REQ_SPLITQ_RXQ_DESC_MULTIPLE);
> +	else
> +		new_rx_count = ALIGN(ring->rx_pending, IECM_REQ_DESC_MULTIPLE);
> +
> +	new_tx_count = ALIGN(ring->tx_pending, IECM_REQ_DESC_MULTIPLE);
> +
> +	/* if nothing to do return success */
> +	if (new_tx_count == vport->txq_desc_count &&
> +	    new_rx_count == vport->rxq_desc_count)
> +		return 0;
> +
> +	vport->adapter->config_data.num_req_txq_desc = new_tx_count;
> +	vport->adapter->config_data.num_req_rxq_desc = new_rx_count;
> +
> +	/* Since we adjusted the RX completion queue count, the RX buffer queue
> +	 * descriptor count needs to be adjusted as well
> +	 */
> +	for (i = 0; i < vport->num_bufqs_per_qgrp; i++)
> +		vport->bufq_desc_count[i] =
> +			IECM_RX_BUFQ_DESC_COUNT(new_rx_count,
> +						vport->num_bufqs_per_qgrp);
> +
> +	return iecm_initiate_soft_reset(vport, __IECM_SR_Q_DESC_CHANGE);
> +}
> +
> +/**
> + * struct iecm_stats - definition for an ethtool statistic
> + * @stat_string: statistic name to display in ethtool -S output
> + * @sizeof_stat: the sizeof() the stat, must be no greater than sizeof(u64)
> + * @stat_offset: offsetof() the stat from a base pointer
> + *
> + * This structure defines a statistic to be added to the ethtool stats buffer.
> + * It defines a statistic as offset from a common base pointer. Stats should
> + * be defined in constant arrays using the IECM_STAT macro, with every element
> + * of the array using the same _type for calculating the sizeof_stat and
> + * stat_offset.
> + *
> + * The @sizeof_stat is expected to be sizeof(u8), sizeof(u16), sizeof(u32) or
> + * sizeof(u64). Other sizes are not expected and will produce a WARN_ONCE from
> + * the iecm_add_ethtool_stat() helper function.
> + *
> + * The @stat_string is interpreted as a format string, allowing formatted
> + * values to be inserted while looping over multiple structures for a given
> + * statistics array. Thus, every statistic string in an array should have the
> + * same type and number of format specifiers, to be formatted by variadic
> + * arguments to the iecm_add_stat_string() helper function.
> + */
> +struct iecm_stats {
> +	char stat_string[ETH_GSTRING_LEN];
> +	int sizeof_stat;
> +	int stat_offset;
> +};
> +
> +/* Helper macro to define an iecm_stat structure with proper size and type.
> + * Use this when defining constant statistics arrays. Note that @_type expects
> + * only a type name and is used multiple times.
> + */
> +#define IECM_STAT(_type, _name, _stat) { \
> +	.stat_string = _name, \
> +	.sizeof_stat = sizeof_field(_type, _stat), \
> +	.stat_offset = offsetof(_type, _stat) \
> +}
> +
> +/* Helper macro for defining some statistics related to queues */
> +#define IECM_QUEUE_STAT(_name, _stat) \
> +	IECM_STAT(struct iecm_queue, _name, _stat)
> +
> +/* Stats associated with a Tx queue */
> +static const struct iecm_stats iecm_gstrings_tx_queue_stats[] = {
> +	IECM_QUEUE_STAT("packets", q_stats.tx.packets),
> +	IECM_QUEUE_STAT("bytes", q_stats.tx.bytes),
> +	IECM_QUEUE_STAT("lso_pkts", q_stats.tx.lso_pkts),
> +};
> +
> +/* Stats associated with an Rx queue */
> +static const struct iecm_stats iecm_gstrings_rx_queue_stats[] = {
> +	IECM_QUEUE_STAT("packets", q_stats.rx.packets),
> +	IECM_QUEUE_STAT("bytes", q_stats.rx.bytes),
> +	IECM_QUEUE_STAT("rsc_pkts", q_stats.rx.rsc_pkts),
> +};
> +
> +#define IECM_TX_QUEUE_STATS_LEN		ARRAY_SIZE(iecm_gstrings_tx_queue_stats)
> +#define IECM_RX_QUEUE_STATS_LEN		ARRAY_SIZE(iecm_gstrings_rx_queue_stats)
> +
> +#define IECM_PORT_STAT(_name, _stat) \
> +	IECM_STAT(struct iecm_vport,  _name, _stat)
> +
> +static const struct iecm_stats iecm_gstrings_port_stats[] = {
> +	IECM_PORT_STAT("port-rx-csum_errors", port_stats.rx_hw_csum_err),
> +	IECM_PORT_STAT("port-rx-hsplit", port_stats.rx_hsplit),
> +	IECM_PORT_STAT("port-rx-hsplit_hbo", port_stats.rx_hsplit_hbo),
> +	IECM_PORT_STAT("tx-linearized_pkts", port_stats.tx_linearize),
> +	IECM_PORT_STAT("rx-bad_descs", port_stats.rx_bad_descs),
> +	IECM_PORT_STAT("port-rx_bytes", port_stats.eth_stats.rx_bytes),
> +	IECM_PORT_STAT("port-rx-unicast_pkts", port_stats.eth_stats.rx_unicast),
> +	IECM_PORT_STAT("port-rx-multicast_pkts", port_stats.eth_stats.rx_multicast),
> +	IECM_PORT_STAT("port-rx-broadcast_pkts", port_stats.eth_stats.rx_broadcast),
> +	IECM_PORT_STAT("port-rx-unknown_protocol", port_stats.eth_stats.rx_unknown_protocol),

94-cols line.

> +	IECM_PORT_STAT("port-tx_bytes", port_stats.eth_stats.tx_bytes),
> +	IECM_PORT_STAT("port-tx-unicast_pkts", port_stats.eth_stats.tx_unicast),
> +	IECM_PORT_STAT("port-tx-multicast_pkts", port_stats.eth_stats.tx_multicast),
> +	IECM_PORT_STAT("port-tx-broadcast_pkts", port_stats.eth_stats.tx_broadcast),
> +	IECM_PORT_STAT("port-tx_errors", port_stats.eth_stats.tx_errors),
> +};
> +
> +#define IECM_PORT_STATS_LEN ARRAY_SIZE(iecm_gstrings_port_stats)
> +
> +struct iecm_priv_flags {
> +	char flag_string[ETH_GSTRING_LEN];
> +	bool read_only;
> +	u32 bitno;
> +};
> +
> +#define IECM_PRIV_FLAG(_name, _bitno, _read_only) { \
> +	.read_only = _read_only, \
> +	.flag_string = _name, \
> +	.bitno = _bitno, \
> +}
> +
> +static const struct iecm_priv_flags iecm_gstrings_priv_flags[] = {
> +	IECM_PRIV_FLAG("header-split", __IECM_PRIV_FLAGS_HDR_SPLIT, 0),
> +};
> +
> +#define IECM_PRIV_FLAGS_STR_LEN ARRAY_SIZE(iecm_gstrings_priv_flags)
> +
> +/**
> + * __iecm_add_qstat_strings - copy stat strings into ethtool buffer
> + * @p: ethtool supplied buffer
> + * @stats: stat definitions array
> + * @size: size of the stats array
> + * @type: stat type
> + * @idx: stat index
> + *
> + * Format and copy the strings described by stats into the buffer pointed at
> + * by p.
> + */
> +static void __iecm_add_qstat_strings(u8 **p, const struct iecm_stats stats[],
> +				     const unsigned int size, const char *type,
> +				     unsigned int idx)
> +{
> +	unsigned int i;
> +
> +	for (i = 0; i < size; i++) {
> +		snprintf((char *)*p, ETH_GSTRING_LEN,
> +			 "%2s-%u.%.17s", type, idx, stats[i].stat_string);
> +		*p += ETH_GSTRING_LEN;
> +	}
> +}
> +
> +/**
> + * iecm_add_qstat_strings - Copy queue stat strings into ethtool buffer
> + * @p: ethtool supplied buffer
> + * @stats: stat definitions array
> + * @type: stat type
> + * @idx: stat idx
> + *
> + * Format and copy the strings described by the const static stats value into
> + * the buffer pointed at by p.
> + *
> + * The parameter @stats is evaluated twice, so parameters with side effects
> + * should be avoided. Additionally, stats must be an array such that
> + * ARRAY_SIZE can be called on it.
> + */
> +#define iecm_add_qstat_strings(p, stats, type, idx) \
> +	__iecm_add_qstat_strings(p, stats, ARRAY_SIZE(stats), type, idx)
> +
> +/**
> + * iecm_add_port_stat_strings - Copy port stat strings into ethtool buffer
> + * @p: ethtool buffer
> + * @stats: struct to copy from
> + */
> +static void iecm_add_port_stat_strings(u8 **p, const struct iecm_stats stats[])
> +{
> +	const unsigned int size = IECM_PORT_STATS_LEN;
> +	unsigned int i;
> +
> +	for (i = 0; i < size; i++) {
> +		snprintf((char *)*p, ETH_GSTRING_LEN, "%.32s",
> +			 stats[i].stat_string);
> +		*p += ETH_GSTRING_LEN;
> +	}
> +}
> +
> +/**
> + * iecm_get_stat_strings - Get stat strings
> + * @netdev: network interface device structure
> + * @data: buffer for string data
> + *
> + * Builds the statistics string table
> + */
> +static void iecm_get_stat_strings(struct net_device *netdev, u8 *data)
> +{
> +	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
> +	unsigned int i;
> +	u16 max_q;
> +
> +	iecm_add_port_stat_strings(&data, iecm_gstrings_port_stats);
> +
> +	/* It's critical that we always report a constant number of strings and
> +	 * that the strings are reported in the same order regardless of how
> +	 * many queues are actually in use.
> +	 */
> +	max_q = vport->adapter->max_queue_limit;
> +
> +	for (i = 0; i < max_q; i++)
> +		iecm_add_qstat_strings(&data, iecm_gstrings_tx_queue_stats,
> +				       "tx", i);
> +	for (i = 0; i < max_q; i++)
> +		iecm_add_qstat_strings(&data, iecm_gstrings_rx_queue_stats,
> +				       "rx", i);
> +}
> +
> +/**
> + * iecm_get_priv_flag_strings - Get private flag strings
> + * @netdev: network interface device structure
> + * @data: buffer for string data
> + *
> + * Builds the private flags string table
> + */
> +static void iecm_get_priv_flag_strings(struct net_device *netdev, u8 *data)
> +{
> +	unsigned int i;
> +
> +	for (i = 0; i < IECM_PRIV_FLAGS_STR_LEN; i++) {
> +		snprintf((char *)data, ETH_GSTRING_LEN, "%s",
> +			 iecm_gstrings_priv_flags[i].flag_string);
> +		data += ETH_GSTRING_LEN;
> +	}
> +}
> +
> +/**
> + * iecm_get_priv_flags - report device private flags
> + * @dev: network interface device structure
> + *
> + * The get string set count and the string set should be matched for each
> + * flag returned.  Add new strings for each flag to the iecm_gstrings_priv_flags
> + * array.
> + *
> + * Returns a u32 bitmap of flags.
> + **/
> +static u32 iecm_get_priv_flags(struct net_device *dev)
> +{
> +	struct iecm_netdev_priv *np = netdev_priv(dev);
> +	struct iecm_user_config_data *user_data;
> +	struct iecm_vport *vport = np->vport;
> +	u32 i, ret_flags = 0;
> +
> +	user_data = &vport->adapter->config_data;
> +
> +	for (i = 0; i < IECM_PRIV_FLAGS_STR_LEN; i++) {
> +		const struct iecm_priv_flags *priv_flag;
> +
> +		priv_flag = &iecm_gstrings_priv_flags[i];
> +
> +		if (test_bit(priv_flag->bitno, user_data->user_flags))
> +			ret_flags |= BIT(i);
> +	}
> +
> +	return ret_flags;
> +}
> +
> +/**
> + * iecm_set_priv_flags - set private flags
> + * @dev: network interface device structure
> + * @flags: bit flags to be set
> + **/
> +static int iecm_set_priv_flags(struct net_device *dev, u32 flags)
> +{
> +	struct iecm_netdev_priv *np = netdev_priv(dev);
> +	DECLARE_BITMAP(change_flags, __IECM_USER_FLAGS_NBITS);
> +	DECLARE_BITMAP(orig_flags, __IECM_USER_FLAGS_NBITS);

RCT.

> +	struct iecm_user_config_data *user_data;
> +	struct iecm_vport *vport = np->vport;
> +	bool is_reset_needed;
> +	int err = 0;
> +	u32 i;
> +
> +	if (flags > BIT(IECM_PRIV_FLAGS_STR_LEN))
> +		return -EINVAL;
> +
> +	user_data = &vport->adapter->config_data;
> +
> +	bitmap_copy(orig_flags, user_data->user_flags, __IECM_USER_FLAGS_NBITS);
> +
> +	for (i = 0; i < IECM_PRIV_FLAGS_STR_LEN; i++) {
> +		const struct iecm_priv_flags *priv_flag;
> +
> +		priv_flag = &iecm_gstrings_priv_flags[i];
> +
> +		if (flags & BIT(i)) {
> +			/* If this is a read-only flag, it can't be changed */
> +			if (priv_flag->read_only)
> +				return -EOPNOTSUPP;
> +
> +			set_bit(priv_flag->bitno, user_data->user_flags);
> +		} else {
> +			clear_bit(priv_flag->bitno, user_data->user_flags);
> +		}
> +	}
> +
> +	bitmap_xor(change_flags, user_data->user_flags,
> +		   orig_flags, __IECM_USER_FLAGS_NBITS);
> +
> +	is_reset_needed =
> +		!!(test_bit(__IECM_PRIV_FLAGS_HDR_SPLIT, change_flags));

Shorter name would allow avoiding line break.

> +
> +	/* Issue reset to cause things to take effect, as additional bits
> +	 * are added we will need to create a mask of bits requiring reset
> +	 */
> +	if (is_reset_needed)
> +		err = iecm_initiate_soft_reset(vport, __IECM_SR_HSPLIT_CHANGE);
> +
> +	return err;
> +}
> +
> +/**
> + * iecm_get_strings - Get string set
> + * @netdev: network interface device structure
> + * @sset: id of string set
> + * @data: buffer for string data
> + *
> + * Builds string tables for various string sets
> + */
> +static void iecm_get_strings(struct net_device *netdev, u32 sset, u8 *data)
> +{
> +	switch (sset) {
> +	case ETH_SS_STATS:
> +		iecm_get_stat_strings(netdev, data);
> +		break;
> +	case ETH_SS_PRIV_FLAGS:
> +		iecm_get_priv_flag_strings(netdev, data);
> +		break;
> +	default:
> +		break;

Equivalent to not having a 'default' case.

> +	}
> +}
> +
> +/**
> + * iecm_get_sset_count - Get length of string set
> + * @netdev: network interface device structure
> + * @sset: id of string set
> + *
> + * Reports size of various string tables.
> + */
> +static
> +int iecm_get_sset_count(struct net_device *netdev, int sset)
> +{
> +	if (sset == ETH_SS_STATS) {
> +		struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
> +		u16 max_q;
> +
> +		/* This size reported back here *must* be constant throughout
> +		 * the lifecycle of the netdevice, i.e. we must report the
> +		 * maximum length even for queues that don't technically exist.
> +		 * This is due to the fact that this userspace API uses three
> +		 * separate ioctl calls to get stats data but has no way to
> +		 * communicate back to userspace when that size has changed,
> +		 * which can typically happen as a result of changing number of
> +		 * queues. If the number/order of stats change in the middle of
> +		 * this call chain it will lead to userspace crashing/accessing
> +		 * bad data through buffer under/overflow.
> +		 */
> +		max_q = vport->adapter->max_queue_limit;
> +
> +		return IECM_PORT_STATS_LEN +
> +		       (IECM_TX_QUEUE_STATS_LEN * max_q) +
> +			(IECM_RX_QUEUE_STATS_LEN * max_q);
> +	} else if (sset == ETH_SS_PRIV_FLAGS) {
> +		return IECM_PRIV_FLAGS_STR_LEN;
> +	} else {
> +		return -EINVAL;

Check for this at first and save 1 level of indentation for
ETH_SS_STATS.

> +	}
> +}
> +
> +/**
> + * iecm_add_one_ethtool_stat - copy the stat into the supplied buffer
> + * @data: location to store the stat value
> + * @pstat: old stat pointer to copy from
> + * @stat: the stat definition
> + *
> + * Copies the stat data defined by the pointer and stat structure pair into
> + * the memory supplied as data. Used to implement iecm_add_ethtool_stats and
> + * iecm_add_queue_stats. If the pointer is null, data will be zero'd.
> + */
> +static void
> +iecm_add_one_ethtool_stat(u64 *data, void *pstat,
> +			  const struct iecm_stats *stat)
> +{
> +	char *p;
> +
> +	if (!pstat) {
> +		/* Ensure that the ethtool data buffer is zero'd for any stats
> +		 * which don't have a valid pointer.
> +		 */
> +		*data = 0;
> +		return;
> +	}
> +
> +	p = (char *)pstat + stat->stat_offset;
> +	switch (stat->sizeof_stat) {
> +	case sizeof(u64):
> +		*data = *((u64 *)p);
> +		break;
> +	case sizeof(u32):
> +		*data = *((u32 *)p);
> +		break;
> +	case sizeof(u16):
> +		*data = *((u16 *)p);
> +		break;
> +	case sizeof(u8):
> +		*data = *((u8 *)p);
> +		break;
> +	default:
> +		WARN_ONCE(1, "unexpected stat size for %s",
> +			  stat->stat_string);
> +		*data = 0;
> +	}
> +}
> +
> +/**
> + * iecm_add_queue_stats - copy queue statistics into supplied buffer
> + * @data: ethtool stats buffer
> + * @q: the queue to copy
> + *
> + * Queue statistics must be copied while protected by
> + * u64_stats_fetch_begin_irq, so we can't directly use iecm_add_ethtool_stats.
> + * Assumes that queue stats are defined in iecm_gstrings_queue_stats. If the
> + * queue pointer is null, zero out the queue stat values and update the data
> + * pointer. Otherwise safely copy the stats from the queue into the supplied
> + * buffer and update the data pointer when finished.
> + *
> + * This function expects to be called while under rcu_read_lock().
> + */
> +static void
> +iecm_add_queue_stats(u64 **data, struct iecm_queue *q)
> +{
> +	const struct iecm_stats *stats;
> +	unsigned int start;
> +	unsigned int size;
> +	unsigned int i;
> +
> +	if (q->q_type == VIRTCHNL2_QUEUE_TYPE_RX) {
> +		size = IECM_RX_QUEUE_STATS_LEN;
> +		stats = iecm_gstrings_rx_queue_stats;
> +	} else {
> +		size = IECM_TX_QUEUE_STATS_LEN;
> +		stats = iecm_gstrings_tx_queue_stats;
> +	}
> +
> +	/* To avoid invalid statistics values, ensure that we keep retrying
> +	 * the copy until we get a consistent value according to
> +	 * u64_stats_fetch_retry_irq. But first, make sure our queue is
> +	 * non-null before attempting to access its syncp.
> +	 */
> +	do {
> +		start = u64_stats_fetch_begin_irq(&q->stats_sync);
> +		for (i = 0; i < size; i++)
> +			iecm_add_one_ethtool_stat(&(*data)[i], q, &stats[i]);
> +	} while (u64_stats_fetch_retry_irq(&q->stats_sync, start));
> +
> +	/* Once we successfully copy the stats in, update the data pointer */
> +	*data += size;
> +}
> +
> +/**
> + * iecm_add_empty_queue_stats - Add stats for a non-existent queue
> + * @data: pointer to data buffer
> + * @qtype: type of data queue
> + *
> + * We must report a constant length of stats back to userspace regardless of
> + * how many queues are actually in use because stats collection happens over
> + * three separate ioctls and there's no way to notify userspace the size
> + * changed between those calls. This adds empty to data to the stats since we
> + * don't have a real queue to refer to for this stats slot.
> + */
> +static void
> +iecm_add_empty_queue_stats(u64 **data, u16 qtype)
> +{
> +	unsigned int i;
> +	int stats_len;
> +
> +	if (qtype == VIRTCHNL2_QUEUE_TYPE_RX)
> +		stats_len = IECM_RX_QUEUE_STATS_LEN;
> +	else
> +		stats_len = IECM_TX_QUEUE_STATS_LEN;
> +
> +	for (i = 0; i < stats_len; i++)
> +		(*data)[i] = 0;
> +	*data += stats_len;
> +}
> +
> +/**
> + * iecm_add_port_stats - Copy port stats into ethtool buffer
> + * @vport: virtual port struct
> + * @data: ethtool buffer to copy into
> + */
> +static void iecm_add_port_stats(struct iecm_vport *vport, u64 **data)
> +{
> +	unsigned int size = IECM_PORT_STATS_LEN;
> +	unsigned int start;
> +	unsigned int i;
> +
> +	/* To avoid invalid statistics values, ensure that we keep retrying
> +	 * the copy until we get a consistent value according to
> +	 * u64_stats_fetch_retry_irq.
> +	 */
> +	do {
> +		start = u64_stats_fetch_begin_irq(&vport->port_stats.stats_sync);
> +		for (i = 0; i < size; i++) {
> +			iecm_add_one_ethtool_stat(&(*data)[i], vport,
> +						  &iecm_gstrings_port_stats[i]);
> +		}

Redundant braces.

> +	} while (u64_stats_fetch_retry_irq(&vport->port_stats.stats_sync, start));
> +
> +	*data += size;
> +}
> +
> +/**
> + * iecm_get_ethtool_stats - report device statistics
> + * @netdev: network interface device structure
> + * @stats: ethtool statistics structure
> + * @data: pointer to data buffer
> + *
> + * All statistics are added to the data buffer as an array of u64.
> + */
> +static void iecm_get_ethtool_stats(struct net_device *netdev,
> +				   struct ethtool_stats __always_unused *stats,

Redundant __always_unused.

> +				   u64 *data)
> +{
> +	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
> +	unsigned int total = 0;
> +	unsigned int i, j;
> +	u16 max_q, qtype;
> +
> +	if (vport->adapter->state != __IECM_UP)
> +		return;
> +
> +	set_bit(__IECM_MB_STATS_PENDING, vport->adapter->flags);
> +
> +	max_q = vport->adapter->max_queue_limit;
> +
> +	rcu_read_lock();
> +
> +	iecm_add_port_stats(vport, &data);
> +
> +	for (i = 0; i < vport->num_txq_grp; i++) {
> +		struct iecm_txq_group *txq_grp = &vport->txq_grps[i];
> +
> +		qtype = VIRTCHNL2_QUEUE_TYPE_TX;
> +
> +		for (j = 0; j < txq_grp->num_txq; j++, total++) {
> +			struct iecm_queue *txq = txq_grp->txqs[j];
> +
> +			if (!txq)
> +				iecm_add_empty_queue_stats(&data, qtype);
> +			else
> +				iecm_add_queue_stats(&data, txq);
> +		}
> +	}
> +	/* It is critical we provide a constant number of stats back to
> +	 * userspace regardless of how many queues are actually in use because
> +	 * there is no way to inform userspace the size has changed between
> +	 * ioctl calls. This will fill in any missing stats with zero.
> +	 */
> +	for (; total < max_q; total++)
> +		iecm_add_empty_queue_stats(&data, VIRTCHNL2_QUEUE_TYPE_TX);
> +	total = 0;
> +
> +	for (i = 0; i < vport->num_rxq_grp; i++) {
> +		struct iecm_rxq_group *rxq_grp = &vport->rxq_grps[i];
> +		int num_rxq;
> +
> +		qtype = VIRTCHNL2_QUEUE_TYPE_RX;
> +
> +		if (iecm_is_queue_model_split(vport->rxq_model))
> +			num_rxq = rxq_grp->splitq.num_rxq_sets;
> +		else
> +			num_rxq = rxq_grp->singleq.num_rxq;
> +
> +		for (j = 0; j < num_rxq; j++, total++) {
> +			struct iecm_queue *rxq;
> +
> +			if (iecm_is_queue_model_split(vport->rxq_model))
> +				rxq = &rxq_grp->splitq.rxq_sets[j]->rxq;
> +			else
> +				rxq = rxq_grp->singleq.rxqs[j];
> +			if (!rxq)
> +				iecm_add_empty_queue_stats(&data, qtype);
> +			else
> +				iecm_add_queue_stats(&data, rxq);
> +		}
> +	}
> +	for (; total < max_q; total++)
> +		iecm_add_empty_queue_stats(&data, VIRTCHNL2_QUEUE_TYPE_RX);
> +	rcu_read_unlock();
> +}
> +
> +/**
> + * iecm_find_rxq - find rxq from q index
> + * @vport: virtual port associated to queue
> + * @q_num: q index used to find queue
> + *
> + * returns pointer to rx queue
> + */
> +static struct iecm_queue *
> +iecm_find_rxq(struct iecm_vport *vport, int q_num)
> +{
> +	struct iecm_queue *rxq;
> +
> +	if (iecm_is_queue_model_split(vport->rxq_model)) {
> +		int q_grp, q_idx;
> +
> +		q_grp = q_num / IECM_DFLT_SPLITQ_RXQ_PER_GROUP;
> +		q_idx = q_num % IECM_DFLT_SPLITQ_RXQ_PER_GROUP;
> +
> +		rxq = &vport->rxq_grps[q_grp].splitq.rxq_sets[q_idx]->rxq;
> +	} else {
> +		rxq = vport->rxq_grps->singleq.rxqs[q_num];
> +	}
> +
> +	return rxq;
> +}
> +
> +/**
> + * iecm_find_txq - find txq from q index
> + * @vport: virtual port associated to queue
> + * @q_num: q index used to find queue
> + *
> + * returns pointer to tx queue
> + */
> +static struct iecm_queue *
> +iecm_find_txq(struct iecm_vport *vport, int q_num)
> +{
> +	struct iecm_queue *txq;
> +
> +	if (iecm_is_queue_model_split(vport->txq_model)) {
> +		int q_grp = q_num / IECM_DFLT_SPLITQ_TXQ_PER_GROUP;
> +
> +		txq = vport->txq_grps[q_grp].complq;
> +	} else {
> +		txq = vport->txqs[q_num];
> +	}
> +
> +	return txq;
> +}
> +
> +/**
> + * __iecm_get_q_coalesce - get ITR values for specific queue
> + * @ec: ethtool structure to fill with driver's coalesce settings
> + * @q: quuee of Rx or Tx
> + */
> +static void
> +__iecm_get_q_coalesce(struct ethtool_coalesce *ec, struct iecm_queue *q)
> +{
> +	if (q->q_type == VIRTCHNL2_QUEUE_TYPE_RX) {
> +		ec->use_adaptive_rx_coalesce =
> +				IECM_ITR_IS_DYNAMIC(q->q_vector->rx_intr_mode);
> +		ec->rx_coalesce_usecs = q->q_vector->rx_itr_value;
> +	} else {
> +		ec->use_adaptive_tx_coalesce =
> +				IECM_ITR_IS_DYNAMIC(q->q_vector->tx_intr_mode);
> +		ec->tx_coalesce_usecs = q->q_vector->tx_itr_value;
> +	}
> +}
> +
> +/**
> + * iecm_get_q_coalesce - get ITR values for specific queue
> + * @netdev: pointer to the netdev associated with this query
> + * @ec: coalesce settings to program the device with
> + * @q_num: update ITR/INTRL (coalesce) settings for this queue number/index
> + *
> + * Return 0 on success, and negative on failure
> + */
> +static int
> +iecm_get_q_coalesce(struct net_device *netdev, struct ethtool_coalesce *ec,
> +		    u32 q_num)
> +{
> +	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
> +
> +	if (vport->adapter->state != __IECM_UP)
> +		return 0;
> +
> +	if (q_num >= vport->num_rxq && q_num >= vport->num_txq)
> +		return -EINVAL;
> +
> +	if (q_num < vport->num_rxq) {
> +		struct iecm_queue *rxq = iecm_find_rxq(vport, q_num);
> +
> +		__iecm_get_q_coalesce(ec, rxq);
> +	}
> +
> +	if (q_num < vport->num_txq) {
> +		struct iecm_queue *txq = iecm_find_txq(vport, q_num);
> +
> +		__iecm_get_q_coalesce(ec, txq);
> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * iecm_get_coalesce - get ITR values as requested by user
> + * @netdev: pointer to the netdev associated with this query
> + * @ec: coalesce settings to be filled
> + * @kec: unused
> + * @extack: unused
> + *
> + * Return 0 on success, and negative on failure
> + */
> +static int
> +iecm_get_coalesce(struct net_device *netdev, struct ethtool_coalesce *ec,
> +		  struct kernel_ethtool_coalesce __always_unused *kec,
> +		  struct netlink_ext_ack __always_unused *extack)

__always_unused.

> +{
> +	/* Return coalesce based on queue number zero */
> +	return iecm_get_q_coalesce(netdev, ec, 0);
> +}
> +
> +/**
> + * iecm_get_per_q_coalesce - get ITR values as requested by user
> + * @netdev: pointer to the netdev associated with this query
> + * @q_num: queue for which the itr values has to retrieved
> + * @ec: coalesce settings to be filled
> + *
> + * Return 0 on success, and negative on failure
> + */
> +
> +static int
> +iecm_get_per_q_coalesce(struct net_device *netdev, u32 q_num,
> +			struct ethtool_coalesce *ec)
> +{
> +	return iecm_get_q_coalesce(netdev, ec, q_num);
> +}
> +
> +/**
> + * __iecm_set_q_coalesce - set ITR values for specific queue
> + * @ec: ethtool structure from user to update ITR settings
> + * @q: queue for which itr values has to be set
> + *
> + * Returns 0 on success, negative otherwise.
> + */
> +static int
> +__iecm_set_q_coalesce(struct ethtool_coalesce *ec, struct iecm_queue *q)
> +{
> +	u32 use_adaptive_coalesce, coalesce_usecs;
> +	struct iecm_q_vector *qv = q->q_vector;
> +	struct iecm_vport *vport;
> +	bool is_dim_ena = false;
> +
> +	vport = q->vport;
> +	if (q->q_type == VIRTCHNL2_QUEUE_TYPE_RX) {
> +		is_dim_ena = IECM_ITR_IS_DYNAMIC(qv->rx_intr_mode);
> +		use_adaptive_coalesce = ec->use_adaptive_rx_coalesce;
> +		coalesce_usecs = ec->rx_coalesce_usecs;
> +	} else {
> +		is_dim_ena = IECM_ITR_IS_DYNAMIC(qv->tx_intr_mode);
> +		use_adaptive_coalesce = ec->use_adaptive_tx_coalesce;
> +		coalesce_usecs = ec->tx_coalesce_usecs;
> +	}
> +
> +	if (coalesce_usecs && use_adaptive_coalesce) {
> +		netdev_info(vport->netdev, "Cannot set coalesce usecs if adaptive enabled\n");
> +		return -EINVAL;
> +	}
> +
> +	if (is_dim_ena && use_adaptive_coalesce)
> +		return 0;
> +
> +	if (coalesce_usecs > IECM_ITR_MAX) {
> +		netdev_info(vport->netdev,
> +			    "Invalid value, %d-usecs range is 0-%d\n",
> +			    coalesce_usecs, IECM_ITR_MAX);
> +		return -EINVAL;
> +	}
> +
> +	if (coalesce_usecs % 2 != 0) {
> +		coalesce_usecs = coalesce_usecs & 0xFFFFFFFE;
> +		netdev_info(vport->netdev, "HW only supports even ITR values, ITR rounded to %d\n",
> +			    coalesce_usecs);
> +	}
> +
> +	if (q->q_type == VIRTCHNL2_QUEUE_TYPE_RX) {
> +		qv->rx_itr_value = coalesce_usecs;
> +		if (use_adaptive_coalesce) {
> +			qv->rx_intr_mode = IECM_ITR_DYNAMIC;
> +		} else {
> +			qv->rx_intr_mode = !IECM_ITR_DYNAMIC;
> +			iecm_vport_intr_write_itr(qv, qv->rx_itr_value,
> +						  false);
> +		}
> +	} else {
> +		qv->tx_itr_value = coalesce_usecs;
> +		if (use_adaptive_coalesce) {
> +			qv->tx_intr_mode = IECM_ITR_DYNAMIC;
> +		} else {
> +			qv->tx_intr_mode = !IECM_ITR_DYNAMIC;
> +			iecm_vport_intr_write_itr(qv, qv->tx_itr_value, true);
> +		}
> +	}
> +	/* Update of static/dynamic itr will be taken care when interrupt is
> +	 * fired
> +	 */
> +	return 0;
> +}
> +
> +/**
> + * iecm_set_q_coalesce - set ITR values for specific queue
> + * @vport: vport associated to the queue that need updating
> + * @ec: coalesce settings to program the device with
> + * @q_num: update ITR/INTRL (coalesce) settings for this queue number/index
> + * @is_rxq: is queue type rx
> + *
> + * Return 0 on success, and negative on failure
> + */
> +static int
> +iecm_set_q_coalesce(struct iecm_vport *vport, struct ethtool_coalesce *ec,
> +		    int q_num, bool is_rxq)
> +{
> +	struct iecm_queue *q;
> +
> +	if (is_rxq)
> +		q = iecm_find_rxq(vport, q_num);
> +	else
> +		q = iecm_find_txq(vport, q_num);
> +
> +	if (q && __iecm_set_q_coalesce(ec, q))
> +		return -EINVAL;
> +
> +	return 0;
> +}
> +
> +/**
> + * iecm_set_coalesce - set ITR values as requested by user
> + * @netdev: pointer to the netdev associated with this query
> + * @ec: coalesce settings to program the device with
> + * @kec: unused
> + * @extack: unused
> + *
> + * Return 0 on success, and negative on failure
> + */
> +static int
> +iecm_set_coalesce(struct net_device *netdev, struct ethtool_coalesce *ec,
> +		  struct kernel_ethtool_coalesce __always_unused *kec,
> +		  struct netlink_ext_ack __always_unused *extack)
> +{
> +	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
> +	int i, err;
> +
> +	if (vport->adapter->state != __IECM_UP)
> +		return 0;
> +
> +	for (i = 0; i < vport->num_txq; i++) {
> +		err = iecm_set_q_coalesce(vport, ec, i, false);
> +		if (err)
> +			return err;
> +	}
> +
> +	for (i = 0; i < vport->num_rxq; i++) {
> +		err = iecm_set_q_coalesce(vport, ec, i, true);
> +		if (err)
> +			return err;
> +	}
> +	return 0;
> +}
> +
> +/**
> + * iecm_set_per_q_coalesce - set ITR values as requested by user
> + * @netdev: pointer to the netdev associated with this query
> + * @q_num: queue for which the itr values has to be set
> + * @ec: coalesce settings to program the device with
> + *
> + * Return 0 on success, and negative on failure
> + */
> +static int
> +iecm_set_per_q_coalesce(struct net_device *netdev, u32 q_num,
> +			struct ethtool_coalesce *ec)
> +{
> +	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
> +	int err;
> +
> +	err = iecm_set_q_coalesce(vport, ec, q_num, false);
> +	if (!err)
> +		err = iecm_set_q_coalesce(vport, ec, q_num, true);
> +
> +	return err;

	err = iecm_set_q_coalesce(vport, ec, q_num, false);
	if (err)
		return err;

	return iecm_set_q_coalesce(vport, ec, q_num, true);

> +}
> +
> +/**
> + * iecm_get_msglevel - Get debug message level
> + * @netdev: network interface device structure
> + *
> + * Returns current debug message level.
> + */
> +static u32 iecm_get_msglevel(struct net_device *netdev)
> +{
> +	struct iecm_adapter *adapter = iecm_netdev_to_adapter(netdev);
> +
> +	return adapter->msg_enable;
> +}
> +
> +/**
> + * iecm_set_msglevel - Set debug message level
> + * @netdev: network interface device structure
> + * @data: message level
> + *
> + * Set current debug message level. Higher values cause the driver to
> + * be noisier.
> + */
> +static void iecm_set_msglevel(struct net_device *netdev, u32 data)
> +{
> +	struct iecm_adapter *adapter = iecm_netdev_to_adapter(netdev);
> +
> +	adapter->msg_enable = data;
> +}
> +
> +/**
> + * iecm_get_link_ksettings - Get Link Speed and Duplex settings
> + * @netdev: network interface device structure
> + * @cmd: ethtool command
> + *
> + * Reports speed/duplex settings.
> + **/
> +static int iecm_get_link_ksettings(struct net_device *netdev,
> +				   struct ethtool_link_ksettings *cmd)
> +{
> +	struct iecm_netdev_priv *np = netdev_priv(netdev);
> +	struct iecm_adapter *adapter = np->vport->adapter;
> +
> +	ethtool_link_ksettings_zero_link_mode(cmd, supported);
> +	cmd->base.autoneg = AUTONEG_DISABLE;
> +	cmd->base.port = PORT_NONE;
> +	cmd->base.duplex = DUPLEX_FULL;
> +
> +	if (adapter->link_speed_mbps) {
> +		cmd->base.speed = adapter->link_speed_mbps;
> +		return 0;
> +	}
> +
> +	/* Set speed and duplex */
> +	switch (adapter->link_speed) {
> +	case VIRTCHNL_LINK_SPEED_40GB:
> +		cmd->base.speed = SPEED_40000;
> +		break;
> +	case VIRTCHNL_LINK_SPEED_25GB:
> +		cmd->base.speed = SPEED_25000;
> +		break;
> +	case VIRTCHNL_LINK_SPEED_20GB:
> +		cmd->base.speed = SPEED_20000;
> +		break;
> +	case VIRTCHNL_LINK_SPEED_10GB:
> +		cmd->base.speed = SPEED_10000;
> +		break;
> +	case VIRTCHNL_LINK_SPEED_1GB:
> +		cmd->base.speed = SPEED_1000;
> +		break;
> +	case VIRTCHNL_LINK_SPEED_100MB:
> +		cmd->base.speed = SPEED_100;
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct ethtool_ops iecm_ethtool_ops = {
> +	.supported_coalesce_params = ETHTOOL_COALESCE_USECS |
> +				     ETHTOOL_COALESCE_USE_ADAPTIVE,
> +	.get_msglevel		= iecm_get_msglevel,
> +	.set_msglevel		= iecm_set_msglevel,
> +	.get_coalesce		= iecm_get_coalesce,
> +	.set_coalesce		= iecm_set_coalesce,
> +	.get_per_queue_coalesce = iecm_get_per_q_coalesce,
> +	.set_per_queue_coalesce = iecm_set_per_q_coalesce,
> +	.get_ethtool_stats	= iecm_get_ethtool_stats,
> +	.get_strings		= iecm_get_strings,
> +	.get_sset_count		= iecm_get_sset_count,
> +	.get_priv_flags		= iecm_get_priv_flags,
> +	.set_priv_flags		= iecm_set_priv_flags,
> +	.get_rxnfc		= iecm_get_rxnfc,
> +	.set_rxnfc		= iecm_set_rxnfc,
> +	.get_rxfh_key_size	= iecm_get_rxfh_key_size,
> +	.get_rxfh_indir_size	= iecm_get_rxfh_indir_size,
> +	.get_rxfh		= iecm_get_rxfh,
> +	.set_rxfh		= iecm_set_rxfh,
> +	.get_channels		= iecm_get_channels,
> +	.set_channels		= iecm_set_channels,
> +	.get_ringparam		= iecm_get_ringparam,
> +	.set_ringparam		= iecm_set_ringparam,
> +	.get_link_ksettings	= iecm_get_link_ksettings,
> +};
> +
> +/**
> + * iecm_set_ethtool_ops - Initialize ethtool ops struct
> + * @netdev: network interface device structure
> + *
> + * Sets ethtool ops struct in our netdev so that ethtool can call
> + * our functions.
> + */
> +void iecm_set_ethtool_ops(struct net_device *netdev)
> +{
> +	netdev->ethtool_ops = &iecm_ethtool_ops;
> +}

Declaring @iecm_ethtool_ops as external and directly assigning it
in iecm_cfg_netdev() would result in smaller code size than this.

> diff --git a/drivers/net/ethernet/intel/iecm/iecm_lib.c b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> index cbde65f1c523..c5900723b018 100644
> --- a/drivers/net/ethernet/intel/iecm/iecm_lib.c
> +++ b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> @@ -871,6 +871,7 @@ static int iecm_cfg_netdev(struct iecm_vport *vport)
>  	netdev->hw_features |= dflt_features | offloads;
>  	netdev->hw_enc_features |= dflt_features | offloads;
>  
> +	iecm_set_ethtool_ops(netdev);
>  	SET_NETDEV_DEV(netdev, &adapter->pdev->dev);
>  
>  	/* carrier off on init to avoid Tx hangs */
> @@ -1150,7 +1151,15 @@ iecm_vport_alloc(struct iecm_adapter *adapter, int vport_id)
>   */
>  static void iecm_statistics_task(struct work_struct *work)
>  {
> -	/* stub */
> +	struct iecm_adapter *adapter = container_of(work,
> +						    struct iecm_adapter,
> +						    stats_task.work);

	struct iecm_adapter *adapter;

	adapter = container_of(work, typeof(*adapter), stats_task.work);

This fits into 79 without a line wrap.

> +	if (test_bit(__IECM_MB_STATS_PENDING, adapter->flags) &&
> +	    !test_bit(__IECM_HR_RESET_IN_PROG, adapter->flags))
> +		adapter->dev_ops.vc_ops.get_stats_msg(adapter->vports[0]);
> +
> +	queue_delayed_work(adapter->stats_wq, &adapter->stats_task,
> +			   msecs_to_jiffies(1000));
>  }
>  
>  /**
> diff --git a/drivers/net/ethernet/intel/include/iecm.h b/drivers/net/ethernet/intel/include/iecm.h
> index 97c9935b832d..d118da1ea8cd 100644
> --- a/drivers/net/ethernet/intel/include/iecm.h
> +++ b/drivers/net/ethernet/intel/include/iecm.h
> @@ -745,6 +745,7 @@ int iecm_recv_mb_msg(struct iecm_adapter *adapter, enum virtchnl_ops op,
>  		     void *msg, int msg_size);
>  int iecm_send_mb_msg(struct iecm_adapter *adapter, enum virtchnl_ops op,
>  		     u16 msg_size, u8 *msg);
> +void iecm_set_ethtool_ops(struct net_device *netdev);
>  void iecm_vport_set_hsplit(struct iecm_vport *vport, bool ena);
>  void iecm_add_del_ether_addrs(struct iecm_vport *vport, bool add, bool async);
>  int iecm_set_promiscuous(struct iecm_adapter *adapter);
> -- 
> 2.33.0

Thanks,
Al


^ permalink raw reply	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 16/19] iecm: implement flow director
  2022-01-28  0:10 ` [Intel-wired-lan] [PATCH net-next 16/19] iecm: implement flow director Alan Brady
@ 2022-01-28 19:04   ` Alexander Lobakin
  2022-02-03  2:41     ` Brady, Alan
  0 siblings, 1 reply; 89+ messages in thread
From: Alexander Lobakin @ 2022-01-28 19:04 UTC (permalink / raw)
  To: intel-wired-lan

From: Alan Brady <alan.brady@intel.com>
Date: Thu, 27 Jan 2022 16:10:06 -0800

> From: Haiyue Wang <haiyue.wang@intel.com>
> 
> This adds everthing needed to do flow director commands.
> 
> Signed-off-by: Haiyue Wang <haiyue.wang@intel.com>
> ---
>  .../net/ethernet/intel/iecm/iecm_ethtool.c    |   17 +-
>  drivers/net/ethernet/intel/iecm/iecm_lib.c    | 1528 ++++++++++++++++-
>  .../net/ethernet/intel/iecm/iecm_virtchnl.c   |  119 ++
>  drivers/net/ethernet/intel/include/iecm.h     |  112 ++
>  4 files changed, 1770 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/net/ethernet/intel/iecm/iecm_ethtool.c b/drivers/net/ethernet/intel/iecm/iecm_ethtool.c
> index 32d905fb1bb6..7e252b25e02d 100644
> --- a/drivers/net/ethernet/intel/iecm/iecm_ethtool.c
> +++ b/drivers/net/ethernet/intel/iecm/iecm_ethtool.c
> @@ -15,6 +15,7 @@ static int iecm_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd,
>  			  u32 __always_unused *rule_locs)
>  {
>  	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
> +	struct iecm_adapter *adapter = vport->adapter;
>  	int ret = -EOPNOTSUPP;
>  
>  	switch (cmd->cmd) {
> @@ -23,14 +24,19 @@ static int iecm_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd,
>  		ret = 0;
>  		break;
>  	case ETHTOOL_GRXCLSRLCNT:
> -		/* stub */
> +		if (!iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
> +				     VIRTCHNL2_CAP_FDIR))
> +			break;
> +		cmd->rule_cnt =
> +			adapter->config_data.fdir_config.num_active_filters;
> +		cmd->data = IECM_MAX_FDIR_FILTERS;
>  		ret = 0;
>  		break;
>  	case ETHTOOL_GRXCLSRULE:
> -		/* stub */
> +		ret = iecm_get_fdir_fltr_entry(vport, cmd);
>  		break;
>  	case ETHTOOL_GRXCLSRLALL:
> -		/* stub */
> +		ret = iecm_get_fdir_fltr_ids(vport, cmd, (u32 *)rule_locs);
>  		break;
>  	case ETHTOOL_GRXFH:
>  		/* stub */
> @@ -51,14 +57,15 @@ static int iecm_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd,
>   */
>  static int iecm_set_rxnfc(struct net_device *netdev, struct ethtool_rxnfc *cmd)
>  {
> +	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
>  	int ret = -EOPNOTSUPP;
>  
>  	switch (cmd->cmd) {
>  	case ETHTOOL_SRXCLSRLINS:
> -		/* stub */
> +		ret = iecm_add_fdir_fltr(vport, cmd);
>  		break;
>  	case ETHTOOL_SRXCLSRLDEL:
> -		/* stub */
> +		ret = iecm_del_fdir_fltr(vport, cmd);
>  		break;
>  	case ETHTOOL_SRXFH:
>  		/* stub */
> diff --git a/drivers/net/ethernet/intel/iecm/iecm_lib.c b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> index c5900723b018..35c0cbc42ebe 100644
> --- a/drivers/net/ethernet/intel/iecm/iecm_lib.c
> +++ b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> @@ -967,6 +967,56 @@ static void iecm_remove_vlan_filters(struct iecm_vport *vport)
>  	}
>  }
>  
> +/**
> + * iecm_remove_fdir_filters - Remove all Flow Director filters
> + * @vport: vport structure
> + */
> +static void iecm_remove_fdir_filters(struct iecm_vport *vport)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct iecm_fdir_fltr_config *fdir_config;
> +
> +	if (!iecm_is_cap_ena(adapter, IECM_OTHER_CAPS, VIRTCHNL2_CAP_FDIR))
> +		return;
> +
> +	fdir_config = &adapter->config_data.fdir_config;
> +	if (!list_empty(&fdir_config->fdir_fltr_list)) {
> +		struct iecm_fdir_fltr *fdir;
> +
> +		spin_lock_bh(&adapter->fdir_fltr_list_lock);
> +		list_for_each_entry(fdir, &fdir_config->fdir_fltr_list, list) {
> +			fdir->remove = true;
> +		}

Braces are redundant here.

> +		spin_unlock_bh(&adapter->fdir_fltr_list_lock);
> +		iecm_send_del_fdir_filter_msg(vport);
> +	}

	if (list_empty())
		return;

	spin_lock_bh(...

-1 indent level.

> +}
> +
> +/**
> + * iecm_del_all_fdir_filters - delete all Flow Director filters
> + * @vport: vport structure
> + *
> + * This function will loop through the list of Flow Director filters and
> + * deletes them.
> + **/
> +static void iecm_del_all_fdir_filters(struct iecm_vport *vport)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct iecm_fdir_fltr_config *fdir_config;
> +	struct iecm_fdir_fltr *fdir, *fdir_tmp;
> +
> +	fdir_config = &adapter->config_data.fdir_config;
> +
> +	spin_lock_bh(&adapter->fdir_fltr_list_lock);
> +	list_for_each_entry_safe(fdir, fdir_tmp, &fdir_config->fdir_fltr_list,
> +				 list) {
> +		list_del(&fdir->list);
> +		kfree(fdir);
> +	}
> +	fdir_config->num_active_filters = 0;
> +	spin_unlock_bh(&adapter->fdir_fltr_list_lock);
> +}
> +
>  /**
>   * iecm_vport_stop - Disable a vport
>   * @vport: vport to disable
> @@ -997,6 +1047,8 @@ static void iecm_vport_stop(struct iecm_vport *vport)
>  	if (!test_bit(__IECM_REL_RES_IN_PROG, adapter->flags))
>  		iecm_remove_vlan_filters(vport);
>  
> +	iecm_remove_fdir_filters(vport);
> +
>  	adapter->link_up = false;
>  	iecm_vport_intr_deinit(vport);
>  	iecm_vport_intr_rel(vport);
> @@ -1206,6 +1258,28 @@ static void iecm_restore_vlans(struct iecm_vport *vport)
>  		iecm_set_all_vlans(vport);
>  }
>  
> +/**
> + * iecm_restore_fdir_filters - Restore all Flow Director filters
> + * @vport: vport structure
> + */
> +static void iecm_restore_fdir_filters(struct iecm_vport *vport)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct iecm_fdir_fltr_config *fdir_config;
> +
> +	fdir_config = &adapter->config_data.fdir_config;
> +	if (!list_empty(&fdir_config->fdir_fltr_list)) {
> +		struct iecm_fdir_fltr *fdir;
> +
> +		spin_lock_bh(&adapter->fdir_fltr_list_lock);
> +		list_for_each_entry(fdir, &fdir_config->fdir_fltr_list, list) {
> +			fdir->add = true;
> +		}

Braces are redundant for one-liners.

> +		spin_unlock_bh(&adapter->fdir_fltr_list_lock);
> +		iecm_send_add_fdir_filter_msg(vport);
> +	}

Invert the condition -> -1 indent level.

> +}
> +
>  /**
>   * iecm_restore_features - Restore feature configs
>   * @vport: virtual port structure
> @@ -1227,6 +1301,9 @@ static void iecm_restore_features(struct iecm_vport *vport)
>  		if (iecm_set_promiscuous(adapter))
>  			dev_info(&adapter->pdev->dev, "Failed to restore promiscuous settings\n");
>  	}
> +
> +	if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS, VIRTCHNL2_CAP_FDIR))
> +		iecm_restore_fdir_filters(vport);
>  }
>  
>  /**
> @@ -2014,6 +2091,7 @@ int iecm_probe(struct pci_dev *pdev,
>  	INIT_LIST_HEAD(&adapter->config_data.mac_filter_list);
>  	INIT_LIST_HEAD(&adapter->config_data.vlan_filter_list);
>  	INIT_LIST_HEAD(&adapter->config_data.adv_rss_list);
> +	INIT_LIST_HEAD(&adapter->config_data.fdir_config.fdir_fltr_list);
>  
>  	INIT_DELAYED_WORK(&adapter->stats_task, iecm_statistics_task);
>  	INIT_DELAYED_WORK(&adapter->serv_task, iecm_service_task);
> @@ -2052,7 +2130,17 @@ EXPORT_SYMBOL(iecm_probe);
>   */
>  static void iecm_del_user_cfg_data(struct iecm_adapter *adapter)
>  {
> -	/* stub */
> +	int i;
> +
> +	if (!adapter->vports)
> +		return;
> +
> +	for (i = 0; i < adapter->num_alloc_vport; i++) {
> +		if (!adapter->vports[i])
> +			continue;
> +
> +		iecm_del_all_fdir_filters(adapter->vports[i]);
> +	}
>  }
>  
>  /**
> @@ -2647,6 +2735,1444 @@ static int iecm_setup_tc(struct net_device *netdev, enum tc_setup_type type,
>  	return err;
>  }
>  
> +/**
> + * iecm_pkt_udp_no_pay_len - the length of UDP packet without payload
> + * @fltr: Flow Director filter data structure
> + */
> +static u16 iecm_pkt_udp_no_pay_len(struct iecm_fdir_fltr *fltr)
> +{
> +	return sizeof(struct ethhdr) +
> +		(fltr->ip_ver == 4 ? sizeof(struct iphdr)

IPv4 hdr may have extensions.

> +				   : sizeof(struct ipv6hdr)) +
> +		sizeof(struct udphdr);
> +}
> +
> +/**
> + * iecm_fill_fdir_gtpu_hdr - fill the GTP-U protocol header
> + * @fltr: Flow Director filter data structure
> + * @proto_hdrs: Flow Director protocol headers data structure
> + *
> + * Returns 0 if the GTP-U protocol header is set successfully
> + */
> +static int
> +iecm_fill_fdir_gtpu_hdr(struct iecm_fdir_fltr *fltr,
> +			struct virtchnl_proto_hdrs *proto_hdrs)
> +{
> +	struct virtchnl_proto_hdr *uhdr = &proto_hdrs->proto_hdr[proto_hdrs->count - 1];
> +	struct virtchnl_proto_hdr *ghdr = &proto_hdrs->proto_hdr[proto_hdrs->count++];

Lines over 79.

> +	struct virtchnl_proto_hdr *ehdr = NULL;
> +	u16 adj_offs, hdr_offs;
> +	int i;
> +
> +	VIRTCHNL_SET_PROTO_HDR_TYPE(ghdr, GTPU_IP);
> +
> +	adj_offs = iecm_pkt_udp_no_pay_len(fltr);
> +
> +	for (i = 0; i < fltr->flex_cnt; i++) {
> +#define IECM_GTPU_HDR_TEID_OFFS0	4
> +#define IECM_GTPU_HDR_TEID_OFFS1	6
> +#define IECM_GTPU_HDR_N_PDU_AND_NEXT_EXTHDR_OFFS	10
> +#define IECM_GTPU_HDR_NEXT_EXTHDR_TYPE_MASK		0x00FF /* skip N_PDU */
> +/* PDU Session Container Extension Header (PSC) */
> +#define IECM_GTPU_PSC_EXTHDR_TYPE			0x85
> +#define IECM_GTPU_HDR_PSC_PDU_TYPE_AND_QFI_OFFS		13
> +#define IECM_GTPU_HDR_PSC_PDU_QFI_MASK			0x3F /* skip Type */
> +#define IECM_GTPU_EH_QFI_IDX				1

It's better to define them outside the function.

> +
> +		if (fltr->flex_words[i].offset < adj_offs)
> +			return -EINVAL;
> +
> +		hdr_offs = fltr->flex_words[i].offset - adj_offs;
> +
> +		switch (hdr_offs) {
> +		case IECM_GTPU_HDR_TEID_OFFS0:
> +		case IECM_GTPU_HDR_TEID_OFFS1: {
> +			__be16 *pay_word = (__force __be16 *)ghdr->buffer;

I'd declare @pay_word outside of switch-case (which is recommended)
to remove braces around the body.

> +
> +			pay_word[hdr_offs >> 1] =
> +					htons(fltr->flex_words[i].word);
> +			VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(ghdr, GTPU_IP, TEID);
> +			}
> +			break;
> +		case IECM_GTPU_HDR_N_PDU_AND_NEXT_EXTHDR_OFFS:
> +			if ((fltr->flex_words[i].word &
> +			     IECM_GTPU_HDR_NEXT_EXTHDR_TYPE_MASK) !=
> +						IECM_GTPU_PSC_EXTHDR_TYPE)
> +				return -EOPNOTSUPP;
> +			if (!ehdr)
> +				ehdr =
> +				   &proto_hdrs->proto_hdr[proto_hdrs->count++];
> +			VIRTCHNL_SET_PROTO_HDR_TYPE(ehdr, GTPU_EH);
> +			break;
> +		case IECM_GTPU_HDR_PSC_PDU_TYPE_AND_QFI_OFFS:
> +			if (!ehdr)
> +				return -EINVAL;
> +			ehdr->buffer[IECM_GTPU_EH_QFI_IDX] =
> +					fltr->flex_words[i].word &
> +						IECM_GTPU_HDR_PSC_PDU_QFI_MASK;

Inconsistent indentation here, filtr-> and IECM_ should be on the
same level.

> +			VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(ehdr, GTPU_EH, QFI);
> +			break;
> +		default:
> +			return -EINVAL;
> +		}
> +	}
> +
> +	/* The PF ignores the UDP header fields */
> +	uhdr->field_selector = 0;
> +
> +	return 0;
> +}
> +
> +/**
> + * iecm_fill_fdir_pfcp_hdr - fill the PFCP protocol header
> + * @fltr: Flow Director filter data structure
> + * @proto_hdrs: Flow Director protocol headers data structure
> + *
> + * Returns 0 if the PFCP protocol header is set successfully
> + */
> +static int
> +iecm_fill_fdir_pfcp_hdr(struct iecm_fdir_fltr *fltr,
> +			struct virtchnl_proto_hdrs *proto_hdrs)
> +{
> +	struct virtchnl_proto_hdr *uhdr =
> +				&proto_hdrs->proto_hdr[proto_hdrs->count - 1];
> +	struct virtchnl_proto_hdr *hdr =
> +				&proto_hdrs->proto_hdr[proto_hdrs->count++];

Both could be declared and assigned separately to avoid line breaks.

> +	u16 adj_offs, hdr_offs;
> +	int i;
> +
> +	VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, PFCP);
> +
> +	adj_offs = iecm_pkt_udp_no_pay_len(fltr);
> +
> +	for (i = 0; i < fltr->flex_cnt; i++) {
> +#define IECM_PFCP_HDR_SFIELD_AND_MSG_TYPE_OFFS	0

Better to define outside the function.

> +		if (fltr->flex_words[i].offset < adj_offs)
> +			return -EINVAL;
> +
> +		hdr_offs = fltr->flex_words[i].offset - adj_offs;
> +
> +		switch (hdr_offs) {
> +		case IECM_PFCP_HDR_SFIELD_AND_MSG_TYPE_OFFS:
> +			hdr->buffer[0] = (fltr->flex_words[i].word >> 8) & 0xff;

>> 8 and & 0xff are candidates for FIELD_GET(GENMASK(15, 8)).

> +			VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, PFCP, S_FIELD);
> +			break;
> +		default:
> +			return -EINVAL;
> +		}
> +	}
> +
> +	/* The PF ignores the UDP header fields */
> +	uhdr->field_selector = 0;
> +
> +	return 0;
> +}
> +
> +/**
> + * iecm_fill_fdir_nat_t_esp_hdr - fill the NAT-T-ESP protocol header
> + * @fltr: Flow Director filter data structure
> + * @proto_hdrs: Flow Director protocol headers data structure
> + *
> + * Returns 0 if the NAT-T-ESP protocol header is set successfully
> + */
> +static int
> +iecm_fill_fdir_nat_t_esp_hdr(struct iecm_fdir_fltr *fltr,
> +			     struct virtchnl_proto_hdrs *proto_hdrs)
> +{
> +	struct virtchnl_proto_hdr *uhdr =
> +				&proto_hdrs->proto_hdr[proto_hdrs->count - 1];
> +	struct virtchnl_proto_hdr *hdr =
> +				&proto_hdrs->proto_hdr[proto_hdrs->count++];

Same here about line wraps.

> +	u16 adj_offs, hdr_offs;
> +	u32 spi = 0;
> +	int i;
> +
> +	VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, ESP);
> +
> +	adj_offs = iecm_pkt_udp_no_pay_len(fltr);
> +
> +	for (i = 0; i < fltr->flex_cnt; i++) {
> +#define IECM_NAT_T_ESP_SPI_OFFS0	0
> +#define IECM_NAT_T_ESP_SPI_OFFS1	2

Same here about definitions. It would be harder to look for them
later (not counting grep and Elixir).

> +		if (fltr->flex_words[i].offset < adj_offs)
> +			return -EINVAL;
> +
> +		hdr_offs = fltr->flex_words[i].offset - adj_offs;
> +
> +		switch (hdr_offs) {
> +		case IECM_NAT_T_ESP_SPI_OFFS0:
> +			spi |= fltr->flex_words[i].word << 16;
> +			break;
> +		case IECM_NAT_T_ESP_SPI_OFFS1:
> +			spi |= fltr->flex_words[i].word;
> +			break;
> +		default:
> +			return -EINVAL;
> +		}
> +	}
> +
> +	/* Not support IKE Header Format with SPI 0 */
> +	if (!spi)
> +		return -EOPNOTSUPP;
> +
> +	*(__force __be32 *)hdr->buffer = htonl(spi);
> +	VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, ESP, SPI);
> +
> +	/* The PF ignores the UDP header fields */
> +	uhdr->field_selector = 0;
> +
> +	return 0;
> +}
> +
> +/**
> + * iecm_fill_fdir_udp_flex_pay_hdr - fill the UDP payload header
> + * @fltr: Flow Director filter data structure
> + * @proto_hdrs: Flow Director protocol headers data structure
> + *
> + * Returns 0 if the UDP payload defined protocol header is set successfully
> + */
> +static int
> +iecm_fill_fdir_udp_flex_pay_hdr(struct iecm_fdir_fltr *fltr,
> +				struct virtchnl_proto_hdrs *proto_hdrs)
> +{
> +#define IECM_GTPU_PORT		2152
> +#define IECM_NAT_T_ESP_PORT	4500
> +#define IECM_PFCP_PORT		8805

Same for those, they don't belong here at all.

> +	int err;
> +
> +	switch (ntohs(fltr->ip_data.dst_port)) {
> +	case IECM_GTPU_PORT:
> +		err = iecm_fill_fdir_gtpu_hdr(fltr, proto_hdrs);
> +		break;
> +	case IECM_NAT_T_ESP_PORT:
> +		err = iecm_fill_fdir_nat_t_esp_hdr(fltr, proto_hdrs);
> +		break;
> +	case IECM_PFCP_PORT:
> +		err = iecm_fill_fdir_pfcp_hdr(fltr, proto_hdrs);
> +		break;
> +	default:
> +		err = -EOPNOTSUPP;
> +		break;

All of those should be converted to just

		return iecm_ ...

directly, rather than assigning it to err just to return err.

> +	}
> +
> +	return err;
> +}
> +
> +/**
> + * iecm_fill_fdir_ip4_hdr - fill the IPv4 protocol header
> + * @fltr: Flow Director filter data structure
> + * @proto_hdrs: Flow Director protocol headers data structure
> + *
> + * Returns 0 if the IPv4 protocol header is set successfully
> + */
> +static int iecm_fill_fdir_ip4_hdr(struct iecm_fdir_fltr *fltr,
> +				  struct virtchnl_proto_hdrs *proto_hdrs)
> +{
> +	struct virtchnl_proto_hdr *hdr =
> +				&proto_hdrs->proto_hdr[proto_hdrs->count++];

Same for this about line break.

> +	struct iphdr *iph = (struct iphdr *)hdr->buffer;
> +
> +	VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, IPV4);
> +
> +	if (fltr->ip_mask.tos == U8_MAX) {
> +		iph->tos = fltr->ip_data.tos;
> +		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, IPV4, DSCP);
> +	}
> +
> +	if (fltr->ip_mask.proto == U8_MAX) {
> +		iph->protocol = fltr->ip_data.proto;
> +		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, IPV4, PROT);
> +	}
> +
> +	if (fltr->ip_mask.v4_addrs.src_ip == htonl(U32_MAX)) {
> +		iph->saddr = fltr->ip_data.v4_addrs.src_ip;
> +		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, IPV4, SRC);
> +	}
> +
> +	if (fltr->ip_mask.v4_addrs.dst_ip == htonl(U32_MAX)) {
> +		iph->daddr = fltr->ip_data.v4_addrs.dst_ip;
> +		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, IPV4, DST);
> +	}
> +
> +	fltr->ip_ver = 4;
> +
> +	return 0;
> +}
> +
> +/**
> + * iecm_fill_fdir_ip6_hdr - fill the IPv6 protocol header
> + * @fltr: Flow Director filter data structure
> + * @proto_hdrs: Flow Director protocol headers data structure
> + *
> + * Returns 0 if the IPv6 protocol header is set successfully
> + */
> +static int
> +iecm_fill_fdir_ip6_hdr(struct iecm_fdir_fltr *fltr,
> +		       struct virtchnl_proto_hdrs *proto_hdrs)
> +{
> +	static const struct in6_addr ipv6_addr_full_mask = {
> +		.in6_u = {
> +			.u6_addr8 = {
> +				0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
> +				0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,

Lowercase is generally more preferred for hex constants.

> +			}
> +		}
> +	};
> +	struct virtchnl_proto_hdr *hdr =
> +				&proto_hdrs->proto_hdr[proto_hdrs->count++];

Same.

> +	struct ipv6hdr *iph = (struct ipv6hdr *)hdr->buffer;
> +
> +	VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, IPV6);
> +
> +	if (fltr->ip_mask.tclass == U8_MAX) {
> +		iph->priority = (fltr->ip_data.tclass >> 4) & 0xF;
> +		iph->flow_lbl[0] = (fltr->ip_data.tclass << 4) & 0xF0;
> +		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, IPV6, TC);
> +	}
> +
> +	if (fltr->ip_mask.proto == U8_MAX) {
> +		iph->nexthdr = fltr->ip_data.proto;
> +		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, IPV6, PROT);
> +	}
> +
> +	if (!memcmp(&fltr->ip_mask.v6_addrs.src_ip, &ipv6_addr_full_mask,
> +		    sizeof(struct in6_addr))) {
> +		memcpy(&iph->saddr, &fltr->ip_data.v6_addrs.src_ip,
> +		       sizeof(struct in6_addr));
> +		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, IPV6, SRC);
> +	}
> +
> +	if (!memcmp(&fltr->ip_mask.v6_addrs.dst_ip, &ipv6_addr_full_mask,
> +		    sizeof(struct in6_addr))) {
> +		memcpy(&iph->daddr, &fltr->ip_data.v6_addrs.dst_ip,
> +		       sizeof(struct in6_addr));
> +		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, IPV6, DST);
> +	}
> +
> +	fltr->ip_ver = 6;
> +
> +	return 0;
> +}
> +
> +/**
> + * iecm_fill_fdir_tcp_hdr - fill the TCP protocol header
> + * @fltr: Flow Director filter data structure
> + * @proto_hdrs: Flow Director protocol headers data structure
> + *
> + * Returns 0 if the TCP protocol header is set successfully
> + */
> +static int
> +iecm_fill_fdir_tcp_hdr(struct iecm_fdir_fltr *fltr,
> +		       struct virtchnl_proto_hdrs *proto_hdrs)
> +{
> +	struct virtchnl_proto_hdr *hdr =
> +				&proto_hdrs->proto_hdr[proto_hdrs->count++];

Same.

> +	struct tcphdr *tcph = (struct tcphdr *)hdr->buffer;
> +
> +	VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, TCP);
> +
> +	if (fltr->ip_mask.src_port == htons(U16_MAX)) {
> +		tcph->source = fltr->ip_data.src_port;
> +		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, TCP, SRC_PORT);
> +	}
> +
> +	if (fltr->ip_mask.dst_port == htons(U16_MAX)) {
> +		tcph->dest = fltr->ip_data.dst_port;
> +		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, TCP, DST_PORT);
> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * iecm_fill_fdir_udp_hdr - fill the UDP protocol header
> + * @fltr: Flow Director filter data structure
> + * @proto_hdrs: Flow Director protocol headers data structure
> + *
> + * Returns 0 if the UDP protocol header is set successfully
> + */
> +static int
> +iecm_fill_fdir_udp_hdr(struct iecm_fdir_fltr *fltr,
> +		       struct virtchnl_proto_hdrs *proto_hdrs)
> +{
> +	struct virtchnl_proto_hdr *hdr =
> +				&proto_hdrs->proto_hdr[proto_hdrs->count++];

...

> +	struct udphdr *udph = (struct udphdr *)hdr->buffer;
> +
> +	VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, UDP);
> +
> +	if (fltr->ip_mask.src_port == htons(U16_MAX)) {
> +		udph->source = fltr->ip_data.src_port;
> +		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, UDP, SRC_PORT);
> +	}
> +
> +	if (fltr->ip_mask.dst_port == htons(U16_MAX)) {
> +		udph->dest = fltr->ip_data.dst_port;
> +		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, UDP, DST_PORT);
> +	}
> +
> +	if (!fltr->flex_cnt)
> +		return 0;
> +
> +	return iecm_fill_fdir_udp_flex_pay_hdr(fltr, proto_hdrs);
> +}
> +
> +/**
> + * iecm_fill_fdir_sctp_hdr - fill the SCTP protocol header
> + * @fltr: Flow Director filter data structure
> + * @proto_hdrs: Flow Director protocol headers data structure
> + *
> + * Returns 0 if the SCTP protocol header is set successfully
> + */
> +static int
> +iecm_fill_fdir_sctp_hdr(struct iecm_fdir_fltr *fltr,
> +			struct virtchnl_proto_hdrs *proto_hdrs)
> +{
> +	struct virtchnl_proto_hdr *hdr =
> +				&proto_hdrs->proto_hdr[proto_hdrs->count++];

...

> +	struct sctphdr *sctph = (struct sctphdr *)hdr->buffer;
> +
> +	VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, SCTP);
> +
> +	if (fltr->ip_mask.src_port == htons(U16_MAX)) {
> +		sctph->source = fltr->ip_data.src_port;
> +		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, SCTP, SRC_PORT);
> +	}
> +
> +	if (fltr->ip_mask.dst_port == htons(U16_MAX)) {
> +		sctph->dest = fltr->ip_data.dst_port;
> +		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, SCTP, DST_PORT);
> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * iecm_fill_fdir_ah_hdr - fill the AH protocol header
> + * @fltr: Flow Director filter data structure
> + * @proto_hdrs: Flow Director protocol headers data structure
> + *
> + * Returns 0 if the AH protocol header is set successfully
> + */
> +static int
> +iecm_fill_fdir_ah_hdr(struct iecm_fdir_fltr *fltr,
> +		      struct virtchnl_proto_hdrs *proto_hdrs)
> +{
> +	struct virtchnl_proto_hdr *hdr =
> +				&proto_hdrs->proto_hdr[proto_hdrs->count++];

...

> +	struct ip_auth_hdr *ah = (struct ip_auth_hdr *)hdr->buffer;
> +
> +	VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, AH);
> +
> +	if (fltr->ip_mask.spi == htonl(U32_MAX)) {
> +		ah->spi = fltr->ip_data.spi;
> +		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, AH, SPI);
> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * iecm_fill_fdir_esp_hdr - fill the ESP protocol header
> + * @fltr: Flow Director filter data structure
> + * @proto_hdrs: Flow Director protocol headers data structure
> + *
> + * Returns 0 if the ESP protocol header is set successfully
> + */
> +static int
> +iecm_fill_fdir_esp_hdr(struct iecm_fdir_fltr *fltr,
> +		       struct virtchnl_proto_hdrs *proto_hdrs)
> +{
> +	struct virtchnl_proto_hdr *hdr =
> +				&proto_hdrs->proto_hdr[proto_hdrs->count++];

...

> +	struct ip_esp_hdr *esph = (struct ip_esp_hdr *)hdr->buffer;
> +
> +	VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, ESP);
> +
> +	if (fltr->ip_mask.spi == htonl(U32_MAX)) {
> +		esph->spi = fltr->ip_data.spi;
> +		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, ESP, SPI);
> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * iecm_fill_fdir_l4_hdr - fill the L4 protocol header
> + * @fltr: Flow Director filter data structure
> + * @proto_hdrs: Flow Director protocol headers data structure
> + *
> + * Returns 0 if the L4 protocol header is set successfully
> + */
> +static int
> +iecm_fill_fdir_l4_hdr(struct iecm_fdir_fltr *fltr,
> +		      struct virtchnl_proto_hdrs *proto_hdrs)
> +{
> +	struct virtchnl_proto_hdr *hdr;
> +	__be32 *l4_4_data;
> +
> +	if (!fltr->ip_mask.proto) /* IPv4/IPv6 header only */
> +		return 0;
> +
> +	hdr = &proto_hdrs->proto_hdr[proto_hdrs->count++];

Here's a good example: assignment occurs separately from the
declaration and doesn't cause a line wrap.

> +	l4_4_data = (__force __be32 *)hdr->buffer;
> +
> +	/* L2TPv3 over IP with 'Session ID' */
> +	if (fltr->ip_data.proto == IPPROTO_L2TP &&
> +	    fltr->ip_mask.l4_header == htonl(U32_MAX)) {
> +		VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, L2TPV3);
> +		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, L2TPV3, SESS_ID);
> +
> +		*l4_4_data = fltr->ip_data.l4_header;
> +	} else {
> +		return -EOPNOTSUPP;
> +	}

	if (!= || !=)
		return -EOPNOTSUPP;

	VIRTCHNL_SET_ ...

-1 level this way.

> +
> +	return 0;
> +}
> +
> +/**
> + * iecm_fill_fdir_eth_hdr - fill the Ethernet protocol header
> + * @fltr: Flow Director filter data structure
> + * @proto_hdrs: Flow Director protocol headers data structure
> + *
> + * Returns 0 if the Ethernet protocol header is set successfully
> + */
> +static int
> +iecm_fill_fdir_eth_hdr(struct iecm_fdir_fltr *fltr,
> +		       struct virtchnl_proto_hdrs *proto_hdrs)
> +{
> +	struct virtchnl_proto_hdr *hdr =
> +				&proto_hdrs->proto_hdr[proto_hdrs->count++];

Here's a bad example again.

> +	struct ethhdr *ehdr = (struct ethhdr *)hdr->buffer;
> +
> +	VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, ETH);
> +
> +	if (fltr->eth_mask.etype == htons(U16_MAX)) {
> +		if (fltr->eth_data.etype == htons(ETH_P_IP) ||
> +		    fltr->eth_data.etype == htons(ETH_P_IPV6))
> +			return -EOPNOTSUPP;
> +
> +		ehdr->h_proto = fltr->eth_data.etype;
> +		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, ETH, ETHERTYPE);
> +	}

This can be inverted as well.

> +
> +	return 0;
> +}
> +
> +/**
> + * iecm_fill_fdir_add_msg - fill the Flow Director filter into virtchnl message
> + * @vport: vport structure
> + * @fltr: Flow Director filter data structure
> + *
> + * Returns 0 if the add Flow Director virtchnl message is filled successfully
> + */
> +static int
> +iecm_fill_fdir_add_msg(struct iecm_vport *vport, struct iecm_fdir_fltr *fltr)
> +{
> +	struct virtchnl_fdir_add *vc_msg = &fltr->vc_add_msg;
> +	struct virtchnl_proto_hdrs *proto_hdrs;
> +	int err;
> +
> +	proto_hdrs = &vc_msg->rule_cfg.proto_hdrs;

And here's good.

> +
> +	err = iecm_fill_fdir_eth_hdr(fltr, proto_hdrs); /* L2 always exists */
> +	if (err)
> +		return err;
> +
> +	switch (fltr->flow_type) {
> +	case IECM_FDIR_FLOW_IPV4_TCP:
> +		err = iecm_fill_fdir_ip4_hdr(fltr, proto_hdrs) |
> +		      iecm_fill_fdir_tcp_hdr(fltr, proto_hdrs);
> +		break;
> +	case IECM_FDIR_FLOW_IPV4_UDP:
> +		err = iecm_fill_fdir_ip4_hdr(fltr, proto_hdrs) |
> +		      iecm_fill_fdir_udp_hdr(fltr, proto_hdrs);
> +		break;
> +	case IECM_FDIR_FLOW_IPV4_SCTP:
> +		err = iecm_fill_fdir_ip4_hdr(fltr, proto_hdrs) |
> +		      iecm_fill_fdir_sctp_hdr(fltr, proto_hdrs);
> +		break;
> +	case IECM_FDIR_FLOW_IPV4_AH:
> +		err = iecm_fill_fdir_ip4_hdr(fltr, proto_hdrs) |
> +		      iecm_fill_fdir_ah_hdr(fltr, proto_hdrs);
> +		break;
> +	case IECM_FDIR_FLOW_IPV4_ESP:
> +		err = iecm_fill_fdir_ip4_hdr(fltr, proto_hdrs) |
> +		      iecm_fill_fdir_esp_hdr(fltr, proto_hdrs);
> +		break;
> +	case IECM_FDIR_FLOW_IPV4_OTHER:
> +		err = iecm_fill_fdir_ip4_hdr(fltr, proto_hdrs) |
> +		      iecm_fill_fdir_l4_hdr(fltr, proto_hdrs);
> +		break;
> +	case IECM_FDIR_FLOW_IPV6_TCP:
> +		err = iecm_fill_fdir_ip6_hdr(fltr, proto_hdrs) |
> +		      iecm_fill_fdir_tcp_hdr(fltr, proto_hdrs);
> +		break;
> +	case IECM_FDIR_FLOW_IPV6_UDP:
> +		err = iecm_fill_fdir_ip6_hdr(fltr, proto_hdrs) |
> +		      iecm_fill_fdir_udp_hdr(fltr, proto_hdrs);
> +		break;
> +	case IECM_FDIR_FLOW_IPV6_SCTP:
> +		err = iecm_fill_fdir_ip6_hdr(fltr, proto_hdrs) |
> +		      iecm_fill_fdir_sctp_hdr(fltr, proto_hdrs);
> +		break;
> +	case IECM_FDIR_FLOW_IPV6_AH:
> +		err = iecm_fill_fdir_ip6_hdr(fltr, proto_hdrs) |
> +		      iecm_fill_fdir_ah_hdr(fltr, proto_hdrs);
> +		break;
> +	case IECM_FDIR_FLOW_IPV6_ESP:
> +		err = iecm_fill_fdir_ip6_hdr(fltr, proto_hdrs) |
> +		      iecm_fill_fdir_esp_hdr(fltr, proto_hdrs);
> +		break;
> +	case IECM_FDIR_FLOW_IPV6_OTHER:
> +		err = iecm_fill_fdir_ip6_hdr(fltr, proto_hdrs) |
> +		      iecm_fill_fdir_l4_hdr(fltr, proto_hdrs);
> +		break;
> +	case IECM_FDIR_FLOW_NON_IP_L2:
> +		break;
> +	default:
> +		err = -EINVAL;
> +		break;
> +	}
> +
> +	if (err)
> +		return err;
> +
> +	vc_msg->vsi_id = vport->vport_id;
> +	vc_msg->rule_cfg.action_set.count = 1;
> +	vc_msg->rule_cfg.action_set.actions[0].type = fltr->action;
> +	vc_msg->rule_cfg.action_set.actions[0].act_conf.queue.index =
> +								fltr->q_index;
> +
> +	return 0;
> +}
> +
> +/**
> + * iecm_fdir_flow_proto_name - get the flow protocol name
> + * @flow_type: Flow Director filter flow type
> + **/
> +static const char *
> +iecm_fdir_flow_proto_name(enum iecm_fdir_flow_type flow_type)
> +{
> +	switch (flow_type) {
> +	case IECM_FDIR_FLOW_IPV4_TCP:
> +	case IECM_FDIR_FLOW_IPV6_TCP:
> +		return "TCP";
> +	case IECM_FDIR_FLOW_IPV4_UDP:
> +	case IECM_FDIR_FLOW_IPV6_UDP:
> +		return "UDP";
> +	case IECM_FDIR_FLOW_IPV4_SCTP:
> +	case IECM_FDIR_FLOW_IPV6_SCTP:
> +		return "SCTP";
> +	case IECM_FDIR_FLOW_IPV4_AH:
> +	case IECM_FDIR_FLOW_IPV6_AH:
> +		return "AH";
> +	case IECM_FDIR_FLOW_IPV4_ESP:
> +	case IECM_FDIR_FLOW_IPV6_ESP:
> +		return "ESP";
> +	case IECM_FDIR_FLOW_IPV4_OTHER:
> +	case IECM_FDIR_FLOW_IPV6_OTHER:
> +		return "Other";
> +	case IECM_FDIR_FLOW_NON_IP_L2:
> +		return "Ethernet";
> +	default:
> +		return NULL;
> +	}
> +}
> +
> +/**
> + * iecm_dump_fdir_fltr
> + * @vport: vport structure
> + * @fltr: Flow Director filter to print
> + *
> + * Print the Flow Director filter
> + **/
> +static void
> +iecm_dump_fdir_fltr(struct iecm_vport *vport, struct iecm_fdir_fltr *fltr)
> +{
> +	const char *proto = iecm_fdir_flow_proto_name(fltr->flow_type);
> +	struct device *dev = &vport->adapter->pdev->dev;
> +
> +	if (!proto)
> +		return;
> +
> +	switch (fltr->flow_type) {
> +	case IECM_FDIR_FLOW_IPV4_TCP:
> +	case IECM_FDIR_FLOW_IPV4_UDP:
> +	case IECM_FDIR_FLOW_IPV4_SCTP:
> +		dev_info(dev, "Rule ID: %u dst_ip: %pI4 src_ip %pI4 %s: dst_port %hu src_port %hu\n",
> +			 fltr->loc,
> +			 &fltr->ip_data.v4_addrs.dst_ip,
> +			 &fltr->ip_data.v4_addrs.src_ip,
> +			 proto,
> +			 ntohs(fltr->ip_data.dst_port),
> +			 ntohs(fltr->ip_data.src_port));

Some of those can fit into previous line, there's no need to put
each argument onto separate one.

> +		break;
> +	case IECM_FDIR_FLOW_IPV4_AH:
> +	case IECM_FDIR_FLOW_IPV4_ESP:
> +		dev_info(dev, "Rule ID: %u dst_ip: %pI4 src_ip %pI4 %s: SPI %u\n",
> +			 fltr->loc,
> +			 &fltr->ip_data.v4_addrs.dst_ip,
> +			 &fltr->ip_data.v4_addrs.src_ip,
> +			 proto,
> +			 ntohl(fltr->ip_data.spi));

Same here.

> +		break;
> +	case IECM_FDIR_FLOW_IPV4_OTHER:
> +		dev_info(dev, "Rule ID: %u dst_ip: %pI4 src_ip %pI4 proto: %u L4_bytes: 0x%x\n",
> +			 fltr->loc,
> +			 &fltr->ip_data.v4_addrs.dst_ip,
> +			 &fltr->ip_data.v4_addrs.src_ip,
> +			 fltr->ip_data.proto,
> +			 ntohl(fltr->ip_data.l4_header));

And here.

> +		break;
> +	case IECM_FDIR_FLOW_IPV6_TCP:
> +	case IECM_FDIR_FLOW_IPV6_UDP:
> +	case IECM_FDIR_FLOW_IPV6_SCTP:
> +		dev_info(dev, "Rule ID: %u dst_ip: %pI6 src_ip %pI6 %s: dst_port %hu src_port %hu\n",
> +			 fltr->loc,
> +			 &fltr->ip_data.v6_addrs.dst_ip,
> +			 &fltr->ip_data.v6_addrs.src_ip,
> +			 proto,
> +			 ntohs(fltr->ip_data.dst_port),
> +			 ntohs(fltr->ip_data.src_port));

...

> +		break;
> +	case IECM_FDIR_FLOW_IPV6_AH:
> +	case IECM_FDIR_FLOW_IPV6_ESP:
> +		dev_info(dev, "Rule ID: %u dst_ip: %pI6 src_ip %pI6 %s: SPI %u\n",
> +			 fltr->loc,
> +			 &fltr->ip_data.v6_addrs.dst_ip,
> +			 &fltr->ip_data.v6_addrs.src_ip,
> +			 proto,
> +			 ntohl(fltr->ip_data.spi));

...

> +		break;
> +	case IECM_FDIR_FLOW_IPV6_OTHER:
> +		dev_info(dev, "Rule ID: %u dst_ip: %pI6 src_ip %pI6 proto: %u L4_bytes: 0x%x\n",
> +			 fltr->loc,
> +			 &fltr->ip_data.v6_addrs.dst_ip,
> +			 &fltr->ip_data.v6_addrs.src_ip,
> +			 fltr->ip_data.proto,
> +			 ntohl(fltr->ip_data.l4_header));

...

> +		break;
> +	case IECM_FDIR_FLOW_NON_IP_L2:
> +		dev_info(dev, "Rule ID: %u eth_type: 0x%x\n",
> +			 fltr->loc,
> +			 ntohs(fltr->eth_data.etype));

...

> +		break;
> +	default:
> +		break;
> +	}
> +}
> +
> +/**
> + * iecm_fdir_is_dup_fltr - test if filter is already in list
> + * @adapter: board private structure
> + * @fltr: Flow Director filter data structure
> + *
> + * Returns true if the filter is found in the list
> + */
> +static bool
> +iecm_fdir_is_dup_fltr(struct iecm_adapter *adapter,
> +		      struct iecm_fdir_fltr *fltr)
> +{
> +	struct iecm_fdir_fltr_config *fdir_config;
> +	struct iecm_fdir_fltr *tmp;
> +
> +	fdir_config = &adapter->config_data.fdir_config;
> +	list_for_each_entry(tmp, &fdir_config->fdir_fltr_list, list) {
> +		if (tmp->flow_type != fltr->flow_type)
> +			continue;
> +
> +		if (!memcmp(&tmp->eth_data, &fltr->eth_data,
> +			    sizeof(fltr->eth_data)) &&
> +		    !memcmp(&tmp->ip_data, &fltr->ip_data,
> +			    sizeof(fltr->ip_data)) &&
> +		    !memcmp(&tmp->ext_data, &fltr->ext_data,
> +			    sizeof(fltr->ext_data)))
> +			return true;
> +	}
> +
> +	return false;
> +}
> +
> +/**
> + * iecm_find_fdir_fltr_by_loc - find filter with location
> + * @adapter: board private structure
> + * @loc: location to find.
> + *
> + * Returns pointer to Flow Director filter if found or null
> + */
> +static struct iecm_fdir_fltr *
> +iecm_find_fdir_fltr_by_loc(struct iecm_adapter *adapter, u32 loc)
> +{
> +	struct iecm_fdir_fltr_config *fdir_config;
> +	struct iecm_fdir_fltr *rule;
> +
> +	fdir_config = &adapter->config_data.fdir_config;
> +	list_for_each_entry(rule, &fdir_config->fdir_fltr_list, list)
> +		if (rule->loc == loc)
> +			return rule;

Here's a good example that a single `if` statement shouldn't be
placed into a pair of braces.

> +
> +	return NULL;
> +}
> +
> +/**
> + * iecm_fdir_list_add_fltr - add a new node to the flow director filter list
> + * @adapter: board private structure
> + * @fltr: filter node to add to structure
> + */
> +static void
> +iecm_fdir_list_add_fltr(struct iecm_adapter *adapter,
> +			struct iecm_fdir_fltr *fltr)
> +{
> +	struct iecm_fdir_fltr *rule, *parent = NULL;
> +	struct iecm_fdir_fltr_config *fdir_config;
> +
> +	fdir_config = &adapter->config_data.fdir_config;
> +
> +	spin_lock_bh(&adapter->fdir_fltr_list_lock);
> +	list_for_each_entry(rule, &fdir_config->fdir_fltr_list, list) {
> +		if (rule->loc >= fltr->loc)
> +			break;
> +		parent = rule;
> +	}
> +
> +	if (parent)
> +		list_add(&fltr->list, &parent->list);
> +	else
> +		list_add(&fltr->list, &fdir_config->fdir_fltr_list);
> +	fltr->add = true;
> +	spin_unlock_bh(&adapter->fdir_fltr_list_lock);
> +}
> +
> +/**
> + * iecm_fltr_to_ethtool_flow - convert filter type values to ethtool
> + * flow type values
> + * @flow: filter type to be converted
> + *
> + * Returns the corresponding ethtool flow type.
> + */
> +static int iecm_fltr_to_ethtool_flow(enum iecm_fdir_flow_type flow)
> +{
> +	switch (flow) {
> +	case IECM_FDIR_FLOW_IPV4_TCP:
> +		return TCP_V4_FLOW;
> +	case IECM_FDIR_FLOW_IPV4_UDP:
> +		return UDP_V4_FLOW;
> +	case IECM_FDIR_FLOW_IPV4_SCTP:
> +		return SCTP_V4_FLOW;
> +	case IECM_FDIR_FLOW_IPV4_AH:
> +		return AH_V4_FLOW;
> +	case IECM_FDIR_FLOW_IPV4_ESP:
> +		return ESP_V4_FLOW;
> +	case IECM_FDIR_FLOW_IPV4_OTHER:
> +		return IPV4_USER_FLOW;
> +	case IECM_FDIR_FLOW_IPV6_TCP:
> +		return TCP_V6_FLOW;
> +	case IECM_FDIR_FLOW_IPV6_UDP:
> +		return UDP_V6_FLOW;
> +	case IECM_FDIR_FLOW_IPV6_SCTP:
> +		return SCTP_V6_FLOW;
> +	case IECM_FDIR_FLOW_IPV6_AH:
> +		return AH_V6_FLOW;
> +	case IECM_FDIR_FLOW_IPV6_ESP:
> +		return ESP_V6_FLOW;
> +	case IECM_FDIR_FLOW_IPV6_OTHER:
> +		return IPV6_USER_FLOW;
> +	case IECM_FDIR_FLOW_NON_IP_L2:
> +		return ETHER_FLOW;
> +	default:
> +		/* 0 is undefined ethtool flow */
> +		return 0;

This switch-case can be converted to an array to reduce code size.

> +	}
> +}
> +
> +/**
> + * iecm_ethtool_flow_to_fltr - convert ethtool flow type to filter enum
> + * @eth: Ethtool flow type to be converted
> + *
> + * Returns flow enum
> + */
> +static enum iecm_fdir_flow_type iecm_ethtool_flow_to_fltr(int eth)
> +{
> +	switch (eth) {
> +	case TCP_V4_FLOW:
> +		return IECM_FDIR_FLOW_IPV4_TCP;
> +	case UDP_V4_FLOW:
> +		return IECM_FDIR_FLOW_IPV4_UDP;
> +	case SCTP_V4_FLOW:
> +		return IECM_FDIR_FLOW_IPV4_SCTP;
> +	case AH_V4_FLOW:
> +		return IECM_FDIR_FLOW_IPV4_AH;
> +	case ESP_V4_FLOW:
> +		return IECM_FDIR_FLOW_IPV4_ESP;
> +	case IPV4_USER_FLOW:
> +		return IECM_FDIR_FLOW_IPV4_OTHER;
> +	case TCP_V6_FLOW:
> +		return IECM_FDIR_FLOW_IPV6_TCP;
> +	case UDP_V6_FLOW:
> +		return IECM_FDIR_FLOW_IPV6_UDP;
> +	case SCTP_V6_FLOW:
> +		return IECM_FDIR_FLOW_IPV6_SCTP;
> +	case AH_V6_FLOW:
> +		return IECM_FDIR_FLOW_IPV6_AH;
> +	case ESP_V6_FLOW:
> +		return IECM_FDIR_FLOW_IPV6_ESP;
> +	case IPV6_USER_FLOW:
> +		return IECM_FDIR_FLOW_IPV6_OTHER;
> +	case ETHER_FLOW:
> +		return IECM_FDIR_FLOW_NON_IP_L2;
> +	default:
> +		return IECM_FDIR_FLOW_NONE;

Same here.

> +	}
> +}
> +
> +/**
> + * iecm_is_mask_valid - check mask field set
> + * @mask: full mask to check
> + * @field: field for which mask should be valid
> + *
> + * If the mask is fully set return true. If it is not valid for field return
> + * false.
> + */
> +static bool iecm_is_mask_valid(u64 mask, u64 field)
> +{
> +	return (mask & field) == field;
> +}

That is something really basic and should at least be placed
somewhere in the headers and used module-wide.

> +
> +/**
> + * iecm_parse_rx_flow_user_data - deconstruct user-defined data
> + * @fsp: pointer to ethtool Rx flow specification
> + * @fltr: pointer to Flow Director filter for userdef data storage
> + *
> + * Returns 0 on success, negative error value on failure
> + */
> +static int
> +iecm_parse_rx_flow_user_data(struct ethtool_rx_flow_spec *fsp,
> +			     struct iecm_fdir_fltr *fltr)
> +{
> +	struct iecm_flex_word *flex;
> +	int i, cnt = 0;
> +
> +	if (!(fsp->flow_type & FLOW_EXT))
> +		return 0;
> +
> +	for (i = 0; i < IECM_FLEX_WORD_NUM; i++) {
> +#define IECM_USERDEF_FLEX_WORD_M	GENMASK(15, 0)
> +#define IECM_USERDEF_FLEX_OFFS_S	16
> +#define IECM_USERDEF_FLEX_OFFS_M	GENMASK(31, IECM_USERDEF_FLEX_OFFS_S)
> +#define IECM_USERDEF_FLEX_FLTR_M	GENMASK(31, 0)

1. Should be placed outside the function.
2. Candidates for FIELD_{GET,PREP}().

> +		u32 value = be32_to_cpu(fsp->h_ext.data[i]);
> +		u32 mask = be32_to_cpu(fsp->m_ext.data[i]);
> +
> +		if (!value || !mask)
> +			continue;
> +
> +		if (!iecm_is_mask_valid(mask, IECM_USERDEF_FLEX_FLTR_M))
> +			return -EINVAL;
> +
> +		/* 504 is the maximum value for offsets, and offset is measured
> +		 * from the start of the MAC address.
> +		 */
> +#define IECM_USERDEF_FLEX_MAX_OFFS_VAL 504

Same.

> +		flex = &fltr->flex_words[cnt++];
> +		flex->word = value & IECM_USERDEF_FLEX_WORD_M;
> +		flex->offset = (value & IECM_USERDEF_FLEX_OFFS_M) >>
> +			     IECM_USERDEF_FLEX_OFFS_S;
> +		if (flex->offset > IECM_USERDEF_FLEX_MAX_OFFS_VAL)
> +			return -EINVAL;
> +	}
> +
> +	fltr->flex_cnt = cnt;
> +
> +	return 0;
> +}
> +
> +/**
> + * iecm_fill_rx_flow_ext_data - fill the additional data
> + * @fsp: pointer to ethtool Rx flow specification
> + * @fltr: pointer to Flow Director filter to get additional data
> + */
> +static void
> +iecm_fill_rx_flow_ext_data(struct ethtool_rx_flow_spec *fsp,
> +			   struct iecm_fdir_fltr *fltr)
> +{
> +	if (!fltr->ext_mask.usr_def[0] && !fltr->ext_mask.usr_def[1])
> +		return;
> +
> +	fsp->flow_type |= FLOW_EXT;
> +
> +	memcpy(fsp->h_ext.data, fltr->ext_data.usr_def,
> +	       sizeof(fsp->h_ext.data));
> +	memcpy(fsp->m_ext.data, fltr->ext_mask.usr_def,
> +	       sizeof(fsp->m_ext.data));
> +}
> +
> +/**
> + * iecm_get_fdir_fltr_entry - fill ethtool structure with Flow Director
> + * filter data
> + * @vport: vport structure
> + * @cmd: ethtool command data structure to receive the filter data
> + *
> + * Returns 0 as expected for success by ethtool
> + */
> +int
> +iecm_get_fdir_fltr_entry(struct iecm_vport *vport, struct ethtool_rxnfc *cmd)
> +{
> +	struct ethtool_rx_flow_spec *fsp =
> +					(struct ethtool_rx_flow_spec *)&cmd->fs;

	struct ethtool_rx_flow_spec *fsp = (typeof(fsp))&cmd->fs;

> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct iecm_fdir_fltr *rule;
> +	int ret = 0;
> +
> +	if (adapter->state != __IECM_UP)
> +		return -EIO;
> +
> +	if (!iecm_is_cap_ena(adapter, IECM_OTHER_CAPS, VIRTCHNL2_CAP_FDIR))
> +		return -EOPNOTSUPP;
> +
> +	spin_lock_bh(&adapter->fdir_fltr_list_lock);
> +
> +	rule = iecm_find_fdir_fltr_by_loc(adapter, fsp->location);
> +	if (!rule) {
> +		ret = -EINVAL;
> +		goto release_lock;
> +	}
> +
> +	fsp->flow_type = iecm_fltr_to_ethtool_flow(rule->flow_type);
> +
> +	memset(&fsp->m_u, 0, sizeof(fsp->m_u));
> +	memset(&fsp->m_ext, 0, sizeof(fsp->m_ext));
> +
> +	switch (fsp->flow_type) {
> +	case TCP_V4_FLOW:
> +	case UDP_V4_FLOW:
> +	case SCTP_V4_FLOW:
> +		fsp->h_u.tcp_ip4_spec.ip4src = rule->ip_data.v4_addrs.src_ip;
> +		fsp->h_u.tcp_ip4_spec.ip4dst = rule->ip_data.v4_addrs.dst_ip;
> +		fsp->h_u.tcp_ip4_spec.psrc = rule->ip_data.src_port;
> +		fsp->h_u.tcp_ip4_spec.pdst = rule->ip_data.dst_port;
> +		fsp->h_u.tcp_ip4_spec.tos = rule->ip_data.tos;
> +		fsp->m_u.tcp_ip4_spec.ip4src = rule->ip_mask.v4_addrs.src_ip;
> +		fsp->m_u.tcp_ip4_spec.ip4dst = rule->ip_mask.v4_addrs.dst_ip;
> +		fsp->m_u.tcp_ip4_spec.psrc = rule->ip_mask.src_port;
> +		fsp->m_u.tcp_ip4_spec.pdst = rule->ip_mask.dst_port;
> +		fsp->m_u.tcp_ip4_spec.tos = rule->ip_mask.tos;
> +		break;
> +	case AH_V4_FLOW:
> +	case ESP_V4_FLOW:
> +		fsp->h_u.ah_ip4_spec.ip4src = rule->ip_data.v4_addrs.src_ip;
> +		fsp->h_u.ah_ip4_spec.ip4dst = rule->ip_data.v4_addrs.dst_ip;
> +		fsp->h_u.ah_ip4_spec.spi = rule->ip_data.spi;
> +		fsp->h_u.ah_ip4_spec.tos = rule->ip_data.tos;
> +		fsp->m_u.ah_ip4_spec.ip4src = rule->ip_mask.v4_addrs.src_ip;
> +		fsp->m_u.ah_ip4_spec.ip4dst = rule->ip_mask.v4_addrs.dst_ip;
> +		fsp->m_u.ah_ip4_spec.spi = rule->ip_mask.spi;
> +		fsp->m_u.ah_ip4_spec.tos = rule->ip_mask.tos;
> +		break;
> +	case IPV4_USER_FLOW:
> +		fsp->h_u.usr_ip4_spec.ip4src = rule->ip_data.v4_addrs.src_ip;
> +		fsp->h_u.usr_ip4_spec.ip4dst = rule->ip_data.v4_addrs.dst_ip;
> +		fsp->h_u.usr_ip4_spec.l4_4_bytes = rule->ip_data.l4_header;
> +		fsp->h_u.usr_ip4_spec.tos = rule->ip_data.tos;
> +		fsp->h_u.usr_ip4_spec.ip_ver = ETH_RX_NFC_IP4;
> +		fsp->h_u.usr_ip4_spec.proto = rule->ip_data.proto;
> +		fsp->m_u.usr_ip4_spec.ip4src = rule->ip_mask.v4_addrs.src_ip;
> +		fsp->m_u.usr_ip4_spec.ip4dst = rule->ip_mask.v4_addrs.dst_ip;
> +		fsp->m_u.usr_ip4_spec.l4_4_bytes = rule->ip_mask.l4_header;
> +		fsp->m_u.usr_ip4_spec.tos = rule->ip_mask.tos;
> +		fsp->m_u.usr_ip4_spec.ip_ver = 0xFF;
> +		fsp->m_u.usr_ip4_spec.proto = rule->ip_mask.proto;
> +		break;
> +	case TCP_V6_FLOW:
> +	case UDP_V6_FLOW:
> +	case SCTP_V6_FLOW:
> +		memcpy(fsp->h_u.usr_ip6_spec.ip6src,
> +		       &rule->ip_data.v6_addrs.src_ip, sizeof(struct in6_addr));
> +		memcpy(fsp->h_u.usr_ip6_spec.ip6dst,
> +		       &rule->ip_data.v6_addrs.dst_ip, sizeof(struct in6_addr));
> +		fsp->h_u.tcp_ip6_spec.psrc = rule->ip_data.src_port;
> +		fsp->h_u.tcp_ip6_spec.pdst = rule->ip_data.dst_port;
> +		fsp->h_u.tcp_ip6_spec.tclass = rule->ip_data.tclass;
> +		memcpy(fsp->m_u.usr_ip6_spec.ip6src,
> +		       &rule->ip_mask.v6_addrs.src_ip, sizeof(struct in6_addr));
> +		memcpy(fsp->m_u.usr_ip6_spec.ip6dst,
> +		       &rule->ip_mask.v6_addrs.dst_ip, sizeof(struct in6_addr));
> +		fsp->m_u.tcp_ip6_spec.psrc = rule->ip_mask.src_port;
> +		fsp->m_u.tcp_ip6_spec.pdst = rule->ip_mask.dst_port;
> +		fsp->m_u.tcp_ip6_spec.tclass = rule->ip_mask.tclass;
> +		break;
> +	case AH_V6_FLOW:
> +	case ESP_V6_FLOW:
> +		memcpy(fsp->h_u.ah_ip6_spec.ip6src,
> +		       &rule->ip_data.v6_addrs.src_ip, sizeof(struct in6_addr));
> +		memcpy(fsp->h_u.ah_ip6_spec.ip6dst,
> +		       &rule->ip_data.v6_addrs.dst_ip, sizeof(struct in6_addr));
> +		fsp->h_u.ah_ip6_spec.spi = rule->ip_data.spi;
> +		fsp->h_u.ah_ip6_spec.tclass = rule->ip_data.tclass;
> +		memcpy(fsp->m_u.ah_ip6_spec.ip6src,
> +		       &rule->ip_mask.v6_addrs.src_ip, sizeof(struct in6_addr));
> +		memcpy(fsp->m_u.ah_ip6_spec.ip6dst,
> +		       &rule->ip_mask.v6_addrs.dst_ip, sizeof(struct in6_addr));
> +		fsp->m_u.ah_ip6_spec.spi = rule->ip_mask.spi;
> +		fsp->m_u.ah_ip6_spec.tclass = rule->ip_mask.tclass;
> +		break;
> +	case IPV6_USER_FLOW:
> +		memcpy(fsp->h_u.usr_ip6_spec.ip6src,
> +		       &rule->ip_data.v6_addrs.src_ip, sizeof(struct in6_addr));
> +		memcpy(fsp->h_u.usr_ip6_spec.ip6dst,
> +		       &rule->ip_data.v6_addrs.dst_ip, sizeof(struct in6_addr));
> +		fsp->h_u.usr_ip6_spec.l4_4_bytes = rule->ip_data.l4_header;
> +		fsp->h_u.usr_ip6_spec.tclass = rule->ip_data.tclass;
> +		fsp->h_u.usr_ip6_spec.l4_proto = rule->ip_data.proto;
> +		memcpy(fsp->m_u.usr_ip6_spec.ip6src,
> +		       &rule->ip_mask.v6_addrs.src_ip, sizeof(struct in6_addr));
> +		memcpy(fsp->m_u.usr_ip6_spec.ip6dst,
> +		       &rule->ip_mask.v6_addrs.dst_ip, sizeof(struct in6_addr));
> +		fsp->m_u.usr_ip6_spec.l4_4_bytes = rule->ip_mask.l4_header;
> +		fsp->m_u.usr_ip6_spec.tclass = rule->ip_mask.tclass;
> +		fsp->m_u.usr_ip6_spec.l4_proto = rule->ip_mask.proto;
> +		break;
> +	case ETHER_FLOW:
> +		fsp->h_u.ether_spec.h_proto = rule->eth_data.etype;
> +		fsp->m_u.ether_spec.h_proto = rule->eth_mask.etype;
> +		break;
> +	default:
> +		ret = -EINVAL;
> +		break;
> +	}
> +
> +	iecm_fill_rx_flow_ext_data(fsp, rule);
> +
> +	if (rule->action == VIRTCHNL_ACTION_DROP)
> +		fsp->ring_cookie = RX_CLS_FLOW_DISC;
> +	else
> +		fsp->ring_cookie = rule->q_index;
> +
> +release_lock:
> +	spin_unlock_bh(&adapter->fdir_fltr_list_lock);
> +	return ret;
> +}
> +
> +/**
> + * iecm_get_fdir_fltr_ids - fill buffer with filter IDs of active filters
> + * @vport: vport structure
> + * @cmd: ethtool command data structure
> + * @rule_locs: ethtool array passed in from OS to receive filter IDs
> + *
> + * Returns 0 as expected for success by ethtool
> + */
> +int
> +iecm_get_fdir_fltr_ids(struct iecm_vport *vport, struct ethtool_rxnfc *cmd,
> +		       u32 *rule_locs)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct iecm_fdir_fltr_config *fdir_config;
> +	struct iecm_fdir_fltr *fltr;
> +	unsigned int cnt = 0;
> +	int ret = 0;
> +
> +	if (adapter->state != __IECM_UP)
> +		return -EIO;
> +
> +	if (!iecm_is_cap_ena(adapter, IECM_OTHER_CAPS, VIRTCHNL2_CAP_FDIR))
> +		return -EOPNOTSUPP;
> +
> +	cmd->data = IECM_MAX_FDIR_FILTERS;
> +
> +	fdir_config = &adapter->config_data.fdir_config;
> +
> +	spin_lock_bh(&adapter->fdir_fltr_list_lock);
> +
> +	list_for_each_entry(fltr, &fdir_config->fdir_fltr_list, list) {
> +		if (cnt == cmd->rule_cnt) {
> +			ret = -EMSGSIZE;
> +			goto release_lock;
> +		}
> +		rule_locs[cnt] = fltr->loc;
> +		cnt++;
> +	}
> +
> +release_lock:
> +	spin_unlock_bh(&adapter->fdir_fltr_list_lock);
> +	if (!ret)
> +		cmd->rule_cnt = cnt;
> +
> +	return ret;
> +}
> +
> +/**
> + * iecm_add_fdir_fltr_info - Set the input set for Flow Director filter
> + * @vport: vport structure
> + * @fsp: pointer to ethtool Rx flow specification
> + * @fltr: filter structure
> + */
> +static int
> +iecm_add_fdir_fltr_info(struct iecm_vport *vport,
> +			struct ethtool_rx_flow_spec *fsp,
> +			struct iecm_fdir_fltr *fltr)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	u32 flow_type, q_index = 0;
> +	enum virtchnl_action act;
> +	int err;
> +
> +	if (fsp->ring_cookie == RX_CLS_FLOW_DISC) {
> +		act = VIRTCHNL_ACTION_DROP;
> +	} else {
> +		q_index = fsp->ring_cookie;
> +		if (q_index >= vport->num_rxq)
> +			return -EINVAL;
> +
> +		act = VIRTCHNL_ACTION_QUEUE;
> +	}
> +
> +	fltr->action = act;
> +	fltr->loc = fsp->location;
> +	fltr->q_index = q_index;
> +
> +	if (fsp->flow_type & FLOW_EXT) {
> +		memcpy(fltr->ext_data.usr_def, fsp->h_ext.data,
> +		       sizeof(fltr->ext_data.usr_def));
> +		memcpy(fltr->ext_mask.usr_def, fsp->m_ext.data,
> +		       sizeof(fltr->ext_mask.usr_def));
> +	}
> +
> +	flow_type = fsp->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT | FLOW_RSS);
> +	fltr->flow_type = iecm_ethtool_flow_to_fltr(flow_type);
> +
> +	switch (flow_type) {
> +	case TCP_V4_FLOW:
> +	case UDP_V4_FLOW:
> +	case SCTP_V4_FLOW:
> +		fltr->ip_data.v4_addrs.src_ip = fsp->h_u.tcp_ip4_spec.ip4src;
> +		fltr->ip_data.v4_addrs.dst_ip = fsp->h_u.tcp_ip4_spec.ip4dst;
> +		fltr->ip_data.src_port = fsp->h_u.tcp_ip4_spec.psrc;
> +		fltr->ip_data.dst_port = fsp->h_u.tcp_ip4_spec.pdst;
> +		fltr->ip_data.tos = fsp->h_u.tcp_ip4_spec.tos;
> +		fltr->ip_mask.v4_addrs.src_ip = fsp->m_u.tcp_ip4_spec.ip4src;
> +		fltr->ip_mask.v4_addrs.dst_ip = fsp->m_u.tcp_ip4_spec.ip4dst;
> +		fltr->ip_mask.src_port = fsp->m_u.tcp_ip4_spec.psrc;
> +		fltr->ip_mask.dst_port = fsp->m_u.tcp_ip4_spec.pdst;
> +		fltr->ip_mask.tos = fsp->m_u.tcp_ip4_spec.tos;
> +		break;
> +	case AH_V4_FLOW:
> +	case ESP_V4_FLOW:
> +		fltr->ip_data.v4_addrs.src_ip = fsp->h_u.ah_ip4_spec.ip4src;
> +		fltr->ip_data.v4_addrs.dst_ip = fsp->h_u.ah_ip4_spec.ip4dst;
> +		fltr->ip_data.spi = fsp->h_u.ah_ip4_spec.spi;
> +		fltr->ip_data.tos = fsp->h_u.ah_ip4_spec.tos;
> +		fltr->ip_mask.v4_addrs.src_ip = fsp->m_u.ah_ip4_spec.ip4src;
> +		fltr->ip_mask.v4_addrs.dst_ip = fsp->m_u.ah_ip4_spec.ip4dst;
> +		fltr->ip_mask.spi = fsp->m_u.ah_ip4_spec.spi;
> +		fltr->ip_mask.tos = fsp->m_u.ah_ip4_spec.tos;
> +		break;
> +	case IPV4_USER_FLOW:
> +		fltr->ip_data.v4_addrs.src_ip = fsp->h_u.usr_ip4_spec.ip4src;
> +		fltr->ip_data.v4_addrs.dst_ip = fsp->h_u.usr_ip4_spec.ip4dst;
> +		fltr->ip_data.l4_header = fsp->h_u.usr_ip4_spec.l4_4_bytes;
> +		fltr->ip_data.tos = fsp->h_u.usr_ip4_spec.tos;
> +		fltr->ip_data.proto = fsp->h_u.usr_ip4_spec.proto;
> +		fltr->ip_mask.v4_addrs.src_ip = fsp->m_u.usr_ip4_spec.ip4src;
> +		fltr->ip_mask.v4_addrs.dst_ip = fsp->m_u.usr_ip4_spec.ip4dst;
> +		fltr->ip_mask.l4_header = fsp->m_u.usr_ip4_spec.l4_4_bytes;
> +		fltr->ip_mask.tos = fsp->m_u.usr_ip4_spec.tos;
> +		fltr->ip_mask.proto = fsp->m_u.usr_ip4_spec.proto;
> +		break;
> +	case TCP_V6_FLOW:
> +	case UDP_V6_FLOW:
> +	case SCTP_V6_FLOW:
> +		memcpy(&fltr->ip_data.v6_addrs.src_ip,
> +		       fsp->h_u.usr_ip6_spec.ip6src, sizeof(struct in6_addr));
> +		memcpy(&fltr->ip_data.v6_addrs.dst_ip,
> +		       fsp->h_u.usr_ip6_spec.ip6dst, sizeof(struct in6_addr));
> +		fltr->ip_data.src_port = fsp->h_u.tcp_ip6_spec.psrc;
> +		fltr->ip_data.dst_port = fsp->h_u.tcp_ip6_spec.pdst;
> +		fltr->ip_data.tclass = fsp->h_u.tcp_ip6_spec.tclass;
> +		memcpy(&fltr->ip_mask.v6_addrs.src_ip,
> +		       fsp->m_u.usr_ip6_spec.ip6src, sizeof(struct in6_addr));
> +		memcpy(&fltr->ip_mask.v6_addrs.dst_ip,
> +		       fsp->m_u.usr_ip6_spec.ip6dst, sizeof(struct in6_addr));
> +		fltr->ip_mask.src_port = fsp->m_u.tcp_ip6_spec.psrc;
> +		fltr->ip_mask.dst_port = fsp->m_u.tcp_ip6_spec.pdst;
> +		fltr->ip_mask.tclass = fsp->m_u.tcp_ip6_spec.tclass;
> +		break;
> +	case AH_V6_FLOW:
> +	case ESP_V6_FLOW:
> +		memcpy(&fltr->ip_data.v6_addrs.src_ip,
> +		       fsp->h_u.ah_ip6_spec.ip6src, sizeof(struct in6_addr));
> +		memcpy(&fltr->ip_data.v6_addrs.dst_ip,
> +		       fsp->h_u.ah_ip6_spec.ip6dst, sizeof(struct in6_addr));
> +		fltr->ip_data.spi = fsp->h_u.ah_ip6_spec.spi;
> +		fltr->ip_data.tclass = fsp->h_u.ah_ip6_spec.tclass;
> +		memcpy(&fltr->ip_mask.v6_addrs.src_ip,
> +		       fsp->m_u.ah_ip6_spec.ip6src, sizeof(struct in6_addr));
> +		memcpy(&fltr->ip_mask.v6_addrs.dst_ip,
> +		       fsp->m_u.ah_ip6_spec.ip6dst, sizeof(struct in6_addr));
> +		fltr->ip_mask.spi = fsp->m_u.ah_ip6_spec.spi;
> +		fltr->ip_mask.tclass = fsp->m_u.ah_ip6_spec.tclass;
> +		break;
> +	case IPV6_USER_FLOW:
> +		memcpy(&fltr->ip_data.v6_addrs.src_ip,
> +		       fsp->h_u.usr_ip6_spec.ip6src, sizeof(struct in6_addr));
> +		memcpy(&fltr->ip_data.v6_addrs.dst_ip,
> +		       fsp->h_u.usr_ip6_spec.ip6dst, sizeof(struct in6_addr));
> +		fltr->ip_data.l4_header = fsp->h_u.usr_ip6_spec.l4_4_bytes;
> +		fltr->ip_data.tclass = fsp->h_u.usr_ip6_spec.tclass;
> +		fltr->ip_data.proto = fsp->h_u.usr_ip6_spec.l4_proto;
> +		memcpy(&fltr->ip_mask.v6_addrs.src_ip,
> +		       fsp->m_u.usr_ip6_spec.ip6src, sizeof(struct in6_addr));
> +		memcpy(&fltr->ip_mask.v6_addrs.dst_ip,
> +		       fsp->m_u.usr_ip6_spec.ip6dst, sizeof(struct in6_addr));
> +		fltr->ip_mask.l4_header = fsp->m_u.usr_ip6_spec.l4_4_bytes;
> +		fltr->ip_mask.tclass = fsp->m_u.usr_ip6_spec.tclass;
> +		fltr->ip_mask.proto = fsp->m_u.usr_ip6_spec.l4_proto;
> +		break;
> +	case ETHER_FLOW:
> +		fltr->eth_data.etype = fsp->h_u.ether_spec.h_proto;
> +		fltr->eth_mask.etype = fsp->m_u.ether_spec.h_proto;
> +		break;
> +	default:
> +		/* not doing un-parsed flow types */
> +		return -EINVAL;
> +	}
> +
> +	if (iecm_fdir_is_dup_fltr(adapter, fltr))
> +		return -EEXIST;
> +
> +	err = iecm_parse_rx_flow_user_data(fsp, fltr);
> +	if (err)
> +		return err;
> +
> +	return iecm_fill_fdir_add_msg(vport, fltr);
> +}
> +
> +/**
> + * iecm_add_fdir_fltr - add Flow Director filter
> + * @vport: vport structure
> + * @cmd: command to add Flow Director filter
> + *
> + * Returns 0 on success and negative values for failure
> + */
> +int iecm_add_fdir_fltr(struct iecm_vport *vport, struct ethtool_rxnfc *cmd)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct ethtool_rx_flow_spec *fsp = &cmd->fs;
> +	struct iecm_fdir_fltr_config *fdir_config;
> +	struct iecm_fdir_fltr *fltr;
> +	int err;
> +
> +	if (adapter->state != __IECM_UP)
> +		return -EIO;
> +
> +	if (!iecm_is_cap_ena(adapter, IECM_OTHER_CAPS, VIRTCHNL2_CAP_FDIR))
> +		return -EOPNOTSUPP;
> +
> +	if (fsp->flow_type & FLOW_MAC_EXT)
> +		return -EINVAL;
> +
> +	fdir_config = &adapter->config_data.fdir_config;
> +	if (fdir_config->num_active_filters >= IECM_MAX_FDIR_FILTERS) {
> +		dev_err(&adapter->pdev->dev,
> +			"Unable to add Flow Director filter because vport reached the limit of max allowed filters (%u)\n",
> +			IECM_MAX_FDIR_FILTERS);
> +		return -ENOSPC;
> +	}
> +
> +	spin_lock_bh(&adapter->fdir_fltr_list_lock);
> +	fltr = iecm_find_fdir_fltr_by_loc(adapter, fsp->location);
> +	if (fltr) {
> +		fltr->remove = false;
> +		dev_err(&adapter->pdev->dev, "Failed to add Flow Director filter, it already exists\n");
> +		spin_unlock_bh(&adapter->fdir_fltr_list_lock);
> +		return -EEXIST;
> +	}
> +	spin_unlock_bh(&adapter->fdir_fltr_list_lock);
> +
> +	fltr = kzalloc(sizeof(*fltr), GFP_KERNEL);
> +	if (!fltr)
> +		return -ENOMEM;
> +
> +	err = iecm_add_fdir_fltr_info(vport, fsp, fltr);
> +	if (err)
> +		goto error;
> +
> +	iecm_fdir_list_add_fltr(adapter, fltr);
> +	err = iecm_send_add_fdir_filter_msg(vport);
> +	if (!err) {
> +		fdir_config->num_active_filters++;
> +	} else {
> +		spin_lock_bh(&adapter->fdir_fltr_list_lock);
> +		list_del(&fltr->list);
> +		spin_unlock_bh(&adapter->fdir_fltr_list_lock);
> +	}
> +
> +error:
> +	if (!err) {
> +		dev_info(&adapter->pdev->dev, "Flow Director filter with location %u is added\n",
> +			 fsp->location);
> +	} else {
> +		dev_info(&adapter->pdev->dev, "Failed to add Flow Director filter\n");
> +		iecm_dump_fdir_fltr(vport, fltr);
> +		kfree(fltr);
> +	}
> +
> +	return err;
> +}
> +
> +/**
> + * iecm_del_fdir_fltr - delete Flow Director filter
> + * @vport: vport structure
> + * @cmd: command to delete Flow Director filter
> + *
> + * Returns 0 on success and negative values for failure
> + */
> +int iecm_del_fdir_fltr(struct iecm_vport *vport, struct ethtool_rxnfc *cmd)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct ethtool_rx_flow_spec *fsp = &cmd->fs;
> +	struct iecm_fdir_fltr_config *fdir_config;
> +	struct iecm_fdir_fltr *fltr = NULL;
> +	int err;
> +
> +	if (adapter->state != __IECM_UP)
> +		return -EIO;
> +
> +	if (!iecm_is_cap_ena(adapter, IECM_OTHER_CAPS, VIRTCHNL2_CAP_FDIR))
> +		return -EOPNOTSUPP;
> +
> +	fdir_config = &adapter->config_data.fdir_config;
> +	spin_lock_bh(&adapter->fdir_fltr_list_lock);
> +	fltr = iecm_find_fdir_fltr_by_loc(adapter, fsp->location);
> +	if (fltr) {
> +		fltr->remove = true;
> +		fdir_config->num_active_filters--;
> +	}
> +	spin_unlock_bh(&adapter->fdir_fltr_list_lock);
> +
> +	err = iecm_send_del_fdir_filter_msg(vport);
> +	if (err)
> +		dev_err(&adapter->pdev->dev, "Failed to del Flow Director filter\n");
> +
> +	/* If the above fails, still delete the filter from the list because
> +	 * either HW thinks it doesn't exist or we have a bad filter somehow
> +	 * and it doesn't do us any good to continue hanging on to it.
> +	 */
> +	spin_lock_bh(&adapter->fdir_fltr_list_lock);
> +	fltr = iecm_find_fdir_fltr_by_loc(adapter, fsp->location);
> +	/* It can happen that asynchronously the filter has already been
> +	 * removed from the list, make sure it's still there under spinlock
> +	 * before deleting it.
> +	 */
> +	if (fltr) {
> +		list_del(&fltr->list);
> +		kfree(fltr);
> +	}
> +	spin_unlock_bh(&adapter->fdir_fltr_list_lock);
> +
> +	return err;
> +}
> +
>  /**
>   * iecm_set_mac - NDO callback to set port mac address
>   * @netdev: network interface device structure
> diff --git a/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> index f2516343c199..5601846b4674 100644
> --- a/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> +++ b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> @@ -2731,6 +2731,125 @@ static int iecm_send_insert_vlan_msg(struct iecm_vport *vport, bool ena)
>  	return err;
>  }
>  
> +/**
> + * iecm_send_add_fdir_filter_msg: Send add Flow Director filter message
> + * @vport: vport structure
> + *
> + * Request the CP/PF to add Flow Director as specified by the user via
> + * ethtool
> + *
> + * Return 0 on success, negative on failure
> + **/
> +int iecm_send_add_fdir_filter_msg(struct iecm_vport *vport)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct iecm_fdir_fltr_config *fdir_config;
> +	struct iecm_fdir_fltr *fdir;
> +	struct virtchnl_fdir_add *f;
> +	int len, err = 0;
> +
> +	fdir_config = &adapter->config_data.fdir_config;
> +	len = sizeof(struct virtchnl_fdir_add);
> +	/* kzalloc required because otherwise stack is over 2k */
> +	f = kzalloc(len, GFP_KERNEL);
> +	if (!f)
> +		return -ENOMEM;
> +
> +	while (true) {
> +		bool process_fltr = false;
> +
> +		/* Only add a single Flow Director per call */
> +		spin_lock_bh(&adapter->fdir_fltr_list_lock);
> +		list_for_each_entry(fdir, &fdir_config->fdir_fltr_list, list) {
> +			if (fdir->add) {
> +				fdir->add = false;
> +				process_fltr = true;
> +				memcpy(f, &fdir->vc_add_msg, len);
> +				break;
> +			}
> +		}

			if (!fdir->add)
				continue;

			fdir->add = false;
			...
			break;

-1 level.

If you want to keep the condition that way, braces around `if` are
redundant.

> +		spin_unlock_bh(&adapter->fdir_fltr_list_lock);
> +
> +		if (!process_fltr)
> +			break;
> +
> +		err = iecm_send_mb_msg(adapter, VIRTCHNL_OP_ADD_FDIR_FILTER,
> +				       len, (u8 *)f);
> +		if (err)
> +			break;
> +
> +		err = iecm_wait_for_event(adapter, IECM_VC_ADD_FDIR_FILTER,
> +					  IECM_VC_ADD_FDIR_FILTER_ERR);
> +		if (err)
> +			break;
> +
> +		memcpy(f, adapter->vc_msg, len);
> +		if (f->status == VIRTCHNL_FDIR_SUCCESS) {
> +			fdir->flow_id = f->flow_id;
> +		} else {
> +			err = -EIO;
> +			break;
> +		}
> +		clear_bit(__IECM_VC_MSG_PENDING, adapter->flags);
> +	}
> +
> +	clear_bit(__IECM_VC_MSG_PENDING, adapter->flags);
> +	kfree(f);
> +	return err;
> +}
> +
> +/**
> + * iecm_send_del_fdir_filter_msg: Send del Flow Director filter message
> + * @vport: vport structure
> + *
> + * Request the CP/PF to del Flow Director as specified by the user via
> + * ethtool
> + *
> + * Return 0 on success, negative on failure
> + **/
> +int iecm_send_del_fdir_filter_msg(struct iecm_vport *vport)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct iecm_fdir_fltr_config *fdir_config;
> +	struct iecm_fdir_fltr *fdir;
> +	struct virtchnl_fdir_del f;
> +	int err = 0;
> +
> +	fdir_config = &adapter->config_data.fdir_config;
> +
> +	while (true) {
> +		bool process_fltr = false;
> +
> +		/* Only del a single Flow Director filter per call */
> +		spin_lock_bh(&adapter->fdir_fltr_list_lock);
> +		list_for_each_entry(fdir, &fdir_config->fdir_fltr_list, list) {
> +			if (fdir->remove) {
> +				process_fltr = true;
> +				fdir->remove = false;
> +				f.vsi_id = fdir->vc_add_msg.vsi_id;
> +				f.flow_id = fdir->flow_id;
> +				break;
> +			}
> +		}

Same here.

> +		spin_unlock_bh(&adapter->fdir_fltr_list_lock);
> +
> +		if (!process_fltr)
> +			break;
> +
> +		err = iecm_send_mb_msg(adapter, VIRTCHNL_OP_DEL_FDIR_FILTER,
> +				       sizeof(struct virtchnl_fdir_del), (u8 *)&f);
> +		if (err)
> +			break;
> +
> +		err = iecm_wait_for_event(adapter, IECM_VC_DEL_FDIR_FILTER,
> +					  IECM_VC_DEL_FDIR_FILTER_ERR);
> +		clear_bit(__IECM_VC_MSG_PENDING, adapter->flags);
> +	}
> +
> +	clear_bit(__IECM_VC_MSG_PENDING, adapter->flags);
> +	return err;
> +}
> +
>  /**
>   * iecm_send_enable_channels_msg - Send enable channels message
>   * @vport: vport structure
> diff --git a/drivers/net/ethernet/intel/include/iecm.h b/drivers/net/ethernet/intel/include/iecm.h
> index d118da1ea8cd..b0785684cc63 100644
> --- a/drivers/net/ethernet/intel/include/iecm.h
> +++ b/drivers/net/ethernet/intel/include/iecm.h
> @@ -12,6 +12,7 @@
>  #include <linux/netdevice.h>
>  #include <linux/etherdevice.h>
>  #include <linux/ethtool.h>
> +#include <linux/l2tp.h>
>  #include <net/tcp.h>
>  #include <net/ip6_checksum.h>
>  #include <net/ipv6.h>
> @@ -409,6 +410,108 @@ struct iecm_channel_config {
>  	u8 num_tc;
>  };
>  
> +enum iecm_fdir_flow_type {
> +	/* NONE - used for undef/error */
> +	IECM_FDIR_FLOW_NONE = 0,

Enums always start with 0 unless other value specified, this is a
bit excessive.

> +	IECM_FDIR_FLOW_IPV4_TCP,
> +	IECM_FDIR_FLOW_IPV4_UDP,
> +	IECM_FDIR_FLOW_IPV4_SCTP,
> +	IECM_FDIR_FLOW_IPV4_AH,
> +	IECM_FDIR_FLOW_IPV4_ESP,
> +	IECM_FDIR_FLOW_IPV4_OTHER,
> +	IECM_FDIR_FLOW_IPV6_TCP,
> +	IECM_FDIR_FLOW_IPV6_UDP,
> +	IECM_FDIR_FLOW_IPV6_SCTP,
> +	IECM_FDIR_FLOW_IPV6_AH,
> +	IECM_FDIR_FLOW_IPV6_ESP,
> +	IECM_FDIR_FLOW_IPV6_OTHER,
> +	IECM_FDIR_FLOW_NON_IP_L2,
> +	/* MAX - this must be last and add anything new just above it */
> +	IECM_FDIR_FLOW_PTYPE_MAX,
> +};
> +
> +/* Must not exceed the array element number of '__be32 data[2]' in the ethtool
> + * 'struct ethtool_rx_flow_spec.m_ext.data[2]' to express the flex-byte (word).
> + */
> +#define IECM_FLEX_WORD_NUM	2
> +
> +struct iecm_flex_word {
> +	u16 offset;
> +	u16 word;
> +};
> +
> +struct iecm_ipv4_addrs {
> +	__be32 src_ip;
> +	__be32 dst_ip;
> +};
> +
> +struct iecm_ipv6_addrs {
> +	struct in6_addr src_ip;
> +	struct in6_addr dst_ip;
> +};
> +
> +struct iecm_fdir_eth {
> +	__be16 etype;
> +};
> +
> +struct iecm_fdir_ip {
> +	union {
> +		struct iecm_ipv4_addrs v4_addrs;
> +		struct iecm_ipv6_addrs v6_addrs;
> +	};
> +	__be16 src_port;
> +	__be16 dst_port;
> +	__be32 l4_header;	/* first 4 bytes of the layer 4 header */
> +	__be32 spi;		/* security parameter index for AH/ESP */
> +	union {
> +		u8 tos;
> +		u8 tclass;
> +	};
> +	u8 proto;
> +};
> +
> +struct iecm_fdir_extra {
> +	u32 usr_def[IECM_FLEX_WORD_NUM];
> +};
> +
> +/* bookkeeping of Flow Director filters */
> +struct iecm_fdir_fltr {
> +	struct list_head list;
> +
> +	enum iecm_fdir_flow_type flow_type;
> +
> +	struct iecm_fdir_eth eth_data;
> +	struct iecm_fdir_eth eth_mask;
> +
> +	struct iecm_fdir_ip ip_data;
> +	struct iecm_fdir_ip ip_mask;
> +
> +	struct iecm_fdir_extra ext_data;
> +	struct iecm_fdir_extra ext_mask;
> +
> +	enum virtchnl_action action;
> +
> +	/* flex byte filter data */
> +	u8 ip_ver; /* used to adjust the flex offset, 4 : IPv4, 6 : IPv6 */
> +	u8 flex_cnt;
> +	struct iecm_flex_word flex_words[IECM_FLEX_WORD_NUM];
> +
> +	u32 flow_id;
> +
> +	u32 loc;	/* Rule location inside the flow table */
> +	u32 q_index;
> +
> +	struct virtchnl_fdir_add vc_add_msg;
> +	bool remove;	/* Flow Director filter needs to be deleted */
> +	bool add;	/* Flow Director filter needs to be added */

	u8 remove:1;
	u8 add:1;

Booleans are discouraged to use in structs.

> +};
> +
> +struct iecm_fdir_fltr_config {
> +	struct list_head fdir_fltr_list;
> +#define IECM_MAX_FDIR_FILTERS	128	/* max allowed Flow Director filters */

Please place outside function definition.

> +	u16 num_active_filters;
> +};
> +
>  #define IECM_GET_PTYPE_SIZE(p) \
>  	(sizeof(struct virtchnl2_ptype) + \
>  	(((p)->proto_id_count ? ((p)->proto_id_count - 1) : 0) * sizeof(u16)))
> @@ -446,6 +549,7 @@ struct iecm_user_config_data {
>  	struct list_head mac_filter_list;
>  	struct list_head vlan_filter_list;
>  	struct list_head adv_rss_list;
> +	struct iecm_fdir_fltr_config fdir_config;
>  	struct iecm_channel_config ch_config;
>  };
>  
> @@ -749,6 +853,14 @@ void iecm_set_ethtool_ops(struct net_device *netdev);
>  void iecm_vport_set_hsplit(struct iecm_vport *vport, bool ena);
>  void iecm_add_del_ether_addrs(struct iecm_vport *vport, bool add, bool async);
>  int iecm_set_promiscuous(struct iecm_adapter *adapter);
> +int iecm_send_add_fdir_filter_msg(struct iecm_vport *vport);
> +int iecm_send_del_fdir_filter_msg(struct iecm_vport *vport);
> +int iecm_get_fdir_fltr_entry(struct iecm_vport *vport,
> +			     struct ethtool_rxnfc *cmd);
> +int iecm_get_fdir_fltr_ids(struct iecm_vport *vport, struct ethtool_rxnfc *cmd,
> +			   u32 *rule_locs);
> +int iecm_add_fdir_fltr(struct iecm_vport *vport, struct ethtool_rxnfc *cmd);
> +int iecm_del_fdir_fltr(struct iecm_vport *vport, struct ethtool_rxnfc *cmd);
>  int iecm_send_enable_channels_msg(struct iecm_vport *vport);
>  int iecm_send_disable_channels_msg(struct iecm_vport *vport);
>  bool iecm_is_feature_ena(struct iecm_vport *vport, netdev_features_t feature);
> -- 
> 2.33.0

Thanks,
Al

^ permalink raw reply	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 17/19] iecm: implement cloud filters
  2022-01-28  0:10 ` [Intel-wired-lan] [PATCH net-next 17/19] iecm: implement cloud filters Alan Brady
@ 2022-01-28 19:38   ` Alexander Lobakin
  2022-02-03  2:53     ` Brady, Alan
  0 siblings, 1 reply; 89+ messages in thread
From: Alexander Lobakin @ 2022-01-28 19:38 UTC (permalink / raw)
  To: intel-wired-lan

From: Alan Brady <alan.brady@intel.com>
Date: Thu, 27 Jan 2022 16:10:07 -0800

> This gives iecm the ability to deal with cloud filters and other traffic
> classes.
> 
> Signed-off-by: Phani Burra <phani.r.burra@intel.com>
> Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
> Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
> Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
> Signed-off-by: Alan Brady <alan.brady@intel.com>
> ---
>  drivers/net/ethernet/intel/iecm/iecm_lib.c    | 900 +++++++++++++++++-
>  .../net/ethernet/intel/iecm/iecm_virtchnl.c   |  68 ++
>  drivers/net/ethernet/intel/include/iecm.h     |  25 +
>  3 files changed, 992 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/net/ethernet/intel/iecm/iecm_lib.c b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> index 35c0cbc42ebe..d11413cb438c 100644
> --- a/drivers/net/ethernet/intel/iecm/iecm_lib.c
> +++ b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> @@ -43,9 +43,16 @@ static int iecm_get_vport_index(struct iecm_adapter *adapter,
>   */
>  bool iecm_is_feature_ena(struct iecm_vport *vport, netdev_features_t feature)
>  {
> +	struct iecm_channel_config *ch_config;
>  	bool ena;
>  
>  	switch (feature) {
> +	case NETIF_F_HW_TC:
> +		ch_config = &vport->adapter->config_data.ch_config;
> +		ena = (vport->netdev->features & feature) &&
> +			(ch_config->num_tc > IECM_START_CHNL_TC) &&
> +			(ch_config->tc_running);
> +		break;
>  	default:
>  		ena = vport->netdev->features & feature;
>  		break;
> @@ -53,6 +60,23 @@ bool iecm_is_feature_ena(struct iecm_vport *vport, netdev_features_t feature)
>  	return ena;
>  }
>  
> +/**
> + * iecm_is_adq_v2_ena - Determine whether ADQ V2 is enabled
> + * @vport: virtual port struct
> + *
> + * This function returns true based on negotiated capability ADQ_V2
> + * if set and ADQ enabled
> + */
> +static bool iecm_is_adq_v2_ena(struct iecm_vport *vport)
> +{
> +	/* iecm_is_feature_ena tells if the netdev flag is set and adq is
> +	 * enabled
> +	 */
> +	return (iecm_is_feature_ena(vport, NETIF_F_HW_TC) &&
> +		iecm_is_cap_ena(vport->adapter, IECM_OTHER_CAPS,
> +				VIRTCHNL2_CAP_ADQ));

The outermost braces are redundant.

> +}
> +
>  /**
>   * iecm_is_vlan_cap_ena - Check if VLAN capability is enabled
>   * @adapter: pointer to adapter
> @@ -946,6 +970,28 @@ static int iecm_get_free_slot(void *array, int size, int curr)
>  	return next;
>  }
>  
> +/**
> + * iecm_remove_cloud_filters - Remove all cloud filters
> + * @vport: vport structure
> + */
> +static void iecm_remove_cloud_filters(struct iecm_vport *vport)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct iecm_cloud_filter_config *cf_config;
> +
> +	cf_config = &adapter->config_data.cf_config;
> +	if (!list_empty(&cf_config->cloud_filter_list)) {
> +		struct iecm_cloud_filter *cf;
> +
> +		spin_lock_bh(&adapter->cloud_filter_list_lock);
> +		list_for_each_entry(cf, &cf_config->cloud_filter_list, list) {
> +			cf->remove = true;
> +		}

One-liner, braces are redundant.

> +		spin_unlock_bh(&adapter->cloud_filter_list_lock);
> +		iecm_send_add_del_cloud_filter_msg(vport, false);
> +	}

	if (list_empty())
		return;

-1 level.

> +}
> +
>  /**
>   * iecm_remove_vlan_filters - Remove all vlan filters
>   * @vport: vport structure
> @@ -1044,8 +1090,14 @@ static void iecm_vport_stop(struct iecm_vport *vport)
>  	if (test_and_clear_bit(__IECM_DEL_QUEUES,
>  			       vport->adapter->flags))
>  		iecm_send_delete_queues_msg(vport);
> -	if (!test_bit(__IECM_REL_RES_IN_PROG, adapter->flags))
> +	/* In function reset/rmmod path we call unregister_netdev which
> +	 * internally calls delete cloud filters. We remove cloud filters only
> +	 * when the interface goes down
> +	 */
> +	if (!test_bit(__IECM_REL_RES_IN_PROG, adapter->flags)) {
> +		iecm_remove_cloud_filters(vport);
>  		iecm_remove_vlan_filters(vport);
> +	}
>  
>  	iecm_remove_fdir_filters(vport);
>  
> @@ -1258,6 +1310,28 @@ static void iecm_restore_vlans(struct iecm_vport *vport)
>  		iecm_set_all_vlans(vport);
>  }
>  
> +/**
> + * iecm_restore_cloud_filters - Restore cloud filters
> + * @vport: vport structure
> + */
> +static void iecm_restore_cloud_filters(struct iecm_vport *vport)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct iecm_cloud_filter_config *cf_config;
> +
> +	cf_config = &adapter->config_data.cf_config;
> +	if (!list_empty(&cf_config->cloud_filter_list)) {
> +		struct iecm_cloud_filter *cf;
> +
> +		spin_lock_bh(&adapter->cloud_filter_list_lock);
> +		list_for_each_entry(cf, &cf_config->cloud_filter_list, list) {
> +			cf->add = true;
> +		}

Same here for braces.

> +		spin_unlock_bh(&adapter->cloud_filter_list_lock);
> +		iecm_send_add_del_cloud_filter_msg(vport, true);
> +	}

Same here for reducing indent level.

> +}
> +
>  /**
>   * iecm_restore_fdir_filters - Restore all Flow Director filters
>   * @vport: vport structure
> @@ -1302,6 +1376,10 @@ static void iecm_restore_features(struct iecm_vport *vport)
>  			dev_info(&adapter->pdev->dev, "Failed to restore promiscuous settings\n");
>  	}
>  
> +	/* Restore cloud filters if ADQ is enabled */
> +	if (iecm_is_feature_ena(vport, NETIF_F_HW_TC))
> +		iecm_restore_cloud_filters(vport);
> +
>  	if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS, VIRTCHNL2_CAP_FDIR))
>  		iecm_restore_fdir_filters(vport);
>  }
> @@ -2088,6 +2166,8 @@ int iecm_probe(struct pci_dev *pdev,
>  	spin_lock_init(&adapter->vlan_list_lock);
>  	spin_lock_init(&adapter->adv_rss_list_lock);
>  	spin_lock_init(&adapter->fdir_fltr_list_lock);
> +	INIT_LIST_HEAD(&adapter->config_data.cf_config.cloud_filter_list);
> +	INIT_LIST_HEAD(&adapter->config_data.cf_config.block_cb_list);
>  	INIT_LIST_HEAD(&adapter->config_data.mac_filter_list);
>  	INIT_LIST_HEAD(&adapter->config_data.vlan_filter_list);
>  	INIT_LIST_HEAD(&adapter->config_data.adv_rss_list);
> @@ -2389,6 +2469,810 @@ static int iecm_offload_txtime(struct iecm_vport *vport,
>  	return -EOPNOTSUPP;
>  }
>  
> +/**
> + * iecm_is_vlan_tc_filter_allowed - allowed to add tc-filter using VLAN
> + * @vport: vport structure
> + * @vlan: VLAN to verify
> + *
> + * Using specified "vlan" ID, there must be active VLAN filter in VF's
> + * MAC-VLAN filter list.
> + */
> +static bool
> +iecm_is_vlan_tc_filter_allowed(struct iecm_vport *vport,
> +			       struct iecm_vlan *vlan)
> +{
> +	struct iecm_vlan_filter *f;
> +	bool allowed;
> +
> +	spin_lock_bh(&vport->adapter->vlan_list_lock);
> +	f = iecm_find_vlan(vport, vlan);
> +	allowed = (f && !f->add && !f->remove);

Redundant braces here.

> +	spin_unlock_bh(&vport->adapter->vlan_list_lock);
> +	return allowed;
> +}
> +
> +/**
> + * iecm_is_mac_tc_filter_allowed - allowed to add tc-filter using MAC addr
> + * @vport: vport structure
> + * @macaddr: MAC address
> + *
> + * Using specified MAC address, there must be active MAC filter in
> + * MAC filter list.
> + */
> +static bool
> +iecm_is_mac_tc_filter_allowed(struct iecm_vport *vport, const u8 *macaddr)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct iecm_mac_filter *f;
> +	bool allowed;
> +
> +	spin_lock_bh(&adapter->mac_filter_list_lock);
> +	f = iecm_find_mac_filter(vport, macaddr);
> +	allowed = (f && !f->add && !f->remove);

Same here.

> +	spin_unlock_bh(&adapter->mac_filter_list_lock);
> +	return allowed;
> +}
> +
> +/**
> + * iecm_parse_keyid - Parse keyid
> + * @rule: Flow rule structure
> + * @field_flags: Cloud filter flags
> + */
> +static void  iecm_parse_keyid(struct flow_rule *rule, u8 *field_flags)
> +{
> +	struct flow_match_enc_keyid match;
> +
> +	flow_rule_match_enc_keyid(rule, &match);
> +
> +	if (match.mask->keyid != 0)
> +		*field_flags |= IECM_CLOUD_FIELD_TEN_ID;
> +}
> +
> +/**
> + * iecm_parse_flow_type - Parse flow type based on L2 and L3 protocols
> + * @vport: vport structure
> + * @rule: rule from user
> + * @cf: Structure for the virtchnl filter
> + * @filter: Structure for the cloud filter
> + *
> + * Return 0 on success, negative on failure
> + */
> +static int
> +iecm_parse_flow_type(struct iecm_vport *vport,
> +		     struct flow_rule *rule, struct virtchnl_filter *cf,
> +		     struct iecm_cloud_filter *filter)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	enum virtchnl_flow_type flow_type;
> +	struct flow_match_basic match;
> +	u16 n_proto_mask = 0;
> +	u16 n_proto_key = 0;
> +	u16 n_proto = 0;
> +	u8 ip_proto = 0;
> +
> +	flow_rule_match_basic(rule, &match);
> +
> +	n_proto_key = ntohs(match.key->n_proto);
> +	n_proto_mask = ntohs(match.mask->n_proto);
> +
> +	if (n_proto_key == ETH_P_ALL) {
> +		n_proto_key = 0;
> +		n_proto_mask = 0;
> +	}

	n_proto_key = ntohs();
	if (n_proto_key != ETH_P_ALL)
		n_proto_mask = ntohs();

> +	n_proto = n_proto_key & n_proto_mask;
> +	if (n_proto != ETH_P_IP && n_proto != ETH_P_IPV6)
> +		return -EINVAL;
> +
> +	if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS, VIRTCHNL2_CAP_ADQ)) {
> +		if (match.key->ip_proto != IPPROTO_TCP &&
> +		    match.key->ip_proto != IPPROTO_UDP) {
> +			dev_err(&adapter->pdev->dev,
> +				"Only TCP or UDP transport is supported\n");
> +			return -EINVAL;
> +		}
> +	} else if (match.key->ip_proto != IPPROTO_TCP) {
> +		dev_err(&adapter->pdev->dev,
> +			"Only TCP transport is supported\n");
> +		return -EINVAL;
> +	}
> +	ip_proto = match.key->ip_proto;
> +
> +	/* determine VIRTCHNL flow_type based on L3 and L4 protocol */
> +	if (n_proto == ETH_P_IP)
> +		flow_type = (ip_proto == IPPROTO_TCP) ?
> +			     VIRTCHNL_TCP_V4_FLOW :
> +			     VIRTCHNL_UDP_V4_FLOW;
> +	else
> +		flow_type = (ip_proto == IPPROTO_TCP) ?
> +			     VIRTCHNL_TCP_V6_FLOW :
> +			     VIRTCHNL_UDP_V6_FLOW;
> +	cf->flow_type = flow_type;
> +	filter->f.flow_type = flow_type;
> +
> +	return 0;
> +}
> +
> +/**
> + * iecm_parse_ether_header - Parse ethernet header fields
> + * @vport: vport structure
> + * @field_flags: Cloud filter flags
> + * @d_spec: Virtchnl structure for L4 specs
> + * @m_spec: Virtchnl structure for L4 specs
> + * @rule: Flow rule structure
> + *
> + * Return 0 on success, negative on failure
> + */
> +static int
> +iecm_parse_ether_header(struct iecm_vport *vport, u8 *field_flags,
> +			struct virtchnl_l4_spec *d_spec,
> +			struct virtchnl_l4_spec *m_spec,
> +			struct flow_rule *rule)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct flow_match_eth_addrs match;
> +	bool adv_adq_ena;
> +
> +	adv_adq_ena = iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
> +				      VIRTCHNL2_CAP_ADQ);
> +
> +	flow_rule_match_eth_addrs(rule, &match);
> +
> +	/* use is_broadcast and is_zero to check for all 0xf or 0 */
> +	if (!is_zero_ether_addr(match.mask->dst)) {
> +		if (adv_adq_ena || is_broadcast_ether_addr(match.mask->dst)) {
> +			*field_flags |= IECM_CLOUD_FIELD_OMAC;
> +		} else {
> +			dev_err(&adapter->pdev->dev, "Bad ether dest mask %pM\n",
> +				match.mask->dst);
> +			return -EINVAL;
> +		}
> +	}
> +
> +	if (!is_zero_ether_addr(match.mask->src)) {
> +		if (adv_adq_ena || is_broadcast_ether_addr(match.mask->src)) {
> +			*field_flags |= IECM_CLOUD_FIELD_IMAC;
> +		} else {
> +			dev_err(&adapter->pdev->dev, "Bad ether src mask %pM\n",
> +				match.mask->src);
> +			return -EINVAL;
> +		}
> +	}
> +
> +	if (!is_zero_ether_addr(match.key->dst)) {
> +		if (!iecm_is_mac_tc_filter_allowed(adapter->vports[0],
> +						   match.key->dst)) {
> +			dev_err(&adapter->pdev->dev,
> +				"Dest MAC %pM doesn't belong to this device\n",
> +				match.key->dst);
> +			return -EINVAL;
> +		}
> +
> +		if (is_valid_ether_addr(match.key->dst) ||
> +		    is_multicast_ether_addr(match.key->dst)) {
> +			/* set the mask if a valid dst_mac address */
> +			if (adv_adq_ena)
> +				ether_addr_copy(m_spec->dst_mac,
> +						match.mask->dst);
> +			else
> +				eth_broadcast_addr(m_spec->dst_mac);
> +			ether_addr_copy(d_spec->dst_mac,
> +					match.key->dst);
> +		}
> +	}
> +
> +	if (!is_zero_ether_addr(match.key->src))
> +		if (is_valid_ether_addr(match.key->src) ||
> +		    is_multicast_ether_addr(match.key->src)) {
> +			/* set the mask if a valid src_mac address */
> +			if (adv_adq_ena) {
> +				ether_addr_copy(m_spec->src_mac,
> +						match.mask->src);
> +			} else {
> +				eth_broadcast_addr(m_spec->src_mac);
> +			}
> +			ether_addr_copy(d_spec->src_mac,
> +					match.key->src);
> +		}

All previous ifs had braces, and it way okay since we have multiple
nested if-elses, but here we don't have them.
Please either add them here or remove all the way above to keep the
code style consistent.

> +	return 0;
> +}
> +
> +/**
> + * iecm_parse_vlan_header - Parse vlan header fields
> + * @vport: vport structure
> + * @field_flags: Cloud filter flags
> + * @d_spec: Virtchnl structure for L4 specs
> + * @m_spec: Virtchnl structure for L4 specs
> + * @rule: Flow rule structure
> + *
> + * Return 0 on success, negative on failure
> + */
> +static int
> +iecm_parse_vlan_header(struct iecm_vport *vport, u8 *field_flags,
> +		       struct virtchnl_l4_spec *d_spec,
> +		       struct virtchnl_l4_spec *m_spec,
> +		       struct flow_rule *rule)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct flow_match_vlan match;
> +
> +	flow_rule_match_vlan(rule, &match);
> +	if (match.mask->vlan_id) {
> +		u16 vid = match.key->vlan_id & VLAN_VID_MASK;
> +		struct iecm_vlan vlan;
> +
> +		vlan = IECM_VLAN(vid, ETH_P_8021Q);
> +
> +		if (match.mask->vlan_id != VLAN_VID_MASK) {
> +			dev_err(&adapter->pdev->dev, "Bad vlan mask %u\n",
> +				match.mask->vlan_id);
> +			return -EINVAL;
> +		}
> +		if (!iecm_is_vlan_tc_filter_allowed(vport, &vlan)) {
> +			dev_err(&adapter->pdev->dev,
> +				"VLAN %u doesn't belong to this VF\n",
> +				vid);
> +			return -EINVAL;
> +		}
> +		*field_flags |= IECM_CLOUD_FIELD_IVLAN;
> +		m_spec->vlan_id = cpu_to_be16(match.mask->vlan_id);
> +		d_spec->vlan_id = cpu_to_be16(match.key->vlan_id);
> +	}

	if (!vlan_id)
		return 0;

-1 level.

> +	return 0;
> +}
> +
> +/**
> + * iecm_parse_ipv4_header - Parse ipv4 header fields
> + * @vport: vport structure
> + * @field_flags: Cloud filter flags
> + * @d_spec: Virtchnl structure for L4 specs
> + * @m_spec: Virtchnl structure for L4 specs
> + * @rule: Flow rule structure
> + *
> + * Return 0 on success, negative on failure
> + */
> +static int
> +iecm_parse_ipv4_header(struct iecm_vport *vport, u8 *field_flags,
> +		       struct virtchnl_l4_spec *d_spec,
> +		       struct virtchnl_l4_spec *m_spec,
> +		       struct flow_rule *rule)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct flow_match_ipv4_addrs match;
> +	bool adv_adq_ena;
> +
> +	adv_adq_ena = iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
> +				      VIRTCHNL2_CAP_ADQ);
> +
> +	flow_rule_match_ipv4_addrs(rule, &match);
> +
> +	if (*field_flags & IECM_CLOUD_FIELD_TEN_ID) {
> +		dev_info(&adapter->pdev->dev,
> +			 "Tenant id not allowed for ip filter\n");
> +		return -EINVAL;
> +	}
> +
> +	if (match.mask->dst) {
> +		if (adv_adq_ena || match.mask->dst == cpu_to_be32(0xffffffff)) {
> +			*field_flags |= IECM_CLOUD_FIELD_IIP;
> +		} else {
> +			dev_err(&adapter->pdev->dev, "Bad ip dst mask 0x%08x\n",
> +				be32_to_cpu(match.mask->dst));
> +			return -EINVAL;
> +		}
> +	}
> +
> +	if (match.mask->src) {
> +		if (adv_adq_ena || match.mask->src == cpu_to_be32(0xffffffff)) {
> +			*field_flags |= IECM_CLOUD_FIELD_IIP;
> +		} else {
> +			dev_err(&adapter->pdev->dev, "Bad ip src mask 0x%08x\n",
> +				be32_to_cpu(match.mask->dst));
> +			return -EINVAL;
> +		}
> +	}
> +
> +	if (match.key->dst) {
> +		if (adv_adq_ena)
> +			m_spec->dst_ip[0] = match.mask->dst;
> +		else
> +			m_spec->dst_ip[0] = cpu_to_be32(0xffffffff);
> +		d_spec->dst_ip[0] = match.key->dst;
> +	}
> +
> +	if (match.key->src) {
> +		if (adv_adq_ena)
> +			m_spec->src_ip[0] = match.mask->src;
> +		else
> +			m_spec->src_ip[0] = cpu_to_be32(0xffffffff);
> +		d_spec->src_ip[0] = match.key->src;
> +	}
> +	return 0;
> +}
> +
> +/**
> + * iecm_parse_ipv6_header - Parse ipv6 header fields
> + * @vport: vport structure
> + * @field_flags: Cloud filter flags
> + * @d_spec: Virtchnl structure for L4 specs
> + * @m_spec: Virtchnl structure for L4 specs
> + * @rule: Flow rule structure
> + *
> + * Return 0 on success, negative on failure
> + */
> +static int
> +iecm_parse_ipv6_header(struct iecm_vport *vport, u8 *field_flags,
> +		       struct virtchnl_l4_spec *d_spec,
> +		       struct virtchnl_l4_spec *m_spec,
> +		       struct flow_rule *rule)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct flow_match_ipv6_addrs match;
> +	int i;
> +
> +	flow_rule_match_ipv6_addrs(rule, &match);
> +
> +	/* validate mask, make sure it is not IPV6_ADDR_ANY */
> +	if (ipv6_addr_any(&match.mask->dst)) {
> +		dev_err(&adapter->pdev->dev, "Bad ipv6 dst mask 0x%02x\n",
> +			IPV6_ADDR_ANY);
> +		return -EINVAL;
> +	}
> +
> +	/* src and dest IPv6 address should not be LOOPBACK
> +	 * (0:0:0:0:0:0:0:1) which can be represented as ::1
> +	 */
> +	if (ipv6_addr_loopback(&match.key->dst) ||
> +	    ipv6_addr_loopback(&match.key->src)) {
> +		dev_err(&adapter->pdev->dev,
> +			"ipv6 addr should not be loopback\n");
> +		return -EINVAL;
> +	}
> +
> +	if (!ipv6_addr_any(&match.mask->dst) ||
> +	    !ipv6_addr_any(&match.mask->src))
> +		*field_flags |= IECM_CLOUD_FIELD_IIP;
> +
> +	/* copy dest IPv6 mask and address */
> +	if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS, VIRTCHNL2_CAP_ADQ)) {
> +		memcpy(&m_spec->dst_ip, &match.mask->dst.s6_addr32,
> +		       sizeof(m_spec->dst_ip));
> +	} else {
> +		for (i = 0; i < 4; i++)
> +			m_spec->dst_ip[i] = cpu_to_be32(0xffffffff);
> +	}

One-liners, no braces needed for both `if` and `else`.

> +	memcpy(&d_spec->dst_ip, &match.key->dst.s6_addr32,
> +	       sizeof(d_spec->dst_ip));
> +
> +	/* copy source IPv6 mask and address */
> +	if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS, VIRTCHNL2_CAP_ADQ)) {
> +		memcpy(&m_spec->src_ip, &match.mask->src.s6_addr32,
> +		       sizeof(m_spec->src_ip));
> +	} else {
> +		for (i = 0; i < 4; i++)
> +			m_spec->src_ip[i] = cpu_to_be32(0xffffffff);
> +	}

Same here.

> +	memcpy(&d_spec->src_ip, &match.key->src.s6_addr32,
> +	       sizeof(d_spec->src_ip));
> +
> +	return 0;
> +}
> +
> +/**
> + * iecm_parse_l4_header - Parse l4 header fields
> + * @vport: vport structure
> + * @d_spec: Virtchnl structure for L4 specs
> + * @m_spec: Virtchnl structure for L4 specs
> + * @rule: Flow rule structure
> + *
> + * Return 0 on success, negative on failure
> + */
> +static int
> +iecm_parse_l4_header(struct iecm_vport *vport,
> +		     struct virtchnl_l4_spec *d_spec,
> +		     struct virtchnl_l4_spec *m_spec,
> +		     struct flow_rule *rule)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct flow_match_ports match;
> +
> +	flow_rule_match_ports(rule, &match);
> +
> +	if (match.key->dst) {
> +		if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
> +				    VIRTCHNL2_CAP_ADQ) ||
> +		    match.mask->dst == cpu_to_be16(0xffff)) {
> +			m_spec->dst_port = match.mask->dst;
> +			d_spec->dst_port = match.key->dst;
> +		} else {
> +			dev_err(&adapter->pdev->dev, "Bad dst port mask %u\n",
> +				be16_to_cpu(match.mask->dst));
> +			return -EINVAL;
> +		}
> +	}
> +
> +	if (match.key->src) {
> +		if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
> +				    VIRTCHNL2_CAP_ADQ) ||
> +		    match.mask->src == cpu_to_be16(0xffff)) {
> +			m_spec->src_port = match.mask->src;
> +			d_spec->src_port = match.key->src;
> +		} else {
> +			dev_err(&adapter->pdev->dev, "Bad src port mask %u\n",
> +				be16_to_cpu(match.mask->src));
> +			return -EINVAL;
> +		}
> +	}
> +	return 0;
> +}
> +
> +/**
> + * iecm_parse_cls_flower - Parse tc flower filters provided by kernel
> + * @vport: vport structure
> + * @f: pointer to struct flow_cls_offload
> + * @filter: pointer to cloud filter structure
> + */
> +static int iecm_parse_cls_flower(struct iecm_vport *vport,
> +				 struct flow_cls_offload *f,
> +				 struct iecm_cloud_filter *filter)
> +{
> +	struct flow_rule *rule = flow_cls_offload_flow_rule(f);
> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct virtchnl_l4_spec *d_spec, *m_spec;
> +	struct virtchnl_filter *cf = &filter->f;
> +	struct flow_dissector *dissector;
> +	u8 field_flags = 0;
> +	u16 addr_type = 0;
> +	int err;
> +
> +	dissector = rule->match.dissector;
> +	if (dissector->used_keys &
> +	    ~(BIT(FLOW_DISSECTOR_KEY_CONTROL) |
> +	      BIT(FLOW_DISSECTOR_KEY_BASIC) |
> +	      BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS) |
> +	      BIT(FLOW_DISSECTOR_KEY_VLAN) |
> +	      BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS) |
> +	      BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS) |
> +	      BIT(FLOW_DISSECTOR_KEY_ENC_KEYID) |
> +	      BIT(FLOW_DISSECTOR_KEY_PORTS))) {
> +		dev_err(&adapter->pdev->dev, "Unsupported key used: 0x%x\n",
> +			dissector->used_keys);
> +		return -EOPNOTSUPP;
> +	}
> +	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_KEYID))
> +		iecm_parse_keyid(rule, &field_flags);
> +
> +	/* even though following code refers as "tcp_sec", it is not
> +	 * just for TCP but a generic struct representing
> +	 * L2, L3 + L4 fields if specified
> +	 */
> +	m_spec = &cf->mask.tcp_spec;
> +	d_spec = &cf->data.tcp_spec;
> +
> +	/* determine flow type, TCP/UDP_V4[6]_FLOW based on
> +	 * L2 proto (aka ETH proto) and L3 proto (aka IP_PROTO)
> +	 */
> +	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {
> +		err = iecm_parse_flow_type(vport, rule, cf, filter);
> +		if (err)
> +			return err;
> +	}
> +
> +	/* process Ethernet header fields */
> +	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS)) {
> +		err = iecm_parse_ether_header(vport, &field_flags,
> +					      d_spec, m_spec, rule);
> +		if (err)
> +			return err;
> +	}
> +
> +	/* process VLAN header for single VLAN (type could be S/C-tag) */
> +	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) {
> +		err = iecm_parse_vlan_header(vport, &field_flags,
> +					     d_spec, m_spec, rule);
> +		if (err)
> +			return err;
> +	}
> +
> +	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CONTROL)) {
> +		struct flow_match_control match;
> +
> +		flow_rule_match_control(rule, &match);
> +		addr_type = match.key->addr_type;
> +	}
> +
> +	/* process IPv4 header */
> +	if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) {
> +		err = iecm_parse_ipv4_header(vport, &field_flags,
> +					     d_spec, m_spec, rule);
> +		if (err)
> +			return err;
> +	}
> +
> +	/* process IPv6 header */
> +	if (addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) {
> +		err = iecm_parse_ipv6_header(vport, &field_flags,
> +					     d_spec, m_spec, rule);
> +		if (err)
> +			return err;
> +	}
> +
> +	/* process L4 header, supported L4 protocols are TCP and UDP */
> +	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) {
> +		err = iecm_parse_l4_header(vport, d_spec, m_spec, rule);
> +		if (err)
> +			return err;
> +	}
> +	cf->field_flags = field_flags;
> +
> +	return 0;
> +}
> +
> +/**
> + * iecm_handle_tclass - Forward to a traffic class on the device
> + * @vport: vport structure
> + * @tc: traffic class index on the device
> + * @filter: pointer to cloud filter structure
> + *
> + * Return 0 on success, negative on failure
> + */
> +static int iecm_handle_tclass(struct iecm_vport *vport, int tc,
> +			      struct iecm_cloud_filter *filter)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +
> +	if (tc == 0)
> +		return 0;
> +	if ((!iecm_is_adq_v2_ena(vport)) &&
> +	    !filter->f.data.tcp_spec.dst_port) {
> +		dev_err(&adapter->pdev->dev,
> +			"Specify destination port to redirect to traffic class other than TC0\n");
> +		return -EINVAL;
> +	}
> +	/* redirect to a traffic class on the same device */
> +	filter->f.action = VIRTCHNL_ACTION_TC_REDIRECT;
> +	filter->f.action_meta = tc;
> +	return 0;
> +}
> +
> +/* iecm_find_cf - Find the cloud filter in the list
> + * @vport: vport structure
> + * @cookie: filter specific cookie
> + *
> + * Returns pointer to the filter object or NULL. Must be called while holding
> + * cloud_filter_list_lock.
> + */
> +static struct iecm_cloud_filter *iecm_find_cf(struct iecm_vport *vport,
> +					      unsigned long *cookie)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct iecm_cloud_filter *filter = NULL;
> +
> +	if (!cookie)
> +		return NULL;
> +
> +	list_for_each_entry(filter,
> +			    &adapter->config_data.cf_config.cloud_filter_list,
> +			    list) {
> +		if (!memcmp(cookie, &filter->cookie, sizeof(filter->cookie)))
> +			return filter;
> +	}

Redundant braces round single statement.

> +	return NULL;
> +}
> +
> +/**
> + * iecm_configure_clsflower - Add tc flower filters
> + * @vport: vport structure
> + * @cls_flower: Pointer to struct flow_cls_offload
> + *
> + * Return 0 on success, negative on failure
> + */
> +static int iecm_configure_clsflower(struct iecm_vport *vport,
> +				    struct flow_cls_offload *cls_flower)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct iecm_user_config_data *config_data;
> +	struct iecm_cloud_filter *filter = NULL;
> +	int err;
> +	int tc;
> +
> +	config_data = &adapter->config_data;
> +	tc = tc_classid_to_hwtc(vport->netdev, cls_flower->classid);
> +	if (tc < 0) {
> +		dev_err(&adapter->pdev->dev, "Invalid traffic class\n");
> +		return -EINVAL;
> +	}
> +
> +#define IECM_MAX_CLOUD_ADQ_FILTERS	128
> +
> +	if (config_data->cf_config.num_cloud_filters >=
> +						IECM_MAX_CLOUD_ADQ_FILTERS) {
> +		dev_err(&adapter->pdev->dev,
> +			"Unable to add filter (action is forward to TC), reached max allowed filters (%u)\n",
> +			IECM_MAX_CLOUD_ADQ_FILTERS);
> +		return -ENOSPC;
> +	}
> +
> +	/* bail out here if filter already exists */
> +	spin_lock_bh(&adapter->cloud_filter_list_lock);
> +	filter = iecm_find_cf(vport, &cls_flower->cookie);
> +	if (filter) {
> +		filter->remove = false;
> +		dev_err(&adapter->pdev->dev, "Failed to add TC Flower filter, it already exists\n");
> +		spin_unlock_bh(&adapter->cloud_filter_list_lock);
> +		return -EEXIST;
> +	}
> +	spin_unlock_bh(&adapter->cloud_filter_list_lock);
> +
> +	filter = kzalloc(sizeof(*filter), GFP_KERNEL);
> +	if (!filter)
> +		return -ENOMEM;
> +
> +	filter->cookie = cls_flower->cookie;
> +
> +	/* set the mask to all zeroes to begin with */
> +	memset(&filter->f.mask.tcp_spec, 0, sizeof(struct virtchnl_l4_spec));
> +
> +	/* start out with flow type and eth type IPv4 to begin with */
> +	filter->f.flow_type = VIRTCHNL_TCP_V4_FLOW;
> +	err = iecm_parse_cls_flower(vport, cls_flower, filter);
> +	if (err)
> +		goto error;
> +
> +	err = iecm_handle_tclass(vport, tc, filter);
> +	if (err)
> +		goto error;
> +
> +	/* add filter to the list */
> +	spin_lock_bh(&adapter->cloud_filter_list_lock);
> +	list_add_tail(&filter->list, &config_data->cf_config.cloud_filter_list);
> +	filter->add = true;
> +	spin_unlock_bh(&adapter->cloud_filter_list_lock);
> +	err = iecm_send_add_del_cloud_filter_msg(vport, true);
> +	spin_lock_bh(&adapter->cloud_filter_list_lock);
> +	/* We have to find it again in case another thread has already
> +	 * deleted and kfreed it.
> +	 */
> +	filter = iecm_find_cf(vport, &cls_flower->cookie);
> +	if (filter && err) {
> +		list_del(&filter->list);
> +		spin_unlock_bh(&adapter->cloud_filter_list_lock);
> +		goto error;
> +	}
> +	spin_unlock_bh(&adapter->cloud_filter_list_lock);
> +
> +	config_data->cf_config.num_cloud_filters++;
> +error:
> +	if (err)
> +		kfree(filter);
> +	return err;
> +}
> +
> +/**
> + * iecm_delete_clsflower - Remove tc flower filters
> + * @vport: vport structure
> + * @cls_flower: Pointer to struct flow_cls_offload
> + *
> + * Return 0 on success, negative on failure
> + */
> +static int iecm_delete_clsflower(struct iecm_vport *vport,
> +				 struct flow_cls_offload *cls_flower)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct iecm_cloud_filter *filter = NULL;
> +	int err = 0;
> +
> +	spin_lock_bh(&adapter->cloud_filter_list_lock);
> +	filter = iecm_find_cf(vport, &cls_flower->cookie);
> +	if (filter) {
> +		filter->remove = true;
> +		adapter->config_data.cf_config.num_cloud_filters--;
> +	} else if (adapter->config_data.cf_config.num_cloud_filters) {
> +		/* "num_cloud_filters" can become zero if egress qdisc is
> +		 * detached as per design, driver deletes related filters
> +		 * when qdisc is detached to avoid stale filters, hence
> +		 * num_cloud_filters can become zero. But since netdev
> +		 * layer doesn't know that filters are deleted by driver
> +		 * implictly when egress qdisc is deleted, it sees filters
> +		 * being present and "in_hw". User can request delete
> +		 * of specific filter of detach ingress qdisc - in either of
> +		 * those operation, filter(s) won't be found in driver cache,
> +		 * hence instead of returning, let this function return SUCCESS
> +		 * Returning of err as -EINVAL is only applicable when
> +		 * unable to find filter and num_cloud_filters is non-zero
> +		 */
> +		err = -EINVAL;
> +	}
> +	spin_unlock_bh(&adapter->cloud_filter_list_lock);
> +
> +	if (filter) {
> +		err = iecm_send_add_del_cloud_filter_msg(vport, false);
> +		spin_lock_bh(&adapter->cloud_filter_list_lock);
> +		/* It can happen that asynchronously the filter was already
> +		 * deleted from the list. Make sure it's still there and marked
> +		 * for remove under spinlock before actually trying to delete
> +		 * from list.
> +		 */
> +		filter = iecm_find_cf(vport, &cls_flower->cookie);
> +		if (filter) {
> +			list_del(&filter->list);
> +			kfree(filter);
> +		}
> +		spin_unlock_bh(&adapter->cloud_filter_list_lock);
> +	}

	if (!filter)
		return err;

	err = ...

-1 level.

> +	return err;
> +}
> +
> +/**
> + * iecm_setup_tc_cls_flower - flower classifier offloads
> + * @vport: vport structure
> + * @cls_flower: pointer to struct flow_cls_offload
> + *
> + * Return 0 on success, negative on failure
> + */
> +static int iecm_setup_tc_cls_flower(struct iecm_vport *vport,
> +				    struct flow_cls_offload *cls_flower)
> +{
> +	if (cls_flower->common.chain_index)
> +		return -EOPNOTSUPP;
> +
> +	switch (cls_flower->command) {
> +	case FLOW_CLS_REPLACE:
> +		return iecm_configure_clsflower(vport, cls_flower);
> +	case FLOW_CLS_DESTROY:
> +		return iecm_delete_clsflower(vport, cls_flower);
> +	case FLOW_CLS_STATS:
> +		return -EOPNOTSUPP;
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +/**
> + * iecm_setup_tc_block_cb - block callback for tc
> + * @type: type of offload
> + * @type_data: offload data
> + * @cb_priv: Private adapter structure
> + *
> + * This function is the block callback for traffic classes
> + * Return 0 on success, negative on failure
> + **/
> +static int iecm_setup_tc_block_cb(enum tc_setup_type type, void *type_data,
> +				  void *cb_priv)
> +{
> +	switch (type) {
> +	case TC_SETUP_CLSFLOWER:
> +		return iecm_setup_tc_cls_flower((struct iecm_vport *)cb_priv,
> +						(struct flow_cls_offload *)
> +						 type_data);

Just dereference them in separate variables and they will fit into
one line. There shouldn't be any spaces or line breaks after a cast.

> +	default:
> +		return -EOPNOTSUPP;
> +	}
> +}
> +
> +/**
> + * iecm_del_all_cloud_filters - delete all cloud filters on the traffic classes
> + * @vport: vport structure
> + *
> + * This function will loop through the list of cloud filters and deletes them.
> + **/
> +static void iecm_del_all_cloud_filters(struct iecm_vport *vport)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct iecm_cloud_filter_config *cf_config;
> +	struct iecm_cloud_filter *cf, *cftmp;
> +
> +	cf_config = &adapter->config_data.cf_config;
> +	spin_lock_bh(&adapter->cloud_filter_list_lock);
> +	list_for_each_entry_safe(cf, cftmp,
> +				 &cf_config->cloud_filter_list,
> +				 list) {
> +		list_del(&cf->list);
> +		kfree(cf);
> +		cf_config->num_cloud_filters--;
> +	}
> +	spin_unlock_bh(&adapter->cloud_filter_list_lock);
> +}
> +
>  /**
>   * iecm_validate_tx_bandwidth - validate the max Tx bandwidth
>   * @vport: vport structure
> @@ -2596,6 +3480,7 @@ static int __iecm_setup_tc(struct iecm_vport *vport, void *type_data)
>  			netif_tx_stop_all_queues(netdev);
>  			netif_tx_disable(netdev);
>  			ret = iecm_send_disable_channels_msg(vport);
> +			iecm_del_all_cloud_filters(vport);
>  			netif_tx_start_all_queues(netdev);
>  			if (!test_bit(__IECM_REL_RES_IN_PROG, adapter->flags) &&
>  			    !ret) {
> @@ -2709,8 +3594,10 @@ static int iecm_setup_tc(struct net_device *netdev, enum tc_setup_type type,
>  {
>  	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
>  	struct iecm_adapter *adapter = vport->adapter;
> +	struct iecm_cloud_filter_config *cf_config;
>  	int err = 0;
>  
> +	cf_config = &adapter->config_data.cf_config;
>  	switch (type) {
>  	case TC_SETUP_QDISC_ETF:
>  		if (iecm_is_queue_model_split(vport->txq_model))
> @@ -2720,6 +3607,17 @@ static int iecm_setup_tc(struct net_device *netdev, enum tc_setup_type type,
>  					     type_data);
>  		break;
>  	case TC_SETUP_BLOCK:
> +		if (iecm_is_cap_ena(adapter, IECM_BASE_CAPS,
> +				    VIRTCHNL2_CAP_ADQ) ||
> +		    iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
> +				    VIRTCHNL2_CAP_ADQ)) {
> +			err =
> +			flow_block_cb_setup_simple((struct flow_block_offload *)
> +						    type_data,
> +						   &cf_config->block_cb_list,
> +						   iecm_setup_tc_block_cb,
> +						   vport, vport, true);
> +		}

Invert the condition, and there'll be no line wraps (esp. if you
assign casted @type_data into a separate var).

>  		break;
>  	case TC_SETUP_QDISC_MQPRIO:
>  		if (iecm_is_cap_ena(adapter, IECM_BASE_CAPS,
> diff --git a/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> index 5601846b4674..94af45c36866 100644
> --- a/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> +++ b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> @@ -2731,6 +2731,74 @@ static int iecm_send_insert_vlan_msg(struct iecm_vport *vport, bool ena)
>  	return err;
>  }
>  
> +/**
> + * iecm_send_add_del_cloud_filter_msg: Send add/del cloud filter message
> + * @vport: vport structure
> + * @add: True to add, false to delete cloud filter
> + *
> + * Request the CP/PF to add/del cloud filters as specified by the user via
> + * tc tool
> + *
> + * Return 0 on success, negative on failure
> + **/
> +int iecm_send_add_del_cloud_filter_msg(struct iecm_vport *vport, bool add)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct iecm_cloud_filter_config *cf_config;
> +	struct iecm_cloud_filter *cf;
> +	struct virtchnl_filter f;
> +	int len = 0, err = 0;
> +
> +	cf_config = &adapter->config_data.cf_config;
> +
> +	while (true) {
> +		bool process_fltr = false;
> +
> +		spin_lock_bh(&adapter->cloud_filter_list_lock);
> +		list_for_each_entry(cf, &cf_config->cloud_filter_list, list) {
> +			if (add && cf->add) {
> +				process_fltr = true;
> +				cf->add = false;
> +				f = cf->f;
> +				break;
> +			} else if (!add && cf->remove) {
> +				process_fltr = true;
> +				cf->remove = false;
> +				f = cf->f;
> +				break;
> +			}
> +		}

Redundant braces.

> +		spin_unlock_bh(&adapter->cloud_filter_list_lock);
> +
> +		/* Don't send mailbox message when there are no filters to add/del */
> +		if (!process_fltr)
> +			goto error;
> +
> +		if (add) {
> +			err = iecm_send_mb_msg(adapter, VIRTCHNL_OP_ADD_CLOUD_FILTER,
> +					       len, (u8 *)&f);
> +			if (err)
> +				goto error;
> +
> +			err = iecm_wait_for_event(adapter, IECM_VC_ADD_CLOUD_FILTER,
> +						  IECM_VC_ADD_CLOUD_FILTER_ERR);
> +		} else {
> +			err = iecm_send_mb_msg(adapter, VIRTCHNL_OP_DEL_CLOUD_FILTER,
> +					       len, (u8 *)&f);
> +			if (err)
> +				goto error;
> +
> +			err =
> +			iecm_min_wait_for_event(adapter, IECM_VC_DEL_CLOUD_FILTER,
> +						IECM_VC_DEL_CLOUD_FILTER_ERR);

Too long lines here.

> +		}
> +		if (err)
> +			break;
> +	}
> +error:
> +	return err;
> +}
> +
>  /**
>   * iecm_send_add_fdir_filter_msg: Send add Flow Director filter message
>   * @vport: vport structure
> diff --git a/drivers/net/ethernet/intel/include/iecm.h b/drivers/net/ethernet/intel/include/iecm.h
> index b0785684cc63..0aab41cf982c 100644
> --- a/drivers/net/ethernet/intel/include/iecm.h
> +++ b/drivers/net/ethernet/intel/include/iecm.h
> @@ -403,6 +403,28 @@ enum iecm_user_flags {
>  	__IECM_USER_FLAGS_NBITS,
>  };
>  
> +#define IECM_CLOUD_FIELD_OMAC		BIT(0)
> +#define IECM_CLOUD_FIELD_IMAC		BIT(1)
> +#define IECM_CLOUD_FIELD_IVLAN		BIT(2)
> +#define IECM_CLOUD_FIELD_TEN_ID		BIT(3)
> +#define IECM_CLOUD_FIELD_IIP		BIT(4)
> +
> +#define IECM_START_CHNL_TC		1
> +
> +struct iecm_cloud_filter {
> +	struct list_head list;
> +	struct virtchnl_filter f;
> +	unsigned long cookie;
> +	bool remove;		/* filter needs to be deleted */
> +	bool add;		/* filter needs to be added */
> +};
> +
> +struct iecm_cloud_filter_config {
> +	struct list_head block_cb_list;		/* need to pass this to stack */
> +	struct list_head cloud_filter_list;
> +	u16 num_cloud_filters;
> +};
> +
>  struct iecm_channel_config {
>  	struct virtchnl_channel_info ch_info[VIRTCHNL_MAX_ADQ_V2_CHANNELS];
>  	bool tc_running;
> @@ -536,6 +558,7 @@ struct iecm_ptype_state {
>  	bool outer_frag;
>  	u8 tunnel_state;
>  };
> +

Please move this newline into the patch where iecm_ptype_state was
introduced, it doesn't belong here.

>  /* User defined configuration values */
>  struct iecm_user_config_data {
>  	u32 num_req_tx_qs; /* user requested TX queues through ethtool */
> @@ -550,6 +573,7 @@ struct iecm_user_config_data {
>  	struct list_head vlan_filter_list;
>  	struct list_head adv_rss_list;
>  	struct iecm_fdir_fltr_config fdir_config;
> +	struct iecm_cloud_filter_config cf_config;
>  	struct iecm_channel_config ch_config;
>  };
>  
> @@ -853,6 +877,7 @@ void iecm_set_ethtool_ops(struct net_device *netdev);
>  void iecm_vport_set_hsplit(struct iecm_vport *vport, bool ena);
>  void iecm_add_del_ether_addrs(struct iecm_vport *vport, bool add, bool async);
>  int iecm_set_promiscuous(struct iecm_adapter *adapter);
> +int iecm_send_add_del_cloud_filter_msg(struct iecm_vport *vport, bool add);
>  int iecm_send_add_fdir_filter_msg(struct iecm_vport *vport);
>  int iecm_send_del_fdir_filter_msg(struct iecm_vport *vport);
>  int iecm_get_fdir_fltr_entry(struct iecm_vport *vport,
> -- 
> 2.33.0

Thanks,
Al


^ permalink raw reply	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 18/19] iecm: add advanced rss
  2022-01-28  0:10 ` [Intel-wired-lan] [PATCH net-next 18/19] iecm: add advanced rss Alan Brady
@ 2022-01-28 19:53   ` Alexander Lobakin
  2022-02-03  2:55     ` Brady, Alan
  0 siblings, 1 reply; 89+ messages in thread
From: Alexander Lobakin @ 2022-01-28 19:53 UTC (permalink / raw)
  To: intel-wired-lan

From: Alan Brady <alan.brady@intel.com>
Date: Thu, 27 Jan 2022 16:10:08 -0800

> From: Haiyue Wang <haiyue.wang@intel.com>
> 
> Continuing with advanced features this implements what's needed to do
> advanced rss.

I'm sorry for not mentioned it before, but most of the series'
commit messages are poor and would probably get rejected upstream.
If they were explaining at least some very basics, it would be
better. Even better if there were explanations of some tricky code
that happens time to time.

> 
> Signed-off-by: Haiyue Wang <haiyue.wang@intel.com>
> ---
>  drivers/net/ethernet/intel/iecm/iecm_lib.c    | 547 ++++++++++++++++++
>  .../net/ethernet/intel/iecm/iecm_virtchnl.c   |  71 +++
>  drivers/net/ethernet/intel/include/iecm.h     |  73 +++
>  3 files changed, 691 insertions(+)
> 
> diff --git a/drivers/net/ethernet/intel/iecm/iecm_lib.c b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> index d11413cb438c..baa1e312652a 100644
> --- a/drivers/net/ethernet/intel/iecm/iecm_lib.c
> +++ b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> @@ -1013,6 +1013,52 @@ static void iecm_remove_vlan_filters(struct iecm_vport *vport)
>  	}
>  }
>  
> +/**
> + * iecm_remove_adv_rss_cfgs - Remove all RSS configuration
> + * @vport: vport structure
> + */
> +static void iecm_remove_adv_rss_cfgs(struct iecm_vport *vport)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +
> +	if (!iecm_is_cap_ena(adapter, IECM_OTHER_CAPS, VIRTCHNL2_CAP_ADV_RSS))
> +		return;
> +
> +	if (!list_empty(&adapter->config_data.adv_rss_list)) {
> +		struct iecm_adv_rss *rss;
> +
> +		spin_lock_bh(&adapter->adv_rss_list_lock);
> +		list_for_each_entry(rss, &adapter->config_data.adv_rss_list,
> +				    list) {
> +			rss->remove = true;
> +		}

Redundant braces arond an one-liner.

> +		spin_unlock_bh(&adapter->adv_rss_list_lock);
> +		iecm_send_add_del_adv_rss_cfg_msg(vport, false);
> +	}

Invert the condition for -1 indent level.

> +}
> +
> +/**
> + * iecm_del_all_adv_rss_cfgs - delete all RSS configuration
> + * @vport: vport structure
> + *
> + * This function will loop through the list of RSS configuration and deletes
> + * them.
> + **/
> +static void iecm_del_all_adv_rss_cfgs(struct iecm_vport *vport)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct iecm_adv_rss *rss, *rss_tmp;
> +
> +	spin_lock_bh(&adapter->adv_rss_list_lock);
> +	list_for_each_entry_safe(rss, rss_tmp,
> +				 &adapter->config_data.adv_rss_list,
> +				 list) {
> +		list_del(&rss->list);
> +		kfree(rss);
> +	}
> +	spin_unlock_bh(&adapter->adv_rss_list_lock);
> +}
> +
>  /**
>   * iecm_remove_fdir_filters - Remove all Flow Director filters
>   * @vport: vport structure
> @@ -1099,6 +1145,7 @@ static void iecm_vport_stop(struct iecm_vport *vport)
>  		iecm_remove_vlan_filters(vport);
>  	}
>  
> +	iecm_remove_adv_rss_cfgs(vport);
>  	iecm_remove_fdir_filters(vport);
>  
>  	adapter->link_up = false;
> @@ -1332,6 +1379,27 @@ static void iecm_restore_cloud_filters(struct iecm_vport *vport)
>  	}
>  }
>  
> +/**
> + * iecm_restore_adv_rss_cfgs - Restore all RSS configuration
> + * @vport: vport structure
> + */
> +static void iecm_restore_adv_rss_cfgs(struct iecm_vport *vport)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +
> +	if (!list_empty(&adapter->config_data.adv_rss_list)) {
> +		struct iecm_adv_rss *rss;
> +
> +		spin_lock_bh(&adapter->adv_rss_list_lock);
> +		list_for_each_entry(rss, &adapter->config_data.adv_rss_list,
> +				    list) {
> +			rss->add = true;
> +		}

...

> +		spin_unlock_bh(&adapter->adv_rss_list_lock);
> +		iecm_send_add_del_adv_rss_cfg_msg(vport, true);
> +	}

...

> +}
> +
>  /**
>   * iecm_restore_fdir_filters - Restore all Flow Director filters
>   * @vport: vport structure
> @@ -1380,6 +1448,9 @@ static void iecm_restore_features(struct iecm_vport *vport)
>  	if (iecm_is_feature_ena(vport, NETIF_F_HW_TC))
>  		iecm_restore_cloud_filters(vport);
>  
> +	if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS, VIRTCHNL2_CAP_ADV_RSS))
> +		iecm_restore_adv_rss_cfgs(vport);
> +
>  	if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS, VIRTCHNL2_CAP_FDIR))
>  		iecm_restore_fdir_filters(vport);
>  }
> @@ -2219,6 +2290,7 @@ static void iecm_del_user_cfg_data(struct iecm_adapter *adapter)
>  		if (!adapter->vports[i])
>  			continue;
>  
> +		iecm_del_all_adv_rss_cfgs(adapter->vports[i]);
>  		iecm_del_all_fdir_filters(adapter->vports[i]);
>  	}
>  }
> @@ -3633,6 +3705,481 @@ static int iecm_setup_tc(struct net_device *netdev, enum tc_setup_type type,
>  	return err;
>  }
>  
> +/**
> + * iecm_fill_adv_rss_ip4_hdr - fill the IPv4 RSS protocol header
> + * @hdr: the virtchnl message protocol header data structure
> + * @hash_flds: the RSS configuration protocol hash fields
> + */
> +static void
> +iecm_fill_adv_rss_ip4_hdr(struct virtchnl_proto_hdr *hdr, u64 hash_flds)
> +{
> +	VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, IPV4);
> +
> +	if (hash_flds & IECM_ADV_RSS_HASH_FLD_IPV4_SA)
> +		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, IPV4, SRC);
> +
> +	if (hash_flds & IECM_ADV_RSS_HASH_FLD_IPV4_DA)
> +		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, IPV4, DST);
> +}
> +
> +/**
> + * iecm_fill_adv_rss_ip6_hdr - fill the IPv6 RSS protocol header
> + * @hdr: the virtchnl message protocol header data structure
> + * @hash_flds: the RSS configuration protocol hash fields
> + */
> +static void
> +iecm_fill_adv_rss_ip6_hdr(struct virtchnl_proto_hdr *hdr, u64 hash_flds)
> +{
> +	VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, IPV6);
> +
> +	if (hash_flds & IECM_ADV_RSS_HASH_FLD_IPV6_SA)
> +		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, IPV6, SRC);
> +
> +	if (hash_flds & IECM_ADV_RSS_HASH_FLD_IPV6_DA)
> +		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, IPV6, DST);
> +}
> +
> +/**
> + * iecm_fill_adv_rss_tcp_hdr - fill the TCP RSS protocol header
> + * @hdr: the virtchnl message protocol header data structure
> + * @hash_flds: the RSS configuration protocol hash fields
> + */
> +static void
> +iecm_fill_adv_rss_tcp_hdr(struct virtchnl_proto_hdr *hdr, u64 hash_flds)
> +{
> +	VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, TCP);
> +
> +	if (hash_flds & IECM_ADV_RSS_HASH_FLD_TCP_SRC_PORT)
> +		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, TCP, SRC_PORT);
> +
> +	if (hash_flds & IECM_ADV_RSS_HASH_FLD_TCP_DST_PORT)
> +		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, TCP, DST_PORT);
> +}
> +
> +/**
> + * iecm_fill_adv_rss_udp_hdr - fill the UDP RSS protocol header
> + * @hdr: the virtchnl message protocol header data structure
> + * @hash_flds: the RSS configuration protocol hash fields
> + */
> +static void
> +iecm_fill_adv_rss_udp_hdr(struct virtchnl_proto_hdr *hdr, u64 hash_flds)
> +{
> +	VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, UDP);
> +
> +	if (hash_flds & IECM_ADV_RSS_HASH_FLD_UDP_SRC_PORT)
> +		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, UDP, SRC_PORT);
> +
> +	if (hash_flds & IECM_ADV_RSS_HASH_FLD_UDP_DST_PORT)
> +		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, UDP, DST_PORT);
> +}
> +
> +/**
> + * iecm_fill_adv_rss_sctp_hdr - fill the SCTP RSS protocol header
> + * @hdr: the virtchnl message protocol header data structure
> + * @hash_flds: the RSS configuration protocol hash fields
> + */
> +static void
> +iecm_fill_adv_rss_sctp_hdr(struct virtchnl_proto_hdr *hdr, s64 hash_flds)
> +{
> +	VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, SCTP);
> +
> +	if (hash_flds & IECM_ADV_RSS_HASH_FLD_SCTP_SRC_PORT)
> +		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, SCTP, SRC_PORT);
> +
> +	if (hash_flds & IECM_ADV_RSS_HASH_FLD_SCTP_DST_PORT)
> +		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, SCTP, DST_PORT);
> +}
> +
> +/**
> + * iecm_fill_adv_rss_cfg_msg - fill the RSS configuration into virtchnl message
> + * @rss_cfg: the virtchnl message to be filled with RSS configuration setting
> + * @packet_hdrs: the RSS configuration protocol header types
> + * @hash_flds: the RSS configuration protocol hash fields
> + *
> + * Returns 0 if the RSS configuration virtchnl message is filled successfully
> + */
> +static int
> +iecm_fill_adv_rss_cfg_msg(struct virtchnl_rss_cfg *rss_cfg,
> +			  u32 packet_hdrs, u64 hash_flds)
> +{
> +	struct virtchnl_proto_hdrs *proto_hdrs = &rss_cfg->proto_hdrs;
> +	struct virtchnl_proto_hdr *hdr;
> +
> +	rss_cfg->rss_algorithm = VIRTCHNL_RSS_ALG_TOEPLITZ_ASYMMETRIC;
> +
> +	proto_hdrs->tunnel_level = 0;	/* always outer layer */
> +
> +	hdr = &proto_hdrs->proto_hdr[proto_hdrs->count++];
> +	switch (packet_hdrs & IECM_ADV_RSS_FLOW_SEG_HDR_L3) {
> +	case IECM_ADV_RSS_FLOW_SEG_HDR_IPV4:
> +		iecm_fill_adv_rss_ip4_hdr(hdr, hash_flds);
> +		break;
> +	case IECM_ADV_RSS_FLOW_SEG_HDR_IPV6:
> +		iecm_fill_adv_rss_ip6_hdr(hdr, hash_flds);
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	hdr = &proto_hdrs->proto_hdr[proto_hdrs->count++];
> +	switch (packet_hdrs & IECM_ADV_RSS_FLOW_SEG_HDR_L4) {
> +	case IECM_ADV_RSS_FLOW_SEG_HDR_TCP:
> +		iecm_fill_adv_rss_tcp_hdr(hdr, hash_flds);
> +		break;
> +	case IECM_ADV_RSS_FLOW_SEG_HDR_UDP:
> +		iecm_fill_adv_rss_udp_hdr(hdr, hash_flds);
> +		break;
> +	case IECM_ADV_RSS_FLOW_SEG_HDR_SCTP:
> +		iecm_fill_adv_rss_sctp_hdr(hdr, hash_flds);
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * iecm_find_adv_rss_cfg_by_hdrs - find RSS configuration with header type
> + * @vport: vport structure
> + * @packet_hdrs: protocol header type to find.
> + *
> + * Returns pointer to advance RSS configuration if found or null
> + */
> +static struct iecm_adv_rss *
> +iecm_find_adv_rss_cfg_by_hdrs(struct iecm_vport *vport, u32 packet_hdrs)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct iecm_adv_rss *rss;
> +
> +	list_for_each_entry(rss, &adapter->config_data.adv_rss_list, list)
> +		if (rss->packet_hdrs == packet_hdrs)
> +			return rss;
> +
> +	return NULL;
> +}
> +
> +/**
> + * iecm_dump_adv_rss_cfg_info
> + * @vport: vport structure
> + * @packet_hdrs: The protocol headers for RSS configuration
> + * @hash_flds: The protocol hash fields for RSS configuration
> + * @prefix: the prefix string description to dump the RSS
> + * @postfix: the postfix string description to dump the RSS
> + *
> + * Dump the advance RSS configuration
> + **/
> +static void
> +iecm_dump_adv_rss_cfg_info(struct iecm_vport *vport,
> +			   u32 packet_hdrs, u64 hash_flds,
> +			   const char *prefix, const char *postfix)
> +{
> +	static char hash_opt[300];

`static` places it into BSS. If multiple cores call this function at
the same time, it will mess up.
I'd just kzalloc() a buffer and then kfree() it.

> +	const char *proto;
> +
> +	if (packet_hdrs & IECM_ADV_RSS_FLOW_SEG_HDR_TCP)
> +		proto = "TCP";
> +	else if (packet_hdrs & IECM_ADV_RSS_FLOW_SEG_HDR_UDP)
> +		proto = "UDP";
> +	else if (packet_hdrs & IECM_ADV_RSS_FLOW_SEG_HDR_SCTP)
> +		proto = "SCTP";
> +	else
> +		return;
> +
> +	memset(hash_opt, 0, sizeof(hash_opt));
> +
> +	strcat(hash_opt, proto);

Consider using strlcat() please.

> +	if (packet_hdrs & IECM_ADV_RSS_FLOW_SEG_HDR_IPV4)
> +		strcat(hash_opt, "v4 ");
> +	else
> +		strcat(hash_opt, "v6 ");
> +
> +	if (hash_flds & (IECM_ADV_RSS_HASH_FLD_IPV4_SA |
> +			 IECM_ADV_RSS_HASH_FLD_IPV6_SA))
> +		strcat(hash_opt, "[IP SA] ");
> +	if (hash_flds & (IECM_ADV_RSS_HASH_FLD_IPV4_DA |
> +			 IECM_ADV_RSS_HASH_FLD_IPV6_DA))
> +		strcat(hash_opt, "[IP DA] ");
> +	if (hash_flds & (IECM_ADV_RSS_HASH_FLD_TCP_SRC_PORT |
> +			 IECM_ADV_RSS_HASH_FLD_UDP_SRC_PORT |
> +			 IECM_ADV_RSS_HASH_FLD_SCTP_SRC_PORT))
> +		strcat(hash_opt, "[src port] ");
> +	if (hash_flds & (IECM_ADV_RSS_HASH_FLD_TCP_DST_PORT |
> +			 IECM_ADV_RSS_HASH_FLD_UDP_DST_PORT |
> +			 IECM_ADV_RSS_HASH_FLD_SCTP_DST_PORT))
> +		strcat(hash_opt, "[dst port] ");
> +
> +	if (!prefix)
> +		prefix = "";
> +
> +	if (!postfix)
> +		postfix = "";
> +
> +	dev_info(&vport->adapter->pdev->dev, "%s %s %s\n",
> +		 prefix, hash_opt, postfix);
> +}
> +
> +/**
> + * iecm_adv_rss_parse_hdrs - parses headers from RSS hash input
> + * @cmd: ethtool rxnfc command
> + *
> + * This function parses the rxnfc command and returns intended
> + * header types for RSS configuration
> + */
> +static u32 iecm_adv_rss_parse_hdrs(struct ethtool_rxnfc *cmd)
> +{
> +	u32 hdrs = IECM_ADV_RSS_FLOW_SEG_HDR_NONE;
> +
> +	switch (cmd->flow_type) {
> +	case TCP_V4_FLOW:
> +		hdrs |= IECM_ADV_RSS_FLOW_SEG_HDR_TCP |
> +			IECM_ADV_RSS_FLOW_SEG_HDR_IPV4;
> +		break;
> +	case UDP_V4_FLOW:
> +		hdrs |= IECM_ADV_RSS_FLOW_SEG_HDR_UDP |
> +			IECM_ADV_RSS_FLOW_SEG_HDR_IPV4;
> +		break;
> +	case SCTP_V4_FLOW:
> +		hdrs |= IECM_ADV_RSS_FLOW_SEG_HDR_SCTP |
> +			IECM_ADV_RSS_FLOW_SEG_HDR_IPV4;
> +		break;
> +	case TCP_V6_FLOW:
> +		hdrs |= IECM_ADV_RSS_FLOW_SEG_HDR_TCP |
> +			IECM_ADV_RSS_FLOW_SEG_HDR_IPV6;
> +		break;
> +	case UDP_V6_FLOW:
> +		hdrs |= IECM_ADV_RSS_FLOW_SEG_HDR_UDP |
> +			IECM_ADV_RSS_FLOW_SEG_HDR_IPV6;
> +		break;
> +	case SCTP_V6_FLOW:
> +		hdrs |= IECM_ADV_RSS_FLOW_SEG_HDR_SCTP |
> +			IECM_ADV_RSS_FLOW_SEG_HDR_IPV6;
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	return hdrs;
> +}
> +
> +/**
> + * iecm_adv_rss_parse_hash_flds - parses hash fields from RSS hash input
> + * @cmd: ethtool rxnfc command
> + *
> + * This function parses the rxnfc command and returns intended hash fields for
> + * RSS configuration
> + */
> +static u64 iecm_adv_rss_parse_hash_flds(struct ethtool_rxnfc *cmd)
> +{
> +	u64 hfld = IECM_ADV_RSS_HASH_INVALID;
> +
> +	if (cmd->data & RXH_IP_SRC || cmd->data & RXH_IP_DST) {

Braces are actually *needed* around bitops. So,

	if ((cmd->data & RXH_IP_SRC) || (...)) {

> +		switch (cmd->flow_type) {
> +		case TCP_V4_FLOW:
> +		case UDP_V4_FLOW:
> +		case SCTP_V4_FLOW:
> +			if (cmd->data & RXH_IP_SRC)
> +				hfld |= IECM_ADV_RSS_HASH_FLD_IPV4_SA;
> +			if (cmd->data & RXH_IP_DST)
> +				hfld |= IECM_ADV_RSS_HASH_FLD_IPV4_DA;
> +			break;
> +		case TCP_V6_FLOW:
> +		case UDP_V6_FLOW:
> +		case SCTP_V6_FLOW:
> +			if (cmd->data & RXH_IP_SRC)
> +				hfld |= IECM_ADV_RSS_HASH_FLD_IPV6_SA;
> +			if (cmd->data & RXH_IP_DST)
> +				hfld |= IECM_ADV_RSS_HASH_FLD_IPV6_DA;
> +			break;
> +		default:
> +			break;
> +		}
> +	}

	if (!condition)
		goto here;

as well (-1 indent).

> +
> +	if (cmd->data & RXH_L4_B_0_1 || cmd->data & RXH_L4_B_2_3) {
> +		switch (cmd->flow_type) {
> +		case TCP_V4_FLOW:
> +		case TCP_V6_FLOW:
> +			if (cmd->data & RXH_L4_B_0_1)
> +				hfld |= IECM_ADV_RSS_HASH_FLD_TCP_SRC_PORT;
> +			if (cmd->data & RXH_L4_B_2_3)
> +				hfld |= IECM_ADV_RSS_HASH_FLD_TCP_DST_PORT;
> +			break;
> +		case UDP_V4_FLOW:
> +		case UDP_V6_FLOW:
> +			if (cmd->data & RXH_L4_B_0_1)
> +				hfld |= IECM_ADV_RSS_HASH_FLD_UDP_SRC_PORT;
> +			if (cmd->data & RXH_L4_B_2_3)
> +				hfld |= IECM_ADV_RSS_HASH_FLD_UDP_DST_PORT;
> +			break;
> +		case SCTP_V4_FLOW:
> +		case SCTP_V6_FLOW:
> +			if (cmd->data & RXH_L4_B_0_1)
> +				hfld |= IECM_ADV_RSS_HASH_FLD_SCTP_SRC_PORT;
> +			if (cmd->data & RXH_L4_B_2_3)
> +				hfld |= IECM_ADV_RSS_HASH_FLD_SCTP_DST_PORT;
> +			break;
> +		default:
> +			break;
> +		}
> +	}
> +
> +	return hfld;
> +}
> +
> +/**
> + * iecm_set_adv_rss_hash_opt - Enable/Disable flow types for RSS hash
> + * @vport: vport structure
> + * @cmd: ethtool rxnfc command
> + *
> + * Returns Success if the flow input set is supported.
> + */
> +int
> +iecm_set_adv_rss_hash_opt(struct iecm_vport *vport, struct ethtool_rxnfc *cmd)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct iecm_adv_rss *rss, *rss_new;
> +	u64 hash_flds;
> +	u32 hdrs;
> +	int err;
> +
> +	if (adapter->state != __IECM_UP)
> +		return -EIO;
> +
> +	if (!iecm_is_cap_ena(adapter, IECM_OTHER_CAPS, VIRTCHNL2_CAP_ADV_RSS))
> +		return -EOPNOTSUPP;
> +
> +	hdrs = iecm_adv_rss_parse_hdrs(cmd);
> +	if (hdrs == IECM_ADV_RSS_FLOW_SEG_HDR_NONE)
> +		return -EINVAL;
> +
> +	hash_flds = iecm_adv_rss_parse_hash_flds(cmd);
> +	if (hash_flds == IECM_ADV_RSS_HASH_INVALID)
> +		return -EINVAL;
> +
> +	rss_new = kzalloc(sizeof(*rss_new), GFP_KERNEL);
> +	if (!rss_new)
> +		return -ENOMEM;
> +
> +	/* Since this can fail, do it now to avoid dirtying the list, we'll
> +	 * copy it from rss_new if it turns out we're updating an existing
> +	 * filter instead of adding a new one.
> +	 */
> +	if (iecm_fill_adv_rss_cfg_msg(&rss_new->cfg_msg, hdrs, hash_flds)) {
> +		kfree(rss_new);
> +		return -EINVAL;
> +	}
> +
> +	iecm_dump_adv_rss_cfg_info(vport, hdrs, hash_flds,
> +				   "Input set change for", "is pending");
> +
> +	spin_lock_bh(&adapter->adv_rss_list_lock);
> +	rss = iecm_find_adv_rss_cfg_by_hdrs(vport, hdrs);
> +	if (rss) {
> +		if (rss->hash_flds != hash_flds) {
> +			rss->remove = false;
> +			memcpy(&rss->cfg_msg, &rss_new->cfg_msg,
> +			       sizeof(rss_new->cfg_msg));
> +			kfree(rss_new);
> +		} else {
> +			kfree(rss_new);
> +			spin_unlock_bh(&adapter->adv_rss_list_lock);
> +			return -EEXIST;
> +		}
> +	} else {
> +		rss = rss_new;
> +		rss->packet_hdrs = hdrs;
> +		list_add_tail(&rss->list, &adapter->config_data.adv_rss_list);
> +	}
> +	rss->add = true;
> +	rss->hash_flds = hash_flds;
> +	spin_unlock_bh(&adapter->adv_rss_list_lock);
> +
> +	err = iecm_send_add_del_adv_rss_cfg_msg(vport, true);
> +	if (err) {
> +		spin_lock_bh(&adapter->adv_rss_list_lock);
> +		/* We have to find it again to make sure another thread hasn't
> +		 * already deleted and kfreed it.
> +		 */
> +		rss = iecm_find_adv_rss_cfg_by_hdrs(vport, hdrs);
> +		if (rss) {
> +			list_del(&rss->list);
> +			kfree(rss);
> +		}
> +		spin_unlock_bh(&adapter->adv_rss_list_lock);
> +	}
> +
> +	if (!err)
> +		iecm_dump_adv_rss_cfg_info(vport, hdrs, hash_flds,
> +					   "Input set change for",
> +					   "successful");
> +	else
> +		iecm_dump_adv_rss_cfg_info(vport, hdrs, hash_flds,
> +					   "Failed to change the input set for",
> +					   NULL);

It doesn't really look good. You could pass `err` or just
`bool success` to the function itself and print those there.

> +
> +	return err;
> +}
> +
> +/**
> + * iecm_get_adv_rss_hash_opt - Retrieve hash fields for a given flow-type
> + * @vport: vport structure
> + * @cmd: ethtool rxnfc command
> + *
> + * Returns Success if the flow input set is supported.
> + */
> +int
> +iecm_get_adv_rss_hash_opt(struct iecm_vport *vport, struct ethtool_rxnfc *cmd)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct iecm_adv_rss *rss;
> +	u64 hash_flds;
> +	u32 hdrs;
> +
> +	if (adapter->state != __IECM_UP)
> +		return -EIO;
> +
> +	if (!iecm_is_cap_ena(adapter, IECM_OTHER_CAPS, VIRTCHNL2_CAP_ADV_RSS))
> +		return -EOPNOTSUPP;
> +
> +	cmd->data = 0;
> +
> +	hdrs = iecm_adv_rss_parse_hdrs(cmd);
> +	if (hdrs == IECM_ADV_RSS_FLOW_SEG_HDR_NONE)
> +		return -EINVAL;
> +
> +	spin_lock_bh(&adapter->adv_rss_list_lock);
> +	rss = iecm_find_adv_rss_cfg_by_hdrs(vport, hdrs);
> +	if (rss)
> +		hash_flds = rss->hash_flds;
> +	else
> +		hash_flds = IECM_ADV_RSS_HASH_INVALID;
> +	spin_unlock_bh(&adapter->adv_rss_list_lock);
> +
> +	if (hash_flds == IECM_ADV_RSS_HASH_INVALID)
> +		return -EINVAL;
> +
> +	if (hash_flds & (IECM_ADV_RSS_HASH_FLD_IPV4_SA |
> +			 IECM_ADV_RSS_HASH_FLD_IPV6_SA))
> +		cmd->data |= (u64)RXH_IP_SRC;
> +
> +	if (hash_flds & (IECM_ADV_RSS_HASH_FLD_IPV4_DA |
> +			 IECM_ADV_RSS_HASH_FLD_IPV6_DA))
> +		cmd->data |= (u64)RXH_IP_DST;
> +
> +	if (hash_flds & (IECM_ADV_RSS_HASH_FLD_TCP_SRC_PORT |
> +			 IECM_ADV_RSS_HASH_FLD_UDP_SRC_PORT |
> +			 IECM_ADV_RSS_HASH_FLD_SCTP_SRC_PORT))
> +		cmd->data |= (u64)RXH_L4_B_0_1;
> +
> +	if (hash_flds & (IECM_ADV_RSS_HASH_FLD_TCP_DST_PORT |
> +			 IECM_ADV_RSS_HASH_FLD_UDP_DST_PORT |
> +			 IECM_ADV_RSS_HASH_FLD_SCTP_DST_PORT))
> +		cmd->data |= (u64)RXH_L4_B_2_3;
> +
> +	return 0;
> +}
> +
>  /**
>   * iecm_pkt_udp_no_pay_len - the length of UDP packet without payload
>   * @fltr: Flow Director filter data structure
> diff --git a/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> index 94af45c36866..c05baf12515c 100644
> --- a/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> +++ b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> @@ -2799,6 +2799,77 @@ int iecm_send_add_del_cloud_filter_msg(struct iecm_vport *vport, bool add)
>  	return err;
>  }
>  
> +/**
> + * iecm_send_add_del_adv_rss_cfg_msg: Send add/del RSS configuration message
> + * @vport: vport structure
> + * @add: True to add, false to delete RSS configuration
> + *
> + * Request the CP/PF to add/del RSS configuration as specified by the user via
> + * ethtool
> + *
> + * Return 0 on success, negative on failure
> + **/
> +int iecm_send_add_del_adv_rss_cfg_msg(struct iecm_vport *vport, bool add)
> +{
> +	struct iecm_adapter *adapter = vport->adapter;
> +	struct virtchnl_rss_cfg *rss_cfg;
> +	struct iecm_adv_rss *rss;
> +	int len, err = -ENXIO;
> +
> +	len = sizeof(struct virtchnl_rss_cfg);
> +	rss_cfg = kzalloc(len, GFP_KERNEL);
> +	if (!rss_cfg)
> +		return -ENOMEM;
> +
> +	while (true) {
> +		bool process_rss = false;
> +
> +		spin_lock_bh(&adapter->adv_rss_list_lock);
> +		list_for_each_entry(rss, &adapter->config_data.adv_rss_list, list) {
> +			if (add && rss->add) {
> +				/* Only add needs print the RSS information */
> +				process_rss = true;
> +				rss->add = false;
> +				memcpy(rss_cfg, &rss->cfg_msg, len);
> +				break;
> +			} else if (!add && rss->remove) {
> +				process_rss = true;
> +				rss->remove = false;
> +				memcpy(rss_cfg, &rss->cfg_msg, len);
> +				break;
> +			}
> +		}
> +		spin_unlock_bh(&adapter->adv_rss_list_lock);
> +
> +		/* Don't send mailbox message when there are no RSS to add/del */
> +		if (!process_rss)
> +			break;
> +
> +		if (add) {
> +			err = iecm_send_mb_msg(adapter, VIRTCHNL_OP_ADD_RSS_CFG,
> +					       len, (u8 *)rss_cfg);
> +			if (err)
> +				break;
> +
> +			err = iecm_wait_for_event(adapter, IECM_VC_ADD_RSS_CFG,
> +						  IECM_VC_ADD_RSS_CFG_ERR);
> +		} else {
> +			err = iecm_send_mb_msg(adapter, VIRTCHNL_OP_DEL_RSS_CFG,
> +					       len, (u8 *)rss_cfg);
> +			if (err)
> +				break;
> +
> +			err = iecm_min_wait_for_event(adapter, IECM_VC_DEL_RSS_CFG,
> +						      IECM_VC_DEL_RSS_CFG_ERR);
> +		}
> +		if (err)
> +			break;
> +	}
> +
> +	kfree(rss_cfg);
> +	return err;
> +}
> +
>  /**
>   * iecm_send_add_fdir_filter_msg: Send add Flow Director filter message
>   * @vport: vport structure
> diff --git a/drivers/net/ethernet/intel/include/iecm.h b/drivers/net/ethernet/intel/include/iecm.h
> index 0aab41cf982c..c7be8c88f9b3 100644
> --- a/drivers/net/ethernet/intel/include/iecm.h
> +++ b/drivers/net/ethernet/intel/include/iecm.h
> @@ -432,6 +432,74 @@ struct iecm_channel_config {
>  	u8 num_tc;
>  };
>  
> +enum iecm_adv_rss_flow_seg_hdr {
> +	IECM_ADV_RSS_FLOW_SEG_HDR_NONE	= 0x00000000,
> +	IECM_ADV_RSS_FLOW_SEG_HDR_IPV4	= 0x00000001,
> +	IECM_ADV_RSS_FLOW_SEG_HDR_IPV6	= 0x00000002,
> +	IECM_ADV_RSS_FLOW_SEG_HDR_TCP	= 0x00000004,
> +	IECM_ADV_RSS_FLOW_SEG_HDR_UDP	= 0x00000008,
> +	IECM_ADV_RSS_FLOW_SEG_HDR_SCTP	= 0x00000010,
> +};
> +
> +#define IECM_ADV_RSS_FLOW_SEG_HDR_L3		\
> +	(IECM_ADV_RSS_FLOW_SEG_HDR_IPV4	|	\
> +	 IECM_ADV_RSS_FLOW_SEG_HDR_IPV6)
> +
> +#define IECM_ADV_RSS_FLOW_SEG_HDR_L4		\
> +	(IECM_ADV_RSS_FLOW_SEG_HDR_TCP |	\
> +	 IECM_ADV_RSS_FLOW_SEG_HDR_UDP |	\
> +	 IECM_ADV_RSS_FLOW_SEG_HDR_SCTP)
> +
> +enum iecm_adv_rss_flow_field {
> +	/* L3 */
> +	IECM_ADV_RSS_FLOW_FIELD_IDX_IPV4_SA,
> +	IECM_ADV_RSS_FLOW_FIELD_IDX_IPV4_DA,
> +	IECM_ADV_RSS_FLOW_FIELD_IDX_IPV6_SA,
> +	IECM_ADV_RSS_FLOW_FIELD_IDX_IPV6_DA,
> +	/* L4 */
> +	IECM_ADV_RSS_FLOW_FIELD_IDX_TCP_SRC_PORT,
> +	IECM_ADV_RSS_FLOW_FIELD_IDX_TCP_DST_PORT,
> +	IECM_ADV_RSS_FLOW_FIELD_IDX_UDP_SRC_PORT,
> +	IECM_ADV_RSS_FLOW_FIELD_IDX_UDP_DST_PORT,
> +	IECM_ADV_RSS_FLOW_FIELD_IDX_SCTP_SRC_PORT,
> +	IECM_ADV_RSS_FLOW_FIELD_IDX_SCTP_DST_PORT,
> +
> +	/* The total number of enums must not exceed 64 */
> +	IECM_ADV_RSS_FLOW_FIELD_IDX_MAX
> +};
> +
> +#define IECM_ADV_RSS_HASH_INVALID	0
> +#define IECM_ADV_RSS_HASH_FLD_IPV4_SA	\
> +	BIT_ULL(IECM_ADV_RSS_FLOW_FIELD_IDX_IPV4_SA)
> +#define IECM_ADV_RSS_HASH_FLD_IPV6_SA	\
> +	BIT_ULL(IECM_ADV_RSS_FLOW_FIELD_IDX_IPV6_SA)
> +#define IECM_ADV_RSS_HASH_FLD_IPV4_DA	\
> +	BIT_ULL(IECM_ADV_RSS_FLOW_FIELD_IDX_IPV4_DA)
> +#define IECM_ADV_RSS_HASH_FLD_IPV6_DA	\
> +	BIT_ULL(IECM_ADV_RSS_FLOW_FIELD_IDX_IPV6_DA)
> +#define IECM_ADV_RSS_HASH_FLD_TCP_SRC_PORT	\
> +	BIT_ULL(IECM_ADV_RSS_FLOW_FIELD_IDX_TCP_SRC_PORT)
> +#define IECM_ADV_RSS_HASH_FLD_TCP_DST_PORT	\
> +	BIT_ULL(IECM_ADV_RSS_FLOW_FIELD_IDX_TCP_DST_PORT)
> +#define IECM_ADV_RSS_HASH_FLD_UDP_SRC_PORT	\
> +	BIT_ULL(IECM_ADV_RSS_FLOW_FIELD_IDX_UDP_SRC_PORT)
> +#define IECM_ADV_RSS_HASH_FLD_UDP_DST_PORT	\
> +	BIT_ULL(IECM_ADV_RSS_FLOW_FIELD_IDX_UDP_DST_PORT)
> +#define IECM_ADV_RSS_HASH_FLD_SCTP_SRC_PORT	\
> +	BIT_ULL(IECM_ADV_RSS_FLOW_FIELD_IDX_SCTP_SRC_PORT)
> +#define IECM_ADV_RSS_HASH_FLD_SCTP_DST_PORT	\
> +	BIT_ULL(IECM_ADV_RSS_FLOW_FIELD_IDX_SCTP_DST_PORT)
> +
> +/* bookkeeping of advanced RSS configuration */
> +struct iecm_adv_rss {
> +	struct list_head list;
> +	u32 packet_hdrs;
> +	u64 hash_flds;
> +	struct virtchnl_rss_cfg cfg_msg;
> +	bool remove;	/* RSS filter needs to be deleted */
> +	bool add;	/* RSS filter needs to be added */
> +};
> +
>  enum iecm_fdir_flow_type {
>  	/* NONE - used for undef/error */
>  	IECM_FDIR_FLOW_NONE = 0,
> @@ -878,6 +946,11 @@ void iecm_vport_set_hsplit(struct iecm_vport *vport, bool ena);
>  void iecm_add_del_ether_addrs(struct iecm_vport *vport, bool add, bool async);
>  int iecm_set_promiscuous(struct iecm_adapter *adapter);
>  int iecm_send_add_del_cloud_filter_msg(struct iecm_vport *vport, bool add);
> +int iecm_send_add_del_adv_rss_cfg_msg(struct iecm_vport *vport, bool add);
> +int iecm_set_adv_rss_hash_opt(struct iecm_vport *vport,
> +			      struct ethtool_rxnfc *cmd);
> +int iecm_get_adv_rss_hash_opt(struct iecm_vport *vport,
> +			      struct ethtool_rxnfc *cmd);
>  int iecm_send_add_fdir_filter_msg(struct iecm_vport *vport);
>  int iecm_send_del_fdir_filter_msg(struct iecm_vport *vport);
>  int iecm_get_fdir_fltr_entry(struct iecm_vport *vport,
> -- 
> 2.33.0

Thanks,
Al

^ permalink raw reply	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 19/19] idpf: introduce idpf driver
  2022-01-28  0:10 ` [Intel-wired-lan] [PATCH net-next 19/19] idpf: introduce idpf driver Alan Brady
@ 2022-01-28 20:08   ` Alexander Lobakin
  2022-02-03  3:07     ` Brady, Alan
  0 siblings, 1 reply; 89+ messages in thread
From: Alexander Lobakin @ 2022-01-28 20:08 UTC (permalink / raw)
  To: intel-wired-lan

From: Alan Brady <alan.brady@intel.com>
Date: Thu, 27 Jan 2022 16:10:09 -0800

> This adds the idpf driver which uses the iecm module to provide common
> functionality. Device specific behavior and registers are defined here and
> handed off to iecm which takes over the rest of the flow.

Ok I missed that before, so I say it now.
Multi-function networking devices (Ethernet, SFs, VF representors,
RDMA, storage offload etc.) nowadays kinda *must* be based on top of
auxiliary bus. Otherwise, maintaining of hundreds a direct call with
recursive dependencies between modules and stuff will become a
burden.
All of the mentioned functionality will be added to the driver(s),
that's a fact, and as these are new drivers, it's way better to
start off the right way now than to bug your mind on how to
refactor this later.

> 
> Signed-off-by: Phani Burra <phani.r.burra@intel.com>
> Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
> Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
> Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
> Signed-off-by: Alan Brady <alan.brady@intel.com>
> ---
>  .../device_drivers/ethernet/intel/idpf.rst    |  47 ++++++
>  drivers/net/ethernet/intel/Kconfig            |  16 ++
>  drivers/net/ethernet/intel/Makefile           |   1 +
>  drivers/net/ethernet/intel/idpf/Makefile      |  15 ++
>  drivers/net/ethernet/intel/idpf/idpf_dev.h    |  17 +++
>  drivers/net/ethernet/intel/idpf/idpf_devids.h |  10 ++
>  drivers/net/ethernet/intel/idpf/idpf_main.c   | 140 ++++++++++++++++++
>  drivers/net/ethernet/intel/idpf/idpf_reg.c    | 130 ++++++++++++++++
>  .../ethernet/intel/include/iecm_lan_pf_regs.h | 131 ++++++++++++++++
>  9 files changed, 507 insertions(+)
>  create mode 100644 Documentation/networking/device_drivers/ethernet/intel/idpf.rst
>  create mode 100644 drivers/net/ethernet/intel/idpf/Makefile
>  create mode 100644 drivers/net/ethernet/intel/idpf/idpf_dev.h
>  create mode 100644 drivers/net/ethernet/intel/idpf/idpf_devids.h
>  create mode 100644 drivers/net/ethernet/intel/idpf/idpf_main.c
>  create mode 100644 drivers/net/ethernet/intel/idpf/idpf_reg.c
>  create mode 100644 drivers/net/ethernet/intel/include/iecm_lan_pf_regs.h
> 
> diff --git a/Documentation/networking/device_drivers/ethernet/intel/idpf.rst b/Documentation/networking/device_drivers/ethernet/intel/idpf.rst
> new file mode 100644
> index 000000000000..973fa9613428
> --- /dev/null
> +++ b/Documentation/networking/device_drivers/ethernet/intel/idpf.rst
> @@ -0,0 +1,47 @@
> +.. SPDX-License-Identifier: GPL-2.0
> +
> +==================================================================
> +Linux Base Driver for the Intel(R) Smart Network Adapter Family Series
> +==================================================================
> +
> +Intel idpf Linux driver.
> +Copyright(c) 2020 Intel Corporation.
> +
> +Contents
> +========
> +
> +- Enabling the driver
> +- Support
> +
> +The driver in this release supports Intel's Smart Network Adapter Family Series
> +of products. For more information, visit Intel's support page at
> +https://support.intel.com.
> +
> +Enabling the driver
> +===================
> +The driver is enabled via the standard kernel configuration system,
> +using the make command::
> +
> +  make oldconfig/menuconfig/etc.
> +
> +The driver is located in the menu structure at:
> +
> +  -> Device Drivers
> +    -> Network device support (NETDEVICES [=y])
> +      -> Ethernet driver support
> +        -> Intel devices
> +          -> Intel(R) Smart Network Adapter Family Series Support
> +
> +Support
> +=======
> +For general information, go to the Intel support website at:
> +
> +https://www.intel.com/support/
> +
> +or the Intel Wired Networking project hosted by Sourceforge at:
> +
> +https://sourceforge.net/projects/e1000
> +
> +If an issue is identified with the released source code on a supported kernel
> +with a supported adapter, email the specific information related to the issue
> +to e1000-devel at lists.sf.net.
> diff --git a/drivers/net/ethernet/intel/Kconfig b/drivers/net/ethernet/intel/Kconfig
> index 754dc7677ad5..93c8883c22ad 100644
> --- a/drivers/net/ethernet/intel/Kconfig
> +++ b/drivers/net/ethernet/intel/Kconfig
> @@ -387,4 +387,20 @@ config IECM
>        To compile this as a module, choose M here. The module will be called
>        iecm.
>  
> +config IDPF
> +	tristate "Intel(R) Data Plane Function Support"
> +	default n
> +	depends on IECM
> +	help
> +	  For more information on how to identify your adapter, go
> +	  to the Adapter & Driver ID Guide that can be located at:
> +
> +	  <http://support.intel.com>
> +
> +	  More specific information on configuring the driver is in
> +	  <file:Documentation/networking/device_drivers/ethernet/intel/idpf.rst>.
> +
> +	  To compile this driver as a module, choose M here. The module
> +	  will be called idpf.
> +
>  endif # NET_VENDOR_INTEL
> diff --git a/drivers/net/ethernet/intel/Makefile b/drivers/net/ethernet/intel/Makefile
> index c9eba9cc5087..3786c2269f3d 100644
> --- a/drivers/net/ethernet/intel/Makefile
> +++ b/drivers/net/ethernet/intel/Makefile
> @@ -17,3 +17,4 @@ obj-$(CONFIG_IAVF) += iavf/
>  obj-$(CONFIG_FM10K) += fm10k/
>  obj-$(CONFIG_ICE) += ice/
>  obj-$(CONFIG_IECM) += iecm/
> +obj-$(CONFIG_IDPF) += idpf/
> diff --git a/drivers/net/ethernet/intel/idpf/Makefile b/drivers/net/ethernet/intel/idpf/Makefile
> new file mode 100644
> index 000000000000..85846620bc9f
> --- /dev/null
> +++ b/drivers/net/ethernet/intel/idpf/Makefile
> @@ -0,0 +1,15 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +# Copyright (C) 2019 Intel Corporation
> +
> +#
> +# Makefile for the Intel(R) Data Plane Function Linux Driver
> +#
> +
> +obj-$(CONFIG_IDPF) += idpf.o
> +
> +ccflags-y += -I$(srctree)/drivers/net/ethernet/intel/include \
> +			 -I$(srctree)/include/linux/avf
> +
> +idpf-y := \
> +	idpf_main.o \
> +	idpf_reg.o
> diff --git a/drivers/net/ethernet/intel/idpf/idpf_dev.h b/drivers/net/ethernet/intel/idpf/idpf_dev.h
> new file mode 100644
> index 000000000000..dc146161f884
> --- /dev/null
> +++ b/drivers/net/ethernet/intel/idpf/idpf_dev.h
> @@ -0,0 +1,17 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/* Copyright (C) 2019 Intel Corporation */
> +
> +#ifndef _IDPF_DEV_H_
> +#define _IDPF_DEV_H_
> +
> +#include "iecm.h"
> +
> +int idpf_intr_reg_init(struct iecm_vport *vport);
> +void idpf_mb_intr_reg_init(struct iecm_adapter *adapter);
> +void idpf_reset_reg_init(struct iecm_reset_reg *reset_reg);
> +void idpf_trigger_reset(struct iecm_adapter *adapter,
> +			enum iecm_flags trig_cause);
> +void idpf_vportq_reg_init(struct iecm_vport *vport);
> +void idpf_ctlq_reg_init(struct iecm_ctlq_create_info *cq);
> +
> +#endif /* _IDPF_DEV_H_ */
> diff --git a/drivers/net/ethernet/intel/idpf/idpf_devids.h b/drivers/net/ethernet/intel/idpf/idpf_devids.h
> new file mode 100644
> index 000000000000..7bf8eb64b76a
> --- /dev/null
> +++ b/drivers/net/ethernet/intel/idpf/idpf_devids.h
> @@ -0,0 +1,10 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/* Copyright (C) 2019 Intel Corporation */
> +
> +#ifndef _IDPF_DEVIDS_H_
> +#define _IDPF_DEVIDS_H_
> +
> +/* Device IDs */
> +#define IDPF_DEV_ID_PF			0x1452
> +
> +#endif /* _IDPF_DEVIDS_H_ */
> diff --git a/drivers/net/ethernet/intel/idpf/idpf_main.c b/drivers/net/ethernet/intel/idpf/idpf_main.c
> new file mode 100644
> index 000000000000..da5e668beabf
> --- /dev/null
> +++ b/drivers/net/ethernet/intel/idpf/idpf_main.c
> @@ -0,0 +1,140 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/* Copyright (C) 2019 Intel Corporation */
> +
> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> +
> +#include "idpf_dev.h"
> +#include "idpf_devids.h"
> +
> +#define DRV_SUMMARY	"Intel(R) Data Plane Function Linux Driver"
> +static const char idpf_driver_string[] = DRV_SUMMARY;
> +static const char idpf_copyright[] = "Copyright (c) 2020, Intel Corporation.";
> +
> +MODULE_DESCRIPTION(DRV_SUMMARY);
> +MODULE_LICENSE("GPL");
> +
> +/**
> + * idpf_reg_ops_init - Initialize register API function pointers
> + * @adapter: Driver specific private structure
> + */
> +static void idpf_reg_ops_init(struct iecm_adapter *adapter)
> +{
> +	adapter->dev_ops.reg_ops.ctlq_reg_init = idpf_ctlq_reg_init;
> +	adapter->dev_ops.reg_ops.intr_reg_init = idpf_intr_reg_init;
> +	adapter->dev_ops.reg_ops.mb_intr_reg_init = idpf_mb_intr_reg_init;
> +	adapter->dev_ops.reg_ops.reset_reg_init = idpf_reset_reg_init;
> +	adapter->dev_ops.reg_ops.trigger_reset = idpf_trigger_reset;

Why not define static const reg_ops for idpf and assigning it?
Filling plenty of callbacks inside functions are generally
discouraged.

> +}
> +
> +/**
> + * idpf_probe - Device initialization routine
> + * @pdev: PCI device information struct
> + * @ent: entry in idpf_pci_tbl
> + *
> + * Returns 0 on success, negative on failure
> + */
> +static int idpf_probe(struct pci_dev *pdev,
> +		      const struct pci_device_id __always_unused *ent)

Again, no __always_unused for function parameters.

> +{
> +	struct iecm_adapter *adapter = NULL;
> +	int err;
> +
> +	adapter = kzalloc(sizeof(*adapter), GFP_KERNEL);
> +	if (!adapter)
> +		return -ENOMEM;
> +
> +	adapter->dev_ops.reg_ops_init = idpf_reg_ops_init;

Why not just directly assign callbacks here? Callback for filling
more callbacks?

> +	set_bit(__IECM_REQ_TX_SPLITQ, adapter->flags);
> +	set_bit(__IECM_REQ_RX_SPLITQ, adapter->flags);
> +
> +	err = iecm_probe(pdev, ent, adapter);
> +	if (err)
> +		kfree(adapter);
> +
> +	return err;
> +}
> +
> +/**
> + * idpf_remove - Device removal routine
> + * @pdev: PCI device information struct
> + */
> +static void idpf_remove(struct pci_dev *pdev)
> +{
> +	struct iecm_adapter *adapter = pci_get_drvdata(pdev);
> +
> +	if (!adapter)
> +		return;
> +
> +	iecm_remove(pdev);
> +	pci_set_drvdata(pdev, NULL);
> +	kfree(adapter);
> +}
> +
> +/**
> + * idpf_shutdown - PCI callback for shutting down device
> + * @pdev: PCI device information struct
> + */
> +static void idpf_shutdown(struct pci_dev *pdev)
> +{
> +	idpf_remove(pdev);
> +
> +	if (system_state == SYSTEM_POWER_OFF)
> +		pci_set_power_state(pdev, PCI_D3hot);
> +}
> +
> +/* idpf_pci_tbl - PCI Dev iapf ID Table
> + *
> + * Wildcard entries (PCI_ANY_ID) should come last
> + * Last entry must be all 0s
> + *
> + * { Vendor ID, Device ID, SubVendor ID, SubDevice ID,
> + *   Class, Class Mask, private data (not used) }
> + */
> +static const struct pci_device_id idpf_pci_tbl[] = {
> +	{ PCI_VDEVICE(INTEL, IDPF_DEV_ID_PF), 0 },
> +	/* required last entry */
> +	{ 0, }
> +};
> +MODULE_DEVICE_TABLE(pci, idpf_pci_tbl);
> +
> +static struct pci_driver idpf_driver = {
> +	.name = KBUILD_MODNAME,
> +	.id_table = idpf_pci_tbl,
> +	.probe = idpf_probe,
> +	.remove = idpf_remove,
> +	.shutdown = idpf_shutdown,
> +};
> +
> +/**
> + * idpf_module_init - Driver registration routine
> + *
> + * idpf_module_init is the first routine called when the driver is
> + * loaded. All it does is register with the PCI subsystem.
> + */
> +static int __init idpf_module_init(void)
> +{
> +	int status;
> +
> +	pr_info("%s - version %d\n", idpf_driver_string, LINUX_VERSION_CODE);
> +	pr_info("%s\n", idpf_copyright);
> +
> +	status = pci_register_driver(&idpf_driver);
> +	if (status)
> +		pr_err("failed to register pci driver, err %d\n", status);
> +
> +	return status;
> +}
> +module_init(idpf_module_init);
> +
> +/**
> + * idpf_module_exit - Driver exit cleanup routine
> + *
> + * idpf_module_exit is called just before the driver is removed
> + * from memory.
> + */
> +static void __exit idpf_module_exit(void)
> +{
> +	pci_unregister_driver(&idpf_driver);
> +	pr_info("module unloaded\n");
> +}
> +module_exit(idpf_module_exit);
> diff --git a/drivers/net/ethernet/intel/idpf/idpf_reg.c b/drivers/net/ethernet/intel/idpf/idpf_reg.c
> new file mode 100644
> index 000000000000..d0ea6c495c62
> --- /dev/null
> +++ b/drivers/net/ethernet/intel/idpf/idpf_reg.c
> @@ -0,0 +1,130 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/* Copyright (C) 2019 Intel Corporation */
> +
> +#include "idpf_dev.h"
> +#include "iecm_lan_pf_regs.h"
> +
> +/**
> + * idpf_ctlq_reg_init - initialize default mailbox registers
> + * @cq: pointer to the array of create control queues
> + */
> +void idpf_ctlq_reg_init(struct iecm_ctlq_create_info *cq)
> +{
> +	int i;
> +
> +#define NUM_Q 2

I'd like to see it defined outside the function and with a bit of
explanation of what that is.

> +	for (i = 0; i < NUM_Q; i++) {
> +		struct iecm_ctlq_create_info *ccq = cq + i;
> +
> +		switch (ccq->type) {
> +		case IECM_CTLQ_TYPE_MAILBOX_TX:
> +			/* set head and tail registers in our local struct */
> +			ccq->reg.head = PF_FW_ATQH;
> +			ccq->reg.tail = PF_FW_ATQT;
> +			ccq->reg.len = PF_FW_ATQLEN;
> +			ccq->reg.bah = PF_FW_ATQBAH;
> +			ccq->reg.bal = PF_FW_ATQBAL;
> +			ccq->reg.len_mask = PF_FW_ATQLEN_ATQLEN_M;
> +			ccq->reg.len_ena_mask = PF_FW_ATQLEN_ATQENABLE_M;
> +			ccq->reg.head_mask = PF_FW_ATQH_ATQH_M;
> +			break;
> +		case IECM_CTLQ_TYPE_MAILBOX_RX:
> +			/* set head and tail registers in our local struct */
> +			ccq->reg.head = PF_FW_ARQH;
> +			ccq->reg.tail = PF_FW_ARQT;
> +			ccq->reg.len = PF_FW_ARQLEN;
> +			ccq->reg.bah = PF_FW_ARQBAH;
> +			ccq->reg.bal = PF_FW_ARQBAL;
> +			ccq->reg.len_mask = PF_FW_ARQLEN_ARQLEN_M;
> +			ccq->reg.len_ena_mask = PF_FW_ARQLEN_ARQENABLE_M;
> +			ccq->reg.head_mask = PF_FW_ARQH_ARQH_M;
> +			break;
> +		default:
> +			break;
> +		}
> +	}
> +}
> +
> +/**
> + * idpf_mb_intr_reg_init - Initialize mailbox interrupt register
> + * @adapter: adapter structure
> + */
> +void idpf_mb_intr_reg_init(struct iecm_adapter *adapter)
> +{
> +	struct iecm_intr_reg *intr = &adapter->mb_vector.intr_reg;
> +	struct virtchnl2_get_capabilities *caps;
> +
> +	caps = (struct virtchnl2_get_capabilities *)adapter->caps;
> +	intr->dyn_ctl = le32_to_cpu(caps->mailbox_dyn_ctl);
> +	intr->dyn_ctl_intena_m = PF_GLINT_DYN_CTL_INTENA_M;
> +	intr->dyn_ctl_itridx_m = PF_GLINT_DYN_CTL_ITR_INDX_M;
> +	intr->icr_ena = PF_INT_DIR_OICR_ENA;
> +	intr->icr_ena_ctlq_m = PF_INT_DIR_OICR_ENA_M;
> +}
> +
> +/**
> + * idpf_intr_reg_init - Initialize interrupt registers
> + * @vport: virtual port structure
> + */
> +int idpf_intr_reg_init(struct iecm_vport *vport)
> +{
> +	int num_vecs = vport->num_q_vectors;
> +	struct iecm_vec_regs *reg_vals;
> +	int num_regs, i, err = 0;
> +
> +	reg_vals = kmalloc(sizeof(void *) * IECM_LARGE_MAX_Q,
> +			   GFP_KERNEL);

	array_size(IECM_LARGE_MAX_Q, sizeof(void *));

> +	if (!reg_vals)
> +		return -ENOMEM;
> +
> +	num_regs = iecm_get_reg_intr_vecs(vport, reg_vals, num_vecs);
> +	if (num_regs != num_vecs) {
> +		err = -EINVAL;
> +		goto free_reg_vals;
> +	}
> +
> +	for (i = 0; i < num_regs; i++) {
> +		struct iecm_q_vector *q_vector = &vport->q_vectors[i];
> +		struct iecm_intr_reg *intr = &q_vector->intr_reg;
> +
> +		intr->dyn_ctl = reg_vals[i].dyn_ctl_reg;
> +		intr->dyn_ctl_clrpba_m = PF_GLINT_DYN_CTL_CLEARPBA_M;
> +		intr->dyn_ctl_intena_m = PF_GLINT_DYN_CTL_INTENA_M;
> +		intr->dyn_ctl_itridx_s = PF_GLINT_DYN_CTL_ITR_INDX_S;
> +		intr->dyn_ctl_intrvl_s = PF_GLINT_DYN_CTL_INTERVAL_S;
> +
> +		intr->rx_itr = PF_GLINT_ITR_V2(VIRTCHNL2_ITR_IDX_0,
> +					       reg_vals[i].itrn_reg);
> +		intr->tx_itr = PF_GLINT_ITR_V2(VIRTCHNL2_ITR_IDX_1,
> +					       reg_vals[i].itrn_reg);
> +	}
> +
> +free_reg_vals:
> +	kfree(reg_vals);
> +	return err;
> +}
> +
> +/**
> + * idpf_reset_reg_init - Initialize reset registers
> + * @reset_reg: struct to be filled in with reset registers
> + */
> +void idpf_reset_reg_init(struct iecm_reset_reg *reset_reg)
> +{
> +	reset_reg->rstat = PFGEN_RSTAT;
> +	reset_reg->rstat_m = PFGEN_RSTAT_PFR_STATE_M;
> +}
> +
> +/**
> + * idpf_trigger_reset - trigger reset
> + * @adapter: Driver specific private structure
> + * @trig_cause: Reason to trigger a reset
> + */
> +void idpf_trigger_reset(struct iecm_adapter *adapter,
> +			enum iecm_flags __always_unused trig_cause)
> +{
> +	u32 reset_reg;
> +
> +	reset_reg = rd32(&adapter->hw, PFGEN_CTRL);
> +	wr32(&adapter->hw, PFGEN_CTRL, (reset_reg | PFGEN_CTRL_PFSWR));
> +}
> +
> diff --git a/drivers/net/ethernet/intel/include/iecm_lan_pf_regs.h b/drivers/net/ethernet/intel/include/iecm_lan_pf_regs.h
> new file mode 100644
> index 000000000000..52ffe5c4a7ca
> --- /dev/null
> +++ b/drivers/net/ethernet/intel/include/iecm_lan_pf_regs.h
> @@ -0,0 +1,131 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/* Copyright (c) 2020, Intel Corporation. */
> +
> +#ifndef _IECM_LAN_PF_REGS_H_
> +#define _IECM_LAN_PF_REGS_H_
> +
> +/* Receive queues */
> +#define PF_QRX_BASE			0x00000000
> +#define PF_QRX_TAIL(_QRX)		(PF_QRX_BASE + (((_QRX) * 0x1000)))
> +#define PF_QRX_BUFFQ_BASE		0x03000000
> +#define PF_QRX_BUFFQ_TAIL(_QRX)		(PF_QRX_BUFFQ_BASE + (((_QRX) * 0x1000)))
> +
> +/* Transmit queues */
> +#define PF_QTX_BASE			0x05000000
> +#define PF_QTX_COMM_DBELL(_DBQM)	(PF_QTX_BASE + ((_DBQM) * 0x1000))
> +
> +/* Control(PF Mailbox) Queue */
> +#define PF_FW_BASE			0x08400000
> +
> +#define PF_FW_ARQBAL			(PF_FW_BASE)
> +#define PF_FW_ARQBAH			(PF_FW_BASE + 0x4)
> +#define PF_FW_ARQLEN			(PF_FW_BASE + 0x8)
> +#define PF_FW_ARQLEN_ARQLEN_S		0
> +#define PF_FW_ARQLEN_ARQLEN_M		MAKEMASK(0x1FFF, PF_FW_ARQLEN_ARQLEN_S)
> +#define PF_FW_ARQLEN_ARQVFE_S		28
> +#define PF_FW_ARQLEN_ARQVFE_M		BIT(PF_FW_ARQLEN_ARQVFE_S)
> +#define PF_FW_ARQLEN_ARQOVFL_S		29
> +#define PF_FW_ARQLEN_ARQOVFL_M		BIT(PF_FW_ARQLEN_ARQOVFL_S)
> +#define PF_FW_ARQLEN_ARQCRIT_S		30
> +#define PF_FW_ARQLEN_ARQCRIT_M		BIT(PF_FW_ARQLEN_ARQCRIT_S)
> +#define PF_FW_ARQLEN_ARQENABLE_S	31
> +#define PF_FW_ARQLEN_ARQENABLE_M	BIT(PF_FW_ARQLEN_ARQENABLE_S)
> +#define PF_FW_ARQH			(PF_FW_BASE + 0xC)
> +#define PF_FW_ARQH_ARQH_S		0
> +#define PF_FW_ARQH_ARQH_M		MAKEMASK(0x1FFF, PF_FW_ARQH_ARQH_S)
> +#define PF_FW_ARQT			(PF_FW_BASE + 0x10)
> +
> +#define PF_FW_ATQBAL			(PF_FW_BASE + 0x14)
> +#define PF_FW_ATQBAH			(PF_FW_BASE + 0x18)
> +#define PF_FW_ATQLEN			(PF_FW_BASE + 0x1C)
> +#define PF_FW_ATQLEN_ATQLEN_S		0
> +#define PF_FW_ATQLEN_ATQLEN_M		MAKEMASK(0x3FF, PF_FW_ATQLEN_ATQLEN_S)
> +#define PF_FW_ATQLEN_ATQVFE_S		28
> +#define PF_FW_ATQLEN_ATQVFE_M		BIT(PF_FW_ATQLEN_ATQVFE_S)
> +#define PF_FW_ATQLEN_ATQOVFL_S		29
> +#define PF_FW_ATQLEN_ATQOVFL_M		BIT(PF_FW_ATQLEN_ATQOVFL_S)
> +#define PF_FW_ATQLEN_ATQCRIT_S		30
> +#define PF_FW_ATQLEN_ATQCRIT_M		BIT(PF_FW_ATQLEN_ATQCRIT_S)
> +#define PF_FW_ATQLEN_ATQENABLE_S	31
> +#define PF_FW_ATQLEN_ATQENABLE_M	BIT(PF_FW_ATQLEN_ATQENABLE_S)
> +#define PF_FW_ATQH			(PF_FW_BASE + 0x20)
> +#define PF_FW_ATQH_ATQH_S		0
> +#define PF_FW_ATQH_ATQH_M		MAKEMASK(0x3FF, PF_FW_ATQH_ATQH_S)
> +#define PF_FW_ATQT			(PF_FW_BASE + 0x24)
> +
> +/* Interrupts */
> +#define PF_GLINT_BASE			0x08900000
> +#define PF_GLINT_DYN_CTL(_INT)		(PF_GLINT_BASE + ((_INT) * 0x1000))
> +#define PF_GLINT_DYN_CTL_INTENA_S	0
> +#define PF_GLINT_DYN_CTL_INTENA_M	BIT(PF_GLINT_DYN_CTL_INTENA_S)
> +#define PF_GLINT_DYN_CTL_CLEARPBA_S	1
> +#define PF_GLINT_DYN_CTL_CLEARPBA_M	BIT(PF_GLINT_DYN_CTL_CLEARPBA_S)
> +#define PF_GLINT_DYN_CTL_SWINT_TRIG_S	2
> +#define PF_GLINT_DYN_CTL_SWINT_TRIG_M	BIT(PF_GLINT_DYN_CTL_SWINT_TRIG_S)
> +#define PF_GLINT_DYN_CTL_ITR_INDX_S	3
> +#define PF_GLINT_DYN_CTL_ITR_INDX_M	MAKEMASK(0x3, PF_GLINT_DYN_CTL_ITR_INDX_S)
> +#define PF_GLINT_DYN_CTL_INTERVAL_S	5
> +#define PF_GLINT_DYN_CTL_INTERVAL_M	BIT(PF_GLINT_DYN_CTL_INTERVAL_S)
> +#define PF_GLINT_DYN_CTL_SW_ITR_INDX_ENA_S	24
> +#define PF_GLINT_DYN_CTL_SW_ITR_INDX_ENA_M BIT(PF_GLINT_DYN_CTL_SW_ITR_INDX_ENA_S)
> +#define PF_GLINT_DYN_CTL_SW_ITR_INDX_S	25
> +#define PF_GLINT_DYN_CTL_SW_ITR_INDX_M	BIT(PF_GLINT_DYN_CTL_SW_ITR_INDX_S)
> +#define PF_GLINT_DYN_CTL_WB_ON_ITR_S	30
> +#define PF_GLINT_DYN_CTL_WB_ON_ITR_M	BIT(PF_GLINT_DYN_CTL_WB_ON_ITR_S)
> +#define PF_GLINT_DYN_CTL_INTENA_MSK_S	31
> +#define PF_GLINT_DYN_CTL_INTENA_MSK_M	BIT(PF_GLINT_DYN_CTL_INTENA_MSK_S)
> +#define PF_GLINT_ITR_V2(_i, _reg_start) (((_i) * 4) + (_reg_start))
> +#define PF_GLINT_ITR(_i, _INT) (PF_GLINT_BASE + (((_i) + 1) * 4) + ((_INT) * 0x1000))
> +#define PF_GLINT_ITR_MAX_INDEX		2
> +#define PF_GLINT_ITR_INTERVAL_S		0
> +#define PF_GLINT_ITR_INTERVAL_M		MAKEMASK(0xFFF, PF_GLINT_ITR_INTERVAL_S)
> +
> +/* Timesync registers */
> +#define PF_TIMESYNC_BASE		0x08404000
> +#define PF_GLTSYN_CMD_SYNC		(PF_TIMESYNC_BASE)
> +#define PF_GLTSYN_CMD_SYNC_EXEC_CMD_S	0
> +#define PF_GLTSYN_CMD_SYNC_EXEC_CMD_M	MAKEMASK(0x3, PF_GLTSYN_CMD_SYNC_EXEC_CMD_S)
> +#define PF_GLTSYN_CMD_SYNC_SHTIME_EN_S	2
> +#define PF_GLTSYN_CMD_SYNC_SHTIME_EN_M	BIT(PF_GLTSYN_CMD_SYNC_SHTIME_EN_S)
> +#define PF_GLTSYN_SHTIME_0		(PF_TIMESYNC_BASE + 0x4)
> +#define PF_GLTSYN_SHTIME_L		(PF_TIMESYNC_BASE + 0x8)
> +#define PF_GLTSYN_SHTIME_H		(PF_TIMESYNC_BASE + 0xC)
> +#define PF_GLTSYN_ART_L			(PF_TIMESYNC_BASE + 0x10)
> +#define PF_GLTSYN_ART_H			(PF_TIMESYNC_BASE + 0x14)
> +
> +/* Generic registers */
> +#define PF_INT_DIR_OICR_ENA		0x08406000
> +#define PF_INT_DIR_OICR_ENA_S		0
> +#define PF_INT_DIR_OICR_ENA_M	MAKEMASK(0xFFFFFFFF, PF_INT_DIR_OICR_ENA_S)
> +#define PF_INT_DIR_OICR			0x08406004
> +#define PF_INT_DIR_OICR_TSYN_EVNT	0
> +#define PF_INT_DIR_OICR_PHY_TS_0	BIT(1)
> +#define PF_INT_DIR_OICR_PHY_TS_1	BIT(2)
> +#define PF_INT_DIR_OICR_CAUSE		0x08406008
> +#define PF_INT_DIR_OICR_CAUSE_CAUSE_S	0
> +#define PF_INT_DIR_OICR_CAUSE_CAUSE_M	MAKEMASK(0xFFFFFFFF, PF_INT_DIR_OICR_CAUSE_CAUSE_S)
> +#define PF_INT_PBA_CLEAR		0x0840600C
> +
> +#define PF_FUNC_RID			0x08406010
> +#define PF_FUNC_RID_FUNCTION_NUMBER_S	0
> +#define PF_FUNC_RID_FUNCTION_NUMBER_M	MAKEMASK(0x7, PF_FUNC_RID_FUNCTION_NUMBER_S)
> +#define PF_FUNC_RID_DEVICE_NUMBER_S	3
> +#define PF_FUNC_RID_DEVICE_NUMBER_M	MAKEMASK(0x1F, PF_FUNC_RID_DEVICE_NUMBER_S)
> +#define PF_FUNC_RID_BUS_NUMBER_S	8
> +#define PF_FUNC_RID_BUS_NUMBER_M	MAKEMASK(0xFF, PF_FUNC_RID_BUS_NUMBER_S)
> +
> +/* Reset registers */
> +#define PFGEN_RTRIG			0x08407000
> +#define PFGEN_RTRIG_CORER_S		0
> +#define PFGEN_RTRIG_CORER_M		BIT(0)
> +#define PFGEN_RTRIG_LINKR_S		1
> +#define PFGEN_RTRIG_LINKR_M		BIT(1)
> +#define PFGEN_RTRIG_IMCR_S		2
> +#define PFGEN_RTRIG_IMCR_M		BIT(2)
> +#define PFGEN_RSTAT			0x08407008 /* PFR Status */
> +#define PFGEN_RSTAT_PFR_STATE_S		0
> +#define PFGEN_RSTAT_PFR_STATE_M		MAKEMASK(0x3, PFGEN_RSTAT_PFR_STATE_S)
> +#define PFGEN_CTRL			0x0840700C
> +#define PFGEN_CTRL_PFSWR		BIT(0)
> +
> +#endif
> -- 
> 2.33.0

Thanks,
Al

^ permalink raw reply	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 02/19] iecm: add basic module init and documentation
  2022-01-28  0:09 ` [Intel-wired-lan] [PATCH net-next 02/19] iecm: add basic module init and documentation Alan Brady
  2022-01-28 11:56   ` Alexander Lobakin
@ 2022-02-01 19:44   ` Shannon Nelson
  2022-02-03  3:08     ` Brady, Alan
  1 sibling, 1 reply; 89+ messages in thread
From: Shannon Nelson @ 2022-02-01 19:44 UTC (permalink / raw)
  To: intel-wired-lan

On Thu, Jan 27, 2022 at 4:34 PM Alan Brady <alan.brady@intel.com> wrote:
>
> This adds the basics needed to make a kernel module and documentation
> needed to use iecm module.
>

[ snip ]

> diff --git a/drivers/net/ethernet/intel/iecm/Makefile b/drivers/net/ethernet/intel/iecm/Makefile
> new file mode 100644
> index 000000000000..d2d087ac71e9
> --- /dev/null
> +++ b/drivers/net/ethernet/intel/iecm/Makefile
> @@ -0,0 +1,13 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +# Copyright (C) 2019 Intel Corporation
> +
> +#
> +# Makefile for the Intel(R) Data Plane Function Linux Driver

Maybe the iecm here rather than idpf?

> +#
> +
> +obj-$(CONFIG_IECM) += iecm.o
> +
> +ccflags-y += -I$(srctree)/drivers/net/ethernet/intel/include
> +
> +iecm-y := \
> +       iecm_main.o
> diff --git a/drivers/net/ethernet/intel/iecm/iecm_main.c b/drivers/net/ethernet/intel/iecm/iecm_main.c
> new file mode 100644
> index 000000000000..7c09403c6918
> --- /dev/null
> +++ b/drivers/net/ethernet/intel/iecm/iecm_main.c
> @@ -0,0 +1,40 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/* Copyright (C) 2019 Intel Corporation */
> +
> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> +
> +#include "iecm.h"
> +
> +#define DRV_SUMMARY    "Intel(R) Ethernet Common Module"
> +static const char iecm_driver_string[] = DRV_SUMMARY;
> +static const char iecm_copyright[] = "Copyright (c) 2020, Intel Corporation.";

Do you want this copyright string "2020" to match the top of the file "2019"?

> +
> +MODULE_DESCRIPTION(DRV_SUMMARY);
> +MODULE_LICENSE("GPL v2");
> +
> +/**
> + * iecm_module_init - Driver registration routine
> + *
> + * iecm_module_init is the first routine called when the driver is
> + * loaded. All it does is register with the PCI subsystem.
> + */
> +static int __init iecm_module_init(void)
> +{
> +       pr_info("%s - version %d\n", iecm_driver_string, LINUX_VERSION_CODE);
> +       pr_info("%s\n", iecm_copyright);
> +
> +       return 0;
> +}
> +module_init(iecm_module_init);
> +
> +/**
> + * iecm_module_exit - Driver exit cleanup routine
> + *
> + * iecm_module_exit is called just before the driver is removed
> + * from memory.
> + */
> +static void __exit iecm_module_exit(void)
> +{
> +       pr_info("module unloaded\n");
> +}
> +module_exit(iecm_module_exit);
> diff --git a/drivers/net/ethernet/intel/include/iecm.h b/drivers/net/ethernet/intel/include/iecm.h
> new file mode 100644
> index 000000000000..f66f0d7db8e7
> --- /dev/null
> +++ b/drivers/net/ethernet/intel/include/iecm.h
> @@ -0,0 +1,10 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/* Copyright (C) 2019 Intel Corporation */
> +
> +#ifndef _IECM_H_
> +#define _IECM_H_
> +
> +#include <linux/etherdevice.h>
> +#include <linux/version.h>
> +
> +#endif /* !_IECM_H_ */
> --
> 2.33.0
>
> _______________________________________________
> Intel-wired-lan mailing list
> Intel-wired-lan at osuosl.org
> https://lists.osuosl.org/mailman/listinfo/intel-wired-lan

^ permalink raw reply	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 03/19] iecm: add probe and remove
  2022-01-28  0:09 ` [Intel-wired-lan] [PATCH net-next 03/19] iecm: add probe and remove Alan Brady
@ 2022-02-01 20:02   ` Shannon Nelson
  2022-02-03  3:13     ` Brady, Alan
  0 siblings, 1 reply; 89+ messages in thread
From: Shannon Nelson @ 2022-02-01 20:02 UTC (permalink / raw)
  To: intel-wired-lan

On Thu, Jan 27, 2022 at 4:34 PM Alan Brady <alan.brady@intel.com> wrote:
>
> This adds everything we need in probe and remove as well as a few stubs
> which will kick off the next step in the init process of device driver
> coming up.
>
> Signed-off-by: Phani Burra <phani.r.burra@intel.com>
> Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
> Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
> Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
> Signed-off-by: Alan Brady <alan.brady@intel.com>
> ---
>  drivers/net/ethernet/intel/iecm/Makefile      |   1 +
>  drivers/net/ethernet/intel/iecm/iecm_lib.c    | 231 ++++++++++++++++++
>  drivers/net/ethernet/intel/include/iecm.h     | 178 +++++++++++++-
>  .../net/ethernet/intel/include/iecm_txrx.h    |  33 +++
>  4 files changed, 442 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/net/ethernet/intel/iecm/iecm_lib.c
>  create mode 100644 drivers/net/ethernet/intel/include/iecm_txrx.h
>
> diff --git a/drivers/net/ethernet/intel/iecm/Makefile b/drivers/net/ethernet/intel/iecm/Makefile
> index d2d087ac71e9..4f497723419d 100644
> --- a/drivers/net/ethernet/intel/iecm/Makefile
> +++ b/drivers/net/ethernet/intel/iecm/Makefile
> @@ -10,4 +10,5 @@ obj-$(CONFIG_IECM) += iecm.o
>  ccflags-y += -I$(srctree)/drivers/net/ethernet/intel/include
>
>  iecm-y := \
> +       iecm_lib.o \
>         iecm_main.o
> diff --git a/drivers/net/ethernet/intel/iecm/iecm_lib.c b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> new file mode 100644
> index 000000000000..e6d0b418a27f
> --- /dev/null
> +++ b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> @@ -0,0 +1,231 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/* Copyright (C) 2019 Intel Corporation */
> +
> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> +
> +#include "iecm.h"
> +
> +/**
> + * iecm_statistics_task - Delayed task to get statistics over mailbox
> + * @work: work_struct handle to our data
> + */
> +static void iecm_statistics_task(struct work_struct *work)
> +{
> +       /* stub */
> +}
> +
> +/**
> + * iecm_service_task - Delayed task for handling mailbox responses
> + * @work: work_struct handle to our data
> + *
> + */
> +static void iecm_service_task(struct work_struct *work)
> +{
> +       /* stub */
> +}
> +
> +/**
> + * iecm_init_task - Delayed initialization task
> + * @work: work_struct handle to our data
> + *
> + * Init task finishes up pending work started in probe.  Due to the asynchronous
> + * nature in which the device communicates with hardware, we may have to wait
> + * several milliseconds to get a response.  Instead of busy polling in probe,
> + * pulling it out into a delayed work task prevents us from bogging down the
> + * whole system waiting for a response from hardware.
> + */
> +static void iecm_init_task(struct work_struct *work)
> +{
> +       /* stub */
> +}
> +
> +/**
> + * iecm_deinit_task - Device deinit routine
> + * @adapter: Driver specific private structue
> + *
> + * Extended remove logic which will be used for
> + * hard reset as well
> + */
> +static void iecm_deinit_task(struct iecm_adapter *adapter)
> +{
> +       /* stub */
> +}
> +
> +/**
> + * iecm_vc_event_task - Handle virtchannel event logic
> + * @work: work queue struct
> + */
> +static void iecm_vc_event_task(struct work_struct *work)
> +{
> +       /* stub */
> +}
> +
> +/**
> + * iecm_probe - Device initialization routine
> + * @pdev: PCI device information struct
> + * @ent: entry in iecm_pci_tbl
> + * @adapter: driver specific private structure
> + *
> + * Returns 0 on success, negative on failure
> + */
> +int iecm_probe(struct pci_dev *pdev,
> +              const struct pci_device_id __always_unused *ent,
> +              struct iecm_adapter *adapter)
> +{
> +       int err;
> +
> +       adapter->pdev = pdev;
> +
> +       err = pcim_enable_device(pdev);
> +       if (err)
> +               return err;
> +
> +       err = pcim_iomap_regions(pdev, BIT(IECM_BAR0), pci_name(pdev));
> +       if (err) {
> +               dev_err(&pdev->dev, "BAR0 I/O map error %d\n", err);
> +               return err;
> +       }
> +
> +       /* set up for high or low dma */
> +       err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
> +       if (err)
> +               err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
> +       if (err) {
> +               dev_err(&pdev->dev, "DMA configuration failed: 0x%x\n", err);
> +               return err;
> +       }
> +
> +       pci_enable_pcie_error_reporting(pdev);
> +       pci_set_master(pdev);
> +       pci_set_drvdata(pdev, adapter);
> +
> +       adapter->init_wq =
> +               alloc_workqueue("%s", WQ_MEM_RECLAIM, 0, KBUILD_MODNAME);
> +       if (!adapter->init_wq) {
> +               dev_err(&pdev->dev, "Failed to allocate workqueue\n");

checkpatch usually complains about these kinds of messages, but if
you're going to put them in anyway, you might make them more useful
and add a bit to each that can tell you which is the one that broke.
For example, in this one, you might write it as
"Failed to allocate init workqueue"

> +               err = -ENOMEM;
> +               goto err_wq_alloc;
> +       }
> +
> +       adapter->serv_wq =
> +               alloc_workqueue("%s", WQ_MEM_RECLAIM, 0, KBUILD_MODNAME);
> +       if (!adapter->serv_wq) {
> +               dev_err(&pdev->dev, "Failed to allocate workqueue\n");
> +               err = -ENOMEM;
> +               goto err_mbx_wq_alloc;
> +       }
> +
> +       adapter->stats_wq =
> +               alloc_workqueue("%s", WQ_MEM_RECLAIM, 0, KBUILD_MODNAME);
> +       if (!adapter->stats_wq) {
> +               dev_err(&pdev->dev, "Failed to allocate workqueue\n");
> +               err = -ENOMEM;
> +               goto err_stats_wq_alloc;
> +       }
> +       adapter->vc_event_wq =
> +               alloc_workqueue("%s", WQ_MEM_RECLAIM, 0, KBUILD_MODNAME);
> +       if (!adapter->vc_event_wq) {
> +               dev_err(&pdev->dev, "Failed to allocate workqueue\n");
> +               err = -ENOMEM;
> +               goto err_vc_event_wq_alloc;
> +       }
> +
> +       /* setup msglvl */
> +       adapter->msg_enable = netif_msg_init(-1, IECM_AVAIL_NETIF_M);
> +
> +       adapter->vports = kcalloc(IECM_MAX_NUM_VPORTS,
> +                                 sizeof(*adapter->vports), GFP_KERNEL);
> +       if (!adapter->vports) {

With error messages on all the above allocations, is there any reason
they aren't here and in the next one?

> +               err = -ENOMEM;
> +               goto err_vport_alloc;
> +       }
> +
> +       adapter->netdevs = kcalloc(IECM_MAX_NUM_VPORTS,
> +                                  sizeof(struct net_device *), GFP_KERNEL);
> +       if (!adapter->netdevs) {
> +               err = -ENOMEM;
> +               goto err_netdev_alloc;
> +       }
> +
> +       mutex_init(&adapter->sw_mutex);
> +       mutex_init(&adapter->reset_lock);
> +       init_waitqueue_head(&adapter->vchnl_wq);
> +       init_waitqueue_head(&adapter->sw_marker_wq);
> +
> +       spin_lock_init(&adapter->cloud_filter_list_lock);
> +       spin_lock_init(&adapter->mac_filter_list_lock);
> +       spin_lock_init(&adapter->vlan_list_lock);
> +       spin_lock_init(&adapter->adv_rss_list_lock);
> +       spin_lock_init(&adapter->fdir_fltr_list_lock);
> +       INIT_LIST_HEAD(&adapter->config_data.mac_filter_list);
> +       INIT_LIST_HEAD(&adapter->config_data.vlan_filter_list);
> +       INIT_LIST_HEAD(&adapter->config_data.adv_rss_list);
> +
> +       INIT_DELAYED_WORK(&adapter->stats_task, iecm_statistics_task);
> +       INIT_DELAYED_WORK(&adapter->serv_task, iecm_service_task);
> +       INIT_DELAYED_WORK(&adapter->init_task, iecm_init_task);
> +       INIT_DELAYED_WORK(&adapter->vc_event_task, iecm_vc_event_task);
> +
> +       set_bit(__IECM_HR_DRV_LOAD, adapter->flags);
> +       queue_delayed_work(adapter->vc_event_wq, &adapter->vc_event_task,
> +                          msecs_to_jiffies(10 * (pdev->devfn & 0x07)));
> +
> +       return 0;
> +err_netdev_alloc:
> +       kfree(adapter->vports);
> +err_vport_alloc:
> +       destroy_workqueue(adapter->vc_event_wq);
> +err_vc_event_wq_alloc:
> +       destroy_workqueue(adapter->stats_wq);
> +err_stats_wq_alloc:
> +       destroy_workqueue(adapter->serv_wq);
> +err_mbx_wq_alloc:
> +       destroy_workqueue(adapter->init_wq);
> +err_wq_alloc:
> +       pci_disable_pcie_error_reporting(pdev);
> +       return err;
> +}
> +EXPORT_SYMBOL(iecm_probe);
> +
> +/**
> + * iecm_del_user_cfg_data - delete all user configuration data
> + * @adapter: Driver specific private structue
> + */
> +static void iecm_del_user_cfg_data(struct iecm_adapter *adapter)
> +{
> +       /* stub */
> +}
> +
> +/**
> + * iecm_remove - Device removal routine
> + * @pdev: PCI device information struct
> + */
> +void iecm_remove(struct pci_dev *pdev)
> +{
> +       struct iecm_adapter *adapter = pci_get_drvdata(pdev);
> +
> +       if (!adapter)
> +               return;
> +       /* Wait until vc_event_task is done to consider if any hard reset is
> +        * in progress else we may go ahead and release the resources but the
> +        * thread doing the hard reset might continue the init path and
> +        * end up in bad state.
> +        */
> +       cancel_delayed_work_sync(&adapter->vc_event_task);
> +       iecm_deinit_task(adapter);
> +       iecm_del_user_cfg_data(adapter);
> +       msleep(20);
> +       destroy_workqueue(adapter->serv_wq);
> +       destroy_workqueue(adapter->vc_event_wq);
> +       destroy_workqueue(adapter->stats_wq);
> +       destroy_workqueue(adapter->init_wq);
> +       kfree(adapter->vports);
> +       kfree(adapter->netdevs);
> +       kfree(adapter->vlan_caps);

Where did vlan_caps get allocated?

> +       mutex_destroy(&adapter->sw_mutex);
> +       mutex_destroy(&adapter->reset_lock);
> +       pci_disable_pcie_error_reporting(pdev);
> +       pcim_iounmap_regions(pdev, BIT(IECM_BAR0));
> +       pci_disable_device(pdev);
> +}
> +EXPORT_SYMBOL(iecm_remove);
> diff --git a/drivers/net/ethernet/intel/include/iecm.h b/drivers/net/ethernet/intel/include/iecm.h
> index f66f0d7db8e7..e19e014e9817 100644
> --- a/drivers/net/ethernet/intel/include/iecm.h
> +++ b/drivers/net/ethernet/intel/include/iecm.h
> @@ -4,7 +4,183 @@
>  #ifndef _IECM_H_
>  #define _IECM_H_
>
> -#include <linux/etherdevice.h>
> +#include <linux/aer.h>
> +#include <linux/pci.h>
> +#include <linux/netdevice.h>
> +#include <linux/ethtool.h>
>  #include <linux/version.h>
> +#include <linux/dim.h>
>
> +#include "iecm_txrx.h"
> +
> +#define IECM_BAR0                      0
> +#define IECM_NO_FREE_SLOT              0xffff
> +
> +#define IECM_MAX_NUM_VPORTS            1
> +
> +/* available message levels */
> +#define IECM_AVAIL_NETIF_M (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK)
> +
> +enum iecm_state {
> +       __IECM_STARTUP,
> +       __IECM_VER_CHECK,
> +       __IECM_GET_CAPS,
> +       __IECM_GET_DFLT_VPORT_PARAMS,
> +       __IECM_INIT_SW,
> +       __IECM_DOWN,
> +       __IECM_UP,
> +       __IECM_STATE_LAST /* this member MUST be last */
> +};
> +
> +enum iecm_flags {
> +       /* Soft reset causes */
> +       __IECM_SR_Q_CHANGE, /* Soft reset to do queue change */
> +       __IECM_SR_Q_DESC_CHANGE,
> +       __IECM_SR_Q_SCH_CHANGE, /* Scheduling mode change in queue context */
> +       __IECM_SR_MTU_CHANGE,
> +       __IECM_SR_TC_CHANGE,
> +       __IECM_SR_RSC_CHANGE,
> +       __IECM_SR_HSPLIT_CHANGE,
> +       /* Hard reset causes */
> +       __IECM_HR_FUNC_RESET, /* Hard reset when txrx timeout */
> +       __IECM_HR_CORE_RESET, /* when reset event is received on virtchannel */
> +       __IECM_HR_DRV_LOAD, /* Set on driver load for a clean HW */
> +       /* Reset in progress */
> +       __IECM_HR_RESET_IN_PROG,
> +       /* Resources release in progress*/
> +       __IECM_REL_RES_IN_PROG,
> +       /* Generic bits to share a message */
> +       __IECM_DEL_QUEUES,
> +       __IECM_UP_REQUESTED, /* Set if open to be called explicitly by driver */
> +       /* Mailbox interrupt event */
> +       __IECM_MB_INTR_MODE,
> +       __IECM_MB_INTR_TRIGGER,
> +       /* Stats message pending on mailbox */
> +       __IECM_MB_STATS_PENDING,
> +       /* Device specific bits */
> +       /* Request split queue model when creating vport */
> +       __IECM_REQ_TX_SPLITQ,
> +       __IECM_REQ_RX_SPLITQ,
> +       /* Asynchronous add/del ether address in flight */
> +       __IECM_ADD_ETH_REQ,
> +       __IECM_DEL_ETH_REQ,
> +       /* Virtchnl message buffer received needs to be processed */
> +       __IECM_VC_MSG_PENDING,
> +       /* To process software marker packets */
> +       __IECM_SW_MARKER,
> +       /* must be last */
> +       __IECM_FLAGS_NBITS,
> +};
> +
> +struct iecm_reset_reg {
> +       u32 rstat;
> +       u32 rstat_m;
> +};
> +
> +/* stub */
> +struct iecm_vport {
> +};
> +
> +enum iecm_user_flags {
> +       __IECM_PRIV_FLAGS_HDR_SPLIT = 0,
> +       __IECM_PROMISC_UC = 32,
> +       __IECM_PROMISC_MC,
> +       __IECM_USER_FLAGS_NBITS,
> +};
> +
> +/* User defined configuration values */
> +struct iecm_user_config_data {
> +       u32 num_req_tx_qs; /* user requested TX queues through ethtool */
> +       u32 num_req_rx_qs; /* user requested RX queues through ethtool */
> +       u32 num_req_txq_desc;
> +       u32 num_req_rxq_desc;
> +       u16 vlan_ethertype;
> +       void *req_qs_chunks;
> +       DECLARE_BITMAP(user_flags, __IECM_USER_FLAGS_NBITS);
> +       DECLARE_BITMAP(etf_qenable, IECM_LARGE_MAX_Q);
> +       struct list_head mac_filter_list;
> +       struct list_head vlan_filter_list;
> +       struct list_head adv_rss_list;
> +};
> +
> +struct iecm_rss_data {
> +       u64 rss_hash;
> +       u16 rss_key_size;
> +       u8 *rss_key;
> +       u16 rss_lut_size;
> +       u32 *rss_lut;
> +};
> +
> +struct iecm_adapter {
> +       struct pci_dev *pdev;
> +       const char *drv_name;
> +       const char *drv_ver;
> +       u32 virt_ver_maj;
> +       u32 virt_ver_min;
> +
> +       u32 tx_timeout_count;
> +       u32 msg_enable;
> +       enum iecm_state state;
> +       DECLARE_BITMAP(flags, __IECM_FLAGS_NBITS);
> +       struct mutex reset_lock; /* lock to protect reset flows */
> +       struct iecm_reset_reg reset_reg;
> +
> +       u16 num_req_msix;
> +       u16 num_msix_entries;
> +       struct msix_entry *msix_entries;
> +       struct virtchnl2_alloc_vectors *req_vec_chunks;
> +
> +       /* vport structs */
> +       struct iecm_vport **vports;     /* vports created by the driver */
> +       struct net_device **netdevs;    /* associated vport netdevs */
> +       u16 num_alloc_vport;
> +       u16 next_vport;         /* Next free slot in pf->vport[] - 0-based! */
> +
> +       struct delayed_work init_task; /* delayed init task */
> +       struct workqueue_struct *init_wq;
> +       u32 mb_wait_count;
> +       struct delayed_work serv_task; /* delayed service task */
> +       struct workqueue_struct *serv_wq;
> +       struct delayed_work stats_task; /* delayed statistics task */
> +       struct workqueue_struct *stats_wq;
> +       struct delayed_work vc_event_task; /* delayed virtchannel event task */
> +       struct workqueue_struct *vc_event_wq;
> +       /* Store the resources data received from control plane */
> +       void **vport_params_reqd;
> +       void **vport_params_recvd;
> +       /* User set parameters */
> +       struct iecm_user_config_data config_data;
> +       void *caps;
> +       struct virtchnl_vlan_caps *vlan_caps;
> +
> +       wait_queue_head_t vchnl_wq;
> +       wait_queue_head_t sw_marker_wq;
> +       struct iecm_rss_data rss_data;
> +       s32 link_speed;
> +       /* This is only populated if the VIRTCHNL_VF_CAP_ADV_LINK_SPEED is set
> +        * in vf_res->vf_cap_flags. This field should be used going forward and
> +        * the enum virtchnl_link_speed above should be considered the legacy
> +        * way of storing/communicating link speeds.
> +        */
> +       u32 link_speed_mbps;
> +       bool link_up;
> +       int num_vfs;
> +
> +       struct mutex sw_mutex;          /* lock to protect vport alloc flow */
> +       /* lock to protect cloud filters*/
> +       spinlock_t cloud_filter_list_lock;
> +       /* lock to protect mac filters */
> +       spinlock_t mac_filter_list_lock;
> +       /* lock to protect vlan filters */
> +       spinlock_t vlan_list_lock;
> +       /* lock to protect advanced RSS filters */
> +       spinlock_t adv_rss_list_lock;
> +       /* lock to protect the Flow Director filters */
> +       spinlock_t fdir_fltr_list_lock;
> +};
> +
> +int iecm_probe(struct pci_dev *pdev,
> +              const struct pci_device_id __always_unused *ent,
> +              struct iecm_adapter *adapter);
> +void iecm_remove(struct pci_dev *pdev);
>  #endif /* !_IECM_H_ */
> diff --git a/drivers/net/ethernet/intel/include/iecm_txrx.h b/drivers/net/ethernet/intel/include/iecm_txrx.h
> new file mode 100644
> index 000000000000..602d3b3b19dd
> --- /dev/null
> +++ b/drivers/net/ethernet/intel/include/iecm_txrx.h
> @@ -0,0 +1,33 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/* Copyright (C) 2019 Intel Corporation */
> +
> +#ifndef _IECM_TXRX_H_
> +#define _IECM_TXRX_H_
> +
> +#define IECM_LARGE_MAX_Q                       256
> +#define IECM_MAX_Q                             16
> +/* Mailbox Queue */
> +#define IECM_MAX_NONQ                          1
> +#define IECM_MAX_TXQ_DESC                      4096
> +#define IECM_MAX_RXQ_DESC                      4096
> +#define IECM_MIN_TXQ_DESC                      32
> +#define IECM_MIN_TXQ_COMPLQ_DESC               64
> +#define IECM_MIN_RXQ_DESC                      32
> +#define IECM_REQ_DESC_MULTIPLE                 32
> +#define IECM_REQ_SPLITQ_RXQ_DESC_MULTIPLE      64
> +#define IECM_MIN_TX_DESC_NEEDED (MAX_SKB_FRAGS + 6)
> +#define IECM_TX_WAKE_THRESH ((s16)IECM_MIN_TX_DESC_NEEDED * 2)
> +
> +#define IECM_DFLT_SINGLEQ_TX_Q_GROUPS          1
> +#define IECM_DFLT_SINGLEQ_RX_Q_GROUPS          1
> +#define IECM_DFLT_SINGLEQ_TXQ_PER_GROUP                4
> +#define IECM_DFLT_SINGLEQ_RXQ_PER_GROUP                4
> +
> +#define IECM_COMPLQ_PER_GROUP                  1
> +#define IECM_MAX_BUFQS_PER_RXQ_GRP             2
> +
> +#define IECM_DFLT_SPLITQ_TX_Q_GROUPS           4
> +#define IECM_DFLT_SPLITQ_RX_Q_GROUPS           4
> +#define IECM_DFLT_SPLITQ_TXQ_PER_GROUP         1
> +#define IECM_DFLT_SPLITQ_RXQ_PER_GROUP         1
> +#endif /* !_IECM_TXRX_H_ */
> --
> 2.33.0
>
> _______________________________________________
> Intel-wired-lan mailing list
> Intel-wired-lan at osuosl.org
> https://lists.osuosl.org/mailman/listinfo/intel-wired-lan

^ permalink raw reply	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 04/19] iecm: add api_init and controlq init
  2022-01-28  0:09 ` [Intel-wired-lan] [PATCH net-next 04/19] iecm: add api_init and controlq init Alan Brady
  2022-01-28 12:09   ` Alexander Lobakin
@ 2022-02-01 21:26   ` Shannon Nelson
  2022-02-03  3:24     ` Brady, Alan
  1 sibling, 1 reply; 89+ messages in thread
From: Shannon Nelson @ 2022-02-01 21:26 UTC (permalink / raw)
  To: intel-wired-lan

On Thu, Jan 27, 2022 at 4:35 PM Alan Brady <alan.brady@intel.com> wrote:
>
> Initializing device registers is offloaded into function pointers given
> to iecm from the dependent device driver for a given device, as offsets
> can vary wildly. This also adds everything needed to setup and use a
> controlq which uses some of those registers.
>
> At the end of probe we kicked off a hard reset and this implements what's
> needed to handle that reset and continue init.
>
> Signed-off-by: Phani Burra <phani.r.burra@intel.com>
> Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
> Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
> Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
> Signed-off-by: Alan Brady <alan.brady@intel.com>
> ---
>  drivers/net/ethernet/intel/iecm/Makefile      |   3 +
>  .../net/ethernet/intel/iecm/iecm_controlq.c   | 649 ++++++++++++++++++
>  .../ethernet/intel/iecm/iecm_controlq_setup.c | 175 +++++
>  drivers/net/ethernet/intel/iecm/iecm_lib.c    | 191 +++++-
>  .../net/ethernet/intel/iecm/iecm_virtchnl.c   | 172 +++++
>  drivers/net/ethernet/intel/include/iecm.h     |  52 ++
>  .../ethernet/intel/include/iecm_controlq.h    | 117 ++++
>  .../intel/include/iecm_controlq_api.h         | 185 +++++
>  drivers/net/ethernet/intel/include/iecm_mem.h |  20 +
>  9 files changed, 1563 insertions(+), 1 deletion(-)
>  create mode 100644 drivers/net/ethernet/intel/iecm/iecm_controlq.c
>  create mode 100644 drivers/net/ethernet/intel/iecm/iecm_controlq_setup.c
>  create mode 100644 drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
>  create mode 100644 drivers/net/ethernet/intel/include/iecm_controlq.h
>  create mode 100644 drivers/net/ethernet/intel/include/iecm_controlq_api.h
>  create mode 100644 drivers/net/ethernet/intel/include/iecm_mem.h
>
> diff --git a/drivers/net/ethernet/intel/iecm/Makefile b/drivers/net/ethernet/intel/iecm/Makefile
> index 4f497723419d..db8fecb075a6 100644
> --- a/drivers/net/ethernet/intel/iecm/Makefile
> +++ b/drivers/net/ethernet/intel/iecm/Makefile
> @@ -11,4 +11,7 @@ ccflags-y += -I$(srctree)/drivers/net/ethernet/intel/include
>
>  iecm-y := \
>         iecm_lib.o \
> +       iecm_virtchnl.o \
> +       iecm_controlq.o \
> +       iecm_controlq_setup.o \
>         iecm_main.o
> diff --git a/drivers/net/ethernet/intel/iecm/iecm_controlq.c b/drivers/net/ethernet/intel/iecm/iecm_controlq.c
> new file mode 100644
> index 000000000000..f9682a7b3e44
> --- /dev/null
> +++ b/drivers/net/ethernet/intel/iecm/iecm_controlq.c
> @@ -0,0 +1,649 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/* Copyright (c) 2020, Intel Corporation. */
> +
> +#include "iecm_controlq.h"
> +
> +/**
> + * iecm_ctlq_setup_regs - initialize control queue registers
> + * @cq: pointer to the specific control queue
> + * @q_create_info: structs containing info for each queue to be initialized
> + */
> +static void
> +iecm_ctlq_setup_regs(struct iecm_ctlq_info *cq,
> +                    struct iecm_ctlq_create_info *q_create_info)
> +{
> +       /* set head and tail registers in our local struct */
> +       cq->reg.head = q_create_info->reg.head;
> +       cq->reg.tail = q_create_info->reg.tail;
> +       cq->reg.len = q_create_info->reg.len;
> +       cq->reg.bah = q_create_info->reg.bah;
> +       cq->reg.bal = q_create_info->reg.bal;
> +       cq->reg.len_mask = q_create_info->reg.len_mask;
> +       cq->reg.len_ena_mask = q_create_info->reg.len_ena_mask;
> +       cq->reg.head_mask = q_create_info->reg.head_mask;
> +}
> +
> +/**
> + * iecm_ctlq_init_regs - Initialize control queue registers
> + * @hw: pointer to hw struct
> + * @cq: pointer to the specific Control queue
> + * @is_rxq: true if receive control queue, false otherwise
> + *
> + * Initialize registers. The caller is expected to have already initialized the
> + * descriptor ring memory and buffer memory
> + */
> +static void iecm_ctlq_init_regs(struct iecm_hw *hw, struct iecm_ctlq_info *cq,
> +                               bool is_rxq)
> +{
> +       /* Update tail to post pre-allocated buffers for rx queues */
> +       if (is_rxq)
> +               wr32(hw, cq->reg.tail, (u32)(cq->ring_size - 1));
> +
> +       /* For non-Mailbox control queues only TAIL need to be set */
> +       if (cq->q_id != -1)
> +               return;
> +
> +       /* Clear Head for both send or receive */
> +       wr32(hw, cq->reg.head, 0);
> +
> +       /* set starting point */
> +       wr32(hw, cq->reg.bal, lower_32_bits(cq->desc_ring.pa));
> +       wr32(hw, cq->reg.bah, upper_32_bits(cq->desc_ring.pa));
> +       wr32(hw, cq->reg.len, (cq->ring_size | cq->reg.len_ena_mask));
> +}
> +
> +/**
> + * iecm_ctlq_init_rxq_bufs - populate receive queue descriptors with buf
> + * @cq: pointer to the specific Control queue
> + *
> + * Record the address of the receive queue DMA buffers in the descriptors.
> + * The buffers must have been previously allocated.
> + */
> +static void iecm_ctlq_init_rxq_bufs(struct iecm_ctlq_info *cq)
> +{
> +       int i = 0;
> +
> +       for (i = 0; i < cq->ring_size; i++) {
> +               struct iecm_ctlq_desc *desc = IECM_CTLQ_DESC(cq, i);
> +               struct iecm_dma_mem *bi = cq->bi.rx_buff[i];
> +
> +               /* No buffer to post to descriptor, continue */
> +               if (!bi)
> +                       continue;
> +
> +               desc->flags =
> +                       cpu_to_le16(IECM_CTLQ_FLAG_BUF | IECM_CTLQ_FLAG_RD);
> +               desc->opcode = 0;
> +               desc->datalen = (__le16)cpu_to_le16(bi->size);

Why the typecast to __le16, aren't we there already with the cpu_to_le16()?

> +               desc->ret_val = 0;
> +               desc->cookie_high = 0;
> +               desc->cookie_low = 0;
> +               desc->params.indirect.addr_high =
> +                       cpu_to_le32(upper_32_bits(bi->pa));
> +               desc->params.indirect.addr_low =
> +                       cpu_to_le32(lower_32_bits(bi->pa));
> +               desc->params.indirect.param0 = 0;
> +               desc->params.indirect.param1 = 0;
> +       }
> +}
> +
> +/**
> + * iecm_ctlq_shutdown - shutdown the CQ
> + * @hw: pointer to hw struct
> + * @cq: pointer to the specific Control queue
> + *
> + * The main shutdown routine for any controq queue
> + */
> +static void iecm_ctlq_shutdown(struct iecm_hw *hw, struct iecm_ctlq_info *cq)
> +{
> +       mutex_lock(&cq->cq_lock);
> +
> +       if (!cq->ring_size)
> +               goto shutdown_sq_out;
> +
> +       /* free ring buffers and the ring itself */
> +       iecm_ctlq_dealloc_ring_res(hw, cq);
> +
> +       /* Set ring_size to 0 to indicate uninitialized queue */
> +       cq->ring_size = 0;
> +
> +shutdown_sq_out:
> +       mutex_unlock(&cq->cq_lock);
> +       mutex_destroy(&cq->cq_lock);
> +}
> +
> +/**
> + * iecm_ctlq_add - add one control queue
> + * @hw: pointer to hardware struct
> + * @qinfo: info for queue to be created
> + * @cq_out: (output) double pointer to control queue to be created
> + *
> + * Allocate and initialize a control queue and add it to the control queue list.
> + * The cq parameter will be allocated/initialized and passed back to the caller
> + * if no errors occur.
> + *
> + * Note: iecm_ctlq_init must be called prior to any calls to iecm_ctlq_add
> + */
> +int iecm_ctlq_add(struct iecm_hw *hw,
> +                 struct iecm_ctlq_create_info *qinfo,
> +                 struct iecm_ctlq_info **cq_out)
> +{
> +       bool is_rxq = false;
> +       int status = 0;
> +
> +       if (!qinfo->len || !qinfo->buf_size ||
> +           qinfo->len > IECM_CTLQ_MAX_RING_SIZE ||
> +           qinfo->buf_size > IECM_CTLQ_MAX_BUF_LEN)
> +               return -EINVAL;
> +
> +       *cq_out = kcalloc(1, sizeof(struct iecm_ctlq_info), GFP_KERNEL);
> +       if (!(*cq_out))
> +               return -ENOMEM;

You might keep this as a local variable until you get to a successful
end, then set *cq_out when done.
Else, you need to be sure to clear it back to NULL on error return to
be sure no one uses a bogus value.

> +
> +       (*cq_out)->cq_type = qinfo->type;
> +       (*cq_out)->q_id = qinfo->id;
> +       (*cq_out)->buf_size = qinfo->buf_size;
> +       (*cq_out)->ring_size = qinfo->len;
> +
> +       (*cq_out)->next_to_use = 0;
> +       (*cq_out)->next_to_clean = 0;
> +       (*cq_out)->next_to_post = (*cq_out)->ring_size - 1;
> +
> +       switch (qinfo->type) {
> +       case IECM_CTLQ_TYPE_MAILBOX_RX:
> +               is_rxq = true;
> +               fallthrough;
> +       case IECM_CTLQ_TYPE_MAILBOX_TX:
> +               status = iecm_ctlq_alloc_ring_res(hw, *cq_out);
> +               break;
> +       default:
> +               status = -EBADR;
> +               break;
> +       }
> +
> +       if (status)
> +               goto init_free_q;
> +
> +       if (is_rxq) {
> +               iecm_ctlq_init_rxq_bufs(*cq_out);
> +       } else {
> +               /* Allocate the array of msg pointers for TX queues */
> +               (*cq_out)->bi.tx_msg = kcalloc(qinfo->len,
> +                                              sizeof(struct iecm_ctlq_msg *),
> +                                              GFP_KERNEL);
> +               if (!(*cq_out)->bi.tx_msg) {
> +                       status = -ENOMEM;
> +                       goto init_dealloc_q_mem;
> +               }
> +       }
> +
> +       iecm_ctlq_setup_regs(*cq_out, qinfo);
> +
> +       iecm_ctlq_init_regs(hw, *cq_out, is_rxq);
> +
> +       mutex_init(&(*cq_out)->cq_lock);
> +
> +       list_add(&(*cq_out)->cq_list, &hw->cq_list_head);
> +
> +       return status;
> +
> +init_dealloc_q_mem:
> +       /* free ring buffers and the ring itself */
> +       iecm_ctlq_dealloc_ring_res(hw, *cq_out);
> +init_free_q:
> +       kfree(*cq_out);

Probably should clear this back to NULL.

> +
> +       return status;
> +}
> +
> +/**
> + * iecm_ctlq_remove - deallocate and remove specified control queue
> + * @hw: pointer to hardware struct
> + * @cq: pointer to control queue to be removed
> + */
> +void iecm_ctlq_remove(struct iecm_hw *hw,
> +                     struct iecm_ctlq_info *cq)
> +{
> +       list_del(&cq->cq_list);
> +       iecm_ctlq_shutdown(hw, cq);
> +       kfree(cq);
> +}
> +
> +/**
> + * iecm_ctlq_init - main initialization routine for all control queues
> + * @hw: pointer to hardware struct
> + * @num_q: number of queues to initialize
> + * @q_info: array of structs containing info for each queue to be initialized
> + *
> + * This initializes any number and any type of control queues. This is an all
> + * or nothing routine; if one fails, all previously allocated queues will be
> + * destroyed. This must be called prior to using the individual add/remove
> + * APIs.
> + */
> +int iecm_ctlq_init(struct iecm_hw *hw, u8 num_q,
> +                  struct iecm_ctlq_create_info *q_info)
> +{
> +       struct iecm_ctlq_info *cq = NULL, *tmp = NULL;
> +       int ret_code = 0;
> +       int i = 0;
> +
> +       INIT_LIST_HEAD(&hw->cq_list_head);
> +
> +       for (i = 0; i < num_q; i++) {
> +               struct iecm_ctlq_create_info *qinfo = q_info + i;
> +
> +               ret_code = iecm_ctlq_add(hw, qinfo, &cq);
> +               if (ret_code)
> +                       goto init_destroy_qs;
> +       }
> +
> +       return ret_code;
> +
> +init_destroy_qs:
> +       list_for_each_entry_safe(cq, tmp, &hw->cq_list_head, cq_list)
> +               iecm_ctlq_remove(hw, cq);

This could call iecm_ctlq_deinit() rather than repeat the same code.

> +
> +       return ret_code;
> +}
> +
> +/**
> + * iecm_ctlq_deinit - destroy all control queues
> + * @hw: pointer to hw struct
> + */
> +int iecm_ctlq_deinit(struct iecm_hw *hw)
> +{
> +       struct iecm_ctlq_info *cq = NULL, *tmp = NULL;
> +       int ret_code = 0;
> +
> +       list_for_each_entry_safe(cq, tmp, &hw->cq_list_head, cq_list)
> +               iecm_ctlq_remove(hw, cq);
> +
> +       return ret_code;

Why is there a return code here when there is never an error?

> +}
> +
> +/**
> + * iecm_ctlq_send - send command to Control Queue (CTQ)
> + * @hw: pointer to hw struct
> + * @cq: handle to control queue struct to send on
> + * @num_q_msg: number of messages to send on control queue
> + * @q_msg: pointer to array of queue messages to be sent
> + *
> + * The caller is expected to allocate DMAable buffers and pass them to the
> + * send routine via the q_msg struct / control queue specific data struct.
> + * The control queue will hold a reference to each send message until
> + * the completion for that message has been cleaned.
> + */
> +int iecm_ctlq_send(struct iecm_hw *hw, struct iecm_ctlq_info *cq,
> +                  u16 num_q_msg, struct iecm_ctlq_msg q_msg[])
> +{
> +       struct iecm_ctlq_desc *desc;
> +       int num_desc_avail = 0;
> +       int status = 0;
> +       int i = 0;
> +
> +       if (!cq || !cq->ring_size)
> +               return -ENOBUFS;
> +
> +       mutex_lock(&cq->cq_lock);
> +
> +       /* Ensure there are enough descriptors to send all messages */
> +       num_desc_avail = IECM_CTLQ_DESC_UNUSED(cq);
> +       if (num_desc_avail == 0 || num_desc_avail < num_q_msg) {
> +               status = -ENOSPC;
> +               goto sq_send_command_out;
> +       }
> +
> +       for (i = 0; i < num_q_msg; i++) {
> +               struct iecm_ctlq_msg *msg = &q_msg[i];
> +               u64 msg_cookie;
> +
> +               desc = IECM_CTLQ_DESC(cq, cq->next_to_use);
> +
> +               desc->opcode = cpu_to_le16(msg->opcode);
> +               desc->pfid_vfid = cpu_to_le16(msg->func_id);
> +
> +               msg_cookie = *(u64 *)&msg->cookie;
> +               desc->cookie_high =
> +                       cpu_to_le32(upper_32_bits(msg_cookie));
> +               desc->cookie_low =
> +                       cpu_to_le32(lower_32_bits(msg_cookie));
> +
> +               if (msg->data_len) {
> +                       struct iecm_dma_mem *buff = msg->ctx.indirect.payload;
> +
> +                       desc->datalen = cpu_to_le16(msg->data_len);
> +                       desc->flags |= cpu_to_le16(IECM_CTLQ_FLAG_BUF);
> +                       desc->flags |= cpu_to_le16(IECM_CTLQ_FLAG_RD);
> +
> +                       /* Update the address values in the desc with the pa
> +                        * value for respective buffer
> +                        */
> +                       desc->params.indirect.addr_high =
> +                               cpu_to_le32(upper_32_bits(buff->pa));
> +                       desc->params.indirect.addr_low =
> +                               cpu_to_le32(lower_32_bits(buff->pa));
> +
> +                       memcpy(&desc->params, msg->ctx.indirect.context,
> +                              IECM_INDIRECT_CTX_SIZE);
> +               } else {
> +                       memcpy(&desc->params, msg->ctx.direct,
> +                              IECM_DIRECT_CTX_SIZE);
> +               }
> +
> +               /* Store buffer info */
> +               cq->bi.tx_msg[cq->next_to_use] = msg;
> +
> +               (cq->next_to_use)++;
> +               if (cq->next_to_use == cq->ring_size)
> +                       cq->next_to_use = 0;
> +       }
> +
> +       /* Force memory write to complete before letting hardware
> +        * know that there are new descriptors to fetch.
> +        */
> +       dma_wmb();
> +
> +       wr32(hw, cq->reg.tail, cq->next_to_use);
> +
> +sq_send_command_out:
> +       mutex_unlock(&cq->cq_lock);
> +
> +       return status;
> +}
> +
> +/**
> + * iecm_ctlq_clean_sq - reclaim send descriptors on HW write back for the
> + * requested queue
> + * @cq: pointer to the specific Control queue
> + * @clean_count: (input|output) number of descriptors to clean as input, and
> + * number of descriptors actually cleaned as output

Rather than two meanings for one parameter, can the number cleaned be
a positive return value?

> + * @msg_status: (output) pointer to msg pointer array to be populated; needs
> + * to be allocated by caller
> + *
> + * Returns an array of message pointers associated with the cleaned
> + * descriptors. The pointers are to the original ctlq_msgs sent on the cleaned
> + * descriptors.  The status will be returned for each; any messages that failed
> + * to send will have a non-zero status. The caller is expected to free original
> + * ctlq_msgs and free or reuse the DMA buffers.
> + */
> +int iecm_ctlq_clean_sq(struct iecm_ctlq_info *cq, u16 *clean_count,
> +                      struct iecm_ctlq_msg *msg_status[])
> +{
> +       struct iecm_ctlq_desc *desc;
> +       u16 i = 0, num_to_clean;
> +       u16 ntc, desc_err;
> +       int ret = 0;
> +
> +       if (!cq || !cq->ring_size)
> +               return -ENOBUFS;
> +
> +       if (*clean_count == 0)
> +               return 0;
> +       if (*clean_count > cq->ring_size)
> +               return -EBADR;
> +
> +       mutex_lock(&cq->cq_lock);
> +
> +       ntc = cq->next_to_clean;
> +
> +       num_to_clean = *clean_count;
> +
> +       for (i = 0; i < num_to_clean; i++) {
> +               /* Fetch next descriptor and check if marked as done */
> +               desc = IECM_CTLQ_DESC(cq, ntc);
> +               if (!(le16_to_cpu(desc->flags) & IECM_CTLQ_FLAG_DD))
> +                       break;
> +
> +               desc_err = le16_to_cpu(desc->ret_val);
> +               if (desc_err) {
> +                       /* strip off FW internal code */
> +                       desc_err &= 0xff;
> +               }

Why not simply
    desc_err = le16_to_cpu(desc->ret_val) & 0xff;

> +
> +               msg_status[i] = cq->bi.tx_msg[ntc];
> +               msg_status[i]->status = desc_err;
> +
> +               cq->bi.tx_msg[ntc] = NULL;
> +
> +               /* Zero out any stale data */
> +               memset(desc, 0, sizeof(*desc));
> +
> +               ntc++;
> +               if (ntc == cq->ring_size)
> +                       ntc = 0;
> +       }
> +
> +       cq->next_to_clean = ntc;
> +
> +       mutex_unlock(&cq->cq_lock);
> +
> +       /* Return number of descriptors actually cleaned */
> +       *clean_count = i;
> +
> +       return ret;
> +}
> +
> +/**
> + * iecm_ctlq_post_rx_buffs - post buffers to descriptor ring
> + * @hw: pointer to hw struct
> + * @cq: pointer to control queue handle
> + * @buff_count: (input|output) input is number of buffers caller is trying to
> + * return; output is number of buffers that were not posted

Same comment.
And the return would make more sense if consistent with the previous
function where the return is how many were successful.

> + * @buffs: array of pointers to dma mem structs to be given to hardware
> + *
> + * Caller uses this function to return DMA buffers to the descriptor ring after
> + * consuming them; buff_count will be the number of buffers.
> + *
> + * Note: this function needs to be called after a receive call even
> + * if there are no DMA buffers to be returned, i.e. buff_count = 0,
> + * buffs = NULL to support direct commands
> + */
> +int iecm_ctlq_post_rx_buffs(struct iecm_hw *hw, struct iecm_ctlq_info *cq,
> +                           u16 *buff_count, struct iecm_dma_mem **buffs)
> +{
> +       struct iecm_ctlq_desc *desc;
> +       u16 ntp = cq->next_to_post;
> +       bool buffs_avail = false;
> +       u16 tbp = ntp + 1;
> +       int status = 0;
> +       int i = 0;
> +
> +       if (*buff_count > cq->ring_size)
> +               return -EBADR;
> +
> +       if (*buff_count > 0)
> +               buffs_avail = true;
> +
> +       mutex_lock(&cq->cq_lock);
> +
> +       if (tbp >= cq->ring_size)
> +               tbp = 0;
> +
> +       if (tbp == cq->next_to_clean)
> +               /* Nothing to do */
> +               goto post_buffs_out;
> +
> +       /* Post buffers for as many as provided or up until the last one used */
> +       while (ntp != cq->next_to_clean) {
> +               desc = IECM_CTLQ_DESC(cq, ntp);
> +
> +               if (cq->bi.rx_buff[ntp])
> +                       goto fill_desc;
> +               if (!buffs_avail) {
> +                       /* If the caller hasn't given us any buffers or
> +                        * there are none left, search the ring itself
> +                        * for an available buffer to move to this
> +                        * entry starting at the next entry in the ring
> +                        */
> +                       tbp = ntp + 1;
> +
> +                       /* Wrap ring if necessary */
> +                       if (tbp >= cq->ring_size)
> +                               tbp = 0;
> +
> +                       while (tbp != cq->next_to_clean) {
> +                               if (cq->bi.rx_buff[tbp]) {
> +                                       cq->bi.rx_buff[ntp] =
> +                                               cq->bi.rx_buff[tbp];
> +                                       cq->bi.rx_buff[tbp] = NULL;
> +
> +                                       /* Found a buffer, no need to
> +                                        * search anymore
> +                                        */
> +                                       break;
> +                               }
> +
> +                               /* Wrap ring if necessary */
> +                               tbp++;
> +                               if (tbp >= cq->ring_size)
> +                                       tbp = 0;
> +                       }
> +
> +                       if (tbp == cq->next_to_clean)
> +                               goto post_buffs_out;
> +               } else {
> +                       /* Give back pointer to DMA buffer */
> +                       cq->bi.rx_buff[ntp] = buffs[i];
> +                       i++;
> +
> +                       if (i >= *buff_count)
> +                               buffs_avail = false;
> +               }
> +
> +fill_desc:
> +               desc->flags =
> +                       cpu_to_le16(IECM_CTLQ_FLAG_BUF | IECM_CTLQ_FLAG_RD);
> +
> +               /* Post buffers to descriptor */
> +               desc->datalen = cpu_to_le16(cq->bi.rx_buff[ntp]->size);
> +               desc->params.indirect.addr_high =
> +                       cpu_to_le32(upper_32_bits(cq->bi.rx_buff[ntp]->pa));
> +               desc->params.indirect.addr_low =
> +                       cpu_to_le32(lower_32_bits(cq->bi.rx_buff[ntp]->pa));
> +
> +               ntp++;
> +               if (ntp == cq->ring_size)
> +                       ntp = 0;
> +       }
> +
> +post_buffs_out:
> +       /* Only update tail if buffers were actually posted */
> +       if (cq->next_to_post != ntp) {
> +               if (ntp)
> +                       /* Update next_to_post to ntp - 1 since current ntp
> +                        * will not have a buffer
> +                        */
> +                       cq->next_to_post = ntp - 1;
> +               else
> +                       /* Wrap to end of end ring since current ntp is 0 */
> +                       cq->next_to_post = cq->ring_size - 1;
> +
> +               wr32(hw, cq->reg.tail, cq->next_to_post);
> +       }
> +
> +       mutex_unlock(&cq->cq_lock);
> +
> +       /* return the number of buffers that were not posted */
> +       *buff_count = *buff_count - i;
> +
> +       return status;
> +}
> +
> +/**
> + * iecm_ctlq_recv - receive control queue message call back
> + * @cq: pointer to control queue handle to receive on
> + * @num_q_msg: (input|output) input number of messages that should be received;
> + * output number of messages actually received
> + * @q_msg: (output) array of received control queue messages on this q;
> + * needs to be pre-allocated by caller for as many messages as requested
> + *
> + * Called by interrupt handler or polling mechanism. Caller is expected
> + * to free buffers
> + */
> +int iecm_ctlq_recv(struct iecm_ctlq_info *cq, u16 *num_q_msg,
> +                  struct iecm_ctlq_msg *q_msg)
> +{
> +       u16 num_to_clean, ntc, ret_val, flags;
> +       struct iecm_ctlq_desc *desc;
> +       int ret_code = 0;
> +       u16 i = 0;
> +
> +       if (!cq || !cq->ring_size)
> +               return -ENOBUFS;

Should *num_q_msgs get set to 0 here since none were received?

> +
> +       if (*num_q_msg == 0)
> +               return 0;
> +       else if (*num_q_msg > cq->ring_size)
> +               return -EBADR;

Again, set *num_q_msgs to 0?

> +
> +       /* take the lock before we start messing with the ring */
> +       mutex_lock(&cq->cq_lock);
> +
> +       ntc = cq->next_to_clean;
> +
> +       num_to_clean = *num_q_msg;
> +
> +       for (i = 0; i < num_to_clean; i++) {
> +               u64 msg_cookie;
> +
> +               /* Fetch next descriptor and check if marked as done */
> +               desc = IECM_CTLQ_DESC(cq, ntc);
> +               flags = le16_to_cpu(desc->flags);
> +
> +               if (!(flags & IECM_CTLQ_FLAG_DD))
> +                       break;
> +
> +               ret_val = le16_to_cpu(desc->ret_val);
> +
> +               q_msg[i].vmvf_type = (flags &
> +                                     (IECM_CTLQ_FLAG_FTYPE_VM |
> +                                      IECM_CTLQ_FLAG_FTYPE_PF)) >>
> +                                     IECM_CTLQ_FLAG_FTYPE_S;
> +
> +               if (flags & IECM_CTLQ_FLAG_ERR)
> +                       ret_code = -EBADMSG;

So you might return -EBADMSG, even if you've cleaned many messages?
Will this be reflected in ret_val and does the caller then need to
hunt down the bad q_msg[].status?
How these work together should probably be clearly explained in the
function header.

> +
> +               msg_cookie = (u64)le32_to_cpu(desc->cookie_high) << 32;
> +               msg_cookie |= (u64)le32_to_cpu(desc->cookie_low);
> +               memcpy(&q_msg[i].cookie, &msg_cookie, sizeof(u64));
> +
> +               q_msg[i].opcode = le16_to_cpu(desc->opcode);
> +               q_msg[i].data_len = le16_to_cpu(desc->datalen);
> +               q_msg[i].status = ret_val;
> +
> +               if (desc->datalen) {
> +                       memcpy(q_msg[i].ctx.indirect.context,
> +                              &desc->params.indirect, IECM_INDIRECT_CTX_SIZE);
> +
> +                       /* Assign pointer to dma buffer to ctlq_msg array
> +                        * to be given to upper layer
> +                        */
> +                       q_msg[i].ctx.indirect.payload = cq->bi.rx_buff[ntc];
> +
> +                       /* Zero out pointer to DMA buffer info;
> +                        * will be repopulated by post buffers API
> +                        */
> +                       cq->bi.rx_buff[ntc] = NULL;
> +               } else {
> +                       memcpy(q_msg[i].ctx.direct, desc->params.raw,
> +                              IECM_DIRECT_CTX_SIZE);
> +               }
> +
> +               /* Zero out stale data in descriptor */
> +               memset(desc, 0, sizeof(struct iecm_ctlq_desc));
> +
> +               ntc++;
> +               if (ntc == cq->ring_size)
> +                       ntc = 0;
> +       };
> +
> +       cq->next_to_clean = ntc;
> +
> +       mutex_unlock(&cq->cq_lock);
> +
> +       *num_q_msg = i;
> +       if (*num_q_msg == 0)
> +               ret_code = -ENOMSG;
> +
> +       return ret_code;
> +}
> diff --git a/drivers/net/ethernet/intel/iecm/iecm_controlq_setup.c b/drivers/net/ethernet/intel/iecm/iecm_controlq_setup.c
> new file mode 100644
> index 000000000000..a36fc88d6bb5
> --- /dev/null
> +++ b/drivers/net/ethernet/intel/iecm/iecm_controlq_setup.c
> @@ -0,0 +1,175 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/* Copyright (c) 2020, Intel Corporation. */
> +
> +#include "iecm_controlq.h"
> +
> +/**
> + * iecm_ctlq_alloc_desc_ring - Allocate Control Queue (CQ) rings
> + * @hw: pointer to hw struct
> + * @cq: pointer to the specific Control queue
> + */
> +static int
> +iecm_ctlq_alloc_desc_ring(struct iecm_hw *hw,
> +                         struct iecm_ctlq_info *cq)
> +{
> +       size_t size = cq->ring_size * sizeof(struct iecm_ctlq_desc);
> +
> +       cq->desc_ring.va = iecm_alloc_dma_mem(hw, &cq->desc_ring, size);
> +       if (!cq->desc_ring.va)
> +               return -ENOMEM;
> +
> +       return 0;
> +}
> +
> +/**
> + * iecm_ctlq_alloc_bufs - Allocate Control Queue (CQ) buffers
> + * @hw: pointer to hw struct
> + * @cq: pointer to the specific Control queue
> + *
> + * Allocate the buffer head for all control queues, and if it's a receive
> + * queue, allocate DMA buffers
> + */
> +static int iecm_ctlq_alloc_bufs(struct iecm_hw *hw,
> +                               struct iecm_ctlq_info *cq)
> +{
> +       int i = 0;
> +
> +       /* Do not allocate DMA buffers for transmit queues */
> +       if (cq->cq_type == IECM_CTLQ_TYPE_MAILBOX_TX)
> +               return 0;
> +
> +       /* We'll be allocating the buffer info memory first, then we can
> +        * allocate the mapped buffers for the event processing
> +        */
> +       cq->bi.rx_buff = kcalloc(cq->ring_size, sizeof(struct iecm_dma_mem *),
> +                                GFP_KERNEL);
> +       if (!cq->bi.rx_buff)
> +               return -ENOMEM;
> +
> +       /* allocate the mapped buffers (except for the last one) */
> +       for (i = 0; i < cq->ring_size - 1; i++) {
> +               struct iecm_dma_mem *bi;
> +               int num = 1; /* number of iecm_dma_mem to be allocated */
> +
> +               cq->bi.rx_buff[i] = kcalloc(num, sizeof(struct iecm_dma_mem),
> +                                           GFP_KERNEL);
> +               if (!cq->bi.rx_buff[i])
> +                       goto unwind_alloc_cq_bufs;
> +
> +               bi = cq->bi.rx_buff[i];
> +
> +               bi->va = iecm_alloc_dma_mem(hw, bi, cq->buf_size);
> +               if (!bi->va) {
> +                       /* unwind will not free the failed entry */
> +                       kfree(cq->bi.rx_buff[i]);
> +                       goto unwind_alloc_cq_bufs;
> +               }
> +       }
> +
> +       return 0;
> +
> +unwind_alloc_cq_bufs:
> +       /* don't try to free the one that failed... */
> +       i--;
> +       for (; i >= 0; i--) {
> +               iecm_free_dma_mem(hw, cq->bi.rx_buff[i]);
> +               kfree(cq->bi.rx_buff[i]);
> +       }
> +       kfree(cq->bi.rx_buff);
> +
> +       return -ENOMEM;
> +}
> +
> +/**
> + * iecm_ctlq_free_desc_ring - Free Control Queue (CQ) rings
> + * @hw: pointer to hw struct
> + * @cq: pointer to the specific Control queue
> + *
> + * This assumes the posted send buffers have already been cleaned
> + * and de-allocated
> + */
> +static void iecm_ctlq_free_desc_ring(struct iecm_hw *hw,
> +                                    struct iecm_ctlq_info *cq)
> +{
> +       iecm_free_dma_mem(hw, &cq->desc_ring);
> +}
> +
> +/**
> + * iecm_ctlq_free_bufs - Free CQ buffer info elements
> + * @hw: pointer to hw struct
> + * @cq: pointer to the specific Control queue
> + *
> + * Free the DMA buffers for RX queues, and DMA buffer header for both RX and TX
> + * queues.  The upper layers are expected to manage freeing of TX DMA buffers
> + */
> +static void iecm_ctlq_free_bufs(struct iecm_hw *hw, struct iecm_ctlq_info *cq)
> +{
> +       void *bi;
> +
> +       if (cq->cq_type == IECM_CTLQ_TYPE_MAILBOX_RX) {
> +               int i;
> +
> +               /* free DMA buffers for rx queues*/
> +               for (i = 0; i < cq->ring_size; i++) {
> +                       if (cq->bi.rx_buff[i]) {
> +                               iecm_free_dma_mem(hw, cq->bi.rx_buff[i]);
> +                               kfree(cq->bi.rx_buff[i]);
> +                       }
> +               }
> +
> +               bi = (void *)cq->bi.rx_buff;
> +       } else {
> +               bi = (void *)cq->bi.tx_msg;
> +       }
> +
> +       /* free the buffer header */
> +       kfree(bi);
> +}
> +
> +/**
> + * iecm_ctlq_dealloc_ring_res - Free memory allocated for control queue
> + * @hw: pointer to hw struct
> + * @cq: pointer to the specific Control queue
> + *
> + * Free the memory used by the ring, buffers and other related structures
> + */
> +void iecm_ctlq_dealloc_ring_res(struct iecm_hw *hw, struct iecm_ctlq_info *cq)
> +{
> +       /* free ring buffers and the ring itself */
> +       iecm_ctlq_free_bufs(hw, cq);
> +       iecm_ctlq_free_desc_ring(hw, cq);
> +}
> +
> +/**
> + * iecm_ctlq_alloc_ring_res - allocate memory for descriptor ring and bufs
> + * @hw: pointer to hw struct
> + * @cq: pointer to control queue struct
> + *
> + * Do *NOT* hold the lock when calling this as the memory allocation routines
> + * called are not going to be atomic context safe
> + */
> +int iecm_ctlq_alloc_ring_res(struct iecm_hw *hw, struct iecm_ctlq_info *cq)
> +{
> +       int ret_code;
> +
> +       /* verify input for valid configuration */
> +       if (!cq->ring_size || !cq->buf_size)
> +               return -EINVAL;
> +
> +       /* allocate the ring memory */
> +       ret_code = iecm_ctlq_alloc_desc_ring(hw, cq);
> +       if (ret_code)
> +               return ret_code;
> +
> +       /* allocate buffers in the rings */
> +       ret_code = iecm_ctlq_alloc_bufs(hw, cq);
> +       if (ret_code)
> +               goto iecm_init_cq_free_ring;
> +
> +       /* success! */
> +       return 0;
> +
> +iecm_init_cq_free_ring:
> +       iecm_free_dma_mem(hw, &cq->desc_ring);
> +       return ret_code;
> +}
> diff --git a/drivers/net/ethernet/intel/iecm/iecm_lib.c b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> index e6d0b418a27f..64cdbce2c842 100644
> --- a/drivers/net/ethernet/intel/iecm/iecm_lib.c
> +++ b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> @@ -5,6 +5,25 @@
>
>  #include "iecm.h"
>
> +/**
> + * iecm_cfg_hw - Initialize HW struct
> + * @adapter: adapter to setup hw struct for
> + *
> + * Returns 0 on success, negative on failure
> + */
> +static int iecm_cfg_hw(struct iecm_adapter *adapter)
> +{
> +       struct pci_dev *pdev = adapter->pdev;
> +       struct iecm_hw *hw = &adapter->hw;
> +
> +       hw->hw_addr = pcim_iomap_table(pdev)[IECM_BAR0];
> +       if (!hw->hw_addr)
> +               return -EIO;
> +       hw->back = adapter;
> +
> +       return 0;
> +}
> +
>  /**
>   * iecm_statistics_task - Delayed task to get statistics over mailbox
>   * @work: work_struct handle to our data
> @@ -39,6 +58,32 @@ static void iecm_init_task(struct work_struct *work)
>         /* stub */
>  }
>
> +/**
> + * iecm_api_init - Initialize and verify device API
> + * @adapter: driver specific private structure
> + *
> + * Returns 0 on success, negative on failure
> + */
> +static int iecm_api_init(struct iecm_adapter *adapter)
> +{
> +       struct iecm_reg_ops *reg_ops = &adapter->dev_ops.reg_ops;
> +       struct pci_dev *pdev = adapter->pdev;
> +
> +       if (!adapter->dev_ops.reg_ops_init) {
> +               dev_err(&pdev->dev, "Invalid device, register API init not defined\n");
> +               return -EINVAL;
> +       }
> +       adapter->dev_ops.reg_ops_init(adapter);
> +       if (!(reg_ops->ctlq_reg_init && reg_ops->intr_reg_init &&
> +             reg_ops->mb_intr_reg_init && reg_ops->reset_reg_init &&
> +             reg_ops->trigger_reset)) {
> +               dev_err(&pdev->dev, "Invalid device, missing one or more register functions\n");
> +               return -EINVAL;
> +       }
> +
> +       return 0;
> +}
> +
>  /**
>   * iecm_deinit_task - Device deinit routine
>   * @adapter: Driver specific private structue
> @@ -51,13 +96,108 @@ static void iecm_deinit_task(struct iecm_adapter *adapter)
>         /* stub */
>  }
>
> +/**
> + * iecm_check_reset_complete - check that reset is complete
> + * @hw: pointer to hw struct
> + * @reset_reg: struct with reset registers
> + *
> + * Returns 0 if device is ready to use, or -EBUSY if it's in reset.
> + **/
> +static int iecm_check_reset_complete(struct iecm_hw *hw,
> +                                    struct iecm_reset_reg *reset_reg)
> +{
> +       struct iecm_adapter *adapter = (struct iecm_adapter *)hw->back;
> +       int i;
> +
> +       for (i = 0; i < 2000; i++) {
> +               u32 reg_val = rd32(hw, reset_reg->rstat);
> +
> +               /* 0xFFFFFFFF might be read if other side hasn't cleared the
> +                * register for us yet and 0xFFFFFFFF is not a valid value for
> +                * the register, so treat that as invalid.
> +                */
> +               if (reg_val != 0xFFFFFFFF && (reg_val & reset_reg->rstat_m))
> +                       return 0;
> +               usleep_range(5000, 10000);
> +       }
> +
> +       dev_warn(&adapter->pdev->dev, "Device reset timeout!\n");
> +       return -EBUSY;
> +}
> +
> +/**
> + * iecm_init_hard_reset - Initiate a hardware reset
> + * @adapter: Driver specific private structure
> + *
> + * Deallocate the vports and all the resources associated with them and
> + * reallocate. Also reinitialize the mailbox. Return 0 on success,
> + * negative on failure.
> + */
> +static int iecm_init_hard_reset(struct iecm_adapter *adapter)
> +{
> +       int err = 0;
> +
> +       mutex_lock(&adapter->reset_lock);
> +
> +       /* Prepare for reset */
> +       if (test_and_clear_bit(__IECM_HR_DRV_LOAD, adapter->flags)) {
> +               adapter->dev_ops.reg_ops.trigger_reset(adapter,
> +                                                      __IECM_HR_DRV_LOAD);
> +       } else if (test_and_clear_bit(__IECM_HR_FUNC_RESET, adapter->flags)) {
> +               bool is_reset = iecm_is_reset_detected(adapter);
> +
> +               if (adapter->state == __IECM_UP)
> +                       set_bit(__IECM_UP_REQUESTED, adapter->flags);
> +               iecm_deinit_task(adapter);
> +               if (!is_reset)
> +                       adapter->dev_ops.reg_ops.trigger_reset(adapter,
> +                                                              __IECM_HR_FUNC_RESET);
> +               iecm_deinit_dflt_mbx(adapter);
> +       } else if (test_and_clear_bit(__IECM_HR_CORE_RESET, adapter->flags)) {
> +               if (adapter->state == __IECM_UP)
> +                       set_bit(__IECM_UP_REQUESTED, adapter->flags);
> +               iecm_deinit_task(adapter);
> +       } else {
> +               dev_err(&adapter->pdev->dev, "Unhandled hard reset cause\n");
> +               err = -EBADRQC;
> +               goto handle_err;
> +       }
> +
> +       /* Wait for reset to complete */
> +       err = iecm_check_reset_complete(&adapter->hw, &adapter->reset_reg);
> +       if (err) {
> +               dev_err(&adapter->pdev->dev, "The driver was unable to contact the device's firmware.  Check that the FW is running. Driver state=%u\n",
> +                       adapter->state);
> +               goto handle_err;
> +       }
> +
> +       /* Reset is complete and so start building the driver resources again */
> +       err = iecm_init_dflt_mbx(adapter);
> +       if (err) {
> +               dev_err(&adapter->pdev->dev, "Failed to initialize default mailbox: %d\n",
> +                       err);
> +       }
> +handle_err:
> +       mutex_unlock(&adapter->reset_lock);
> +       return err;
> +}
> +
>  /**
>   * iecm_vc_event_task - Handle virtchannel event logic
>   * @work: work queue struct
>   */
>  static void iecm_vc_event_task(struct work_struct *work)
>  {
> -       /* stub */
> +       struct iecm_adapter *adapter = container_of(work,
> +                                                   struct iecm_adapter,
> +                                                   vc_event_task.work);
> +
> +       if (test_bit(__IECM_HR_CORE_RESET, adapter->flags) ||
> +           test_bit(__IECM_HR_FUNC_RESET, adapter->flags) ||
> +           test_bit(__IECM_HR_DRV_LOAD, adapter->flags)) {
> +               set_bit(__IECM_HR_RESET_IN_PROG, adapter->flags);
> +               iecm_init_hard_reset(adapter);
> +       }
>  }
>
>  /**
> @@ -75,6 +215,11 @@ int iecm_probe(struct pci_dev *pdev,
>         int err;
>
>         adapter->pdev = pdev;
> +       err = iecm_api_init(adapter);
> +       if (err) {
> +               dev_err(&pdev->dev, "Device API is incorrectly configured\n");
> +               return err;
> +       }
>
>         err = pcim_enable_device(pdev);
>         if (err)
> @@ -147,6 +292,20 @@ int iecm_probe(struct pci_dev *pdev,
>                 goto err_netdev_alloc;
>         }
>
> +       err = iecm_vport_params_buf_alloc(adapter);
> +       if (err) {
> +               dev_err(&pdev->dev, "Failed to alloc vport params buffer: %d\n",
> +                       err);
> +               goto err_mb_res;
> +       }
> +
> +       err = iecm_cfg_hw(adapter);
> +       if (err) {
> +               dev_err(&pdev->dev, "Failed to configure HW structure for adapter: %d\n",
> +                       err);
> +               goto err_cfg_hw;
> +       }
> +
>         mutex_init(&adapter->sw_mutex);
>         mutex_init(&adapter->reset_lock);
>         init_waitqueue_head(&adapter->vchnl_wq);
> @@ -166,11 +325,16 @@ int iecm_probe(struct pci_dev *pdev,
>         INIT_DELAYED_WORK(&adapter->init_task, iecm_init_task);
>         INIT_DELAYED_WORK(&adapter->vc_event_task, iecm_vc_event_task);
>
> +       adapter->dev_ops.reg_ops.reset_reg_init(&adapter->reset_reg);
>         set_bit(__IECM_HR_DRV_LOAD, adapter->flags);
>         queue_delayed_work(adapter->vc_event_wq, &adapter->vc_event_task,
>                            msecs_to_jiffies(10 * (pdev->devfn & 0x07)));
>
>         return 0;
> +err_cfg_hw:
> +       iecm_vport_params_buf_rel(adapter);
> +err_mb_res:
> +       kfree(adapter->netdevs);
>  err_netdev_alloc:
>         kfree(adapter->vports);
>  err_vport_alloc:
> @@ -214,6 +378,7 @@ void iecm_remove(struct pci_dev *pdev)
>         cancel_delayed_work_sync(&adapter->vc_event_task);
>         iecm_deinit_task(adapter);
>         iecm_del_user_cfg_data(adapter);
> +       iecm_deinit_dflt_mbx(adapter);
>         msleep(20);
>         destroy_workqueue(adapter->serv_wq);
>         destroy_workqueue(adapter->vc_event_wq);
> @@ -222,6 +387,7 @@ void iecm_remove(struct pci_dev *pdev)
>         kfree(adapter->vports);
>         kfree(adapter->netdevs);
>         kfree(adapter->vlan_caps);
> +       iecm_vport_params_buf_rel(adapter);
>         mutex_destroy(&adapter->sw_mutex);
>         mutex_destroy(&adapter->reset_lock);
>         pci_disable_pcie_error_reporting(pdev);
> @@ -229,3 +395,26 @@ void iecm_remove(struct pci_dev *pdev)
>         pci_disable_device(pdev);
>  }
>  EXPORT_SYMBOL(iecm_remove);
> +
> +void *iecm_alloc_dma_mem(struct iecm_hw *hw, struct iecm_dma_mem *mem, u64 size)
> +{
> +       struct iecm_adapter *adapter = (struct iecm_adapter *)hw->back;
> +       size_t sz = ALIGN(size, 4096);
> +
> +       mem->va = dma_alloc_coherent(&adapter->pdev->dev, sz,
> +                                    &mem->pa, GFP_KERNEL | __GFP_ZERO);
> +       mem->size = size;
> +
> +       return mem->va;
> +}
> +
> +void iecm_free_dma_mem(struct iecm_hw *hw, struct iecm_dma_mem *mem)
> +{
> +       struct iecm_adapter *adapter = (struct iecm_adapter *)hw->back;
> +
> +       dma_free_coherent(&adapter->pdev->dev, mem->size,
> +                         mem->va, mem->pa);
> +       mem->size = 0;
> +       mem->va = NULL;
> +       mem->pa = 0;
> +}
> diff --git a/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> new file mode 100644
> index 000000000000..b8f54b8c700a
> --- /dev/null
> +++ b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> @@ -0,0 +1,172 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/* Copyright (C) 2019 Intel Corporation */
> +
> +#include "iecm.h"
> +
> +/**
> + * iecm_mb_clean - Reclaim the send mailbox queue entries
> + * @adapter: Driver specific private structure
> + *
> + * Reclaim the send mailbox queue entries to be used to send further messages
> + *
> + * Returns 0 on success, negative on failure
> + */
> +static int iecm_mb_clean(struct iecm_adapter *adapter)
> +{
> +       u16 i, num_q_msg = IECM_DFLT_MBX_Q_LEN;
> +       struct iecm_ctlq_msg **q_msg;
> +       struct iecm_dma_mem *dma_mem;
> +       int err = 0;
> +
> +       q_msg = kcalloc(num_q_msg, sizeof(struct iecm_ctlq_msg *), GFP_KERNEL);
> +       if (!q_msg)
> +               return -ENOMEM;
> +
> +       err = iecm_ctlq_clean_sq(adapter->hw.asq, &num_q_msg, q_msg);
> +       if (err)
> +               goto error;
> +
> +       for (i = 0; i < num_q_msg; i++) {
> +               dma_mem = q_msg[i]->ctx.indirect.payload;
> +               if (dma_mem)
> +                       dmam_free_coherent(&adapter->pdev->dev, dma_mem->size,
> +                                          dma_mem->va, dma_mem->pa);
> +               kfree(q_msg[i]);
> +               kfree(dma_mem);
> +       }
> +error:
> +       kfree(q_msg);
> +       return err;
> +}
> +
> +/**
> + * iecm_find_ctlq - Given a type and id, find ctlq info
> + * @hw: hardware struct
> + * @type: type of ctrlq to find
> + * @id: ctlq id to find
> + *
> + * Returns pointer to found ctlq info struct, NULL otherwise.
> + */
> +static struct iecm_ctlq_info *iecm_find_ctlq(struct iecm_hw *hw,
> +                                            enum iecm_ctlq_type type, int id)
> +{
> +       struct iecm_ctlq_info *cq, *tmp;
> +
> +       list_for_each_entry_safe(cq, tmp, &hw->cq_list_head, cq_list) {
> +               if (cq->q_id == id && cq->cq_type == type)
> +                       return cq;
> +       }
> +
> +       return NULL;
> +}
> +
> +/**
> + * iecm_init_dflt_mbx - Setup default mailbox parameters and make request
> + * @adapter: adapter info struct
> + *
> + * Returns 0 on success, negative otherwise
> + */
> +int iecm_init_dflt_mbx(struct iecm_adapter *adapter)
> +{
> +       struct iecm_ctlq_create_info ctlq_info[] = {
> +               {
> +                       .type = IECM_CTLQ_TYPE_MAILBOX_TX,
> +                       .id = IECM_DFLT_MBX_ID,
> +                       .len = IECM_DFLT_MBX_Q_LEN,
> +                       .buf_size = IECM_DFLT_MBX_BUF_SIZE
> +               },
> +               {
> +                       .type = IECM_CTLQ_TYPE_MAILBOX_RX,
> +                       .id = IECM_DFLT_MBX_ID,
> +                       .len = IECM_DFLT_MBX_Q_LEN,
> +                       .buf_size = IECM_DFLT_MBX_BUF_SIZE
> +               }
> +       };
> +       struct iecm_hw *hw = &adapter->hw;
> +       int err;
> +
> +       adapter->dev_ops.reg_ops.ctlq_reg_init(ctlq_info);
> +
> +#define NUM_Q 2
> +       err = iecm_ctlq_init(hw, NUM_Q, ctlq_info);
> +       if (err)
> +               return err;
> +
> +       hw->asq = iecm_find_ctlq(hw, IECM_CTLQ_TYPE_MAILBOX_TX,
> +                                IECM_DFLT_MBX_ID);
> +       hw->arq = iecm_find_ctlq(hw, IECM_CTLQ_TYPE_MAILBOX_RX,
> +                                IECM_DFLT_MBX_ID);
> +
> +       if (!hw->asq || !hw->arq) {
> +               iecm_ctlq_deinit(hw);
> +               return -ENOENT;
> +       }
> +       adapter->state = __IECM_STARTUP;
> +       /* Skew the delay for init tasks for each function based on fn number
> +        * to prevent every function from making the same call simulatenously.
> +        */
> +       queue_delayed_work(adapter->init_wq, &adapter->init_task,
> +                          msecs_to_jiffies(5 * (adapter->pdev->devfn & 0x07)));
> +       return 0;
> +}
> +
> +/**
> + * iecm_deinit_dflt_mbx - Free up ctlqs setup
> + * @adapter: Driver specific private data structure
> + */
> +void iecm_deinit_dflt_mbx(struct iecm_adapter *adapter)
> +{
> +       if (adapter->hw.arq && adapter->hw.asq) {
> +               iecm_mb_clean(adapter);
> +               iecm_ctlq_deinit(&adapter->hw);
> +       }
> +       adapter->hw.arq = NULL;
> +       adapter->hw.asq = NULL;
> +}
> +
> +/**
> + * iecm_vport_params_buf_alloc - Allocate memory for MailBox resources
> + * @adapter: Driver specific private data structure
> + *
> + * Will alloc memory to hold the vport parameters received on MailBox
> + */
> +int iecm_vport_params_buf_alloc(struct iecm_adapter *adapter)
> +{
> +       adapter->vport_params_reqd = kcalloc(IECM_MAX_NUM_VPORTS,
> +                                            sizeof(*adapter->vport_params_reqd),
> +                                            GFP_KERNEL);
> +       if (!adapter->vport_params_reqd)
> +               return -ENOMEM;
> +
> +       adapter->vport_params_recvd = kcalloc(IECM_MAX_NUM_VPORTS,
> +                                             sizeof(*adapter->vport_params_recvd),
> +                                             GFP_KERNEL);
> +       if (!adapter->vport_params_recvd) {
> +               kfree(adapter->vport_params_reqd);
> +               return -ENOMEM;
> +       }
> +
> +       return 0;
> +}
> +
> +/**
> + * iecm_vport_params_buf_rel - Release memory for MailBox resources
> + * @adapter: Driver specific private data structure
> + *
> + * Will release memory to hold the vport parameters received on MailBox
> + */
> +void iecm_vport_params_buf_rel(struct iecm_adapter *adapter)
> +{
> +       int i = 0;
> +
> +       for (i = 0; i < IECM_MAX_NUM_VPORTS; i++) {
> +               kfree(adapter->vport_params_recvd[i]);
> +               kfree(adapter->vport_params_reqd[i]);
> +       }
> +
> +       kfree(adapter->vport_params_recvd);
> +       kfree(adapter->vport_params_reqd);
> +
> +       kfree(adapter->caps);
> +       kfree(adapter->config_data.req_qs_chunks);
> +}
> diff --git a/drivers/net/ethernet/intel/include/iecm.h b/drivers/net/ethernet/intel/include/iecm.h
> index e19e014e9817..ca9029224e06 100644
> --- a/drivers/net/ethernet/intel/include/iecm.h
> +++ b/drivers/net/ethernet/intel/include/iecm.h
> @@ -12,15 +12,33 @@
>  #include <linux/dim.h>
>
>  #include "iecm_txrx.h"
> +#include "iecm_controlq.h"
>
>  #define IECM_BAR0                      0
>  #define IECM_NO_FREE_SLOT              0xffff
>
> +/* Default Mailbox settings */
> +#define IECM_DFLT_MBX_BUF_SIZE         (4 * 1024)
> +#define IECM_NUM_QCTX_PER_MSG          3
> +#define IECM_NUM_FILTERS_PER_MSG       20
> +#define IECM_VLANS_PER_MSG \
> +       ((IECM_DFLT_MBX_BUF_SIZE - sizeof(struct virtchnl_vlan_filter_list)) \
> +        / sizeof(u16))
> +#define IECM_DFLT_MBX_Q_LEN            64
> +#define IECM_DFLT_MBX_ID               -1
> +/* maximum number of times to try before resetting mailbox */
> +#define IECM_MB_MAX_ERR                        20
> +#define IECM_NUM_CHUNKS_PER_MSG(a, b)  ((IECM_DFLT_MBX_BUF_SIZE - (a)) / (b))
> +
>  #define IECM_MAX_NUM_VPORTS            1
>
>  /* available message levels */
>  #define IECM_AVAIL_NETIF_M (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK)
>
> +/* Forward declaration */
> +struct iecm_adapter;
> +struct iecm_vport;
> +
>  enum iecm_state {
>         __IECM_STARTUP,
>         __IECM_VER_CHECK,
> @@ -77,6 +95,22 @@ struct iecm_reset_reg {
>         u32 rstat_m;
>  };
>
> +/* product specific register API */
> +struct iecm_reg_ops {
> +       void (*ctlq_reg_init)(struct iecm_ctlq_create_info *cq);
> +       int (*intr_reg_init)(struct iecm_vport *vport);
> +       void (*mb_intr_reg_init)(struct iecm_adapter *adapter);
> +       void (*reset_reg_init)(struct iecm_reset_reg *reset_reg);
> +       void (*trigger_reset)(struct iecm_adapter *adapter,
> +                             enum iecm_flags trig_cause);
> +};
> +
> +struct iecm_dev_ops {
> +       void (*reg_ops_init)(struct iecm_adapter *adapter);
> +       void (*crc_enable)(u64 *td_cmd);
> +       struct iecm_reg_ops reg_ops;
> +};
> +
>  /* stub */
>  struct iecm_vport {
>  };
> @@ -124,6 +158,7 @@ struct iecm_adapter {
>         DECLARE_BITMAP(flags, __IECM_FLAGS_NBITS);
>         struct mutex reset_lock; /* lock to protect reset flows */
>         struct iecm_reset_reg reset_reg;
> +       struct iecm_hw hw;
>
>         u16 num_req_msix;
>         u16 num_msix_entries;
> @@ -156,6 +191,7 @@ struct iecm_adapter {
>         wait_queue_head_t vchnl_wq;
>         wait_queue_head_t sw_marker_wq;
>         struct iecm_rss_data rss_data;
> +       struct iecm_dev_ops dev_ops;
>         s32 link_speed;
>         /* This is only populated if the VIRTCHNL_VF_CAP_ADV_LINK_SPEED is set
>          * in vf_res->vf_cap_flags. This field should be used going forward and
> @@ -179,8 +215,24 @@ struct iecm_adapter {
>         spinlock_t fdir_fltr_list_lock;
>  };
>
> +/**
> + * iecm_is_reset_detected - check if we were reset at some point
> + * @adapter: driver specific private structure
> + *
> + * Returns true if we are either in reset currently or were previously reset.
> + */
> +static inline bool iecm_is_reset_detected(struct iecm_adapter *adapter)
> +{
> +       return !(rd32(&adapter->hw, adapter->hw.arq->reg.len) &
> +                adapter->hw.arq->reg.len_ena_mask);
> +}
> +
>  int iecm_probe(struct pci_dev *pdev,
>                const struct pci_device_id __always_unused *ent,
>                struct iecm_adapter *adapter);
>  void iecm_remove(struct pci_dev *pdev);
> +int iecm_init_dflt_mbx(struct iecm_adapter *adapter);
> +void iecm_deinit_dflt_mbx(struct iecm_adapter *adapter);
> +int iecm_vport_params_buf_alloc(struct iecm_adapter *adapter);
> +void iecm_vport_params_buf_rel(struct iecm_adapter *adapter);
>  #endif /* !_IECM_H_ */
> diff --git a/drivers/net/ethernet/intel/include/iecm_controlq.h b/drivers/net/ethernet/intel/include/iecm_controlq.h
> new file mode 100644
> index 000000000000..f2539baa2ce1
> --- /dev/null
> +++ b/drivers/net/ethernet/intel/include/iecm_controlq.h
> @@ -0,0 +1,117 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/* Copyright (c) 2020, Intel Corporation. */
> +
> +#ifndef _IECM_CONTROLQ_H_
> +#define _IECM_CONTROLQ_H_
> +
> +#include <linux/slab.h>
> +
> +#include "iecm_controlq_api.h"
> +
> +/* Maximum buffer lengths for all control queue types */
> +#define IECM_CTLQ_MAX_RING_SIZE 1024
> +#define IECM_CTLQ_MAX_BUF_LEN  4096
> +
> +#define IECM_CTLQ_DESC(R, i) \
> +       (&(((struct iecm_ctlq_desc *)((R)->desc_ring.va))[i]))
> +
> +#define IECM_CTLQ_DESC_UNUSED(R) \
> +       ((u16)((((R)->next_to_clean > (R)->next_to_use) ? 0 : (R)->ring_size) + \
> +             (R)->next_to_clean - (R)->next_to_use - 1))
> +
> +/* Control Queue default settings */
> +#define IECM_CTRL_SQ_CMD_TIMEOUT       250  /* msecs */
> +
> +struct iecm_ctlq_desc {
> +       __le16  flags;
> +       __le16  opcode;
> +       __le16  datalen;        /* 0 for direct commands */
> +       union {
> +               __le16 ret_val;
> +               __le16 pfid_vfid;
> +#define IECM_CTLQ_DESC_VF_ID_S 0
> +#define IECM_CTLQ_DESC_VF_ID_M (0x7FF << IECM_CTLQ_DESC_VF_ID_S)
> +#define IECM_CTLQ_DESC_PF_ID_S 11
> +#define IECM_CTLQ_DESC_PF_ID_M (0x1F << IECM_CTLQ_DESC_PF_ID_S)
> +       };
> +       __le32 cookie_high;
> +       __le32 cookie_low;
> +       union {
> +               struct {
> +                       __le32 param0;
> +                       __le32 param1;
> +                       __le32 param2;
> +                       __le32 param3;
> +               } direct;
> +               struct {
> +                       __le32 param0;
> +                       __le32 param1;
> +                       __le32 addr_high;
> +                       __le32 addr_low;
> +               } indirect;
> +               u8 raw[16];
> +       } params;
> +};
> +
> +/* Flags sub-structure
> + * |0  |1  |2  |3  |4  |5  |6  |7  |8  |9  |10 |11 |12 |13 |14 |15 |
> + * |DD |CMP|ERR|  * RSV *  |FTYPE  | *RSV* |RD |VFC|BUF|  * RSV *  |
> + */
> +/* command flags and offsets */
> +#define IECM_CTLQ_FLAG_DD_S    0
> +#define IECM_CTLQ_FLAG_CMP_S   1
> +#define IECM_CTLQ_FLAG_ERR_S   2
> +#define IECM_CTLQ_FLAG_FTYPE_S 6
> +#define IECM_CTLQ_FLAG_RD_S    10
> +#define IECM_CTLQ_FLAG_VFC_S   11
> +#define IECM_CTLQ_FLAG_BUF_S   12
> +
> +#define IECM_CTLQ_FLAG_DD      BIT(IECM_CTLQ_FLAG_DD_S)        /* 0x1    */
> +#define IECM_CTLQ_FLAG_CMP     BIT(IECM_CTLQ_FLAG_CMP_S)       /* 0x2    */
> +#define IECM_CTLQ_FLAG_ERR     BIT(IECM_CTLQ_FLAG_ERR_S)       /* 0x4    */
> +#define IECM_CTLQ_FLAG_FTYPE_VM        BIT(IECM_CTLQ_FLAG_FTYPE_S)     /* 0x40   */
> +#define IECM_CTLQ_FLAG_FTYPE_PF        BIT(IECM_CTLQ_FLAG_FTYPE_S + 1) /* 0x80   */
> +#define IECM_CTLQ_FLAG_RD      BIT(IECM_CTLQ_FLAG_RD_S)        /* 0x400  */
> +#define IECM_CTLQ_FLAG_VFC     BIT(IECM_CTLQ_FLAG_VFC_S)       /* 0x800  */
> +#define IECM_CTLQ_FLAG_BUF     BIT(IECM_CTLQ_FLAG_BUF_S)       /* 0x1000 */
> +
> +struct iecm_mbxq_desc {
> +       u8 pad[8];              /* CTLQ flags/opcode/len/retval fields */
> +       u32 chnl_opcode;        /* avoid confusion with desc->opcode */
> +       u32 chnl_retval;        /* ditto for desc->retval */
> +       u32 pf_vf_id;           /* used by CP when sending to PF */
> +};
> +
> +/* Define the APF hardware struct to replace other control structs as needed
> + * Align to ctlq_hw_info
> + */
> +struct iecm_hw {
> +       u8 __iomem *hw_addr;
> +       u64 hw_addr_len;
> +       void *back;
> +
> +       /* control queue - send and receive */
> +       struct iecm_ctlq_info *asq;
> +       struct iecm_ctlq_info *arq;
> +
> +       /* pci info */
> +       u16 device_id;
> +       u16 vendor_id;
> +       u16 subsystem_device_id;
> +       u16 subsystem_vendor_id;
> +       u8 revision_id;
> +       bool adapter_stopped;
> +
> +       struct list_head cq_list_head;
> +};
> +
> +int iecm_ctlq_alloc_ring_res(struct iecm_hw *hw,
> +                            struct iecm_ctlq_info *cq);
> +
> +void iecm_ctlq_dealloc_ring_res(struct iecm_hw *hw, struct iecm_ctlq_info *cq);
> +
> +/* prototype for functions used for dynamic memory allocation */
> +void *iecm_alloc_dma_mem(struct iecm_hw *hw, struct iecm_dma_mem *mem,
> +                        u64 size);
> +void iecm_free_dma_mem(struct iecm_hw *hw, struct iecm_dma_mem *mem);
> +#endif /* _IECM_CONTROLQ_H_ */
> diff --git a/drivers/net/ethernet/intel/include/iecm_controlq_api.h b/drivers/net/ethernet/intel/include/iecm_controlq_api.h
> new file mode 100644
> index 000000000000..5f624f005d33
> --- /dev/null
> +++ b/drivers/net/ethernet/intel/include/iecm_controlq_api.h
> @@ -0,0 +1,185 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/* Copyright (c) 2020, Intel Corporation. */
> +
> +#ifndef _IECM_CONTROLQ_API_H_
> +#define _IECM_CONTROLQ_API_H_
> +
> +#include "iecm_mem.h"
> +
> +struct iecm_hw;
> +
> +/* Used for queue init, response and events */
> +enum iecm_ctlq_type {
> +       IECM_CTLQ_TYPE_MAILBOX_TX       = 0,
> +       IECM_CTLQ_TYPE_MAILBOX_RX       = 1,
> +       IECM_CTLQ_TYPE_CONFIG_TX        = 2,
> +       IECM_CTLQ_TYPE_CONFIG_RX        = 3,
> +       IECM_CTLQ_TYPE_EVENT_RX         = 4,
> +       IECM_CTLQ_TYPE_RDMA_TX          = 5,
> +       IECM_CTLQ_TYPE_RDMA_RX          = 6,
> +       IECM_CTLQ_TYPE_RDMA_COMPL       = 7
> +};
> +
> +/* Generic Control Queue Structures */
> +struct iecm_ctlq_reg {
> +       /* used for queue tracking */
> +       u32 head;
> +       u32 tail;
> +       /* Below applies only to default mb (if present) */
> +       u32 len;
> +       u32 bah;
> +       u32 bal;
> +       u32 len_mask;
> +       u32 len_ena_mask;
> +       u32 head_mask;
> +};
> +
> +/* Generic queue msg structure */
> +struct iecm_ctlq_msg {
> +       u16 vmvf_type; /* represents the source of the message on recv */
> +#define IECM_VMVF_TYPE_VF 0
> +#define IECM_VMVF_TYPE_VM 1
> +#define IECM_VMVF_TYPE_PF 2
> +       u16 opcode;
> +       u16 data_len;   /* data_len = 0 when no payload is attached */
> +       union {
> +               u16 func_id;    /* when sending a message */
> +               u16 status;     /* when receiving a message */
> +       };
> +       union {
> +               struct {
> +                       u32 chnl_retval;
> +                       u32 chnl_opcode;
> +               } mbx;
> +       } cookie;
> +       union {
> +#define IECM_DIRECT_CTX_SIZE   16
> +#define IECM_INDIRECT_CTX_SIZE 8
> +               /* 16 bytes of context can be provided or 8 bytes of context
> +                * plus the address of a DMA buffer
> +                */
> +               u8 direct[IECM_DIRECT_CTX_SIZE];
> +               struct {
> +                       u8 context[IECM_INDIRECT_CTX_SIZE];
> +                       struct iecm_dma_mem *payload;
> +               } indirect;
> +       } ctx;
> +};
> +
> +/* Generic queue info structures */
> +/* MB, CONFIG and EVENT q do not have extended info */
> +struct iecm_ctlq_create_info {
> +       enum iecm_ctlq_type type;
> +       int id; /* absolute queue offset passed as input
> +                * -1 for default mailbox if present
> +                */
> +       u16 len; /* Queue length passed as input */
> +       u16 buf_size; /* buffer size passed as input */
> +       u64 base_address; /* output, HPA of the Queue start  */
> +       struct iecm_ctlq_reg reg; /* registers accessed by ctlqs */
> +
> +       int ext_info_size;
> +       void *ext_info; /* Specific to q type */
> +};
> +
> +/* Control Queue information */
> +struct iecm_ctlq_info {
> +       struct list_head cq_list;
> +
> +       enum iecm_ctlq_type cq_type;
> +       int q_id;
> +       /* control queue lock */
> +       struct mutex cq_lock;
> +
> +       /* used for interrupt processing */
> +       u16 next_to_use;
> +       u16 next_to_clean;
> +       u16 next_to_post;               /* starting descriptor to post buffers
> +                                        * to after recev
> +                                        */
> +
> +       struct iecm_dma_mem desc_ring;  /* descriptor ring memory
> +                                        * iecm_dma_mem is defined in OSdep.h
> +                                        */
> +       union {
> +               struct iecm_dma_mem **rx_buff;
> +               struct iecm_ctlq_msg **tx_msg;
> +       } bi;
> +
> +       u16 buf_size;                   /* queue buffer size */
> +       u16 ring_size;                  /* Number of descriptors */
> +       struct iecm_ctlq_reg reg;       /* registers accessed by ctlqs */
> +};
> +
> +/* PF/VF mailbox commands */
> +enum iecm_mbx_opc {
> +       /* iecm_mbq_opc_send_msg_to_pf:
> +        *      usage: used by PF or VF to send a message to its CPF
> +        *      target: RX queue and function ID of parent PF taken from HW
> +        */
> +       iecm_mbq_opc_send_msg_to_pf             = 0x0801,
> +
> +       /* iecm_mbq_opc_send_msg_to_vf:
> +        *      usage: used by PF to send message to a VF
> +        *      target: VF control queue ID must be specified in descriptor
> +        */
> +       iecm_mbq_opc_send_msg_to_vf             = 0x0802,
> +
> +       /* iecm_mbq_opc_send_msg_to_peer_pf:
> +        *      usage: used by any function to send message to any peer PF
> +        *      target: RX queue and host of parent PF taken from HW
> +        */
> +       iecm_mbq_opc_send_msg_to_peer_pf        = 0x0803,
> +
> +       /* iecm_mbq_opc_send_msg_to_peer_drv:
> +        *      usage: used by any function to send message to any peer driver
> +        *      target: RX queue and target host must be specific in descriptor
> +        */
> +       iecm_mbq_opc_send_msg_to_peer_drv       = 0x0804,
> +};
> +
> +/* API support for control queue management */
> +
> +/* Will init all required q including default mb.  "q_info" is an array of
> + * create_info structs equal to the number of control queues to be created.
> + */
> +int iecm_ctlq_init(struct iecm_hw *hw, u8 num_q,
> +                  struct iecm_ctlq_create_info *q_info);
> +
> +/* Allocate and initialize a single control queue, which will be added to the
> + * control queue list; returns a handle to the created control queue
> + */
> +int iecm_ctlq_add(struct iecm_hw *hw,
> +                 struct iecm_ctlq_create_info *qinfo,
> +                 struct iecm_ctlq_info **cq);
> +
> +/* Deinitialize and deallocate a single control queue */
> +void iecm_ctlq_remove(struct iecm_hw *hw,
> +                     struct iecm_ctlq_info *cq);
> +
> +/* Sends messages to HW and will also free the buffer*/
> +int iecm_ctlq_send(struct iecm_hw *hw,
> +                  struct iecm_ctlq_info *cq,
> +                  u16 num_q_msg,
> +                  struct iecm_ctlq_msg q_msg[]);
> +
> +/* Receives messages and called by interrupt handler/polling
> + * initiated by app/process. Also caller is supposed to free the buffers
> + */
> +int iecm_ctlq_recv(struct iecm_ctlq_info *cq, u16 *num_q_msg,
> +                  struct iecm_ctlq_msg *q_msg);
> +
> +/* Reclaims send descriptors on HW write back */
> +int iecm_ctlq_clean_sq(struct iecm_ctlq_info *cq, u16 *clean_count,
> +                      struct iecm_ctlq_msg *msg_status[]);
> +
> +/* Indicate RX buffers are done being processed */
> +int iecm_ctlq_post_rx_buffs(struct iecm_hw *hw,
> +                           struct iecm_ctlq_info *cq,
> +                           u16 *buff_count,
> +                           struct iecm_dma_mem **buffs);
> +
> +/* Will destroy all q including the default mb */
> +int iecm_ctlq_deinit(struct iecm_hw *hw);
> +
> +#endif /* _IECM_CONTROLQ_API_H_ */
> diff --git a/drivers/net/ethernet/intel/include/iecm_mem.h b/drivers/net/ethernet/intel/include/iecm_mem.h
> new file mode 100644
> index 000000000000..064dd6e10c24
> --- /dev/null
> +++ b/drivers/net/ethernet/intel/include/iecm_mem.h
> @@ -0,0 +1,20 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/* Copyright (C) 2019 Intel Corporation */
> +
> +#ifndef _IECM_MEM_H_
> +#define _IECM_MEM_H_
> +
> +#include <linux/io.h>
> +
> +struct iecm_dma_mem {
> +       void *va;
> +       dma_addr_t pa;
> +       size_t size;
> +};
> +
> +#define wr32(a, reg, value)    writel((value), ((a)->hw_addr + (reg)))
> +#define rd32(a, reg)           readl((a)->hw_addr + (reg))
> +#define wr64(a, reg, value)    writeq((value), ((a)->hw_addr + (reg)))
> +#define rd64(a, reg)           readq((a)->hw_addr + (reg))
> +
> +#endif /* _IECM_MEM_H_ */
> --
> 2.33.0
>
> _______________________________________________
> Intel-wired-lan mailing list
> Intel-wired-lan at osuosl.org
> https://lists.osuosl.org/mailman/listinfo/intel-wired-lan

^ permalink raw reply	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 01/19] virtchnl: Add new virtchnl2 ops
  2022-01-28  0:09 ` [Intel-wired-lan] [PATCH net-next 01/19] virtchnl: Add new virtchnl2 ops Alan Brady
@ 2022-02-02 22:13   ` Brady, Alan
  0 siblings, 0 replies; 89+ messages in thread
From: Brady, Alan @ 2022-02-02 22:13 UTC (permalink / raw)
  To: intel-wired-lan

> -----Original Message-----
> From: Lobakin, Alexandr <alexandr.lobakin@intel.com>
> Sent: Friday, January 28, 2022 3:48 AM
> To: Brady, Alan <alan.brady@intel.com>
> Cc: Lobakin, Alexandr <alexandr.lobakin@intel.com>; intel-wired-
> lan at lists.osuosl.org; Burra, Phani R <phani.r.burra@intel.com>; Chittim, Madhu
> <madhu.chittim@intel.com>; Linga, Pavan Kumar
> <pavan.kumar.linga@intel.com>
> Subject: Re: [Intel-wired-lan] [PATCH net-next 01/19] virtchnl: Add new virtchnl2
> ops
> 
> From: Alan Brady <alan.brady@intel.com>
> Date: Thu, 27 Jan 2022 16:09:51 -0800
> 
> > This extends the virtchnl interface to add new virtchnl ops and
> > defines needed to implement virtchnl 2.0.
> >
> > Signed-off-by: Phani Burra <phani.r.burra@intel.com>
> > Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
> > Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
> > Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
> > Signed-off-by: Alan Brady <alan.brady@intel.com>
> > ---
> >  MAINTAINERS                           |    1 +
> >  include/linux/avf/virtchnl.h          | 1507 +++++++++++++++++++++++--
> >  include/linux/avf/virtchnl_2.h        | 1243 ++++++++++++++++++++
> >  include/linux/avf/virtchnl_lan_desc.h |  603 ++++++++++
> >  4 files changed, 3237 insertions(+), 117 deletions(-)  create mode
> > 100644 include/linux/avf/virtchnl_2.h  create mode 100644
> > include/linux/avf/virtchnl_lan_desc.h
> >
> > diff --git a/MAINTAINERS b/MAINTAINERS index
> > 0d7883977e9b..5685d64afd76 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -9602,6 +9602,7 @@ F:
> 	Documentation/networking/device_drivers/ethernet/intel/
> >  F:	drivers/net/ethernet/intel/
> >  F:	drivers/net/ethernet/intel/*/
> >  F:	include/linux/avf/virtchnl.h
> > +F:	include/linux/avf/virtchnl_2.h
> >  F:	include/linux/net/intel/iidc.h
> >
> >  INTEL ETHERNET PROTOCOL DRIVER FOR RDMA diff --git
> > a/include/linux/avf/virtchnl.h b/include/linux/avf/virtchnl.h index
> > 2ce27e8e4f19..aee5e2677e1c 100644
> > --- a/include/linux/avf/virtchnl.h
> > +++ b/include/linux/avf/virtchnl.h
> > @@ -14,8 +14,9 @@
> >  #define _VIRTCHNL_H_
> >
> >  /* Description:
> > - * This header file describes the VF-PF communication protocol used
> > - * by the drivers for all devices starting from our 40G product line
> > + * This header file describes the Virtual Function (VF) - Physical
> > + Function
> > + * (PF) communication protocol used by the drivers for all devices
> > + starting
> > + * from our 40G product line
> >   *
> >   * Admin queue buffer usage:
> >   * desc->opcode is always aqc_opc_send_msg_to_pf @@ -29,8 +30,8 @@
> >   * have a maximum of sixteen queues for all of its VSIs.
> >   *
> >   * The PF is required to return a status code in v_retval for all
> > messages
> > - * except RESET_VF, which does not require any response. The return
> > value
> > - * is of status_code type, defined in the shared type.h.
> > + * except RESET_VF, which does not require any response. The returned
> > + value
> > + * is of virtchnl_status_code type, defined here.
> >   *
> >   * In general, VF driver initialization should roughly follow the order of
> >   * these opcodes. The VF driver must first validate the API version
> > of the @@ -45,7 +46,21 @@
> >   * value in current and future projects
> >   */
> >
> > -/* Error Codes */
> > +/* These macros are used to generate compilation errors if a
> > +structure/union
> > + * is not exactly the correct length. It gives a divide by zero error
> > +if the
> > + * structure/union is not of the correct size, otherwise it creates
> > +an enum
> > + * that is never used.
> > + */
> > +#define VIRTCHNL_CHECK_STRUCT_LEN(n, X) enum
> virtchnl_static_assert_enum_##X \
> > +	{ virtchnl_static_assert_##X = (n) / ((sizeof(struct X) == (n)) ? 1
> > +: 0) } #define VIRTCHNL_CHECK_UNION_LEN(n, X) enum
> virtchnl_static_asset_enum_##X \
> > +	{ virtchnl_static_assert_##X = (n) / ((sizeof(union X) == (n)) ? 1 :
> > +0) }
> > +
> > +/* Error Codes
> > + * Note that many older versions of various iAVF drivers convert the
> > +reported
> > + * status code directly into an iavf_status enumeration. For this
> > +reason, it
> > + * is important that the values of these enumerations line up.
> > + */
> >  enum virtchnl_status_code {
> >  	VIRTCHNL_STATUS_SUCCESS				= 0,
> >  	VIRTCHNL_STATUS_ERR_PARAM			= -5,
> > @@ -92,6 +107,9 @@ enum virtchnl_rx_hsplit {
> >  	VIRTCHNL_RX_HSPLIT_SPLIT_SCTP    = 8,
> >  };
> >
> > +enum virtchnl_bw_limit_type {
> > +	VIRTCHNL_BW_SHAPER = 0,
> > +};
> >  /* END GENERIC DEFINES */
> >
> >  /* Opcodes for VF-PF communication. These are placed in the v_opcode
> > field @@ -136,11 +154,14 @@ enum virtchnl_ops {
> >  	VIRTCHNL_OP_DISABLE_CHANNELS = 31,
> >  	VIRTCHNL_OP_ADD_CLOUD_FILTER = 32,
> >  	VIRTCHNL_OP_DEL_CLOUD_FILTER = 33,
> > -	/* opcode 34 - 44 are reserved */
> > +	/* opcode 34 is reserved */
> 
> Here's a gap between 34 and 38. If you decided to comment gaps, it should be
> mentioned as well.
> 

Will fix

> > +	/* opcodes 38, 39, 40, 41, 42 and 43 are reserved */
> > +	VIRTCHNL_OP_GET_SUPPORTED_RXDIDS = 44,
> >  	VIRTCHNL_OP_ADD_RSS_CFG = 45,
> >  	VIRTCHNL_OP_DEL_RSS_CFG = 46,
> >  	VIRTCHNL_OP_ADD_FDIR_FILTER = 47,
> >  	VIRTCHNL_OP_DEL_FDIR_FILTER = 48,
> > +	VIRTCHNL_OP_GET_MAX_RSS_QREGION = 50,
> >  	VIRTCHNL_OP_GET_OFFLOAD_VLAN_V2_CAPS = 51,
> >  	VIRTCHNL_OP_ADD_VLAN_V2 = 52,
> >  	VIRTCHNL_OP_DEL_VLAN_V2 = 53,
> > @@ -148,31 +169,206 @@ enum virtchnl_ops {
> >  	VIRTCHNL_OP_DISABLE_VLAN_STRIPPING_V2 = 55,
> >  	VIRTCHNL_OP_ENABLE_VLAN_INSERTION_V2 = 56,
> >  	VIRTCHNL_OP_DISABLE_VLAN_INSERTION_V2 = 57,
> > +	VIRTCHNL_OP_ENABLE_VLAN_FILTERING_V2 = 58,
> > +	VIRTCHNL_OP_DISABLE_VLAN_FILTERING_V2 = 59,
> > +	VIRTCHNL_OP_1588_PTP_GET_CAPS = 60,
> > +	VIRTCHNL_OP_1588_PTP_GET_TIME = 61,
> > +	VIRTCHNL_OP_1588_PTP_SET_TIME = 62,
> > +	VIRTCHNL_OP_1588_PTP_ADJ_TIME = 63,
> > +	VIRTCHNL_OP_1588_PTP_ADJ_FREQ = 64,
> > +	VIRTCHNL_OP_1588_PTP_TX_TIMESTAMP = 65,
> > +	VIRTCHNL_OP_GET_QOS_CAPS = 66,
> > +	VIRTCHNL_OP_CONFIG_QUEUE_TC_MAP = 67,
> > +	VIRTCHNL_OP_1588_PTP_GET_PIN_CFGS = 68,
> > +	VIRTCHNL_OP_1588_PTP_SET_PIN_CFG = 69,
> > +	VIRTCHNL_OP_1588_PTP_EXT_TIMESTAMP = 70,
> > +	VIRTCHNL_OP_ENABLE_QUEUES_V2 = 107,
> > +	VIRTCHNL_OP_DISABLE_QUEUES_V2 = 108,
> > +	VIRTCHNL_OP_MAP_QUEUE_VECTOR = 111,
> > +	/* New major set of opcodes introduced and so leaving room for
> > +	 * old misc opcodes to be added in future. Also these opcodes may only
> > +	 * be used if both the PF and VF have successfully negotiated the
> > +	 * VIRTCHNL version as 2.0 during VIRTCHNL_OP_VERSION exchange.
> > +	 */
> > +	VIRTCHNL2_OP_GET_CAPS = 500,
> > +	VIRTCHNL2_OP_CREATE_VPORT = 501,
> > +	VIRTCHNL2_OP_DESTROY_VPORT = 502,
> > +	VIRTCHNL2_OP_ENABLE_VPORT = 503,
> > +	VIRTCHNL2_OP_DISABLE_VPORT = 504,
> > +	VIRTCHNL2_OP_CONFIG_TX_QUEUES = 505,
> > +	VIRTCHNL2_OP_CONFIG_RX_QUEUES = 506,
> > +	VIRTCHNL2_OP_ENABLE_QUEUES = 507,
> > +	VIRTCHNL2_OP_DISABLE_QUEUES = 508,
> > +	VIRTCHNL2_OP_ADD_QUEUES = 509,
> > +	VIRTCHNL2_OP_DEL_QUEUES = 510,
> > +	VIRTCHNL2_OP_MAP_QUEUE_VECTOR = 511,
> > +	VIRTCHNL2_OP_UNMAP_QUEUE_VECTOR = 512,
> > +	VIRTCHNL2_OP_GET_RSS_KEY = 513,
> > +	VIRTCHNL2_OP_SET_RSS_KEY = 514,
> > +	VIRTCHNL2_OP_GET_RSS_LUT = 515,
> > +	VIRTCHNL2_OP_SET_RSS_LUT = 516,
> > +	VIRTCHNL2_OP_GET_RSS_HASH = 517,
> > +	VIRTCHNL2_OP_SET_RSS_HASH = 518,
> > +	VIRTCHNL2_OP_SET_SRIOV_VFS = 519,
> > +	VIRTCHNL2_OP_ALLOC_VECTORS = 520,
> > +	VIRTCHNL2_OP_DEALLOC_VECTORS = 521,
> > +	VIRTCHNL2_OP_EVENT = 522,
> > +	VIRTCHNL2_OP_GET_STATS = 523,
> > +	VIRTCHNL2_OP_RESET_VF = 524,
> > +	/* opcode 525 is reserved */
> > +	VIRTCHNL2_OP_GET_PTYPE_INFO = 526,
> > +	/* opcode 527 and 528 are reserved for VIRTCHNL2_OP_GET_PTYPE_ID
> and
> > +	 * VIRTCHNL2_OP_GET_PTYPE_INFO_RAW
> > +	 */
> > +	/* opcodes 529, 530, and 531 are reserved */
> >  	VIRTCHNL_OP_MAX,
> >  };
> >
> > -/* These macros are used to generate compilation errors if a
> > structure/union
> > - * is not exactly the correct length. It gives a divide by zero error
> > if the
> > - * structure/union is not of the correct size, otherwise it creates
> > an enum
> > - * that is never used.
> > - */
> > -#define VIRTCHNL_CHECK_STRUCT_LEN(n, X) enum
> virtchnl_static_assert_enum_##X \
> > -	{ virtchnl_static_assert_##X = (n)/((sizeof(struct X) == (n)) ? 1 : 0) }
> > -#define VIRTCHNL_CHECK_UNION_LEN(n, X) enum
> virtchnl_static_asset_enum_##X \
> > -	{ virtchnl_static_assert_##X = (n)/((sizeof(union X) == (n)) ? 1 : 0) }
> > -
> > -/* Virtual channel message descriptor. This overlays the admin queue
> > - * descriptor. All other data is passed in external buffers.
> > - */
> > -
> > -struct virtchnl_msg {
> > -	u8 pad[8];			 /* AQ flags/opcode/len/retval fields */
> > -	enum virtchnl_ops v_opcode; /* avoid confusion with desc->opcode */
> > -	enum virtchnl_status_code v_retval;  /* ditto for desc->retval */
> > -	u32 vfid;			 /* used by PF when sending to VF */
> > -};
> > +static inline const char *virtchnl_op_str(enum virtchnl_ops v_opcode)
> > +{
> > +	switch (v_opcode) {
> > +	case VIRTCHNL_OP_UNKNOWN:
> > +		return "VIRTCHNL_OP_UNKNOWN";
> > +	case VIRTCHNL_OP_VERSION:
> > +		return "VIRTCHNL_OP_VERSION";
> > +	case VIRTCHNL_OP_RESET_VF:
> > +		return "VIRTCHNL_OP_RESET_VF";
> > +	case VIRTCHNL_OP_GET_VF_RESOURCES:
> > +		return "VIRTCHNL_OP_GET_VF_RESOURCES";
> > +	case VIRTCHNL_OP_CONFIG_TX_QUEUE:
> > +		return "VIRTCHNL_OP_CONFIG_TX_QUEUE";
> > +	case VIRTCHNL_OP_CONFIG_RX_QUEUE:
> > +		return "VIRTCHNL_OP_CONFIG_RX_QUEUE";
> > +	case VIRTCHNL_OP_CONFIG_VSI_QUEUES:
> > +		return "VIRTCHNL_OP_CONFIG_VSI_QUEUES";
> > +	case VIRTCHNL_OP_CONFIG_IRQ_MAP:
> > +		return "VIRTCHNL_OP_CONFIG_IRQ_MAP";
> > +	case VIRTCHNL_OP_ENABLE_QUEUES:
> > +		return "VIRTCHNL_OP_ENABLE_QUEUES";
> > +	case VIRTCHNL_OP_DISABLE_QUEUES:
> > +		return "VIRTCHNL_OP_DISABLE_QUEUES";
> > +	case VIRTCHNL_OP_ADD_ETH_ADDR:
> > +		return "VIRTCHNL_OP_ADD_ETH_ADDR";
> > +	case VIRTCHNL_OP_DEL_ETH_ADDR:
> > +		return "VIRTCHNL_OP_DEL_ETH_ADDR";
> > +	case VIRTCHNL_OP_ADD_VLAN:
> > +		return "VIRTCHNL_OP_ADD_VLAN";
> > +	case VIRTCHNL_OP_DEL_VLAN:
> > +		return "VIRTCHNL_OP_DEL_VLAN";
> > +	case VIRTCHNL_OP_CONFIG_PROMISCUOUS_MODE:
> > +		return "VIRTCHNL_OP_CONFIG_PROMISCUOUS_MODE";
> > +	case VIRTCHNL_OP_GET_STATS:
> > +		return "VIRTCHNL_OP_GET_STATS";
> > +	case VIRTCHNL_OP_RSVD:
> > +		return "VIRTCHNL_OP_RSVD";
> > +	case VIRTCHNL_OP_EVENT:
> > +		return "VIRTCHNL_OP_EVENT";
> > +	case VIRTCHNL_OP_CONFIG_RSS_KEY:
> > +		return "VIRTCHNL_OP_CONFIG_RSS_KEY";
> > +	case VIRTCHNL_OP_CONFIG_RSS_LUT:
> > +		return "VIRTCHNL_OP_CONFIG_RSS_LUT";
> > +	case VIRTCHNL_OP_GET_RSS_HENA_CAPS:
> > +		return "VIRTCHNL_OP_GET_RSS_HENA_CAPS";
> > +	case VIRTCHNL_OP_SET_RSS_HENA:
> > +		return "VIRTCHNL_OP_SET_RSS_HENA";
> > +	case VIRTCHNL_OP_ENABLE_VLAN_STRIPPING:
> > +		return "VIRTCHNL_OP_ENABLE_VLAN_STRIPPING";
> > +	case VIRTCHNL_OP_DISABLE_VLAN_STRIPPING:
> > +		return "VIRTCHNL_OP_DISABLE_VLAN_STRIPPING";
> > +	case VIRTCHNL_OP_REQUEST_QUEUES:
> > +		return "VIRTCHNL_OP_REQUEST_QUEUES";
> > +	case VIRTCHNL_OP_ENABLE_CHANNELS:
> > +		return "VIRTCHNL_OP_ENABLE_CHANNELS";
> > +	case VIRTCHNL_OP_DISABLE_CHANNELS:
> > +		return "VIRTCHNL_OP_DISABLE_CHANNELS";
> > +	case VIRTCHNL_OP_ADD_CLOUD_FILTER:
> > +		return "VIRTCHNL_OP_ADD_CLOUD_FILTER";
> > +	case VIRTCHNL_OP_DEL_CLOUD_FILTER:
> > +		return "VIRTCHNL_OP_DEL_CLOUD_FILTER";
> > +	case VIRTCHNL_OP_GET_SUPPORTED_RXDIDS:
> > +		return "VIRTCHNL_OP_GET_SUPPORTED_RXDIDS";
> > +	case VIRTCHNL_OP_ADD_RSS_CFG:
> > +		return "VIRTCHNL_OP_ADD_RSS_CFG";
> > +	case VIRTCHNL_OP_DEL_RSS_CFG:
> > +		return "VIRTCHNL_OP_DEL_RSS_CFG";
> > +	case VIRTCHNL_OP_ADD_FDIR_FILTER:
> > +		return "VIRTCHNL_OP_ADD_FDIR_FILTER";
> > +	case VIRTCHNL_OP_DEL_FDIR_FILTER:
> > +		return "VIRTCHNL_OP_DEL_FDIR_FILTER";
> > +	case VIRTCHNL_OP_GET_MAX_RSS_QREGION:
> > +		return "VIRTCHNL_OP_GET_MAX_RSS_QREGION";
> > +	case VIRTCHNL_OP_GET_OFFLOAD_VLAN_V2_CAPS:
> > +		return "VIRTCHNL_OP_GET_OFFLOAD_VLAN_V2_CAPS";
> > +	case VIRTCHNL_OP_ADD_VLAN_V2:
> > +		return "VIRTCHNL_OP_ADD_VLAN_V2";
> > +	case VIRTCHNL_OP_DEL_VLAN_V2:
> > +		return "VIRTCHNL_OP_DEL_VLAN_V2";
> > +	case VIRTCHNL_OP_ENABLE_VLAN_STRIPPING_V2:
> > +		return "VIRTCHNL_OP_ENABLE_VLAN_STRIPPING_V2";
> > +	case VIRTCHNL_OP_DISABLE_VLAN_STRIPPING_V2:
> > +		return "VIRTCHNL_OP_DISABLE_VLAN_STRIPPING_V2";
> > +	case VIRTCHNL_OP_ENABLE_VLAN_INSERTION_V2:
> > +		return "VIRTCHNL_OP_ENABLE_VLAN_INSERTION_V2";
> > +	case VIRTCHNL_OP_DISABLE_VLAN_INSERTION_V2:
> > +		return "VIRTCHNL_OP_DISABLE_VLAN_INSERTION_V2";
> > +	case VIRTCHNL_OP_ENABLE_VLAN_FILTERING_V2:
> > +		return "VIRTCHNL_OP_ENABLE_VLAN_FILTERING_V2";
> > +	case VIRTCHNL_OP_DISABLE_VLAN_FILTERING_V2:
> > +		return "VIRTCHNL_OP_DISABLE_VLAN_FILTERING_V2";
> > +	case VIRTCHNL_OP_1588_PTP_GET_CAPS:
> > +		return "VIRTCHNL_OP_1588_PTP_GET_CAPS";
> > +	case VIRTCHNL_OP_1588_PTP_GET_TIME:
> > +		return "VIRTCHNL_OP_1588_PTP_GET_TIME";
> > +	case VIRTCHNL_OP_1588_PTP_SET_TIME:
> > +		return "VIRTCHNL_OP_1588_PTP_SET_TIME";
> > +	case VIRTCHNL_OP_1588_PTP_ADJ_TIME:
> > +		return "VIRTCHNL_OP_1588_PTP_ADJ_TIME";
> > +	case VIRTCHNL_OP_1588_PTP_ADJ_FREQ:
> > +		return "VIRTCHNL_OP_1588_PTP_ADJ_FREQ";
> > +	case VIRTCHNL_OP_1588_PTP_TX_TIMESTAMP:
> > +		return "VIRTCHNL_OP_1588_PTP_TX_TIMESTAMP";
> > +	case VIRTCHNL_OP_1588_PTP_GET_PIN_CFGS:
> > +		return "VIRTCHNL_OP_1588_PTP_GET_PIN_CFGS";
> > +	case VIRTCHNL_OP_1588_PTP_SET_PIN_CFG:
> > +		return "VIRTCHNL_OP_1588_PTP_SET_PIN_CFG";
> > +	case VIRTCHNL_OP_1588_PTP_EXT_TIMESTAMP:
> > +		return "VIRTCHNL_OP_1588_PTP_EXT_TIMESTAMP";
> > +	case VIRTCHNL_OP_ENABLE_QUEUES_V2:
> > +		return "VIRTCHNL_OP_ENABLE_QUEUES_V2";
> > +	case VIRTCHNL_OP_DISABLE_QUEUES_V2:
> > +		return "VIRTCHNL_OP_DISABLE_QUEUES_V2";
> > +	case VIRTCHNL_OP_MAP_QUEUE_VECTOR:
> > +		return "VIRTCHNL_OP_MAP_QUEUE_VECTOR";
> > +	case VIRTCHNL_OP_MAX:
> > +		return "VIRTCHNL_OP_MAX";
> > +	default:
> > +		return "Unsupported (update virtchnl.h)";
> > +	}
> > +}
> 
> This static inline will result in bloated functions and text (code). As it's far from
> hotpath, it should be moved into a C file.
> Moreover, all those could be defined in an array to reduce text size even more
> and compressed via definition.
> 
> #define VIRTCHNL_OP_STR(op)	\
> 	[VIRTCHNL_OP_##op] = "VIRTCHNL_OP_" #op
> 
> static const char * const virtchnl_op_strings[] = {
> 	VIRTCHNL_OP_STR(UNKNOWN),
> 	...
> };
> 
> const char *virtchnl_op_str(enum virtchnl_ops v_opcode) {
> 	/* Will be NULL for not defined codes */
> 	return virtchnl_op_strings[v_opcode] ? :
> 	       "Unsupported (update virtchnl.h)"; }
> 

I'm concerned about adding a C file and the complexity that will add but we will take a look.

> >
> > -VIRTCHNL_CHECK_STRUCT_LEN(20, virtchnl_msg);
> > +static inline const char *virtchnl_stat_str(enum virtchnl_status_code
> > +v_status) {
> > +	switch (v_status) {
> > +	case VIRTCHNL_STATUS_SUCCESS:
> > +		return "VIRTCHNL_STATUS_SUCCESS";
> > +	case VIRTCHNL_STATUS_ERR_PARAM:
> > +		return "VIRTCHNL_STATUS_ERR_PARAM";
> > +	case VIRTCHNL_STATUS_ERR_NO_MEMORY:
> > +		return "VIRTCHNL_STATUS_ERR_NO_MEMORY";
> > +	case VIRTCHNL_STATUS_ERR_OPCODE_MISMATCH:
> > +		return "VIRTCHNL_STATUS_ERR_OPCODE_MISMATCH";
> > +	case VIRTCHNL_STATUS_ERR_CQP_COMPL_ERROR:
> > +		return "VIRTCHNL_STATUS_ERR_CQP_COMPL_ERROR";
> > +	case VIRTCHNL_STATUS_ERR_INVALID_VF_ID:
> > +		return "VIRTCHNL_STATUS_ERR_INVALID_VF_ID";
> > +	case VIRTCHNL_STATUS_ERR_ADMIN_QUEUE_ERROR:
> > +		return "VIRTCHNL_STATUS_ERR_ADMIN_QUEUE_ERROR";
> > +	case VIRTCHNL_STATUS_ERR_NOT_SUPPORTED:
> > +		return "VIRTCHNL_STATUS_ERR_NOT_SUPPORTED";
> > +	default:
> > +		return "Unknown status code (update virtchnl.h)";
> > +	}
> > +}
> 
> Same here.
> 
> >
> >  /* Message descriptions and data structures. */
> >
> 
> --- 8< ---
> 
> > @@ -538,17 +851,17 @@ VIRTCHNL_CHECK_STRUCT_LEN(6,
> virtchnl_vlan_filter_list);
> >   */
> >  enum virtchnl_vlan_support {
> >  	VIRTCHNL_VLAN_UNSUPPORTED =		0,
> > -	VIRTCHNL_VLAN_ETHERTYPE_8100 =		BIT(0),
> > -	VIRTCHNL_VLAN_ETHERTYPE_88A8 =		BIT(1),
> > -	VIRTCHNL_VLAN_ETHERTYPE_9100 =		BIT(2),
> > -	VIRTCHNL_VLAN_TAG_LOCATION_L2TAG1 =	BIT(8),
> > -	VIRTCHNL_VLAN_TAG_LOCATION_L2TAG2 =	BIT(9),
> > -	VIRTCHNL_VLAN_TAG_LOCATION_L2TAG2_2 =	BIT(10),
> > -	VIRTCHNL_VLAN_PRIO =			BIT(24),
> > -	VIRTCHNL_VLAN_FILTER_MASK =		BIT(28),
> > -	VIRTCHNL_VLAN_ETHERTYPE_AND =		BIT(29),
> > -	VIRTCHNL_VLAN_ETHERTYPE_XOR =		BIT(30),
> > -	VIRTCHNL_VLAN_TOGGLE =			BIT(31),
> > +	VIRTCHNL_VLAN_ETHERTYPE_8100 =		0x00000001,
> > +	VIRTCHNL_VLAN_ETHERTYPE_88A8 =		0x00000002,
> > +	VIRTCHNL_VLAN_ETHERTYPE_9100 =		0x00000004,
> > +	VIRTCHNL_VLAN_TAG_LOCATION_L2TAG1 =	0x00000100,
> > +	VIRTCHNL_VLAN_TAG_LOCATION_L2TAG2 =	0x00000200,
> > +	VIRTCHNL_VLAN_TAG_LOCATION_L2TAG2_2 =	0x00000400,
> > +	VIRTCHNL_VLAN_PRIO =			0x01000000,
> > +	VIRTCHNL_VLAN_FILTER_MASK =		0x10000000,
> > +	VIRTCHNL_VLAN_ETHERTYPE_AND =		0x20000000,
> > +	VIRTCHNL_VLAN_ETHERTYPE_XOR =		0x40000000,
> > +	VIRTCHNL_VLAN_TOGGLE =			0x80000000
> 
> Any particular reason for reverting this? Upstream maintainers would most likely
> ask the same.
> 

Unintentional, will fix.

> >  };
> >
> >  /* This structure is used as part of the
> > VIRTCHNL_OP_GET_OFFLOAD_VLAN_V2_CAPS
> 
> --- 8< ---
> 
> > +
> > +static inline const char *virtchnl2_op_str(enum virtchnl_ops
> > +v_opcode) {
> > +	switch (v_opcode) {
> > +	case VIRTCHNL2_OP_GET_CAPS:
> > +		return "VIRTCHNL2_OP_GET_CAPS";
> > +	case VIRTCHNL2_OP_CREATE_VPORT:
> > +		return "VIRTCHNL2_OP_CREATE_VPORT";
> > +	case VIRTCHNL2_OP_DESTROY_VPORT:
> > +		return "VIRTCHNL2_OP_DESTROY_VPORT";
> > +	case VIRTCHNL2_OP_ENABLE_VPORT:
> > +		return "VIRTCHNL2_OP_ENABLE_VPORT";
> > +	case VIRTCHNL2_OP_DISABLE_VPORT:
> > +		return "VIRTCHNL2_OP_DISABLE_VPORT";
> > +	case VIRTCHNL2_OP_CONFIG_TX_QUEUES:
> > +		return "VIRTCHNL2_OP_CONFIG_TX_QUEUES";
> > +	case VIRTCHNL2_OP_CONFIG_RX_QUEUES:
> > +		return "VIRTCHNL2_OP_CONFIG_RX_QUEUES";
> > +	case VIRTCHNL2_OP_ENABLE_QUEUES:
> > +		return "VIRTCHNL2_OP_ENABLE_QUEUES";
> > +	case VIRTCHNL2_OP_DISABLE_QUEUES:
> > +		return "VIRTCHNL2_OP_DISABLE_QUEUES";
> > +	case VIRTCHNL2_OP_ADD_QUEUES:
> > +		return "VIRTCHNL2_OP_ADD_QUEUES";
> > +	case VIRTCHNL2_OP_DEL_QUEUES:
> > +		return "VIRTCHNL2_OP_DEL_QUEUES";
> > +	case VIRTCHNL2_OP_MAP_QUEUE_VECTOR:
> > +		return "VIRTCHNL2_OP_MAP_QUEUE_VECTOR";
> > +	case VIRTCHNL2_OP_UNMAP_QUEUE_VECTOR:
> > +		return "VIRTCHNL2_OP_UNMAP_QUEUE_VECTOR";
> > +	case VIRTCHNL2_OP_GET_RSS_KEY:
> > +		return "VIRTCHNL2_OP_GET_RSS_KEY";
> > +	case VIRTCHNL2_OP_SET_RSS_KEY:
> > +		return "VIRTCHNL2_OP_SET_RSS_KEY";
> > +	case VIRTCHNL2_OP_GET_RSS_LUT:
> > +		return "VIRTCHNL2_OP_GET_RSS_LUT";
> > +	case VIRTCHNL2_OP_SET_RSS_LUT:
> > +		return "VIRTCHNL2_OP_SET_RSS_LUT";
> > +	case VIRTCHNL2_OP_GET_RSS_HASH:
> > +		return "VIRTCHNL2_OP_GET_RSS_HASH";
> > +	case VIRTCHNL2_OP_SET_RSS_HASH:
> > +		return "VIRTCHNL2_OP_SET_RSS_HASH";
> > +	case VIRTCHNL2_OP_SET_SRIOV_VFS:
> > +		return "VIRTCHNL2_OP_SET_SRIOV_VFS";
> > +	case VIRTCHNL2_OP_ALLOC_VECTORS:
> > +		return "VIRTCHNL2_OP_ALLOC_VECTORS";
> > +	case VIRTCHNL2_OP_DEALLOC_VECTORS:
> > +		return "VIRTCHNL2_OP_DEALLOC_VECTORS";
> > +	case VIRTCHNL2_OP_GET_PTYPE_INFO:
> > +		return "VIRTCHNL2_OP_GET_PTYPE_INFO";
> > +	case VIRTCHNL2_OP_GET_STATS:
> > +		return "VIRTCHNL2_OP_GET_STATS";
> > +	case VIRTCHNL2_OP_EVENT:
> > +		return "VIRTCHNL2_OP_EVENT";
> > +	case VIRTCHNL2_OP_RESET_VF:
> > +		return "VIRTCHNL2_OP_RESET_VF";
> > +	default:
> > +		return virtchnl_op_str(v_opcode);
> > +	}
> > +}
> 
> Same here (array + compression). Since v2 ops start from 500, array indexes can
> be shifted by this value.
> 
> > +
> > +/**
> > + * virtchnl2_vc_validate_vf_msg
> > + * @ver: Virtchnl version info
> > + * @v_opcode: Opcode for the message
> > + * @msg: pointer to the msg buffer
> > + * @msglen: msg length
> > + *
> > + * validate msg format against struct for each opcode  */
> 
> --- 8< ---
> 
> > --
> > 2.33.0
> 
> Thanks,
> Al

^ permalink raw reply	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 02/19] iecm: add basic module init and documentation
  2022-01-28 11:56   ` Alexander Lobakin
@ 2022-02-02 22:15     ` Brady, Alan
  0 siblings, 0 replies; 89+ messages in thread
From: Brady, Alan @ 2022-02-02 22:15 UTC (permalink / raw)
  To: intel-wired-lan

> -----Original Message-----
> From: Lobakin, Alexandr <alexandr.lobakin@intel.com>
> Sent: Friday, January 28, 2022 3:56 AM
> To: Brady, Alan <alan.brady@intel.com>
> Cc: Lobakin, Alexandr <alexandr.lobakin@intel.com>; intel-wired-
> lan at lists.osuosl.org; Burra, Phani R <phani.r.burra@intel.com>; Chittim, Madhu
> <madhu.chittim@intel.com>; Linga, Pavan Kumar
> <pavan.kumar.linga@intel.com>
> Subject: Re: [Intel-wired-lan] [PATCH net-next 02/19] iecm: add basic module
> init and documentation
> 
> From: Alan Brady <alan.brady@intel.com>
> Date: Thu, 27 Jan 2022 16:09:52 -0800
> 
> > This adds the basics needed to make a kernel module and documentation
> > needed to use iecm module.
> >
> > Signed-off-by: Alan Brady <alan.brady@intel.com>
> > Signed-off-by: Phani Burra <phani.r.burra@intel.com>
> > Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
> > Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
> > Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
> > ---
> >  .../device_drivers/ethernet/intel/iecm.rst    | 93 +++++++++++++++++++
> >  drivers/net/ethernet/intel/Kconfig            | 15 +++
> >  drivers/net/ethernet/intel/Makefile           |  1 +
> >  drivers/net/ethernet/intel/iecm/Makefile      | 13 +++
> >  drivers/net/ethernet/intel/iecm/iecm_main.c   | 40 ++++++++
> >  drivers/net/ethernet/intel/include/iecm.h     | 10 ++
> >  6 files changed, 172 insertions(+)
> >  create mode 100644
> > Documentation/networking/device_drivers/ethernet/intel/iecm.rst
> >  create mode 100644 drivers/net/ethernet/intel/iecm/Makefile
> >  create mode 100644 drivers/net/ethernet/intel/iecm/iecm_main.c
> >  create mode 100644 drivers/net/ethernet/intel/include/iecm.h
> >
> > diff --git
> > a/Documentation/networking/device_drivers/ethernet/intel/iecm.rst
> > b/Documentation/networking/device_drivers/ethernet/intel/iecm.rst
> > new file mode 100644
> > index 000000000000..5634e3e65c74
> > --- /dev/null
> > +++ b/Documentation/networking/device_drivers/ethernet/intel/iecm.rst
> > @@ -0,0 +1,93 @@
> > +.. SPDX-License-Identifier: GPL-2.0
> > +
> > +========================
> > +Intel Ethernet Common Module
> > +========================
> > +
> > +The Intel Ethernet Common Module is meant to serve as an abstraction
> > +layer between device specific implementation details and common net
> > +device driver flows. This library provides several function hooks
> > +which allow a device driver to specify register addresses, control
> > +queue communications, and other device specific functionality.  Some
> > +functions are required to be implemented while others have a default
> > +implementation that is used when none is supplied by the device
> > +driver.  Doing this, a device driver can be written to take advantage
> > +of existing code while also giving the flexibility to implement device specific
> features.
> > +
> > +The common use case for this library is for a network device driver
> > +that wants specify its own device specific details but also leverage
> > +the more common code flows found in network device drivers.
> > +
> > +Sections in this document:
> > +	Entry Point
> > +	Exit Point
> > +	Register Operations API
> > +	Virtchnl Operations API
> > +
> > +Entry Point
> > +~~~~~~~~~~~
> > +The primary entry point to the library is the iecm_probe function.
> > +Prior to calling this, device drivers must have allocated an
> > +iecm_adapter struct and initialized it with the required API
> > +functions.  The adapter struct, along with the pci_dev struct and the
> > +pci_device_id struct, is provided to iecm_probe which finalizes device
> initialization and prepares the device for open.
> > +
> > +The iecm_dev_ops struct within the iecm_adapter struct is the primary
> > +vehicle for passing information from device drivers to the common
> > +module.  A dependent module must define and assign a reg_ops_init
> > +function which will assign the respective function pointers to
> > +initialize register values (see iecm_reg_ops struct).  These are
> > +required to be provided by the dependent device driver as no suitable default
> can be assumed for register addresses.
> > +
> > +The vc_ops_init function pointer and the related iecm_virtchnl_ops
> > +struct are optional and should only be necessary for device drivers
> > +which use a different method/timing for communicating across a
> > +mailbox to the hardware.  Within iecm is a default interface provided
> > +in the case where one is not provided by the device driver.
> > +
> > +Exit Point
> > +~~~~~~~~~~
> > +When the device driver is being prepared to be removed through the
> > +pci_driver remove callback, it is required for the device driver to
> > +call iecm_remove with the pci_dev struct provided.  This is required
> > +to ensure all resources are properly freed and returned to the operating
> system.
> > +
> > +Register Operations API
> > +~~~~~~~~~~~~~~~~~~~~~~~
> > +iecm_reg_ops contains three different function pointers relating to
> > +initializing registers for the specific net device using the library.
> > +
> > +ctlq_reg_init relates specifically to setting up registers related to
> > +control queue/mailbox communications.  Registers that should be
> > +defined include: head, tail, len, bah, bal, len_mask, len_ena_mask, and
> head_mask.
> > +
> > +vportq_reg_init relates to setting up queue registers.  The tail
> > +registers to be assigned to the iecm_queue struct for each RX/TX queue.
> > +
> > +intr_reg_init relates to any registers needed to setup interrupts.
> > +These include registers needed to enable the interrupt and change ITR
> settings.
> > +
> > +If the initialization function finds that one or more required
> > +function pointers were not provided, an error will be issued and the
> > +device will be inoperable.
> > +
> > +
> > +Virtchnl Operations API
> > +~~~~~~~~~~~~~~~~~~~~~~~
> > +The virtchnl is a conduit between driver and hardware that allows
> > +device drivers to send and receive control messages to/from hardware.
> > +This is optional to be specified as there is a general interface that
> > +can be assumed when using this library.  However, if a device
> > +deviates in some way to communicate across the mailbox correctly,
> > +this interface is provided to allow that.
> > +
> > +If vc_ops_init is set in the dev_ops field of the iecm_adapter
> > +struct, then it is assumed the device driver is using providing it's
> > +own interface to do virtchnl communications.  While providing
> > +vc_ops_init is optional, if it is provided, it is required that the
> > +device driver provide function pointers for those functions in
> > +vc_ops, with exception for the enable_vport, disable_vport, and
> destroy_vport functions which may not be required for all devices.
> > +
> > +If the initialization function finds that vc_ops_init was defined but
> > +one or more required function pointers were not provided, an error
> > +will be issued and the device will be inoperable.
> > diff --git a/drivers/net/ethernet/intel/Kconfig
> > b/drivers/net/ethernet/intel/Kconfig
> > index 3facb55b7161..754dc7677ad5 100644
> > --- a/drivers/net/ethernet/intel/Kconfig
> > +++ b/drivers/net/ethernet/intel/Kconfig
> > @@ -372,4 +372,19 @@ config IGC
> >  	  To compile this driver as a module, choose M here. The module
> >  	  will be called igc.
> >
> > +config IECM
> > +	tristate "Intel(R) Ethernet Common Module Support"
> > +	default n
> > +	depends on PCI_MSI
> > +	select DIMLIB
> > +	help
> > +      This supplies needed functions to device specific device
> > +drivers
> 
> One Tab + two spaces instead of 6 spaces. And one Tab is 8 cols in the kernel.
> 

Will fix

> > +      implementing common module.
> > +
> > +	  More specific information on configuring the driver is in
> > +
> <file:Documentation/networking/device_drivers/ethernet/intel/iecm.rst>.
> > +
> > +      To compile this as a module, choose M here. The module will be called
> > +      iecm.
> > +
> >  endif # NET_VENDOR_INTEL
> > diff --git a/drivers/net/ethernet/intel/Makefile
> > b/drivers/net/ethernet/intel/Makefile
> > index 3075290063f6..c9eba9cc5087 100644
> > --- a/drivers/net/ethernet/intel/Makefile
> > +++ b/drivers/net/ethernet/intel/Makefile
> > @@ -16,3 +16,4 @@ obj-$(CONFIG_IXGB) += ixgb/
> >  obj-$(CONFIG_IAVF) += iavf/
> >  obj-$(CONFIG_FM10K) += fm10k/
> >  obj-$(CONFIG_ICE) += ice/
> > +obj-$(CONFIG_IECM) += iecm/
> > diff --git a/drivers/net/ethernet/intel/iecm/Makefile
> > b/drivers/net/ethernet/intel/iecm/Makefile
> > new file mode 100644
> > index 000000000000..d2d087ac71e9
> > --- /dev/null
> > +++ b/drivers/net/ethernet/intel/iecm/Makefile
> > @@ -0,0 +1,13 @@
> > +# SPDX-License-Identifier: GPL-2.0-only # Copyright (C) 2019 Intel
> > +Corporation
> > +
> > +#
> > +# Makefile for the Intel(R) Data Plane Function Linux Driver #
> > +
> > +obj-$(CONFIG_IECM) += iecm.o
> > +
> > +ccflags-y += -I$(srctree)/drivers/net/ethernet/intel/include
> 
> Common includes are usually being added to include/linux/. Files outside include
> directories are usually considered "private", i.e.
> used only inside a particular folder.
> 

Will fix.

> > +
> > +iecm-y := \
> > +	iecm_main.o
> > diff --git a/drivers/net/ethernet/intel/iecm/iecm_main.c
> > b/drivers/net/ethernet/intel/iecm/iecm_main.c
> > new file mode 100644
> > index 000000000000..7c09403c6918
> > --- /dev/null
> > +++ b/drivers/net/ethernet/intel/iecm/iecm_main.c
> > @@ -0,0 +1,40 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +/* Copyright (C) 2019 Intel Corporation */
> > +
> > +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> > +
> > +#include "iecm.h"
> 
> Quotes are used for local includes. For includes from the search directories
> please use <>.
> 
> > +
> > +#define DRV_SUMMARY	"Intel(R) Ethernet Common Module"
> > +static const char iecm_driver_string[] = DRV_SUMMARY; static const
> > +char iecm_copyright[] = "Copyright (c) 2020, Intel Corporation.";
> > +
> > +MODULE_DESCRIPTION(DRV_SUMMARY);
> > +MODULE_LICENSE("GPL v2");
> 
> "GPL v2" is a deprecated identifier and is not recommended for new modules.
> Just "GPL" is enough and means exactly the same.
> 

Will fix

> > +
> > +/**
> > + * iecm_module_init - Driver registration routine
> > + *
> > + * iecm_module_init is the first routine called when the driver is
> > + * loaded. All it does is register with the PCI subsystem.
> > + */
> > +static int __init iecm_module_init(void) {
> > +	pr_info("%s - version %d\n", iecm_driver_string,
> LINUX_VERSION_CODE);
> > +	pr_info("%s\n", iecm_copyright);
> > +
> > +	return 0;
> > +}
> > +module_init(iecm_module_init);
> > +
> > +/**
> > + * iecm_module_exit - Driver exit cleanup routine
> > + *
> > + * iecm_module_exit is called just before the driver is removed
> > + * from memory.
> > + */
> > +static void __exit iecm_module_exit(void) {
> > +	pr_info("module unloaded\n");
> > +}
> > +module_exit(iecm_module_exit);
> > diff --git a/drivers/net/ethernet/intel/include/iecm.h
> > b/drivers/net/ethernet/intel/include/iecm.h
> > new file mode 100644
> > index 000000000000..f66f0d7db8e7
> > --- /dev/null
> > +++ b/drivers/net/ethernet/intel/include/iecm.h
> > @@ -0,0 +1,10 @@
> > +/* SPDX-License-Identifier: GPL-2.0-only */
> > +/* Copyright (C) 2019 Intel Corporation */
> > +
> > +#ifndef _IECM_H_
> > +#define _IECM_H_
> > +
> > +#include <linux/etherdevice.h>
> > +#include <linux/version.h>
> > +
> > +#endif /* !_IECM_H_ */
> > --
> > 2.33.0
> 
> Thanks,
> Al

^ permalink raw reply	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 04/19] iecm: add api_init and controlq init
  2022-01-28 12:09   ` Alexander Lobakin
@ 2022-02-02 22:16     ` Brady, Alan
  0 siblings, 0 replies; 89+ messages in thread
From: Brady, Alan @ 2022-02-02 22:16 UTC (permalink / raw)
  To: intel-wired-lan

> -----Original Message-----
> From: Lobakin, Alexandr <alexandr.lobakin@intel.com>
> Sent: Friday, January 28, 2022 4:09 AM
> To: Brady, Alan <alan.brady@intel.com>
> Cc: Lobakin, Alexandr <alexandr.lobakin@intel.com>; intel-wired-
> lan at lists.osuosl.org; Burra, Phani R <phani.r.burra@intel.com>; Chittim, Madhu
> <madhu.chittim@intel.com>; Linga, Pavan Kumar
> <pavan.kumar.linga@intel.com>
> Subject: Re: [Intel-wired-lan] [PATCH net-next 04/19] iecm: add api_init and
> controlq init
> 
> From: Alan Brady <alan.brady@intel.com>
> Date: Thu, 27 Jan 2022 16:09:54 -0800
> 
> > Initializing device registers is offloaded into function pointers
> > given to iecm from the dependent device driver for a given device, as
> > offsets can vary wildly. This also adds everything needed to setup and
> > use a controlq which uses some of those registers.
> >
> > At the end of probe we kicked off a hard reset and this implements
> > what's needed to handle that reset and continue init.
> >
> > Signed-off-by: Phani Burra <phani.r.burra@intel.com>
> > Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
> > Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
> > Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
> > Signed-off-by: Alan Brady <alan.brady@intel.com>
> > ---
> >  drivers/net/ethernet/intel/iecm/Makefile      |   3 +
> >  .../net/ethernet/intel/iecm/iecm_controlq.c   | 649 ++++++++++++++++++
> >  .../ethernet/intel/iecm/iecm_controlq_setup.c | 175 +++++
> >  drivers/net/ethernet/intel/iecm/iecm_lib.c    | 191 +++++-
> >  .../net/ethernet/intel/iecm/iecm_virtchnl.c   | 172 +++++
> >  drivers/net/ethernet/intel/include/iecm.h     |  52 ++
> >  .../ethernet/intel/include/iecm_controlq.h    | 117 ++++
> >  .../intel/include/iecm_controlq_api.h         | 185 +++++
> >  drivers/net/ethernet/intel/include/iecm_mem.h |  20 +
> >  9 files changed, 1563 insertions(+), 1 deletion(-)  create mode
> > 100644 drivers/net/ethernet/intel/iecm/iecm_controlq.c
> >  create mode 100644
> > drivers/net/ethernet/intel/iecm/iecm_controlq_setup.c
> >  create mode 100644 drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> >  create mode 100644 drivers/net/ethernet/intel/include/iecm_controlq.h
> >  create mode 100644
> > drivers/net/ethernet/intel/include/iecm_controlq_api.h
> >  create mode 100644 drivers/net/ethernet/intel/include/iecm_mem.h
> >
> 
> --- 8< ---
> 
> > diff --git a/drivers/net/ethernet/intel/include/iecm_controlq_api.h
> > b/drivers/net/ethernet/intel/include/iecm_controlq_api.h
> > new file mode 100644
> > index 000000000000..5f624f005d33
> > --- /dev/null
> > +++ b/drivers/net/ethernet/intel/include/iecm_controlq_api.h
> > @@ -0,0 +1,185 @@
> > +/* SPDX-License-Identifier: GPL-2.0-only */
> > +/* Copyright (c) 2020, Intel Corporation. */
> > +
> > +#ifndef _IECM_CONTROLQ_API_H_
> > +#define _IECM_CONTROLQ_API_H_
> > +
> > +#include "iecm_mem.h"
> > +
> > +struct iecm_hw;
> > +
> > +/* Used for queue init, response and events */ enum iecm_ctlq_type {
> > +	IECM_CTLQ_TYPE_MAILBOX_TX	= 0,
> > +	IECM_CTLQ_TYPE_MAILBOX_RX	= 1,
> > +	IECM_CTLQ_TYPE_CONFIG_TX	= 2,
> > +	IECM_CTLQ_TYPE_CONFIG_RX	= 3,
> > +	IECM_CTLQ_TYPE_EVENT_RX		= 4,
> > +	IECM_CTLQ_TYPE_RDMA_TX		= 5,
> > +	IECM_CTLQ_TYPE_RDMA_RX		= 6,
> > +	IECM_CTLQ_TYPE_RDMA_COMPL	= 7
> > +};
> > +
> > +/* Generic Control Queue Structures */ struct iecm_ctlq_reg {
> > +	/* used for queue tracking */
> > +	u32 head;
> > +	u32 tail;
> > +	/* Below applies only to default mb (if present) */
> > +	u32 len;
> > +	u32 bah;
> > +	u32 bal;
> > +	u32 len_mask;
> > +	u32 len_ena_mask;
> > +	u32 head_mask;
> > +};
> > +
> > +/* Generic queue msg structure */
> > +struct iecm_ctlq_msg {
> > +	u16 vmvf_type; /* represents the source of the message on recv */
> > +#define IECM_VMVF_TYPE_VF 0 #define IECM_VMVF_TYPE_VM 1 #define
> > +IECM_VMVF_TYPE_PF 2
> > +	u16 opcode;
> > +	u16 data_len;	/* data_len = 0 when no payload is attached */
> > +	union {
> > +		u16 func_id;	/* when sending a message */
> > +		u16 status;	/* when receiving a message */
> > +	};
> > +	union {
> > +		struct {
> > +			u32 chnl_retval;
> > +			u32 chnl_opcode;
> > +		} mbx;
> > +	} cookie;
> 
> One field union? If it will be expanded later, please unionize it only then.
> 

Will fix

> > +	union {
> > +#define IECM_DIRECT_CTX_SIZE	16
> > +#define IECM_INDIRECT_CTX_SIZE	8
> > +		/* 16 bytes of context can be provided or 8 bytes of context
> > +		 * plus the address of a DMA buffer
> > +		 */
> > +		u8 direct[IECM_DIRECT_CTX_SIZE];
> > +		struct {
> > +			u8 context[IECM_INDIRECT_CTX_SIZE];
> > +			struct iecm_dma_mem *payload;
> > +		} indirect;
> > +	} ctx;
> > +};
> > +
> > +/* Generic queue info structures */
> > +/* MB, CONFIG and EVENT q do not have extended info */ struct
> > +iecm_ctlq_create_info {
> > +	enum iecm_ctlq_type type;
> > +	int id; /* absolute queue offset passed as input
> > +		 * -1 for default mailbox if present
> > +		 */
> > +	u16 len; /* Queue length passed as input */
> > +	u16 buf_size; /* buffer size passed as input */
> > +	u64 base_address; /* output, HPA of the Queue start  */
> > +	struct iecm_ctlq_reg reg; /* registers accessed by ctlqs */
> > +
> > +	int ext_info_size;
> > +	void *ext_info; /* Specific to q type */ };
> > +
> > +/* Control Queue information */
> > +struct iecm_ctlq_info {
> > +	struct list_head cq_list;
> > +
> > +	enum iecm_ctlq_type cq_type;
> > +	int q_id;
> > +	/* control queue lock */
> > +	struct mutex cq_lock;
> > +
> > +	/* used for interrupt processing */
> > +	u16 next_to_use;
> > +	u16 next_to_clean;
> > +	u16 next_to_post;		/* starting descriptor to post buffers
> > +					 * to after recev
> > +					 */
> > +
> > +	struct iecm_dma_mem desc_ring;	/* descriptor ring memory
> > +					 * iecm_dma_mem is defined in
> OSdep.h
> > +					 */
> > +	union {
> > +		struct iecm_dma_mem **rx_buff;
> > +		struct iecm_ctlq_msg **tx_msg;
> > +	} bi;
> > +
> > +	u16 buf_size;			/* queue buffer size */
> > +	u16 ring_size;			/* Number of descriptors */
> > +	struct iecm_ctlq_reg reg;	/* registers accessed by ctlqs */
> > +};
> > +
> > +/* PF/VF mailbox commands */
> > +enum iecm_mbx_opc {
> > +	/* iecm_mbq_opc_send_msg_to_pf:
> > +	 *	usage: used by PF or VF to send a message to its CPF
> > +	 *	target: RX queue and function ID of parent PF taken from HW
> > +	 */
> > +	iecm_mbq_opc_send_msg_to_pf		= 0x0801,
> > +
> > +	/* iecm_mbq_opc_send_msg_to_vf:
> > +	 *	usage: used by PF to send message to a VF
> > +	 *	target: VF control queue ID must be specified in descriptor
> > +	 */
> > +	iecm_mbq_opc_send_msg_to_vf		= 0x0802,
> > +
> > +	/* iecm_mbq_opc_send_msg_to_peer_pf:
> > +	 *	usage: used by any function to send message to any peer PF
> > +	 *	target: RX queue and host of parent PF taken from HW
> > +	 */
> > +	iecm_mbq_opc_send_msg_to_peer_pf	= 0x0803,
> > +
> > +	/* iecm_mbq_opc_send_msg_to_peer_drv:
> > +	 *	usage: used by any function to send message to any peer driver
> > +	 *	target: RX queue and target host must be specific in descriptor
> > +	 */
> > +	iecm_mbq_opc_send_msg_to_peer_drv	= 0x0804,
> > +};
> > +
> > +/* API support for control queue management */
> > +
> > +/* Will init all required q including default mb.  "q_info" is an
> > +array of
> > + * create_info structs equal to the number of control queues to be created.
> > + */
> > +int iecm_ctlq_init(struct iecm_hw *hw, u8 num_q,
> > +		   struct iecm_ctlq_create_info *q_info);
> > +
> > +/* Allocate and initialize a single control queue, which will be
> > +added to the
> > + * control queue list; returns a handle to the created control queue
> > +*/ int iecm_ctlq_add(struct iecm_hw *hw,
> > +		  struct iecm_ctlq_create_info *qinfo,
> > +		  struct iecm_ctlq_info **cq);
> > +
> > +/* Deinitialize and deallocate a single control queue */ void
> > +iecm_ctlq_remove(struct iecm_hw *hw,
> > +		      struct iecm_ctlq_info *cq);
> > +
> > +/* Sends messages to HW and will also free the buffer*/ int
> > +iecm_ctlq_send(struct iecm_hw *hw,
> > +		   struct iecm_ctlq_info *cq,
> > +		   u16 num_q_msg,
> > +		   struct iecm_ctlq_msg q_msg[]);
> > +
> > +/* Receives messages and called by interrupt handler/polling
> > + * initiated by app/process. Also caller is supposed to free the
> > +buffers  */ int iecm_ctlq_recv(struct iecm_ctlq_info *cq, u16
> > +*num_q_msg,
> > +		   struct iecm_ctlq_msg *q_msg);
> > +
> > +/* Reclaims send descriptors on HW write back */ int
> > +iecm_ctlq_clean_sq(struct iecm_ctlq_info *cq, u16 *clean_count,
> > +		       struct iecm_ctlq_msg *msg_status[]);
> > +
> > +/* Indicate RX buffers are done being processed */ int
> > +iecm_ctlq_post_rx_buffs(struct iecm_hw *hw,
> > +			    struct iecm_ctlq_info *cq,
> > +			    u16 *buff_count,
> > +			    struct iecm_dma_mem **buffs);
> > +
> > +/* Will destroy all q including the default mb */ int
> > +iecm_ctlq_deinit(struct iecm_hw *hw);
> > +
> > +#endif /* _IECM_CONTROLQ_API_H_ */
> 
> --- 8< ---
> 
> > --
> > 2.33.0
> 
> Thanks,
> Al

^ permalink raw reply	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 05/19] iecm: add vport alloc and virtchnl messages
  2022-01-28 12:32   ` Alexander Lobakin
@ 2022-02-02 22:21     ` Brady, Alan
  2022-02-03 13:23       ` Alexander Lobakin
  0 siblings, 1 reply; 89+ messages in thread
From: Brady, Alan @ 2022-02-02 22:21 UTC (permalink / raw)
  To: intel-wired-lan

> -----Original Message-----
> From: Lobakin, Alexandr <alexandr.lobakin@intel.com>
> Sent: Friday, January 28, 2022 4:33 AM
> To: Brady, Alan <alan.brady@intel.com>
> Cc: Lobakin, Alexandr <alexandr.lobakin@intel.com>; intel-wired-
> lan at lists.osuosl.org; Burra, Phani R <phani.r.burra@intel.com>; Chittim, Madhu
> <madhu.chittim@intel.com>; Linga, Pavan Kumar
> <pavan.kumar.linga@intel.com>
> Subject: Re: [Intel-wired-lan] [PATCH net-next 05/19] iecm: add vport alloc and
> virtchnl messages
> 
> From: Alan Brady <alan.brady@intel.com>
> Date: Thu, 27 Jan 2022 16:09:55 -0800
> 
> > After handling hard reset, we end up in init task. This starts by
> > allocating and setting up a vport. To do that we need implement virtchnl
> > messages.
> >
> > The virtchnl messages are also offloaded into function pointers so that a
> > device driver may override them. Here a default implementation is provided
> > for devices using virtchnl 2.0 but there exists the flexibility add
> > virtchnl 1.1 support through function pointers.
> >
> > Signed-off-by: Phani Burra <phani.r.burra@intel.com>
> > Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
> > Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
> > Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
> > Signed-off-by: Alan Brady <alan.brady@intel.com>
> > ---
> >  drivers/net/ethernet/intel/iecm/Makefile      |    4 +-
> >  drivers/net/ethernet/intel/iecm/iecm_lib.c    |  167 ++-
> >  drivers/net/ethernet/intel/iecm/iecm_txrx.c   |   22 +
> >  .../net/ethernet/intel/iecm/iecm_virtchnl.c   | 1299 +++++++++++++++++
> >  drivers/net/ethernet/intel/include/iecm.h     |  316 +++-
> >  .../net/ethernet/intel/include/iecm_txrx.h    |   94 ++
> >  6 files changed, 1898 insertions(+), 4 deletions(-)
> >  create mode 100644 drivers/net/ethernet/intel/iecm/iecm_txrx.c
> >
> > diff --git a/drivers/net/ethernet/intel/iecm/Makefile
> b/drivers/net/ethernet/intel/iecm/Makefile
> > index db8fecb075a6..fcb49402334f 100644
> > --- a/drivers/net/ethernet/intel/iecm/Makefile
> > +++ b/drivers/net/ethernet/intel/iecm/Makefile
> > @@ -7,11 +7,13 @@
> >
> >  obj-$(CONFIG_IECM) += iecm.o
> >
> > -ccflags-y += -I$(srctree)/drivers/net/ethernet/intel/include
> > +ccflags-y += -I$(srctree)/drivers/net/ethernet/intel/include \
> > +	     -I$(srctree)/include/linux/avf
> >
> >  iecm-y := \
> >  	iecm_lib.o \
> >  	iecm_virtchnl.o \
> > +	iecm_txrx.o \
> >  	iecm_controlq.o \
> >  	iecm_controlq_setup.o \
> >  	iecm_main.o
> > diff --git a/drivers/net/ethernet/intel/iecm/iecm_lib.c
> b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > index 64cdbce2c842..e2e523f0700e 100644
> > --- a/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > +++ b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > @@ -5,6 +5,11 @@
> >
> >  #include "iecm.h"
> >
> > +const char * const iecm_vport_vc_state_str[] = {
> > +	IECM_FOREACH_VPORT_VC_STATE(IECM_GEN_STRING)
> > +};
> > +EXPORT_SYMBOL(iecm_vport_vc_state_str);
> > +
> >  /**
> >   * iecm_cfg_hw - Initialize HW struct
> >   * @adapter: adapter to setup hw struct for
> > @@ -24,6 +29,113 @@ static int iecm_cfg_hw(struct iecm_adapter *adapter)
> >  	return 0;
> >  }
> >
> > +/**
> > + * iecm_get_free_slot - get the next non-NULL location index in array
> > + * @array: array to search
> > + * @size: size of the array
> > + * @curr: last known occupied index to be used as a search hint
> > + *
> > + * void * is being used to keep the functionality generic. This lets us use this
> > + * function on any array of pointers.
> > + */
> > +static int iecm_get_free_slot(void *array, int size, int curr)
> > +{
> > +	int **tmp_array = (int **)array;
> > +	int next;
> > +
> > +	if (curr < (size - 1) && !tmp_array[curr + 1]) {
> 
> Redundant braces around `size - 1`.

Will fix

> 
> > +		next = curr + 1;
> > +	} else {
> > +		int i = 0;
> > +
> > +		while ((i < size) && (tmp_array[i]))
> > +			i++;
> > +		if (i == size)
> > +			next = IECM_NO_FREE_SLOT;
> > +		else
> > +			next = i;
> > +	}
> 
> One indent level is redundant here. First condition is an oneliner:
> 
> 	if (curr < (size - 1) && !tmp_array[curr + 1]) {
> 		return curr + 1;
> 
> 	while ((i < size) && (tmp_array[i])) {
> 		...
> 
> > +	return next;
> > +}
> > +
> > +/**
> > + * iecm_vport_rel - Delete a vport and free its resources
> > + * @vport: the vport being removed
> > + */
> > +static void iecm_vport_rel(struct iecm_vport *vport)
> > +{
> > +	mutex_destroy(&vport->stop_mutex);
> > +	kfree(vport);
> > +}
> > +
> > +/**
> > + * iecm_vport_rel_all - Delete all vports
> > + * @adapter: adapter from which all vports are being removed
> > + */
> > +static void iecm_vport_rel_all(struct iecm_adapter *adapter)
> > +{
> > +	int i;
> > +
> > +	if (!adapter->vports)
> > +		return;
> > +
> > +	for (i = 0; i < adapter->num_alloc_vport; i++) {
> > +		if (!adapter->vports[i])
> > +			continue;
> > +
> > +		iecm_vport_rel(adapter->vports[i]);
> > +		adapter->vports[i] = NULL;
> > +		adapter->next_vport = 0;
> > +	}
> > +	adapter->num_alloc_vport = 0;
> > +}
> > +
> > +/**
> > + * iecm_vport_alloc - Allocates the next available struct vport in the adapter
> > + * @adapter: board private structure
> > + * @vport_id: vport identifier
> > + *
> > + * returns a pointer to a vport on success, NULL on failure.
> > + */
> > +static struct iecm_vport *
> > +iecm_vport_alloc(struct iecm_adapter *adapter, int vport_id)
> > +{
> > +	struct iecm_vport *vport = NULL;
> > +
> > +	if (adapter->next_vport == IECM_NO_FREE_SLOT)
> > +		return vport;
> > +
> > +	/* Need to protect the allocation of the vports at the adapter level */
> > +	mutex_lock(&adapter->sw_mutex);
> > +
> > +	vport = kzalloc(sizeof(*vport), GFP_KERNEL);
> > +	if (!vport)
> > +		goto unlock_adapter;
> > +
> > +	vport->adapter = adapter;
> > +	vport->idx = adapter->next_vport;
> > +	vport->compln_clean_budget = IECM_TX_COMPLQ_CLEAN_BUDGET;
> > +	adapter->num_alloc_vport++;
> > +
> > +	/* Setup default MSIX irq handler for the vport */
> > +	vport->irq_q_handler = iecm_vport_intr_clean_queues;
> > +	vport->q_vector_base = IECM_NONQ_VEC;
> > +
> > +	mutex_init(&vport->stop_mutex);
> > +
> > +	/* fill vport slot in the adapter struct */
> > +	adapter->vports[adapter->next_vport] = vport;
> > +
> > +	/* prepare adapter->next_vport for next use */
> > +	adapter->next_vport = iecm_get_free_slot(adapter->vports,
> > +						 adapter->num_alloc_vport,
> > +						 adapter->next_vport);
> > +
> > +unlock_adapter:
> > +	mutex_unlock(&adapter->sw_mutex);
> > +	return vport;
> > +}
> > +
> >  /**
> >   * iecm_statistics_task - Delayed task to get statistics over mailbox
> >   * @work: work_struct handle to our data
> > @@ -55,7 +167,25 @@ static void iecm_service_task(struct work_struct
> *work)
> >   */
> >  static void iecm_init_task(struct work_struct *work)
> >  {
> > -	/* stub */
> > +	struct iecm_adapter *adapter = container_of(work,
> > +						    struct iecm_adapter,
> > +						    init_task.work);
> > +	struct iecm_vport *vport;
> > +	struct pci_dev *pdev;
> > +	int vport_id, err;
> > +
> > +	err = adapter->dev_ops.vc_ops.core_init(adapter, &vport_id);
> > +	if (err)
> > +		return;
> > +
> > +	pdev = adapter->pdev;
> > +	vport = iecm_vport_alloc(adapter, vport_id);
> > +	if (!vport) {
> > +		err = -EFAULT;
> > +		dev_err(&pdev->dev, "failed to allocate vport: %d\n",
> > +			err);
> > +		return;
> > +	}
> >  }
> >
> >  /**
> > @@ -81,6 +211,31 @@ static int iecm_api_init(struct iecm_adapter *adapter)
> >  		return -EINVAL;
> >  	}
> >
> > +	if (adapter->dev_ops.vc_ops_init) {
> > +		struct iecm_virtchnl_ops *vc_ops;
> > +
> > +		adapter->dev_ops.vc_ops_init(adapter);
> > +		vc_ops = &adapter->dev_ops.vc_ops;
> > +		if (!(vc_ops->core_init &&
> > +		      vc_ops->vport_init &&
> > +		      vc_ops->vport_queue_ids_init &&
> > +		      vc_ops->get_caps &&
> > +		      vc_ops->config_queues &&
> > +		      vc_ops->enable_queues &&
> > +		      vc_ops->disable_queues &&
> > +		      vc_ops->irq_map_unmap &&
> > +		      vc_ops->get_set_rss_lut &&
> > +		      vc_ops->get_set_rss_hash &&
> > +		      vc_ops->adjust_qs &&
> > +		      vc_ops->get_ptype &&
> > +		      vc_ops->init_max_queues)) {
> 
> if (!op1 ||
>     !op2 ||
>     !opn) would look more natural and more readable here.
> 

I'm not sure I understand this comment.  Adding an extra symbol for '!' seems worse.

> > +			dev_err(&pdev->dev, "Invalid device, missing one or
> more virtchnl functions\n");
> > +			return -EINVAL;
> > +		}
> > +	} else {
> > +		iecm_vc_ops_init(adapter);
> > +	}
> 
> 'else' path is a two-liner allowing to save one indent level:
> 
> 	if (!adapter->dev_ops.vc_ops_init) {
> 		iecm_vc_ops_init(adapter);
> 		return 0;
> 	}
> 
> 	adapter->dev_ops.vc_ops_init(adapter);
> 	vc_ops = &adapter->dev_ops.vc_ops;
> 	if (!(vc_ops->core_init &&
> 	...
> 
> > +
> >  	return 0;
> >  }
> >
> > @@ -93,7 +248,15 @@ static int iecm_api_init(struct iecm_adapter *adapter)
> >   */
> >  static void iecm_deinit_task(struct iecm_adapter *adapter)
> >  {
> > -	/* stub */
> > +	set_bit(__IECM_REL_RES_IN_PROG, adapter->flags);
> > +	/* Wait until the init_task is done else this thread might release
> > +	 * the resources first and the other thread might end up in a bad state
> > +	 */
> > +	cancel_delayed_work_sync(&adapter->init_task);
> > +	iecm_vport_rel_all(adapter);
> > +
> > +	cancel_delayed_work_sync(&adapter->serv_task);
> > +	cancel_delayed_work_sync(&adapter->stats_task);
> >  }
> >
> >  /**
> > diff --git a/drivers/net/ethernet/intel/iecm/iecm_txrx.c
> b/drivers/net/ethernet/intel/iecm/iecm_txrx.c
> > new file mode 100644
> > index 000000000000..2f5c16a28266
> > --- /dev/null
> > +++ b/drivers/net/ethernet/intel/iecm/iecm_txrx.c
> > @@ -0,0 +1,22 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +/* Copyright (C) 2019 Intel Corporation */
> > +
> > +#include "iecm.h"
> > +
> > +/**
> > + * iecm_vport_intr_clean_queues - MSIX mode Interrupt Handler
> > + * @irq: interrupt number
> > + * @data: pointer to a q_vector
> > + *
> > + */
> > +irqreturn_t
> > +iecm_vport_intr_clean_queues(int __always_unused irq, void *data)
> > +{
> > +	struct iecm_q_vector *q_vector = (struct iecm_q_vector *)data;
> > +
> > +	q_vector->total_events++;
> > +	napi_schedule(&q_vector->napi);
> > +
> > +	return IRQ_HANDLED;
> > +}
> > +
> > diff --git a/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> > index b8f54b8c700a..aae06064d706 100644
> > --- a/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> > +++ b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> > @@ -3,6 +3,74 @@
> >
> >  #include "iecm.h"
> >
> > +/**
> > + * iecm_recv_event_msg - Receive virtchnl event message
> > + * @vport: virtual port structure
> > + *
> > + * Receive virtchnl event message
> > + */
> > +static void iecm_recv_event_msg(struct iecm_vport *vport)
> > +{
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	struct virtchnl_pf_event *vpe;
> > +	struct virtchnl2_event *v2e;
> > +	bool link_status;
> > +	u32 event;
> > +
> > +	if (adapter->virt_ver_maj < VIRTCHNL_VERSION_MAJOR_2) {
> > +		vpe = (struct virtchnl_pf_event *)adapter->vc_msg;
> > +		event = vpe->event;
> > +	} else {
> > +		v2e = (struct virtchnl2_event *)adapter->vc_msg;
> > +		event = le32_to_cpu(v2e->event);
> > +	}
> > +
> > +	switch (event) {
> > +	case VIRTCHNL2_EVENT_LINK_CHANGE:
> > +		if (adapter->virt_ver_maj < VIRTCHNL_VERSION_MAJOR_2) {
> > +			if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
> > +					    VIRTCHNL2_CAP_LINK_SPEED)) {
> > +				adapter->link_speed_mbps =
> > +				vpe->event_data.link_event_adv.link_speed;
> > +				link_status =
> > +				vpe->event_data.link_event_adv.link_status;
> > +			} else {
> > +				adapter->link_speed =
> > +				vpe->event_data.link_event.link_speed;
> > +				link_status =
> > +				vpe->event_data.link_event.link_status;
> > +			}
> > +		} else {
> > +			adapter->link_speed_mbps = le32_to_cpu(v2e-
> >link_speed);
> > +			link_status = v2e->link_status;
> > +		}
> > +		if (adapter->link_up != link_status) {
> > +			adapter->link_up = link_status;
> > +			if (adapter->state == __IECM_UP) {
> > +				if (adapter->link_up) {
> > +					netif_tx_start_all_queues(vport-
> >netdev);
> > +					netif_carrier_on(vport->netdev);
> > +				} else {
> > +					netif_tx_stop_all_queues(vport-
> >netdev);
> > +					netif_carrier_off(vport->netdev);
> > +				}
> > +			}
> > +		}
> > +		break;
> > +	case VIRTCHNL_EVENT_RESET_IMPENDING:
> > +		set_bit(__IECM_HR_CORE_RESET, adapter->flags);
> > +		queue_delayed_work(adapter->vc_event_wq,
> > +				   &adapter->vc_event_task,
> > +				   msecs_to_jiffies(10));
> > +		break;
> > +	default:
> > +		dev_err(&vport->adapter->pdev->dev,
> > +			"Unknown event %d from PF\n", event);
> > +		break;
> > +	}
> > +	clear_bit(__IECM_VC_MSG_PENDING, adapter->flags);
> > +}
> > +
> >  /**
> >   * iecm_mb_clean - Reclaim the send mailbox queue entries
> >   * @adapter: Driver specific private structure
> > @@ -39,6 +107,865 @@ static int iecm_mb_clean(struct iecm_adapter
> *adapter)
> >  	return err;
> >  }
> >
> > +/**
> > + * iecm_send_mb_msg - Send message over mailbox
> > + * @adapter: Driver specific private structure
> > + * @op: virtchnl opcode
> > + * @msg_size: size of the payload
> > + * @msg: pointer to buffer holding the payload
> > + *
> > + * Will prepare the control queue message and initiates the send api
> > + *
> > + * Returns 0 on success, negative on failure
> > + */
> > +int iecm_send_mb_msg(struct iecm_adapter *adapter, enum virtchnl_ops op,
> > +		     u16 msg_size, u8 *msg)
> > +{
> > +	struct iecm_ctlq_msg *ctlq_msg;
> > +	struct iecm_dma_mem *dma_mem;
> > +	int err = 0;
> > +
> > +	if (iecm_is_reset_detected(adapter))
> > +		return -EBUSY;
> > +
> > +	err = iecm_mb_clean(adapter);
> > +	if (err)
> > +		return err;
> > +
> > +	ctlq_msg = kzalloc(sizeof(*ctlq_msg), GFP_KERNEL);
> > +	if (!ctlq_msg)
> > +		return -ENOMEM;
> > +
> > +	dma_mem = kzalloc(sizeof(*dma_mem), GFP_KERNEL);
> > +	if (!dma_mem) {
> > +		err = -ENOMEM;
> > +		goto dma_mem_error;
> > +	}
> > +
> > +	memset(ctlq_msg, 0, sizeof(struct iecm_ctlq_msg));
> > +	ctlq_msg->opcode = iecm_mbq_opc_send_msg_to_pf;
> > +	ctlq_msg->func_id = 0;
> > +	ctlq_msg->data_len = msg_size;
> > +	ctlq_msg->cookie.mbx.chnl_opcode = op;
> > +	ctlq_msg->cookie.mbx.chnl_retval = VIRTCHNL_STATUS_SUCCESS;
> > +	dma_mem->size = IECM_DFLT_MBX_BUF_SIZE;
> > +	dma_mem->va = dmam_alloc_coherent(&adapter->pdev->dev,
> dma_mem->size,
> > +					  &dma_mem->pa, GFP_KERNEL);
> > +	if (!dma_mem->va) {
> > +		err = -ENOMEM;
> > +		goto dma_alloc_error;
> > +	}
> > +	memcpy(dma_mem->va, msg, msg_size);
> > +	ctlq_msg->ctx.indirect.payload = dma_mem;
> > +
> > +	err = iecm_ctlq_send(&adapter->hw, adapter->hw.asq, 1, ctlq_msg);
> > +	if (err)
> > +		goto send_error;
> > +
> > +	return 0;
> > +send_error:
> > +	dmam_free_coherent(&adapter->pdev->dev, dma_mem->size,
> dma_mem->va,
> > +			   dma_mem->pa);
> > +dma_alloc_error:
> > +	kfree(dma_mem);
> > +dma_mem_error:
> > +	kfree(ctlq_msg);
> > +	return err;
> > +}
> > +EXPORT_SYMBOL(iecm_send_mb_msg);
> > +
> > +/**
> > + * iecm_set_msg_pending_bit - Wait for clear and set msg pending
> > + * @adapter: driver specific private structure
> > + *
> > + * If clear sets msg pending bit, otherwise waits for it to clear before
> > + * setting it again. Returns 0 on success, negative on failure.
> > + */
> > +static int iecm_set_msg_pending_bit(struct iecm_adapter *adapter)
> > +{
> > +	unsigned int retries = 100;
> > +
> > +	/* If msg pending bit already set, there's a message waiting to be
> > +	 * parsed and we must wait for it to be cleared before copying a new
> > +	 * message into the vc_msg buffer or else we'll stomp all over the
> > +	 * previous message.
> > +	 */
> > +	while (retries) {
> > +		if (!test_and_set_bit(__IECM_VC_MSG_PENDING,
> > +				      adapter->flags))
> > +			break;
> > +		msleep(20);
> > +		retries--;
> > +	}
> > +	return retries ? 0 : -ETIMEDOUT;
> > +}
> > +
> > +/**
> > + * iecm_set_msg_pending - Wait for msg pending bit and copy msg to buf
> > + * @adapter: driver specific private structure
> > + * @ctlq_msg: msg to copy from
> > + * @err_enum: err bit to set on error
> > + *
> > + * Copies payload from ctlq_msg into vc_msg buf in adapter and sets msg
> pending
> > + * bit. Returns 0 on success, negative on failure.
> > + */
> > +int iecm_set_msg_pending(struct iecm_adapter *adapter,
> > +			 struct iecm_ctlq_msg *ctlq_msg,
> > +			 enum iecm_vport_vc_state err_enum)
> > +{
> > +	if (ctlq_msg->cookie.mbx.chnl_retval) {
> > +		set_bit(err_enum, adapter->vc_state);
> > +		return -EINVAL;
> > +	}
> > +
> > +	if (iecm_set_msg_pending_bit(adapter)) {
> > +		set_bit(err_enum, adapter->vc_state);
> > +		dev_info(&adapter->pdev->dev, "Timed out setting msg
> pending\n");
> > +		return -ETIMEDOUT;
> > +	}
> > +
> > +	memcpy(adapter->vc_msg, ctlq_msg->ctx.indirect.payload->va,
> > +	       min_t(int, ctlq_msg->ctx.indirect.payload->size,
> > +		     IECM_DFLT_MBX_BUF_SIZE));
> > +	return 0;
> > +}
> > +EXPORT_SYMBOL(iecm_set_msg_pending);
> > +
> > +/**
> > + * iecm_recv_mb_msg - Receive message over mailbox
> > + * @adapter: Driver specific private structure
> > + * @op: virtchannel operation code
> > + * @msg: Received message holding buffer
> > + * @msg_size: message size
> > + *
> > + * Will receive control queue message and posts the receive buffer. Returns 0
> > + * on success and negative on failure.
> > + */
> > +int iecm_recv_mb_msg(struct iecm_adapter *adapter, enum virtchnl_ops op,
> > +		     void *msg, int msg_size)
> > +{
> > +	struct iecm_ctlq_msg ctlq_msg;
> > +	struct iecm_dma_mem *dma_mem;
> > +	struct iecm_vport *vport;
> > +	bool work_done = false;
> > +	int num_retry = 2000;
> > +	int payload_size = 0;
> > +	u16 num_q_msg;
> > +	int err = 0;
> > +
> > +	vport = adapter->vports[0];
> > +	while (1) {
> > +		/* Try to get one message */
> > +		num_q_msg = 1;
> > +		dma_mem = NULL;
> > +		err = iecm_ctlq_recv(adapter->hw.arq, &num_q_msg,
> &ctlq_msg);
> > +		/* If no message then decide if we have to retry based on
> > +		 * opcode
> > +		 */
> > +		if (err || !num_q_msg) {
> > +			/* Increasing num_retry to consider the delayed
> > +			 * responses because of large number of VF's mailbox
> > +			 * messages. If the mailbox message is received from
> > +			 * the other side, we come out of the sleep cycle
> > +			 * immediately else we wait for more time.
> > +			 */
> > +			if (op && num_retry-- &&
> > +			    !test_bit(__IECM_REL_RES_IN_PROG, adapter-
> >flags)) {
> > +				msleep(20);
> > +				continue;
> > +			} else {
> > +				break;
> > +			}
> 
> Since if-condition always ends with a 'continue', 'else' is
> reduntant here.
> 

Will fix

> > +		}
> > +
> > +		/* If we are here a message is received. Check if we are looking
> > +		 * for a specific message based on opcode. If it is different
> > +		 * ignore and post buffers
> > +		 */
> > +		if (op && ctlq_msg.cookie.mbx.chnl_opcode != op)
> > +			goto post_buffs;
> > +
> > +		if (ctlq_msg.data_len)
> > +			payload_size = ctlq_msg.ctx.indirect.payload->size;
> > +
> > +		/* All conditions are met. Either a message requested is
> > +		 * received or we received a message to be processed
> > +		 */
> > +		switch (ctlq_msg.cookie.mbx.chnl_opcode) {
> > +		case VIRTCHNL_OP_VERSION:
> > +		case VIRTCHNL2_OP_GET_CAPS:
> > +		case VIRTCHNL2_OP_CREATE_VPORT:
> > +			if (ctlq_msg.cookie.mbx.chnl_retval) {
> > +				dev_info(&adapter->pdev->dev, "Failure
> initializing, vc op: %u retval: %u\n",
> > +					 ctlq_msg.cookie.mbx.chnl_opcode,
> > +					 ctlq_msg.cookie.mbx.chnl_retval);
> > +				err = -EBADMSG;
> > +			} else if (msg) {
> > +				memcpy(msg, ctlq_msg.ctx.indirect.payload-
> >va,
> > +				       min_t(int,
> > +					     payload_size, msg_size));
> > +			}
> > +			work_done = true;
> > +			break;
> > +		case VIRTCHNL2_OP_ENABLE_VPORT:
> > +			if (ctlq_msg.cookie.mbx.chnl_retval)
> > +				set_bit(IECM_VC_ENA_VPORT_ERR,
> > +					adapter->vc_state);
> > +			set_bit(IECM_VC_ENA_VPORT, adapter->vc_state);
> > +			wake_up(&adapter->vchnl_wq);
> > +			break;
> > +		case VIRTCHNL2_OP_DISABLE_VPORT:
> > +			if (ctlq_msg.cookie.mbx.chnl_retval)
> > +				set_bit(IECM_VC_DIS_VPORT_ERR,
> > +					adapter->vc_state);
> > +			set_bit(IECM_VC_DIS_VPORT, adapter->vc_state);
> > +			wake_up(&adapter->vchnl_wq);
> > +			break;
> > +		case VIRTCHNL2_OP_DESTROY_VPORT:
> > +			if (ctlq_msg.cookie.mbx.chnl_retval)
> > +				set_bit(IECM_VC_DESTROY_VPORT_ERR,
> > +					adapter->vc_state);
> > +			set_bit(IECM_VC_DESTROY_VPORT, adapter-
> >vc_state);
> > +			wake_up(&adapter->vchnl_wq);
> > +			break;
> > +		case VIRTCHNL2_OP_CONFIG_TX_QUEUES:
> > +			if (ctlq_msg.cookie.mbx.chnl_retval)
> > +				set_bit(IECM_VC_CONFIG_TXQ_ERR,
> > +					adapter->vc_state);
> > +			set_bit(IECM_VC_CONFIG_TXQ, adapter->vc_state);
> > +			wake_up(&adapter->vchnl_wq);
> > +			break;
> > +		case VIRTCHNL2_OP_CONFIG_RX_QUEUES:
> > +			if (ctlq_msg.cookie.mbx.chnl_retval)
> > +				set_bit(IECM_VC_CONFIG_RXQ_ERR,
> > +					adapter->vc_state);
> > +			set_bit(IECM_VC_CONFIG_RXQ, adapter->vc_state);
> > +			wake_up(&adapter->vchnl_wq);
> > +			break;
> > +		case VIRTCHNL2_OP_ENABLE_QUEUES:
> > +			if (ctlq_msg.cookie.mbx.chnl_retval)
> > +				set_bit(IECM_VC_ENA_QUEUES_ERR,
> > +					adapter->vc_state);
> > +			set_bit(IECM_VC_ENA_QUEUES, adapter->vc_state);
> > +			wake_up(&adapter->vchnl_wq);
> > +			break;
> > +		case VIRTCHNL2_OP_DISABLE_QUEUES:
> > +			if (ctlq_msg.cookie.mbx.chnl_retval)
> > +				set_bit(IECM_VC_DIS_QUEUES_ERR,
> > +					adapter->vc_state);
> > +			set_bit(IECM_VC_DIS_QUEUES, adapter->vc_state);
> > +			wake_up(&adapter->vchnl_wq);
> > +			break;
> > +		case VIRTCHNL2_OP_ADD_QUEUES:
> > +			iecm_set_msg_pending(adapter, &ctlq_msg,
> > +					     IECM_VC_ADD_QUEUES_ERR);
> > +			set_bit(IECM_VC_ADD_QUEUES, adapter->vc_state);
> > +			wake_up(&adapter->vchnl_wq);
> > +			break;
> > +		case VIRTCHNL2_OP_DEL_QUEUES:
> > +			if (ctlq_msg.cookie.mbx.chnl_retval)
> > +				set_bit(IECM_VC_DEL_QUEUES_ERR,
> > +					adapter->vc_state);
> > +			set_bit(IECM_VC_DEL_QUEUES, adapter->vc_state);
> > +			wake_up(&adapter->vchnl_wq);
> > +			break;
> > +		case VIRTCHNL2_OP_MAP_QUEUE_VECTOR:
> > +			if (ctlq_msg.cookie.mbx.chnl_retval)
> > +				set_bit(IECM_VC_MAP_IRQ_ERR,
> > +					adapter->vc_state);
> > +			set_bit(IECM_VC_MAP_IRQ, adapter->vc_state);
> > +			wake_up(&adapter->vchnl_wq);
> > +			break;
> > +		case VIRTCHNL2_OP_UNMAP_QUEUE_VECTOR:
> > +			if (ctlq_msg.cookie.mbx.chnl_retval)
> > +				set_bit(IECM_VC_UNMAP_IRQ_ERR,
> > +					adapter->vc_state);
> > +			set_bit(IECM_VC_UNMAP_IRQ, adapter->vc_state);
> > +			wake_up(&adapter->vchnl_wq);
> > +			break;
> > +		case VIRTCHNL2_OP_GET_STATS:
> > +			iecm_set_msg_pending(adapter, &ctlq_msg,
> > +					     IECM_VC_GET_STATS_ERR);
> > +			set_bit(IECM_VC_GET_STATS, adapter->vc_state);
> > +			wake_up(&adapter->vchnl_wq);
> > +			break;
> > +		case VIRTCHNL2_OP_GET_RSS_HASH:
> > +			iecm_set_msg_pending(adapter, &ctlq_msg,
> > +					     IECM_VC_GET_RSS_HASH_ERR);
> > +			set_bit(IECM_VC_GET_RSS_HASH, adapter->vc_state);
> > +			wake_up(&adapter->vchnl_wq);
> > +			break;
> > +		case VIRTCHNL2_OP_SET_RSS_HASH:
> > +			if (ctlq_msg.cookie.mbx.chnl_retval)
> > +				set_bit(IECM_VC_SET_RSS_HASH_ERR,
> > +					adapter->vc_state);
> > +			set_bit(IECM_VC_SET_RSS_HASH, adapter->vc_state);
> > +			wake_up(&adapter->vchnl_wq);
> > +			break;
> > +		case VIRTCHNL2_OP_GET_RSS_LUT:
> > +			iecm_set_msg_pending(adapter, &ctlq_msg,
> > +					     IECM_VC_GET_RSS_LUT_ERR);
> > +			set_bit(IECM_VC_GET_RSS_LUT, adapter->vc_state);
> > +			wake_up(&adapter->vchnl_wq);
> > +			break;
> > +		case VIRTCHNL2_OP_SET_RSS_LUT:
> > +			if (ctlq_msg.cookie.mbx.chnl_retval)
> > +				set_bit(IECM_VC_SET_RSS_LUT_ERR,
> > +					adapter->vc_state);
> > +			set_bit(IECM_VC_SET_RSS_LUT, adapter->vc_state);
> > +			wake_up(&adapter->vchnl_wq);
> > +			break;
> > +		case VIRTCHNL2_OP_GET_RSS_KEY:
> > +			iecm_set_msg_pending(adapter, &ctlq_msg,
> > +					     IECM_VC_GET_RSS_KEY_ERR);
> > +			set_bit(IECM_VC_GET_RSS_KEY, adapter->vc_state);
> > +			wake_up(&adapter->vchnl_wq);
> > +			break;
> > +		case VIRTCHNL2_OP_SET_RSS_KEY:
> > +			if (ctlq_msg.cookie.mbx.chnl_retval)
> > +				set_bit(IECM_VC_SET_RSS_KEY_ERR,
> > +					adapter->vc_state);
> > +			set_bit(IECM_VC_SET_RSS_KEY, adapter->vc_state);
> > +			wake_up(&adapter->vchnl_wq);
> > +			break;
> > +		case VIRTCHNL2_OP_ALLOC_VECTORS:
> > +			iecm_set_msg_pending(adapter, &ctlq_msg,
> > +					     IECM_VC_ALLOC_VECTORS_ERR);
> > +			set_bit(IECM_VC_ALLOC_VECTORS, adapter-
> >vc_state);
> > +			wake_up(&adapter->vchnl_wq);
> > +			break;
> > +		case VIRTCHNL2_OP_DEALLOC_VECTORS:
> > +			if (ctlq_msg.cookie.mbx.chnl_retval)
> > +				set_bit(IECM_VC_DEALLOC_VECTORS_ERR,
> > +					adapter->vc_state);
> > +			set_bit(IECM_VC_DEALLOC_VECTORS, adapter-
> >vc_state);
> > +			wake_up(&adapter->vchnl_wq);
> > +			break;
> > +		case VIRTCHNL2_OP_GET_PTYPE_INFO:
> > +			iecm_set_msg_pending(adapter, &ctlq_msg,
> > +					     IECM_VC_GET_PTYPE_INFO_ERR);
> > +			set_bit(IECM_VC_GET_PTYPE_INFO, adapter-
> >vc_state);
> > +			wake_up(&adapter->vchnl_wq);
> > +			break;
> > +		case VIRTCHNL2_OP_EVENT:
> > +		case VIRTCHNL_OP_EVENT:
> > +			if (iecm_set_msg_pending_bit(adapter)) {
> > +				dev_info(&adapter->pdev->dev, "Timed out
> setting msg pending\n");
> > +			} else {
> > +				memcpy(adapter->vc_msg,
> > +				       ctlq_msg.ctx.indirect.payload->va,
> > +				       min_t(int, payload_size,
> > +					     IECM_DFLT_MBX_BUF_SIZE));
> > +				iecm_recv_event_msg(vport);
> > +			}
> > +			break;
> > +		case VIRTCHNL_OP_ADD_ETH_ADDR:
> > +			if (test_and_clear_bit(__IECM_ADD_ETH_REQ,
> adapter->flags)) {
> > +				/* Message was sent asynchronously. We don't
> > +				 * normally print errors here, instead
> > +				 * preferring to handle errors in the function
> > +				 * calling wait_for_event. However, we will
> > +				 * have lost the context in which we sent the
> > +				 * message if asynchronous. We can't really do
> > +				 * anything about at it this point, but we
> > +				 * should at a minimum indicate that it looks
> > +				 * like something went wrong. Also don't
> bother
> > +				 * setting ERR bit or waking vchnl_wq since no
> > +				 * one will be waiting to read the async
> > +				 * message.
> > +				 */
> > +				if (ctlq_msg.cookie.mbx.chnl_retval) {
> > +					dev_err(&adapter->pdev->dev, "Failed
> to add MAC address: %d\n",
> > +
> 	ctlq_msg.cookie.mbx.chnl_retval);
> > +				}
> > +				break;
> > +			}
> > +			if (ctlq_msg.cookie.mbx.chnl_retval) {
> > +				set_bit(IECM_VC_ADD_ETH_ADDR_ERR,
> > +					adapter->vc_state);
> > +			}
> > +			set_bit(IECM_VC_ADD_ETH_ADDR, adapter->vc_state);
> > +			wake_up(&adapter->vchnl_wq);
> > +			break;
> > +		case VIRTCHNL_OP_DEL_ETH_ADDR:
> > +			if (test_and_clear_bit(__IECM_DEL_ETH_REQ, adapter-
> >flags)) {
> > +				/* Message was sent asynchronously. We don't
> > +				 * normally print errors here, instead
> > +				 * preferring to handle errors in the function
> > +				 * calling wait_for_event. However, we will
> > +				 * have lost the context in which we sent the
> > +				 * message if asynchronous. We can't really do
> > +				 * anything about at it this point, but we
> > +				 * should at a minimum indicate that it looks
> > +				 * like something went wrong. Also don't
> bother
> > +				 * setting ERR bit or waking vchnl_wq since no
> > +				 * one will be waiting to read the async
> > +				 * message.
> > +				 */
> > +				if (ctlq_msg.cookie.mbx.chnl_retval) {
> > +					dev_err(&adapter->pdev->dev, "Failed
> to delete MAC address: %d\n",
> > +
> 	ctlq_msg.cookie.mbx.chnl_retval);
> > +				}
> > +				break;
> > +			}
> > +			if (ctlq_msg.cookie.mbx.chnl_retval) {
> > +				set_bit(IECM_VC_DEL_ETH_ADDR_ERR,
> > +					adapter->vc_state);
> > +			}
> > +			set_bit(IECM_VC_DEL_ETH_ADDR, adapter->vc_state);
> > +			wake_up(&adapter->vchnl_wq);
> > +			break;
> > +		case VIRTCHNL_OP_GET_OFFLOAD_VLAN_V2_CAPS:
> > +			if (ctlq_msg.cookie.mbx.chnl_retval) {
> > +
> 	set_bit(IECM_VC_OFFLOAD_VLAN_V2_CAPS_ERR, adapter->vc_state);
> > +			} else {
> > +				memcpy(adapter->vc_msg,
> > +				       ctlq_msg.ctx.indirect.payload->va,
> > +				       min_t(int, payload_size,
> > +					     IECM_DFLT_MBX_BUF_SIZE));
> > +			}
> > +			set_bit(IECM_VC_OFFLOAD_VLAN_V2_CAPS, adapter-
> >vc_state);
> > +			wake_up(&adapter->vchnl_wq);
> > +			break;
> > +		case VIRTCHNL_OP_ADD_VLAN_V2:
> > +			if (ctlq_msg.cookie.mbx.chnl_retval) {
> > +				dev_err(&adapter->pdev->dev, "Failed to add
> vlan filter: %d\n",
> > +					ctlq_msg.cookie.mbx.chnl_retval);
> > +			}
> > +			break;
> > +		case VIRTCHNL_OP_DEL_VLAN_V2:
> > +			if (ctlq_msg.cookie.mbx.chnl_retval) {
> > +				dev_err(&adapter->pdev->dev, "Failed to
> delete vlan filter: %d\n",
> > +					ctlq_msg.cookie.mbx.chnl_retval);
> > +			}
> > +			break;
> > +		case VIRTCHNL_OP_ENABLE_VLAN_INSERTION_V2:
> > +			if (ctlq_msg.cookie.mbx.chnl_retval) {
> > +
> 	set_bit(IECM_VC_INSERTION_ENA_VLAN_V2_ERR,
> > +					adapter->vc_state);
> > +			}
> > +			set_bit(IECM_VC_INSERTION_ENA_VLAN_V2,
> > +				adapter->vc_state);
> > +			wake_up(&adapter->vchnl_wq);
> > +			break;
> > +		case VIRTCHNL_OP_DISABLE_VLAN_INSERTION_V2:
> > +			if (ctlq_msg.cookie.mbx.chnl_retval) {
> > +
> 	set_bit(IECM_VC_INSERTION_DIS_VLAN_V2_ERR,
> > +					adapter->vc_state);
> > +			}
> > +			set_bit(IECM_VC_INSERTION_DIS_VLAN_V2,
> > +				adapter->vc_state);
> > +			wake_up(&adapter->vchnl_wq);
> > +			break;
> > +		case VIRTCHNL_OP_ENABLE_VLAN_STRIPPING_V2:
> > +			if (ctlq_msg.cookie.mbx.chnl_retval) {
> > +
> 	set_bit(IECM_VC_STRIPPING_ENA_VLAN_V2_ERR,
> > +					adapter->vc_state);
> > +			}
> > +			set_bit(IECM_VC_STRIPPING_ENA_VLAN_V2,
> > +				adapter->vc_state);
> > +			wake_up(&adapter->vchnl_wq);
> > +			break;
> > +		case VIRTCHNL_OP_DISABLE_VLAN_STRIPPING_V2:
> > +			if (ctlq_msg.cookie.mbx.chnl_retval) {
> > +
> 	set_bit(IECM_VC_STRIPPING_DIS_VLAN_V2_ERR,
> > +					adapter->vc_state);
> > +			}
> > +			set_bit(IECM_VC_STRIPPING_DIS_VLAN_V2,
> > +				adapter->vc_state);
> > +			wake_up(&adapter->vchnl_wq);
> > +			break;
> > +		case VIRTCHNL_OP_CONFIG_PROMISCUOUS_MODE:
> > +			/* This message can only be sent asynchronously. As
> > +			 * such we'll have lost the context in which it was
> > +			 * called and thus can only really report if it looks
> > +			 * like an error occurred. Don't bother setting ERR bit
> > +			 * or waking chnl_wq since no will be waiting to
> > +			 * reading message.
> > +			 */
> > +			if (ctlq_msg.cookie.mbx.chnl_retval) {
> > +				dev_err(&adapter->pdev->dev, "Failed to set
> promiscuous mode: %d\n",
> > +					ctlq_msg.cookie.mbx.chnl_retval);
> > +			}
> > +			break;
> > +		case VIRTCHNL_OP_ADD_CLOUD_FILTER:
> > +			if (ctlq_msg.cookie.mbx.chnl_retval) {
> > +				dev_err(&adapter->pdev->dev, "Failed to add
> cloud filter: %d\n",
> > +					ctlq_msg.cookie.mbx.chnl_retval);
> > +				set_bit(IECM_VC_ADD_CLOUD_FILTER_ERR,
> > +					adapter->vc_state);
> > +			}
> > +			set_bit(IECM_VC_ADD_CLOUD_FILTER, adapter-
> >vc_state);
> > +			wake_up(&adapter->vchnl_wq);
> > +			break;
> > +		case VIRTCHNL_OP_DEL_CLOUD_FILTER:
> > +			if (ctlq_msg.cookie.mbx.chnl_retval) {
> > +				dev_err(&adapter->pdev->dev, "Failed to
> delete cloud filter: %d\n",
> > +					ctlq_msg.cookie.mbx.chnl_retval);
> > +				set_bit(IECM_VC_DEL_CLOUD_FILTER_ERR,
> > +					adapter->vc_state);
> > +			}
> > +			set_bit(IECM_VC_DEL_CLOUD_FILTER, adapter-
> >vc_state);
> > +			wake_up(&adapter->vchnl_wq);
> > +			break;
> > +		case VIRTCHNL_OP_ADD_RSS_CFG:
> > +			if (ctlq_msg.cookie.mbx.chnl_retval) {
> > +				dev_err(&adapter->pdev->dev, "Failed to add
> RSS configuration: %d\n",
> > +					ctlq_msg.cookie.mbx.chnl_retval);
> > +				set_bit(IECM_VC_ADD_RSS_CFG_ERR,
> > +					adapter->vc_state);
> > +			}
> > +			set_bit(IECM_VC_ADD_RSS_CFG, adapter->vc_state);
> > +			wake_up(&adapter->vchnl_wq);
> > +			break;
> > +		case VIRTCHNL_OP_DEL_RSS_CFG:
> > +			if (ctlq_msg.cookie.mbx.chnl_retval) {
> > +				dev_err(&adapter->pdev->dev, "Failed to
> delete RSS configuration: %d\n",
> > +					ctlq_msg.cookie.mbx.chnl_retval);
> > +				set_bit(IECM_VC_DEL_RSS_CFG_ERR,
> > +					adapter->vc_state);
> > +			}
> > +			set_bit(IECM_VC_DEL_RSS_CFG, adapter->vc_state);
> > +			wake_up(&adapter->vchnl_wq);
> > +			break;
> > +		case VIRTCHNL_OP_ADD_FDIR_FILTER:
> > +			iecm_set_msg_pending(adapter, &ctlq_msg,
> > +					     IECM_VC_ADD_FDIR_FILTER_ERR);
> > +			set_bit(IECM_VC_ADD_FDIR_FILTER, adapter-
> >vc_state);
> > +			wake_up(&adapter->vchnl_wq);
> > +			break;
> > +		case VIRTCHNL_OP_DEL_FDIR_FILTER:
> > +			iecm_set_msg_pending(adapter, &ctlq_msg,
> > +					     IECM_VC_DEL_FDIR_FILTER_ERR);
> > +			set_bit(IECM_VC_DEL_FDIR_FILTER, adapter-
> >vc_state);
> > +			wake_up(&adapter->vchnl_wq);
> > +			break;
> > +		case VIRTCHNL_OP_ENABLE_CHANNELS:
> > +			if (ctlq_msg.cookie.mbx.chnl_retval) {
> > +				dev_err(&adapter->pdev->dev, "Failed to
> enable channels: %d\n",
> > +					ctlq_msg.cookie.mbx.chnl_retval);
> > +				set_bit(IECM_VC_ENA_CHANNELS_ERR,
> > +					adapter->vc_state);
> > +			}
> > +			set_bit(IECM_VC_ENA_CHANNELS, adapter->vc_state);
> > +			wake_up(&adapter->vchnl_wq);
> > +			break;
> > +		case VIRTCHNL_OP_DISABLE_CHANNELS:
> > +			if (ctlq_msg.cookie.mbx.chnl_retval) {
> > +				dev_err(&adapter->pdev->dev, "Failed to
> disable channels: %d\n",
> > +					ctlq_msg.cookie.mbx.chnl_retval);
> > +				set_bit(IECM_VC_DIS_CHANNELS_ERR,
> > +					adapter->vc_state);
> > +			}
> > +			set_bit(IECM_VC_DIS_CHANNELS, adapter->vc_state);
> > +			wake_up(&adapter->vchnl_wq);
> > +			break;
> > +		default:
> > +			if (adapter->dev_ops.vc_ops.recv_mbx_msg)
> > +				err =
> > +				adapter-
> >dev_ops.vc_ops.recv_mbx_msg(adapter,
> > +								   msg,
> > +								   msg_size,
> > +								   &ctlq_msg,
> > +
> &work_done);
> > +			else
> > +				dev_warn(&adapter->pdev->dev,
> > +					 "Unhandled virtchnl response %d\n",
> > +					 ctlq_msg.cookie.mbx.chnl_opcode);
> > +			break;
> > +		} /* switch v_opcode */
> > +post_buffs:
> > +		if (ctlq_msg.data_len)
> > +			dma_mem = ctlq_msg.ctx.indirect.payload;
> > +		else
> > +			num_q_msg = 0;
> > +
> > +		err = iecm_ctlq_post_rx_buffs(&adapter->hw, adapter->hw.arq,
> > +					      &num_q_msg, &dma_mem);
> > +		/* If post failed clear the only buffer we supplied */
> > +		if (err && dma_mem)
> > +			dmam_free_coherent(&adapter->pdev->dev,
> dma_mem->size,
> > +					   dma_mem->va, dma_mem->pa);
> > +		/* Applies only if we are looking for a specific opcode */
> > +		if (work_done)
> > +			break;
> > +	}
> > +
> > +	return err;
> > +}
> > +EXPORT_SYMBOL(iecm_recv_mb_msg);
> > +
> > +/**
> > + * iecm_send_ver_msg - send virtchnl version message
> > + * @adapter: Driver specific private structure
> > + *
> > + * Send virtchnl version message.  Returns 0 on success, negative on failure.
> > + */
> > +static int iecm_send_ver_msg(struct iecm_adapter *adapter)
> > +{
> > +	struct virtchnl_version_info vvi;
> > +
> > +	if (adapter->virt_ver_maj) {
> > +		vvi.major = adapter->virt_ver_maj;
> > +		vvi.minor = adapter->virt_ver_min;
> > +	} else {
> > +		vvi.major = IECM_VIRTCHNL_VERSION_MAJOR;
> > +		vvi.minor = IECM_VIRTCHNL_VERSION_MINOR;
> > +	}
> > +
> > +	return iecm_send_mb_msg(adapter, VIRTCHNL_OP_VERSION,
> sizeof(vvi),
> > +				(u8 *)&vvi);
> > +}
> > +
> > +/**
> > + * iecm_recv_ver_msg - Receive virtchnl version message
> > + * @adapter: Driver specific private structure
> > + *
> > + * Receive virtchnl version message. Returns 0 on success, -EAGAIN if we
> need
> > + * to send version message again, otherwise negative on failure.
> > + */
> > +static int iecm_recv_ver_msg(struct iecm_adapter *adapter)
> > +{
> > +	struct virtchnl_version_info vvi;
> > +	int err = 0;
> > +
> > +	err = iecm_recv_mb_msg(adapter, VIRTCHNL_OP_VERSION, &vvi,
> sizeof(vvi));
> > +	if (err)
> > +		return err;
> > +
> > +	if (vvi.major > IECM_VIRTCHNL_VERSION_MAJOR) {
> > +		dev_warn(&adapter->pdev->dev, "Virtchnl major version
> greater than supported\n");
> > +		return -EINVAL;
> > +	}
> > +	if (vvi.major == IECM_VIRTCHNL_VERSION_MAJOR &&
> > +	    vvi.minor > IECM_VIRTCHNL_VERSION_MINOR)
> > +		dev_warn(&adapter->pdev->dev, "Virtchnl minor version not
> matched\n");
> > +
> > +	/* If we have a mismatch, resend version to update receiver on what
> > +	 * version we will use.
> > +	 */
> > +	if (!adapter->virt_ver_maj &&
> > +	    vvi.major != IECM_VIRTCHNL_VERSION_MAJOR &&
> > +	    vvi.minor != IECM_VIRTCHNL_VERSION_MINOR)
> > +		err = -EAGAIN;
> > +
> > +	adapter->virt_ver_maj = vvi.major;
> > +	adapter->virt_ver_min = vvi.minor;
> > +
> > +	return err;
> > +}
> > +
> > +/**
> > + * iecm_send_get_caps_msg - Send virtchnl get capabilities message
> > + * @adapter: Driver specific private structure
> > + *
> > + * Send virtchl get capabilities message. Returns 0 on success, negative on
> > + * failure.
> > + */
> > +int iecm_send_get_caps_msg(struct iecm_adapter *adapter)
> > +{
> > +	struct virtchnl2_get_capabilities caps = {0};
> > +	int buf_size;
> > +
> > +	buf_size = sizeof(struct virtchnl2_get_capabilities);
> > +	adapter->caps = kzalloc(buf_size, GFP_KERNEL);
> > +	if (!adapter->caps)
> > +		return -ENOMEM;
> > +
> > +	caps.csum_caps =
> > +		cpu_to_le32(VIRTCHNL2_CAP_TX_CSUM_L3_IPV4	|
> > +			    VIRTCHNL2_CAP_TX_CSUM_L4_IPV4_TCP	|
> > +			    VIRTCHNL2_CAP_TX_CSUM_L4_IPV4_UDP	|
> > +			    VIRTCHNL2_CAP_TX_CSUM_L4_IPV4_SCTP	|
> > +			    VIRTCHNL2_CAP_TX_CSUM_L4_IPV6_TCP	|
> > +			    VIRTCHNL2_CAP_TX_CSUM_L4_IPV6_UDP	|
> > +			    VIRTCHNL2_CAP_TX_CSUM_L4_IPV6_SCTP	|
> > +			    VIRTCHNL2_CAP_TX_CSUM_GENERIC	|
> > +			    VIRTCHNL2_CAP_RX_CSUM_L3_IPV4	|
> > +			    VIRTCHNL2_CAP_RX_CSUM_L4_IPV4_TCP	|
> > +			    VIRTCHNL2_CAP_RX_CSUM_L4_IPV4_UDP	|
> > +			    VIRTCHNL2_CAP_RX_CSUM_L4_IPV4_SCTP	|
> > +			    VIRTCHNL2_CAP_RX_CSUM_L4_IPV6_TCP	|
> > +			    VIRTCHNL2_CAP_RX_CSUM_L4_IPV6_UDP	|
> > +			    VIRTCHNL2_CAP_RX_CSUM_L4_IPV6_SCTP	|
> > +			    VIRTCHNL2_CAP_RX_CSUM_GENERIC);
> > +
> > +	caps.seg_caps =
> > +		cpu_to_le32(VIRTCHNL2_CAP_SEG_IPV4_TCP		|
> > +			    VIRTCHNL2_CAP_SEG_IPV4_UDP		|
> > +			    VIRTCHNL2_CAP_SEG_IPV4_SCTP		|
> > +			    VIRTCHNL2_CAP_SEG_IPV6_TCP		|
> > +			    VIRTCHNL2_CAP_SEG_IPV6_UDP		|
> > +			    VIRTCHNL2_CAP_SEG_IPV6_SCTP		|
> > +			    VIRTCHNL2_CAP_SEG_GENERIC);
> > +
> > +	caps.rss_caps =
> > +		cpu_to_le64(VIRTCHNL2_CAP_RSS_IPV4_TCP		|
> > +			    VIRTCHNL2_CAP_RSS_IPV4_UDP		|
> > +			    VIRTCHNL2_CAP_RSS_IPV4_SCTP		|
> > +			    VIRTCHNL2_CAP_RSS_IPV4_OTHER	|
> > +			    VIRTCHNL2_CAP_RSS_IPV6_TCP		|
> > +			    VIRTCHNL2_CAP_RSS_IPV6_UDP		|
> > +			    VIRTCHNL2_CAP_RSS_IPV6_SCTP		|
> > +			    VIRTCHNL2_CAP_RSS_IPV6_OTHER	|
> > +			    VIRTCHNL2_CAP_RSS_IPV4_AH		|
> > +			    VIRTCHNL2_CAP_RSS_IPV4_ESP		|
> > +			    VIRTCHNL2_CAP_RSS_IPV4_AH_ESP	|
> > +			    VIRTCHNL2_CAP_RSS_IPV6_AH		|
> > +			    VIRTCHNL2_CAP_RSS_IPV6_ESP		|
> > +			    VIRTCHNL2_CAP_RSS_IPV6_AH_ESP);
> > +
> > +	caps.hsplit_caps =
> > +		cpu_to_le32(VIRTCHNL2_CAP_RX_HSPLIT_AT_L2	|
> > +			    VIRTCHNL2_CAP_RX_HSPLIT_AT_L3	|
> > +			    VIRTCHNL2_CAP_RX_HSPLIT_AT_L4V4	|
> > +			    VIRTCHNL2_CAP_RX_HSPLIT_AT_L4V6);
> > +
> > +	caps.rsc_caps =
> > +		cpu_to_le32(VIRTCHNL2_CAP_RSC_IPV4_TCP		|
> > +			    VIRTCHNL2_CAP_RSC_IPV4_SCTP		|
> > +			    VIRTCHNL2_CAP_RSC_IPV6_TCP		|
> > +			    VIRTCHNL2_CAP_RSC_IPV6_SCTP);
> > +
> > +	caps.other_caps =
> > +		cpu_to_le64(VIRTCHNL2_CAP_RDMA			|
> > +			    VIRTCHNL2_CAP_SRIOV			|
> > +			    VIRTCHNL2_CAP_MACFILTER		|
> > +			    VIRTCHNL2_CAP_FLOW_DIRECTOR		|
> > +			    VIRTCHNL2_CAP_SPLITQ_QSCHED		|
> > +			    VIRTCHNL2_CAP_CRC			|
> > +			    VIRTCHNL2_CAP_ADQ			|
> > +			    VIRTCHNL2_CAP_WB_ON_ITR		|
> > +			    VIRTCHNL2_CAP_PROMISC		|
> > +			    VIRTCHNL2_CAP_INLINE_IPSEC		|
> > +			    VIRTCHNL2_CAP_VLAN			|
> > +			    VIRTCHNL2_CAP_RX_FLEX_DESC);
> > +
> > +	return iecm_send_mb_msg(adapter, VIRTCHNL2_OP_GET_CAPS,
> sizeof(caps),
> > +				(u8 *)&caps);
> > +}
> > +EXPORT_SYMBOL(iecm_send_get_caps_msg);
> > +
> > +/**
> > + * iecm_recv_get_caps_msg - Receive virtchnl get capabilities message
> > + * @adapter: Driver specific private structure
> > + *
> > + * Receive virtchnl get capabilities message.  Returns 0 on succes, negative on
> > + * failure.
> > + */
> > +static int iecm_recv_get_caps_msg(struct iecm_adapter *adapter)
> > +{
> > +	return iecm_recv_mb_msg(adapter, VIRTCHNL2_OP_GET_CAPS,
> adapter->caps,
> > +				sizeof(struct virtchnl2_get_capabilities));
> > +}
> > +
> > +/**
> > + * iecm_send_create_vport_msg - Send virtchnl create vport message
> > + * @adapter: Driver specific private structure
> > + *
> > + * send virtchnl creae vport message
> > + *
> > + * Returns 0 on success, negative on failure
> > + */
> > +static int iecm_send_create_vport_msg(struct iecm_adapter *adapter)
> > +{
> > +	/* stub */
> > +	return 0;
> > +}
> > +
> > +/**
> > + * iecm_recv_create_vport_msg - Receive virtchnl create vport message
> > + * @adapter: Driver specific private structure
> > + * @vport_id: Virtual port identifier
> > + *
> > + * Receive virtchnl create vport message.  Returns 0 on success, negative on
> > + * failure.
> > + */
> > +static int iecm_recv_create_vport_msg(struct iecm_adapter *adapter,
> > +				      int *vport_id)
> > +{
> > +	/* stub */
> > +	return 0;
> > +}
> > +
> > +/**
> > + * __iecm_wait_for_event - wrapper function for wait on virtchannel
> response
> > + * @adapter: Driver private data structure
> > + * @state: check on state upon timeout
> > + * @err_check: check if this specific error bit is set
> > + * @timeout: Max time to wait
> > + *
> > + * Checks if state is set upon expiry of timeout.  Returns 0 on success,
> > + * negative on failure.
> > + */
> > +static int __iecm_wait_for_event(struct iecm_adapter *adapter,
> > +				 enum iecm_vport_vc_state state,
> > +				 enum iecm_vport_vc_state err_check,
> > +				 int timeout)
> > +{
> > +	int event;
> > +
> > +	event = wait_event_timeout(adapter->vchnl_wq,
> > +				   test_and_clear_bit(state, adapter->vc_state),
> > +				   msecs_to_jiffies(timeout));
> > +	if (event) {
> > +		if (test_and_clear_bit(err_check, adapter->vc_state)) {
> > +			dev_err(&adapter->pdev->dev, "VC response error
> %s\n",
> > +				iecm_vport_vc_state_str[err_check]);
> > +			return -EINVAL;
> > +		}
> > +		return 0;
> > +	}
> > +
> > +	/* Timeout occurred */
> > +	dev_err(&adapter->pdev->dev, "VC timeout, state = %s\n",
> > +		iecm_vport_vc_state_str[state]);
> > +	return -ETIMEDOUT;
> > +}
> > +
> > +/**
> > + * iecm_min_wait_for_event - wait for virtchannel response
> > + * @adapter: Driver private data structure
> > + * @state: check on state upon timeout
> > + * @err_check: check if this specific error bit is set
> > + *
> > + * Returns 0 on success, negative on failure.
> > + */
> > +int iecm_min_wait_for_event(struct iecm_adapter *adapter,
> > +			    enum iecm_vport_vc_state state,
> > +			    enum iecm_vport_vc_state err_check)
> > +{
> > +	int timeout = 2000;
> > +
> > +	return __iecm_wait_for_event(adapter, state, err_check, timeout);
> > +}
> > +EXPORT_SYMBOL(iecm_min_wait_for_event);
> > +
> > +/**
> > + * iecm_wait_for_event - wait for virtchannel response
> > + * @adapter: Driver private data structure
> > + * @state: check on state upon timeout after 500ms
> > + * @err_check: check if this specific error bit is set
> > + *
> > + * Returns 0 on success, negative on failure.
> > + */
> > +int iecm_wait_for_event(struct iecm_adapter *adapter,
> > +			enum iecm_vport_vc_state state,
> > +			enum iecm_vport_vc_state err_check)
> > +{
> > +	/* Increasing the timeout in __IECM_INIT_SW flow to consider large
> > +	 * number of VF's mailbox message responses. When a message is
> received
> > +	 * on mailbox, this thread is wake up by the iecm_recv_mb_msg before
> the
> > +	 * timeout expires. Only in the error case i.e. if no message is
> > +	 * received on mailbox, we wait for the complete timeout which is
> > +	 * less likely to happen.
> > +	 */
> > +	int timeout = 60000;
> 
> If you make a definition from it, it would be easier to find it
> later in case of adjustments needed.
> 

Will fix

> > +
> > +	return __iecm_wait_for_event(adapter, state, err_check, timeout);
> > +}
> > +EXPORT_SYMBOL(iecm_wait_for_event);
> > +
> >  /**
> >   * iecm_find_ctlq - Given a type and id, find ctlq info
> >   * @hw: hardware struct
> > @@ -170,3 +1097,375 @@ void iecm_vport_params_buf_rel(struct
> iecm_adapter *adapter)
> >  	kfree(adapter->caps);
> >  	kfree(adapter->config_data.req_qs_chunks);
> >  }
> > +
> > +/**
> > + * iecm_vc_core_init - Initialize mailbox and get resources
> > + * @adapter: Driver specific private structure
> > + * @vport_id: Virtual port identifier
> > + *
> > + * Will check if HW is ready with reset complete. Initializes the mailbox and
> > + * communicate with master to get all the default vport parameters. Returns
> 0
> > + * on success, -EAGAIN function will get called again, otherwise negative on
> > + * failure.
> > + */
> > +int iecm_vc_core_init(struct iecm_adapter *adapter, int *vport_id)
> > +{
> > +	int err;
> > +
> > +	switch (adapter->state) {
> > +	case __IECM_STARTUP:
> > +		if (iecm_send_ver_msg(adapter))
> > +			goto init_failed;
> > +		adapter->state = __IECM_VER_CHECK;
> > +		goto restart;
> > +	case __IECM_VER_CHECK:
> > +		err = iecm_recv_ver_msg(adapter);
> > +		if (err == -EAGAIN) {
> > +			adapter->state = __IECM_STARTUP;
> > +			goto restart;
> > +		} else if (err) {
> > +			goto init_failed;
> > +		}
> > +		adapter->state = __IECM_GET_CAPS;
> > +		if (adapter->dev_ops.vc_ops.get_caps(adapter))
> > +			goto init_failed;
> > +		goto restart;
> > +	case __IECM_GET_CAPS:
> > +		if (iecm_recv_get_caps_msg(adapter))
> > +			goto init_failed;
> > +		if (iecm_send_create_vport_msg(adapter))
> > +			goto init_failed;
> > +		adapter->state = __IECM_GET_DFLT_VPORT_PARAMS;
> > +		goto restart;
> > +	case __IECM_GET_DFLT_VPORT_PARAMS:
> > +		if (iecm_recv_create_vport_msg(adapter, vport_id))
> > +			goto init_failed;
> > +		adapter->state = __IECM_INIT_SW;
> > +		break;
> > +	case __IECM_INIT_SW:
> > +		break;
> > +	default:
> > +		dev_err(&adapter->pdev->dev, "Device is in bad state: %d\n",
> > +			adapter->state);
> > +		goto init_failed;
> > +	}
> > +
> > +	return 0;
> > +restart:
> > +	queue_delayed_work(adapter->init_wq, &adapter->init_task,
> > +			   msecs_to_jiffies(30));
> > +	/* Not an error. Using try again to continue with state machine */
> > +	return -EAGAIN;
> > +init_failed:
> > +	if (++adapter->mb_wait_count > IECM_MB_MAX_ERR) {
> > +		dev_err(&adapter->pdev->dev, "Failed to establish mailbox
> communications with hardware\n");
> > +		return -EFAULT;
> > +	}
> > +	adapter->state = __IECM_STARTUP;
> > +	/* If it reaches here, it is possible that mailbox queue initialization
> > +	 * register writes might not have taken effect. Retry to initialize
> > +	 * the mailbox again
> > +	 */
> > +	iecm_deinit_dflt_mbx(adapter);
> > +	set_bit(__IECM_HR_DRV_LOAD, adapter->flags);
> > +	queue_delayed_work(adapter->vc_event_wq, &adapter-
> >vc_event_task,
> > +			   msecs_to_jiffies(20));
> > +	return -EAGAIN;
> > +}
> > +EXPORT_SYMBOL(iecm_vc_core_init);
> > +
> > +/**
> > + * iecm_vport_init - Initialize virtual port
> > + * @vport: virtual port to be initialized
> > + * @vport_id: Unique identification number of vport
> > + *
> > + * Will initialize vport with the info received through MB earlier
> > + */
> > +static void iecm_vport_init(struct iecm_vport *vport,
> > +			    __always_unused int vport_id)
> > +{
> > +	struct virtchnl2_create_vport *vport_msg;
> > +	u16 rx_itr[] = {2, 8, 32, 96, 128};
> > +	u16 tx_itr[] = {2, 8, 64, 128, 256};
> > +
> > +	vport_msg = (struct virtchnl2_create_vport *)
> > +				vport->adapter->vport_params_recvd[0];
> > +	vport->txq_model = le16_to_cpu(vport_msg->txq_model);
> > +	vport->rxq_model = le16_to_cpu(vport_msg->rxq_model);
> > +	vport->vport_type = le16_to_cpu(vport_msg->vport_type);
> > +	vport->vport_id = le32_to_cpu(vport_msg->vport_id);
> > +	vport->adapter->rss_data.rss_key_size =
> > +				min_t(u16, NETDEV_RSS_KEY_LEN,
> > +				      le16_to_cpu(vport_msg->rss_key_size));
> > +	vport->adapter->rss_data.rss_lut_size =
> > +				le16_to_cpu(vport_msg->rss_lut_size);
> > +	ether_addr_copy(vport->default_mac_addr, vport_msg-
> >default_mac_addr);
> > +	vport->max_mtu = IECM_MAX_MTU;
> > +
> > +	if (iecm_is_queue_model_split(vport->rxq_model)) {
> > +		vport->num_bufqs_per_qgrp =
> IECM_MAX_BUFQS_PER_RXQ_GRP;
> > +		/* Bufq[0] default buffer size is 4K
> > +		 * Bufq[1] default buffer size is 2K
> > +		 */
> > +		vport->bufq_size[0] = IECM_RX_BUF_4096;
> > +		vport->bufq_size[1] = IECM_RX_BUF_2048;
> > +	} else {
> > +		vport->num_bufqs_per_qgrp = 0;
> > +		vport->bufq_size[0] = IECM_RX_BUF_2048;
> > +	}
> > +
> > +	/*Initialize Tx and Rx profiles for Dynamic Interrupt Moderation */
> > +	memcpy(vport->rx_itr_profile, rx_itr, IECM_DIM_PROFILE_SLOTS);
> > +	memcpy(vport->tx_itr_profile, tx_itr, IECM_DIM_PROFILE_SLOTS);
> > +}
> > +
> > +/**
> > + * iecm_get_vec_ids - Initialize vector id from Mailbox parameters
> > + * @adapter: adapter structure to get the mailbox vector id
> > + * @vecids: Array of vector ids
> > + * @num_vecids: number of vector ids
> > + * @chunks: vector ids received over mailbox
> > + *
> > + * Will initialize the mailbox vector id which is received from the
> > + * get capabilities and data queue vector ids with ids received as
> > + * mailbox parameters.
> > + * Returns number of ids filled
> > + */
> > +int iecm_get_vec_ids(struct iecm_adapter *adapter,
> > +		     u16 *vecids, int num_vecids,
> > +		     struct virtchnl2_vector_chunks *chunks)
> > +{
> > +	u16 num_chunks = le16_to_cpu(chunks->num_vchunks);
> > +	u16 start_vecid, num_vec;
> > +	int num_vecid_filled = 0;
> > +	int i, j;
> > +
> > +	vecids[num_vecid_filled] = adapter->mb_vector.v_idx;
> > +	num_vecid_filled++;
> > +
> > +	for (j = 0; j < num_chunks; j++) {
> > +		struct virtchnl2_vector_chunk *chunk = &chunks->vchunks[j];
> > +
> > +		num_vec = le16_to_cpu(chunk->num_vectors);
> > +		start_vecid = le16_to_cpu(chunk->start_vector_id);
> > +		for (i = 0; i < num_vec; i++) {
> > +			if ((num_vecid_filled + i) < num_vecids) {
> > +				vecids[num_vecid_filled + i] = start_vecid;
> > +				start_vecid++;
> > +			} else {
> > +				break;
> > +			}
> > +		}
> > +		num_vecid_filled = num_vecid_filled + i;
> > +	}
> > +
> > +	return num_vecid_filled;
> > +}
> > +
> > +/**
> > + * iecm_vport_get_queue_ids - Initialize queue id from Mailbox parameters
> > + * @qids: Array of queue ids
> > + * @num_qids: number of queue ids
> > + * @q_type: queue model
> > + * @chunks: queue ids received over mailbox
> > + *
> > + * Will initialize all queue ids with ids received as mailbox parameters
> > + * Returns number of ids filled
> > + */
> > +static int
> > +iecm_vport_get_queue_ids(u32 *qids, int num_qids, u16 q_type,
> > +			 struct virtchnl2_queue_reg_chunks *chunks)
> > +{
> > +	u16 num_chunks = le16_to_cpu(chunks->num_chunks);
> > +	u32 num_q_id_filled = 0, i;
> > +	u32 start_q_id, num_q;
> > +
> > +	while (num_chunks) {
> > +		struct virtchnl2_queue_reg_chunk *chunk = &chunks-
> >chunks[num_chunks - 1];
> > +
> > +		if (le32_to_cpu(chunk->type) == q_type) {
> > +			num_q = le32_to_cpu(chunk->num_queues);
> > +			start_q_id = le32_to_cpu(chunk->start_queue_id);
> > +			for (i = 0; i < num_q; i++) {
> > +				if ((num_q_id_filled + i) < num_qids) {
> > +					qids[num_q_id_filled + i] = start_q_id;
> > +					start_q_id++;
> > +				} else {
> > +					break;
> > +				}
> > +			}
> > +			num_q_id_filled = num_q_id_filled + i;
> > +		}
> > +		num_chunks--;
> > +	}
> > +
> > +	return num_q_id_filled;
> > +}
> > +
> > +/**
> > + * __iecm_vport_queue_ids_init - Initialize queue ids from Mailbox
> parameters
> > + * @vport: virtual port for which the queues ids are initialized
> > + * @qids: queue ids
> > + * @num_qids: number of queue ids
> > + * @q_type: type of queue
> > + *
> > + * Will initialize all queue ids with ids received as mailbox
> > + * parameters. Returns number of queue ids initialized.
> > + */
> > +static int
> > +__iecm_vport_queue_ids_init(struct iecm_vport *vport, u32 *qids,
> > +			    int num_qids, u32 q_type)
> > +{
> > +	/* stub */
> > +	return 0;
> > +}
> > +
> > +/**
> > + * iecm_vport_queue_ids_init - Initialize queue ids from Mailbox parameters
> > + * @vport: virtual port for which the queues ids are initialized
> > + *
> > + * Will initialize all queue ids with ids received as mailbox parameters.
> > + * Returns 0 on success, negative if all the queues are not initialized.
> > + */
> > +static int iecm_vport_queue_ids_init(struct iecm_vport *vport)
> > +{
> > +	struct virtchnl2_create_vport *vport_params;
> > +	struct virtchnl2_queue_reg_chunks *chunks;
> > +	/* We may never deal with more than 256 same type of queues */
> > +#define IECM_MAX_QIDS	256
> > +	u32 qids[IECM_MAX_QIDS];
> > +	int num_ids;
> > +	u16 q_type;
> > +
> > +	if (vport->adapter->config_data.req_qs_chunks) {
> > +		struct virtchnl2_add_queues *vc_aq =
> > +			(struct virtchnl2_add_queues *)
> > +			vport->adapter->config_data.req_qs_chunks;
> > +		chunks = &vc_aq->chunks;
> > +	} else {
> > +		vport_params = (struct virtchnl2_create_vport *)
> > +				vport->adapter->vport_params_recvd[0];
> > +		chunks = &vport_params->chunks;
> > +	}
> > +
> > +	num_ids = iecm_vport_get_queue_ids(qids, IECM_MAX_QIDS,
> > +					   VIRTCHNL2_QUEUE_TYPE_TX,
> > +					   chunks);
> > +	if (num_ids != vport->num_txq)
> > +		return -EINVAL;
> > +	num_ids = __iecm_vport_queue_ids_init(vport, qids, num_ids,
> > +					      VIRTCHNL2_QUEUE_TYPE_TX);
> > +	if (num_ids != vport->num_txq)
> > +		return -EINVAL;
> > +	num_ids = iecm_vport_get_queue_ids(qids, IECM_MAX_QIDS,
> > +					   VIRTCHNL2_QUEUE_TYPE_RX,
> > +					   chunks);
> > +	if (num_ids != vport->num_rxq)
> > +		return -EINVAL;
> > +	num_ids = __iecm_vport_queue_ids_init(vport, qids, num_ids,
> > +					      VIRTCHNL2_QUEUE_TYPE_RX);
> > +	if (num_ids != vport->num_rxq)
> > +		return -EINVAL;
> > +
> > +	if (iecm_is_queue_model_split(vport->txq_model)) {
> > +		q_type = VIRTCHNL2_QUEUE_TYPE_TX_COMPLETION;
> > +		num_ids = iecm_vport_get_queue_ids(qids, IECM_MAX_QIDS,
> q_type,
> > +						   chunks);
> > +		if (num_ids != vport->num_complq)
> > +			return -EINVAL;
> > +		num_ids = __iecm_vport_queue_ids_init(vport, qids,
> > +						      num_ids,
> > +						      q_type);
> > +		if (num_ids != vport->num_complq)
> > +			return -EINVAL;
> > +	}
> > +
> > +	if (iecm_is_queue_model_split(vport->rxq_model)) {
> > +		q_type = VIRTCHNL2_QUEUE_TYPE_RX_BUFFER;
> > +		num_ids = iecm_vport_get_queue_ids(qids, IECM_MAX_QIDS,
> q_type,
> > +						   chunks);
> > +		if (num_ids != vport->num_bufq)
> > +			return -EINVAL;
> > +		num_ids = __iecm_vport_queue_ids_init(vport, qids, num_ids,
> > +						      q_type);
> > +		if (num_ids != vport->num_bufq)
> > +			return -EINVAL;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +/**
> > + * iecm_is_capability_ena - Default implementation of capability checking
> > + * @adapter: Private data struct
> > + * @all: all or one flag
> > + * @field: caps field to check for flags
> > + * @flag: flag to check
> > + *
> > + * Return true if all capabilities are supported, false otherwise
> > + */
> > +static bool iecm_is_capability_ena(struct iecm_adapter *adapter, bool all,
> > +				   enum iecm_cap_field field, u64 flag)
> > +{
> > +	u8 *caps = (u8 *)adapter->caps;
> > +	u32 *cap_field;
> > +
> > +	if (field == IECM_BASE_CAPS)
> > +		return false;
> > +	if (field >= IECM_CAP_FIELD_LAST) {
> > +		dev_err(&adapter->pdev->dev, "Bad capability field: %d\n",
> > +			field);
> > +		return false;
> > +	}
> > +	cap_field = (u32 *)(caps + field);
> > +
> > +	if (all)
> > +		return (*cap_field & flag) == flag;
> > +	else
> > +		return !!(*cap_field & flag);
> > +}
> > +
> > +/**
> > + * iecm_vc_ops_init - Initialize virtchnl common api
> > + * @adapter: Driver specific private structure
> > + *
> > + * Initialize the function pointers with the extended feature set functions
> > + * as APF will deal only with new set of opcodes.
> > + */
> > +void iecm_vc_ops_init(struct iecm_adapter *adapter)
> > +{
> > +	struct iecm_virtchnl_ops *vc_ops = &adapter->dev_ops.vc_ops;
> > +
> > +	vc_ops->core_init = iecm_vc_core_init;
> > +	vc_ops->vport_init = iecm_vport_init;
> > +	vc_ops->vport_queue_ids_init = iecm_vport_queue_ids_init;
> > +	vc_ops->get_caps = iecm_send_get_caps_msg;
> > +	vc_ops->is_cap_ena = iecm_is_capability_ena;
> > +	vc_ops->get_reserved_vecs = NULL;
> > +	vc_ops->config_queues = NULL;
> > +	vc_ops->enable_queues = NULL;
> > +	vc_ops->disable_queues = NULL;
> > +	vc_ops->add_queues = NULL;
> > +	vc_ops->delete_queues = NULL;
> > +	vc_ops->irq_map_unmap = NULL;
> > +	vc_ops->enable_vport = NULL;
> > +	vc_ops->disable_vport = NULL;
> > +	vc_ops->destroy_vport = NULL;
> > +	vc_ops->get_ptype = NULL;
> > +	vc_ops->get_set_rss_key = NULL;
> > +	vc_ops->get_set_rss_lut = NULL;
> > +	vc_ops->get_set_rss_hash = NULL;
> > +	vc_ops->adjust_qs = NULL;
> > +	vc_ops->add_del_vlans = NULL;
> > +	vc_ops->strip_vlan_msg = NULL;
> > +	vc_ops->insert_vlan_msg = NULL;
> > +	vc_ops->init_max_queues = NULL;
> > +	vc_ops->get_max_tx_bufs = NULL;
> > +	vc_ops->vportq_reg_init = NULL;
> > +	vc_ops->alloc_vectors = NULL;
> > +	vc_ops->dealloc_vectors = NULL;
> > +	vc_ops->get_supported_desc_ids = NULL;
> > +	vc_ops->get_stats_msg = NULL;
> > +	vc_ops->recv_mbx_msg = NULL;
> > +}
> > +EXPORT_SYMBOL(iecm_vc_ops_init);
> > diff --git a/drivers/net/ethernet/intel/include/iecm.h
> b/drivers/net/ethernet/intel/include/iecm.h
> > index ca9029224e06..994664dfe419 100644
> > --- a/drivers/net/ethernet/intel/include/iecm.h
> > +++ b/drivers/net/ethernet/intel/include/iecm.h
> > @@ -7,10 +7,13 @@
> >  #include <linux/aer.h>
> >  #include <linux/pci.h>
> >  #include <linux/netdevice.h>
> > +#include <linux/etherdevice.h>
> >  #include <linux/ethtool.h>
> > +#include <net/tcp.h>
> >  #include <linux/version.h>
> >  #include <linux/dim.h>
> >
> > +#include "virtchnl_2.h"
> >  #include "iecm_txrx.h"
> >  #include "iecm_controlq.h"
> >
> > @@ -35,10 +38,34 @@
> >  /* available message levels */
> >  #define IECM_AVAIL_NETIF_M (NETIF_MSG_DRV | NETIF_MSG_PROBE |
> NETIF_MSG_LINK)
> >
> > +#define IECM_VIRTCHNL_VERSION_MAJOR VIRTCHNL_VERSION_MAJOR_2
> > +#define IECM_VIRTCHNL_VERSION_MINOR VIRTCHNL_VERSION_MINOR_0
> > +
> >  /* Forward declaration */
> >  struct iecm_adapter;
> >  struct iecm_vport;
> >
> > +struct iecm_mac_filter {
> > +	struct list_head list;
> > +	u8 macaddr[ETH_ALEN];
> > +	bool remove;		/* filter needs to be removed */
> > +	bool add;		/* filter needs to be added */
> > +};
> > +
> > +#define IECM_VLAN(vid, tpid) ((struct iecm_vlan){ vid, tpid })
> > +
> > +struct iecm_vlan {
> > +	u16 vid;
> > +	u16 tpid;
> > +};
> > +
> > +struct iecm_vlan_filter {
> > +	struct list_head list;
> > +	struct iecm_vlan vlan;
> > +	bool remove;		/* filter needs to be removed */
> > +	bool add;		/* filter needs to be added */
> > +};
> > +
> >  enum iecm_state {
> >  	__IECM_STARTUP,
> >  	__IECM_VER_CHECK,
> > @@ -90,6 +117,24 @@ enum iecm_flags {
> >  	__IECM_FLAGS_NBITS,
> >  };
> >
> > +/* enum used to distinquish which capability field to check */
> > +enum iecm_cap_field {
> > +	IECM_BASE_CAPS		= -1,
> > +	IECM_CSUM_CAPS		= offsetof(struct
> virtchnl2_get_capabilities,
> > +					   csum_caps),
> > +	IECM_SEG_CAPS		= offsetof(struct
> virtchnl2_get_capabilities,
> > +					   seg_caps),
> > +	IECM_RSS_CAPS		= offsetof(struct
> virtchnl2_get_capabilities,
> > +					   rss_caps),
> > +	IECM_HSPLIT_CAPS	= offsetof(struct virtchnl2_get_capabilities,
> > +					   hsplit_caps),
> > +	IECM_RSC_CAPS		= offsetof(struct
> virtchnl2_get_capabilities,
> > +					   rsc_caps),
> > +	IECM_OTHER_CAPS		= offsetof(struct
> virtchnl2_get_capabilities,
> > +					   other_caps),
> > +	IECM_CAP_FIELD_LAST,
> > +};
> > +
> >  struct iecm_reset_reg {
> >  	u32 rstat;
> >  	u32 rstat_m;
> > @@ -105,14 +150,229 @@ struct iecm_reg_ops {
> >  			      enum iecm_flags trig_cause);
> >  };
> >
> > +struct iecm_virtchnl_ops {
> > +	int (*core_init)(struct iecm_adapter *adapter, int *vport_id);
> > +	void (*vport_init)(struct iecm_vport *vport, int vport_id);
> > +	int (*vport_queue_ids_init)(struct iecm_vport *vport);
> > +	int (*get_caps)(struct iecm_adapter *adapter);
> > +	int (*config_queues)(struct iecm_vport *vport);
> > +	int (*enable_queues)(struct iecm_vport *vport);
> > +	int (*disable_queues)(struct iecm_vport *vport);
> > +	int (*add_queues)(struct iecm_vport *vport, u16 num_tx_q,
> > +			  u16 num_complq, u16 num_rx_q,
> > +			  u16 num_rx_bufq);
> > +	int (*delete_queues)(struct iecm_vport *vport);
> > +	int (*irq_map_unmap)(struct iecm_vport *vport, bool map);
> > +	int (*enable_vport)(struct iecm_vport *vport);
> > +	int (*disable_vport)(struct iecm_vport *vport);
> > +	int (*destroy_vport)(struct iecm_vport *vport);
> > +	int (*get_ptype)(struct iecm_vport *vport);
> > +	int (*get_set_rss_key)(struct iecm_vport *vport, bool get);
> > +	int (*get_set_rss_lut)(struct iecm_vport *vport, bool get);
> > +	int (*get_set_rss_hash)(struct iecm_vport *vport, bool get);
> > +	void (*adjust_qs)(struct iecm_vport *vport);
> > +	int (*recv_mbx_msg)(struct iecm_adapter *adapter,
> > +			    void *msg, int msg_size,
> > +			    struct iecm_ctlq_msg *ctlq_msg, bool *work_done);
> > +	bool (*is_cap_ena)(struct iecm_adapter *adapter, bool all,
> > +			   enum iecm_cap_field field, u64 flag);
> > +	u16 (*get_reserved_vecs)(struct iecm_adapter *adapter);
> > +	void (*add_del_vlans)(struct iecm_vport *vport, bool add);
> > +	int (*strip_vlan_msg)(struct iecm_vport *vport, bool ena);
> > +	int (*insert_vlan_msg)(struct iecm_vport *vport, bool ena);
> > +	void (*init_max_queues)(struct iecm_adapter *adapter);
> > +	unsigned int (*get_max_tx_bufs)(struct iecm_adapter *adapter);
> > +	int (*vportq_reg_init)(struct iecm_vport *vport);
> > +	int (*alloc_vectors)(struct iecm_adapter *adapter, u16 num_vectors);
> > +	int (*dealloc_vectors)(struct iecm_adapter *adapter);
> > +	int (*get_supported_desc_ids)(struct iecm_vport *vport);
> > +	int (*get_stats_msg)(struct iecm_vport *vport);
> > +};
> > +
> >  struct iecm_dev_ops {
> >  	void (*reg_ops_init)(struct iecm_adapter *adapter);
> > +	void (*vc_ops_init)(struct iecm_adapter *adapter);
> >  	void (*crc_enable)(u64 *td_cmd);
> >  	struct iecm_reg_ops reg_ops;
> > +	struct iecm_virtchnl_ops vc_ops;
> > +};
> > +
> > +/* These macros allow us to generate an enum and a matching char * array
> of
> > + * stringified enums that are always in sync. Checkpatch issues a bogus
> warning
> > + * about this being a complex macro; but it's wrong, these are never used as a
> > + * statement and instead only used to define the enum and array.
> > + */
> > +#define IECM_FOREACH_VPORT_VC_STATE(STATE)	\
> > +	STATE(IECM_VC_ENA_VPORT)		\
> > +	STATE(IECM_VC_ENA_VPORT_ERR)		\
> > +	STATE(IECM_VC_DIS_VPORT)		\
> > +	STATE(IECM_VC_DIS_VPORT_ERR)		\
> > +	STATE(IECM_VC_DESTROY_VPORT)		\
> > +	STATE(IECM_VC_DESTROY_VPORT_ERR)	\
> > +	STATE(IECM_VC_CONFIG_TXQ)		\
> > +	STATE(IECM_VC_CONFIG_TXQ_ERR)		\
> > +	STATE(IECM_VC_CONFIG_RXQ)		\
> > +	STATE(IECM_VC_CONFIG_RXQ_ERR)		\
> > +	STATE(IECM_VC_CONFIG_Q)			\
> > +	STATE(IECM_VC_CONFIG_Q_ERR)		\
> > +	STATE(IECM_VC_ENA_QUEUES)		\
> > +	STATE(IECM_VC_ENA_QUEUES_ERR)		\
> > +	STATE(IECM_VC_DIS_QUEUES)		\
> > +	STATE(IECM_VC_DIS_QUEUES_ERR)		\
> > +	STATE(IECM_VC_ENA_CHANNELS)		\
> > +	STATE(IECM_VC_ENA_CHANNELS_ERR)		\
> > +	STATE(IECM_VC_DIS_CHANNELS)		\
> > +	STATE(IECM_VC_DIS_CHANNELS_ERR)		\
> > +	STATE(IECM_VC_MAP_IRQ)			\
> > +	STATE(IECM_VC_MAP_IRQ_ERR)		\
> > +	STATE(IECM_VC_UNMAP_IRQ)		\
> > +	STATE(IECM_VC_UNMAP_IRQ_ERR)		\
> > +	STATE(IECM_VC_ADD_QUEUES)		\
> > +	STATE(IECM_VC_ADD_QUEUES_ERR)		\
> > +	STATE(IECM_VC_DEL_QUEUES)		\
> > +	STATE(IECM_VC_REQUEST_QUEUES)		\
> > +	STATE(IECM_VC_REQUEST_QUEUES_ERR)	\
> > +	STATE(IECM_VC_DEL_QUEUES_ERR)		\
> > +	STATE(IECM_VC_ALLOC_VECTORS)		\
> > +	STATE(IECM_VC_ALLOC_VECTORS_ERR)	\
> > +	STATE(IECM_VC_DEALLOC_VECTORS)		\
> > +	STATE(IECM_VC_DEALLOC_VECTORS_ERR)	\
> > +	STATE(IECM_VC_SET_SRIOV_VFS)		\
> > +	STATE(IECM_VC_SET_SRIOV_VFS_ERR)	\
> > +	STATE(IECM_VC_GET_RSS_HASH)		\
> > +	STATE(IECM_VC_GET_RSS_HASH_ERR)		\
> > +	STATE(IECM_VC_SET_RSS_HASH)		\
> > +	STATE(IECM_VC_SET_RSS_HASH_ERR)		\
> > +	STATE(IECM_VC_GET_RSS_LUT)		\
> > +	STATE(IECM_VC_GET_RSS_LUT_ERR)		\
> > +	STATE(IECM_VC_SET_RSS_LUT)		\
> > +	STATE(IECM_VC_SET_RSS_LUT_ERR)		\
> > +	STATE(IECM_VC_GET_RSS_KEY)		\
> > +	STATE(IECM_VC_GET_RSS_KEY_ERR)		\
> > +	STATE(IECM_VC_SET_RSS_KEY)		\
> > +	STATE(IECM_VC_SET_RSS_KEY_ERR)		\
> > +	STATE(IECM_VC_GET_STATS)		\
> > +	STATE(IECM_VC_GET_STATS_ERR)		\
> > +	STATE(IECM_VC_ENA_STRIP_VLAN_TAG)	\
> > +	STATE(IECM_VC_ENA_STRIP_VLAN_TAG_ERR)	\
> > +	STATE(IECM_VC_DIS_STRIP_VLAN_TAG)	\
> > +	STATE(IECM_VC_DIS_STRIP_VLAN_TAG_ERR)	\
> > +	STATE(IECM_VC_IWARP_IRQ_MAP)		\
> > +	STATE(IECM_VC_IWARP_IRQ_MAP_ERR)	\
> > +	STATE(IECM_VC_ADD_ETH_ADDR)		\
> > +	STATE(IECM_VC_ADD_ETH_ADDR_ERR)		\
> > +	STATE(IECM_VC_DEL_ETH_ADDR)		\
> > +	STATE(IECM_VC_DEL_ETH_ADDR_ERR)		\
> > +	STATE(IECM_VC_PROMISC)			\
> > +	STATE(IECM_VC_ADD_CLOUD_FILTER)		\
> > +	STATE(IECM_VC_ADD_CLOUD_FILTER_ERR)	\
> > +	STATE(IECM_VC_DEL_CLOUD_FILTER)		\
> > +	STATE(IECM_VC_DEL_CLOUD_FILTER_ERR)	\
> > +	STATE(IECM_VC_ADD_RSS_CFG)		\
> > +	STATE(IECM_VC_ADD_RSS_CFG_ERR)		\
> > +	STATE(IECM_VC_DEL_RSS_CFG)		\
> > +	STATE(IECM_VC_DEL_RSS_CFG_ERR)		\
> > +	STATE(IECM_VC_ADD_FDIR_FILTER)		\
> > +	STATE(IECM_VC_ADD_FDIR_FILTER_ERR)	\
> > +	STATE(IECM_VC_DEL_FDIR_FILTER)		\
> > +	STATE(IECM_VC_DEL_FDIR_FILTER_ERR)	\
> > +	STATE(IECM_VC_OFFLOAD_VLAN_V2_CAPS)	\
> > +	STATE(IECM_VC_OFFLOAD_VLAN_V2_CAPS_ERR)	\
> > +	STATE(IECM_VC_INSERTION_ENA_VLAN_V2)	\
> > +	STATE(IECM_VC_INSERTION_ENA_VLAN_V2_ERR)\
> > +	STATE(IECM_VC_INSERTION_DIS_VLAN_V2)	\
> > +	STATE(IECM_VC_INSERTION_DIS_VLAN_V2_ERR)\
> > +	STATE(IECM_VC_STRIPPING_ENA_VLAN_V2)	\
> > +	STATE(IECM_VC_STRIPPING_ENA_VLAN_V2_ERR)\
> > +	STATE(IECM_VC_STRIPPING_DIS_VLAN_V2)	\
> > +	STATE(IECM_VC_STRIPPING_DIS_VLAN_V2_ERR)\
> > +	STATE(IECM_VC_GET_SUPPORTED_RXDIDS)	\
> > +	STATE(IECM_VC_GET_SUPPORTED_RXDIDS_ERR)	\
> > +	STATE(IECM_VC_GET_PTYPE_INFO)		\
> > +	STATE(IECM_VC_GET_PTYPE_INFO_ERR)	\
> > +	STATE(IECM_VC_NBITS)
> > +
> > +#define IECM_GEN_ENUM(ENUM) ENUM,
> > +#define IECM_GEN_STRING(STRING) #STRING,
> > +
> > +enum iecm_vport_vc_state {
> > +	IECM_FOREACH_VPORT_VC_STATE(IECM_GEN_ENUM)
> > +};
> > +
> > +extern const char * const iecm_vport_vc_state_str[];
> > +
> > +enum iecm_vport_flags {
> > +	__IECM_VPORT_INIT_PROMISC,
> > +	__IECM_VPORT_FLAGS_NBITS,
> > +};
> > +
> > +struct iecm_port_stats {
> > +	struct u64_stats_sync stats_sync;
> > +	u64 rx_hw_csum_err;
> > +	u64 rx_hsplit;
> > +	u64 rx_hsplit_hbo;
> > +	u64 tx_linearize;
> > +	u64 rx_bad_descs;
> > +	struct virtchnl2_vport_stats vport_stats;
> > +	struct virtchnl_eth_stats eth_stats;
> >  };
> >
> > -/* stub */
> >  struct iecm_vport {
> > +	/* TX */
> > +	int num_txq;
> > +	int num_complq;
> > +	/* It makes more sense for descriptor count to be part of only idpf
> > +	 * queue structure. But when user changes the count via ethtool, driver
> > +	 * has to store that value somewhere other than queue structure as the
> > +	 * queues will be freed and allocated again.
> > +	 */
> > +	int txq_desc_count;
> > +	int complq_desc_count;
> > +	int compln_clean_budget;
> > +	int num_txq_grp;
> > +	struct iecm_txq_group *txq_grps;
> > +	u32 txq_model;
> > +	/* Used only in hotpath to get to the right queue very fast */
> > +	struct iecm_queue **txqs;
> > +	DECLARE_BITMAP(flags, __IECM_VPORT_FLAGS_NBITS);
> > +
> > +	/* RX */
> > +	int num_rxq;
> > +	int num_bufq;
> > +	int rxq_desc_count;
> > +	u8 num_bufqs_per_qgrp;
> > +	int bufq_desc_count[IECM_MAX_BUFQS_PER_RXQ_GRP];
> > +	u32 bufq_size[IECM_MAX_BUFQS_PER_RXQ_GRP];
> > +	int num_rxq_grp;
> > +	struct iecm_rxq_group *rxq_grps;
> > +	u32 rxq_model;
> > +
> > +	struct iecm_adapter *adapter;
> > +	struct net_device *netdev;
> > +	u16 vport_type;
> > +	u16 vport_id;
> > +	u16 idx;		 /* software index in adapter vports struct */
> > +	bool base_rxd;
> 
> Bools are not really advised to use inside structures since they may
> have different sizes and alignments. There's a 2-byte hole here, so
> u8 or u16 for base_rxd would do the job.
> 

Will fix

> > +
> > +	/* handler for hard interrupt */
> > +	irqreturn_t (*irq_q_handler)(int irq, void *data);
> > +	struct iecm_q_vector *q_vectors;	/* q vector array */
> > +	u16 num_q_vectors;
> > +	u16 q_vector_base;
> > +	u16 max_mtu;
> > +	u8 default_mac_addr[ETH_ALEN];
> > +	u16 qset_handle;
> > +	/* ITR profiles for the DIM algorithm */
> > +#define IECM_DIM_PROFILE_SLOTS	5
> > +	u16 rx_itr_profile[IECM_DIM_PROFILE_SLOTS];
> > +	u16 tx_itr_profile[IECM_DIM_PROFILE_SLOTS];
> > +	struct rtnl_link_stats64 netstats;
> > +	struct iecm_port_stats port_stats;
> > +
> > +	/* lock to protect against multiple stop threads, which can happen when
> > +	 * the driver is in a namespace in a system that is being shutdown
> > +	 */
> > +	struct mutex stop_mutex;
> >  };
> >
> >  enum iecm_user_flags {
> > @@ -164,6 +424,7 @@ struct iecm_adapter {
> >  	u16 num_msix_entries;
> >  	struct msix_entry *msix_entries;
> >  	struct virtchnl2_alloc_vectors *req_vec_chunks;
> > +	struct iecm_q_vector mb_vector;
> >
> >  	/* vport structs */
> >  	struct iecm_vport **vports;	/* vports created by the driver */
> > @@ -190,6 +451,8 @@ struct iecm_adapter {
> >
> >  	wait_queue_head_t vchnl_wq;
> >  	wait_queue_head_t sw_marker_wq;
> > +	DECLARE_BITMAP(vc_state, IECM_VC_NBITS);
> > +	char vc_msg[IECM_DFLT_MBX_BUF_SIZE];
> >  	struct iecm_rss_data rss_data;
> >  	struct iecm_dev_ops dev_ops;
> >  	s32 link_speed;
> > @@ -215,6 +478,38 @@ struct iecm_adapter {
> >  	spinlock_t fdir_fltr_list_lock;
> >  };
> >
> > +/**
> > + * iecm_is_queue_model_split - check if queue model is split
> > + * @q_model: queue model single or split
> > + *
> > + * Returns true if queue model is split else false
> > + */
> > +static inline int iecm_is_queue_model_split(u16 q_model)
> > +{
> > +	return (q_model == VIRTCHNL2_QUEUE_MODEL_SPLIT);
> > +}
> > +
> > +#define iecm_is_cap_ena(adapter, field, flag) \
> > +	__iecm_is_cap_ena(adapter, false, field, flag)
> > +#define iecm_is_cap_ena_all(adapter, field, flag) \
> > +	__iecm_is_cap_ena(adapter, true, field, flag)
> > +/**
> > + * __iecm_is_cap_ena - Determine if HW capability is supported
> > + * @adapter: private data struct
> > + * @all: all or one flag
> > + * @field: cap field to check
> > + * @flag: Feature flag to check
> > + *
> > + * iecm_is_cap_ena_all is used to check if all the capability bits are set
> > + * ('AND' operation) where as iecm_is_cap_ena is used to check if
> > + * any one of the capability bits is set ('OR' operation)
> > + */
> > +static inline bool __iecm_is_cap_ena(struct iecm_adapter *adapter, bool all,
> > +				     enum iecm_cap_field field, u64 flag)
> > +{
> > +	return adapter->dev_ops.vc_ops.is_cap_ena(adapter, all, field, flag);
> > +}
> > +
> >  /**
> >   * iecm_is_reset_detected - check if we were reset at some point
> >   * @adapter: driver specific private structure
> > @@ -233,6 +528,25 @@ int iecm_probe(struct pci_dev *pdev,
> >  void iecm_remove(struct pci_dev *pdev);
> >  int iecm_init_dflt_mbx(struct iecm_adapter *adapter);
> >  void iecm_deinit_dflt_mbx(struct iecm_adapter *adapter);
> > +void iecm_vc_ops_init(struct iecm_adapter *adapter);
> > +int iecm_vc_core_init(struct iecm_adapter *adapter, int *vport_id);
> > +int iecm_wait_for_event(struct iecm_adapter *adapter,
> > +			enum iecm_vport_vc_state state,
> > +			enum iecm_vport_vc_state err_check);
> > +int iecm_min_wait_for_event(struct iecm_adapter *adapter,
> > +			    enum iecm_vport_vc_state state,
> > +			    enum iecm_vport_vc_state err_check);
> > +int iecm_send_get_caps_msg(struct iecm_adapter *adapter);
> >  int iecm_vport_params_buf_alloc(struct iecm_adapter *adapter);
> >  void iecm_vport_params_buf_rel(struct iecm_adapter *adapter);
> > +int iecm_get_vec_ids(struct iecm_adapter *adapter,
> > +		     u16 *vecids, int num_vecids,
> > +		     struct virtchnl2_vector_chunks *chunks);
> > +int iecm_recv_mb_msg(struct iecm_adapter *adapter, enum virtchnl_ops op,
> > +		     void *msg, int msg_size);
> > +int iecm_send_mb_msg(struct iecm_adapter *adapter, enum virtchnl_ops op,
> > +		     u16 msg_size, u8 *msg);
> > +int iecm_set_msg_pending(struct iecm_adapter *adapter,
> > +			 struct iecm_ctlq_msg *ctlq_msg,
> > +			 enum iecm_vport_vc_state err_enum);
> >  #endif /* !_IECM_H_ */
> > diff --git a/drivers/net/ethernet/intel/include/iecm_txrx.h
> b/drivers/net/ethernet/intel/include/iecm_txrx.h
> > index 602d3b3b19dd..e1348011c991 100644
> > --- a/drivers/net/ethernet/intel/include/iecm_txrx.h
> > +++ b/drivers/net/ethernet/intel/include/iecm_txrx.h
> > @@ -30,4 +30,98 @@
> >  #define IECM_DFLT_SPLITQ_RX_Q_GROUPS		4
> >  #define IECM_DFLT_SPLITQ_TXQ_PER_GROUP		1
> >  #define IECM_DFLT_SPLITQ_RXQ_PER_GROUP		1
> > +
> > +/* Default vector sharing */
> > +#define IECM_NONQ_VEC		1
> > +#define IECM_MAX_Q_VEC		4 /* For Tx Completion queue and Rx
> queue */
> > +#define IECM_MIN_Q_VEC		1
> > +#define IECM_MAX_RDMA_VEC	2 /* To share with RDMA */
> > +#define IECM_MIN_RDMA_VEC	1 /* Minimum vectors to be shared
> with RDMA */
> > +#define IECM_MIN_VEC		3 /* One for mailbox, one for data
> queues, one
> > +				   * for RDMA
> > +				   */
> > +
> > +#define IECM_DFLT_TX_Q_DESC_COUNT		512
> > +#define IECM_DFLT_TX_COMPLQ_DESC_COUNT		512
> > +#define IECM_DFLT_RX_Q_DESC_COUNT		512
> > +/* IMPORTANT: We absolutely _cannot_ have more buffers in the system
> than a
> > + * given RX completion queue has descriptors. This includes _ALL_ buffer
> > + * queues. E.g.: If you have two buffer queues of 512 descriptors and buffers,
> > + * you have a total of 1024 buffers so your RX queue _must_ have at least
> that
> > + * many descriptors. This macro divides a given number of RX descriptors by
> > + * number of buffer queues to calculate how many descriptors each buffer
> queue
> > + * can have without overrunning the RX queue.
> > + *
> > + * If you give hardware more buffers than completion descriptors what will
> > + * happen is that if hardware gets a chance to post more than ring wrap of
> > + * descriptors before SW gets an interrupt and overwrites SW head, the gen
> bit
> > + * in the descriptor will be wrong. Any overwritten descriptors' buffers will
> > + * be gone forever and SW has no reasonable way to tell that this has
> happened.
> > + * From SW perspective, when we finally get an interrupt, it looks like we're
> > + * still waiting for descriptor to be done, stalling forever.
> > + */
> > +#define IECM_RX_BUFQ_DESC_COUNT(RXD, NUM_BUFQ)	((RXD) /
> (NUM_BUFQ))
> > +
> > +#define IECM_RX_BUFQ_WORKING_SET(R)		((R)->desc_count - 1)
> > +#define IECM_RX_BUFQ_NON_WORKING_SET(R)		((R)-
> >desc_count - \
> > +
> IECM_RX_BUFQ_WORKING_SET(R))
> > +
> > +#define IECM_RX_HDR_SIZE			256
> > +#define IECM_RX_BUF_2048			2048
> > +#define IECM_RX_BUF_4096			4096
> > +#define IECM_RX_BUF_STRIDE			64
> > +#define IECM_LOW_WATERMARK			64
> > +#define IECM_HDR_BUF_SIZE			256
> > +#define IECM_PACKET_HDR_PAD	\
> > +	(ETH_HLEN + ETH_FCS_LEN + (VLAN_HLEN * 2))
> > +#define IECM_MAX_RXBUFFER			9728
> > +#define IECM_MAX_MTU		\
> > +	(IECM_MAX_RXBUFFER - IECM_PACKET_HDR_PAD)
> > +#define IECM_INT_NAME_STR_LEN	(IFNAMSIZ + 16)
> > +
> > +#define IECM_TX_COMPLQ_CLEAN_BUDGET	256
> > +
> > +struct iecm_intr_reg {
> > +	u32 dyn_ctl;
> > +	u32 dyn_ctl_intena_m;
> > +	u32 dyn_ctl_clrpba_m;
> > +	u32 dyn_ctl_itridx_s;
> > +	u32 dyn_ctl_itridx_m;
> > +	u32 dyn_ctl_intrvl_s;
> > +	u32 rx_itr;
> > +	u32 tx_itr;
> > +	u32 icr_ena;
> > +	u32 icr_ena_ctlq_m;
> > +};
> > +
> > +struct iecm_q_vector {
> > +	struct iecm_vport *vport;
> > +	cpumask_t affinity_mask;
> > +	struct napi_struct napi;
> > +	u16 v_idx;		/* index in the vport->q_vector array */
> > +	struct iecm_intr_reg intr_reg;
> > +
> > +	int num_txq;
> > +	struct iecm_queue **tx;
> > +	struct dim tx_dim;	/* data for net_dim algorithm */
> > +	u16 tx_itr_value;
> > +	bool tx_intr_mode;
> 
> Same here.
> 
> > +	u32 tx_itr_idx;
> > +
> > +	int num_rxq;
> > +	struct iecm_queue **rx;
> > +	struct dim rx_dim;	/* data for net_dim algorithm */
> > +	u16 rx_itr_value;
> > +	bool rx_intr_mode;
> 
> ...here as well, and most likely in more places I overlooked.
> 
> > +	u32 rx_itr_idx;
> > +
> > +	int num_bufq;
> > +	struct iecm_queue **bufq;
> > +
> > +	u16 total_events;       /* net_dim(): number of interrupts processed */
> > +	char name[IECM_INT_NAME_STR_LEN];
> > +};
> > +
> > +irqreturn_t
> > +iecm_vport_intr_clean_queues(int __always_unused irq, void *data);
> >  #endif /* !_IECM_TXRX_H_ */
> > --
> > 2.33.0
> 
> Thanks,
> Al


^ permalink raw reply	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 05/19] iecm: add vport alloc and virtchnl messages
  2022-01-28 12:39       ` Alexander Lobakin
@ 2022-02-02 22:23         ` Brady, Alan
  -1 siblings, 0 replies; 89+ messages in thread
From: Brady, Alan @ 2022-02-02 22:23 UTC (permalink / raw)
  To: intel-wired-lan

> -----Original Message-----
> From: Lobakin, Alexandr <alexandr.lobakin@intel.com>
> Sent: Friday, January 28, 2022 4:40 AM
> To: Brady, Alan <alan.brady@intel.com>
> Cc: Lobakin, Alexandr <alexandr.lobakin@intel.com>; intel-wired-
> lan at lists.osuosl.org; lkp <lkp@intel.com>; Burra, Phani R
> <phani.r.burra@intel.com>; Chittim, Madhu <madhu.chittim@intel.com>;
> kbuild-all at lists.01.org; Linga, Pavan Kumar <pavan.kumar.linga@intel.com>
> Subject: Re: [Intel-wired-lan] [PATCH net-next 05/19] iecm: add vport alloc and
> virtchnl messages
> 
> From: kernel test robot <lkp@intel.com>
> Date: Fri, 28 Jan 2022 12:19:10 +0800
> 
> > Hi Alan,
> >
> > Thank you for the patch! Perhaps something to improve:
> >
> > [auto build test WARNING on net-next/master]
> >
> > url:    https://github.com/0day-ci/linux/commits/Alan-Brady/Add-iecm-and-
> idpf/20220128-085513
> > base:   https://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next.git
> e2cf07654efb0fd7bbcb475c6f74be7b5755a8fd
> > config: i386-allyesconfig
> > (https://download.01.org/0day-ci/archive/20220128/202201281200.CI9u45b
> > S-lkp at intel.com/config)
> > compiler: gcc-9 (Debian 9.3.0-22) 9.3.0 reproduce (this is a W=1
> > build):
> >         # https://github.com/0day-
> ci/linux/commit/1233d9631b312eea5aebbce63590e27f9993bacc
> >         git remote add linux-review https://github.com/0day-ci/linux
> >         git fetch --no-tags linux-review Alan-Brady/Add-iecm-and-idpf/20220128-
> 085513
> >         git checkout 1233d9631b312eea5aebbce63590e27f9993bacc
> >         # save the config file to linux build tree
> >         mkdir build_dir
> >         make W=1 O=build_dir ARCH=i386 SHELL=/bin/bash
> > drivers/net/ethernet/intel/iecm/
> >
> > If you fix the issue, kindly add following tag as appropriate
> > Reported-by: kernel test robot <lkp@intel.com>
> >
> > All warnings (new ones prefixed by >>):
> >
> >    drivers/net/ethernet/intel/iecm/iecm_virtchnl.c: In function
> 'iecm_vport_queue_ids_init':
> > >> drivers/net/ethernet/intel/iecm/iecm_virtchnl.c:1396:1: warning:
> > >> the frame size of 1052 bytes is larger than 1024 bytes
> > >> [-Wframe-larger-than=]
> >     1396 | }
> >          | ^
> >
> >
> > vim +1396 drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> >
> >   1322
> >   1323	/**
> >   1324	 * iecm_vport_queue_ids_init - Initialize queue ids from Mailbox
> parameters
> >   1325	 * @vport: virtual port for which the queues ids are initialized
> >   1326	 *
> >   1327	 * Will initialize all queue ids with ids received as mailbox parameters.
> >   1328	 * Returns 0 on success, negative if all the queues are not initialized.
> >   1329	 */
> >   1330	static int iecm_vport_queue_ids_init(struct iecm_vport *vport)
> >   1331	{
> >   1332		struct virtchnl2_create_vport *vport_params;
> >   1333		struct virtchnl2_queue_reg_chunks *chunks;
> >   1334		/* We may never deal with more than 256 same type of queues
> */
> >   1335	#define IECM_MAX_QIDS	256
> >   1336		u32 qids[IECM_MAX_QIDS];
> 
> I'll elaborate on this warning a bit: you declare 4 * 256 = 1 Kb array directly on
> the stack here.
> 
> 1 Kb is a limit inside the kernel, it won't cause any issues since stacks on x86 are
> huge, but raises such warnings and we should listen to them, especially given
> that compile tests are now being done with -Werror.
> 
> Just use kzalloc(array_size(IECM_MAX_QIDS, sizeof(u32))) + kfree() here to avoid
> that.
> 

Will fix

> >   1337		int num_ids;
> >   1338		u16 q_type;
> >   1339
> >   1340		if (vport->adapter->config_data.req_qs_chunks) {
> >   1341			struct virtchnl2_add_queues *vc_aq =
> >   1342				(struct virtchnl2_add_queues *)
> >   1343				vport->adapter->config_data.req_qs_chunks;
> >   1344			chunks = &vc_aq->chunks;
> >   1345		} else {
> >   1346			vport_params = (struct virtchnl2_create_vport *)
> >   1347					vport->adapter-
> >vport_params_recvd[0];
> >   1348			chunks = &vport_params->chunks;
> >   1349		}
> >   1350
> >   1351		num_ids = iecm_vport_get_queue_ids(qids, IECM_MAX_QIDS,
> >   1352
> VIRTCHNL2_QUEUE_TYPE_TX,
> >   1353						   chunks);
> >   1354		if (num_ids != vport->num_txq)
> >   1355			return -EINVAL;
> >   1356		num_ids = __iecm_vport_queue_ids_init(vport, qids, num_ids,
> >   1357
> VIRTCHNL2_QUEUE_TYPE_TX);
> >   1358		if (num_ids != vport->num_txq)
> >   1359			return -EINVAL;
> >   1360		num_ids = iecm_vport_get_queue_ids(qids, IECM_MAX_QIDS,
> >   1361
> VIRTCHNL2_QUEUE_TYPE_RX,
> >   1362						   chunks);
> >   1363		if (num_ids != vport->num_rxq)
> >   1364			return -EINVAL;
> >   1365		num_ids = __iecm_vport_queue_ids_init(vport, qids, num_ids,
> >   1366
> VIRTCHNL2_QUEUE_TYPE_RX);
> >   1367		if (num_ids != vport->num_rxq)
> >   1368			return -EINVAL;
> >   1369
> >   1370		if (iecm_is_queue_model_split(vport->txq_model)) {
> >   1371			q_type = VIRTCHNL2_QUEUE_TYPE_TX_COMPLETION;
> >   1372			num_ids = iecm_vport_get_queue_ids(qids,
> IECM_MAX_QIDS, q_type,
> >   1373							   chunks);
> >   1374			if (num_ids != vport->num_complq)
> >   1375				return -EINVAL;
> >   1376			num_ids = __iecm_vport_queue_ids_init(vport, qids,
> >   1377							      num_ids,
> >   1378							      q_type);
> >   1379			if (num_ids != vport->num_complq)
> >   1380				return -EINVAL;
> >   1381		}
> >   1382
> >   1383		if (iecm_is_queue_model_split(vport->rxq_model)) {
> >   1384			q_type = VIRTCHNL2_QUEUE_TYPE_RX_BUFFER;
> >   1385			num_ids = iecm_vport_get_queue_ids(qids,
> IECM_MAX_QIDS, q_type,
> >   1386							   chunks);
> >   1387			if (num_ids != vport->num_bufq)
> >   1388				return -EINVAL;
> >   1389			num_ids = __iecm_vport_queue_ids_init(vport, qids,
> num_ids,
> >   1390							      q_type);
> >   1391			if (num_ids != vport->num_bufq)
> >   1392				return -EINVAL;
> >   1393		}
> >   1394
> >   1395		return 0;
> > > 1396	}
> >   1397
> >
> > ---
> > 0-DAY CI Kernel Test Service, Intel Corporation
> > https://lists.01.org/hyperkitty/list/kbuild-all at lists.01.org
> 
> Thanks,
> Al

^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [Intel-wired-lan] [PATCH net-next 05/19] iecm: add vport alloc and virtchnl messages
@ 2022-02-02 22:23         ` Brady, Alan
  0 siblings, 0 replies; 89+ messages in thread
From: Brady, Alan @ 2022-02-02 22:23 UTC (permalink / raw)
  To: kbuild-all

[-- Attachment #1: Type: text/plain, Size: 6146 bytes --]

> -----Original Message-----
> From: Lobakin, Alexandr <alexandr.lobakin@intel.com>
> Sent: Friday, January 28, 2022 4:40 AM
> To: Brady, Alan <alan.brady@intel.com>
> Cc: Lobakin, Alexandr <alexandr.lobakin@intel.com>; intel-wired-
> lan(a)lists.osuosl.org; lkp <lkp@intel.com>; Burra, Phani R
> <phani.r.burra@intel.com>; Chittim, Madhu <madhu.chittim@intel.com>;
> kbuild-all(a)lists.01.org; Linga, Pavan Kumar <pavan.kumar.linga@intel.com>
> Subject: Re: [Intel-wired-lan] [PATCH net-next 05/19] iecm: add vport alloc and
> virtchnl messages
> 
> From: kernel test robot <lkp@intel.com>
> Date: Fri, 28 Jan 2022 12:19:10 +0800
> 
> > Hi Alan,
> >
> > Thank you for the patch! Perhaps something to improve:
> >
> > [auto build test WARNING on net-next/master]
> >
> > url:    https://github.com/0day-ci/linux/commits/Alan-Brady/Add-iecm-and-
> idpf/20220128-085513
> > base:   https://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next.git
> e2cf07654efb0fd7bbcb475c6f74be7b5755a8fd
> > config: i386-allyesconfig
> > (https://download.01.org/0day-ci/archive/20220128/202201281200.CI9u45b
> > S-lkp(a)intel.com/config)
> > compiler: gcc-9 (Debian 9.3.0-22) 9.3.0 reproduce (this is a W=1
> > build):
> >         # https://github.com/0day-
> ci/linux/commit/1233d9631b312eea5aebbce63590e27f9993bacc
> >         git remote add linux-review https://github.com/0day-ci/linux
> >         git fetch --no-tags linux-review Alan-Brady/Add-iecm-and-idpf/20220128-
> 085513
> >         git checkout 1233d9631b312eea5aebbce63590e27f9993bacc
> >         # save the config file to linux build tree
> >         mkdir build_dir
> >         make W=1 O=build_dir ARCH=i386 SHELL=/bin/bash
> > drivers/net/ethernet/intel/iecm/
> >
> > If you fix the issue, kindly add following tag as appropriate
> > Reported-by: kernel test robot <lkp@intel.com>
> >
> > All warnings (new ones prefixed by >>):
> >
> >    drivers/net/ethernet/intel/iecm/iecm_virtchnl.c: In function
> 'iecm_vport_queue_ids_init':
> > >> drivers/net/ethernet/intel/iecm/iecm_virtchnl.c:1396:1: warning:
> > >> the frame size of 1052 bytes is larger than 1024 bytes
> > >> [-Wframe-larger-than=]
> >     1396 | }
> >          | ^
> >
> >
> > vim +1396 drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> >
> >   1322
> >   1323	/**
> >   1324	 * iecm_vport_queue_ids_init - Initialize queue ids from Mailbox
> parameters
> >   1325	 * @vport: virtual port for which the queues ids are initialized
> >   1326	 *
> >   1327	 * Will initialize all queue ids with ids received as mailbox parameters.
> >   1328	 * Returns 0 on success, negative if all the queues are not initialized.
> >   1329	 */
> >   1330	static int iecm_vport_queue_ids_init(struct iecm_vport *vport)
> >   1331	{
> >   1332		struct virtchnl2_create_vport *vport_params;
> >   1333		struct virtchnl2_queue_reg_chunks *chunks;
> >   1334		/* We may never deal with more than 256 same type of queues
> */
> >   1335	#define IECM_MAX_QIDS	256
> >   1336		u32 qids[IECM_MAX_QIDS];
> 
> I'll elaborate on this warning a bit: you declare 4 * 256 = 1 Kb array directly on
> the stack here.
> 
> 1 Kb is a limit inside the kernel, it won't cause any issues since stacks on x86 are
> huge, but raises such warnings and we should listen to them, especially given
> that compile tests are now being done with -Werror.
> 
> Just use kzalloc(array_size(IECM_MAX_QIDS, sizeof(u32))) + kfree() here to avoid
> that.
> 

Will fix

> >   1337		int num_ids;
> >   1338		u16 q_type;
> >   1339
> >   1340		if (vport->adapter->config_data.req_qs_chunks) {
> >   1341			struct virtchnl2_add_queues *vc_aq =
> >   1342				(struct virtchnl2_add_queues *)
> >   1343				vport->adapter->config_data.req_qs_chunks;
> >   1344			chunks = &vc_aq->chunks;
> >   1345		} else {
> >   1346			vport_params = (struct virtchnl2_create_vport *)
> >   1347					vport->adapter-
> >vport_params_recvd[0];
> >   1348			chunks = &vport_params->chunks;
> >   1349		}
> >   1350
> >   1351		num_ids = iecm_vport_get_queue_ids(qids, IECM_MAX_QIDS,
> >   1352
> VIRTCHNL2_QUEUE_TYPE_TX,
> >   1353						   chunks);
> >   1354		if (num_ids != vport->num_txq)
> >   1355			return -EINVAL;
> >   1356		num_ids = __iecm_vport_queue_ids_init(vport, qids, num_ids,
> >   1357
> VIRTCHNL2_QUEUE_TYPE_TX);
> >   1358		if (num_ids != vport->num_txq)
> >   1359			return -EINVAL;
> >   1360		num_ids = iecm_vport_get_queue_ids(qids, IECM_MAX_QIDS,
> >   1361
> VIRTCHNL2_QUEUE_TYPE_RX,
> >   1362						   chunks);
> >   1363		if (num_ids != vport->num_rxq)
> >   1364			return -EINVAL;
> >   1365		num_ids = __iecm_vport_queue_ids_init(vport, qids, num_ids,
> >   1366
> VIRTCHNL2_QUEUE_TYPE_RX);
> >   1367		if (num_ids != vport->num_rxq)
> >   1368			return -EINVAL;
> >   1369
> >   1370		if (iecm_is_queue_model_split(vport->txq_model)) {
> >   1371			q_type = VIRTCHNL2_QUEUE_TYPE_TX_COMPLETION;
> >   1372			num_ids = iecm_vport_get_queue_ids(qids,
> IECM_MAX_QIDS, q_type,
> >   1373							   chunks);
> >   1374			if (num_ids != vport->num_complq)
> >   1375				return -EINVAL;
> >   1376			num_ids = __iecm_vport_queue_ids_init(vport, qids,
> >   1377							      num_ids,
> >   1378							      q_type);
> >   1379			if (num_ids != vport->num_complq)
> >   1380				return -EINVAL;
> >   1381		}
> >   1382
> >   1383		if (iecm_is_queue_model_split(vport->rxq_model)) {
> >   1384			q_type = VIRTCHNL2_QUEUE_TYPE_RX_BUFFER;
> >   1385			num_ids = iecm_vport_get_queue_ids(qids,
> IECM_MAX_QIDS, q_type,
> >   1386							   chunks);
> >   1387			if (num_ids != vport->num_bufq)
> >   1388				return -EINVAL;
> >   1389			num_ids = __iecm_vport_queue_ids_init(vport, qids,
> num_ids,
> >   1390							      q_type);
> >   1391			if (num_ids != vport->num_bufq)
> >   1392				return -EINVAL;
> >   1393		}
> >   1394
> >   1395		return 0;
> > > 1396	}
> >   1397
> >
> > ---
> > 0-DAY CI Kernel Test Service, Intel Corporation
> > https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org
> 
> Thanks,
> Al

^ permalink raw reply	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 06/19] iecm: add virtchnl messages for queues
  2022-01-28 13:03   ` Alexander Lobakin
@ 2022-02-02 22:48     ` Brady, Alan
  2022-02-03 10:08       ` Maciej Fijalkowski
  2022-02-03 14:09       ` Alexander Lobakin
  0 siblings, 2 replies; 89+ messages in thread
From: Brady, Alan @ 2022-02-02 22:48 UTC (permalink / raw)
  To: intel-wired-lan

> -----Original Message-----
> From: Lobakin, Alexandr <alexandr.lobakin@intel.com>
> Sent: Friday, January 28, 2022 5:03 AM
> To: Brady, Alan <alan.brady@intel.com>
> Cc: Lobakin, Alexandr <alexandr.lobakin@intel.com>; intel-wired-
> lan at lists.osuosl.org; Linga, Pavan Kumar <pavan.kumar.linga@intel.com>;
> Chittim, Madhu <madhu.chittim@intel.com>; Burra, Phani R
> <phani.r.burra@intel.com>
> Subject: Re: [Intel-wired-lan] [PATCH net-next 06/19] iecm: add virtchnl
> messages for queues
> 
> From: Alan Brady <alan.brady@intel.com>
> Date: Thu, 27 Jan 2022 16:09:56 -0800
> 
> > This continues adding virtchnl messages. This largely relates to adding
> > messages needed to negotiate and setup traffic queues.
> >
> > Signed-off-by: Phani Burra <phani.r.burra@intel.com>
> > Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
> > Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
> > Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
> > Signed-off-by: Alice Michael <alice.michael@intel.com>
> > Signed-off-by: Alan Brady <alan.brady@intel.com>
> > ---
> >  drivers/net/ethernet/intel/iecm/iecm_lib.c    |   14 +
> >  drivers/net/ethernet/intel/iecm/iecm_txrx.c   |  161 +++
> >  .../net/ethernet/intel/iecm/iecm_virtchnl.c   | 1127 ++++++++++++++++-
> >  drivers/net/ethernet/intel/include/iecm.h     |   22 +
> >  .../net/ethernet/intel/include/iecm_txrx.h    |  196 +++
> >  5 files changed, 1505 insertions(+), 15 deletions(-)
> >
> > diff --git a/drivers/net/ethernet/intel/iecm/iecm_lib.c
> b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > index e2e523f0700e..4e9cc7f2d138 100644
> > --- a/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > +++ b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> 
> --- 8< ---
> 
> > +void iecm_vport_calc_num_q_desc(struct iecm_vport *vport)
> > +{
> > +	int num_req_txq_desc = vport->adapter-
> >config_data.num_req_txq_desc;
> > +	int num_req_rxq_desc = vport->adapter-
> >config_data.num_req_rxq_desc;
> > +	int num_bufqs = vport->num_bufqs_per_qgrp;
> > +	int i = 0;
> > +
> > +	vport->complq_desc_count = 0;
> > +	if (num_req_txq_desc) {
> > +		vport->txq_desc_count = num_req_txq_desc;
> > +		if (iecm_is_queue_model_split(vport->txq_model)) {
> > +			vport->complq_desc_count = num_req_txq_desc;
> > +			if (vport->complq_desc_count <
> IECM_MIN_TXQ_COMPLQ_DESC)
> > +				vport->complq_desc_count =
> > +					IECM_MIN_TXQ_COMPLQ_DESC;
> > +		}
> > +	} else {
> > +		vport->txq_desc_count =
> > +			IECM_DFLT_TX_Q_DESC_COUNT;
> > +		if (iecm_is_queue_model_split(vport->txq_model)) {
> > +			vport->complq_desc_count =
> > +				IECM_DFLT_TX_COMPLQ_DESC_COUNT;
> > +		}
> 
> Braces are redundant here since the path is a one-liner.
> 

Correct me if I'm wrong but believe the guidance here is if it goes beyond one line with line wrapping, it is optional whether or not to use braces, even if the statement is 'one line'. We have generally preferred to keep braces in multiline statements. However you do have a point that it is not consistent in this function. Will fix.

> > +	}
> > +
> > +	if (num_req_rxq_desc)
> > +		vport->rxq_desc_count = num_req_rxq_desc;
> > +	else
> > +		vport->rxq_desc_count = IECM_DFLT_RX_Q_DESC_COUNT;
> > +
> > +	for (i = 0; i < num_bufqs; i++) {
> > +		if (!vport->bufq_desc_count[i])
> > +			vport->bufq_desc_count[i] =
> > +				IECM_RX_BUFQ_DESC_COUNT(vport-
> >rxq_desc_count,
> > +							num_bufqs);
> 
> 		if (vport->bufq_desc_count[i])
> 			continue;
> 
> 		vport-> ...
> 
> -1 indent level with that.
> 
> > +	}
> > +}
> > +EXPORT_SYMBOL(iecm_vport_calc_num_q_desc);
> > +
> > +/**
> > + * iecm_vport_calc_total_qs - Calculate total number of queues
> > + * @adapter: private data struct
> > + * @vport_msg: message to fill with data
> > + */
> > +void iecm_vport_calc_total_qs(struct iecm_adapter *adapter,
> > +			      struct virtchnl2_create_vport *vport_msg)
> > +{
> > +	unsigned int num_req_tx_qs = adapter->config_data.num_req_tx_qs;
> > +	unsigned int num_req_rx_qs = adapter->config_data.num_req_rx_qs;
> > +	int dflt_splitq_txq_grps, dflt_singleq_txqs;
> > +	int dflt_splitq_rxq_grps, dflt_singleq_rxqs;
> > +	int num_txq_grps, num_rxq_grps;
> > +	int num_cpus;
> > +	u16 max_q;
> > +
> > +	/* Restrict num of queues to cpus online as a default configuration to
> > +	 * give best performance. User can always override to a max number
> > +	 * of queues via ethtool.
> > +	 */
> > +	num_cpus = num_online_cpus();
> > +	max_q = adapter->max_queue_limit;
> > +
> > +	dflt_splitq_txq_grps = min_t(int, max_q, num_cpus);
> > +	dflt_singleq_txqs = min_t(int, max_q, num_cpus);
> > +	dflt_splitq_rxq_grps = min_t(int, max_q, num_cpus);
> > +	dflt_singleq_rxqs = min_t(int, max_q, num_cpus);
> > +
> > +	if (iecm_is_queue_model_split(le16_to_cpu(vport_msg->txq_model))) {
> > +		num_txq_grps = num_req_tx_qs ? num_req_tx_qs :
> dflt_splitq_txq_grps;
> > +		vport_msg->num_tx_complq = cpu_to_le16(num_txq_grps *
> > +
> IECM_COMPLQ_PER_GROUP);
> > +		vport_msg->num_tx_q = cpu_to_le16(num_txq_grps *
> > +
> IECM_DFLT_SPLITQ_TXQ_PER_GROUP);
> > +	} else {
> > +		num_txq_grps = IECM_DFLT_SINGLEQ_TX_Q_GROUPS;
> > +		vport_msg->num_tx_q =
> > +				cpu_to_le16(num_txq_grps *
> > +					    (num_req_tx_qs ? num_req_tx_qs :
> > +					    dflt_singleq_txqs));
> > +		vport_msg->num_tx_complq = 0;
> > +	}
> > +	if (iecm_is_queue_model_split(le16_to_cpu(vport_msg->rxq_model))) {
> > +		num_rxq_grps = num_req_rx_qs ? num_req_rx_qs :
> dflt_splitq_rxq_grps;
> > +		vport_msg->num_rx_bufq =
> > +					cpu_to_le16(num_rxq_grps *
> > +
> IECM_MAX_BUFQS_PER_RXQ_GRP);
> > +
> > +		vport_msg->num_rx_q = cpu_to_le16(num_rxq_grps *
> > +
> IECM_DFLT_SPLITQ_RXQ_PER_GROUP);
> > +	} else {
> > +		num_rxq_grps = IECM_DFLT_SINGLEQ_RX_Q_GROUPS;
> > +		vport_msg->num_rx_bufq = 0;
> > +		vport_msg->num_rx_q =
> > +				cpu_to_le16(num_rxq_grps *
> > +					    (num_req_rx_qs ? num_req_rx_qs :
> > +					    dflt_singleq_rxqs));
> > +	}
> > +}
> > +
> > +/**
> > + * iecm_vport_calc_num_q_groups - Calculate number of queue groups
> > + * @vport: vport to calculate q groups for
> > + */
> > +void iecm_vport_calc_num_q_groups(struct iecm_vport *vport)
> > +{
> > +	if (iecm_is_queue_model_split(vport->txq_model))
> > +		vport->num_txq_grp = vport->num_txq;
> > +	else
> > +		vport->num_txq_grp = IECM_DFLT_SINGLEQ_TX_Q_GROUPS;
> > +
> > +	if (iecm_is_queue_model_split(vport->rxq_model))
> > +		vport->num_rxq_grp = vport->num_rxq;
> > +	else
> > +		vport->num_rxq_grp = IECM_DFLT_SINGLEQ_RX_Q_GROUPS;
> > +}
> > +EXPORT_SYMBOL(iecm_vport_calc_num_q_groups);
> > +
> > +/**
> > + * iecm_vport_calc_num_q_vec - Calculate total number of vectors required
> for
> > + * this vport
> > + * @vport: virtual port
> > + *
> > + */
> > +void iecm_vport_calc_num_q_vec(struct iecm_vport *vport)
> > +{
> > +	if (iecm_is_queue_model_split(vport->txq_model))
> > +		vport->num_q_vectors = vport->num_txq_grp;
> > +	else
> > +		vport->num_q_vectors = vport->num_txq;
> > +}
> > +EXPORT_SYMBOL(iecm_vport_calc_num_q_vec);
> > diff --git a/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> > index aae06064d706..d8152e657e24 100644
> > --- a/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> > +++ b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> > @@ -859,6 +859,48 @@ static int iecm_recv_get_caps_msg(struct
> iecm_adapter *adapter)
> >  				sizeof(struct virtchnl2_get_capabilities));
> >  }
> >
> > +/**
> > + * iecm_get_reg_intr_vecs - Get vector queue register offset
> > + * @vport: virtual port structure
> > + * @reg_vals: Register offsets to store in
> > + * @num_vecs: Number of vector registers
> > + *
> > + * Returns number of regsiters that got populated
> > + */
> > +int iecm_get_reg_intr_vecs(struct iecm_vport *vport,
> > +			   struct iecm_vec_regs *reg_vals, int num_vecs)
> > +{
> > +	struct virtchnl2_vector_chunks *chunks;
> > +	struct iecm_vec_regs reg_val;
> > +	u16 num_vchunks, num_vec;
> > +	int num_regs = 0, i, j;
> > +
> > +	chunks = &vport->adapter->req_vec_chunks->vchunks;
> > +	num_vchunks = le16_to_cpu(chunks->num_vchunks);
> > +
> > +	for (j = 0; j < num_vchunks; j++) {
> > +		struct virtchnl2_vector_chunk *chunk = &chunks->vchunks[j];
> > +
> > +		num_vec = le16_to_cpu(chunk->num_vectors);
> > +		reg_val.dyn_ctl_reg = le32_to_cpu(chunk->dynctl_reg_start);
> > +		reg_val.itrn_reg = le32_to_cpu(chunk->itrn_reg_start);
> > +		for (i = 0; i < num_vec; i++) {
> > +			if (num_regs == num_vecs)
> > +				break;
> > +			reg_vals[i].dyn_ctl_reg = reg_val.dyn_ctl_reg;
> > +			reg_vals[i].itrn_reg = reg_val.itrn_reg;
> > +			reg_val.dyn_ctl_reg +=
> > +				le32_to_cpu(chunk->dynctl_reg_spacing);
> > +			reg_val.itrn_reg +=
> > +				le32_to_cpu(chunk->itrn_reg_spacing);
> > +			num_regs++;
> > +		}
> > +	}
> > +
> > +	return num_regs;
> > +}
> > +EXPORT_SYMBOL(iecm_get_reg_intr_vecs);
> > +
> >  /**
> >   * iecm_send_create_vport_msg - Send virtchnl create vport message
> >   * @adapter: Driver specific private structure
> > @@ -869,8 +911,36 @@ static int iecm_recv_get_caps_msg(struct
> iecm_adapter *adapter)
> >   */
> >  static int iecm_send_create_vport_msg(struct iecm_adapter *adapter)
> >  {
> > -	/* stub */
> > -	return 0;
> > +	struct virtchnl2_create_vport *vport_msg;
> > +	int buf_size;
> > +
> > +	buf_size = sizeof(struct virtchnl2_create_vport);
> > +	if (!adapter->vport_params_reqd[0]) {
> > +		adapter->vport_params_reqd[0] = kzalloc(buf_size,
> GFP_KERNEL);
> > +		if (!adapter->vport_params_reqd[0])
> > +			return -ENOMEM;
> > +	}
> > +
> > +	vport_msg = (struct virtchnl2_create_vport *)
> > +			adapter->vport_params_reqd[0];
> > +	vport_msg->vport_type =
> cpu_to_le16(VIRTCHNL2_VPORT_TYPE_DEFAULT);
> > +
> > +	if (test_bit(__IECM_REQ_TX_SPLITQ, adapter->flags))
> > +		vport_msg->txq_model =
> cpu_to_le16(VIRTCHNL2_QUEUE_MODEL_SPLIT);
> > +	else
> > +		vport_msg->txq_model =
> cpu_to_le16(VIRTCHNL2_QUEUE_MODEL_SINGLE);
> > +
> > +	if (test_bit(__IECM_REQ_RX_SPLITQ, adapter->flags))
> > +		vport_msg->rxq_model =
> cpu_to_le16(VIRTCHNL2_QUEUE_MODEL_SPLIT);
> > +	else
> > +		vport_msg->rxq_model =
> cpu_to_le16(VIRTCHNL2_QUEUE_MODEL_SINGLE);
> > +
> > +	adapter->dev_ops.vc_ops.init_max_queues(adapter);
> > +
> > +	iecm_vport_calc_total_qs(adapter, vport_msg);
> > +
> > +	return iecm_send_mb_msg(adapter, VIRTCHNL2_OP_CREATE_VPORT,
> buf_size,
> > +				(u8 *)vport_msg);
> >  }
> >
> >  /**
> > @@ -884,7 +954,25 @@ static int iecm_send_create_vport_msg(struct
> iecm_adapter *adapter)
> >  static int iecm_recv_create_vport_msg(struct iecm_adapter *adapter,
> >  				      int *vport_id)
> >  {
> > -	/* stub */
> > +	struct virtchnl2_create_vport *vport_msg;
> > +	int err;
> > +
> > +	if (!adapter->vport_params_recvd[0]) {
> > +		adapter->vport_params_recvd[0] =
> kzalloc(IECM_DFLT_MBX_BUF_SIZE,
> > +							 GFP_KERNEL);
> > +		if (!adapter->vport_params_recvd[0])
> > +			return -ENOMEM;
> > +	}
> > +
> > +	vport_msg = (struct virtchnl2_create_vport *)
> > +			adapter->vport_params_recvd[0];
> > +
> > +	err = iecm_recv_mb_msg(adapter, VIRTCHNL2_OP_CREATE_VPORT,
> vport_msg,
> > +			       IECM_DFLT_MBX_BUF_SIZE);
> > +	if (err)
> > +		return err;
> > +
> > +	*vport_id = le32_to_cpu(vport_msg->vport_id);
> >  	return 0;
> >  }
> >
> > @@ -966,6 +1054,920 @@ int iecm_wait_for_event(struct iecm_adapter
> *adapter,
> >  }
> >  EXPORT_SYMBOL(iecm_wait_for_event);
> >
> > +/**
> > + * iecm_wait_for_marker_event - wait for software marker response
> > + * @vport: virtual port data structure
> > + *
> > + * Returns 0 success, negative on failure.
> > + **/
> > +static int iecm_wait_for_marker_event(struct iecm_vport *vport)
> > +{
> > +	int event = 0;
> > +	int i;
> > +
> > +	for (i = 0; i < vport->num_txq; i++)
> > +		set_bit(__IECM_Q_SW_MARKER, vport->txqs[i]->flags);
> > +
> > +	event = wait_event_timeout(vport->adapter->sw_marker_wq,
> > +				   test_and_clear_bit(__IECM_SW_MARKER,
> > +						      vport->adapter->flags),
> > +				   msecs_to_jiffies(500));
> > +	if (event)
> > +		return 0;
> > +	return -ETIMEDOUT;
> > +}
> > +
> > +/**
> > + * iecm_send_destroy_vport_msg - Send virtchnl destroy vport message
> > + * @vport: virtual port data structure
> > + *
> > + * Send virtchnl destroy vport message.  Returns 0 on success, negative on
> > + * failure.
> > + */
> > +int iecm_send_destroy_vport_msg(struct iecm_vport *vport)
> > +{
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	struct virtchnl2_vport v_id;
> > +	int err;
> > +
> > +	v_id.vport_id = cpu_to_le32(vport->vport_id);
> > +
> > +	err = iecm_send_mb_msg(adapter, VIRTCHNL2_OP_DESTROY_VPORT,
> > +			       sizeof(v_id), (u8 *)&v_id);
> > +	if (err)
> > +		return err;
> > +
> > +	return iecm_min_wait_for_event(adapter, IECM_VC_DESTROY_VPORT,
> > +				       IECM_VC_DESTROY_VPORT_ERR);
> > +}
> > +
> > +/**
> > + * iecm_send_enable_vport_msg - Send virtchnl enable vport message
> > + * @vport: virtual port data structure
> > + *
> > + * Send enable vport virtchnl message.  Returns 0 on success, negative on
> > + * failure.
> > + */
> > +int iecm_send_enable_vport_msg(struct iecm_vport *vport)
> > +{
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	struct virtchnl2_vport v_id;
> > +	int err;
> > +
> > +	v_id.vport_id = cpu_to_le32(vport->vport_id);
> > +
> > +	err = iecm_send_mb_msg(adapter, VIRTCHNL2_OP_ENABLE_VPORT,
> > +			       sizeof(v_id), (u8 *)&v_id);
> > +	if (err)
> > +		return err;
> > +
> > +	return iecm_wait_for_event(adapter, IECM_VC_ENA_VPORT,
> > +				   IECM_VC_ENA_VPORT_ERR);
> > +}
> > +
> > +/**
> > + * iecm_send_disable_vport_msg - Send virtchnl disable vport message
> > + * @vport: virtual port data structure
> > + *
> > + * Send disable vport virtchnl message.  Returns 0 on success, negative on
> > + * failure.
> > + */
> > +int iecm_send_disable_vport_msg(struct iecm_vport *vport)
> > +{
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	struct virtchnl2_vport v_id;
> > +	int err;
> > +
> > +	v_id.vport_id = cpu_to_le32(vport->vport_id);
> > +
> > +	err = iecm_send_mb_msg(adapter, VIRTCHNL2_OP_DISABLE_VPORT,
> > +			       sizeof(v_id), (u8 *)&v_id);
> > +	if (err)
> > +		return err;
> > +
> > +	return iecm_min_wait_for_event(adapter, IECM_VC_DIS_VPORT,
> > +				       IECM_VC_DIS_VPORT_ERR);
> > +}
> > +
> > +/**
> > + * iecm_send_config_tx_queues_msg - Send virtchnl config tx queues
> message
> > + * @vport: virtual port data structure
> > + *
> > + * Send config tx queues virtchnl message. Returns 0 on success, negative on
> > + * failure.
> > + */
> > +int iecm_send_config_tx_queues_msg(struct iecm_vport *vport)
> > +{
> > +	struct virtchnl2_config_tx_queues *ctq = NULL;
> > +	int config_data_size, chunk_size, buf_size = 0;
> > +	int totqs, num_msgs, num_chunks;
> > +	struct virtchnl2_txq_info *qi;
> > +	int err = 0, i, k = 0;
> > +	bool alloc = false;
> > +
> > +	totqs = vport->num_txq + vport->num_complq;
> > +	qi = kcalloc(totqs, sizeof(struct virtchnl2_txq_info), GFP_KERNEL);
> > +	if (!qi)
> > +		return -ENOMEM;
> > +
> > +	/* Populate the queue info buffer with all queue context info */
> > +	for (i = 0; i < vport->num_txq_grp; i++) {
> > +		struct iecm_txq_group *tx_qgrp = &vport->txq_grps[i];
> > +		int j;
> > +
> > +		for (j = 0; j < tx_qgrp->num_txq; j++, k++) {
> > +			qi[k].queue_id =
> > +				cpu_to_le32(tx_qgrp->txqs[j]->q_id);
> > +			qi[k].model =
> > +				cpu_to_le16(vport->txq_model);
> > +			qi[k].type =
> > +				cpu_to_le32(tx_qgrp->txqs[j]->q_type);
> > +			qi[k].ring_len =
> > +				cpu_to_le16(tx_qgrp->txqs[j]->desc_count);
> > +			qi[k].dma_ring_addr =
> > +				cpu_to_le64(tx_qgrp->txqs[j]->dma);
> > +			if (iecm_is_queue_model_split(vport->txq_model)) {
> > +				struct iecm_queue *q = tx_qgrp->txqs[j];
> > +
> > +				qi[k].tx_compl_queue_id =
> > +					cpu_to_le16(tx_qgrp->complq->q_id);
> > +
> > +				if (test_bit(__IECM_Q_FLOW_SCH_EN, q-
> >flags))
> > +					qi[k].sched_mode =
> > +
> 	cpu_to_le16(VIRTCHNL2_TXQ_SCHED_MODE_FLOW);
> > +				else
> > +					qi[k].sched_mode =
> > +
> 	cpu_to_le16(VIRTCHNL2_TXQ_SCHED_MODE_QUEUE);
> > +			} else {
> > +				qi[k].sched_mode =
> > +
> 	cpu_to_le16(VIRTCHNL2_TXQ_SCHED_MODE_QUEUE);
> > +			}
> > +		}
> > +
> > +		if (iecm_is_queue_model_split(vport->txq_model)) {
> > +			qi[k].queue_id =
> > +				cpu_to_le32(tx_qgrp->complq->q_id);
> > +			qi[k].model =
> > +				cpu_to_le16(vport->txq_model);
> > +			qi[k].type =
> > +				cpu_to_le32(tx_qgrp->complq->q_type);
> > +			qi[k].ring_len =
> > +				cpu_to_le16(tx_qgrp->complq->desc_count);
> > +			qi[k].dma_ring_addr =
> > +				cpu_to_le64(tx_qgrp->complq->dma);
> > +			k++;
> > +		}
> > +	}
> > +
> > +	/* Make sure accounting agrees */
> > +	if (k != totqs) {
> > +		err = -EINVAL;
> > +		goto error;
> > +	}
> > +
> > +	/* Chunk up the queue contexts into multiple messages to avoid
> > +	 * sending a control queue message buffer that is too large
> > +	 */
> > +	config_data_size = sizeof(struct virtchnl2_config_tx_queues);
> > +	chunk_size = sizeof(struct virtchnl2_txq_info);
> > +
> > +	num_chunks = IECM_NUM_CHUNKS_PER_MSG(config_data_size,
> chunk_size) + 1;
> > +	if (totqs < num_chunks)
> > +		num_chunks = totqs;
> > +
> > +	num_msgs = totqs / num_chunks;
> > +	if (totqs % num_chunks)
> > +		num_msgs++;
> > +
> > +	for (i = 0, k = 0; i < num_msgs; i++) {
> > +		if (!ctq || alloc) {
> > +			buf_size = (chunk_size * (num_chunks - 1)) +
> > +					config_data_size;
> > +			kfree(ctq);
> > +			ctq = kzalloc(buf_size, GFP_KERNEL);
> > +			if (!ctq) {
> > +				err = -ENOMEM;
> > +				goto error;
> > +			}
> > +		} else {
> > +			memset(ctq, 0, buf_size);
> > +		}
> > +
> > +		ctq->vport_id = cpu_to_le32(vport->vport_id);
> > +		ctq->num_qinfo = cpu_to_le16(num_chunks);
> > +		memcpy(ctq->qinfo, &qi[k], chunk_size * num_chunks);
> > +
> > +		err = iecm_send_mb_msg(vport->adapter,
> > +				       VIRTCHNL2_OP_CONFIG_TX_QUEUES,
> > +				       buf_size, (u8 *)ctq);
> > +		if (err)
> > +			goto mbx_error;
> > +
> > +		err = iecm_wait_for_event(vport->adapter,
> IECM_VC_CONFIG_TXQ,
> > +					  IECM_VC_CONFIG_TXQ_ERR);
> > +		if (err)
> > +			goto mbx_error;
> > +
> > +		k += num_chunks;
> > +		totqs -= num_chunks;
> > +		if (totqs < num_chunks) {
> > +			num_chunks = totqs;
> > +			alloc = true;
> > +		}
> > +	}
> > +
> > +mbx_error:
> > +	kfree(ctq);
> > +error:
> > +	kfree(qi);
> > +	return err;
> > +}
> > +
> > +/**
> > + * iecm_send_config_rx_queues_msg - Send virtchnl config rx queues
> message
> > + * @vport: virtual port data structure
> > + *
> > + * Send config rx queues virtchnl message.  Returns 0 on success, negative on
> > + * failure.
> > + */
> > +int iecm_send_config_rx_queues_msg(struct iecm_vport *vport)
> > +{
> > +	struct virtchnl2_config_rx_queues *crq = NULL;
> > +	int config_data_size, chunk_size, buf_size = 0;
> > +	int totqs, num_msgs, num_chunks;
> > +	struct virtchnl2_rxq_info *qi;
> > +	int err = 0, i, k = 0;
> > +	bool alloc = false;
> > +
> > +	totqs = vport->num_rxq + vport->num_bufq;
> > +	qi = kcalloc(totqs, sizeof(struct virtchnl2_rxq_info), GFP_KERNEL);
> > +	if (!qi)
> > +		return -ENOMEM;
> > +
> > +	/* Populate the queue info buffer with all queue context info */
> > +	for (i = 0; i < vport->num_rxq_grp; i++) {
> > +		struct iecm_rxq_group *rx_qgrp = &vport->rxq_grps[i];
> > +		int num_rxq;
> > +		int j;
> > +
> > +		if (iecm_is_queue_model_split(vport->rxq_model)) {
> > +			for (j = 0; j < vport->num_bufqs_per_qgrp; j++, k++) {
> > +				struct iecm_queue *bufq =
> > +					&rx_qgrp->splitq.bufq_sets[j].bufq;
> > +
> > +				qi[k].queue_id =
> > +					cpu_to_le32(bufq->q_id);
> > +				qi[k].model =
> > +					cpu_to_le16(vport->rxq_model);
> > +				qi[k].type =
> > +					cpu_to_le32(bufq->q_type);
> > +				qi[k].desc_ids =
> > +
> 	cpu_to_le64(VIRTCHNL2_RXDID_1_FLEX_SPLITQ_M);
> > +				qi[k].ring_len =
> > +					cpu_to_le16(bufq->desc_count);
> > +				qi[k].dma_ring_addr =
> > +					cpu_to_le64(bufq->dma);
> > +				qi[k].data_buffer_size =
> > +					cpu_to_le32(bufq->rx_buf_size);
> > +				qi[k].buffer_notif_stride =
> > +					bufq->rx_buf_stride;
> > +				qi[k].rx_buffer_low_watermark =
> > +					cpu_to_le16(bufq-
> >rx_buffer_low_watermark);
> > +			}
> > +		}
> 
> 		if (iecm_is_queue_model_split(vport->rxq_model))
> 			goto here;
> 
> -1 indent level for the for-loop.

I'm afraid I'm not following, please elaborate. Where are we goto'ing? The for loop below needs to be executed for both and if we just tack the above for loop at the bottom of the function and goto in and out of it to save an indent does not sound great and makes the code harder to follow IMO.

> Braces for 'if' are not needed since the for-loop has their own.
> 

They're not required but we have generally preferred to keep braces on statements extending across more than one line.

> > +
> > +		if (iecm_is_queue_model_split(vport->rxq_model))
> > +			num_rxq = rx_qgrp->splitq.num_rxq_sets;
> > +		else
> > +			num_rxq = rx_qgrp->singleq.num_rxq;
> > +
> > +		for (j = 0; j < num_rxq; j++, k++) {
> > +			struct iecm_queue *rxq;
> > +
> > +			if (iecm_is_queue_model_split(vport->rxq_model)) {
> > +				rxq = &rx_qgrp->splitq.rxq_sets[j]->rxq;
> > +				qi[k].rx_bufq1_id =
> > +				  cpu_to_le16(rxq->rxq_grp-
> >splitq.bufq_sets[0].bufq.q_id);
> > +				qi[k].rx_bufq2_id =
> > +				  cpu_to_le16(rxq->rxq_grp-
> >splitq.bufq_sets[1].bufq.q_id);
> > +				qi[k].hdr_buffer_size =
> > +					cpu_to_le16(rxq->rx_hbuf_size);
> > +				qi[k].rx_buffer_low_watermark =
> > +					cpu_to_le16(rxq-
> >rx_buffer_low_watermark);
> > +
> > +				if (rxq->rx_hsplit_en) {
> > +					qi[k].qflags =
> > +
> 	cpu_to_le16(VIRTCHNL2_RXQ_HDR_SPLIT);
> > +					qi[k].hdr_buffer_size =
> > +						cpu_to_le16(rxq-
> >rx_hbuf_size);
> > +				}
> > +			} else {
> > +				rxq = rx_qgrp->singleq.rxqs[j];
> > +			}
> 
> Same here, but with rxq = ... + goto.
> 

Please elaborate.

> > +
> > +			qi[k].queue_id =
> > +				cpu_to_le32(rxq->q_id);
> > +			qi[k].model =
> > +				cpu_to_le16(vport->rxq_model);
> > +			qi[k].type =
> > +				cpu_to_le32(rxq->q_type);
> > +			qi[k].ring_len =
> > +				cpu_to_le16(rxq->desc_count);
> > +			qi[k].dma_ring_addr =
> > +				cpu_to_le64(rxq->dma);
> > +			qi[k].max_pkt_size =
> > +				cpu_to_le32(rxq->rx_max_pkt_size);
> > +			qi[k].data_buffer_size =
> > +				cpu_to_le32(rxq->rx_buf_size);
> > +			qi[k].qflags |=
> > +
> 	cpu_to_le16(VIRTCHNL2_RX_DESC_SIZE_32BYTE);
> > +			qi[k].desc_ids =
> > +				cpu_to_le64(rxq->rxdids);
> > +		}
> > +	}
> > +
> > +	/* Make sure accounting agrees */
> > +	if (k != totqs) {
> > +		err = -EINVAL;
> > +		goto error;
> > +	}
> > +
> > +	/* Chunk up the queue contexts into multiple messages to avoid
> > +	 * sending a control queue message buffer that is too large
> > +	 */
> > +	config_data_size = sizeof(struct virtchnl2_config_rx_queues);
> > +	chunk_size = sizeof(struct virtchnl2_rxq_info);
> > +
> > +	num_chunks = IECM_NUM_CHUNKS_PER_MSG(config_data_size,
> chunk_size) + 1;
> > +	if (totqs < num_chunks)
> > +		num_chunks = totqs;
> > +
> > +	num_msgs = totqs / num_chunks;
> > +	if (totqs % num_chunks)
> > +		num_msgs++;
> > +
> > +	for (i = 0, k = 0; i < num_msgs; i++) {
> > +		if (!crq || alloc) {
> > +			buf_size = (chunk_size * (num_chunks - 1)) +
> > +					config_data_size;
> > +			kfree(crq);
> > +			crq = kzalloc(buf_size, GFP_KERNEL);
> > +			if (!crq) {
> > +				err = -ENOMEM;
> > +				goto error;
> > +			}
> > +		} else {
> > +			memset(crq, 0, buf_size);
> > +		}
> > +
> > +		crq->vport_id = cpu_to_le32(vport->vport_id);
> > +		crq->num_qinfo = cpu_to_le16(num_chunks);
> > +		memcpy(crq->qinfo, &qi[k], chunk_size * num_chunks);
> > +
> > +		err = iecm_send_mb_msg(vport->adapter,
> > +				       VIRTCHNL2_OP_CONFIG_RX_QUEUES,
> > +				       buf_size, (u8 *)crq);
> > +		if (err)
> > +			goto mbx_error;
> > +
> > +		err = iecm_wait_for_event(vport->adapter,
> IECM_VC_CONFIG_RXQ,
> > +					  IECM_VC_CONFIG_RXQ_ERR);
> > +		if (err)
> > +			goto mbx_error;
> > +
> > +		k += num_chunks;
> > +		totqs -= num_chunks;
> > +		if (totqs < num_chunks) {
> > +			num_chunks = totqs;
> > +			alloc = true;
> > +		}
> > +	}
> > +
> > +mbx_error:
> > +	kfree(crq);
> > +error:
> > +	kfree(qi);
> > +	return err;
> > +}
> > +
> > +/**
> > + * iecm_send_ena_dis_queues_msg - Send virtchnl enable or disable
> > + * queues message
> > + * @vport: virtual port data structure
> > + * @vc_op: virtchnl op code to send
> > + *
> > + * Send enable or disable queues virtchnl message. Returns 0 on success,
> > + * negative on failure.
> > + */
> > +static int iecm_send_ena_dis_queues_msg(struct iecm_vport *vport,
> > +					enum virtchnl_ops vc_op)
> > +{
> > +	int num_msgs, num_chunks, config_data_size, chunk_size;
> > +	int num_txq, num_rxq, num_q, buf_size, err = 0;
> > +	struct virtchnl2_del_ena_dis_queues *eq = NULL;
> > +	struct virtchnl2_queue_chunk *qc;
> > +	bool alloc = false;
> > +	int i, j, k = 0;
> > +
> > +	/* validate virtchnl op */
> > +	switch (vc_op) {
> > +	case VIRTCHNL2_OP_ENABLE_QUEUES:
> > +	case VIRTCHNL2_OP_DISABLE_QUEUES:
> > +		break;
> > +	default:
> > +		return -EINVAL;
> > +	}
> > +
> > +	num_txq = vport->num_txq + vport->num_complq;
> > +	num_rxq = vport->num_rxq + vport->num_bufq;
> > +	num_q = num_txq + num_rxq;
> > +	buf_size = sizeof(struct virtchnl2_queue_chunk) * (num_q);
> > +	qc = kzalloc(buf_size, GFP_KERNEL);
> > +	if (!qc)
> > +		return -ENOMEM;
> > +
> > +	for (i = 0; i < vport->num_txq_grp; i++) {
> > +		struct iecm_txq_group *tx_qgrp = &vport->txq_grps[i];
> > +
> > +		for (j = 0; j < tx_qgrp->num_txq; j++, k++) {
> > +			qc[k].type = cpu_to_le32(tx_qgrp->txqs[j]->q_type);
> > +			qc[k].start_queue_id =
> > +					cpu_to_le32(tx_qgrp->txqs[j]->q_id);
> > +			qc[k].num_queues = cpu_to_le32(1);
> > +		}
> > +	}
> > +	if (vport->num_txq != k) {
> > +		err = -EINVAL;
> > +		goto error;
> > +	}
> > +
> > +	if (iecm_is_queue_model_split(vport->txq_model)) {
> > +		for (i = 0; i < vport->num_txq_grp; i++, k++) {
> > +			struct iecm_txq_group *tx_qgrp = &vport->txq_grps[i];
> > +
> > +			qc[k].type = cpu_to_le32(tx_qgrp->complq->q_type);
> > +			qc[k].start_queue_id =
> > +					cpu_to_le32(tx_qgrp->complq->q_id);
> > +			qc[k].num_queues = cpu_to_le32(1);
> > +		}
> > +		if (vport->num_complq != (k - vport->num_txq)) {
> > +			err = -EINVAL;
> > +			goto error;
> > +		}
> > +	}
> 
> ...and here.
> 
> > +
> > +	for (i = 0; i < vport->num_rxq_grp; i++) {
> > +		struct iecm_rxq_group *rx_qgrp = &vport->rxq_grps[i];
> > +
> > +		if (iecm_is_queue_model_split(vport->rxq_model))
> > +			num_rxq = rx_qgrp->splitq.num_rxq_sets;
> > +		else
> > +			num_rxq = rx_qgrp->singleq.num_rxq;
> > +
> > +		for (j = 0; j < num_rxq; j++, k++) {
> > +			if (iecm_is_queue_model_split(vport->rxq_model)) {
> > +				qc[k].start_queue_id =
> > +				cpu_to_le32(rx_qgrp->splitq.rxq_sets[j]-
> >rxq.q_id);
> > +				qc[k].type =
> > +				cpu_to_le32(rx_qgrp->splitq.rxq_sets[j]-
> >rxq.q_type);
> > +			} else {
> > +				qc[k].start_queue_id =
> > +				cpu_to_le32(rx_qgrp->singleq.rxqs[j]->q_id);
> > +				qc[k].type =
> > +				cpu_to_le32(rx_qgrp->singleq.rxqs[j]->q_type);
> > +			}
> > +			qc[k].num_queues = cpu_to_le32(1);
> > +		}
> > +	}
> > +	if (vport->num_rxq != k - (vport->num_txq + vport->num_complq)) {
> > +		err = -EINVAL;
> > +		goto error;
> > +	}
> > +
> > +	if (iecm_is_queue_model_split(vport->rxq_model)) {
> > +		for (i = 0; i < vport->num_rxq_grp; i++) {
> > +			struct iecm_rxq_group *rx_qgrp = &vport->rxq_grps[i];
> > +
> > +			for (j = 0; j < vport->num_bufqs_per_qgrp; j++, k++) {
> > +				struct iecm_queue *q = &rx_qgrp-
> >splitq.bufq_sets[j].bufq;
> > +
> > +				qc[k].type = cpu_to_le32(q->q_type);
> > +				qc[k].start_queue_id = cpu_to_le32(q->q_id);
> > +				qc[k].num_queues = cpu_to_le32(1);
> > +			}
> > +		}
> > +		if (vport->num_bufq != k - (vport->num_txq +
> > +					       vport->num_complq +
> > +					       vport->num_rxq)) {
> > +			err = -EINVAL;
> > +			goto error;
> > +		}
> > +	}
> 
> ...and here.
> 
> > +
> > +	/* Chunk up the queue info into multiple messages */
> > +	config_data_size = sizeof(struct virtchnl2_del_ena_dis_queues);
> > +	chunk_size = sizeof(struct virtchnl2_queue_chunk);
> > +
> > +	num_chunks = IECM_NUM_CHUNKS_PER_MSG(config_data_size,
> chunk_size) + 1;
> > +	if (num_q < num_chunks)
> > +		num_chunks = num_q;
> > +
> > +	num_msgs = num_q / num_chunks;
> > +	if (num_q % num_chunks)
> > +		num_msgs++;
> > +
> > +	for (i = 0, k = 0; i < num_msgs; i++) {
> > +		if (!eq || alloc) {
> > +			buf_size = (chunk_size * (num_chunks - 1)) +
> > +					config_data_size;
> > +			kfree(eq);
> > +			eq = kzalloc(buf_size, GFP_KERNEL);
> > +			if (!eq) {
> > +				err = -ENOMEM;
> > +				goto error;
> > +			}
> > +		} else {
> > +			memset(eq, 0, buf_size);
> > +		}
> > +		eq->vport_id = cpu_to_le32(vport->vport_id);
> > +		eq->chunks.num_chunks = cpu_to_le16(num_chunks);
> > +		memcpy(eq->chunks.chunks, &qc[k], chunk_size *
> num_chunks);
> > +
> > +		err = iecm_send_mb_msg(vport->adapter, vc_op, buf_size,
> > +				       (u8 *)eq);
> > +		if (err)
> > +			goto mbx_error;
> > +		k += num_chunks;
> > +		num_q -= num_chunks;
> > +		if (num_q < num_chunks) {
> > +			num_chunks = num_q;
> > +			alloc = true;
> > +		}
> > +	}
> > +mbx_error:
> > +	kfree(eq);
> > +error:
> > +	kfree(qc);
> > +	return err;
> > +}
> > +
> > +/**
> > + * iecm_send_map_unmap_queue_vector_msg - Send virtchnl map or unmap
> queue
> > + * vector message
> > + * @vport: virtual port data structure
> > + * @map: true for map and false for unmap
> > + *
> > + * Send map or unmap queue vector virtchnl message.  Returns 0 on success,
> > + * negative on failure.
> > + */
> > +int iecm_send_map_unmap_queue_vector_msg(struct iecm_vport *vport,
> bool map)
> > +{
> > +	int num_msgs, num_chunks, config_data_size, chunk_size;
> > +	struct virtchnl2_queue_vector_maps *vqvm = NULL;
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	struct virtchnl2_queue_vector *vqv;
> > +	int buf_size, num_q, err = 0;
> > +	bool alloc = false;
> > +	int i, j, k = 0;
> > +
> > +	num_q = vport->num_txq + vport->num_rxq;
> > +
> > +	buf_size = sizeof(struct virtchnl2_queue_vector) * num_q;
> > +	vqv = kzalloc(buf_size, GFP_KERNEL);
> > +	if (!vqv)
> > +		return -ENOMEM;
> > +
> > +	for (i = 0; i < vport->num_txq_grp; i++) {
> > +		struct iecm_txq_group *tx_qgrp = &vport->txq_grps[i];
> > +
> > +		for (j = 0; j < tx_qgrp->num_txq; j++, k++) {
> > +			vqv[k].queue_type = cpu_to_le32(tx_qgrp->txqs[j]-
> >q_type);
> > +			vqv[k].queue_id = cpu_to_le32(tx_qgrp->txqs[j]->q_id);
> > +
> > +			if (iecm_is_queue_model_split(vport->txq_model)) {
> > +				vqv[k].vector_id =
> > +				cpu_to_le16(tx_qgrp->complq->q_vector-
> >v_idx);
> > +				vqv[k].itr_idx =
> > +				cpu_to_le32(tx_qgrp->complq->q_vector-
> >tx_itr_idx);
> > +			} else {
> > +				vqv[k].vector_id =
> > +				cpu_to_le16(tx_qgrp->txqs[j]->q_vector-
> >v_idx);
> > +				vqv[k].itr_idx =
> > +				cpu_to_le32(tx_qgrp->txqs[j]->q_vector-
> >tx_itr_idx);
> > +			}
> > +		}
> > +	}
> > +
> > +	if (vport->num_txq != k) {
> > +		err = -EINVAL;
> > +		goto error;
> > +	}
> > +
> > +	for (i = 0; i < vport->num_rxq_grp; i++) {
> > +		struct iecm_rxq_group *rx_qgrp = &vport->rxq_grps[i];
> > +		int num_rxq;
> > +
> > +		if (iecm_is_queue_model_split(vport->rxq_model))
> > +			num_rxq = rx_qgrp->splitq.num_rxq_sets;
> > +		else
> > +			num_rxq = rx_qgrp->singleq.num_rxq;
> > +
> > +		for (j = 0; j < num_rxq; j++, k++) {
> > +			struct iecm_queue *rxq;
> > +
> > +			if (iecm_is_queue_model_split(vport->rxq_model))
> > +				rxq = &rx_qgrp->splitq.rxq_sets[j]->rxq;
> > +			else
> > +				rxq = rx_qgrp->singleq.rxqs[j];
> > +
> > +			vqv[k].queue_type = cpu_to_le32(rxq->q_type);
> > +			vqv[k].queue_id = cpu_to_le32(rxq->q_id);
> > +			vqv[k].vector_id = cpu_to_le16(rxq->q_vector->v_idx);
> > +			vqv[k].itr_idx = cpu_to_le32(rxq->q_vector->rx_itr_idx);
> > +		}
> > +	}
> > +
> > +	if (iecm_is_queue_model_split(vport->txq_model)) {
> > +		if (vport->num_rxq != k - vport->num_complq) {
> 
> 	if (iecm_is_queue_model_split() && vport->num_rxq != ...) {
> 
> > +			err = -EINVAL;
> > +			goto error;
> > +		}
> > +	} else {
> 
> Don't forget to convert this then into `!split + ...`, either with
> 'else' or not.
> 
> > +		if (vport->num_rxq != k - vport->num_txq) {
> > +			err = -EINVAL;
> > +			goto error;
> > +		}
> > +	}
> > +
> > +	/* Chunk up the vector info into multiple messages */
> > +	config_data_size = sizeof(struct virtchnl2_queue_vector_maps);
> > +	chunk_size = sizeof(struct virtchnl2_queue_vector);
> > +
> > +	num_chunks = IECM_NUM_CHUNKS_PER_MSG(config_data_size,
> chunk_size) + 1;
> > +	if (num_q < num_chunks)
> > +		num_chunks = num_q;
> > +
> > +	num_msgs = num_q / num_chunks;
> > +	if (num_q % num_chunks)
> > +		num_msgs++;
> > +
> > +	for (i = 0, k = 0; i < num_msgs; i++) {
> > +		if (!vqvm || alloc) {
> > +			buf_size = (chunk_size * (num_chunks - 1)) +
> > +					config_data_size;
> > +			kfree(vqvm);
> > +			vqvm = kzalloc(buf_size, GFP_KERNEL);
> > +			if (!vqvm) {
> > +				err = -ENOMEM;
> > +				goto error;
> > +			}
> > +		} else {
> > +			memset(vqvm, 0, buf_size);
> > +		}
> > +		vqvm->vport_id = cpu_to_le32(vport->vport_id);
> > +		vqvm->num_qv_maps = cpu_to_le16(num_chunks);
> > +		memcpy(vqvm->qv_maps, &vqv[k], chunk_size * num_chunks);
> > +
> > +		if (map) {
> > +			err = iecm_send_mb_msg(adapter,
> > +
> VIRTCHNL2_OP_MAP_QUEUE_VECTOR,
> > +					       buf_size, (u8 *)vqvm);
> > +			if (!err)
> > +				err = iecm_wait_for_event(adapter,
> > +							  IECM_VC_MAP_IRQ,
> > +
> IECM_VC_MAP_IRQ_ERR);
> > +		} else {
> > +			err = iecm_send_mb_msg(adapter,
> > +
> VIRTCHNL2_OP_UNMAP_QUEUE_VECTOR,
> > +					       buf_size, (u8 *)vqvm);
> > +			if (!err)
> > +				err =
> > +				iecm_min_wait_for_event(adapter,
> > +
> 	IECM_VC_UNMAP_IRQ,
> > +
> 	IECM_VC_UNMAP_IRQ_ERR);
> > +		}
> > +		if (err)
> > +			goto mbx_error;
> > +
> > +		k += num_chunks;
> > +		num_q -= num_chunks;
> > +		if (num_q < num_chunks) {
> > +			num_chunks = num_q;
> > +			alloc = true;
> > +		}
> > +	}
> > +mbx_error:
> > +	kfree(vqvm);
> > +error:
> > +	kfree(vqv);
> > +	return err;
> > +}
> > +EXPORT_SYMBOL(iecm_send_map_unmap_queue_vector_msg);
> > +
> > +/**
> > + * iecm_send_enable_queues_msg - send enable queues virtchnl message
> > + * @vport: Virtual port private data structure
> > + *
> > + * Will send enable queues virtchnl message.  Returns 0 on success, negative
> on
> > + * failure.
> > + */
> > +static int iecm_send_enable_queues_msg(struct iecm_vport *vport)
> > +{
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	int err;
> > +
> > +	err = iecm_send_ena_dis_queues_msg(vport,
> > +					   VIRTCHNL2_OP_ENABLE_QUEUES);
> > +	if (err)
> > +		return err;
> > +
> > +	return iecm_wait_for_event(adapter, IECM_VC_ENA_QUEUES,
> > +				   IECM_VC_ENA_QUEUES_ERR);
> > +}
> > +
> > +/**
> > + * iecm_send_disable_queues_msg - send disable queues virtchnl message
> > + * @vport: Virtual port private data structure
> > + *
> > + * Will send disable queues virtchnl message.  Returns 0 on success, negative
> > + * on failure.
> > + */
> > +static int iecm_send_disable_queues_msg(struct iecm_vport *vport)
> > +{
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	int err;
> > +
> > +	err = iecm_send_ena_dis_queues_msg(vport,
> > +					   VIRTCHNL2_OP_DISABLE_QUEUES);
> > +	if (err)
> > +		return err;
> > +
> > +	err = iecm_min_wait_for_event(adapter, IECM_VC_DIS_QUEUES,
> > +				      IECM_VC_DIS_QUEUES_ERR);
> > +	if (err)
> > +		return err;
> > +
> > +	return iecm_wait_for_marker_event(vport);
> > +}
> > +
> > +/**
> > + * iecm_convert_reg_to_queue_chunks - Copy queue chunk information to
> the right
> > + * structure
> > + * @dchunks: Destination chunks to store data to
> > + * @schunks: Source chunks to copy data from
> > + * @num_chunks: number of chunks to copy
> > + */
> > +static void
> > +iecm_convert_reg_to_queue_chunks(struct virtchnl2_queue_chunk
> *dchunks,
> > +				 struct virtchnl2_queue_reg_chunk *schunks,
> > +				 u16 num_chunks)
> > +{
> > +	u16 i;
> > +
> > +	for (i = 0; i < num_chunks; i++) {
> > +		dchunks[i].type = schunks[i].type;
> > +		dchunks[i].start_queue_id = schunks[i].start_queue_id;
> > +		dchunks[i].num_queues = schunks[i].num_queues;
> > +	}
> > +}
> > +
> > +/**
> > + * iecm_send_delete_queues_msg - send delete queues virtchnl message
> > + * @vport: Virtual port private data structure
> > + *
> > + * Will send delete queues virtchnl message. Return 0 on success, negative on
> > + * failure.
> > + */
> > +int iecm_send_delete_queues_msg(struct iecm_vport *vport)
> > +{
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	struct virtchnl2_create_vport *vport_params;
> > +	struct virtchnl2_queue_reg_chunks *chunks;
> > +	struct virtchnl2_del_ena_dis_queues *eq;
> > +	int buf_size, err;
> > +	u16 num_chunks;
> > +
> > +	if (vport->adapter->config_data.req_qs_chunks) {
> > +		struct virtchnl2_add_queues *vc_aq =
> > +			(struct virtchnl2_add_queues *)
> > +			vport->adapter->config_data.req_qs_chunks;
> > +		chunks = &vc_aq->chunks;
> > +	} else {
> > +		vport_params = (struct virtchnl2_create_vport *)
> > +				vport->adapter->vport_params_recvd[0];
> > +		 chunks = &vport_params->chunks;
> > +	}
> > +
> > +	num_chunks = le16_to_cpu(chunks->num_chunks);
> > +	buf_size = sizeof(struct virtchnl2_del_ena_dis_queues) +
> > +			  (sizeof(struct virtchnl2_queue_chunk) *
> > +			  (num_chunks - 1));
> > +
> > +	eq = kzalloc(buf_size, GFP_KERNEL);
> > +	if (!eq)
> > +		return -ENOMEM;
> > +
> > +	eq->vport_id = cpu_to_le32(vport->vport_id);
> > +	eq->chunks.num_chunks = cpu_to_le16(num_chunks);
> > +
> > +	iecm_convert_reg_to_queue_chunks(eq->chunks.chunks, chunks-
> >chunks,
> > +					 num_chunks);
> > +
> > +	err = iecm_send_mb_msg(vport->adapter,
> VIRTCHNL2_OP_DEL_QUEUES,
> > +			       buf_size, (u8 *)eq);
> > +	if (err)
> > +		goto error;
> > +
> > +	err = iecm_min_wait_for_event(adapter, IECM_VC_DEL_QUEUES,
> > +				      IECM_VC_DEL_QUEUES_ERR);
> > +error:
> > +	kfree(eq);
> > +	return err;
> > +}
> > +
> > +/**
> > + * iecm_send_config_queues_msg - Send config queues virtchnl message
> > + * @vport: Virtual port private data structure
> > + *
> > + * Will send config queues virtchnl message. Returns 0 on success, negative
> on
> > + * failure.
> > + */
> > +static int iecm_send_config_queues_msg(struct iecm_vport *vport)
> > +{
> > +	int err;
> > +
> > +	err = iecm_send_config_tx_queues_msg(vport);
> > +	if (err)
> > +		return err;
> > +
> > +	return iecm_send_config_rx_queues_msg(vport);
> > +}
> > +
> > +/**
> > + * iecm_send_add_queues_msg - Send virtchnl add queues message
> > + * @vport: Virtual port private data structure
> > + * @num_tx_q: number of transmit queues
> > + * @num_complq: number of transmit completion queues
> > + * @num_rx_q: number of receive queues
> > + * @num_rx_bufq: number of receive buffer queues
> > + *
> > + * Returns 0 on success, negative on failure.
> > + */
> > +int iecm_send_add_queues_msg(struct iecm_vport *vport, u16 num_tx_q,
> > +			     u16 num_complq, u16 num_rx_q, u16 num_rx_bufq)
> > +{
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	struct virtchnl2_add_queues aq = {0};
> > +	struct virtchnl2_add_queues *vc_msg;
> > +	int size, err;
> > +
> > +	vc_msg = (struct virtchnl2_add_queues *)adapter->vc_msg;
> > +
> > +	aq.vport_id = cpu_to_le32(vport->vport_id);
> > +	aq.num_tx_q = cpu_to_le16(num_tx_q);
> > +	aq.num_tx_complq = cpu_to_le16(num_complq);
> > +	aq.num_rx_q = cpu_to_le16(num_rx_q);
> > +	aq.num_rx_bufq = cpu_to_le16(num_rx_bufq);
> > +
> > +	err = iecm_send_mb_msg(adapter,
> > +			       VIRTCHNL2_OP_ADD_QUEUES,
> > +			       sizeof(struct virtchnl2_add_queues), (u8 *)&aq);
> > +	if (err)
> > +		return err;
> > +
> > +	err = iecm_wait_for_event(adapter, IECM_VC_ADD_QUEUES,
> > +				  IECM_VC_ADD_QUEUES_ERR);
> > +	if (err)
> > +		return err;
> > +
> > +	kfree(adapter->config_data.req_qs_chunks);
> > +	adapter->config_data.req_qs_chunks = NULL;
> > +
> > +	/* compare vc_msg num queues with vport num queues */
> > +	if (le16_to_cpu(vc_msg->num_tx_q) != num_tx_q ||
> > +	    le16_to_cpu(vc_msg->num_rx_q) != num_rx_q ||
> > +	    le16_to_cpu(vc_msg->num_tx_complq) != num_complq ||
> > +	    le16_to_cpu(vc_msg->num_rx_bufq) != num_rx_bufq) {
> > +		err = -EINVAL;
> > +		goto error;
> > +	}
> > +
> > +	size = sizeof(struct virtchnl2_add_queues) +
> > +			((le16_to_cpu(vc_msg->chunks.num_chunks) - 1) *
> > +			sizeof(struct virtchnl2_queue_reg_chunk));
> > +	adapter->config_data.req_qs_chunks =
> > +		kzalloc(size, GFP_KERNEL);
> > +	if (!adapter->config_data.req_qs_chunks) {
> > +		err = -ENOMEM;
> > +		goto error;
> > +	}
> > +	memcpy(adapter->config_data.req_qs_chunks,
> > +	       adapter->vc_msg, size);
> > +error:
> > +	clear_bit(__IECM_VC_MSG_PENDING, adapter->flags);
> > +	return err;
> > +}
> > +
> >  /**
> >   * iecm_find_ctlq - Given a type and id, find ctlq info
> >   * @hw: hardware struct
> > @@ -1217,6 +2219,13 @@ static void iecm_vport_init(struct iecm_vport
> *vport,
> >  	/*Initialize Tx and Rx profiles for Dynamic Interrupt Moderation */
> >  	memcpy(vport->rx_itr_profile, rx_itr, IECM_DIM_PROFILE_SLOTS);
> >  	memcpy(vport->tx_itr_profile, tx_itr, IECM_DIM_PROFILE_SLOTS);
> > +
> > +	iecm_vport_set_hsplit(vport, true);
> > +
> > +	iecm_vport_init_num_qs(vport, vport_msg);
> > +	iecm_vport_calc_num_q_desc(vport);
> > +	iecm_vport_calc_num_q_groups(vport);
> > +	iecm_vport_calc_num_q_vec(vport);
> >  }
> >
> >  /**
> > @@ -1316,8 +2325,82 @@ static int
> >  __iecm_vport_queue_ids_init(struct iecm_vport *vport, u32 *qids,
> >  			    int num_qids, u32 q_type)
> >  {
> > -	/* stub */
> > -	return 0;
> > +	struct iecm_queue *q;
> > +	int i, j, k = 0;
> > +
> > +	switch (q_type) {
> > +	case VIRTCHNL2_QUEUE_TYPE_TX:
> > +		for (i = 0; i < vport->num_txq_grp; i++) {
> > +			struct iecm_txq_group *tx_qgrp = &vport->txq_grps[i];
> > +
> > +			for (j = 0; j < tx_qgrp->num_txq; j++) {
> > +				if (k < num_qids) {
> > +					tx_qgrp->txqs[j]->q_id = qids[k];
> > +					tx_qgrp->txqs[j]->q_type =
> > +						VIRTCHNL2_QUEUE_TYPE_TX;
> > +					k++;
> > +				} else {
> > +					break;
> > +				}
> > +			}
> > +		}
> > +		break;
> > +	case VIRTCHNL2_QUEUE_TYPE_RX:
> > +		for (i = 0; i < vport->num_rxq_grp; i++) {
> > +			struct iecm_rxq_group *rx_qgrp = &vport->rxq_grps[i];
> > +			int num_rxq;
> > +
> > +			if (iecm_is_queue_model_split(vport->rxq_model))
> > +				num_rxq = rx_qgrp->splitq.num_rxq_sets;
> > +			else
> > +				num_rxq = rx_qgrp->singleq.num_rxq;
> > +
> > +			for (j = 0; j < num_rxq && k < num_qids; j++, k++) {
> > +				if (iecm_is_queue_model_split(vport-
> >rxq_model))
> > +					q = &rx_qgrp->splitq.rxq_sets[j]->rxq;
> > +				else
> > +					q = rx_qgrp->singleq.rxqs[j];
> > +				q->q_id = qids[k];
> > +				q->q_type = VIRTCHNL2_QUEUE_TYPE_RX;
> > +			}
> > +		}
> > +		break;
> > +	case VIRTCHNL2_QUEUE_TYPE_TX_COMPLETION:
> > +		for (i = 0; i < vport->num_txq_grp; i++) {
> > +			struct iecm_txq_group *tx_qgrp = &vport->txq_grps[i];
> > +
> > +			if (k < num_qids) {
> > +				tx_qgrp->complq->q_id = qids[k];
> > +				tx_qgrp->complq->q_type =
> > +
> 	VIRTCHNL2_QUEUE_TYPE_TX_COMPLETION;
> > +				k++;
> > +			} else {
> > +				break;
> > +			}
> > +		}
> > +		break;
> > +	case VIRTCHNL2_QUEUE_TYPE_RX_BUFFER:
> > +		for (i = 0; i < vport->num_rxq_grp; i++) {
> > +			struct iecm_rxq_group *rx_qgrp = &vport->rxq_grps[i];
> > +
> > +			for (j = 0; j < vport->num_bufqs_per_qgrp; j++) {
> > +				if (k < num_qids) {
> > +					q = &rx_qgrp->splitq.bufq_sets[j].bufq;
> > +					q->q_id = qids[k];
> > +					q->q_type =
> > +
> 	VIRTCHNL2_QUEUE_TYPE_RX_BUFFER;
> > +					k++;
> > +				} else {
> > +					break;
> > +				}
> > +			}
> > +		}
> > +		break;
> > +	default:
> > +		break;
> > +	}
> > +
> > +	return k;
> >  }
> >
> >  /**
> > @@ -1425,6 +2508,20 @@ static bool iecm_is_capability_ena(struct
> iecm_adapter *adapter, bool all,
> >  		return !!(*cap_field & flag);
> >  }
> >
> > +/**
> > + * iecm_get_reserved_vectors - Default implementation to get reserved
> vectors
> > + * @adapter: Private data struct
> > + *
> > + * Return number of vectors reserved
> > + */
> > +static u16 iecm_get_reserved_vectors(struct iecm_adapter *adapter)
> > +{
> > +	struct virtchnl2_get_capabilities *caps;
> > +
> > +	caps = (struct virtchnl2_get_capabilities *)adapter->caps;
> > +	return le16_to_cpu(caps->num_allocated_vectors);
> > +}
> > +
> >  /**
> >   * iecm_vc_ops_init - Initialize virtchnl common api
> >   * @adapter: Driver specific private structure
> > @@ -1441,16 +2538,16 @@ void iecm_vc_ops_init(struct iecm_adapter
> *adapter)
> >  	vc_ops->vport_queue_ids_init = iecm_vport_queue_ids_init;
> >  	vc_ops->get_caps = iecm_send_get_caps_msg;
> >  	vc_ops->is_cap_ena = iecm_is_capability_ena;
> > -	vc_ops->get_reserved_vecs = NULL;
> > -	vc_ops->config_queues = NULL;
> > -	vc_ops->enable_queues = NULL;
> > -	vc_ops->disable_queues = NULL;
> > -	vc_ops->add_queues = NULL;
> > -	vc_ops->delete_queues = NULL;
> > -	vc_ops->irq_map_unmap = NULL;
> > -	vc_ops->enable_vport = NULL;
> > -	vc_ops->disable_vport = NULL;
> > -	vc_ops->destroy_vport = NULL;
> > +	vc_ops->get_reserved_vecs = iecm_get_reserved_vectors;
> > +	vc_ops->config_queues = iecm_send_config_queues_msg;
> > +	vc_ops->enable_queues = iecm_send_enable_queues_msg;
> > +	vc_ops->disable_queues = iecm_send_disable_queues_msg;
> > +	vc_ops->add_queues = iecm_send_add_queues_msg;
> > +	vc_ops->delete_queues = iecm_send_delete_queues_msg;
> > +	vc_ops->irq_map_unmap =
> iecm_send_map_unmap_queue_vector_msg;
> > +	vc_ops->enable_vport = iecm_send_enable_vport_msg;
> > +	vc_ops->disable_vport = iecm_send_disable_vport_msg;
> > +	vc_ops->destroy_vport = iecm_send_destroy_vport_msg;
> >  	vc_ops->get_ptype = NULL;
> >  	vc_ops->get_set_rss_key = NULL;
> >  	vc_ops->get_set_rss_lut = NULL;
> 
> Forgot to mention earlier, any reason to not declare this ops as
> static const and just assign a pointer then? I don't see any
> alternations here to e.g. fill callbacks with different functions
> or so.
> 
> > diff --git a/drivers/net/ethernet/intel/include/iecm.h
> b/drivers/net/ethernet/intel/include/iecm.h
> > index 994664dfe419..8dd6272db7d3 100644
> > --- a/drivers/net/ethernet/intel/include/iecm.h
> > +++ b/drivers/net/ethernet/intel/include/iecm.h
> > @@ -432,6 +432,8 @@ struct iecm_adapter {
> >  	u16 num_alloc_vport;
> >  	u16 next_vport;		/* Next free slot in pf->vport[] - 0-based! */
> >
> > +	u16 max_queue_limit;	/* Max number of queues user can request */
> > +
> >  	struct delayed_work init_task; /* delayed init task */
> >  	struct workqueue_struct *init_wq;
> >  	u32 mb_wait_count;
> > @@ -510,6 +512,12 @@ static inline bool __iecm_is_cap_ena(struct
> iecm_adapter *adapter, bool all,
> >  	return adapter->dev_ops.vc_ops.is_cap_ena(adapter, all, field, flag);
> >  }
> >
> > +#define IECM_CAP_HSPLIT (\
> > +	VIRTCHNL2_CAP_RX_HSPLIT_AT_L2   |\
> > +	VIRTCHNL2_CAP_RX_HSPLIT_AT_L3   |\
> > +	VIRTCHNL2_CAP_RX_HSPLIT_AT_L4V4 |\
> > +	VIRTCHNL2_CAP_RX_HSPLIT_AT_L4V6)
> > +
> >  /**
> >   * iecm_is_reset_detected - check if we were reset at some point
> >   * @adapter: driver specific private structure
> > @@ -530,6 +538,8 @@ int iecm_init_dflt_mbx(struct iecm_adapter *adapter);
> >  void iecm_deinit_dflt_mbx(struct iecm_adapter *adapter);
> >  void iecm_vc_ops_init(struct iecm_adapter *adapter);
> >  int iecm_vc_core_init(struct iecm_adapter *adapter, int *vport_id);
> > +int iecm_get_reg_intr_vecs(struct iecm_vport *vport,
> > +			   struct iecm_vec_regs *reg_vals, int num_vecs);
> >  int iecm_wait_for_event(struct iecm_adapter *adapter,
> >  			enum iecm_vport_vc_state state,
> >  			enum iecm_vport_vc_state err_check);
> > @@ -537,6 +547,14 @@ int iecm_min_wait_for_event(struct iecm_adapter
> *adapter,
> >  			    enum iecm_vport_vc_state state,
> >  			    enum iecm_vport_vc_state err_check);
> >  int iecm_send_get_caps_msg(struct iecm_adapter *adapter);
> > +int iecm_send_delete_queues_msg(struct iecm_vport *vport);
> > +int iecm_send_add_queues_msg(struct iecm_vport *vport, u16 num_tx_q,
> > +			     u16 num_complq, u16 num_rx_q, u16 num_rx_bufq);
> > +int iecm_send_config_tx_queues_msg(struct iecm_vport *vport);
> > +int iecm_send_config_rx_queues_msg(struct iecm_vport *vport);
> > +int iecm_send_enable_vport_msg(struct iecm_vport *vport);
> > +int iecm_send_disable_vport_msg(struct iecm_vport *vport);
> > +int iecm_send_destroy_vport_msg(struct iecm_vport *vport);
> >  int iecm_vport_params_buf_alloc(struct iecm_adapter *adapter);
> >  void iecm_vport_params_buf_rel(struct iecm_adapter *adapter);
> >  int iecm_get_vec_ids(struct iecm_adapter *adapter,
> > @@ -546,7 +564,11 @@ int iecm_recv_mb_msg(struct iecm_adapter
> *adapter, enum virtchnl_ops op,
> >  		     void *msg, int msg_size);
> >  int iecm_send_mb_msg(struct iecm_adapter *adapter, enum virtchnl_ops op,
> >  		     u16 msg_size, u8 *msg);
> > +void iecm_vport_set_hsplit(struct iecm_vport *vport, bool ena);
> > +int iecm_send_enable_channels_msg(struct iecm_vport *vport);
> > +int iecm_send_disable_channels_msg(struct iecm_vport *vport);
> >  int iecm_set_msg_pending(struct iecm_adapter *adapter,
> >  			 struct iecm_ctlq_msg *ctlq_msg,
> >  			 enum iecm_vport_vc_state err_enum);
> > +int iecm_send_map_unmap_queue_vector_msg(struct iecm_vport *vport,
> bool map);
> >  #endif /* !_IECM_H_ */
> > diff --git a/drivers/net/ethernet/intel/include/iecm_txrx.h
> b/drivers/net/ethernet/intel/include/iecm_txrx.h
> > index e1348011c991..448cae0bf6e7 100644
> > --- a/drivers/net/ethernet/intel/include/iecm_txrx.h
> > +++ b/drivers/net/ethernet/intel/include/iecm_txrx.h
> > @@ -81,6 +81,22 @@
> >
> >  #define IECM_TX_COMPLQ_CLEAN_BUDGET	256
> >
> > +enum iecm_queue_flags_t {
> > +	__IECM_Q_GEN_CHK,
> > +	__IECM_RFLQ_GEN_CHK,
> > +	__IECM_Q_FLOW_SCH_EN,
> > +	__IECM_Q_ETF_EN,
> > +	__IECM_Q_SW_MARKER,
> > +	__IECM_Q_VLAN_TAG_LOC_L2TAG1,
> > +	__IECM_Q_VLAN_TAG_LOC_L2TAG2,
> > +	__IECM_Q_FLAGS_NBITS,
> > +};
> > +
> > +struct iecm_vec_regs {
> > +	u32 dyn_ctl_reg;
> > +	u32 itrn_reg;
> > +};
> > +
> >  struct iecm_intr_reg {
> >  	u32 dyn_ctl;
> >  	u32 dyn_ctl_intena_m;
> > @@ -122,6 +138,186 @@ struct iecm_q_vector {
> >  	char name[IECM_INT_NAME_STR_LEN];
> >  };
> >
> > +struct iecm_rx_queue_stats {
> > +	u64 packets;
> > +	u64 bytes;
> > +	u64 rsc_pkts;
> > +};
> > +
> > +struct iecm_tx_queue_stats {
> > +	u64 packets;
> > +	u64 bytes;
> > +	u64 lso_pkts;
> > +};
> > +
> > +union iecm_queue_stats {
> > +	struct iecm_rx_queue_stats rx;
> > +	struct iecm_tx_queue_stats tx;
> > +};
> > +
> > +/* queue associated with a vport */
> > +struct iecm_queue {
> > +	struct device *dev;		/* Used for DMA mapping */
> > +	struct iecm_vport *vport;	/* Backreference to associated vport
> */
> > +	union {
> > +		struct iecm_txq_group *txq_grp;
> > +		struct iecm_rxq_group *rxq_grp;
> > +	};
> > +	/* bufq: Used as group id, either 0 or 1, on clean Buf Q uses this
> > +	 *       index to determine which group of refill queues to clean.
> > +	 *       Bufqs are use in splitq only.
> > +	 * txq: Index to map between Tx Q group and hot path Tx ptrs stored in
> > +	 *      vport.  Used in both single Q/split Q
> > +	 * rxq: Index to total rxq across groups, used for skb reporting
> > +	 */
> > +	u16 idx;
> > +	/* Used for both Q models single and split. In split Q model relevant
> > +	 * only to Tx Q and Rx Q
> > +	 */
> > +	u8 __iomem *tail;
> > +	/* Used in both single and split Q.  In single Q, Tx Q uses tx_buf and
> > +	 * Rx Q uses rx_buf.  In split Q, Tx Q uses tx_buf, Rx Q uses skb, and
> > +	 * Buf Q uses rx_buf.
> > +	 */
> > +	union {
> > +		struct iecm_tx_buf *tx_buf;
> > +		struct {
> > +			struct iecm_rx_buf *buf;
> > +			struct iecm_dma_mem **hdr_buf;
> > +		} rx_buf;
> > +		struct sk_buff *skb;
> > +	};
> > +	u16 q_type;
> > +	/* Queue id(Tx/Tx compl/Rx/Bufq) */
> > +	u32 q_id;
> > +	u16 desc_count;		/* Number of descriptors */
> > +
> > +	/* Relevant in both split & single Tx Q & Buf Q*/
> > +	u16 next_to_use;
> > +	/* In split q model only relevant for Tx Compl Q and Rx Q */
> > +	u16 next_to_clean;	/* used in interrupt processing */
> > +	/* Used only for Rx. In split Q model only relevant to Rx Q */
> > +	u16 next_to_alloc;
> > +	/* Generation bit check stored, as HW flips the bit at Queue end */
> > +	DECLARE_BITMAP(flags, __IECM_Q_FLAGS_NBITS);
> > +
> > +	union iecm_queue_stats q_stats;
> > +	struct u64_stats_sync stats_sync;
> > +
> > +	bool rx_hsplit_en;
> > +
> > +	u16 rx_hbuf_size;	/* Header buffer size */
> > +	u16 rx_buf_size;
> > +	u16 rx_max_pkt_size;
> > +	u16 rx_buf_stride;
> > +	u8 rx_buffer_low_watermark;
> > +	u64 rxdids;
> > +	/* Used for both Q models single and split. In split Q model relavant
> > +	 * only to Tx compl Q and Rx compl Q
> > +	 */
> > +	struct iecm_q_vector *q_vector;	/* Backreference to associated vector
> */
> > +	unsigned int size;		/* length of descriptor ring in bytes */
> > +	dma_addr_t dma;			/* physical address of ring */
> > +	void *desc_ring;		/* Descriptor ring memory */
> > +
> > +	u16 tx_buf_key;			/* 16 bit unique "identifier" (index)
> > +					 * to be used as the completion tag
> when
> > +					 * queue is using flow based scheduling
> > +					 */
> > +	u16 tx_max_bufs;		/* Max buffers that can be transmitted
> > +					 * with scatter-gather
> > +					 */
> > +	DECLARE_HASHTABLE(sched_buf_hash, 12);
> > +} ____cacheline_internodealigned_in_smp;
> > +
> > +/* Software queues are used in splitq mode to manage buffers between rxq
> > + * producer and the bufq consumer.  These are required in order to maintain a
> > + * lockless buffer management system and are strictly software only
> constructs.
> > + */
> > +struct iecm_sw_queue {
> > +	u16 next_to_clean ____cacheline_aligned_in_smp;
> > +	u16 next_to_alloc ____cacheline_aligned_in_smp;
> > +	u16 next_to_use ____cacheline_aligned_in_smp;
> > +	DECLARE_BITMAP(flags, __IECM_Q_FLAGS_NBITS)
> > +		____cacheline_aligned_in_smp;
> > +	u16 *ring ____cacheline_aligned_in_smp;
> 
> This will result in this part being FIVE cachelines long for
> 3 * 2 + 8 + 8 = 22 bytes, i.e. 320 bytes for 22!
> Just making the entire structure cacheline-aligned after its
> declaration is enough, these ones are not even an overkill,
> it's an overslaughter.
> 
> > +	u16 desc_count;
> > +	u16 buf_size;
> > +	struct device *dev;
> > +} ____cacheline_internodealigned_in_smp;
> > +
> > +/* Splitq only.  iecm_rxq_set associates an rxq with at an array of refillqs.
> > + * Each rxq needs a refillq to return used buffers back to the respective bufq.
> > + * Bufqs then clean these refillqs for buffers to give to hardware.
> > + */
> > +struct iecm_rxq_set {
> > +	struct iecm_queue rxq;
> > +	/* refillqs assoc with bufqX mapped to this rxq */
> > +	struct iecm_sw_queue *refillq0;
> > +	struct iecm_sw_queue *refillq1;
> > +};
> > +
> > +/* Splitq only.  iecm_bufq_set associates a bufq to an array of refillqs.
> > + * In this bufq_set, there will be one refillq for each rxq in this rxq_group.
> > + * Used buffers received by rxqs will be put on refillqs which bufqs will
> > + * clean to return new buffers back to hardware.
> > + *
> > + * Buffers needed by some number of rxqs associated in this rxq_group are
> > + * managed by at most two bufqs (depending on performance configuration).
> > + */
> > +struct iecm_bufq_set {
> > +	struct iecm_queue bufq;
> > +	/* This is always equal to num_rxq_sets in iecm_rxq_group */
> > +	int num_refillqs;
> > +	struct iecm_sw_queue *refillqs;
> > +};
> > +
> > +/* In singleq mode, an rxq_group is simply an array of rxqs.  In splitq, a
> > + * rxq_group contains all the rxqs, bufqs and refillqs needed to
> > + * manage buffers in splitq mode.
> > + */
> > +struct iecm_rxq_group {
> > +	struct iecm_vport *vport; /* back pointer */
> > +
> > +	union {
> > +		struct {
> > +			int num_rxq;
> > +			/* store queue pointers */
> > +			struct iecm_queue *rxqs[IECM_LARGE_MAX_Q];
> > +		} singleq;
> > +		struct {
> > +			int num_rxq_sets;
> > +			/* store queue pointers */
> > +			struct iecm_rxq_set *rxq_sets[IECM_LARGE_MAX_Q];
> > +			struct iecm_bufq_set *bufq_sets;
> > +		} splitq;
> > +	};
> > +};
> > +
> > +/* Between singleq and splitq, a txq_group is largely the same except for the
> > + * complq.  In splitq a single complq is responsible for handling completions
> > + * for some number of txqs associated in this txq_group.
> > + */
> > +struct iecm_txq_group {
> > +	struct iecm_vport *vport; /* back pointer */
> > +
> > +	int num_txq;
> > +	/* store queue pointers */
> > +	struct iecm_queue *txqs[IECM_LARGE_MAX_Q];
> > +
> > +	/* splitq only */
> > +	struct iecm_queue *complq;
> > +};
> > +
> > +struct iecm_adapter;
> > +
> > +void iecm_vport_init_num_qs(struct iecm_vport *vport,
> > +			    struct virtchnl2_create_vport *vport_msg);
> > +void iecm_vport_calc_num_q_desc(struct iecm_vport *vport);
> > +void iecm_vport_calc_total_qs(struct iecm_adapter *adapter,
> > +			      struct virtchnl2_create_vport *vport_msg);
> > +void iecm_vport_calc_num_q_groups(struct iecm_vport *vport);
> > +void iecm_vport_calc_num_q_vec(struct iecm_vport *vport);
> >  irqreturn_t
> >  iecm_vport_intr_clean_queues(int __always_unused irq, void *data);
> >  #endif /* !_IECM_TXRX_H_ */
> > --
> > 2.33.0
> 
> Thanks,
> Al

^ permalink raw reply	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 07/19] iecm: finish virtchnl messages
  2022-01-28 13:19   ` Alexander Lobakin
@ 2022-02-02 23:06     ` Brady, Alan
  2022-02-03 15:05       ` Alexander Lobakin
  0 siblings, 1 reply; 89+ messages in thread
From: Brady, Alan @ 2022-02-02 23:06 UTC (permalink / raw)
  To: intel-wired-lan

> -----Original Message-----
> From: Lobakin, Alexandr <alexandr.lobakin@intel.com>
> Sent: Friday, January 28, 2022 5:20 AM
> To: Brady, Alan <alan.brady@intel.com>
> Cc: Lobakin, Alexandr <alexandr.lobakin@intel.com>; intel-wired-
> lan at lists.osuosl.org; Linga, Pavan Kumar <pavan.kumar.linga@intel.com>;
> Chittim, Madhu <madhu.chittim@intel.com>; Burra, Phani R
> <phani.r.burra@intel.com>
> Subject: Re: [Intel-wired-lan] [PATCH net-next 07/19] iecm: finish virtchnl
> messages
> 
> From: Alan Brady <alan.brady@intel.com>
> Date: Thu, 27 Jan 2022 16:09:57 -0800
> 
> > This adds the rest of the needed virtchnl messages mostly related to
> > negotiating ptypes and initializing queue registers.
> >
> > Signed-off-by: Phani Burra <phani.r.burra@intel.com>
> > Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
> > Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
> > Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
> > Signed-off-by: Alice Michael <alice.michael@intel.com>
> > Signed-off-by: Alan Brady <alan.brady@intel.com>
> > ---
> >  drivers/net/ethernet/intel/iecm/iecm_lib.c    |   21 +-
> >  drivers/net/ethernet/intel/iecm/iecm_txrx.c   |  226 +++-
> >  .../net/ethernet/intel/iecm/iecm_virtchnl.c   | 1187 ++++++++++++++++-
> >  drivers/net/ethernet/intel/include/iecm.h     |   36 +
> >  .../net/ethernet/intel/include/iecm_txrx.h    |  198 ++-
> >  5 files changed, 1635 insertions(+), 33 deletions(-)
> >
> > diff --git a/drivers/net/ethernet/intel/iecm/iecm_lib.c
> b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > index 4e9cc7f2d138..aab8ee40424e 100644
> > --- a/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > +++ b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > @@ -10,6 +10,25 @@ const char * const iecm_vport_vc_state_str[] = {
> >  };
> >  EXPORT_SYMBOL(iecm_vport_vc_state_str);
> >
> > +/**
> > + * iecm_is_feature_ena - Determine if a particular feature is enabled
> > + * @vport: vport to check
> > + * @feature: netdev flag to check
> > + *
> > + * Returns true or false if a particular feature is enabled.
> > + */
> > +bool iecm_is_feature_ena(struct iecm_vport *vport, netdev_features_t
> feature)
> > +{
> > +	bool ena;
> > +
> > +	switch (feature) {
> > +	default:
> > +		ena = vport->netdev->features & feature;
> > +		break;
> > +	}
> > +	return ena;
> > +}
> 
> This makes absolutely no sense, please rewrite to
> 
> 	return vport->netdev->features & feature;
> 
> If it will be expanded later, convert it to a switch-case only then.
> 

A case is added later in this series of patches but I can thrash this in the middle of the series if you feel strongly about it.

> > +
> >  /**
> >   * iecm_cfg_hw - Initialize HW struct
> >   * @adapter: adapter to setup hw struct for
> > @@ -132,7 +151,7 @@ iecm_vport_alloc(struct iecm_adapter *adapter, int
> vport_id)
> >  	adapter->num_alloc_vport++;
> >
> >  	/* Setup default MSIX irq handler for the vport */
> > -	vport->irq_q_handler = iecm_vport_intr_clean_queues;
> > +	vport->irq_q_handler = NULL;
> >  	vport->q_vector_base = IECM_NONQ_VEC;
> >
> >  	mutex_init(&vport->stop_mutex);
> > diff --git a/drivers/net/ethernet/intel/iecm/iecm_txrx.c
> b/drivers/net/ethernet/intel/iecm/iecm_txrx.c
> > index 2dfb0be002e3..bd0cfd89bf03 100644
> > --- a/drivers/net/ethernet/intel/iecm/iecm_txrx.c
> > +++ b/drivers/net/ethernet/intel/iecm/iecm_txrx.c
> > @@ -3,22 +3,220 @@
> >
> >  #include "iecm.h"
> >
> > -/**
> > - * iecm_vport_intr_clean_queues - MSIX mode Interrupt Handler
> > - * @irq: interrupt number
> > - * @data: pointer to a q_vector
> > - *
> > - */
> > -irqreturn_t
> > -iecm_vport_intr_clean_queues(int __always_unused irq, void *data)
> > -{
> > -	struct iecm_q_vector *q_vector = (struct iecm_q_vector *)data;
> > +const struct iecm_rx_ptype_decoded
> iecm_ptype_lookup[IECM_RX_MAX_PTYPE] = {
> > +	/* ptype indices are dynamic and package dependent. Indices
> represented
> > +	 * in this lookup table are for reference and will be replaced by the
> > +	 * values which CP sends. Also these values are static for older
> > +	 * versions of virtchnl and if VIRTCHNL2_CAP_PTYPE is not set in
> > +	 * virtchnl2_get_capabilities.
> > +	 */
> > +	/* L2 Packet types */
> > +	IECM_PTT_UNUSED_ENTRY(0),
> > +	IECM_PTT(1,  L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY2),
> > +	IECM_PTT(2,  L2, NONE, NOF, NONE, NONE, NOF, TS,   PAY2),
> > +	IECM_PTT(3,  L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY2),
> > +	IECM_PTT_UNUSED_ENTRY(4),
> > +	IECM_PTT_UNUSED_ENTRY(5),
> > +	IECM_PTT(6,  L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY2),
> > +	IECM_PTT(7,  L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY2),
> > +	IECM_PTT_UNUSED_ENTRY(8),
> > +	IECM_PTT_UNUSED_ENTRY(9),
> > +	IECM_PTT(10, L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY2),
> > +	IECM_PTT(11, L2, NONE, NOF, NONE, NONE, NOF, NONE, NONE),
> > +	IECM_PTT(12, L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY3),
> > +	IECM_PTT(13, L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY3),
> > +	IECM_PTT(14, L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY3),
> > +	IECM_PTT(15, L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY3),
> > +	IECM_PTT(16, L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY3),
> > +	IECM_PTT(17, L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY3),
> > +	IECM_PTT(18, L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY3),
> > +	IECM_PTT(19, L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY3),
> > +	IECM_PTT(20, L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY3),
> > +	IECM_PTT(21, L2, NONE, NOF, NONE, NONE, NOF, NONE, PAY3),
> >
> > -	q_vector->total_events++;
> > -	napi_schedule(&q_vector->napi);
> > +	/* Non Tunneled IPv4 */
> > +	IECM_PTT(22, IP, IPV4, FRG, NONE, NONE, NOF, NONE, PAY3),
> > +	IECM_PTT(23, IP, IPV4, NOF, NONE, NONE, NOF, NONE, PAY3),
> > +	IECM_PTT(24, IP, IPV4, NOF, NONE, NONE, NOF, UDP,  PAY4),
> > +	IECM_PTT_UNUSED_ENTRY(25),
> > +	IECM_PTT(26, IP, IPV4, NOF, NONE, NONE, NOF, TCP,  PAY4),
> > +	IECM_PTT(27, IP, IPV4, NOF, NONE, NONE, NOF, SCTP, PAY4),
> > +	IECM_PTT(28, IP, IPV4, NOF, NONE, NONE, NOF, ICMP, PAY4),
> >
> > -	return IRQ_HANDLED;
> > -}
> > +	/* IPv4 --> IPv4 */
> > +	IECM_PTT(29, IP, IPV4, NOF, IP_IP, IPV4, FRG, NONE, PAY3),
> > +	IECM_PTT(30, IP, IPV4, NOF, IP_IP, IPV4, NOF, NONE, PAY3),
> > +	IECM_PTT(31, IP, IPV4, NOF, IP_IP, IPV4, NOF, UDP,  PAY4),
> > +	IECM_PTT_UNUSED_ENTRY(32),
> > +	IECM_PTT(33, IP, IPV4, NOF, IP_IP, IPV4, NOF, TCP,  PAY4),
> > +	IECM_PTT(34, IP, IPV4, NOF, IP_IP, IPV4, NOF, SCTP, PAY4),
> > +	IECM_PTT(35, IP, IPV4, NOF, IP_IP, IPV4, NOF, ICMP, PAY4),
> > +
> > +	/* IPv4 --> IPv6 */
> > +	IECM_PTT(36, IP, IPV4, NOF, IP_IP, IPV6, FRG, NONE, PAY3),
> > +	IECM_PTT(37, IP, IPV4, NOF, IP_IP, IPV6, NOF, NONE, PAY3),
> > +	IECM_PTT(38, IP, IPV4, NOF, IP_IP, IPV6, NOF, UDP,  PAY4),
> > +	IECM_PTT_UNUSED_ENTRY(39),
> > +	IECM_PTT(40, IP, IPV4, NOF, IP_IP, IPV6, NOF, TCP,  PAY4),
> > +	IECM_PTT(41, IP, IPV4, NOF, IP_IP, IPV6, NOF, SCTP, PAY4),
> > +	IECM_PTT(42, IP, IPV4, NOF, IP_IP, IPV6, NOF, ICMP, PAY4),
> > +
> > +	/* IPv4 --> GRE/NAT */
> > +	IECM_PTT(43, IP, IPV4, NOF, IP_GRENAT, NONE, NOF, NONE, PAY3),
> > +
> > +	/* IPv4 --> GRE/NAT --> IPv4 */
> > +	IECM_PTT(44, IP, IPV4, NOF, IP_GRENAT, IPV4, FRG, NONE, PAY3),
> > +	IECM_PTT(45, IP, IPV4, NOF, IP_GRENAT, IPV4, NOF, NONE, PAY3),
> > +	IECM_PTT(46, IP, IPV4, NOF, IP_GRENAT, IPV4, NOF, UDP,  PAY4),
> > +	IECM_PTT_UNUSED_ENTRY(47),
> > +	IECM_PTT(48, IP, IPV4, NOF, IP_GRENAT, IPV4, NOF, TCP,  PAY4),
> > +	IECM_PTT(49, IP, IPV4, NOF, IP_GRENAT, IPV4, NOF, SCTP, PAY4),
> > +	IECM_PTT(50, IP, IPV4, NOF, IP_GRENAT, IPV4, NOF, ICMP, PAY4),
> > +
> > +	/* IPv4 --> GRE/NAT --> IPv6 */
> > +	IECM_PTT(51, IP, IPV4, NOF, IP_GRENAT, IPV6, FRG, NONE, PAY3),
> > +	IECM_PTT(52, IP, IPV4, NOF, IP_GRENAT, IPV6, NOF, NONE, PAY3),
> > +	IECM_PTT(53, IP, IPV4, NOF, IP_GRENAT, IPV6, NOF, UDP,  PAY4),
> > +	IECM_PTT_UNUSED_ENTRY(54),
> > +	IECM_PTT(55, IP, IPV4, NOF, IP_GRENAT, IPV6, NOF, TCP,  PAY4),
> > +	IECM_PTT(56, IP, IPV4, NOF, IP_GRENAT, IPV6, NOF, SCTP, PAY4),
> > +	IECM_PTT(57, IP, IPV4, NOF, IP_GRENAT, IPV6, NOF, ICMP, PAY4),
> > +
> > +	/* IPv4 --> GRE/NAT --> MAC */
> > +	IECM_PTT(58, IP, IPV4, NOF, IP_GRENAT_MAC, NONE, NOF, NONE,
> PAY3),
> > +
> > +	/* IPv4 --> GRE/NAT --> MAC --> IPv4 */
> > +	IECM_PTT(59, IP, IPV4, NOF, IP_GRENAT_MAC, IPV4, FRG, NONE,
> PAY3),
> > +	IECM_PTT(60, IP, IPV4, NOF, IP_GRENAT_MAC, IPV4, NOF, NONE,
> PAY3),
> > +	IECM_PTT(61, IP, IPV4, NOF, IP_GRENAT_MAC, IPV4, NOF, UDP,  PAY4),
> > +	IECM_PTT_UNUSED_ENTRY(62),
> > +	IECM_PTT(63, IP, IPV4, NOF, IP_GRENAT_MAC, IPV4, NOF, TCP,  PAY4),
> > +	IECM_PTT(64, IP, IPV4, NOF, IP_GRENAT_MAC, IPV4, NOF, SCTP, PAY4),
> > +	IECM_PTT(65, IP, IPV4, NOF, IP_GRENAT_MAC, IPV4, NOF, ICMP, PAY4),
> > +
> > +	/* IPv4 --> GRE/NAT -> MAC --> IPv6 */
> > +	IECM_PTT(66, IP, IPV4, NOF, IP_GRENAT_MAC, IPV6, FRG, NONE,
> PAY3),
> > +	IECM_PTT(67, IP, IPV4, NOF, IP_GRENAT_MAC, IPV6, NOF, NONE,
> PAY3),
> > +	IECM_PTT(68, IP, IPV4, NOF, IP_GRENAT_MAC, IPV6, NOF, UDP,  PAY4),
> > +	IECM_PTT_UNUSED_ENTRY(69),
> > +	IECM_PTT(70, IP, IPV4, NOF, IP_GRENAT_MAC, IPV6, NOF, TCP,  PAY4),
> > +	IECM_PTT(71, IP, IPV4, NOF, IP_GRENAT_MAC, IPV6, NOF, SCTP, PAY4),
> > +	IECM_PTT(72, IP, IPV4, NOF, IP_GRENAT_MAC, IPV6, NOF, ICMP, PAY4),
> > +
> > +	/* IPv4 --> GRE/NAT --> MAC/VLAN */
> > +	IECM_PTT(73, IP, IPV4, NOF, IP_GRENAT_MAC_VLAN, NONE, NOF,
> NONE, PAY3),
> > +
> > +	/* IPv4 ---> GRE/NAT -> MAC/VLAN --> IPv4 */
> > +	IECM_PTT(74, IP, IPV4, NOF, IP_GRENAT_MAC_VLAN, IPV4, FRG,
> NONE, PAY3),
> > +	IECM_PTT(75, IP, IPV4, NOF, IP_GRENAT_MAC_VLAN, IPV4, NOF,
> NONE, PAY3),
> > +	IECM_PTT(76, IP, IPV4, NOF, IP_GRENAT_MAC_VLAN, IPV4, NOF, UDP,
> PAY4),
> > +	IECM_PTT_UNUSED_ENTRY(77),
> > +	IECM_PTT(78, IP, IPV4, NOF, IP_GRENAT_MAC_VLAN, IPV4, NOF, TCP,
> PAY4),
> > +	IECM_PTT(79, IP, IPV4, NOF, IP_GRENAT_MAC_VLAN, IPV4, NOF, SCTP,
> PAY4),
> > +	IECM_PTT(80, IP, IPV4, NOF, IP_GRENAT_MAC_VLAN, IPV4, NOF, ICMP,
> PAY4),
> > +
> > +	/* IPv4 -> GRE/NAT -> MAC/VLAN --> IPv6 */
> > +	IECM_PTT(81, IP, IPV4, NOF, IP_GRENAT_MAC_VLAN, IPV6, FRG,
> NONE, PAY3),
> > +	IECM_PTT(82, IP, IPV4, NOF, IP_GRENAT_MAC_VLAN, IPV6, NOF,
> NONE, PAY3),
> > +	IECM_PTT(83, IP, IPV4, NOF, IP_GRENAT_MAC_VLAN, IPV6, NOF, UDP,
> PAY4),
> > +	IECM_PTT_UNUSED_ENTRY(84),
> > +	IECM_PTT(85, IP, IPV4, NOF, IP_GRENAT_MAC_VLAN, IPV6, NOF, TCP,
> PAY4),
> > +	IECM_PTT(86, IP, IPV4, NOF, IP_GRENAT_MAC_VLAN, IPV6, NOF, SCTP,
> PAY4),
> > +	IECM_PTT(87, IP, IPV4, NOF, IP_GRENAT_MAC_VLAN, IPV6, NOF, ICMP,
> PAY4),
> > +
> > +	/* Non Tunneled IPv6 */
> > +	IECM_PTT(88, IP, IPV6, FRG, NONE, NONE, NOF, NONE, PAY3),
> > +	IECM_PTT(89, IP, IPV6, NOF, NONE, NONE, NOF, NONE, PAY3),
> > +	IECM_PTT(90, IP, IPV6, NOF, NONE, NONE, NOF, UDP,  PAY4),
> > +	IECM_PTT_UNUSED_ENTRY(91),
> > +	IECM_PTT(92, IP, IPV6, NOF, NONE, NONE, NOF, TCP,  PAY4),
> > +	IECM_PTT(93, IP, IPV6, NOF, NONE, NONE, NOF, SCTP, PAY4),
> > +	IECM_PTT(94, IP, IPV6, NOF, NONE, NONE, NOF, ICMP, PAY4),
> > +
> > +	/* IPv6 --> IPv4 */
> > +	IECM_PTT(95,  IP, IPV6, NOF, IP_IP, IPV4, FRG, NONE, PAY3),
> > +	IECM_PTT(96,  IP, IPV6, NOF, IP_IP, IPV4, NOF, NONE, PAY3),
> > +	IECM_PTT(97,  IP, IPV6, NOF, IP_IP, IPV4, NOF, UDP,  PAY4),
> > +	IECM_PTT_UNUSED_ENTRY(98),
> > +	IECM_PTT(99,  IP, IPV6, NOF, IP_IP, IPV4, NOF, TCP,  PAY4),
> > +	IECM_PTT(100, IP, IPV6, NOF, IP_IP, IPV4, NOF, SCTP, PAY4),
> > +	IECM_PTT(101, IP, IPV6, NOF, IP_IP, IPV4, NOF, ICMP, PAY4),
> > +
> > +	/* IPv6 --> IPv6 */
> > +	IECM_PTT(102, IP, IPV6, NOF, IP_IP, IPV6, FRG, NONE, PAY3),
> > +	IECM_PTT(103, IP, IPV6, NOF, IP_IP, IPV6, NOF, NONE, PAY3),
> > +	IECM_PTT(104, IP, IPV6, NOF, IP_IP, IPV6, NOF, UDP,  PAY4),
> > +	IECM_PTT_UNUSED_ENTRY(105),
> > +	IECM_PTT(106, IP, IPV6, NOF, IP_IP, IPV6, NOF, TCP,  PAY4),
> > +	IECM_PTT(107, IP, IPV6, NOF, IP_IP, IPV6, NOF, SCTP, PAY4),
> > +	IECM_PTT(108, IP, IPV6, NOF, IP_IP, IPV6, NOF, ICMP, PAY4),
> > +
> > +	/* IPv6 --> GRE/NAT */
> > +	IECM_PTT(109, IP, IPV6, NOF, IP_GRENAT, NONE, NOF, NONE, PAY3),
> > +
> > +	/* IPv6 --> GRE/NAT -> IPv4 */
> > +	IECM_PTT(110, IP, IPV6, NOF, IP_GRENAT, IPV4, FRG, NONE, PAY3),
> > +	IECM_PTT(111, IP, IPV6, NOF, IP_GRENAT, IPV4, NOF, NONE, PAY3),
> > +	IECM_PTT(112, IP, IPV6, NOF, IP_GRENAT, IPV4, NOF, UDP,  PAY4),
> > +	IECM_PTT_UNUSED_ENTRY(113),
> > +	IECM_PTT(114, IP, IPV6, NOF, IP_GRENAT, IPV4, NOF, TCP,  PAY4),
> > +	IECM_PTT(115, IP, IPV6, NOF, IP_GRENAT, IPV4, NOF, SCTP, PAY4),
> > +	IECM_PTT(116, IP, IPV6, NOF, IP_GRENAT, IPV4, NOF, ICMP, PAY4),
> > +
> > +	/* IPv6 --> GRE/NAT -> IPv6 */
> > +	IECM_PTT(117, IP, IPV6, NOF, IP_GRENAT, IPV6, FRG, NONE, PAY3),
> > +	IECM_PTT(118, IP, IPV6, NOF, IP_GRENAT, IPV6, NOF, NONE, PAY3),
> > +	IECM_PTT(119, IP, IPV6, NOF, IP_GRENAT, IPV6, NOF, UDP,  PAY4),
> > +	IECM_PTT_UNUSED_ENTRY(120),
> > +	IECM_PTT(121, IP, IPV6, NOF, IP_GRENAT, IPV6, NOF, TCP,  PAY4),
> > +	IECM_PTT(122, IP, IPV6, NOF, IP_GRENAT, IPV6, NOF, SCTP, PAY4),
> > +	IECM_PTT(123, IP, IPV6, NOF, IP_GRENAT, IPV6, NOF, ICMP, PAY4),
> > +
> > +	/* IPv6 --> GRE/NAT -> MAC */
> > +	IECM_PTT(124, IP, IPV6, NOF, IP_GRENAT_MAC, NONE, NOF, NONE,
> PAY3),
> > +
> > +	/* IPv6 --> GRE/NAT -> MAC -> IPv4 */
> > +	IECM_PTT(125, IP, IPV6, NOF, IP_GRENAT_MAC, IPV4, FRG, NONE,
> PAY3),
> > +	IECM_PTT(126, IP, IPV6, NOF, IP_GRENAT_MAC, IPV4, NOF, NONE,
> PAY3),
> > +	IECM_PTT(127, IP, IPV6, NOF, IP_GRENAT_MAC, IPV4, NOF, UDP,
> PAY4),
> > +	IECM_PTT_UNUSED_ENTRY(128),
> > +	IECM_PTT(129, IP, IPV6, NOF, IP_GRENAT_MAC, IPV4, NOF, TCP,
> PAY4),
> > +	IECM_PTT(130, IP, IPV6, NOF, IP_GRENAT_MAC, IPV4, NOF, SCTP,
> PAY4),
> > +	IECM_PTT(131, IP, IPV6, NOF, IP_GRENAT_MAC, IPV4, NOF, ICMP,
> PAY4),
> > +
> > +	/* IPv6 --> GRE/NAT -> MAC -> IPv6 */
> > +	IECM_PTT(132, IP, IPV6, NOF, IP_GRENAT_MAC, IPV6, FRG, NONE,
> PAY3),
> > +	IECM_PTT(133, IP, IPV6, NOF, IP_GRENAT_MAC, IPV6, NOF, NONE,
> PAY3),
> > +	IECM_PTT(134, IP, IPV6, NOF, IP_GRENAT_MAC, IPV6, NOF, UDP,
> PAY4),
> > +	IECM_PTT_UNUSED_ENTRY(135),
> > +	IECM_PTT(136, IP, IPV6, NOF, IP_GRENAT_MAC, IPV6, NOF, TCP,
> PAY4),
> > +	IECM_PTT(137, IP, IPV6, NOF, IP_GRENAT_MAC, IPV6, NOF, SCTP,
> PAY4),
> > +	IECM_PTT(138, IP, IPV6, NOF, IP_GRENAT_MAC, IPV6, NOF, ICMP,
> PAY4),
> > +
> > +	/* IPv6 --> GRE/NAT -> MAC/VLAN */
> > +	IECM_PTT(139, IP, IPV6, NOF, IP_GRENAT_MAC_VLAN, NONE, NOF,
> NONE, PAY3),
> > +
> > +	/* IPv6 --> GRE/NAT -> MAC/VLAN --> IPv4 */
> > +	IECM_PTT(140, IP, IPV6, NOF, IP_GRENAT_MAC_VLAN, IPV4, FRG,
> NONE, PAY3),
> > +	IECM_PTT(141, IP, IPV6, NOF, IP_GRENAT_MAC_VLAN, IPV4, NOF,
> NONE, PAY3),
> > +	IECM_PTT(142, IP, IPV6, NOF, IP_GRENAT_MAC_VLAN, IPV4, NOF, UDP,
> PAY4),
> > +	IECM_PTT_UNUSED_ENTRY(143),
> > +	IECM_PTT(144, IP, IPV6, NOF, IP_GRENAT_MAC_VLAN, IPV4, NOF, TCP,
> PAY4),
> > +	IECM_PTT(145, IP, IPV6, NOF, IP_GRENAT_MAC_VLAN, IPV4, NOF,
> SCTP, PAY4),
> > +	IECM_PTT(146, IP, IPV6, NOF, IP_GRENAT_MAC_VLAN, IPV4, NOF,
> ICMP, PAY4),
> > +
> > +	/* IPv6 --> GRE/NAT -> MAC/VLAN --> IPv6 */
> > +	IECM_PTT(147, IP, IPV6, NOF, IP_GRENAT_MAC_VLAN, IPV6, FRG,
> NONE, PAY3),
> > +	IECM_PTT(148, IP, IPV6, NOF, IP_GRENAT_MAC_VLAN, IPV6, NOF,
> NONE, PAY3),
> > +	IECM_PTT(149, IP, IPV6, NOF, IP_GRENAT_MAC_VLAN, IPV6, NOF, UDP,
> PAY4),
> > +	IECM_PTT_UNUSED_ENTRY(150),
> > +	IECM_PTT(151, IP, IPV6, NOF, IP_GRENAT_MAC_VLAN, IPV6, NOF, TCP,
> PAY4),
> > +	IECM_PTT(152, IP, IPV6, NOF, IP_GRENAT_MAC_VLAN, IPV6, NOF,
> SCTP, PAY4),
> > +	IECM_PTT(153, IP, IPV6, NOF, IP_GRENAT_MAC_VLAN, IPV6, NOF,
> ICMP, PAY4),
> > +
> > +	/* rest of the entries are unused */
> > +};
> > +EXPORT_SYMBOL(iecm_ptype_lookup);
> >
> >  /**
> >   * iecm_vport_init_num_qs - Initialize number of queues
> > diff --git a/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> > index d8152e657e24..c4ae56897d1b 100644
> > --- a/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> > +++ b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> > @@ -859,6 +859,15 @@ static int iecm_recv_get_caps_msg(struct
> iecm_adapter *adapter)
> >  				sizeof(struct virtchnl2_get_capabilities));
> >  }
> >
> > +/**
> > + * iecm_vport_init_max_qs - Initialize max queues supported on this device
> > + * @adapter: Driver specific private structure
> > + */
> > +static void iecm_vport_init_max_qs(struct iecm_adapter *adapter)
> > +{
> > +	adapter->max_queue_limit = IECM_MAX_Q;
> > +}
> > +
> >  /**
> >   * iecm_get_reg_intr_vecs - Get vector queue register offset
> >   * @vport: virtual port structure
> > @@ -901,6 +910,199 @@ int iecm_get_reg_intr_vecs(struct iecm_vport
> *vport,
> >  }
> >  EXPORT_SYMBOL(iecm_get_reg_intr_vecs);
> >
> > +/**
> > + * iecm_vport_get_q_reg - Get the queue registers for the vport
> > + * @reg_vals: register values needing to be set
> > + * @num_regs: amount we expect to fill
> > + * @q_type: queue model
> > + * @chunks: queue regs received over mailbox
> > + */
> > +static int
> > +iecm_vport_get_q_reg(u32 *reg_vals, int num_regs, u32 q_type,
> > +		     struct virtchnl2_queue_reg_chunks *chunks)
> > +{
> > +	u16 num_chunks = le16_to_cpu(chunks->num_chunks);
> > +	int reg_filled = 0, i;
> > +	u32 reg_val;
> > +	u16 num_q;
> > +
> > +	while (num_chunks) {
> > +		struct virtchnl2_queue_reg_chunk *chunk = &chunks-
> >chunks[num_chunks - 1];
> > +
> > +		if (le32_to_cpu(chunk->type) == q_type) {
> > +			num_q = le32_to_cpu(chunk->num_queues);
> > +			reg_val = le64_to_cpu(chunk->qtail_reg_start);
> > +			for (i = 0; i < num_q; i++) {
> > +				if (reg_filled == num_regs)
> > +					break;
> > +				reg_vals[reg_filled++] = reg_val;
> > +				reg_val +=
> > +					le32_to_cpu(chunk-
> >qtail_reg_spacing);
> > +			}
> > +		}
> > +		num_chunks--;
> > +	}
> 
> 	while (num_chunks--) {
> 		struct ... = ... [num_chunks];
> 
> 		if (le32_to_cpu(chunk->type) != q_type)
> 			continue;
> 
> 		...
> 	}
> 
> -1 indent level, -complexity.
> 
> > +
> > +	return reg_filled;
> > +}
> > +
> > +/**
> > + * __iecm_queue_reg_init - initialize queue registers
> > + * @vport: virtual port structure
> > + * @reg_vals: registers we are initializing
> > + * @num_regs: how many registers there are in total
> > + * @q_type: queue model
> > + *
> > + * Return number of queues that are initialized
> > + */
> > +static int
> > +__iecm_queue_reg_init(struct iecm_vport *vport, u32 *reg_vals,
> > +		      int num_regs, u32 q_type)
> > +{
> > +	struct iecm_hw *hw = &vport->adapter->hw;
> > +	struct iecm_queue *q;
> > +	int i, j, k = 0;
> > +
> > +	switch (q_type) {
> > +	case VIRTCHNL2_QUEUE_TYPE_TX:
> > +		for (i = 0; i < vport->num_txq_grp; i++) {
> > +			struct iecm_txq_group *tx_qgrp = &vport->txq_grps[i];
> > +
> > +			for (j = 0; j < tx_qgrp->num_txq; j++) {
> > +				if (k == num_regs)
> > +					break;
> > +
> > +				tx_qgrp->txqs[j]->tail =
> > +				  (__force u8 __iomem *)(hw->hw_addr +
> > +							 reg_vals[k]);
> > +				k++;
> > +			}
> > +		}
> > +		break;
> > +	case VIRTCHNL2_QUEUE_TYPE_RX:
> > +		for (i = 0; i < vport->num_rxq_grp; i++) {
> > +			struct iecm_rxq_group *rx_qgrp = &vport->rxq_grps[i];
> > +			int num_rxq = rx_qgrp->singleq.num_rxq;
> > +
> > +			for (j = 0; j < num_rxq; j++) {
> > +				if (k == num_regs)
> > +					break;
> > +
> > +				q = rx_qgrp->singleq.rxqs[j];
> > +				q->tail = (__force u8 __iomem *)(hw->hw_addr
> +
> > +								 reg_vals[k]);
> > +				k++;
> > +			}
> > +		}
> > +		break;
> > +	case VIRTCHNL2_QUEUE_TYPE_RX_BUFFER:
> > +		for (i = 0; i < vport->num_rxq_grp; i++) {
> > +			struct iecm_rxq_group *rx_qgrp = &vport->rxq_grps[i];
> > +
> > +			for (j = 0; j < vport->num_bufqs_per_qgrp; j++) {
> > +				if (k == num_regs)
> > +					break;
> > +
> > +				q = &rx_qgrp->splitq.bufq_sets[j].bufq;
> > +				q->tail = (__force u8 __iomem *)(hw->hw_addr
> +
> > +								 reg_vals[k]);
> > +				k++;
> > +			}
> > +		}
> > +		break;
> > +	default:
> > +		break;
> > +	}
> > +
> > +	return k;
> > +}
> > +
> > +/**
> > + * iecm_queue_reg_init - initialize queue registers
> > + * @vport: virtual port structure
> > + *
> > + * Return 0 on success, negative on failure
> > + */
> > +static int iecm_queue_reg_init(struct iecm_vport *vport)
> > +{
> > +	struct virtchnl2_create_vport *vport_params;
> > +	struct virtchnl2_queue_reg_chunks *chunks;
> > +	int num_regs, ret = 0;
> > +	u32 *reg_vals;
> > +
> > +	/* We may never deal with more than 256 same type of queues */
> > +	reg_vals = kmalloc(sizeof(void *) * IECM_LARGE_MAX_Q,
> > +			   GFP_KERNEL);
> > +	if (!reg_vals)
> > +		return -ENOMEM;
> > +
> > +	if (vport->adapter->config_data.req_qs_chunks) {
> > +		struct virtchnl2_add_queues *vc_aq =
> > +		  (struct virtchnl2_add_queues *)
> > +		  vport->adapter->config_data.req_qs_chunks;
> > +		chunks = &vc_aq->chunks;
> > +	} else {
> > +		vport_params = (struct virtchnl2_create_vport *)
> > +			vport->adapter->vport_params_recvd[0];
> > +		chunks = &vport_params->chunks;
> > +	}
> > +
> > +	/* Initialize Tx queue tail register address */
> > +	num_regs = iecm_vport_get_q_reg(reg_vals, IECM_LARGE_MAX_Q,
> > +					VIRTCHNL2_QUEUE_TYPE_TX,
> > +					chunks);
> > +	if (num_regs < vport->num_txq) {
> > +		ret = -EINVAL;
> > +		goto free_reg_vals;
> > +	}
> > +
> > +	num_regs = __iecm_queue_reg_init(vport, reg_vals, num_regs,
> > +					 VIRTCHNL2_QUEUE_TYPE_TX);
> > +	if (num_regs < vport->num_txq) {
> > +		ret = -EINVAL;
> > +		goto free_reg_vals;
> > +	}
> > +
> > +	/* Initialize Rx/buffer queue tail register address based on Rx queue
> > +	 * model
> > +	 */
> > +	if (iecm_is_queue_model_split(vport->rxq_model)) {
> > +		num_regs = iecm_vport_get_q_reg(reg_vals,
> IECM_LARGE_MAX_Q,
> > +
> 	VIRTCHNL2_QUEUE_TYPE_RX_BUFFER,
> > +						chunks);
> > +		if (num_regs < vport->num_bufq) {
> > +			ret = -EINVAL;
> > +			goto free_reg_vals;
> > +		}
> > +
> > +		num_regs = __iecm_queue_reg_init(vport, reg_vals, num_regs,
> > +
> VIRTCHNL2_QUEUE_TYPE_RX_BUFFER);
> > +		if (num_regs < vport->num_bufq) {
> > +			ret = -EINVAL;
> > +			goto free_reg_vals;
> > +		}
> > +	} else {
> > +		num_regs = iecm_vport_get_q_reg(reg_vals,
> IECM_LARGE_MAX_Q,
> > +						VIRTCHNL2_QUEUE_TYPE_RX,
> > +						chunks);
> > +		if (num_regs < vport->num_rxq) {
> > +			ret = -EINVAL;
> > +			goto free_reg_vals;
> > +		}
> > +
> > +		num_regs = __iecm_queue_reg_init(vport, reg_vals, num_regs,
> > +
> VIRTCHNL2_QUEUE_TYPE_RX);
> > +		if (num_regs < vport->num_rxq) {
> > +			ret = -EINVAL;
> > +			goto free_reg_vals;
> > +		}
> > +	}
> > +
> > +free_reg_vals:
> > +	kfree(reg_vals);
> > +	return ret;
> > +}
> > +
> >  /**
> >   * iecm_send_create_vport_msg - Send virtchnl create vport message
> >   * @adapter: Driver specific private structure
> > @@ -943,6 +1145,66 @@ static int iecm_send_create_vport_msg(struct
> iecm_adapter *adapter)
> >  				(u8 *)vport_msg);
> >  }
> >
> > +/**
> > + * iecm_check_descs - Verify we have the descriptor support required
> > + * @vport: virtual port structure
> > + * @rx_desc_ids: Rx descriptor ids to check
> > + * @tx_desc_ids: Tx descriptor ids to check
> > + * @rxq_model: Rx queue model
> > + * @txq_model: Tx queue model
> > + *
> > + * Returns 0 on success, negative if we didn't get sufficient descriptor
> > + * support.
> > + */
> > +int iecm_check_descs(struct iecm_vport *vport, u64 rx_desc_ids,
> > +		     u64 tx_desc_ids, u16 rxq_model, u16 txq_model)
> > +{
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +
> > +	if (rxq_model == VIRTCHNL2_QUEUE_MODEL_SPLIT) {
> > +		if (!(rx_desc_ids & VIRTCHNL2_RXDID_1_FLEX_SPLITQ_M)) {
> > +			dev_err(&adapter->pdev->dev, "No supported RX
> descriptors provided");
> > +			return -EINVAL;
> > +		}
> > +	} else {
> > +		if (!(rx_desc_ids & VIRTCHNL2_RXDID_2_FLEX_SQ_NIC_M))
> > +			vport->base_rxd = true;
> > +	}
> > +
> > +	if (txq_model == VIRTCHNL2_QUEUE_MODEL_SPLIT) {
> > +#define MIN_SUPPORT_TXDID (\
> > +		VIRTCHNL2_TXDID_FLEX_FLOW_SCHED |\
> > +		VIRTCHNL2_TXDID_FLEX_TSO_CTX |\
> > +		VIRTCHNL2_TXDID_FLEX_DATA)
> > +		if ((tx_desc_ids & MIN_SUPPORT_TXDID) !=
> MIN_SUPPORT_TXDID) {
> > +			dev_err(&adapter->pdev->dev, "Minimum TX descriptor
> support not provided");
> > +			return -EINVAL;
> > +		}
> > +	}
> > +	return 0;
> > +}
> > +EXPORT_SYMBOL(iecm_check_descs);
> > +
> > +/**
> > + * iecm_get_supported_desc_ids - Get supported Rx and Tx descriptor ids
> > + * @vport: virtual port structure
> > + *
> > + * Return 0 on success, error on failure
> > + */
> > +static int iecm_get_supported_desc_ids(struct iecm_vport *vport)
> > +{
> > +	struct virtchnl2_create_vport *vport_msg;
> > +
> > +	vport_msg = (struct virtchnl2_create_vport *)
> > +			vport->adapter->vport_params_recvd[0];
> > +	vport_msg->rx_desc_ids =
> cpu_to_le64(VIRTCHNL2_RXDID_1_FLEX_SPLITQ_M);
> > +	vport_msg->tx_desc_ids = cpu_to_le64(MIN_SUPPORT_TXDID);
> > +
> > +	return iecm_check_descs(vport, le64_to_cpu(vport_msg->rx_desc_ids),
> > +				le64_to_cpu(vport_msg->tx_desc_ids),
> > +				vport->rxq_model, vport->txq_model);
> > +}
> > +
> >  /**
> >   * iecm_recv_create_vport_msg - Receive virtchnl create vport message
> >   * @adapter: Driver specific private structure
> > @@ -1333,6 +1595,9 @@ int iecm_send_config_rx_queues_msg(struct
> iecm_vport *vport)
> >  					bufq->rx_buf_stride;
> >  				qi[k].rx_buffer_low_watermark =
> >  					cpu_to_le16(bufq-
> >rx_buffer_low_watermark);
> > +				if (iecm_is_feature_ena(vport,
> NETIF_F_GRO_HW))
> > +					qi[k].qflags |=
> > +
> 	cpu_to_le16(VIRTCHNL2_RXQ_RSC);
> >  			}
> >  		}
> >
> > @@ -1361,6 +1626,9 @@ int iecm_send_config_rx_queues_msg(struct
> iecm_vport *vport)
> >  					qi[k].hdr_buffer_size =
> >  						cpu_to_le16(rxq-
> >rx_hbuf_size);
> >  				}
> > +				if (iecm_is_feature_ena(vport,
> NETIF_F_GRO_HW))
> > +					qi[k].qflags |=
> > +
> 	cpu_to_le16(VIRTCHNL2_RXQ_RSC);
> >  			} else {
> >  				rxq = rx_qgrp->singleq.rxqs[j];
> >  			}
> > @@ -1968,6 +2236,765 @@ int iecm_send_add_queues_msg(struct
> iecm_vport *vport, u16 num_tx_q,
> >  	return err;
> >  }
> >
> > +/**
> > + * iecm_send_alloc_vectors_msg - Send virtchnl alloc vectors message
> > + * @adapter: Driver specific private structure
> > + * @num_vectors: number of vectors to be allocated
> > + *
> > + * Returns 0 on success, negative on failure.
> > + */
> > +int iecm_send_alloc_vectors_msg(struct iecm_adapter *adapter, u16
> num_vectors)
> > +{
> > +	struct virtchnl2_alloc_vectors *alloc_vec;
> > +	struct virtchnl2_alloc_vectors ac = {0};
> > +	int size, err;
> > +
> > +	ac.num_vectors = cpu_to_le16(num_vectors);
> > +
> > +	err = iecm_send_mb_msg(adapter, VIRTCHNL2_OP_ALLOC_VECTORS,
> > +			       sizeof(ac), (u8 *)&ac);
> > +	if (err)
> > +		return err;
> > +
> > +	err = iecm_wait_for_event(adapter, IECM_VC_ALLOC_VECTORS,
> > +				  IECM_VC_ALLOC_VECTORS_ERR);
> > +	if (err)
> > +		return err;
> > +
> > +	size = sizeof(struct virtchnl2_alloc_vectors) +
> > +		((num_vectors - 1) *
> > +		sizeof(struct virtchnl2_vector_chunk));
> > +
> > +	kfree(adapter->req_vec_chunks);
> > +	adapter->req_vec_chunks = NULL;
> > +	adapter->req_vec_chunks = kzalloc(size, GFP_KERNEL);
> > +	if (!adapter->req_vec_chunks) {
> > +		err = -ENOMEM;
> > +		goto error;
> > +	}
> > +	memcpy(adapter->req_vec_chunks, adapter->vc_msg, size);
> > +
> > +	alloc_vec = adapter->req_vec_chunks;
> > +	if (le16_to_cpu(alloc_vec->num_vectors) < num_vectors) {
> > +		kfree(adapter->req_vec_chunks);
> > +		adapter->req_vec_chunks = NULL;
> > +		err = -EINVAL;
> > +	}
> > +error:
> > +	clear_bit(__IECM_VC_MSG_PENDING, adapter->flags);
> > +	return err;
> > +}
> > +
> > +/**
> > + * iecm_send_dealloc_vectors_msg - Send virtchnl de allocate vectors
> message
> > + * @adapter: Driver specific private structure
> > + *
> > + * Returns 0 on success, negative on failure.
> > + */
> > +int iecm_send_dealloc_vectors_msg(struct iecm_adapter *adapter)
> > +{
> > +	struct virtchnl2_vector_chunks *vcs;
> > +	struct virtchnl2_alloc_vectors *ac;
> > +	int buf_size, err;
> > +
> > +	ac = adapter->req_vec_chunks;
> > +	vcs = &ac->vchunks;
> > +
> > +	buf_size = sizeof(struct virtchnl2_vector_chunks) +
> > +			((le16_to_cpu(vcs->num_vchunks) - 1) *
> > +			sizeof(struct virtchnl2_vector_chunk));
> > +
> > +	err = iecm_send_mb_msg(adapter,
> VIRTCHNL2_OP_DEALLOC_VECTORS, buf_size,
> > +			       (u8 *)vcs);
> > +	if (err)
> > +		return err;
> > +	err = iecm_min_wait_for_event(adapter,
> IECM_VC_DEALLOC_VECTORS,
> > +				      IECM_VC_DEALLOC_VECTORS_ERR);
> > +	if (err)
> > +		return err;
> > +
> > +	kfree(adapter->req_vec_chunks);
> > +	adapter->req_vec_chunks = NULL;
> > +	return 0;
> > +}
> > +
> > +/**
> > + * iecm_send_get_stats_msg - Send virtchnl get statistics message
> > + * @vport: vport to get stats for
> > + *
> > + * Returns 0 on success, negative on failure.
> > + */
> > +int iecm_send_get_stats_msg(struct iecm_vport *vport)
> > +{
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	struct virtchnl2_vport_stats *stats;
> > +	int err = 0;
> > +
> > +	stats = (struct virtchnl2_vport_stats *)adapter->vc_msg;
> > +
> > +	/* Don't send get_stats message if one is pending or the
> > +	 * link is down
> > +	 */
> > +	if (test_bit(IECM_VC_GET_STATS, adapter->vc_state) ||
> > +	    adapter->state <= __IECM_DOWN)
> > +		goto error;
> > +
> > +	stats->vport_id = cpu_to_le32(vport->vport_id);
> > +
> > +	err = iecm_send_mb_msg(adapter, VIRTCHNL2_OP_GET_STATS,
> > +			       sizeof(stats), (u8 *)&stats);
> > +	if (err)
> > +		goto error;
> > +
> > +	err = iecm_wait_for_event(adapter, IECM_VC_GET_STATS,
> > +				  IECM_VC_GET_STATS_ERR);
> > +	if (err)
> > +		goto error;
> > +
> > +	vport->netstats.rx_packets = le64_to_cpu(stats->rx_unicast) +
> > +				     le64_to_cpu(stats->rx_multicast) +
> > +				     le64_to_cpu(stats->rx_broadcast);
> > +	vport->netstats.tx_packets = le64_to_cpu(stats->tx_unicast) +
> > +				     le64_to_cpu(stats->tx_multicast) +
> > +				     le64_to_cpu(stats->tx_broadcast);
> > +	vport->netstats.rx_bytes = le64_to_cpu(stats->rx_bytes);
> > +	vport->netstats.tx_bytes = le64_to_cpu(stats->tx_bytes);
> > +	vport->netstats.tx_errors = le64_to_cpu(stats->tx_errors);
> > +	vport->netstats.rx_dropped = le64_to_cpu(stats->rx_discards);
> > +	vport->netstats.tx_dropped = le64_to_cpu(stats->tx_discards);
> > +	vport->port_stats.vport_stats = *stats;
> > +	clear_bit(__IECM_VC_MSG_PENDING, adapter->flags);
> > +error:
> > +	clear_bit(__IECM_MB_STATS_PENDING, vport->adapter->flags);
> > +
> > +	return err;
> > +}
> > +
> > +/**
> > + * iecm_send_get_set_rss_hash_msg - Send set or get rss hash message
> > + * @vport: virtual port data structure
> > + * @get: flag to get or set rss hash
> > + *
> > + * Returns 0 on success, negative on failure.
> > + */
> > +int iecm_send_get_set_rss_hash_msg(struct iecm_vport *vport, bool get)
> > +{
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	struct virtchnl2_rss_hash rh = {0};
> > +	int err;
> > +
> > +	rh.vport_id = cpu_to_le32(vport->vport_id);
> > +	rh.ptype_groups = cpu_to_le64(adapter->rss_data.rss_hash);
> > +
> > +	if (get) {
> > +		err = iecm_send_mb_msg(adapter,
> VIRTCHNL2_OP_GET_RSS_HASH,
> > +				       sizeof(rh), (u8 *)&rh);
> > +		if (err)
> > +			return err;
> > +
> > +		err = iecm_wait_for_event(adapter, IECM_VC_GET_RSS_HASH,
> > +					  IECM_VC_GET_RSS_HASH_ERR);
> > +		if (err)
> > +			return err;
> > +
> > +		memcpy(&rh, adapter->vc_msg, sizeof(rh));
> > +		adapter->rss_data.rss_hash = le64_to_cpu(rh.ptype_groups);
> > +		/* Leave the buffer clean for next message */
> > +		memset(adapter->vc_msg, 0, IECM_DFLT_MBX_BUF_SIZE);
> > +		clear_bit(__IECM_VC_MSG_PENDING, adapter->flags);
> > +
> > +		return 0;
> > +	}
> > +
> > +	err = iecm_send_mb_msg(adapter, VIRTCHNL2_OP_SET_RSS_HASH,
> > +			       sizeof(rh), (u8 *)&rh);
> > +	if (err)
> > +		return err;
> > +
> > +	return  iecm_wait_for_event(adapter, IECM_VC_SET_RSS_HASH,
> > +				    IECM_VC_SET_RSS_HASH_ERR);
> > +}
> > +
> > +/**
> > + * iecm_send_get_set_rss_lut_msg - Send virtchnl get or set rss lut message
> > + * @vport: virtual port data structure
> > + * @get: flag to set or get rss look up table
> > + *
> > + * Returns 0 on success, negative on failure.
> > + */
> > +int iecm_send_get_set_rss_lut_msg(struct iecm_vport *vport, bool get)
> > +{
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	struct virtchnl2_rss_lut *recv_rl;
> > +	struct virtchnl2_rss_lut *rl;
> > +	int buf_size, lut_buf_size;
> > +	int i, err = 0;
> > +
> > +	buf_size = sizeof(struct virtchnl2_rss_lut) +
> > +		       (sizeof(u32) * (adapter->rss_data.rss_lut_size - 1));
> > +	rl = kzalloc(buf_size, GFP_KERNEL);
> > +	if (!rl)
> > +		return -ENOMEM;
> > +
> > +	if (!get) {
> > +		rl->lut_entries = cpu_to_le16(adapter->rss_data.rss_lut_size);
> > +		for (i = 0; i < adapter->rss_data.rss_lut_size; i++)
> > +			rl->lut[i] = cpu_to_le32(adapter->rss_data.rss_lut[i]);
> > +	}
> > +	rl->vport_id = cpu_to_le32(vport->vport_id);
> > +
> > +	if (get) {
> > +		err = iecm_send_mb_msg(vport->adapter,
> VIRTCHNL2_OP_GET_RSS_LUT,
> > +				       buf_size, (u8 *)rl);
> > +		if (err)
> > +			goto error;
> > +
> > +		err = iecm_wait_for_event(adapter, IECM_VC_GET_RSS_LUT,
> > +					  IECM_VC_GET_RSS_LUT_ERR);
> > +		if (err)
> > +			goto error;
> > +
> > +		recv_rl = (struct virtchnl2_rss_lut *)adapter->vc_msg;
> > +		if (adapter->rss_data.rss_lut_size !=
> > +		    le16_to_cpu(recv_rl->lut_entries)) {
> > +			adapter->rss_data.rss_lut_size =
> > +				le16_to_cpu(recv_rl->lut_entries);
> > +			kfree(adapter->rss_data.rss_lut);
> > +
> > +			lut_buf_size = adapter->rss_data.rss_lut_size *
> > +					sizeof(u32);
> > +			adapter->rss_data.rss_lut = kzalloc(lut_buf_size,
> > +							    GFP_KERNEL);
> > +			if (!adapter->rss_data.rss_lut) {
> > +				adapter->rss_data.rss_lut_size = 0;
> > +				/* Leave the buffer clean */
> > +				memset(adapter->vc_msg, 0,
> > +				       IECM_DFLT_MBX_BUF_SIZE);
> > +				clear_bit(__IECM_VC_MSG_PENDING,
> > +					  adapter->flags);
> > +				err = -ENOMEM;
> > +				goto error;
> > +			}
> > +		}
> > +		memcpy(adapter->rss_data.rss_lut, adapter->vc_msg,
> > +		       adapter->rss_data.rss_lut_size);
> > +		/* Leave the buffer clean for next message */
> > +		memset(adapter->vc_msg, 0, IECM_DFLT_MBX_BUF_SIZE);
> > +		clear_bit(__IECM_VC_MSG_PENDING, adapter->flags);
> > +	} else {
> > +		err = iecm_send_mb_msg(adapter,
> VIRTCHNL2_OP_SET_RSS_LUT,
> > +				       buf_size, (u8 *)rl);
> > +		if (err)
> > +			goto error;
> > +
> > +		err = iecm_wait_for_event(adapter, IECM_VC_SET_RSS_LUT,
> > +					  IECM_VC_SET_RSS_LUT_ERR);
> > +	}
> > +error:
> > +	kfree(rl);
> > +	return err;
> > +}
> > +
> > +/**
> > + * iecm_send_get_set_rss_key_msg - Send virtchnl get or set rss key message
> > + * @vport: virtual port data structure
> > + * @get: flag to set or get rss look up table
> > + *
> > + * Returns 0 on success, negative on failure
> > + */
> > +int iecm_send_get_set_rss_key_msg(struct iecm_vport *vport, bool get)
> > +{
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	struct virtchnl2_rss_key *recv_rk;
> > +	struct virtchnl2_rss_key *rk;
> > +	int i, buf_size, err = 0;
> > +
> > +	buf_size = sizeof(struct virtchnl2_rss_key) +
> > +		       (sizeof(u8) * (adapter->rss_data.rss_key_size - 1));
> > +	rk = kzalloc(buf_size, GFP_KERNEL);
> > +	if (!rk)
> > +		return -ENOMEM;
> > +	rk->vport_id = cpu_to_le32(vport->vport_id);
> > +
> > +	if (get) {
> > +		err = iecm_send_mb_msg(adapter,
> VIRTCHNL2_OP_GET_RSS_KEY,
> > +				       buf_size, (u8 *)rk);
> > +		if (err)
> > +			goto error;
> > +
> > +		err = iecm_wait_for_event(adapter, IECM_VC_GET_RSS_KEY,
> > +					  IECM_VC_GET_RSS_KEY_ERR);
> > +		if (err)
> > +			goto error;
> > +
> > +		recv_rk = (struct virtchnl2_rss_key *)adapter->vc_msg;
> > +		if (adapter->rss_data.rss_key_size !=
> > +		    le16_to_cpu(recv_rk->key_len)) {
> > +			adapter->rss_data.rss_key_size =
> > +				min_t(u16, NETDEV_RSS_KEY_LEN,
> > +				      le16_to_cpu(recv_rk->key_len));
> > +			kfree(adapter->rss_data.rss_key);
> > +			adapter->rss_data.rss_key = kzalloc(adapter-
> >rss_data.rss_key_size,
> > +							    GFP_KERNEL);
> > +			if (!adapter->rss_data.rss_key) {
> > +				adapter->rss_data.rss_key_size = 0;
> > +				/* Leave the buffer clean */
> > +				memset(adapter->vc_msg, 0,
> > +				       IECM_DFLT_MBX_BUF_SIZE);
> > +				clear_bit(__IECM_VC_MSG_PENDING,
> > +					  adapter->flags);
> > +				err = -ENOMEM;
> > +				goto error;
> > +			}
> > +		}
> > +		memcpy(adapter->rss_data.rss_key, adapter->vc_msg,
> > +		       adapter->rss_data.rss_key_size);
> > +		/* Leave the buffer clean for next message */
> > +		memset(adapter->vc_msg, 0, IECM_DFLT_MBX_BUF_SIZE);
> > +		clear_bit(__IECM_VC_MSG_PENDING, adapter->flags);
> > +	} else {
> > +		rk->key_len = cpu_to_le16(adapter->rss_data.rss_key_size);
> > +		for (i = 0; i < adapter->rss_data.rss_key_size; i++)
> > +			rk->key[i] = adapter->rss_data.rss_key[i];
> > +
> > +		err = iecm_send_mb_msg(adapter,
> VIRTCHNL2_OP_SET_RSS_KEY,
> > +				       buf_size, (u8 *)rk);
> > +		if (err)
> > +			goto error;
> > +
> > +		err = iecm_wait_for_event(adapter, IECM_VC_SET_RSS_KEY,
> > +					  IECM_VC_SET_RSS_KEY_ERR);
> > +	}
> > +error:
> > +	kfree(rk);
> > +	return err;
> > +}
> > +
> > +/**
> > + * iecm_tpid_to_ethertype - transform from VLAN TPID to virtchnl ethertype
> > + * @tpid: VLAN TPID (i.e. 0x8100, 0x88a8, etc.)
> > + *
> > + * Return virtchnl ethertype
> > + */
> > +static u32 iecm_tpid_to_ethertype(u16 tpid)
> > +{
> > +	switch (tpid) {
> > +	case ETH_P_8021Q:
> > +		return VIRTCHNL_VLAN_ETHERTYPE_8100;
> > +	case ETH_P_8021AD:
> > +		return VIRTCHNL_VLAN_ETHERTYPE_88A8;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +/**
> > + * iecm_set_vlan_offload_ethertype - set ethertype for offload message
> > + * @adapter: adapter structure
> > + * @msg: message structure used for updating offloads
> > + * @offload_op: opcode used to determine which support structure to check
> > + *
> > + * Return 0 on success, negative on failure.
> > + */
> > +static int
> > +iecm_set_vlan_offload_ethertype(struct iecm_adapter *adapter,
> > +				struct virtchnl_vlan_setting *msg,
> > +				enum virtchnl_ops offload_op)
> > +{
> > +	struct virtchnl_vlan_supported_caps *offload_support;
> > +	u16 tpid = adapter->config_data.vlan_ethertype;
> > +	u32 vc_ethertype;
> > +
> > +	vc_ethertype = iecm_tpid_to_ethertype(tpid);
> > +	/* reference the correct offload support structure */
> > +	switch (offload_op) {
> > +	case VIRTCHNL_OP_ENABLE_VLAN_STRIPPING_V2:
> > +	case VIRTCHNL_OP_DISABLE_VLAN_STRIPPING_V2:
> > +		offload_support =
> > +			&adapter->vlan_caps->offloads.stripping_support;
> > +		break;
> > +	case VIRTCHNL_OP_ENABLE_VLAN_INSERTION_V2:
> > +	case VIRTCHNL_OP_DISABLE_VLAN_INSERTION_V2:
> > +		offload_support =
> > +			&adapter->vlan_caps->offloads.insertion_support;
> > +		break;
> > +	default:
> > +		dev_err(&adapter->pdev->dev, "Invalid opcode %d for setting
> virtchnl ethertype to enable/disable VLAN offloads\n",
> > +			offload_op);
> > +		return -EINVAL;
> > +	}
> > +
> > +	/* make sure ethertype is supported and turning feature on/off
> > +	 * is allowed
> > +	 */
> > +	if ((offload_support->outer & vc_ethertype) &&
> > +	    (offload_support->outer & VIRTCHNL_VLAN_TOGGLE)) {
> > +		msg->outer_ethertype_setting = vc_ethertype;
> > +	} else if ((offload_support->inner & vc_ethertype) &&
> > +		   (offload_support->inner & VIRTCHNL_VLAN_TOGGLE)) {
> > +		msg->inner_ethertype_setting = vc_ethertype;
> > +	} else {
> > +		dev_err(&adapter->pdev->dev, "opcode %d unsupported for
> VLAN TPID 0x%04x\n",
> > +			offload_op, tpid);
> > +		return -EINVAL;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +/**
> > + * iecm_send_strip_vlan_msg - Send enable/disable vlan stripping message
> > + * @vport: vport structure
> > + * @ena: enable or disable vlan stripping
> > + *
> > + * Returns 0 on success, negative on failure.
> > + */
> > +static int iecm_send_strip_vlan_msg(struct iecm_vport *vport, bool ena)
> > +{
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	enum iecm_vport_vc_state vc, vc_err;
> > +	struct virtchnl_vlan_setting *msg;
> > +	enum virtchnl_ops vop;
> > +	int err, len;
> > +
> > +	len = sizeof(struct virtchnl_vlan_setting);
> > +	msg = kzalloc(sizeof(*msg), GFP_KERNEL);
> > +
> > +	if (!msg)
> > +		return -ENOMEM;
> > +
> > +	msg->vport_id = vport->vport_id;
> > +	if (ena) {
> > +		vop = VIRTCHNL_OP_ENABLE_VLAN_STRIPPING_V2;
> > +		vc = IECM_VC_STRIPPING_ENA_VLAN_V2;
> > +		vc_err = IECM_VC_STRIPPING_ENA_VLAN_V2_ERR;
> > +	} else {
> > +		vop = VIRTCHNL_OP_DISABLE_VLAN_STRIPPING_V2;
> > +		vc = IECM_VC_STRIPPING_DIS_VLAN_V2;
> > +		vc_err = IECM_VC_STRIPPING_DIS_VLAN_V2_ERR;
> > +	}
> > +
> > +	err = iecm_set_vlan_offload_ethertype(adapter, msg, vop);
> > +	if (!err) {
> > +		err = iecm_send_mb_msg(adapter, vop, len, (u8 *)msg);
> > +		if (!err)
> > +			err = iecm_wait_for_event(adapter, vc, vc_err);
> > +	}
> > +
> > +	kfree(msg);
> > +
> > +	return err;
> > +}
> > +
> > +/**
> > + * iecm_send_insert_vlan_msg - Send enable/disable vlan insertion message
> > + * @vport: vport structure
> > + * @ena: enable/disable vlan insertion
> > + *
> > + * Returns 0 on success, negative on failure.
> > + */
> > +static int iecm_send_insert_vlan_msg(struct iecm_vport *vport, bool ena)
> > +{
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	enum iecm_vport_vc_state vc, vc_err;
> > +	struct virtchnl_vlan_setting *msg;
> > +	enum virtchnl_ops vop;
> > +	int err, len;
> > +
> > +	len = sizeof(struct virtchnl_vlan_setting);
> > +	msg = kzalloc(sizeof(*msg), GFP_KERNEL);
> > +
> > +	if (!msg)
> > +		return -ENOMEM;
> > +
> > +	msg->vport_id = vport->vport_id;
> > +
> > +	if (ena) {
> > +		vop = VIRTCHNL_OP_ENABLE_VLAN_INSERTION_V2;
> > +		vc = IECM_VC_INSERTION_ENA_VLAN_V2;
> > +		vc_err = IECM_VC_INSERTION_ENA_VLAN_V2_ERR;
> > +	} else {
> > +		vop = VIRTCHNL_OP_DISABLE_VLAN_INSERTION_V2;
> > +		vc = IECM_VC_INSERTION_DIS_VLAN_V2;
> > +		vc_err = IECM_VC_INSERTION_DIS_VLAN_V2_ERR;
> > +	}
> > +
> > +	err = iecm_set_vlan_offload_ethertype(adapter, msg, vop);
> > +	if (!err) {
> > +		err = iecm_send_mb_msg(adapter, vop, len, (u8 *)msg);
> > +		if (!err)
> > +			err = iecm_wait_for_event(adapter, vc, vc_err);
> > +	}
> > +
> > +	kfree(msg);
> > +
> > +	return err;
> > +}
> > +
> > +/**
> > + * iecm_fill_ptype_lookup - Fill L3 specific fields in ptype lookup table
> > + * @ptype: ptype lookup table
> > + * @pstate: state machine for ptype lookup table
> > + * @ipv4: ipv4 or ipv6
> > + * @frag: fragmentation allowed
> > + *
> > + */
> > +static void iecm_fill_ptype_lookup(struct iecm_rx_ptype_decoded *ptype,
> > +				   struct iecm_ptype_state *pstate,
> > +				   bool ipv4, bool frag)
> > +{
> > +	if (!pstate->outer_ip || !pstate->outer_frag) {
> > +		ptype->outer_ip = IECM_RX_PTYPE_OUTER_IP;
> > +		pstate->outer_ip = true;
> > +
> > +		if (ipv4)
> > +			ptype->outer_ip_ver = IECM_RX_PTYPE_OUTER_IPV4;
> > +		else
> > +			ptype->outer_ip_ver = IECM_RX_PTYPE_OUTER_IPV6;
> > +
> > +		if (frag) {
> > +			ptype->outer_frag = IECM_RX_PTYPE_FRAG;
> > +			pstate->outer_frag = true;
> > +		}
> > +	} else {
> > +		ptype->tunnel_type = IECM_RX_PTYPE_TUNNEL_IP_IP;
> > +		pstate->tunnel_state = IECM_PTYPE_TUNNEL_IP;
> > +
> > +		if (ipv4)
> > +			ptype->tunnel_end_prot =
> > +					IECM_RX_PTYPE_TUNNEL_END_IPV4;
> > +		else
> > +			ptype->tunnel_end_prot =
> > +					IECM_RX_PTYPE_TUNNEL_END_IPV6;
> > +
> > +		if (frag)
> > +			ptype->tunnel_end_frag = IECM_RX_PTYPE_FRAG;
> > +	}
> > +}
> > +
> > +/**
> > + * iecm_send_get_rx_ptype_msg - Send virtchnl for ptype info
> > + * @vport: virtual port data structure
> > + *
> > + * Returns 0 on success, negative on failure.
> > + */
> > +int iecm_send_get_rx_ptype_msg(struct iecm_vport *vport)
> > +{
> > +	struct iecm_rx_ptype_decoded *ptype_lkup = vport->rx_ptype_lkup;
> > +	struct virtchnl2_get_ptype_info *get_ptype_info, *ptype_info;
> > +	int max_ptype, ptypes_recvd = 0, len, ptype_offset;
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	int err = 0, i, j, k = 0;
> > +
> > +	if (iecm_is_queue_model_split(vport->rxq_model))
> > +		max_ptype = IECM_RX_MAX_PTYPE;
> > +	else
> > +		max_ptype = IECM_RX_MAX_BASE_PTYPE;
> > +
> > +	for (i = 0; i < max_ptype; i++)
> > +		ptype_lkup[i] = iecm_ptype_lookup[0];
> > +
> > +	len = sizeof(struct virtchnl2_get_ptype_info);
> > +	get_ptype_info = kzalloc(len, GFP_KERNEL);
> > +	if (!get_ptype_info)
> > +		return -ENOMEM;
> > +
> > +	get_ptype_info->start_ptype_id = 0;
> > +	get_ptype_info->num_ptypes = cpu_to_le16(max_ptype);
> > +
> > +	err = iecm_send_mb_msg(adapter, VIRTCHNL2_OP_GET_PTYPE_INFO,
> > +			       len, (u8 *)get_ptype_info);
> > +	if (err)
> > +		goto get_ptype_rel;
> > +
> > +	while (ptypes_recvd < max_ptype) {
> > +		err = iecm_wait_for_event(adapter,
> IECM_VC_GET_PTYPE_INFO,
> > +					  IECM_VC_GET_PTYPE_INFO_ERR);
> > +		if (err)
> > +			goto get_ptype_rel;
> > +
> > +		len = IECM_DFLT_MBX_BUF_SIZE;
> > +		ptype_info = kzalloc(len, GFP_KERNEL);
> > +		if (!ptype_info) {
> > +			err = -ENOMEM;
> > +			goto clear_vc_flag;
> > +		}
> > +
> > +		memcpy(ptype_info, adapter->vc_msg, len);
> > +
> > +		ptypes_recvd += le16_to_cpu(ptype_info->num_ptypes);
> > +		if (ptypes_recvd > max_ptype) {
> > +			err = -EINVAL;
> > +			goto ptype_rel;
> > +		}
> > +
> > +		ptype_offset = sizeof(struct virtchnl2_get_ptype_info) -
> > +						sizeof(struct virtchnl2_ptype);
> > +
> > +		for (i = 0; i < le16_to_cpu(ptype_info->num_ptypes); i++) {
> > +			struct iecm_ptype_state pstate = { 0 };
> > +			struct virtchnl2_ptype *ptype;
> > +			u16 id;
> > +
> > +			ptype = (struct virtchnl2_ptype *)
> > +					((u8 *)ptype_info + ptype_offset);
> > +
> > +			ptype_offset += IECM_GET_PTYPE_SIZE(ptype);
> > +			if (ptype_offset > len) {
> > +				err = -EINVAL;
> > +				goto ptype_rel;
> > +			}
> > +
> > +			if (le16_to_cpu(ptype->ptype_id_10) == 0xFFFF)
> > +				goto ptype_rel;
> > +
> > +			if (iecm_is_queue_model_split(vport->rxq_model))
> > +				k = le16_to_cpu(ptype->ptype_id_10);
> > +			else
> > +				k = ptype->ptype_id_8;
> > +
> > +			if (ptype->proto_id_count)
> > +				ptype_lkup[k].known = 1;
> > +
> > +			for (j = 0; j < ptype->proto_id_count; j++) {
> > +				id = le16_to_cpu(ptype->proto_id[j]);
> > +				switch (id) {
> > +				case VIRTCHNL2_PROTO_HDR_GRE:
> > +					if (pstate.tunnel_state ==
> > +
> 	IECM_PTYPE_TUNNEL_IP) {
> > +						ptype_lkup[k].tunnel_type =
> > +
> 	IECM_RX_PTYPE_TUNNEL_IP_GRENAT;
> > +						pstate.tunnel_state |=
> > +
> 	IECM_PTYPE_TUNNEL_IP_GRENAT;
> > +					}
> > +					break;
> > +				case VIRTCHNL2_PROTO_HDR_MAC:
> > +					ptype_lkup[k].outer_ip =
> > +						IECM_RX_PTYPE_OUTER_L2;
> > +					if (pstate.tunnel_state ==
> > +							IECM_TUN_IP_GRE) {
> > +						ptype_lkup[k].tunnel_type =
> > +
> 	IECM_RX_PTYPE_TUNNEL_IP_GRENAT_MAC;
> > +						pstate.tunnel_state |=
> > +
> 	IECM_PTYPE_TUNNEL_IP_GRENAT_MAC;
> > +					}
> > +					break;
> > +				case VIRTCHNL2_PROTO_HDR_VLAN:
> > +					if (pstate.tunnel_state ==
> > +
> 	IECM_TUN_IP_GRE_MAC) {
> > +						ptype_lkup[k].tunnel_type =
> > +
> 	IECM_RX_PTYPE_TUNNEL_IP_GRENAT_MAC_VLAN;
> > +						pstate.tunnel_state |=
> > +
> 	IECM_PTYPE_TUNNEL_IP_GRENAT_MAC_VLAN;
> > +					}
> > +					break;
> > +				case VIRTCHNL2_PROTO_HDR_IPV4:
> > +
> 	iecm_fill_ptype_lookup(&ptype_lkup[k],
> > +							       &pstate, true,
> > +							       false);
> > +					break;
> > +				case VIRTCHNL2_PROTO_HDR_IPV6:
> > +
> 	iecm_fill_ptype_lookup(&ptype_lkup[k],
> > +							       &pstate, false,
> > +							       false);
> > +					break;
> > +				case VIRTCHNL2_PROTO_HDR_IPV4_FRAG:
> > +
> 	iecm_fill_ptype_lookup(&ptype_lkup[k],
> > +							       &pstate, true,
> > +							       true);
> > +					break;
> > +				case VIRTCHNL2_PROTO_HDR_IPV6_FRAG:
> > +
> 	iecm_fill_ptype_lookup(&ptype_lkup[k],
> > +							       &pstate, false,
> > +							       true);
> > +					break;
> > +				case VIRTCHNL2_PROTO_HDR_UDP:
> > +					ptype_lkup[k].inner_prot =
> > +					IECM_RX_PTYPE_INNER_PROT_UDP;
> > +					break;
> > +				case VIRTCHNL2_PROTO_HDR_TCP:
> > +					ptype_lkup[k].inner_prot =
> > +					IECM_RX_PTYPE_INNER_PROT_TCP;
> > +					break;
> > +				case VIRTCHNL2_PROTO_HDR_SCTP:
> > +					ptype_lkup[k].inner_prot =
> > +					IECM_RX_PTYPE_INNER_PROT_SCTP;
> > +					break;
> > +				case VIRTCHNL2_PROTO_HDR_ICMP:
> > +					ptype_lkup[k].inner_prot =
> > +					IECM_RX_PTYPE_INNER_PROT_ICMP;
> > +					break;
> > +				case VIRTCHNL2_PROTO_HDR_PAY:
> > +					ptype_lkup[k].payload_layer =
> > +
> 	IECM_RX_PTYPE_PAYLOAD_LAYER_PAY2;
> > +					break;
> > +				case VIRTCHNL2_PROTO_HDR_ICMPV6:
> > +				case VIRTCHNL2_PROTO_HDR_IPV6_EH:
> > +				case VIRTCHNL2_PROTO_HDR_PRE_MAC:
> > +				case VIRTCHNL2_PROTO_HDR_POST_MAC:
> > +				case VIRTCHNL2_PROTO_HDR_ETHERTYPE:
> > +				case VIRTCHNL2_PROTO_HDR_SVLAN:
> > +				case VIRTCHNL2_PROTO_HDR_CVLAN:
> > +				case VIRTCHNL2_PROTO_HDR_MPLS:
> > +				case VIRTCHNL2_PROTO_HDR_MMPLS:
> > +				case VIRTCHNL2_PROTO_HDR_PTP:
> > +				case VIRTCHNL2_PROTO_HDR_CTRL:
> > +				case VIRTCHNL2_PROTO_HDR_LLDP:
> > +				case VIRTCHNL2_PROTO_HDR_ARP:
> > +				case VIRTCHNL2_PROTO_HDR_ECP:
> > +				case VIRTCHNL2_PROTO_HDR_EAPOL:
> > +				case VIRTCHNL2_PROTO_HDR_PPPOD:
> > +				case VIRTCHNL2_PROTO_HDR_PPPOE:
> > +				case VIRTCHNL2_PROTO_HDR_IGMP:
> > +				case VIRTCHNL2_PROTO_HDR_AH:
> > +				case VIRTCHNL2_PROTO_HDR_ESP:
> > +				case VIRTCHNL2_PROTO_HDR_IKE:
> > +				case VIRTCHNL2_PROTO_HDR_NATT_KEEP:
> > +				case VIRTCHNL2_PROTO_HDR_L2TPV2:
> > +				case
> VIRTCHNL2_PROTO_HDR_L2TPV2_CONTROL:
> > +				case VIRTCHNL2_PROTO_HDR_L2TPV3:
> > +				case VIRTCHNL2_PROTO_HDR_GTP:
> > +				case VIRTCHNL2_PROTO_HDR_GTP_EH:
> > +				case VIRTCHNL2_PROTO_HDR_GTPCV2:
> > +				case VIRTCHNL2_PROTO_HDR_GTPC_TEID:
> > +				case VIRTCHNL2_PROTO_HDR_GTPU:
> > +				case VIRTCHNL2_PROTO_HDR_GTPU_UL:
> > +				case VIRTCHNL2_PROTO_HDR_GTPU_DL:
> > +				case VIRTCHNL2_PROTO_HDR_ECPRI:
> > +				case VIRTCHNL2_PROTO_HDR_VRRP:
> > +				case VIRTCHNL2_PROTO_HDR_OSPF:
> > +				case VIRTCHNL2_PROTO_HDR_TUN:
> > +				case VIRTCHNL2_PROTO_HDR_NVGRE:
> > +				case VIRTCHNL2_PROTO_HDR_VXLAN:
> > +				case VIRTCHNL2_PROTO_HDR_VXLAN_GPE:
> > +				case VIRTCHNL2_PROTO_HDR_GENEVE:
> > +				case VIRTCHNL2_PROTO_HDR_NSH:
> > +				case VIRTCHNL2_PROTO_HDR_QUIC:
> > +				case VIRTCHNL2_PROTO_HDR_PFCP:
> > +				case VIRTCHNL2_PROTO_HDR_PFCP_NODE:
> > +				case VIRTCHNL2_PROTO_HDR_PFCP_SESSION:
> > +				case VIRTCHNL2_PROTO_HDR_RTP:
> > +				case VIRTCHNL2_PROTO_HDR_NO_PROTO:
> > +				default:
> > +					continue;
> > +				}
> > +			}
> > +		}
> > +		clear_bit(__IECM_VC_MSG_PENDING, adapter->flags);
> > +		kfree(ptype_info);
> > +	}
> > +	kfree(get_ptype_info);
> > +	return 0;
> > +
> > +ptype_rel:
> > +	kfree(ptype_info);
> > +clear_vc_flag:
> > +	clear_bit(__IECM_VC_MSG_PENDING, adapter->flags);
> > +get_ptype_rel:
> > +	kfree(get_ptype_info);
> > +	return err;
> > +}
> > +
> >  /**
> >   * iecm_find_ctlq - Given a type and id, find ctlq info
> >   * @hw: hardware struct
> > @@ -2478,6 +3505,25 @@ static int iecm_vport_queue_ids_init(struct
> iecm_vport *vport)
> >  	return 0;
> >  }
> >
> > +/**
> > + * iecm_vport_adjust_qs - Adjust to new requested queues
> > + * @vport: virtual port data struct
> > + *
> > + * Renegotiate queues.  Returns 0 on success, negative on failure.
> > + */
> > +void iecm_vport_adjust_qs(struct iecm_vport *vport)
> > +{
> > +	struct virtchnl2_create_vport vport_msg;
> > +
> > +	vport_msg.txq_model = cpu_to_le16(vport->txq_model);
> > +	vport_msg.rxq_model = cpu_to_le16(vport->rxq_model);
> > +	iecm_vport_calc_total_qs(vport->adapter, &vport_msg);
> > +
> > +	iecm_vport_init_num_qs(vport, &vport_msg);
> > +	iecm_vport_calc_num_q_groups(vport);
> > +	iecm_vport_calc_num_q_vec(vport);
> > +}
> > +
> >  /**
> >   * iecm_is_capability_ena - Default implementation of capability checking
> >   * @adapter: Private data struct
> > @@ -2522,6 +3568,117 @@ static u16 iecm_get_reserved_vectors(struct
> iecm_adapter *adapter)
> >  	return le16_to_cpu(caps->num_allocated_vectors);
> >  }
> >
> > +/**
> > + * iecm_get_max_tx_bufs - Max scatter-gather TX buffers
> > + * @adapter: Private data struct
> > + *
> > + * Return maximum number of buffers that can be used in scatter-gather
> before
> > + * they need to be linearized for hardware.
> > + */
> > +static unsigned int iecm_get_max_tx_bufs(struct iecm_adapter *adapter)
> > +{
> > +	return ((struct virtchnl2_get_capabilities *)adapter->caps)-
> >max_sg_bufs_per_tx_pkt;
> 
> 93-cols line here, you can introduce some interm variables to reduce
> the burden.
> 

Will fix.

> > +}
> > +
> > +/**
> > + * iecm_add_del_vlans - Add or delete vlan filter
> > + * @vport: vport structure
> > + * @add: add or delete
> > + *
> > + * Request that the PF add one or more VLAN filters to our VSI.
> > + */
> > +static void iecm_add_del_vlans(struct iecm_vport *vport, bool add)
> > +{
> > +	struct virtchnl_vlan_supported_caps *filtering_support;
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	struct virtchnl_vlan_filter_list_v2 *vvfl_v2;
> > +	int total_vlans = 0, num_vlans, i, len;
> > +	struct iecm_vlan_filter *f, *ftmp;
> > +	struct virtchnl_vlan *vlan;
> > +	enum virtchnl_ops vop;
> > +	int err = 0;
> > +
> > +	spin_lock_bh(&adapter->vlan_list_lock);
> > +
> > +	list_for_each_entry(f, &adapter->config_data.vlan_filter_list, list) {
> > +		if ((add && f->add) || (!add && f->remove))
> > +			total_vlans++;
> > +	}
> 
> Braces are redundant, if-something is one command from the outer view.
>
> > +
> > +	if (!total_vlans) {
> > +		spin_unlock_bh(&adapter->vlan_list_lock);
> > +		return;
> > +	}
> > +
> > +	len = sizeof(struct virtchnl_vlan_filter_list_v2) +
> > +		(IECM_VLANS_PER_MSG * sizeof(struct virtchnl_vlan_filter));
> > +	vvfl_v2 = kzalloc(len, GFP_ATOMIC);
> > +
> > +	if (!vvfl_v2) {
> > +		err = -ENOMEM;
> > +		goto error;
> > +	}
> > +
> > +	if (add)
> > +		vop = VIRTCHNL_OP_ADD_VLAN_V2;
> > +	else
> > +		vop = VIRTCHNL_OP_DEL_VLAN_V2;
> > +
> > +	while (total_vlans) {
> > +		if (total_vlans > IECM_VLANS_PER_MSG)
> > +			num_vlans = IECM_VLANS_PER_MSG;
> > +		else
> > +			num_vlans = total_vlans;
> > +		total_vlans -= num_vlans;
> > +
> > +		len = sizeof(struct virtchnl_vlan_filter_list_v2) +
> > +			((num_vlans - 1) * sizeof(struct virtchnl_vlan_filter));
> > +		vvfl_v2->vport_id = vport->vport_id;
> > +		vvfl_v2->num_elements = num_vlans;
> > +		i = 0;
> > +		list_for_each_entry_safe(f, ftmp,
> > +					 &adapter-
> >config_data.vlan_filter_list,
> > +					 list) {
> > +			filtering_support =
> > +			&adapter->vlan_caps->filtering.filtering_support;
> > +			if (add && f->add) {
> > +				/* give priority over outer if it's enabled */
> > +				if (filtering_support->outer)
> > +					vlan = &vvfl_v2->filters[i].outer;
> > +				else
> > +					vlan = &vvfl_v2->filters[i].inner;
> > +				vlan->tci = f->vlan.vid;
> > +				vlan->tpid = f->vlan.tpid;
> > +				i++;
> > +				f->add = false;
> > +			} else if (!add && f->remove) {
> > +				/* give priority over outer if it's enabled */
> > +				if (filtering_support->outer)
> > +					vlan = &vvfl_v2->filters[i].outer;
> > +				else
> > +					vlan = &vvfl_v2->filters[i].inner;
> > +				vlan->tci = f->vlan.vid;
> > +				vlan->tpid = f->vlan.tpid;
> > +				i++;
> > +				f->remove = false;
> > +			}
> > +			if (i == num_vlans)
> > +				break;
> > +		}
> > +		spin_unlock_bh(&adapter->vlan_list_lock);
> > +		iecm_send_mb_msg(adapter, vop, len, (u8 *)vvfl_v2);
> > +		spin_lock_bh(&adapter->vlan_list_lock);
> > +	}
> > +	spin_unlock_bh(&adapter->vlan_list_lock);
> > +	kfree(vvfl_v2);
> > +	return;
> > +error:
> > +	spin_unlock_bh(&adapter->vlan_list_lock);
> > +	if (err)
> > +		dev_err(&adapter->pdev->dev,
> > +			"Failed to add or del vlan filters %d", err);
> > +}
> > +
> >  /**
> >   * iecm_vc_ops_init - Initialize virtchnl common api
> >   * @adapter: Driver specific private structure
> > @@ -2548,21 +3705,21 @@ void iecm_vc_ops_init(struct iecm_adapter
> *adapter)
> >  	vc_ops->enable_vport = iecm_send_enable_vport_msg;
> >  	vc_ops->disable_vport = iecm_send_disable_vport_msg;
> >  	vc_ops->destroy_vport = iecm_send_destroy_vport_msg;
> > -	vc_ops->get_ptype = NULL;
> > -	vc_ops->get_set_rss_key = NULL;
> > -	vc_ops->get_set_rss_lut = NULL;
> > -	vc_ops->get_set_rss_hash = NULL;
> > -	vc_ops->adjust_qs = NULL;
> > -	vc_ops->add_del_vlans = NULL;
> > -	vc_ops->strip_vlan_msg = NULL;
> > -	vc_ops->insert_vlan_msg = NULL;
> > -	vc_ops->init_max_queues = NULL;
> > -	vc_ops->get_max_tx_bufs = NULL;
> > -	vc_ops->vportq_reg_init = NULL;
> > -	vc_ops->alloc_vectors = NULL;
> > -	vc_ops->dealloc_vectors = NULL;
> > -	vc_ops->get_supported_desc_ids = NULL;
> > -	vc_ops->get_stats_msg = NULL;
> > +	vc_ops->get_ptype = iecm_send_get_rx_ptype_msg;
> > +	vc_ops->get_set_rss_key = iecm_send_get_set_rss_key_msg;
> > +	vc_ops->get_set_rss_lut = iecm_send_get_set_rss_lut_msg;
> > +	vc_ops->get_set_rss_hash = iecm_send_get_set_rss_hash_msg;
> > +	vc_ops->adjust_qs = iecm_vport_adjust_qs;
> > +	vc_ops->add_del_vlans = iecm_add_del_vlans;
> > +	vc_ops->strip_vlan_msg = iecm_send_strip_vlan_msg;
> > +	vc_ops->insert_vlan_msg = iecm_send_insert_vlan_msg;
> > +	vc_ops->init_max_queues = iecm_vport_init_max_qs;
> > +	vc_ops->get_max_tx_bufs = iecm_get_max_tx_bufs;
> > +	vc_ops->vportq_reg_init = iecm_queue_reg_init;
> > +	vc_ops->alloc_vectors = iecm_send_alloc_vectors_msg;
> > +	vc_ops->dealloc_vectors = iecm_send_dealloc_vectors_msg;
> > +	vc_ops->get_supported_desc_ids = iecm_get_supported_desc_ids;
> > +	vc_ops->get_stats_msg = iecm_send_get_stats_msg;
> >  	vc_ops->recv_mbx_msg = NULL;
> >  }
> >  EXPORT_SYMBOL(iecm_vc_ops_init);
> > diff --git a/drivers/net/ethernet/intel/include/iecm.h
> b/drivers/net/ethernet/intel/include/iecm.h
> > index 8dd6272db7d3..d736db65da06 100644
> > --- a/drivers/net/ethernet/intel/include/iecm.h
> > +++ b/drivers/net/ethernet/intel/include/iecm.h
> > @@ -346,6 +346,7 @@ struct iecm_vport {
> >  	int num_rxq_grp;
> >  	struct iecm_rxq_group *rxq_grps;
> >  	u32 rxq_model;
> > +	struct iecm_rx_ptype_decoded rx_ptype_lkup[IECM_RX_MAX_PTYPE];
> >
> >  	struct iecm_adapter *adapter;
> >  	struct net_device *netdev;
> > @@ -382,6 +383,30 @@ enum iecm_user_flags {
> >  	__IECM_USER_FLAGS_NBITS,
> >  };
> >
> > +#define IECM_GET_PTYPE_SIZE(p) \
> > +	(sizeof(struct virtchnl2_ptype) + \
> > +	(((p)->proto_id_count ? ((p)->proto_id_count - 1) : 0) * sizeof(u16)))
> > +
> > +#define IECM_TUN_IP_GRE (\
> > +	IECM_PTYPE_TUNNEL_IP |\
> > +	IECM_PTYPE_TUNNEL_IP_GRENAT)
> > +
> > +#define IECM_TUN_IP_GRE_MAC (\
> > +	IECM_TUN_IP_GRE |\
> > +	IECM_PTYPE_TUNNEL_IP_GRENAT_MAC)
> > +
> > +enum iecm_tunnel_state {
> > +	IECM_PTYPE_TUNNEL_IP                    = BIT(0),
> > +	IECM_PTYPE_TUNNEL_IP_GRENAT             = BIT(1),
> > +	IECM_PTYPE_TUNNEL_IP_GRENAT_MAC         = BIT(2),
> > +	IECM_PTYPE_TUNNEL_IP_GRENAT_MAC_VLAN    = BIT(3),
> > +};
> > +
> > +struct iecm_ptype_state {
> > +	bool outer_ip;
> > +	bool outer_frag;
> > +	u8 tunnel_state;
> > +};
> >  /* User defined configuration values */
> >  struct iecm_user_config_data {
> >  	u32 num_req_tx_qs; /* user requested TX queues through ethtool */
> > @@ -534,6 +559,7 @@ int iecm_probe(struct pci_dev *pdev,
> >  	       const struct pci_device_id __always_unused *ent,
> >  	       struct iecm_adapter *adapter);
> >  void iecm_remove(struct pci_dev *pdev);
> > +void iecm_vport_adjust_qs(struct iecm_vport *vport);
> >  int iecm_init_dflt_mbx(struct iecm_adapter *adapter);
> >  void iecm_deinit_dflt_mbx(struct iecm_adapter *adapter);
> >  void iecm_vc_ops_init(struct iecm_adapter *adapter);
> > @@ -555,8 +581,15 @@ int iecm_send_config_rx_queues_msg(struct
> iecm_vport *vport);
> >  int iecm_send_enable_vport_msg(struct iecm_vport *vport);
> >  int iecm_send_disable_vport_msg(struct iecm_vport *vport);
> >  int iecm_send_destroy_vport_msg(struct iecm_vport *vport);
> > +int iecm_send_get_rx_ptype_msg(struct iecm_vport *vport);
> > +int iecm_send_get_set_rss_key_msg(struct iecm_vport *vport, bool get);
> > +int iecm_send_get_set_rss_lut_msg(struct iecm_vport *vport, bool get);
> > +int iecm_send_get_set_rss_hash_msg(struct iecm_vport *vport, bool get);
> > +int iecm_send_dealloc_vectors_msg(struct iecm_adapter *adapter);
> > +int iecm_send_alloc_vectors_msg(struct iecm_adapter *adapter, u16
> num_vectors);
> >  int iecm_vport_params_buf_alloc(struct iecm_adapter *adapter);
> >  void iecm_vport_params_buf_rel(struct iecm_adapter *adapter);
> > +int iecm_send_get_stats_msg(struct iecm_vport *vport);
> >  int iecm_get_vec_ids(struct iecm_adapter *adapter,
> >  		     u16 *vecids, int num_vecids,
> >  		     struct virtchnl2_vector_chunks *chunks);
> > @@ -567,6 +600,9 @@ int iecm_send_mb_msg(struct iecm_adapter
> *adapter, enum virtchnl_ops op,
> >  void iecm_vport_set_hsplit(struct iecm_vport *vport, bool ena);
> >  int iecm_send_enable_channels_msg(struct iecm_vport *vport);
> >  int iecm_send_disable_channels_msg(struct iecm_vport *vport);
> > +bool iecm_is_feature_ena(struct iecm_vport *vport, netdev_features_t
> feature);
> > +int iecm_check_descs(struct iecm_vport *vport, u64 rx_desc_ids,
> > +		     u64 tx_desc_ids, u16 rxq_model, u16 txq_model);
> >  int iecm_set_msg_pending(struct iecm_adapter *adapter,
> >  			 struct iecm_ctlq_msg *ctlq_msg,
> >  			 enum iecm_vport_vc_state err_enum);
> > diff --git a/drivers/net/ethernet/intel/include/iecm_txrx.h
> b/drivers/net/ethernet/intel/include/iecm_txrx.h
> > index 448cae0bf6e7..9f3086bfe575 100644
> > --- a/drivers/net/ethernet/intel/include/iecm_txrx.h
> > +++ b/drivers/net/ethernet/intel/include/iecm_txrx.h
> > @@ -4,6 +4,9 @@
> >  #ifndef _IECM_TXRX_H_
> >  #define _IECM_TXRX_H_
> >
> > +#include "virtchnl_lan_desc.h"
> > +#include <linux/indirect_call_wrapper.h>
> > +
> >  #define IECM_LARGE_MAX_Q			256
> >  #define IECM_MAX_Q				16
> >  /* Mailbox Queue */
> > @@ -77,9 +80,200 @@
> >  #define IECM_MAX_RXBUFFER			9728
> >  #define IECM_MAX_MTU		\
> >  	(IECM_MAX_RXBUFFER - IECM_PACKET_HDR_PAD)
> > -#define IECM_INT_NAME_STR_LEN	(IFNAMSIZ + 16)
> > +
> > +#define MAKEMASK(m, s)	((m) << (s))
> > +
> > +/* Checksum offload bits decoded from the receive descriptor. */
> > +struct iecm_rx_csum_decoded {
> > +	u8 l3l4p : 1;
> > +	u8 ipe : 1;
> > +	u8 eipe : 1;
> > +	u8 eudpe : 1;
> > +	u8 ipv6exadd : 1;
> > +	u8 l4e : 1;
> > +	u8 pprs : 1;
> > +	u8 nat : 1;
> > +	u8 rsc : 1;
> > +	u8 raw_csum_inv : 1;
> > +	u16 raw_csum;
> > +};
> > +
> > +struct iecm_rx_extracted {
> > +	unsigned int size;
> > +	u16 vlan_tag;
> > +	u16 rx_ptype;
> > +};
> >
> >  #define IECM_TX_COMPLQ_CLEAN_BUDGET	256
> > +#define IECM_TX_MIN_LEN			17
> > +#define IECM_TX_DESCS_FOR_SKB_DATA_PTR	1
> > +#define IECM_TX_MAX_BUF			8
> > +#define IECM_TX_DESCS_PER_CACHE_LINE	4
> > +#define IECM_TX_DESCS_FOR_CTX		1
> > +/* TX descriptors needed, worst case */
> > +#define IECM_TX_DESC_NEEDED (MAX_SKB_FRAGS +
> IECM_TX_DESCS_FOR_CTX + \
> > +			     IECM_TX_DESCS_PER_CACHE_LINE + \
> > +			     IECM_TX_DESCS_FOR_SKB_DATA_PTR)
> > +
> > +/* The size limit for a transmit buffer in a descriptor is (16K - 1).
> > + * In order to align with the read requests we will align the value to
> > + * the nearest 4K which represents our maximum read request size.
> > + */
> > +#define IECM_TX_MAX_READ_REQ_SIZE	4096
> > +#define IECM_TX_MAX_DESC_DATA		(16 * 1024 - 1)
> > +#define IECM_TX_MAX_DESC_DATA_ALIGNED \
> > +	(~(IECM_TX_MAX_READ_REQ_SIZE - 1) & IECM_TX_MAX_DESC_DATA)
> > +
> > +#define IECM_RX_DMA_ATTR \
> > +	(DMA_ATTR_SKIP_CPU_SYNC | DMA_ATTR_WEAK_ORDERING)
> > +#define IECM_RX_DESC(R, i)	\
> > +	(&(((union virtchnl2_rx_desc *)((R)->desc_ring))[i]))
> > +
> > +struct iecm_page_info {
> > +	dma_addr_t dma;
> > +	struct page *page;
> > +	unsigned int page_offset;
> > +	u16 pagecnt_bias;
> > +};
> > +
> > +struct iecm_rx_buf {
> > +#define IECM_RX_BUF_MAX_PAGES 2
> > +	struct iecm_page_info page_info[IECM_RX_BUF_MAX_PAGES];
> 
> As I said previously, it will most likely be rejected upstream. They
> will either suggest using compounds or page_pool (it uses compounds
> for non-zero-order pages) or maybe introduce folio support to the
> networking stack or so, but not such stuff.
> 

Perhaps I missed it but I didn't see previous comment about this. We have done our own buffer management in the past and it hasn't been issue. I believe there was an attempt to implement this with compound pages but it didn't work in the ways we needed it to for one reason or another (I don't recall exactly why it was problematic but I can check if you're interested).

A page pool might be a different solution here that may be worth trying, but for many caveats of the data path we've relied on the methods otherwise done in other Intel drivers that have otherwise seemed to do well.

> > +	u8 page_indx;
> > +	u16 buf_id;
> > +	u16 buf_size;
> > +	struct sk_buff *skb;
> > +};
> > +
> > +/* Packet type non-ip values */
> > +enum iecm_rx_ptype_l2 {
> > +	IECM_RX_PTYPE_L2_RESERVED	= 0,
> > +	IECM_RX_PTYPE_L2_MAC_PAY2	= 1,
> > +	IECM_RX_PTYPE_L2_TIMESYNC_PAY2	= 2,
> > +	IECM_RX_PTYPE_L2_FIP_PAY2	= 3,
> > +	IECM_RX_PTYPE_L2_OUI_PAY2	= 4,
> > +	IECM_RX_PTYPE_L2_MACCNTRL_PAY2	= 5,
> > +	IECM_RX_PTYPE_L2_LLDP_PAY2	= 6,
> > +	IECM_RX_PTYPE_L2_ECP_PAY2	= 7,
> > +	IECM_RX_PTYPE_L2_EVB_PAY2	= 8,
> > +	IECM_RX_PTYPE_L2_QCN_PAY2	= 9,
> > +	IECM_RX_PTYPE_L2_EAPOL_PAY2	= 10,
> > +	IECM_RX_PTYPE_L2_ARP		= 11,
> > +};
> > +
> > +enum iecm_rx_ptype_outer_ip {
> > +	IECM_RX_PTYPE_OUTER_L2	= 0,
> > +	IECM_RX_PTYPE_OUTER_IP	= 1,
> > +};
> > +
> > +enum iecm_rx_ptype_outer_ip_ver {
> > +	IECM_RX_PTYPE_OUTER_NONE	= 0,
> > +	IECM_RX_PTYPE_OUTER_IPV4	= 1,
> > +	IECM_RX_PTYPE_OUTER_IPV6	= 2,
> > +};
> > +
> > +enum iecm_rx_ptype_outer_fragmented {
> > +	IECM_RX_PTYPE_NOT_FRAG	= 0,
> > +	IECM_RX_PTYPE_FRAG	= 1,
> > +};
> > +
> > +enum iecm_rx_ptype_tunnel_type {
> > +	IECM_RX_PTYPE_TUNNEL_NONE		= 0,
> > +	IECM_RX_PTYPE_TUNNEL_IP_IP		= 1,
> > +	IECM_RX_PTYPE_TUNNEL_IP_GRENAT		= 2,
> > +	IECM_RX_PTYPE_TUNNEL_IP_GRENAT_MAC	= 3,
> > +	IECM_RX_PTYPE_TUNNEL_IP_GRENAT_MAC_VLAN	= 4,
> > +};
> > +
> > +enum iecm_rx_ptype_tunnel_end_prot {
> > +	IECM_RX_PTYPE_TUNNEL_END_NONE	= 0,
> > +	IECM_RX_PTYPE_TUNNEL_END_IPV4	= 1,
> > +	IECM_RX_PTYPE_TUNNEL_END_IPV6	= 2,
> > +};
> > +
> > +enum iecm_rx_ptype_inner_prot {
> > +	IECM_RX_PTYPE_INNER_PROT_NONE		= 0,
> > +	IECM_RX_PTYPE_INNER_PROT_UDP		= 1,
> > +	IECM_RX_PTYPE_INNER_PROT_TCP		= 2,
> > +	IECM_RX_PTYPE_INNER_PROT_SCTP		= 3,
> > +	IECM_RX_PTYPE_INNER_PROT_ICMP		= 4,
> > +	IECM_RX_PTYPE_INNER_PROT_TIMESYNC	= 5,
> > +};
> > +
> > +enum iecm_rx_ptype_payload_layer {
> > +	IECM_RX_PTYPE_PAYLOAD_LAYER_NONE	= 0,
> > +	IECM_RX_PTYPE_PAYLOAD_LAYER_PAY2	= 1,
> > +	IECM_RX_PTYPE_PAYLOAD_LAYER_PAY3	= 2,
> > +	IECM_RX_PTYPE_PAYLOAD_LAYER_PAY4	= 3,
> > +};
> > +
> > +struct iecm_rx_ptype_decoded {
> > +	u32 ptype:10;
> > +	u32 known:1;
> > +	u32 outer_ip:1;
> > +	u32 outer_ip_ver:2;
> > +	u32 outer_frag:1;
> > +	u32 tunnel_type:3;
> > +	u32 tunnel_end_prot:2;
> > +	u32 tunnel_end_frag:1;
> > +	u32 inner_prot:4;
> > +	u32 payload_layer:3;
> > +};
> > +
> > +enum iecm_rx_hsplit {
> > +	IECM_RX_NO_HDR_SPLIT = 0,
> > +	IECM_RX_HDR_SPLIT = 1,
> > +};
> > +
> > +/* The iecm_ptype_lkup table is used to convert from the 10-bit ptype in the
> > + * hardware to a bit-field that can be used by SW to more easily determine
> the
> > + * packet type.
> > + *
> > + * Macros are used to shorten the table lines and make this table human
> > + * readable.
> > + *
> > + * We store the PTYPE in the top byte of the bit field - this is just so that
> > + * we can check that the table doesn't have a row missing, as the index into
> > + * the table should be the PTYPE.
> > + *
> > + * Typical work flow:
> > + *
> > + * IF NOT iecm_ptype_lkup[ptype].known
> > + * THEN
> > + *      Packet is unknown
> > + * ELSE IF iecm_ptype_lkup[ptype].outer_ip == IECM_RX_PTYPE_OUTER_IP
> > + *      Use the rest of the fields to look at the tunnels, inner protocols, etc
> > + * ELSE
> > + *      Use the enum iecm_rx_ptype_l2 to decode the packet type
> > + * ENDIF
> > + */
> > +/* macro to make the table lines short */
> > +#define IECM_PTT(PTYPE, OUTER_IP, OUTER_IP_VER, OUTER_FRAG, T, TE,
> TEF, I, PL)\
> > +	{	PTYPE, \
> > +		1, \
> > +		IECM_RX_PTYPE_OUTER_##OUTER_IP, \
> > +		IECM_RX_PTYPE_OUTER_##OUTER_IP_VER, \
> > +		IECM_RX_PTYPE_##OUTER_FRAG, \
> > +		IECM_RX_PTYPE_TUNNEL_##T, \
> > +		IECM_RX_PTYPE_TUNNEL_END_##TE, \
> > +		IECM_RX_PTYPE_##TEF, \
> > +		IECM_RX_PTYPE_INNER_PROT_##I, \
> > +		IECM_RX_PTYPE_PAYLOAD_LAYER_##PL }
> > +
> > +#define IECM_PTT_UNUSED_ENTRY(PTYPE) { PTYPE, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
> > +
> > +/* shorter macros makes the table fit but are terse */
> > +#define IECM_RX_PTYPE_NOF		IECM_RX_PTYPE_NOT_FRAG
> > +#define IECM_RX_PTYPE_FRG		IECM_RX_PTYPE_FRAG
> > +#define IECM_RX_PTYPE_INNER_PROT_TS
> 	IECM_RX_PTYPE_INNER_PROT_TIMESYNC
> > +#define IECM_RX_SUPP_PTYPE		18
> > +#define IECM_RX_MAX_PTYPE		1024
> > +#define IECM_RX_MAX_BASE_PTYPE		256
> > +
> > +#define IECM_INT_NAME_STR_LEN	(IFNAMSIZ + 16)
> > +
> > +/* Lookup table mapping the HW PTYPE to the bit field for decoding */
> > +extern const struct iecm_rx_ptype_decoded
> iecm_ptype_lookup[IECM_RX_MAX_PTYPE];
> >
> >  enum iecm_queue_flags_t {
> >  	__IECM_Q_GEN_CHK,
> > @@ -318,6 +512,4 @@ void iecm_vport_calc_total_qs(struct iecm_adapter
> *adapter,
> >  			      struct virtchnl2_create_vport *vport_msg);
> >  void iecm_vport_calc_num_q_groups(struct iecm_vport *vport);
> >  void iecm_vport_calc_num_q_vec(struct iecm_vport *vport);
> > -irqreturn_t
> > -iecm_vport_intr_clean_queues(int __always_unused irq, void *data);
> >  #endif /* !_IECM_TXRX_H_ */
> > --
> > 2.33.0
> 
> Thanks,
> Al

^ permalink raw reply	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 08/19] iecm: add interrupts and configure netdev
  2022-01-28 13:34   ` Alexander Lobakin
@ 2022-02-02 23:17     ` Brady, Alan
  2022-02-03 15:55       ` Alexander Lobakin
  0 siblings, 1 reply; 89+ messages in thread
From: Brady, Alan @ 2022-02-02 23:17 UTC (permalink / raw)
  To: intel-wired-lan

> -----Original Message-----
> From: Lobakin, Alexandr <alexandr.lobakin@intel.com>
> Sent: Friday, January 28, 2022 5:35 AM
> To: Brady, Alan <alan.brady@intel.com>
> Cc: Lobakin, Alexandr <alexandr.lobakin@intel.com>; intel-wired-
> lan at lists.osuosl.org; Linga, Pavan Kumar <pavan.kumar.linga@intel.com>;
> Chittim, Madhu <madhu.chittim@intel.com>; Burra, Phani R
> <phani.r.burra@intel.com>
> Subject: Re: [Intel-wired-lan] [PATCH net-next 08/19] iecm: add interrupts and
> configure netdev
> 
> From: Alan Brady <alan.brady@intel.com>
> Date: Thu, 27 Jan 2022 16:09:58 -0800
> 
> > This finishes implementing init_task by adding everything we need to
> > configure the netdevice for the vport and setup its interrupts.
> >
> > Signed-off-by: Phani Burra <phani.r.burra@intel.com>
> > Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
> > Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
> > Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
> > Signed-off-by: Alice Michael <alice.michael@intel.com>
> > Signed-off-by: Alan Brady <alan.brady@intel.com>
> > ---
> >  drivers/net/ethernet/intel/iecm/iecm_lib.c    | 813 +++++++++++++++++-
> >  drivers/net/ethernet/intel/iecm/iecm_txrx.c   |  17 +
> >  .../net/ethernet/intel/iecm/iecm_virtchnl.c   | 165 ++++
> >  drivers/net/ethernet/intel/include/iecm.h     | 112 ++-
> >  .../net/ethernet/intel/include/iecm_txrx.h    |   2 +
> >  5 files changed, 1104 insertions(+), 5 deletions(-)
> >
> > diff --git a/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > index aab8ee40424e..255b04c25683 100644
> > --- a/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > +++ b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > @@ -5,11 +5,35 @@
> >
> >  #include "iecm.h"
> >
> > +static const struct net_device_ops iecm_netdev_ops_splitq; static
> > +const struct net_device_ops iecm_netdev_ops_singleq;
> > +
> >  const char * const iecm_vport_vc_state_str[] = {
> >  	IECM_FOREACH_VPORT_VC_STATE(IECM_GEN_STRING)
> >  };
> >  EXPORT_SYMBOL(iecm_vport_vc_state_str);
> >
> > +/**
> > + * iecm_get_vport_index - Get the vport index
> > + * @adapter: adapter structure to get the vports array
> > + * @vport: vport pointer for which the index to find  */ static int
> > +iecm_get_vport_index(struct iecm_adapter *adapter,
> > +				struct iecm_vport *vport)
> > +{
> > +	int i, err = -EINVAL;
> > +
> > +	if (!adapter->vports)
> > +		return err;
> > +
> > +	for (i = 0; i < adapter->num_alloc_vport; i++) {
> > +		if (adapter->vports[i] != vport)
> > +			continue;
> > +		return i;
> > +	}
> > +	return err;
> > +}
> > +
> >  /**
> >   * iecm_is_feature_ena - Determine if a particular feature is enabled
> >   * @vport: vport to check
> > @@ -29,6 +53,595 @@ bool iecm_is_feature_ena(struct iecm_vport *vport,
> netdev_features_t feature)
> >  	return ena;
> >  }
> >
> > +/**
> > + * iecm_is_vlan_cap_ena - Check if VLAN capability is enabled
> > + * @adapter: pointer to adapter
> > + * @vcaps: VLAN capability bit
> > + *
> > + * Returns true if VLAN capability is set, false otherwise  */ static
> > +bool iecm_is_vlan_cap_ena(struct iecm_adapter *adapter,
> > +				 enum iecm_vlan_caps vcaps)
> > +{
> > +	if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
> VIRTCHNL2_CAP_VLAN)) {
> > +		struct virtchnl_vlan_supported_caps *offload;
> > +
> > +		if (!adapter->vlan_caps)
> > +			return false;
> > +
> > +		switch (vcaps) {
> > +		case IECM_CAP_VLAN_CTAG_INSERT:
> > +			offload =
> > +			&adapter->vlan_caps->offloads.insertion_support;
> > +			if ((offload->outer & IECM_VLAN_8100) ==
> IECM_VLAN_8100 ||
> > +			    (offload->inner & IECM_VLAN_8100) ==
> IECM_VLAN_8100)
> > +				return true;
> > +			break;
> > +		case IECM_CAP_VLAN_STAG_INSERT:
> > +			offload =
> > +			&adapter->vlan_caps->offloads.insertion_support;
> > +			if ((offload->outer & IECM_VLAN_88A8) ==
> IECM_VLAN_88A8)
> > +				return true;
> > +			break;
> > +		case IECM_CAP_VLAN_CTAG_STRIP:
> > +			offload =
> > +			&adapter->vlan_caps->offloads.stripping_support;
> > +			if ((offload->outer & IECM_VLAN_8100) ==
> IECM_VLAN_8100 ||
> > +			    (offload->inner & IECM_VLAN_8100) ==
> IECM_VLAN_8100)
> > +				return true;
> > +			break;
> > +		case IECM_CAP_VLAN_STAG_STRIP:
> > +			offload =
> > +			&adapter->vlan_caps->offloads.stripping_support;
> > +			if ((offload->outer & IECM_VLAN_88A8) ==
> IECM_VLAN_88A8)
> > +				return true;
> > +			break;
> > +		case IECM_CAP_VLAN_CTAG_ADD_DEL:
> > +			offload =
> > +			&adapter->vlan_caps->filtering.filtering_support;
> > +			if ((offload->outer &
> VIRTCHNL_VLAN_ETHERTYPE_8100) ||
> > +			    (offload->inner &
> VIRTCHNL_VLAN_ETHERTYPE_8100))
> > +				return true;
> > +			break;
> > +		case IECM_CAP_VLAN_STAG_ADD_DEL:
> > +			offload =
> > +			&adapter->vlan_caps->filtering.filtering_support;
> > +			if ((offload->outer &
> VIRTCHNL_VLAN_ETHERTYPE_88A8) ||
> > +			    (offload->inner &
> VIRTCHNL_VLAN_ETHERTYPE_88A8))
> > +				return true;
> > +			break;
> > +		default:
> > +			dev_err(&adapter->pdev->dev, "Invalid VLAN capability
> %d\n",
> > +				vcaps);
> > +			return false;
> > +		}
> > +	} else if (iecm_is_cap_ena(adapter, IECM_BASE_CAPS,
> > +				   VIRTCHNL2_CAP_VLAN)) {
> > +		switch (vcaps) {
> > +		case IECM_CAP_VLAN_CTAG_INSERT:
> > +		case IECM_CAP_VLAN_CTAG_STRIP:
> > +		case IECM_CAP_VLAN_CTAG_ADD_DEL:
> > +			return true;
> > +		default:
> > +			return false;
> > +		}
> > +	}
> > +
> > +	return false;
> > +}
> > +
> > +/**
> > + * iecm_netdev_to_vport - get a vport handle from a netdev
> > + * @netdev: network interface device structure  */ struct iecm_vport
> > +*iecm_netdev_to_vport(struct net_device *netdev) {
> > +	struct iecm_netdev_priv *np = netdev_priv(netdev);
> > +
> > +	return np->vport;
> > +}
> > +
> > +/**
> > + * iecm_mb_intr_rel_irq - Free the IRQ association with the OS
> > + * @adapter: adapter structure
> > + */
> > +static void iecm_mb_intr_rel_irq(struct iecm_adapter *adapter) {
> > +	int irq_num;
> > +
> > +	irq_num = adapter->msix_entries[0].vector;
> > +	free_irq(irq_num, adapter);
> > +}
> > +
> > +/**
> > + * iecm_intr_rel - Release interrupt capabilities and free memory
> > + * @adapter: adapter to disable interrupts on  */ static void
> > +iecm_intr_rel(struct iecm_adapter *adapter) {
> > +	if (!adapter->msix_entries)
> > +		return;
> > +	clear_bit(__IECM_MB_INTR_MODE, adapter->flags);
> > +	clear_bit(__IECM_MB_INTR_TRIGGER, adapter->flags);
> > +	iecm_mb_intr_rel_irq(adapter);
> > +
> > +	pci_free_irq_vectors(adapter->pdev);
> > +	if (adapter->dev_ops.vc_ops.dealloc_vectors) {
> > +		int err;
> > +
> > +		err = adapter->dev_ops.vc_ops.dealloc_vectors(adapter);
> > +		if (err) {
> > +			dev_err(&adapter->pdev->dev,
> > +				"Failed to deallocate vectors: %d\n", err);
> > +		}
> > +	}
> > +	kfree(adapter->msix_entries);
> > +	adapter->msix_entries = NULL;
> > +	kfree(adapter->req_vec_chunks);
> > +	adapter->req_vec_chunks = NULL;
> > +}
> > +
> > +/**
> > + * iecm_mb_intr_clean - Interrupt handler for the mailbox
> > + * @irq: interrupt number
> > + * @data: pointer to the adapter structure  */ static irqreturn_t
> > +iecm_mb_intr_clean(int __always_unused irq, void *data) {
> > +	struct iecm_adapter *adapter = (struct iecm_adapter *)data;
> > +
> > +	set_bit(__IECM_MB_INTR_TRIGGER, adapter->flags);
> > +	queue_delayed_work(adapter->serv_wq, &adapter->serv_task,
> > +			   msecs_to_jiffies(0));
> > +	return IRQ_HANDLED;
> > +}
> > +
> > +/**
> > + * iecm_mb_irq_enable - Enable MSIX interrupt for the mailbox
> > + * @adapter: adapter to get the hardware address for register write
> > +*/ static void iecm_mb_irq_enable(struct iecm_adapter *adapter) {
> > +	struct iecm_hw *hw = &adapter->hw;
> > +	struct iecm_intr_reg *intr = &adapter->mb_vector.intr_reg;
> 
> Please don't break Reverse Christmas Tree declaration style. *intr doesn't
> depend on *hw.
> 

Will fix

> > +	u32 val;
> > +
> > +	val = intr->dyn_ctl_intena_m | intr->dyn_ctl_itridx_m;
> > +	wr32(hw, intr->dyn_ctl, val);
> > +	wr32(hw, intr->icr_ena, intr->icr_ena_ctlq_m); }
> > +
> > +/**
> > + * iecm_mb_intr_req_irq - Request irq for the mailbox interrupt
> > + * @adapter: adapter structure to pass to the mailbox irq handler  */
> > +static int iecm_mb_intr_req_irq(struct iecm_adapter *adapter) {
> > +	struct iecm_q_vector *mb_vector = &adapter->mb_vector;
> > +	int irq_num, mb_vidx = 0, err;
> > +
> > +	irq_num = adapter->msix_entries[mb_vidx].vector;
> > +	snprintf(mb_vector->name, sizeof(mb_vector->name) - 1,
> > +		 "%s-%s-%d", dev_driver_string(&adapter->pdev->dev),
> > +		 "Mailbox", mb_vidx);
> > +	err = request_irq(irq_num, adapter->irq_mb_handler, 0,
> > +			  mb_vector->name, adapter);
> > +	if (err) {
> > +		dev_err(&adapter->pdev->dev,
> > +			"Request_irq for mailbox failed, error: %d\n", err);
> > +		return err;
> > +	}
> > +	set_bit(__IECM_MB_INTR_MODE, adapter->flags);
> > +	return 0;
> > +}
> > +
> > +/**
> > + * iecm_get_mb_vec_id - Get vector index for mailbox
> > + * @adapter: adapter structure to access the vector chunks
> > + *
> > + * The first vector id in the requested vector chunks from the CP is
> > +for
> > + * the mailbox
> > + */
> > +static void iecm_get_mb_vec_id(struct iecm_adapter *adapter) {
> > +	if (adapter->req_vec_chunks) {
> > +		struct virtchnl2_get_capabilities *caps;
> > +
> > +		caps = (struct virtchnl2_get_capabilities *)adapter->caps;
> > +		adapter->mb_vector.v_idx = le16_to_cpu(caps-
> >mailbox_vector_id);
> > +	} else {
> > +		adapter->mb_vector.v_idx = 0;
> > +	}
> > +}
> > +
> > +/**
> > + * iecm_mb_intr_init - Initialize the mailbox interrupt
> > + * @adapter: adapter structure to store the mailbox vector  */ static
> > +int iecm_mb_intr_init(struct iecm_adapter *adapter) {
> > +	adapter->dev_ops.reg_ops.mb_intr_reg_init(adapter);
> > +	adapter->irq_mb_handler = iecm_mb_intr_clean;
> > +	return iecm_mb_intr_req_irq(adapter); }
> > +
> > +/**
> > + * iecm_intr_distribute - Distribute MSIX vectors
> > + * @adapter: adapter structure to get the vports
> > + * @pre_req: before or after msi request
> > + *
> > + * Distribute the MSIX vectors acquired from the OS to the vports
> > +based on the
> > + * num of vectors requested by each vport  */ static int
> > +iecm_intr_distribute(struct iecm_adapter *adapter, bool pre_req) {
> > +	struct iecm_vport *vport = adapter->vports[0];
> > +	int err = 0;
> > +
> > +	if (pre_req) {
> > +		u16 vecs_avail;
> > +
> > +		vecs_avail = iecm_get_reserved_vecs(adapter);
> > +		if (vecs_avail < IECM_MIN_VEC) {
> > +			return -EAGAIN;
> > +		} else if (vecs_avail == IECM_MIN_VEC) {
> > +			vport->num_q_vectors = IECM_MIN_Q_VEC;
> > +		} else {
> > +			vport->num_q_vectors = vecs_avail - IECM_NONQ_VEC
> -
> > +						IECM_MAX_RDMA_VEC;
> > +		}
> > +	} else {
> > +		if (adapter->num_msix_entries != adapter->num_req_msix)
> > +			vport->num_q_vectors = adapter->num_msix_entries -
> > +					       IECM_NONQ_VEC;
> > +	}
> > +
> > +	return err;
> > +}
> > +
> > +/**
> > + * iecm_intr_req - Request interrupt capabilities
> > + * @adapter: adapter to enable interrupts on
> > + *
> > + * Returns 0 on success, negative on failure  */ static int
> > +iecm_intr_req(struct iecm_adapter *adapter) {
> > +	int min_vectors, max_vectors, err = 0;
> > +	int num_q_vecs, total_num_vecs;
> > +	u16 vecids[IECM_MAX_VECIDS];
> > +	unsigned int vector;
> > +	int v_actual;
> > +
> > +	err = iecm_intr_distribute(adapter, true);
> > +	if (err)
> > +		return err;
> > +
> > +	num_q_vecs = adapter->vports[0]->num_q_vectors;
> > +
> > +	total_num_vecs = num_q_vecs + IECM_NONQ_VEC;
> > +
> > +	if (adapter->dev_ops.vc_ops.alloc_vectors) {
> > +		err = adapter->dev_ops.vc_ops.alloc_vectors(adapter,
> > +							    num_q_vecs);
> > +		if (err) {
> > +			dev_err(&adapter->pdev->dev,
> > +				"Failed to allocate vectors: %d\n", err);
> > +			return -EAGAIN;
> > +		}
> > +	}
> > +
> > +	min_vectors = IECM_MIN_VEC;
> > +	max_vectors = total_num_vecs;
> > +	v_actual = pci_alloc_irq_vectors(adapter->pdev, min_vectors,
> > +					 max_vectors, PCI_IRQ_MSIX);
> > +	if (v_actual < 0) {
> > +		dev_err(&adapter->pdev->dev, "Failed to allocate MSIX vectors:
> %d\n",
> > +			v_actual);
> > +		if (adapter->dev_ops.vc_ops.dealloc_vectors)
> > +			adapter->dev_ops.vc_ops.dealloc_vectors(adapter);
> > +		return -EAGAIN;
> > +	}
> > +
> > +	adapter->msix_entries = kcalloc(v_actual, sizeof(struct msix_entry),
> > +					GFP_KERNEL);
> > +
> > +	if (!adapter->msix_entries) {
> > +		pci_free_irq_vectors(adapter->pdev);
> > +		if (adapter->dev_ops.vc_ops.dealloc_vectors)
> > +			adapter->dev_ops.vc_ops.dealloc_vectors(adapter);
> > +		return -ENOMEM;
> > +	}
> > +
> > +	iecm_get_mb_vec_id(adapter);
> > +
> > +	if (adapter->req_vec_chunks) {
> > +		struct virtchnl2_vector_chunks *vchunks;
> > +		struct virtchnl2_alloc_vectors *ac;
> > +
> > +		ac = adapter->req_vec_chunks;
> > +		vchunks = &ac->vchunks;
> > +
> > +		iecm_get_vec_ids(adapter, vecids, IECM_MAX_VECIDS,
> vchunks);
> > +	} else {
> > +		int i = 0;
> > +
> > +		for (i = 0; i < v_actual; i++)
> > +			vecids[i] = i;
> > +	}
> > +
> > +	for (vector = 0; vector < v_actual; vector++) {
> > +		adapter->msix_entries[vector].entry = vecids[vector];
> > +		adapter->msix_entries[vector].vector =
> > +			pci_irq_vector(adapter->pdev, vector);
> > +	}
> > +	adapter->num_msix_entries = v_actual;
> > +	adapter->num_req_msix = total_num_vecs;
> > +
> > +	iecm_intr_distribute(adapter, false);
> > +
> > +	err = iecm_mb_intr_init(adapter);
> > +	if (err)
> > +		goto intr_rel;
> > +	iecm_mb_irq_enable(adapter);
> > +	return err;
> > +
> > +intr_rel:
> > +	iecm_intr_rel(adapter);
> > +	return err;
> > +}
> > +
> > +/**
> > + * iecm_find_mac_filter - Search filter list for specific mac filter
> > + * @vport: main vport structure
> > + * @macaddr: the MAC address
> > + *
> > + * Returns ptr to the filter object or NULL. Must be called while
> > +holding the
> > + * mac_filter_list_lock.
> > + **/
> > +static struct
> > +iecm_mac_filter *iecm_find_mac_filter(struct iecm_vport *vport,
> > +				      const u8 *macaddr)
> > +{
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	struct iecm_mac_filter *f;
> > +
> > +	if (!macaddr)
> > +		return NULL;
> > +
> > +	list_for_each_entry(f, &adapter->config_data.mac_filter_list, list) {
> > +		if (ether_addr_equal(macaddr, f->macaddr))
> > +			return f;
> > +	}
> 
> Excessive braces again.
> 

Will not fix.

> > +	return NULL;
> > +}
> > +
> > +/**
> > + * __iecm_add_mac_filter - Add mac filter helper function
> > + * @vport: main vport struct
> > + * @macaddr: address to add
> > + *
> > + * Takes mac_filter_list_lock spinlock to add new filter to list.
> > + */
> > +static struct
> > +iecm_mac_filter *__iecm_add_mac_filter(struct iecm_vport *vport,
> > +				       const u8 *macaddr)
> > +{
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	struct iecm_mac_filter *f = NULL;
> > +
> > +	spin_lock_bh(&adapter->mac_filter_list_lock);
> > +	f = iecm_find_mac_filter(vport, macaddr);
> > +	if (!f) {
> > +		f = kzalloc(sizeof(*f), GFP_ATOMIC);
> > +		if (!f) {
> > +			dev_err(&adapter->pdev->dev, "Failed to allocate filter:
> %pM",
> > +				macaddr);
> > +			goto error;
> > +		}
> > +
> > +		ether_addr_copy(f->macaddr, macaddr);
> > +
> > +		list_add_tail(&f->list, &adapter->config_data.mac_filter_list);
> > +		f->add = true;
> > +	} else {
> > +		f->remove = false;
> > +	}
> 
> 	if (f) {
> 		f->remove = false;
> 		goto error; /* It's better to rename it */
> 	}
> 
> 	f = kzalloc(...
> 
> -1 indent level.

Will fix

> > +error:
> > +	spin_unlock_bh(&adapter->mac_filter_list_lock);
> > +
> > +	return f;
> > +}
> > +
> > +/**
> > + * iecm_add_mac_filter - Add a mac filter to the filter list
> > + * @vport: main vport structure
> > + * @macaddr: the MAC address
> > + *
> > + * Returns ptr to the filter or NULL on error. If interface is up,
> > +we'll also
> > + * send the virtchnl message to tell hardware about the filter.
> > + **/
> > +static struct iecm_mac_filter *iecm_add_mac_filter(struct iecm_vport
> *vport,
> > +						   const u8 *macaddr)
> > +{
> > +	struct iecm_mac_filter *f;
> > +
> > +	if (!macaddr)
> > +		return NULL;
> > +
> > +	f = __iecm_add_mac_filter(vport, macaddr);
> > +	if (!f)
> > +		return NULL;
> > +
> > +	if (vport->adapter->state == __IECM_UP)
> > +		iecm_add_del_ether_addrs(vport, true, false);
> > +
> > +	return f;
> > +}
> > +
> > +/**
> > + * iecm_init_mac_addr - initialize mac address for vport
> > + * @vport: main vport structure
> > + * @netdev: pointer to netdev struct associated with this vport  */
> > +static int iecm_init_mac_addr(struct iecm_vport *vport,
> > +			      struct net_device *netdev)
> > +{
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +
> > +	if (!is_valid_ether_addr(vport->default_mac_addr)) {
> > +		if (!iecm_is_cap_ena(vport->adapter, IECM_OTHER_CAPS,
> > +				     VIRTCHNL2_CAP_MACFILTER)) {
> > +			dev_err(&adapter->pdev->dev,
> > +				"MAC address not provided and capability is not
> set\n");
> > +			return -EINVAL;
> > +		}
> > +
> > +		dev_info(&adapter->pdev->dev, "Invalid MAC address %pM,
> using random\n",
> > +			 vport->default_mac_addr);
> > +		eth_hw_addr_random(netdev);
> > +
> > +		if (!iecm_add_mac_filter(vport, netdev->dev_addr))
> > +			return -ENOMEM;
> > +
> > +		ether_addr_copy(vport->default_mac_addr, netdev-
> >dev_addr);
> > +	} else {
> > +		dev_addr_mod(netdev, 0, vport->default_mac_addr,
> ETH_ALEN);
> > +		ether_addr_copy(netdev->perm_addr, vport-
> >default_mac_addr);
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +/**
> > + * iecm_cfg_netdev - Allocate, configure and register a netdev
> > + * @vport: main vport structure
> > + *
> > + * Returns 0 on success, negative value on failure.
> > + */
> > +static int iecm_cfg_netdev(struct iecm_vport *vport) {
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	netdev_features_t dflt_features;
> > +	netdev_features_t offloads = 0;
> > +	struct iecm_netdev_priv *np;
> > +	struct net_device *netdev;
> > +	u16 max_q;
> > +	int err;
> > +
> > +	lockdep_assert_held(&adapter->sw_mutex);
> > +
> > +	/* It's possible we already have a netdev allocated and registered for
> > +	 * this vport
> > +	 */
> > +	if (adapter->netdevs[vport->idx]) {
> > +		netdev = adapter->netdevs[vport->idx];
> > +		np = netdev_priv(netdev);
> > +		np->vport = vport;
> > +		vport->netdev = netdev;
> > +
> > +		return iecm_init_mac_addr(vport, netdev);
> > +	}
> > +
> > +	max_q = adapter->max_queue_limit;
> > +
> > +	netdev = alloc_etherdev_mqs(sizeof(struct iecm_netdev_priv),
> > +				    max_q, max_q);
> > +	if (!netdev)
> > +		return -ENOMEM;
> > +	vport->netdev = netdev;
> > +	np = netdev_priv(netdev);
> > +	np->vport = vport;
> > +
> > +	err = iecm_init_mac_addr(vport, netdev);
> > +	if (err)
> > +		goto err;
> > +
> > +	/* assign netdev_ops */
> > +	if (iecm_is_queue_model_split(vport->txq_model))
> > +		netdev->netdev_ops = &iecm_netdev_ops_splitq;
> > +	else
> > +		netdev->netdev_ops = &iecm_netdev_ops_singleq;
> > +
> > +	/* setup watchdog timeout value to be 5 second */
> > +	netdev->watchdog_timeo = 5 * HZ;
> > +
> > +	/* configure default MTU size */
> > +	netdev->min_mtu = ETH_MIN_MTU;
> > +	netdev->max_mtu = vport->max_mtu;
> > +
> > +	dflt_features = NETIF_F_SG	|
> > +			NETIF_F_HIGHDMA;
> > +
> > +	if (iecm_is_cap_ena_all(adapter, IECM_RSS_CAPS, IECM_CAP_RSS))
> > +		dflt_features |= NETIF_F_RXHASH;
> > +	if (iecm_is_cap_ena_all(adapter, IECM_CSUM_CAPS,
> IECM_CAP_RX_CSUM_L4V4))
> > +		dflt_features |= NETIF_F_IP_CSUM;
> > +	if (iecm_is_cap_ena_all(adapter, IECM_CSUM_CAPS,
> IECM_CAP_RX_CSUM_L4V6))
> > +		dflt_features |= NETIF_F_IPV6_CSUM;
> > +	if (iecm_is_cap_ena(adapter, IECM_CSUM_CAPS,
> IECM_CAP_RX_CSUM))
> > +		dflt_features |= NETIF_F_RXCSUM;
> > +	if (iecm_is_cap_ena_all(adapter, IECM_CSUM_CAPS,
> IECM_CAP_SCTP_CSUM))
> > +		dflt_features |= NETIF_F_SCTP_CRC;
> > +
> > +	if (iecm_is_vlan_cap_ena(adapter, IECM_CAP_VLAN_CTAG_INSERT))
> > +		dflt_features |= IECM_F_HW_VLAN_CTAG_TX;
> > +	if (iecm_is_vlan_cap_ena(adapter, IECM_CAP_VLAN_CTAG_STRIP))
> > +		dflt_features |= IECM_F_HW_VLAN_CTAG_RX;
> > +	if (iecm_is_vlan_cap_ena(adapter, IECM_CAP_VLAN_CTAG_ADD_DEL))
> > +		dflt_features |= IECM_F_HW_VLAN_CTAG_FILTER;
> > +
> > +	if (iecm_is_vlan_cap_ena(adapter, IECM_CAP_VLAN_STAG_INSERT))
> > +		dflt_features |= NETIF_F_HW_VLAN_STAG_TX;
> > +	if (iecm_is_vlan_cap_ena(adapter, IECM_CAP_VLAN_STAG_STRIP))
> > +		dflt_features |= NETIF_F_HW_VLAN_STAG_RX;
> > +	if (iecm_is_vlan_cap_ena(adapter, IECM_CAP_VLAN_STAG_ADD_DEL))
> > +		dflt_features |= NETIF_F_HW_VLAN_STAG_FILTER;
> > +	/* Enable cloud filter if ADQ is supported */
> > +	if (iecm_is_cap_ena(adapter, IECM_BASE_CAPS, VIRTCHNL2_CAP_ADQ)
> ||
> > +	    iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
> VIRTCHNL2_CAP_ADQ))
> > +		dflt_features |= NETIF_F_HW_TC;
> > +	if (iecm_is_cap_ena(adapter, IECM_SEG_CAPS,
> VIRTCHNL2_CAP_SEG_IPV4_TCP))
> > +		dflt_features |= NETIF_F_TSO;
> > +	if (iecm_is_cap_ena(adapter, IECM_SEG_CAPS,
> VIRTCHNL2_CAP_SEG_IPV6_TCP))
> > +		dflt_features |= NETIF_F_TSO6;
> > +	if (iecm_is_cap_ena_all(adapter, IECM_SEG_CAPS,
> > +				VIRTCHNL2_CAP_SEG_IPV4_UDP |
> > +				VIRTCHNL2_CAP_SEG_IPV6_UDP))
> > +		dflt_features |= NETIF_F_GSO_UDP_L4;
> > +	if (iecm_is_cap_ena_all(adapter, IECM_RSC_CAPS, IECM_CAP_RSC))
> > +		offloads |= NETIF_F_GRO_HW;
> 
> I see `offloads` being used only here, |= -> =.
> 

It makes it easier to add others in future or if some happen to get stripped in/out in the future it makes the code more resilient.  We prefer to keep it this way.

> > +	netdev->features |= dflt_features;
> > +	netdev->hw_features |= dflt_features | offloads;
> > +	netdev->hw_enc_features |= dflt_features | offloads;
> > +
> > +	SET_NETDEV_DEV(netdev, &adapter->pdev->dev);
> > +
> > +	/* carrier off on init to avoid Tx hangs */
> > +	netif_carrier_off(netdev);
> > +
> > +	/* make sure transmit queues start off as stopped */
> > +	netif_tx_stop_all_queues(netdev);
> > +
> > +	/* register last */
> > +	err = register_netdev(netdev);
> > +	if (err)
> > +		goto err;
> > +
> > +	/* The vport can be arbitrarily released so we need to also track
> > +	 * netdevs in the adapter struct
> > +	 */
> > +	adapter->netdevs[vport->idx] = netdev;
> > +
> > +	return 0;
> > +err:
> > +	free_netdev(vport->netdev);
> > +	vport->netdev = NULL;
> > +
> > +	return err;
> > +}
> > +
> >  /**
> >   * iecm_cfg_hw - Initialize HW struct
> >   * @adapter: adapter to setup hw struct for @@ -77,6 +690,24 @@
> > static int iecm_get_free_slot(void *array, int size, int curr)
> >  	return next;
> >  }
> >
> > +/**
> > + * iecm_decfg_netdev - Unregister the netdev
> > + * @vport: vport for which netdev to be unregistred  */ static void
> > +iecm_decfg_netdev(struct iecm_vport *vport) {
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +
> > +	if (!vport->netdev)
> > +		return;
> > +
> > +	unregister_netdev(vport->netdev);
> > +	free_netdev(vport->netdev);
> > +	vport->netdev = NULL;
> > +
> > +	adapter->netdevs[vport->idx] = NULL; }
> > +
> >  /**
> >   * iecm_vport_rel - Delete a vport and free its resources
> >   * @vport: the vport being removed
> > @@ -102,6 +733,8 @@ static void iecm_vport_rel_all(struct iecm_adapter
> *adapter)
> >  		if (!adapter->vports[i])
> >  			continue;
> >
> > +		if (!test_bit(__IECM_HR_RESET_IN_PROG, adapter->flags))
> > +			iecm_decfg_netdev(adapter->vports[i]);
> >  		iecm_vport_rel(adapter->vports[i]);
> >  		adapter->vports[i] = NULL;
> >  		adapter->next_vport = 0;
> > @@ -151,7 +784,7 @@ iecm_vport_alloc(struct iecm_adapter *adapter, int
> vport_id)
> >  	adapter->num_alloc_vport++;
> >
> >  	/* Setup default MSIX irq handler for the vport */
> > -	vport->irq_q_handler = NULL;
> > +	vport->irq_q_handler = iecm_vport_intr_clean_queues;
> >  	vport->q_vector_base = IECM_NONQ_VEC;
> >
> >  	mutex_init(&vport->stop_mutex);
> > @@ -184,8 +817,94 @@ static void iecm_statistics_task(struct work_struct
> *work)
> >   *
> >   */
> >  static void iecm_service_task(struct work_struct *work)
> > +{
> > +	struct iecm_adapter *adapter = container_of(work,
> > +						    struct iecm_adapter,
> > +						    serv_task.work);
> 
> 	struct iecm_adapter *adapter;
> 
> 	adapter = container_of(work, typeof(*adapter), serv_task.work);
> 
> Same line count, but more elegant with no line wraps.
> 

Will fix

> > +
> > +	if (test_bit(__IECM_MB_INTR_MODE, adapter->flags)) {
> > +		if (test_and_clear_bit(__IECM_MB_INTR_TRIGGER,
> > +				       adapter->flags)) {
> > +			iecm_recv_mb_msg(adapter,
> VIRTCHNL_OP_UNKNOWN, NULL, 0);
> > +			iecm_mb_irq_enable(adapter);
> > +		}
> > +	} else {
> > +		iecm_recv_mb_msg(adapter, VIRTCHNL_OP_UNKNOWN,
> NULL, 0);
> > +	}
> > +
> > +	if (iecm_is_reset_detected(adapter) &&
> > +	    !iecm_is_reset_in_prog(adapter)) {
> > +		dev_info(&adapter->pdev->dev, "HW reset detected\n");
> > +		set_bit(__IECM_HR_FUNC_RESET, adapter->flags);
> > +		queue_delayed_work(adapter->vc_event_wq,
> > +				   &adapter->vc_event_task,
> > +				   msecs_to_jiffies(10));
> > +	}
> > +
> > +	queue_delayed_work(adapter->serv_wq, &adapter->serv_task,
> > +			   msecs_to_jiffies(300));
> > +}
> > +
> > +/* iecm_set_vlan_offload_features - set vlan offload features
> > + * @netdev: netdev structure
> > + * @prev_features: previously set features
> > + * @features: current features received from user
> > + *
> > + * Returns 0 on success, error value on failure  */ static int
> > +iecm_set_vlan_offload_features(struct net_device *netdev,
> > +			       netdev_features_t prev_features,
> > +			       netdev_features_t features) {
> > +	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
> > +	bool stripping_ena = true, insertion_ena = true;
> > +	struct iecm_virtchnl_ops *vc_ops;
> > +	u16 vlan_ethertype = 0;
> > +
> > +	vc_ops = &vport->adapter->dev_ops.vc_ops;
> > +	/* keep cases separate because one ethertype for offloads can be
> > +	 * disabled at the same time as another is disabled, so check for an
> > +	 * enabled ethertype first, then check for disabled. Default to
> > +	 * ETH_P_8021Q so an ethertype is specified if disabling insertion
> > +	 * and stripping.
> > +	 */
> > +	if (features & (NETIF_F_HW_VLAN_STAG_RX |
> NETIF_F_HW_VLAN_STAG_TX))
> > +		vlan_ethertype = ETH_P_8021AD;
> > +	else if (features & (NETIF_F_HW_VLAN_CTAG_RX |
> NETIF_F_HW_VLAN_CTAG_TX))
> > +		vlan_ethertype = ETH_P_8021Q;
> > +	else if (prev_features & (NETIF_F_HW_VLAN_STAG_RX |
> > +				  NETIF_F_HW_VLAN_STAG_TX))
> > +		vlan_ethertype = ETH_P_8021AD;
> > +	else if (prev_features & (NETIF_F_HW_VLAN_CTAG_RX |
> > +				  NETIF_F_HW_VLAN_CTAG_TX))
> > +		vlan_ethertype = ETH_P_8021Q;
> > +	else
> > +		vlan_ethertype = ETH_P_8021Q;
> > +
> > +	if (!(features & (NETIF_F_HW_VLAN_STAG_RX |
> NETIF_F_HW_VLAN_CTAG_RX)))
> > +		stripping_ena = false;
> > +	if (!(features & (NETIF_F_HW_VLAN_STAG_TX |
> NETIF_F_HW_VLAN_CTAG_TX)))
> > +		insertion_ena = false;
> > +
> > +	vport->adapter->config_data.vlan_ethertype = vlan_ethertype;
> > +
> > +	vc_ops->strip_vlan_msg(vport, stripping_ena);
> > +	if (vc_ops->insert_vlan_msg)
> > +		vc_ops->insert_vlan_msg(vport, insertion_ena);
> > +
> > +	return 0;
> > +}
> > +
> > +/**
> > + * iecm_vport_open - Bring up a vport
> > + * @vport: vport to bring up
> > + * @alloc_res: allocate queue resources  */ static int
> > +iecm_vport_open(struct iecm_vport *vport, bool alloc_res)
> >  {
> >  	/* stub */
> > +	return 0;
> >  }
> >
> >  /**
> > @@ -206,6 +925,7 @@ static void iecm_init_task(struct work_struct *work)
> >  	struct iecm_vport *vport;
> >  	struct pci_dev *pdev;
> >  	int vport_id, err;
> > +	int index;
> >
> >  	err = adapter->dev_ops.vc_ops.core_init(adapter, &vport_id);
> >  	if (err)
> > @@ -219,6 +939,65 @@ static void iecm_init_task(struct work_struct *work)
> >  			err);
> >  		return;
> >  	}
> > +	/* Start the service task before requesting vectors. This will ensure
> > +	 * vector information response from mailbox is handled
> > +	 */
> > +	queue_delayed_work(adapter->serv_wq, &adapter->serv_task,
> > +			   msecs_to_jiffies(5 * (pdev->devfn & 0x07)));
> > +	err = iecm_intr_req(adapter);
> > +	if (err) {
> > +		dev_err(&pdev->dev, "failed to enable interrupt vectors: %d\n",
> > +			err);
> > +		goto intr_req_err;
> > +	}
> > +	err = iecm_send_vlan_v2_caps_msg(adapter);
> > +	if (err)
> > +		goto vlan_v2_caps_failed;
> > +
> > +	err = adapter->dev_ops.vc_ops.get_supported_desc_ids(vport);
> > +	if (err) {
> > +		dev_err(&pdev->dev, "failed to get required descriptor ids\n");
> > +		goto rxdids_failed;
> > +	}
> > +
> > +	if (iecm_cfg_netdev(vport))
> > +		goto cfg_netdev_err;
> > +
> > +	if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
> VIRTCHNL2_CAP_VLAN) ||
> > +	    iecm_is_cap_ena(adapter, IECM_BASE_CAPS,
> VIRTCHNL2_CAP_VLAN)) {
> > +		err = iecm_set_vlan_offload_features(vport->netdev, 0,
> > +						     vport->netdev->features);
> > +		if (err)
> > +			goto cfg_netdev_err;
> > +	}
> > +
> > +	err = adapter->dev_ops.vc_ops.get_ptype(vport);
> > +	if (err)
> > +		goto cfg_netdev_err;
> > +	queue_delayed_work(adapter->stats_wq, &adapter->stats_task,
> > +			   msecs_to_jiffies(10 * (pdev->devfn & 0x07)));
> > +	set_bit(__IECM_VPORT_INIT_PROMISC, vport->flags);
> > +	/* Once state is put into DOWN, driver is ready for dev_open */
> > +	adapter->state = __IECM_DOWN;
> > +	if (test_and_clear_bit(__IECM_UP_REQUESTED, adapter->flags))
> > +		iecm_vport_open(vport, true);
> > +
> > +	/* Clear the reset flag unconditionally here in case we were in reset
> > +	 * and the link was down
> > +	 */
> > +	clear_bit(__IECM_HR_RESET_IN_PROG, vport->adapter->flags);
> > +
> > +	return;
> > +
> > +vlan_v2_caps_failed:
> > +rxdids_failed:
> > +cfg_netdev_err:
> 
> No reason to declare 3 labels at the same point.
> 

Will fix.

> > +	iecm_intr_rel(adapter);
> > +intr_req_err:
> > +	index = iecm_get_vport_index(adapter, vport);
> > +	if (index >= 0)
> > +		adapter->vports[index] = NULL;
> > +	iecm_vport_rel(vport);
> >  }
> >
> >  /**
> > @@ -614,3 +1393,35 @@ void iecm_free_dma_mem(struct iecm_hw *hw,
> struct iecm_dma_mem *mem)
> >  	mem->va = NULL;
> >  	mem->pa = 0;
> >  }
> > +
> > +static const struct net_device_ops iecm_netdev_ops_splitq = {
> > +	.ndo_open = NULL,
> > +	.ndo_stop = NULL,
> > +	.ndo_start_xmit = NULL,
> > +	.ndo_set_rx_mode = NULL,
> > +	.ndo_validate_addr = eth_validate_addr,
> 
> eth_validate_addr() (or ether_addr_valid()) gets run by default when no
> .ndo_validate_addr is specified. It's redundant to declare it here.
> 

Will fix

> > +	.ndo_set_mac_address = NULL,
> > +	.ndo_change_mtu = NULL,
> > +	.ndo_get_stats64 = NULL,
> > +	.ndo_fix_features = NULL,
> > +	.ndo_set_features = NULL,
> > +	.ndo_vlan_rx_add_vid = NULL,
> > +	.ndo_vlan_rx_kill_vid = NULL,
> > +	.ndo_setup_tc = NULL,
> 
> Non-initialized members get zeroed by default, NULLing them is excessive.
> 

The next patches will add these, they're placeholders. I left it like that because as you read the patches you can know what functions are there yet (as well as a checklist for myself as made the patches).

> > +};
> > +
> > +static const struct net_device_ops iecm_netdev_ops_singleq = {
> > +	.ndo_open = NULL,
> > +	.ndo_stop = NULL,
> > +	.ndo_start_xmit = NULL,
> > +	.ndo_set_rx_mode = NULL,
> > +	.ndo_validate_addr = eth_validate_addr,
> > +	.ndo_set_mac_address = NULL,
> > +	.ndo_change_mtu = NULL,
> > +	.ndo_get_stats64 = NULL,
> > +	.ndo_fix_features = NULL,
> > +	.ndo_set_features = NULL,
> > +	.ndo_vlan_rx_add_vid = NULL,
> > +	.ndo_vlan_rx_kill_vid = NULL,
> > +	.ndo_setup_tc           = NULL,
> 
> Same 2 previous points.
> 
> .ndo_setup_tc assignment indentation is weird. Either align all the initializers
> with tabs or don't align at all (both are valid cases).
> 
> > +};
> > diff --git a/drivers/net/ethernet/intel/iecm/iecm_txrx.c
> > b/drivers/net/ethernet/intel/iecm/iecm_txrx.c
> > index bd0cfd89bf03..bb7f5830cffb 100644
> > --- a/drivers/net/ethernet/intel/iecm/iecm_txrx.c
> > +++ b/drivers/net/ethernet/intel/iecm/iecm_txrx.c
> > @@ -218,6 +218,23 @@ const struct iecm_rx_ptype_decoded
> > iecm_ptype_lookup[IECM_RX_MAX_PTYPE] = {  };
> > EXPORT_SYMBOL(iecm_ptype_lookup);
> >
> > +/**
> > + * iecm_vport_intr_clean_queues - MSIX mode Interrupt Handler
> > + * @irq: interrupt number
> > + * @data: pointer to a q_vector
> > + *
> > + */
> > +irqreturn_t
> > +iecm_vport_intr_clean_queues(int __always_unused irq, void *data) {
> > +	struct iecm_q_vector *q_vector = (struct iecm_q_vector *)data;
> > +
> > +	q_vector->total_events++;
> > +	napi_schedule(&q_vector->napi);
> > +
> > +	return IRQ_HANDLED;
> > +}
> > +
> >  /**
> >   * iecm_vport_init_num_qs - Initialize number of queues
> >   * @vport: vport to initialize queues diff --git
> > a/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> > b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> > index c4ae56897d1b..b91716aeef6f 100644
> > --- a/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> > +++ b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> > @@ -2731,6 +2731,45 @@ static int iecm_send_insert_vlan_msg(struct
> iecm_vport *vport, bool ena)
> >  	return err;
> >  }
> >
> > +/**
> > + * iecm_send_vlan_v2_caps_msg - send virtchnl get offload VLAN V2
> > +caps message
> > + * @adapter: adapter info struct
> > + *
> > + * Returns 0 on success and if VLAN V1 capability is set`, negative on failure.
> > + */
> > +int iecm_send_vlan_v2_caps_msg(struct iecm_adapter *adapter) {
> > +	int err = 0;
> > +
> > +	if (!iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
> VIRTCHNL2_CAP_VLAN))
> > +		return err;
> > +
> > +	err = iecm_send_mb_msg(adapter,
> VIRTCHNL_OP_GET_OFFLOAD_VLAN_V2_CAPS,
> > +			       0, NULL);
> > +	if (err)
> > +		return err;
> > +
> > +	err = iecm_min_wait_for_event(adapter,
> IECM_VC_OFFLOAD_VLAN_V2_CAPS,
> > +				      IECM_VC_OFFLOAD_VLAN_V2_CAPS_ERR);
> > +
> > +	if (err) {
> > +		dev_err(&adapter->pdev->dev, "Failed to recv get caps");
> > +		return err;
> > +	}
> > +
> > +	if (!adapter->vlan_caps) {
> > +		adapter->vlan_caps =
> > +		  kzalloc(sizeof(*adapter->vlan_caps), GFP_KERNEL);
> > +		if (!adapter->vlan_caps)
> > +			return -ENOMEM;
> > +	}
> > +
> > +	memcpy(adapter->vlan_caps,
> > +	       adapter->vc_msg, sizeof(*adapter->vlan_caps));
> > +
> > +	return err;
> > +}
> > +
> >  /**
> >   * iecm_fill_ptype_lookup - Fill L3 specific fields in ptype lookup table
> >   * @ptype: ptype lookup table
> > @@ -3580,6 +3619,132 @@ static unsigned int iecm_get_max_tx_bufs(struct
> iecm_adapter *adapter)
> >  	return ((struct virtchnl2_get_capabilities
> > *)adapter->caps)->max_sg_bufs_per_tx_pkt;
> >  }
> >
> > +/**
> > + * iecm_add_del_ether_addrs
> > + * @vport: virtual port data structure
> > + * @add: Add or delete flag
> > + * @async: Don't wait for return message
> > + *
> > + * Request that the PF add or delete one or more addresses to our filters.
> > + **/
> > +void iecm_add_del_ether_addrs(struct iecm_vport *vport, bool add,
> > +bool async) {
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	struct virtchnl_ether_addr_list *veal = NULL;
> > +	int num_entries, num_msgs, total_filters = 0;
> > +	struct pci_dev *pdev = adapter->pdev;
> > +	enum iecm_vport_vc_state vc, vc_err;
> > +	struct virtchnl_ether_addr *eal;
> > +	struct iecm_mac_filter *f, *tmp;
> > +	int i = 0, k = 0, err = 0;
> > +	enum virtchnl_ops vop;
> > +
> > +	spin_lock_bh(&adapter->mac_filter_list_lock);
> > +
> > +	/* Find the number of newly added filters */
> > +	list_for_each_entry(f, &adapter->config_data.mac_filter_list, list) {
> > +		if (add && f->add)
> > +			total_filters++;
> > +		else if (!add && f->remove)
> > +			total_filters++;
> > +	}
> > +	if (!total_filters) {
> > +		spin_unlock_bh(&adapter->mac_filter_list_lock);
> > +		goto error;
> > +	}
> > +
> > +	/* Fill all the new filters into virtchannel message */
> > +	eal = kcalloc(total_filters, sizeof(struct virtchnl_ether_addr),
> > +		      GFP_ATOMIC);
> > +	if (!eal) {
> > +		err = -ENOMEM;
> > +		spin_unlock_bh(&adapter->mac_filter_list_lock);
> > +		goto error;
> > +	}
> > +	list_for_each_entry_safe(f, tmp, &adapter->config_data.mac_filter_list,
> > +				 list) {
> > +		if (add && f->add) {
> > +			ether_addr_copy(eal[i].addr, f->macaddr);
> > +			i++;
> > +			f->add = false;
> > +			if (i == total_filters)
> > +				break;
> > +		}
> > +		if (!add && f->remove) {
> > +			ether_addr_copy(eal[i].addr, f->macaddr);
> > +			i++;
> > +			list_del(&f->list);
> > +			kfree(f);
> > +			if (i == total_filters)
> > +				break;
> > +		}
> > +	}
> > +
> > +	spin_unlock_bh(&adapter->mac_filter_list_lock);
> > +
> > +	/* Chunk up the filters into multiple messages to avoid
> > +	 * sending a control queue message buffer that is too large
> > +	 */
> > +	if (total_filters < IECM_NUM_FILTERS_PER_MSG)
> > +		num_entries = total_filters;
> > +	else
> > +		num_entries = IECM_NUM_FILTERS_PER_MSG;
> > +
> > +	num_msgs = DIV_ROUND_UP(total_filters,
> IECM_NUM_FILTERS_PER_MSG);
> > +
> > +	for (i = 0, k = 0; i < num_msgs || num_entries; i++) {
> > +		int buf_size = sizeof(struct virtchnl_ether_addr_list) +
> > +			(sizeof(struct virtchnl_ether_addr) * num_entries);
> > +		if (!veal || num_entries != IECM_NUM_FILTERS_PER_MSG) {
> > +			kfree(veal);
> > +			veal = kzalloc(buf_size, GFP_KERNEL);
> > +			if (!veal) {
> > +				err = -ENOMEM;
> > +				goto list_prep_error;
> > +			}
> > +		} else {
> > +			memset(veal, 0, buf_size);
> > +		}
> > +
> > +		veal->vsi_id = vport->vport_id;
> > +		veal->num_elements = num_entries;
> > +		memcpy(veal->list, &eal[k],
> > +		       sizeof(struct virtchnl_ether_addr) * num_entries);
> > +
> > +		if (add) {
> > +			vop = VIRTCHNL_OP_ADD_ETH_ADDR;
> > +			vc = IECM_VC_ADD_ETH_ADDR;
> > +			vc_err = IECM_VC_ADD_ETH_ADDR_ERR;
> > +		} else  {
> > +			vop = VIRTCHNL_OP_DEL_ETH_ADDR;
> > +			vc = IECM_VC_DEL_ETH_ADDR;
> > +			vc_err = IECM_VC_DEL_ETH_ADDR_ERR;
> > +		}
> > +		err = iecm_send_mb_msg(vport->adapter, vop, buf_size,
> > +				       (u8 *)veal);
> > +		if (err)
> > +			goto mbx_error;
> > +
> > +		if (!async) {
> > +			err = iecm_wait_for_event(vport->adapter, vc, vc_err);
> > +			if (err)
> > +				goto mbx_error;
> > +		}
> > +
> > +		k += num_entries;
> > +		total_filters -= num_entries;
> > +		if (total_filters < IECM_NUM_FILTERS_PER_MSG)
> > +			num_entries = total_filters;
> > +	}
> > +mbx_error:
> > +	kfree(veal);
> > +list_prep_error:
> > +	kfree(eal);
> > +error:
> > +	if (err)
> > +		dev_err(&pdev->dev, "Failed to add or del mac filters %d", err);
> }
> > +
> >  /**
> >   * iecm_add_del_vlans - Add or delete vlan filter
> >   * @vport: vport structure
> > diff --git a/drivers/net/ethernet/intel/include/iecm.h
> > b/drivers/net/ethernet/intel/include/iecm.h
> > index d736db65da06..b5bd73be2855 100644
> > --- a/drivers/net/ethernet/intel/include/iecm.h
> > +++ b/drivers/net/ethernet/intel/include/iecm.h
> > @@ -33,6 +33,11 @@
> >  #define IECM_MB_MAX_ERR			20
> >  #define IECM_NUM_CHUNKS_PER_MSG(a, b)	((IECM_DFLT_MBX_BUF_SIZE -
> (a)) / (b))
> >
> > +/* 2K is the real maximum, but the driver should not be using more
> > +than the
> > + * below limit
> > + */
> > +#define IECM_MAX_VECIDS			256
> > +
> >  #define IECM_MAX_NUM_VPORTS		1
> >
> >  /* available message levels */
> > @@ -135,6 +140,10 @@ enum iecm_cap_field {
> >  	IECM_CAP_FIELD_LAST,
> >  };
> >
> > +struct iecm_netdev_priv {
> > +	struct iecm_vport *vport;
> > +};
> > +
> >  struct iecm_reset_reg {
> >  	u32 rstat;
> >  	u32 rstat_m;
> > @@ -450,6 +459,8 @@ struct iecm_adapter {
> >  	struct msix_entry *msix_entries;
> >  	struct virtchnl2_alloc_vectors *req_vec_chunks;
> >  	struct iecm_q_vector mb_vector;
> > +	/* handler for hard interrupt for mailbox*/
> > +	irqreturn_t (*irq_mb_handler)(int irq, void *data);
> >
> >  	/* vport structs */
> >  	struct iecm_vport **vports;	/* vports created by the driver */
> > @@ -537,12 +548,88 @@ static inline bool __iecm_is_cap_ena(struct
> iecm_adapter *adapter, bool all,
> >  	return adapter->dev_ops.vc_ops.is_cap_ena(adapter, all, field,
> > flag);  }
> >
> > -#define IECM_CAP_HSPLIT (\
> > -	VIRTCHNL2_CAP_RX_HSPLIT_AT_L2   |\
> > -	VIRTCHNL2_CAP_RX_HSPLIT_AT_L3   |\
> > -	VIRTCHNL2_CAP_RX_HSPLIT_AT_L4V4 |\
> > +/* enum used to distinguish vlan capabilities */ enum iecm_vlan_caps
> > +{
> > +	IECM_CAP_VLAN_CTAG_INSERT,
> > +	IECM_CAP_VLAN_STAG_INSERT,
> > +	IECM_CAP_VLAN_CTAG_STRIP,
> > +	IECM_CAP_VLAN_STAG_STRIP,
> > +	IECM_CAP_VLAN_CTAG_ADD_DEL,
> > +	IECM_CAP_VLAN_STAG_ADD_DEL,
> > +	IECM_CAP_VLAN_LAST,
> > +};
> > +
> > +#define IECM_VLAN_8100 (VIRTCHNL_VLAN_TOGGLE |
> > +VIRTCHNL_VLAN_ETHERTYPE_8100) #define IECM_VLAN_88A8
> > +(VIRTCHNL_VLAN_TOGGLE | VIRTCHNL_VLAN_ETHERTYPE_88A8)
> > +
> > +#define IECM_F_HW_VLAN_CTAG_TX NETIF_F_HW_VLAN_CTAG_TX
> > +
> > +#define IECM_F_HW_VLAN_CTAG_RX NETIF_F_HW_VLAN_CTAG_RX
> > +
> > +#define IECM_F_HW_VLAN_CTAG_FILTER NETIF_F_HW_VLAN_CTAG_FILTER
> > +
> > +#define IECM_CAP_RSS (\
> > +	VIRTCHNL2_CAP_RSS_IPV4_TCP	|\
> > +	VIRTCHNL2_CAP_RSS_IPV4_TCP	|\
> > +	VIRTCHNL2_CAP_RSS_IPV4_UDP	|\
> > +	VIRTCHNL2_CAP_RSS_IPV4_SCTP	|\
> > +	VIRTCHNL2_CAP_RSS_IPV4_OTHER	|\
> > +	VIRTCHNL2_CAP_RSS_IPV4_AH	|\
> > +	VIRTCHNL2_CAP_RSS_IPV4_ESP	|\
> > +	VIRTCHNL2_CAP_RSS_IPV4_AH_ESP	|\
> > +	VIRTCHNL2_CAP_RSS_IPV6_TCP	|\
> > +	VIRTCHNL2_CAP_RSS_IPV6_TCP	|\
> > +	VIRTCHNL2_CAP_RSS_IPV6_UDP	|\
> > +	VIRTCHNL2_CAP_RSS_IPV6_SCTP	|\
> > +	VIRTCHNL2_CAP_RSS_IPV6_OTHER	|\
> > +	VIRTCHNL2_CAP_RSS_IPV6_AH	|\
> > +	VIRTCHNL2_CAP_RSS_IPV6_ESP	|\
> > +	VIRTCHNL2_CAP_RSS_IPV6_AH_ESP)
> > +
> > +#define IECM_CAP_RSC (\
> > +	VIRTCHNL2_CAP_RSC_IPV4_TCP	|\
> > +	VIRTCHNL2_CAP_RSC_IPV4_SCTP	|\
> > +	VIRTCHNL2_CAP_RSC_IPV6_TCP	|\
> > +	VIRTCHNL2_CAP_RSC_IPV6_SCTP)
> > +
> > +#define IECM_CAP_HSPLIT	(\
> > +	VIRTCHNL2_CAP_RX_HSPLIT_AT_L2	|\
> > +	VIRTCHNL2_CAP_RX_HSPLIT_AT_L3	|\
> > +	VIRTCHNL2_CAP_RX_HSPLIT_AT_L4V4	|\
> >  	VIRTCHNL2_CAP_RX_HSPLIT_AT_L4V6)
> >
> > +#define IECM_CAP_RX_CSUM_L4V4 (\
> > +	VIRTCHNL2_CAP_RX_CSUM_L4_IPV4_TCP	|\
> > +	VIRTCHNL2_CAP_RX_CSUM_L4_IPV4_UDP)
> > +
> > +#define IECM_CAP_RX_CSUM_L4V6 (\
> > +	VIRTCHNL2_CAP_RX_CSUM_L4_IPV6_TCP	|\
> > +	VIRTCHNL2_CAP_RX_CSUM_L4_IPV6_UDP)
> > +
> > +#define IECM_CAP_RX_CSUM (\
> > +	VIRTCHNL2_CAP_RX_CSUM_L3_IPV4		|\
> > +	VIRTCHNL2_CAP_RX_CSUM_L4_IPV4_TCP	|\
> > +	VIRTCHNL2_CAP_RX_CSUM_L4_IPV4_UDP	|\
> > +	VIRTCHNL2_CAP_RX_CSUM_L4_IPV4_SCTP	|\
> > +	VIRTCHNL2_CAP_RX_CSUM_L4_IPV6_TCP	|\
> > +	VIRTCHNL2_CAP_RX_CSUM_L4_IPV6_UDP	|\
> > +	VIRTCHNL2_CAP_RX_CSUM_L4_IPV6_SCTP)
> > +
> > +#define IECM_CAP_SCTP_CSUM (\
> > +	VIRTCHNL2_CAP_TX_CSUM_L4_IPV4_SCTP	|\
> > +	VIRTCHNL2_CAP_TX_CSUM_L4_IPV6_SCTP	|\
> > +	VIRTCHNL2_CAP_RX_CSUM_L4_IPV4_SCTP	|\
> > +	VIRTCHNL2_CAP_RX_CSUM_L4_IPV6_SCTP)
> > +
> > +/**
> > + * iecm_get_reserved_vecs - Get reserved vectors
> > + * @adapter: private data struct
> > + */
> > +static inline u16 iecm_get_reserved_vecs(struct iecm_adapter
> > +*adapter) {
> > +	return adapter->dev_ops.vc_ops.get_reserved_vecs(adapter);
> > +}
> > +
> >  /**
> >   * iecm_is_reset_detected - check if we were reset at some point
> >   * @adapter: driver specific private structure @@ -555,6 +642,20 @@
> > static inline bool iecm_is_reset_detected(struct iecm_adapter *adapter)
> >  		 adapter->hw.arq->reg.len_ena_mask);
> >  }
> >
> > +/**
> > + * iecm_is_reset_in_prog - check if reset is in progress
> > + * @adapter: driver specific private structure
> > + *
> > + * Returns true if hard reset is in progress, false otherwise  */
> > +static inline bool iecm_is_reset_in_prog(struct iecm_adapter
> > +*adapter) {
> > +	return (test_bit(__IECM_HR_RESET_IN_PROG, adapter->flags) ||
> > +		test_bit(__IECM_HR_FUNC_RESET, adapter->flags) ||
> > +		test_bit(__IECM_HR_CORE_RESET, adapter->flags) ||
> > +		test_bit(__IECM_HR_DRV_LOAD, adapter->flags)); }
> > +
> >  int iecm_probe(struct pci_dev *pdev,
> >  	       const struct pci_device_id __always_unused *ent,
> >  	       struct iecm_adapter *adapter); @@ -576,6 +677,7 @@ int
> > iecm_send_get_caps_msg(struct iecm_adapter *adapter);  int
> > iecm_send_delete_queues_msg(struct iecm_vport *vport);  int
> > iecm_send_add_queues_msg(struct iecm_vport *vport, u16 num_tx_q,
> >  			     u16 num_complq, u16 num_rx_q, u16 num_rx_bufq);
> > +int iecm_send_vlan_v2_caps_msg(struct iecm_adapter *adapter);
> >  int iecm_send_config_tx_queues_msg(struct iecm_vport *vport);  int
> > iecm_send_config_rx_queues_msg(struct iecm_vport *vport);  int
> > iecm_send_enable_vport_msg(struct iecm_vport *vport); @@ -589,6 +691,7
> > @@ int iecm_send_dealloc_vectors_msg(struct iecm_adapter *adapter);
> > int iecm_send_alloc_vectors_msg(struct iecm_adapter *adapter, u16
> > num_vectors);  int iecm_vport_params_buf_alloc(struct iecm_adapter
> > *adapter);  void iecm_vport_params_buf_rel(struct iecm_adapter
> > *adapter);
> > +struct iecm_vport *iecm_netdev_to_vport(struct net_device *netdev);
> >  int iecm_send_get_stats_msg(struct iecm_vport *vport);  int
> > iecm_get_vec_ids(struct iecm_adapter *adapter,
> >  		     u16 *vecids, int num_vecids,
> > @@ -598,6 +701,7 @@ int iecm_recv_mb_msg(struct iecm_adapter *adapter,
> > enum virtchnl_ops op,  int iecm_send_mb_msg(struct iecm_adapter *adapter,
> enum virtchnl_ops op,
> >  		     u16 msg_size, u8 *msg);
> >  void iecm_vport_set_hsplit(struct iecm_vport *vport, bool ena);
> > +void iecm_add_del_ether_addrs(struct iecm_vport *vport, bool add,
> > +bool async);
> >  int iecm_send_enable_channels_msg(struct iecm_vport *vport);  int
> > iecm_send_disable_channels_msg(struct iecm_vport *vport);  bool
> > iecm_is_feature_ena(struct iecm_vport *vport, netdev_features_t
> > feature); diff --git a/drivers/net/ethernet/intel/include/iecm_txrx.h
> > b/drivers/net/ethernet/intel/include/iecm_txrx.h
> > index 9f3086bfe575..0aa1eac70e7c 100644
> > --- a/drivers/net/ethernet/intel/include/iecm_txrx.h
> > +++ b/drivers/net/ethernet/intel/include/iecm_txrx.h
> > @@ -512,4 +512,6 @@ void iecm_vport_calc_total_qs(struct iecm_adapter
> *adapter,
> >  			      struct virtchnl2_create_vport *vport_msg);  void
> > iecm_vport_calc_num_q_groups(struct iecm_vport *vport);  void
> > iecm_vport_calc_num_q_vec(struct iecm_vport *vport);
> > +irqreturn_t
> > +iecm_vport_intr_clean_queues(int __always_unused irq, void *data);
> >  #endif /* !_IECM_TXRX_H_ */
> > --
> > 2.33.0
> 
> Thanks,
> Al

^ permalink raw reply	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 09/19] iecm: alloc vport TX resources
  2022-01-28  0:09 ` [Intel-wired-lan] [PATCH net-next 09/19] iecm: alloc vport TX resources Alan Brady
@ 2022-02-02 23:45   ` Brady, Alan
  2022-02-03 17:56     ` Alexander Lobakin
  0 siblings, 1 reply; 89+ messages in thread
From: Brady, Alan @ 2022-02-02 23:45 UTC (permalink / raw)
  To: intel-wired-lan

> -----Original Message-----
> From: Lobakin, Alexandr <alexandr.lobakin@intel.com>
> Sent: Friday, January 28, 2022 5:57 AM
> To: Brady, Alan <alan.brady@intel.com>
> Cc: Lobakin, Alexandr <alexandr.lobakin@intel.com>; intel-wired-
> lan at lists.osuosl.org; Burra, Phani R <phani.r.burra@intel.com>; Chittim, Madhu
> <madhu.chittim@intel.com>; Linga, Pavan Kumar
> <pavan.kumar.linga@intel.com>
> Subject: Re: [Intel-wired-lan] [PATCH net-next 09/19] iecm: alloc vport TX
> resources
> 
> From: Alan Brady <alan.brady@intel.com>
> Date: Thu, 27 Jan 2022 16:09:59 -0800
> 
> > With init_task out of the way, we can start implementing open and data
> > path. During open we'll allocate queue resources for vport. This only
> > includes what's needed to get the TX resources. The next patch will get RX
> > resources.
> >
> > The splitq model is unique in that it introduces the concept of "queue
> > groups" where, for TX, we have some number of descriptor queues being
> > serviced by one completion queue in a given group association. By
> > 'splitting' a normal queue into two queues, one context is just handling
> > descriptors and one context handling buffers, we can more effeciently deal
> > with both and configure asymmetric setups (multiple descriptor queues to
> > one completion queue).
> >
> > Signed-off-by: Phani Burra <phani.r.burra@intel.com>
> > Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
> > Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
> > Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
> > Signed-off-by: Alan Brady <alan.brady@intel.com>
> > ---
> >  drivers/net/ethernet/intel/iecm/Makefile      |    1 +
> >  drivers/net/ethernet/intel/iecm/iecm_lib.c    |  369 ++++-
> >  .../ethernet/intel/iecm/iecm_singleq_txrx.c   |   29 +
> >  drivers/net/ethernet/intel/iecm/iecm_txrx.c   | 1282 ++++++++++++++++-
> >  .../net/ethernet/intel/iecm/iecm_virtchnl.c   |   29 +
> >  drivers/net/ethernet/intel/include/iecm.h     |   28 +
> >  .../ethernet/intel/include/iecm_lan_txrx.h    |  394 +++++
> >  .../net/ethernet/intel/include/iecm_txrx.h    |   96 ++
> >  8 files changed, 2214 insertions(+), 14 deletions(-)
> >  create mode 100644 drivers/net/ethernet/intel/iecm/iecm_singleq_txrx.c
> >  create mode 100644 drivers/net/ethernet/intel/include/iecm_lan_txrx.h
> >
> > diff --git a/drivers/net/ethernet/intel/iecm/Makefile
> b/drivers/net/ethernet/intel/iecm/Makefile
> > index fcb49402334f..205d6f2b436a 100644
> > --- a/drivers/net/ethernet/intel/iecm/Makefile
> > +++ b/drivers/net/ethernet/intel/iecm/Makefile
> > @@ -14,6 +14,7 @@ iecm-y := \
> >  	iecm_lib.o \
> >  	iecm_virtchnl.o \
> >  	iecm_txrx.o \
> > +	iecm_singleq_txrx.o \
> >  	iecm_controlq.o \
> >  	iecm_controlq_setup.o \
> >  	iecm_main.o
> > diff --git a/drivers/net/ethernet/intel/iecm/iecm_lib.c
> b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > index 255b04c25683..037a0e06bb7b 100644
> > --- a/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > +++ b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > @@ -480,6 +480,54 @@ static struct iecm_mac_filter
> *iecm_add_mac_filter(struct iecm_vport *vport,
> >  	return f;
> >  }
> >
> > +/**
> > + * iecm_set_all_filters - Re-add all MAC filters in list
> > + * @vport: main vport struct
> > + *
> > + * Takes mac_filter_list_lock spinlock.  Sets add field to true for filters to
> > + * resync filters back to HW.
> > + */
> > +static void iecm_set_all_filters(struct iecm_vport *vport)
> > +{
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	struct iecm_mac_filter *f;
> > +
> > +	spin_lock_bh(&adapter->mac_filter_list_lock);
> > +	list_for_each_entry(f, &adapter->config_data.mac_filter_list, list) {
> > +		if (!f->remove)
> > +			f->add = true;
> > +	}
> 
> Redundant braces around a single statement.
> 

Will not fix.

> > +	spin_unlock_bh(&adapter->mac_filter_list_lock);
> > +
> > +	iecm_add_del_ether_addrs(vport, true, false);
> > +}
> > +
> > +/**
> > + * iecm_set_all_vlans - Re-add all VLANs in list
> > + * @vport: main vport struct
> > + *
> > + * Takes vlan_list_lock spinlock.  Sets add field to true for vlan filters and
> > + * resyncs vlans back to HW.
> > + */
> > +static void iecm_set_all_vlans(struct iecm_vport *vport)
> > +{
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	struct iecm_vlan_filter *f;
> > +
> > +	spin_lock_bh(&adapter->vlan_list_lock);
> > +	list_for_each_entry(f, &adapter->config_data.vlan_filter_list, list) {
> > +		if (!f->remove)
> > +			f->add = true;
> > +	}
> 
> Same.
> 
> > +	spin_unlock_bh(&adapter->vlan_list_lock);
> > +
> > +	/* Do both add and remove to make sure list is in sync in the case
> > +	 * filters were added and removed before up.
> > +	 */
> > +	adapter->dev_ops.vc_ops.add_del_vlans(vport, false);
> > +	adapter->dev_ops.vc_ops.add_del_vlans(vport, true);
> > +}
> > +
> >  /**
> >   * iecm_init_mac_addr - initialize mac address for vport
> >   * @vport: main vport structure
> > @@ -690,6 +738,63 @@ static int iecm_get_free_slot(void *array, int size, int
> curr)
> >  	return next;
> >  }
> >
> > +/**
> > + * iecm_vport_stop - Disable a vport
> > + * @vport: vport to disable
> > + */
> > +static void iecm_vport_stop(struct iecm_vport *vport)
> > +{
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +
> > +	mutex_lock(&vport->stop_mutex);
> > +	if (adapter->state <= __IECM_DOWN)
> > +		goto stop_unlock;
> > +
> > +	netif_tx_stop_all_queues(vport->netdev);
> > +	netif_carrier_off(vport->netdev);
> > +	netif_tx_disable(vport->netdev);
> > +
> > +	if (adapter->dev_ops.vc_ops.disable_vport)
> > +		adapter->dev_ops.vc_ops.disable_vport(vport);
> > +	adapter->dev_ops.vc_ops.disable_queues(vport);
> > +	adapter->dev_ops.vc_ops.irq_map_unmap(vport, false);
> > +	/* Normally we ask for queues in create_vport, but if we're changing
> > +	 * number of requested queues we do a delete then add instead of
> > +	 * deleting and reallocating the vport.
> > +	 */
> > +	if (test_and_clear_bit(__IECM_DEL_QUEUES,
> > +			       vport->adapter->flags))
> > +		iecm_send_delete_queues_msg(vport);
> > +
> > +	adapter->link_up = false;
> > +	iecm_vport_intr_deinit(vport);
> > +	iecm_vport_intr_rel(vport);
> > +	iecm_vport_queues_rel(vport);
> > +	adapter->state = __IECM_DOWN;
> > +
> > +stop_unlock:
> > +	mutex_unlock(&vport->stop_mutex);
> > +}
> > +
> > +/**
> > + * iecm_stop - Disables a network interface
> > + * @netdev: network interface device structure
> > + *
> > + * The stop entry point is called when an interface is de-activated by the OS,
> > + * and the netdevice enters the DOWN state.  The hardware is still under the
> > + * driver's control, but the netdev interface is disabled.
> > + *
> > + * Returns success only - not allowed to fail
> > + */
> > +static int iecm_stop(struct net_device *netdev)
> > +{
> > +	struct iecm_netdev_priv *np = netdev_priv(netdev);
> > +
> > +	iecm_vport_stop(np->vport);
> > +
> > +	return 0;
> > +}
> > +
> >  /**
> >   * iecm_decfg_netdev - Unregister the netdev
> >   * @vport: vport for which netdev to be unregistred
> > @@ -714,6 +819,11 @@ static void iecm_decfg_netdev(struct iecm_vport
> *vport)
> >   */
> >  static void iecm_vport_rel(struct iecm_vport *vport)
> >  {
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +
> > +	iecm_deinit_rss(vport);
> > +	if (adapter->dev_ops.vc_ops.destroy_vport)
> > +		adapter->dev_ops.vc_ops.destroy_vport(vport);
> >  	mutex_destroy(&vport->stop_mutex);
> >  	kfree(vport);
> >  }
> > @@ -733,6 +843,7 @@ static void iecm_vport_rel_all(struct iecm_adapter
> *adapter)
> >  		if (!adapter->vports[i])
> >  			continue;
> >
> > +		iecm_vport_stop(adapter->vports[i]);
> >  		if (!test_bit(__IECM_HR_RESET_IN_PROG, adapter->flags))
> >  			iecm_decfg_netdev(adapter->vports[i]);
> >  		iecm_vport_rel(adapter->vports[i]);
> > @@ -782,6 +893,7 @@ iecm_vport_alloc(struct iecm_adapter *adapter, int
> vport_id)
> >  	vport->idx = adapter->next_vport;
> >  	vport->compln_clean_budget = IECM_TX_COMPLQ_CLEAN_BUDGET;
> >  	adapter->num_alloc_vport++;
> > +	adapter->dev_ops.vc_ops.vport_init(vport, vport_id);
> >
> >  	/* Setup default MSIX irq handler for the vport */
> >  	vport->irq_q_handler = iecm_vport_intr_clean_queues;
> > @@ -845,6 +957,117 @@ static void iecm_service_task(struct work_struct
> *work)
> >  			   msecs_to_jiffies(300));
> >  }
> >
> > +/**
> > + * iecm_restore_vlans - Restore vlan filters/vlan stripping/insert config
> > + * @vport: virtual port structure
> > + */
> > +static void iecm_restore_vlans(struct iecm_vport *vport)
> > +{
> > +	if (iecm_is_feature_ena(vport, NETIF_F_HW_VLAN_CTAG_FILTER))
> > +		iecm_set_all_vlans(vport);
> > +}
> > +
> > +/**
> > + * iecm_restore_features - Restore feature configs
> > + * @vport: virtual port structure
> > + */
> > +static void iecm_restore_features(struct iecm_vport *vport)
> > +{
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +
> > +	if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
> VIRTCHNL2_CAP_MACFILTER))
> > +		iecm_set_all_filters(vport);
> > +
> > +	if (iecm_is_cap_ena(adapter, IECM_BASE_CAPS,
> VIRTCHNL2_CAP_VLAN) ||
> > +	    iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
> VIRTCHNL2_CAP_VLAN))
> > +		iecm_restore_vlans(vport);
> > +
> > +	if ((iecm_is_user_flag_ena(adapter, __IECM_PROMISC_UC) ||
> > +	     iecm_is_user_flag_ena(adapter, __IECM_PROMISC_MC)) &&
> > +	    test_and_clear_bit(__IECM_VPORT_INIT_PROMISC, vport->flags)) {
> > +		if (iecm_set_promiscuous(adapter))
> > +			dev_info(&adapter->pdev->dev, "Failed to restore
> promiscuous settings\n");
> > +	}
> > +}
> > +
> > +/**
> > + * iecm_set_real_num_queues - set number of queues for netdev
> > + * @vport: virtual port structure
> > + *
> > + * Returns 0 on success, negative on failure.
> > + */
> > +static int iecm_set_real_num_queues(struct iecm_vport *vport)
> > +{
> > +	int err;
> > +
> > +	/* If we're in normal up path, the stack already takes the rtnl_lock
> > +	 * for us, however, if we're doing up as a part of a hard reset, we'll
> > +	 * need to take the lock ourself before touching the netdev.
> > +	 */
> > +	if (test_bit(__IECM_HR_RESET_IN_PROG, vport->adapter->flags))
> > +		rtnl_lock();
> > +	err = netif_set_real_num_rx_queues(vport->netdev, vport->num_rxq);
> > +	if (err)
> > +		goto error;
> > +	err = netif_set_real_num_tx_queues(vport->netdev, vport->num_txq);
> > +error:
> > +	if (test_bit(__IECM_HR_RESET_IN_PROG, vport->adapter->flags))
> > +		rtnl_unlock();
> > +	return err;
> > +}
> > +
> > +/**
> > + * iecm_up_complete - Complete interface up sequence
> > + * @vport: virtual port strucutre
> > + *
> > + * Returns 0 on success, negative on failure.
> > + */
> > +static int iecm_up_complete(struct iecm_vport *vport)
> > +{
> > +	int err;
> > +
> > +	err = iecm_set_real_num_queues(vport);
> > +	if (err)
> > +		return err;
> > +
> > +	if (vport->adapter->link_up && !netif_carrier_ok(vport->netdev)) {
> > +		netif_carrier_on(vport->netdev);
> > +		netif_tx_start_all_queues(vport->netdev);
> > +	}
> > +
> > +	vport->adapter->state = __IECM_UP;
> > +	return 0;
> > +}
> > +
> > +/**
> > + * iecm_rx_init_buf_tail - Write initial buffer ring tail value
> > + * @vport: virtual port struct
> > + */
> > +static void iecm_rx_init_buf_tail(struct iecm_vport *vport)
> > +{
> > +	int i, j;
> > +
> > +	for (i = 0; i < vport->num_rxq_grp; i++) {
> > +		struct iecm_rxq_group *grp = &vport->rxq_grps[i];
> > +
> > +		if (iecm_is_queue_model_split(vport->rxq_model)) {
> > +			for (j = 0; j < vport->num_bufqs_per_qgrp; j++) {
> > +				struct iecm_queue *q =
> > +					&grp->splitq.bufq_sets[j].bufq;
> > +
> > +				writel(q->next_to_alloc, q->tail);
> > +			}
> > +		} else {
> > +			for (j = 0; j < grp->singleq.num_rxq; j++) {
> > +				struct iecm_queue *q =
> > +					grp->singleq.rxqs[j];
> > +
> > +				writel(q->next_to_alloc, q->tail);
> > +			}
> > +		}
> > +	}
> > +}
> > +
> >  /* iecm_set_vlan_offload_features - set vlan offload features
> >   * @netdev: netdev structure
> >   * @prev_features: previously set features
> > @@ -903,8 +1126,110 @@ iecm_set_vlan_offload_features(struct net_device
> *netdev,
> >   */
> >  static int iecm_vport_open(struct iecm_vport *vport, bool alloc_res)
> >  {
> > -	/* stub */
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	int err;
> > +
> > +	if (vport->adapter->state != __IECM_DOWN)
> > +		return -EBUSY;
> > +
> > +	/* we do not allow interface up just yet */
> > +	netif_carrier_off(vport->netdev);
> > +
> > +	if (alloc_res) {
> > +		err = iecm_vport_queues_alloc(vport);
> > +		if (err)
> > +			return err;
> > +	}
> > +
> > +	err = iecm_vport_intr_alloc(vport);
> > +	if (err) {
> > +		dev_err(&adapter->pdev->dev, "Call to interrupt alloc returned
> %d\n",
> > +			err);
> > +		goto unroll_queues_alloc;
> > +	}
> > +
> > +	err = adapter->dev_ops.vc_ops.vport_queue_ids_init(vport);
> > +	if (err) {
> > +		dev_err(&adapter->pdev->dev, "Call to queue ids init returned
> %d\n",
> > +			err);
> > +		goto unroll_intr_alloc;
> > +	}
> > +
> > +	err = adapter->dev_ops.vc_ops.vportq_reg_init(vport);
> > +	if (err) {
> > +		dev_err(&adapter->pdev->dev, "Call to queue reg init returned
> %d\n",
> > +			err);
> > +		goto unroll_intr_alloc;
> > +	}
> > +	iecm_rx_init_buf_tail(vport);
> > +
> > +	err = iecm_vport_intr_init(vport);
> > +	if (err) {
> > +		dev_err(&adapter->pdev->dev, "Call to vport interrupt init
> returned %d\n",
> > +			err);
> > +		goto unroll_intr_alloc;
> > +	}
> > +	err = adapter->dev_ops.vc_ops.config_queues(vport);
> > +	if (err) {
> > +		dev_err(&adapter->pdev->dev, "Failed to config queues\n");
> > +		goto unroll_config_queues;
> > +	}
> > +	err = adapter->dev_ops.vc_ops.irq_map_unmap(vport, true);
> > +	if (err) {
> > +		dev_err(&adapter->pdev->dev, "Call to irq_map_unmap
> returned %d\n",
> > +			err);
> > +		goto unroll_config_queues;
> > +	}
> > +	err = adapter->dev_ops.vc_ops.enable_queues(vport);
> > +	if (err) {
> > +		dev_err(&adapter->pdev->dev, "Failed to enable queues\n");
> > +		goto unroll_enable_queues;
> > +	}
> > +
> > +	if (adapter->dev_ops.vc_ops.enable_vport) {
> > +		err = adapter->dev_ops.vc_ops.enable_vport(vport);
> > +		if (err) {
> > +			dev_err(&adapter->pdev->dev, "Failed to enable
> vport\n");
> > +			err = -EAGAIN;
> > +			goto unroll_vport_enable;
> > +		}
> > +	}
> > +
> > +	iecm_restore_features(vport);
> > +
> > +	if (adapter->rss_data.rss_lut)
> > +		err = iecm_config_rss(vport);
> > +	else
> > +		err = iecm_init_rss(vport);
> > +	if (err) {
> > +		dev_err(&adapter->pdev->dev, "Failed to init RSS\n");
> > +		goto unroll_init_rss;
> > +	}
> > +	err = iecm_up_complete(vport);
> > +	if (err) {
> > +		dev_err(&adapter->pdev->dev, "Failed to complete up\n");
> > +		goto unroll_up_comp;
> > +	}
> > +
> >  	return 0;
> > +
> > +unroll_up_comp:
> > +	iecm_deinit_rss(vport);
> > +unroll_init_rss:
> > +	adapter->dev_ops.vc_ops.disable_vport(vport);
> > +unroll_vport_enable:
> > +	adapter->dev_ops.vc_ops.disable_queues(vport);
> > +unroll_enable_queues:
> > +	adapter->dev_ops.vc_ops.irq_map_unmap(vport, false);
> > +unroll_config_queues:
> > +	iecm_vport_intr_deinit(vport);
> > +unroll_intr_alloc:
> > +	iecm_vport_intr_rel(vport);
> > +unroll_queues_alloc:
> > +	if (alloc_res)
> > +		iecm_vport_queues_rel(vport);
> > +
> > +	return err;
> >  }
> >
> >  /**
> > @@ -1060,6 +1385,8 @@ static int iecm_api_init(struct iecm_adapter
> *adapter)
> >   */
> >  static void iecm_deinit_task(struct iecm_adapter *adapter)
> >  {
> > +	int i;
> > +
> >  	set_bit(__IECM_REL_RES_IN_PROG, adapter->flags);
> >  	/* Wait until the init_task is done else this thread might release
> >  	 * the resources first and the other thread might end up in a bad state
> > @@ -1067,8 +1394,21 @@ static void iecm_deinit_task(struct iecm_adapter
> *adapter)
> >  	cancel_delayed_work_sync(&adapter->init_task);
> >  	iecm_vport_rel_all(adapter);
> >
> > +	/* Set all bits as we dont know on which vc_state the vhnl_wq is
> > +	 * waiting on and wakeup the virtchnl workqueue even if it is waiting
> > +	 * for the response as we are going down
> > +	 */
> > +	for (i = 0; i < IECM_VC_NBITS; i++)
> > +		set_bit(i, adapter->vc_state);
> > +	wake_up(&adapter->vchnl_wq);
> > +
> >  	cancel_delayed_work_sync(&adapter->serv_task);
> >  	cancel_delayed_work_sync(&adapter->stats_task);
> > +	iecm_intr_rel(adapter);
> > +	/* Clear all the bits */
> > +	for (i = 0; i < IECM_VC_NBITS; i++)
> > +		clear_bit(i, adapter->vc_state);
> > +	clear_bit(__IECM_REL_RES_IN_PROG, adapter->flags);
> >  }
> >
> >  /**
> > @@ -1371,6 +1711,25 @@ void iecm_remove(struct pci_dev *pdev)
> >  }
> >  EXPORT_SYMBOL(iecm_remove);
> >
> > +/**
> > + * iecm_open - Called when a network interface becomes active
> > + * @netdev: network interface device structure
> > + *
> > + * The open entry point is called when a network interface is made
> > + * active by the system (IFF_UP).  At this point all resources needed
> > + * for transmit and receive operations are allocated, the interrupt
> > + * handler is registered with the OS, the netdev watchdog is enabled,
> > + * and the stack is notified that the interface is ready.
> > + *
> > + * Returns 0 on success, negative value on failure
> > + */
> > +static int iecm_open(struct net_device *netdev)
> > +{
> > +	struct iecm_netdev_priv *np = netdev_priv(netdev);
> > +
> > +	return iecm_vport_open(np->vport, true);
> > +}
> > +
> >  void *iecm_alloc_dma_mem(struct iecm_hw *hw, struct iecm_dma_mem
> *mem, u64 size)
> >  {
> >  	struct iecm_adapter *adapter = (struct iecm_adapter *)hw->back;
> > @@ -1395,8 +1754,8 @@ void iecm_free_dma_mem(struct iecm_hw *hw,
> struct iecm_dma_mem *mem)
> >  }
> >
> >  static const struct net_device_ops iecm_netdev_ops_splitq = {
> > -	.ndo_open = NULL,
> > -	.ndo_stop = NULL,
> > +	.ndo_open = iecm_open,
> > +	.ndo_stop = iecm_stop,
> >  	.ndo_start_xmit = NULL,
> 
> Hmm, forgot to mention this earlier as well. Consider marking
> CONFIG_IECM as `depends on BROKEN` in Kconfig and remove this line
> in the last commit. Otherwise, it will be possible to panic the
> kernel as at least .ndo_start_xmit should always be set, kernel
> doesn't check for it being non-NULL, it just calls it. Same with
> open, stop and probably more, so it's a good practice to disable
> drivers with depending on BROKEN until it receives the workable
> state.
> 

I don't have the full history on 'BROKEN' being a dependency but it seems silly to me to add some thrash just for that. Will consider.

> >  	.ndo_set_rx_mode = NULL,
> >  	.ndo_validate_addr = eth_validate_addr,
> > @@ -1411,8 +1770,8 @@ static const struct net_device_ops
> iecm_netdev_ops_splitq = {
> >  };
> >
> >  static const struct net_device_ops iecm_netdev_ops_singleq = {
> > -	.ndo_open = NULL,
> > -	.ndo_stop = NULL,
> > +	.ndo_open = iecm_open,
> > +	.ndo_stop = iecm_stop,
> >  	.ndo_start_xmit = NULL,
> >  	.ndo_set_rx_mode = NULL,
> >  	.ndo_validate_addr = eth_validate_addr,
> > diff --git a/drivers/net/ethernet/intel/iecm/iecm_singleq_txrx.c
> b/drivers/net/ethernet/intel/iecm/iecm_singleq_txrx.c
> > new file mode 100644
> > index 000000000000..d6c47cb84249
> > --- /dev/null
> > +++ b/drivers/net/ethernet/intel/iecm/iecm_singleq_txrx.c
> > @@ -0,0 +1,29 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +/* Copyright (C) 2019 Intel Corporation */
> > +
> > +#include "iecm.h"
> > +
> > +/**
> > + * iecm_rx_singleq_buf_hw_alloc_all - Replace used receive buffers
> > + * @rx_q: queue for which the hw buffers are allocated
> > + * @cleaned_count: number of buffers to replace
> > + *
> > + * Returns false if all allocations were successful, true if any fail
> > + */
> > +bool iecm_rx_singleq_buf_hw_alloc_all(struct iecm_queue *rx_q,
> > +				      u16 cleaned_count)
> > +{
> > +	/* stub */
> > +	return true;
> > +}
> > +
> > +/**
> > + * iecm_vport_singleq_napi_poll - NAPI handler
> > + * @napi: struct from which you get q_vector
> > + * @budget: budget provided by stack
> > + */
> > +int iecm_vport_singleq_napi_poll(struct napi_struct *napi, int budget)
> > +{
> > +	/* stub */
> > +	return 0;
> > +}
> > diff --git a/drivers/net/ethernet/intel/iecm/iecm_txrx.c
> b/drivers/net/ethernet/intel/iecm/iecm_txrx.c
> > index bb7f5830cffb..85e88a30370d 100644
> > --- a/drivers/net/ethernet/intel/iecm/iecm_txrx.c
> > +++ b/drivers/net/ethernet/intel/iecm/iecm_txrx.c
> > @@ -219,20 +219,318 @@ const struct iecm_rx_ptype_decoded
> iecm_ptype_lookup[IECM_RX_MAX_PTYPE] = {
> >  EXPORT_SYMBOL(iecm_ptype_lookup);
> >
> >  /**
> > - * iecm_vport_intr_clean_queues - MSIX mode Interrupt Handler
> > - * @irq: interrupt number
> > - * @data: pointer to a q_vector
> > + * iecm_tx_buf_rel - Release a Tx buffer
> > + * @tx_q: the queue that owns the buffer
> > + * @tx_buf: the buffer to free
> > + */
> > +void iecm_tx_buf_rel(struct iecm_queue *tx_q, struct iecm_tx_buf *tx_buf)
> > +{
> > +	if (tx_buf->skb) {
> > +		if (dma_unmap_len(tx_buf, len))
> > +			dma_unmap_single(tx_q->dev,
> > +					 dma_unmap_addr(tx_buf, dma),
> > +					 dma_unmap_len(tx_buf, len),
> > +					 DMA_TO_DEVICE);
> > +		dev_kfree_skb_any(tx_buf->skb);
> > +	} else if (dma_unmap_len(tx_buf, len)) {
> > +		dma_unmap_page(tx_q->dev,
> > +			       dma_unmap_addr(tx_buf, dma),
> > +			       dma_unmap_len(tx_buf, len),
> > +			       DMA_TO_DEVICE);
> > +	}
> > +
> > +	tx_buf->next_to_watch = NULL;
> > +	tx_buf->skb = NULL;
> > +	dma_unmap_len_set(tx_buf, len, 0);
> > +}
> > +
> > +/**
> > + * iecm_tx_buf_rel_all - Free any empty Tx buffers
> > + * @txq: queue to be cleaned
> > + */
> > +static void iecm_tx_buf_rel_all(struct iecm_queue *txq)
> > +{
> > +	u16 i;
> > +
> > +	/* Buffers already cleared, nothing to do */
> > +	if (!txq->tx_buf)
> > +		return;
> > +
> > +	/* Free all the Tx buffer sk_buffs */
> > +	for (i = 0; i < txq->desc_count; i++)
> > +		iecm_tx_buf_rel(txq, &txq->tx_buf[i]);
> > +
> > +	kfree(txq->tx_buf);
> > +	txq->tx_buf = NULL;
> > +
> > +	if (txq->buf_stack.bufs) {
> > +		for (i = 0; i < txq->buf_stack.size; i++) {
> > +			iecm_tx_buf_rel(txq, txq->buf_stack.bufs[i]);
> > +			kfree(txq->buf_stack.bufs[i]);
> > +		}
> > +		kfree(txq->buf_stack.bufs);
> > +		txq->buf_stack.bufs = NULL;
> > +	}
> 
> 	if (!txq->buf.stack.bufs)
> 		return;
> 
> 	for (...
> 
> -1 indent level.
> 

Will fix.

> > +}
> > +
> > +/**
> > + * iecm_tx_desc_rel - Free Tx resources per queue
> > + * @txq: Tx descriptor ring for a specific queue
> > + * @bufq: buffer q or completion q
> >   *
> > + * Free all transmit software resources
> >   */
> > -irqreturn_t
> > -iecm_vport_intr_clean_queues(int __always_unused irq, void *data)
> > +static void iecm_tx_desc_rel(struct iecm_queue *txq, bool bufq)
> >  {
> > -	struct iecm_q_vector *q_vector = (struct iecm_q_vector *)data;
> > +	if (bufq) {
> > +		iecm_tx_buf_rel_all(txq);
> > +		netdev_tx_reset_queue(netdev_get_tx_queue(txq->vport-
> >netdev,
> > +							  txq->idx));
> > +	}
> >
> > -	q_vector->total_events++;
> > -	napi_schedule(&q_vector->napi);
> > +	if (txq->desc_ring) {
> > +		dmam_free_coherent(txq->dev, txq->size,
> > +				   txq->desc_ring, txq->dma);
> > +		txq->desc_ring = NULL;
> > +		txq->next_to_alloc = 0;
> > +		txq->next_to_use = 0;
> > +		txq->next_to_clean = 0;
> > +	}
> 
> 	Same here, !desc_ring -> return immediately.
> 

Will fix

> > +}
> >
> > -	return IRQ_HANDLED;
> > +/**
> > + * iecm_tx_desc_rel_all - Free Tx Resources for All Queues
> > + * @vport: virtual port structure
> > + *
> > + * Free all transmit software resources
> > + */
> > +static void iecm_tx_desc_rel_all(struct iecm_vport *vport)
> > +{
> > +	int i, j;
> > +
> > +	if (!vport->txq_grps)
> > +		return;
> > +
> > +	for (i = 0; i < vport->num_txq_grp; i++) {
> > +		struct iecm_txq_group *txq_grp = &vport->txq_grps[i];
> > +
> > +		for (j = 0; j < txq_grp->num_txq; j++)
> > +			iecm_tx_desc_rel(txq_grp->txqs[j], true);
> > +		if (iecm_is_queue_model_split(vport->txq_model))
> > +			iecm_tx_desc_rel(txq_grp->complq, false);
> > +	}
> > +}
> > +
> > +/**
> > + * iecm_tx_buf_alloc_all - Allocate memory for all buffer resources
> > + * @tx_q: queue for which the buffers are allocated
> > + *
> > + * Returns 0 on success, negative on failure
> > + */
> > +static int iecm_tx_buf_alloc_all(struct iecm_queue *tx_q)
> > +{
> > +	int buf_size;
> > +	int i = 0;
> > +
> > +	/* Allocate book keeping buffers only. Buffers to be supplied to HW
> > +	 * are allocated by kernel network stack and received as part of skb
> > +	 */
> > +	buf_size = sizeof(struct iecm_tx_buf) * tx_q->desc_count;
> > +	tx_q->tx_buf = kzalloc(buf_size, GFP_KERNEL);
> > +	if (!tx_q->tx_buf)
> > +		return -ENOMEM;
> > +
> > +	/* Initialize tx buf stack for out-of-order completions if
> > +	 * flow scheduling offload is enabled
> > +	 */
> > +	tx_q->buf_stack.bufs =
> > +		kcalloc(tx_q->desc_count, sizeof(struct iecm_tx_buf *),
> > +			GFP_KERNEL);
> > +	if (!tx_q->buf_stack.bufs)
> > +		return -ENOMEM;
> > +
> > +	tx_q->buf_stack.size = tx_q->desc_count;
> > +	tx_q->buf_stack.top = tx_q->desc_count;
> > +
> > +	for (i = 0; i < tx_q->desc_count; i++) {
> > +		tx_q->buf_stack.bufs[i] = kzalloc(sizeof(*tx_q-
> >buf_stack.bufs[i]),
> > +						  GFP_KERNEL);
> > +		if (!tx_q->buf_stack.bufs[i])
> > +			return -ENOMEM;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +/**
> > + * iecm_tx_desc_alloc - Allocate the Tx descriptors
> > + * @tx_q: the tx ring to set up
> > + * @bufq: buffer or completion queue
> > + *
> > + * Returns 0 on success, negative on failure
> > + */
> > +static int iecm_tx_desc_alloc(struct iecm_queue *tx_q, bool bufq)
> > +{
> > +	struct device *dev = tx_q->dev;
> > +	int err = 0;
> > +
> > +	if (bufq) {
> > +		err = iecm_tx_buf_alloc_all(tx_q);
> > +		if (err)
> > +			goto err_alloc;
> > +		tx_q->size = tx_q->desc_count *
> > +				sizeof(struct iecm_base_tx_desc);
> > +	} else {
> > +		tx_q->size = tx_q->desc_count *
> > +				sizeof(struct iecm_splitq_tx_compl_desc);
> > +	}
> > +
> > +	/* Allocate descriptors also round up to nearest 4K */
> > +	tx_q->size = ALIGN(tx_q->size, 4096);
> > +	tx_q->desc_ring = dmam_alloc_coherent(dev, tx_q->size, &tx_q->dma,
> > +					      GFP_KERNEL);
> > +	if (!tx_q->desc_ring) {
> > +		dev_info(dev, "Unable to allocate memory for the Tx descriptor
> ring, size=%d\n",
> > +			 tx_q->size);
> > +		err = -ENOMEM;
> > +		goto err_alloc;
> > +	}
> > +
> > +	tx_q->next_to_alloc = 0;
> > +	tx_q->next_to_use = 0;
> > +	tx_q->next_to_clean = 0;
> > +	set_bit(__IECM_Q_GEN_CHK, tx_q->flags);
> > +
> > +err_alloc:
> > +	if (err)
> > +		iecm_tx_desc_rel(tx_q, bufq);
> > +	return err;
> > +}
> > +
> > +/**
> > + * iecm_tx_desc_alloc_all - allocate all queues Tx resources
> > + * @vport: virtual port private structure
> > + *
> > + * Returns 0 on success, negative on failure
> > + */
> > +static int iecm_tx_desc_alloc_all(struct iecm_vport *vport)
> > +{
> > +	struct pci_dev *pdev = vport->adapter->pdev;
> > +	int err = 0;
> > +	int i, j;
> > +
> > +	/* Setup buffer queues. In single queue model buffer queues and
> > +	 * completion queues will be same
> > +	 */
> > +	for (i = 0; i < vport->num_txq_grp; i++) {
> > +		for (j = 0; j < vport->txq_grps[i].num_txq; j++) {
> > +			err = iecm_tx_desc_alloc(vport->txq_grps[i].txqs[j],
> > +						 true);
> > +			if (err) {
> > +				dev_err(&pdev->dev,
> > +					"Allocation for Tx Queue %u failed\n",
> > +					i);
> > +				goto err_out;
> > +			}
> > +		}
> > +
> > +		if (iecm_is_queue_model_split(vport->txq_model)) {
> > +			/* Setup completion queues */
> > +			err = iecm_tx_desc_alloc(vport->txq_grps[i].complq,
> > +						 false);
> > +			if (err) {
> > +				dev_err(&pdev->dev,
> > +					"Allocation for Tx Completion Queue
> %u failed\n",
> > +					i);
> > +				goto err_out;
> > +			}
> > +		}
> > +	}
> > +err_out:
> > +	if (err)
> > +		iecm_tx_desc_rel_all(vport);
> > +	return err;
> > +}
> > +
> > +/**
> > + * iecm_txq_group_rel - Release all resources for txq groups
> > + * @vport: vport to release txq groups on
> > + */
> > +static void iecm_txq_group_rel(struct iecm_vport *vport)
> > +{
> > +	struct iecm_txq_group *txq_grp;
> > +	int i, j, num_txq;
> > +
> > +	if (vport->txq_grps) {
> > +		for (i = 0; i < vport->num_txq_grp; i++) {
> > +			txq_grp = &vport->txq_grps[i];
> > +			num_txq = txq_grp->num_txq;
> > +
> > +			for (j = 0; j < num_txq; j++) {
> > +				kfree(txq_grp->txqs[j]);
> > +				txq_grp->txqs[j] = NULL;
> > +			}
> > +			kfree(txq_grp->complq);
> > +			txq_grp->complq = NULL;
> > +		}
> > +		kfree(vport->txq_grps);
> > +		vport->txq_grps = NULL;
> > +	}
> 
> Same here, !txq_grps -> return.
> 

Will fix

> > +}
> > +
> > +/**
> > + * iecm_vport_queue_grp_rel_all - Release all queue groups
> > + * @vport: vport to release queue groups for
> > + */
> > +static void iecm_vport_queue_grp_rel_all(struct iecm_vport *vport)
> > +{
> > +	iecm_txq_group_rel(vport);
> > +}
> > +
> > +/**
> > + * iecm_vport_queues_rel - Free memory for all queues
> > + * @vport: virtual port
> > + *
> > + * Free the memory allocated for queues associated to a vport
> > + */
> > +void iecm_vport_queues_rel(struct iecm_vport *vport)
> > +{
> > +	iecm_tx_desc_rel_all(vport);
> > +	iecm_vport_queue_grp_rel_all(vport);
> > +
> > +	kfree(vport->txqs);
> > +	vport->txqs = NULL;
> > +}
> > +
> > +/**
> > + * iecm_vport_init_fast_path_txqs - Initialize fast path txq array
> > + * @vport: vport to init txqs on
> > + *
> > + * We get a queue index from skb->queue_mapping and we need a fast way
> to
> > + * dereference the queue from queue groups.  This allows us to quickly pull a
> > + * txq based on a queue index.
> > + *
> > + * Returns 0 on success, negative on failure
> > + */
> > +static int iecm_vport_init_fast_path_txqs(struct iecm_vport *vport)
> > +{
> > +	int i, j, k = 0;
> > +
> > +	vport->txqs = kcalloc(vport->num_txq, sizeof(struct iecm_queue *),
> > +			      GFP_KERNEL);
> > +
> > +	if (!vport->txqs)
> > +		return -ENOMEM;
> > +
> > +	for (i = 0; i < vport->num_txq_grp; i++) {
> > +		struct iecm_txq_group *tx_grp = &vport->txq_grps[i];
> > +
> > +		for (j = 0; j < tx_grp->num_txq; j++, k++) {
> > +			vport->txqs[k] = tx_grp->txqs[j];
> > +			vport->txqs[k]->idx = k;
> > +		}
> > +	}
> > +	return 0;
> >  }
> >
> >  /**
> > @@ -382,6 +680,26 @@ void iecm_vport_calc_num_q_groups(struct
> iecm_vport *vport)
> >  }
> >  EXPORT_SYMBOL(iecm_vport_calc_num_q_groups);
> >
> > +/**
> > + * iecm_vport_calc_numq_per_grp - Calculate number of queues per group
> > + * @vport: vport to calculate queues for
> > + * @num_txq: int return parameter
> > + * @num_rxq: int return parameter
> > + */
> > +static void iecm_vport_calc_numq_per_grp(struct iecm_vport *vport,
> > +					 int *num_txq, int *num_rxq)
> > +{
> > +	if (iecm_is_queue_model_split(vport->txq_model))
> > +		*num_txq = IECM_DFLT_SPLITQ_TXQ_PER_GROUP;
> > +	else
> > +		*num_txq = vport->num_txq;
> > +
> > +	if (iecm_is_queue_model_split(vport->rxq_model))
> > +		*num_rxq = IECM_DFLT_SPLITQ_RXQ_PER_GROUP;
> > +	else
> > +		*num_rxq = vport->num_rxq;
> > +}
> > +
> >  /**
> >   * iecm_vport_calc_num_q_vec - Calculate total number of vectors required
> for
> >   * this vport
> > @@ -396,3 +714,949 @@ void iecm_vport_calc_num_q_vec(struct
> iecm_vport *vport)
> >  		vport->num_q_vectors = vport->num_txq;
> >  }
> >  EXPORT_SYMBOL(iecm_vport_calc_num_q_vec);
> > +
> > +/**
> > + * iecm_set_vlan_tag_loc - set the tag location for a tx/rx queue
> > + * @adapter: adapter structure
> > + * @q: tx/rx queue to set tag location for
> > + *
> > + */
> > +static void iecm_set_vlan_tag_loc(struct iecm_adapter *adapter,
> > +				  struct iecm_queue *q)
> > +{
> > +	if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
> VIRTCHNL2_CAP_VLAN)) {
> > +		struct virtchnl_vlan_supported_caps *insertion_support;
> > +
> > +		insertion_support =
> > +				&adapter->vlan_caps-
> >offloads.insertion_support;
> > +		if (insertion_support->outer) {
> > +			if (insertion_support->outer &
> > +			    VIRTCHNL_VLAN_TAG_LOCATION_L2TAG1)
> > +				set_bit(__IECM_Q_VLAN_TAG_LOC_L2TAG1,
> > +					q->flags);
> > +			else if (insertion_support->outer &
> > +				 VIRTCHNL_VLAN_TAG_LOCATION_L2TAG2)
> > +				set_bit(__IECM_Q_VLAN_TAG_LOC_L2TAG2,
> > +					q->flags);
> > +		} else if (insertion_support->inner) {
> > +			if (insertion_support->inner &
> > +			    VIRTCHNL_VLAN_TAG_LOCATION_L2TAG1)
> > +				set_bit(__IECM_Q_VLAN_TAG_LOC_L2TAG1,
> > +					q->flags);
> > +			else if (insertion_support->inner &
> > +				 VIRTCHNL_VLAN_TAG_LOCATION_L2TAG2)
> > +				set_bit(__IECM_Q_VLAN_TAG_LOC_L2TAG2,
> > +					q->flags);
> > +		}
> > +	} else if (iecm_is_cap_ena(adapter, IECM_BASE_CAPS,
> > +				   VIRTCHNL2_CAP_VLAN)) {
> > +		set_bit(__IECM_Q_VLAN_TAG_LOC_L2TAG1, q->flags);
> > +	}
> 
> If !ena -> set_bit() + return, -1 indent.
> 

I'm afraid I'm not following here.

> > +}
> > +
> > +/**
> > + * iecm_txq_group_alloc - Allocate all txq group resources
> > + * @vport: vport to allocate txq groups for
> > + * @num_txq: number of txqs to allocate for each group
> > + *
> > + * Returns 0 on success, negative on failure
> > + */
> > +static int iecm_txq_group_alloc(struct iecm_vport *vport, int num_txq)
> > +{
> > +	int err = 0, i;
> > +
> > +	vport->txq_grps = kcalloc(vport->num_txq_grp,
> > +				  sizeof(*vport->txq_grps), GFP_KERNEL);
> > +	if (!vport->txq_grps)
> > +		return -ENOMEM;
> > +
> > +	for (i = 0; i < vport->num_txq_grp; i++) {
> > +		struct iecm_txq_group *tx_qgrp = &vport->txq_grps[i];
> > +		int j;
> > +
> > +		tx_qgrp->vport = vport;
> > +		tx_qgrp->num_txq = num_txq;
> > +
> > +		for (j = 0; j < tx_qgrp->num_txq; j++) {
> > +			tx_qgrp->txqs[j] = kzalloc(sizeof(*tx_qgrp->txqs[j]),
> > +						   GFP_KERNEL);
> > +			if (!tx_qgrp->txqs[j]) {
> > +				err = -ENOMEM;
> > +				goto err_alloc;
> > +			}
> > +		}
> > +
> > +		for (j = 0; j < tx_qgrp->num_txq; j++) {
> > +			struct iecm_queue *q = tx_qgrp->txqs[j];
> > +
> > +			q->dev = &vport->adapter->pdev->dev;
> > +			q->desc_count = vport->txq_desc_count;
> > +			q->tx_max_bufs =
> > +				vport->adapter-
> >dev_ops.vc_ops.get_max_tx_bufs(vport->adapter);
> > +			q->vport = vport;
> > +			q->txq_grp = tx_qgrp;
> > +			hash_init(q->sched_buf_hash);
> > +
> > +			if (!iecm_is_cap_ena(vport->adapter,
> > +					     IECM_OTHER_CAPS,
> > +					     VIRTCHNL2_CAP_SPLITQ_QSCHED))
> > +				set_bit(__IECM_Q_FLOW_SCH_EN, q->flags);
> > +			iecm_set_vlan_tag_loc(vport->adapter, q);
> > +		}
> > +
> > +		if (!iecm_is_queue_model_split(vport->txq_model))
> > +			continue;
> > +
> > +		tx_qgrp->complq = kcalloc(IECM_COMPLQ_PER_GROUP,
> > +					  sizeof(*tx_qgrp->complq),
> > +					  GFP_KERNEL);
> > +		if (!tx_qgrp->complq) {
> > +			err = -ENOMEM;
> > +			goto err_alloc;
> > +		}
> > +
> > +		tx_qgrp->complq->dev = &vport->adapter->pdev->dev;
> > +		tx_qgrp->complq->desc_count = vport->complq_desc_count;
> > +		tx_qgrp->complq->vport = vport;
> > +		tx_qgrp->complq->txq_grp = tx_qgrp;
> > +	}
> > +
> > +err_alloc:
> > +	if (err)
> > +		iecm_txq_group_rel(vport);
> > +	return err;
> > +}
> > +
> > +/**
> > + * iecm_vport_queue_grp_alloc_all - Allocate all queue groups/resources
> > + * @vport: vport with qgrps to allocate
> > + *
> > + * Returns 0 on success, negative on failure
> > + */
> > +static int iecm_vport_queue_grp_alloc_all(struct iecm_vport *vport)
> > +{
> > +	int num_txq, num_rxq;
> > +	int err;
> > +
> > +	iecm_vport_calc_numq_per_grp(vport, &num_txq, &num_rxq);
> > +
> > +	err = iecm_txq_group_alloc(vport, num_txq);
> > +	if (err)
> > +		iecm_vport_queue_grp_rel_all(vport);
> > +	return err;
> > +}
> > +
> > +/**
> > + * iecm_vport_queues_alloc - Allocate memory for all queues
> > + * @vport: virtual port
> > + *
> > + * Allocate memory for queues associated with a vport.  Returns 0 on
> success,
> > + * negative on failure.
> > + */
> > +int iecm_vport_queues_alloc(struct iecm_vport *vport)
> > +{
> > +	int err;
> > +	int i;
> > +
> > +	err = iecm_vport_queue_grp_alloc_all(vport);
> > +	if (err)
> > +		goto err_out;
> > +
> > +	err = iecm_tx_desc_alloc_all(vport);
> > +	if (err)
> > +		goto err_out;
> > +
> > +	err = iecm_vport_init_fast_path_txqs(vport);
> > +	if (err)
> > +		goto err_out;
> > +
> > +	/* Initialize flow scheduling for queues that were requested
> > +	 * before the interface was brought up
> > +	 */
> > +	for (i = 0; i < vport->num_txq; i++) {
> > +		if (test_bit(i, vport->adapter->config_data.etf_qenable)) {
> > +			set_bit(__IECM_Q_FLOW_SCH_EN, vport->txqs[i]-
> >flags);
> > +			set_bit(__IECM_Q_ETF_EN, vport->txqs[i]->flags);
> > +		}
> > +	}
> 
> Redundant braces for the for-loop.
> 

Will not fix.

> > +
> > +	return 0;
> > +err_out:
> > +	iecm_vport_queues_rel(vport);
> > +	return err;
> > +}
> > +
> > +/**
> > + * iecm_vport_intr_clean_queues - MSIX mode Interrupt Handler
> > + * @irq: interrupt number
> > + * @data: pointer to a q_vector
> > + *
> > + */
> > +irqreturn_t
> > +iecm_vport_intr_clean_queues(int __always_unused irq, void *data)
> > +{
> > +	struct iecm_q_vector *q_vector = (struct iecm_q_vector *)data;
> > +
> > +	q_vector->total_events++;
> > +	napi_schedule(&q_vector->napi);
> > +
> > +	return IRQ_HANDLED;
> > +}
> > +
> > +/**
> > + * iecm_vport_intr_napi_del_all - Unregister napi for all q_vectors in vport
> > + * @vport: virtual port structure
> > + *
> > + */
> > +static void iecm_vport_intr_napi_del_all(struct iecm_vport *vport)
> > +{
> > +	u16 v_idx;
> > +
> > +	for (v_idx = 0; v_idx < vport->num_q_vectors; v_idx++) {
> > +		struct iecm_q_vector *q_vector = &vport->q_vectors[v_idx];
> > +
> > +		netif_napi_del(&q_vector->napi);
> 
> One-liner:
> 
> 		netif_napi_del(&vport->q_vectors[v_idx].napi);
> 

Will fix.

> > +	}
> > +}
> > +
> > +/**
> > + * iecm_vport_intr_napi_dis_all - Disable NAPI for all q_vectors in the vport
> > + * @vport: main vport structure
> > + */
> > +static void iecm_vport_intr_napi_dis_all(struct iecm_vport *vport)
> > +{
> > +	int q_idx;
> > +
> > +	if (!vport->netdev)
> > +		return;
> > +
> > +	for (q_idx = 0; q_idx < vport->num_q_vectors; q_idx++) {
> > +		struct iecm_q_vector *q_vector = &vport->q_vectors[q_idx];
> > +
> > +		napi_disable(&q_vector->napi);
> 
> Same here.
> 
> > +	}
> > +}
> > +
> > +/**
> > + * iecm_vport_intr_rel - Free memory allocated for interrupt vectors
> > + * @vport: virtual port
> > + *
> > + * Free the memory allocated for interrupt vectors  associated to a vport
> > + */
> > +void iecm_vport_intr_rel(struct iecm_vport *vport)
> > +{
> > +	int i, j, v_idx;
> > +
> > +	if (!vport->netdev)
> > +		return;
> > +
> > +	for (v_idx = 0; v_idx < vport->num_q_vectors; v_idx++) {
> > +		struct iecm_q_vector *q_vector = &vport->q_vectors[v_idx];
> > +
> > +		kfree(q_vector->bufq);
> > +		q_vector->bufq = NULL;
> > +		kfree(q_vector->tx);
> > +		q_vector->tx = NULL;
> > +		kfree(q_vector->rx);
> > +		q_vector->rx = NULL;
> > +	}
> > +
> > +	/* Clean up the mapping of queues to vectors */
> > +	for (i = 0; i < vport->num_rxq_grp; i++) {
> > +		struct iecm_rxq_group *rx_qgrp = &vport->rxq_grps[i];
> > +
> > +		if (iecm_is_queue_model_split(vport->rxq_model)) {
> > +			for (j = 0; j < rx_qgrp->splitq.num_rxq_sets; j++)
> > +				rx_qgrp->splitq.rxq_sets[j]->rxq.q_vector =
> > +
> NULL;
> > +		} else {
> > +			for (j = 0; j < rx_qgrp->singleq.num_rxq; j++)
> > +				rx_qgrp->singleq.rxqs[j]->q_vector = NULL;
> > +		}
> > +	}
> > +
> > +	if (iecm_is_queue_model_split(vport->txq_model)) {
> > +		for (i = 0; i < vport->num_txq_grp; i++)
> > +			vport->txq_grps[i].complq->q_vector = NULL;
> > +	} else {
> > +		for (i = 0; i < vport->num_txq_grp; i++) {
> > +			for (j = 0; j < vport->txq_grps[i].num_txq; j++)
> > +				vport->txq_grps[i].txqs[j]->q_vector = NULL;
> > +		}
> 
> Redundant braces.
> 

Will not fix.

> > +	}
> > +
> > +	kfree(vport->q_vectors);
> > +	vport->q_vectors = NULL;
> > +}
> > +
> > +/**
> > + * iecm_vport_intr_rel_irq - Free the IRQ association with the OS
> > + * @vport: main vport structure
> > + */
> > +static void iecm_vport_intr_rel_irq(struct iecm_vport *vport)
> > +{
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	int vector;
> > +
> > +	for (vector = 0; vector < vport->num_q_vectors; vector++) {
> > +		struct iecm_q_vector *q_vector = &vport->q_vectors[vector];
> > +		int irq_num, vidx;
> > +
> > +		/* free only the irqs that were actually requested */
> > +		if (!q_vector)
> > +			continue;
> > +
> > +		vidx = vector + vport->q_vector_base;
> > +		irq_num = adapter->msix_entries[vidx].vector;
> > +
> > +		/* clear the affinity_mask in the IRQ descriptor */
> > +		irq_set_affinity_hint(irq_num, NULL);
> > +		free_irq(irq_num, q_vector);
> > +	}
> > +}
> > +
> > +/**
> > + * iecm_vport_intr_dis_irq_all - Disable each interrupt
> > + * @vport: main vport structure
> > + */
> > +void iecm_vport_intr_dis_irq_all(struct iecm_vport *vport)
> > +{
> > +	struct iecm_q_vector *q_vector = vport->q_vectors;
> > +	struct iecm_hw *hw = &vport->adapter->hw;
> > +	int q_idx;
> > +
> > +	for (q_idx = 0; q_idx < vport->num_q_vectors; q_idx++)
> > +		wr32(hw, q_vector[q_idx].intr_reg.dyn_ctl, 0);
> > +}
> > +
> > +/**
> > + * iecm_vport_intr_buildreg_itr - Enable default interrupt generation settings
> > + * @q_vector: pointer to q_vector
> > + * @type: itr index
> > + * @itr: itr value
> > + */
> > +static u32 iecm_vport_intr_buildreg_itr(struct iecm_q_vector *q_vector,
> > +					const int type, u16 itr)
> > +{
> > +	u32 itr_val;
> > +
> > +	itr &= IECM_ITR_MASK;
> > +	/* Don't clear PBA because that can cause lost interrupts that
> > +	 * came in while we were cleaning/polling
> > +	 */
> > +	itr_val = q_vector->intr_reg.dyn_ctl_intena_m |
> > +		  (type << q_vector->intr_reg.dyn_ctl_itridx_s) |
> > +		  (itr << (q_vector->intr_reg.dyn_ctl_intrvl_s - 1));
> > +
> > +	return itr_val;
> > +}
> > +
> > +/**
> > + * iecm_net_dim - Update net DIM algorithm
> > + * @q_vector: the vector associated with the interrupt
> > + *
> > + * Create a DIM sample and notify net_dim() so that it can possibly decide
> > + * a new ITR value based on incoming packets, bytes, and interrupts.
> > + *
> > + * This function is a no-op if the queue is not configured to dynamic ITR.
> > + */
> > +static void iecm_net_dim(struct iecm_q_vector *q_vector)
> > +{
> > +	if (IECM_ITR_IS_DYNAMIC(q_vector->tx_intr_mode)) {
> > +		struct dim_sample dim_sample = {};
> > +		u64 packets = 0, bytes = 0;
> > +		int i;
> > +
> > +		for (i = 0; i < q_vector->num_txq; i++) {
> > +			packets += q_vector->tx[i]->q_stats.tx.packets;
> > +			bytes += q_vector->tx[i]->q_stats.tx.bytes;
> > +		}
> > +
> > +		dim_update_sample(q_vector->total_events, packets, bytes,
> > +				  &dim_sample);
> > +		net_dim(&q_vector->tx_dim, dim_sample);
> > +	}
> 
> 	if (!dynamic_tx)
> 		goto check_rx;
> 
> -1 level.
> 
> > +
> > +	if (IECM_ITR_IS_DYNAMIC(q_vector->rx_intr_mode)) {
> > +		struct dim_sample dim_sample = {};
> > +		u64 packets = 0, bytes = 0;
> > +		int i;
> > +
> > +		for (i = 0; i < q_vector->num_rxq; i++) {
> > +			packets += q_vector->rx[i]->q_stats.rx.packets;
> > +			bytes += q_vector->rx[i]->q_stats.rx.bytes;
> > +		}
> > +
> > +		dim_update_sample(q_vector->total_events, packets, bytes,
> > +				  &dim_sample);
> > +		net_dim(&q_vector->rx_dim, dim_sample);
> > +	}
> 
> 	if (!dynamic_rx)
> 		return;
> 
> -1 as well.
> 

I'm not entirely convinced this is better or more readable but I guess will fix.

> > +}
> > +
> > +/**
> > + * iecm_vport_intr_update_itr_ena_irq - Update itr and re-enable MSIX
> interrupt
> > + * @q_vector: q_vector for which itr is being updated and interrupt enabled
> > + *
> > + * Update the net_dim() algorithm and re-enable the interrupt associated
> with
> > + * this vector.
> > + */
> > +void iecm_vport_intr_update_itr_ena_irq(struct iecm_q_vector *q_vector)
> > +{
> > +	struct iecm_hw *hw = &q_vector->vport->adapter->hw;
> > +	u32 intval;
> > +
> > +	/* net_dim() updates ITR out-of-band using a work item */
> > +	iecm_net_dim(q_vector);
> > +
> > +	intval = iecm_vport_intr_buildreg_itr(q_vector,
> > +					      VIRTCHNL2_ITR_IDX_NO_ITR, 0);
> > +
> > +	wr32(hw, q_vector->intr_reg.dyn_ctl, intval);
> > +}
> > +
> > +/**
> > + * iecm_vport_intr_req_irq - get MSI-X vectors from the OS for the vport
> > + * @vport: main vport structure
> > + * @basename: name for the vector
> > + */
> > +static int
> > +iecm_vport_intr_req_irq(struct iecm_vport *vport, char *basename)
> > +{
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	int vector, err, irq_num, vidx;
> > +
> > +	for (vector = 0; vector < vport->num_q_vectors; vector++) {
> > +		struct iecm_q_vector *q_vector = &vport->q_vectors[vector];
> > +
> > +		vidx = vector + vport->q_vector_base;
> > +		irq_num = adapter->msix_entries[vidx].vector;
> > +
> > +		snprintf(q_vector->name, sizeof(q_vector->name) - 1,
> > +			 "%s-%s-%d", basename, "TxRx", vidx);
> > +
> > +		err = request_irq(irq_num, vport->irq_q_handler, 0,
> > +				  q_vector->name, q_vector);
> > +		if (err) {
> > +			netdev_err(vport->netdev,
> > +				   "Request_irq failed, error: %d\n", err);
> > +			goto free_q_irqs;
> > +		}
> > +		/* assign the mask for this irq */
> > +		irq_set_affinity_hint(irq_num, &q_vector->affinity_mask);
> > +	}
> > +
> > +	return 0;
> > +
> > +free_q_irqs:
> > +	while (vector) {
> > +		vector--;
> > +		vidx = vector + vport->q_vector_base;
> > +		irq_num = adapter->msix_entries[vidx].vector;
> > +		free_irq(irq_num, &vport->q_vectors[vector]);
> > +	}
> > +	return err;
> > +}
> > +
> > +/**
> > + * iecm_vport_intr_write_itr - Write ITR value to the ITR register
> > + * @q_vector: q_vector structure
> > + * @itr: Interrupt throttling rate
> > + * @tx: Tx or Rx ITR
> > + */
> > +void iecm_vport_intr_write_itr(struct iecm_q_vector *q_vector, u16 itr, bool
> tx)
> > +{
> > +	struct iecm_hw *hw = &q_vector->vport->adapter->hw;
> > +	struct iecm_intr_reg *intr_reg;
> > +
> > +	if (tx && !q_vector->tx)
> > +		return;
> > +	else if (!tx && !q_vector->rx)
> > +		return;
> 
> 	if ((tx && !q_vector->tx) || (!tx && !q_vector->rx))
> 		return;
> 
> Fits into 79 cols and looks more elegant-ish.
> 

Yours is a bit harder for humans to grok, would be prefer to keep this as-is.

> > +
> > +	intr_reg = &q_vector->intr_reg;
> > +	wr32(hw, tx ? intr_reg->tx_itr : intr_reg->rx_itr,
> > +	     ITR_REG_ALIGN(itr) >> IECM_ITR_GRAN_S);
> > +}
> > +
> > +/**
> > + * iecm_vport_intr_ena_irq_all - Enable IRQ for the given vport
> > + * @vport: main vport structure
> > + */
> > +void iecm_vport_intr_ena_irq_all(struct iecm_vport *vport)
> > +{
> > +	int q_idx;
> > +
> > +	for (q_idx = 0; q_idx < vport->num_q_vectors; q_idx++) {
> > +		struct iecm_q_vector *q_vector = &vport->q_vectors[q_idx];
> > +
> > +		if (q_vector->num_txq || q_vector->num_rxq) {
> > +			/* Write the default ITR values */
> > +			iecm_vport_intr_write_itr(q_vector,
> > +						  q_vector->rx_itr_value,
> > +						  false);
> > +			iecm_vport_intr_write_itr(q_vector,
> > +						  q_vector->tx_itr_value,
> > +						  true);
> > +			iecm_vport_intr_update_itr_ena_irq(q_vector);
> > +		}
> 
> 		if (!num_txq && !num_rxq)
> 			continue;
> 
> -1 level.
> 

Will fix

> > +	}
> > +}
> > +
> > +/**
> > + * iecm_vport_intr_deinit - Release all vector associations for the vport
> > + * @vport: main vport structure
> > + */
> > +void iecm_vport_intr_deinit(struct iecm_vport *vport)
> > +{
> > +	iecm_vport_intr_napi_dis_all(vport);
> > +	iecm_vport_intr_napi_del_all(vport);
> > +	iecm_vport_intr_dis_irq_all(vport);
> > +	iecm_vport_intr_rel_irq(vport);
> > +}
> > +
> > +/**
> > + * iecm_tx_dim_work - Call back from the stack
> > + * @work: work queue structure
> > + */
> > +static void iecm_tx_dim_work(struct work_struct *work)
> > +{
> > +	struct iecm_q_vector *q_vector;
> > +	struct iecm_vport *vport;
> > +	struct dim *dim;
> > +	u16 itr;
> > +
> > +	dim = container_of(work, struct dim, work);
> > +	q_vector = container_of(dim, struct iecm_q_vector, tx_dim);
> > +	vport = q_vector->vport;
> > +
> > +	if (dim->profile_ix >= ARRAY_SIZE(vport->tx_itr_profile))
> > +		dim->profile_ix = ARRAY_SIZE(vport->tx_itr_profile) - 1;
> > +
> > +	/* look up the values in our local table */
> > +	itr = vport->tx_itr_profile[dim->profile_ix];
> > +
> > +	iecm_vport_intr_write_itr(q_vector, itr, true);
> > +
> > +	dim->state = DIM_START_MEASURE;
> > +}
> > +
> > +/**
> > + * iecm_rx_dim_work - Call back from the stack
> > + * @work: work queue structure
> > + */
> > +static void iecm_rx_dim_work(struct work_struct *work)
> > +{
> > +	struct iecm_q_vector *q_vector;
> > +	struct iecm_vport *vport;
> > +	struct dim *dim;
> > +	u16 itr;
> > +
> > +	dim = container_of(work, struct dim, work);
> > +	q_vector = container_of(dim, struct iecm_q_vector, rx_dim);
> > +	vport = q_vector->vport;
> > +
> > +	if (dim->profile_ix >= ARRAY_SIZE(vport->rx_itr_profile))
> > +		dim->profile_ix = ARRAY_SIZE(vport->rx_itr_profile) - 1;
> > +
> > +	/* look up the values in our local table */
> > +	itr = vport->rx_itr_profile[dim->profile_ix];
> > +
> > +	iecm_vport_intr_write_itr(q_vector, itr, false);
> > +
> > +	dim->state = DIM_START_MEASURE;
> > +}
> > +
> > +/**
> > + * iecm_vport_intr_napi_ena_all - Enable NAPI for all q_vectors in the vport
> > + * @vport: main vport structure
> > + */
> > +static void
> > +iecm_vport_intr_napi_ena_all(struct iecm_vport *vport)
> > +{
> > +	int q_idx;
> > +
> > +	if (!vport->netdev)
> > +		return;
> > +
> > +	for (q_idx = 0; q_idx < vport->num_q_vectors; q_idx++) {
> > +		struct iecm_q_vector *q_vector = &vport->q_vectors[q_idx];
> > +
> > +		INIT_WORK(&q_vector->tx_dim.work, iecm_tx_dim_work);
> > +		q_vector->tx_dim.mode =
> DIM_CQ_PERIOD_MODE_START_FROM_EQE;
> > +
> > +		INIT_WORK(&q_vector->rx_dim.work, iecm_rx_dim_work);
> > +		q_vector->rx_dim.mode =
> DIM_CQ_PERIOD_MODE_START_FROM_EQE;
> > +
> > +		napi_enable(&q_vector->napi);
> > +	}
> > +}
> > +
> > +/**
> > + * iecm_vport_splitq_napi_poll - NAPI handler
> > + * @napi: struct from which you get q_vector
> > + * @budget: budget provided by stack
> > + */
> > +static int iecm_vport_splitq_napi_poll(struct napi_struct *napi, int budget)
> > +{
> > +	/* stub */
> > +	return 0;
> > +}
> > +
> > +/**
> > + * iecm_vport_intr_map_vector_to_qs - Map vectors to queues
> > + * @vport: virtual port
> > + *
> > + * Mapping for vectors to queues
> > + */
> > +static void iecm_vport_intr_map_vector_to_qs(struct iecm_vport *vport)
> > +{
> > +	int num_txq_grp = vport->num_txq_grp, bufq_vidx = 0;
> > +	int i, j, qv_idx = 0, num_rxq, num_txq, q_index;
> > +	struct iecm_rxq_group *rx_qgrp;
> > +	struct iecm_txq_group *tx_qgrp;
> > +	struct iecm_queue *q, *bufq;
> > +
> > +	for (i = 0; i < vport->num_rxq_grp; i++) {
> > +		rx_qgrp = &vport->rxq_grps[i];
> > +		if (iecm_is_queue_model_split(vport->rxq_model))
> > +			num_rxq = rx_qgrp->splitq.num_rxq_sets;
> > +		else
> > +			num_rxq = rx_qgrp->singleq.num_rxq;
> > +
> > +		for (j = 0; j < num_rxq; j++) {
> > +			if (qv_idx >= vport->num_q_vectors)
> > +				qv_idx = 0;
> > +
> > +			if (iecm_is_queue_model_split(vport->rxq_model))
> > +				q = &rx_qgrp->splitq.rxq_sets[j]->rxq;
> > +			else
> > +				q = rx_qgrp->singleq.rxqs[j];
> > +			q->q_vector = &vport->q_vectors[qv_idx];
> > +			q_index = q->q_vector->num_rxq;
> > +			q->q_vector->rx[q_index] = q;
> > +			q->q_vector->num_rxq++;
> > +			qv_idx++;
> > +		}
> > +
> > +		if (iecm_is_queue_model_split(vport->rxq_model)) {
> > +			for (j = 0; j < vport->num_bufqs_per_qgrp; j++) {
> > +				bufq = &rx_qgrp->splitq.bufq_sets[j].bufq;
> > +				bufq->q_vector = &vport-
> >q_vectors[bufq_vidx];
> > +				q_index = bufq->q_vector->num_bufq;
> > +				bufq->q_vector->bufq[q_index] = bufq;
> > +				bufq->q_vector->num_bufq++;
> > +			}
> > +			if (++bufq_vidx >= vport->num_q_vectors)
> > +				bufq_vidx = 0;
> > +		}
> > +	}
> > +	qv_idx = 0;
> > +	for (i = 0; i < num_txq_grp; i++) {
> > +		tx_qgrp = &vport->txq_grps[i];
> > +		num_txq = tx_qgrp->num_txq;
> > +
> > +		if (iecm_is_queue_model_split(vport->txq_model)) {
> > +			if (qv_idx >= vport->num_q_vectors)
> > +				qv_idx = 0;
> > +
> > +			q = tx_qgrp->complq;
> > +			q->q_vector = &vport->q_vectors[qv_idx];
> > +			q_index = q->q_vector->num_txq;
> > +			q->q_vector->tx[q_index] = q;
> > +			q->q_vector->num_txq++;
> > +			qv_idx++;
> > +		} else {
> > +			for (j = 0; j < num_txq; j++) {
> > +				if (qv_idx >= vport->num_q_vectors)
> > +					qv_idx = 0;
> > +
> > +				q = tx_qgrp->txqs[j];
> > +				q->q_vector = &vport->q_vectors[qv_idx];
> > +				q_index = q->q_vector->num_txq;
> > +				q->q_vector->tx[q_index] = q;
> > +				q->q_vector->num_txq++;
> > +
> > +				qv_idx++;
> > +			}
> > +		}
> > +	}
> > +}
> > +
> > +/**
> > + * iecm_vport_intr_init_vec_idx - Initialize the vector indexes
> > + * @vport: virtual port
> > + *
> > + * Initialize vector indexes with values returened over mailbox
> > + */
> > +static int iecm_vport_intr_init_vec_idx(struct iecm_vport *vport)
> > +{
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	struct iecm_q_vector *q_vector;
> > +	int i;
> > +
> > +	if (adapter->req_vec_chunks) {
> > +		struct virtchnl2_vector_chunks *vchunks;
> > +		struct virtchnl2_alloc_vectors *ac;
> > +		u16 vecids[IECM_MAX_VECIDS];
> > +		int num_ids;
> > +
> > +		ac = adapter->req_vec_chunks;
> > +		vchunks = &ac->vchunks;
> > +
> > +		num_ids = iecm_get_vec_ids(adapter, vecids,
> IECM_MAX_VECIDS,
> > +					   vchunks);
> > +
> > +		if (num_ids < adapter->num_msix_entries)
> > +			return -EFAULT;
> > +
> > +		for (i = 0; i < vport->num_q_vectors; i++) {
> > +			q_vector = &vport->q_vectors[i];
> > +			q_vector->v_idx = vecids[i + vport->q_vector_base];
> > +		}
> > +	} else {
> > +		for (i = 0; i < vport->num_q_vectors; i++) {
> > +			q_vector = &vport->q_vectors[i];
> > +			q_vector->v_idx = i + vport->q_vector_base;
> > +		}
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +/**
> > + * iecm_vport_intr_napi_add_all- Register napi handler for all qvectors
> > + * @vport: virtual port structure
> > + */
> > +static void iecm_vport_intr_napi_add_all(struct iecm_vport *vport)
> > +{
> > +	u16 v_idx;
> > +
> > +	for (v_idx = 0; v_idx < vport->num_q_vectors; v_idx++) {
> > +		struct iecm_q_vector *q_vector = &vport->q_vectors[v_idx];
> > +
> > +		if (vport->netdev) {
> > +			if (iecm_is_queue_model_split(vport->txq_model))
> > +				netif_napi_add(vport->netdev, &q_vector-
> >napi,
> > +					       iecm_vport_splitq_napi_poll,
> > +					       NAPI_POLL_WEIGHT);
> > +			else
> > +				netif_napi_add(vport->netdev, &q_vector-
> >napi,
> > +					       iecm_vport_singleq_napi_poll,
> > +					       NAPI_POLL_WEIGHT);
> > +		}
> 
> 		if (!vport->netdev)
> 			goto check_affinity;
> 
> -1 indent level.
> 
> > +
> > +		/* only set affinity_mask if the CPU is online */
> > +		if (cpu_online(v_idx))
> > +			cpumask_set_cpu(v_idx, &q_vector->affinity_mask);
> > +	}
> > +}
> > +
> > +/**
> > + * iecm_vport_intr_alloc - Allocate memory for interrupt vectors
> > + * @vport: virtual port
> > + *
> > + * We allocate one q_vector per queue interrupt. If allocation fails we
> > + * return -ENOMEM.
> > + */
> > +int iecm_vport_intr_alloc(struct iecm_vport *vport)
> > +{
> > +	int txqs_per_vector, rxqs_per_vector, bufqs_per_vector;
> > +	struct iecm_q_vector *q_vector;
> > +	int v_idx, err;
> > +
> > +	vport->q_vectors = kcalloc(vport->num_q_vectors,
> > +				   sizeof(struct iecm_q_vector), GFP_KERNEL);
> > +
> > +	if (!vport->q_vectors)
> > +		return -ENOMEM;
> > +
> > +	txqs_per_vector = DIV_ROUND_UP(vport->num_txq, vport-
> >num_q_vectors);
> > +	rxqs_per_vector = DIV_ROUND_UP(vport->num_rxq, vport-
> >num_q_vectors);
> > +	bufqs_per_vector = DIV_ROUND_UP(vport->num_bufqs_per_qgrp *
> > +					vport->num_rxq_grp,
> > +					vport->num_q_vectors);
> > +
> > +	for (v_idx = 0; v_idx < vport->num_q_vectors; v_idx++) {
> > +		q_vector = &vport->q_vectors[v_idx];
> > +		q_vector->vport = vport;
> > +
> > +		q_vector->tx_itr_value = IECM_ITR_TX_DEF;
> > +		q_vector->tx_intr_mode = IECM_ITR_DYNAMIC;
> > +		q_vector->tx_itr_idx = VIRTCHNL2_ITR_IDX_1;
> > +
> > +		q_vector->rx_itr_value = IECM_ITR_RX_DEF;
> > +		q_vector->rx_intr_mode = IECM_ITR_DYNAMIC;
> > +		q_vector->rx_itr_idx = VIRTCHNL2_ITR_IDX_0;
> > +
> > +		q_vector->tx = kcalloc(txqs_per_vector,
> > +				       sizeof(struct iecm_queue *),
> > +				       GFP_KERNEL);
> > +		if (!q_vector->tx) {
> > +			err = -ENOMEM;
> > +			goto error;
> > +		}
> > +
> > +		q_vector->rx = kcalloc(rxqs_per_vector,
> > +				       sizeof(struct iecm_queue *),
> > +				       GFP_KERNEL);
> > +		if (!q_vector->rx) {
> > +			err = -ENOMEM;
> > +			goto error;
> > +		}
> > +
> > +		if (iecm_is_queue_model_split(vport->rxq_model)) {
> > +			q_vector->bufq = kcalloc(bufqs_per_vector,
> > +						 sizeof(struct iecm_queue *),
> > +						 GFP_KERNEL);
> > +			if (!q_vector->bufq) {
> > +				err = -ENOMEM;
> > +				goto error;
> > +			}
> > +		}
> 
> 		if (!split)
> 			continue;
> 
> -1 as well.
> 
> > +	}
> > +
> > +	return 0;
> > +
> > +error:
> > +	iecm_vport_intr_rel(vport);
> > +	return err;
> > +}
> > +
> > +/**
> > + * iecm_vport_intr_init - Setup all vectors for the given vport
> > + * @vport: virtual port
> > + *
> > + * Returns 0 on success or negative on failure
> > + */
> > +int iecm_vport_intr_init(struct iecm_vport *vport)
> > +{
> > +	char int_name[IECM_INT_NAME_STR_LEN];
> > +	int err = 0;
> > +
> > +	err = iecm_vport_intr_init_vec_idx(vport);
> > +	if (err)
> > +		goto handle_err;
> > +
> > +	iecm_vport_intr_map_vector_to_qs(vport);
> > +	iecm_vport_intr_napi_add_all(vport);
> > +	iecm_vport_intr_napi_ena_all(vport);
> > +
> > +	err = vport->adapter->dev_ops.reg_ops.intr_reg_init(vport);
> > +	if (err)
> > +		goto unroll_vectors_alloc;
> > +
> > +	snprintf(int_name, sizeof(int_name) - 1, "%s-%s",
> > +		 dev_driver_string(&vport->adapter->pdev->dev),
> > +		 vport->netdev->name);
> > +
> > +	err = iecm_vport_intr_req_irq(vport, int_name);
> > +	if (err)
> > +		goto unroll_vectors_alloc;
> > +
> > +	iecm_vport_intr_ena_irq_all(vport);
> > +	goto handle_err;
> > +unroll_vectors_alloc:
> > +	iecm_vport_intr_napi_dis_all(vport);
> > +	iecm_vport_intr_napi_del_all(vport);
> > +handle_err:
> > +	return err;
> > +}
> > +
> > +/**
> > + * iecm_config_rss - Prepare for RSS
> > + * @vport: virtual port
> > + *
> > + * Return 0 on success, negative on failure
> > + */
> > +int iecm_config_rss(struct iecm_vport *vport)
> > +{
> > +	int err;
> > +
> > +	err = vport->adapter->dev_ops.vc_ops.get_set_rss_key(vport, false);
> > +	if (!err)
> > +		err = vport->adapter->dev_ops.vc_ops.get_set_rss_lut(vport,
> > +								     false);
> 
> Please dereference vs_ops into a variable and use it directly to
> shorten this.
> 

Will fix.

> > +
> > +	return err;
> > +}
> > +
> > +/**
> > + * iecm_fill_dflt_rss_lut - Fill the indirection table with the default values
> > + * @vport: virtual port structure
> > + */
> > +void iecm_fill_dflt_rss_lut(struct iecm_vport *vport)
> > +{
> > +	u16 num_active_rxq = vport->num_rxq;
> > +	int i;
> > +
> > +	for (i = 0; i < vport->adapter->rss_data.rss_lut_size; i++)
> > +		vport->adapter->rss_data.rss_lut[i] = i % num_active_rxq;
> 
> I think I saw a built-in kernel function for that, I'd grep for sth
> like fill_default_rss.
> 

Hmm I grep'd around and didn't see anything like that. Didn't see anything like that in other drivers I briefly looked at either.

> > +}
> > +
> > +/**
> > + * iecm_init_rss - Prepare for RSS
> > + * @vport: virtual port
> > + *
> > + * Return 0 on success, negative on failure
> > + */
> > +int iecm_init_rss(struct iecm_vport *vport)
> > +{
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	u32 lut_size;
> > +
> > +	adapter->rss_data.rss_key = kzalloc(adapter->rss_data.rss_key_size,
> > +					    GFP_KERNEL);
> > +	if (!adapter->rss_data.rss_key)
> > +		return -ENOMEM;
> > +
> > +	lut_size = adapter->rss_data.rss_lut_size * sizeof(u32);
> > +	adapter->rss_data.rss_lut = kzalloc(lut_size, GFP_KERNEL);
> > +	if (!adapter->rss_data.rss_lut) {
> > +		kfree(adapter->rss_data.rss_key);
> > +		adapter->rss_data.rss_key = NULL;
> > +		return -ENOMEM;
> > +	}
> > +
> > +	/* Initialize default rss key */
> > +	netdev_rss_key_fill((void *)adapter->rss_data.rss_key,
> > +			    adapter->rss_data.rss_key_size);
> > +
> > +	/* Initialize default rss lut */
> > +	if (adapter->rss_data.rss_lut_size % vport->num_rxq) {
> > +		u32 dflt_qid;
> > +		int i;
> > +
> > +		/* Set all entries to a default RX queue if the algorithm below
> > +		 * won't fill all entries
> > +		 */
> > +		if (iecm_is_queue_model_split(vport->rxq_model))
> > +			dflt_qid =
> > +				vport->rxq_grps[0].splitq.rxq_sets[0]->rxq.q_id;
> > +		else
> > +			dflt_qid =
> > +				vport->rxq_grps[0].singleq.rxqs[0]->q_id;
> > +
> > +		for (i = 0; i < adapter->rss_data.rss_lut_size; i++)
> > +			adapter->rss_data.rss_lut[i] = dflt_qid;
> > +	}
> > +
> > +	/* Fill the default RSS lut values*/
> > +	iecm_fill_dflt_rss_lut(vport);
> > +
> > +	return iecm_config_rss(vport);
> > +}
> > +
> > +/**
> > + * iecm_deinit_rss - Prepare for RSS
> > + * @vport: virtual port
> > + *
> > + */
> > +void iecm_deinit_rss(struct iecm_vport *vport)
> > +{
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +
> > +	kfree(adapter->rss_data.rss_key);
> > +	adapter->rss_data.rss_key = NULL;
> > +	kfree(adapter->rss_data.rss_lut);
> > +	adapter->rss_data.rss_lut = NULL;
> > +}
> > diff --git a/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> > index b91716aeef6f..919fb3958cf8 100644
> > --- a/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> > +++ b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> > @@ -3745,6 +3745,35 @@ void iecm_add_del_ether_addrs(struct iecm_vport
> *vport, bool add, bool async)
> >  		dev_err(&pdev->dev, "Failed to add or del mac filters %d", err);
> >  }
> >
> > +/**
> > + * iecm_set_promiscuous - set promiscuous and send message to mailbox
> > + * @adapter: Driver specific private structure
> > + *
> > + * Request that the PF enable promiscuous mode for our VSI.  Message is sent
> > + * asynchronously and won't wait for response.  Returns 0 on success,
> negative
> > + * on failure;
> > + */
> > +int iecm_set_promiscuous(struct iecm_adapter *adapter)
> > +{
> > +	struct iecm_vport *vport = adapter->vports[0];
> > +	struct virtchnl_promisc_info vpi;
> > +	u16 flags = 0;
> > +	int err = 0;
> > +
> > +	if (test_bit(__IECM_PROMISC_UC, adapter->config_data.user_flags))
> > +		flags |= FLAG_VF_UNICAST_PROMISC;
> > +	if (test_bit(__IECM_PROMISC_MC,
> > +		     adapter->config_data.user_flags))
> > +		flags |= FLAG_VF_MULTICAST_PROMISC;
> > +
> > +	vpi.vsi_id = vport->vport_id;
> > +	vpi.flags = flags;
> > +	err = iecm_send_mb_msg(adapter,
> VIRTCHNL_OP_CONFIG_PROMISCUOUS_MODE,
> > +			       sizeof(struct virtchnl_promisc_info),
> > +			       (u8 *)&vpi);
> > +	return err;
> > +}
> > +
> >  /**
> >   * iecm_add_del_vlans - Add or delete vlan filter
> >   * @vport: vport structure
> > diff --git a/drivers/net/ethernet/intel/include/iecm.h
> b/drivers/net/ethernet/intel/include/iecm.h
> > index b5bd73be2855..4304256f7010 100644
> > --- a/drivers/net/ethernet/intel/include/iecm.h
> > +++ b/drivers/net/ethernet/intel/include/iecm.h
> > @@ -13,6 +13,7 @@
> >  #include <linux/version.h>
> >  #include <linux/dim.h>
> >
> > +#include "iecm_lan_txrx.h"
> >  #include "virtchnl_2.h"
> >  #include "iecm_txrx.h"
> >  #include "iecm_controlq.h"
> > @@ -621,6 +622,17 @@ enum iecm_vlan_caps {
> >  	VIRTCHNL2_CAP_RX_CSUM_L4_IPV4_SCTP	|\
> >  	VIRTCHNL2_CAP_RX_CSUM_L4_IPV6_SCTP)
> >
> > +/**
> > + * iecm_restore_features - Restore feature configs
> > + * @adapter: driver specific private structure
> > + * @flag: User settings flag to check
> > + */
> > +static inline bool iecm_is_user_flag_ena(struct iecm_adapter *adapter,
> > +					 enum iecm_user_flags flag)
> > +{
> > +	return test_bit(flag, adapter->config_data.user_flags);
> > +}
> > +
> >  /**
> >   * iecm_get_reserved_vecs - Get reserved vectors
> >   * @adapter: private data struct
> > @@ -656,6 +668,19 @@ static inline bool iecm_is_reset_in_prog(struct
> iecm_adapter *adapter)
> >  		test_bit(__IECM_HR_DRV_LOAD, adapter->flags));
> >  }
> >
> > +/**
> > + * iecm_rx_offset - Return expected offset into page to access data
> > + * @rx_q: queue we are requesting offset of
> > + *
> > + * Returns the offset value for queue into the data buffer.
> > + */
> > +static inline unsigned int
> > +iecm_rx_offset(struct iecm_queue __maybe_unused *rx_q)
> > +{
> > +	/* could be non-zero if xdp is enabled */
> > +	return 0;
> > +}
> > +
> >  int iecm_probe(struct pci_dev *pdev,
> >  	       const struct pci_device_id __always_unused *ent,
> >  	       struct iecm_adapter *adapter);
> > @@ -702,6 +727,7 @@ int iecm_send_mb_msg(struct iecm_adapter
> *adapter, enum virtchnl_ops op,
> >  		     u16 msg_size, u8 *msg);
> >  void iecm_vport_set_hsplit(struct iecm_vport *vport, bool ena);
> >  void iecm_add_del_ether_addrs(struct iecm_vport *vport, bool add, bool
> async);
> > +int iecm_set_promiscuous(struct iecm_adapter *adapter);
> >  int iecm_send_enable_channels_msg(struct iecm_vport *vport);
> >  int iecm_send_disable_channels_msg(struct iecm_vport *vport);
> >  bool iecm_is_feature_ena(struct iecm_vport *vport, netdev_features_t
> feature);
> > @@ -710,5 +736,7 @@ int iecm_check_descs(struct iecm_vport *vport, u64
> rx_desc_ids,
> >  int iecm_set_msg_pending(struct iecm_adapter *adapter,
> >  			 struct iecm_ctlq_msg *ctlq_msg,
> >  			 enum iecm_vport_vc_state err_enum);
> > +void iecm_vport_intr_write_itr(struct iecm_q_vector *q_vector,
> > +			       u16 itr, bool tx);
> >  int iecm_send_map_unmap_queue_vector_msg(struct iecm_vport *vport,
> bool map);
> >  #endif /* !_IECM_H_ */
> > diff --git a/drivers/net/ethernet/intel/include/iecm_lan_txrx.h
> b/drivers/net/ethernet/intel/include/iecm_lan_txrx.h
> > new file mode 100644
> > index 000000000000..967308036eba
> > --- /dev/null
> > +++ b/drivers/net/ethernet/intel/include/iecm_lan_txrx.h
> > @@ -0,0 +1,394 @@
> > +/* SPDX-License-Identifier: GPL-2.0-only */
> > +/* Copyright (c) 2020, Intel Corporation. */
> > +
> > +#ifndef _IECM_LAN_TXRX_H_
> > +#define _IECM_LAN_TXRX_H_
> > +
> > +enum iecm_rss_hash {
> > +	/* Values 0 - 28 are reserved for future use */
> > +	IECM_HASH_INVALID		= 0,
> > +	IECM_HASH_NONF_UNICAST_IPV4_UDP	= 29,
> > +	IECM_HASH_NONF_MULTICAST_IPV4_UDP,
> > +	IECM_HASH_NONF_IPV4_UDP,
> > +	IECM_HASH_NONF_IPV4_TCP_SYN_NO_ACK,
> > +	IECM_HASH_NONF_IPV4_TCP,
> > +	IECM_HASH_NONF_IPV4_SCTP,
> > +	IECM_HASH_NONF_IPV4_OTHER,
> > +	IECM_HASH_FRAG_IPV4,
> > +	/* Values 37-38 are reserved */
> > +	IECM_HASH_NONF_UNICAST_IPV6_UDP	= 39,
> > +	IECM_HASH_NONF_MULTICAST_IPV6_UDP,
> > +	IECM_HASH_NONF_IPV6_UDP,
> > +	IECM_HASH_NONF_IPV6_TCP_SYN_NO_ACK,
> > +	IECM_HASH_NONF_IPV6_TCP,
> > +	IECM_HASH_NONF_IPV6_SCTP,
> > +	IECM_HASH_NONF_IPV6_OTHER,
> > +	IECM_HASH_FRAG_IPV6,
> > +	IECM_HASH_NONF_RSVD47,
> > +	IECM_HASH_NONF_FCOE_OX,
> > +	IECM_HASH_NONF_FCOE_RX,
> > +	IECM_HASH_NONF_FCOE_OTHER,
> > +	/* Values 51-62 are reserved */
> > +	IECM_HASH_L2_PAYLOAD		= 63,
> > +	IECM_HASH_MAX
> > +};
> > +
> > +/* Supported RSS offloads */
> > +#define IECM_DEFAULT_RSS_HASH ( \
> > +	BIT_ULL(IECM_HASH_NONF_IPV4_UDP) | \
> > +	BIT_ULL(IECM_HASH_NONF_IPV4_SCTP) | \
> > +	BIT_ULL(IECM_HASH_NONF_IPV4_TCP) | \
> > +	BIT_ULL(IECM_HASH_NONF_IPV4_OTHER) | \
> > +	BIT_ULL(IECM_HASH_FRAG_IPV4) | \
> > +	BIT_ULL(IECM_HASH_NONF_IPV6_UDP) | \
> > +	BIT_ULL(IECM_HASH_NONF_IPV6_TCP) | \
> > +	BIT_ULL(IECM_HASH_NONF_IPV6_SCTP) | \
> > +	BIT_ULL(IECM_HASH_NONF_IPV6_OTHER) | \
> > +	BIT_ULL(IECM_HASH_FRAG_IPV6) | \
> > +	BIT_ULL(IECM_HASH_L2_PAYLOAD))
> > +
> > +	/* TODO: Wrap belwo comment under internal flag
> > +	 * Below 6 pcktypes are not supported by FVL or older products
> > +	 * They are supported by FPK and future products
> > +	 */
> > +#define IECM_DEFAULT_RSS_HASH_EXPANDED (IECM_DEFAULT_RSS_HASH
> | \
> > +	BIT_ULL(IECM_HASH_NONF_IPV4_TCP_SYN_NO_ACK) | \
> > +	BIT_ULL(IECM_HASH_NONF_UNICAST_IPV4_UDP) | \
> > +	BIT_ULL(IECM_HASH_NONF_MULTICAST_IPV4_UDP) | \
> > +	BIT_ULL(IECM_HASH_NONF_IPV6_TCP_SYN_NO_ACK) | \
> > +	BIT_ULL(IECM_HASH_NONF_UNICAST_IPV6_UDP) | \
> > +	BIT_ULL(IECM_HASH_NONF_MULTICAST_IPV6_UDP))
> > +
> > +/* For iecm_splitq_base_tx_compl_desc */
> > +#define IECM_TXD_COMPLQ_GEN_S	15
> > +#define IECM_TXD_COMPLQ_GEN_M
> 	BIT_ULL(IECM_TXD_COMPLQ_GEN_S)
> > +#define IECM_TXD_COMPLQ_COMPL_TYPE_S	11
> > +#define IECM_TXD_COMPLQ_COMPL_TYPE_M	\
> > +	MAKEMASK(0x7UL, IECM_TXD_COMPLQ_COMPL_TYPE_S)
> > +#define IECM_TXD_COMPLQ_QID_S	0
> > +#define IECM_TXD_COMPLQ_QID_M		MAKEMASK(0x3FFUL,
> IECM_TXD_COMPLQ_QID_S)
> > +
> > +/* For base mode TX descriptors */
> > +#define IECM_TXD_CTX_QW1_MSS_S		50
> > +#define IECM_TXD_CTX_QW1_MSS_M		\
> > +	MAKEMASK(0x3FFFULL, IECM_TXD_CTX_QW1_MSS_S)
> > +#define IECM_TXD_CTX_QW1_TSO_LEN_S	30
> > +#define IECM_TXD_CTX_QW1_TSO_LEN_M	\
> > +	MAKEMASK(0x3FFFFULL, IECM_TXD_CTX_QW1_TSO_LEN_S)
> > +#define IECM_TXD_CTX_QW1_CMD_S		4
> > +#define IECM_TXD_CTX_QW1_CMD_M		\
> > +	MAKEMASK(0xFFFUL, IECM_TXD_CTX_QW1_CMD_S)
> > +#define IECM_TXD_CTX_QW1_DTYPE_S	0
> > +#define IECM_TXD_CTX_QW1_DTYPE_M	\
> > +	MAKEMASK(0xFUL, IECM_TXD_CTX_QW1_DTYPE_S)
> > +#define IECM_TXD_QW1_L2TAG1_S		48
> > +#define IECM_TXD_QW1_L2TAG1_M		\
> > +	MAKEMASK(0xFFFFULL, IECM_TXD_QW1_L2TAG1_S)
> > +#define IECM_TXD_QW1_TX_BUF_SZ_S	34
> > +#define IECM_TXD_QW1_TX_BUF_SZ_M	\
> > +	MAKEMASK(0x3FFFULL, IECM_TXD_QW1_TX_BUF_SZ_S)
> > +#define IECM_TXD_QW1_OFFSET_S		16
> > +#define IECM_TXD_QW1_OFFSET_M		\
> > +	MAKEMASK(0x3FFFFULL, IECM_TXD_QW1_OFFSET_S)
> > +#define IECM_TXD_QW1_CMD_S		4
> > +#define IECM_TXD_QW1_CMD_M		MAKEMASK(0xFFFUL,
> IECM_TXD_QW1_CMD_S)
> > +#define IECM_TXD_QW1_DTYPE_S		0
> > +#define IECM_TXD_QW1_DTYPE_M		MAKEMASK(0xFUL,
> IECM_TXD_QW1_DTYPE_S)
> > +
> > +/* TX Completion Descriptor Completion Types */
> > +#define IECM_TXD_COMPLT_ITR_FLUSH	0
> > +#define IECM_TXD_COMPLT_RULE_MISS	1
> > +#define IECM_TXD_COMPLT_RS		2
> > +#define IECM_TXD_COMPLT_REINJECTED	3
> > +#define IECM_TXD_COMPLT_RE		4
> > +#define IECM_TXD_COMPLT_SW_MARKER	5
> > +
> > +enum iecm_tx_desc_dtype_value {
> > +	IECM_TX_DESC_DTYPE_DATA				= 0,
> > +	IECM_TX_DESC_DTYPE_CTX				= 1,
> > +	IECM_TX_DESC_DTYPE_REINJECT_CTX			= 2,
> > +	IECM_TX_DESC_DTYPE_FLEX_DATA			= 3,
> > +	IECM_TX_DESC_DTYPE_FLEX_CTX			= 4,
> > +	IECM_TX_DESC_DTYPE_FLEX_TSO_CTX			= 5,
> > +	IECM_TX_DESC_DTYPE_FLEX_TSYN_L2TAG1		= 6,
> > +	IECM_TX_DESC_DTYPE_FLEX_L2TAG1_L2TAG2		= 7,
> > +	IECM_TX_DESC_DTYPE_FLEX_TSO_L2TAG2_PARSTAG_CTX	= 8,
> > +	IECM_TX_DESC_DTYPE_FLEX_HOSTSPLIT_SA_TSO_CTX	= 9,
> > +	IECM_TX_DESC_DTYPE_FLEX_HOSTSPLIT_SA_CTX	= 10,
> > +	IECM_TX_DESC_DTYPE_FLEX_L2TAG2_CTX		= 11,
> > +	IECM_TX_DESC_DTYPE_FLEX_FLOW_SCHE		= 12,
> > +	IECM_TX_DESC_DTYPE_FLEX_HOSTSPLIT_TSO_CTX	= 13,
> > +	IECM_TX_DESC_DTYPE_FLEX_HOSTSPLIT_CTX		= 14,
> > +	/* DESC_DONE - HW has completed write-back of descriptor */
> > +	IECM_TX_DESC_DTYPE_DESC_DONE			= 15,
> > +};
> > +
> > +enum iecm_tx_ctx_desc_cmd_bits {
> > +	IECM_TX_CTX_DESC_TSO		= 0x01,
> > +	IECM_TX_CTX_DESC_TSYN		= 0x02,
> > +	IECM_TX_CTX_DESC_IL2TAG2	= 0x04,
> > +	IECM_TX_CTX_DESC_RSVD		= 0x08,
> > +	IECM_TX_CTX_DESC_SWTCH_NOTAG	= 0x00,
> > +	IECM_TX_CTX_DESC_SWTCH_UPLINK	= 0x10,
> > +	IECM_TX_CTX_DESC_SWTCH_LOCAL	= 0x20,
> > +	IECM_TX_CTX_DESC_SWTCH_VSI	= 0x30,
> > +	IECM_TX_CTX_DESC_FILT_AU_EN	= 0x40,
> > +	IECM_TX_CTX_DESC_FILT_AU_EVICT	= 0x80,
> > +	IECM_TX_CTX_DESC_RSVD1		= 0xF00
> > +};
> > +
> > +enum iecm_tx_desc_len_fields {
> > +	/* Note: These are predefined bit offsets */
> > +	IECM_TX_DESC_LEN_MACLEN_S	= 0, /* 7 BITS */
> > +	IECM_TX_DESC_LEN_IPLEN_S	= 7, /* 7 BITS */
> > +	IECM_TX_DESC_LEN_L4_LEN_S	= 14 /* 4 BITS */
> > +};
> > +
> > +enum iecm_tx_base_desc_cmd_bits {
> > +	IECM_TX_DESC_CMD_EOP			= 0x0001,
> > +	IECM_TX_DESC_CMD_RS			= 0x0002,
> > +	 /* only on VFs else RSVD */
> > +	IECM_TX_DESC_CMD_ICRC			= 0x0004,
> > +	IECM_TX_DESC_CMD_IL2TAG1		= 0x0008,
> > +	IECM_TX_DESC_CMD_RSVD1			= 0x0010,
> > +	IECM_TX_DESC_CMD_IIPT_NONIP		= 0x0000, /* 2 BITS */
> > +	IECM_TX_DESC_CMD_IIPT_IPV6		= 0x0020, /* 2 BITS */
> > +	IECM_TX_DESC_CMD_IIPT_IPV4		= 0x0040, /* 2 BITS */
> > +	IECM_TX_DESC_CMD_IIPT_IPV4_CSUM		= 0x0060, /* 2 BITS */
> > +	IECM_TX_DESC_CMD_RSVD2			= 0x0080,
> > +	IECM_TX_DESC_CMD_L4T_EOFT_UNK		= 0x0000, /* 2 BITS */
> > +	IECM_TX_DESC_CMD_L4T_EOFT_TCP		= 0x0100, /* 2 BITS */
> > +	IECM_TX_DESC_CMD_L4T_EOFT_SCTP		= 0x0200, /* 2 BITS */
> > +	IECM_TX_DESC_CMD_L4T_EOFT_UDP		= 0x0300, /* 2 BITS */
> > +	IECM_TX_DESC_CMD_RSVD3			= 0x0400,
> > +	IECM_TX_DESC_CMD_RSVD4			= 0x0800,
> > +};
> > +
> > +/* Transmit descriptors  */
> > +/* splitq tx buf, singleq tx buf and singleq compl desc */
> > +struct iecm_base_tx_desc {
> > +	__le64 buf_addr; /* Address of descriptor's data buf */
> > +	__le64 qw1; /* type_cmd_offset_bsz_l2tag1 */
> > +};/* read used with buffer queues*/
> > +
> > +struct iecm_splitq_tx_compl_desc {
> > +	/* qid=[10:0] comptype=[13:11] rsvd=[14] gen=[15] */
> > +	__le16 qid_comptype_gen;
> > +	union {
> > +		__le16 q_head; /* Queue head */
> > +		__le16 compl_tag; /* Completion tag */
> > +	} q_head_compl_tag;
> > +	u32 rsvd;
> > +
> > +};/* writeback used with completion queues*/
> > +
> > +/* Context descriptors */
> > +struct iecm_base_tx_ctx_desc {
> > +	struct {
> > +		__le32 rsvd0;
> > +		__le16 l2tag2;
> > +		__le16 rsvd1;
> > +	} qw0;
> > +	__le64 qw1; /* type_cmd_tlen_mss/rt_hint */
> > +};
> > +
> > +/* Common cmd field defines for all desc except Flex Flow Scheduler (0x0C)
> */
> > +enum iecm_tx_flex_desc_cmd_bits {
> > +	IECM_TX_FLEX_DESC_CMD_EOP			= 0x01,
> > +	IECM_TX_FLEX_DESC_CMD_RS			= 0x02,
> > +	IECM_TX_FLEX_DESC_CMD_RE			= 0x04,
> > +	IECM_TX_FLEX_DESC_CMD_IL2TAG1			= 0x08,
> > +	IECM_TX_FLEX_DESC_CMD_DUMMY			= 0x10,
> > +	IECM_TX_FLEX_DESC_CMD_CS_EN			= 0x20,
> > +	IECM_TX_FLEX_DESC_CMD_FILT_AU_EN		= 0x40,
> > +	IECM_TX_FLEX_DESC_CMD_FILT_AU_EVICT		= 0x80,
> > +};
> > +
> > +struct iecm_flex_tx_desc {
> > +	__le64 buf_addr;	/* Packet buffer address */
> > +	struct {
> > +		__le16 cmd_dtype;
> > +#define IECM_FLEX_TXD_QW1_DTYPE_S		0
> > +#define IECM_FLEX_TXD_QW1_DTYPE_M		\
> > +		MAKEMASK(0x1FUL, IECM_FLEX_TXD_QW1_DTYPE_S)
> > +#define IECM_FLEX_TXD_QW1_CMD_S		5
> > +#define IECM_FLEX_TXD_QW1_CMD_M		MAKEMASK(0x7FFUL,
> IECM_TXD_QW1_CMD_S)
> > +		union {
> > +			/* DTYPE = IECM_TX_DESC_DTYPE_FLEX_DATA_(0x03)
> */
> > +			u8 raw[4];
> > +
> > +			/* DTYPE = IECM_TX_DESC_DTYPE_FLEX_TSYN_L2TAG1
> (0x06) */
> > +			struct {
> > +				__le16 l2tag1;
> > +				u8 flex;
> > +				u8 tsync;
> > +			} tsync;
> > +
> > +			/*
> DTYPE=IECM_TX_DESC_DTYPE_FLEX_L2TAG1_L2TAG2 (0x07) */
> > +			struct {
> > +				__le16 l2tag1;
> > +				__le16 l2tag2;
> > +			} l2tags;
> > +		} flex;
> > +		__le16 buf_size;
> > +	} qw1;
> > +};
> > +
> > +struct iecm_flex_tx_sched_desc {
> > +	__le64 buf_addr;	/* Packet buffer address */
> > +
> > +	/* DTYPE = IECM_TX_DESC_DTYPE_FLEX_FLOW_SCHE_16B (0x0C) */
> > +	struct {
> > +		u8 cmd_dtype;
> > +#define IECM_TXD_FLEX_FLOW_DTYPE_M	0x1F
> > +#define IECM_TXD_FLEX_FLOW_CMD_EOP	0x20
> > +#define IECM_TXD_FLEX_FLOW_CMD_CS_EN	0x40
> > +#define IECM_TXD_FLEX_FLOW_CMD_RE	0x80
> > +
> > +		u8 rsvd[3];
> > +
> > +		__le16 compl_tag;
> > +		__le16 rxr_bufsize;
> > +#define IECM_TXD_FLEX_FLOW_RXR		0x4000
> > +#define IECM_TXD_FLEX_FLOW_BUFSIZE_M	0x3FFF
> > +	} qw1;
> > +};
> > +
> > +/* Common cmd fields for all flex context descriptors
> > + * Note: these defines already account for the 5 bit dtype in the cmd_dtype
> > + * field
> > + */
> > +enum iecm_tx_flex_ctx_desc_cmd_bits {
> > +	IECM_TX_FLEX_CTX_DESC_CMD_TSO			= 0x0020,
> > +	IECM_TX_FLEX_CTX_DESC_CMD_TSYN_EN		= 0x0040,
> > +	IECM_TX_FLEX_CTX_DESC_CMD_L2TAG2		= 0x0080,
> > +	IECM_TX_FLEX_CTX_DESC_CMD_SWTCH_UPLNK		=
> 0x0200, /* 2 bits */
> > +	IECM_TX_FLEX_CTX_DESC_CMD_SWTCH_LOCAL		= 0x0400, /* 2
> bits */
> > +	IECM_TX_FLEX_CTX_DESC_CMD_SWTCH_TARGETVSI	= 0x0600, /* 2
> bits */
> > +};
> > +
> > +/* Standard flex descriptor TSO context quad word */
> > +struct iecm_flex_tx_tso_ctx_qw {
> > +	__le32 flex_tlen;
> > +#define IECM_TXD_FLEX_CTX_TLEN_M	0x1FFFF
> > +#define IECM_TXD_FLEX_TSO_CTX_FLEX_S	24
> > +	__le16 mss_rt;
> > +#define IECM_TXD_FLEX_CTX_MSS_RT_M	0x3FFF
> > +	u8 hdr_len;
> > +	u8 flex;
> > +};
> > +
> > +union iecm_flex_tx_ctx_desc {
> > +	/* DTYPE = IECM_TX_DESC_DTYPE_FLEX_CTX (0x04) */
> > +	struct {
> > +		u8 qw0_flex[8];
> > +		struct {
> > +			__le16 cmd_dtype;
> > +			__le16 l2tag1;
> > +			u8 qw1_flex[4];
> > +		} qw1;
> > +	} gen;
> > +
> > +	/* DTYPE = IECM_TX_DESC_DTYPE_FLEX_TSO_CTX (0x05) */
> > +	struct {
> > +		struct iecm_flex_tx_tso_ctx_qw qw0;
> > +		struct {
> > +			__le16 cmd_dtype;
> > +			u8 flex[6];
> > +		} qw1;
> > +	} tso;
> > +
> > +	/* DTYPE = IECM_TX_DESC_DTYPE_FLEX_TSO_L2TAG2_PARSTAG_CTX
> (0x08) */
> > +	struct {
> > +		struct iecm_flex_tx_tso_ctx_qw qw0;
> > +		struct {
> > +			__le16 cmd_dtype;
> > +			__le16 l2tag2;
> > +			u8 flex0;
> > +			u8 ptag;
> > +			u8 flex1[2];
> > +		} qw1;
> > +	} tso_l2tag2_ptag;
> > +
> > +	/* DTYPE = IECM_TX_DESC_DTYPE_FLEX_L2TAG2_CTX (0x0B) */
> > +	struct {
> > +		u8 qw0_flex[8];
> > +		struct {
> > +			__le16 cmd_dtype;
> > +			__le16 l2tag2;
> > +			u8 flex[4];
> > +		} qw1;
> > +	} l2tag2;
> > +
> > +	/* DTYPE = IECM_TX_DESC_DTYPE_REINJECT_CTX (0x02) */
> > +	struct {
> > +		struct {
> > +			__le32 sa_domain;
> > +#define IECM_TXD_FLEX_CTX_SA_DOM_M	0xFFFF
> > +#define IECM_TXD_FLEX_CTX_SA_DOM_VAL	0x10000
> > +			__le32 sa_idx;
> > +#define IECM_TXD_FLEX_CTX_SAIDX_M	0x1FFFFF
> > +		} qw0;
> > +		struct {
> > +			__le16 cmd_dtype;
> > +			__le16 txr2comp;
> > +#define IECM_TXD_FLEX_CTX_TXR2COMP	0x1
> > +			__le16 miss_txq_comp_tag;
> > +			__le16 miss_txq_id;
> > +		} qw1;
> > +	} reinjection_pkt;
> > +};
> > +
> > +/* Host Split Context Descriptors */
> > +struct iecm_flex_tx_hs_ctx_desc {
> > +	union {
> > +		struct {
> > +			__le32 host_fnum_tlen;
> > +#define IECM_TXD_FLEX_CTX_TLEN_S	0
> > +#define IECM_TXD_FLEX_CTX_TLEN_M	0x1FFFF
> > +#define IECM_TXD_FLEX_CTX_FNUM_S	18
> > +#define IECM_TXD_FLEX_CTX_FNUM_M	0x7FF
> > +#define IECM_TXD_FLEX_CTX_HOST_S	29
> > +#define IECM_TXD_FLEX_CTX_HOST_M	0x7
> > +			__le16 ftype_mss_rt;
> > +#define IECM_TXD_FLEX_CTX_MSS_RT_0	0
> > +#define IECM_TXD_FLEX_CTX_MSS_RT_M	0x3FFF
> > +#define IECM_TXD_FLEX_CTX_FTYPE_S	14
> > +#define IECM_TXD_FLEX_CTX_FTYPE_VF	MAKEMASK(0x0,
> IECM_TXD_FLEX_CTX_FTYPE_S)
> > +#define IECM_TXD_FLEX_CTX_FTYPE_VDEV	MAKEMASK(0x1,
> IECM_TXD_FLEX_CTX_FTYPE_S)
> > +#define IECM_TXD_FLEX_CTX_FTYPE_PF	MAKEMASK(0x2,
> IECM_TXD_FLEX_CTX_FTYPE_S)
> > +			u8 hdr_len;
> > +			u8 ptag;
> > +		} tso;
> > +		struct {
> > +			u8 flex0[2];
> > +			__le16 host_fnum_ftype;
> > +			u8 flex1[3];
> > +			u8 ptag;
> > +		} no_tso;
> > +	} qw0;
> > +
> > +	__le64 qw1_cmd_dtype;
> > +#define IECM_TXD_FLEX_CTX_QW1_PASID_S		16
> > +#define IECM_TXD_FLEX_CTX_QW1_PASID_M		0xFFFFF
> > +#define IECM_TXD_FLEX_CTX_QW1_PASID_VALID_S	36
> > +#define IECM_TXD_FLEX_CTX_QW1_PASID_VALID	\
> > +		MAKEMASK(0x1, IECM_TXD_FLEX_CTX_PASID_VALID_S)
> > +#define IECM_TXD_FLEX_CTX_QW1_TPH_S		37
> > +#define IECM_TXD_FLEX_CTX_QW1_TPH \
> > +		MAKEMASK(0x1, IECM_TXD_FLEX_CTX_TPH_S)
> > +#define IECM_TXD_FLEX_CTX_QW1_PFNUM_S		38
> > +#define IECM_TXD_FLEX_CTX_QW1_PFNUM_M		0xF
> > +/* The following are only valid for DTYPE = 0x09 and DTYPE = 0x0A */
> > +#define IECM_TXD_FLEX_CTX_QW1_SAIDX_S		42
> > +#define IECM_TXD_FLEX_CTX_QW1_SAIDX_M		0x1FFFFF
> > +#define IECM_TXD_FLEX_CTX_QW1_SAIDX_VAL_S	63
> > +#define IECM_TXD_FLEX_CTX_QW1_SAIDX_VALID	\
> > +		MAKEMASK(0x1, IECM_TXD_FLEX_CTX_QW1_SAIDX_VAL_S)
> > +/* The following are only valid for DTYPE = 0x0D and DTYPE = 0x0E */
> > +#define IECM_TXD_FLEX_CTX_QW1_FLEX0_S		48
> > +#define IECM_TXD_FLEX_CTX_QW1_FLEX0_M		0xFF
> > +#define IECM_TXD_FLEX_CTX_QW1_FLEX1_S		56
> > +#define IECM_TXD_FLEX_CTX_QW1_FLEX1_M		0xFF
> > +};
> > +#endif /* _IECM_LAN_TXRX_H_ */
> > diff --git a/drivers/net/ethernet/intel/include/iecm_txrx.h
> b/drivers/net/ethernet/intel/include/iecm_txrx.h
> > index 0aa1eac70e7c..44c20f8a2039 100644
> > --- a/drivers/net/ethernet/intel/include/iecm_txrx.h
> > +++ b/drivers/net/ethernet/intel/include/iecm_txrx.h
> > @@ -81,8 +81,70 @@
> >  #define IECM_MAX_MTU		\
> >  	(IECM_MAX_RXBUFFER - IECM_PACKET_HDR_PAD)
> >
> > +#define IECM_RX_BI_BUFID_S		0
> > +#define IECM_RX_BI_BUFID_M		MAKEMASK(0x7FFF,
> IECM_RX_BI_BUFID_S)
> > +#define IECM_RX_BI_GEN_S		15
> > +#define IECM_RX_BI_GEN_M		BIT(IECM_RX_BI_GEN_S)
> > +
> > +#define IECM_SINGLEQ_RX_BUF_DESC(R, i)	\
> > +	(&(((struct virtchnl2_singleq_rx_buf_desc *)((R)->desc_ring))[i]))
> > +#define IECM_SPLITQ_RX_BUF_DESC(R, i)	\
> > +	(&(((struct virtchnl2_splitq_rx_buf_desc *)((R)->desc_ring))[i]))
> > +#define IECM_SPLITQ_RX_BI_DESC(R, i)	\
> > +	(&(((u16 *)((R)->ring))[i]))
> > +
> > +#define IECM_BASE_TX_DESC(R, i)	\
> > +	(&(((struct iecm_base_tx_desc *)((R)->desc_ring))[i]))
> > +#define IECM_BASE_TX_CTX_DESC(R, i) \
> > +	(&(((struct iecm_base_tx_ctx_desc *)((R)->desc_ring))[i]))
> > +#define IECM_SPLITQ_TX_COMPLQ_DESC(R, i)	\
> > +	(&(((struct iecm_splitq_tx_compl_desc *)((R)->desc_ring))[i]))
> > +
> > +#define IECM_FLEX_TX_DESC(R, i)	\
> > +	(&(((union iecm_tx_flex_desc *)((R)->desc_ring))[i]))
> > +#define IECM_FLEX_TX_CTX_DESC(R, i)	\
> > +	(&(((union iecm_flex_tx_ctx_desc *)((R)->desc_ring))[i]))
> > +
> > +#define IECM_DESC_UNUSED(R)	\
> > +	((((R)->next_to_clean > (R)->next_to_use) ? 0 : (R)->desc_count) + \
> > +	(R)->next_to_clean - (R)->next_to_use - 1)
> > +
> > +#define IECM_TX_BUF_UNUSED(R)	((R)->buf_stack.top)
> > +
> > +#define IECM_TXD_LAST_DESC_CMD (IECM_TX_DESC_CMD_EOP |
> IECM_TX_DESC_CMD_RS)
> > +
> >  #define MAKEMASK(m, s)	((m) << (s))
> 
> Consider using stock BIT(s) insteead of introducing this MAKEMASK().
> 
> >
> > +struct iecm_tx_buf {
> > +	struct hlist_node hlist;
> > +	void *next_to_watch;
> > +	union {
> > +		struct sk_buff *skb;
> > +		struct xdp_frame *xdpf;
> > +	};
> > +	unsigned int bytecount;
> > +	unsigned short gso_segs;
> > +#define IECM_TX_FLAGS_TSO			BIT(0)
> > +#define IECM_TX_FLAGS_VLAN_TAG			BIT(1)
> > +#define IECM_TX_FLAGS_HW_VLAN			BIT(2)
> > +#define IECM_TX_FLAGS_HW_OUTER_SINGLE_VLAN	BIT(3)
> > +#define IECM_TX_FLAGS_VLAN_SHIFT		16
> > +#define IECM_TX_FLAGS_VLAN_MASK			0xFFFF0000
> > +	u32 tx_flags;
> > +	DEFINE_DMA_UNMAP_ADDR(dma);
> > +	DEFINE_DMA_UNMAP_LEN(len);
> > +	u16 compl_tag;		/* Unique identifier for buffer; used to
> > +				 * compare with completion tag returned
> > +				 * in buffer completion event
> > +				 */
> > +};
> > +
> > +struct iecm_buf_lifo {
> > +	u16 top;
> > +	u16 size;
> > +	struct iecm_tx_buf **bufs;
> 
> There'll probably be a 4-byte gap before @bufs, move @top and @size
> to the bottop to avoid this.
> 
> > +};
> > +
> >  /* Checksum offload bits decoded from the receive descriptor. */
> >  struct iecm_rx_csum_decoded {
> >  	u8 l3l4p : 1;
> > @@ -349,6 +411,16 @@ union iecm_queue_stats {
> >  	struct iecm_tx_queue_stats tx;
> >  };
> >
> > +#define IECM_ITR_DYNAMIC	1
> > +#define IECM_ITR_MAX		0x1FE0
> > +#define IECM_ITR_20K		0x0032
> > +#define IECM_ITR_GRAN_S		1	/* Assume ITR granularity is
> 2us */
> > +#define IECM_ITR_MASK		0x1FFE	/* ITR register value alignment
> mask */
> > +#define ITR_REG_ALIGN(setting)	((setting) & IECM_ITR_MASK)
> > +#define IECM_ITR_IS_DYNAMIC(itr_mode) (itr_mode)
> > +#define IECM_ITR_TX_DEF		IECM_ITR_20K
> > +#define IECM_ITR_RX_DEF		IECM_ITR_20K
> > +
> >  /* queue associated with a vport */
> >  struct iecm_queue {
> >  	struct device *dev;		/* Used for DMA mapping */
> > @@ -414,6 +486,10 @@ struct iecm_queue {
> >  	dma_addr_t dma;			/* physical address of ring */
> >  	void *desc_ring;		/* Descriptor ring memory */
> >
> > +	struct iecm_buf_lifo buf_stack; /* Stack of empty buffers to store
> > +					 * buffer info for out of order
> > +					 * buffer completions
> > +					 */
> >  	u16 tx_buf_key;			/* 16 bit unique "identifier" (index)
> >  					 * to be used as the completion tag
> when
> >  					 * queue is using flow based scheduling
> > @@ -505,13 +581,33 @@ struct iecm_txq_group {
> >
> >  struct iecm_adapter;
> >
> > +int iecm_vport_singleq_napi_poll(struct napi_struct *napi, int budget);
> >  void iecm_vport_init_num_qs(struct iecm_vport *vport,
> >  			    struct virtchnl2_create_vport *vport_msg);
> >  void iecm_vport_calc_num_q_desc(struct iecm_vport *vport);
> >  void iecm_vport_calc_total_qs(struct iecm_adapter *adapter,
> >  			      struct virtchnl2_create_vport *vport_msg);
> >  void iecm_vport_calc_num_q_groups(struct iecm_vport *vport);
> > +int iecm_vport_queues_alloc(struct iecm_vport *vport);
> > +void iecm_vport_queues_rel(struct iecm_vport *vport);
> >  void iecm_vport_calc_num_q_vec(struct iecm_vport *vport);
> > +void iecm_vport_intr_rel(struct iecm_vport *vport);
> > +int iecm_vport_intr_alloc(struct iecm_vport *vport);
> > +void iecm_vport_intr_dis_irq_all(struct iecm_vport *vport);
> > +void iecm_vport_intr_clear_dflt_itr(struct iecm_vport *vport);
> > +void iecm_vport_intr_update_itr_ena_irq(struct iecm_q_vector *q_vector);
> > +void iecm_vport_intr_deinit(struct iecm_vport *vport);
> > +int iecm_vport_intr_init(struct iecm_vport *vport);
> >  irqreturn_t
> >  iecm_vport_intr_clean_queues(int __always_unused irq, void *data);
> > +void iecm_vport_intr_ena_irq_all(struct iecm_vport *vport);
> > +int iecm_config_rss(struct iecm_vport *vport);
> > +void iecm_fill_dflt_rss_lut(struct iecm_vport *vport);
> > +int iecm_init_rss(struct iecm_vport *vport);
> > +void iecm_deinit_rss(struct iecm_vport *vport);
> > +bool iecm_init_rx_buf_hw_alloc(struct iecm_queue *rxq, struct iecm_rx_buf
> *buf);
> > +void iecm_rx_buf_hw_update(struct iecm_queue *rxq, u32 val);
> > +void iecm_tx_buf_rel(struct iecm_queue *tx_q, struct iecm_tx_buf *tx_buf);
> > +bool iecm_rx_singleq_buf_hw_alloc_all(struct iecm_queue *rxq,
> > +				      u16 cleaned_count);
> >  #endif /* !_IECM_TXRX_H_ */
> > --
> > 2.33.0
> 
> Thanks,
> Al

^ permalink raw reply	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 10/19] iecm: alloc vport RX resources
  2022-01-28 14:16   ` Alexander Lobakin
@ 2022-02-03  0:13     ` Brady, Alan
  2022-02-03 18:29       ` Alexander Lobakin
  0 siblings, 1 reply; 89+ messages in thread
From: Brady, Alan @ 2022-02-03  0:13 UTC (permalink / raw)
  To: intel-wired-lan

> -----Original Message-----
> From: Lobakin, Alexandr <alexandr.lobakin@intel.com>
> Sent: Friday, January 28, 2022 6:16 AM
> To: Brady, Alan <alan.brady@intel.com>
> Cc: Lobakin, Alexandr <alexandr.lobakin@intel.com>; intel-wired-
> lan at lists.osuosl.org; Burra, Phani R <phani.r.burra@intel.com>; Chittim, Madhu
> <madhu.chittim@intel.com>; Linga, Pavan Kumar
> <pavan.kumar.linga@intel.com>
> Subject: Re: [Intel-wired-lan] [PATCH net-next 10/19] iecm: alloc vport RX
> resources
> 
> From: Alan Brady <alan.brady@intel.com>
> Date: Thu, 27 Jan 2022 16:10:00 -0800
> 
> > This finishes what we need to do for open by adding RX resource
> > allocations.
> >
> > As noted in the TX alloc patch, the splitq model is unique in
> > introducing the concept of queue groups, which also applies to RX,
> > albeit in a slightly different way. For RX we also split the queue
> > between descriptor handling and buffer handling. We have some number
> > of RX completion queues associated with up to two buffer queues in a
> > given queue group. Once buffers are cleaned and recycled, they're
> > given the buffer queues which then posts the buffers back to hardware.
> > To enable this in a lockless way, there's also the concept of 'refill
> > queues' introduced. Recycled buffers are put onto refill queues which is what
> the buffer queues clean to get buffers back.
> >
> > Signed-off-by: Phani Burra <phani.r.burra@intel.com>
> > Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
> > Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
> > Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
> > Signed-off-by: Alan Brady <alan.brady@intel.com>
> > ---
> >  drivers/net/ethernet/intel/iecm/iecm_txrx.c   | 769 ++++++++++++++++++
> >  .../net/ethernet/intel/include/iecm_txrx.h    |   7 +
> >  2 files changed, 776 insertions(+)
> >
> > diff --git a/drivers/net/ethernet/intel/iecm/iecm_txrx.c
> > b/drivers/net/ethernet/intel/iecm/iecm_txrx.c
> > index 85e88a30370d..fb6a61277b00 100644
> > --- a/drivers/net/ethernet/intel/iecm/iecm_txrx.c
> > +++ b/drivers/net/ethernet/intel/iecm/iecm_txrx.c
> > @@ -452,6 +452,545 @@ static int iecm_tx_desc_alloc_all(struct iecm_vport
> *vport)
> >  	return err;
> >  }
> >
> > +/**
> > + * iecm_rx_page_rel - Release an rx buffer page
> > + * @rxq: the queue that owns the buffer
> > + * @page_info: pointer to page metadata of page to be freed  */
> > +static void iecm_rx_page_rel(struct iecm_queue *rxq,
> > +			     struct iecm_page_info *page_info) {
> > +	if (!page_info->page)
> > +		return;
> > +
> > +	/* free resources associated with mapping */
> > +	dma_unmap_page_attrs(rxq->dev, page_info->dma, PAGE_SIZE,
> > +			     DMA_FROM_DEVICE, IECM_RX_DMA_ATTR);
> > +
> > +	__page_frag_cache_drain(page_info->page, page_info->pagecnt_bias);
> > +
> > +	page_info->page = NULL;
> > +	page_info->page_offset = 0;
> > +}
> > +
> > +/**
> > + * iecm_rx_buf_rel - Release a rx buffer
> > + * @rxq: the queue that owns the buffer
> > + * @rx_buf: the buffer to free
> > + */
> > +static void iecm_rx_buf_rel(struct iecm_queue *rxq,
> > +			    struct iecm_rx_buf *rx_buf)
> > +{
> > +	iecm_rx_page_rel(rxq, &rx_buf->page_info[0]); #if (PAGE_SIZE < 8192)
> > +	if (rx_buf->buf_size > IECM_RX_BUF_2048)
> > +		iecm_rx_page_rel(rxq, &rx_buf->page_info[1]);
> > +
> > +#endif
> 
> PAGE_SIZE is always defined, thus can be embedded in an if statement.
> 
> 	if (PAGE_SIZE < 8192 && rx_buf ...)
> 
> will produce the same code, but without ifdeffery.
> 

Will fix

> > +	if (rx_buf->skb) {
> > +		dev_kfree_skb_any(rx_buf->skb);
> > +		rx_buf->skb = NULL;
> > +	}
> > +}
> > +
> > +/**
> > + * iecm_rx_hdr_buf_rel_all - Release header buffer memory
> > + * @rxq: queue to use
> > + */
> > +static void iecm_rx_hdr_buf_rel_all(struct iecm_queue *rxq) {
> > +	struct iecm_hw *hw = &rxq->vport->adapter->hw;
> > +	int i;
> > +
> > +	if (!rxq)
> > +		return;
> > +
> > +	if (rxq->rx_buf.hdr_buf) {
> > +		for (i = 0; i < rxq->desc_count; i++) {
> > +			struct iecm_dma_mem *hbuf = rxq->rx_buf.hdr_buf[i];
> > +
> > +			if (hbuf) {
> > +				iecm_free_dma_mem(hw, hbuf);
> > +				kfree(hbuf);
> > +			}
> > +			rxq->rx_buf.hdr_buf[i] = NULL;
> > +		}
> > +		kfree(rxq->rx_buf.hdr_buf);
> > +		rxq->rx_buf.hdr_buf = NULL;
> > +	}
> 
> 	if (!hdr_buf)
> 		goto release_pages;
> 
> -1 indent level.
> 

Will fix

> > +
> > +	for (i = 0; i < rxq->hbuf_pages.nr_pages; i++)
> > +		iecm_rx_page_rel(rxq, &rxq->hbuf_pages.pages[i]);
> > +
> > +	kfree(rxq->hbuf_pages.pages);
> > +}
> > +
> > +/**
> > + * iecm_rx_buf_rel_all - Free all Rx buffer resources for a queue
> > + * @rxq: queue to be cleaned
> > + */
> > +static void iecm_rx_buf_rel_all(struct iecm_queue *rxq) {
> > +	u16 i;
> > +
> > +	/* queue already cleared, nothing to do */
> > +	if (!rxq->rx_buf.buf)
> > +		return;
> > +
> > +	/* Free all the bufs allocated and given to hw on Rx queue */
> > +	for (i = 0; i < rxq->desc_count; i++)
> > +		iecm_rx_buf_rel(rxq, &rxq->rx_buf.buf[i]);
> > +	if (rxq->rx_hsplit_en)
> > +		iecm_rx_hdr_buf_rel_all(rxq);
> > +
> > +	kfree(rxq->rx_buf.buf);
> > +	rxq->rx_buf.buf = NULL;
> > +	kfree(rxq->rx_buf.hdr_buf);
> > +	rxq->rx_buf.hdr_buf = NULL;
> > +}
> > +
> > +/**
> > + * iecm_rx_desc_rel - Free a specific Rx q resources
> > + * @rxq: queue to clean the resources from
> > + * @bufq: buffer q or completion q
> > + * @q_model: single or split q model
> > + *
> > + * Free a specific rx queue resources  */ static void
> > +iecm_rx_desc_rel(struct iecm_queue *rxq, bool bufq, s32 q_model) {
> > +	if (!rxq)
> > +		return;
> > +
> > +	if (!bufq && iecm_is_queue_model_split(q_model) && rxq->skb) {
> > +		dev_kfree_skb_any(rxq->skb);
> > +		rxq->skb = NULL;
> > +	}
> > +
> > +	if (bufq || !iecm_is_queue_model_split(q_model))
> > +		iecm_rx_buf_rel_all(rxq);
> > +
> > +	if (rxq->desc_ring) {
> > +		dmam_free_coherent(rxq->dev, rxq->size,
> > +				   rxq->desc_ring, rxq->dma);
> > +		rxq->desc_ring = NULL;
> > +		rxq->next_to_alloc = 0;
> > +		rxq->next_to_clean = 0;
> > +		rxq->next_to_use = 0;
> > +	}
> 
> 	if (!desc_ring)
> 		return;
> 
> Same.
> 
> > +}
> > +
> > +/**
> > + * iecm_rx_desc_rel_all - Free Rx Resources for All Queues
> > + * @vport: virtual port structure
> > + *
> > + * Free all rx queues resources
> > + */
> > +static void iecm_rx_desc_rel_all(struct iecm_vport *vport) {
> > +	struct iecm_rxq_group *rx_qgrp;
> > +	struct iecm_queue *q;
> > +	int i, j, num_rxq;
> > +
> > +	if (!vport->rxq_grps)
> > +		return;
> > +
> > +	for (i = 0; i < vport->num_rxq_grp; i++) {
> > +		rx_qgrp = &vport->rxq_grps[i];
> > +
> > +		if (iecm_is_queue_model_split(vport->rxq_model)) {
> > +			num_rxq = rx_qgrp->splitq.num_rxq_sets;
> > +			for (j = 0; j < num_rxq; j++) {
> > +				q = &rx_qgrp->splitq.rxq_sets[j]->rxq;
> > +				iecm_rx_desc_rel(q, false,
> > +						 vport->rxq_model);
> > +			}
> > +
> > +			if (!rx_qgrp->splitq.bufq_sets)
> > +				continue;
> > +			for (j = 0; j < vport->num_bufqs_per_qgrp; j++) {
> > +				struct iecm_bufq_set *bufq_set =
> > +					&rx_qgrp->splitq.bufq_sets[j];
> > +
> > +				q = &bufq_set->bufq;
> > +				iecm_rx_desc_rel(q, true, vport->rxq_model);
> > +			}
> > +		} else {
> > +			for (j = 0; j < rx_qgrp->singleq.num_rxq; j++) {
> > +				q = rx_qgrp->singleq.rxqs[j];
> > +				iecm_rx_desc_rel(q, false,
> > +						 vport->rxq_model);
> > +			}
> > +		}
> > +	}
> > +}
> > +
> > +/**
> > + * iecm_rx_buf_hw_update - Store the new tail and head values
> > + * @rxq: queue to bump
> > + * @val: new head index
> > + */
> > +void iecm_rx_buf_hw_update(struct iecm_queue *rxq, u32 val) {
> > +	rxq->next_to_use = val;
> > +
> > +	if (unlikely(!rxq->tail))
> > +		return;
> > +	/* writel has an implicit memory barrier */
> > +	writel(val, rxq->tail);
> > +}
> > +
> > +/**
> > + * iecm_alloc_page - allocate page to back RX buffer
> > + * @rxbufq: pointer to queue struct
> > + * @page_info: pointer to page metadata struct  */ static int
> > +iecm_alloc_page(struct iecm_queue *rxbufq, struct iecm_page_info
> > +*page_info) {
> > +	/* alloc new page for storage */
> > +	page_info->page = alloc_page(GFP_ATOMIC | __GFP_NOWARN);
> > +	if (unlikely(!page_info->page))
> > +		return -ENOMEM;
> > +
> > +	/* map page for use */
> > +	page_info->dma = dma_map_page_attrs(rxbufq->dev, page_info-
> >page,
> > +					    0, PAGE_SIZE, DMA_FROM_DEVICE,
> > +					    IECM_RX_DMA_ATTR);
> > +
> > +	/* if mapping failed free memory back to system since
> > +	 * there isn't much point in holding memory we can't use
> > +	 */
> > +	if (dma_mapping_error(rxbufq->dev, page_info->dma)) {
> > +		__free_pages(page_info->page, 0);
> > +		return -ENOMEM;
> > +	}
> > +
> > +	page_info->page_offset = 0;
> > +
> > +	/* initialize pagecnt_bias to claim we fully own page */
> > +	page_ref_add(page_info->page, USHRT_MAX - 1);
> 
> Too many references to page_info->page, could be optimized by placing *page
> onstack and then assigning page_info->page later.
> 

Will fix

> > +	page_info->pagecnt_bias = USHRT_MAX;
> > +
> > +	return 0;
> > +}
> > +
> > +/**
> > + * iecm_init_rx_buf_hw_alloc - allocate initial RX buffer pages
> > + * @rxbufq: ring to use; equivalent to rxq when operating in singleq
> > +mode
> > + * @buf: rx_buffer struct to modify
> > + *
> > + * Returns true if the page was successfully allocated or
> > + * reused.
> > + */
> > +bool iecm_init_rx_buf_hw_alloc(struct iecm_queue *rxbufq, struct
> > +iecm_rx_buf *buf) {
> > +	if (iecm_alloc_page(rxbufq, &buf->page_info[0]))
> > +		return false;
> > +
> > +#if (PAGE_SIZE < 8192)
> > +	if (rxbufq->rx_buf_size > IECM_RX_BUF_2048)
> > +		if (iecm_alloc_page(rxbufq, &buf->page_info[1]))
> > +			return false;
> > +#endif
> 
> Same here with embedding the check.
> 
> > +
> > +	buf->page_indx = 0;
> > +	buf->buf_size = rxbufq->rx_buf_size;
> > +
> > +	return true;
> > +}
> > +
> > +/**
> > + * iecm_rx_hdr_buf_alloc_all - Allocate memory for header buffers
> > + * @rxq: ring to use
> > + *
> > + * Returns 0 on success, negative on failure.
> > + */
> > +static int iecm_rx_hdr_buf_alloc_all(struct iecm_queue *rxq) {
> > +	struct iecm_page_info *page_info;
> > +	int nr_pages, offset;
> > +	int i, j = 0;
> > +
> > +	rxq->rx_buf.hdr_buf = kcalloc(rxq->desc_count,
> > +				      sizeof(struct iecm_dma_mem *),
> > +				      GFP_KERNEL);
> > +	if (!rxq->rx_buf.hdr_buf)
> > +		return -ENOMEM;
> > +
> > +	for (i = 0; i < rxq->desc_count; i++) {
> > +		rxq->rx_buf.hdr_buf[i] = kcalloc(1,
> > +						 sizeof(struct
> iecm_dma_mem),
> > +						 GFP_KERNEL);
> > +		if (!rxq->rx_buf.hdr_buf[i])
> > +			goto unroll_buf_alloc;
> > +	}
> > +
> > +	/* Determine the number of pages necessary to back the total number
> of header buffers */
> > +	nr_pages = (rxq->desc_count * rxq->rx_hbuf_size) / PAGE_SIZE;
> > +	rxq->hbuf_pages.pages = kcalloc(nr_pages,
> > +					sizeof(struct iecm_page_info),
> > +					GFP_KERNEL);
> > +	if (!rxq->hbuf_pages.pages)
> > +		goto unroll_buf_alloc;
> > +
> > +	rxq->hbuf_pages.nr_pages = nr_pages;
> > +	for (i = 0; i < nr_pages; i++) {
> > +		if (iecm_alloc_page(rxq, &rxq->hbuf_pages.pages[i]))
> 
> And here you allocate pages with GFP_ATOMIC in process context.
> Atomic allocations must not be used if the function may sleep.
> Please add gfp_t gfp argument to iecm_alloc_page() and use GFP_KERNEL here
> (and GFP_ATOMIC on buffer refill hotpath).
> 

Perhaps I am confused here but it's my understanding we need GFP_ATOMIC when potentially used in a case where we can't sleep as it signals to the memory allocator to not sleep.  Not the other way around; we can't sleep if we have memory taken with GFP_ATOMIC.  We use it in hotpath as you said, where we can't sleep. What it really means to us is that it has a higher chance of failure to not get alloc'd if the kernel isn't allowed to sleep to free up some memory.

> > +			goto unroll_buf_alloc;
> > +	}
> > +
> > +	page_info = &rxq->hbuf_pages.pages[0];
> > +	for (i = 0, offset = 0; i < rxq->desc_count; i++, offset += rxq-
> >rx_hbuf_size) {
> > +		struct iecm_dma_mem *hbuf = rxq->rx_buf.hdr_buf[i];
> > +
> > +		/* Move to next page */
> > +		if (offset >= PAGE_SIZE) {
> > +			offset = 0;
> > +			page_info = &rxq->hbuf_pages.pages[++j];
> > +		}
> > +
> > +		hbuf->va = page_address(page_info->page) + offset;
> > +		hbuf->pa = page_info->dma + offset;
> > +		hbuf->size = rxq->rx_hbuf_size;
> > +	}
> > +
> > +	return 0;
> > +unroll_buf_alloc:
> > +	iecm_rx_hdr_buf_rel_all(rxq);
> > +	return -ENOMEM;
> > +}
> > +
> > +/**
> > + * iecm_rx_buf_hw_alloc_all - Allocate receive buffers
> > + * @rxbufq: queue for which the hw buffers are allocated; equivalent
> > +to rxq
> > + * when operating in singleq mode
> > + * @alloc_count: number of buffers to allocate
> > + *
> > + * Returns false if all allocations were successful, true if any fail
> > +*/ static bool iecm_rx_buf_hw_alloc_all(struct iecm_queue *rxbufq,
> > +u16 alloc_count) {
> > +	u16 nta = rxbufq->next_to_alloc;
> > +	struct iecm_rx_buf *buf;
> > +
> > +	if (!alloc_count)
> > +		return false;
> > +
> > +	buf = &rxbufq->rx_buf.buf[nta];
> > +
> > +	do {
> > +		if (!iecm_init_rx_buf_hw_alloc(rxbufq, buf))
> > +			break;
> > +
> > +		buf++;
> > +		nta++;
> > +		if (unlikely(nta == rxbufq->desc_count)) {
> 
> 		if (unlikely(++nta == ...)) { /* Just in one line */
> 

Yes but pre-increments are gross and hard for humans to grok.

> > +			buf = rxbufq->rx_buf.buf;
> > +			nta = 0;
> > +		}
> > +
> > +		alloc_count--;
> > +	} while (alloc_count);
> 
> 	} while (alloc_count--); /* Just in one line */
> 

I believe

} while (--alloc_count);

would be accurate but pre increment/decrement are hard for humans to grok (as evidenced here).

> > +
> > +	return !!alloc_count;
> > +}
> > +
> > +/**
> > + * iecm_rx_post_buf_desc - Post buffer to bufq descriptor ring
> > + * @bufq: buffer queue to post to
> > + * @buf_id: buffer id to post
> > + */
> > +static void iecm_rx_post_buf_desc(struct iecm_queue *bufq, u16
> > +buf_id) {
> > +	struct virtchnl2_splitq_rx_buf_desc *splitq_rx_desc = NULL;
> > +	struct iecm_page_info *page_info;
> > +	u16 nta = bufq->next_to_alloc;
> > +	struct iecm_rx_buf *buf;
> > +
> > +	splitq_rx_desc = IECM_SPLITQ_RX_BUF_DESC(bufq, nta);
> > +	buf = &bufq->rx_buf.buf[buf_id];
> > +	page_info = &buf->page_info[buf->page_indx];
> > +	if (bufq->rx_hsplit_en)
> > +		splitq_rx_desc->hdr_addr =
> > +cpu_to_le64(bufq->rx_buf.hdr_buf[buf_id]->pa);
> 
> 90-cols line.
> 

Yes there are many of these not otherwise pointed out.  Wil fix.

> > +
> > +	splitq_rx_desc->pkt_addr = cpu_to_le64(page_info->dma +
> > +					       page_info->page_offset);
> > +	splitq_rx_desc->qword0.buf_id = cpu_to_le16(buf_id);
> > +
> > +	nta++;
> > +	if (unlikely(nta == bufq->desc_count))
> > +		nta = 0;
> 
> Post-increment can be embedded into a condition check (with converting into a
> pre-increment obviously).
> 

See previous comments about pre-inrcrement.

> > +	bufq->next_to_alloc = nta;
> > +}
> > +
> > +/**
> > + * iecm_rx_post_init_bufs - Post initial buffers to bufq
> > + * @bufq: buffer queue to post working set to
> > + * @working_set: number of buffers to put in working set  */ static
> > +void iecm_rx_post_init_bufs(struct iecm_queue *bufq,
> > +				   u16 working_set)
> > +{
> > +	int i;
> > +
> > +	for (i = 0; i < working_set; i++)
> > +		iecm_rx_post_buf_desc(bufq, i);
> > +
> > +	iecm_rx_buf_hw_update(bufq, bufq->next_to_alloc &
> > +~(bufq->rx_buf_stride - 1));
> 
> 87-cols line.
> Please test all your patches with `checkpatch --strict --codespell`.
> 

Just an FYI, all of these patches do mostly pass checkpatch since otherwise (except for net apparently) in the kernel 100 cols are now acceptable. You must also add `--max-line-length=80` to get a warning about 80 cols now.

> > +}
> > +
> > +/**
> > + * iecm_rx_buf_alloc_all - Allocate memory for all buffer resources
> > + * @rxbufq: queue for which the buffers are allocated; equivalent to
> > + * rxq when operating in singleq mode
> > + *
> > + * Returns 0 on success, negative on failure  */ static int
> > +iecm_rx_buf_alloc_all(struct iecm_queue *rxbufq) {
> > +	int err = 0;
> > +
> > +	/* Allocate book keeping buffers */
> > +	rxbufq->rx_buf.buf = kcalloc(rxbufq->desc_count,
> > +				     sizeof(struct iecm_rx_buf), GFP_KERNEL);
> > +	if (!rxbufq->rx_buf.buf) {
> > +		err = -ENOMEM;
> > +		goto rx_buf_alloc_all_out;
> > +	}
> > +
> > +	if (rxbufq->rx_hsplit_en) {
> > +		err = iecm_rx_hdr_buf_alloc_all(rxbufq);
> > +		if (err)
> > +			goto rx_buf_alloc_all_out;
> > +	}
> > +
> > +	/* Allocate buffers to be given to HW.	 */
> > +	if (iecm_is_queue_model_split(rxbufq->vport->rxq_model)) {
> > +		if (iecm_rx_buf_hw_alloc_all(rxbufq, rxbufq->desc_count - 1))
> > +			err = -ENOMEM;
> > +	} else {
> > +		if (iecm_rx_singleq_buf_hw_alloc_all(rxbufq, rxbufq-
> >desc_count - 1))
> > +			err = -ENOMEM;
> > +	}
> > +
> > +rx_buf_alloc_all_out:
> > +	if (err)
> > +		iecm_rx_buf_rel_all(rxbufq);
> > +	return err;
> > +}
> > +
> > +/**
> > + * iecm_rx_desc_alloc - Allocate queue Rx resources
> > + * @rxq: Rx queue for which the resources are setup
> > + * @bufq: buffer or completion queue
> > + * @q_model: single or split queue model
> > + *
> > + * Returns 0 on success, negative on failure  */ static int
> > +iecm_rx_desc_alloc(struct iecm_queue *rxq, bool bufq, s32 q_model) {
> > +	struct device *dev = rxq->dev;
> > +
> > +	/* As both single and split descriptors are 32 byte, memory size
> > +	 * will be same for all three singleq_base rx, buf., splitq_base
> > +	 * rx. So pick anyone of them for size
> > +	 */
> > +	if (bufq) {
> > +		rxq->size = rxq->desc_count *
> > +			sizeof(struct virtchnl2_splitq_rx_buf_desc);
> > +	} else {
> > +		rxq->size = rxq->desc_count *
> > +			sizeof(union virtchnl2_rx_desc);
> > +	}
> 
> Oneliners, braces are unneeded.
> 

Keeping because multi-line with line wrap.

> For counting the array sizes it's required to use array_size():
> 
> 	rxq->size = array_size(rxq->desc_count, sizeof(...));
> 	if (unlikely(rxq->size == -EOVERFLOW))
> 		/* Error path */
> 
> There are more such places in the code, I couldn't catch them all.
> 

Will fix.

> > +
> > +	/* Allocate descriptors and also round up to nearest 4K */
> > +	rxq->size = ALIGN(rxq->size, 4096);
> 
> 4096 = SZ_4K, no need to open-code.
> 

Will fix.

> > +	rxq->desc_ring = dmam_alloc_coherent(dev, rxq->size,
> > +					     &rxq->dma, GFP_KERNEL);
> > +	if (!rxq->desc_ring) {
> > +		dev_info(dev, "Unable to allocate memory for the Rx descriptor
> ring, size=%d\n",
> > +			 rxq->size);
> > +		return -ENOMEM;
> > +	}
> > +
> > +	rxq->next_to_alloc = 0;
> > +	rxq->next_to_clean = 0;
> > +	rxq->next_to_use = 0;
> 
> You allocate rxq with kzalloc() (or derivative) IIRC, 'z'-versions zero the memory
> before returning. These initializers are redundant.
> 

This is allocating descriptors which can change.  If we change the descriptor ring it's probably a good idea to reset the queue indexes.

> > +	set_bit(__IECM_Q_GEN_CHK, rxq->flags);
> > +
> > +	/* Allocate buffers for a rx queue if the q_model is single OR if it
> > +	 * is a buffer queue in split queue model
> > +	 */
> > +	if (bufq || !iecm_is_queue_model_split(q_model)) {
> > +		int err = 0;
> > +
> > +		err = iecm_rx_buf_alloc_all(rxq);
> > +		if (err) {
> > +			iecm_rx_desc_rel(rxq, bufq, q_model);
> > +			return err;
> > +		}
> > +	}
> 
> 	if (inverse_the_condition_above)
> 		return 0;
> 
> 	err = ...
> 
> -1 indent level.
> 

Will fix

> > +	return 0;
> > +}
> > +
> > +/**
> > + * iecm_rx_desc_alloc_all - allocate all RX queues resources
> > + * @vport: virtual port structure
> > + *
> > + * Returns 0 on success, negative on failure  */ static int
> > +iecm_rx_desc_alloc_all(struct iecm_vport *vport) {
> > +	struct device *dev = &vport->adapter->pdev->dev;
> > +	struct iecm_rxq_group *rx_qgrp;
> > +	int i, j, num_rxq, working_set;
> > +	struct iecm_queue *q;
> > +	int err = 0;
> > +
> > +	for (i = 0; i < vport->num_rxq_grp; i++) {
> > +		rx_qgrp = &vport->rxq_grps[i];
> > +		if (iecm_is_queue_model_split(vport->rxq_model))
> > +			num_rxq = rx_qgrp->splitq.num_rxq_sets;
> > +		else
> > +			num_rxq = rx_qgrp->singleq.num_rxq;
> > +
> > +		for (j = 0; j < num_rxq; j++) {
> > +			if (iecm_is_queue_model_split(vport->rxq_model))
> > +				q = &rx_qgrp->splitq.rxq_sets[j]->rxq;
> > +			else
> > +				q = rx_qgrp->singleq.rxqs[j];
> > +			err = iecm_rx_desc_alloc(q, false, vport->rxq_model);
> > +			if (err) {
> > +				dev_err(dev, "Memory allocation for Rx Queue
> %u failed\n",
> > +					i);
> > +				goto err_out;
> > +			}
> > +		}
> > +
> > +		if (iecm_is_queue_model_split(vport->rxq_model)) {
> > +			for (j = 0; j < vport->num_bufqs_per_qgrp; j++) {
> > +				q = &rx_qgrp->splitq.bufq_sets[j].bufq;
> > +				err = iecm_rx_desc_alloc(q, true,
> > +							 vport->rxq_model);
> > +				if (err) {
> > +					dev_err(dev, "Memory allocation for
> Rx Buffer Queue %u failed\n",
> > +						i);
> > +					goto err_out;
> > +				}
> > +
> > +				working_set =
> IECM_RX_BUFQ_WORKING_SET(q);
> > +				iecm_rx_post_init_bufs(q, working_set);
> > +			}
> > +		}
> > +	}
> > +err_out:
> > +	if (err)
> > +		iecm_rx_desc_rel_all(vport);
> > +	return err;
> > +}
> > +
> >  /**
> >   * iecm_txq_group_rel - Release all resources for txq groups
> >   * @vport: vport to release txq groups on @@ -478,6 +1017,61 @@
> > static void iecm_txq_group_rel(struct iecm_vport *vport)
> >  	}
> >  }
> >
> > +/**
> > + * iecm_rxq_sw_queue_rel - Release software queue resources
> > + * @rx_qgrp: rx queue group with software queues  */ static void
> > +iecm_rxq_sw_queue_rel(struct iecm_rxq_group *rx_qgrp) {
> > +	int i, j;
> > +
> > +	for (i = 0; i < rx_qgrp->vport->num_bufqs_per_qgrp; i++) {
> > +		struct iecm_bufq_set *bufq_set = &rx_qgrp->splitq.bufq_sets[i];
> > +
> > +		for (j = 0; j < bufq_set->num_refillqs; j++) {
> > +			kfree(bufq_set->refillqs[j].ring);
> > +			bufq_set->refillqs[j].ring = NULL;
> > +		}
> > +		kfree(bufq_set->refillqs);
> > +		bufq_set->refillqs = NULL;
> > +	}
> > +}
> > +
> > +/**
> > + * iecm_rxq_group_rel - Release all resources for rxq groups
> > + * @vport: vport to release rxq groups on  */ static void
> > +iecm_rxq_group_rel(struct iecm_vport *vport) {
> > +	if (vport->rxq_grps) {
> > +		int i;
> > +
> > +		for (i = 0; i < vport->num_rxq_grp; i++) {
> > +			struct iecm_rxq_group *rx_qgrp = &vport->rxq_grps[i];
> > +			int j, num_rxq;
> > +
> > +			if (iecm_is_queue_model_split(vport->rxq_model)) {
> > +				num_rxq = rx_qgrp->splitq.num_rxq_sets;
> > +				for (j = 0; j < num_rxq; j++) {
> > +					kfree(rx_qgrp->splitq.rxq_sets[j]);
> > +					rx_qgrp->splitq.rxq_sets[j] = NULL;
> > +				}
> > +				iecm_rxq_sw_queue_rel(rx_qgrp);
> > +				kfree(rx_qgrp->splitq.bufq_sets);
> > +				rx_qgrp->splitq.bufq_sets = NULL;
> > +			} else {
> > +				num_rxq = rx_qgrp->singleq.num_rxq;
> > +				for (j = 0; j < num_rxq; j++) {
> > +					kfree(rx_qgrp->singleq.rxqs[j]);
> > +					rx_qgrp->singleq.rxqs[j] = NULL;
> > +				}
> > +			}
> > +		}
> > +		kfree(vport->rxq_grps);
> > +		vport->rxq_grps = NULL;
> > +	}
> 
> 	if (!rxq_grps)
> 		return;
> 
> -1 lvl.
> 

Will fix

> > +}
> > +
> >  /**
> >   * iecm_vport_queue_grp_rel_all - Release all queue groups
> >   * @vport: vport to release queue groups for @@ -485,6 +1079,7 @@
> > static void iecm_txq_group_rel(struct iecm_vport *vport)  static void
> > iecm_vport_queue_grp_rel_all(struct iecm_vport *vport)  {
> >  	iecm_txq_group_rel(vport);
> > +	iecm_rxq_group_rel(vport);
> >  }
> >
> >  /**
> > @@ -496,6 +1091,7 @@ static void iecm_vport_queue_grp_rel_all(struct
> > iecm_vport *vport)  void iecm_vport_queues_rel(struct iecm_vport
> > *vport)  {
> >  	iecm_tx_desc_rel_all(vport);
> > +	iecm_rx_desc_rel_all(vport);
> >  	iecm_vport_queue_grp_rel_all(vport);
> >
> >  	kfree(vport->txqs);
> > @@ -715,6 +1311,24 @@ void iecm_vport_calc_num_q_vec(struct
> iecm_vport
> > *vport)  }  EXPORT_SYMBOL(iecm_vport_calc_num_q_vec);
> >
> > +/**
> > + * iecm_rxq_set_descids - set the descids supported by this queue
> > + * @vport: virtual port data structure
> > + * @q: rx queue for which descids are set
> > + *
> > + */
> > +static void iecm_rxq_set_descids(struct iecm_vport *vport, struct
> > +iecm_queue *q) {
> > +	if (vport->rxq_model == VIRTCHNL2_QUEUE_MODEL_SPLIT) {
> > +		q->rxdids = VIRTCHNL2_RXDID_1_FLEX_SPLITQ_M;
> > +	} else {
> > +		if (vport->base_rxd)
> > +			q->rxdids = VIRTCHNL2_RXDID_1_32B_BASE_M;
> > +		else
> > +			q->rxdids = VIRTCHNL2_RXDID_2_FLEX_SQ_NIC_M;
> > +	}
> > +}
> > +
> >  /**
> >   * iecm_set_vlan_tag_loc - set the tag location for a tx/rx queue
> >   * @adapter: adapter structure
> > @@ -827,6 +1441,152 @@ static int iecm_txq_group_alloc(struct iecm_vport
> *vport, int num_txq)
> >  	return err;
> >  }
> >
> > +/**
> > + * iecm_rxq_group_alloc - Allocate all rxq group resources
> > + * @vport: vport to allocate rxq groups for
> > + * @num_rxq: number of rxqs to allocate for each group
> > + *
> > + * Returns 0 on success, negative on failure  */ static int
> > +iecm_rxq_group_alloc(struct iecm_vport *vport, int num_rxq) {
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	struct iecm_queue *q;
> > +	int i, k, err = 0;
> > +
> > +	vport->rxq_grps = kcalloc(vport->num_rxq_grp,
> > +				  sizeof(struct iecm_rxq_group), GFP_KERNEL);
> > +	if (!vport->rxq_grps)
> > +		return -ENOMEM;
> > +
> > +	for (i = 0; i < vport->num_rxq_grp; i++) {
> > +		struct iecm_rxq_group *rx_qgrp = &vport->rxq_grps[i];
> > +		int j;
> > +
> > +		rx_qgrp->vport = vport;
> > +		if (iecm_is_queue_model_split(vport->rxq_model)) {
> > +			rx_qgrp->splitq.num_rxq_sets = num_rxq;
> > +
> > +			for (j = 0; j < num_rxq; j++) {
> > +				rx_qgrp->splitq.rxq_sets[j] =
> > +					kzalloc(sizeof(struct iecm_rxq_set),
> > +						GFP_KERNEL);
> > +				if (!rx_qgrp->splitq.rxq_sets[j]) {
> > +					err = -ENOMEM;
> > +					goto err_alloc;
> > +				}
> > +			}
> > +
> > +			rx_qgrp->splitq.bufq_sets = kcalloc(vport-
> >num_bufqs_per_qgrp,
> > +							    sizeof(struct
> iecm_bufq_set),
> > +							    GFP_KERNEL);
> > +			if (!rx_qgrp->splitq.bufq_sets) {
> > +				err = -ENOMEM;
> > +				goto err_alloc;
> > +			}
> > +
> > +			for (j = 0; j < vport->num_bufqs_per_qgrp; j++) {
> > +				struct iecm_bufq_set *bufq_set =
> > +					&rx_qgrp->splitq.bufq_sets[j];
> > +				int swq_size = sizeof(struct iecm_sw_queue);
> > +
> > +				q = &rx_qgrp->splitq.bufq_sets[j].bufq;
> > +				q->dev = &adapter->pdev->dev;
> > +				q->desc_count = vport->bufq_desc_count[j];
> > +				q->vport = vport;
> > +				q->rxq_grp = rx_qgrp;
> > +				q->idx = j;
> > +				q->rx_buf_size = vport->bufq_size[j];
> > +				q->rx_buffer_low_watermark =
> IECM_LOW_WATERMARK;
> > +				q->rx_buf_stride = IECM_RX_BUF_STRIDE;
> > +
> > +				if (test_bit(__IECM_PRIV_FLAGS_HDR_SPLIT,
> > +					     adapter->config_data.user_flags)) {
> > +					q->rx_hsplit_en = true;
> > +					q->rx_hbuf_size =
> IECM_HDR_BUF_SIZE;
> > +				}
> > +
> > +				bufq_set->num_refillqs = num_rxq;
> > +				bufq_set->refillqs = kcalloc(num_rxq,
> > +							     swq_size,
> > +							     GFP_KERNEL);
> > +				if (!bufq_set->refillqs) {
> > +					err = -ENOMEM;
> > +					goto err_alloc;
> > +				}
> > +				for (k = 0; k < bufq_set->num_refillqs; k++) {
> > +					struct iecm_sw_queue *refillq =
> > +						&bufq_set->refillqs[k];
> > +
> > +					refillq->dev =
> > +						&vport->adapter->pdev->dev;
> > +					refillq->buf_size = q->rx_buf_size;
> > +					refillq->desc_count =
> > +						vport->bufq_desc_count[j];
> > +					set_bit(__IECM_Q_GEN_CHK,
> > +						refillq->flags);
> > +					set_bit(__IECM_RFLQ_GEN_CHK,
> > +						refillq->flags);
> > +					refillq->ring = kcalloc(refillq-
> >desc_count,
> > +								sizeof(u16),
> > +								GFP_KERNEL);
> > +					if (!refillq->ring) {
> > +						err = -ENOMEM;
> > +						goto err_alloc;
> > +					}
> > +				}
> > +			}
> > +		} else {
> > +			rx_qgrp->singleq.num_rxq = num_rxq;
> > +			for (j = 0; j < num_rxq; j++) {
> > +				rx_qgrp->singleq.rxqs[j] =
> > +					kzalloc(sizeof(*rx_qgrp-
> >singleq.rxqs[j]), GFP_KERNEL);
> > +				if (!rx_qgrp->singleq.rxqs[j]) {
> > +					err = -ENOMEM;
> > +					goto err_alloc;
> > +				}
> > +			}
> > +		}
> > +
> > +		for (j = 0; j < num_rxq; j++) {
> > +			if (iecm_is_queue_model_split(vport->rxq_model)) {
> > +				q = &rx_qgrp->splitq.rxq_sets[j]->rxq;
> > +				rx_qgrp->splitq.rxq_sets[j]->refillq0 =
> > +				      &rx_qgrp->splitq.bufq_sets[0].refillqs[j];
> > +				rx_qgrp->splitq.rxq_sets[j]->refillq1 =
> > +				      &rx_qgrp->splitq.bufq_sets[1].refillqs[j];
> > +
> > +				if (test_bit(__IECM_PRIV_FLAGS_HDR_SPLIT,
> > +					     adapter->config_data.user_flags)) {
> > +					q->rx_hsplit_en = true;
> > +					q->rx_hbuf_size =
> IECM_HDR_BUF_SIZE;
> > +				}
> > +			} else {
> > +				q = rx_qgrp->singleq.rxqs[j];
> > +			}
> > +			q->dev = &adapter->pdev->dev;
> > +			q->desc_count = vport->rxq_desc_count;
> > +			q->vport = vport;
> > +			q->rxq_grp = rx_qgrp;
> > +			q->idx = (i * num_rxq) + j;
> > +			/* In splitq mode, RXQ buffer size should be
> > +			 * set to that of the first buffer queue
> > +			 * associated with this RXQ
> > +			 */
> > +			q->rx_buf_size = vport->bufq_size[0];
> > +			q->rx_buffer_low_watermark =
> IECM_LOW_WATERMARK;
> > +			q->rx_max_pkt_size = vport->netdev->mtu +
> > +
> 	IECM_PACKET_HDR_PAD;
> > +			iecm_rxq_set_descids(vport, q);
> > +			iecm_set_vlan_tag_loc(adapter, q);
> > +		}
> > +	}
> > +err_alloc:
> > +	if (err)
> > +		iecm_rxq_group_rel(vport);
> > +	return err;
> > +}
> > +
> >  /**
> >   * iecm_vport_queue_grp_alloc_all - Allocate all queue groups/resources
> >   * @vport: vport with qgrps to allocate @@ -841,6 +1601,11 @@ static
> > int iecm_vport_queue_grp_alloc_all(struct iecm_vport *vport)
> >  	iecm_vport_calc_numq_per_grp(vport, &num_txq, &num_rxq);
> >
> >  	err = iecm_txq_group_alloc(vport, num_txq);
> > +	if (err)
> > +		goto err_out;
> > +
> > +	err = iecm_rxq_group_alloc(vport, num_rxq);
> > +err_out:
> >  	if (err)
> >  		iecm_vport_queue_grp_rel_all(vport);
> >  	return err;
> > @@ -866,6 +1631,10 @@ int iecm_vport_queues_alloc(struct iecm_vport
> *vport)
> >  	if (err)
> >  		goto err_out;
> >
> > +	err = iecm_rx_desc_alloc_all(vport);
> > +	if (err)
> > +		goto err_out;
> > +
> >  	err = iecm_vport_init_fast_path_txqs(vport);
> >  	if (err)
> >  		goto err_out;
> > diff --git a/drivers/net/ethernet/intel/include/iecm_txrx.h
> > b/drivers/net/ethernet/intel/include/iecm_txrx.h
> > index 44c20f8a2039..5e29148938fb 100644
> > --- a/drivers/net/ethernet/intel/include/iecm_txrx.h
> > +++ b/drivers/net/ethernet/intel/include/iecm_txrx.h
> > @@ -198,6 +198,11 @@ struct iecm_page_info {
> >  	u16 pagecnt_bias;
> >  };
> >
> > +struct iecm_rx_hdr_buf_pages {
> > +	u32 nr_pages;
> > +	struct iecm_page_info *pages;
> 
> Place the pointer at the beginning to avoid gaps and alignment issues.
> 
> > +};
> > +
> >  struct iecm_rx_buf {
> >  #define IECM_RX_BUF_MAX_PAGES 2
> >  	struct iecm_page_info page_info[IECM_RX_BUF_MAX_PAGES]; @@ -
> 498,6
> > +503,8 @@ struct iecm_queue {
> >  					 * with scatter-gather
> >  					 */
> >  	DECLARE_HASHTABLE(sched_buf_hash, 12);
> > +
> > +	struct iecm_rx_hdr_buf_pages hbuf_pages;
> >  } ____cacheline_internodealigned_in_smp;
> >
> >  /* Software queues are used in splitq mode to manage buffers between
> > rxq
> > --
> > 2.33.0
> 
> Thanks,
> Al


^ permalink raw reply	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 13/19] iecm: implement splitq napi_poll
  2022-01-28 17:38   ` Alexander Lobakin
@ 2022-02-03  1:07     ` Brady, Alan
  2022-02-04 11:50       ` Alexander Lobakin
  0 siblings, 1 reply; 89+ messages in thread
From: Brady, Alan @ 2022-02-03  1:07 UTC (permalink / raw)
  To: intel-wired-lan

> -----Original Message-----
> From: Lobakin, Alexandr <alexandr.lobakin@intel.com>
> Sent: Friday, January 28, 2022 9:39 AM
> To: Brady, Alan <alan.brady@intel.com>
> Cc: Lobakin, Alexandr <alexandr.lobakin@intel.com>; intel-wired-
> lan at lists.osuosl.org; Burra, Phani R <phani.r.burra@intel.com>; Chittim, Madhu
> <madhu.chittim@intel.com>; Linga, Pavan Kumar
> <pavan.kumar.linga@intel.com>
> Subject: Re: [Intel-wired-lan] [PATCH net-next 13/19] iecm: implement splitq
> napi_poll
> 
> From: Alan Brady <alan.brady@intel.com>
> Date: Thu, 27 Jan 2022 16:10:03 -0800
> 
> > This adds everything we need to actually receive packets and process spent
> > buffers using the splitq model. This contrasts to more traditional queueing
> > models by essentially splitting a normal queue of descriptors and mapped
> > buffers into separate queues. This allows us to deal with both more
> > efficiently and also allows us to implement asymmetric queuing setups where
> > you have multiple completion queues associated with a single buffer queue.
> >
> > Signed-off-by: Phani Burra <phani.r.burra@intel.com>
> > Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
> > Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
> > Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
> > Signed-off-by: Alan Brady <alan.brady@intel.com>
> > ---
> >  drivers/net/ethernet/intel/iecm/iecm_txrx.c   | 1468 ++++++++++++++++-
> >  drivers/net/ethernet/intel/include/iecm.h     |    4 +
> >  .../net/ethernet/intel/include/iecm_txrx.h    |   20 +
> >  3 files changed, 1490 insertions(+), 2 deletions(-)
> >
> > diff --git a/drivers/net/ethernet/intel/iecm/iecm_txrx.c
> b/drivers/net/ethernet/intel/iecm/iecm_txrx.c
> > index 4b9288e1c254..85a82b58525a 100644
> > --- a/drivers/net/ethernet/intel/iecm/iecm_txrx.c
> > +++ b/drivers/net/ethernet/intel/iecm/iecm_txrx.c
> > @@ -218,6 +218,36 @@ const struct iecm_rx_ptype_decoded
> iecm_ptype_lookup[IECM_RX_MAX_PTYPE] = {
> >  };
> >  EXPORT_SYMBOL(iecm_ptype_lookup);
> >
> > +/**
> > + * iecm_buf_lifo_push - push a buffer pointer onto stack
> > + * @stack: pointer to stack struct
> > + * @buf: pointer to buf to push
> > + *
> > + * Returns 0 on success, negative on failure
> > + **/
> > +static int iecm_buf_lifo_push(struct iecm_buf_lifo *stack,
> > +			      struct iecm_tx_buf *buf)
> > +{
> > +	if (stack->top == stack->size)
> 
> How frequent is this? A candidate for unlikely() maybe?
> 

Sure seems reasonable.

> > +		return -ENOSPC;
> > +
> > +	stack->bufs[stack->top++] = buf;
> > +
> > +	return 0;
> > +}
> > +
> > +/**
> > + * iecm_buf_lifo_pop - pop a buffer pointer from stack
> > + * @stack: pointer to stack struct
> > + **/
> > +static struct iecm_tx_buf *iecm_buf_lifo_pop(struct iecm_buf_lifo *stack)
> > +{
> > +	if (!stack->top)
> > +		return NULL;
> > +
> > +	return stack->bufs[--stack->top];
> > +}
> > +
> >  /**
> >   * iecm_get_stats64 - get statistics for network device structure
> >   * @netdev: network interface device structure
> > @@ -812,6 +842,30 @@ iecm_rx_buf_hw_alloc_all(struct iecm_queue
> *rxbufq, u16 alloc_count)
> >  	return !!alloc_count;
> >  }
> >
> > +/**
> > + * iecm_rx_post_buf_refill - Post buffer id to refill queue
> > + * @refillq: refill queue to post to
> > + * @buf_id: buffer id to post
> > + */
> > +void iecm_rx_post_buf_refill(struct iecm_sw_queue *refillq, u16 buf_id)
> > +{
> > +	u16 nta = refillq->next_to_alloc;
> > +	u16 *bi;
> > +
> > +	bi = IECM_SPLITQ_RX_BI_DESC(refillq, nta);
> > +	/* store the buffer ID and the SW maintained GEN bit to the refillq */
> > +	*bi = ((buf_id << IECM_RX_BI_BUFID_S) & IECM_RX_BI_BUFID_M) |
> > +	      (!!(test_bit(__IECM_Q_GEN_CHK, refillq->flags)) <<
> > +	       IECM_RX_BI_GEN_S);
> 
> Please use FIELD_GET() and FIELD_PREP() for masks. This won't pass
> the maintainers.
> 

We've never had a problem before, I'm assuming these are new? Will check.

> > +
> > +	nta++;
> > +	if (unlikely(nta == refillq->desc_count)) {
> 
> Could be compressed into one line.
> 

Could be, but we'd prefer not to.

> > +		nta = 0;
> > +		change_bit(__IECM_Q_GEN_CHK, refillq->flags);
> > +	}
> > +	refillq->next_to_alloc = nta;
> > +}
> > +
> >  /**
> >   * iecm_rx_post_buf_desc - Post buffer to bufq descriptor ring
> >   * @bufq: buffer queue to post to
> > @@ -1670,6 +1724,398 @@ int iecm_vport_queues_alloc(struct iecm_vport
> *vport)
> >  	return err;
> >  }
> >
> > +/**
> > + * iecm_tx_find_q - Find the tx q based on q id
> > + * @vport: the vport we care about
> > + * @q_id: Id of the queue
> > + *
> > + * Returns queue ptr if found else returns NULL
> > + */
> > +static struct iecm_queue *
> > +iecm_tx_find_q(struct iecm_vport *vport, int q_id)
> > +{
> > +	int i;
> > +
> > +	for (i = 0; i < vport->num_txq; i++) {
> > +		struct iecm_queue *tx_q = vport->txqs[i];
> > +
> > +		if (tx_q->q_id == q_id)
> > +			return tx_q;
> > +	}
> 
> 	for (i = ...)
> 		if (vport->txqs[i].q_id == q_id)
> 			return tx_q;
> 
> No need to create a variable.
>

It would actually look like

 	for (i = ...)
 		if (vport->txqs[i]->q_id == q_id)
 			return vport->txqs[i];


You had another comment about adding a vc_ops variable where it was being used twice.  I'm not seeing a huge difference here and seems like splitting hairs. I think we would prefer to keep this.

> > +
> > +	return NULL;
> > +}
> > +
> > +/**
> > + * iecm_tx_handle_sw_marker - Handle queue marker packet
> > + * @tx_q: tx queue to handle software marker
> > + */
> > +static void iecm_tx_handle_sw_marker(struct iecm_queue *tx_q)
> > +{
> > +	struct iecm_vport *vport = tx_q->vport;
> > +	bool drain_complete = true;
> > +	int i;
> > +
> > +	clear_bit(__IECM_Q_SW_MARKER, tx_q->flags);
> > +	/* Hardware must write marker packets to all queues associated with
> > +	 * completion queues. So check if all queues received marker packets
> > +	 */
> > +	for (i = 0; i < vport->num_txq; i++) {
> > +		if (test_bit(__IECM_Q_SW_MARKER, vport->txqs[i]->flags))
> > +			drain_complete = false;
> > +	}
> > +	if (drain_complete) {
> > +		set_bit(__IECM_SW_MARKER, vport->adapter->flags);
> > +		wake_up(&vport->adapter->sw_marker_wq);
> > +	}
> > +}
> > +
> > +/**
> > + * iecm_tx_splitq_clean_buf - Clean TX buffer resources
> > + * @tx_q: tx queue to clean buffer from
> > + * @tx_buf: buffer to be cleaned
> > + * @napi_budget: Used to determine if we are in netpoll
> > + */
> > +static void
> > +iecm_tx_splitq_clean_buf(struct iecm_queue *tx_q, struct iecm_tx_buf
> *tx_buf,
> > +			 int napi_budget)
> > +{
> > +	/* unmap skb header data */
> > +	dma_unmap_single(tx_q->dev,
> > +			 dma_unmap_addr(tx_buf, dma),
> 
> These two lines can fit into 79 without wrapping.
> 
> > +			 dma_unmap_len(tx_buf, len),
> > +			 DMA_TO_DEVICE);
> 
> These two as well.
> 

Will fix.

> > +
> > +	napi_consume_skb(tx_buf->skb, napi_budget);
> > +
> > +	/* clear tx_buf data */
> > +	tx_buf->skb = NULL;
> > +	dma_unmap_len_set(tx_buf, len, 0);
> > +}
> > +
> > +/**
> > + * iecm_stash_flow_sch_buffers - store buffere parameter info to be freed at
> a
> > + * later time (only relevant for flow scheduling mode)
> > + * @txq: Tx queue to clean
> > + * @tx_buf: buffer to store
> > + */
> > +static int
> > +iecm_stash_flow_sch_buffers(struct iecm_queue *txq, struct iecm_tx_buf
> *tx_buf)
> > +{
> > +	struct iecm_adapter *adapter = txq->vport->adapter;
> > +	struct iecm_tx_buf *shadow_buf;
> > +
> > +	shadow_buf = iecm_buf_lifo_pop(&txq->buf_stack);
> > +	if (!shadow_buf) {
> > +		dev_err(&adapter->pdev->dev,
> > +			"No out-of-order TX buffers left!\n");
> > +		return -ENOMEM;
> > +	}
> > +
> > +	/* Store buffer params in shadow buffer */
> > +	shadow_buf->skb = tx_buf->skb;
> > +	shadow_buf->bytecount = tx_buf->bytecount;
> > +	shadow_buf->gso_segs = tx_buf->gso_segs;
> > +	dma_unmap_addr_set(shadow_buf, dma, dma_unmap_addr(tx_buf,
> dma));
> > +	dma_unmap_len_set(shadow_buf, len, dma_unmap_len(tx_buf, len));
> > +	shadow_buf->compl_tag = tx_buf->compl_tag;
> > +
> > +	/* Add buffer to buf_hash table to be freed
> > +	 * later
> > +	 */
> > +	hash_add(txq->sched_buf_hash, &shadow_buf->hlist,
> > +		 shadow_buf->compl_tag);
> > +
> > +	memset(tx_buf, 0, sizeof(struct iecm_tx_buf));
> > +
> > +	return 0;
> > +}
> > +
> > +/**
> > + * iecm_tx_splitq_clean - Reclaim resources from buffer queue
> > + * @tx_q: Tx queue to clean
> > + * @end: queue index until which it should be cleaned
> > + * @napi_budget: Used to determine if we are in netpoll
> > + * @descs_only: true if queue is using flow-based scheduling and should
> > + * not clean buffers at this time
> > + *
> > + * Cleans the queue descriptor ring. If the queue is using queue-based
> > + * scheduling, the buffers will be cleaned as well and this function will
> > + * return the number of bytes/packets cleaned. If the queue is using flow-
> based
> > + * scheduling, only the descriptors are cleaned at this time. Separate packet
> > + * completion events will be reported on the completion queue, and the
> buffers
> > + * will be cleaned separately. The stats returned from this function when
> using
> > + * flow-based scheduling are irrelevant.
> > + */
> > +static struct iecm_tx_queue_stats
> > +iecm_tx_splitq_clean(struct iecm_queue *tx_q, u16 end, int napi_budget,
> > +		     bool descs_only)
> > +{
> > +	union iecm_tx_flex_desc *next_pending_desc = NULL;
> > +	struct iecm_tx_queue_stats cleaned_stats = {0};
> > +	union iecm_tx_flex_desc *tx_desc;
> > +	s16 ntc = tx_q->next_to_clean;
> > +	struct iecm_tx_buf *tx_buf;
> > +	unsigned short gso_segs = 0;
> > +	unsigned int bytecount = 0;
> > +	struct netdev_queue *nq;
> > +
> > +	tx_desc = IECM_FLEX_TX_DESC(tx_q, ntc);
> > +	next_pending_desc = IECM_FLEX_TX_DESC(tx_q, end);
> > +	tx_buf = &tx_q->tx_buf[ntc];
> > +	ntc -= tx_q->desc_count;
> > +
> > +	while (tx_desc != next_pending_desc) {
> > +		union iecm_tx_flex_desc *eop_desc =
> > +			(union iecm_tx_flex_desc *)tx_buf->next_to_watch;
> > +
> > +		/* clear next_to_watch to prevent false hangs */
> > +		tx_buf->next_to_watch = NULL;
> > +
> > +		bytecount += tx_buf->bytecount;
> > +		gso_segs += tx_buf->gso_segs;
> > +
> > +		if (descs_only) {
> > +			if (iecm_stash_flow_sch_buffers(tx_q, tx_buf))
> > +				goto tx_splitq_clean_out;
> > +
> > +			while (tx_desc != eop_desc) {
> > +				tx_buf++;
> > +				tx_desc++;
> > +				ntc++;
> > +				if (unlikely(!ntc)) {
> > +					ntc -= tx_q->desc_count;
> > +					tx_buf = tx_q->tx_buf;
> > +					tx_desc = IECM_FLEX_TX_DESC(tx_q,
> 0);
> > +				}
> > +
> > +				if (dma_unmap_len(tx_buf, len)) {
> > +					if (iecm_stash_flow_sch_buffers(tx_q,
> > +
> 	tx_buf))
> > +						goto tx_splitq_clean_out;
> > +				}
> 
> Redundant braces, redundant nested ifs.
> 
> Also, it's actually an error, since dma_unmap_len() always equals 0
> for architectures which don't select NEED_DMA_MAP_STATE. So this
> should either check for any other signs of mapped buffer or field
> `len` should be present and defined with correct values
> unconditionally.
> 

We'll take a look.

> > +			}
> > +		} else {
> > +			/* update the statistics for this packet */
> > +			cleaned_stats.bytes += tx_buf->bytecount;
> > +			cleaned_stats.packets += tx_buf->gso_segs;
> > +
> > +			iecm_tx_splitq_clean_buf(tx_q, tx_buf, napi_budget);
> > +
> > +			/* unmap remaining buffers */
> > +			while (tx_desc != eop_desc) {
> > +				tx_buf++;
> > +				tx_desc++;
> > +				ntc++;
> > +				if (unlikely(!ntc)) {
> > +					ntc -= tx_q->desc_count;
> > +					tx_buf = tx_q->tx_buf;
> > +					tx_desc = IECM_FLEX_TX_DESC(tx_q,
> 0);
> > +				}
> > +
> > +				/* unmap any remaining paged data */
> > +				if (dma_unmap_len(tx_buf, len)) {
> 
> Same here.
> 
> > +					dma_unmap_page(tx_q->dev,
> > +						       dma_unmap_addr(tx_buf,
> dma),
> > +						       dma_unmap_len(tx_buf,
> len),
> > +						       DMA_TO_DEVICE);
> > +					dma_unmap_len_set(tx_buf, len, 0);
> > +				}
> > +			}
> > +		}
> > +
> > +		tx_buf++;
> > +		tx_desc++;
> > +		ntc++;
> > +		if (unlikely(!ntc)) {
> > +			ntc -= tx_q->desc_count;
> > +			tx_buf = tx_q->tx_buf;
> > +			tx_desc = IECM_FLEX_TX_DESC(tx_q, 0);
> > +		}
> > +	}
> > +
> > +tx_splitq_clean_out:
> > +	ntc += tx_q->desc_count;
> > +	tx_q->next_to_clean = ntc;
> > +
> > +	nq = netdev_get_tx_queue(tx_q->vport->netdev, tx_q->idx);
> > +	netdev_tx_completed_queue(nq, gso_segs, bytecount);
> > +
> > +	return cleaned_stats;
> > +}
> > +
> > +/**
> > + * iecm_tx_clean_flow_sch_bufs - clean bufs that were stored for
> > + * out of order completions
> > + * @txq: queue to clean
> > + * @compl_tag: completion tag of packet to clean (from completion
> descriptor)
> > + * @budget: Used to determine if we are in netpoll
> > + */
> > +static struct iecm_tx_queue_stats
> > +iecm_tx_clean_flow_sch_bufs(struct iecm_queue *txq, u16 compl_tag,
> > +			    int budget)
> > +{
> > +	struct iecm_tx_queue_stats cleaned_stats = {0};
> > +	struct hlist_node *tmp_buf = NULL;
> > +	struct iecm_tx_buf *tx_buf = NULL;
> > +
> > +	/* Buffer completion */
> > +	hash_for_each_possible_safe(txq->sched_buf_hash, tx_buf, tmp_buf,
> > +				    hlist, compl_tag) {
> > +		if (tx_buf->compl_tag != compl_tag)
> > +			continue;
> > +
> > +		if (likely(tx_buf->skb)) {
> > +			/* update the statistics for this packet */
> > +			cleaned_stats.bytes += tx_buf->bytecount;
> > +			cleaned_stats.packets += tx_buf->gso_segs;
> > +
> > +			iecm_tx_splitq_clean_buf(txq, tx_buf, budget);
> > +		} else if (dma_unmap_len(tx_buf, len)) {
> 
> Here as well.
> 
> > +			dma_unmap_page(txq->dev,
> > +				       dma_unmap_addr(tx_buf, dma),
> > +				       dma_unmap_len(tx_buf, len),
> > +				       DMA_TO_DEVICE);
> > +			dma_unmap_len_set(tx_buf, len, 0);
> > +		}
> > +		/* Push shadow buf back onto stack */
> > +		iecm_buf_lifo_push(&txq->buf_stack, tx_buf);
> > +
> > +		hash_del(&tx_buf->hlist);
> > +	}
> > +
> > +	return cleaned_stats;
> > +}
> > +
> > +/**
> > + * iecm_tx_clean_complq - Reclaim resources on completion queue
> > + * @complq: Tx ring to clean
> > + * @budget: Used to determine if we are in netpoll
> > + *
> > + * Returns true if there's any budget left (e.g. the clean is finished)
> > + */
> > +static bool
> > +iecm_tx_clean_complq(struct iecm_queue *complq, int budget)
> > +{
> > +	struct iecm_splitq_tx_compl_desc *tx_desc;
> > +	struct iecm_vport *vport = complq->vport;
> > +	s16 ntc = complq->next_to_clean;
> > +	bool clean_completed = false;
> > +	unsigned int complq_budget;
> > +
> > +	complq_budget = vport->compln_clean_budget;
> > +	tx_desc = IECM_SPLITQ_TX_COMPLQ_DESC(complq, ntc);
> > +	ntc -= complq->desc_count;
> > +
> > +	do {
> > +		struct iecm_tx_queue_stats cleaned_stats = {0};
> > +		struct iecm_queue *tx_q;
> > +		u16 compl_tag, hw_head;
> > +		int tx_qid;
> > +		u8 ctype;	/* completion type */
> > +		u16 gen;
> > +
> > +		/* if the descriptor isn't done, no work yet to do */
> > +		gen = (le16_to_cpu(tx_desc->qid_comptype_gen) &
> > +		      IECM_TXD_COMPLQ_GEN_M) >>
> IECM_TXD_COMPLQ_GEN_S;
> 
> Same stuff about FIELD_{GET,PREP}().
> 
> > +		if (test_bit(__IECM_Q_GEN_CHK, complq->flags) != gen)
> > +			break;
> > +
> > +		/* Find necessary info of TX queue to clean buffers */
> > +		tx_qid = (le16_to_cpu(tx_desc->qid_comptype_gen) &
> > +			 IECM_TXD_COMPLQ_QID_M) >>
> IECM_TXD_COMPLQ_QID_S;
> 
> Here as well, and in other places I missed.
> 
> > +		tx_q = iecm_tx_find_q(vport, tx_qid);
> > +		if (!tx_q) {
> > +			dev_err(&complq->vport->adapter->pdev->dev,
> > +				"TxQ #%d not found\n", tx_qid);
> > +			goto fetch_next_desc;
> > +		}
> > +
> > +		/* Determine completion type */
> > +		ctype = (le16_to_cpu(tx_desc->qid_comptype_gen) &
> > +			IECM_TXD_COMPLQ_COMPL_TYPE_M) >>
> > +			IECM_TXD_COMPLQ_COMPL_TYPE_S;
> > +		switch (ctype) {
> > +		case IECM_TXD_COMPLT_RE:
> > +			hw_head = le16_to_cpu(tx_desc-
> >q_head_compl_tag.q_head);
> > +
> > +			cleaned_stats = iecm_tx_splitq_clean(tx_q, hw_head,
> > +							     budget, true);
> > +			break;
> > +		case IECM_TXD_COMPLT_RS:
> > +			if (test_bit(__IECM_Q_FLOW_SCH_EN, tx_q->flags)) {
> > +				compl_tag =
> > +				le16_to_cpu(tx_desc-
> >q_head_compl_tag.compl_tag);
> > +
> > +				cleaned_stats =
> > +					iecm_tx_clean_flow_sch_bufs(tx_q,
> > +								    compl_tag,
> > +								    budget);
> > +			} else {
> > +				hw_head =
> > +				le16_to_cpu(tx_desc-
> >q_head_compl_tag.q_head);
> > +
> > +				cleaned_stats = iecm_tx_splitq_clean(tx_q,
> > +								     hw_head,
> > +								     budget,
> > +								     false);
> > +			}
> > +
> > +			break;
> > +		case IECM_TXD_COMPLT_SW_MARKER:
> > +			iecm_tx_handle_sw_marker(tx_q);
> > +			break;
> > +		default:
> > +			dev_err(&tx_q->vport->adapter->pdev->dev,
> > +				"Unknown TX completion type: %d\n",
> > +				ctype);
> > +			goto fetch_next_desc;
> > +		}
> > +
> > +		u64_stats_update_begin(&tx_q->stats_sync);
> > +		tx_q->q_stats.tx.packets += cleaned_stats.packets;
> > +		tx_q->q_stats.tx.bytes += cleaned_stats.bytes;
> > +		u64_stats_update_end(&tx_q->stats_sync);
> > +
> > +		if (unlikely(cleaned_stats.packets &&
> > +			     netif_carrier_ok(tx_q->vport->netdev) &&
> > +			     (IECM_DESC_UNUSED(tx_q) >=
> IECM_TX_WAKE_THRESH) &&
> > +			     (IECM_TX_BUF_UNUSED(tx_q) >= tx_q->desc_count
> >> 2))) {
> > +			/* Make sure any other threads stopping queue after
> > +			 * this see new next_to_clean.
> > +			 */
> > +			smp_mb();
> > +			if (tx_q->vport->adapter->state == __IECM_UP &&
> > +			    __netif_subqueue_stopped(tx_q->vport->netdev,
> > +						     tx_q->idx)) {
> > +				netif_wake_subqueue(tx_q->vport->netdev,
> > +						    tx_q->idx);
> > +			}
> > +		}
> > +
> > +fetch_next_desc:
> > +		tx_desc++;
> > +		ntc++;
> > +		if (unlikely(!ntc)) {
> > +			ntc -= complq->desc_count;
> > +			tx_desc = IECM_SPLITQ_TX_COMPLQ_DESC(complq, 0);
> > +			change_bit(__IECM_Q_GEN_CHK, complq->flags);
> > +		}
> > +
> > +		prefetch(tx_desc);
> > +
> > +		/* update budget accounting */
> > +		complq_budget--;
> > +	} while (likely(complq_budget));
> > +
> > +	ntc += complq->desc_count;
> > +	complq->next_to_clean = ntc;
> > +
> > +	clean_completed = !!complq_budget;
> > +
> > +	return clean_completed;
> > +}
> > +
> >  /**
> >   * iecm_tx_splitq_build_ctb - populate command tag and size for queue
> >   * based scheduling descriptors
> > @@ -2337,6 +2783,940 @@ netdev_tx_t iecm_tx_splitq_start(struct sk_buff
> *skb,
> >  	return iecm_tx_splitq_frame(skb, tx_q);
> >  }
> >
> > +/**
> > + * iecm_ptype_to_htype - get a hash type
> > + * @decoded: Decoded Rx packet type related fields
> > + *
> > + * Returns appropriate hash type (such as PKT_HASH_TYPE_L2/L3/L4) to be
> used by
> > + * skb_set_hash based on PTYPE as parsed by HW Rx pipeline and is part of
> > + * Rx desc.
> > + */
> > +enum
> > +pkt_hash_types iecm_ptype_to_htype(struct iecm_rx_ptype_decoded
> *decoded)
> > +{
> > +	if (!decoded->known)
> > +		return PKT_HASH_TYPE_NONE;
> > +	if (decoded->payload_layer == IECM_RX_PTYPE_PAYLOAD_LAYER_PAY2
> &&
> > +	    decoded->inner_prot)
> > +		return PKT_HASH_TYPE_L4;
> > +	if (decoded->payload_layer == IECM_RX_PTYPE_PAYLOAD_LAYER_PAY2
> &&
> > +	    decoded->outer_ip)
> > +		return PKT_HASH_TYPE_L3;
> > +	if (decoded->outer_ip == IECM_RX_PTYPE_OUTER_L2)
> > +		return PKT_HASH_TYPE_L2;
> > +
> > +	return PKT_HASH_TYPE_NONE;
> > +}
> > +
> > +/**
> > + * iecm_rx_hash - set the hash value in the skb
> > + * @rxq: Rx descriptor ring packet is being transacted on
> > + * @skb: pointer to current skb being populated
> > + * @rx_desc: Receive descriptor
> > + * @decoded: Decoded Rx packet type related fields
> > + */
> > +static void
> > +iecm_rx_hash(struct iecm_queue *rxq, struct sk_buff *skb,
> > +	     struct virtchnl2_rx_flex_desc_adv_nic_3 *rx_desc,
> > +	     struct iecm_rx_ptype_decoded *decoded)
> > +{
> > +	u32 hash;
> > +
> > +	if (!iecm_is_feature_ena(rxq->vport, NETIF_F_RXHASH))
> > +		return;
> > +
> > +	hash = le16_to_cpu(rx_desc->hash1) |
> > +	       (rx_desc->ff2_mirrid_hash2.hash2 << 16) |
> > +	       (rx_desc->hash3 << 24);
> > +
> > +	skb_set_hash(skb, hash, iecm_ptype_to_htype(decoded));
> > +}
> > +
> > +/**
> > + * iecm_rx_csum - Indicate in skb if checksum is good
> > + * @rxq: Rx descriptor ring packet is being transacted on
> > + * @skb: pointer to current skb being populated
> > + * @csum_bits: checksum fields extracted from the descriptor
> > + * @decoded: Decoded Rx packet type related fields
> > + *
> > + * skb->protocol must be set before this function is called
> > + */
> > +static void iecm_rx_csum(struct iecm_queue *rxq, struct sk_buff *skb,
> > +			 struct iecm_rx_csum_decoded *csum_bits,
> > +			 struct iecm_rx_ptype_decoded *decoded)
> > +{
> > +	bool ipv4, ipv6;
> > +
> > +	/* Start with CHECKSUM_NONE and by default csum_level = 0 */
> > +	skb->ip_summed = CHECKSUM_NONE;
> > +
> > +	/* check if Rx checksum is enabled */
> > +	if (!iecm_is_feature_ena(rxq->vport, NETIF_F_RXCSUM))
> > +		return;
> > +
> > +	/* check if HW has decoded the packet and checksum */
> > +	if (!(csum_bits->l3l4p))
> > +		return;
> > +
> > +	ipv4 = (decoded->outer_ip == IECM_RX_PTYPE_OUTER_IP) &&
> > +	       (decoded->outer_ip_ver == IECM_RX_PTYPE_OUTER_IPV4);
> > +	ipv6 = (decoded->outer_ip == IECM_RX_PTYPE_OUTER_IP) &&
> > +	       (decoded->outer_ip_ver == IECM_RX_PTYPE_OUTER_IPV6);
> > +
> > +	if (ipv4 && (csum_bits->ipe || csum_bits->eipe))
> > +		goto checksum_fail;
> > +
> > +	if (ipv6 && csum_bits->ipv6exadd)
> > +		return;
> > +
> > +	/* HW checksum will be invalid if vlan stripping is not enabled and
> > +	 * packet has an outer vlan tag. raw_csum_inv will also not be set
> > +	 * even though it's invalid.
> > +	 */
> > +	if (skb_vlan_tag_present(skb))
> > +		return;
> > +
> > +	/* check for L4 errors and handle packets that were not able to be
> > +	 * checksummed
> > +	 */
> > +	if (csum_bits->l4e)
> > +		goto checksum_fail;
> > +
> > +	/* Only report checksum unnecessary for ICMP, TCP, UDP, or SCTP */
> > +	switch (decoded->inner_prot) {
> > +	case IECM_RX_PTYPE_INNER_PROT_ICMP:
> > +	case IECM_RX_PTYPE_INNER_PROT_TCP:
> > +	case IECM_RX_PTYPE_INNER_PROT_UDP:
> > +	case IECM_RX_PTYPE_INNER_PROT_SCTP:
> > +		skb->ip_summed = CHECKSUM_UNNECESSARY;
> > +	default:
> > +		break;
> > +	}
> > +	return;
> > +
> > +checksum_fail:
> > +	rxq->vport->port_stats.rx_hw_csum_err++;
> > +}
> > +
> > +/**
> > + * iecm_rx_splitq_extract_csum_bits - Extract checksum bits from descriptor
> > + * @rx_desc: receive descriptor
> > + * @csum: structure to extract checksum fields
> > + *
> > + **/
> > +static void
> > +iecm_rx_splitq_extract_csum_bits(struct virtchnl2_rx_flex_desc_adv_nic_3
> *rx_desc,
> > +				 struct iecm_rx_csum_decoded *csum)
> > +{
> > +	u8 qword0, qword1;
> > +
> > +	qword0 = rx_desc->status_err0_qw0;
> > +	qword1 = rx_desc->status_err0_qw1;
> > +
> > +	csum->ipe = !!(qword1 &
> > +
> BIT(VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_XSUM_IPE_S));
> > +	csum->eipe = !!(qword1 &
> > +
> 	BIT(VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_XSUM_EIPE_S));
> > +	csum->l4e = !!(qword1 &
> > +
> BIT(VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_XSUM_L4E_S));
> > +	csum->l3l4p = !!(qword1 &
> > +
> BIT(VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_L3L4P_S));
> > +	csum->ipv6exadd =
> > +			!!(qword0 &
> > +
> BIT(VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_IPV6EXADD_S));
> > +	csum->rsc = !!(le16_to_cpu(rx_desc->hdrlen_flags) &
> > +		       VIRTCHNL2_RX_FLEX_DESC_ADV_RSC_M);
> > +	csum->raw_csum_inv = !!(le16_to_cpu(rx_desc->ptype_err_fflags0) &
> > +				BIT(VIRTCHNL2_RX_FLEX_DESC_ADV_FF0_S));
> > +	csum->raw_csum = le16_to_cpu(rx_desc->misc.raw_cs);
> > +	csum->pprs = 0;
> > +}
> > +
> > +/**
> > + * iecm_rx_rsc - Set the RSC fields in the skb
> > + * @rxq : Rx descriptor ring packet is being transacted on
> > + * @skb : pointer to current skb being populated
> > + * @rx_desc: Receive descriptor
> > + * @decoded: Decoded Rx packet type related fields
> > + *
> > + * Return 0 on success and error code on failure
> > + *
> > + * Populate the skb fields with the total number of RSC segments, RSC
> payload
> > + * length and packet type.
> > + */
> > +static int iecm_rx_rsc(struct iecm_queue *rxq, struct sk_buff *skb,
> > +		       struct virtchnl2_rx_flex_desc_adv_nic_3 *rx_desc,
> > +		       struct iecm_rx_ptype_decoded *decoded)
> > +{
> > +	u16 rsc_segments, rsc_payload_len;
> > +	struct tcphdr *tcph;
> > +	bool ipv4, ipv6;
> > +
> > +	if (!decoded->outer_ip)
> > +		return -EINVAL;
> > +
> > +	rsc_payload_len = le16_to_cpu(rx_desc->misc.rscseglen);
> > +	if (!rsc_payload_len)
> > +		return -EINVAL;
> > +
> > +	ipv4 = (decoded->outer_ip == IECM_RX_PTYPE_OUTER_IP) &&
> > +		(decoded->outer_ip_ver == IECM_RX_PTYPE_OUTER_IPV4);
> > +	ipv6 = (decoded->outer_ip == IECM_RX_PTYPE_OUTER_IP) &&
> > +		(decoded->outer_ip_ver == IECM_RX_PTYPE_OUTER_IPV6);
> > +
> > +	if (!(ipv4 ^ ipv6))
> > +		return -EINVAL;
> > +
> > +	rsc_segments = DIV_ROUND_UP(skb->data_len, rsc_payload_len);
> > +
> > +	NAPI_GRO_CB(skb)->count = rsc_segments;
> > +	skb_shinfo(skb)->gso_size = rsc_payload_len;
> > +
> > +	skb_reset_network_header(skb);
> > +
> > +	if (ipv4) {
> > +		struct iphdr *ipv4h = ip_hdr(skb);
> > +
> > +		skb_shinfo(skb)->gso_type = SKB_GSO_TCPV4;
> > +
> > +		/* Reset and set transport header offset in skb */
> > +		skb_set_transport_header(skb, sizeof(struct iphdr));
> > +		tcph = tcp_hdr(skb);
> > +
> > +		/* Compute the TCP pseudo header checksum*/
> > +		tcph->check =
> > +			~tcp_v4_check(skb->len - skb_transport_offset(skb),
> > +				      ipv4h->saddr, ipv4h->daddr, 0);
> > +	} else {
> > +		struct ipv6hdr *ipv6h = ipv6_hdr(skb);
> > +
> > +		skb_shinfo(skb)->gso_type = SKB_GSO_TCPV6;
> > +		skb_set_transport_header(skb, sizeof(struct ipv6hdr));
> > +		tcph = tcp_hdr(skb);
> > +		tcph->check =
> > +			~tcp_v6_check(skb->len - skb_transport_offset(skb),
> > +				      &ipv6h->saddr, &ipv6h->daddr, 0);
> > +	}
> > +
> > +	tcp_gro_complete(skb);
> > +
> > +	u64_stats_update_begin(&rxq->stats_sync);
> > +	rxq->q_stats.rx.rsc_pkts++;
> > +	u64_stats_update_end(&rxq->stats_sync);
> > +
> > +	return 0;
> > +}
> > +
> > +/**
> > + * iecm_rx_process_skb_fields - Populate skb header fields from Rx descriptor
> > + * @rxq: Rx descriptor ring packet is being transacted on
> > + * @skb: pointer to current skb being populated
> > + * @rx_desc: Receive descriptor
> > + *
> > + * This function checks the ring, descriptor, and packet information in
> > + * order to populate the hash, checksum, VLAN, protocol, and
> > + * other fields within the skb.
> > + */
> > +int
> > +iecm_rx_process_skb_fields(struct iecm_queue *rxq, struct sk_buff *skb,
> > +			   struct virtchnl2_rx_flex_desc_adv_nic_3 *rx_desc)
> > +{
> > +	struct iecm_rx_ptype_decoded decoded;
> > +	struct iecm_rx_csum_decoded csum_bits;
> 
> Please follow RCT.
> 

Sure will fix.

> > +	u16 rx_ptype;
> > +	int err = 0;
> > +
> > +	rx_ptype = le16_to_cpu(rx_desc->ptype_err_fflags0) &
> > +				VIRTCHNL2_RX_FLEX_DESC_ADV_PTYPE_M;
> > +
> > +	decoded = rxq->vport->rx_ptype_lkup[rx_ptype];
> > +	if (!decoded.known)
> > +		return -EINVAL;
> > +
> > +	/* modifies the skb - consumes the enet header */
> > +	skb->protocol = eth_type_trans(skb, rxq->vport->netdev);
> 
> eth_type_trans() should generally be called *right* before
> napi_gro_receive() to still have caches warm.
> 

I'm pretty sure this happening here because we need to consume header before messing with checksum stuff but I'll have to dig deeper.  Will check.

> > +	iecm_rx_splitq_extract_csum_bits(rx_desc, &csum_bits);
> > +	iecm_rx_csum(rxq, skb, &csum_bits, &decoded);
> > +	/* process RSS/hash */
> > +	iecm_rx_hash(rxq, skb, rx_desc, &decoded);
> > +
> > +	if (csum_bits.rsc)
> > +		err = iecm_rx_rsc(rxq, skb, rx_desc, &decoded);
> > +
> > +	return err;
> > +}
> > +
> > +/**
> > + * iecm_rx_skb - Send a completed packet up the stack
> > + * @rxq: Rx ring in play
> > + * @skb: packet to send up
> > + * @vlan_tag: packet vlan tag
> > + *
> > + * This function sends the completed packet (via. skb) up the stack using
> > + * gro receive functions
> > + */
> > +void iecm_rx_skb(struct iecm_queue *rxq, struct sk_buff *skb, u16 vlan_tag)
> > +{
> > +	/* Adding HW VLAN tag to skb must occur after processing csum */
> > +	if (vlan_tag & VLAN_VID_MASK)
> > +		__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan_tag);
> 
> Why can't this be placed into process_skb_fields()? This function
> becomes redundant then, and you could call napi_gro_receive()
> directly instead.
> 

Seems reasonable, will try.

> > +
> > +	napi_gro_receive(&rxq->q_vector->napi, skb);
> > +}
> > +
> > +/**
> > + * iecm_rx_page_is_reserved - check if reuse is possible
> > + * @page: page struct to check
> > + */
> > +static bool iecm_rx_page_is_reserved(struct page *page)
> > +{
> > +	return (page_to_nid(page) != numa_mem_id()) ||
> page_is_pfmemalloc(page);
> > +}
> 
> Please check generic dev_page_is_reusable(), it's almost the same
> (a bit more optimized).
> 

Will check and see if it does what we need.

> > +
> > +/**
> > + * iecm_rx_buf_adjust_pg - Prepare rx buffer for reuse
> > + * @rx_buf: Rx buffer to adjust
> > + * @size: Size of adjustment
> > + *
> > + * Update the offset within page so that rx buf will be ready to be reused.
> > + * For systems with PAGE_SIZE < 8192 this function will flip the page offset
> > + * so the second half of page assigned to rx buffer will be used, otherwise
> > + * the offset is moved by the @size bytes
> > + */
> > +void
> > +iecm_rx_buf_adjust_pg(struct iecm_rx_buf *rx_buf, unsigned int size)
> > +{
> > +	struct iecm_page_info *page_info = &rx_buf->page_info[rx_buf-
> >page_indx];
> > +
> > +#if (PAGE_SIZE < 8192)
> > +	if (rx_buf->buf_size > IECM_RX_BUF_2048)
> > +		/* flip to second page */
> > +		rx_buf->page_indx = !rx_buf->page_indx;
> > +	else
> > +		/* flip page offset to other buffer */
> > +		page_info->page_offset ^= size;
> > +#else
> > +	/* move offset up to the next cache line */
> > +	page_info->page_offset += size;
> > +#endif
> 
> Again, no need for ifdeffery, PAGE_SIZE check can be placed into
> an if statement.
> 

Sure will fix.

> > +}
> > +
> > +/**
> > + * iecm_rx_can_reuse_page - Determine if page can be reused for another rx
> > + * @rx_buf: buffer containing the page
> > + *
> > + * If page is reusable, we have a green light for calling iecm_reuse_rx_page,
> > + * which will assign the current buffer to the buffer that next_to_alloc is
> > + * pointing to; otherwise, the dma mapping needs to be destroyed and
> > + * page freed
> > + */
> > +bool iecm_rx_can_reuse_page(struct iecm_rx_buf *rx_buf)
> > +{
> > +	struct iecm_page_info *page_info = &rx_buf->page_info[rx_buf-
> >page_indx];
> > +
> > +#if (PAGE_SIZE >= 8192)
> > +	unsigned int last_offset = PAGE_SIZE - rx_buf->buf_size;
> > +#endif /* PAGE_SIZE < 8192) */
> > +	unsigned int pagecnt_bias = page_info->pagecnt_bias;
> > +	struct page *page = page_info->page;
> > +
> > +	/* avoid re-using remote pages */
> > +	if (unlikely(iecm_rx_page_is_reserved(page)))
> > +		return false;
> > +
> > +#if (PAGE_SIZE < 8192)
> > +	/* if we are only owner of page we can reuse it */
> > +	if (unlikely((page_count(page) - pagecnt_bias) > 1))
> > +		return false;
> > +#else
> > +	if (rx_buf->page_offset > last_offset)
> > +		return false;
> > +#endif /* PAGE_SIZE < 8192) */
> 
> Same here 2 times.
> 
> > +
> > +	/* If we have drained the page fragment pool we need to update
> > +	 * the pagecnt_bias and page count so that we fully restock the
> > +	 * number of references the driver holds.
> > +	 */
> > +	if (unlikely(pagecnt_bias == 1)) {
> 
> With 1532 byte frames, this condition will be hit 50% of times. It's
> definitely not a good place for unlkely().
> 

I'm afraid I'm not following here, mind elaborating? Keep in mind the buffer is 4k not 2k on MEV.

> > +		page_ref_add(page, USHRT_MAX - 1);
> > +		page_info->pagecnt_bias = USHRT_MAX;
> > +	}
> > +
> > +	return true;
> > +}
> > +
> > +/**
> > + * iecm_rx_add_frag - Add contents of Rx buffer to sk_buff as a frag
> > + * @rx_buf: buffer containing page to add
> > + * @skb: sk_buff to place the data into
> > + * @size: packet length from rx_desc
> > + *
> > + * This function will add the data contained in rx_buf->page to the skb.
> > + * It will just attach the page as a frag to the skb.
> > + * The function will then update the page offset.
> > + */
> > +void iecm_rx_add_frag(struct iecm_rx_buf *rx_buf, struct sk_buff *skb,
> > +		      unsigned int size)
> > +{
> > +	struct iecm_page_info *page_info = &rx_buf->page_info[rx_buf-
> >page_indx];
> > +
> > +#if (PAGE_SIZE >= 8192)
> > +	unsigned int truesize = SKB_DATA_ALIGN(size);
> > +#else
> > +	unsigned int truesize = rx_buf->buf_size;
> > +#endif
> 
> Same.
> 
> > +
> > +	skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page_info->page,
> > +			page_info->page_offset, size, truesize);
> > +
> > +	iecm_rx_buf_adjust_pg(rx_buf, truesize);
> > +}
> > +
> > +/**
> > + * iecm_rx_get_buf_page - Fetch Rx buffer page and synchronize data for use
> > + * @dev: device struct
> > + * @rx_buf: Rx buf to fetch page for
> > + * @size: size of buffer to add to skb
> > + *
> > + * This function will pull an Rx buffer page from the ring and synchronize it
> > + * for use by the CPU.
> > + */
> > +static void
> > +iecm_rx_get_buf_page(struct device *dev, struct iecm_rx_buf *rx_buf,
> > +		     const unsigned int size)
> > +{
> > +	struct iecm_page_info *page_info = &rx_buf->page_info[rx_buf-
> >page_indx];
> > +
> > +	prefetch(page_info->page);
> > +
> > +	/* we are reusing so sync this buffer for CPU use */
> > +	dma_sync_single_range_for_cpu(dev, page_info->dma,
> > +				      page_info->page_offset, size,
> > +				      DMA_FROM_DEVICE);
> > +
> > +	/* We have pulled a buffer for use, so decrement pagecnt_bias */
> > +	page_info->pagecnt_bias--;
> > +}
> > +
> > +/**
> > + * iecm_rx_construct_skb - Allocate skb and populate it
> > + * @rxq: Rx descriptor queue
> > + * @rx_buf: Rx buffer to pull data from
> > + * @size: the length of the packet
> > + *
> > + * This function allocates an skb. It then populates it with the page
> > + * data from the current receive descriptor, taking care to set up the
> > + * skb correctly.
> > + */
> > +struct sk_buff *
> > +iecm_rx_construct_skb(struct iecm_queue *rxq, struct iecm_rx_buf *rx_buf,
> > +		      unsigned int size)
> > +{
> > +	struct iecm_page_info *page_info = &rx_buf->page_info[rx_buf-
> >page_indx];
> > +
> > +	void *va = page_address(page_info->page) + page_info->page_offset;
> > +	unsigned int headlen;
> > +	struct sk_buff *skb;
> > +
> > +	/* prefetch first cache line of first page */
> > +	prefetch(va);
> > +#if L1_CACHE_BYTES < 128
> > +	prefetch((u8 *)va + L1_CACHE_BYTES);
> > +#endif /* L1_CACHE_BYTES */
> 
> Here's open-coded net_prefetch().
> 

Will fix.

> > +	/* allocate a skb to store the frags */
> > +	skb = __napi_alloc_skb(&rxq->q_vector->napi, IECM_RX_HDR_SIZE,
> > +			       GFP_ATOMIC | __GFP_NOWARN);
> > +	if (unlikely(!skb))
> > +		return NULL;
> > +
> > +	skb_record_rx_queue(skb, rxq->idx);
> > +
> > +	/* Determine available headroom for copy */
> > +	headlen = size;
> > +	if (headlen > IECM_RX_HDR_SIZE)
> > +		headlen = eth_get_headlen(skb->dev, va, IECM_RX_HDR_SIZE);
> > +
> > +	/* align pull length to size of long to optimize memcpy performance */
> > +	memcpy(__skb_put(skb, headlen), va, ALIGN(headlen, sizeof(long)));
> > +
> > +	/* if we exhaust the linear part then add what is left as a frag */
> > +	size -= headlen;
> > +	if (size) {
> > +#if (PAGE_SIZE >= 8192)
> > +		unsigned int truesize = SKB_DATA_ALIGN(size);
> > +#else
> > +		unsigned int truesize = rx_buf->buf_size;
> > +#endif
> 
> Again.
> 
> > +		skb_add_rx_frag(skb, 0, page_info->page,
> > +				page_info->page_offset + headlen, size,
> > +				truesize);
> > +		/* buffer is used by skb, update page_offset */
> > +		iecm_rx_buf_adjust_pg(rx_buf, truesize);
> > +
> > +	} else {
> > +		/* buffer is unused, reset bias back to rx_buf; data was copied
> > +		 * onto skb's linear part so there's no need for adjusting
> > +		 * page offset and we can reuse this buffer as-is
> > +		 */
> > +		page_info->pagecnt_bias++;
> > +	}
> > +
> > +	return skb;
> > +}
> > +
> > +/**
> > + * iecm_rx_hdr_construct_skb - Allocate skb and populate it from header
> buffer
> > + * @rxq: Rx descriptor queue
> > + * @hdr_buf: Rx buffer to pull data from
> > + * @size: the length of the packet
> > + *
> > + * This function allocates an skb. It then populates it with the page data from
> > + * the current receive descriptor, taking care to set up the skb correctly.
> > + * This specifcally uses a header buffer to start building the skb.
> > + */
> > +static struct sk_buff *
> > +iecm_rx_hdr_construct_skb(struct iecm_queue *rxq, struct iecm_dma_mem
> *hdr_buf,
> > +			  unsigned int size)
> > +{
> > +	struct sk_buff *skb;
> > +
> > +	/* allocate a skb to store the frags */
> > +	skb = __napi_alloc_skb(&rxq->q_vector->napi, IECM_RX_HDR_SIZE,
> > +			       GFP_ATOMIC | __GFP_NOWARN);
> > +	if (unlikely(!skb))
> > +		return NULL;
> > +
> > +	skb_record_rx_queue(skb, rxq->idx);
> > +
> > +	memcpy(__skb_put(skb, size), hdr_buf->va, size);
> > +
> > +	return skb;
> > +}
> > +
> > +/**
> > + * iecm_rx_splitq_test_staterr - tests bits in Rx descriptor
> > + * status and error fields
> > + * @stat_err_field: field from descriptor to test bits in
> > + * @stat_err_bits: value to mask
> > + *
> > + */
> > +bool
> > +iecm_rx_splitq_test_staterr(u8 stat_err_field, const u8 stat_err_bits)
> > +{
> > +	return !!(stat_err_field & stat_err_bits);
> > +}
> > +
> > +/**
> > + * iecm_rx_splitq_extract_vlan_tag - Extract vlan tag from the descriptor
> > + * @desc: Rx flex descriptor
> > + * @rxq: rxq to check the vlan flags
> > + * @vlan_tag: vlan tag to fill in
> > + *
> > + * Return true if error bit is set in the descriptor, else return false and
> > + * store the vlan_tag in the variable passed in the function parameters
> > + */
> > +bool iecm_rx_splitq_extract_vlan_tag(struct
> virtchnl2_rx_flex_desc_adv_nic_3 *desc,
> > +				     struct iecm_queue *rxq, u16 *vlan_tag)
> > +{
> > +	u8 stat_err0_qw0, stat_err_bits, stat_err1;
> > +
> > +	stat_err_bits = BIT(VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_RXE_S);
> > +	stat_err0_qw0 = desc->status_err0_qw0;
> > +	if (unlikely(iecm_rx_splitq_test_staterr(stat_err0_qw0, stat_err_bits)))
> > +		return true;
> > +
> > +	stat_err1 = desc->status_err1;
> > +
> > +	if (stat_err0_qw0 &
> BIT(VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_L2TAG1P_S) &&
> > +	    test_bit(__IECM_Q_VLAN_TAG_LOC_L2TAG1, rxq->flags))
> > +		*vlan_tag = le16_to_cpu(desc->l2tag1);
> > +	if (stat_err1 &
> BIT(VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS1_L2TAG2P_S) &&
> > +	    test_bit(__IECM_Q_VLAN_TAG_LOC_L2TAG2, rxq->flags))
> > +		*vlan_tag = le16_to_cpu(desc->l2tag2);
> > +
> > +	return false;
> > +}
> > +
> > +/**
> > + * iecm_rx_splitq_is_non_eop - process handling of non-EOP buffers
> > + * @rx_desc: Rx descriptor for current buffer
> > + *
> > + * If the buffer is an EOP buffer, this function exits returning false,
> > + * otherwise return true indicating that this is in fact a non-EOP buffer.
> > + */
> > +static bool
> > +iecm_rx_splitq_is_non_eop(struct virtchnl2_rx_flex_desc_adv_nic_3
> *rx_desc)
> > +{
> > +	/* if we are the last buffer then there is nothing else to do */
> > +#define IECM_RXD_EOF
> BIT(VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_EOF_S)
> 
> This is a wrong place to define anything, it can be easily
> overlooked.
> 

Sure will fix.

> > +	if (likely(iecm_rx_splitq_test_staterr(rx_desc->status_err0_qw1,
> > +					       IECM_RXD_EOF)))
> > +		return false;
> > +
> > +	return true;
> 
> This can be converted to an one-liner
> 
> 	return likely(...);
> 

Will fix.

> > +}
> > +
> > +/**
> > + * iecm_rx_splitq_recycle_buf - Attempt to recycle or realloc buffer
> > + * @rxbufq: receive queue
> > + * @rx_buf: Rx buffer to pull data from
> > + *
> > + * This function will clean up the contents of the rx_buf. It will either
> > + * recycle the buffer or unmap it and free the associated resources. The
> buffer
> > + * will then be placed on a refillq where it will later be reclaimed by the
> > + * corresponding bufq.
> > + *
> > + * This works based on page flipping. If we assume e.g., a 4k page, it will be
> > + * divided into two 2k buffers. We post the first half to hardware and, after
> > + * using it, flip to second half of the page with iecm_adjust_pg_offset and
> > + * post that to hardware. The third time through we'll flip back to first half
> > + * of page and check if stack is still using it, if not we can reuse the buffer
> > + * as is, otherwise we'll drain it and get a new page.
> > + */
> > +static void iecm_rx_splitq_recycle_buf(struct iecm_queue *rxbufq,
> > +				       struct iecm_rx_buf *rx_buf)
> > +{
> > +	struct iecm_page_info *page_info = &rx_buf->page_info[rx_buf-
> >page_indx];
> > +
> > +	if (!iecm_rx_can_reuse_page(rx_buf)) {
> > +		/* we are not reusing the buffer so unmap it */
> > +		dma_unmap_page_attrs(rxbufq->dev, page_info->dma,
> PAGE_SIZE,
> > +				     DMA_FROM_DEVICE,
> IECM_RX_DMA_ATTR);
> > +		__page_frag_cache_drain(page_info->page,
> > +					page_info->pagecnt_bias);
> > +
> > +		/* clear contents of buffer_info */
> > +		page_info->page = NULL;
> > +		rx_buf->skb = NULL;
> > +
> > +		/* It's possible the alloc can fail here but there's not much
> > +		 * we can do, bufq will have to try and realloc to fill the
> > +		 * hole.
> > +		 */
> > +		iecm_alloc_page(rxbufq, page_info);
> > +	}
> > +
> > +	/* We sync the memory back to hardware now to do as much work in
> this
> > +	 * context as feasible.  Hardware won't actually know about the buffer
> > +	 * until it's reclaimed off the refillq and put back into the bufq.
> > +	 */
> > +	if (likely(page_info->page)) {
> > +		dma_sync_single_range_for_device(rxbufq->dev, page_info-
> >dma,
> > +						 page_info->page_offset,
> > +						 rxbufq->rx_buf_size,
> > +						 DMA_FROM_DEVICE);
> > +	}
> 
> One-liner, braces are redundant.
> 

Will not fix.

> > +}
> 
> Here you set page_info->page to NULL in the first condition branch
> and check for it in the second.
> So it's equivalent to
> 
> 	if (iecm_rx_can_reuse_page(rx_buf)) {
> 		dma_sync_single_range();
> 		return;
> 	}
> 
> 	/* we are not reusing the buffer ... */
> 	...
> 
> but saves 1 indent level and generally more readable.
> 

Will fix.

> > +
> > +/**
> > + * iecm_rx_bump_ntc - Bump and wrap q->next_to_clean value
> > + * @q: queue to bump
> > + */
> > +void iecm_rx_bump_ntc(struct iecm_queue *q)
> > +{
> > +	u16 ntc = q->next_to_clean + 1;
> 
> There should be an empty line (between variable declarations and
> function body). checkpatch should've mentioned this.
> 
> > +	/* fetch, update, and store next to clean */
> > +	if (ntc < q->desc_count) {
> > +		q->next_to_clean = ntc;
> > +	} else {
> > +		q->next_to_clean = 0;
> > +		change_bit(__IECM_Q_GEN_CHK, q->flags);
> > +	}
> > +}
> > +
> > +/**
> > + * iecm_rx_splitq_clean - Clean completed descriptors from Rx queue
> > + * @rxq: Rx descriptor queue to retrieve receive buffer queue
> > + * @budget: Total limit on number of packets to process
> > + *
> > + * This function provides a "bounce buffer" approach to Rx interrupt
> > + * processing. The advantage to this is that on systems that have
> > + * expensive overhead for IOMMU access this provides a means of avoiding
> > + * it by maintaining the mapping of the page to the system.
> > + *
> > + * Returns amount of work completed
> > + */
> > +static int iecm_rx_splitq_clean(struct iecm_queue *rxq, int budget)
> > +{
> > +	int total_rx_bytes = 0, total_rx_pkts = 0;
> > +	struct iecm_queue *rx_bufq = NULL;
> > +	struct sk_buff *skb = rxq->skb;
> > +	bool failure = false;
> > +
> > +	/* Process Rx packets bounded by budget */
> > +	while (likely(total_rx_pkts < budget)) {
> > +		struct virtchnl2_rx_flex_desc_adv_nic_3 *splitq_flex_rx_desc;
> > +		struct iecm_sw_queue *refillq = NULL;
> > +		struct iecm_dma_mem *hdr_buf = NULL;
> > +		struct iecm_rxq_set *rxq_set = NULL;
> > +		struct iecm_rx_buf *rx_buf = NULL;
> > +		u16 gen_id, buf_id, vlan_tag = 0;
> > +		union virtchnl2_rx_desc *rx_desc;
> > +		unsigned int pkt_len = 0;
> > +		unsigned int hdr_len = 0;
> > +		 /* Header buffer overflow only valid for header split */
> > +		bool hbo = false;
> > +		int bufq_id;
> > +
> > +		/* get the Rx desc from Rx queue based on 'next_to_clean' */
> > +		rx_desc = IECM_RX_DESC(rxq, rxq->next_to_clean);
> > +		splitq_flex_rx_desc = (struct virtchnl2_rx_flex_desc_adv_nic_3
> *)rx_desc;
> > +
> > +		/* This memory barrier is needed to keep us from reading
> > +		 * any other fields out of the rx_desc
> > +		 */
> > +		dma_rmb();
> > +
> > +		/* if the descriptor isn't done, no work yet to do */
> > +		gen_id = le16_to_cpu(splitq_flex_rx_desc-
> >pktlen_gen_bufq_id);
> > +		gen_id = (gen_id & VIRTCHNL2_RX_FLEX_DESC_ADV_GEN_M)
> >>
> > +
> 	VIRTCHNL2_RX_FLEX_DESC_ADV_GEN_S;
> > +		if (test_bit(__IECM_Q_GEN_CHK, rxq->flags) != gen_id)
> > +			break;
> > +
> > +		if ((splitq_flex_rx_desc->rxdid_ucast &
> > +		    VIRTCHNL2_RX_FLEX_DESC_ADV_RXDID_M) !=
> VIRTCHNL2_RXDID_1_FLEX_SPLITQ) {
> > +			iecm_rx_bump_ntc(rxq);
> > +			rxq->vport->port_stats.rx_bad_descs++;
> > +			continue;
> > +		}
> > +
> > +		pkt_len = le16_to_cpu(splitq_flex_rx_desc-
> >pktlen_gen_bufq_id) &
> > +			  VIRTCHNL2_RX_FLEX_DESC_ADV_LEN_PBUF_M;
> > +
> > +		hbo = splitq_flex_rx_desc->status_err0_qw1 &
> > +		      BIT(VIRTCHNL2_RX_FLEX_DESC_ADV_STATUS0_HBO_S);
> > +
> > +		if (unlikely(hbo)) {
> > +			rxq->vport->port_stats.rx_hsplit_hbo++;
> > +			goto bypass_hsplit;
> > +		}
> > +
> > +		hdr_len =
> > +			le16_to_cpu(splitq_flex_rx_desc->hdrlen_flags) &
> > +			VIRTCHNL2_RX_FLEX_DESC_ADV_LEN_HDR_M;
> > +
> > +bypass_hsplit:
> > +		bufq_id = le16_to_cpu(splitq_flex_rx_desc-
> >pktlen_gen_bufq_id);
> > +		bufq_id = (bufq_id &
> VIRTCHNL2_RX_FLEX_DESC_ADV_BUFQ_ID_M) >>
> > +			  VIRTCHNL2_RX_FLEX_DESC_ADV_BUFQ_ID_S;
> > +
> > +		rxq_set = container_of(rxq, struct iecm_rxq_set, rxq);
> > +		if (!bufq_id)
> > +			refillq = rxq_set->refillq0;
> > +		else
> > +			refillq = rxq_set->refillq1;
> > +
> > +		/* retrieve buffer from the rxq */
> > +		rx_bufq = &rxq->rxq_grp->splitq.bufq_sets[bufq_id].bufq;
> > +
> > +		buf_id = le16_to_cpu(splitq_flex_rx_desc->buf_id);
> > +
> > +		if (pkt_len) {
> > +			rx_buf = &rx_bufq->rx_buf.buf[buf_id];
> > +			iecm_rx_get_buf_page(rx_bufq->dev, rx_buf, pkt_len);
> > +		}
> > +
> > +		if (hdr_len) {
> > +			hdr_buf = rx_bufq->rx_buf.hdr_buf[buf_id];
> > +
> > +			dma_sync_single_for_cpu(rxq->dev, hdr_buf->pa,
> hdr_buf->size,
> > +						DMA_FROM_DEVICE);
> > +
> > +			skb = iecm_rx_hdr_construct_skb(rxq, hdr_buf,
> hdr_len);
> > +			rxq->vport->port_stats.rx_hsplit++;
> > +		}
> > +
> > +		if (pkt_len) {
> > +			if (skb)
> > +				iecm_rx_add_frag(rx_buf, skb, pkt_len);
> > +			else
> > +				skb = iecm_rx_construct_skb(rxq, rx_buf,
> > +							    pkt_len);
> > +		}
> > +
> > +		/* exit if we failed to retrieve a buffer */
> > +		if (!skb) {
> > +			/* If we fetched a buffer, but didn't use it
> > +			 * undo pagecnt_bias decrement
> > +			 */
> > +			if (rx_buf)
> > +				rx_buf->page_info[rx_buf-
> >page_indx].pagecnt_bias++;
> 
> Line of 85 cols here.
> 
> > +			break;
> > +		}
> > +
> > +		if (rx_buf)
> > +			iecm_rx_splitq_recycle_buf(rx_bufq, rx_buf);
> > +		iecm_rx_post_buf_refill(refillq, buf_id);
> > +
> > +		iecm_rx_bump_ntc(rxq);
> > +		/* skip if it is non EOP desc */
> > +		if (iecm_rx_splitq_is_non_eop(splitq_flex_rx_desc))
> > +			continue;
> > +
> > +		/* extract vlan tag from the descriptor */
> > +		if (unlikely(iecm_rx_splitq_extract_vlan_tag(splitq_flex_rx_desc,
> > +							     rxq, &vlan_tag))) {
> 
> 81 and 81 respectively.
> 
> > +			dev_kfree_skb_any(skb);
> > +			skb = NULL;
> > +			continue;
> > +		}
> > +
> > +		/* pad skb if needed (to make valid ethernet frame) */
> > +		if (eth_skb_pad(skb)) {
> > +			skb = NULL;
> > +			continue;
> > +		}
> > +
> > +		/* probably a little skewed due to removing CRC */
> > +		total_rx_bytes += skb->len;
> > +
> > +		/* protocol */
> > +		if (unlikely(iecm_rx_process_skb_fields(rxq, skb,
> > +							splitq_flex_rx_desc))) {
> 
> You can define variables with shorter names to avoid this.
> 

The only variable I see here with a questionably long name is splitq_flex_rx_desc but I don't see a way to shorten it without losing information. Note 'rx_desc' is also a variable that exists in this context and `splitq_base_rx_desc` could conceivably also exist so splitq_rx_desc doesn't work.

> > +			dev_kfree_skb_any(skb);
> > +			skb = NULL;
> > +			continue;
> > +		}
> > +
> > +		/* send completed skb up the stack */
> > +		iecm_rx_skb(rxq, skb, vlan_tag);
> > +		skb = NULL;
> > +
> > +		/* update budget accounting */
> > +		total_rx_pkts++;
> > +	}
> > +	rxq->skb = skb;
> > +	u64_stats_update_begin(&rxq->stats_sync);
> > +	rxq->q_stats.rx.packets += total_rx_pkts;
> > +	rxq->q_stats.rx.bytes += total_rx_bytes;
> > +	u64_stats_update_end(&rxq->stats_sync);
> > +
> > +	/* guarantee a trip back through this routine if there was a failure */
> > +	return unlikely(failure) ? budget : total_rx_pkts;
> > +}
> > +
> > +/**
> > + * iecm_rx_update_bufq_desc - Update buffer queue descriptor
> > + * @bufq: Pointer to the buffer queue
> > + * @desc: Refill queue descriptor
> > + * @buf_desc: Buffer queue descriptor
> > + *
> > + * Return 0 on success and negative on failure.
> > + */
> > +static int iecm_rx_update_bufq_desc(struct iecm_queue *bufq, u16 *desc,
> > +				    struct virtchnl2_splitq_rx_buf_desc
> *buf_desc)
> > +{
> > +	struct iecm_page_info *page_info;
> > +	struct iecm_rx_buf *buf;
> > +	u16 buf_id;
> > +
> > +	buf_id = ((*desc) & IECM_RX_BI_BUFID_M) >> IECM_RX_BI_BUFID_S;
> 
> FIELD_GET().
> 
> > +
> > +	buf = &bufq->rx_buf.buf[buf_id];
> > +	page_info = &buf->page_info[buf->page_indx];
> > +
> > +	/* It's possible page alloc failed during rxq clean, try to
> > +	 * recover here.
> > +	 */
> > +	if (unlikely(!page_info->page)) {
> > +		if (iecm_alloc_page(bufq, page_info))
> > +			return -ENOMEM;
> > +	}
> 
> if (1 && 2).
> 

Will fix

> > +	buf_desc->pkt_addr = cpu_to_le64(page_info->dma + page_info-
> >page_offset);
> > +	buf_desc->qword0.buf_id = cpu_to_le16(buf_id);
> > +
> > +	if (bufq->rx_hsplit_en) {
> > +		struct iecm_dma_mem *hdr_buf = bufq-
> >rx_buf.hdr_buf[buf_id];
> > +
> > +		buf_desc->hdr_addr = cpu_to_le64(hdr_buf->pa);
> > +		dma_sync_single_for_device(bufq->dev, hdr_buf->pa,
> > +					   hdr_buf->size, DMA_FROM_DEVICE);
> > +	}
> 
> 	if (!hsplit_en)
> 		return 0;
> 
> 	hdr_buf = ...
> 

Will fix

> > +
> > +	return 0;
> > +}
> > +
> > +/**
> > + * iecm_rx_clean_refillq - Clean refill queue buffers
> > + * @bufq: buffer queue to post buffers back to
> > + * @refillq: refill queue to clean
> > + *
> > + * This function takes care of the buffer refill management
> > + */
> > +static void iecm_rx_clean_refillq(struct iecm_queue *bufq,
> > +				  struct iecm_sw_queue *refillq)
> > +{
> > +	struct virtchnl2_splitq_rx_buf_desc *buf_desc;
> > +	u16 refillq_ntc = refillq->next_to_clean;
> > +	u16 bufq_nta = bufq->next_to_alloc;
> > +	u16 *refill_desc;
> > +	int cleaned = 0;
> > +	u16 gen;
> > +
> > +	refill_desc = IECM_SPLITQ_RX_BI_DESC(refillq, refillq_ntc);
> > +	buf_desc = IECM_SPLITQ_RX_BUF_DESC(bufq, bufq_nta);
> > +
> > +	/* make sure we stop at ring wrap in the unlikely case ring is full */
> > +	while (likely(cleaned < refillq->desc_count)) {
> > +		bool failure;
> > +
> > +		gen = ((*refill_desc) & IECM_RX_BI_GEN_M) >>
> IECM_RX_BI_GEN_S;
> 
> FIELD_GET().
> 
> > +		if (test_bit(__IECM_RFLQ_GEN_CHK, refillq->flags) != gen)
> > +			break;
> > +
> > +		failure = iecm_rx_update_bufq_desc(bufq, refill_desc,
> > +						   buf_desc);
> > +		if (failure)
> > +			break;
> > +
> > +		refillq_ntc++;
> > +		refill_desc++;
> > +		bufq_nta++;
> > +		buf_desc++;
> > +		cleaned++;
> > +
> > +		if (unlikely(refillq_ntc == refillq->desc_count)) {
> > +			change_bit(__IECM_RFLQ_GEN_CHK, refillq->flags);
> > +			refill_desc = IECM_SPLITQ_RX_BI_DESC(refillq, 0);
> > +			refillq_ntc = 0;
> > +		}
> > +		if (unlikely(bufq_nta == bufq->desc_count)) {
> > +			buf_desc = IECM_SPLITQ_RX_BUF_DESC(bufq, 0);
> > +			bufq_nta = 0;
> > +		}
> > +	}
> > +
> > +	if (cleaned) {
> > +		/* only update hardware tail in strides */
> > +		if (((bufq->next_to_use <= bufq_nta ? 0 : bufq->desc_count) +
> > +		    bufq_nta - bufq->next_to_use) >= bufq->rx_buf_stride)
> > +			iecm_rx_buf_hw_update(bufq, bufq_nta & ~(bufq-
> >rx_buf_stride - 1));
> 
> 92 chars.
> 
> > +
> > +		/* update next to alloc since we have filled the ring */
> > +		refillq->next_to_clean = refillq_ntc;
> > +		bufq->next_to_alloc = bufq_nta;
> > +	}
> > +}
> > +
> > +/**
> > + * iecm_rx_clean_refillq_all - Clean all refill queues
> > + * @bufq: bufq with refillqs to clean
> > + *
> > + * Iterates through all refill queues assigned to the buffer queue assigned to
> > + * this vector.  Returns true if clean is complete within budget, false
> > + * otherwise.
> > + */
> > +static void iecm_rx_clean_refillq_all(struct iecm_queue *bufq)
> > +{
> > +	struct iecm_bufq_set *bufq_set;
> > +	int i = 0;
> > +
> > +	bufq_set = container_of(bufq, struct iecm_bufq_set, bufq);
> > +	for (i = 0; i < bufq_set->num_refillqs; i++)
> > +		iecm_rx_clean_refillq(bufq, &bufq_set->refillqs[i]);
> > +}
> > +
> >  /**
> >   * iecm_vport_intr_clean_queues - MSIX mode Interrupt Handler
> >   * @irq: interrupt number
> > @@ -2742,6 +4122,64 @@ iecm_vport_intr_napi_ena_all(struct iecm_vport
> *vport)
> >  	}
> >  }
> >
> > +/**
> > + * iecm_tx_splitq_clean_all- Clean completetion queues
> > + * @q_vec: queue vector
> > + * @budget: Used to determine if we are in netpoll
> > + *
> > + * Returns false if clean is not complete else returns true
> > + */
> > +static bool
> > +iecm_tx_splitq_clean_all(struct iecm_q_vector *q_vec, int budget)
> > +{
> > +	bool clean_complete = true;
> > +	int i, budget_per_q;
> > +
> > +	budget_per_q = max(budget / q_vec->num_txq, 1);
> > +	for (i = 0; i < q_vec->num_txq; i++) {
> > +		if (!iecm_tx_clean_complq(q_vec->tx[i], budget_per_q))
> > +			clean_complete = false;
> > +	}
> > +	return clean_complete;
> > +}
> > +
> > +/**
> > + * iecm_rx_splitq_clean_all- Clean completetion queues
> > + * @q_vec: queue vector
> > + * @budget: Used to determine if we are in netpoll
> > + * @cleaned: returns number of packets cleaned
> > + *
> > + * Returns false if clean is not complete else returns true
> > + */
> > +static bool
> > +iecm_rx_splitq_clean_all(struct iecm_q_vector *q_vec, int budget,
> > +			 int *cleaned)
> > +{
> > +	bool clean_complete = true;
> > +	int pkts_cleaned = 0;
> > +	int i, budget_per_q;
> > +
> > +	budget_per_q = max(budget / q_vec->num_rxq, 1);
> > +	for (i = 0; i < q_vec->num_rxq; i++) {
> > +		struct iecm_queue *rxq = q_vec->rx[i];
> > +		int pkts_cleaned_per_q;
> > +
> > +		pkts_cleaned_per_q = iecm_rx_splitq_clean(rxq, budget_per_q);
> > +		/* if we clean as many as budgeted, we must not
> > +		 * be done
> > +		 */
> > +		if (pkts_cleaned_per_q >= budget_per_q)
> > +			clean_complete = false;
> > +		pkts_cleaned += pkts_cleaned_per_q;
> > +	}
> > +	*cleaned = pkts_cleaned;
> > +
> > +	for (i = 0; i < q_vec->num_bufq; i++)
> > +		iecm_rx_clean_refillq_all(q_vec->bufq[i]);
> > +
> > +	return clean_complete;
> > +}
> > +
> >  /**
> >   * iecm_vport_splitq_napi_poll - NAPI handler
> >   * @napi: struct from which you get q_vector
> > @@ -2749,8 +4187,34 @@ iecm_vport_intr_napi_ena_all(struct iecm_vport
> *vport)
> >   */
> >  static int iecm_vport_splitq_napi_poll(struct napi_struct *napi, int budget)
> >  {
> > -	/* stub */
> > -	return 0;
> > +	struct iecm_q_vector *q_vector =
> > +				container_of(napi, struct iecm_q_vector, napi);
> 
> You can assign it later (before clean_complete assignment) to avoid
> this.
> 

To avoid the word wrap? I'm not sure I see the difference between letting it wrap and assigning it on it's own line.

> > +	bool clean_complete;
> > +	int work_done = 0;
> > +
> > +	clean_complete = iecm_tx_splitq_clean_all(q_vector, budget);
> > +
> > +	/* Handle case where we are called by netpoll with a budget of 0 */
> > +	if (budget <= 0)
> > +		return budget;
> > +
> > +	/* We attempt to distribute budget to each Rx queue fairly, but don't
> > +	 * allow the budget to go below 1 because that would exit polling early.
> > +	 */
> > +	clean_complete |= iecm_rx_splitq_clean_all(q_vector, budget,
> > +						   &work_done);
> > +
> > +	/* If work not completed, return budget and polling will return */
> > +	if (!clean_complete)
> > +		return budget;
> > +
> > +	/* Exit the polling mode, but don't re-enable interrupts if stack might
> > +	 * poll us due to busy-polling
> > +	 */
> > +	if (likely(napi_complete_done(napi, work_done)))
> > +		iecm_vport_intr_update_itr_ena_irq(q_vector);
> > +
> > +	return min_t(int, work_done, budget - 1);
> >  }
> >
> >  /**
> > diff --git a/drivers/net/ethernet/intel/include/iecm.h
> b/drivers/net/ethernet/intel/include/iecm.h
> > index a655e797f457..3cf2a2f0cb0f 100644
> > --- a/drivers/net/ethernet/intel/include/iecm.h
> > +++ b/drivers/net/ethernet/intel/include/iecm.h
> > @@ -12,6 +12,10 @@
> >  #include <linux/etherdevice.h>
> >  #include <linux/ethtool.h>
> >  #include <net/tcp.h>
> > +#include <net/ip6_checksum.h>
> > +#include <net/ipv6.h>
> > +#include <net/sch_generic.h>
> > +#include <net/gro.h>
> >  #include <linux/version.h>
> >  #include <linux/dim.h>
> >
> > diff --git a/drivers/net/ethernet/intel/include/iecm_txrx.h
> b/drivers/net/ethernet/intel/include/iecm_txrx.h
> > index 7ec742fd4c6b..086b0efc989a 100644
> > --- a/drivers/net/ethernet/intel/include/iecm_txrx.h
> > +++ b/drivers/net/ethernet/intel/include/iecm_txrx.h
> > @@ -638,6 +638,7 @@ void iecm_vport_calc_total_qs(struct iecm_adapter
> *adapter,
> >  			      struct virtchnl2_create_vport *vport_msg);
> >  void iecm_vport_calc_num_q_groups(struct iecm_vport *vport);
> >  int iecm_vport_queues_alloc(struct iecm_vport *vport);
> > +void iecm_rx_post_buf_refill(struct iecm_sw_queue *refillq, u16 buf_id);
> >  void iecm_vport_queues_rel(struct iecm_vport *vport);
> >  void iecm_vport_calc_num_q_vec(struct iecm_vport *vport);
> >  void iecm_vport_intr_rel(struct iecm_vport *vport);
> > @@ -650,14 +651,33 @@ int iecm_vport_intr_init(struct iecm_vport *vport);
> >  irqreturn_t
> >  iecm_vport_intr_clean_queues(int __always_unused irq, void *data);
> >  void iecm_vport_intr_ena_irq_all(struct iecm_vport *vport);
> > +enum
> > +pkt_hash_types iecm_ptype_to_htype(struct iecm_rx_ptype_decoded
> *decoded);
> >  int iecm_config_rss(struct iecm_vport *vport);
> >  void iecm_fill_dflt_rss_lut(struct iecm_vport *vport);
> >  int iecm_init_rss(struct iecm_vport *vport);
> >  void iecm_deinit_rss(struct iecm_vport *vport);
> > +bool iecm_rx_can_reuse_page(struct iecm_rx_buf *rx_buf);
> > +void iecm_rx_buf_adjust_pg(struct iecm_rx_buf *rx_buf, unsigned int size);
> > +void iecm_rx_add_frag(struct iecm_rx_buf *rx_buf, struct sk_buff *skb,
> > +		      unsigned int size);
> > +struct sk_buff *iecm_rx_construct_skb(struct iecm_queue *rxq,
> > +				      struct iecm_rx_buf *rx_buf,
> > +				      unsigned int size);
> > +void iecm_rx_skb(struct iecm_queue *rxq, struct sk_buff *skb, u16 vlan_tag);
> >  bool iecm_init_rx_buf_hw_alloc(struct iecm_queue *rxq, struct iecm_rx_buf
> *buf);
> >  void iecm_rx_buf_hw_update(struct iecm_queue *rxq, u32 val);
> >  void iecm_tx_buf_hw_update(struct iecm_queue *tx_q, u32 val,
> >  			   bool xmit_more);
> > +void iecm_rx_splitq_put_bufs(struct iecm_queue *rx_bufq,
> > +			     struct iecm_rx_buf *hdr_buf,
> > +			     struct iecm_rx_buf *rx_buf);
> > +bool iecm_rx_splitq_test_staterr(u8 stat_err_field, const u8 stat_err_bits);
> > +int iecm_rx_process_skb_fields(struct iecm_queue *rxq, struct sk_buff *skb,
> > +			       struct virtchnl2_rx_flex_desc_adv_nic_3 *rx_desc);
> > +bool iecm_rx_splitq_extract_vlan_tag(struct
> virtchnl2_rx_flex_desc_adv_nic_3 *desc,
> > +				     struct iecm_queue *rxq, u16 *vlan_tag);
> > +void iecm_rx_bump_ntc(struct iecm_queue *q);
> >  void iecm_tx_buf_rel(struct iecm_queue *tx_q, struct iecm_tx_buf *tx_buf);
> >  unsigned int iecm_size_to_txd_count(unsigned int size);
> >  unsigned int iecm_tx_desc_count_required(struct sk_buff *skb);
> > --
> > 2.33.0
> 
> Thanks,
> Al

^ permalink raw reply	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 13/19] iecm: implement splitq napi_poll
  2022-01-28 17:44       ` Alexander Lobakin
@ 2022-02-03  1:15         ` Brady, Alan
  -1 siblings, 0 replies; 89+ messages in thread
From: Brady, Alan @ 2022-02-03  1:15 UTC (permalink / raw)
  To: intel-wired-lan

> -----Original Message-----
> From: Lobakin, Alexandr <alexandr.lobakin@intel.com>
> Sent: Friday, January 28, 2022 9:44 AM
> To: Brady, Alan <alan.brady@intel.com>
> Cc: Lobakin, Alexandr <alexandr.lobakin@intel.com>; lkp <lkp@intel.com>;
> intel-wired-lan at lists.osuosl.org; Burra, Phani R <phani.r.burra@intel.com>;
> Chittim, Madhu <madhu.chittim@intel.com>; kbuild-all at lists.01.org; Linga,
> Pavan Kumar <pavan.kumar.linga@intel.com>
> Subject: Re: [Intel-wired-lan] [PATCH net-next 13/19] iecm: implement splitq
> napi_poll
> 
> From: kernel test robot <lkp@intel.com>
> Date: Fri, 28 Jan 2022 13:21:42 +0800
> 
> > Hi Alan,
> >
> > Thank you for the patch! Yet something to improve:
> >
> > [auto build test ERROR on net-next/master]
> >
> > url:    https://github.com/0day-ci/linux/commits/Alan-Brady/Add-iecm-and-
> idpf/20220128-085513
> > base:   https://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next.git
> e2cf07654efb0fd7bbcb475c6f74be7b5755a8fd
> > config: arc-allyesconfig
> > (https://download.01.org/0day-ci/archive/20220128/202201281316.ZdiaZw6
> > q-lkp at intel.com/config)
> > compiler: arceb-elf-gcc (GCC) 11.2.0
> > reproduce (this is a W=1 build):
> >         wget https://raw.githubusercontent.com/intel/lkp-
> tests/master/sbin/make.cross -O ~/bin/make.cross
> >         chmod +x ~/bin/make.cross
> >         # https://github.com/0day-
> ci/linux/commit/8e9b2451747f81363327cf5a4e07aaf88af52397
> >         git remote add linux-review https://github.com/0day-ci/linux
> >         git fetch --no-tags linux-review Alan-Brady/Add-iecm-and-idpf/20220128-
> 085513
> >         git checkout 8e9b2451747f81363327cf5a4e07aaf88af52397
> >         # save the config file to linux build tree
> >         mkdir build_dir
> >         COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0
> > make.cross O=build_dir ARCH=arc SHELL=/bin/bash
> >
> > If you fix the issue, kindly add following tag as appropriate
> > Reported-by: kernel test robot <lkp@intel.com>
> >
> > All errors (new ones prefixed by >>):
> >
> >    drivers/net/ethernet/intel/iecm/iecm_txrx.c: In function
> 'iecm_rx_can_reuse_page':
> > >> drivers/net/ethernet/intel/iecm/iecm_txrx.c:3132:19: error: 'struct
> iecm_rx_buf' has no member named 'page_offset'
> >     3132 |         if (rx_buf->page_offset > last_offset)
> >          |                   ^~
> 
> So these series wasn't even compile-tested properly.
> x86 has only 4k pages, but lots of other architectures have much more. If ARC is
> not relevant enough to MEV, then I say that
> ARM64 has 4-64 Kb pages, and PowerPC64 has up to 256 Kb.
> 

It definitely was compile tested, but juggling around 30k lines of code gets tricky and a mistake or two was inevitable. This should be a trivial fix.

> >
> >
> > vim +3132 drivers/net/ethernet/intel/iecm/iecm_txrx.c
> >
> >   3103
> >   3104	/**
> >   3105	 * iecm_rx_can_reuse_page - Determine if page can be reused for
> another rx
> >   3106	 * @rx_buf: buffer containing the page
> >   3107	 *
> >   3108	 * If page is reusable, we have a green light for calling
> iecm_reuse_rx_page,
> >   3109	 * which will assign the current buffer to the buffer that next_to_alloc is
> >   3110	 * pointing to; otherwise, the dma mapping needs to be destroyed and
> >   3111	 * page freed
> >   3112	 */
> >   3113	bool iecm_rx_can_reuse_page(struct iecm_rx_buf *rx_buf)
> >   3114	{
> >   3115		struct iecm_page_info *page_info = &rx_buf-
> >page_info[rx_buf->page_indx];
> >   3116
> >   3117	#if (PAGE_SIZE >= 8192)
> >   3118		unsigned int last_offset = PAGE_SIZE - rx_buf->buf_size;
> >   3119	#endif /* PAGE_SIZE < 8192) */
> >   3120		unsigned int pagecnt_bias = page_info->pagecnt_bias;
> >   3121		struct page *page = page_info->page;
> >   3122
> >   3123		/* avoid re-using remote pages */
> >   3124		if (unlikely(iecm_rx_page_is_reserved(page)))
> >   3125			return false;
> >   3126
> >   3127	#if (PAGE_SIZE < 8192)
> >   3128		/* if we are only owner of page we can reuse it */
> >   3129		if (unlikely((page_count(page) - pagecnt_bias) > 1))
> >   3130			return false;
> >   3131	#else
> > > 3132		if (rx_buf->page_offset > last_offset)
> >   3133			return false;
> >   3134	#endif /* PAGE_SIZE < 8192) */
> >   3135
> >   3136		/* If we have drained the page fragment pool we need to
> update
> >   3137		 * the pagecnt_bias and page count so that we fully restock the
> >   3138		 * number of references the driver holds.
> >   3139		 */
> >   3140		if (unlikely(pagecnt_bias == 1)) {
> >   3141			page_ref_add(page, USHRT_MAX - 1);
> >   3142			page_info->pagecnt_bias = USHRT_MAX;
> >   3143		}
> >   3144
> >   3145		return true;
> >   3146	}
> >   3147
> >
> > ---
> > 0-DAY CI Kernel Test Service, Intel Corporation
> > https://lists.01.org/hyperkitty/list/kbuild-all at lists.01.org
> 
> Al

^ permalink raw reply	[flat|nested] 89+ messages in thread

* Re: [Intel-wired-lan] [PATCH net-next 13/19] iecm: implement splitq napi_poll
@ 2022-02-03  1:15         ` Brady, Alan
  0 siblings, 0 replies; 89+ messages in thread
From: Brady, Alan @ 2022-02-03  1:15 UTC (permalink / raw)
  To: kbuild-all

[-- Attachment #1: Type: text/plain, Size: 4929 bytes --]

> -----Original Message-----
> From: Lobakin, Alexandr <alexandr.lobakin@intel.com>
> Sent: Friday, January 28, 2022 9:44 AM
> To: Brady, Alan <alan.brady@intel.com>
> Cc: Lobakin, Alexandr <alexandr.lobakin@intel.com>; lkp <lkp@intel.com>;
> intel-wired-lan(a)lists.osuosl.org; Burra, Phani R <phani.r.burra@intel.com>;
> Chittim, Madhu <madhu.chittim@intel.com>; kbuild-all(a)lists.01.org; Linga,
> Pavan Kumar <pavan.kumar.linga@intel.com>
> Subject: Re: [Intel-wired-lan] [PATCH net-next 13/19] iecm: implement splitq
> napi_poll
> 
> From: kernel test robot <lkp@intel.com>
> Date: Fri, 28 Jan 2022 13:21:42 +0800
> 
> > Hi Alan,
> >
> > Thank you for the patch! Yet something to improve:
> >
> > [auto build test ERROR on net-next/master]
> >
> > url:    https://github.com/0day-ci/linux/commits/Alan-Brady/Add-iecm-and-
> idpf/20220128-085513
> > base:   https://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next.git
> e2cf07654efb0fd7bbcb475c6f74be7b5755a8fd
> > config: arc-allyesconfig
> > (https://download.01.org/0day-ci/archive/20220128/202201281316.ZdiaZw6
> > q-lkp(a)intel.com/config)
> > compiler: arceb-elf-gcc (GCC) 11.2.0
> > reproduce (this is a W=1 build):
> >         wget https://raw.githubusercontent.com/intel/lkp-
> tests/master/sbin/make.cross -O ~/bin/make.cross
> >         chmod +x ~/bin/make.cross
> >         # https://github.com/0day-
> ci/linux/commit/8e9b2451747f81363327cf5a4e07aaf88af52397
> >         git remote add linux-review https://github.com/0day-ci/linux
> >         git fetch --no-tags linux-review Alan-Brady/Add-iecm-and-idpf/20220128-
> 085513
> >         git checkout 8e9b2451747f81363327cf5a4e07aaf88af52397
> >         # save the config file to linux build tree
> >         mkdir build_dir
> >         COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0
> > make.cross O=build_dir ARCH=arc SHELL=/bin/bash
> >
> > If you fix the issue, kindly add following tag as appropriate
> > Reported-by: kernel test robot <lkp@intel.com>
> >
> > All errors (new ones prefixed by >>):
> >
> >    drivers/net/ethernet/intel/iecm/iecm_txrx.c: In function
> 'iecm_rx_can_reuse_page':
> > >> drivers/net/ethernet/intel/iecm/iecm_txrx.c:3132:19: error: 'struct
> iecm_rx_buf' has no member named 'page_offset'
> >     3132 |         if (rx_buf->page_offset > last_offset)
> >          |                   ^~
> 
> So these series wasn't even compile-tested properly.
> x86 has only 4k pages, but lots of other architectures have much more. If ARC is
> not relevant enough to MEV, then I say that
> ARM64 has 4-64 Kb pages, and PowerPC64 has up to 256 Kb.
> 

It definitely was compile tested, but juggling around 30k lines of code gets tricky and a mistake or two was inevitable. This should be a trivial fix.

> >
> >
> > vim +3132 drivers/net/ethernet/intel/iecm/iecm_txrx.c
> >
> >   3103
> >   3104	/**
> >   3105	 * iecm_rx_can_reuse_page - Determine if page can be reused for
> another rx
> >   3106	 * @rx_buf: buffer containing the page
> >   3107	 *
> >   3108	 * If page is reusable, we have a green light for calling
> iecm_reuse_rx_page,
> >   3109	 * which will assign the current buffer to the buffer that next_to_alloc is
> >   3110	 * pointing to; otherwise, the dma mapping needs to be destroyed and
> >   3111	 * page freed
> >   3112	 */
> >   3113	bool iecm_rx_can_reuse_page(struct iecm_rx_buf *rx_buf)
> >   3114	{
> >   3115		struct iecm_page_info *page_info = &rx_buf-
> >page_info[rx_buf->page_indx];
> >   3116
> >   3117	#if (PAGE_SIZE >= 8192)
> >   3118		unsigned int last_offset = PAGE_SIZE - rx_buf->buf_size;
> >   3119	#endif /* PAGE_SIZE < 8192) */
> >   3120		unsigned int pagecnt_bias = page_info->pagecnt_bias;
> >   3121		struct page *page = page_info->page;
> >   3122
> >   3123		/* avoid re-using remote pages */
> >   3124		if (unlikely(iecm_rx_page_is_reserved(page)))
> >   3125			return false;
> >   3126
> >   3127	#if (PAGE_SIZE < 8192)
> >   3128		/* if we are only owner of page we can reuse it */
> >   3129		if (unlikely((page_count(page) - pagecnt_bias) > 1))
> >   3130			return false;
> >   3131	#else
> > > 3132		if (rx_buf->page_offset > last_offset)
> >   3133			return false;
> >   3134	#endif /* PAGE_SIZE < 8192) */
> >   3135
> >   3136		/* If we have drained the page fragment pool we need to
> update
> >   3137		 * the pagecnt_bias and page count so that we fully restock the
> >   3138		 * number of references the driver holds.
> >   3139		 */
> >   3140		if (unlikely(pagecnt_bias == 1)) {
> >   3141			page_ref_add(page, USHRT_MAX - 1);
> >   3142			page_info->pagecnt_bias = USHRT_MAX;
> >   3143		}
> >   3144
> >   3145		return true;
> >   3146	}
> >   3147
> >
> > ---
> > 0-DAY CI Kernel Test Service, Intel Corporation
> > https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org
> 
> Al

^ permalink raw reply	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 14/19] iecm: implement singleq napi_poll
  2022-01-28 17:57   ` Alexander Lobakin
@ 2022-02-03  1:45     ` Brady, Alan
  2022-02-03 19:05       ` Alexander Lobakin
  0 siblings, 1 reply; 89+ messages in thread
From: Brady, Alan @ 2022-02-03  1:45 UTC (permalink / raw)
  To: intel-wired-lan

> -----Original Message-----
> From: Lobakin, Alexandr <alexandr.lobakin@intel.com>
> Sent: Friday, January 28, 2022 9:58 AM
> To: Brady, Alan <alan.brady@intel.com>
> Cc: Lobakin, Alexandr <alexandr.lobakin@intel.com>; intel-wired-
> lan at lists.osuosl.org; Burra, Phani R <phani.r.burra@intel.com>; Chittim,
> Madhu <madhu.chittim@intel.com>; Linga, Pavan Kumar
> <pavan.kumar.linga@intel.com>
> Subject: [Intel-wired-lan] [PATCH net-next 14/19] iecm: implement singleq
> napi_poll
> 
> > From: Alan Brady <alan.brady@intel.com>
> > Date: Thu, 27 Jan 2022 16:10:04 -0800
> >
> > This adds everything we do the more traditional singleq model data path.
> >
> > Signed-off-by: Phani Burra <phani.r.burra@intel.com>
> > Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
> > Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
> > Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
> > Signed-off-by: Alan Brady <alan.brady@intel.com>
> > ---
> >  drivers/net/ethernet/intel/iecm/iecm_lib.c    |    2 +-
> >  .../ethernet/intel/iecm/iecm_singleq_txrx.c   | 1208
> ++++++++++++++++-
> >  drivers/net/ethernet/intel/include/iecm.h     |    1 +
> >  .../net/ethernet/intel/include/iecm_txrx.h    |   31 +
> >  4 files changed, 1237 insertions(+), 5 deletions(-)
> >
> > diff --git a/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > index cc82e665dfaf..cbde65f1c523 100644
> > --- a/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > +++ b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > @@ -2723,7 +2723,7 @@ static const struct net_device_ops
> > iecm_netdev_ops_splitq = {  static const struct net_device_ops
> iecm_netdev_ops_singleq = {
> >  	.ndo_open = iecm_open,
> >  	.ndo_stop = iecm_stop,
> > -	.ndo_start_xmit = NULL,
> > +	.ndo_start_xmit = iecm_tx_singleq_start,
> >  	.ndo_set_rx_mode = iecm_set_rx_mode,
> >  	.ndo_validate_addr = eth_validate_addr,
> >  	.ndo_set_mac_address = iecm_set_mac, diff --git
> > a/drivers/net/ethernet/intel/iecm/iecm_singleq_txrx.c
> > b/drivers/net/ethernet/intel/iecm/iecm_singleq_txrx.c
> > index d6c47cb84249..7bfec79e6afc 100644
> > --- a/drivers/net/ethernet/intel/iecm/iecm_singleq_txrx.c
> > +++ b/drivers/net/ethernet/intel/iecm/iecm_singleq_txrx.c
> > @@ -3,6 +3,779 @@
> >
> >  #include "iecm.h"
> >
> > +/**
> > + * iecm_tx_singleq_csum - Enable tx checksum offloads
> > + * @first: pointer to first descriptor
> > + * @off: pointer to struct that holds offload parameters
> > + *
> > + * Returns 0 or error (negative) if checksum offload  */ static int
> > +iecm_tx_singleq_csum(struct iecm_tx_buf *first,
> > +			 struct iecm_tx_offload_params *off) {
> > +	u32 l4_len = 0, l3_len = 0, l2_len = 0;
> > +	struct sk_buff *skb = first->skb;
> > +	union {
> > +		struct iphdr *v4;
> > +		struct ipv6hdr *v6;
> > +		unsigned char *hdr;
> > +	} ip;
> > +	union {
> > +		struct tcphdr *tcp;
> > +		unsigned char *hdr;
> > +	} l4;
> > +	__be16 frag_off, protocol;
> > +	unsigned char *exthdr;
> > +	u32 offset, cmd = 0;
> > +	u8 l4_proto = 0;
> > +
> > +	if (skb->ip_summed != CHECKSUM_PARTIAL)
> > +		return 0;
> > +
> > +	if (skb->encapsulation)
> > +		return -1;
> > +
> > +	ip.hdr = skb_network_header(skb);
> > +	l4.hdr = skb_transport_header(skb);
> > +
> > +	/* compute outer L2 header size */
> > +	l2_len = ip.hdr - skb->data;
> > +	offset = (l2_len / 2) << IECM_TX_DESC_LEN_MACLEN_S;
> > +
> > +	/* Enable IP checksum offloads */
> > +	protocol = vlan_get_protocol(skb);
> > +	if (protocol == htons(ETH_P_IP)) {
> > +		l4_proto = ip.v4->protocol;
> > +		/* the stack computes the IP header already, the only time
> we
> > +		 * need the hardware to recompute it is in the case of TSO.
> > +		 */
> > +		if (first->tx_flags & IECM_TX_FLAGS_TSO)
> > +			cmd |= IECM_TX_DESC_CMD_IIPT_IPV4_CSUM;
> > +		else
> > +			cmd |= IECM_TX_DESC_CMD_IIPT_IPV4;
> > +
> > +	} else if (protocol == htons(ETH_P_IPV6)) {
> > +		cmd |= IECM_TX_DESC_CMD_IIPT_IPV6;
> > +		exthdr = ip.hdr + sizeof(struct ipv6hdr);
> > +		l4_proto = ip.v6->nexthdr;
> > +		if (l4.hdr != exthdr)
> > +			ipv6_skip_exthdr(skb, exthdr - skb->data,
> &l4_proto,
> > +					 &frag_off);
> > +	} else {
> > +		return -1;
> > +	}
> > +
> > +	/* compute inner L3 header size */
> > +	l3_len = l4.hdr - ip.hdr;
> > +	offset |= (l3_len / 4) << IECM_TX_DESC_LEN_IPLEN_S;
> > +
> > +	/* Enable L4 checksum offloads */
> > +	switch (l4_proto) {
> > +	case IPPROTO_TCP:
> > +		/* enable checksum offloads */
> > +		cmd |= IECM_TX_DESC_CMD_L4T_EOFT_TCP;
> > +		l4_len = l4.tcp->doff;
> > +		offset |= l4_len << IECM_TX_DESC_LEN_L4_LEN_S;
> > +		break;
> > +	case IPPROTO_UDP:
> > +		/* enable UDP checksum offload */
> > +		cmd |= IECM_TX_DESC_CMD_L4T_EOFT_UDP;
> > +		l4_len = (sizeof(struct udphdr) >> 2);
> > +		offset |= l4_len << IECM_TX_DESC_LEN_L4_LEN_S;
> > +		break;
> > +	case IPPROTO_SCTP:
> > +		/* enable SCTP checksum offload */
> > +		cmd |= IECM_TX_DESC_CMD_L4T_EOFT_SCTP;
> > +		l4_len = sizeof(struct sctphdr) >> 2;
> > +		offset |= l4_len << IECM_TX_DESC_LEN_L4_LEN_S;
> > +		break;
> > +
> > +	default:
> > +		if (first->tx_flags & IECM_TX_FLAGS_TSO)
> > +			return -1;
> > +		skb_checksum_help(skb);
> > +		return 0;
> > +	}
> > +
> > +	off->td_cmd |= cmd;
> > +	off->hdr_offsets |= offset;
> > +	return 1;
> > +}
> > +
> > +/**
> > + * iecm_tx_singleq_map - Build the Tx base descriptor
> > + * @tx_q: queue to send buffer on
> > + * @first: first buffer info buffer to use
> > + * @offloads: pointer to struct that holds offload parameters
> > + *
> > + * This function loops over the skb data pointed to by *first
> > + * and gets a physical address for each memory location and programs
> > + * it and the length into the transmit base mode descriptor.
> > + */
> > +static void
> > +iecm_tx_singleq_map(struct iecm_queue *tx_q, struct iecm_tx_buf
> *first,
> > +		    struct iecm_tx_offload_params *offloads) {
> > +	u32 offsets = offloads->hdr_offsets;
> > +	struct iecm_base_tx_desc *tx_desc;
> > +	u64 td_cmd = offloads->td_cmd;
> > +	unsigned int data_len, size;
> > +	struct iecm_tx_buf *tx_buf;
> > +	u16 i = tx_q->next_to_use;
> > +	struct netdev_queue *nq;
> > +	struct sk_buff *skb;
> > +	skb_frag_t *frag;
> > +	dma_addr_t dma;
> > +	u64 td_tag = 0;
> > +
> > +	skb = first->skb;
> > +
> > +	data_len = skb->data_len;
> > +	size = skb_headlen(skb);
> > +
> > +	tx_desc = IECM_BASE_TX_DESC(tx_q, i);
> > +
> > +	if (first->tx_flags & IECM_TX_FLAGS_VLAN_TAG) {
> > +		td_cmd |= (u64)IECM_TX_DESC_CMD_IL2TAG1;
> > +		td_tag = (first->tx_flags & IECM_TX_FLAGS_VLAN_MASK)
> >>
> > +			 IECM_TX_FLAGS_VLAN_SHIFT;
> > +	}
> > +
> > +	dma = dma_map_single(tx_q->dev, skb->data, size,
> DMA_TO_DEVICE);
> > +
> > +	tx_buf = first;
> > +
> > +	/* write each descriptor with CRC bit */
> > +	if (tx_q->vport->adapter->dev_ops.crc_enable)
> > +		tx_q->vport->adapter->dev_ops.crc_enable(&td_cmd);
> > +
> > +	for (frag = &skb_shinfo(skb)->frags[0];; frag++) {
> > +		unsigned int max_data =
> IECM_TX_MAX_DESC_DATA_ALIGNED;
> > +
> > +		if (dma_mapping_error(tx_q->dev, dma))
> > +			goto dma_error;
> > +
> > +		/* record length, and DMA address */
> > +		dma_unmap_len_set(tx_buf, len, size);
> > +		dma_unmap_addr_set(tx_buf, dma, dma);
> > +
> > +		/* align size to end of page */
> > +		max_data += -dma & (IECM_TX_MAX_READ_REQ_SIZE - 1);
> 
> Here applies the same I said for splitq before, this code is counter-intuitive.
> 

I'm failing to find a matching comment for iecm_tx_splitq_map but I'm sure we're open to suggestion how to make it better.

> > +		tx_desc->buf_addr = cpu_to_le64(dma);
> > +
> > +		/* account for data chunks larger than the hardware
> > +		 * can handle
> > +		 */
> > +		while (unlikely(size > IECM_TX_MAX_DESC_DATA)) {
> > +			tx_desc->qw1 =
> iecm_tx_singleq_build_ctob(td_cmd,
> > +								  offsets,
> > +								  max_data,
> > +								  td_tag);
> > +			tx_desc++;
> > +			i++;
> > +
> > +			if (i == tx_q->desc_count) {
> > +				tx_desc = IECM_BASE_TX_DESC(tx_q, 0);
> > +				i = 0;
> > +			}
> > +
> > +			dma += max_data;
> > +			size -= max_data;
> > +
> > +			max_data = IECM_TX_MAX_DESC_DATA_ALIGNED;
> > +			tx_desc->buf_addr = cpu_to_le64(dma);
> > +		}
> > +
> > +		if (likely(!data_len))
> > +			break;
> > +		tx_desc->qw1 = iecm_tx_singleq_build_ctob(td_cmd,
> offsets,
> > +							  size, td_tag);
> > +		tx_desc++;
> > +		i++;
> > +
> > +		if (i == tx_q->desc_count) {
> > +			tx_desc = IECM_BASE_TX_DESC(tx_q, 0);
> > +			i = 0;
> > +		}
> > +
> > +		size = skb_frag_size(frag);
> > +		data_len -= size;
> > +
> > +		dma = skb_frag_dma_map(tx_q->dev, frag, 0, size,
> > +				       DMA_TO_DEVICE);
> > +
> > +		tx_buf = &tx_q->tx_buf[i];
> > +	}
> > +
> > +	/* record bytecount for BQL */
> > +	nq = netdev_get_tx_queue(tx_q->vport->netdev, tx_q->idx);
> > +	netdev_tx_sent_queue(nq, first->bytecount);
> > +
> > +	/* record SW timestamp if HW timestamp is not available */
> > +	skb_tx_timestamp(first->skb);
> > +
> > +	/* write last descriptor with RS and EOP bits */
> > +	td_cmd |= (u64)(IECM_TX_DESC_CMD_EOP |
> IECM_TX_DESC_CMD_RS);
> > +
> > +	tx_desc->qw1 = iecm_tx_singleq_build_ctob(td_cmd, offsets, size,
> > +td_tag);
> > +
> > +	i++;
> > +	if (i == tx_q->desc_count)
> > +		i = 0;
> > +
> > +	/* set next_to_watch value indicating a packet is present */
> > +	first->next_to_watch = tx_desc;
> > +
> > +	iecm_tx_buf_hw_update(tx_q, i, netdev_xmit_more());
> > +
> > +	return;
> > +
> > +dma_error:
> > +	/* clear dma mappings for failed tx_buf map */
> > +	for (;;) {
> > +		tx_buf = &tx_q->tx_buf[i];
> > +		iecm_tx_buf_rel(tx_q, tx_buf);
> > +		if (tx_buf == first)
> > +			break;
> > +		if (i == 0)
> > +			i = tx_q->desc_count;
> > +		i--;
> > +	}
> > +
> > +	tx_q->next_to_use = i;
> > +}
> > +
> > +/**
> > + * iecm_tx_singleq_frame - Sends buffer on Tx ring using base
> > +descriptors
> > + * @skb: send buffer
> > + * @tx_q: queue to send buffer on
> > + *
> > + * Returns NETDEV_TX_OK if sent, else an error code  */ static
> > +netdev_tx_t iecm_tx_singleq_frame(struct sk_buff *skb, struct
> > +iecm_queue *tx_q) {
> > +	struct iecm_tx_offload_params offload = {0};
> > +	struct iecm_tx_buf *first;
> > +	unsigned int count;
> > +	int csum, tso;
> > +
> > +	count = iecm_tx_desc_count_required(skb);
> > +
> > +	if (iecm_chk_linearize(skb, tx_q->tx_max_bufs, count)) {
> > +		if (__skb_linearize(skb)) {
> > +			dev_kfree_skb_any(skb);
> > +			return NETDEV_TX_OK;
> > +		}
> > +		count = iecm_size_to_txd_count(skb->len);
> > +		tx_q->vport->port_stats.tx_linearize++;
> > +	}
> > +
> > +	if (iecm_tx_maybe_stop(tx_q, count +
> IECM_TX_DESCS_PER_CACHE_LINE +
> > +			       IECM_TX_DESCS_FOR_CTX)) {
> > +		return NETDEV_TX_BUSY;
> > +	}
> > +
> > +	/* record the location of the first descriptor for this packet */
> > +	first = &tx_q->tx_buf[tx_q->next_to_use];
> > +	first->skb = skb;
> > +	first->bytecount = max_t(unsigned int, skb->len, ETH_ZLEN);
> > +	first->gso_segs = 1;
> > +	first->tx_flags = 0;
> > +
> > +	iecm_tx_prepare_vlan_flags(tx_q, first, skb);
> > +
> > +	tso = iecm_tso(first, &offload);
> > +	if (tso < 0)
> > +		goto out_drop;
> > +
> > +	csum = iecm_tx_singleq_csum(first, &offload);
> > +	if (csum < 0)
> > +		goto out_drop;
> > +
> > +	if (first->tx_flags & IECM_TX_FLAGS_TSO) {
> > +		struct iecm_base_tx_ctx_desc *ctx_desc;
> > +		int i = tx_q->next_to_use;
> > +		u64 qw1 = (u64)IECM_TX_DESC_DTYPE_CTX |
> > +			       IECM_TX_CTX_DESC_TSO <<
> IECM_TXD_CTX_QW1_CMD_S;
> > +
> > +		/* grab the next descriptor */
> > +		ctx_desc = IECM_BASE_TX_CTX_DESC(tx_q, i);
> > +		i++;
> > +		tx_q->next_to_use = (i < tx_q->desc_count) ? i : 0;
> > +
> > +		qw1 |= ((u64)offload.tso_len <<
> IECM_TXD_CTX_QW1_TSO_LEN_S) &
> > +			IECM_TXD_CTX_QW1_TSO_LEN_M;
> > +
> > +		qw1 |= ((u64)offload.mss << IECM_TXD_CTX_QW1_MSS_S)
> &
> > +			IECM_TXD_CTX_QW1_MSS_M;
> > +
> > +		ctx_desc->qw0.rsvd0 = cpu_to_le32(0);
> > +		ctx_desc->qw0.l2tag2 = cpu_to_le16(0);
> > +		ctx_desc->qw0.rsvd1 = cpu_to_le16(0);
> > +		ctx_desc->qw1 = cpu_to_le64(qw1);
> > +	}
> > +
> > +	iecm_tx_singleq_map(tx_q, first, &offload);
> > +
> > +	return NETDEV_TX_OK;
> > +
> > +out_drop:
> > +	dev_kfree_skb_any(skb);
> > +	return NETDEV_TX_OK;
> > +}
> > +
> > +/**
> > + * iecm_tx_singleq_start - Selects the right Tx queue to send buffer
> > + * @skb: send buffer
> > + * @netdev: network interface device structure
> > + *
> > + * Returns NETDEV_TX_OK if sent, else an error code  */ netdev_tx_t
> > +iecm_tx_singleq_start(struct sk_buff *skb,
> > +				  struct net_device *netdev)
> > +{
> > +	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
> > +	struct iecm_queue *tx_q;
> > +
> > +	if (test_bit(__IECM_HR_RESET_IN_PROG, vport->adapter->flags))
> > +		return NETDEV_TX_BUSY;
> > +
> > +	tx_q = vport->txqs[skb->queue_mapping];
> > +
> > +	/* hardware can't handle really short frames, hardware padding
> works
> > +	 * beyond this point
> > +	 */
> > +	if (skb_put_padto(skb, IECM_TX_MIN_LEN))
> > +		return NETDEV_TX_OK;
> > +
> > +	return iecm_tx_singleq_frame(skb, tx_q); }
> > +
> > +/**
> > + * iecm_tx_singleq_clean - Reclaim resources from queue
> > + * @tx_q: Tx queue to clean
> > + * @napi_budget: Used to determine if we are in netpoll
> > + *
> > + */
> > +static bool iecm_tx_singleq_clean(struct iecm_queue *tx_q, int
> > +napi_budget) {
> > +	unsigned int budget = tx_q->vport->compln_clean_budget;
> > +	unsigned int total_bytes = 0, total_pkts = 0;
> > +	struct iecm_base_tx_desc *tx_desc;
> > +	s16 ntc = tx_q->next_to_clean;
> > +	struct iecm_tx_buf *tx_buf;
> > +	struct netdev_queue *nq;
> > +
> > +	tx_desc = IECM_BASE_TX_DESC(tx_q, ntc);
> > +	tx_buf = &tx_q->tx_buf[ntc];
> > +	ntc -= tx_q->desc_count;
> > +
> > +	do {
> > +		struct iecm_base_tx_desc *eop_desc =
> > +			(struct iecm_base_tx_desc *)tx_buf-
> >next_to_watch;
> 
> 		struct iecm_base_tx_desc *eop_desc;
> 
> 		eop_desc = (typeof(*eop_desc))tx_buf->next_to_watch;
> 

Again not sure I see the benefit to prefer assigning it's own line over letting it wrap.  Suggestion as written is wrong also.

> > +
> > +		/* if next_to_watch is not set then no work pending */
> > +		if (!eop_desc)
> > +			break;
> > +
> > +		/* prevent any other reads prior to eop_desc */
> > +		smp_rmb();
> > +
> > +		/* if the descriptor isn't done, no work yet to do */
> > +		if (!(eop_desc->qw1 &
> > +		      cpu_to_le64(IECM_TX_DESC_DTYPE_DESC_DONE)))
> > +			break;
> > +
> > +		/* clear next_to_watch to prevent false hangs */
> > +		tx_buf->next_to_watch = NULL;
> > +
> > +		/* update the statistics for this packet */
> > +		total_bytes += tx_buf->bytecount;
> > +		total_pkts += tx_buf->gso_segs;
> > +
> > +		/* free the skb */
> > +		napi_consume_skb(tx_buf->skb, napi_budget);
> > +
> > +		/* unmap skb header data */
> > +		dma_unmap_single(tx_q->dev,
> > +				 dma_unmap_addr(tx_buf, dma),
> > +				 dma_unmap_len(tx_buf, len),
> > +				 DMA_TO_DEVICE);
> > +
> > +		/* clear tx_buf data */
> > +		tx_buf->skb = NULL;
> > +		dma_unmap_len_set(tx_buf, len, 0);
> > +
> > +		/* unmap remaining buffers */
> > +		while (tx_desc != eop_desc) {
> > +			tx_buf++;
> > +			tx_desc++;
> > +			ntc++;
> > +			if (unlikely(!ntc)) {
> > +				ntc -= tx_q->desc_count;
> > +				tx_buf = tx_q->tx_buf;
> > +				tx_desc = IECM_BASE_TX_DESC(tx_q, 0);
> > +			}
> > +
> > +			/* unmap any remaining paged data */
> > +			if (dma_unmap_len(tx_buf, len)) {
> 
> Here applies the same I said for splitq about dma_unmap_len().
> 

Sure we'll take a look at dma_unmap_len

> > +				dma_unmap_page(tx_q->dev,
> > +					       dma_unmap_addr(tx_buf, dma),
> > +					       dma_unmap_len(tx_buf, len),
> > +					       DMA_TO_DEVICE);
> > +				dma_unmap_len_set(tx_buf, len, 0);
> > +			}
> > +		}
> > +
> > +		tx_buf++;
> > +		tx_desc++;
> > +		ntc++;
> > +		if (unlikely(!ntc)) {
> > +			ntc -= tx_q->desc_count;
> > +			tx_buf = tx_q->tx_buf;
> > +			tx_desc = IECM_BASE_TX_DESC(tx_q, 0);
> > +		}
> > +		/* update budget */
> > +		budget--;
> > +	} while (likely(budget));
> > +
> > +	ntc += tx_q->desc_count;
> > +	tx_q->next_to_clean = ntc;
> > +
> > +	u64_stats_update_begin(&tx_q->stats_sync);
> > +	tx_q->q_stats.tx.packets += total_pkts;
> > +	tx_q->q_stats.tx.bytes += total_bytes;
> > +	u64_stats_update_end(&tx_q->stats_sync);
> > +
> > +	nq = netdev_get_tx_queue(tx_q->vport->netdev, tx_q->idx);
> > +	netdev_tx_completed_queue(nq, total_pkts, total_bytes);
> > +
> > +	if (unlikely(total_pkts && netif_carrier_ok(tx_q->vport->netdev)
> &&
> > +		     (IECM_DESC_UNUSED(tx_q) >=
> IECM_TX_WAKE_THRESH))) {
> > +		/* Make sure any other threads stopping queue after this
> see
> > +		 * new next_to_clean.
> > +		 */
> > +		smp_mb();
> > +		if (__netif_subqueue_stopped(tx_q->vport->netdev, tx_q-
> >idx) &&
> > +		    tx_q->vport->adapter->state == __IECM_UP)
> > +			netif_wake_subqueue(tx_q->vport->netdev, tx_q-
> >idx);
> > +	}
> > +
> > +	return !!budget;
> > +}
> > +
> > +/**
> > + * iecm_tx_singleq_clean_all - Clean all Tx queues
> > + * @q_vec: queue vector
> > + * @budget: Used to determine if we are in netpoll
> > + *
> > + * Returns false if clean is not complete else returns true  */
> > +static bool iecm_tx_singleq_clean_all(struct iecm_q_vector *q_vec,
> > +int budget) {
> > +	bool clean_complete = true;
> > +	int i, budget_per_q;
> > +
> > +	budget_per_q = max(budget / q_vec->num_txq, 1);
> > +	for (i = 0; i < q_vec->num_txq; i++)
> > +		clean_complete = iecm_tx_singleq_clean(q_vec->tx[i],
> budget_per_q);
> 
> 84 cols per line.
> 
> > +
> > +	return clean_complete;
> > +}
> > +
> > +/**
> > + * iecm_rx_singleq_test_staterr - tests bits in Rx descriptor
> > + * status and error fields
> > + * @rx_desc: pointer to receive descriptor (in le64 format)
> > + * @stat_err_bits: value to mask
> > + *
> > + * This function does some fast chicanery in order to return the
> > + * value of the mask which is really only used for boolean tests.
> > + * The status_error_ptype_len doesn't need to be shifted because it
> > +begins
> > + * at offset zero.
> > + */
> > +bool
> > +iecm_rx_singleq_test_staterr(union virtchnl2_rx_desc *rx_desc,
> > +			     const u64 stat_err_bits)
> > +{
> > +	return !!(rx_desc->base_wb.qword1.status_error_ptype_len &
> > +		  cpu_to_le64(stat_err_bits));
> > +}
> > +
> > +/**
> > + * iecm_rx_singleq_is_non_eop - process handling of non-EOP buffers
> > + * @rxq: Rx ring being processed
> > + * @rx_desc: Rx descriptor for current buffer
> > + * @skb: Current socket buffer containing buffer in progress  */ bool
> > +iecm_rx_singleq_is_non_eop(struct iecm_queue *rxq,
> > +				union virtchnl2_rx_desc *rx_desc,
> > +				struct sk_buff *skb)
> > +{
> > +	/* if we are the last buffer then there is nothing else to do */
> > +#define IECM_RXD_EOF
> BIT(VIRTCHNL2_RX_BASE_DESC_STATUS_EOF_S)
> 
> Not a good place for a definition, makes reading difficult.
> 

Will fix.

> > +	if (likely(iecm_rx_singleq_test_staterr(rx_desc, IECM_RXD_EOF)))
> > +		return false;
> > +
> > +	/* place skb in next buffer to be received */
> > +	rxq->rx_buf.buf[rxq->next_to_clean].skb = skb;
> > +
> > +	return true;
> > +}
> > +
> > +/**
> > + * iecm_rx_singleq_csum - Indicate in skb if checksum is good
> > + * @rxq: Rx descriptor ring packet is being transacted on
> > + * @skb: skb currently being received and modified
> > + * @csum_bits: descriptor csum bits
> > + * @ptype: the packet type decoded by hardware
> > + *
> > + * skb->protocol must be set before this function is called  */
> > +static void iecm_rx_singleq_csum(struct iecm_queue *rxq, struct
> sk_buff *skb,
> > +				 struct iecm_rx_csum_decoded *csum_bits,
> > +				 u16 ptype)
> > +{
> > +	struct iecm_rx_ptype_decoded decoded;
> > +	bool ipv4, ipv6;
> > +
> > +	/* Start with CHECKSUM_NONE and by default csum_level = 0 */
> > +	skb->ip_summed = CHECKSUM_NONE;
> > +	skb_checksum_none_assert(skb);
> > +
> > +	/* check if Rx checksum is enabled */
> > +	if (!(rxq->vport->netdev->features & NETIF_F_RXCSUM))
> > +		return;
> > +
> > +	/* check if HW has decoded the packet and checksum */
> > +	if (!(csum_bits->l3l4p))
> > +		return;
> > +
> > +	decoded = rxq->vport->rx_ptype_lkup[ptype];
> > +	if (!(decoded.known && decoded.outer_ip))
> > +		return;
> > +
> > +	ipv4 = (decoded.outer_ip == IECM_RX_PTYPE_OUTER_IP) &&
> > +	       (decoded.outer_ip_ver == IECM_RX_PTYPE_OUTER_IPV4);
> > +	ipv6 = (decoded.outer_ip == IECM_RX_PTYPE_OUTER_IP) &&
> > +	       (decoded.outer_ip_ver == IECM_RX_PTYPE_OUTER_IPV6);
> > +
> > +	if (ipv4 && (csum_bits->ipe || csum_bits->eipe))
> > +		goto checksum_fail;
> > +	else if (ipv6 && (csum_bits->ipv6exadd))
> > +		goto checksum_fail;
> > +
> > +	/* check for L4 errors and handle packets that were not able to be
> > +	 * checksummed due to arrival speed
> > +	 */
> > +	if (csum_bits->l4e)
> > +		goto checksum_fail;
> > +
> > +	if (csum_bits->nat && csum_bits->eudpe)
> > +		goto checksum_fail;
> > +
> > +	/* Handle packets that were not able to be checksummed due to
> arrival
> > +	 * speed, in this case the stack can compute the csum.
> > +	 */
> > +	if (csum_bits->pprs)
> > +		return;
> > +
> > +	/* If there is an outer header present that might contain a
> checksum
> > +	 * we need to bump the checksum level by 1 to reflect the fact that
> > +	 * we are indicating we validated the inner checksum.
> > +	 */
> > +	if (decoded.tunnel_type >= IECM_RX_PTYPE_TUNNEL_IP_GRENAT)
> > +		skb->csum_level = 1;
> > +
> > +	/* Only report checksum unnecessary for ICMP, TCP, UDP, or SCTP
> */
> > +	switch (decoded.inner_prot) {
> > +	case IECM_RX_PTYPE_INNER_PROT_ICMP:
> > +	case IECM_RX_PTYPE_INNER_PROT_TCP:
> > +	case IECM_RX_PTYPE_INNER_PROT_UDP:
> > +	case IECM_RX_PTYPE_INNER_PROT_SCTP:
> > +		skb->ip_summed = CHECKSUM_UNNECESSARY;
> > +	default:
> > +		break;
> > +	}
> > +	return;
> > +
> > +checksum_fail:
> > +	rxq->vport->port_stats.rx_hw_csum_err++;
> > +}
> > +
> > +/**
> > + * iecm_rx_singleq_base_csum - Indicate in skb if hw indicated a good
> > +cksum
> > + * @rx_q: Rx completion queue
> > + * @skb: skb currently being received and modified
> > + * @rx_desc: the receive descriptor
> > + * @ptype: Rx packet type
> > + *
> > + * This function only operates on the
> VIRTCHNL2_RXDID_1_32B_BASE_M
> > +legacy 32byte
> > + * descriptor writeback format.
> > + **/
> > +static void
> > +iecm_rx_singleq_base_csum(struct iecm_queue *rx_q, struct sk_buff
> *skb,
> > +			  union virtchnl2_rx_desc *rx_desc, u16 ptype) {
> > +	struct iecm_rx_csum_decoded csum_bits;
> > +	u32 rx_error, rx_status;
> > +	u64 qword;
> > +
> > +	qword = le64_to_cpu(rx_desc-
> >base_wb.qword1.status_error_ptype_len);
> > +
> > +	rx_status = ((qword &
> VIRTCHNL2_RX_BASE_DESC_QW1_STATUS_M) >>
> > +
> 	VIRTCHNL2_RX_BASE_DESC_QW1_STATUS_S);
> > +	rx_error = ((qword & VIRTCHNL2_RX_BASE_DESC_QW1_ERROR_M)
> >>
> > +
> 	VIRTCHNL2_RX_BASE_DESC_QW1_ERROR_S);
> > +
> > +	csum_bits.ipe = !!(rx_error &
> BIT(VIRTCHNL2_RX_BASE_DESC_ERROR_IPE_S));
> > +	csum_bits.eipe = !!(rx_error &
> BIT(VIRTCHNL2_RX_BASE_DESC_ERROR_EIPE_S));
> > +	csum_bits.l4e = !!(rx_error &
> BIT(VIRTCHNL2_RX_BASE_DESC_ERROR_L4E_S));
> > +	csum_bits.pprs = !!(rx_error &
> BIT(VIRTCHNL2_RX_BASE_DESC_ERROR_PPRS_S));
> > +	csum_bits.l3l4p = !!(rx_status &
> > +
> BIT(VIRTCHNL2_RX_BASE_DESC_STATUS_L3L4P_S));
> > +	csum_bits.ipv6exadd = !!(rx_status &
> > +
> BIT(VIRTCHNL2_RX_BASE_DESC_STATUS_IPV6EXADD_S));
> > +	csum_bits.nat = 0;
> > +	csum_bits.eudpe = 0;
> > +
> > +	iecm_rx_singleq_csum(rx_q, skb, &csum_bits, ptype); }
> > +
> > +/**
> > + * iecm_rx_singleq_flex_csum - Indicate in skb if hw indicated a good
> > +cksum
> > + * @rx_q: Rx completion queue
> > + * @skb: skb currently being received and modified
> > + * @rx_desc: the receive descriptor
> > + * @ptype: Rx packet type
> > + *
> > + * This function only operates on the
> VIRTCHNL2_RXDID_2_FLEX_SQ_NIC
> > +flexible
> > + * descriptor writeback format.
> > + **/
> > +static void
> > +iecm_rx_singleq_flex_csum(struct iecm_queue *rx_q, struct sk_buff
> *skb,
> > +			  union virtchnl2_rx_desc *rx_desc, u16 ptype) {
> > +	struct iecm_rx_csum_decoded csum_bits;
> > +	u16 rx_status0, rx_status1;
> > +
> > +	rx_status0 = le16_to_cpu(rx_desc->flex_nic_wb.status_error0);
> > +	rx_status1 = le16_to_cpu(rx_desc->flex_nic_wb.status_error1);
> > +
> > +	csum_bits.ipe = !!(rx_status0 &
> > +
> BIT(VIRTCHNL2_RX_FLEX_DESC_STATUS0_XSUM_IPE_S));
> > +	csum_bits.eipe = !!(rx_status0 &
> > +
> BIT(VIRTCHNL2_RX_FLEX_DESC_STATUS0_XSUM_EIPE_S));
> > +	csum_bits.l4e = !!(rx_status0 &
> > +
> BIT(VIRTCHNL2_RX_FLEX_DESC_STATUS0_XSUM_L4E_S));
> > +	csum_bits.eudpe = !!(rx_status0 &
> > +
> BIT(VIRTCHNL2_RX_FLEX_DESC_STATUS0_XSUM_EUDPE_S));
> > +	csum_bits.l3l4p = !!(rx_status0 &
> > +
> BIT(VIRTCHNL2_RX_FLEX_DESC_STATUS0_L3L4P_S));
> > +	csum_bits.ipv6exadd =
> > +			!!(rx_status0 &
> > +
> BIT(VIRTCHNL2_RX_FLEX_DESC_STATUS0_IPV6EXADD_S));
> > +	csum_bits.nat = !!(rx_status1 &
> > +
> BIT(VIRTCHNL2_RX_FLEX_DESC_STATUS1_NAT_S));
> > +	csum_bits.pprs = 0;
> > +
> > +	iecm_rx_singleq_csum(rx_q, skb, &csum_bits, ptype); }
> > +
> > +/**
> > + * iecm_rx_singleq_base_hash - set the hash value in the skb
> > + * @rx_q: Rx completion queue
> > + * @skb: skb currently being received and modified
> > + * @rx_desc: specific descriptor
> > + * @decoded: Decoded Rx packet type related fields
> > + *
> > + * This function only operates on the
> VIRTCHNL2_RXDID_1_32B_BASE_M
> > +legacy 32byte
> > + * descriptor writeback format.
> > + **/
> > +static void
> > +iecm_rx_singleq_base_hash(struct iecm_queue *rx_q, struct sk_buff
> *skb,
> > +			  union virtchnl2_rx_desc *rx_desc,
> > +			  struct iecm_rx_ptype_decoded *decoded) {
> > +	const __le64 rss_mask =
> > +
> 	cpu_to_le64((u64)VIRTCHNL2_RX_BASE_DESC_FLTSTAT_RSS_HASH
> <<
> 
> You can define _RSS_HASH with a 'ull' suffix to avoid casting.
> You can define a shorthand for this, like _RSS_HASH_LE to avoid these
> wraps.
> 

Good idea, will fix.

> > +
> 	VIRTCHNL2_RX_BASE_DESC_STATUS_FLTSTAT_S);
> > +	u32 hash;
> > +
> > +	if (!(rx_q->vport->netdev->features & NETIF_F_RXHASH))
> > +		return;
> > +
> > +	if ((rx_desc->base_wb.qword1.status_error_ptype_len & rss_mask)
> ==
> > +								rss_mask) {
> 
> This is wrong indentation, just align it to the first opening brace.
> 

Sure will fix.

> > +		hash = le32_to_cpu(rx_desc-
> >base_wb.qword0.hi_dword.rss);
> > +		skb_set_hash(skb, hash, iecm_ptype_to_htype(decoded));
> > +	}
> > +}
> > +
> > +/**
> > + * iecm_rx_singleq_flex_hash - set the hash value in the skb
> > + * @rx_q: Rx completion queue
> > + * @skb: skb currently being received and modified
> > + * @rx_desc: specific descriptor
> > + * @decoded: Decoded Rx packet type related fields
> > + *
> > + * This function only operates on the
> VIRTCHNL2_RXDID_2_FLEX_SQ_NIC
> > +flexible
> > + * descriptor writeback format.
> > + **/
> > +static void
> > +iecm_rx_singleq_flex_hash(struct iecm_queue *rx_q, struct sk_buff
> *skb,
> > +			  union virtchnl2_rx_desc *rx_desc,
> > +			  struct iecm_rx_ptype_decoded *decoded) {
> > +	__le16 status0;
> > +
> > +	if (!(rx_q->vport->netdev->features & NETIF_F_RXHASH))
> > +		return;
> > +
> > +	status0 = rx_desc->flex_nic_wb.status_error0;
> > +	if (status0 &
> > +
> 	cpu_to_le16(BIT(VIRTCHNL2_RX_FLEX_DESC_STATUS0_RSS_VALID_
> S))) {
> > +		u32 hash = le32_to_cpu(rx_desc->flex_nic_wb.rss_hash);
> > +
> > +		skb_set_hash(skb, hash, iecm_ptype_to_htype(decoded));
> > +	}
> 
> 	if (!status)
> 		return;
> 
> -1 indent level.
> 

if (!(status0 & cpu_to_le16(BIT(VIRTCHNL2_RX_FLEX_DESC_STATUS0_RSS_VALID_S))))
    return;

skb_set_hash(...);

vs.

if (status0 & cpu_to_le16(BIT(VIRTCHNL2_RX_FLEX_DESC_STATUS0_RSS_VALID_S)))
    skb_set_hash(....);

Doesn't seem any better to me, in fact it seems worse.

> > +}
> > +
> > +/**
> > + * iecm_rx_singleq_process_skb_fields - Populate skb header fields
> > +from Rx
> > + * descriptor
> > + * @rx_q: Rx descriptor ring packet is being transacted on
> > + * @skb: pointer to current skb being populated
> > + * @rx_desc: descriptor for skb
> > + * @ptype: packet type
> > + *
> > + * This function checks the ring, descriptor, and packet information
> > +in
> > + * order to populate the hash, checksum, VLAN, protocol, and
> > + * other fields within the skb.
> > + */
> > +void
> > +iecm_rx_singleq_process_skb_fields(struct iecm_queue *rx_q, struct
> sk_buff *skb,
> > +				   union virtchnl2_rx_desc *rx_desc,
> > +				   u16 ptype)
> > +{
> > +	struct iecm_rx_ptype_decoded decoded =
> > +					rx_q->vport->rx_ptype_lkup[ptype];
> 
> Declare, then assign to avoid wraps.
> 

I'm just not seeing the benefit in doing that.  If 'net' tree is concerned with wrapping I would assume they would move to 100 cols with the rest of the kernel.

> > +
> > +	/* modifies the skb - consumes the enet header */
> > +	skb->protocol = eth_type_trans(skb, rx_q->vport->netdev);
> 
> Here applies the same I said for splitq, eth_type_trans() should be right
> before napi_gro_receive().
> 
> > +
> > +	if (rx_q->rxdids == VIRTCHNL2_RXDID_1_32B_BASE_M) {
> > +		iecm_rx_singleq_base_hash(rx_q, skb, rx_desc, &decoded);
> > +		iecm_rx_singleq_base_csum(rx_q, skb, rx_desc, ptype);
> > +	} else {
> > +		iecm_rx_singleq_flex_hash(rx_q, skb, rx_desc, &decoded);
> > +		iecm_rx_singleq_flex_csum(rx_q, skb, rx_desc, ptype);
> > +	}
> > +}
> > +
> >  /**
> >   * iecm_rx_singleq_buf_hw_alloc_all - Replace used receive buffers
> >   * @rx_q: queue for which the hw buffers are allocated @@ -13,8
> > +786,410 @@  bool iecm_rx_singleq_buf_hw_alloc_all(struct
> iecm_queue
> > *rx_q,
> >  				      u16 cleaned_count)
> >  {
> > -	/* stub */
> > -	return true;
> > +	struct virtchnl2_singleq_rx_buf_desc *singleq_rx_desc = NULL;
> > +	struct iecm_page_info *page_info;
> > +	u16 nta = rx_q->next_to_alloc;
> > +	struct iecm_rx_buf *buf;
> > +
> > +	/* do nothing if no valid netdev defined */
> > +	if (!rx_q->vport->netdev || !cleaned_count)
> > +		return false;
> > +
> > +	singleq_rx_desc = IECM_SINGLEQ_RX_BUF_DESC(rx_q, nta);
> > +	buf = &rx_q->rx_buf.buf[nta];
> > +	page_info = &buf->page_info[buf->page_indx];
> > +
> > +	do {
> > +		if (unlikely(!page_info->page)) {
> > +			if (!iecm_init_rx_buf_hw_alloc(rx_q, buf))
> > +				break;
> > +		}
> 
> 	if (1 && 2)
> 

Will fix.

> > +
> > +		/* Refresh the desc even if buffer_addrs didn't change
> > +		 * because each write-back erases this info.
> > +		 */
> > +		singleq_rx_desc->pkt_addr =
> > +			cpu_to_le64(page_info->dma + page_info-
> >page_offset);
> > +		singleq_rx_desc->hdr_addr = 0;
> > +		singleq_rx_desc++;
> > +
> > +		buf++;
> > +		nta++;
> > +		if (unlikely(nta == rx_q->desc_count)) {
> 
> Can be squashed into ++nta == count.
> 

Will not fix.

> > +			singleq_rx_desc =
> IECM_SINGLEQ_RX_BUF_DESC(rx_q, 0);
> > +			buf = rx_q->rx_buf.buf;
> > +			nta = 0;
> > +		}
> > +
> > +		page_info = &buf->page_info[buf->page_indx];
> > +
> > +		cleaned_count--;
> > +	} while (cleaned_count);
> 
> And these into `while (--cleaned_count);`
> 

We would prefer not to.

> > +
> > +	if (rx_q->next_to_alloc != nta) {
> > +		iecm_rx_buf_hw_update(rx_q, nta);
> > +		rx_q->next_to_alloc = nta;
> > +	}
> > +
> > +	return !!cleaned_count;
> > +}
> > +
> > +/**
> > + * iecm_rx_reuse_page - Put recycled buffer back onto ring
> > + * @rxq: Rx descriptor ring to store buffers on
> > + * @old_buf: donor buffer to have page reused  */ static void
> > +iecm_rx_reuse_page(struct iecm_queue *rxq,
> > +			       struct iecm_rx_buf *old_buf) {
> > +	u16 ntu = rxq->next_to_use;
> > +	struct iecm_rx_buf *new_buf;
> 
> RCTree.
> 

Will fix.

> > +
> > +	new_buf = &rxq->rx_buf.buf[ntu];
> > +
> > +	/* Transfer page from old buffer to new buffer.  Move each member
> > +	 * individually to avoid possible store forwarding stalls and
> > +	 * unnecessary copy of skb.
> > +	 */
> > +	new_buf->page_info[new_buf->page_indx].dma =
> > +				old_buf->page_info[old_buf-
> >page_indx].dma;
> > +	new_buf->page_info[new_buf->page_indx].page =
> > +				old_buf->page_info[old_buf-
> >page_indx].page;
> > +	new_buf->page_info[new_buf->page_indx].page_offset =
> > +			old_buf->page_info[old_buf-
> >page_indx].page_offset;
> > +	new_buf->page_info[new_buf->page_indx].pagecnt_bias =
> > +			old_buf->page_info[old_buf-
> >page_indx].pagecnt_bias;
> > +}
> > +
> > +/**
> > + * iecm_rx_singleq_recycle_buf - Clean up used buffer and either
> > +recycle or free
> > + * @rxq: Rx descriptor queue to transact packets on
> > + * @rx_buf: Rx buffer to pull data from
> > + *
> > + * This function will clean up the contents of the rx_buf. It will
> > +either
> > + * recycle the buffer or unmap it and free the associated resources.
> > + *
> > + * Returns true if the buffer is reused, false if the buffer is freed.
> > + */
> > +static bool iecm_rx_singleq_recycle_buf(struct iecm_queue *rxq,
> > +					struct iecm_rx_buf *rx_buf)
> > +{
> > +	struct iecm_page_info *page_info =
> > +			&rx_buf->page_info[rx_buf->page_indx];
> 
> Declare, then assign to avoid wraps.
>

Will not fix.
 
> > +
> 
> No empty lines between declarations.
> 

Will fix

> > +	bool recycled = false;
> > +
> > +	if (iecm_rx_can_reuse_page(rx_buf)) {
> > +		/* hand second half of page back to the queue */
> > +		iecm_rx_reuse_page(rxq, rx_buf);
> > +		recycled = true;
> > +	} else {
> > +		/* we are not reusing the buffer so unmap it */
> > +		dma_unmap_page_attrs(rxq->dev, page_info->dma,
> PAGE_SIZE,
> > +				     DMA_FROM_DEVICE,
> IECM_RX_DMA_ATTR);
> > +		__page_frag_cache_drain(page_info->page,
> > +					page_info->pagecnt_bias);
> > +	}
> > +
> > +	/* clear contents of buffer_info */
> > +	page_info->page = NULL;
> > +	rx_buf->skb = NULL;
> > +
> > +	return recycled;
> > +}
> > +
> > +/**
> > + * iecm_rx_singleq_put_buf - Wrapper function to clean and recycle
> > +buffers
> > + * @rxq: Rx descriptor queue to transact packets on
> > + * @rx_buf: Rx buffer to pull data from
> > + *
> > + * This function will update the next_to_use/next_to_alloc if the
> > +current
> > + * buffer is recycled.
> > + */
> > +static void iecm_rx_singleq_put_buf(struct iecm_queue *rxq,
> > +				    struct iecm_rx_buf *rx_buf)
> > +{
> > +	u16 ntu = rxq->next_to_use;
> > +	bool recycled = false;
> > +
> > +	recycled = iecm_rx_singleq_recycle_buf(rxq, rx_buf);
> > +
> > +	/* update, and store next to alloc if the buffer was recycled */
> > +	if (recycled) {
> > +		ntu++;
> > +		rxq->next_to_use = (ntu < rxq->desc_count) ? ntu : 0;
> > +	}
> > +}
> > +
> > +/**
> > + * iecm_rx_singleq_bump_ntc - Bump and wrap q->next_to_clean value
> > + * @rxq: queue to bump
> > + */
> > +void iecm_rx_singleq_bump_ntc(struct iecm_queue *rxq) {
> > +	u16 ntc = rxq->next_to_clean + 1;
> > +	/* fetch, update, and store next to clean */
> > +	if (ntc < rxq->desc_count)
> > +		rxq->next_to_clean = ntc;
> > +	else
> > +		rxq->next_to_clean = 0;
> > +}
> > +
> > +/**
> > + * iecm_rx_singleq_get_buf_page - Fetch Rx buffer page and
> > +synchronize data
> > + * @dev: device struct
> > + * @rx_buf: Rx buf to fetch page for
> > + * @size: size of buffer to add to skb
> > + *
> > + * This function will pull an Rx buffer page from the ring and
> > +synchronize it
> > + * for use by the CPU.
> > + */
> > +static struct sk_buff *
> > +iecm_rx_singleq_get_buf_page(struct device *dev, struct iecm_rx_buf
> *rx_buf,
> > +			     const unsigned int size)
> > +{
> > +	struct iecm_page_info *page_info;
> > +
> > +	page_info = &rx_buf->page_info[rx_buf->page_indx];
> > +	prefetch(page_info->page);
> > +
> > +	/* we are reusing so sync this buffer for CPU use */
> > +	dma_sync_single_range_for_cpu(dev, page_info->dma,
> > +				      page_info->page_offset, size,
> > +				      DMA_FROM_DEVICE);
> > +
> > +	/* We have pulled a buffer for use, so decrement pagecnt_bias */
> > +	page_info->pagecnt_bias--;
> > +
> > +	return rx_buf->skb;
> > +}
> > +
> > +/**
> > + * iecm_rx_singleq_extract_base_fields - Extract fields from the Rx
> > +descriptor
> > + * @rx_q: Rx descriptor queue
> > + * @rx_desc: the descriptor to process
> > + * @fields: storage for extracted values
> > + *
> > + * Decode the Rx descriptor and extract relevant information
> > +including the
> > + * size, VLAN tag, and Rx packet type.
> > + *
> > + * This function only operates on the
> VIRTCHNL2_RXDID_1_32B_BASE_M
> > +legacy 32byte
> > + * descriptor writeback format.
> > + */
> > +static inline void
> > +iecm_rx_singleq_extract_base_fields(struct iecm_queue *rx_q,
> > +				    union virtchnl2_rx_desc *rx_desc,
> > +				    struct iecm_rx_extracted *fields) {
> > +	u64 qw2_status, qword;
> > +
> > +	qword = le64_to_cpu(rx_desc-
> >base_wb.qword1.status_error_ptype_len);
> > +	qw2_status = le16_to_cpu(rx_desc->base_wb.qword2.ext_status);
> > +
> > +	fields->size = (qword &
> VIRTCHNL2_RX_BASE_DESC_QW1_LEN_PBUF_M) >>
> > +
> 	VIRTCHNL2_RX_BASE_DESC_QW1_LEN_PBUF_S;
> > +	fields->rx_ptype = (qword &
> VIRTCHNL2_RX_BASE_DESC_QW1_PTYPE_M) >>
> > +
> 	VIRTCHNL2_RX_BASE_DESC_QW1_PTYPE_S;
> > +
> > +	if (qword & BIT(VIRTCHNL2_RX_BASE_DESC_STATUS_L2TAG1P_S)
> &&
> > +	    test_bit(__IECM_Q_VLAN_TAG_LOC_L2TAG1, rx_q->flags))
> > +		fields->vlan_tag =
> > +			le16_to_cpu(rx_desc-
> >base_wb.qword0.lo_dword.l2tag1);
> > +	if (qw2_status &
> BIT(VIRTCHNL2_RX_BASE_DESC_EXT_STATUS_L2TAG2P_S) &&
> > +	    test_bit(__IECM_Q_VLAN_TAG_LOC_L2TAG2, rx_q->flags))
> > +		fields->vlan_tag =
> > +			le16_to_cpu(rx_desc->base_wb.qword2.l2tag2_1);
> > +}
> > +
> > +/**
> > + * iecm_rx_singleq_extract_flex_fields - Extract fields from the Rx
> > +descriptor
> > + * @rx_q: Rx descriptor queue
> > + * @rx_desc: the descriptor to process
> > + * @fields: storage for extracted values
> > + *
> > + * Decode the Rx descriptor and extract relevant information
> > +including the
> > + * size, VLAN tag, and Rx packet type.
> > + *
> > + * This function only operates on the
> VIRTCHNL2_RXDID_2_FLEX_SQ_NIC
> > +flexible
> > + * descriptor writeback format.
> > + */
> > +static inline void
> > +iecm_rx_singleq_extract_flex_fields(struct iecm_queue *rx_q,
> > +				    union virtchnl2_rx_desc *rx_desc,
> > +				    struct iecm_rx_extracted *fields) {
> > +	__le16 status0, status1;
> > +
> > +	fields->size = le16_to_cpu(rx_desc->flex_nic_wb.pkt_len) &
> > +
> 	VIRTCHNL2_RX_FLEX_DESC_PKT_LEN_M;
> > +	fields->rx_ptype = le16_to_cpu(rx_desc-
> >flex_nic_wb.ptype_flex_flags0) &
> > +
> 	VIRTCHNL2_RX_FLEX_DESC_PTYPE_M;
> > +
> > +	status0 = rx_desc->flex_nic_wb.status_error0;
> > +	if (status0 &
> cpu_to_le16(BIT(VIRTCHNL2_RX_FLEX_DESC_STATUS0_L2TAG1P_S)) &&
> > +	    test_bit(__IECM_Q_VLAN_TAG_LOC_L2TAG1, rx_q->flags))
> > +		fields->vlan_tag = le16_to_cpu(rx_desc-
> >flex_nic_wb.l2tag1);
> > +
> > +	status1 = rx_desc->flex_nic_wb.status_error1;
> > +	if (status1 &
> cpu_to_le16(BIT(VIRTCHNL2_RX_FLEX_DESC_STATUS1_L2TAG2P_S)) &&
> > +	    test_bit(__IECM_Q_VLAN_TAG_LOC_L2TAG2, rx_q->flags))
> > +		fields->vlan_tag = le16_to_cpu(rx_desc-
> >flex_nic_wb.l2tag2_2nd);
> > +}
> > +
> > +/**
> > + * iecm_rx_singleq_extract_fields - Extract fields from the Rx
> > +descriptor
> > + * @rx_q: Rx descriptor queue
> > + * @rx_desc: the descriptor to process
> > + * @fields: storage for extracted values
> > + *
> > + */
> > +void
> > +iecm_rx_singleq_extract_fields(struct iecm_queue *rx_q,
> > +			       union virtchnl2_rx_desc *rx_desc,
> > +			       struct iecm_rx_extracted *fields) {
> > +	if (rx_q->rxdids == VIRTCHNL2_RXDID_1_32B_BASE_M)
> > +		iecm_rx_singleq_extract_base_fields(rx_q, rx_desc, fields);
> > +	else
> > +		iecm_rx_singleq_extract_flex_fields(rx_q, rx_desc, fields); }
> > +
> > +/**
> > + * iecm_rx_singleq_clean - Reclaim resources after receive completes
> > + * @rx_q: rx queue to clean
> > + * @budget: Total limit on number of packets to process
> > + *
> > + * Returns true if there's any budget left (e.g. the clean is
> > +finished)  */ static int iecm_rx_singleq_clean(struct iecm_queue
> > +*rx_q, int budget) {
> > +	unsigned int total_rx_bytes = 0, total_rx_pkts = 0;
> > +	u16 cleaned_count = 0;
> > +	bool failure = false;
> > +
> > +	/* Process Rx packets bounded by budget */
> > +	while (likely(total_rx_pkts < (unsigned int)budget)) {
> > +		struct iecm_rx_extracted fields = {};
> > +		union virtchnl2_rx_desc *rx_desc;
> > +		struct sk_buff *skb = NULL;
> > +		struct iecm_rx_buf *rx_buf;
> > +
> > +		/* get the Rx desc from Rx queue based on 'next_to_clean'
> */
> > +		rx_desc = IECM_RX_DESC(rx_q, rx_q->next_to_clean);
> > +
> > +		/* This memory barrier is needed to keep us from reading
> > +		 * any other fields out of the rx_desc
> > +		 */
> > +		dma_rmb();
> > +
> > +		/* status_error_ptype_len will always be zero for unused
> > +		 * descriptors because it's cleared in cleanup, and overlaps
> > +		 * with hdr_addr which is always zero because packet split
> > +		 * isn't used, if the hardware wrote DD then the length will
> be
> > +		 * non-zero
> > +		 */
> > +#define IECM_RXD_DD BIT(VIRTCHNL2_RX_BASE_DESC_STATUS_DD_S)
> 
> Should be moved from here as well.
> 

Sure we can move it.

> > +		if (!iecm_rx_singleq_test_staterr(rx_desc,
> > +						  IECM_RXD_DD))
> > +			break;
> > +		iecm_rx_singleq_extract_fields(rx_q, rx_desc, &fields);
> > +
> > +		if (!fields.size)
> > +			break;
> > +
> > +		rx_buf = &rx_q->rx_buf.buf[rx_q->next_to_clean];
> > +		skb = iecm_rx_singleq_get_buf_page(rx_q->dev, rx_buf,
> > +						   fields.size);
> > +
> > +		if (skb)
> > +			iecm_rx_add_frag(rx_buf, skb, fields.size);
> > +		else
> > +			skb = iecm_rx_construct_skb(rx_q, rx_buf,
> fields.size);
> > +
> > +		/* exit if we failed to retrieve a buffer */
> > +		if (!skb) {
> > +			rx_buf->page_info[rx_buf-
> >page_indx].pagecnt_bias++;
> > +			break;
> > +		}
> > +
> > +		iecm_rx_singleq_put_buf(rx_q, rx_buf);
> > +		iecm_rx_singleq_bump_ntc(rx_q);
> > +
> > +		cleaned_count++;
> > +
> > +		/* skip if it is non EOP desc */
> > +		if (iecm_rx_singleq_is_non_eop(rx_q, rx_desc,
> > +					       skb))
> 
> skb fits into previous line.
> 

Will fix

> > +			continue;
> > +
> > +#define IECM_RXD_ERR_S
> BIT(VIRTCHNL2_RX_BASE_DESC_QW1_ERROR_S)
> 
> Same.
> 
> > +		if (unlikely(iecm_rx_singleq_test_staterr(rx_desc,
> > +							  IECM_RXD_ERR_S)))
> {
> > +			dev_kfree_skb_any(skb);
> > +			skb = NULL;
> > +			continue;
> > +		}
> > +
> > +		/* pad skb if needed (to make valid ethernet frame) */
> > +		if (eth_skb_pad(skb)) {
> > +			skb = NULL;
> > +			continue;
> > +		}
> > +
> > +		/* probably a little skewed due to removing CRC */
> > +		total_rx_bytes += skb->len;
> > +
> > +		/* protocol */
> > +		iecm_rx_singleq_process_skb_fields(rx_q, skb,
> > +						   rx_desc, fields.rx_ptype);
> > +
> > +		/* send completed skb up the stack */
> > +		iecm_rx_skb(rx_q, skb, fields.vlan_tag);
> > +
> > +		/* update budget accounting */
> > +		total_rx_pkts++;
> > +	}
> > +	if (cleaned_count)
> > +		failure = iecm_rx_singleq_buf_hw_alloc_all(rx_q,
> cleaned_count);
> > +
> > +	u64_stats_update_begin(&rx_q->stats_sync);
> > +	rx_q->q_stats.rx.packets += total_rx_pkts;
> > +	rx_q->q_stats.rx.bytes += total_rx_bytes;
> > +	u64_stats_update_end(&rx_q->stats_sync);
> > +
> > +	/* guarantee a trip back through this routine if there was a failure
> */
> > +	return failure ? budget : (int)total_rx_pkts; }
> > +
> > +/**
> > + * iecm_rx_singleq_clean_all - Clean all Rx queues
> > + * @q_vec: queue vector
> > + * @budget: Used to determine if we are in netpoll
> > + * @cleaned: returns number of packets cleaned
> > + *
> > + * Returns false if clean is not complete else returns true  */
> > +static bool iecm_rx_singleq_clean_all(struct iecm_q_vector *q_vec,
> > +int budget,
> > +			  int *cleaned)
> > +{
> > +	bool clean_complete = true;
> > +	int budget_per_q, i;
> > +
> > +	budget_per_q = max(budget / q_vec->num_rxq, 1);
> > +	for (i = 0; i < q_vec->num_rxq; i++) {
> > +		struct iecm_queue *rxq = q_vec->rx[i];
> > +		int pkts_cleaned_per_q;
> > +
> > +		pkts_cleaned_per_q = iecm_rx_singleq_clean(rxq,
> budget_per_q);
> > +
> > +		/* if we clean as many as budgeted, we must not be done */
> > +		if (pkts_cleaned_per_q >= budget_per_q)
> > +			clean_complete = false;
> > +		*cleaned += pkts_cleaned_per_q;
> > +	}
> > +
> > +	return clean_complete;
> >  }
> >
> >  /**
> > @@ -24,6 +1199,31 @@ bool iecm_rx_singleq_buf_hw_alloc_all(struct
> iecm_queue *rx_q,
> >   */
> >  int iecm_vport_singleq_napi_poll(struct napi_struct *napi, int
> > budget)  {
> > -	/* stub */
> > -	return 0;
> > +	struct iecm_q_vector *q_vector =
> > +				container_of(napi, struct iecm_q_vector,
> napi);
> 
> Declare + assign to avoid.
> 

Will not fix

> > +	bool clean_complete;
> > +	int work_done = 0;
> > +
> > +	clean_complete = iecm_tx_singleq_clean_all(q_vector, budget);
> > +
> > +	/* Handle case where we are called by netpoll with a budget of 0 */
> > +	if (budget <= 0)
> > +		return budget;
> > +
> > +	/* We attempt to distribute budget to each Rx queue fairly, but
> don't
> > +	 * allow the budget to go below 1 because that would exit polling
> early.
> > +	 */
> > +	clean_complete |= iecm_rx_singleq_clean_all(q_vector, budget,
> > +						    &work_done);
> > +
> > +	/* If work not completed, return budget and polling will return */
> > +	if (!clean_complete)
> > +		return budget;
> > +
> > +	/* Exit the polling mode, but don't re-enable interrupts if stack
> might
> > +	 * poll us due to busy-polling
> > +	 */
> > +	if (likely(napi_complete_done(napi, work_done)))
> > +		iecm_vport_intr_update_itr_ena_irq(q_vector);
> > +	return min_t(int, work_done, budget - 1);
> >  }
> > diff --git a/drivers/net/ethernet/intel/include/iecm.h
> > b/drivers/net/ethernet/intel/include/iecm.h
> > index 3cf2a2f0cb0f..97c9935b832d 100644
> > --- a/drivers/net/ethernet/intel/include/iecm.h
> > +++ b/drivers/net/ethernet/intel/include/iecm.h
> > @@ -8,6 +8,7 @@
> >  #include <net/pkt_cls.h>
> >  #include <linux/aer.h>
> >  #include <linux/pci.h>
> > +#include <linux/sctp.h>
> >  #include <linux/netdevice.h>
> >  #include <linux/etherdevice.h>
> >  #include <linux/ethtool.h>
> > diff --git a/drivers/net/ethernet/intel/include/iecm_txrx.h
> > b/drivers/net/ethernet/intel/include/iecm_txrx.h
> > index 086b0efc989a..4dba4f52be98 100644
> > --- a/drivers/net/ethernet/intel/include/iecm_txrx.h
> > +++ b/drivers/net/ethernet/intel/include/iecm_txrx.h
> > @@ -624,6 +624,26 @@ struct iecm_txq_group {
> >
> >  struct iecm_adapter;
> >
> > +/**
> > + * iecm_tx_singleq_build_ctob - populate command tag offset and size
> > + * @td_cmd: Command to be filled in desc
> > + * @td_offset: Offset to be filled in desc
> > + * @size: Size of the buffer
> > + * @td_tag: vlan tag to be filled
> > + *
> > + * Returns the 64 bit value populated with the input parameters  */
> > +static inline __le64
> > +iecm_tx_singleq_build_ctob(u64 td_cmd, u64 td_offset, unsigned int
> size,
> > +			   u64 td_tag)
> > +{
> > +	return cpu_to_le64(IECM_TX_DESC_DTYPE_DATA |
> > +			   (td_cmd    << IECM_TXD_QW1_CMD_S) |
> > +			   (td_offset << IECM_TXD_QW1_OFFSET_S) |
> > +			   ((u64)size << IECM_TXD_QW1_TX_BUF_SZ_S) |
> > +			   (td_tag    << IECM_TXD_QW1_L2TAG1_S));
> > +}
> > +
> >  void iecm_tx_splitq_build_ctb(union iecm_tx_flex_desc *desc,
> >  			      struct iecm_tx_splitq_params *parms,
> >  			      u16 td_cmd, u16 size);
> > @@ -673,8 +693,19 @@ void iecm_rx_splitq_put_bufs(struct
> iecm_queue *rx_bufq,
> >  			     struct iecm_rx_buf *hdr_buf,
> >  			     struct iecm_rx_buf *rx_buf);
> >  bool iecm_rx_splitq_test_staterr(u8 stat_err_field, const u8
> > stat_err_bits);
> > +bool iecm_rx_singleq_test_staterr(union virtchnl2_rx_desc *rx_desc,
> > +				  const u64 stat_err_bits);
> >  int iecm_rx_process_skb_fields(struct iecm_queue *rxq, struct sk_buff
> *skb,
> >  			       struct virtchnl2_rx_flex_desc_adv_nic_3
> *rx_desc);
> > +void iecm_rx_singleq_process_skb_fields(struct iecm_queue *rx_q,
> struct sk_buff *skb,
> > +					union virtchnl2_rx_desc *rx_desc,
> u16 ptype);
> 
> Break the line after `void` to avoid (huh) going past 79.
> 

Will fix

> > +void iecm_rx_singleq_extract_fields(struct iecm_queue *rx_q,
> > +				    union virtchnl2_rx_desc *rx_desc,
> > +				    struct iecm_rx_extracted *fields); void
> > +iecm_rx_singleq_bump_ntc(struct iecm_queue *q); bool
> > +iecm_rx_singleq_is_non_eop(struct iecm_queue *rxq,
> > +				union virtchnl2_rx_desc *rx_desc,
> > +				struct sk_buff *skb);
> >  bool iecm_rx_splitq_extract_vlan_tag(struct
> virtchnl2_rx_flex_desc_adv_nic_3 *desc,
> >  				     struct iecm_queue *rxq, u16 *vlan_tag);
> void
> > iecm_rx_bump_ntc(struct iecm_queue *q);
> > --
> > 2.33.0
> 
> Thanks,
> Al

^ permalink raw reply	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 15/19] iecm: implement ethtool callbacks
  2022-01-28 18:13   ` Alexander Lobakin
@ 2022-02-03  2:13     ` Brady, Alan
  2022-02-03 19:54       ` Alexander Lobakin
  0 siblings, 1 reply; 89+ messages in thread
From: Brady, Alan @ 2022-02-03  2:13 UTC (permalink / raw)
  To: intel-wired-lan

> -----Original Message-----
> From: Lobakin, Alexandr <alexandr.lobakin@intel.com>
> Sent: Friday, January 28, 2022 10:14 AM
> To: Brady, Alan <alan.brady@intel.com>
> Cc: Lobakin, Alexandr <alexandr.lobakin@intel.com>; intel-wired-
> lan at lists.osuosl.org; Burra, Phani R <phani.r.burra@intel.com>; Chittim,
> Madhu <madhu.chittim@intel.com>; Linga, Pavan Kumar
> <pavan.kumar.linga@intel.com>
> Subject: Re: [Intel-wired-lan] [PATCH net-next 15/19] iecm: implement
> ethtool callbacks
> 
> From: Alan Brady <alan.brady@intel.com>
> Date: Thu, 27 Jan 2022 16:10:05 -0800
> 
> > This does everything needed to handle ethtool ops minus a few stubs
> > for cloud filters and other advanced features which will be added in
> > later in this series.
> >
> > Signed-off-by: Phani Burra <phani.r.burra@intel.com>
> > Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
> > Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
> > Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
> > Signed-off-by: Alan Brady <alan.brady@intel.com>
> > ---
> >  drivers/net/ethernet/intel/iecm/Makefile      |    1 +
> >  .../net/ethernet/intel/iecm/iecm_ethtool.c    | 1325
> +++++++++++++++++
> >  drivers/net/ethernet/intel/iecm/iecm_lib.c    |   11 +-
> >  drivers/net/ethernet/intel/include/iecm.h     |    1 +
> >  4 files changed, 1337 insertions(+), 1 deletion(-)  create mode
> > 100644 drivers/net/ethernet/intel/iecm/iecm_ethtool.c
> >
> > diff --git a/drivers/net/ethernet/intel/iecm/Makefile
> > b/drivers/net/ethernet/intel/iecm/Makefile
> > index 205d6f2b436a..fe2ed403d35c 100644
> > --- a/drivers/net/ethernet/intel/iecm/Makefile
> > +++ b/drivers/net/ethernet/intel/iecm/Makefile
> > @@ -15,6 +15,7 @@ iecm-y := \
> >  	iecm_virtchnl.o \
> >  	iecm_txrx.o \
> >  	iecm_singleq_txrx.o \
> > +	iecm_ethtool.o \
> >  	iecm_controlq.o \
> >  	iecm_controlq_setup.o \
> >  	iecm_main.o
> > diff --git a/drivers/net/ethernet/intel/iecm/iecm_ethtool.c
> > b/drivers/net/ethernet/intel/iecm/iecm_ethtool.c
> > new file mode 100644
> > index 000000000000..32d905fb1bb6
> > --- /dev/null
> > +++ b/drivers/net/ethernet/intel/iecm/iecm_ethtool.c
> > @@ -0,0 +1,1325 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +/* Copyright (C) 2019 Intel Corporation */
> > +
> > +#include "iecm.h"
> > +
> > +/**
> > + * iecm_get_rxnfc - command to get RX flow classification rules
> > + * @netdev: network interface device structure
> > + * @cmd: ethtool rxnfc command
> > + * @rule_locs: pointer to store rule locations
> > + *
> > + * Returns Success if the command is supported.
> > + */
> > +static int iecm_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc
> *cmd,
> > +			  u32 __always_unused *rule_locs)
> 
> Kernel Kbuild system tell compilers to not complain on unused function
> arguments.
> It's pointless to add __always_unused here.
> 

Sparse does complain about it (e.g. make ... C=2) and it's present in other (non-Intel) network drivers. I think I need a better argument to remove it.

> > +{
> > +	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
> > +	int ret = -EOPNOTSUPP;
> > +
> > +	switch (cmd->cmd) {
> > +	case ETHTOOL_GRXRINGS:
> > +		cmd->data = vport->num_rxq;
> > +		ret = 0;
> > +		break;
> > +	case ETHTOOL_GRXCLSRLCNT:
> > +		/* stub */
> > +		ret = 0;
> > +		break;
> > +	case ETHTOOL_GRXCLSRULE:
> > +		/* stub */
> > +		break;
> > +	case ETHTOOL_GRXCLSRLALL:
> > +		/* stub */
> > +		break;
> > +	case ETHTOOL_GRXFH:
> > +		/* stub */
> > +		break;
> > +	default:
> > +		break;
> > +	}
> > +
> > +	return ret;
> > +}
> > +
> > +/**
> > + * iecm_set_rxnfc - command to set Rx flow rules.
> > + * @netdev: network interface device structure
> > + * @cmd: ethtool rxnfc command
> > + *
> > + * Returns 0 for success and negative values for errors  */ static
> > +int iecm_set_rxnfc(struct net_device *netdev, struct ethtool_rxnfc
> > +*cmd) {
> > +	int ret = -EOPNOTSUPP;
> > +
> > +	switch (cmd->cmd) {
> > +	case ETHTOOL_SRXCLSRLINS:
> > +		/* stub */
> > +		break;
> > +	case ETHTOOL_SRXCLSRLDEL:
> > +		/* stub */
> > +		break;
> > +	case ETHTOOL_SRXFH:
> > +		/* stub */
> > +		break;
> > +	default:
> > +		break;
> > +	}
> > +
> > +	return ret;
> > +}
> > +
> > +/**
> > + * iecm_get_rxfh_key_size - get the RSS hash key size
> > + * @netdev: network interface device structure
> > + *
> > + * Returns the table size.
> > + */
> > +static u32 iecm_get_rxfh_key_size(struct net_device *netdev) {
> > +	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
> > +
> > +	if (!iecm_is_cap_ena_all(vport->adapter, IECM_RSS_CAPS,
> IECM_CAP_RSS)) {
> > +		dev_info(&vport->adapter->pdev->dev, "RSS is not
> supported on this device\n");
> > +		return 0;
> > +	}
> > +
> > +	return vport->adapter->rss_data.rss_key_size;
> 
> If you invert the condition, it would take less lines.
> 

Sure why not. Will fix.

> > +}
> > +
> > +/**
> > + * iecm_get_rxfh_indir_size - get the rx flow hash indirection table
> > +size
> > + * @netdev: network interface device structure
> > + *
> > + * Returns the table size.
> > + */
> > +static u32 iecm_get_rxfh_indir_size(struct net_device *netdev) {
> > +	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
> > +
> > +	if (!iecm_is_cap_ena_all(vport->adapter, IECM_RSS_CAPS,
> IECM_CAP_RSS)) {
> > +		dev_info(&vport->adapter->pdev->dev, "RSS is not
> supported on this device\n");
> > +		return 0;
> > +	}
> > +
> > +	return vport->adapter->rss_data.rss_lut_size;
> 
> Same here.
> 
> > +}
> > +
> > +/**
> > + * iecm_get_rxfh - get the rx flow hash indirection table
> > + * @netdev: network interface device structure
> > + * @indir: indirection table
> > + * @key: hash key
> > + * @hfunc: hash function in use
> > + *
> > + * Reads the indirection table directly from the hardware. Always returns
> 0.
> > + */
> > +static int iecm_get_rxfh(struct net_device *netdev, u32 *indir, u8 *key,
> > +			 u8 *hfunc)
> > +{
> > +	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
> > +	struct iecm_adapter *adapter;
> > +	u16 i;
> > +
> > +	adapter = vport->adapter;
> > +
> > +	if (!iecm_is_cap_ena_all(adapter, IECM_RSS_CAPS,
> IECM_CAP_RSS)) {
> > +		dev_info(&vport->adapter->pdev->dev, "RSS is not
> supported on this device\n");
> > +		return 0;
> > +	}
> > +
> > +	if (adapter->state != __IECM_UP)
> > +		return 0;
> > +
> > +	if (hfunc)
> > +		*hfunc = ETH_RSS_HASH_TOP;
> > +
> > +	if (key)
> > +		memcpy(key, adapter->rss_data.rss_key,
> > +		       adapter->rss_data.rss_key_size);
> > +
> > +	if (indir)
> > +		/* Each 32 bits pointed by 'indir' is stored with a lut entry
> */
> > +		for (i = 0; i < adapter->rss_data.rss_lut_size; i++)
> > +			indir[i] = adapter->rss_data.rss_lut[i];
> > +
> > +	return 0;
> > +}
> > +
> > +/**
> > + * iecm_set_rxfh - set the rx flow hash indirection table
> > + * @netdev: network interface device structure
> > + * @indir: indirection table
> > + * @key: hash key
> > + * @hfunc: hash function to use
> > + *
> > + * Returns -EINVAL if the table specifies an invalid queue id,
> > +otherwise
> > + * returns 0 after programming the table.
> > + */
> > +static int iecm_set_rxfh(struct net_device *netdev, const u32 *indir,
> > +			 const u8 *key, const u8 hfunc)
> > +{
> > +	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
> > +	struct iecm_adapter *adapter;
> > +	u16 lut;
> > +
> > +	adapter = vport->adapter;
> > +
> > +	if (!iecm_is_cap_ena_all(adapter, IECM_RSS_CAPS,
> IECM_CAP_RSS)) {
> > +		dev_info(&adapter->pdev->dev, "RSS is not supported on
> this device\n");
> > +		return 0;
> > +	}
> > +	if (adapter->state != __IECM_UP)
> > +		return 0;
> > +	if (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc !=
> ETH_RSS_HASH_TOP)
> > +		return -EOPNOTSUPP;
> > +
> > +	if (key)
> > +		memcpy(adapter->rss_data.rss_key, key,
> > +		       adapter->rss_data.rss_key_size);
> > +
> > +	if (indir) {
> > +		for (lut = 0; lut < adapter->rss_data.rss_lut_size; lut++)
> > +			adapter->rss_data.rss_lut[lut] = indir[lut];
> > +	}
> > +
> > +	return iecm_config_rss(vport);
> > +}
> > +
> > +/**
> > + * iecm_get_channels: get the number of channels supported by the
> > +device
> > + * @netdev: network interface device structure
> > + * @ch: channel information structure
> > + *
> > + * Report maximum of TX and RX. Report one extra channel to match our
> > +MailBox
> > + * Queue.
> > + */
> > +static void iecm_get_channels(struct net_device *netdev,
> > +			      struct ethtool_channels *ch) {
> > +	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
> > +	unsigned int combined;
> > +	int num_txq, num_rxq;
> > +
> > +	num_txq = vport->adapter->config_data.num_req_tx_qs;
> > +	num_rxq = vport->adapter->config_data.num_req_rx_qs;
> > +
> > +	combined = min(num_txq, num_rxq);
> > +
> > +	/* Report maximum channels */
> > +	ch->max_combined = vport->adapter->max_queue_limit;
> > +
> > +	/* For now we've only enabled combined queues but will be
> enabling
> > +	 * asymmetric queues after splitq model is fleshed out more.
> > +	 */
> > +	ch->max_rx = 0;
> > +	ch->max_tx = 0;
> > +
> > +	ch->max_other = IECM_MAX_NONQ;
> > +	ch->other_count = IECM_MAX_NONQ;
> > +
> > +	ch->combined_count = combined;
> > +	ch->rx_count = num_rxq - combined;
> > +	ch->tx_count = num_txq - combined;
> > +}
> > +
> > +/**
> > + * iecm_set_channels: set the new channel count
> > + * @netdev: network interface device structure
> > + * @ch: channel information structure
> > + *
> > + * Negotiate a new number of channels with CP. Returns 0 on success,
> > +negative
> > + * on failure.
> > + */
> > +static int iecm_set_channels(struct net_device *netdev,
> > +			     struct ethtool_channels *ch)
> > +{
> > +	unsigned int num_req_tx_q = ch->combined_count + ch->tx_count;
> > +	unsigned int num_req_rx_q = ch->combined_count + ch->rx_count;
> > +	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	int num_txq, num_rxq, err;
> > +
> > +	if (num_req_tx_q == 0 || num_req_rx_q == 0)
> > +		return -EINVAL;
> > +
> > +	num_txq = vport->adapter->config_data.num_req_tx_qs;
> > +	num_rxq = vport->adapter->config_data.num_req_rx_qs;
> > +
> > +	if (num_req_tx_q == num_txq && num_req_rx_q == num_rxq)
> > +		return 0;
> > +
> > +	vport->adapter->config_data.num_req_tx_qs = num_req_tx_q;
> > +	vport->adapter->config_data.num_req_rx_qs = num_req_rx_q;
> > +
> > +	if (adapter->virt_ver_maj < VIRTCHNL_VERSION_MAJOR_2) {
> > +		err = adapter->dev_ops.vc_ops.add_queues(vport,
> > +							 num_req_tx_q, 0,
> > +							 num_req_rx_q, 0);
> > +	} else {
> > +		err = iecm_initiate_soft_reset(vport,
> __IECM_SR_Q_CHANGE);
> > +	}
> 
> One-liners, no need for braces.
> 

The `if` exists across multiple lines so we would prefer to keep braces. As such the 'else' also gets them.

> > +
> > +	if (err) {
> > +		/* roll back queue change */
> > +		vport->adapter->config_data.num_req_tx_qs = num_txq;
> > +		vport->adapter->config_data.num_req_rx_qs = num_rxq;
> > +	}
> > +
> > +	return err;
> > +}
> > +
> > +/**
> > + * iecm_get_ringparam - Get ring parameters
> > + * @netdev: network interface device structure
> > + * @ring: ethtool ringparam structure
> > + * @kring: unused
> > + * @ext_ack: unused
> > + *
> > + * Returns current ring parameters. TX and RX rings are reported
> > +separately,
> > + * but the number of rings is not reported.
> > + */
> > +static void iecm_get_ringparam(struct net_device *netdev,
> > +			       struct ethtool_ringparam *ring,
> > +			       __always_unused struct
> kernel_ethtool_ringparam *kring,
> > +			       __always_unused struct netlink_ext_ack
> *ext_ack)
> 
> Same here for __always_unused. +lines longer than 79.
> 
> > +{
> > +	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
> > +
> > +	ring->rx_max_pending = IECM_MAX_RXQ_DESC;
> > +	ring->tx_max_pending = IECM_MAX_TXQ_DESC;
> > +	ring->rx_pending = vport->rxq_desc_count;
> > +	ring->tx_pending = vport->txq_desc_count; }
> > +
> > +/**
> > + * iecm_set_ringparam - Set ring parameters
> > + * @netdev: network interface device structure
> > + * @ring: ethtool ringparam structure
> > + * @kring: unused
> > + * @ext_ack: unused
> > + *
> > + * Sets ring parameters. TX and RX rings are controlled separately,
> > +but the
> > + * number of rings is not specified, so all rings get the same settings.
> > + */
> > +static int iecm_set_ringparam(struct net_device *netdev,
> > +			      struct ethtool_ringparam *ring,
> > +			      __always_unused struct
> kernel_ethtool_ringparam *kring,
> > +			      __always_unused struct netlink_ext_ack
> *ext_ack)
> 
> Same.
> 
> > +{
> > +	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
> > +	u32 new_rx_count, new_tx_count;
> > +	int i;
> > +
> > +	if (iecm_is_queue_model_split(vport->rxq_model))
> > +		/* When in splitq mode, any one RXQ needs to contain
> enough
> > +		 * descriptors to service the 2 buffer queues associated with
> > +		 * it. Since each buffer queue must be a multiple of 32, the
> > +		 * RXQ then needs to be a multiple of 64 to be divided into
> > +		 * multiples of 32 for each buffer queue
> > +		 */
> > +		new_rx_count = ALIGN(ring->rx_pending,
> > +
> IECM_REQ_SPLITQ_RXQ_DESC_MULTIPLE);
> > +	else
> > +		new_rx_count = ALIGN(ring->rx_pending,
> IECM_REQ_DESC_MULTIPLE);
> > +
> > +	new_tx_count = ALIGN(ring->tx_pending,
> IECM_REQ_DESC_MULTIPLE);
> > +
> > +	/* if nothing to do return success */
> > +	if (new_tx_count == vport->txq_desc_count &&
> > +	    new_rx_count == vport->rxq_desc_count)
> > +		return 0;
> > +
> > +	vport->adapter->config_data.num_req_txq_desc = new_tx_count;
> > +	vport->adapter->config_data.num_req_rxq_desc = new_rx_count;
> > +
> > +	/* Since we adjusted the RX completion queue count, the RX buffer
> queue
> > +	 * descriptor count needs to be adjusted as well
> > +	 */
> > +	for (i = 0; i < vport->num_bufqs_per_qgrp; i++)
> > +		vport->bufq_desc_count[i] =
> > +			IECM_RX_BUFQ_DESC_COUNT(new_rx_count,
> > +						vport-
> >num_bufqs_per_qgrp);
> > +
> > +	return iecm_initiate_soft_reset(vport,
> __IECM_SR_Q_DESC_CHANGE); }
> > +
> > +/**
> > + * struct iecm_stats - definition for an ethtool statistic
> > + * @stat_string: statistic name to display in ethtool -S output
> > + * @sizeof_stat: the sizeof() the stat, must be no greater than
> > +sizeof(u64)
> > + * @stat_offset: offsetof() the stat from a base pointer
> > + *
> > + * This structure defines a statistic to be added to the ethtool stats
> buffer.
> > + * It defines a statistic as offset from a common base pointer. Stats
> > +should
> > + * be defined in constant arrays using the IECM_STAT macro, with
> > +every element
> > + * of the array using the same _type for calculating the sizeof_stat
> > +and
> > + * stat_offset.
> > + *
> > + * The @sizeof_stat is expected to be sizeof(u8), sizeof(u16),
> > +sizeof(u32) or
> > + * sizeof(u64). Other sizes are not expected and will produce a
> > +WARN_ONCE from
> > + * the iecm_add_ethtool_stat() helper function.
> > + *
> > + * The @stat_string is interpreted as a format string, allowing
> > +formatted
> > + * values to be inserted while looping over multiple structures for a
> > +given
> > + * statistics array. Thus, every statistic string in an array should
> > +have the
> > + * same type and number of format specifiers, to be formatted by
> > +variadic
> > + * arguments to the iecm_add_stat_string() helper function.
> > + */
> > +struct iecm_stats {
> > +	char stat_string[ETH_GSTRING_LEN];
> > +	int sizeof_stat;
> > +	int stat_offset;
> > +};
> > +
> > +/* Helper macro to define an iecm_stat structure with proper size and
> type.
> > + * Use this when defining constant statistics arrays. Note that
> > + at _type expects
> > + * only a type name and is used multiple times.
> > + */
> > +#define IECM_STAT(_type, _name, _stat) { \
> > +	.stat_string = _name, \
> > +	.sizeof_stat = sizeof_field(_type, _stat), \
> > +	.stat_offset = offsetof(_type, _stat) \ }
> > +
> > +/* Helper macro for defining some statistics related to queues */
> > +#define IECM_QUEUE_STAT(_name, _stat) \
> > +	IECM_STAT(struct iecm_queue, _name, _stat)
> > +
> > +/* Stats associated with a Tx queue */ static const struct iecm_stats
> > +iecm_gstrings_tx_queue_stats[] = {
> > +	IECM_QUEUE_STAT("packets", q_stats.tx.packets),
> > +	IECM_QUEUE_STAT("bytes", q_stats.tx.bytes),
> > +	IECM_QUEUE_STAT("lso_pkts", q_stats.tx.lso_pkts), };
> > +
> > +/* Stats associated with an Rx queue */ static const struct
> > +iecm_stats iecm_gstrings_rx_queue_stats[] = {
> > +	IECM_QUEUE_STAT("packets", q_stats.rx.packets),
> > +	IECM_QUEUE_STAT("bytes", q_stats.rx.bytes),
> > +	IECM_QUEUE_STAT("rsc_pkts", q_stats.rx.rsc_pkts), };
> > +
> > +#define IECM_TX_QUEUE_STATS_LEN
> 	ARRAY_SIZE(iecm_gstrings_tx_queue_stats)
> > +#define IECM_RX_QUEUE_STATS_LEN
> 	ARRAY_SIZE(iecm_gstrings_rx_queue_stats)
> > +
> > +#define IECM_PORT_STAT(_name, _stat) \
> > +	IECM_STAT(struct iecm_vport,  _name, _stat)
> > +
> > +static const struct iecm_stats iecm_gstrings_port_stats[] = {
> > +	IECM_PORT_STAT("port-rx-csum_errors",
> port_stats.rx_hw_csum_err),
> > +	IECM_PORT_STAT("port-rx-hsplit", port_stats.rx_hsplit),
> > +	IECM_PORT_STAT("port-rx-hsplit_hbo", port_stats.rx_hsplit_hbo),
> > +	IECM_PORT_STAT("tx-linearized_pkts", port_stats.tx_linearize),
> > +	IECM_PORT_STAT("rx-bad_descs", port_stats.rx_bad_descs),
> > +	IECM_PORT_STAT("port-rx_bytes", port_stats.eth_stats.rx_bytes),
> > +	IECM_PORT_STAT("port-rx-unicast_pkts",
> port_stats.eth_stats.rx_unicast),
> > +	IECM_PORT_STAT("port-rx-multicast_pkts",
> port_stats.eth_stats.rx_multicast),
> > +	IECM_PORT_STAT("port-rx-broadcast_pkts",
> port_stats.eth_stats.rx_broadcast),
> > +	IECM_PORT_STAT("port-rx-unknown_protocol",
> > +port_stats.eth_stats.rx_unknown_protocol),
> 
> 94-cols line.
> 
> > +	IECM_PORT_STAT("port-tx_bytes", port_stats.eth_stats.tx_bytes),
> > +	IECM_PORT_STAT("port-tx-unicast_pkts",
> port_stats.eth_stats.tx_unicast),
> > +	IECM_PORT_STAT("port-tx-multicast_pkts",
> port_stats.eth_stats.tx_multicast),
> > +	IECM_PORT_STAT("port-tx-broadcast_pkts",
> port_stats.eth_stats.tx_broadcast),
> > +	IECM_PORT_STAT("port-tx_errors", port_stats.eth_stats.tx_errors),
> };
> > +
> > +#define IECM_PORT_STATS_LEN ARRAY_SIZE(iecm_gstrings_port_stats)
> > +
> > +struct iecm_priv_flags {
> > +	char flag_string[ETH_GSTRING_LEN];
> > +	bool read_only;
> > +	u32 bitno;
> > +};
> > +
> > +#define IECM_PRIV_FLAG(_name, _bitno, _read_only) { \
> > +	.read_only = _read_only, \
> > +	.flag_string = _name, \
> > +	.bitno = _bitno, \
> > +}
> > +
> > +static const struct iecm_priv_flags iecm_gstrings_priv_flags[] = {
> > +	IECM_PRIV_FLAG("header-split", __IECM_PRIV_FLAGS_HDR_SPLIT,
> 0), };
> > +
> > +#define IECM_PRIV_FLAGS_STR_LEN
> ARRAY_SIZE(iecm_gstrings_priv_flags)
> > +
> > +/**
> > + * __iecm_add_qstat_strings - copy stat strings into ethtool buffer
> > + * @p: ethtool supplied buffer
> > + * @stats: stat definitions array
> > + * @size: size of the stats array
> > + * @type: stat type
> > + * @idx: stat index
> > + *
> > + * Format and copy the strings described by stats into the buffer
> > +pointed at
> > + * by p.
> > + */
> > +static void __iecm_add_qstat_strings(u8 **p, const struct iecm_stats
> stats[],
> > +				     const unsigned int size, const char *type,
> > +				     unsigned int idx)
> > +{
> > +	unsigned int i;
> > +
> > +	for (i = 0; i < size; i++) {
> > +		snprintf((char *)*p, ETH_GSTRING_LEN,
> > +			 "%2s-%u.%.17s", type, idx, stats[i].stat_string);
> > +		*p += ETH_GSTRING_LEN;
> > +	}
> > +}
> > +
> > +/**
> > + * iecm_add_qstat_strings - Copy queue stat strings into ethtool
> > +buffer
> > + * @p: ethtool supplied buffer
> > + * @stats: stat definitions array
> > + * @type: stat type
> > + * @idx: stat idx
> > + *
> > + * Format and copy the strings described by the const static stats
> > +value into
> > + * the buffer pointed at by p.
> > + *
> > + * The parameter @stats is evaluated twice, so parameters with side
> > +effects
> > + * should be avoided. Additionally, stats must be an array such that
> > + * ARRAY_SIZE can be called on it.
> > + */
> > +#define iecm_add_qstat_strings(p, stats, type, idx) \
> > +	__iecm_add_qstat_strings(p, stats, ARRAY_SIZE(stats), type, idx)
> > +
> > +/**
> > + * iecm_add_port_stat_strings - Copy port stat strings into ethtool
> > +buffer
> > + * @p: ethtool buffer
> > + * @stats: struct to copy from
> > + */
> > +static void iecm_add_port_stat_strings(u8 **p, const struct
> > +iecm_stats stats[]) {
> > +	const unsigned int size = IECM_PORT_STATS_LEN;
> > +	unsigned int i;
> > +
> > +	for (i = 0; i < size; i++) {
> > +		snprintf((char *)*p, ETH_GSTRING_LEN, "%.32s",
> > +			 stats[i].stat_string);
> > +		*p += ETH_GSTRING_LEN;
> > +	}
> > +}
> > +
> > +/**
> > + * iecm_get_stat_strings - Get stat strings
> > + * @netdev: network interface device structure
> > + * @data: buffer for string data
> > + *
> > + * Builds the statistics string table  */ static void
> > +iecm_get_stat_strings(struct net_device *netdev, u8 *data) {
> > +	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
> > +	unsigned int i;
> > +	u16 max_q;
> > +
> > +	iecm_add_port_stat_strings(&data, iecm_gstrings_port_stats);
> > +
> > +	/* It's critical that we always report a constant number of strings
> and
> > +	 * that the strings are reported in the same order regardless of how
> > +	 * many queues are actually in use.
> > +	 */
> > +	max_q = vport->adapter->max_queue_limit;
> > +
> > +	for (i = 0; i < max_q; i++)
> > +		iecm_add_qstat_strings(&data,
> iecm_gstrings_tx_queue_stats,
> > +				       "tx", i);
> > +	for (i = 0; i < max_q; i++)
> > +		iecm_add_qstat_strings(&data,
> iecm_gstrings_rx_queue_stats,
> > +				       "rx", i);
> > +}
> > +
> > +/**
> > + * iecm_get_priv_flag_strings - Get private flag strings
> > + * @netdev: network interface device structure
> > + * @data: buffer for string data
> > + *
> > + * Builds the private flags string table  */ static void
> > +iecm_get_priv_flag_strings(struct net_device *netdev, u8 *data) {
> > +	unsigned int i;
> > +
> > +	for (i = 0; i < IECM_PRIV_FLAGS_STR_LEN; i++) {
> > +		snprintf((char *)data, ETH_GSTRING_LEN, "%s",
> > +			 iecm_gstrings_priv_flags[i].flag_string);
> > +		data += ETH_GSTRING_LEN;
> > +	}
> > +}
> > +
> > +/**
> > + * iecm_get_priv_flags - report device private flags
> > + * @dev: network interface device structure
> > + *
> > + * The get string set count and the string set should be matched for
> > +each
> > + * flag returned.  Add new strings for each flag to the
> > +iecm_gstrings_priv_flags
> > + * array.
> > + *
> > + * Returns a u32 bitmap of flags.
> > + **/
> > +static u32 iecm_get_priv_flags(struct net_device *dev) {
> > +	struct iecm_netdev_priv *np = netdev_priv(dev);
> > +	struct iecm_user_config_data *user_data;
> > +	struct iecm_vport *vport = np->vport;
> > +	u32 i, ret_flags = 0;
> > +
> > +	user_data = &vport->adapter->config_data;
> > +
> > +	for (i = 0; i < IECM_PRIV_FLAGS_STR_LEN; i++) {
> > +		const struct iecm_priv_flags *priv_flag;
> > +
> > +		priv_flag = &iecm_gstrings_priv_flags[i];
> > +
> > +		if (test_bit(priv_flag->bitno, user_data->user_flags))
> > +			ret_flags |= BIT(i);
> > +	}
> > +
> > +	return ret_flags;
> > +}
> > +
> > +/**
> > + * iecm_set_priv_flags - set private flags
> > + * @dev: network interface device structure
> > + * @flags: bit flags to be set
> > + **/
> > +static int iecm_set_priv_flags(struct net_device *dev, u32 flags) {
> > +	struct iecm_netdev_priv *np = netdev_priv(dev);
> > +	DECLARE_BITMAP(change_flags, __IECM_USER_FLAGS_NBITS);
> > +	DECLARE_BITMAP(orig_flags, __IECM_USER_FLAGS_NBITS);
> 
> RCT.
> 

Will fix.

> > +	struct iecm_user_config_data *user_data;
> > +	struct iecm_vport *vport = np->vport;
> > +	bool is_reset_needed;
> > +	int err = 0;
> > +	u32 i;
> > +
> > +	if (flags > BIT(IECM_PRIV_FLAGS_STR_LEN))
> > +		return -EINVAL;
> > +
> > +	user_data = &vport->adapter->config_data;
> > +
> > +	bitmap_copy(orig_flags, user_data->user_flags,
> > +__IECM_USER_FLAGS_NBITS);
> > +
> > +	for (i = 0; i < IECM_PRIV_FLAGS_STR_LEN; i++) {
> > +		const struct iecm_priv_flags *priv_flag;
> > +
> > +		priv_flag = &iecm_gstrings_priv_flags[i];
> > +
> > +		if (flags & BIT(i)) {
> > +			/* If this is a read-only flag, it can't be changed */
> > +			if (priv_flag->read_only)
> > +				return -EOPNOTSUPP;
> > +
> > +			set_bit(priv_flag->bitno, user_data->user_flags);
> > +		} else {
> > +			clear_bit(priv_flag->bitno, user_data->user_flags);
> > +		}
> > +	}
> > +
> > +	bitmap_xor(change_flags, user_data->user_flags,
> > +		   orig_flags, __IECM_USER_FLAGS_NBITS);
> > +
> > +	is_reset_needed =
> > +		!!(test_bit(__IECM_PRIV_FLAGS_HDR_SPLIT,
> change_flags));
> 
> Shorter name would allow avoiding line break.
> 

For IECM_PRIV_FLAGS_HDR_SPLIT? Sure we're open to suggestion if you have a name that communicates the same level of information effectively.

> > +
> > +	/* Issue reset to cause things to take effect, as additional bits
> > +	 * are added we will need to create a mask of bits requiring reset
> > +	 */
> > +	if (is_reset_needed)
> > +		err = iecm_initiate_soft_reset(vport,
> __IECM_SR_HSPLIT_CHANGE);
> > +
> > +	return err;
> > +}
> > +
> > +/**
> > + * iecm_get_strings - Get string set
> > + * @netdev: network interface device structure
> > + * @sset: id of string set
> > + * @data: buffer for string data
> > + *
> > + * Builds string tables for various string sets  */ static void
> > +iecm_get_strings(struct net_device *netdev, u32 sset, u8 *data) {
> > +	switch (sset) {
> > +	case ETH_SS_STATS:
> > +		iecm_get_stat_strings(netdev, data);
> > +		break;
> > +	case ETH_SS_PRIV_FLAGS:
> > +		iecm_get_priv_flag_strings(netdev, data);
> > +		break;
> > +	default:
> > +		break;
> 
> Equivalent to not having a 'default' case.
> 

Yes static tools will complain about uncaught switch without it.

> > +	}
> > +}
> > +
> > +/**
> > + * iecm_get_sset_count - Get length of string set
> > + * @netdev: network interface device structure
> > + * @sset: id of string set
> > + *
> > + * Reports size of various string tables.
> > + */
> > +static
> > +int iecm_get_sset_count(struct net_device *netdev, int sset) {
> > +	if (sset == ETH_SS_STATS) {
> > +		struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
> > +		u16 max_q;
> > +
> > +		/* This size reported back here *must* be constant
> throughout
> > +		 * the lifecycle of the netdevice, i.e. we must report the
> > +		 * maximum length even for queues that don't technically
> exist.
> > +		 * This is due to the fact that this userspace API uses three
> > +		 * separate ioctl calls to get stats data but has no way to
> > +		 * communicate back to userspace when that size has
> changed,
> > +		 * which can typically happen as a result of changing
> number of
> > +		 * queues. If the number/order of stats change in the middle
> of
> > +		 * this call chain it will lead to userspace crashing/accessing
> > +		 * bad data through buffer under/overflow.
> > +		 */
> > +		max_q = vport->adapter->max_queue_limit;
> > +
> > +		return IECM_PORT_STATS_LEN +
> > +		       (IECM_TX_QUEUE_STATS_LEN * max_q) +
> > +			(IECM_RX_QUEUE_STATS_LEN * max_q);
> > +	} else if (sset == ETH_SS_PRIV_FLAGS) {
> > +		return IECM_PRIV_FLAGS_STR_LEN;
> > +	} else {
> > +		return -EINVAL;
> 
> Check for this at first and save 1 level of indentation for ETH_SS_STATS.
> 

So you would have it something like:

if (sset != ETH_SS_STATS && sset != ETH_SS_PRIV_FLAGS)
   return -EINVAL

if (sset == ETH_SS_PRIV_FLAGS)
   return IECM_PRIV_FLAGS_STR_LEN

/* if sset code */

This to me seems less readable because now the reader has to identify the bottom half of the function is actually for the SSET case whereas the way it is written currently, it's very explicit about what we're doing for what. I commend the intent to shave off indents where possible but many of these indent suggestions are teetering on excessive and sacrificing readability to save a tab.

> > +	}
> > +}
> > +
> > +/**
> > + * iecm_add_one_ethtool_stat - copy the stat into the supplied buffer
> > + * @data: location to store the stat value
> > + * @pstat: old stat pointer to copy from
> > + * @stat: the stat definition
> > + *
> > + * Copies the stat data defined by the pointer and stat structure
> > +pair into
> > + * the memory supplied as data. Used to implement
> > +iecm_add_ethtool_stats and
> > + * iecm_add_queue_stats. If the pointer is null, data will be zero'd.
> > + */
> > +static void
> > +iecm_add_one_ethtool_stat(u64 *data, void *pstat,
> > +			  const struct iecm_stats *stat)
> > +{
> > +	char *p;
> > +
> > +	if (!pstat) {
> > +		/* Ensure that the ethtool data buffer is zero'd for any stats
> > +		 * which don't have a valid pointer.
> > +		 */
> > +		*data = 0;
> > +		return;
> > +	}
> > +
> > +	p = (char *)pstat + stat->stat_offset;
> > +	switch (stat->sizeof_stat) {
> > +	case sizeof(u64):
> > +		*data = *((u64 *)p);
> > +		break;
> > +	case sizeof(u32):
> > +		*data = *((u32 *)p);
> > +		break;
> > +	case sizeof(u16):
> > +		*data = *((u16 *)p);
> > +		break;
> > +	case sizeof(u8):
> > +		*data = *((u8 *)p);
> > +		break;
> > +	default:
> > +		WARN_ONCE(1, "unexpected stat size for %s",
> > +			  stat->stat_string);
> > +		*data = 0;
> > +	}
> > +}
> > +
> > +/**
> > + * iecm_add_queue_stats - copy queue statistics into supplied buffer
> > + * @data: ethtool stats buffer
> > + * @q: the queue to copy
> > + *
> > + * Queue statistics must be copied while protected by
> > + * u64_stats_fetch_begin_irq, so we can't directly use
> iecm_add_ethtool_stats.
> > + * Assumes that queue stats are defined in iecm_gstrings_queue_stats.
> > +If the
> > + * queue pointer is null, zero out the queue stat values and update
> > +the data
> > + * pointer. Otherwise safely copy the stats from the queue into the
> > +supplied
> > + * buffer and update the data pointer when finished.
> > + *
> > + * This function expects to be called while under rcu_read_lock().
> > + */
> > +static void
> > +iecm_add_queue_stats(u64 **data, struct iecm_queue *q) {
> > +	const struct iecm_stats *stats;
> > +	unsigned int start;
> > +	unsigned int size;
> > +	unsigned int i;
> > +
> > +	if (q->q_type == VIRTCHNL2_QUEUE_TYPE_RX) {
> > +		size = IECM_RX_QUEUE_STATS_LEN;
> > +		stats = iecm_gstrings_rx_queue_stats;
> > +	} else {
> > +		size = IECM_TX_QUEUE_STATS_LEN;
> > +		stats = iecm_gstrings_tx_queue_stats;
> > +	}
> > +
> > +	/* To avoid invalid statistics values, ensure that we keep retrying
> > +	 * the copy until we get a consistent value according to
> > +	 * u64_stats_fetch_retry_irq. But first, make sure our queue is
> > +	 * non-null before attempting to access its syncp.
> > +	 */
> > +	do {
> > +		start = u64_stats_fetch_begin_irq(&q->stats_sync);
> > +		for (i = 0; i < size; i++)
> > +			iecm_add_one_ethtool_stat(&(*data)[i], q,
> &stats[i]);
> > +	} while (u64_stats_fetch_retry_irq(&q->stats_sync, start));
> > +
> > +	/* Once we successfully copy the stats in, update the data pointer
> */
> > +	*data += size;
> > +}
> > +
> > +/**
> > + * iecm_add_empty_queue_stats - Add stats for a non-existent queue
> > + * @data: pointer to data buffer
> > + * @qtype: type of data queue
> > + *
> > + * We must report a constant length of stats back to userspace
> > +regardless of
> > + * how many queues are actually in use because stats collection
> > +happens over
> > + * three separate ioctls and there's no way to notify userspace the
> > +size
> > + * changed between those calls. This adds empty to data to the stats
> > +since we
> > + * don't have a real queue to refer to for this stats slot.
> > + */
> > +static void
> > +iecm_add_empty_queue_stats(u64 **data, u16 qtype) {
> > +	unsigned int i;
> > +	int stats_len;
> > +
> > +	if (qtype == VIRTCHNL2_QUEUE_TYPE_RX)
> > +		stats_len = IECM_RX_QUEUE_STATS_LEN;
> > +	else
> > +		stats_len = IECM_TX_QUEUE_STATS_LEN;
> > +
> > +	for (i = 0; i < stats_len; i++)
> > +		(*data)[i] = 0;
> > +	*data += stats_len;
> > +}
> > +
> > +/**
> > + * iecm_add_port_stats - Copy port stats into ethtool buffer
> > + * @vport: virtual port struct
> > + * @data: ethtool buffer to copy into  */ static void
> > +iecm_add_port_stats(struct iecm_vport *vport, u64 **data) {
> > +	unsigned int size = IECM_PORT_STATS_LEN;
> > +	unsigned int start;
> > +	unsigned int i;
> > +
> > +	/* To avoid invalid statistics values, ensure that we keep retrying
> > +	 * the copy until we get a consistent value according to
> > +	 * u64_stats_fetch_retry_irq.
> > +	 */
> > +	do {
> > +		start = u64_stats_fetch_begin_irq(&vport-
> >port_stats.stats_sync);
> > +		for (i = 0; i < size; i++) {
> > +			iecm_add_one_ethtool_stat(&(*data)[i], vport,
> > +
> &iecm_gstrings_port_stats[i]);
> > +		}
> 
> Redundant braces.
> 

I believe this is multi line with wrapping.  Will not fix.

> > +	} while (u64_stats_fetch_retry_irq(&vport->port_stats.stats_sync,
> > +start));
> > +
> > +	*data += size;
> > +}
> > +
> > +/**
> > + * iecm_get_ethtool_stats - report device statistics
> > + * @netdev: network interface device structure
> > + * @stats: ethtool statistics structure
> > + * @data: pointer to data buffer
> > + *
> > + * All statistics are added to the data buffer as an array of u64.
> > + */
> > +static void iecm_get_ethtool_stats(struct net_device *netdev,
> > +				   struct ethtool_stats __always_unused
> *stats,
> 
> Redundant __always_unused.
> 
> > +				   u64 *data)
> > +{
> > +	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
> > +	unsigned int total = 0;
> > +	unsigned int i, j;
> > +	u16 max_q, qtype;
> > +
> > +	if (vport->adapter->state != __IECM_UP)
> > +		return;
> > +
> > +	set_bit(__IECM_MB_STATS_PENDING, vport->adapter->flags);
> > +
> > +	max_q = vport->adapter->max_queue_limit;
> > +
> > +	rcu_read_lock();
> > +
> > +	iecm_add_port_stats(vport, &data);
> > +
> > +	for (i = 0; i < vport->num_txq_grp; i++) {
> > +		struct iecm_txq_group *txq_grp = &vport->txq_grps[i];
> > +
> > +		qtype = VIRTCHNL2_QUEUE_TYPE_TX;
> > +
> > +		for (j = 0; j < txq_grp->num_txq; j++, total++) {
> > +			struct iecm_queue *txq = txq_grp->txqs[j];
> > +
> > +			if (!txq)
> > +				iecm_add_empty_queue_stats(&data,
> qtype);
> > +			else
> > +				iecm_add_queue_stats(&data, txq);
> > +		}
> > +	}
> > +	/* It is critical we provide a constant number of stats back to
> > +	 * userspace regardless of how many queues are actually in use
> because
> > +	 * there is no way to inform userspace the size has changed
> between
> > +	 * ioctl calls. This will fill in any missing stats with zero.
> > +	 */
> > +	for (; total < max_q; total++)
> > +		iecm_add_empty_queue_stats(&data,
> VIRTCHNL2_QUEUE_TYPE_TX);
> > +	total = 0;
> > +
> > +	for (i = 0; i < vport->num_rxq_grp; i++) {
> > +		struct iecm_rxq_group *rxq_grp = &vport->rxq_grps[i];
> > +		int num_rxq;
> > +
> > +		qtype = VIRTCHNL2_QUEUE_TYPE_RX;
> > +
> > +		if (iecm_is_queue_model_split(vport->rxq_model))
> > +			num_rxq = rxq_grp->splitq.num_rxq_sets;
> > +		else
> > +			num_rxq = rxq_grp->singleq.num_rxq;
> > +
> > +		for (j = 0; j < num_rxq; j++, total++) {
> > +			struct iecm_queue *rxq;
> > +
> > +			if (iecm_is_queue_model_split(vport->rxq_model))
> > +				rxq = &rxq_grp->splitq.rxq_sets[j]->rxq;
> > +			else
> > +				rxq = rxq_grp->singleq.rxqs[j];
> > +			if (!rxq)
> > +				iecm_add_empty_queue_stats(&data,
> qtype);
> > +			else
> > +				iecm_add_queue_stats(&data, rxq);
> > +		}
> > +	}
> > +	for (; total < max_q; total++)
> > +		iecm_add_empty_queue_stats(&data,
> VIRTCHNL2_QUEUE_TYPE_RX);
> > +	rcu_read_unlock();
> > +}
> > +
> > +/**
> > + * iecm_find_rxq - find rxq from q index
> > + * @vport: virtual port associated to queue
> > + * @q_num: q index used to find queue
> > + *
> > + * returns pointer to rx queue
> > + */
> > +static struct iecm_queue *
> > +iecm_find_rxq(struct iecm_vport *vport, int q_num) {
> > +	struct iecm_queue *rxq;
> > +
> > +	if (iecm_is_queue_model_split(vport->rxq_model)) {
> > +		int q_grp, q_idx;
> > +
> > +		q_grp = q_num / IECM_DFLT_SPLITQ_RXQ_PER_GROUP;
> > +		q_idx = q_num % IECM_DFLT_SPLITQ_RXQ_PER_GROUP;
> > +
> > +		rxq = &vport->rxq_grps[q_grp].splitq.rxq_sets[q_idx]->rxq;
> > +	} else {
> > +		rxq = vport->rxq_grps->singleq.rxqs[q_num];
> > +	}
> > +
> > +	return rxq;
> > +}
> > +
> > +/**
> > + * iecm_find_txq - find txq from q index
> > + * @vport: virtual port associated to queue
> > + * @q_num: q index used to find queue
> > + *
> > + * returns pointer to tx queue
> > + */
> > +static struct iecm_queue *
> > +iecm_find_txq(struct iecm_vport *vport, int q_num) {
> > +	struct iecm_queue *txq;
> > +
> > +	if (iecm_is_queue_model_split(vport->txq_model)) {
> > +		int q_grp = q_num / IECM_DFLT_SPLITQ_TXQ_PER_GROUP;
> > +
> > +		txq = vport->txq_grps[q_grp].complq;
> > +	} else {
> > +		txq = vport->txqs[q_num];
> > +	}
> > +
> > +	return txq;
> > +}
> > +
> > +/**
> > + * __iecm_get_q_coalesce - get ITR values for specific queue
> > + * @ec: ethtool structure to fill with driver's coalesce settings
> > + * @q: quuee of Rx or Tx
> > + */
> > +static void
> > +__iecm_get_q_coalesce(struct ethtool_coalesce *ec, struct iecm_queue
> > +*q) {
> > +	if (q->q_type == VIRTCHNL2_QUEUE_TYPE_RX) {
> > +		ec->use_adaptive_rx_coalesce =
> > +				IECM_ITR_IS_DYNAMIC(q->q_vector-
> >rx_intr_mode);
> > +		ec->rx_coalesce_usecs = q->q_vector->rx_itr_value;
> > +	} else {
> > +		ec->use_adaptive_tx_coalesce =
> > +				IECM_ITR_IS_DYNAMIC(q->q_vector-
> >tx_intr_mode);
> > +		ec->tx_coalesce_usecs = q->q_vector->tx_itr_value;
> > +	}
> > +}
> > +
> > +/**
> > + * iecm_get_q_coalesce - get ITR values for specific queue
> > + * @netdev: pointer to the netdev associated with this query
> > + * @ec: coalesce settings to program the device with
> > + * @q_num: update ITR/INTRL (coalesce) settings for this queue
> > +number/index
> > + *
> > + * Return 0 on success, and negative on failure  */ static int
> > +iecm_get_q_coalesce(struct net_device *netdev, struct ethtool_coalesce
> *ec,
> > +		    u32 q_num)
> > +{
> > +	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
> > +
> > +	if (vport->adapter->state != __IECM_UP)
> > +		return 0;
> > +
> > +	if (q_num >= vport->num_rxq && q_num >= vport->num_txq)
> > +		return -EINVAL;
> > +
> > +	if (q_num < vport->num_rxq) {
> > +		struct iecm_queue *rxq = iecm_find_rxq(vport, q_num);
> > +
> > +		__iecm_get_q_coalesce(ec, rxq);
> > +	}
> > +
> > +	if (q_num < vport->num_txq) {
> > +		struct iecm_queue *txq = iecm_find_txq(vport, q_num);
> > +
> > +		__iecm_get_q_coalesce(ec, txq);
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +/**
> > + * iecm_get_coalesce - get ITR values as requested by user
> > + * @netdev: pointer to the netdev associated with this query
> > + * @ec: coalesce settings to be filled
> > + * @kec: unused
> > + * @extack: unused
> > + *
> > + * Return 0 on success, and negative on failure  */ static int
> > +iecm_get_coalesce(struct net_device *netdev, struct ethtool_coalesce
> *ec,
> > +		  struct kernel_ethtool_coalesce __always_unused *kec,
> > +		  struct netlink_ext_ack __always_unused *extack)
> 
> __always_unused.
> 
> > +{
> > +	/* Return coalesce based on queue number zero */
> > +	return iecm_get_q_coalesce(netdev, ec, 0); }
> > +
> > +/**
> > + * iecm_get_per_q_coalesce - get ITR values as requested by user
> > + * @netdev: pointer to the netdev associated with this query
> > + * @q_num: queue for which the itr values has to retrieved
> > + * @ec: coalesce settings to be filled
> > + *
> > + * Return 0 on success, and negative on failure  */
> > +
> > +static int
> > +iecm_get_per_q_coalesce(struct net_device *netdev, u32 q_num,
> > +			struct ethtool_coalesce *ec)
> > +{
> > +	return iecm_get_q_coalesce(netdev, ec, q_num); }
> > +
> > +/**
> > + * __iecm_set_q_coalesce - set ITR values for specific queue
> > + * @ec: ethtool structure from user to update ITR settings
> > + * @q: queue for which itr values has to be set
> > + *
> > + * Returns 0 on success, negative otherwise.
> > + */
> > +static int
> > +__iecm_set_q_coalesce(struct ethtool_coalesce *ec, struct iecm_queue
> > +*q) {
> > +	u32 use_adaptive_coalesce, coalesce_usecs;
> > +	struct iecm_q_vector *qv = q->q_vector;
> > +	struct iecm_vport *vport;
> > +	bool is_dim_ena = false;
> > +
> > +	vport = q->vport;
> > +	if (q->q_type == VIRTCHNL2_QUEUE_TYPE_RX) {
> > +		is_dim_ena = IECM_ITR_IS_DYNAMIC(qv->rx_intr_mode);
> > +		use_adaptive_coalesce = ec->use_adaptive_rx_coalesce;
> > +		coalesce_usecs = ec->rx_coalesce_usecs;
> > +	} else {
> > +		is_dim_ena = IECM_ITR_IS_DYNAMIC(qv->tx_intr_mode);
> > +		use_adaptive_coalesce = ec->use_adaptive_tx_coalesce;
> > +		coalesce_usecs = ec->tx_coalesce_usecs;
> > +	}
> > +
> > +	if (coalesce_usecs && use_adaptive_coalesce) {
> > +		netdev_info(vport->netdev, "Cannot set coalesce usecs if
> adaptive enabled\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	if (is_dim_ena && use_adaptive_coalesce)
> > +		return 0;
> > +
> > +	if (coalesce_usecs > IECM_ITR_MAX) {
> > +		netdev_info(vport->netdev,
> > +			    "Invalid value, %d-usecs range is 0-%d\n",
> > +			    coalesce_usecs, IECM_ITR_MAX);
> > +		return -EINVAL;
> > +	}
> > +
> > +	if (coalesce_usecs % 2 != 0) {
> > +		coalesce_usecs = coalesce_usecs & 0xFFFFFFFE;
> > +		netdev_info(vport->netdev, "HW only supports even ITR
> values, ITR rounded to %d\n",
> > +			    coalesce_usecs);
> > +	}
> > +
> > +	if (q->q_type == VIRTCHNL2_QUEUE_TYPE_RX) {
> > +		qv->rx_itr_value = coalesce_usecs;
> > +		if (use_adaptive_coalesce) {
> > +			qv->rx_intr_mode = IECM_ITR_DYNAMIC;
> > +		} else {
> > +			qv->rx_intr_mode = !IECM_ITR_DYNAMIC;
> > +			iecm_vport_intr_write_itr(qv, qv->rx_itr_value,
> > +						  false);
> > +		}
> > +	} else {
> > +		qv->tx_itr_value = coalesce_usecs;
> > +		if (use_adaptive_coalesce) {
> > +			qv->tx_intr_mode = IECM_ITR_DYNAMIC;
> > +		} else {
> > +			qv->tx_intr_mode = !IECM_ITR_DYNAMIC;
> > +			iecm_vport_intr_write_itr(qv, qv->tx_itr_value,
> true);
> > +		}
> > +	}
> > +	/* Update of static/dynamic itr will be taken care when interrupt is
> > +	 * fired
> > +	 */
> > +	return 0;
> > +}
> > +
> > +/**
> > + * iecm_set_q_coalesce - set ITR values for specific queue
> > + * @vport: vport associated to the queue that need updating
> > + * @ec: coalesce settings to program the device with
> > + * @q_num: update ITR/INTRL (coalesce) settings for this queue
> > +number/index
> > + * @is_rxq: is queue type rx
> > + *
> > + * Return 0 on success, and negative on failure  */ static int
> > +iecm_set_q_coalesce(struct iecm_vport *vport, struct ethtool_coalesce
> *ec,
> > +		    int q_num, bool is_rxq)
> > +{
> > +	struct iecm_queue *q;
> > +
> > +	if (is_rxq)
> > +		q = iecm_find_rxq(vport, q_num);
> > +	else
> > +		q = iecm_find_txq(vport, q_num);
> > +
> > +	if (q && __iecm_set_q_coalesce(ec, q))
> > +		return -EINVAL;
> > +
> > +	return 0;
> > +}
> > +
> > +/**
> > + * iecm_set_coalesce - set ITR values as requested by user
> > + * @netdev: pointer to the netdev associated with this query
> > + * @ec: coalesce settings to program the device with
> > + * @kec: unused
> > + * @extack: unused
> > + *
> > + * Return 0 on success, and negative on failure  */ static int
> > +iecm_set_coalesce(struct net_device *netdev, struct ethtool_coalesce
> *ec,
> > +		  struct kernel_ethtool_coalesce __always_unused *kec,
> > +		  struct netlink_ext_ack __always_unused *extack) {
> > +	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
> > +	int i, err;
> > +
> > +	if (vport->adapter->state != __IECM_UP)
> > +		return 0;
> > +
> > +	for (i = 0; i < vport->num_txq; i++) {
> > +		err = iecm_set_q_coalesce(vport, ec, i, false);
> > +		if (err)
> > +			return err;
> > +	}
> > +
> > +	for (i = 0; i < vport->num_rxq; i++) {
> > +		err = iecm_set_q_coalesce(vport, ec, i, true);
> > +		if (err)
> > +			return err;
> > +	}
> > +	return 0;
> > +}
> > +
> > +/**
> > + * iecm_set_per_q_coalesce - set ITR values as requested by user
> > + * @netdev: pointer to the netdev associated with this query
> > + * @q_num: queue for which the itr values has to be set
> > + * @ec: coalesce settings to program the device with
> > + *
> > + * Return 0 on success, and negative on failure  */ static int
> > +iecm_set_per_q_coalesce(struct net_device *netdev, u32 q_num,
> > +			struct ethtool_coalesce *ec)
> > +{
> > +	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
> > +	int err;
> > +
> > +	err = iecm_set_q_coalesce(vport, ec, q_num, false);
> > +	if (!err)
> > +		err = iecm_set_q_coalesce(vport, ec, q_num, true);
> > +
> > +	return err;
> 
> 	err = iecm_set_q_coalesce(vport, ec, q_num, false);
> 	if (err)
> 		return err;
> 
> 	return iecm_set_q_coalesce(vport, ec, q_num, true);
> 

Will fix

> > +}
> > +
> > +/**
> > + * iecm_get_msglevel - Get debug message level
> > + * @netdev: network interface device structure
> > + *
> > + * Returns current debug message level.
> > + */
> > +static u32 iecm_get_msglevel(struct net_device *netdev) {
> > +	struct iecm_adapter *adapter = iecm_netdev_to_adapter(netdev);
> > +
> > +	return adapter->msg_enable;
> > +}
> > +
> > +/**
> > + * iecm_set_msglevel - Set debug message level
> > + * @netdev: network interface device structure
> > + * @data: message level
> > + *
> > + * Set current debug message level. Higher values cause the driver to
> > + * be noisier.
> > + */
> > +static void iecm_set_msglevel(struct net_device *netdev, u32 data) {
> > +	struct iecm_adapter *adapter = iecm_netdev_to_adapter(netdev);
> > +
> > +	adapter->msg_enable = data;
> > +}
> > +
> > +/**
> > + * iecm_get_link_ksettings - Get Link Speed and Duplex settings
> > + * @netdev: network interface device structure
> > + * @cmd: ethtool command
> > + *
> > + * Reports speed/duplex settings.
> > + **/
> > +static int iecm_get_link_ksettings(struct net_device *netdev,
> > +				   struct ethtool_link_ksettings *cmd) {
> > +	struct iecm_netdev_priv *np = netdev_priv(netdev);
> > +	struct iecm_adapter *adapter = np->vport->adapter;
> > +
> > +	ethtool_link_ksettings_zero_link_mode(cmd, supported);
> > +	cmd->base.autoneg = AUTONEG_DISABLE;
> > +	cmd->base.port = PORT_NONE;
> > +	cmd->base.duplex = DUPLEX_FULL;
> > +
> > +	if (adapter->link_speed_mbps) {
> > +		cmd->base.speed = adapter->link_speed_mbps;
> > +		return 0;
> > +	}
> > +
> > +	/* Set speed and duplex */
> > +	switch (adapter->link_speed) {
> > +	case VIRTCHNL_LINK_SPEED_40GB:
> > +		cmd->base.speed = SPEED_40000;
> > +		break;
> > +	case VIRTCHNL_LINK_SPEED_25GB:
> > +		cmd->base.speed = SPEED_25000;
> > +		break;
> > +	case VIRTCHNL_LINK_SPEED_20GB:
> > +		cmd->base.speed = SPEED_20000;
> > +		break;
> > +	case VIRTCHNL_LINK_SPEED_10GB:
> > +		cmd->base.speed = SPEED_10000;
> > +		break;
> > +	case VIRTCHNL_LINK_SPEED_1GB:
> > +		cmd->base.speed = SPEED_1000;
> > +		break;
> > +	case VIRTCHNL_LINK_SPEED_100MB:
> > +		cmd->base.speed = SPEED_100;
> > +		break;
> > +	default:
> > +		break;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static const struct ethtool_ops iecm_ethtool_ops = {
> > +	.supported_coalesce_params = ETHTOOL_COALESCE_USECS |
> > +				     ETHTOOL_COALESCE_USE_ADAPTIVE,
> > +	.get_msglevel		= iecm_get_msglevel,
> > +	.set_msglevel		= iecm_set_msglevel,
> > +	.get_coalesce		= iecm_get_coalesce,
> > +	.set_coalesce		= iecm_set_coalesce,
> > +	.get_per_queue_coalesce = iecm_get_per_q_coalesce,
> > +	.set_per_queue_coalesce = iecm_set_per_q_coalesce,
> > +	.get_ethtool_stats	= iecm_get_ethtool_stats,
> > +	.get_strings		= iecm_get_strings,
> > +	.get_sset_count		= iecm_get_sset_count,
> > +	.get_priv_flags		= iecm_get_priv_flags,
> > +	.set_priv_flags		= iecm_set_priv_flags,
> > +	.get_rxnfc		= iecm_get_rxnfc,
> > +	.set_rxnfc		= iecm_set_rxnfc,
> > +	.get_rxfh_key_size	= iecm_get_rxfh_key_size,
> > +	.get_rxfh_indir_size	= iecm_get_rxfh_indir_size,
> > +	.get_rxfh		= iecm_get_rxfh,
> > +	.set_rxfh		= iecm_set_rxfh,
> > +	.get_channels		= iecm_get_channels,
> > +	.set_channels		= iecm_set_channels,
> > +	.get_ringparam		= iecm_get_ringparam,
> > +	.set_ringparam		= iecm_set_ringparam,
> > +	.get_link_ksettings	= iecm_get_link_ksettings,
> > +};
> > +
> > +/**
> > + * iecm_set_ethtool_ops - Initialize ethtool ops struct
> > + * @netdev: network interface device structure
> > + *
> > + * Sets ethtool ops struct in our netdev so that ethtool can call
> > + * our functions.
> > + */
> > +void iecm_set_ethtool_ops(struct net_device *netdev) {
> > +	netdev->ethtool_ops = &iecm_ethtool_ops; }
> 
> Declaring @iecm_ethtool_ops as external and directly assigning it in
> iecm_cfg_netdev() would result in smaller code size than this.
> 

It seems trivial either way, but I'm having a hard time justifying this function, so will fix.

> > diff --git a/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > index cbde65f1c523..c5900723b018 100644
> > --- a/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > +++ b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > @@ -871,6 +871,7 @@ static int iecm_cfg_netdev(struct iecm_vport
> *vport)
> >  	netdev->hw_features |= dflt_features | offloads;
> >  	netdev->hw_enc_features |= dflt_features | offloads;
> >
> > +	iecm_set_ethtool_ops(netdev);
> >  	SET_NETDEV_DEV(netdev, &adapter->pdev->dev);
> >
> >  	/* carrier off on init to avoid Tx hangs */ @@ -1150,7 +1151,15
> @@
> > iecm_vport_alloc(struct iecm_adapter *adapter, int vport_id)
> >   */
> >  static void iecm_statistics_task(struct work_struct *work)  {
> > -	/* stub */
> > +	struct iecm_adapter *adapter = container_of(work,
> > +						    struct iecm_adapter,
> > +						    stats_task.work);
> 
> 	struct iecm_adapter *adapter;
> 
> 	adapter = container_of(work, typeof(*adapter), stats_task.work);
> 
> This fits into 79 without a line wrap.
> 
> > +	if (test_bit(__IECM_MB_STATS_PENDING, adapter->flags) &&
> > +	    !test_bit(__IECM_HR_RESET_IN_PROG, adapter->flags))
> > +		adapter->dev_ops.vc_ops.get_stats_msg(adapter-
> >vports[0]);
> > +
> > +	queue_delayed_work(adapter->stats_wq, &adapter->stats_task,
> > +			   msecs_to_jiffies(1000));
> >  }
> >
> >  /**
> > diff --git a/drivers/net/ethernet/intel/include/iecm.h
> > b/drivers/net/ethernet/intel/include/iecm.h
> > index 97c9935b832d..d118da1ea8cd 100644
> > --- a/drivers/net/ethernet/intel/include/iecm.h
> > +++ b/drivers/net/ethernet/intel/include/iecm.h
> > @@ -745,6 +745,7 @@ int iecm_recv_mb_msg(struct iecm_adapter
> *adapter, enum virtchnl_ops op,
> >  		     void *msg, int msg_size);
> >  int iecm_send_mb_msg(struct iecm_adapter *adapter, enum
> virtchnl_ops op,
> >  		     u16 msg_size, u8 *msg);
> > +void iecm_set_ethtool_ops(struct net_device *netdev);
> >  void iecm_vport_set_hsplit(struct iecm_vport *vport, bool ena);  void
> > iecm_add_del_ether_addrs(struct iecm_vport *vport, bool add, bool
> > async);  int iecm_set_promiscuous(struct iecm_adapter *adapter);
> > --
> > 2.33.0
> 
> Thanks,
> Al

^ permalink raw reply	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 16/19] iecm: implement flow director
  2022-01-28 19:04   ` Alexander Lobakin
@ 2022-02-03  2:41     ` Brady, Alan
  2022-02-04 10:08       ` Alexander Lobakin
  0 siblings, 1 reply; 89+ messages in thread
From: Brady, Alan @ 2022-02-03  2:41 UTC (permalink / raw)
  To: intel-wired-lan

> -----Original Message-----
> From: Lobakin, Alexandr <alexandr.lobakin@intel.com>
> Sent: Friday, January 28, 2022 11:04 AM
> To: Brady, Alan <alan.brady@intel.com>
> Cc: Lobakin, Alexandr <alexandr.lobakin@intel.com>; Wang, Haiyue
> <haiyue.wang@intel.com>; Burra, Phani R <phani.r.burra@intel.com>;
> Chittim, Madhu <madhu.chittim@intel.com>; Linga, Pavan Kumar
> <pavan.kumar.linga@intel.com>; intel-wired-lan at lists.osuosl.org
> Subject: Re: [Intel-wired-lan] [PATCH net-next 16/19] iecm: implement flow
> director
> 
> From: Alan Brady <alan.brady@intel.com>
> Date: Thu, 27 Jan 2022 16:10:06 -0800
> 
> > From: Haiyue Wang <haiyue.wang@intel.com>
> >
> > This adds everthing needed to do flow director commands.
> >
> > Signed-off-by: Haiyue Wang <haiyue.wang@intel.com>
> > ---
> >  .../net/ethernet/intel/iecm/iecm_ethtool.c    |   17 +-
> >  drivers/net/ethernet/intel/iecm/iecm_lib.c    | 1528
> ++++++++++++++++-
> >  .../net/ethernet/intel/iecm/iecm_virtchnl.c   |  119 ++
> >  drivers/net/ethernet/intel/include/iecm.h     |  112 ++
> >  4 files changed, 1770 insertions(+), 6 deletions(-)
> >
> > diff --git a/drivers/net/ethernet/intel/iecm/iecm_ethtool.c
> b/drivers/net/ethernet/intel/iecm/iecm_ethtool.c
> > index 32d905fb1bb6..7e252b25e02d 100644
> > --- a/drivers/net/ethernet/intel/iecm/iecm_ethtool.c
> > +++ b/drivers/net/ethernet/intel/iecm/iecm_ethtool.c
> > @@ -15,6 +15,7 @@ static int iecm_get_rxnfc(struct net_device *netdev,
> struct ethtool_rxnfc *cmd,
> >  			  u32 __always_unused *rule_locs)
> >  {
> >  	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
> > +	struct iecm_adapter *adapter = vport->adapter;
> >  	int ret = -EOPNOTSUPP;
> >
> >  	switch (cmd->cmd) {
> > @@ -23,14 +24,19 @@ static int iecm_get_rxnfc(struct net_device
> *netdev, struct ethtool_rxnfc *cmd,
> >  		ret = 0;
> >  		break;
> >  	case ETHTOOL_GRXCLSRLCNT:
> > -		/* stub */
> > +		if (!iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
> > +				     VIRTCHNL2_CAP_FDIR))
> > +			break;
> > +		cmd->rule_cnt =
> > +			adapter-
> >config_data.fdir_config.num_active_filters;
> > +		cmd->data = IECM_MAX_FDIR_FILTERS;
> >  		ret = 0;
> >  		break;
> >  	case ETHTOOL_GRXCLSRULE:
> > -		/* stub */
> > +		ret = iecm_get_fdir_fltr_entry(vport, cmd);
> >  		break;
> >  	case ETHTOOL_GRXCLSRLALL:
> > -		/* stub */
> > +		ret = iecm_get_fdir_fltr_ids(vport, cmd, (u32 *)rule_locs);
> >  		break;
> >  	case ETHTOOL_GRXFH:
> >  		/* stub */
> > @@ -51,14 +57,15 @@ static int iecm_get_rxnfc(struct net_device
> *netdev, struct ethtool_rxnfc *cmd,
> >   */
> >  static int iecm_set_rxnfc(struct net_device *netdev, struct ethtool_rxnfc
> *cmd)
> >  {
> > +	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
> >  	int ret = -EOPNOTSUPP;
> >
> >  	switch (cmd->cmd) {
> >  	case ETHTOOL_SRXCLSRLINS:
> > -		/* stub */
> > +		ret = iecm_add_fdir_fltr(vport, cmd);
> >  		break;
> >  	case ETHTOOL_SRXCLSRLDEL:
> > -		/* stub */
> > +		ret = iecm_del_fdir_fltr(vport, cmd);
> >  		break;
> >  	case ETHTOOL_SRXFH:
> >  		/* stub */
> > diff --git a/drivers/net/ethernet/intel/iecm/iecm_lib.c
> b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > index c5900723b018..35c0cbc42ebe 100644
> > --- a/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > +++ b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > @@ -967,6 +967,56 @@ static void iecm_remove_vlan_filters(struct
> iecm_vport *vport)
> >  	}
> >  }
> >
> > +/**
> > + * iecm_remove_fdir_filters - Remove all Flow Director filters
> > + * @vport: vport structure
> > + */
> > +static void iecm_remove_fdir_filters(struct iecm_vport *vport)
> > +{
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	struct iecm_fdir_fltr_config *fdir_config;
> > +
> > +	if (!iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
> VIRTCHNL2_CAP_FDIR))
> > +		return;
> > +
> > +	fdir_config = &adapter->config_data.fdir_config;
> > +	if (!list_empty(&fdir_config->fdir_fltr_list)) {
> > +		struct iecm_fdir_fltr *fdir;
> > +
> > +		spin_lock_bh(&adapter->fdir_fltr_list_lock);
> > +		list_for_each_entry(fdir, &fdir_config->fdir_fltr_list, list) {
> > +			fdir->remove = true;
> > +		}
> 
> Braces are redundant here.
> 

Will fix

> > +		spin_unlock_bh(&adapter->fdir_fltr_list_lock);
> > +		iecm_send_del_fdir_filter_msg(vport);
> > +	}
> 
> 	if (list_empty())
> 		return;
> 
> 	spin_lock_bh(...
> 
> -1 indent level.
> 

Yeah there's probably a bunch of these.  Will fix.

> > +}
> > +
> > +/**
> > + * iecm_del_all_fdir_filters - delete all Flow Director filters
> > + * @vport: vport structure
> > + *
> > + * This function will loop through the list of Flow Director filters and
> > + * deletes them.
> > + **/
> > +static void iecm_del_all_fdir_filters(struct iecm_vport *vport)
> > +{
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	struct iecm_fdir_fltr_config *fdir_config;
> > +	struct iecm_fdir_fltr *fdir, *fdir_tmp;
> > +
> > +	fdir_config = &adapter->config_data.fdir_config;
> > +
> > +	spin_lock_bh(&adapter->fdir_fltr_list_lock);
> > +	list_for_each_entry_safe(fdir, fdir_tmp, &fdir_config->fdir_fltr_list,
> > +				 list) {
> > +		list_del(&fdir->list);
> > +		kfree(fdir);
> > +	}
> > +	fdir_config->num_active_filters = 0;
> > +	spin_unlock_bh(&adapter->fdir_fltr_list_lock);
> > +}
> > +
> >  /**
> >   * iecm_vport_stop - Disable a vport
> >   * @vport: vport to disable
> > @@ -997,6 +1047,8 @@ static void iecm_vport_stop(struct iecm_vport
> *vport)
> >  	if (!test_bit(__IECM_REL_RES_IN_PROG, adapter->flags))
> >  		iecm_remove_vlan_filters(vport);
> >
> > +	iecm_remove_fdir_filters(vport);
> > +
> >  	adapter->link_up = false;
> >  	iecm_vport_intr_deinit(vport);
> >  	iecm_vport_intr_rel(vport);
> > @@ -1206,6 +1258,28 @@ static void iecm_restore_vlans(struct
> iecm_vport *vport)
> >  		iecm_set_all_vlans(vport);
> >  }
> >
> > +/**
> > + * iecm_restore_fdir_filters - Restore all Flow Director filters
> > + * @vport: vport structure
> > + */
> > +static void iecm_restore_fdir_filters(struct iecm_vport *vport)
> > +{
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	struct iecm_fdir_fltr_config *fdir_config;
> > +
> > +	fdir_config = &adapter->config_data.fdir_config;
> > +	if (!list_empty(&fdir_config->fdir_fltr_list)) {
> > +		struct iecm_fdir_fltr *fdir;
> > +
> > +		spin_lock_bh(&adapter->fdir_fltr_list_lock);
> > +		list_for_each_entry(fdir, &fdir_config->fdir_fltr_list, list) {
> > +			fdir->add = true;
> > +		}
> 
> Braces are redundant for one-liners.
> 
> > +		spin_unlock_bh(&adapter->fdir_fltr_list_lock);
> > +		iecm_send_add_fdir_filter_msg(vport);
> > +	}
> 
> Invert the condition -> -1 indent level.
> 
> > +}
> > +
> >  /**
> >   * iecm_restore_features - Restore feature configs
> >   * @vport: virtual port structure
> > @@ -1227,6 +1301,9 @@ static void iecm_restore_features(struct
> iecm_vport *vport)
> >  		if (iecm_set_promiscuous(adapter))
> >  			dev_info(&adapter->pdev->dev, "Failed to restore
> promiscuous settings\n");
> >  	}
> > +
> > +	if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
> VIRTCHNL2_CAP_FDIR))
> > +		iecm_restore_fdir_filters(vport);
> >  }
> >
> >  /**
> > @@ -2014,6 +2091,7 @@ int iecm_probe(struct pci_dev *pdev,
> >  	INIT_LIST_HEAD(&adapter->config_data.mac_filter_list);
> >  	INIT_LIST_HEAD(&adapter->config_data.vlan_filter_list);
> >  	INIT_LIST_HEAD(&adapter->config_data.adv_rss_list);
> > +	INIT_LIST_HEAD(&adapter->config_data.fdir_config.fdir_fltr_list);
> >
> >  	INIT_DELAYED_WORK(&adapter->stats_task, iecm_statistics_task);
> >  	INIT_DELAYED_WORK(&adapter->serv_task, iecm_service_task);
> > @@ -2052,7 +2130,17 @@ EXPORT_SYMBOL(iecm_probe);
> >   */
> >  static void iecm_del_user_cfg_data(struct iecm_adapter *adapter)
> >  {
> > -	/* stub */
> > +	int i;
> > +
> > +	if (!adapter->vports)
> > +		return;
> > +
> > +	for (i = 0; i < adapter->num_alloc_vport; i++) {
> > +		if (!adapter->vports[i])
> > +			continue;
> > +
> > +		iecm_del_all_fdir_filters(adapter->vports[i]);
> > +	}
> >  }
> >
> >  /**
> > @@ -2647,6 +2735,1444 @@ static int iecm_setup_tc(struct net_device
> *netdev, enum tc_setup_type type,
> >  	return err;
> >  }
> >
> > +/**
> > + * iecm_pkt_udp_no_pay_len - the length of UDP packet without
> payload
> > + * @fltr: Flow Director filter data structure
> > + */
> > +static u16 iecm_pkt_udp_no_pay_len(struct iecm_fdir_fltr *fltr)
> > +{
> > +	return sizeof(struct ethhdr) +
> > +		(fltr->ip_ver == 4 ? sizeof(struct iphdr)
> 
> IPv4 hdr may have extensions.
> 

This one I'm not confident on.  Will have to check.

> > +				   : sizeof(struct ipv6hdr)) +
> > +		sizeof(struct udphdr);
> > +}
> > +
> > +/**
> > + * iecm_fill_fdir_gtpu_hdr - fill the GTP-U protocol header
> > + * @fltr: Flow Director filter data structure
> > + * @proto_hdrs: Flow Director protocol headers data structure
> > + *
> > + * Returns 0 if the GTP-U protocol header is set successfully
> > + */
> > +static int
> > +iecm_fill_fdir_gtpu_hdr(struct iecm_fdir_fltr *fltr,
> > +			struct virtchnl_proto_hdrs *proto_hdrs)
> > +{
> > +	struct virtchnl_proto_hdr *uhdr = &proto_hdrs-
> >proto_hdr[proto_hdrs->count - 1];
> > +	struct virtchnl_proto_hdr *ghdr = &proto_hdrs-
> >proto_hdr[proto_hdrs->count++];
> 
> Lines over 79.
> 
> > +	struct virtchnl_proto_hdr *ehdr = NULL;
> > +	u16 adj_offs, hdr_offs;
> > +	int i;
> > +
> > +	VIRTCHNL_SET_PROTO_HDR_TYPE(ghdr, GTPU_IP);
> > +
> > +	adj_offs = iecm_pkt_udp_no_pay_len(fltr);
> > +
> > +	for (i = 0; i < fltr->flex_cnt; i++) {
> > +#define IECM_GTPU_HDR_TEID_OFFS0	4
> > +#define IECM_GTPU_HDR_TEID_OFFS1	6
> > +#define IECM_GTPU_HDR_N_PDU_AND_NEXT_EXTHDR_OFFS	10
> > +#define IECM_GTPU_HDR_NEXT_EXTHDR_TYPE_MASK
> 	0x00FF /* skip N_PDU */
> > +/* PDU Session Container Extension Header (PSC) */
> > +#define IECM_GTPU_PSC_EXTHDR_TYPE			0x85
> > +#define IECM_GTPU_HDR_PSC_PDU_TYPE_AND_QFI_OFFS
> 	13
> > +#define IECM_GTPU_HDR_PSC_PDU_QFI_MASK			0x3F
> /* skip Type */
> > +#define IECM_GTPU_EH_QFI_IDX				1
> 
> It's better to define them outside the function.
> 

I'll take a look.

> > +
> > +		if (fltr->flex_words[i].offset < adj_offs)
> > +			return -EINVAL;
> > +
> > +		hdr_offs = fltr->flex_words[i].offset - adj_offs;
> > +
> > +		switch (hdr_offs) {
> > +		case IECM_GTPU_HDR_TEID_OFFS0:
> > +		case IECM_GTPU_HDR_TEID_OFFS1: {
> > +			__be16 *pay_word = (__force __be16 *)ghdr-
> >buffer;
> 
> I'd declare @pay_word outside of switch-case (which is recommended)
> to remove braces around the body.
> 

Sure will fix.
> > +
> > +			pay_word[hdr_offs >> 1] =
> > +					htons(fltr->flex_words[i].word);
> > +			VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(ghdr,
> GTPU_IP, TEID);
> > +			}
> > +			break;
> > +		case IECM_GTPU_HDR_N_PDU_AND_NEXT_EXTHDR_OFFS:
> > +			if ((fltr->flex_words[i].word &
> > +			     IECM_GTPU_HDR_NEXT_EXTHDR_TYPE_MASK)
> !=
> > +
> 	IECM_GTPU_PSC_EXTHDR_TYPE)
> > +				return -EOPNOTSUPP;
> > +			if (!ehdr)
> > +				ehdr =
> > +				   &proto_hdrs->proto_hdr[proto_hdrs-
> >count++];
> > +			VIRTCHNL_SET_PROTO_HDR_TYPE(ehdr, GTPU_EH);
> > +			break;
> > +		case IECM_GTPU_HDR_PSC_PDU_TYPE_AND_QFI_OFFS:
> > +			if (!ehdr)
> > +				return -EINVAL;
> > +			ehdr->buffer[IECM_GTPU_EH_QFI_IDX] =
> > +					fltr->flex_words[i].word &
> > +
> 	IECM_GTPU_HDR_PSC_PDU_QFI_MASK;
> 
> Inconsistent indentation here, filtr-> and IECM_ should be on the
> same level.
> 
Will fix

> > +			VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(ehdr,
> GTPU_EH, QFI);
> > +			break;
> > +		default:
> > +			return -EINVAL;
> > +		}
> > +	}
> > +
> > +	/* The PF ignores the UDP header fields */
> > +	uhdr->field_selector = 0;
> > +
> > +	return 0;
> > +}
> > +
> > +/**
> > + * iecm_fill_fdir_pfcp_hdr - fill the PFCP protocol header
> > + * @fltr: Flow Director filter data structure
> > + * @proto_hdrs: Flow Director protocol headers data structure
> > + *
> > + * Returns 0 if the PFCP protocol header is set successfully
> > + */
> > +static int
> > +iecm_fill_fdir_pfcp_hdr(struct iecm_fdir_fltr *fltr,
> > +			struct virtchnl_proto_hdrs *proto_hdrs)
> > +{
> > +	struct virtchnl_proto_hdr *uhdr =
> > +				&proto_hdrs->proto_hdr[proto_hdrs-
> >count - 1];
> > +	struct virtchnl_proto_hdr *hdr =
> > +				&proto_hdrs->proto_hdr[proto_hdrs-
> >count++];
> 
> Both could be declared and assigned separately to avoid line breaks.
> 
> > +	u16 adj_offs, hdr_offs;
> > +	int i;
> > +
> > +	VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, PFCP);
> > +
> > +	adj_offs = iecm_pkt_udp_no_pay_len(fltr);
> > +
> > +	for (i = 0; i < fltr->flex_cnt; i++) {
> > +#define IECM_PFCP_HDR_SFIELD_AND_MSG_TYPE_OFFS	0
> 
> Better to define outside the function.
> 

Sure

> > +		if (fltr->flex_words[i].offset < adj_offs)
> > +			return -EINVAL;
> > +
> > +		hdr_offs = fltr->flex_words[i].offset - adj_offs;
> > +
> > +		switch (hdr_offs) {
> > +		case IECM_PFCP_HDR_SFIELD_AND_MSG_TYPE_OFFS:
> > +			hdr->buffer[0] = (fltr->flex_words[i].word >> 8) &
> 0xff;
> 
> >> 8 and & 0xff are candidates for FIELD_GET(GENMASK(15, 8)).
> 

Will fix
> > +			VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, PFCP,
> S_FIELD);
> > +			break;
> > +		default:
> > +			return -EINVAL;
> > +		}
> > +	}
> > +
> > +	/* The PF ignores the UDP header fields */
> > +	uhdr->field_selector = 0;
> > +
> > +	return 0;
> > +}
> > +
> > +/**
> > + * iecm_fill_fdir_nat_t_esp_hdr - fill the NAT-T-ESP protocol header
> > + * @fltr: Flow Director filter data structure
> > + * @proto_hdrs: Flow Director protocol headers data structure
> > + *
> > + * Returns 0 if the NAT-T-ESP protocol header is set successfully
> > + */
> > +static int
> > +iecm_fill_fdir_nat_t_esp_hdr(struct iecm_fdir_fltr *fltr,
> > +			     struct virtchnl_proto_hdrs *proto_hdrs)
> > +{
> > +	struct virtchnl_proto_hdr *uhdr =
> > +				&proto_hdrs->proto_hdr[proto_hdrs-
> >count - 1];
> > +	struct virtchnl_proto_hdr *hdr =
> > +				&proto_hdrs->proto_hdr[proto_hdrs-
> >count++];
> 
> Same here about line wraps.
> 
> > +	u16 adj_offs, hdr_offs;
> > +	u32 spi = 0;
> > +	int i;
> > +
> > +	VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, ESP);
> > +
> > +	adj_offs = iecm_pkt_udp_no_pay_len(fltr);
> > +
> > +	for (i = 0; i < fltr->flex_cnt; i++) {
> > +#define IECM_NAT_T_ESP_SPI_OFFS0	0
> > +#define IECM_NAT_T_ESP_SPI_OFFS1	2
> 
> Same here about definitions. It would be harder to look for them
> later (not counting grep and Elixir).
> 
> > +		if (fltr->flex_words[i].offset < adj_offs)
> > +			return -EINVAL;
> > +
> > +		hdr_offs = fltr->flex_words[i].offset - adj_offs;
> > +
> > +		switch (hdr_offs) {
> > +		case IECM_NAT_T_ESP_SPI_OFFS0:
> > +			spi |= fltr->flex_words[i].word << 16;
> > +			break;
> > +		case IECM_NAT_T_ESP_SPI_OFFS1:
> > +			spi |= fltr->flex_words[i].word;
> > +			break;
> > +		default:
> > +			return -EINVAL;
> > +		}
> > +	}
> > +
> > +	/* Not support IKE Header Format with SPI 0 */
> > +	if (!spi)
> > +		return -EOPNOTSUPP;
> > +
> > +	*(__force __be32 *)hdr->buffer = htonl(spi);
> > +	VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, ESP, SPI);
> > +
> > +	/* The PF ignores the UDP header fields */
> > +	uhdr->field_selector = 0;
> > +
> > +	return 0;
> > +}
> > +
> > +/**
> > + * iecm_fill_fdir_udp_flex_pay_hdr - fill the UDP payload header
> > + * @fltr: Flow Director filter data structure
> > + * @proto_hdrs: Flow Director protocol headers data structure
> > + *
> > + * Returns 0 if the UDP payload defined protocol header is set
> successfully
> > + */
> > +static int
> > +iecm_fill_fdir_udp_flex_pay_hdr(struct iecm_fdir_fltr *fltr,
> > +				struct virtchnl_proto_hdrs *proto_hdrs)
> > +{
> > +#define IECM_GTPU_PORT		2152
> > +#define IECM_NAT_T_ESP_PORT	4500
> > +#define IECM_PFCP_PORT		8805
> 
> Same for those, they don't belong here at all.
> 
> > +	int err;
> > +
> > +	switch (ntohs(fltr->ip_data.dst_port)) {
> > +	case IECM_GTPU_PORT:
> > +		err = iecm_fill_fdir_gtpu_hdr(fltr, proto_hdrs);
> > +		break;
> > +	case IECM_NAT_T_ESP_PORT:
> > +		err = iecm_fill_fdir_nat_t_esp_hdr(fltr, proto_hdrs);
> > +		break;
> > +	case IECM_PFCP_PORT:
> > +		err = iecm_fill_fdir_pfcp_hdr(fltr, proto_hdrs);
> > +		break;
> > +	default:
> > +		err = -EOPNOTSUPP;
> > +		break;
> 
> All of those should be converted to just
> 
> 		return iecm_ ...
> 
> directly, rather than assigning it to err just to return err.
> 

Sure will fix.

> > +	}
> > +
> > +	return err;
> > +}
> > +
> > +/**
> > + * iecm_fill_fdir_ip4_hdr - fill the IPv4 protocol header
> > + * @fltr: Flow Director filter data structure
> > + * @proto_hdrs: Flow Director protocol headers data structure
> > + *
> > + * Returns 0 if the IPv4 protocol header is set successfully
> > + */
> > +static int iecm_fill_fdir_ip4_hdr(struct iecm_fdir_fltr *fltr,
> > +				  struct virtchnl_proto_hdrs *proto_hdrs)
> > +{
> > +	struct virtchnl_proto_hdr *hdr =
> > +				&proto_hdrs->proto_hdr[proto_hdrs-
> >count++];
> 
> Same for this about line break.
> 
> > +	struct iphdr *iph = (struct iphdr *)hdr->buffer;
> > +
> > +	VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, IPV4);
> > +
> > +	if (fltr->ip_mask.tos == U8_MAX) {
> > +		iph->tos = fltr->ip_data.tos;
> > +		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, IPV4, DSCP);
> > +	}
> > +
> > +	if (fltr->ip_mask.proto == U8_MAX) {
> > +		iph->protocol = fltr->ip_data.proto;
> > +		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, IPV4, PROT);
> > +	}
> > +
> > +	if (fltr->ip_mask.v4_addrs.src_ip == htonl(U32_MAX)) {
> > +		iph->saddr = fltr->ip_data.v4_addrs.src_ip;
> > +		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, IPV4, SRC);
> > +	}
> > +
> > +	if (fltr->ip_mask.v4_addrs.dst_ip == htonl(U32_MAX)) {
> > +		iph->daddr = fltr->ip_data.v4_addrs.dst_ip;
> > +		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, IPV4, DST);
> > +	}
> > +
> > +	fltr->ip_ver = 4;
> > +
> > +	return 0;
> > +}
> > +
> > +/**
> > + * iecm_fill_fdir_ip6_hdr - fill the IPv6 protocol header
> > + * @fltr: Flow Director filter data structure
> > + * @proto_hdrs: Flow Director protocol headers data structure
> > + *
> > + * Returns 0 if the IPv6 protocol header is set successfully
> > + */
> > +static int
> > +iecm_fill_fdir_ip6_hdr(struct iecm_fdir_fltr *fltr,
> > +		       struct virtchnl_proto_hdrs *proto_hdrs)
> > +{
> > +	static const struct in6_addr ipv6_addr_full_mask = {
> > +		.in6_u = {
> > +			.u6_addr8 = {
> > +				0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
> 0xFF,
> > +				0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
> 0xFF,
> 
> Lowercase is generally more preferred for hex constants.
> 

Will fix.

> > +			}
> > +		}
> > +	};
> > +	struct virtchnl_proto_hdr *hdr =
> > +				&proto_hdrs->proto_hdr[proto_hdrs-
> >count++];
> 
> Same.
> 
> > +	struct ipv6hdr *iph = (struct ipv6hdr *)hdr->buffer;
> > +
> > +	VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, IPV6);
> > +
> > +	if (fltr->ip_mask.tclass == U8_MAX) {
> > +		iph->priority = (fltr->ip_data.tclass >> 4) & 0xF;
> > +		iph->flow_lbl[0] = (fltr->ip_data.tclass << 4) & 0xF0;
> > +		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, IPV6, TC);
> > +	}
> > +
> > +	if (fltr->ip_mask.proto == U8_MAX) {
> > +		iph->nexthdr = fltr->ip_data.proto;
> > +		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, IPV6, PROT);
> > +	}
> > +
> > +	if (!memcmp(&fltr->ip_mask.v6_addrs.src_ip,
> &ipv6_addr_full_mask,
> > +		    sizeof(struct in6_addr))) {
> > +		memcpy(&iph->saddr, &fltr->ip_data.v6_addrs.src_ip,
> > +		       sizeof(struct in6_addr));
> > +		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, IPV6, SRC);
> > +	}
> > +
> > +	if (!memcmp(&fltr->ip_mask.v6_addrs.dst_ip,
> &ipv6_addr_full_mask,
> > +		    sizeof(struct in6_addr))) {
> > +		memcpy(&iph->daddr, &fltr->ip_data.v6_addrs.dst_ip,
> > +		       sizeof(struct in6_addr));
> > +		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, IPV6, DST);
> > +	}
> > +
> > +	fltr->ip_ver = 6;
> > +
> > +	return 0;
> > +}
> > +
> > +/**
> > + * iecm_fill_fdir_tcp_hdr - fill the TCP protocol header
> > + * @fltr: Flow Director filter data structure
> > + * @proto_hdrs: Flow Director protocol headers data structure
> > + *
> > + * Returns 0 if the TCP protocol header is set successfully
> > + */
> > +static int
> > +iecm_fill_fdir_tcp_hdr(struct iecm_fdir_fltr *fltr,
> > +		       struct virtchnl_proto_hdrs *proto_hdrs)
> > +{
> > +	struct virtchnl_proto_hdr *hdr =
> > +				&proto_hdrs->proto_hdr[proto_hdrs-
> >count++];
> 
> Same.
> 
> > +	struct tcphdr *tcph = (struct tcphdr *)hdr->buffer;
> > +
> > +	VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, TCP);
> > +
> > +	if (fltr->ip_mask.src_port == htons(U16_MAX)) {
> > +		tcph->source = fltr->ip_data.src_port;
> > +		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, TCP,
> SRC_PORT);
> > +	}
> > +
> > +	if (fltr->ip_mask.dst_port == htons(U16_MAX)) {
> > +		tcph->dest = fltr->ip_data.dst_port;
> > +		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, TCP,
> DST_PORT);
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +/**
> > + * iecm_fill_fdir_udp_hdr - fill the UDP protocol header
> > + * @fltr: Flow Director filter data structure
> > + * @proto_hdrs: Flow Director protocol headers data structure
> > + *
> > + * Returns 0 if the UDP protocol header is set successfully
> > + */
> > +static int
> > +iecm_fill_fdir_udp_hdr(struct iecm_fdir_fltr *fltr,
> > +		       struct virtchnl_proto_hdrs *proto_hdrs)
> > +{
> > +	struct virtchnl_proto_hdr *hdr =
> > +				&proto_hdrs->proto_hdr[proto_hdrs-
> >count++];
> 
> ...
> 
> > +	struct udphdr *udph = (struct udphdr *)hdr->buffer;
> > +
> > +	VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, UDP);
> > +
> > +	if (fltr->ip_mask.src_port == htons(U16_MAX)) {
> > +		udph->source = fltr->ip_data.src_port;
> > +		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, UDP,
> SRC_PORT);
> > +	}
> > +
> > +	if (fltr->ip_mask.dst_port == htons(U16_MAX)) {
> > +		udph->dest = fltr->ip_data.dst_port;
> > +		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, UDP,
> DST_PORT);
> > +	}
> > +
> > +	if (!fltr->flex_cnt)
> > +		return 0;
> > +
> > +	return iecm_fill_fdir_udp_flex_pay_hdr(fltr, proto_hdrs);
> > +}
> > +
> > +/**
> > + * iecm_fill_fdir_sctp_hdr - fill the SCTP protocol header
> > + * @fltr: Flow Director filter data structure
> > + * @proto_hdrs: Flow Director protocol headers data structure
> > + *
> > + * Returns 0 if the SCTP protocol header is set successfully
> > + */
> > +static int
> > +iecm_fill_fdir_sctp_hdr(struct iecm_fdir_fltr *fltr,
> > +			struct virtchnl_proto_hdrs *proto_hdrs)
> > +{
> > +	struct virtchnl_proto_hdr *hdr =
> > +				&proto_hdrs->proto_hdr[proto_hdrs-
> >count++];
> 
> ...
> 
> > +	struct sctphdr *sctph = (struct sctphdr *)hdr->buffer;
> > +
> > +	VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, SCTP);
> > +
> > +	if (fltr->ip_mask.src_port == htons(U16_MAX)) {
> > +		sctph->source = fltr->ip_data.src_port;
> > +		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, SCTP,
> SRC_PORT);
> > +	}
> > +
> > +	if (fltr->ip_mask.dst_port == htons(U16_MAX)) {
> > +		sctph->dest = fltr->ip_data.dst_port;
> > +		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, SCTP,
> DST_PORT);
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +/**
> > + * iecm_fill_fdir_ah_hdr - fill the AH protocol header
> > + * @fltr: Flow Director filter data structure
> > + * @proto_hdrs: Flow Director protocol headers data structure
> > + *
> > + * Returns 0 if the AH protocol header is set successfully
> > + */
> > +static int
> > +iecm_fill_fdir_ah_hdr(struct iecm_fdir_fltr *fltr,
> > +		      struct virtchnl_proto_hdrs *proto_hdrs)
> > +{
> > +	struct virtchnl_proto_hdr *hdr =
> > +				&proto_hdrs->proto_hdr[proto_hdrs-
> >count++];
> 
> ...
> 
> > +	struct ip_auth_hdr *ah = (struct ip_auth_hdr *)hdr->buffer;
> > +
> > +	VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, AH);
> > +
> > +	if (fltr->ip_mask.spi == htonl(U32_MAX)) {
> > +		ah->spi = fltr->ip_data.spi;
> > +		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, AH, SPI);
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +/**
> > + * iecm_fill_fdir_esp_hdr - fill the ESP protocol header
> > + * @fltr: Flow Director filter data structure
> > + * @proto_hdrs: Flow Director protocol headers data structure
> > + *
> > + * Returns 0 if the ESP protocol header is set successfully
> > + */
> > +static int
> > +iecm_fill_fdir_esp_hdr(struct iecm_fdir_fltr *fltr,
> > +		       struct virtchnl_proto_hdrs *proto_hdrs)
> > +{
> > +	struct virtchnl_proto_hdr *hdr =
> > +				&proto_hdrs->proto_hdr[proto_hdrs-
> >count++];
> 
> ...
> 
> > +	struct ip_esp_hdr *esph = (struct ip_esp_hdr *)hdr->buffer;
> > +
> > +	VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, ESP);
> > +
> > +	if (fltr->ip_mask.spi == htonl(U32_MAX)) {
> > +		esph->spi = fltr->ip_data.spi;
> > +		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, ESP, SPI);
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +/**
> > + * iecm_fill_fdir_l4_hdr - fill the L4 protocol header
> > + * @fltr: Flow Director filter data structure
> > + * @proto_hdrs: Flow Director protocol headers data structure
> > + *
> > + * Returns 0 if the L4 protocol header is set successfully
> > + */
> > +static int
> > +iecm_fill_fdir_l4_hdr(struct iecm_fdir_fltr *fltr,
> > +		      struct virtchnl_proto_hdrs *proto_hdrs)
> > +{
> > +	struct virtchnl_proto_hdr *hdr;
> > +	__be32 *l4_4_data;
> > +
> > +	if (!fltr->ip_mask.proto) /* IPv4/IPv6 header only */
> > +		return 0;
> > +
> > +	hdr = &proto_hdrs->proto_hdr[proto_hdrs->count++];
> 
> Here's a good example: assignment occurs separately from the
> declaration and doesn't cause a line wrap.
> 
> > +	l4_4_data = (__force __be32 *)hdr->buffer;
> > +
> > +	/* L2TPv3 over IP with 'Session ID' */
> > +	if (fltr->ip_data.proto == IPPROTO_L2TP &&
> > +	    fltr->ip_mask.l4_header == htonl(U32_MAX)) {
> > +		VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, L2TPV3);
> > +		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, L2TPV3,
> SESS_ID);
> > +
> > +		*l4_4_data = fltr->ip_data.l4_header;
> > +	} else {
> > +		return -EOPNOTSUPP;
> > +	}
> 
> 	if (!= || !=)
> 		return -EOPNOTSUPP;
> 
> 	VIRTCHNL_SET_ ...
> 
> -1 level this way.
> 
> > +
> > +	return 0;
> > +}
> > +
> > +/**
> > + * iecm_fill_fdir_eth_hdr - fill the Ethernet protocol header
> > + * @fltr: Flow Director filter data structure
> > + * @proto_hdrs: Flow Director protocol headers data structure
> > + *
> > + * Returns 0 if the Ethernet protocol header is set successfully
> > + */
> > +static int
> > +iecm_fill_fdir_eth_hdr(struct iecm_fdir_fltr *fltr,
> > +		       struct virtchnl_proto_hdrs *proto_hdrs)
> > +{
> > +	struct virtchnl_proto_hdr *hdr =
> > +				&proto_hdrs->proto_hdr[proto_hdrs-
> >count++];
> 
> Here's a bad example again.
> 
> > +	struct ethhdr *ehdr = (struct ethhdr *)hdr->buffer;
> > +
> > +	VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, ETH);
> > +
> > +	if (fltr->eth_mask.etype == htons(U16_MAX)) {
> > +		if (fltr->eth_data.etype == htons(ETH_P_IP) ||
> > +		    fltr->eth_data.etype == htons(ETH_P_IPV6))
> > +			return -EOPNOTSUPP;
> > +
> > +		ehdr->h_proto = fltr->eth_data.etype;
> > +		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, ETH,
> ETHERTYPE);
> > +	}
> 
> This can be inverted as well.
> 
> > +
> > +	return 0;
> > +}
> > +
> > +/**
> > + * iecm_fill_fdir_add_msg - fill the Flow Director filter into virtchnl
> message
> > + * @vport: vport structure
> > + * @fltr: Flow Director filter data structure
> > + *
> > + * Returns 0 if the add Flow Director virtchnl message is filled
> successfully
> > + */
> > +static int
> > +iecm_fill_fdir_add_msg(struct iecm_vport *vport, struct iecm_fdir_fltr
> *fltr)
> > +{
> > +	struct virtchnl_fdir_add *vc_msg = &fltr->vc_add_msg;
> > +	struct virtchnl_proto_hdrs *proto_hdrs;
> > +	int err;
> > +
> > +	proto_hdrs = &vc_msg->rule_cfg.proto_hdrs;
> 
> And here's good.
> 
> > +
> > +	err = iecm_fill_fdir_eth_hdr(fltr, proto_hdrs); /* L2 always exists */
> > +	if (err)
> > +		return err;
> > +
> > +	switch (fltr->flow_type) {
> > +	case IECM_FDIR_FLOW_IPV4_TCP:
> > +		err = iecm_fill_fdir_ip4_hdr(fltr, proto_hdrs) |
> > +		      iecm_fill_fdir_tcp_hdr(fltr, proto_hdrs);
> > +		break;
> > +	case IECM_FDIR_FLOW_IPV4_UDP:
> > +		err = iecm_fill_fdir_ip4_hdr(fltr, proto_hdrs) |
> > +		      iecm_fill_fdir_udp_hdr(fltr, proto_hdrs);
> > +		break;
> > +	case IECM_FDIR_FLOW_IPV4_SCTP:
> > +		err = iecm_fill_fdir_ip4_hdr(fltr, proto_hdrs) |
> > +		      iecm_fill_fdir_sctp_hdr(fltr, proto_hdrs);
> > +		break;
> > +	case IECM_FDIR_FLOW_IPV4_AH:
> > +		err = iecm_fill_fdir_ip4_hdr(fltr, proto_hdrs) |
> > +		      iecm_fill_fdir_ah_hdr(fltr, proto_hdrs);
> > +		break;
> > +	case IECM_FDIR_FLOW_IPV4_ESP:
> > +		err = iecm_fill_fdir_ip4_hdr(fltr, proto_hdrs) |
> > +		      iecm_fill_fdir_esp_hdr(fltr, proto_hdrs);
> > +		break;
> > +	case IECM_FDIR_FLOW_IPV4_OTHER:
> > +		err = iecm_fill_fdir_ip4_hdr(fltr, proto_hdrs) |
> > +		      iecm_fill_fdir_l4_hdr(fltr, proto_hdrs);
> > +		break;
> > +	case IECM_FDIR_FLOW_IPV6_TCP:
> > +		err = iecm_fill_fdir_ip6_hdr(fltr, proto_hdrs) |
> > +		      iecm_fill_fdir_tcp_hdr(fltr, proto_hdrs);
> > +		break;
> > +	case IECM_FDIR_FLOW_IPV6_UDP:
> > +		err = iecm_fill_fdir_ip6_hdr(fltr, proto_hdrs) |
> > +		      iecm_fill_fdir_udp_hdr(fltr, proto_hdrs);
> > +		break;
> > +	case IECM_FDIR_FLOW_IPV6_SCTP:
> > +		err = iecm_fill_fdir_ip6_hdr(fltr, proto_hdrs) |
> > +		      iecm_fill_fdir_sctp_hdr(fltr, proto_hdrs);
> > +		break;
> > +	case IECM_FDIR_FLOW_IPV6_AH:
> > +		err = iecm_fill_fdir_ip6_hdr(fltr, proto_hdrs) |
> > +		      iecm_fill_fdir_ah_hdr(fltr, proto_hdrs);
> > +		break;
> > +	case IECM_FDIR_FLOW_IPV6_ESP:
> > +		err = iecm_fill_fdir_ip6_hdr(fltr, proto_hdrs) |
> > +		      iecm_fill_fdir_esp_hdr(fltr, proto_hdrs);
> > +		break;
> > +	case IECM_FDIR_FLOW_IPV6_OTHER:
> > +		err = iecm_fill_fdir_ip6_hdr(fltr, proto_hdrs) |
> > +		      iecm_fill_fdir_l4_hdr(fltr, proto_hdrs);
> > +		break;
> > +	case IECM_FDIR_FLOW_NON_IP_L2:
> > +		break;
> > +	default:
> > +		err = -EINVAL;
> > +		break;
> > +	}
> > +
> > +	if (err)
> > +		return err;
> > +
> > +	vc_msg->vsi_id = vport->vport_id;
> > +	vc_msg->rule_cfg.action_set.count = 1;
> > +	vc_msg->rule_cfg.action_set.actions[0].type = fltr->action;
> > +	vc_msg->rule_cfg.action_set.actions[0].act_conf.queue.index =
> > +								fltr->q_index;
> > +
> > +	return 0;
> > +}
> > +
> > +/**
> > + * iecm_fdir_flow_proto_name - get the flow protocol name
> > + * @flow_type: Flow Director filter flow type
> > + **/
> > +static const char *
> > +iecm_fdir_flow_proto_name(enum iecm_fdir_flow_type flow_type)
> > +{
> > +	switch (flow_type) {
> > +	case IECM_FDIR_FLOW_IPV4_TCP:
> > +	case IECM_FDIR_FLOW_IPV6_TCP:
> > +		return "TCP";
> > +	case IECM_FDIR_FLOW_IPV4_UDP:
> > +	case IECM_FDIR_FLOW_IPV6_UDP:
> > +		return "UDP";
> > +	case IECM_FDIR_FLOW_IPV4_SCTP:
> > +	case IECM_FDIR_FLOW_IPV6_SCTP:
> > +		return "SCTP";
> > +	case IECM_FDIR_FLOW_IPV4_AH:
> > +	case IECM_FDIR_FLOW_IPV6_AH:
> > +		return "AH";
> > +	case IECM_FDIR_FLOW_IPV4_ESP:
> > +	case IECM_FDIR_FLOW_IPV6_ESP:
> > +		return "ESP";
> > +	case IECM_FDIR_FLOW_IPV4_OTHER:
> > +	case IECM_FDIR_FLOW_IPV6_OTHER:
> > +		return "Other";
> > +	case IECM_FDIR_FLOW_NON_IP_L2:
> > +		return "Ethernet";
> > +	default:
> > +		return NULL;
> > +	}
> > +}
> > +
> > +/**
> > + * iecm_dump_fdir_fltr
> > + * @vport: vport structure
> > + * @fltr: Flow Director filter to print
> > + *
> > + * Print the Flow Director filter
> > + **/
> > +static void
> > +iecm_dump_fdir_fltr(struct iecm_vport *vport, struct iecm_fdir_fltr
> *fltr)
> > +{
> > +	const char *proto = iecm_fdir_flow_proto_name(fltr->flow_type);
> > +	struct device *dev = &vport->adapter->pdev->dev;
> > +
> > +	if (!proto)
> > +		return;
> > +
> > +	switch (fltr->flow_type) {
> > +	case IECM_FDIR_FLOW_IPV4_TCP:
> > +	case IECM_FDIR_FLOW_IPV4_UDP:
> > +	case IECM_FDIR_FLOW_IPV4_SCTP:
> > +		dev_info(dev, "Rule ID: %u dst_ip: %pI4 src_ip %pI4 %s:
> dst_port %hu src_port %hu\n",
> > +			 fltr->loc,
> > +			 &fltr->ip_data.v4_addrs.dst_ip,
> > +			 &fltr->ip_data.v4_addrs.src_ip,
> > +			 proto,
> > +			 ntohs(fltr->ip_data.dst_port),
> > +			 ntohs(fltr->ip_data.src_port));
> 
> Some of those can fit into previous line, there's no need to put
> each argument onto separate one.
> 

It technically can fit on one line, but it's much easier for humans to parse what's going where the way it's written now.

> > +		break;
> > +	case IECM_FDIR_FLOW_IPV4_AH:
> > +	case IECM_FDIR_FLOW_IPV4_ESP:
> > +		dev_info(dev, "Rule ID: %u dst_ip: %pI4 src_ip %pI4 %s: SPI
> %u\n",
> > +			 fltr->loc,
> > +			 &fltr->ip_data.v4_addrs.dst_ip,
> > +			 &fltr->ip_data.v4_addrs.src_ip,
> > +			 proto,
> > +			 ntohl(fltr->ip_data.spi));
> 
> Same here.
> 
> > +		break;
> > +	case IECM_FDIR_FLOW_IPV4_OTHER:
> > +		dev_info(dev, "Rule ID: %u dst_ip: %pI4 src_ip %pI4 proto:
> %u L4_bytes: 0x%x\n",
> > +			 fltr->loc,
> > +			 &fltr->ip_data.v4_addrs.dst_ip,
> > +			 &fltr->ip_data.v4_addrs.src_ip,
> > +			 fltr->ip_data.proto,
> > +			 ntohl(fltr->ip_data.l4_header));
> 
> And here.
> 
> > +		break;
> > +	case IECM_FDIR_FLOW_IPV6_TCP:
> > +	case IECM_FDIR_FLOW_IPV6_UDP:
> > +	case IECM_FDIR_FLOW_IPV6_SCTP:
> > +		dev_info(dev, "Rule ID: %u dst_ip: %pI6 src_ip %pI6 %s:
> dst_port %hu src_port %hu\n",
> > +			 fltr->loc,
> > +			 &fltr->ip_data.v6_addrs.dst_ip,
> > +			 &fltr->ip_data.v6_addrs.src_ip,
> > +			 proto,
> > +			 ntohs(fltr->ip_data.dst_port),
> > +			 ntohs(fltr->ip_data.src_port));
> 
> ...
> 
> > +		break;
> > +	case IECM_FDIR_FLOW_IPV6_AH:
> > +	case IECM_FDIR_FLOW_IPV6_ESP:
> > +		dev_info(dev, "Rule ID: %u dst_ip: %pI6 src_ip %pI6 %s: SPI
> %u\n",
> > +			 fltr->loc,
> > +			 &fltr->ip_data.v6_addrs.dst_ip,
> > +			 &fltr->ip_data.v6_addrs.src_ip,
> > +			 proto,
> > +			 ntohl(fltr->ip_data.spi));
> 
> ...
> 
> > +		break;
> > +	case IECM_FDIR_FLOW_IPV6_OTHER:
> > +		dev_info(dev, "Rule ID: %u dst_ip: %pI6 src_ip %pI6 proto:
> %u L4_bytes: 0x%x\n",
> > +			 fltr->loc,
> > +			 &fltr->ip_data.v6_addrs.dst_ip,
> > +			 &fltr->ip_data.v6_addrs.src_ip,
> > +			 fltr->ip_data.proto,
> > +			 ntohl(fltr->ip_data.l4_header));
> 
> ...
> 
> > +		break;
> > +	case IECM_FDIR_FLOW_NON_IP_L2:
> > +		dev_info(dev, "Rule ID: %u eth_type: 0x%x\n",
> > +			 fltr->loc,
> > +			 ntohs(fltr->eth_data.etype));
> 
> ...
> 

This one probably doesn't need it will fix.

> > +		break;
> > +	default:
> > +		break;
> > +	}
> > +}
> > +
> > +/**
> > + * iecm_fdir_is_dup_fltr - test if filter is already in list
> > + * @adapter: board private structure
> > + * @fltr: Flow Director filter data structure
> > + *
> > + * Returns true if the filter is found in the list
> > + */
> > +static bool
> > +iecm_fdir_is_dup_fltr(struct iecm_adapter *adapter,
> > +		      struct iecm_fdir_fltr *fltr)
> > +{
> > +	struct iecm_fdir_fltr_config *fdir_config;
> > +	struct iecm_fdir_fltr *tmp;
> > +
> > +	fdir_config = &adapter->config_data.fdir_config;
> > +	list_for_each_entry(tmp, &fdir_config->fdir_fltr_list, list) {
> > +		if (tmp->flow_type != fltr->flow_type)
> > +			continue;
> > +
> > +		if (!memcmp(&tmp->eth_data, &fltr->eth_data,
> > +			    sizeof(fltr->eth_data)) &&
> > +		    !memcmp(&tmp->ip_data, &fltr->ip_data,
> > +			    sizeof(fltr->ip_data)) &&
> > +		    !memcmp(&tmp->ext_data, &fltr->ext_data,
> > +			    sizeof(fltr->ext_data)))
> > +			return true;
> > +	}
> > +
> > +	return false;
> > +}
> > +
> > +/**
> > + * iecm_find_fdir_fltr_by_loc - find filter with location
> > + * @adapter: board private structure
> > + * @loc: location to find.
> > + *
> > + * Returns pointer to Flow Director filter if found or null
> > + */
> > +static struct iecm_fdir_fltr *
> > +iecm_find_fdir_fltr_by_loc(struct iecm_adapter *adapter, u32 loc)
> > +{
> > +	struct iecm_fdir_fltr_config *fdir_config;
> > +	struct iecm_fdir_fltr *rule;
> > +
> > +	fdir_config = &adapter->config_data.fdir_config;
> > +	list_for_each_entry(rule, &fdir_config->fdir_fltr_list, list)
> > +		if (rule->loc == loc)
> > +			return rule;
> 
> Here's a good example that a single `if` statement shouldn't be
> placed into a pair of braces.
> 

You're right it does need some braces on the list_for_each, will fix.

> > +
> > +	return NULL;
> > +}
> > +
> > +/**
> > + * iecm_fdir_list_add_fltr - add a new node to the flow director filter list
> > + * @adapter: board private structure
> > + * @fltr: filter node to add to structure
> > + */
> > +static void
> > +iecm_fdir_list_add_fltr(struct iecm_adapter *adapter,
> > +			struct iecm_fdir_fltr *fltr)
> > +{
> > +	struct iecm_fdir_fltr *rule, *parent = NULL;
> > +	struct iecm_fdir_fltr_config *fdir_config;
> > +
> > +	fdir_config = &adapter->config_data.fdir_config;
> > +
> > +	spin_lock_bh(&adapter->fdir_fltr_list_lock);
> > +	list_for_each_entry(rule, &fdir_config->fdir_fltr_list, list) {
> > +		if (rule->loc >= fltr->loc)
> > +			break;
> > +		parent = rule;
> > +	}
> > +
> > +	if (parent)
> > +		list_add(&fltr->list, &parent->list);
> > +	else
> > +		list_add(&fltr->list, &fdir_config->fdir_fltr_list);
> > +	fltr->add = true;
> > +	spin_unlock_bh(&adapter->fdir_fltr_list_lock);
> > +}
> > +
> > +/**
> > + * iecm_fltr_to_ethtool_flow - convert filter type values to ethtool
> > + * flow type values
> > + * @flow: filter type to be converted
> > + *
> > + * Returns the corresponding ethtool flow type.
> > + */
> > +static int iecm_fltr_to_ethtool_flow(enum iecm_fdir_flow_type flow)
> > +{
> > +	switch (flow) {
> > +	case IECM_FDIR_FLOW_IPV4_TCP:
> > +		return TCP_V4_FLOW;
> > +	case IECM_FDIR_FLOW_IPV4_UDP:
> > +		return UDP_V4_FLOW;
> > +	case IECM_FDIR_FLOW_IPV4_SCTP:
> > +		return SCTP_V4_FLOW;
> > +	case IECM_FDIR_FLOW_IPV4_AH:
> > +		return AH_V4_FLOW;
> > +	case IECM_FDIR_FLOW_IPV4_ESP:
> > +		return ESP_V4_FLOW;
> > +	case IECM_FDIR_FLOW_IPV4_OTHER:
> > +		return IPV4_USER_FLOW;
> > +	case IECM_FDIR_FLOW_IPV6_TCP:
> > +		return TCP_V6_FLOW;
> > +	case IECM_FDIR_FLOW_IPV6_UDP:
> > +		return UDP_V6_FLOW;
> > +	case IECM_FDIR_FLOW_IPV6_SCTP:
> > +		return SCTP_V6_FLOW;
> > +	case IECM_FDIR_FLOW_IPV6_AH:
> > +		return AH_V6_FLOW;
> > +	case IECM_FDIR_FLOW_IPV6_ESP:
> > +		return ESP_V6_FLOW;
> > +	case IECM_FDIR_FLOW_IPV6_OTHER:
> > +		return IPV6_USER_FLOW;
> > +	case IECM_FDIR_FLOW_NON_IP_L2:
> > +		return ETHER_FLOW;
> > +	default:
> > +		/* 0 is undefined ethtool flow */
> > +		return 0;
> 
> This switch-case can be converted to an array to reduce code size.
> 

Seems reasonable, will look.

> > +	}
> > +}
> > +
> > +/**
> > + * iecm_ethtool_flow_to_fltr - convert ethtool flow type to filter enum
> > + * @eth: Ethtool flow type to be converted
> > + *
> > + * Returns flow enum
> > + */
> > +static enum iecm_fdir_flow_type iecm_ethtool_flow_to_fltr(int eth)
> > +{
> > +	switch (eth) {
> > +	case TCP_V4_FLOW:
> > +		return IECM_FDIR_FLOW_IPV4_TCP;
> > +	case UDP_V4_FLOW:
> > +		return IECM_FDIR_FLOW_IPV4_UDP;
> > +	case SCTP_V4_FLOW:
> > +		return IECM_FDIR_FLOW_IPV4_SCTP;
> > +	case AH_V4_FLOW:
> > +		return IECM_FDIR_FLOW_IPV4_AH;
> > +	case ESP_V4_FLOW:
> > +		return IECM_FDIR_FLOW_IPV4_ESP;
> > +	case IPV4_USER_FLOW:
> > +		return IECM_FDIR_FLOW_IPV4_OTHER;
> > +	case TCP_V6_FLOW:
> > +		return IECM_FDIR_FLOW_IPV6_TCP;
> > +	case UDP_V6_FLOW:
> > +		return IECM_FDIR_FLOW_IPV6_UDP;
> > +	case SCTP_V6_FLOW:
> > +		return IECM_FDIR_FLOW_IPV6_SCTP;
> > +	case AH_V6_FLOW:
> > +		return IECM_FDIR_FLOW_IPV6_AH;
> > +	case ESP_V6_FLOW:
> > +		return IECM_FDIR_FLOW_IPV6_ESP;
> > +	case IPV6_USER_FLOW:
> > +		return IECM_FDIR_FLOW_IPV6_OTHER;
> > +	case ETHER_FLOW:
> > +		return IECM_FDIR_FLOW_NON_IP_L2;
> > +	default:
> > +		return IECM_FDIR_FLOW_NONE;
> 
> Same here.
> 
> > +	}
> > +}
> > +
> > +/**
> > + * iecm_is_mask_valid - check mask field set
> > + * @mask: full mask to check
> > + * @field: field for which mask should be valid
> > + *
> > + * If the mask is fully set return true. If it is not valid for field return
> > + * false.
> > + */
> > +static bool iecm_is_mask_valid(u64 mask, u64 field)
> > +{
> > +	return (mask & field) == field;
> > +}
> 
> That is something really basic and should at least be placed
> somewhere in the headers and used module-wide.
> 

I'm not convinced it makes sense to do that if it's not actually being used module wide. I'm pretty sure this is only validating user input for flow director filters.

> > +
> > +/**
> > + * iecm_parse_rx_flow_user_data - deconstruct user-defined data
> > + * @fsp: pointer to ethtool Rx flow specification
> > + * @fltr: pointer to Flow Director filter for userdef data storage
> > + *
> > + * Returns 0 on success, negative error value on failure
> > + */
> > +static int
> > +iecm_parse_rx_flow_user_data(struct ethtool_rx_flow_spec *fsp,
> > +			     struct iecm_fdir_fltr *fltr)
> > +{
> > +	struct iecm_flex_word *flex;
> > +	int i, cnt = 0;
> > +
> > +	if (!(fsp->flow_type & FLOW_EXT))
> > +		return 0;
> > +
> > +	for (i = 0; i < IECM_FLEX_WORD_NUM; i++) {
> > +#define IECM_USERDEF_FLEX_WORD_M	GENMASK(15, 0)
> > +#define IECM_USERDEF_FLEX_OFFS_S	16
> > +#define IECM_USERDEF_FLEX_OFFS_M	GENMASK(31,
> IECM_USERDEF_FLEX_OFFS_S)
> > +#define IECM_USERDEF_FLEX_FLTR_M	GENMASK(31, 0)
> 
> 1. Should be placed outside the function.
> 2. Candidates for FIELD_{GET,PREP}().
> 

Sure will fix.

> > +		u32 value = be32_to_cpu(fsp->h_ext.data[i]);
> > +		u32 mask = be32_to_cpu(fsp->m_ext.data[i]);
> > +
> > +		if (!value || !mask)
> > +			continue;
> > +
> > +		if (!iecm_is_mask_valid(mask,
> IECM_USERDEF_FLEX_FLTR_M))
> > +			return -EINVAL;
> > +
> > +		/* 504 is the maximum value for offsets, and offset is
> measured
> > +		 * from the start of the MAC address.
> > +		 */
> > +#define IECM_USERDEF_FLEX_MAX_OFFS_VAL 504
> 
> Same.
> 
> > +		flex = &fltr->flex_words[cnt++];
> > +		flex->word = value & IECM_USERDEF_FLEX_WORD_M;
> > +		flex->offset = (value & IECM_USERDEF_FLEX_OFFS_M) >>
> > +			     IECM_USERDEF_FLEX_OFFS_S;
> > +		if (flex->offset > IECM_USERDEF_FLEX_MAX_OFFS_VAL)
> > +			return -EINVAL;
> > +	}
> > +
> > +	fltr->flex_cnt = cnt;
> > +
> > +	return 0;
> > +}
> > +
> > +/**
> > + * iecm_fill_rx_flow_ext_data - fill the additional data
> > + * @fsp: pointer to ethtool Rx flow specification
> > + * @fltr: pointer to Flow Director filter to get additional data
> > + */
> > +static void
> > +iecm_fill_rx_flow_ext_data(struct ethtool_rx_flow_spec *fsp,
> > +			   struct iecm_fdir_fltr *fltr)
> > +{
> > +	if (!fltr->ext_mask.usr_def[0] && !fltr->ext_mask.usr_def[1])
> > +		return;
> > +
> > +	fsp->flow_type |= FLOW_EXT;
> > +
> > +	memcpy(fsp->h_ext.data, fltr->ext_data.usr_def,
> > +	       sizeof(fsp->h_ext.data));
> > +	memcpy(fsp->m_ext.data, fltr->ext_mask.usr_def,
> > +	       sizeof(fsp->m_ext.data));
> > +}
> > +
> > +/**
> > + * iecm_get_fdir_fltr_entry - fill ethtool structure with Flow Director
> > + * filter data
> > + * @vport: vport structure
> > + * @cmd: ethtool command data structure to receive the filter data
> > + *
> > + * Returns 0 as expected for success by ethtool
> > + */
> > +int
> > +iecm_get_fdir_fltr_entry(struct iecm_vport *vport, struct ethtool_rxnfc
> *cmd)
> > +{
> > +	struct ethtool_rx_flow_spec *fsp =
> > +					(struct ethtool_rx_flow_spec
> *)&cmd->fs;
> 
> 	struct ethtool_rx_flow_spec *fsp = (typeof(fsp))&cmd->fs;
> 
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	struct iecm_fdir_fltr *rule;
> > +	int ret = 0;
> > +
> > +	if (adapter->state != __IECM_UP)
> > +		return -EIO;
> > +
> > +	if (!iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
> VIRTCHNL2_CAP_FDIR))
> > +		return -EOPNOTSUPP;
> > +
> > +	spin_lock_bh(&adapter->fdir_fltr_list_lock);
> > +
> > +	rule = iecm_find_fdir_fltr_by_loc(adapter, fsp->location);
> > +	if (!rule) {
> > +		ret = -EINVAL;
> > +		goto release_lock;
> > +	}
> > +
> > +	fsp->flow_type = iecm_fltr_to_ethtool_flow(rule->flow_type);
> > +
> > +	memset(&fsp->m_u, 0, sizeof(fsp->m_u));
> > +	memset(&fsp->m_ext, 0, sizeof(fsp->m_ext));
> > +
> > +	switch (fsp->flow_type) {
> > +	case TCP_V4_FLOW:
> > +	case UDP_V4_FLOW:
> > +	case SCTP_V4_FLOW:
> > +		fsp->h_u.tcp_ip4_spec.ip4src = rule-
> >ip_data.v4_addrs.src_ip;
> > +		fsp->h_u.tcp_ip4_spec.ip4dst = rule-
> >ip_data.v4_addrs.dst_ip;
> > +		fsp->h_u.tcp_ip4_spec.psrc = rule->ip_data.src_port;
> > +		fsp->h_u.tcp_ip4_spec.pdst = rule->ip_data.dst_port;
> > +		fsp->h_u.tcp_ip4_spec.tos = rule->ip_data.tos;
> > +		fsp->m_u.tcp_ip4_spec.ip4src = rule-
> >ip_mask.v4_addrs.src_ip;
> > +		fsp->m_u.tcp_ip4_spec.ip4dst = rule-
> >ip_mask.v4_addrs.dst_ip;
> > +		fsp->m_u.tcp_ip4_spec.psrc = rule->ip_mask.src_port;
> > +		fsp->m_u.tcp_ip4_spec.pdst = rule->ip_mask.dst_port;
> > +		fsp->m_u.tcp_ip4_spec.tos = rule->ip_mask.tos;
> > +		break;
> > +	case AH_V4_FLOW:
> > +	case ESP_V4_FLOW:
> > +		fsp->h_u.ah_ip4_spec.ip4src = rule-
> >ip_data.v4_addrs.src_ip;
> > +		fsp->h_u.ah_ip4_spec.ip4dst = rule-
> >ip_data.v4_addrs.dst_ip;
> > +		fsp->h_u.ah_ip4_spec.spi = rule->ip_data.spi;
> > +		fsp->h_u.ah_ip4_spec.tos = rule->ip_data.tos;
> > +		fsp->m_u.ah_ip4_spec.ip4src = rule-
> >ip_mask.v4_addrs.src_ip;
> > +		fsp->m_u.ah_ip4_spec.ip4dst = rule-
> >ip_mask.v4_addrs.dst_ip;
> > +		fsp->m_u.ah_ip4_spec.spi = rule->ip_mask.spi;
> > +		fsp->m_u.ah_ip4_spec.tos = rule->ip_mask.tos;
> > +		break;
> > +	case IPV4_USER_FLOW:
> > +		fsp->h_u.usr_ip4_spec.ip4src = rule-
> >ip_data.v4_addrs.src_ip;
> > +		fsp->h_u.usr_ip4_spec.ip4dst = rule-
> >ip_data.v4_addrs.dst_ip;
> > +		fsp->h_u.usr_ip4_spec.l4_4_bytes = rule-
> >ip_data.l4_header;
> > +		fsp->h_u.usr_ip4_spec.tos = rule->ip_data.tos;
> > +		fsp->h_u.usr_ip4_spec.ip_ver = ETH_RX_NFC_IP4;
> > +		fsp->h_u.usr_ip4_spec.proto = rule->ip_data.proto;
> > +		fsp->m_u.usr_ip4_spec.ip4src = rule-
> >ip_mask.v4_addrs.src_ip;
> > +		fsp->m_u.usr_ip4_spec.ip4dst = rule-
> >ip_mask.v4_addrs.dst_ip;
> > +		fsp->m_u.usr_ip4_spec.l4_4_bytes = rule-
> >ip_mask.l4_header;
> > +		fsp->m_u.usr_ip4_spec.tos = rule->ip_mask.tos;
> > +		fsp->m_u.usr_ip4_spec.ip_ver = 0xFF;
> > +		fsp->m_u.usr_ip4_spec.proto = rule->ip_mask.proto;
> > +		break;
> > +	case TCP_V6_FLOW:
> > +	case UDP_V6_FLOW:
> > +	case SCTP_V6_FLOW:
> > +		memcpy(fsp->h_u.usr_ip6_spec.ip6src,
> > +		       &rule->ip_data.v6_addrs.src_ip, sizeof(struct
> in6_addr));
> > +		memcpy(fsp->h_u.usr_ip6_spec.ip6dst,
> > +		       &rule->ip_data.v6_addrs.dst_ip, sizeof(struct
> in6_addr));
> > +		fsp->h_u.tcp_ip6_spec.psrc = rule->ip_data.src_port;
> > +		fsp->h_u.tcp_ip6_spec.pdst = rule->ip_data.dst_port;
> > +		fsp->h_u.tcp_ip6_spec.tclass = rule->ip_data.tclass;
> > +		memcpy(fsp->m_u.usr_ip6_spec.ip6src,
> > +		       &rule->ip_mask.v6_addrs.src_ip, sizeof(struct
> in6_addr));
> > +		memcpy(fsp->m_u.usr_ip6_spec.ip6dst,
> > +		       &rule->ip_mask.v6_addrs.dst_ip, sizeof(struct
> in6_addr));
> > +		fsp->m_u.tcp_ip6_spec.psrc = rule->ip_mask.src_port;
> > +		fsp->m_u.tcp_ip6_spec.pdst = rule->ip_mask.dst_port;
> > +		fsp->m_u.tcp_ip6_spec.tclass = rule->ip_mask.tclass;
> > +		break;
> > +	case AH_V6_FLOW:
> > +	case ESP_V6_FLOW:
> > +		memcpy(fsp->h_u.ah_ip6_spec.ip6src,
> > +		       &rule->ip_data.v6_addrs.src_ip, sizeof(struct
> in6_addr));
> > +		memcpy(fsp->h_u.ah_ip6_spec.ip6dst,
> > +		       &rule->ip_data.v6_addrs.dst_ip, sizeof(struct
> in6_addr));
> > +		fsp->h_u.ah_ip6_spec.spi = rule->ip_data.spi;
> > +		fsp->h_u.ah_ip6_spec.tclass = rule->ip_data.tclass;
> > +		memcpy(fsp->m_u.ah_ip6_spec.ip6src,
> > +		       &rule->ip_mask.v6_addrs.src_ip, sizeof(struct
> in6_addr));
> > +		memcpy(fsp->m_u.ah_ip6_spec.ip6dst,
> > +		       &rule->ip_mask.v6_addrs.dst_ip, sizeof(struct
> in6_addr));
> > +		fsp->m_u.ah_ip6_spec.spi = rule->ip_mask.spi;
> > +		fsp->m_u.ah_ip6_spec.tclass = rule->ip_mask.tclass;
> > +		break;
> > +	case IPV6_USER_FLOW:
> > +		memcpy(fsp->h_u.usr_ip6_spec.ip6src,
> > +		       &rule->ip_data.v6_addrs.src_ip, sizeof(struct
> in6_addr));
> > +		memcpy(fsp->h_u.usr_ip6_spec.ip6dst,
> > +		       &rule->ip_data.v6_addrs.dst_ip, sizeof(struct
> in6_addr));
> > +		fsp->h_u.usr_ip6_spec.l4_4_bytes = rule-
> >ip_data.l4_header;
> > +		fsp->h_u.usr_ip6_spec.tclass = rule->ip_data.tclass;
> > +		fsp->h_u.usr_ip6_spec.l4_proto = rule->ip_data.proto;
> > +		memcpy(fsp->m_u.usr_ip6_spec.ip6src,
> > +		       &rule->ip_mask.v6_addrs.src_ip, sizeof(struct
> in6_addr));
> > +		memcpy(fsp->m_u.usr_ip6_spec.ip6dst,
> > +		       &rule->ip_mask.v6_addrs.dst_ip, sizeof(struct
> in6_addr));
> > +		fsp->m_u.usr_ip6_spec.l4_4_bytes = rule-
> >ip_mask.l4_header;
> > +		fsp->m_u.usr_ip6_spec.tclass = rule->ip_mask.tclass;
> > +		fsp->m_u.usr_ip6_spec.l4_proto = rule->ip_mask.proto;
> > +		break;
> > +	case ETHER_FLOW:
> > +		fsp->h_u.ether_spec.h_proto = rule->eth_data.etype;
> > +		fsp->m_u.ether_spec.h_proto = rule->eth_mask.etype;
> > +		break;
> > +	default:
> > +		ret = -EINVAL;
> > +		break;
> > +	}
> > +
> > +	iecm_fill_rx_flow_ext_data(fsp, rule);
> > +
> > +	if (rule->action == VIRTCHNL_ACTION_DROP)
> > +		fsp->ring_cookie = RX_CLS_FLOW_DISC;
> > +	else
> > +		fsp->ring_cookie = rule->q_index;
> > +
> > +release_lock:
> > +	spin_unlock_bh(&adapter->fdir_fltr_list_lock);
> > +	return ret;
> > +}
> > +
> > +/**
> > + * iecm_get_fdir_fltr_ids - fill buffer with filter IDs of active filters
> > + * @vport: vport structure
> > + * @cmd: ethtool command data structure
> > + * @rule_locs: ethtool array passed in from OS to receive filter IDs
> > + *
> > + * Returns 0 as expected for success by ethtool
> > + */
> > +int
> > +iecm_get_fdir_fltr_ids(struct iecm_vport *vport, struct ethtool_rxnfc
> *cmd,
> > +		       u32 *rule_locs)
> > +{
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	struct iecm_fdir_fltr_config *fdir_config;
> > +	struct iecm_fdir_fltr *fltr;
> > +	unsigned int cnt = 0;
> > +	int ret = 0;
> > +
> > +	if (adapter->state != __IECM_UP)
> > +		return -EIO;
> > +
> > +	if (!iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
> VIRTCHNL2_CAP_FDIR))
> > +		return -EOPNOTSUPP;
> > +
> > +	cmd->data = IECM_MAX_FDIR_FILTERS;
> > +
> > +	fdir_config = &adapter->config_data.fdir_config;
> > +
> > +	spin_lock_bh(&adapter->fdir_fltr_list_lock);
> > +
> > +	list_for_each_entry(fltr, &fdir_config->fdir_fltr_list, list) {
> > +		if (cnt == cmd->rule_cnt) {
> > +			ret = -EMSGSIZE;
> > +			goto release_lock;
> > +		}
> > +		rule_locs[cnt] = fltr->loc;
> > +		cnt++;
> > +	}
> > +
> > +release_lock:
> > +	spin_unlock_bh(&adapter->fdir_fltr_list_lock);
> > +	if (!ret)
> > +		cmd->rule_cnt = cnt;
> > +
> > +	return ret;
> > +}
> > +
> > +/**
> > + * iecm_add_fdir_fltr_info - Set the input set for Flow Director filter
> > + * @vport: vport structure
> > + * @fsp: pointer to ethtool Rx flow specification
> > + * @fltr: filter structure
> > + */
> > +static int
> > +iecm_add_fdir_fltr_info(struct iecm_vport *vport,
> > +			struct ethtool_rx_flow_spec *fsp,
> > +			struct iecm_fdir_fltr *fltr)
> > +{
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	u32 flow_type, q_index = 0;
> > +	enum virtchnl_action act;
> > +	int err;
> > +
> > +	if (fsp->ring_cookie == RX_CLS_FLOW_DISC) {
> > +		act = VIRTCHNL_ACTION_DROP;
> > +	} else {
> > +		q_index = fsp->ring_cookie;
> > +		if (q_index >= vport->num_rxq)
> > +			return -EINVAL;
> > +
> > +		act = VIRTCHNL_ACTION_QUEUE;
> > +	}
> > +
> > +	fltr->action = act;
> > +	fltr->loc = fsp->location;
> > +	fltr->q_index = q_index;
> > +
> > +	if (fsp->flow_type & FLOW_EXT) {
> > +		memcpy(fltr->ext_data.usr_def, fsp->h_ext.data,
> > +		       sizeof(fltr->ext_data.usr_def));
> > +		memcpy(fltr->ext_mask.usr_def, fsp->m_ext.data,
> > +		       sizeof(fltr->ext_mask.usr_def));
> > +	}
> > +
> > +	flow_type = fsp->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT |
> FLOW_RSS);
> > +	fltr->flow_type = iecm_ethtool_flow_to_fltr(flow_type);
> > +
> > +	switch (flow_type) {
> > +	case TCP_V4_FLOW:
> > +	case UDP_V4_FLOW:
> > +	case SCTP_V4_FLOW:
> > +		fltr->ip_data.v4_addrs.src_ip = fsp-
> >h_u.tcp_ip4_spec.ip4src;
> > +		fltr->ip_data.v4_addrs.dst_ip = fsp-
> >h_u.tcp_ip4_spec.ip4dst;
> > +		fltr->ip_data.src_port = fsp->h_u.tcp_ip4_spec.psrc;
> > +		fltr->ip_data.dst_port = fsp->h_u.tcp_ip4_spec.pdst;
> > +		fltr->ip_data.tos = fsp->h_u.tcp_ip4_spec.tos;
> > +		fltr->ip_mask.v4_addrs.src_ip = fsp-
> >m_u.tcp_ip4_spec.ip4src;
> > +		fltr->ip_mask.v4_addrs.dst_ip = fsp-
> >m_u.tcp_ip4_spec.ip4dst;
> > +		fltr->ip_mask.src_port = fsp->m_u.tcp_ip4_spec.psrc;
> > +		fltr->ip_mask.dst_port = fsp->m_u.tcp_ip4_spec.pdst;
> > +		fltr->ip_mask.tos = fsp->m_u.tcp_ip4_spec.tos;
> > +		break;
> > +	case AH_V4_FLOW:
> > +	case ESP_V4_FLOW:
> > +		fltr->ip_data.v4_addrs.src_ip = fsp-
> >h_u.ah_ip4_spec.ip4src;
> > +		fltr->ip_data.v4_addrs.dst_ip = fsp-
> >h_u.ah_ip4_spec.ip4dst;
> > +		fltr->ip_data.spi = fsp->h_u.ah_ip4_spec.spi;
> > +		fltr->ip_data.tos = fsp->h_u.ah_ip4_spec.tos;
> > +		fltr->ip_mask.v4_addrs.src_ip = fsp-
> >m_u.ah_ip4_spec.ip4src;
> > +		fltr->ip_mask.v4_addrs.dst_ip = fsp-
> >m_u.ah_ip4_spec.ip4dst;
> > +		fltr->ip_mask.spi = fsp->m_u.ah_ip4_spec.spi;
> > +		fltr->ip_mask.tos = fsp->m_u.ah_ip4_spec.tos;
> > +		break;
> > +	case IPV4_USER_FLOW:
> > +		fltr->ip_data.v4_addrs.src_ip = fsp-
> >h_u.usr_ip4_spec.ip4src;
> > +		fltr->ip_data.v4_addrs.dst_ip = fsp-
> >h_u.usr_ip4_spec.ip4dst;
> > +		fltr->ip_data.l4_header = fsp-
> >h_u.usr_ip4_spec.l4_4_bytes;
> > +		fltr->ip_data.tos = fsp->h_u.usr_ip4_spec.tos;
> > +		fltr->ip_data.proto = fsp->h_u.usr_ip4_spec.proto;
> > +		fltr->ip_mask.v4_addrs.src_ip = fsp-
> >m_u.usr_ip4_spec.ip4src;
> > +		fltr->ip_mask.v4_addrs.dst_ip = fsp-
> >m_u.usr_ip4_spec.ip4dst;
> > +		fltr->ip_mask.l4_header = fsp-
> >m_u.usr_ip4_spec.l4_4_bytes;
> > +		fltr->ip_mask.tos = fsp->m_u.usr_ip4_spec.tos;
> > +		fltr->ip_mask.proto = fsp->m_u.usr_ip4_spec.proto;
> > +		break;
> > +	case TCP_V6_FLOW:
> > +	case UDP_V6_FLOW:
> > +	case SCTP_V6_FLOW:
> > +		memcpy(&fltr->ip_data.v6_addrs.src_ip,
> > +		       fsp->h_u.usr_ip6_spec.ip6src, sizeof(struct in6_addr));
> > +		memcpy(&fltr->ip_data.v6_addrs.dst_ip,
> > +		       fsp->h_u.usr_ip6_spec.ip6dst, sizeof(struct in6_addr));
> > +		fltr->ip_data.src_port = fsp->h_u.tcp_ip6_spec.psrc;
> > +		fltr->ip_data.dst_port = fsp->h_u.tcp_ip6_spec.pdst;
> > +		fltr->ip_data.tclass = fsp->h_u.tcp_ip6_spec.tclass;
> > +		memcpy(&fltr->ip_mask.v6_addrs.src_ip,
> > +		       fsp->m_u.usr_ip6_spec.ip6src, sizeof(struct in6_addr));
> > +		memcpy(&fltr->ip_mask.v6_addrs.dst_ip,
> > +		       fsp->m_u.usr_ip6_spec.ip6dst, sizeof(struct in6_addr));
> > +		fltr->ip_mask.src_port = fsp->m_u.tcp_ip6_spec.psrc;
> > +		fltr->ip_mask.dst_port = fsp->m_u.tcp_ip6_spec.pdst;
> > +		fltr->ip_mask.tclass = fsp->m_u.tcp_ip6_spec.tclass;
> > +		break;
> > +	case AH_V6_FLOW:
> > +	case ESP_V6_FLOW:
> > +		memcpy(&fltr->ip_data.v6_addrs.src_ip,
> > +		       fsp->h_u.ah_ip6_spec.ip6src, sizeof(struct in6_addr));
> > +		memcpy(&fltr->ip_data.v6_addrs.dst_ip,
> > +		       fsp->h_u.ah_ip6_spec.ip6dst, sizeof(struct in6_addr));
> > +		fltr->ip_data.spi = fsp->h_u.ah_ip6_spec.spi;
> > +		fltr->ip_data.tclass = fsp->h_u.ah_ip6_spec.tclass;
> > +		memcpy(&fltr->ip_mask.v6_addrs.src_ip,
> > +		       fsp->m_u.ah_ip6_spec.ip6src, sizeof(struct in6_addr));
> > +		memcpy(&fltr->ip_mask.v6_addrs.dst_ip,
> > +		       fsp->m_u.ah_ip6_spec.ip6dst, sizeof(struct in6_addr));
> > +		fltr->ip_mask.spi = fsp->m_u.ah_ip6_spec.spi;
> > +		fltr->ip_mask.tclass = fsp->m_u.ah_ip6_spec.tclass;
> > +		break;
> > +	case IPV6_USER_FLOW:
> > +		memcpy(&fltr->ip_data.v6_addrs.src_ip,
> > +		       fsp->h_u.usr_ip6_spec.ip6src, sizeof(struct in6_addr));
> > +		memcpy(&fltr->ip_data.v6_addrs.dst_ip,
> > +		       fsp->h_u.usr_ip6_spec.ip6dst, sizeof(struct in6_addr));
> > +		fltr->ip_data.l4_header = fsp-
> >h_u.usr_ip6_spec.l4_4_bytes;
> > +		fltr->ip_data.tclass = fsp->h_u.usr_ip6_spec.tclass;
> > +		fltr->ip_data.proto = fsp->h_u.usr_ip6_spec.l4_proto;
> > +		memcpy(&fltr->ip_mask.v6_addrs.src_ip,
> > +		       fsp->m_u.usr_ip6_spec.ip6src, sizeof(struct in6_addr));
> > +		memcpy(&fltr->ip_mask.v6_addrs.dst_ip,
> > +		       fsp->m_u.usr_ip6_spec.ip6dst, sizeof(struct in6_addr));
> > +		fltr->ip_mask.l4_header = fsp-
> >m_u.usr_ip6_spec.l4_4_bytes;
> > +		fltr->ip_mask.tclass = fsp->m_u.usr_ip6_spec.tclass;
> > +		fltr->ip_mask.proto = fsp->m_u.usr_ip6_spec.l4_proto;
> > +		break;
> > +	case ETHER_FLOW:
> > +		fltr->eth_data.etype = fsp->h_u.ether_spec.h_proto;
> > +		fltr->eth_mask.etype = fsp->m_u.ether_spec.h_proto;
> > +		break;
> > +	default:
> > +		/* not doing un-parsed flow types */
> > +		return -EINVAL;
> > +	}
> > +
> > +	if (iecm_fdir_is_dup_fltr(adapter, fltr))
> > +		return -EEXIST;
> > +
> > +	err = iecm_parse_rx_flow_user_data(fsp, fltr);
> > +	if (err)
> > +		return err;
> > +
> > +	return iecm_fill_fdir_add_msg(vport, fltr);
> > +}
> > +
> > +/**
> > + * iecm_add_fdir_fltr - add Flow Director filter
> > + * @vport: vport structure
> > + * @cmd: command to add Flow Director filter
> > + *
> > + * Returns 0 on success and negative values for failure
> > + */
> > +int iecm_add_fdir_fltr(struct iecm_vport *vport, struct ethtool_rxnfc
> *cmd)
> > +{
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	struct ethtool_rx_flow_spec *fsp = &cmd->fs;
> > +	struct iecm_fdir_fltr_config *fdir_config;
> > +	struct iecm_fdir_fltr *fltr;
> > +	int err;
> > +
> > +	if (adapter->state != __IECM_UP)
> > +		return -EIO;
> > +
> > +	if (!iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
> VIRTCHNL2_CAP_FDIR))
> > +		return -EOPNOTSUPP;
> > +
> > +	if (fsp->flow_type & FLOW_MAC_EXT)
> > +		return -EINVAL;
> > +
> > +	fdir_config = &adapter->config_data.fdir_config;
> > +	if (fdir_config->num_active_filters >= IECM_MAX_FDIR_FILTERS) {
> > +		dev_err(&adapter->pdev->dev,
> > +			"Unable to add Flow Director filter because vport
> reached the limit of max allowed filters (%u)\n",
> > +			IECM_MAX_FDIR_FILTERS);
> > +		return -ENOSPC;
> > +	}
> > +
> > +	spin_lock_bh(&adapter->fdir_fltr_list_lock);
> > +	fltr = iecm_find_fdir_fltr_by_loc(adapter, fsp->location);
> > +	if (fltr) {
> > +		fltr->remove = false;
> > +		dev_err(&adapter->pdev->dev, "Failed to add Flow Director
> filter, it already exists\n");
> > +		spin_unlock_bh(&adapter->fdir_fltr_list_lock);
> > +		return -EEXIST;
> > +	}
> > +	spin_unlock_bh(&adapter->fdir_fltr_list_lock);
> > +
> > +	fltr = kzalloc(sizeof(*fltr), GFP_KERNEL);
> > +	if (!fltr)
> > +		return -ENOMEM;
> > +
> > +	err = iecm_add_fdir_fltr_info(vport, fsp, fltr);
> > +	if (err)
> > +		goto error;
> > +
> > +	iecm_fdir_list_add_fltr(adapter, fltr);
> > +	err = iecm_send_add_fdir_filter_msg(vport);
> > +	if (!err) {
> > +		fdir_config->num_active_filters++;
> > +	} else {
> > +		spin_lock_bh(&adapter->fdir_fltr_list_lock);
> > +		list_del(&fltr->list);
> > +		spin_unlock_bh(&adapter->fdir_fltr_list_lock);
> > +	}
> > +
> > +error:
> > +	if (!err) {
> > +		dev_info(&adapter->pdev->dev, "Flow Director filter with
> location %u is added\n",
> > +			 fsp->location);
> > +	} else {
> > +		dev_info(&adapter->pdev->dev, "Failed to add Flow Director
> filter\n");
> > +		iecm_dump_fdir_fltr(vport, fltr);
> > +		kfree(fltr);
> > +	}
> > +
> > +	return err;
> > +}
> > +
> > +/**
> > + * iecm_del_fdir_fltr - delete Flow Director filter
> > + * @vport: vport structure
> > + * @cmd: command to delete Flow Director filter
> > + *
> > + * Returns 0 on success and negative values for failure
> > + */
> > +int iecm_del_fdir_fltr(struct iecm_vport *vport, struct ethtool_rxnfc
> *cmd)
> > +{
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	struct ethtool_rx_flow_spec *fsp = &cmd->fs;
> > +	struct iecm_fdir_fltr_config *fdir_config;
> > +	struct iecm_fdir_fltr *fltr = NULL;
> > +	int err;
> > +
> > +	if (adapter->state != __IECM_UP)
> > +		return -EIO;
> > +
> > +	if (!iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
> VIRTCHNL2_CAP_FDIR))
> > +		return -EOPNOTSUPP;
> > +
> > +	fdir_config = &adapter->config_data.fdir_config;
> > +	spin_lock_bh(&adapter->fdir_fltr_list_lock);
> > +	fltr = iecm_find_fdir_fltr_by_loc(adapter, fsp->location);
> > +	if (fltr) {
> > +		fltr->remove = true;
> > +		fdir_config->num_active_filters--;
> > +	}
> > +	spin_unlock_bh(&adapter->fdir_fltr_list_lock);
> > +
> > +	err = iecm_send_del_fdir_filter_msg(vport);
> > +	if (err)
> > +		dev_err(&adapter->pdev->dev, "Failed to del Flow Director
> filter\n");
> > +
> > +	/* If the above fails, still delete the filter from the list because
> > +	 * either HW thinks it doesn't exist or we have a bad filter somehow
> > +	 * and it doesn't do us any good to continue hanging on to it.
> > +	 */
> > +	spin_lock_bh(&adapter->fdir_fltr_list_lock);
> > +	fltr = iecm_find_fdir_fltr_by_loc(adapter, fsp->location);
> > +	/* It can happen that asynchronously the filter has already been
> > +	 * removed from the list, make sure it's still there under spinlock
> > +	 * before deleting it.
> > +	 */
> > +	if (fltr) {
> > +		list_del(&fltr->list);
> > +		kfree(fltr);
> > +	}
> > +	spin_unlock_bh(&adapter->fdir_fltr_list_lock);
> > +
> > +	return err;
> > +}
> > +
> >  /**
> >   * iecm_set_mac - NDO callback to set port mac address
> >   * @netdev: network interface device structure
> > diff --git a/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> > index f2516343c199..5601846b4674 100644
> > --- a/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> > +++ b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> > @@ -2731,6 +2731,125 @@ static int
> iecm_send_insert_vlan_msg(struct iecm_vport *vport, bool ena)
> >  	return err;
> >  }
> >
> > +/**
> > + * iecm_send_add_fdir_filter_msg: Send add Flow Director filter
> message
> > + * @vport: vport structure
> > + *
> > + * Request the CP/PF to add Flow Director as specified by the user via
> > + * ethtool
> > + *
> > + * Return 0 on success, negative on failure
> > + **/
> > +int iecm_send_add_fdir_filter_msg(struct iecm_vport *vport)
> > +{
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	struct iecm_fdir_fltr_config *fdir_config;
> > +	struct iecm_fdir_fltr *fdir;
> > +	struct virtchnl_fdir_add *f;
> > +	int len, err = 0;
> > +
> > +	fdir_config = &adapter->config_data.fdir_config;
> > +	len = sizeof(struct virtchnl_fdir_add);
> > +	/* kzalloc required because otherwise stack is over 2k */
> > +	f = kzalloc(len, GFP_KERNEL);
> > +	if (!f)
> > +		return -ENOMEM;
> > +
> > +	while (true) {
> > +		bool process_fltr = false;
> > +
> > +		/* Only add a single Flow Director per call */
> > +		spin_lock_bh(&adapter->fdir_fltr_list_lock);
> > +		list_for_each_entry(fdir, &fdir_config->fdir_fltr_list, list) {
> > +			if (fdir->add) {
> > +				fdir->add = false;
> > +				process_fltr = true;
> > +				memcpy(f, &fdir->vc_add_msg, len);
> > +				break;
> > +			}
> > +		}
> 
> 			if (!fdir->add)
> 				continue;
> 
> 			fdir->add = false;
> 			...
> 			break;
> 
> -1 level.
> 
> If you want to keep the condition that way, braces around `if` are
> redundant.
> 

Seems trivial but sure will fix.

> > +		spin_unlock_bh(&adapter->fdir_fltr_list_lock);
> > +
> > +		if (!process_fltr)
> > +			break;
> > +
> > +		err = iecm_send_mb_msg(adapter,
> VIRTCHNL_OP_ADD_FDIR_FILTER,
> > +				       len, (u8 *)f);
> > +		if (err)
> > +			break;
> > +
> > +		err = iecm_wait_for_event(adapter,
> IECM_VC_ADD_FDIR_FILTER,
> > +					  IECM_VC_ADD_FDIR_FILTER_ERR);
> > +		if (err)
> > +			break;
> > +
> > +		memcpy(f, adapter->vc_msg, len);
> > +		if (f->status == VIRTCHNL_FDIR_SUCCESS) {
> > +			fdir->flow_id = f->flow_id;
> > +		} else {
> > +			err = -EIO;
> > +			break;
> > +		}
> > +		clear_bit(__IECM_VC_MSG_PENDING, adapter->flags);
> > +	}
> > +
> > +	clear_bit(__IECM_VC_MSG_PENDING, adapter->flags);
> > +	kfree(f);
> > +	return err;
> > +}
> > +
> > +/**
> > + * iecm_send_del_fdir_filter_msg: Send del Flow Director filter message
> > + * @vport: vport structure
> > + *
> > + * Request the CP/PF to del Flow Director as specified by the user via
> > + * ethtool
> > + *
> > + * Return 0 on success, negative on failure
> > + **/
> > +int iecm_send_del_fdir_filter_msg(struct iecm_vport *vport)
> > +{
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	struct iecm_fdir_fltr_config *fdir_config;
> > +	struct iecm_fdir_fltr *fdir;
> > +	struct virtchnl_fdir_del f;
> > +	int err = 0;
> > +
> > +	fdir_config = &adapter->config_data.fdir_config;
> > +
> > +	while (true) {
> > +		bool process_fltr = false;
> > +
> > +		/* Only del a single Flow Director filter per call */
> > +		spin_lock_bh(&adapter->fdir_fltr_list_lock);
> > +		list_for_each_entry(fdir, &fdir_config->fdir_fltr_list, list) {
> > +			if (fdir->remove) {
> > +				process_fltr = true;
> > +				fdir->remove = false;
> > +				f.vsi_id = fdir->vc_add_msg.vsi_id;
> > +				f.flow_id = fdir->flow_id;
> > +				break;
> > +			}
> > +		}
> 
> Same here.
> 
> > +		spin_unlock_bh(&adapter->fdir_fltr_list_lock);
> > +
> > +		if (!process_fltr)
> > +			break;
> > +
> > +		err = iecm_send_mb_msg(adapter,
> VIRTCHNL_OP_DEL_FDIR_FILTER,
> > +				       sizeof(struct virtchnl_fdir_del), (u8
> *)&f);
> > +		if (err)
> > +			break;
> > +
> > +		err = iecm_wait_for_event(adapter,
> IECM_VC_DEL_FDIR_FILTER,
> > +					  IECM_VC_DEL_FDIR_FILTER_ERR);
> > +		clear_bit(__IECM_VC_MSG_PENDING, adapter->flags);
> > +	}
> > +
> > +	clear_bit(__IECM_VC_MSG_PENDING, adapter->flags);
> > +	return err;
> > +}
> > +
> >  /**
> >   * iecm_send_enable_channels_msg - Send enable channels message
> >   * @vport: vport structure
> > diff --git a/drivers/net/ethernet/intel/include/iecm.h
> b/drivers/net/ethernet/intel/include/iecm.h
> > index d118da1ea8cd..b0785684cc63 100644
> > --- a/drivers/net/ethernet/intel/include/iecm.h
> > +++ b/drivers/net/ethernet/intel/include/iecm.h
> > @@ -12,6 +12,7 @@
> >  #include <linux/netdevice.h>
> >  #include <linux/etherdevice.h>
> >  #include <linux/ethtool.h>
> > +#include <linux/l2tp.h>
> >  #include <net/tcp.h>
> >  #include <net/ip6_checksum.h>
> >  #include <net/ipv6.h>
> > @@ -409,6 +410,108 @@ struct iecm_channel_config {
> >  	u8 num_tc;
> >  };
> >
> > +enum iecm_fdir_flow_type {
> > +	/* NONE - used for undef/error */
> > +	IECM_FDIR_FLOW_NONE = 0,
> 
> Enums always start with 0 unless other value specified, this is a
> bit excessive.
> 

AFAIK this is the normal thing to do in Linux kernel.  In cases where the value actually matters, as is here, it's better to be explicit even though yes you're right it should be getting the default value of zero.

E.g. in kernel/sched/sched.h you can find:

/* The regions in numa_faults array from task_struct */
enum numa_faults_stats {
        NUMA_MEM = 0,
        NUMA_CPU,
        NUMA_MEMBUF,
        NUMA_CPUBUF
};

and probably many more.

> > +	IECM_FDIR_FLOW_IPV4_TCP,
> > +	IECM_FDIR_FLOW_IPV4_UDP,
> > +	IECM_FDIR_FLOW_IPV4_SCTP,
> > +	IECM_FDIR_FLOW_IPV4_AH,
> > +	IECM_FDIR_FLOW_IPV4_ESP,
> > +	IECM_FDIR_FLOW_IPV4_OTHER,
> > +	IECM_FDIR_FLOW_IPV6_TCP,
> > +	IECM_FDIR_FLOW_IPV6_UDP,
> > +	IECM_FDIR_FLOW_IPV6_SCTP,
> > +	IECM_FDIR_FLOW_IPV6_AH,
> > +	IECM_FDIR_FLOW_IPV6_ESP,
> > +	IECM_FDIR_FLOW_IPV6_OTHER,
> > +	IECM_FDIR_FLOW_NON_IP_L2,
> > +	/* MAX - this must be last and add anything new just above it */
> > +	IECM_FDIR_FLOW_PTYPE_MAX,
> > +};
> > +
> > +/* Must not exceed the array element number of '__be32 data[2]' in the
> ethtool
> > + * 'struct ethtool_rx_flow_spec.m_ext.data[2]' to express the flex-byte
> (word).
> > + */
> > +#define IECM_FLEX_WORD_NUM	2
> > +
> > +struct iecm_flex_word {
> > +	u16 offset;
> > +	u16 word;
> > +};
> > +
> > +struct iecm_ipv4_addrs {
> > +	__be32 src_ip;
> > +	__be32 dst_ip;
> > +};
> > +
> > +struct iecm_ipv6_addrs {
> > +	struct in6_addr src_ip;
> > +	struct in6_addr dst_ip;
> > +};
> > +
> > +struct iecm_fdir_eth {
> > +	__be16 etype;
> > +};
> > +
> > +struct iecm_fdir_ip {
> > +	union {
> > +		struct iecm_ipv4_addrs v4_addrs;
> > +		struct iecm_ipv6_addrs v6_addrs;
> > +	};
> > +	__be16 src_port;
> > +	__be16 dst_port;
> > +	__be32 l4_header;	/* first 4 bytes of the layer 4 header */
> > +	__be32 spi;		/* security parameter index for AH/ESP */
> > +	union {
> > +		u8 tos;
> > +		u8 tclass;
> > +	};
> > +	u8 proto;
> > +};
> > +
> > +struct iecm_fdir_extra {
> > +	u32 usr_def[IECM_FLEX_WORD_NUM];
> > +};
> > +
> > +/* bookkeeping of Flow Director filters */
> > +struct iecm_fdir_fltr {
> > +	struct list_head list;
> > +
> > +	enum iecm_fdir_flow_type flow_type;
> > +
> > +	struct iecm_fdir_eth eth_data;
> > +	struct iecm_fdir_eth eth_mask;
> > +
> > +	struct iecm_fdir_ip ip_data;
> > +	struct iecm_fdir_ip ip_mask;
> > +
> > +	struct iecm_fdir_extra ext_data;
> > +	struct iecm_fdir_extra ext_mask;
> > +
> > +	enum virtchnl_action action;
> > +
> > +	/* flex byte filter data */
> > +	u8 ip_ver; /* used to adjust the flex offset, 4 : IPv4, 6 : IPv6 */
> > +	u8 flex_cnt;
> > +	struct iecm_flex_word flex_words[IECM_FLEX_WORD_NUM];
> > +
> > +	u32 flow_id;
> > +
> > +	u32 loc;	/* Rule location inside the flow table */
> > +	u32 q_index;
> > +
> > +	struct virtchnl_fdir_add vc_add_msg;
> > +	bool remove;	/* Flow Director filter needs to be deleted */
> > +	bool add;	/* Flow Director filter needs to be added */
> 
> 	u8 remove:1;
> 	u8 add:1;
> 
> Booleans are discouraged to use in structs.
> 

Will fix.

> > +};
> > +
> > +struct iecm_fdir_fltr_config {
> > +	struct list_head fdir_fltr_list;
> > +#define IECM_MAX_FDIR_FILTERS	128	/* max allowed Flow Director
> filters */
> 
> Please place outside function definition.
> 

Sure

> > +	u16 num_active_filters;
> > +};
> > +
> >  #define IECM_GET_PTYPE_SIZE(p) \
> >  	(sizeof(struct virtchnl2_ptype) + \
> >  	(((p)->proto_id_count ? ((p)->proto_id_count - 1) : 0) * sizeof(u16)))
> > @@ -446,6 +549,7 @@ struct iecm_user_config_data {
> >  	struct list_head mac_filter_list;
> >  	struct list_head vlan_filter_list;
> >  	struct list_head adv_rss_list;
> > +	struct iecm_fdir_fltr_config fdir_config;
> >  	struct iecm_channel_config ch_config;
> >  };
> >
> > @@ -749,6 +853,14 @@ void iecm_set_ethtool_ops(struct net_device
> *netdev);
> >  void iecm_vport_set_hsplit(struct iecm_vport *vport, bool ena);
> >  void iecm_add_del_ether_addrs(struct iecm_vport *vport, bool add,
> bool async);
> >  int iecm_set_promiscuous(struct iecm_adapter *adapter);
> > +int iecm_send_add_fdir_filter_msg(struct iecm_vport *vport);
> > +int iecm_send_del_fdir_filter_msg(struct iecm_vport *vport);
> > +int iecm_get_fdir_fltr_entry(struct iecm_vport *vport,
> > +			     struct ethtool_rxnfc *cmd);
> > +int iecm_get_fdir_fltr_ids(struct iecm_vport *vport, struct ethtool_rxnfc
> *cmd,
> > +			   u32 *rule_locs);
> > +int iecm_add_fdir_fltr(struct iecm_vport *vport, struct ethtool_rxnfc
> *cmd);
> > +int iecm_del_fdir_fltr(struct iecm_vport *vport, struct ethtool_rxnfc
> *cmd);
> >  int iecm_send_enable_channels_msg(struct iecm_vport *vport);
> >  int iecm_send_disable_channels_msg(struct iecm_vport *vport);
> >  bool iecm_is_feature_ena(struct iecm_vport *vport, netdev_features_t
> feature);
> > --
> > 2.33.0
> 
> Thanks,
> Al

^ permalink raw reply	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 17/19] iecm: implement cloud filters
  2022-01-28 19:38   ` Alexander Lobakin
@ 2022-02-03  2:53     ` Brady, Alan
  0 siblings, 0 replies; 89+ messages in thread
From: Brady, Alan @ 2022-02-03  2:53 UTC (permalink / raw)
  To: intel-wired-lan

> -----Original Message-----
> From: Lobakin, Alexandr <alexandr.lobakin@intel.com>
> Sent: Friday, January 28, 2022 11:38 AM
> To: Brady, Alan <alan.brady@intel.com>
> Cc: Lobakin, Alexandr <alexandr.lobakin@intel.com>; intel-wired-
> lan at lists.osuosl.org; Burra, Phani R <phani.r.burra@intel.com>; Chittim,
> Madhu <madhu.chittim@intel.com>; Linga, Pavan Kumar
> <pavan.kumar.linga@intel.com>
> Subject: Re: [Intel-wired-lan] [PATCH net-next 17/19] iecm: implement
> cloud filters
> 
> From: Alan Brady <alan.brady@intel.com>
> Date: Thu, 27 Jan 2022 16:10:07 -0800
> 
> > This gives iecm the ability to deal with cloud filters and other
> > traffic classes.
> >
> > Signed-off-by: Phani Burra <phani.r.burra@intel.com>
> > Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
> > Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
> > Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
> > Signed-off-by: Alan Brady <alan.brady@intel.com>
> > ---
> >  drivers/net/ethernet/intel/iecm/iecm_lib.c    | 900
> +++++++++++++++++-
> >  .../net/ethernet/intel/iecm/iecm_virtchnl.c   |  68 ++
> >  drivers/net/ethernet/intel/include/iecm.h     |  25 +
> >  3 files changed, 992 insertions(+), 1 deletion(-)
> >
> > diff --git a/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > index 35c0cbc42ebe..d11413cb438c 100644
> > --- a/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > +++ b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > @@ -43,9 +43,16 @@ static int iecm_get_vport_index(struct
> iecm_adapter *adapter,
> >   */
> >  bool iecm_is_feature_ena(struct iecm_vport *vport, netdev_features_t
> > feature)  {
> > +	struct iecm_channel_config *ch_config;
> >  	bool ena;
> >
> >  	switch (feature) {
> > +	case NETIF_F_HW_TC:
> > +		ch_config = &vport->adapter->config_data.ch_config;
> > +		ena = (vport->netdev->features & feature) &&
> > +			(ch_config->num_tc > IECM_START_CHNL_TC) &&
> > +			(ch_config->tc_running);
> > +		break;
> >  	default:
> >  		ena = vport->netdev->features & feature;
> >  		break;
> > @@ -53,6 +60,23 @@ bool iecm_is_feature_ena(struct iecm_vport
> *vport, netdev_features_t feature)
> >  	return ena;
> >  }
> >
> > +/**
> > + * iecm_is_adq_v2_ena - Determine whether ADQ V2 is enabled
> > + * @vport: virtual port struct
> > + *
> > + * This function returns true based on negotiated capability ADQ_V2
> > + * if set and ADQ enabled
> > + */
> > +static bool iecm_is_adq_v2_ena(struct iecm_vport *vport) {
> > +	/* iecm_is_feature_ena tells if the netdev flag is set and adq is
> > +	 * enabled
> > +	 */
> > +	return (iecm_is_feature_ena(vport, NETIF_F_HW_TC) &&
> > +		iecm_is_cap_ena(vport->adapter, IECM_OTHER_CAPS,
> > +				VIRTCHNL2_CAP_ADQ));
> 
> The outermost braces are redundant.
> 

Will fix.

> > +}
> > +
> >  /**
> >   * iecm_is_vlan_cap_ena - Check if VLAN capability is enabled
> >   * @adapter: pointer to adapter
> > @@ -946,6 +970,28 @@ static int iecm_get_free_slot(void *array, int
> size, int curr)
> >  	return next;
> >  }
> >
> > +/**
> > + * iecm_remove_cloud_filters - Remove all cloud filters
> > + * @vport: vport structure
> > + */
> > +static void iecm_remove_cloud_filters(struct iecm_vport *vport) {
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	struct iecm_cloud_filter_config *cf_config;
> > +
> > +	cf_config = &adapter->config_data.cf_config;
> > +	if (!list_empty(&cf_config->cloud_filter_list)) {
> > +		struct iecm_cloud_filter *cf;
> > +
> > +		spin_lock_bh(&adapter->cloud_filter_list_lock);
> > +		list_for_each_entry(cf, &cf_config->cloud_filter_list, list) {
> > +			cf->remove = true;
> > +		}
> 
> One-liner, braces are redundant.
> 
> > +		spin_unlock_bh(&adapter->cloud_filter_list_lock);
> > +		iecm_send_add_del_cloud_filter_msg(vport, false);
> > +	}
> 
> 	if (list_empty())
> 		return;
> 
> -1 level.
> 
> > +}
> > +
> >  /**
> >   * iecm_remove_vlan_filters - Remove all vlan filters
> >   * @vport: vport structure
> > @@ -1044,8 +1090,14 @@ static void iecm_vport_stop(struct
> iecm_vport *vport)
> >  	if (test_and_clear_bit(__IECM_DEL_QUEUES,
> >  			       vport->adapter->flags))
> >  		iecm_send_delete_queues_msg(vport);
> > -	if (!test_bit(__IECM_REL_RES_IN_PROG, adapter->flags))
> > +	/* In function reset/rmmod path we call unregister_netdev which
> > +	 * internally calls delete cloud filters. We remove cloud filters only
> > +	 * when the interface goes down
> > +	 */
> > +	if (!test_bit(__IECM_REL_RES_IN_PROG, adapter->flags)) {
> > +		iecm_remove_cloud_filters(vport);
> >  		iecm_remove_vlan_filters(vport);
> > +	}
> >
> >  	iecm_remove_fdir_filters(vport);
> >
> > @@ -1258,6 +1310,28 @@ static void iecm_restore_vlans(struct
> iecm_vport *vport)
> >  		iecm_set_all_vlans(vport);
> >  }
> >
> > +/**
> > + * iecm_restore_cloud_filters - Restore cloud filters
> > + * @vport: vport structure
> > + */
> > +static void iecm_restore_cloud_filters(struct iecm_vport *vport) {
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	struct iecm_cloud_filter_config *cf_config;
> > +
> > +	cf_config = &adapter->config_data.cf_config;
> > +	if (!list_empty(&cf_config->cloud_filter_list)) {
> > +		struct iecm_cloud_filter *cf;
> > +
> > +		spin_lock_bh(&adapter->cloud_filter_list_lock);
> > +		list_for_each_entry(cf, &cf_config->cloud_filter_list, list) {
> > +			cf->add = true;
> > +		}
> 
> Same here for braces.
> 

Will fix.

> > +		spin_unlock_bh(&adapter->cloud_filter_list_lock);
> > +		iecm_send_add_del_cloud_filter_msg(vport, true);
> > +	}
> 
> Same here for reducing indent level.
> 
> > +}
> > +
> >  /**
> >   * iecm_restore_fdir_filters - Restore all Flow Director filters
> >   * @vport: vport structure
> > @@ -1302,6 +1376,10 @@ static void iecm_restore_features(struct
> iecm_vport *vport)
> >  			dev_info(&adapter->pdev->dev, "Failed to restore
> promiscuous settings\n");
> >  	}
> >
> > +	/* Restore cloud filters if ADQ is enabled */
> > +	if (iecm_is_feature_ena(vport, NETIF_F_HW_TC))
> > +		iecm_restore_cloud_filters(vport);
> > +
> >  	if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
> VIRTCHNL2_CAP_FDIR))
> >  		iecm_restore_fdir_filters(vport);
> >  }
> > @@ -2088,6 +2166,8 @@ int iecm_probe(struct pci_dev *pdev,
> >  	spin_lock_init(&adapter->vlan_list_lock);
> >  	spin_lock_init(&adapter->adv_rss_list_lock);
> >  	spin_lock_init(&adapter->fdir_fltr_list_lock);
> > +	INIT_LIST_HEAD(&adapter->config_data.cf_config.cloud_filter_list);
> > +	INIT_LIST_HEAD(&adapter->config_data.cf_config.block_cb_list);
> >  	INIT_LIST_HEAD(&adapter->config_data.mac_filter_list);
> >  	INIT_LIST_HEAD(&adapter->config_data.vlan_filter_list);
> >  	INIT_LIST_HEAD(&adapter->config_data.adv_rss_list);
> > @@ -2389,6 +2469,810 @@ static int iecm_offload_txtime(struct
> iecm_vport *vport,
> >  	return -EOPNOTSUPP;
> >  }
> >
> > +/**
> > + * iecm_is_vlan_tc_filter_allowed - allowed to add tc-filter using
> > +VLAN
> > + * @vport: vport structure
> > + * @vlan: VLAN to verify
> > + *
> > + * Using specified "vlan" ID, there must be active VLAN filter in
> > +VF's
> > + * MAC-VLAN filter list.
> > + */
> > +static bool
> > +iecm_is_vlan_tc_filter_allowed(struct iecm_vport *vport,
> > +			       struct iecm_vlan *vlan)
> > +{
> > +	struct iecm_vlan_filter *f;
> > +	bool allowed;
> > +
> > +	spin_lock_bh(&vport->adapter->vlan_list_lock);
> > +	f = iecm_find_vlan(vport, vlan);
> > +	allowed = (f && !f->add && !f->remove);
> 
> Redundant braces here.
> 

Will fix.

> > +	spin_unlock_bh(&vport->adapter->vlan_list_lock);
> > +	return allowed;
> > +}
> > +
> > +/**
> > + * iecm_is_mac_tc_filter_allowed - allowed to add tc-filter using MAC
> > +addr
> > + * @vport: vport structure
> > + * @macaddr: MAC address
> > + *
> > + * Using specified MAC address, there must be active MAC filter in
> > + * MAC filter list.
> > + */
> > +static bool
> > +iecm_is_mac_tc_filter_allowed(struct iecm_vport *vport, const u8
> > +*macaddr) {
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	struct iecm_mac_filter *f;
> > +	bool allowed;
> > +
> > +	spin_lock_bh(&adapter->mac_filter_list_lock);
> > +	f = iecm_find_mac_filter(vport, macaddr);
> > +	allowed = (f && !f->add && !f->remove);
> 
> Same here.
> 
> > +	spin_unlock_bh(&adapter->mac_filter_list_lock);
> > +	return allowed;
> > +}
> > +
> > +/**
> > + * iecm_parse_keyid - Parse keyid
> > + * @rule: Flow rule structure
> > + * @field_flags: Cloud filter flags
> > + */
> > +static void  iecm_parse_keyid(struct flow_rule *rule, u8
> > +*field_flags) {
> > +	struct flow_match_enc_keyid match;
> > +
> > +	flow_rule_match_enc_keyid(rule, &match);
> > +
> > +	if (match.mask->keyid != 0)
> > +		*field_flags |= IECM_CLOUD_FIELD_TEN_ID; }
> > +
> > +/**
> > + * iecm_parse_flow_type - Parse flow type based on L2 and L3
> > +protocols
> > + * @vport: vport structure
> > + * @rule: rule from user
> > + * @cf: Structure for the virtchnl filter
> > + * @filter: Structure for the cloud filter
> > + *
> > + * Return 0 on success, negative on failure  */ static int
> > +iecm_parse_flow_type(struct iecm_vport *vport,
> > +		     struct flow_rule *rule, struct virtchnl_filter *cf,
> > +		     struct iecm_cloud_filter *filter) {
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	enum virtchnl_flow_type flow_type;
> > +	struct flow_match_basic match;
> > +	u16 n_proto_mask = 0;
> > +	u16 n_proto_key = 0;
> > +	u16 n_proto = 0;
> > +	u8 ip_proto = 0;
> > +
> > +	flow_rule_match_basic(rule, &match);
> > +
> > +	n_proto_key = ntohs(match.key->n_proto);
> > +	n_proto_mask = ntohs(match.mask->n_proto);
> > +
> > +	if (n_proto_key == ETH_P_ALL) {
> > +		n_proto_key = 0;
> > +		n_proto_mask = 0;
> > +	}
> 
> 	n_proto_key = ntohs();
> 	if (n_proto_key != ETH_P_ALL)
> 		n_proto_mask = ntohs();
> 

Will fix

> > +	n_proto = n_proto_key & n_proto_mask;
> > +	if (n_proto != ETH_P_IP && n_proto != ETH_P_IPV6)
> > +		return -EINVAL;
> > +
> > +	if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
> VIRTCHNL2_CAP_ADQ)) {
> > +		if (match.key->ip_proto != IPPROTO_TCP &&
> > +		    match.key->ip_proto != IPPROTO_UDP) {
> > +			dev_err(&adapter->pdev->dev,
> > +				"Only TCP or UDP transport is
> supported\n");
> > +			return -EINVAL;
> > +		}
> > +	} else if (match.key->ip_proto != IPPROTO_TCP) {
> > +		dev_err(&adapter->pdev->dev,
> > +			"Only TCP transport is supported\n");
> > +		return -EINVAL;
> > +	}
> > +	ip_proto = match.key->ip_proto;
> > +
> > +	/* determine VIRTCHNL flow_type based on L3 and L4 protocol */
> > +	if (n_proto == ETH_P_IP)
> > +		flow_type = (ip_proto == IPPROTO_TCP) ?
> > +			     VIRTCHNL_TCP_V4_FLOW :
> > +			     VIRTCHNL_UDP_V4_FLOW;
> > +	else
> > +		flow_type = (ip_proto == IPPROTO_TCP) ?
> > +			     VIRTCHNL_TCP_V6_FLOW :
> > +			     VIRTCHNL_UDP_V6_FLOW;
> > +	cf->flow_type = flow_type;
> > +	filter->f.flow_type = flow_type;
> > +
> > +	return 0;
> > +}
> > +
> > +/**
> > + * iecm_parse_ether_header - Parse ethernet header fields
> > + * @vport: vport structure
> > + * @field_flags: Cloud filter flags
> > + * @d_spec: Virtchnl structure for L4 specs
> > + * @m_spec: Virtchnl structure for L4 specs
> > + * @rule: Flow rule structure
> > + *
> > + * Return 0 on success, negative on failure  */ static int
> > +iecm_parse_ether_header(struct iecm_vport *vport, u8 *field_flags,
> > +			struct virtchnl_l4_spec *d_spec,
> > +			struct virtchnl_l4_spec *m_spec,
> > +			struct flow_rule *rule)
> > +{
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	struct flow_match_eth_addrs match;
> > +	bool adv_adq_ena;
> > +
> > +	adv_adq_ena = iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
> > +				      VIRTCHNL2_CAP_ADQ);
> > +
> > +	flow_rule_match_eth_addrs(rule, &match);
> > +
> > +	/* use is_broadcast and is_zero to check for all 0xf or 0 */
> > +	if (!is_zero_ether_addr(match.mask->dst)) {
> > +		if (adv_adq_ena || is_broadcast_ether_addr(match.mask-
> >dst)) {
> > +			*field_flags |= IECM_CLOUD_FIELD_OMAC;
> > +		} else {
> > +			dev_err(&adapter->pdev->dev, "Bad ether dest
> mask %pM\n",
> > +				match.mask->dst);
> > +			return -EINVAL;
> > +		}
> > +	}
> > +
> > +	if (!is_zero_ether_addr(match.mask->src)) {
> > +		if (adv_adq_ena || is_broadcast_ether_addr(match.mask-
> >src)) {
> > +			*field_flags |= IECM_CLOUD_FIELD_IMAC;
> > +		} else {
> > +			dev_err(&adapter->pdev->dev, "Bad ether src mask
> %pM\n",
> > +				match.mask->src);
> > +			return -EINVAL;
> > +		}
> > +	}
> > +
> > +	if (!is_zero_ether_addr(match.key->dst)) {
> > +		if (!iecm_is_mac_tc_filter_allowed(adapter->vports[0],
> > +						   match.key->dst)) {
> > +			dev_err(&adapter->pdev->dev,
> > +				"Dest MAC %pM doesn't belong to this
> device\n",
> > +				match.key->dst);
> > +			return -EINVAL;
> > +		}
> > +
> > +		if (is_valid_ether_addr(match.key->dst) ||
> > +		    is_multicast_ether_addr(match.key->dst)) {
> > +			/* set the mask if a valid dst_mac address */
> > +			if (adv_adq_ena)
> > +				ether_addr_copy(m_spec->dst_mac,
> > +						match.mask->dst);
> > +			else
> > +				eth_broadcast_addr(m_spec->dst_mac);
> > +			ether_addr_copy(d_spec->dst_mac,
> > +					match.key->dst);
> > +		}
> > +	}
> > +
> > +	if (!is_zero_ether_addr(match.key->src))
> > +		if (is_valid_ether_addr(match.key->src) ||
> > +		    is_multicast_ether_addr(match.key->src)) {
> > +			/* set the mask if a valid src_mac address */
> > +			if (adv_adq_ena) {
> > +				ether_addr_copy(m_spec->src_mac,
> > +						match.mask->src);
> > +			} else {
> > +				eth_broadcast_addr(m_spec->src_mac);
> > +			}
> > +			ether_addr_copy(d_spec->src_mac,
> > +					match.key->src);
> > +		}
> 
> All previous ifs had braces, and it way okay since we have multiple nested if-
> elses, but here we don't have them.
> Please either add them here or remove all the way above to keep the code
> style consistent.
> 

Yes this needs braces will fix.

> > +	return 0;
> > +}
> > +
> > +/**
> > + * iecm_parse_vlan_header - Parse vlan header fields
> > + * @vport: vport structure
> > + * @field_flags: Cloud filter flags
> > + * @d_spec: Virtchnl structure for L4 specs
> > + * @m_spec: Virtchnl structure for L4 specs
> > + * @rule: Flow rule structure
> > + *
> > + * Return 0 on success, negative on failure  */ static int
> > +iecm_parse_vlan_header(struct iecm_vport *vport, u8 *field_flags,
> > +		       struct virtchnl_l4_spec *d_spec,
> > +		       struct virtchnl_l4_spec *m_spec,
> > +		       struct flow_rule *rule)
> > +{
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	struct flow_match_vlan match;
> > +
> > +	flow_rule_match_vlan(rule, &match);
> > +	if (match.mask->vlan_id) {
> > +		u16 vid = match.key->vlan_id & VLAN_VID_MASK;
> > +		struct iecm_vlan vlan;
> > +
> > +		vlan = IECM_VLAN(vid, ETH_P_8021Q);
> > +
> > +		if (match.mask->vlan_id != VLAN_VID_MASK) {
> > +			dev_err(&adapter->pdev->dev, "Bad vlan mask
> %u\n",
> > +				match.mask->vlan_id);
> > +			return -EINVAL;
> > +		}
> > +		if (!iecm_is_vlan_tc_filter_allowed(vport, &vlan)) {
> > +			dev_err(&adapter->pdev->dev,
> > +				"VLAN %u doesn't belong to this VF\n",
> > +				vid);
> > +			return -EINVAL;
> > +		}
> > +		*field_flags |= IECM_CLOUD_FIELD_IVLAN;
> > +		m_spec->vlan_id = cpu_to_be16(match.mask->vlan_id);
> > +		d_spec->vlan_id = cpu_to_be16(match.key->vlan_id);
> > +	}
> 
> 	if (!vlan_id)
> 		return 0;
> 
> -1 level.
> 

Will fix.

> > +	return 0;
> > +}
> > +
> > +/**
> > + * iecm_parse_ipv4_header - Parse ipv4 header fields
> > + * @vport: vport structure
> > + * @field_flags: Cloud filter flags
> > + * @d_spec: Virtchnl structure for L4 specs
> > + * @m_spec: Virtchnl structure for L4 specs
> > + * @rule: Flow rule structure
> > + *
> > + * Return 0 on success, negative on failure  */ static int
> > +iecm_parse_ipv4_header(struct iecm_vport *vport, u8 *field_flags,
> > +		       struct virtchnl_l4_spec *d_spec,
> > +		       struct virtchnl_l4_spec *m_spec,
> > +		       struct flow_rule *rule)
> > +{
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	struct flow_match_ipv4_addrs match;
> > +	bool adv_adq_ena;
> > +
> > +	adv_adq_ena = iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
> > +				      VIRTCHNL2_CAP_ADQ);
> > +
> > +	flow_rule_match_ipv4_addrs(rule, &match);
> > +
> > +	if (*field_flags & IECM_CLOUD_FIELD_TEN_ID) {
> > +		dev_info(&adapter->pdev->dev,
> > +			 "Tenant id not allowed for ip filter\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	if (match.mask->dst) {
> > +		if (adv_adq_ena || match.mask->dst ==
> cpu_to_be32(0xffffffff)) {
> > +			*field_flags |= IECM_CLOUD_FIELD_IIP;
> > +		} else {
> > +			dev_err(&adapter->pdev->dev, "Bad ip dst mask
> 0x%08x\n",
> > +				be32_to_cpu(match.mask->dst));
> > +			return -EINVAL;
> > +		}
> > +	}
> > +
> > +	if (match.mask->src) {
> > +		if (adv_adq_ena || match.mask->src ==
> cpu_to_be32(0xffffffff)) {
> > +			*field_flags |= IECM_CLOUD_FIELD_IIP;
> > +		} else {
> > +			dev_err(&adapter->pdev->dev, "Bad ip src mask
> 0x%08x\n",
> > +				be32_to_cpu(match.mask->dst));
> > +			return -EINVAL;
> > +		}
> > +	}
> > +
> > +	if (match.key->dst) {
> > +		if (adv_adq_ena)
> > +			m_spec->dst_ip[0] = match.mask->dst;
> > +		else
> > +			m_spec->dst_ip[0] = cpu_to_be32(0xffffffff);
> > +		d_spec->dst_ip[0] = match.key->dst;
> > +	}
> > +
> > +	if (match.key->src) {
> > +		if (adv_adq_ena)
> > +			m_spec->src_ip[0] = match.mask->src;
> > +		else
> > +			m_spec->src_ip[0] = cpu_to_be32(0xffffffff);
> > +		d_spec->src_ip[0] = match.key->src;
> > +	}
> > +	return 0;
> > +}
> > +
> > +/**
> > + * iecm_parse_ipv6_header - Parse ipv6 header fields
> > + * @vport: vport structure
> > + * @field_flags: Cloud filter flags
> > + * @d_spec: Virtchnl structure for L4 specs
> > + * @m_spec: Virtchnl structure for L4 specs
> > + * @rule: Flow rule structure
> > + *
> > + * Return 0 on success, negative on failure  */ static int
> > +iecm_parse_ipv6_header(struct iecm_vport *vport, u8 *field_flags,
> > +		       struct virtchnl_l4_spec *d_spec,
> > +		       struct virtchnl_l4_spec *m_spec,
> > +		       struct flow_rule *rule)
> > +{
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	struct flow_match_ipv6_addrs match;
> > +	int i;
> > +
> > +	flow_rule_match_ipv6_addrs(rule, &match);
> > +
> > +	/* validate mask, make sure it is not IPV6_ADDR_ANY */
> > +	if (ipv6_addr_any(&match.mask->dst)) {
> > +		dev_err(&adapter->pdev->dev, "Bad ipv6 dst mask
> 0x%02x\n",
> > +			IPV6_ADDR_ANY);
> > +		return -EINVAL;
> > +	}
> > +
> > +	/* src and dest IPv6 address should not be LOOPBACK
> > +	 * (0:0:0:0:0:0:0:1) which can be represented as ::1
> > +	 */
> > +	if (ipv6_addr_loopback(&match.key->dst) ||
> > +	    ipv6_addr_loopback(&match.key->src)) {
> > +		dev_err(&adapter->pdev->dev,
> > +			"ipv6 addr should not be loopback\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	if (!ipv6_addr_any(&match.mask->dst) ||
> > +	    !ipv6_addr_any(&match.mask->src))
> > +		*field_flags |= IECM_CLOUD_FIELD_IIP;
> > +
> > +	/* copy dest IPv6 mask and address */
> > +	if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
> VIRTCHNL2_CAP_ADQ)) {
> > +		memcpy(&m_spec->dst_ip, &match.mask->dst.s6_addr32,
> > +		       sizeof(m_spec->dst_ip));
> > +	} else {
> > +		for (i = 0; i < 4; i++)
> > +			m_spec->dst_ip[i] = cpu_to_be32(0xffffffff);
> > +	}
> 
> One-liners, no braces needed for both `if` and `else`.
> 

Will not fix

> > +	memcpy(&d_spec->dst_ip, &match.key->dst.s6_addr32,
> > +	       sizeof(d_spec->dst_ip));
> > +
> > +	/* copy source IPv6 mask and address */
> > +	if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
> VIRTCHNL2_CAP_ADQ)) {
> > +		memcpy(&m_spec->src_ip, &match.mask->src.s6_addr32,
> > +		       sizeof(m_spec->src_ip));
> > +	} else {
> > +		for (i = 0; i < 4; i++)
> > +			m_spec->src_ip[i] = cpu_to_be32(0xffffffff);
> > +	}
> 
> Same here.
> 

Will not fix.

> > +	memcpy(&d_spec->src_ip, &match.key->src.s6_addr32,
> > +	       sizeof(d_spec->src_ip));
> > +
> > +	return 0;
> > +}
> > +
> > +/**
> > + * iecm_parse_l4_header - Parse l4 header fields
> > + * @vport: vport structure
> > + * @d_spec: Virtchnl structure for L4 specs
> > + * @m_spec: Virtchnl structure for L4 specs
> > + * @rule: Flow rule structure
> > + *
> > + * Return 0 on success, negative on failure  */ static int
> > +iecm_parse_l4_header(struct iecm_vport *vport,
> > +		     struct virtchnl_l4_spec *d_spec,
> > +		     struct virtchnl_l4_spec *m_spec,
> > +		     struct flow_rule *rule)
> > +{
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	struct flow_match_ports match;
> > +
> > +	flow_rule_match_ports(rule, &match);
> > +
> > +	if (match.key->dst) {
> > +		if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
> > +				    VIRTCHNL2_CAP_ADQ) ||
> > +		    match.mask->dst == cpu_to_be16(0xffff)) {
> > +			m_spec->dst_port = match.mask->dst;
> > +			d_spec->dst_port = match.key->dst;
> > +		} else {
> > +			dev_err(&adapter->pdev->dev, "Bad dst port mask
> %u\n",
> > +				be16_to_cpu(match.mask->dst));
> > +			return -EINVAL;
> > +		}
> > +	}
> > +
> > +	if (match.key->src) {
> > +		if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
> > +				    VIRTCHNL2_CAP_ADQ) ||
> > +		    match.mask->src == cpu_to_be16(0xffff)) {
> > +			m_spec->src_port = match.mask->src;
> > +			d_spec->src_port = match.key->src;
> > +		} else {
> > +			dev_err(&adapter->pdev->dev, "Bad src port mask
> %u\n",
> > +				be16_to_cpu(match.mask->src));
> > +			return -EINVAL;
> > +		}
> > +	}
> > +	return 0;
> > +}
> > +
> > +/**
> > + * iecm_parse_cls_flower - Parse tc flower filters provided by kernel
> > + * @vport: vport structure
> > + * @f: pointer to struct flow_cls_offload
> > + * @filter: pointer to cloud filter structure  */ static int
> > +iecm_parse_cls_flower(struct iecm_vport *vport,
> > +				 struct flow_cls_offload *f,
> > +				 struct iecm_cloud_filter *filter) {
> > +	struct flow_rule *rule = flow_cls_offload_flow_rule(f);
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	struct virtchnl_l4_spec *d_spec, *m_spec;
> > +	struct virtchnl_filter *cf = &filter->f;
> > +	struct flow_dissector *dissector;
> > +	u8 field_flags = 0;
> > +	u16 addr_type = 0;
> > +	int err;
> > +
> > +	dissector = rule->match.dissector;
> > +	if (dissector->used_keys &
> > +	    ~(BIT(FLOW_DISSECTOR_KEY_CONTROL) |
> > +	      BIT(FLOW_DISSECTOR_KEY_BASIC) |
> > +	      BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS) |
> > +	      BIT(FLOW_DISSECTOR_KEY_VLAN) |
> > +	      BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS) |
> > +	      BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS) |
> > +	      BIT(FLOW_DISSECTOR_KEY_ENC_KEYID) |
> > +	      BIT(FLOW_DISSECTOR_KEY_PORTS))) {
> > +		dev_err(&adapter->pdev->dev, "Unsupported key used:
> 0x%x\n",
> > +			dissector->used_keys);
> > +		return -EOPNOTSUPP;
> > +	}
> > +	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ENC_KEYID))
> > +		iecm_parse_keyid(rule, &field_flags);
> > +
> > +	/* even though following code refers as "tcp_sec", it is not
> > +	 * just for TCP but a generic struct representing
> > +	 * L2, L3 + L4 fields if specified
> > +	 */
> > +	m_spec = &cf->mask.tcp_spec;
> > +	d_spec = &cf->data.tcp_spec;
> > +
> > +	/* determine flow type, TCP/UDP_V4[6]_FLOW based on
> > +	 * L2 proto (aka ETH proto) and L3 proto (aka IP_PROTO)
> > +	 */
> > +	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_BASIC)) {
> > +		err = iecm_parse_flow_type(vport, rule, cf, filter);
> > +		if (err)
> > +			return err;
> > +	}
> > +
> > +	/* process Ethernet header fields */
> > +	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_ETH_ADDRS))
> {
> > +		err = iecm_parse_ether_header(vport, &field_flags,
> > +					      d_spec, m_spec, rule);
> > +		if (err)
> > +			return err;
> > +	}
> > +
> > +	/* process VLAN header for single VLAN (type could be S/C-tag) */
> > +	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_VLAN)) {
> > +		err = iecm_parse_vlan_header(vport, &field_flags,
> > +					     d_spec, m_spec, rule);
> > +		if (err)
> > +			return err;
> > +	}
> > +
> > +	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_CONTROL)) {
> > +		struct flow_match_control match;
> > +
> > +		flow_rule_match_control(rule, &match);
> > +		addr_type = match.key->addr_type;
> > +	}
> > +
> > +	/* process IPv4 header */
> > +	if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) {
> > +		err = iecm_parse_ipv4_header(vport, &field_flags,
> > +					     d_spec, m_spec, rule);
> > +		if (err)
> > +			return err;
> > +	}
> > +
> > +	/* process IPv6 header */
> > +	if (addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) {
> > +		err = iecm_parse_ipv6_header(vport, &field_flags,
> > +					     d_spec, m_spec, rule);
> > +		if (err)
> > +			return err;
> > +	}
> > +
> > +	/* process L4 header, supported L4 protocols are TCP and UDP */
> > +	if (flow_rule_match_key(rule, FLOW_DISSECTOR_KEY_PORTS)) {
> > +		err = iecm_parse_l4_header(vport, d_spec, m_spec, rule);
> > +		if (err)
> > +			return err;
> > +	}
> > +	cf->field_flags = field_flags;
> > +
> > +	return 0;
> > +}
> > +
> > +/**
> > + * iecm_handle_tclass - Forward to a traffic class on the device
> > + * @vport: vport structure
> > + * @tc: traffic class index on the device
> > + * @filter: pointer to cloud filter structure
> > + *
> > + * Return 0 on success, negative on failure  */ static int
> > +iecm_handle_tclass(struct iecm_vport *vport, int tc,
> > +			      struct iecm_cloud_filter *filter) {
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +
> > +	if (tc == 0)
> > +		return 0;
> > +	if ((!iecm_is_adq_v2_ena(vport)) &&
> > +	    !filter->f.data.tcp_spec.dst_port) {
> > +		dev_err(&adapter->pdev->dev,
> > +			"Specify destination port to redirect to traffic class
> other than TC0\n");
> > +		return -EINVAL;
> > +	}
> > +	/* redirect to a traffic class on the same device */
> > +	filter->f.action = VIRTCHNL_ACTION_TC_REDIRECT;
> > +	filter->f.action_meta = tc;
> > +	return 0;
> > +}
> > +
> > +/* iecm_find_cf - Find the cloud filter in the list
> > + * @vport: vport structure
> > + * @cookie: filter specific cookie
> > + *
> > + * Returns pointer to the filter object or NULL. Must be called while
> > +holding
> > + * cloud_filter_list_lock.
> > + */
> > +static struct iecm_cloud_filter *iecm_find_cf(struct iecm_vport *vport,
> > +					      unsigned long *cookie)
> > +{
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	struct iecm_cloud_filter *filter = NULL;
> > +
> > +	if (!cookie)
> > +		return NULL;
> > +
> > +	list_for_each_entry(filter,
> > +			    &adapter->config_data.cf_config.cloud_filter_list,
> > +			    list) {
> > +		if (!memcmp(cookie, &filter->cookie, sizeof(filter->cookie)))
> > +			return filter;
> > +	}
> 
> Redundant braces round single statement.
> 

Will not fix.

> > +	return NULL;
> > +}
> > +
> > +/**
> > + * iecm_configure_clsflower - Add tc flower filters
> > + * @vport: vport structure
> > + * @cls_flower: Pointer to struct flow_cls_offload
> > + *
> > + * Return 0 on success, negative on failure  */ static int
> > +iecm_configure_clsflower(struct iecm_vport *vport,
> > +				    struct flow_cls_offload *cls_flower) {
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	struct iecm_user_config_data *config_data;
> > +	struct iecm_cloud_filter *filter = NULL;
> > +	int err;
> > +	int tc;
> > +
> > +	config_data = &adapter->config_data;
> > +	tc = tc_classid_to_hwtc(vport->netdev, cls_flower->classid);
> > +	if (tc < 0) {
> > +		dev_err(&adapter->pdev->dev, "Invalid traffic class\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +#define IECM_MAX_CLOUD_ADQ_FILTERS	128
> > +
> > +	if (config_data->cf_config.num_cloud_filters >=
> > +
> 	IECM_MAX_CLOUD_ADQ_FILTERS) {
> > +		dev_err(&adapter->pdev->dev,
> > +			"Unable to add filter (action is forward to TC),
> reached max allowed filters (%u)\n",
> > +			IECM_MAX_CLOUD_ADQ_FILTERS);
> > +		return -ENOSPC;
> > +	}
> > +
> > +	/* bail out here if filter already exists */
> > +	spin_lock_bh(&adapter->cloud_filter_list_lock);
> > +	filter = iecm_find_cf(vport, &cls_flower->cookie);
> > +	if (filter) {
> > +		filter->remove = false;
> > +		dev_err(&adapter->pdev->dev, "Failed to add TC Flower
> filter, it already exists\n");
> > +		spin_unlock_bh(&adapter->cloud_filter_list_lock);
> > +		return -EEXIST;
> > +	}
> > +	spin_unlock_bh(&adapter->cloud_filter_list_lock);
> > +
> > +	filter = kzalloc(sizeof(*filter), GFP_KERNEL);
> > +	if (!filter)
> > +		return -ENOMEM;
> > +
> > +	filter->cookie = cls_flower->cookie;
> > +
> > +	/* set the mask to all zeroes to begin with */
> > +	memset(&filter->f.mask.tcp_spec, 0, sizeof(struct
> > +virtchnl_l4_spec));
> > +
> > +	/* start out with flow type and eth type IPv4 to begin with */
> > +	filter->f.flow_type = VIRTCHNL_TCP_V4_FLOW;
> > +	err = iecm_parse_cls_flower(vport, cls_flower, filter);
> > +	if (err)
> > +		goto error;
> > +
> > +	err = iecm_handle_tclass(vport, tc, filter);
> > +	if (err)
> > +		goto error;
> > +
> > +	/* add filter to the list */
> > +	spin_lock_bh(&adapter->cloud_filter_list_lock);
> > +	list_add_tail(&filter->list, &config_data->cf_config.cloud_filter_list);
> > +	filter->add = true;
> > +	spin_unlock_bh(&adapter->cloud_filter_list_lock);
> > +	err = iecm_send_add_del_cloud_filter_msg(vport, true);
> > +	spin_lock_bh(&adapter->cloud_filter_list_lock);
> > +	/* We have to find it again in case another thread has already
> > +	 * deleted and kfreed it.
> > +	 */
> > +	filter = iecm_find_cf(vport, &cls_flower->cookie);
> > +	if (filter && err) {
> > +		list_del(&filter->list);
> > +		spin_unlock_bh(&adapter->cloud_filter_list_lock);
> > +		goto error;
> > +	}
> > +	spin_unlock_bh(&adapter->cloud_filter_list_lock);
> > +
> > +	config_data->cf_config.num_cloud_filters++;
> > +error:
> > +	if (err)
> > +		kfree(filter);
> > +	return err;
> > +}
> > +
> > +/**
> > + * iecm_delete_clsflower - Remove tc flower filters
> > + * @vport: vport structure
> > + * @cls_flower: Pointer to struct flow_cls_offload
> > + *
> > + * Return 0 on success, negative on failure  */ static int
> > +iecm_delete_clsflower(struct iecm_vport *vport,
> > +				 struct flow_cls_offload *cls_flower) {
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	struct iecm_cloud_filter *filter = NULL;
> > +	int err = 0;
> > +
> > +	spin_lock_bh(&adapter->cloud_filter_list_lock);
> > +	filter = iecm_find_cf(vport, &cls_flower->cookie);
> > +	if (filter) {
> > +		filter->remove = true;
> > +		adapter->config_data.cf_config.num_cloud_filters--;
> > +	} else if (adapter->config_data.cf_config.num_cloud_filters) {
> > +		/* "num_cloud_filters" can become zero if egress qdisc is
> > +		 * detached as per design, driver deletes related filters
> > +		 * when qdisc is detached to avoid stale filters, hence
> > +		 * num_cloud_filters can become zero. But since netdev
> > +		 * layer doesn't know that filters are deleted by driver
> > +		 * implictly when egress qdisc is deleted, it sees filters
> > +		 * being present and "in_hw". User can request delete
> > +		 * of specific filter of detach ingress qdisc - in either of
> > +		 * those operation, filter(s) won't be found in driver cache,
> > +		 * hence instead of returning, let this function return
> SUCCESS
> > +		 * Returning of err as -EINVAL is only applicable when
> > +		 * unable to find filter and num_cloud_filters is non-zero
> > +		 */
> > +		err = -EINVAL;
> > +	}
> > +	spin_unlock_bh(&adapter->cloud_filter_list_lock);
> > +
> > +	if (filter) {
> > +		err = iecm_send_add_del_cloud_filter_msg(vport, false);
> > +		spin_lock_bh(&adapter->cloud_filter_list_lock);
> > +		/* It can happen that asynchronously the filter was already
> > +		 * deleted from the list. Make sure it's still there and
> marked
> > +		 * for remove under spinlock before actually trying to delete
> > +		 * from list.
> > +		 */
> > +		filter = iecm_find_cf(vport, &cls_flower->cookie);
> > +		if (filter) {
> > +			list_del(&filter->list);
> > +			kfree(filter);
> > +		}
> > +		spin_unlock_bh(&adapter->cloud_filter_list_lock);
> > +	}
> 
> 	if (!filter)
> 		return err;
> 
> 	err = ...
> 
> -1 level.
> 

Will fix.

> > +	return err;
> > +}
> > +
> > +/**
> > + * iecm_setup_tc_cls_flower - flower classifier offloads
> > + * @vport: vport structure
> > + * @cls_flower: pointer to struct flow_cls_offload
> > + *
> > + * Return 0 on success, negative on failure  */ static int
> > +iecm_setup_tc_cls_flower(struct iecm_vport *vport,
> > +				    struct flow_cls_offload *cls_flower) {
> > +	if (cls_flower->common.chain_index)
> > +		return -EOPNOTSUPP;
> > +
> > +	switch (cls_flower->command) {
> > +	case FLOW_CLS_REPLACE:
> > +		return iecm_configure_clsflower(vport, cls_flower);
> > +	case FLOW_CLS_DESTROY:
> > +		return iecm_delete_clsflower(vport, cls_flower);
> > +	case FLOW_CLS_STATS:
> > +		return -EOPNOTSUPP;
> > +	default:
> > +		return -EINVAL;
> > +	}
> > +}
> > +
> > +/**
> > + * iecm_setup_tc_block_cb - block callback for tc
> > + * @type: type of offload
> > + * @type_data: offload data
> > + * @cb_priv: Private adapter structure
> > + *
> > + * This function is the block callback for traffic classes
> > + * Return 0 on success, negative on failure  **/ static int
> > +iecm_setup_tc_block_cb(enum tc_setup_type type, void *type_data,
> > +				  void *cb_priv)
> > +{
> > +	switch (type) {
> > +	case TC_SETUP_CLSFLOWER:
> > +		return iecm_setup_tc_cls_flower((struct iecm_vport
> *)cb_priv,
> > +						(struct flow_cls_offload *)
> > +						 type_data);
> 
> Just dereference them in separate variables and they will fit into one line.
> There shouldn't be any spaces or line breaks after a cast.
> 

Will take a look.

> > +	default:
> > +		return -EOPNOTSUPP;
> > +	}
> > +}
> > +
> > +/**
> > + * iecm_del_all_cloud_filters - delete all cloud filters on the
> > +traffic classes
> > + * @vport: vport structure
> > + *
> > + * This function will loop through the list of cloud filters and deletes
> them.
> > + **/
> > +static void iecm_del_all_cloud_filters(struct iecm_vport *vport) {
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	struct iecm_cloud_filter_config *cf_config;
> > +	struct iecm_cloud_filter *cf, *cftmp;
> > +
> > +	cf_config = &adapter->config_data.cf_config;
> > +	spin_lock_bh(&adapter->cloud_filter_list_lock);
> > +	list_for_each_entry_safe(cf, cftmp,
> > +				 &cf_config->cloud_filter_list,
> > +				 list) {
> > +		list_del(&cf->list);
> > +		kfree(cf);
> > +		cf_config->num_cloud_filters--;
> > +	}
> > +	spin_unlock_bh(&adapter->cloud_filter_list_lock);
> > +}
> > +
> >  /**
> >   * iecm_validate_tx_bandwidth - validate the max Tx bandwidth
> >   * @vport: vport structure
> > @@ -2596,6 +3480,7 @@ static int __iecm_setup_tc(struct iecm_vport
> *vport, void *type_data)
> >  			netif_tx_stop_all_queues(netdev);
> >  			netif_tx_disable(netdev);
> >  			ret = iecm_send_disable_channels_msg(vport);
> > +			iecm_del_all_cloud_filters(vport);
> >  			netif_tx_start_all_queues(netdev);
> >  			if (!test_bit(__IECM_REL_RES_IN_PROG, adapter-
> >flags) &&
> >  			    !ret) {
> > @@ -2709,8 +3594,10 @@ static int iecm_setup_tc(struct net_device
> > *netdev, enum tc_setup_type type,  {
> >  	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);
> >  	struct iecm_adapter *adapter = vport->adapter;
> > +	struct iecm_cloud_filter_config *cf_config;
> >  	int err = 0;
> >
> > +	cf_config = &adapter->config_data.cf_config;
> >  	switch (type) {
> >  	case TC_SETUP_QDISC_ETF:
> >  		if (iecm_is_queue_model_split(vport->txq_model))
> > @@ -2720,6 +3607,17 @@ static int iecm_setup_tc(struct net_device
> *netdev, enum tc_setup_type type,
> >  					     type_data);
> >  		break;
> >  	case TC_SETUP_BLOCK:
> > +		if (iecm_is_cap_ena(adapter, IECM_BASE_CAPS,
> > +				    VIRTCHNL2_CAP_ADQ) ||
> > +		    iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
> > +				    VIRTCHNL2_CAP_ADQ)) {
> > +			err =
> > +			flow_block_cb_setup_simple((struct
> flow_block_offload *)
> > +						    type_data,
> > +						   &cf_config->block_cb_list,
> > +						   iecm_setup_tc_block_cb,
> > +						   vport, vport, true);
> > +		}
> 
> Invert the condition, and there'll be no line wraps (esp. if you assign casted
> @type_data into a separate var).
> 

Will fix

> >  		break;
> >  	case TC_SETUP_QDISC_MQPRIO:
> >  		if (iecm_is_cap_ena(adapter, IECM_BASE_CAPS, diff --git
> > a/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> > b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> > index 5601846b4674..94af45c36866 100644
> > --- a/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> > +++ b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> > @@ -2731,6 +2731,74 @@ static int iecm_send_insert_vlan_msg(struct
> iecm_vport *vport, bool ena)
> >  	return err;
> >  }
> >
> > +/**
> > + * iecm_send_add_del_cloud_filter_msg: Send add/del cloud filter
> > +message
> > + * @vport: vport structure
> > + * @add: True to add, false to delete cloud filter
> > + *
> > + * Request the CP/PF to add/del cloud filters as specified by the
> > +user via
> > + * tc tool
> > + *
> > + * Return 0 on success, negative on failure  **/ int
> > +iecm_send_add_del_cloud_filter_msg(struct iecm_vport *vport, bool
> > +add) {
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	struct iecm_cloud_filter_config *cf_config;
> > +	struct iecm_cloud_filter *cf;
> > +	struct virtchnl_filter f;
> > +	int len = 0, err = 0;
> > +
> > +	cf_config = &adapter->config_data.cf_config;
> > +
> > +	while (true) {
> > +		bool process_fltr = false;
> > +
> > +		spin_lock_bh(&adapter->cloud_filter_list_lock);
> > +		list_for_each_entry(cf, &cf_config->cloud_filter_list, list) {
> > +			if (add && cf->add) {
> > +				process_fltr = true;
> > +				cf->add = false;
> > +				f = cf->f;
> > +				break;
> > +			} else if (!add && cf->remove) {
> > +				process_fltr = true;
> > +				cf->remove = false;
> > +				f = cf->f;
> > +				break;
> > +			}
> > +		}
> 
> Redundant braces.

Will not fix.

> 
> > +		spin_unlock_bh(&adapter->cloud_filter_list_lock);
> > +
> > +		/* Don't send mailbox message when there are no filters to
> add/del */
> > +		if (!process_fltr)
> > +			goto error;
> > +
> > +		if (add) {
> > +			err = iecm_send_mb_msg(adapter,
> VIRTCHNL_OP_ADD_CLOUD_FILTER,
> > +					       len, (u8 *)&f);
> > +			if (err)
> > +				goto error;
> > +
> > +			err = iecm_wait_for_event(adapter,
> IECM_VC_ADD_CLOUD_FILTER,
> > +
> IECM_VC_ADD_CLOUD_FILTER_ERR);
> > +		} else {
> > +			err = iecm_send_mb_msg(adapter,
> VIRTCHNL_OP_DEL_CLOUD_FILTER,
> > +					       len, (u8 *)&f);
> > +			if (err)
> > +				goto error;
> > +
> > +			err =
> > +			iecm_min_wait_for_event(adapter,
> IECM_VC_DEL_CLOUD_FILTER,
> > +
> 	IECM_VC_DEL_CLOUD_FILTER_ERR);
> 
> Too long lines here.
> 
> > +		}
> > +		if (err)
> > +			break;
> > +	}
> > +error:
> > +	return err;
> > +}
> > +
> >  /**
> >   * iecm_send_add_fdir_filter_msg: Send add Flow Director filter message
> >   * @vport: vport structure
> > diff --git a/drivers/net/ethernet/intel/include/iecm.h
> > b/drivers/net/ethernet/intel/include/iecm.h
> > index b0785684cc63..0aab41cf982c 100644
> > --- a/drivers/net/ethernet/intel/include/iecm.h
> > +++ b/drivers/net/ethernet/intel/include/iecm.h
> > @@ -403,6 +403,28 @@ enum iecm_user_flags {
> >  	__IECM_USER_FLAGS_NBITS,
> >  };
> >
> > +#define IECM_CLOUD_FIELD_OMAC		BIT(0)
> > +#define IECM_CLOUD_FIELD_IMAC		BIT(1)
> > +#define IECM_CLOUD_FIELD_IVLAN		BIT(2)
> > +#define IECM_CLOUD_FIELD_TEN_ID		BIT(3)
> > +#define IECM_CLOUD_FIELD_IIP		BIT(4)
> > +
> > +#define IECM_START_CHNL_TC		1
> > +
> > +struct iecm_cloud_filter {
> > +	struct list_head list;
> > +	struct virtchnl_filter f;
> > +	unsigned long cookie;
> > +	bool remove;		/* filter needs to be deleted */
> > +	bool add;		/* filter needs to be added */
> > +};
> > +
> > +struct iecm_cloud_filter_config {
> > +	struct list_head block_cb_list;		/* need to pass this to stack
> */
> > +	struct list_head cloud_filter_list;
> > +	u16 num_cloud_filters;
> > +};
> > +
> >  struct iecm_channel_config {
> >  	struct virtchnl_channel_info
> ch_info[VIRTCHNL_MAX_ADQ_V2_CHANNELS];
> >  	bool tc_running;
> > @@ -536,6 +558,7 @@ struct iecm_ptype_state {
> >  	bool outer_frag;
> >  	u8 tunnel_state;
> >  };
> > +
> 
> Please move this newline into the patch where iecm_ptype_state was
> introduced, it doesn't belong here.
> 

Will fix

> >  /* User defined configuration values */  struct iecm_user_config_data
> > {
> >  	u32 num_req_tx_qs; /* user requested TX queues through ethtool
> */ @@
> > -550,6 +573,7 @@ struct iecm_user_config_data {
> >  	struct list_head vlan_filter_list;
> >  	struct list_head adv_rss_list;
> >  	struct iecm_fdir_fltr_config fdir_config;
> > +	struct iecm_cloud_filter_config cf_config;
> >  	struct iecm_channel_config ch_config;  };
> >
> > @@ -853,6 +877,7 @@ void iecm_set_ethtool_ops(struct net_device
> > *netdev);  void iecm_vport_set_hsplit(struct iecm_vport *vport, bool
> > ena);  void iecm_add_del_ether_addrs(struct iecm_vport *vport, bool
> > add, bool async);  int iecm_set_promiscuous(struct iecm_adapter
> > *adapter);
> > +int iecm_send_add_del_cloud_filter_msg(struct iecm_vport *vport,
> bool
> > +add);
> >  int iecm_send_add_fdir_filter_msg(struct iecm_vport *vport);  int
> > iecm_send_del_fdir_filter_msg(struct iecm_vport *vport);  int
> > iecm_get_fdir_fltr_entry(struct iecm_vport *vport,
> > --
> > 2.33.0
> 
> Thanks,
> Al

^ permalink raw reply	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 18/19] iecm: add advanced rss
  2022-01-28 19:53   ` Alexander Lobakin
@ 2022-02-03  2:55     ` Brady, Alan
  2022-02-03 10:46       ` Maciej Fijalkowski
  2022-02-04 10:22       ` Alexander Lobakin
  0 siblings, 2 replies; 89+ messages in thread
From: Brady, Alan @ 2022-02-03  2:55 UTC (permalink / raw)
  To: intel-wired-lan

> -----Original Message-----
> From: Lobakin, Alexandr <alexandr.lobakin@intel.com>
> Sent: Friday, January 28, 2022 11:54 AM
> To: Brady, Alan <alan.brady@intel.com>
> Cc: Lobakin, Alexandr <alexandr.lobakin@intel.com>; Wang, Haiyue
> <haiyue.wang@intel.com>; intel-wired-lan at lists.osuosl.org; Burra, Phani R
> <phani.r.burra@intel.com>; Chittim, Madhu <madhu.chittim@intel.com>;
> Linga, Pavan Kumar <pavan.kumar.linga@intel.com>
> Subject: Re: [Intel-wired-lan] [PATCH net-next 18/19] iecm: add advanced
> rss
> 
> From: Alan Brady <alan.brady@intel.com>
> Date: Thu, 27 Jan 2022 16:10:08 -0800
> 
> > From: Haiyue Wang <haiyue.wang@intel.com>
> >
> > Continuing with advanced features this implements what's needed to do
> > advanced rss.
> 
> I'm sorry for not mentioned it before, but most of the series'
> commit messages are poor and would probably get rejected upstream.
> If they were explaining at least some very basics, it would be better. Even
> better if there were explanations of some tricky code that happens time to
> time.
> 
> >
> > Signed-off-by: Haiyue Wang <haiyue.wang@intel.com>
> > ---
> >  drivers/net/ethernet/intel/iecm/iecm_lib.c    | 547
> ++++++++++++++++++
> >  .../net/ethernet/intel/iecm/iecm_virtchnl.c   |  71 +++
> >  drivers/net/ethernet/intel/include/iecm.h     |  73 +++
> >  3 files changed, 691 insertions(+)
> >
> > diff --git a/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > index d11413cb438c..baa1e312652a 100644
> > --- a/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > +++ b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > @@ -1013,6 +1013,52 @@ static void iecm_remove_vlan_filters(struct
> iecm_vport *vport)
> >  	}
> >  }
> >
> > +/**
> > + * iecm_remove_adv_rss_cfgs - Remove all RSS configuration
> > + * @vport: vport structure
> > + */
> > +static void iecm_remove_adv_rss_cfgs(struct iecm_vport *vport) {
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +
> > +	if (!iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
> VIRTCHNL2_CAP_ADV_RSS))
> > +		return;
> > +
> > +	if (!list_empty(&adapter->config_data.adv_rss_list)) {
> > +		struct iecm_adv_rss *rss;
> > +
> > +		spin_lock_bh(&adapter->adv_rss_list_lock);
> > +		list_for_each_entry(rss, &adapter-
> >config_data.adv_rss_list,
> > +				    list) {
> > +			rss->remove = true;
> > +		}
> 
> Redundant braces arond an one-liner.
> 

Maybe will fix.

> > +		spin_unlock_bh(&adapter->adv_rss_list_lock);
> > +		iecm_send_add_del_adv_rss_cfg_msg(vport, false);
> > +	}
> 
> Invert the condition for -1 indent level.
> 

Will fix.

> > +}
> > +
> > +/**
> > + * iecm_del_all_adv_rss_cfgs - delete all RSS configuration
> > + * @vport: vport structure
> > + *
> > + * This function will loop through the list of RSS configuration and
> > +deletes
> > + * them.
> > + **/
> > +static void iecm_del_all_adv_rss_cfgs(struct iecm_vport *vport) {
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	struct iecm_adv_rss *rss, *rss_tmp;
> > +
> > +	spin_lock_bh(&adapter->adv_rss_list_lock);
> > +	list_for_each_entry_safe(rss, rss_tmp,
> > +				 &adapter->config_data.adv_rss_list,
> > +				 list) {
> > +		list_del(&rss->list);
> > +		kfree(rss);
> > +	}
> > +	spin_unlock_bh(&adapter->adv_rss_list_lock);
> > +}
> > +
> >  /**
> >   * iecm_remove_fdir_filters - Remove all Flow Director filters
> >   * @vport: vport structure
> > @@ -1099,6 +1145,7 @@ static void iecm_vport_stop(struct iecm_vport
> *vport)
> >  		iecm_remove_vlan_filters(vport);
> >  	}
> >
> > +	iecm_remove_adv_rss_cfgs(vport);
> >  	iecm_remove_fdir_filters(vport);
> >
> >  	adapter->link_up = false;
> > @@ -1332,6 +1379,27 @@ static void iecm_restore_cloud_filters(struct
> iecm_vport *vport)
> >  	}
> >  }
> >
> > +/**
> > + * iecm_restore_adv_rss_cfgs - Restore all RSS configuration
> > + * @vport: vport structure
> > + */
> > +static void iecm_restore_adv_rss_cfgs(struct iecm_vport *vport) {
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +
> > +	if (!list_empty(&adapter->config_data.adv_rss_list)) {
> > +		struct iecm_adv_rss *rss;
> > +
> > +		spin_lock_bh(&adapter->adv_rss_list_lock);
> > +		list_for_each_entry(rss, &adapter-
> >config_data.adv_rss_list,
> > +				    list) {
> > +			rss->add = true;
> > +		}
> 
> ...
> 
> > +		spin_unlock_bh(&adapter->adv_rss_list_lock);
> > +		iecm_send_add_del_adv_rss_cfg_msg(vport, true);
> > +	}
> 
> ...
> 
> > +}
> > +
> >  /**
> >   * iecm_restore_fdir_filters - Restore all Flow Director filters
> >   * @vport: vport structure
> > @@ -1380,6 +1448,9 @@ static void iecm_restore_features(struct
> iecm_vport *vport)
> >  	if (iecm_is_feature_ena(vport, NETIF_F_HW_TC))
> >  		iecm_restore_cloud_filters(vport);
> >
> > +	if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
> VIRTCHNL2_CAP_ADV_RSS))
> > +		iecm_restore_adv_rss_cfgs(vport);
> > +
> >  	if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
> VIRTCHNL2_CAP_FDIR))
> >  		iecm_restore_fdir_filters(vport);
> >  }
> > @@ -2219,6 +2290,7 @@ static void iecm_del_user_cfg_data(struct
> iecm_adapter *adapter)
> >  		if (!adapter->vports[i])
> >  			continue;
> >
> > +		iecm_del_all_adv_rss_cfgs(adapter->vports[i]);
> >  		iecm_del_all_fdir_filters(adapter->vports[i]);
> >  	}
> >  }
> > @@ -3633,6 +3705,481 @@ static int iecm_setup_tc(struct net_device
> *netdev, enum tc_setup_type type,
> >  	return err;
> >  }
> >
> > +/**
> > + * iecm_fill_adv_rss_ip4_hdr - fill the IPv4 RSS protocol header
> > + * @hdr: the virtchnl message protocol header data structure
> > + * @hash_flds: the RSS configuration protocol hash fields  */ static
> > +void iecm_fill_adv_rss_ip4_hdr(struct virtchnl_proto_hdr *hdr, u64
> > +hash_flds) {
> > +	VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, IPV4);
> > +
> > +	if (hash_flds & IECM_ADV_RSS_HASH_FLD_IPV4_SA)
> > +		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, IPV4, SRC);
> > +
> > +	if (hash_flds & IECM_ADV_RSS_HASH_FLD_IPV4_DA)
> > +		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, IPV4, DST); }
> > +
> > +/**
> > + * iecm_fill_adv_rss_ip6_hdr - fill the IPv6 RSS protocol header
> > + * @hdr: the virtchnl message protocol header data structure
> > + * @hash_flds: the RSS configuration protocol hash fields  */ static
> > +void iecm_fill_adv_rss_ip6_hdr(struct virtchnl_proto_hdr *hdr, u64
> > +hash_flds) {
> > +	VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, IPV6);
> > +
> > +	if (hash_flds & IECM_ADV_RSS_HASH_FLD_IPV6_SA)
> > +		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, IPV6, SRC);
> > +
> > +	if (hash_flds & IECM_ADV_RSS_HASH_FLD_IPV6_DA)
> > +		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, IPV6, DST); }
> > +
> > +/**
> > + * iecm_fill_adv_rss_tcp_hdr - fill the TCP RSS protocol header
> > + * @hdr: the virtchnl message protocol header data structure
> > + * @hash_flds: the RSS configuration protocol hash fields  */ static
> > +void iecm_fill_adv_rss_tcp_hdr(struct virtchnl_proto_hdr *hdr, u64
> > +hash_flds) {
> > +	VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, TCP);
> > +
> > +	if (hash_flds & IECM_ADV_RSS_HASH_FLD_TCP_SRC_PORT)
> > +		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, TCP,
> SRC_PORT);
> > +
> > +	if (hash_flds & IECM_ADV_RSS_HASH_FLD_TCP_DST_PORT)
> > +		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, TCP,
> DST_PORT); }
> > +
> > +/**
> > + * iecm_fill_adv_rss_udp_hdr - fill the UDP RSS protocol header
> > + * @hdr: the virtchnl message protocol header data structure
> > + * @hash_flds: the RSS configuration protocol hash fields  */ static
> > +void iecm_fill_adv_rss_udp_hdr(struct virtchnl_proto_hdr *hdr, u64
> > +hash_flds) {
> > +	VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, UDP);
> > +
> > +	if (hash_flds & IECM_ADV_RSS_HASH_FLD_UDP_SRC_PORT)
> > +		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, UDP,
> SRC_PORT);
> > +
> > +	if (hash_flds & IECM_ADV_RSS_HASH_FLD_UDP_DST_PORT)
> > +		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, UDP,
> DST_PORT); }
> > +
> > +/**
> > + * iecm_fill_adv_rss_sctp_hdr - fill the SCTP RSS protocol header
> > + * @hdr: the virtchnl message protocol header data structure
> > + * @hash_flds: the RSS configuration protocol hash fields  */ static
> > +void iecm_fill_adv_rss_sctp_hdr(struct virtchnl_proto_hdr *hdr, s64
> > +hash_flds) {
> > +	VIRTCHNL_SET_PROTO_HDR_TYPE(hdr, SCTP);
> > +
> > +	if (hash_flds & IECM_ADV_RSS_HASH_FLD_SCTP_SRC_PORT)
> > +		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, SCTP,
> SRC_PORT);
> > +
> > +	if (hash_flds & IECM_ADV_RSS_HASH_FLD_SCTP_DST_PORT)
> > +		VIRTCHNL_ADD_PROTO_HDR_FIELD_BIT(hdr, SCTP,
> DST_PORT); }
> > +
> > +/**
> > + * iecm_fill_adv_rss_cfg_msg - fill the RSS configuration into
> > +virtchnl message
> > + * @rss_cfg: the virtchnl message to be filled with RSS configuration
> > +setting
> > + * @packet_hdrs: the RSS configuration protocol header types
> > + * @hash_flds: the RSS configuration protocol hash fields
> > + *
> > + * Returns 0 if the RSS configuration virtchnl message is filled
> > +successfully  */ static int iecm_fill_adv_rss_cfg_msg(struct
> > +virtchnl_rss_cfg *rss_cfg,
> > +			  u32 packet_hdrs, u64 hash_flds)
> > +{
> > +	struct virtchnl_proto_hdrs *proto_hdrs = &rss_cfg->proto_hdrs;
> > +	struct virtchnl_proto_hdr *hdr;
> > +
> > +	rss_cfg->rss_algorithm =
> VIRTCHNL_RSS_ALG_TOEPLITZ_ASYMMETRIC;
> > +
> > +	proto_hdrs->tunnel_level = 0;	/* always outer layer */
> > +
> > +	hdr = &proto_hdrs->proto_hdr[proto_hdrs->count++];
> > +	switch (packet_hdrs & IECM_ADV_RSS_FLOW_SEG_HDR_L3) {
> > +	case IECM_ADV_RSS_FLOW_SEG_HDR_IPV4:
> > +		iecm_fill_adv_rss_ip4_hdr(hdr, hash_flds);
> > +		break;
> > +	case IECM_ADV_RSS_FLOW_SEG_HDR_IPV6:
> > +		iecm_fill_adv_rss_ip6_hdr(hdr, hash_flds);
> > +		break;
> > +	default:
> > +		return -EINVAL;
> > +	}
> > +
> > +	hdr = &proto_hdrs->proto_hdr[proto_hdrs->count++];
> > +	switch (packet_hdrs & IECM_ADV_RSS_FLOW_SEG_HDR_L4) {
> > +	case IECM_ADV_RSS_FLOW_SEG_HDR_TCP:
> > +		iecm_fill_adv_rss_tcp_hdr(hdr, hash_flds);
> > +		break;
> > +	case IECM_ADV_RSS_FLOW_SEG_HDR_UDP:
> > +		iecm_fill_adv_rss_udp_hdr(hdr, hash_flds);
> > +		break;
> > +	case IECM_ADV_RSS_FLOW_SEG_HDR_SCTP:
> > +		iecm_fill_adv_rss_sctp_hdr(hdr, hash_flds);
> > +		break;
> > +	default:
> > +		return -EINVAL;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +/**
> > + * iecm_find_adv_rss_cfg_by_hdrs - find RSS configuration with header
> > +type
> > + * @vport: vport structure
> > + * @packet_hdrs: protocol header type to find.
> > + *
> > + * Returns pointer to advance RSS configuration if found or null  */
> > +static struct iecm_adv_rss * iecm_find_adv_rss_cfg_by_hdrs(struct
> > +iecm_vport *vport, u32 packet_hdrs) {
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	struct iecm_adv_rss *rss;
> > +
> > +	list_for_each_entry(rss, &adapter->config_data.adv_rss_list, list)
> > +		if (rss->packet_hdrs == packet_hdrs)
> > +			return rss;
> > +
> > +	return NULL;
> > +}
> > +
> > +/**
> > + * iecm_dump_adv_rss_cfg_info
> > + * @vport: vport structure
> > + * @packet_hdrs: The protocol headers for RSS configuration
> > + * @hash_flds: The protocol hash fields for RSS configuration
> > + * @prefix: the prefix string description to dump the RSS
> > + * @postfix: the postfix string description to dump the RSS
> > + *
> > + * Dump the advance RSS configuration  **/ static void
> > +iecm_dump_adv_rss_cfg_info(struct iecm_vport *vport,
> > +			   u32 packet_hdrs, u64 hash_flds,
> > +			   const char *prefix, const char *postfix) {
> > +	static char hash_opt[300];
> 
> `static` places it into BSS. If multiple cores call this function at the same
> time, it will mess up.
> I'd just kzalloc() a buffer and then kfree() it.
> 

Hmm yes this is suspect, will fix.

> > +	const char *proto;
> > +
> > +	if (packet_hdrs & IECM_ADV_RSS_FLOW_SEG_HDR_TCP)
> > +		proto = "TCP";
> > +	else if (packet_hdrs & IECM_ADV_RSS_FLOW_SEG_HDR_UDP)
> > +		proto = "UDP";
> > +	else if (packet_hdrs & IECM_ADV_RSS_FLOW_SEG_HDR_SCTP)
> > +		proto = "SCTP";
> > +	else
> > +		return;
> > +
> > +	memset(hash_opt, 0, sizeof(hash_opt));
> > +
> > +	strcat(hash_opt, proto);
> 
> Consider using strlcat() please.
> 

Will check.

> > +	if (packet_hdrs & IECM_ADV_RSS_FLOW_SEG_HDR_IPV4)
> > +		strcat(hash_opt, "v4 ");
> > +	else
> > +		strcat(hash_opt, "v6 ");
> > +
> > +	if (hash_flds & (IECM_ADV_RSS_HASH_FLD_IPV4_SA |
> > +			 IECM_ADV_RSS_HASH_FLD_IPV6_SA))
> > +		strcat(hash_opt, "[IP SA] ");
> > +	if (hash_flds & (IECM_ADV_RSS_HASH_FLD_IPV4_DA |
> > +			 IECM_ADV_RSS_HASH_FLD_IPV6_DA))
> > +		strcat(hash_opt, "[IP DA] ");
> > +	if (hash_flds & (IECM_ADV_RSS_HASH_FLD_TCP_SRC_PORT |
> > +			 IECM_ADV_RSS_HASH_FLD_UDP_SRC_PORT |
> > +			 IECM_ADV_RSS_HASH_FLD_SCTP_SRC_PORT))
> > +		strcat(hash_opt, "[src port] ");
> > +	if (hash_flds & (IECM_ADV_RSS_HASH_FLD_TCP_DST_PORT |
> > +			 IECM_ADV_RSS_HASH_FLD_UDP_DST_PORT |
> > +			 IECM_ADV_RSS_HASH_FLD_SCTP_DST_PORT))
> > +		strcat(hash_opt, "[dst port] ");
> > +
> > +	if (!prefix)
> > +		prefix = "";
> > +
> > +	if (!postfix)
> > +		postfix = "";
> > +
> > +	dev_info(&vport->adapter->pdev->dev, "%s %s %s\n",
> > +		 prefix, hash_opt, postfix);
> > +}
> > +
> > +/**
> > + * iecm_adv_rss_parse_hdrs - parses headers from RSS hash input
> > + * @cmd: ethtool rxnfc command
> > + *
> > + * This function parses the rxnfc command and returns intended
> > + * header types for RSS configuration  */ static u32
> > +iecm_adv_rss_parse_hdrs(struct ethtool_rxnfc *cmd) {
> > +	u32 hdrs = IECM_ADV_RSS_FLOW_SEG_HDR_NONE;
> > +
> > +	switch (cmd->flow_type) {
> > +	case TCP_V4_FLOW:
> > +		hdrs |= IECM_ADV_RSS_FLOW_SEG_HDR_TCP |
> > +			IECM_ADV_RSS_FLOW_SEG_HDR_IPV4;
> > +		break;
> > +	case UDP_V4_FLOW:
> > +		hdrs |= IECM_ADV_RSS_FLOW_SEG_HDR_UDP |
> > +			IECM_ADV_RSS_FLOW_SEG_HDR_IPV4;
> > +		break;
> > +	case SCTP_V4_FLOW:
> > +		hdrs |= IECM_ADV_RSS_FLOW_SEG_HDR_SCTP |
> > +			IECM_ADV_RSS_FLOW_SEG_HDR_IPV4;
> > +		break;
> > +	case TCP_V6_FLOW:
> > +		hdrs |= IECM_ADV_RSS_FLOW_SEG_HDR_TCP |
> > +			IECM_ADV_RSS_FLOW_SEG_HDR_IPV6;
> > +		break;
> > +	case UDP_V6_FLOW:
> > +		hdrs |= IECM_ADV_RSS_FLOW_SEG_HDR_UDP |
> > +			IECM_ADV_RSS_FLOW_SEG_HDR_IPV6;
> > +		break;
> > +	case SCTP_V6_FLOW:
> > +		hdrs |= IECM_ADV_RSS_FLOW_SEG_HDR_SCTP |
> > +			IECM_ADV_RSS_FLOW_SEG_HDR_IPV6;
> > +		break;
> > +	default:
> > +		break;
> > +	}
> > +
> > +	return hdrs;
> > +}
> > +
> > +/**
> > + * iecm_adv_rss_parse_hash_flds - parses hash fields from RSS hash
> > +input
> > + * @cmd: ethtool rxnfc command
> > + *
> > + * This function parses the rxnfc command and returns intended hash
> > +fields for
> > + * RSS configuration
> > + */
> > +static u64 iecm_adv_rss_parse_hash_flds(struct ethtool_rxnfc *cmd) {
> > +	u64 hfld = IECM_ADV_RSS_HASH_INVALID;
> > +
> > +	if (cmd->data & RXH_IP_SRC || cmd->data & RXH_IP_DST) {
> 
> Braces are actually *needed* around bitops. So,
> 
> 	if ((cmd->data & RXH_IP_SRC) || (...)) {
> 

Will fix

> > +		switch (cmd->flow_type) {
> > +		case TCP_V4_FLOW:
> > +		case UDP_V4_FLOW:
> > +		case SCTP_V4_FLOW:
> > +			if (cmd->data & RXH_IP_SRC)
> > +				hfld |=
> IECM_ADV_RSS_HASH_FLD_IPV4_SA;
> > +			if (cmd->data & RXH_IP_DST)
> > +				hfld |=
> IECM_ADV_RSS_HASH_FLD_IPV4_DA;
> > +			break;
> > +		case TCP_V6_FLOW:
> > +		case UDP_V6_FLOW:
> > +		case SCTP_V6_FLOW:
> > +			if (cmd->data & RXH_IP_SRC)
> > +				hfld |=
> IECM_ADV_RSS_HASH_FLD_IPV6_SA;
> > +			if (cmd->data & RXH_IP_DST)
> > +				hfld |=
> IECM_ADV_RSS_HASH_FLD_IPV6_DA;
> > +			break;
> > +		default:
> > +			break;
> > +		}
> > +	}
> 
> 	if (!condition)
> 		goto here;
> 
> as well (-1 indent).
> 

Will fix.

> > +
> > +	if (cmd->data & RXH_L4_B_0_1 || cmd->data & RXH_L4_B_2_3) {
> > +		switch (cmd->flow_type) {
> > +		case TCP_V4_FLOW:
> > +		case TCP_V6_FLOW:
> > +			if (cmd->data & RXH_L4_B_0_1)
> > +				hfld |=
> IECM_ADV_RSS_HASH_FLD_TCP_SRC_PORT;
> > +			if (cmd->data & RXH_L4_B_2_3)
> > +				hfld |=
> IECM_ADV_RSS_HASH_FLD_TCP_DST_PORT;
> > +			break;
> > +		case UDP_V4_FLOW:
> > +		case UDP_V6_FLOW:
> > +			if (cmd->data & RXH_L4_B_0_1)
> > +				hfld |=
> IECM_ADV_RSS_HASH_FLD_UDP_SRC_PORT;
> > +			if (cmd->data & RXH_L4_B_2_3)
> > +				hfld |=
> IECM_ADV_RSS_HASH_FLD_UDP_DST_PORT;
> > +			break;
> > +		case SCTP_V4_FLOW:
> > +		case SCTP_V6_FLOW:
> > +			if (cmd->data & RXH_L4_B_0_1)
> > +				hfld |=
> IECM_ADV_RSS_HASH_FLD_SCTP_SRC_PORT;
> > +			if (cmd->data & RXH_L4_B_2_3)
> > +				hfld |=
> IECM_ADV_RSS_HASH_FLD_SCTP_DST_PORT;
> > +			break;
> > +		default:
> > +			break;
> > +		}
> > +	}
> > +
> > +	return hfld;
> > +}
> > +
> > +/**
> > + * iecm_set_adv_rss_hash_opt - Enable/Disable flow types for RSS hash
> > + * @vport: vport structure
> > + * @cmd: ethtool rxnfc command
> > + *
> > + * Returns Success if the flow input set is supported.
> > + */
> > +int
> > +iecm_set_adv_rss_hash_opt(struct iecm_vport *vport, struct
> > +ethtool_rxnfc *cmd) {
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	struct iecm_adv_rss *rss, *rss_new;
> > +	u64 hash_flds;
> > +	u32 hdrs;
> > +	int err;
> > +
> > +	if (adapter->state != __IECM_UP)
> > +		return -EIO;
> > +
> > +	if (!iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
> VIRTCHNL2_CAP_ADV_RSS))
> > +		return -EOPNOTSUPP;
> > +
> > +	hdrs = iecm_adv_rss_parse_hdrs(cmd);
> > +	if (hdrs == IECM_ADV_RSS_FLOW_SEG_HDR_NONE)
> > +		return -EINVAL;
> > +
> > +	hash_flds = iecm_adv_rss_parse_hash_flds(cmd);
> > +	if (hash_flds == IECM_ADV_RSS_HASH_INVALID)
> > +		return -EINVAL;
> > +
> > +	rss_new = kzalloc(sizeof(*rss_new), GFP_KERNEL);
> > +	if (!rss_new)
> > +		return -ENOMEM;
> > +
> > +	/* Since this can fail, do it now to avoid dirtying the list, we'll
> > +	 * copy it from rss_new if it turns out we're updating an existing
> > +	 * filter instead of adding a new one.
> > +	 */
> > +	if (iecm_fill_adv_rss_cfg_msg(&rss_new->cfg_msg, hdrs,
> hash_flds)) {
> > +		kfree(rss_new);
> > +		return -EINVAL;
> > +	}
> > +
> > +	iecm_dump_adv_rss_cfg_info(vport, hdrs, hash_flds,
> > +				   "Input set change for", "is pending");
> > +
> > +	spin_lock_bh(&adapter->adv_rss_list_lock);
> > +	rss = iecm_find_adv_rss_cfg_by_hdrs(vport, hdrs);
> > +	if (rss) {
> > +		if (rss->hash_flds != hash_flds) {
> > +			rss->remove = false;
> > +			memcpy(&rss->cfg_msg, &rss_new->cfg_msg,
> > +			       sizeof(rss_new->cfg_msg));
> > +			kfree(rss_new);
> > +		} else {
> > +			kfree(rss_new);
> > +			spin_unlock_bh(&adapter->adv_rss_list_lock);
> > +			return -EEXIST;
> > +		}
> > +	} else {
> > +		rss = rss_new;
> > +		rss->packet_hdrs = hdrs;
> > +		list_add_tail(&rss->list, &adapter-
> >config_data.adv_rss_list);
> > +	}
> > +	rss->add = true;
> > +	rss->hash_flds = hash_flds;
> > +	spin_unlock_bh(&adapter->adv_rss_list_lock);
> > +
> > +	err = iecm_send_add_del_adv_rss_cfg_msg(vport, true);
> > +	if (err) {
> > +		spin_lock_bh(&adapter->adv_rss_list_lock);
> > +		/* We have to find it again to make sure another thread
> hasn't
> > +		 * already deleted and kfreed it.
> > +		 */
> > +		rss = iecm_find_adv_rss_cfg_by_hdrs(vport, hdrs);
> > +		if (rss) {
> > +			list_del(&rss->list);
> > +			kfree(rss);
> > +		}
> > +		spin_unlock_bh(&adapter->adv_rss_list_lock);
> > +	}
> > +
> > +	if (!err)
> > +		iecm_dump_adv_rss_cfg_info(vport, hdrs, hash_flds,
> > +					   "Input set change for",
> > +					   "successful");
> > +	else
> > +		iecm_dump_adv_rss_cfg_info(vport, hdrs, hash_flds,
> > +					   "Failed to change the input set
> for",
> > +					   NULL);
> 
> It doesn't really look good. You could pass `err` or just `bool success` to the
> function itself and print those there.
> 

Fair.  Will fix.

> > +
> > +	return err;
> > +}
> > +
> > +/**
> > + * iecm_get_adv_rss_hash_opt - Retrieve hash fields for a given
> > +flow-type
> > + * @vport: vport structure
> > + * @cmd: ethtool rxnfc command
> > + *
> > + * Returns Success if the flow input set is supported.
> > + */
> > +int
> > +iecm_get_adv_rss_hash_opt(struct iecm_vport *vport, struct
> > +ethtool_rxnfc *cmd) {
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	struct iecm_adv_rss *rss;
> > +	u64 hash_flds;
> > +	u32 hdrs;
> > +
> > +	if (adapter->state != __IECM_UP)
> > +		return -EIO;
> > +
> > +	if (!iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
> VIRTCHNL2_CAP_ADV_RSS))
> > +		return -EOPNOTSUPP;
> > +
> > +	cmd->data = 0;
> > +
> > +	hdrs = iecm_adv_rss_parse_hdrs(cmd);
> > +	if (hdrs == IECM_ADV_RSS_FLOW_SEG_HDR_NONE)
> > +		return -EINVAL;
> > +
> > +	spin_lock_bh(&adapter->adv_rss_list_lock);
> > +	rss = iecm_find_adv_rss_cfg_by_hdrs(vport, hdrs);
> > +	if (rss)
> > +		hash_flds = rss->hash_flds;
> > +	else
> > +		hash_flds = IECM_ADV_RSS_HASH_INVALID;
> > +	spin_unlock_bh(&adapter->adv_rss_list_lock);
> > +
> > +	if (hash_flds == IECM_ADV_RSS_HASH_INVALID)
> > +		return -EINVAL;
> > +
> > +	if (hash_flds & (IECM_ADV_RSS_HASH_FLD_IPV4_SA |
> > +			 IECM_ADV_RSS_HASH_FLD_IPV6_SA))
> > +		cmd->data |= (u64)RXH_IP_SRC;
> > +
> > +	if (hash_flds & (IECM_ADV_RSS_HASH_FLD_IPV4_DA |
> > +			 IECM_ADV_RSS_HASH_FLD_IPV6_DA))
> > +		cmd->data |= (u64)RXH_IP_DST;
> > +
> > +	if (hash_flds & (IECM_ADV_RSS_HASH_FLD_TCP_SRC_PORT |
> > +			 IECM_ADV_RSS_HASH_FLD_UDP_SRC_PORT |
> > +			 IECM_ADV_RSS_HASH_FLD_SCTP_SRC_PORT))
> > +		cmd->data |= (u64)RXH_L4_B_0_1;
> > +
> > +	if (hash_flds & (IECM_ADV_RSS_HASH_FLD_TCP_DST_PORT |
> > +			 IECM_ADV_RSS_HASH_FLD_UDP_DST_PORT |
> > +			 IECM_ADV_RSS_HASH_FLD_SCTP_DST_PORT))
> > +		cmd->data |= (u64)RXH_L4_B_2_3;
> > +
> > +	return 0;
> > +}
> > +
> >  /**
> >   * iecm_pkt_udp_no_pay_len - the length of UDP packet without
> payload
> >   * @fltr: Flow Director filter data structure diff --git
> > a/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> > b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> > index 94af45c36866..c05baf12515c 100644
> > --- a/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> > +++ b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> > @@ -2799,6 +2799,77 @@ int
> iecm_send_add_del_cloud_filter_msg(struct iecm_vport *vport, bool add)
> >  	return err;
> >  }
> >
> > +/**
> > + * iecm_send_add_del_adv_rss_cfg_msg: Send add/del RSS
> configuration
> > +message
> > + * @vport: vport structure
> > + * @add: True to add, false to delete RSS configuration
> > + *
> > + * Request the CP/PF to add/del RSS configuration as specified by the
> > +user via
> > + * ethtool
> > + *
> > + * Return 0 on success, negative on failure  **/ int
> > +iecm_send_add_del_adv_rss_cfg_msg(struct iecm_vport *vport, bool
> add)
> > +{
> > +	struct iecm_adapter *adapter = vport->adapter;
> > +	struct virtchnl_rss_cfg *rss_cfg;
> > +	struct iecm_adv_rss *rss;
> > +	int len, err = -ENXIO;
> > +
> > +	len = sizeof(struct virtchnl_rss_cfg);
> > +	rss_cfg = kzalloc(len, GFP_KERNEL);
> > +	if (!rss_cfg)
> > +		return -ENOMEM;
> > +
> > +	while (true) {
> > +		bool process_rss = false;
> > +
> > +		spin_lock_bh(&adapter->adv_rss_list_lock);
> > +		list_for_each_entry(rss, &adapter-
> >config_data.adv_rss_list, list) {
> > +			if (add && rss->add) {
> > +				/* Only add needs print the RSS information
> */
> > +				process_rss = true;
> > +				rss->add = false;
> > +				memcpy(rss_cfg, &rss->cfg_msg, len);
> > +				break;
> > +			} else if (!add && rss->remove) {
> > +				process_rss = true;
> > +				rss->remove = false;
> > +				memcpy(rss_cfg, &rss->cfg_msg, len);
> > +				break;
> > +			}
> > +		}
> > +		spin_unlock_bh(&adapter->adv_rss_list_lock);
> > +
> > +		/* Don't send mailbox message when there are no RSS to
> add/del */
> > +		if (!process_rss)
> > +			break;
> > +
> > +		if (add) {
> > +			err = iecm_send_mb_msg(adapter,
> VIRTCHNL_OP_ADD_RSS_CFG,
> > +					       len, (u8 *)rss_cfg);
> > +			if (err)
> > +				break;
> > +
> > +			err = iecm_wait_for_event(adapter,
> IECM_VC_ADD_RSS_CFG,
> > +
> IECM_VC_ADD_RSS_CFG_ERR);
> > +		} else {
> > +			err = iecm_send_mb_msg(adapter,
> VIRTCHNL_OP_DEL_RSS_CFG,
> > +					       len, (u8 *)rss_cfg);
> > +			if (err)
> > +				break;
> > +
> > +			err = iecm_min_wait_for_event(adapter,
> IECM_VC_DEL_RSS_CFG,
> > +
> IECM_VC_DEL_RSS_CFG_ERR);
> > +		}
> > +		if (err)
> > +			break;
> > +	}
> > +
> > +	kfree(rss_cfg);
> > +	return err;
> > +}
> > +
> >  /**
> >   * iecm_send_add_fdir_filter_msg: Send add Flow Director filter message
> >   * @vport: vport structure
> > diff --git a/drivers/net/ethernet/intel/include/iecm.h
> > b/drivers/net/ethernet/intel/include/iecm.h
> > index 0aab41cf982c..c7be8c88f9b3 100644
> > --- a/drivers/net/ethernet/intel/include/iecm.h
> > +++ b/drivers/net/ethernet/intel/include/iecm.h
> > @@ -432,6 +432,74 @@ struct iecm_channel_config {
> >  	u8 num_tc;
> >  };
> >
> > +enum iecm_adv_rss_flow_seg_hdr {
> > +	IECM_ADV_RSS_FLOW_SEG_HDR_NONE	= 0x00000000,
> > +	IECM_ADV_RSS_FLOW_SEG_HDR_IPV4	= 0x00000001,
> > +	IECM_ADV_RSS_FLOW_SEG_HDR_IPV6	= 0x00000002,
> > +	IECM_ADV_RSS_FLOW_SEG_HDR_TCP	= 0x00000004,
> > +	IECM_ADV_RSS_FLOW_SEG_HDR_UDP	= 0x00000008,
> > +	IECM_ADV_RSS_FLOW_SEG_HDR_SCTP	= 0x00000010,
> > +};
> > +
> > +#define IECM_ADV_RSS_FLOW_SEG_HDR_L3		\
> > +	(IECM_ADV_RSS_FLOW_SEG_HDR_IPV4	|	\
> > +	 IECM_ADV_RSS_FLOW_SEG_HDR_IPV6)
> > +
> > +#define IECM_ADV_RSS_FLOW_SEG_HDR_L4		\
> > +	(IECM_ADV_RSS_FLOW_SEG_HDR_TCP |	\
> > +	 IECM_ADV_RSS_FLOW_SEG_HDR_UDP |	\
> > +	 IECM_ADV_RSS_FLOW_SEG_HDR_SCTP)
> > +
> > +enum iecm_adv_rss_flow_field {
> > +	/* L3 */
> > +	IECM_ADV_RSS_FLOW_FIELD_IDX_IPV4_SA,
> > +	IECM_ADV_RSS_FLOW_FIELD_IDX_IPV4_DA,
> > +	IECM_ADV_RSS_FLOW_FIELD_IDX_IPV6_SA,
> > +	IECM_ADV_RSS_FLOW_FIELD_IDX_IPV6_DA,
> > +	/* L4 */
> > +	IECM_ADV_RSS_FLOW_FIELD_IDX_TCP_SRC_PORT,
> > +	IECM_ADV_RSS_FLOW_FIELD_IDX_TCP_DST_PORT,
> > +	IECM_ADV_RSS_FLOW_FIELD_IDX_UDP_SRC_PORT,
> > +	IECM_ADV_RSS_FLOW_FIELD_IDX_UDP_DST_PORT,
> > +	IECM_ADV_RSS_FLOW_FIELD_IDX_SCTP_SRC_PORT,
> > +	IECM_ADV_RSS_FLOW_FIELD_IDX_SCTP_DST_PORT,
> > +
> > +	/* The total number of enums must not exceed 64 */
> > +	IECM_ADV_RSS_FLOW_FIELD_IDX_MAX
> > +};
> > +
> > +#define IECM_ADV_RSS_HASH_INVALID	0
> > +#define IECM_ADV_RSS_HASH_FLD_IPV4_SA	\
> > +	BIT_ULL(IECM_ADV_RSS_FLOW_FIELD_IDX_IPV4_SA)
> > +#define IECM_ADV_RSS_HASH_FLD_IPV6_SA	\
> > +	BIT_ULL(IECM_ADV_RSS_FLOW_FIELD_IDX_IPV6_SA)
> > +#define IECM_ADV_RSS_HASH_FLD_IPV4_DA	\
> > +	BIT_ULL(IECM_ADV_RSS_FLOW_FIELD_IDX_IPV4_DA)
> > +#define IECM_ADV_RSS_HASH_FLD_IPV6_DA	\
> > +	BIT_ULL(IECM_ADV_RSS_FLOW_FIELD_IDX_IPV6_DA)
> > +#define IECM_ADV_RSS_HASH_FLD_TCP_SRC_PORT	\
> > +	BIT_ULL(IECM_ADV_RSS_FLOW_FIELD_IDX_TCP_SRC_PORT)
> > +#define IECM_ADV_RSS_HASH_FLD_TCP_DST_PORT	\
> > +	BIT_ULL(IECM_ADV_RSS_FLOW_FIELD_IDX_TCP_DST_PORT)
> > +#define IECM_ADV_RSS_HASH_FLD_UDP_SRC_PORT	\
> > +	BIT_ULL(IECM_ADV_RSS_FLOW_FIELD_IDX_UDP_SRC_PORT)
> > +#define IECM_ADV_RSS_HASH_FLD_UDP_DST_PORT	\
> > +	BIT_ULL(IECM_ADV_RSS_FLOW_FIELD_IDX_UDP_DST_PORT)
> > +#define IECM_ADV_RSS_HASH_FLD_SCTP_SRC_PORT	\
> > +	BIT_ULL(IECM_ADV_RSS_FLOW_FIELD_IDX_SCTP_SRC_PORT)
> > +#define IECM_ADV_RSS_HASH_FLD_SCTP_DST_PORT	\
> > +	BIT_ULL(IECM_ADV_RSS_FLOW_FIELD_IDX_SCTP_DST_PORT)
> > +
> > +/* bookkeeping of advanced RSS configuration */ struct iecm_adv_rss {
> > +	struct list_head list;
> > +	u32 packet_hdrs;
> > +	u64 hash_flds;
> > +	struct virtchnl_rss_cfg cfg_msg;
> > +	bool remove;	/* RSS filter needs to be deleted */
> > +	bool add;	/* RSS filter needs to be added */
> > +};
> > +
> >  enum iecm_fdir_flow_type {
> >  	/* NONE - used for undef/error */
> >  	IECM_FDIR_FLOW_NONE = 0,
> > @@ -878,6 +946,11 @@ void iecm_vport_set_hsplit(struct iecm_vport
> > *vport, bool ena);  void iecm_add_del_ether_addrs(struct iecm_vport
> > *vport, bool add, bool async);  int iecm_set_promiscuous(struct
> > iecm_adapter *adapter);  int
> iecm_send_add_del_cloud_filter_msg(struct
> > iecm_vport *vport, bool add);
> > +int iecm_send_add_del_adv_rss_cfg_msg(struct iecm_vport *vport,
> bool
> > +add); int iecm_set_adv_rss_hash_opt(struct iecm_vport *vport,
> > +			      struct ethtool_rxnfc *cmd);
> > +int iecm_get_adv_rss_hash_opt(struct iecm_vport *vport,
> > +			      struct ethtool_rxnfc *cmd);
> >  int iecm_send_add_fdir_filter_msg(struct iecm_vport *vport);  int
> > iecm_send_del_fdir_filter_msg(struct iecm_vport *vport);  int
> > iecm_get_fdir_fltr_entry(struct iecm_vport *vport,
> > --
> > 2.33.0
> 
> Thanks,
> Al


^ permalink raw reply	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 19/19] idpf: introduce idpf driver
  2022-01-28 20:08   ` Alexander Lobakin
@ 2022-02-03  3:07     ` Brady, Alan
  2022-02-04 10:35       ` Alexander Lobakin
  0 siblings, 1 reply; 89+ messages in thread
From: Brady, Alan @ 2022-02-03  3:07 UTC (permalink / raw)
  To: intel-wired-lan

> -----Original Message-----
> From: Lobakin, Alexandr <alexandr.lobakin@intel.com>
> Sent: Friday, January 28, 2022 12:08 PM
> To: Brady, Alan <alan.brady@intel.com>
> Cc: Lobakin, Alexandr <alexandr.lobakin@intel.com>; intel-wired-
> lan at lists.osuosl.org; Burra, Phani R <phani.r.burra@intel.com>; Chittim,
> Madhu <madhu.chittim@intel.com>; Linga, Pavan Kumar
> <pavan.kumar.linga@intel.com>
> Subject: Re: [Intel-wired-lan] [PATCH net-next 19/19] idpf: introduce idpf
> driver
> 
> From: Alan Brady <alan.brady@intel.com>
> Date: Thu, 27 Jan 2022 16:10:09 -0800
> 
> > This adds the idpf driver which uses the iecm module to provide common
> > functionality. Device specific behavior and registers are defined here
> > and handed off to iecm which takes over the rest of the flow.
> 
> Ok I missed that before, so I say it now.
> Multi-function networking devices (Ethernet, SFs, VF representors, RDMA,
> storage offload etc.) nowadays kinda *must* be based on top of auxiliary
> bus. Otherwise, maintaining of hundreds a direct call with recursive
> dependencies between modules and stuff will become a burden.
> All of the mentioned functionality will be added to the driver(s), that's a
> fact, and as these are new drivers, it's way better to start off the right way
> now than to bug your mind on how to refactor this later.
> 

I suspect a refactor now will actually be more painful than later for other reasons and I believe we have other motivations for not using aux bus in this. It is however worth considering but we need some time to discuss. Will reply with something firm after some internal discussion.

> >
> > Signed-off-by: Phani Burra <phani.r.burra@intel.com>
> > Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
> > Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
> > Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
> > Signed-off-by: Alan Brady <alan.brady@intel.com>
> > ---
> >  .../device_drivers/ethernet/intel/idpf.rst    |  47 ++++++
> >  drivers/net/ethernet/intel/Kconfig            |  16 ++
> >  drivers/net/ethernet/intel/Makefile           |   1 +
> >  drivers/net/ethernet/intel/idpf/Makefile      |  15 ++
> >  drivers/net/ethernet/intel/idpf/idpf_dev.h    |  17 +++
> >  drivers/net/ethernet/intel/idpf/idpf_devids.h |  10 ++
> >  drivers/net/ethernet/intel/idpf/idpf_main.c   | 140
> ++++++++++++++++++
> >  drivers/net/ethernet/intel/idpf/idpf_reg.c    | 130 ++++++++++++++++
> >  .../ethernet/intel/include/iecm_lan_pf_regs.h | 131 ++++++++++++++++
> >  9 files changed, 507 insertions(+)
> >  create mode 100644
> > Documentation/networking/device_drivers/ethernet/intel/idpf.rst
> >  create mode 100644 drivers/net/ethernet/intel/idpf/Makefile
> >  create mode 100644 drivers/net/ethernet/intel/idpf/idpf_dev.h
> >  create mode 100644 drivers/net/ethernet/intel/idpf/idpf_devids.h
> >  create mode 100644 drivers/net/ethernet/intel/idpf/idpf_main.c
> >  create mode 100644 drivers/net/ethernet/intel/idpf/idpf_reg.c
> >  create mode 100644
> > drivers/net/ethernet/intel/include/iecm_lan_pf_regs.h
> >
> > diff --git
> > a/Documentation/networking/device_drivers/ethernet/intel/idpf.rst
> > b/Documentation/networking/device_drivers/ethernet/intel/idpf.rst
> > new file mode 100644
> > index 000000000000..973fa9613428
> > --- /dev/null
> > +++ b/Documentation/networking/device_drivers/ethernet/intel/idpf.rst
> > @@ -0,0 +1,47 @@
> > +.. SPDX-License-Identifier: GPL-2.0
> > +
> >
> +=========================================================
> =========
> > +Linux Base Driver for the Intel(R) Smart Network Adapter Family
> > +Series
> >
> +=========================================================
> =========
> > +
> > +Intel idpf Linux driver.
> > +Copyright(c) 2020 Intel Corporation.
> > +
> > +Contents
> > +========
> > +
> > +- Enabling the driver
> > +- Support
> > +
> > +The driver in this release supports Intel's Smart Network Adapter
> > +Family Series of products. For more information, visit Intel's
> > +support page at https://support.intel.com.
> > +
> > +Enabling the driver
> > +===================
> > +The driver is enabled via the standard kernel configuration system,
> > +using the make command::
> > +
> > +  make oldconfig/menuconfig/etc.
> > +
> > +The driver is located in the menu structure at:
> > +
> > +  -> Device Drivers
> > +    -> Network device support (NETDEVICES [=y])
> > +      -> Ethernet driver support
> > +        -> Intel devices
> > +          -> Intel(R) Smart Network Adapter Family Series Support
> > +
> > +Support
> > +=======
> > +For general information, go to the Intel support website at:
> > +
> > +https://www.intel.com/support/
> > +
> > +or the Intel Wired Networking project hosted by Sourceforge at:
> > +
> > +https://sourceforge.net/projects/e1000
> > +
> > +If an issue is identified with the released source code on a
> > +supported kernel with a supported adapter, email the specific
> > +information related to the issue to e1000-devel at lists.sf.net.
> > diff --git a/drivers/net/ethernet/intel/Kconfig
> > b/drivers/net/ethernet/intel/Kconfig
> > index 754dc7677ad5..93c8883c22ad 100644
> > --- a/drivers/net/ethernet/intel/Kconfig
> > +++ b/drivers/net/ethernet/intel/Kconfig
> > @@ -387,4 +387,20 @@ config IECM
> >        To compile this as a module, choose M here. The module will be called
> >        iecm.
> >
> > +config IDPF
> > +	tristate "Intel(R) Data Plane Function Support"
> > +	default n
> > +	depends on IECM
> > +	help
> > +	  For more information on how to identify your adapter, go
> > +	  to the Adapter & Driver ID Guide that can be located at:
> > +
> > +	  <http://support.intel.com>
> > +
> > +	  More specific information on configuring the driver is in
> > +
> <file:Documentation/networking/device_drivers/ethernet/intel/idpf.rst>.
> > +
> > +	  To compile this driver as a module, choose M here. The module
> > +	  will be called idpf.
> > +
> >  endif # NET_VENDOR_INTEL
> > diff --git a/drivers/net/ethernet/intel/Makefile
> > b/drivers/net/ethernet/intel/Makefile
> > index c9eba9cc5087..3786c2269f3d 100644
> > --- a/drivers/net/ethernet/intel/Makefile
> > +++ b/drivers/net/ethernet/intel/Makefile
> > @@ -17,3 +17,4 @@ obj-$(CONFIG_IAVF) += iavf/
> >  obj-$(CONFIG_FM10K) += fm10k/
> >  obj-$(CONFIG_ICE) += ice/
> >  obj-$(CONFIG_IECM) += iecm/
> > +obj-$(CONFIG_IDPF) += idpf/
> > diff --git a/drivers/net/ethernet/intel/idpf/Makefile
> > b/drivers/net/ethernet/intel/idpf/Makefile
> > new file mode 100644
> > index 000000000000..85846620bc9f
> > --- /dev/null
> > +++ b/drivers/net/ethernet/intel/idpf/Makefile
> > @@ -0,0 +1,15 @@
> > +# SPDX-License-Identifier: GPL-2.0-only # Copyright (C) 2019 Intel
> > +Corporation
> > +
> > +#
> > +# Makefile for the Intel(R) Data Plane Function Linux Driver #
> > +
> > +obj-$(CONFIG_IDPF) += idpf.o
> > +
> > +ccflags-y += -I$(srctree)/drivers/net/ethernet/intel/include \
> > +			 -I$(srctree)/include/linux/avf
> > +
> > +idpf-y := \
> > +	idpf_main.o \
> > +	idpf_reg.o
> > diff --git a/drivers/net/ethernet/intel/idpf/idpf_dev.h
> > b/drivers/net/ethernet/intel/idpf/idpf_dev.h
> > new file mode 100644
> > index 000000000000..dc146161f884
> > --- /dev/null
> > +++ b/drivers/net/ethernet/intel/idpf/idpf_dev.h
> > @@ -0,0 +1,17 @@
> > +/* SPDX-License-Identifier: GPL-2.0-only */
> > +/* Copyright (C) 2019 Intel Corporation */
> > +
> > +#ifndef _IDPF_DEV_H_
> > +#define _IDPF_DEV_H_
> > +
> > +#include "iecm.h"
> > +
> > +int idpf_intr_reg_init(struct iecm_vport *vport); void
> > +idpf_mb_intr_reg_init(struct iecm_adapter *adapter); void
> > +idpf_reset_reg_init(struct iecm_reset_reg *reset_reg); void
> > +idpf_trigger_reset(struct iecm_adapter *adapter,
> > +			enum iecm_flags trig_cause);
> > +void idpf_vportq_reg_init(struct iecm_vport *vport); void
> > +idpf_ctlq_reg_init(struct iecm_ctlq_create_info *cq);
> > +
> > +#endif /* _IDPF_DEV_H_ */
> > diff --git a/drivers/net/ethernet/intel/idpf/idpf_devids.h
> > b/drivers/net/ethernet/intel/idpf/idpf_devids.h
> > new file mode 100644
> > index 000000000000..7bf8eb64b76a
> > --- /dev/null
> > +++ b/drivers/net/ethernet/intel/idpf/idpf_devids.h
> > @@ -0,0 +1,10 @@
> > +/* SPDX-License-Identifier: GPL-2.0-only */
> > +/* Copyright (C) 2019 Intel Corporation */
> > +
> > +#ifndef _IDPF_DEVIDS_H_
> > +#define _IDPF_DEVIDS_H_
> > +
> > +/* Device IDs */
> > +#define IDPF_DEV_ID_PF			0x1452
> > +
> > +#endif /* _IDPF_DEVIDS_H_ */
> > diff --git a/drivers/net/ethernet/intel/idpf/idpf_main.c
> > b/drivers/net/ethernet/intel/idpf/idpf_main.c
> > new file mode 100644
> > index 000000000000..da5e668beabf
> > --- /dev/null
> > +++ b/drivers/net/ethernet/intel/idpf/idpf_main.c
> > @@ -0,0 +1,140 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +/* Copyright (C) 2019 Intel Corporation */
> > +
> > +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> > +
> > +#include "idpf_dev.h"
> > +#include "idpf_devids.h"
> > +
> > +#define DRV_SUMMARY	"Intel(R) Data Plane Function Linux Driver"
> > +static const char idpf_driver_string[] = DRV_SUMMARY; static const
> > +char idpf_copyright[] = "Copyright (c) 2020, Intel Corporation.";
> > +
> > +MODULE_DESCRIPTION(DRV_SUMMARY);
> > +MODULE_LICENSE("GPL");
> > +
> > +/**
> > + * idpf_reg_ops_init - Initialize register API function pointers
> > + * @adapter: Driver specific private structure  */ static void
> > +idpf_reg_ops_init(struct iecm_adapter *adapter) {
> > +	adapter->dev_ops.reg_ops.ctlq_reg_init = idpf_ctlq_reg_init;
> > +	adapter->dev_ops.reg_ops.intr_reg_init = idpf_intr_reg_init;
> > +	adapter->dev_ops.reg_ops.mb_intr_reg_init =
> idpf_mb_intr_reg_init;
> > +	adapter->dev_ops.reg_ops.reset_reg_init = idpf_reset_reg_init;
> > +	adapter->dev_ops.reg_ops.trigger_reset = idpf_trigger_reset;
> 
> Why not define static const reg_ops for idpf and assigning it?
> Filling plenty of callbacks inside functions are generally discouraged.
> 

Seems reasonable, will check.

> > +}
> > +
> > +/**
> > + * idpf_probe - Device initialization routine
> > + * @pdev: PCI device information struct
> > + * @ent: entry in idpf_pci_tbl
> > + *
> > + * Returns 0 on success, negative on failure  */ static int
> > +idpf_probe(struct pci_dev *pdev,
> > +		      const struct pci_device_id __always_unused *ent)
> 
> Again, no __always_unused for function parameters.
> 

Will not fix.

> > +{
> > +	struct iecm_adapter *adapter = NULL;
> > +	int err;
> > +
> > +	adapter = kzalloc(sizeof(*adapter), GFP_KERNEL);
> > +	if (!adapter)
> > +		return -ENOMEM;
> > +
> > +	adapter->dev_ops.reg_ops_init = idpf_reg_ops_init;
> 
> Why not just directly assign callbacks here? Callback for filling more
> callbacks?
> 

It was mostly following the pattern we had initially established for setting up vc_ops, which does have a good excuse to be callback to assign callbacks, but you're right I can't justify doing this for reg_ops, will fix.

> > +	set_bit(__IECM_REQ_TX_SPLITQ, adapter->flags);
> > +	set_bit(__IECM_REQ_RX_SPLITQ, adapter->flags);
> > +
> > +	err = iecm_probe(pdev, ent, adapter);
> > +	if (err)
> > +		kfree(adapter);
> > +
> > +	return err;
> > +}
> > +
> > +/**
> > + * idpf_remove - Device removal routine
> > + * @pdev: PCI device information struct  */ static void
> > +idpf_remove(struct pci_dev *pdev) {
> > +	struct iecm_adapter *adapter = pci_get_drvdata(pdev);
> > +
> > +	if (!adapter)
> > +		return;
> > +
> > +	iecm_remove(pdev);
> > +	pci_set_drvdata(pdev, NULL);
> > +	kfree(adapter);
> > +}
> > +
> > +/**
> > + * idpf_shutdown - PCI callback for shutting down device
> > + * @pdev: PCI device information struct  */ static void
> > +idpf_shutdown(struct pci_dev *pdev) {
> > +	idpf_remove(pdev);
> > +
> > +	if (system_state == SYSTEM_POWER_OFF)
> > +		pci_set_power_state(pdev, PCI_D3hot); }
> > +
> > +/* idpf_pci_tbl - PCI Dev iapf ID Table
> > + *
> > + * Wildcard entries (PCI_ANY_ID) should come last
> > + * Last entry must be all 0s
> > + *
> > + * { Vendor ID, Device ID, SubVendor ID, SubDevice ID,
> > + *   Class, Class Mask, private data (not used) }
> > + */
> > +static const struct pci_device_id idpf_pci_tbl[] = {
> > +	{ PCI_VDEVICE(INTEL, IDPF_DEV_ID_PF), 0 },
> > +	/* required last entry */
> > +	{ 0, }
> > +};
> > +MODULE_DEVICE_TABLE(pci, idpf_pci_tbl);
> > +
> > +static struct pci_driver idpf_driver = {
> > +	.name = KBUILD_MODNAME,
> > +	.id_table = idpf_pci_tbl,
> > +	.probe = idpf_probe,
> > +	.remove = idpf_remove,
> > +	.shutdown = idpf_shutdown,
> > +};
> > +
> > +/**
> > + * idpf_module_init - Driver registration routine
> > + *
> > + * idpf_module_init is the first routine called when the driver is
> > + * loaded. All it does is register with the PCI subsystem.
> > + */
> > +static int __init idpf_module_init(void) {
> > +	int status;
> > +
> > +	pr_info("%s - version %d\n", idpf_driver_string,
> LINUX_VERSION_CODE);
> > +	pr_info("%s\n", idpf_copyright);
> > +
> > +	status = pci_register_driver(&idpf_driver);
> > +	if (status)
> > +		pr_err("failed to register pci driver, err %d\n", status);
> > +
> > +	return status;
> > +}
> > +module_init(idpf_module_init);
> > +
> > +/**
> > + * idpf_module_exit - Driver exit cleanup routine
> > + *
> > + * idpf_module_exit is called just before the driver is removed
> > + * from memory.
> > + */
> > +static void __exit idpf_module_exit(void) {
> > +	pci_unregister_driver(&idpf_driver);
> > +	pr_info("module unloaded\n");
> > +}
> > +module_exit(idpf_module_exit);
> > diff --git a/drivers/net/ethernet/intel/idpf/idpf_reg.c
> > b/drivers/net/ethernet/intel/idpf/idpf_reg.c
> > new file mode 100644
> > index 000000000000..d0ea6c495c62
> > --- /dev/null
> > +++ b/drivers/net/ethernet/intel/idpf/idpf_reg.c
> > @@ -0,0 +1,130 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +/* Copyright (C) 2019 Intel Corporation */
> > +
> > +#include "idpf_dev.h"
> > +#include "iecm_lan_pf_regs.h"
> > +
> > +/**
> > + * idpf_ctlq_reg_init - initialize default mailbox registers
> > + * @cq: pointer to the array of create control queues  */ void
> > +idpf_ctlq_reg_init(struct iecm_ctlq_create_info *cq) {
> > +	int i;
> > +
> > +#define NUM_Q 2
> 
> I'd like to see it defined outside the function and with a bit of explanation of
> what that is.
> 

Sure will fix.

> > +	for (i = 0; i < NUM_Q; i++) {
> > +		struct iecm_ctlq_create_info *ccq = cq + i;
> > +
> > +		switch (ccq->type) {
> > +		case IECM_CTLQ_TYPE_MAILBOX_TX:
> > +			/* set head and tail registers in our local struct */
> > +			ccq->reg.head = PF_FW_ATQH;
> > +			ccq->reg.tail = PF_FW_ATQT;
> > +			ccq->reg.len = PF_FW_ATQLEN;
> > +			ccq->reg.bah = PF_FW_ATQBAH;
> > +			ccq->reg.bal = PF_FW_ATQBAL;
> > +			ccq->reg.len_mask = PF_FW_ATQLEN_ATQLEN_M;
> > +			ccq->reg.len_ena_mask =
> PF_FW_ATQLEN_ATQENABLE_M;
> > +			ccq->reg.head_mask = PF_FW_ATQH_ATQH_M;
> > +			break;
> > +		case IECM_CTLQ_TYPE_MAILBOX_RX:
> > +			/* set head and tail registers in our local struct */
> > +			ccq->reg.head = PF_FW_ARQH;
> > +			ccq->reg.tail = PF_FW_ARQT;
> > +			ccq->reg.len = PF_FW_ARQLEN;
> > +			ccq->reg.bah = PF_FW_ARQBAH;
> > +			ccq->reg.bal = PF_FW_ARQBAL;
> > +			ccq->reg.len_mask = PF_FW_ARQLEN_ARQLEN_M;
> > +			ccq->reg.len_ena_mask =
> PF_FW_ARQLEN_ARQENABLE_M;
> > +			ccq->reg.head_mask = PF_FW_ARQH_ARQH_M;
> > +			break;
> > +		default:
> > +			break;
> > +		}
> > +	}
> > +}
> > +
> > +/**
> > + * idpf_mb_intr_reg_init - Initialize mailbox interrupt register
> > + * @adapter: adapter structure
> > + */
> > +void idpf_mb_intr_reg_init(struct iecm_adapter *adapter) {
> > +	struct iecm_intr_reg *intr = &adapter->mb_vector.intr_reg;
> > +	struct virtchnl2_get_capabilities *caps;
> > +
> > +	caps = (struct virtchnl2_get_capabilities *)adapter->caps;
> > +	intr->dyn_ctl = le32_to_cpu(caps->mailbox_dyn_ctl);
> > +	intr->dyn_ctl_intena_m = PF_GLINT_DYN_CTL_INTENA_M;
> > +	intr->dyn_ctl_itridx_m = PF_GLINT_DYN_CTL_ITR_INDX_M;
> > +	intr->icr_ena = PF_INT_DIR_OICR_ENA;
> > +	intr->icr_ena_ctlq_m = PF_INT_DIR_OICR_ENA_M; }
> > +
> > +/**
> > + * idpf_intr_reg_init - Initialize interrupt registers
> > + * @vport: virtual port structure
> > + */
> > +int idpf_intr_reg_init(struct iecm_vport *vport) {
> > +	int num_vecs = vport->num_q_vectors;
> > +	struct iecm_vec_regs *reg_vals;
> > +	int num_regs, i, err = 0;
> > +
> > +	reg_vals = kmalloc(sizeof(void *) * IECM_LARGE_MAX_Q,
> > +			   GFP_KERNEL);
> 
> 	array_size(IECM_LARGE_MAX_Q, sizeof(void *));
> 
> > +	if (!reg_vals)
> > +		return -ENOMEM;
> > +
> > +	num_regs = iecm_get_reg_intr_vecs(vport, reg_vals, num_vecs);
> > +	if (num_regs != num_vecs) {
> > +		err = -EINVAL;
> > +		goto free_reg_vals;
> > +	}
> > +
> > +	for (i = 0; i < num_regs; i++) {
> > +		struct iecm_q_vector *q_vector = &vport->q_vectors[i];
> > +		struct iecm_intr_reg *intr = &q_vector->intr_reg;
> > +
> > +		intr->dyn_ctl = reg_vals[i].dyn_ctl_reg;
> > +		intr->dyn_ctl_clrpba_m =
> PF_GLINT_DYN_CTL_CLEARPBA_M;
> > +		intr->dyn_ctl_intena_m = PF_GLINT_DYN_CTL_INTENA_M;
> > +		intr->dyn_ctl_itridx_s = PF_GLINT_DYN_CTL_ITR_INDX_S;
> > +		intr->dyn_ctl_intrvl_s = PF_GLINT_DYN_CTL_INTERVAL_S;
> > +
> > +		intr->rx_itr = PF_GLINT_ITR_V2(VIRTCHNL2_ITR_IDX_0,
> > +					       reg_vals[i].itrn_reg);
> > +		intr->tx_itr = PF_GLINT_ITR_V2(VIRTCHNL2_ITR_IDX_1,
> > +					       reg_vals[i].itrn_reg);
> > +	}
> > +
> > +free_reg_vals:
> > +	kfree(reg_vals);
> > +	return err;
> > +}
> > +
> > +/**
> > + * idpf_reset_reg_init - Initialize reset registers
> > + * @reset_reg: struct to be filled in with reset registers  */ void
> > +idpf_reset_reg_init(struct iecm_reset_reg *reset_reg) {
> > +	reset_reg->rstat = PFGEN_RSTAT;
> > +	reset_reg->rstat_m = PFGEN_RSTAT_PFR_STATE_M; }
> > +
> > +/**
> > + * idpf_trigger_reset - trigger reset
> > + * @adapter: Driver specific private structure
> > + * @trig_cause: Reason to trigger a reset  */ void
> > +idpf_trigger_reset(struct iecm_adapter *adapter,
> > +			enum iecm_flags __always_unused trig_cause) {
> > +	u32 reset_reg;
> > +
> > +	reset_reg = rd32(&adapter->hw, PFGEN_CTRL);
> > +	wr32(&adapter->hw, PFGEN_CTRL, (reset_reg |
> PFGEN_CTRL_PFSWR)); }
> > +
> > diff --git a/drivers/net/ethernet/intel/include/iecm_lan_pf_regs.h
> > b/drivers/net/ethernet/intel/include/iecm_lan_pf_regs.h
> > new file mode 100644
> > index 000000000000..52ffe5c4a7ca
> > --- /dev/null
> > +++ b/drivers/net/ethernet/intel/include/iecm_lan_pf_regs.h
> > @@ -0,0 +1,131 @@
> > +/* SPDX-License-Identifier: GPL-2.0-only */
> > +/* Copyright (c) 2020, Intel Corporation. */
> > +
> > +#ifndef _IECM_LAN_PF_REGS_H_
> > +#define _IECM_LAN_PF_REGS_H_
> > +
> > +/* Receive queues */
> > +#define PF_QRX_BASE			0x00000000
> > +#define PF_QRX_TAIL(_QRX)		(PF_QRX_BASE + (((_QRX) *
> 0x1000)))
> > +#define PF_QRX_BUFFQ_BASE		0x03000000
> > +#define PF_QRX_BUFFQ_TAIL(_QRX)
> 	(PF_QRX_BUFFQ_BASE + (((_QRX) * 0x1000)))
> > +
> > +/* Transmit queues */
> > +#define PF_QTX_BASE			0x05000000
> > +#define PF_QTX_COMM_DBELL(_DBQM)	(PF_QTX_BASE + ((_DBQM) *
> 0x1000))
> > +
> > +/* Control(PF Mailbox) Queue */
> > +#define PF_FW_BASE			0x08400000
> > +
> > +#define PF_FW_ARQBAL			(PF_FW_BASE)
> > +#define PF_FW_ARQBAH			(PF_FW_BASE + 0x4)
> > +#define PF_FW_ARQLEN			(PF_FW_BASE + 0x8)
> > +#define PF_FW_ARQLEN_ARQLEN_S		0
> > +#define PF_FW_ARQLEN_ARQLEN_M		MAKEMASK(0x1FFF,
> PF_FW_ARQLEN_ARQLEN_S)
> > +#define PF_FW_ARQLEN_ARQVFE_S		28
> > +#define PF_FW_ARQLEN_ARQVFE_M
> 	BIT(PF_FW_ARQLEN_ARQVFE_S)
> > +#define PF_FW_ARQLEN_ARQOVFL_S		29
> > +#define PF_FW_ARQLEN_ARQOVFL_M
> 	BIT(PF_FW_ARQLEN_ARQOVFL_S)
> > +#define PF_FW_ARQLEN_ARQCRIT_S		30
> > +#define PF_FW_ARQLEN_ARQCRIT_M
> 	BIT(PF_FW_ARQLEN_ARQCRIT_S)
> > +#define PF_FW_ARQLEN_ARQENABLE_S	31
> > +#define PF_FW_ARQLEN_ARQENABLE_M
> 	BIT(PF_FW_ARQLEN_ARQENABLE_S)
> > +#define PF_FW_ARQH			(PF_FW_BASE + 0xC)
> > +#define PF_FW_ARQH_ARQH_S		0
> > +#define PF_FW_ARQH_ARQH_M		MAKEMASK(0x1FFF,
> PF_FW_ARQH_ARQH_S)
> > +#define PF_FW_ARQT			(PF_FW_BASE + 0x10)
> > +
> > +#define PF_FW_ATQBAL			(PF_FW_BASE + 0x14)
> > +#define PF_FW_ATQBAH			(PF_FW_BASE + 0x18)
> > +#define PF_FW_ATQLEN			(PF_FW_BASE + 0x1C)
> > +#define PF_FW_ATQLEN_ATQLEN_S		0
> > +#define PF_FW_ATQLEN_ATQLEN_M		MAKEMASK(0x3FF,
> PF_FW_ATQLEN_ATQLEN_S)
> > +#define PF_FW_ATQLEN_ATQVFE_S		28
> > +#define PF_FW_ATQLEN_ATQVFE_M
> 	BIT(PF_FW_ATQLEN_ATQVFE_S)
> > +#define PF_FW_ATQLEN_ATQOVFL_S		29
> > +#define PF_FW_ATQLEN_ATQOVFL_M
> 	BIT(PF_FW_ATQLEN_ATQOVFL_S)
> > +#define PF_FW_ATQLEN_ATQCRIT_S		30
> > +#define PF_FW_ATQLEN_ATQCRIT_M
> 	BIT(PF_FW_ATQLEN_ATQCRIT_S)
> > +#define PF_FW_ATQLEN_ATQENABLE_S	31
> > +#define PF_FW_ATQLEN_ATQENABLE_M
> 	BIT(PF_FW_ATQLEN_ATQENABLE_S)
> > +#define PF_FW_ATQH			(PF_FW_BASE + 0x20)
> > +#define PF_FW_ATQH_ATQH_S		0
> > +#define PF_FW_ATQH_ATQH_M		MAKEMASK(0x3FF,
> PF_FW_ATQH_ATQH_S)
> > +#define PF_FW_ATQT			(PF_FW_BASE + 0x24)
> > +
> > +/* Interrupts */
> > +#define PF_GLINT_BASE			0x08900000
> > +#define PF_GLINT_DYN_CTL(_INT)		(PF_GLINT_BASE + ((_INT) *
> 0x1000))
> > +#define PF_GLINT_DYN_CTL_INTENA_S	0
> > +#define PF_GLINT_DYN_CTL_INTENA_M
> 	BIT(PF_GLINT_DYN_CTL_INTENA_S)
> > +#define PF_GLINT_DYN_CTL_CLEARPBA_S	1
> > +#define PF_GLINT_DYN_CTL_CLEARPBA_M
> 	BIT(PF_GLINT_DYN_CTL_CLEARPBA_S)
> > +#define PF_GLINT_DYN_CTL_SWINT_TRIG_S	2
> > +#define PF_GLINT_DYN_CTL_SWINT_TRIG_M
> 	BIT(PF_GLINT_DYN_CTL_SWINT_TRIG_S)
> > +#define PF_GLINT_DYN_CTL_ITR_INDX_S	3
> > +#define PF_GLINT_DYN_CTL_ITR_INDX_M	MAKEMASK(0x3,
> PF_GLINT_DYN_CTL_ITR_INDX_S)
> > +#define PF_GLINT_DYN_CTL_INTERVAL_S	5
> > +#define PF_GLINT_DYN_CTL_INTERVAL_M
> 	BIT(PF_GLINT_DYN_CTL_INTERVAL_S)
> > +#define PF_GLINT_DYN_CTL_SW_ITR_INDX_ENA_S	24
> > +#define PF_GLINT_DYN_CTL_SW_ITR_INDX_ENA_M
> BIT(PF_GLINT_DYN_CTL_SW_ITR_INDX_ENA_S)
> > +#define PF_GLINT_DYN_CTL_SW_ITR_INDX_S	25
> > +#define PF_GLINT_DYN_CTL_SW_ITR_INDX_M
> 	BIT(PF_GLINT_DYN_CTL_SW_ITR_INDX_S)
> > +#define PF_GLINT_DYN_CTL_WB_ON_ITR_S	30
> > +#define PF_GLINT_DYN_CTL_WB_ON_ITR_M
> 	BIT(PF_GLINT_DYN_CTL_WB_ON_ITR_S)
> > +#define PF_GLINT_DYN_CTL_INTENA_MSK_S	31
> > +#define PF_GLINT_DYN_CTL_INTENA_MSK_M
> 	BIT(PF_GLINT_DYN_CTL_INTENA_MSK_S)
> > +#define PF_GLINT_ITR_V2(_i, _reg_start) (((_i) * 4) + (_reg_start))
> > +#define PF_GLINT_ITR(_i, _INT) (PF_GLINT_BASE + (((_i) + 1) * 4) +
> ((_INT) * 0x1000))
> > +#define PF_GLINT_ITR_MAX_INDEX		2
> > +#define PF_GLINT_ITR_INTERVAL_S		0
> > +#define PF_GLINT_ITR_INTERVAL_M		MAKEMASK(0xFFF,
> PF_GLINT_ITR_INTERVAL_S)
> > +
> > +/* Timesync registers */
> > +#define PF_TIMESYNC_BASE		0x08404000
> > +#define PF_GLTSYN_CMD_SYNC		(PF_TIMESYNC_BASE)
> > +#define PF_GLTSYN_CMD_SYNC_EXEC_CMD_S	0
> > +#define PF_GLTSYN_CMD_SYNC_EXEC_CMD_M	MAKEMASK(0x3,
> PF_GLTSYN_CMD_SYNC_EXEC_CMD_S)
> > +#define PF_GLTSYN_CMD_SYNC_SHTIME_EN_S	2
> > +#define PF_GLTSYN_CMD_SYNC_SHTIME_EN_M
> 	BIT(PF_GLTSYN_CMD_SYNC_SHTIME_EN_S)
> > +#define PF_GLTSYN_SHTIME_0		(PF_TIMESYNC_BASE + 0x4)
> > +#define PF_GLTSYN_SHTIME_L		(PF_TIMESYNC_BASE + 0x8)
> > +#define PF_GLTSYN_SHTIME_H		(PF_TIMESYNC_BASE + 0xC)
> > +#define PF_GLTSYN_ART_L			(PF_TIMESYNC_BASE + 0x10)
> > +#define PF_GLTSYN_ART_H			(PF_TIMESYNC_BASE + 0x14)
> > +
> > +/* Generic registers */
> > +#define PF_INT_DIR_OICR_ENA		0x08406000
> > +#define PF_INT_DIR_OICR_ENA_S		0
> > +#define PF_INT_DIR_OICR_ENA_M	MAKEMASK(0xFFFFFFFF,
> PF_INT_DIR_OICR_ENA_S)
> > +#define PF_INT_DIR_OICR			0x08406004
> > +#define PF_INT_DIR_OICR_TSYN_EVNT	0
> > +#define PF_INT_DIR_OICR_PHY_TS_0	BIT(1)
> > +#define PF_INT_DIR_OICR_PHY_TS_1	BIT(2)
> > +#define PF_INT_DIR_OICR_CAUSE		0x08406008
> > +#define PF_INT_DIR_OICR_CAUSE_CAUSE_S	0
> > +#define PF_INT_DIR_OICR_CAUSE_CAUSE_M
> 	MAKEMASK(0xFFFFFFFF, PF_INT_DIR_OICR_CAUSE_CAUSE_S)
> > +#define PF_INT_PBA_CLEAR		0x0840600C
> > +
> > +#define PF_FUNC_RID			0x08406010
> > +#define PF_FUNC_RID_FUNCTION_NUMBER_S	0
> > +#define PF_FUNC_RID_FUNCTION_NUMBER_M	MAKEMASK(0x7,
> PF_FUNC_RID_FUNCTION_NUMBER_S)
> > +#define PF_FUNC_RID_DEVICE_NUMBER_S	3
> > +#define PF_FUNC_RID_DEVICE_NUMBER_M	MAKEMASK(0x1F,
> PF_FUNC_RID_DEVICE_NUMBER_S)
> > +#define PF_FUNC_RID_BUS_NUMBER_S	8
> > +#define PF_FUNC_RID_BUS_NUMBER_M	MAKEMASK(0xFF,
> PF_FUNC_RID_BUS_NUMBER_S)
> > +
> > +/* Reset registers */
> > +#define PFGEN_RTRIG			0x08407000
> > +#define PFGEN_RTRIG_CORER_S		0
> > +#define PFGEN_RTRIG_CORER_M		BIT(0)
> > +#define PFGEN_RTRIG_LINKR_S		1
> > +#define PFGEN_RTRIG_LINKR_M		BIT(1)
> > +#define PFGEN_RTRIG_IMCR_S		2
> > +#define PFGEN_RTRIG_IMCR_M		BIT(2)
> > +#define PFGEN_RSTAT			0x08407008 /* PFR Status
> */
> > +#define PFGEN_RSTAT_PFR_STATE_S		0
> > +#define PFGEN_RSTAT_PFR_STATE_M		MAKEMASK(0x3,
> PFGEN_RSTAT_PFR_STATE_S)
> > +#define PFGEN_CTRL			0x0840700C
> > +#define PFGEN_CTRL_PFSWR		BIT(0)
> > +
> > +#endif
> > --
> > 2.33.0
> 
> Thanks,
> Al

^ permalink raw reply	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 02/19] iecm: add basic module init and documentation
  2022-02-01 19:44   ` Shannon Nelson
@ 2022-02-03  3:08     ` Brady, Alan
  0 siblings, 0 replies; 89+ messages in thread
From: Brady, Alan @ 2022-02-03  3:08 UTC (permalink / raw)
  To: intel-wired-lan

> -----Original Message-----
> From: Shannon Nelson <shannon.lee.nelson@gmail.com>
> Sent: Tuesday, February 1, 2022 11:45 AM
> To: Brady, Alan <alan.brady@intel.com>
> Cc: Intel Wired LAN <intel-wired-lan@lists.osuosl.org>; Burra, Phani R
> <phani.r.burra@intel.com>; Chittim, Madhu <madhu.chittim@intel.com>;
> Linga, Pavan Kumar <pavan.kumar.linga@intel.com>
> Subject: Re: [Intel-wired-lan] [PATCH net-next 02/19] iecm: add basic
> module init and documentation
> 
> On Thu, Jan 27, 2022 at 4:34 PM Alan Brady <alan.brady@intel.com>
> wrote:
> >
> > This adds the basics needed to make a kernel module and documentation
> > needed to use iecm module.
> >
> 
> [ snip ]
> 
> > diff --git a/drivers/net/ethernet/intel/iecm/Makefile
> > b/drivers/net/ethernet/intel/iecm/Makefile
> > new file mode 100644
> > index 000000000000..d2d087ac71e9
> > --- /dev/null
> > +++ b/drivers/net/ethernet/intel/iecm/Makefile
> > @@ -0,0 +1,13 @@
> > +# SPDX-License-Identifier: GPL-2.0-only # Copyright (C) 2019 Intel
> > +Corporation
> > +
> > +#
> > +# Makefile for the Intel(R) Data Plane Function Linux Driver
> 
> Maybe the iecm here rather than idpf?
> 

Woops.  Thanks, will fix.

> > +#
> > +
> > +obj-$(CONFIG_IECM) += iecm.o
> > +
> > +ccflags-y += -I$(srctree)/drivers/net/ethernet/intel/include
> > +
> > +iecm-y := \
> > +       iecm_main.o
> > diff --git a/drivers/net/ethernet/intel/iecm/iecm_main.c
> > b/drivers/net/ethernet/intel/iecm/iecm_main.c
> > new file mode 100644
> > index 000000000000..7c09403c6918
> > --- /dev/null
> > +++ b/drivers/net/ethernet/intel/iecm/iecm_main.c
> > @@ -0,0 +1,40 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +/* Copyright (C) 2019 Intel Corporation */
> > +
> > +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> > +
> > +#include "iecm.h"
> > +
> > +#define DRV_SUMMARY    "Intel(R) Ethernet Common Module"
> > +static const char iecm_driver_string[] = DRV_SUMMARY; static const
> > +char iecm_copyright[] = "Copyright (c) 2020, Intel Corporation.";
> 
> Do you want this copyright string "2020" to match the top of the file
> "2019"?
> 

Hmm yes we should probably fix that.  Thanks!

> > +
> > +MODULE_DESCRIPTION(DRV_SUMMARY);
> > +MODULE_LICENSE("GPL v2");
> > +
> > +/**
> > + * iecm_module_init - Driver registration routine
> > + *
> > + * iecm_module_init is the first routine called when the driver is
> > + * loaded. All it does is register with the PCI subsystem.
> > + */
> > +static int __init iecm_module_init(void) {
> > +       pr_info("%s - version %d\n", iecm_driver_string,
> LINUX_VERSION_CODE);
> > +       pr_info("%s\n", iecm_copyright);
> > +
> > +       return 0;
> > +}
> > +module_init(iecm_module_init);
> > +
> > +/**
> > + * iecm_module_exit - Driver exit cleanup routine
> > + *
> > + * iecm_module_exit is called just before the driver is removed
> > + * from memory.
> > + */
> > +static void __exit iecm_module_exit(void) {
> > +       pr_info("module unloaded\n");
> > +}
> > +module_exit(iecm_module_exit);
> > diff --git a/drivers/net/ethernet/intel/include/iecm.h
> > b/drivers/net/ethernet/intel/include/iecm.h
> > new file mode 100644
> > index 000000000000..f66f0d7db8e7
> > --- /dev/null
> > +++ b/drivers/net/ethernet/intel/include/iecm.h
> > @@ -0,0 +1,10 @@
> > +/* SPDX-License-Identifier: GPL-2.0-only */
> > +/* Copyright (C) 2019 Intel Corporation */
> > +
> > +#ifndef _IECM_H_
> > +#define _IECM_H_
> > +
> > +#include <linux/etherdevice.h>
> > +#include <linux/version.h>
> > +
> > +#endif /* !_IECM_H_ */
> > --
> > 2.33.0
> >
> > _______________________________________________
> > Intel-wired-lan mailing list
> > Intel-wired-lan at osuosl.org
> > https://lists.osuosl.org/mailman/listinfo/intel-wired-lan

^ permalink raw reply	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 03/19] iecm: add probe and remove
  2022-02-01 20:02   ` Shannon Nelson
@ 2022-02-03  3:13     ` Brady, Alan
  0 siblings, 0 replies; 89+ messages in thread
From: Brady, Alan @ 2022-02-03  3:13 UTC (permalink / raw)
  To: intel-wired-lan

> -----Original Message-----
> From: Shannon Nelson <shannon.lee.nelson@gmail.com>
> Sent: Tuesday, February 1, 2022 12:02 PM
> To: Brady, Alan <alan.brady@intel.com>
> Cc: Intel Wired LAN <intel-wired-lan@lists.osuosl.org>; Burra, Phani R
> <phani.r.burra@intel.com>; Chittim, Madhu <madhu.chittim@intel.com>;
> Linga, Pavan Kumar <pavan.kumar.linga@intel.com>
> Subject: Re: [Intel-wired-lan] [PATCH net-next 03/19] iecm: add probe and
> remove
> 
> On Thu, Jan 27, 2022 at 4:34 PM Alan Brady <alan.brady@intel.com>
> wrote:
> >
> > This adds everything we need in probe and remove as well as a few
> > stubs which will kick off the next step in the init process of device
> > driver coming up.
> >
> > Signed-off-by: Phani Burra <phani.r.burra@intel.com>
> > Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
> > Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
> > Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
> > Signed-off-by: Alan Brady <alan.brady@intel.com>
> > ---
> >  drivers/net/ethernet/intel/iecm/Makefile      |   1 +
> >  drivers/net/ethernet/intel/iecm/iecm_lib.c    | 231
> ++++++++++++++++++
> >  drivers/net/ethernet/intel/include/iecm.h     | 178 +++++++++++++-
> >  .../net/ethernet/intel/include/iecm_txrx.h    |  33 +++
> >  4 files changed, 442 insertions(+), 1 deletion(-)  create mode 100644
> > drivers/net/ethernet/intel/iecm/iecm_lib.c
> >  create mode 100644 drivers/net/ethernet/intel/include/iecm_txrx.h
> >
> > diff --git a/drivers/net/ethernet/intel/iecm/Makefile
> > b/drivers/net/ethernet/intel/iecm/Makefile
> > index d2d087ac71e9..4f497723419d 100644
> > --- a/drivers/net/ethernet/intel/iecm/Makefile
> > +++ b/drivers/net/ethernet/intel/iecm/Makefile
> > @@ -10,4 +10,5 @@ obj-$(CONFIG_IECM) += iecm.o  ccflags-y +=
> > -I$(srctree)/drivers/net/ethernet/intel/include
> >
> >  iecm-y := \
> > +       iecm_lib.o \
> >         iecm_main.o
> > diff --git a/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > new file mode 100644
> > index 000000000000..e6d0b418a27f
> > --- /dev/null
> > +++ b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > @@ -0,0 +1,231 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +/* Copyright (C) 2019 Intel Corporation */
> > +
> > +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> > +
> > +#include "iecm.h"
> > +
> > +/**
> > + * iecm_statistics_task - Delayed task to get statistics over mailbox
> > + * @work: work_struct handle to our data  */ static void
> > +iecm_statistics_task(struct work_struct *work) {
> > +       /* stub */
> > +}
> > +
> > +/**
> > + * iecm_service_task - Delayed task for handling mailbox responses
> > + * @work: work_struct handle to our data
> > + *
> > + */
> > +static void iecm_service_task(struct work_struct *work) {
> > +       /* stub */
> > +}
> > +
> > +/**
> > + * iecm_init_task - Delayed initialization task
> > + * @work: work_struct handle to our data
> > + *
> > + * Init task finishes up pending work started in probe.  Due to the
> > +asynchronous
> > + * nature in which the device communicates with hardware, we may
> have
> > +to wait
> > + * several milliseconds to get a response.  Instead of busy polling
> > +in probe,
> > + * pulling it out into a delayed work task prevents us from bogging
> > +down the
> > + * whole system waiting for a response from hardware.
> > + */
> > +static void iecm_init_task(struct work_struct *work) {
> > +       /* stub */
> > +}
> > +
> > +/**
> > + * iecm_deinit_task - Device deinit routine
> > + * @adapter: Driver specific private structue
> > + *
> > + * Extended remove logic which will be used for
> > + * hard reset as well
> > + */
> > +static void iecm_deinit_task(struct iecm_adapter *adapter) {
> > +       /* stub */
> > +}
> > +
> > +/**
> > + * iecm_vc_event_task - Handle virtchannel event logic
> > + * @work: work queue struct
> > + */
> > +static void iecm_vc_event_task(struct work_struct *work) {
> > +       /* stub */
> > +}
> > +
> > +/**
> > + * iecm_probe - Device initialization routine
> > + * @pdev: PCI device information struct
> > + * @ent: entry in iecm_pci_tbl
> > + * @adapter: driver specific private structure
> > + *
> > + * Returns 0 on success, negative on failure  */ int
> > +iecm_probe(struct pci_dev *pdev,
> > +              const struct pci_device_id __always_unused *ent,
> > +              struct iecm_adapter *adapter) {
> > +       int err;
> > +
> > +       adapter->pdev = pdev;
> > +
> > +       err = pcim_enable_device(pdev);
> > +       if (err)
> > +               return err;
> > +
> > +       err = pcim_iomap_regions(pdev, BIT(IECM_BAR0), pci_name(pdev));
> > +       if (err) {
> > +               dev_err(&pdev->dev, "BAR0 I/O map error %d\n", err);
> > +               return err;
> > +       }
> > +
> > +       /* set up for high or low dma */
> > +       err = dma_set_mask_and_coherent(&pdev->dev,
> DMA_BIT_MASK(64));
> > +       if (err)
> > +               err = dma_set_mask_and_coherent(&pdev->dev,
> DMA_BIT_MASK(32));
> > +       if (err) {
> > +               dev_err(&pdev->dev, "DMA configuration failed: 0x%x\n", err);
> > +               return err;
> > +       }
> > +
> > +       pci_enable_pcie_error_reporting(pdev);
> > +       pci_set_master(pdev);
> > +       pci_set_drvdata(pdev, adapter);
> > +
> > +       adapter->init_wq =
> > +               alloc_workqueue("%s", WQ_MEM_RECLAIM, 0,
> KBUILD_MODNAME);
> > +       if (!adapter->init_wq) {
> > +               dev_err(&pdev->dev, "Failed to allocate workqueue\n");
> 
> checkpatch usually complains about these kinds of messages, but if you're
> going to put them in anyway, you might make them more useful and add a
> bit to each that can tell you which is the one that broke.
> For example, in this one, you might write it as "Failed to allocate init
> workqueue"
> 

I agree it seems excessive, will remove.

> > +               err = -ENOMEM;
> > +               goto err_wq_alloc;
> > +       }
> > +
> > +       adapter->serv_wq =
> > +               alloc_workqueue("%s", WQ_MEM_RECLAIM, 0,
> KBUILD_MODNAME);
> > +       if (!adapter->serv_wq) {
> > +               dev_err(&pdev->dev, "Failed to allocate workqueue\n");
> > +               err = -ENOMEM;
> > +               goto err_mbx_wq_alloc;
> > +       }
> > +
> > +       adapter->stats_wq =
> > +               alloc_workqueue("%s", WQ_MEM_RECLAIM, 0,
> KBUILD_MODNAME);
> > +       if (!adapter->stats_wq) {
> > +               dev_err(&pdev->dev, "Failed to allocate workqueue\n");
> > +               err = -ENOMEM;
> > +               goto err_stats_wq_alloc;
> > +       }
> > +       adapter->vc_event_wq =
> > +               alloc_workqueue("%s", WQ_MEM_RECLAIM, 0,
> KBUILD_MODNAME);
> > +       if (!adapter->vc_event_wq) {
> > +               dev_err(&pdev->dev, "Failed to allocate workqueue\n");
> > +               err = -ENOMEM;
> > +               goto err_vc_event_wq_alloc;
> > +       }
> > +
> > +       /* setup msglvl */
> > +       adapter->msg_enable = netif_msg_init(-1, IECM_AVAIL_NETIF_M);
> > +
> > +       adapter->vports = kcalloc(IECM_MAX_NUM_VPORTS,
> > +                                 sizeof(*adapter->vports), GFP_KERNEL);
> > +       if (!adapter->vports) {
> 
> With error messages on all the above allocations, is there any reason they
> aren't here and in the next one?
> 

Probably not a good reason no. Will fix.

> > +               err = -ENOMEM;
> > +               goto err_vport_alloc;
> > +       }
> > +
> > +       adapter->netdevs = kcalloc(IECM_MAX_NUM_VPORTS,
> > +                                  sizeof(struct net_device *), GFP_KERNEL);
> > +       if (!adapter->netdevs) {
> > +               err = -ENOMEM;
> > +               goto err_netdev_alloc;
> > +       }
> > +
> > +       mutex_init(&adapter->sw_mutex);
> > +       mutex_init(&adapter->reset_lock);
> > +       init_waitqueue_head(&adapter->vchnl_wq);
> > +       init_waitqueue_head(&adapter->sw_marker_wq);
> > +
> > +       spin_lock_init(&adapter->cloud_filter_list_lock);
> > +       spin_lock_init(&adapter->mac_filter_list_lock);
> > +       spin_lock_init(&adapter->vlan_list_lock);
> > +       spin_lock_init(&adapter->adv_rss_list_lock);
> > +       spin_lock_init(&adapter->fdir_fltr_list_lock);
> > +       INIT_LIST_HEAD(&adapter->config_data.mac_filter_list);
> > +       INIT_LIST_HEAD(&adapter->config_data.vlan_filter_list);
> > +       INIT_LIST_HEAD(&adapter->config_data.adv_rss_list);
> > +
> > +       INIT_DELAYED_WORK(&adapter->stats_task, iecm_statistics_task);
> > +       INIT_DELAYED_WORK(&adapter->serv_task, iecm_service_task);
> > +       INIT_DELAYED_WORK(&adapter->init_task, iecm_init_task);
> > +       INIT_DELAYED_WORK(&adapter->vc_event_task,
> > + iecm_vc_event_task);
> > +
> > +       set_bit(__IECM_HR_DRV_LOAD, adapter->flags);
> > +       queue_delayed_work(adapter->vc_event_wq, &adapter-
> >vc_event_task,
> > +                          msecs_to_jiffies(10 * (pdev->devfn &
> > + 0x07)));
> > +
> > +       return 0;
> > +err_netdev_alloc:
> > +       kfree(adapter->vports);
> > +err_vport_alloc:
> > +       destroy_workqueue(adapter->vc_event_wq);
> > +err_vc_event_wq_alloc:
> > +       destroy_workqueue(adapter->stats_wq);
> > +err_stats_wq_alloc:
> > +       destroy_workqueue(adapter->serv_wq);
> > +err_mbx_wq_alloc:
> > +       destroy_workqueue(adapter->init_wq);
> > +err_wq_alloc:
> > +       pci_disable_pcie_error_reporting(pdev);
> > +       return err;
> > +}
> > +EXPORT_SYMBOL(iecm_probe);
> > +
> > +/**
> > + * iecm_del_user_cfg_data - delete all user configuration data
> > + * @adapter: Driver specific private structue  */ static void
> > +iecm_del_user_cfg_data(struct iecm_adapter *adapter) {
> > +       /* stub */
> > +}
> > +
> > +/**
> > + * iecm_remove - Device removal routine
> > + * @pdev: PCI device information struct  */ void iecm_remove(struct
> > +pci_dev *pdev) {
> > +       struct iecm_adapter *adapter = pci_get_drvdata(pdev);
> > +
> > +       if (!adapter)
> > +               return;
> > +       /* Wait until vc_event_task is done to consider if any hard reset is
> > +        * in progress else we may go ahead and release the resources but
> the
> > +        * thread doing the hard reset might continue the init path and
> > +        * end up in bad state.
> > +        */
> > +       cancel_delayed_work_sync(&adapter->vc_event_task);
> > +       iecm_deinit_task(adapter);
> > +       iecm_del_user_cfg_data(adapter);
> > +       msleep(20);
> > +       destroy_workqueue(adapter->serv_wq);
> > +       destroy_workqueue(adapter->vc_event_wq);
> > +       destroy_workqueue(adapter->stats_wq);
> > +       destroy_workqueue(adapter->init_wq);
> > +       kfree(adapter->vports);
> > +       kfree(adapter->netdevs);
> > +       kfree(adapter->vlan_caps);
> 
> Where did vlan_caps get allocated?
> 

This probably leaked into this patch as I was breaking down the code into patches. i.e. it doesn't get allocated until a later patch.  Will fix.  Thanks!

> > +       mutex_destroy(&adapter->sw_mutex);
> > +       mutex_destroy(&adapter->reset_lock);
> > +       pci_disable_pcie_error_reporting(pdev);
> > +       pcim_iounmap_regions(pdev, BIT(IECM_BAR0));
> > +       pci_disable_device(pdev);
> > +}
> > +EXPORT_SYMBOL(iecm_remove);
> > diff --git a/drivers/net/ethernet/intel/include/iecm.h
> > b/drivers/net/ethernet/intel/include/iecm.h
> > index f66f0d7db8e7..e19e014e9817 100644
> > --- a/drivers/net/ethernet/intel/include/iecm.h
> > +++ b/drivers/net/ethernet/intel/include/iecm.h
> > @@ -4,7 +4,183 @@
> >  #ifndef _IECM_H_
> >  #define _IECM_H_
> >
> > -#include <linux/etherdevice.h>
> > +#include <linux/aer.h>
> > +#include <linux/pci.h>
> > +#include <linux/netdevice.h>
> > +#include <linux/ethtool.h>
> >  #include <linux/version.h>
> > +#include <linux/dim.h>
> >
> > +#include "iecm_txrx.h"
> > +
> > +#define IECM_BAR0                      0
> > +#define IECM_NO_FREE_SLOT              0xffff
> > +
> > +#define IECM_MAX_NUM_VPORTS            1
> > +
> > +/* available message levels */
> > +#define IECM_AVAIL_NETIF_M (NETIF_MSG_DRV | NETIF_MSG_PROBE
> |
> > +NETIF_MSG_LINK)
> > +
> > +enum iecm_state {
> > +       __IECM_STARTUP,
> > +       __IECM_VER_CHECK,
> > +       __IECM_GET_CAPS,
> > +       __IECM_GET_DFLT_VPORT_PARAMS,
> > +       __IECM_INIT_SW,
> > +       __IECM_DOWN,
> > +       __IECM_UP,
> > +       __IECM_STATE_LAST /* this member MUST be last */ };
> > +
> > +enum iecm_flags {
> > +       /* Soft reset causes */
> > +       __IECM_SR_Q_CHANGE, /* Soft reset to do queue change */
> > +       __IECM_SR_Q_DESC_CHANGE,
> > +       __IECM_SR_Q_SCH_CHANGE, /* Scheduling mode change in queue
> context */
> > +       __IECM_SR_MTU_CHANGE,
> > +       __IECM_SR_TC_CHANGE,
> > +       __IECM_SR_RSC_CHANGE,
> > +       __IECM_SR_HSPLIT_CHANGE,
> > +       /* Hard reset causes */
> > +       __IECM_HR_FUNC_RESET, /* Hard reset when txrx timeout */
> > +       __IECM_HR_CORE_RESET, /* when reset event is received on
> virtchannel */
> > +       __IECM_HR_DRV_LOAD, /* Set on driver load for a clean HW */
> > +       /* Reset in progress */
> > +       __IECM_HR_RESET_IN_PROG,
> > +       /* Resources release in progress*/
> > +       __IECM_REL_RES_IN_PROG,
> > +       /* Generic bits to share a message */
> > +       __IECM_DEL_QUEUES,
> > +       __IECM_UP_REQUESTED, /* Set if open to be called explicitly by
> driver */
> > +       /* Mailbox interrupt event */
> > +       __IECM_MB_INTR_MODE,
> > +       __IECM_MB_INTR_TRIGGER,
> > +       /* Stats message pending on mailbox */
> > +       __IECM_MB_STATS_PENDING,
> > +       /* Device specific bits */
> > +       /* Request split queue model when creating vport */
> > +       __IECM_REQ_TX_SPLITQ,
> > +       __IECM_REQ_RX_SPLITQ,
> > +       /* Asynchronous add/del ether address in flight */
> > +       __IECM_ADD_ETH_REQ,
> > +       __IECM_DEL_ETH_REQ,
> > +       /* Virtchnl message buffer received needs to be processed */
> > +       __IECM_VC_MSG_PENDING,
> > +       /* To process software marker packets */
> > +       __IECM_SW_MARKER,
> > +       /* must be last */
> > +       __IECM_FLAGS_NBITS,
> > +};
> > +
> > +struct iecm_reset_reg {
> > +       u32 rstat;
> > +       u32 rstat_m;
> > +};
> > +
> > +/* stub */
> > +struct iecm_vport {
> > +};
> > +
> > +enum iecm_user_flags {
> > +       __IECM_PRIV_FLAGS_HDR_SPLIT = 0,
> > +       __IECM_PROMISC_UC = 32,
> > +       __IECM_PROMISC_MC,
> > +       __IECM_USER_FLAGS_NBITS,
> > +};
> > +
> > +/* User defined configuration values */ struct iecm_user_config_data
> > +{
> > +       u32 num_req_tx_qs; /* user requested TX queues through ethtool
> */
> > +       u32 num_req_rx_qs; /* user requested RX queues through ethtool
> */
> > +       u32 num_req_txq_desc;
> > +       u32 num_req_rxq_desc;
> > +       u16 vlan_ethertype;
> > +       void *req_qs_chunks;
> > +       DECLARE_BITMAP(user_flags, __IECM_USER_FLAGS_NBITS);
> > +       DECLARE_BITMAP(etf_qenable, IECM_LARGE_MAX_Q);
> > +       struct list_head mac_filter_list;
> > +       struct list_head vlan_filter_list;
> > +       struct list_head adv_rss_list; };
> > +
> > +struct iecm_rss_data {
> > +       u64 rss_hash;
> > +       u16 rss_key_size;
> > +       u8 *rss_key;
> > +       u16 rss_lut_size;
> > +       u32 *rss_lut;
> > +};
> > +
> > +struct iecm_adapter {
> > +       struct pci_dev *pdev;
> > +       const char *drv_name;
> > +       const char *drv_ver;
> > +       u32 virt_ver_maj;
> > +       u32 virt_ver_min;
> > +
> > +       u32 tx_timeout_count;
> > +       u32 msg_enable;
> > +       enum iecm_state state;
> > +       DECLARE_BITMAP(flags, __IECM_FLAGS_NBITS);
> > +       struct mutex reset_lock; /* lock to protect reset flows */
> > +       struct iecm_reset_reg reset_reg;
> > +
> > +       u16 num_req_msix;
> > +       u16 num_msix_entries;
> > +       struct msix_entry *msix_entries;
> > +       struct virtchnl2_alloc_vectors *req_vec_chunks;
> > +
> > +       /* vport structs */
> > +       struct iecm_vport **vports;     /* vports created by the driver */
> > +       struct net_device **netdevs;    /* associated vport netdevs */
> > +       u16 num_alloc_vport;
> > +       u16 next_vport;         /* Next free slot in pf->vport[] - 0-based! */
> > +
> > +       struct delayed_work init_task; /* delayed init task */
> > +       struct workqueue_struct *init_wq;
> > +       u32 mb_wait_count;
> > +       struct delayed_work serv_task; /* delayed service task */
> > +       struct workqueue_struct *serv_wq;
> > +       struct delayed_work stats_task; /* delayed statistics task */
> > +       struct workqueue_struct *stats_wq;
> > +       struct delayed_work vc_event_task; /* delayed virtchannel event
> task */
> > +       struct workqueue_struct *vc_event_wq;
> > +       /* Store the resources data received from control plane */
> > +       void **vport_params_reqd;
> > +       void **vport_params_recvd;
> > +       /* User set parameters */
> > +       struct iecm_user_config_data config_data;
> > +       void *caps;
> > +       struct virtchnl_vlan_caps *vlan_caps;
> > +
> > +       wait_queue_head_t vchnl_wq;
> > +       wait_queue_head_t sw_marker_wq;
> > +       struct iecm_rss_data rss_data;
> > +       s32 link_speed;
> > +       /* This is only populated if the
> VIRTCHNL_VF_CAP_ADV_LINK_SPEED is set
> > +        * in vf_res->vf_cap_flags. This field should be used going forward
> and
> > +        * the enum virtchnl_link_speed above should be considered the
> legacy
> > +        * way of storing/communicating link speeds.
> > +        */
> > +       u32 link_speed_mbps;
> > +       bool link_up;
> > +       int num_vfs;
> > +
> > +       struct mutex sw_mutex;          /* lock to protect vport alloc flow */
> > +       /* lock to protect cloud filters*/
> > +       spinlock_t cloud_filter_list_lock;
> > +       /* lock to protect mac filters */
> > +       spinlock_t mac_filter_list_lock;
> > +       /* lock to protect vlan filters */
> > +       spinlock_t vlan_list_lock;
> > +       /* lock to protect advanced RSS filters */
> > +       spinlock_t adv_rss_list_lock;
> > +       /* lock to protect the Flow Director filters */
> > +       spinlock_t fdir_fltr_list_lock; };
> > +
> > +int iecm_probe(struct pci_dev *pdev,
> > +              const struct pci_device_id __always_unused *ent,
> > +              struct iecm_adapter *adapter); void iecm_remove(struct
> > +pci_dev *pdev);
> >  #endif /* !_IECM_H_ */
> > diff --git a/drivers/net/ethernet/intel/include/iecm_txrx.h
> > b/drivers/net/ethernet/intel/include/iecm_txrx.h
> > new file mode 100644
> > index 000000000000..602d3b3b19dd
> > --- /dev/null
> > +++ b/drivers/net/ethernet/intel/include/iecm_txrx.h
> > @@ -0,0 +1,33 @@
> > +/* SPDX-License-Identifier: GPL-2.0-only */
> > +/* Copyright (C) 2019 Intel Corporation */
> > +
> > +#ifndef _IECM_TXRX_H_
> > +#define _IECM_TXRX_H_
> > +
> > +#define IECM_LARGE_MAX_Q                       256
> > +#define IECM_MAX_Q                             16
> > +/* Mailbox Queue */
> > +#define IECM_MAX_NONQ                          1
> > +#define IECM_MAX_TXQ_DESC                      4096
> > +#define IECM_MAX_RXQ_DESC                      4096
> > +#define IECM_MIN_TXQ_DESC                      32
> > +#define IECM_MIN_TXQ_COMPLQ_DESC               64
> > +#define IECM_MIN_RXQ_DESC                      32
> > +#define IECM_REQ_DESC_MULTIPLE                 32
> > +#define IECM_REQ_SPLITQ_RXQ_DESC_MULTIPLE      64
> > +#define IECM_MIN_TX_DESC_NEEDED (MAX_SKB_FRAGS + 6) #define
> > +IECM_TX_WAKE_THRESH ((s16)IECM_MIN_TX_DESC_NEEDED * 2)
> > +
> > +#define IECM_DFLT_SINGLEQ_TX_Q_GROUPS          1
> > +#define IECM_DFLT_SINGLEQ_RX_Q_GROUPS          1
> > +#define IECM_DFLT_SINGLEQ_TXQ_PER_GROUP                4
> > +#define IECM_DFLT_SINGLEQ_RXQ_PER_GROUP                4
> > +
> > +#define IECM_COMPLQ_PER_GROUP                  1
> > +#define IECM_MAX_BUFQS_PER_RXQ_GRP             2
> > +
> > +#define IECM_DFLT_SPLITQ_TX_Q_GROUPS           4
> > +#define IECM_DFLT_SPLITQ_RX_Q_GROUPS           4
> > +#define IECM_DFLT_SPLITQ_TXQ_PER_GROUP         1
> > +#define IECM_DFLT_SPLITQ_RXQ_PER_GROUP         1
> > +#endif /* !_IECM_TXRX_H_ */
> > --
> > 2.33.0
> >
> > _______________________________________________
> > Intel-wired-lan mailing list
> > Intel-wired-lan at osuosl.org
> > https://lists.osuosl.org/mailman/listinfo/intel-wired-lan

^ permalink raw reply	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 04/19] iecm: add api_init and controlq init
  2022-02-01 21:26   ` Shannon Nelson
@ 2022-02-03  3:24     ` Brady, Alan
  2022-02-03  3:40       ` Brady, Alan
  2022-02-03 13:13       ` Alexander Lobakin
  0 siblings, 2 replies; 89+ messages in thread
From: Brady, Alan @ 2022-02-03  3:24 UTC (permalink / raw)
  To: intel-wired-lan

> -----Original Message-----
> From: Shannon Nelson <shannon.lee.nelson@gmail.com>
> Sent: Tuesday, February 1, 2022 1:27 PM
> To: Brady, Alan <alan.brady@intel.com>
> Cc: Intel Wired LAN <intel-wired-lan@lists.osuosl.org>; Burra, Phani R
> <phani.r.burra@intel.com>; Chittim, Madhu <madhu.chittim@intel.com>;
> Linga, Pavan Kumar <pavan.kumar.linga@intel.com>
> Subject: Re: [Intel-wired-lan] [PATCH net-next 04/19] iecm: add api_init and
> controlq init
> 
> On Thu, Jan 27, 2022 at 4:35 PM Alan Brady <alan.brady@intel.com>
> wrote:
> >
> > Initializing device registers is offloaded into function pointers given
> > to iecm from the dependent device driver for a given device, as offsets
> > can vary wildly. This also adds everything needed to setup and use a
> > controlq which uses some of those registers.
> >
> > At the end of probe we kicked off a hard reset and this implements what's
> > needed to handle that reset and continue init.
> >
> > Signed-off-by: Phani Burra <phani.r.burra@intel.com>
> > Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
> > Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
> > Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
> > Signed-off-by: Alan Brady <alan.brady@intel.com>
> > ---
> >  drivers/net/ethernet/intel/iecm/Makefile      |   3 +
> >  .../net/ethernet/intel/iecm/iecm_controlq.c   | 649
> ++++++++++++++++++
> >  .../ethernet/intel/iecm/iecm_controlq_setup.c | 175 +++++
> >  drivers/net/ethernet/intel/iecm/iecm_lib.c    | 191 +++++-
> >  .../net/ethernet/intel/iecm/iecm_virtchnl.c   | 172 +++++
> >  drivers/net/ethernet/intel/include/iecm.h     |  52 ++
> >  .../ethernet/intel/include/iecm_controlq.h    | 117 ++++
> >  .../intel/include/iecm_controlq_api.h         | 185 +++++
> >  drivers/net/ethernet/intel/include/iecm_mem.h |  20 +
> >  9 files changed, 1563 insertions(+), 1 deletion(-)
> >  create mode 100644 drivers/net/ethernet/intel/iecm/iecm_controlq.c
> >  create mode 100644
> drivers/net/ethernet/intel/iecm/iecm_controlq_setup.c
> >  create mode 100644 drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> >  create mode 100644 drivers/net/ethernet/intel/include/iecm_controlq.h
> >  create mode 100644
> drivers/net/ethernet/intel/include/iecm_controlq_api.h
> >  create mode 100644 drivers/net/ethernet/intel/include/iecm_mem.h
> >
> > diff --git a/drivers/net/ethernet/intel/iecm/Makefile
> b/drivers/net/ethernet/intel/iecm/Makefile
> > index 4f497723419d..db8fecb075a6 100644
> > --- a/drivers/net/ethernet/intel/iecm/Makefile
> > +++ b/drivers/net/ethernet/intel/iecm/Makefile
> > @@ -11,4 +11,7 @@ ccflags-y += -
> I$(srctree)/drivers/net/ethernet/intel/include
> >
> >  iecm-y := \
> >         iecm_lib.o \
> > +       iecm_virtchnl.o \
> > +       iecm_controlq.o \
> > +       iecm_controlq_setup.o \
> >         iecm_main.o
> > diff --git a/drivers/net/ethernet/intel/iecm/iecm_controlq.c
> b/drivers/net/ethernet/intel/iecm/iecm_controlq.c
> > new file mode 100644
> > index 000000000000..f9682a7b3e44
> > --- /dev/null
> > +++ b/drivers/net/ethernet/intel/iecm/iecm_controlq.c
> > @@ -0,0 +1,649 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +/* Copyright (c) 2020, Intel Corporation. */
> > +
> > +#include "iecm_controlq.h"
> > +
> > +/**
> > + * iecm_ctlq_setup_regs - initialize control queue registers
> > + * @cq: pointer to the specific control queue
> > + * @q_create_info: structs containing info for each queue to be
> initialized
> > + */
> > +static void
> > +iecm_ctlq_setup_regs(struct iecm_ctlq_info *cq,
> > +                    struct iecm_ctlq_create_info *q_create_info)
> > +{
> > +       /* set head and tail registers in our local struct */
> > +       cq->reg.head = q_create_info->reg.head;
> > +       cq->reg.tail = q_create_info->reg.tail;
> > +       cq->reg.len = q_create_info->reg.len;
> > +       cq->reg.bah = q_create_info->reg.bah;
> > +       cq->reg.bal = q_create_info->reg.bal;
> > +       cq->reg.len_mask = q_create_info->reg.len_mask;
> > +       cq->reg.len_ena_mask = q_create_info->reg.len_ena_mask;
> > +       cq->reg.head_mask = q_create_info->reg.head_mask;
> > +}
> > +
> > +/**
> > + * iecm_ctlq_init_regs - Initialize control queue registers
> > + * @hw: pointer to hw struct
> > + * @cq: pointer to the specific Control queue
> > + * @is_rxq: true if receive control queue, false otherwise
> > + *
> > + * Initialize registers. The caller is expected to have already initialized the
> > + * descriptor ring memory and buffer memory
> > + */
> > +static void iecm_ctlq_init_regs(struct iecm_hw *hw, struct
> iecm_ctlq_info *cq,
> > +                               bool is_rxq)
> > +{
> > +       /* Update tail to post pre-allocated buffers for rx queues */
> > +       if (is_rxq)
> > +               wr32(hw, cq->reg.tail, (u32)(cq->ring_size - 1));
> > +
> > +       /* For non-Mailbox control queues only TAIL need to be set */
> > +       if (cq->q_id != -1)
> > +               return;
> > +
> > +       /* Clear Head for both send or receive */
> > +       wr32(hw, cq->reg.head, 0);
> > +
> > +       /* set starting point */
> > +       wr32(hw, cq->reg.bal, lower_32_bits(cq->desc_ring.pa));
> > +       wr32(hw, cq->reg.bah, upper_32_bits(cq->desc_ring.pa));
> > +       wr32(hw, cq->reg.len, (cq->ring_size | cq->reg.len_ena_mask));
> > +}
> > +
> > +/**
> > + * iecm_ctlq_init_rxq_bufs - populate receive queue descriptors with buf
> > + * @cq: pointer to the specific Control queue
> > + *
> > + * Record the address of the receive queue DMA buffers in the
> descriptors.
> > + * The buffers must have been previously allocated.
> > + */
> > +static void iecm_ctlq_init_rxq_bufs(struct iecm_ctlq_info *cq)
> > +{
> > +       int i = 0;
> > +
> > +       for (i = 0; i < cq->ring_size; i++) {
> > +               struct iecm_ctlq_desc *desc = IECM_CTLQ_DESC(cq, i);
> > +               struct iecm_dma_mem *bi = cq->bi.rx_buff[i];
> > +
> > +               /* No buffer to post to descriptor, continue */
> > +               if (!bi)
> > +                       continue;
> > +
> > +               desc->flags =
> > +                       cpu_to_le16(IECM_CTLQ_FLAG_BUF |
> IECM_CTLQ_FLAG_RD);
> > +               desc->opcode = 0;
> > +               desc->datalen = (__le16)cpu_to_le16(bi->size);
> 
> Why the typecast to __le16, aren't we there already with the
> cpu_to_le16()?
> 

Yeah this is weird. Will fix.

> > +               desc->ret_val = 0;
> > +               desc->cookie_high = 0;
> > +               desc->cookie_low = 0;
> > +               desc->params.indirect.addr_high =
> > +                       cpu_to_le32(upper_32_bits(bi->pa));
> > +               desc->params.indirect.addr_low =
> > +                       cpu_to_le32(lower_32_bits(bi->pa));
> > +               desc->params.indirect.param0 = 0;
> > +               desc->params.indirect.param1 = 0;
> > +       }
> > +}
> > +
> > +/**
> > + * iecm_ctlq_shutdown - shutdown the CQ
> > + * @hw: pointer to hw struct
> > + * @cq: pointer to the specific Control queue
> > + *
> > + * The main shutdown routine for any controq queue
> > + */
> > +static void iecm_ctlq_shutdown(struct iecm_hw *hw, struct
> iecm_ctlq_info *cq)
> > +{
> > +       mutex_lock(&cq->cq_lock);
> > +
> > +       if (!cq->ring_size)
> > +               goto shutdown_sq_out;
> > +
> > +       /* free ring buffers and the ring itself */
> > +       iecm_ctlq_dealloc_ring_res(hw, cq);
> > +
> > +       /* Set ring_size to 0 to indicate uninitialized queue */
> > +       cq->ring_size = 0;
> > +
> > +shutdown_sq_out:
> > +       mutex_unlock(&cq->cq_lock);
> > +       mutex_destroy(&cq->cq_lock);
> > +}
> > +
> > +/**
> > + * iecm_ctlq_add - add one control queue
> > + * @hw: pointer to hardware struct
> > + * @qinfo: info for queue to be created
> > + * @cq_out: (output) double pointer to control queue to be created
> > + *
> > + * Allocate and initialize a control queue and add it to the control queue
> list.
> > + * The cq parameter will be allocated/initialized and passed back to the
> caller
> > + * if no errors occur.
> > + *
> > + * Note: iecm_ctlq_init must be called prior to any calls to
> iecm_ctlq_add
> > + */
> > +int iecm_ctlq_add(struct iecm_hw *hw,
> > +                 struct iecm_ctlq_create_info *qinfo,
> > +                 struct iecm_ctlq_info **cq_out)
> > +{
> > +       bool is_rxq = false;
> > +       int status = 0;
> > +
> > +       if (!qinfo->len || !qinfo->buf_size ||
> > +           qinfo->len > IECM_CTLQ_MAX_RING_SIZE ||
> > +           qinfo->buf_size > IECM_CTLQ_MAX_BUF_LEN)
> > +               return -EINVAL;
> > +
> > +       *cq_out = kcalloc(1, sizeof(struct iecm_ctlq_info), GFP_KERNEL);
> > +       if (!(*cq_out))
> > +               return -ENOMEM;
> 
> You might keep this as a local variable until you get to a successful
> end, then set *cq_out when done.
> Else, you need to be sure to clear it back to NULL on error return to
> be sure no one uses a bogus value.
> 

This one I'm not sure I follow.  If it's NULL we fall into the if(!*cq_out) and don't need to set it to NULL (it already is?). If it's not NULL then we go on to use it like a valid memory address so I hope it's valid.

> > +
> > +       (*cq_out)->cq_type = qinfo->type;
> > +       (*cq_out)->q_id = qinfo->id;
> > +       (*cq_out)->buf_size = qinfo->buf_size;
> > +       (*cq_out)->ring_size = qinfo->len;
> > +
> > +       (*cq_out)->next_to_use = 0;
> > +       (*cq_out)->next_to_clean = 0;
> > +       (*cq_out)->next_to_post = (*cq_out)->ring_size - 1;
> > +
> > +       switch (qinfo->type) {
> > +       case IECM_CTLQ_TYPE_MAILBOX_RX:
> > +               is_rxq = true;
> > +               fallthrough;
> > +       case IECM_CTLQ_TYPE_MAILBOX_TX:
> > +               status = iecm_ctlq_alloc_ring_res(hw, *cq_out);
> > +               break;
> > +       default:
> > +               status = -EBADR;
> > +               break;
> > +       }
> > +
> > +       if (status)
> > +               goto init_free_q;
> > +
> > +       if (is_rxq) {
> > +               iecm_ctlq_init_rxq_bufs(*cq_out);
> > +       } else {
> > +               /* Allocate the array of msg pointers for TX queues */
> > +               (*cq_out)->bi.tx_msg = kcalloc(qinfo->len,
> > +                                              sizeof(struct iecm_ctlq_msg *),
> > +                                              GFP_KERNEL);
> > +               if (!(*cq_out)->bi.tx_msg) {
> > +                       status = -ENOMEM;
> > +                       goto init_dealloc_q_mem;
> > +               }
> > +       }
> > +
> > +       iecm_ctlq_setup_regs(*cq_out, qinfo);
> > +
> > +       iecm_ctlq_init_regs(hw, *cq_out, is_rxq);
> > +
> > +       mutex_init(&(*cq_out)->cq_lock);
> > +
> > +       list_add(&(*cq_out)->cq_list, &hw->cq_list_head);
> > +
> > +       return status;
> > +
> > +init_dealloc_q_mem:
> > +       /* free ring buffers and the ring itself */
> > +       iecm_ctlq_dealloc_ring_res(hw, *cq_out);
> > +init_free_q:
> > +       kfree(*cq_out);
> 
> Probably should clear this back to NULL.
> 

Will fix.

> > +
> > +       return status;
> > +}
> > +
> > +/**
> > + * iecm_ctlq_remove - deallocate and remove specified control queue
> > + * @hw: pointer to hardware struct
> > + * @cq: pointer to control queue to be removed
> > + */
> > +void iecm_ctlq_remove(struct iecm_hw *hw,
> > +                     struct iecm_ctlq_info *cq)
> > +{
> > +       list_del(&cq->cq_list);
> > +       iecm_ctlq_shutdown(hw, cq);
> > +       kfree(cq);
> > +}
> > +
> > +/**
> > + * iecm_ctlq_init - main initialization routine for all control queues
> > + * @hw: pointer to hardware struct
> > + * @num_q: number of queues to initialize
> > + * @q_info: array of structs containing info for each queue to be
> initialized
> > + *
> > + * This initializes any number and any type of control queues. This is an
> all
> > + * or nothing routine; if one fails, all previously allocated queues will be
> > + * destroyed. This must be called prior to using the individual
> add/remove
> > + * APIs.
> > + */
> > +int iecm_ctlq_init(struct iecm_hw *hw, u8 num_q,
> > +                  struct iecm_ctlq_create_info *q_info)
> > +{
> > +       struct iecm_ctlq_info *cq = NULL, *tmp = NULL;
> > +       int ret_code = 0;
> > +       int i = 0;
> > +
> > +       INIT_LIST_HEAD(&hw->cq_list_head);
> > +
> > +       for (i = 0; i < num_q; i++) {
> > +               struct iecm_ctlq_create_info *qinfo = q_info + i;
> > +
> > +               ret_code = iecm_ctlq_add(hw, qinfo, &cq);
> > +               if (ret_code)
> > +                       goto init_destroy_qs;
> > +       }
> > +
> > +       return ret_code;
> > +
> > +init_destroy_qs:
> > +       list_for_each_entry_safe(cq, tmp, &hw->cq_list_head, cq_list)
> > +               iecm_ctlq_remove(hw, cq);
> 
> This could call iecm_ctlq_deinit() rather than repeat the same code.
> 

Seems fair, will fix.

> > +
> > +       return ret_code;
> > +}
> > +
> > +/**
> > + * iecm_ctlq_deinit - destroy all control queues
> > + * @hw: pointer to hw struct
> > + */
> > +int iecm_ctlq_deinit(struct iecm_hw *hw)
> > +{
> > +       struct iecm_ctlq_info *cq = NULL, *tmp = NULL;
> > +       int ret_code = 0;
> > +
> > +       list_for_each_entry_safe(cq, tmp, &hw->cq_list_head, cq_list)
> > +               iecm_ctlq_remove(hw, cq);
> > +
> > +       return ret_code;
> 
> Why is there a return code here when there is never an error?
> 

Yeah this is weird, will fix.

> > +}
> > +
> > +/**
> > + * iecm_ctlq_send - send command to Control Queue (CTQ)
> > + * @hw: pointer to hw struct
> > + * @cq: handle to control queue struct to send on
> > + * @num_q_msg: number of messages to send on control queue
> > + * @q_msg: pointer to array of queue messages to be sent
> > + *
> > + * The caller is expected to allocate DMAable buffers and pass them to
> the
> > + * send routine via the q_msg struct / control queue specific data struct.
> > + * The control queue will hold a reference to each send message until
> > + * the completion for that message has been cleaned.
> > + */
> > +int iecm_ctlq_send(struct iecm_hw *hw, struct iecm_ctlq_info *cq,
> > +                  u16 num_q_msg, struct iecm_ctlq_msg q_msg[])
> > +{
> > +       struct iecm_ctlq_desc *desc;
> > +       int num_desc_avail = 0;
> > +       int status = 0;
> > +       int i = 0;
> > +
> > +       if (!cq || !cq->ring_size)
> > +               return -ENOBUFS;
> > +
> > +       mutex_lock(&cq->cq_lock);
> > +
> > +       /* Ensure there are enough descriptors to send all messages */
> > +       num_desc_avail = IECM_CTLQ_DESC_UNUSED(cq);
> > +       if (num_desc_avail == 0 || num_desc_avail < num_q_msg) {
> > +               status = -ENOSPC;
> > +               goto sq_send_command_out;
> > +       }
> > +
> > +       for (i = 0; i < num_q_msg; i++) {
> > +               struct iecm_ctlq_msg *msg = &q_msg[i];
> > +               u64 msg_cookie;
> > +
> > +               desc = IECM_CTLQ_DESC(cq, cq->next_to_use);
> > +
> > +               desc->opcode = cpu_to_le16(msg->opcode);
> > +               desc->pfid_vfid = cpu_to_le16(msg->func_id);
> > +
> > +               msg_cookie = *(u64 *)&msg->cookie;
> > +               desc->cookie_high =
> > +                       cpu_to_le32(upper_32_bits(msg_cookie));
> > +               desc->cookie_low =
> > +                       cpu_to_le32(lower_32_bits(msg_cookie));
> > +
> > +               if (msg->data_len) {
> > +                       struct iecm_dma_mem *buff = msg->ctx.indirect.payload;
> > +
> > +                       desc->datalen = cpu_to_le16(msg->data_len);
> > +                       desc->flags |= cpu_to_le16(IECM_CTLQ_FLAG_BUF);
> > +                       desc->flags |= cpu_to_le16(IECM_CTLQ_FLAG_RD);
> > +
> > +                       /* Update the address values in the desc with the pa
> > +                        * value for respective buffer
> > +                        */
> > +                       desc->params.indirect.addr_high =
> > +                               cpu_to_le32(upper_32_bits(buff->pa));
> > +                       desc->params.indirect.addr_low =
> > +                               cpu_to_le32(lower_32_bits(buff->pa));
> > +
> > +                       memcpy(&desc->params, msg->ctx.indirect.context,
> > +                              IECM_INDIRECT_CTX_SIZE);
> > +               } else {
> > +                       memcpy(&desc->params, msg->ctx.direct,
> > +                              IECM_DIRECT_CTX_SIZE);
> > +               }
> > +
> > +               /* Store buffer info */
> > +               cq->bi.tx_msg[cq->next_to_use] = msg;
> > +
> > +               (cq->next_to_use)++;
> > +               if (cq->next_to_use == cq->ring_size)
> > +                       cq->next_to_use = 0;
> > +       }
> > +
> > +       /* Force memory write to complete before letting hardware
> > +        * know that there are new descriptors to fetch.
> > +        */
> > +       dma_wmb();
> > +
> > +       wr32(hw, cq->reg.tail, cq->next_to_use);
> > +
> > +sq_send_command_out:
> > +       mutex_unlock(&cq->cq_lock);
> > +
> > +       return status;
> > +}
> > +
> > +/**
> > + * iecm_ctlq_clean_sq - reclaim send descriptors on HW write back for
> the
> > + * requested queue
> > + * @cq: pointer to the specific Control queue
> > + * @clean_count: (input|output) number of descriptors to clean as
> input, and
> > + * number of descriptors actually cleaned as output
> 
> Rather than two meanings for one parameter, can the number cleaned be
> a positive return value?
> 

That seems fair. Will check.

> > + * @msg_status: (output) pointer to msg pointer array to be populated;
> needs
> > + * to be allocated by caller
> > + *
> > + * Returns an array of message pointers associated with the cleaned
> > + * descriptors. The pointers are to the original ctlq_msgs sent on the
> cleaned
> > + * descriptors.  The status will be returned for each; any messages that
> failed
> > + * to send will have a non-zero status. The caller is expected to free
> original
> > + * ctlq_msgs and free or reuse the DMA buffers.
> > + */
> > +int iecm_ctlq_clean_sq(struct iecm_ctlq_info *cq, u16 *clean_count,
> > +                      struct iecm_ctlq_msg *msg_status[])
> > +{
> > +       struct iecm_ctlq_desc *desc;
> > +       u16 i = 0, num_to_clean;
> > +       u16 ntc, desc_err;
> > +       int ret = 0;
> > +
> > +       if (!cq || !cq->ring_size)
> > +               return -ENOBUFS;
> > +
> > +       if (*clean_count == 0)
> > +               return 0;
> > +       if (*clean_count > cq->ring_size)
> > +               return -EBADR;
> > +
> > +       mutex_lock(&cq->cq_lock);
> > +
> > +       ntc = cq->next_to_clean;
> > +
> > +       num_to_clean = *clean_count;
> > +
> > +       for (i = 0; i < num_to_clean; i++) {
> > +               /* Fetch next descriptor and check if marked as done */
> > +               desc = IECM_CTLQ_DESC(cq, ntc);
> > +               if (!(le16_to_cpu(desc->flags) & IECM_CTLQ_FLAG_DD))
> > +                       break;
> > +
> > +               desc_err = le16_to_cpu(desc->ret_val);
> > +               if (desc_err) {
> > +                       /* strip off FW internal code */
> > +                       desc_err &= 0xff;
> > +               }
> 
> Why not simply
>     desc_err = le16_to_cpu(desc->ret_val) & 0xff;
> 

Will fix.

> > +
> > +               msg_status[i] = cq->bi.tx_msg[ntc];
> > +               msg_status[i]->status = desc_err;
> > +
> > +               cq->bi.tx_msg[ntc] = NULL;
> > +
> > +               /* Zero out any stale data */
> > +               memset(desc, 0, sizeof(*desc));
> > +
> > +               ntc++;
> > +               if (ntc == cq->ring_size)
> > +                       ntc = 0;
> > +       }
> > +
> > +       cq->next_to_clean = ntc;
> > +
> > +       mutex_unlock(&cq->cq_lock);
> > +
> > +       /* Return number of descriptors actually cleaned */
> > +       *clean_count = i;
> > +
> > +       return ret;
> > +}
> > +
> > +/**
> > + * iecm_ctlq_post_rx_buffs - post buffers to descriptor ring
> > + * @hw: pointer to hw struct
> > + * @cq: pointer to control queue handle
> > + * @buff_count: (input|output) input is number of buffers caller is
> trying to
> > + * return; output is number of buffers that were not posted
> 
> Same comment.
> And the return would make more sense if consistent with the previous
> function where the return is how many were successful.
> 

Yeah this seems more sane, need a little time to see what the refactor looks like.

> > + * @buffs: array of pointers to dma mem structs to be given to
> hardware
> > + *
> > + * Caller uses this function to return DMA buffers to the descriptor ring
> after
> > + * consuming them; buff_count will be the number of buffers.
> > + *
> > + * Note: this function needs to be called after a receive call even
> > + * if there are no DMA buffers to be returned, i.e. buff_count = 0,
> > + * buffs = NULL to support direct commands
> > + */
> > +int iecm_ctlq_post_rx_buffs(struct iecm_hw *hw, struct iecm_ctlq_info
> *cq,
> > +                           u16 *buff_count, struct iecm_dma_mem **buffs)
> > +{
> > +       struct iecm_ctlq_desc *desc;
> > +       u16 ntp = cq->next_to_post;
> > +       bool buffs_avail = false;
> > +       u16 tbp = ntp + 1;
> > +       int status = 0;
> > +       int i = 0;
> > +
> > +       if (*buff_count > cq->ring_size)
> > +               return -EBADR;
> > +
> > +       if (*buff_count > 0)
> > +               buffs_avail = true;
> > +
> > +       mutex_lock(&cq->cq_lock);
> > +
> > +       if (tbp >= cq->ring_size)
> > +               tbp = 0;
> > +
> > +       if (tbp == cq->next_to_clean)
> > +               /* Nothing to do */
> > +               goto post_buffs_out;
> > +
> > +       /* Post buffers for as many as provided or up until the last one used
> */
> > +       while (ntp != cq->next_to_clean) {
> > +               desc = IECM_CTLQ_DESC(cq, ntp);
> > +
> > +               if (cq->bi.rx_buff[ntp])
> > +                       goto fill_desc;
> > +               if (!buffs_avail) {
> > +                       /* If the caller hasn't given us any buffers or
> > +                        * there are none left, search the ring itself
> > +                        * for an available buffer to move to this
> > +                        * entry starting at the next entry in the ring
> > +                        */
> > +                       tbp = ntp + 1;
> > +
> > +                       /* Wrap ring if necessary */
> > +                       if (tbp >= cq->ring_size)
> > +                               tbp = 0;
> > +
> > +                       while (tbp != cq->next_to_clean) {
> > +                               if (cq->bi.rx_buff[tbp]) {
> > +                                       cq->bi.rx_buff[ntp] =
> > +                                               cq->bi.rx_buff[tbp];
> > +                                       cq->bi.rx_buff[tbp] = NULL;
> > +
> > +                                       /* Found a buffer, no need to
> > +                                        * search anymore
> > +                                        */
> > +                                       break;
> > +                               }
> > +
> > +                               /* Wrap ring if necessary */
> > +                               tbp++;
> > +                               if (tbp >= cq->ring_size)
> > +                                       tbp = 0;
> > +                       }
> > +
> > +                       if (tbp == cq->next_to_clean)
> > +                               goto post_buffs_out;
> > +               } else {
> > +                       /* Give back pointer to DMA buffer */
> > +                       cq->bi.rx_buff[ntp] = buffs[i];
> > +                       i++;
> > +
> > +                       if (i >= *buff_count)
> > +                               buffs_avail = false;
> > +               }
> > +
> > +fill_desc:
> > +               desc->flags =
> > +                       cpu_to_le16(IECM_CTLQ_FLAG_BUF |
> IECM_CTLQ_FLAG_RD);
> > +
> > +               /* Post buffers to descriptor */
> > +               desc->datalen = cpu_to_le16(cq->bi.rx_buff[ntp]->size);
> > +               desc->params.indirect.addr_high =
> > +                       cpu_to_le32(upper_32_bits(cq->bi.rx_buff[ntp]->pa));
> > +               desc->params.indirect.addr_low =
> > +                       cpu_to_le32(lower_32_bits(cq->bi.rx_buff[ntp]->pa));
> > +
> > +               ntp++;
> > +               if (ntp == cq->ring_size)
> > +                       ntp = 0;
> > +       }
> > +
> > +post_buffs_out:
> > +       /* Only update tail if buffers were actually posted */
> > +       if (cq->next_to_post != ntp) {
> > +               if (ntp)
> > +                       /* Update next_to_post to ntp - 1 since current ntp
> > +                        * will not have a buffer
> > +                        */
> > +                       cq->next_to_post = ntp - 1;
> > +               else
> > +                       /* Wrap to end of end ring since current ntp is 0 */
> > +                       cq->next_to_post = cq->ring_size - 1;
> > +
> > +               wr32(hw, cq->reg.tail, cq->next_to_post);
> > +       }
> > +
> > +       mutex_unlock(&cq->cq_lock);
> > +
> > +       /* return the number of buffers that were not posted */
> > +       *buff_count = *buff_count - i;
> > +
> > +       return status;
> > +}
> > +
> > +/**
> > + * iecm_ctlq_recv - receive control queue message call back
> > + * @cq: pointer to control queue handle to receive on
> > + * @num_q_msg: (input|output) input number of messages that should
> be received;
> > + * output number of messages actually received
> > + * @q_msg: (output) array of received control queue messages on this
> q;
> > + * needs to be pre-allocated by caller for as many messages as requested
> > + *
> > + * Called by interrupt handler or polling mechanism. Caller is expected
> > + * to free buffers
> > + */
> > +int iecm_ctlq_recv(struct iecm_ctlq_info *cq, u16 *num_q_msg,
> > +                  struct iecm_ctlq_msg *q_msg)
> > +{
> > +       u16 num_to_clean, ntc, ret_val, flags;
> > +       struct iecm_ctlq_desc *desc;
> > +       int ret_code = 0;
> > +       u16 i = 0;
> > +
> > +       if (!cq || !cq->ring_size)
> > +               return -ENOBUFS;
> 
> Should *num_q_msgs get set to 0 here since none were received?
> 

Based on the function description it does indeed sound like we should be.

> > +
> > +       if (*num_q_msg == 0)
> > +               return 0;
> > +       else if (*num_q_msg > cq->ring_size)
> > +               return -EBADR;
> 
> Again, set *num_q_msgs to 0?
> 
> > +
> > +       /* take the lock before we start messing with the ring */
> > +       mutex_lock(&cq->cq_lock);
> > +
> > +       ntc = cq->next_to_clean;
> > +
> > +       num_to_clean = *num_q_msg;
> > +
> > +       for (i = 0; i < num_to_clean; i++) {
> > +               u64 msg_cookie;
> > +
> > +               /* Fetch next descriptor and check if marked as done */
> > +               desc = IECM_CTLQ_DESC(cq, ntc);
> > +               flags = le16_to_cpu(desc->flags);
> > +
> > +               if (!(flags & IECM_CTLQ_FLAG_DD))
> > +                       break;
> > +
> > +               ret_val = le16_to_cpu(desc->ret_val);
> > +
> > +               q_msg[i].vmvf_type = (flags &
> > +                                     (IECM_CTLQ_FLAG_FTYPE_VM |
> > +                                      IECM_CTLQ_FLAG_FTYPE_PF)) >>
> > +                                     IECM_CTLQ_FLAG_FTYPE_S;
> > +
> > +               if (flags & IECM_CTLQ_FLAG_ERR)
> > +                       ret_code = -EBADMSG;
> 
> So you might return -EBADMSG, even if you've cleaned many messages?
> Will this be reflected in ret_val and does the caller then need to
> hunt down the bad q_msg[].status?
> How these work together should probably be clearly explained in the
> function header.
> 

I'll need some time to look at this, but it does seem suspect.

> > +
> > +               msg_cookie = (u64)le32_to_cpu(desc->cookie_high) << 32;
> > +               msg_cookie |= (u64)le32_to_cpu(desc->cookie_low);
> > +               memcpy(&q_msg[i].cookie, &msg_cookie, sizeof(u64));
> > +
> > +               q_msg[i].opcode = le16_to_cpu(desc->opcode);
> > +               q_msg[i].data_len = le16_to_cpu(desc->datalen);
> > +               q_msg[i].status = ret_val;
> > +
> > +               if (desc->datalen) {
> > +                       memcpy(q_msg[i].ctx.indirect.context,
> > +                              &desc->params.indirect, IECM_INDIRECT_CTX_SIZE);
> > +
> > +                       /* Assign pointer to dma buffer to ctlq_msg array
> > +                        * to be given to upper layer
> > +                        */
> > +                       q_msg[i].ctx.indirect.payload = cq->bi.rx_buff[ntc];
> > +
> > +                       /* Zero out pointer to DMA buffer info;
> > +                        * will be repopulated by post buffers API
> > +                        */
> > +                       cq->bi.rx_buff[ntc] = NULL;
> > +               } else {
> > +                       memcpy(q_msg[i].ctx.direct, desc->params.raw,
> > +                              IECM_DIRECT_CTX_SIZE);
> > +               }
> > +
> > +               /* Zero out stale data in descriptor */
> > +               memset(desc, 0, sizeof(struct iecm_ctlq_desc));
> > +
> > +               ntc++;
> > +               if (ntc == cq->ring_size)
> > +                       ntc = 0;
> > +       };
> > +
> > +       cq->next_to_clean = ntc;
> > +
> > +       mutex_unlock(&cq->cq_lock);
> > +
> > +       *num_q_msg = i;
> > +       if (*num_q_msg == 0)
> > +               ret_code = -ENOMSG;
> > +
> > +       return ret_code;
> > +}
> > diff --git a/drivers/net/ethernet/intel/iecm/iecm_controlq_setup.c
> b/drivers/net/ethernet/intel/iecm/iecm_controlq_setup.c
> > new file mode 100644
> > index 000000000000..a36fc88d6bb5
> > --- /dev/null
> > +++ b/drivers/net/ethernet/intel/iecm/iecm_controlq_setup.c
> > @@ -0,0 +1,175 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +/* Copyright (c) 2020, Intel Corporation. */
> > +
> > +#include "iecm_controlq.h"
> > +
> > +/**
> > + * iecm_ctlq_alloc_desc_ring - Allocate Control Queue (CQ) rings
> > + * @hw: pointer to hw struct
> > + * @cq: pointer to the specific Control queue
> > + */
> > +static int
> > +iecm_ctlq_alloc_desc_ring(struct iecm_hw *hw,
> > +                         struct iecm_ctlq_info *cq)
> > +{
> > +       size_t size = cq->ring_size * sizeof(struct iecm_ctlq_desc);
> > +
> > +       cq->desc_ring.va = iecm_alloc_dma_mem(hw, &cq->desc_ring,
> size);
> > +       if (!cq->desc_ring.va)
> > +               return -ENOMEM;
> > +
> > +       return 0;
> > +}
> > +
> > +/**
> > + * iecm_ctlq_alloc_bufs - Allocate Control Queue (CQ) buffers
> > + * @hw: pointer to hw struct
> > + * @cq: pointer to the specific Control queue
> > + *
> > + * Allocate the buffer head for all control queues, and if it's a receive
> > + * queue, allocate DMA buffers
> > + */
> > +static int iecm_ctlq_alloc_bufs(struct iecm_hw *hw,
> > +                               struct iecm_ctlq_info *cq)
> > +{
> > +       int i = 0;
> > +
> > +       /* Do not allocate DMA buffers for transmit queues */
> > +       if (cq->cq_type == IECM_CTLQ_TYPE_MAILBOX_TX)
> > +               return 0;
> > +
> > +       /* We'll be allocating the buffer info memory first, then we can
> > +        * allocate the mapped buffers for the event processing
> > +        */
> > +       cq->bi.rx_buff = kcalloc(cq->ring_size, sizeof(struct iecm_dma_mem
> *),
> > +                                GFP_KERNEL);
> > +       if (!cq->bi.rx_buff)
> > +               return -ENOMEM;
> > +
> > +       /* allocate the mapped buffers (except for the last one) */
> > +       for (i = 0; i < cq->ring_size - 1; i++) {
> > +               struct iecm_dma_mem *bi;
> > +               int num = 1; /* number of iecm_dma_mem to be allocated */
> > +
> > +               cq->bi.rx_buff[i] = kcalloc(num, sizeof(struct iecm_dma_mem),
> > +                                           GFP_KERNEL);
> > +               if (!cq->bi.rx_buff[i])
> > +                       goto unwind_alloc_cq_bufs;
> > +
> > +               bi = cq->bi.rx_buff[i];
> > +
> > +               bi->va = iecm_alloc_dma_mem(hw, bi, cq->buf_size);
> > +               if (!bi->va) {
> > +                       /* unwind will not free the failed entry */
> > +                       kfree(cq->bi.rx_buff[i]);
> > +                       goto unwind_alloc_cq_bufs;
> > +               }
> > +       }
> > +
> > +       return 0;
> > +
> > +unwind_alloc_cq_bufs:
> > +       /* don't try to free the one that failed... */
> > +       i--;
> > +       for (; i >= 0; i--) {
> > +               iecm_free_dma_mem(hw, cq->bi.rx_buff[i]);
> > +               kfree(cq->bi.rx_buff[i]);
> > +       }
> > +       kfree(cq->bi.rx_buff);
> > +
> > +       return -ENOMEM;
> > +}
> > +
> > +/**
> > + * iecm_ctlq_free_desc_ring - Free Control Queue (CQ) rings
> > + * @hw: pointer to hw struct
> > + * @cq: pointer to the specific Control queue
> > + *
> > + * This assumes the posted send buffers have already been cleaned
> > + * and de-allocated
> > + */
> > +static void iecm_ctlq_free_desc_ring(struct iecm_hw *hw,
> > +                                    struct iecm_ctlq_info *cq)
> > +{
> > +       iecm_free_dma_mem(hw, &cq->desc_ring);
> > +}
> > +
> > +/**
> > + * iecm_ctlq_free_bufs - Free CQ buffer info elements
> > + * @hw: pointer to hw struct
> > + * @cq: pointer to the specific Control queue
> > + *
> > + * Free the DMA buffers for RX queues, and DMA buffer header for both
> RX and TX
> > + * queues.  The upper layers are expected to manage freeing of TX DMA
> buffers
> > + */
> > +static void iecm_ctlq_free_bufs(struct iecm_hw *hw, struct
> iecm_ctlq_info *cq)
> > +{
> > +       void *bi;
> > +
> > +       if (cq->cq_type == IECM_CTLQ_TYPE_MAILBOX_RX) {
> > +               int i;
> > +
> > +               /* free DMA buffers for rx queues*/
> > +               for (i = 0; i < cq->ring_size; i++) {
> > +                       if (cq->bi.rx_buff[i]) {
> > +                               iecm_free_dma_mem(hw, cq->bi.rx_buff[i]);
> > +                               kfree(cq->bi.rx_buff[i]);
> > +                       }
> > +               }
> > +
> > +               bi = (void *)cq->bi.rx_buff;
> > +       } else {
> > +               bi = (void *)cq->bi.tx_msg;
> > +       }
> > +
> > +       /* free the buffer header */
> > +       kfree(bi);
> > +}
> > +
> > +/**
> > + * iecm_ctlq_dealloc_ring_res - Free memory allocated for control queue
> > + * @hw: pointer to hw struct
> > + * @cq: pointer to the specific Control queue
> > + *
> > + * Free the memory used by the ring, buffers and other related
> structures
> > + */
> > +void iecm_ctlq_dealloc_ring_res(struct iecm_hw *hw, struct
> iecm_ctlq_info *cq)
> > +{
> > +       /* free ring buffers and the ring itself */
> > +       iecm_ctlq_free_bufs(hw, cq);
> > +       iecm_ctlq_free_desc_ring(hw, cq);
> > +}
> > +
> > +/**
> > + * iecm_ctlq_alloc_ring_res - allocate memory for descriptor ring and
> bufs
> > + * @hw: pointer to hw struct
> > + * @cq: pointer to control queue struct
> > + *
> > + * Do *NOT* hold the lock when calling this as the memory allocation
> routines
> > + * called are not going to be atomic context safe
> > + */
> > +int iecm_ctlq_alloc_ring_res(struct iecm_hw *hw, struct iecm_ctlq_info
> *cq)
> > +{
> > +       int ret_code;
> > +
> > +       /* verify input for valid configuration */
> > +       if (!cq->ring_size || !cq->buf_size)
> > +               return -EINVAL;
> > +
> > +       /* allocate the ring memory */
> > +       ret_code = iecm_ctlq_alloc_desc_ring(hw, cq);
> > +       if (ret_code)
> > +               return ret_code;
> > +
> > +       /* allocate buffers in the rings */
> > +       ret_code = iecm_ctlq_alloc_bufs(hw, cq);
> > +       if (ret_code)
> > +               goto iecm_init_cq_free_ring;
> > +
> > +       /* success! */
> > +       return 0;
> > +
> > +iecm_init_cq_free_ring:
> > +       iecm_free_dma_mem(hw, &cq->desc_ring);
> > +       return ret_code;
> > +}
> > diff --git a/drivers/net/ethernet/intel/iecm/iecm_lib.c
> b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > index e6d0b418a27f..64cdbce2c842 100644
> > --- a/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > +++ b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > @@ -5,6 +5,25 @@
> >
> >  #include "iecm.h"
> >
> > +/**
> > + * iecm_cfg_hw - Initialize HW struct
> > + * @adapter: adapter to setup hw struct for
> > + *
> > + * Returns 0 on success, negative on failure
> > + */
> > +static int iecm_cfg_hw(struct iecm_adapter *adapter)
> > +{
> > +       struct pci_dev *pdev = adapter->pdev;
> > +       struct iecm_hw *hw = &adapter->hw;
> > +
> > +       hw->hw_addr = pcim_iomap_table(pdev)[IECM_BAR0];
> > +       if (!hw->hw_addr)
> > +               return -EIO;
> > +       hw->back = adapter;
> > +
> > +       return 0;
> > +}
> > +
> >  /**
> >   * iecm_statistics_task - Delayed task to get statistics over mailbox
> >   * @work: work_struct handle to our data
> > @@ -39,6 +58,32 @@ static void iecm_init_task(struct work_struct
> *work)
> >         /* stub */
> >  }
> >
> > +/**
> > + * iecm_api_init - Initialize and verify device API
> > + * @adapter: driver specific private structure
> > + *
> > + * Returns 0 on success, negative on failure
> > + */
> > +static int iecm_api_init(struct iecm_adapter *adapter)
> > +{
> > +       struct iecm_reg_ops *reg_ops = &adapter->dev_ops.reg_ops;
> > +       struct pci_dev *pdev = adapter->pdev;
> > +
> > +       if (!adapter->dev_ops.reg_ops_init) {
> > +               dev_err(&pdev->dev, "Invalid device, register API init not
> defined\n");
> > +               return -EINVAL;
> > +       }
> > +       adapter->dev_ops.reg_ops_init(adapter);
> > +       if (!(reg_ops->ctlq_reg_init && reg_ops->intr_reg_init &&
> > +             reg_ops->mb_intr_reg_init && reg_ops->reset_reg_init &&
> > +             reg_ops->trigger_reset)) {
> > +               dev_err(&pdev->dev, "Invalid device, missing one or more
> register functions\n");
> > +               return -EINVAL;
> > +       }
> > +
> > +       return 0;
> > +}
> > +
> >  /**
> >   * iecm_deinit_task - Device deinit routine
> >   * @adapter: Driver specific private structue
> > @@ -51,13 +96,108 @@ static void iecm_deinit_task(struct
> iecm_adapter *adapter)
> >         /* stub */
> >  }
> >
> > +/**
> > + * iecm_check_reset_complete - check that reset is complete
> > + * @hw: pointer to hw struct
> > + * @reset_reg: struct with reset registers
> > + *
> > + * Returns 0 if device is ready to use, or -EBUSY if it's in reset.
> > + **/
> > +static int iecm_check_reset_complete(struct iecm_hw *hw,
> > +                                    struct iecm_reset_reg *reset_reg)
> > +{
> > +       struct iecm_adapter *adapter = (struct iecm_adapter *)hw->back;
> > +       int i;
> > +
> > +       for (i = 0; i < 2000; i++) {
> > +               u32 reg_val = rd32(hw, reset_reg->rstat);
> > +
> > +               /* 0xFFFFFFFF might be read if other side hasn't cleared the
> > +                * register for us yet and 0xFFFFFFFF is not a valid value for
> > +                * the register, so treat that as invalid.
> > +                */
> > +               if (reg_val != 0xFFFFFFFF && (reg_val & reset_reg->rstat_m))
> > +                       return 0;
> > +               usleep_range(5000, 10000);
> > +       }
> > +
> > +       dev_warn(&adapter->pdev->dev, "Device reset timeout!\n");
> > +       return -EBUSY;
> > +}
> > +
> > +/**
> > + * iecm_init_hard_reset - Initiate a hardware reset
> > + * @adapter: Driver specific private structure
> > + *
> > + * Deallocate the vports and all the resources associated with them and
> > + * reallocate. Also reinitialize the mailbox. Return 0 on success,
> > + * negative on failure.
> > + */
> > +static int iecm_init_hard_reset(struct iecm_adapter *adapter)
> > +{
> > +       int err = 0;
> > +
> > +       mutex_lock(&adapter->reset_lock);
> > +
> > +       /* Prepare for reset */
> > +       if (test_and_clear_bit(__IECM_HR_DRV_LOAD, adapter->flags)) {
> > +               adapter->dev_ops.reg_ops.trigger_reset(adapter,
> > +                                                      __IECM_HR_DRV_LOAD);
> > +       } else if (test_and_clear_bit(__IECM_HR_FUNC_RESET, adapter-
> >flags)) {
> > +               bool is_reset = iecm_is_reset_detected(adapter);
> > +
> > +               if (adapter->state == __IECM_UP)
> > +                       set_bit(__IECM_UP_REQUESTED, adapter->flags);
> > +               iecm_deinit_task(adapter);
> > +               if (!is_reset)
> > +                       adapter->dev_ops.reg_ops.trigger_reset(adapter,
> > +                                                              __IECM_HR_FUNC_RESET);
> > +               iecm_deinit_dflt_mbx(adapter);
> > +       } else if (test_and_clear_bit(__IECM_HR_CORE_RESET, adapter-
> >flags)) {
> > +               if (adapter->state == __IECM_UP)
> > +                       set_bit(__IECM_UP_REQUESTED, adapter->flags);
> > +               iecm_deinit_task(adapter);
> > +       } else {
> > +               dev_err(&adapter->pdev->dev, "Unhandled hard reset
> cause\n");
> > +               err = -EBADRQC;
> > +               goto handle_err;
> > +       }
> > +
> > +       /* Wait for reset to complete */
> > +       err = iecm_check_reset_complete(&adapter->hw, &adapter-
> >reset_reg);
> > +       if (err) {
> > +               dev_err(&adapter->pdev->dev, "The driver was unable to
> contact the device's firmware.  Check that the FW is running. Driver
> state=%u\n",
> > +                       adapter->state);
> > +               goto handle_err;
> > +       }
> > +
> > +       /* Reset is complete and so start building the driver resources again
> */
> > +       err = iecm_init_dflt_mbx(adapter);
> > +       if (err) {
> > +               dev_err(&adapter->pdev->dev, "Failed to initialize default
> mailbox: %d\n",
> > +                       err);
> > +       }
> > +handle_err:
> > +       mutex_unlock(&adapter->reset_lock);
> > +       return err;
> > +}
> > +
> >  /**
> >   * iecm_vc_event_task - Handle virtchannel event logic
> >   * @work: work queue struct
> >   */
> >  static void iecm_vc_event_task(struct work_struct *work)
> >  {
> > -       /* stub */
> > +       struct iecm_adapter *adapter = container_of(work,
> > +                                                   struct iecm_adapter,
> > +                                                   vc_event_task.work);
> > +
> > +       if (test_bit(__IECM_HR_CORE_RESET, adapter->flags) ||
> > +           test_bit(__IECM_HR_FUNC_RESET, adapter->flags) ||
> > +           test_bit(__IECM_HR_DRV_LOAD, adapter->flags)) {
> > +               set_bit(__IECM_HR_RESET_IN_PROG, adapter->flags);
> > +               iecm_init_hard_reset(adapter);
> > +       }
> >  }
> >
> >  /**
> > @@ -75,6 +215,11 @@ int iecm_probe(struct pci_dev *pdev,
> >         int err;
> >
> >         adapter->pdev = pdev;
> > +       err = iecm_api_init(adapter);
> > +       if (err) {
> > +               dev_err(&pdev->dev, "Device API is incorrectly configured\n");
> > +               return err;
> > +       }
> >
> >         err = pcim_enable_device(pdev);
> >         if (err)
> > @@ -147,6 +292,20 @@ int iecm_probe(struct pci_dev *pdev,
> >                 goto err_netdev_alloc;
> >         }
> >
> > +       err = iecm_vport_params_buf_alloc(adapter);
> > +       if (err) {
> > +               dev_err(&pdev->dev, "Failed to alloc vport params buffer:
> %d\n",
> > +                       err);
> > +               goto err_mb_res;
> > +       }
> > +
> > +       err = iecm_cfg_hw(adapter);
> > +       if (err) {
> > +               dev_err(&pdev->dev, "Failed to configure HW structure for
> adapter: %d\n",
> > +                       err);
> > +               goto err_cfg_hw;
> > +       }
> > +
> >         mutex_init(&adapter->sw_mutex);
> >         mutex_init(&adapter->reset_lock);
> >         init_waitqueue_head(&adapter->vchnl_wq);
> > @@ -166,11 +325,16 @@ int iecm_probe(struct pci_dev *pdev,
> >         INIT_DELAYED_WORK(&adapter->init_task, iecm_init_task);
> >         INIT_DELAYED_WORK(&adapter->vc_event_task,
> iecm_vc_event_task);
> >
> > +       adapter->dev_ops.reg_ops.reset_reg_init(&adapter->reset_reg);
> >         set_bit(__IECM_HR_DRV_LOAD, adapter->flags);
> >         queue_delayed_work(adapter->vc_event_wq, &adapter-
> >vc_event_task,
> >                            msecs_to_jiffies(10 * (pdev->devfn & 0x07)));
> >
> >         return 0;
> > +err_cfg_hw:
> > +       iecm_vport_params_buf_rel(adapter);
> > +err_mb_res:
> > +       kfree(adapter->netdevs);
> >  err_netdev_alloc:
> >         kfree(adapter->vports);
> >  err_vport_alloc:
> > @@ -214,6 +378,7 @@ void iecm_remove(struct pci_dev *pdev)
> >         cancel_delayed_work_sync(&adapter->vc_event_task);
> >         iecm_deinit_task(adapter);
> >         iecm_del_user_cfg_data(adapter);
> > +       iecm_deinit_dflt_mbx(adapter);
> >         msleep(20);
> >         destroy_workqueue(adapter->serv_wq);
> >         destroy_workqueue(adapter->vc_event_wq);
> > @@ -222,6 +387,7 @@ void iecm_remove(struct pci_dev *pdev)
> >         kfree(adapter->vports);
> >         kfree(adapter->netdevs);
> >         kfree(adapter->vlan_caps);
> > +       iecm_vport_params_buf_rel(adapter);
> >         mutex_destroy(&adapter->sw_mutex);
> >         mutex_destroy(&adapter->reset_lock);
> >         pci_disable_pcie_error_reporting(pdev);
> > @@ -229,3 +395,26 @@ void iecm_remove(struct pci_dev *pdev)
> >         pci_disable_device(pdev);
> >  }
> >  EXPORT_SYMBOL(iecm_remove);
> > +
> > +void *iecm_alloc_dma_mem(struct iecm_hw *hw, struct
> iecm_dma_mem *mem, u64 size)
> > +{
> > +       struct iecm_adapter *adapter = (struct iecm_adapter *)hw->back;
> > +       size_t sz = ALIGN(size, 4096);
> > +
> > +       mem->va = dma_alloc_coherent(&adapter->pdev->dev, sz,
> > +                                    &mem->pa, GFP_KERNEL | __GFP_ZERO);
> > +       mem->size = size;
> > +
> > +       return mem->va;
> > +}
> > +
> > +void iecm_free_dma_mem(struct iecm_hw *hw, struct iecm_dma_mem
> *mem)
> > +{
> > +       struct iecm_adapter *adapter = (struct iecm_adapter *)hw->back;
> > +
> > +       dma_free_coherent(&adapter->pdev->dev, mem->size,
> > +                         mem->va, mem->pa);
> > +       mem->size = 0;
> > +       mem->va = NULL;
> > +       mem->pa = 0;
> > +}
> > diff --git a/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> > new file mode 100644
> > index 000000000000..b8f54b8c700a
> > --- /dev/null
> > +++ b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> > @@ -0,0 +1,172 @@
> > +// SPDX-License-Identifier: GPL-2.0-only
> > +/* Copyright (C) 2019 Intel Corporation */
> > +
> > +#include "iecm.h"
> > +
> > +/**
> > + * iecm_mb_clean - Reclaim the send mailbox queue entries
> > + * @adapter: Driver specific private structure
> > + *
> > + * Reclaim the send mailbox queue entries to be used to send further
> messages
> > + *
> > + * Returns 0 on success, negative on failure
> > + */
> > +static int iecm_mb_clean(struct iecm_adapter *adapter)
> > +{
> > +       u16 i, num_q_msg = IECM_DFLT_MBX_Q_LEN;
> > +       struct iecm_ctlq_msg **q_msg;
> > +       struct iecm_dma_mem *dma_mem;
> > +       int err = 0;
> > +
> > +       q_msg = kcalloc(num_q_msg, sizeof(struct iecm_ctlq_msg *),
> GFP_KERNEL);
> > +       if (!q_msg)
> > +               return -ENOMEM;
> > +
> > +       err = iecm_ctlq_clean_sq(adapter->hw.asq, &num_q_msg, q_msg);
> > +       if (err)
> > +               goto error;
> > +
> > +       for (i = 0; i < num_q_msg; i++) {
> > +               dma_mem = q_msg[i]->ctx.indirect.payload;
> > +               if (dma_mem)
> > +                       dmam_free_coherent(&adapter->pdev->dev, dma_mem-
> >size,
> > +                                          dma_mem->va, dma_mem->pa);
> > +               kfree(q_msg[i]);
> > +               kfree(dma_mem);
> > +       }
> > +error:
> > +       kfree(q_msg);
> > +       return err;
> > +}
> > +
> > +/**
> > + * iecm_find_ctlq - Given a type and id, find ctlq info
> > + * @hw: hardware struct
> > + * @type: type of ctrlq to find
> > + * @id: ctlq id to find
> > + *
> > + * Returns pointer to found ctlq info struct, NULL otherwise.
> > + */
> > +static struct iecm_ctlq_info *iecm_find_ctlq(struct iecm_hw *hw,
> > +                                            enum iecm_ctlq_type type, int id)
> > +{
> > +       struct iecm_ctlq_info *cq, *tmp;
> > +
> > +       list_for_each_entry_safe(cq, tmp, &hw->cq_list_head, cq_list) {
> > +               if (cq->q_id == id && cq->cq_type == type)
> > +                       return cq;
> > +       }
> > +
> > +       return NULL;
> > +}
> > +
> > +/**
> > + * iecm_init_dflt_mbx - Setup default mailbox parameters and make
> request
> > + * @adapter: adapter info struct
> > + *
> > + * Returns 0 on success, negative otherwise
> > + */
> > +int iecm_init_dflt_mbx(struct iecm_adapter *adapter)
> > +{
> > +       struct iecm_ctlq_create_info ctlq_info[] = {
> > +               {
> > +                       .type = IECM_CTLQ_TYPE_MAILBOX_TX,
> > +                       .id = IECM_DFLT_MBX_ID,
> > +                       .len = IECM_DFLT_MBX_Q_LEN,
> > +                       .buf_size = IECM_DFLT_MBX_BUF_SIZE
> > +               },
> > +               {
> > +                       .type = IECM_CTLQ_TYPE_MAILBOX_RX,
> > +                       .id = IECM_DFLT_MBX_ID,
> > +                       .len = IECM_DFLT_MBX_Q_LEN,
> > +                       .buf_size = IECM_DFLT_MBX_BUF_SIZE
> > +               }
> > +       };
> > +       struct iecm_hw *hw = &adapter->hw;
> > +       int err;
> > +
> > +       adapter->dev_ops.reg_ops.ctlq_reg_init(ctlq_info);
> > +
> > +#define NUM_Q 2
> > +       err = iecm_ctlq_init(hw, NUM_Q, ctlq_info);
> > +       if (err)
> > +               return err;
> > +
> > +       hw->asq = iecm_find_ctlq(hw, IECM_CTLQ_TYPE_MAILBOX_TX,
> > +                                IECM_DFLT_MBX_ID);
> > +       hw->arq = iecm_find_ctlq(hw, IECM_CTLQ_TYPE_MAILBOX_RX,
> > +                                IECM_DFLT_MBX_ID);
> > +
> > +       if (!hw->asq || !hw->arq) {
> > +               iecm_ctlq_deinit(hw);
> > +               return -ENOENT;
> > +       }
> > +       adapter->state = __IECM_STARTUP;
> > +       /* Skew the delay for init tasks for each function based on fn
> number
> > +        * to prevent every function from making the same call
> simulatenously.
> > +        */
> > +       queue_delayed_work(adapter->init_wq, &adapter->init_task,
> > +                          msecs_to_jiffies(5 * (adapter->pdev->devfn & 0x07)));
> > +       return 0;
> > +}
> > +
> > +/**
> > + * iecm_deinit_dflt_mbx - Free up ctlqs setup
> > + * @adapter: Driver specific private data structure
> > + */
> > +void iecm_deinit_dflt_mbx(struct iecm_adapter *adapter)
> > +{
> > +       if (adapter->hw.arq && adapter->hw.asq) {
> > +               iecm_mb_clean(adapter);
> > +               iecm_ctlq_deinit(&adapter->hw);
> > +       }
> > +       adapter->hw.arq = NULL;
> > +       adapter->hw.asq = NULL;
> > +}
> > +
> > +/**
> > + * iecm_vport_params_buf_alloc - Allocate memory for MailBox
> resources
> > + * @adapter: Driver specific private data structure
> > + *
> > + * Will alloc memory to hold the vport parameters received on MailBox
> > + */
> > +int iecm_vport_params_buf_alloc(struct iecm_adapter *adapter)
> > +{
> > +       adapter->vport_params_reqd = kcalloc(IECM_MAX_NUM_VPORTS,
> > +                                            sizeof(*adapter->vport_params_reqd),
> > +                                            GFP_KERNEL);
> > +       if (!adapter->vport_params_reqd)
> > +               return -ENOMEM;
> > +
> > +       adapter->vport_params_recvd = kcalloc(IECM_MAX_NUM_VPORTS,
> > +                                             sizeof(*adapter->vport_params_recvd),
> > +                                             GFP_KERNEL);
> > +       if (!adapter->vport_params_recvd) {
> > +               kfree(adapter->vport_params_reqd);
> > +               return -ENOMEM;
> > +       }
> > +
> > +       return 0;
> > +}
> > +
> > +/**
> > + * iecm_vport_params_buf_rel - Release memory for MailBox resources
> > + * @adapter: Driver specific private data structure
> > + *
> > + * Will release memory to hold the vport parameters received on
> MailBox
> > + */
> > +void iecm_vport_params_buf_rel(struct iecm_adapter *adapter)
> > +{
> > +       int i = 0;
> > +
> > +       for (i = 0; i < IECM_MAX_NUM_VPORTS; i++) {
> > +               kfree(adapter->vport_params_recvd[i]);
> > +               kfree(adapter->vport_params_reqd[i]);
> > +       }
> > +
> > +       kfree(adapter->vport_params_recvd);
> > +       kfree(adapter->vport_params_reqd);
> > +
> > +       kfree(adapter->caps);
> > +       kfree(adapter->config_data.req_qs_chunks);
> > +}
> > diff --git a/drivers/net/ethernet/intel/include/iecm.h
> b/drivers/net/ethernet/intel/include/iecm.h
> > index e19e014e9817..ca9029224e06 100644
> > --- a/drivers/net/ethernet/intel/include/iecm.h
> > +++ b/drivers/net/ethernet/intel/include/iecm.h
> > @@ -12,15 +12,33 @@
> >  #include <linux/dim.h>
> >
> >  #include "iecm_txrx.h"
> > +#include "iecm_controlq.h"
> >
> >  #define IECM_BAR0                      0
> >  #define IECM_NO_FREE_SLOT              0xffff
> >
> > +/* Default Mailbox settings */
> > +#define IECM_DFLT_MBX_BUF_SIZE         (4 * 1024)
> > +#define IECM_NUM_QCTX_PER_MSG          3
> > +#define IECM_NUM_FILTERS_PER_MSG       20
> > +#define IECM_VLANS_PER_MSG \
> > +       ((IECM_DFLT_MBX_BUF_SIZE - sizeof(struct
> virtchnl_vlan_filter_list)) \
> > +        / sizeof(u16))
> > +#define IECM_DFLT_MBX_Q_LEN            64
> > +#define IECM_DFLT_MBX_ID               -1
> > +/* maximum number of times to try before resetting mailbox */
> > +#define IECM_MB_MAX_ERR                        20
> > +#define IECM_NUM_CHUNKS_PER_MSG(a, b)
> ((IECM_DFLT_MBX_BUF_SIZE - (a)) / (b))
> > +
> >  #define IECM_MAX_NUM_VPORTS            1
> >
> >  /* available message levels */
> >  #define IECM_AVAIL_NETIF_M (NETIF_MSG_DRV | NETIF_MSG_PROBE |
> NETIF_MSG_LINK)
> >
> > +/* Forward declaration */
> > +struct iecm_adapter;
> > +struct iecm_vport;
> > +
> >  enum iecm_state {
> >         __IECM_STARTUP,
> >         __IECM_VER_CHECK,
> > @@ -77,6 +95,22 @@ struct iecm_reset_reg {
> >         u32 rstat_m;
> >  };
> >
> > +/* product specific register API */
> > +struct iecm_reg_ops {
> > +       void (*ctlq_reg_init)(struct iecm_ctlq_create_info *cq);
> > +       int (*intr_reg_init)(struct iecm_vport *vport);
> > +       void (*mb_intr_reg_init)(struct iecm_adapter *adapter);
> > +       void (*reset_reg_init)(struct iecm_reset_reg *reset_reg);
> > +       void (*trigger_reset)(struct iecm_adapter *adapter,
> > +                             enum iecm_flags trig_cause);
> > +};
> > +
> > +struct iecm_dev_ops {
> > +       void (*reg_ops_init)(struct iecm_adapter *adapter);
> > +       void (*crc_enable)(u64 *td_cmd);
> > +       struct iecm_reg_ops reg_ops;
> > +};
> > +
> >  /* stub */
> >  struct iecm_vport {
> >  };
> > @@ -124,6 +158,7 @@ struct iecm_adapter {
> >         DECLARE_BITMAP(flags, __IECM_FLAGS_NBITS);
> >         struct mutex reset_lock; /* lock to protect reset flows */
> >         struct iecm_reset_reg reset_reg;
> > +       struct iecm_hw hw;
> >
> >         u16 num_req_msix;
> >         u16 num_msix_entries;
> > @@ -156,6 +191,7 @@ struct iecm_adapter {
> >         wait_queue_head_t vchnl_wq;
> >         wait_queue_head_t sw_marker_wq;
> >         struct iecm_rss_data rss_data;
> > +       struct iecm_dev_ops dev_ops;
> >         s32 link_speed;
> >         /* This is only populated if the VIRTCHNL_VF_CAP_ADV_LINK_SPEED
> is set
> >          * in vf_res->vf_cap_flags. This field should be used going forward
> and
> > @@ -179,8 +215,24 @@ struct iecm_adapter {
> >         spinlock_t fdir_fltr_list_lock;
> >  };
> >
> > +/**
> > + * iecm_is_reset_detected - check if we were reset at some point
> > + * @adapter: driver specific private structure
> > + *
> > + * Returns true if we are either in reset currently or were previously
> reset.
> > + */
> > +static inline bool iecm_is_reset_detected(struct iecm_adapter *adapter)
> > +{
> > +       return !(rd32(&adapter->hw, adapter->hw.arq->reg.len) &
> > +                adapter->hw.arq->reg.len_ena_mask);
> > +}
> > +
> >  int iecm_probe(struct pci_dev *pdev,
> >                const struct pci_device_id __always_unused *ent,
> >                struct iecm_adapter *adapter);
> >  void iecm_remove(struct pci_dev *pdev);
> > +int iecm_init_dflt_mbx(struct iecm_adapter *adapter);
> > +void iecm_deinit_dflt_mbx(struct iecm_adapter *adapter);
> > +int iecm_vport_params_buf_alloc(struct iecm_adapter *adapter);
> > +void iecm_vport_params_buf_rel(struct iecm_adapter *adapter);
> >  #endif /* !_IECM_H_ */
> > diff --git a/drivers/net/ethernet/intel/include/iecm_controlq.h
> b/drivers/net/ethernet/intel/include/iecm_controlq.h
> > new file mode 100644
> > index 000000000000..f2539baa2ce1
> > --- /dev/null
> > +++ b/drivers/net/ethernet/intel/include/iecm_controlq.h
> > @@ -0,0 +1,117 @@
> > +/* SPDX-License-Identifier: GPL-2.0-only */
> > +/* Copyright (c) 2020, Intel Corporation. */
> > +
> > +#ifndef _IECM_CONTROLQ_H_
> > +#define _IECM_CONTROLQ_H_
> > +
> > +#include <linux/slab.h>
> > +
> > +#include "iecm_controlq_api.h"
> > +
> > +/* Maximum buffer lengths for all control queue types */
> > +#define IECM_CTLQ_MAX_RING_SIZE 1024
> > +#define IECM_CTLQ_MAX_BUF_LEN  4096
> > +
> > +#define IECM_CTLQ_DESC(R, i) \
> > +       (&(((struct iecm_ctlq_desc *)((R)->desc_ring.va))[i]))
> > +
> > +#define IECM_CTLQ_DESC_UNUSED(R) \
> > +       ((u16)((((R)->next_to_clean > (R)->next_to_use) ? 0 : (R)->ring_size)
> + \
> > +             (R)->next_to_clean - (R)->next_to_use - 1))
> > +
> > +/* Control Queue default settings */
> > +#define IECM_CTRL_SQ_CMD_TIMEOUT       250  /* msecs */
> > +
> > +struct iecm_ctlq_desc {
> > +       __le16  flags;
> > +       __le16  opcode;
> > +       __le16  datalen;        /* 0 for direct commands */
> > +       union {
> > +               __le16 ret_val;
> > +               __le16 pfid_vfid;
> > +#define IECM_CTLQ_DESC_VF_ID_S 0
> > +#define IECM_CTLQ_DESC_VF_ID_M (0x7FF <<
> IECM_CTLQ_DESC_VF_ID_S)
> > +#define IECM_CTLQ_DESC_PF_ID_S 11
> > +#define IECM_CTLQ_DESC_PF_ID_M (0x1F <<
> IECM_CTLQ_DESC_PF_ID_S)
> > +       };
> > +       __le32 cookie_high;
> > +       __le32 cookie_low;
> > +       union {
> > +               struct {
> > +                       __le32 param0;
> > +                       __le32 param1;
> > +                       __le32 param2;
> > +                       __le32 param3;
> > +               } direct;
> > +               struct {
> > +                       __le32 param0;
> > +                       __le32 param1;
> > +                       __le32 addr_high;
> > +                       __le32 addr_low;
> > +               } indirect;
> > +               u8 raw[16];
> > +       } params;
> > +};
> > +
> > +/* Flags sub-structure
> > + * |0  |1  |2  |3  |4  |5  |6  |7  |8  |9  |10 |11 |12 |13 |14 |15 |
> > + * |DD |CMP|ERR|  * RSV *  |FTYPE  | *RSV* |RD |VFC|BUF|  * RSV *  |
> > + */
> > +/* command flags and offsets */
> > +#define IECM_CTLQ_FLAG_DD_S    0
> > +#define IECM_CTLQ_FLAG_CMP_S   1
> > +#define IECM_CTLQ_FLAG_ERR_S   2
> > +#define IECM_CTLQ_FLAG_FTYPE_S 6
> > +#define IECM_CTLQ_FLAG_RD_S    10
> > +#define IECM_CTLQ_FLAG_VFC_S   11
> > +#define IECM_CTLQ_FLAG_BUF_S   12
> > +
> > +#define IECM_CTLQ_FLAG_DD      BIT(IECM_CTLQ_FLAG_DD_S)        /*
> 0x1    */
> > +#define IECM_CTLQ_FLAG_CMP     BIT(IECM_CTLQ_FLAG_CMP_S)       /*
> 0x2    */
> > +#define IECM_CTLQ_FLAG_ERR     BIT(IECM_CTLQ_FLAG_ERR_S)       /*
> 0x4    */
> > +#define IECM_CTLQ_FLAG_FTYPE_VM
> BIT(IECM_CTLQ_FLAG_FTYPE_S)     /* 0x40   */
> > +#define IECM_CTLQ_FLAG_FTYPE_PF        BIT(IECM_CTLQ_FLAG_FTYPE_S
> + 1) /* 0x80   */
> > +#define IECM_CTLQ_FLAG_RD      BIT(IECM_CTLQ_FLAG_RD_S)        /*
> 0x400  */
> > +#define IECM_CTLQ_FLAG_VFC     BIT(IECM_CTLQ_FLAG_VFC_S)       /*
> 0x800  */
> > +#define IECM_CTLQ_FLAG_BUF     BIT(IECM_CTLQ_FLAG_BUF_S)       /*
> 0x1000 */
> > +
> > +struct iecm_mbxq_desc {
> > +       u8 pad[8];              /* CTLQ flags/opcode/len/retval fields */
> > +       u32 chnl_opcode;        /* avoid confusion with desc->opcode */
> > +       u32 chnl_retval;        /* ditto for desc->retval */
> > +       u32 pf_vf_id;           /* used by CP when sending to PF */
> > +};
> > +
> > +/* Define the APF hardware struct to replace other control structs as
> needed
> > + * Align to ctlq_hw_info
> > + */
> > +struct iecm_hw {
> > +       u8 __iomem *hw_addr;
> > +       u64 hw_addr_len;
> > +       void *back;
> > +
> > +       /* control queue - send and receive */
> > +       struct iecm_ctlq_info *asq;
> > +       struct iecm_ctlq_info *arq;
> > +
> > +       /* pci info */
> > +       u16 device_id;
> > +       u16 vendor_id;
> > +       u16 subsystem_device_id;
> > +       u16 subsystem_vendor_id;
> > +       u8 revision_id;
> > +       bool adapter_stopped;
> > +
> > +       struct list_head cq_list_head;
> > +};
> > +
> > +int iecm_ctlq_alloc_ring_res(struct iecm_hw *hw,
> > +                            struct iecm_ctlq_info *cq);
> > +
> > +void iecm_ctlq_dealloc_ring_res(struct iecm_hw *hw, struct
> iecm_ctlq_info *cq);
> > +
> > +/* prototype for functions used for dynamic memory allocation */
> > +void *iecm_alloc_dma_mem(struct iecm_hw *hw, struct
> iecm_dma_mem *mem,
> > +                        u64 size);
> > +void iecm_free_dma_mem(struct iecm_hw *hw, struct iecm_dma_mem
> *mem);
> > +#endif /* _IECM_CONTROLQ_H_ */
> > diff --git a/drivers/net/ethernet/intel/include/iecm_controlq_api.h
> b/drivers/net/ethernet/intel/include/iecm_controlq_api.h
> > new file mode 100644
> > index 000000000000..5f624f005d33
> > --- /dev/null
> > +++ b/drivers/net/ethernet/intel/include/iecm_controlq_api.h
> > @@ -0,0 +1,185 @@
> > +/* SPDX-License-Identifier: GPL-2.0-only */
> > +/* Copyright (c) 2020, Intel Corporation. */
> > +
> > +#ifndef _IECM_CONTROLQ_API_H_
> > +#define _IECM_CONTROLQ_API_H_
> > +
> > +#include "iecm_mem.h"
> > +
> > +struct iecm_hw;
> > +
> > +/* Used for queue init, response and events */
> > +enum iecm_ctlq_type {
> > +       IECM_CTLQ_TYPE_MAILBOX_TX       = 0,
> > +       IECM_CTLQ_TYPE_MAILBOX_RX       = 1,
> > +       IECM_CTLQ_TYPE_CONFIG_TX        = 2,
> > +       IECM_CTLQ_TYPE_CONFIG_RX        = 3,
> > +       IECM_CTLQ_TYPE_EVENT_RX         = 4,
> > +       IECM_CTLQ_TYPE_RDMA_TX          = 5,
> > +       IECM_CTLQ_TYPE_RDMA_RX          = 6,
> > +       IECM_CTLQ_TYPE_RDMA_COMPL       = 7
> > +};
> > +
> > +/* Generic Control Queue Structures */
> > +struct iecm_ctlq_reg {
> > +       /* used for queue tracking */
> > +       u32 head;
> > +       u32 tail;
> > +       /* Below applies only to default mb (if present) */
> > +       u32 len;
> > +       u32 bah;
> > +       u32 bal;
> > +       u32 len_mask;
> > +       u32 len_ena_mask;
> > +       u32 head_mask;
> > +};
> > +
> > +/* Generic queue msg structure */
> > +struct iecm_ctlq_msg {
> > +       u16 vmvf_type; /* represents the source of the message on recv */
> > +#define IECM_VMVF_TYPE_VF 0
> > +#define IECM_VMVF_TYPE_VM 1
> > +#define IECM_VMVF_TYPE_PF 2
> > +       u16 opcode;
> > +       u16 data_len;   /* data_len = 0 when no payload is attached */
> > +       union {
> > +               u16 func_id;    /* when sending a message */
> > +               u16 status;     /* when receiving a message */
> > +       };
> > +       union {
> > +               struct {
> > +                       u32 chnl_retval;
> > +                       u32 chnl_opcode;
> > +               } mbx;
> > +       } cookie;
> > +       union {
> > +#define IECM_DIRECT_CTX_SIZE   16
> > +#define IECM_INDIRECT_CTX_SIZE 8
> > +               /* 16 bytes of context can be provided or 8 bytes of context
> > +                * plus the address of a DMA buffer
> > +                */
> > +               u8 direct[IECM_DIRECT_CTX_SIZE];
> > +               struct {
> > +                       u8 context[IECM_INDIRECT_CTX_SIZE];
> > +                       struct iecm_dma_mem *payload;
> > +               } indirect;
> > +       } ctx;
> > +};
> > +
> > +/* Generic queue info structures */
> > +/* MB, CONFIG and EVENT q do not have extended info */
> > +struct iecm_ctlq_create_info {
> > +       enum iecm_ctlq_type type;
> > +       int id; /* absolute queue offset passed as input
> > +                * -1 for default mailbox if present
> > +                */
> > +       u16 len; /* Queue length passed as input */
> > +       u16 buf_size; /* buffer size passed as input */
> > +       u64 base_address; /* output, HPA of the Queue start  */
> > +       struct iecm_ctlq_reg reg; /* registers accessed by ctlqs */
> > +
> > +       int ext_info_size;
> > +       void *ext_info; /* Specific to q type */
> > +};
> > +
> > +/* Control Queue information */
> > +struct iecm_ctlq_info {
> > +       struct list_head cq_list;
> > +
> > +       enum iecm_ctlq_type cq_type;
> > +       int q_id;
> > +       /* control queue lock */
> > +       struct mutex cq_lock;
> > +
> > +       /* used for interrupt processing */
> > +       u16 next_to_use;
> > +       u16 next_to_clean;
> > +       u16 next_to_post;               /* starting descriptor to post buffers
> > +                                        * to after recev
> > +                                        */
> > +
> > +       struct iecm_dma_mem desc_ring;  /* descriptor ring memory
> > +                                        * iecm_dma_mem is defined in OSdep.h
> > +                                        */
> > +       union {
> > +               struct iecm_dma_mem **rx_buff;
> > +               struct iecm_ctlq_msg **tx_msg;
> > +       } bi;
> > +
> > +       u16 buf_size;                   /* queue buffer size */
> > +       u16 ring_size;                  /* Number of descriptors */
> > +       struct iecm_ctlq_reg reg;       /* registers accessed by ctlqs */
> > +};
> > +
> > +/* PF/VF mailbox commands */
> > +enum iecm_mbx_opc {
> > +       /* iecm_mbq_opc_send_msg_to_pf:
> > +        *      usage: used by PF or VF to send a message to its CPF
> > +        *      target: RX queue and function ID of parent PF taken from HW
> > +        */
> > +       iecm_mbq_opc_send_msg_to_pf             = 0x0801,
> > +
> > +       /* iecm_mbq_opc_send_msg_to_vf:
> > +        *      usage: used by PF to send message to a VF
> > +        *      target: VF control queue ID must be specified in descriptor
> > +        */
> > +       iecm_mbq_opc_send_msg_to_vf             = 0x0802,
> > +
> > +       /* iecm_mbq_opc_send_msg_to_peer_pf:
> > +        *      usage: used by any function to send message to any peer PF
> > +        *      target: RX queue and host of parent PF taken from HW
> > +        */
> > +       iecm_mbq_opc_send_msg_to_peer_pf        = 0x0803,
> > +
> > +       /* iecm_mbq_opc_send_msg_to_peer_drv:
> > +        *      usage: used by any function to send message to any peer driver
> > +        *      target: RX queue and target host must be specific in descriptor
> > +        */
> > +       iecm_mbq_opc_send_msg_to_peer_drv       = 0x0804,
> > +};
> > +
> > +/* API support for control queue management */
> > +
> > +/* Will init all required q including default mb.  "q_info" is an array of
> > + * create_info structs equal to the number of control queues to be
> created.
> > + */
> > +int iecm_ctlq_init(struct iecm_hw *hw, u8 num_q,
> > +                  struct iecm_ctlq_create_info *q_info);
> > +
> > +/* Allocate and initialize a single control queue, which will be added to
> the
> > + * control queue list; returns a handle to the created control queue
> > + */
> > +int iecm_ctlq_add(struct iecm_hw *hw,
> > +                 struct iecm_ctlq_create_info *qinfo,
> > +                 struct iecm_ctlq_info **cq);
> > +
> > +/* Deinitialize and deallocate a single control queue */
> > +void iecm_ctlq_remove(struct iecm_hw *hw,
> > +                     struct iecm_ctlq_info *cq);
> > +
> > +/* Sends messages to HW and will also free the buffer*/
> > +int iecm_ctlq_send(struct iecm_hw *hw,
> > +                  struct iecm_ctlq_info *cq,
> > +                  u16 num_q_msg,
> > +                  struct iecm_ctlq_msg q_msg[]);
> > +
> > +/* Receives messages and called by interrupt handler/polling
> > + * initiated by app/process. Also caller is supposed to free the buffers
> > + */
> > +int iecm_ctlq_recv(struct iecm_ctlq_info *cq, u16 *num_q_msg,
> > +                  struct iecm_ctlq_msg *q_msg);
> > +
> > +/* Reclaims send descriptors on HW write back */
> > +int iecm_ctlq_clean_sq(struct iecm_ctlq_info *cq, u16 *clean_count,
> > +                      struct iecm_ctlq_msg *msg_status[]);
> > +
> > +/* Indicate RX buffers are done being processed */
> > +int iecm_ctlq_post_rx_buffs(struct iecm_hw *hw,
> > +                           struct iecm_ctlq_info *cq,
> > +                           u16 *buff_count,
> > +                           struct iecm_dma_mem **buffs);
> > +
> > +/* Will destroy all q including the default mb */
> > +int iecm_ctlq_deinit(struct iecm_hw *hw);
> > +
> > +#endif /* _IECM_CONTROLQ_API_H_ */
> > diff --git a/drivers/net/ethernet/intel/include/iecm_mem.h
> b/drivers/net/ethernet/intel/include/iecm_mem.h
> > new file mode 100644
> > index 000000000000..064dd6e10c24
> > --- /dev/null
> > +++ b/drivers/net/ethernet/intel/include/iecm_mem.h
> > @@ -0,0 +1,20 @@
> > +/* SPDX-License-Identifier: GPL-2.0-only */
> > +/* Copyright (C) 2019 Intel Corporation */
> > +
> > +#ifndef _IECM_MEM_H_
> > +#define _IECM_MEM_H_
> > +
> > +#include <linux/io.h>
> > +
> > +struct iecm_dma_mem {
> > +       void *va;
> > +       dma_addr_t pa;
> > +       size_t size;
> > +};
> > +
> > +#define wr32(a, reg, value)    writel((value), ((a)->hw_addr + (reg)))
> > +#define rd32(a, reg)           readl((a)->hw_addr + (reg))
> > +#define wr64(a, reg, value)    writeq((value), ((a)->hw_addr + (reg)))
> > +#define rd64(a, reg)           readq((a)->hw_addr + (reg))
> > +
> > +#endif /* _IECM_MEM_H_ */
> > --
> > 2.33.0
> >
> > _______________________________________________
> > Intel-wired-lan mailing list
> > Intel-wired-lan at osuosl.org
> > https://lists.osuosl.org/mailman/listinfo/intel-wired-lan

^ permalink raw reply	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 04/19] iecm: add api_init and controlq init
  2022-02-03  3:24     ` Brady, Alan
@ 2022-02-03  3:40       ` Brady, Alan
  2022-02-03  5:26         ` Shannon Nelson
  2022-02-03 13:13       ` Alexander Lobakin
  1 sibling, 1 reply; 89+ messages in thread
From: Brady, Alan @ 2022-02-03  3:40 UTC (permalink / raw)
  To: intel-wired-lan

> -----Original Message-----
> From: Brady, Alan
> Sent: Wednesday, February 2, 2022 7:25 PM
> To: Shannon Nelson <shannon.lee.nelson@gmail.com>
> Cc: Intel Wired LAN <intel-wired-lan@lists.osuosl.org>; Burra, Phani R
> <phani.r.burra@intel.com>; Chittim, Madhu <madhu.chittim@intel.com>;
> Linga, Pavan Kumar <Pavan.Kumar.Linga@intel.com>
> Subject: RE: [Intel-wired-lan] [PATCH net-next 04/19] iecm: add api_init and
> controlq init
> 
> > -----Original Message-----
> > From: Shannon Nelson <shannon.lee.nelson@gmail.com>
> > Sent: Tuesday, February 1, 2022 1:27 PM
> > To: Brady, Alan <alan.brady@intel.com>
> > Cc: Intel Wired LAN <intel-wired-lan@lists.osuosl.org>; Burra, Phani R
> > <phani.r.burra@intel.com>; Chittim, Madhu
> <madhu.chittim@intel.com>;
> > Linga, Pavan Kumar <pavan.kumar.linga@intel.com>
> > Subject: Re: [Intel-wired-lan] [PATCH net-next 04/19] iecm: add api_init
> and
> > controlq init
> >
> > On Thu, Jan 27, 2022 at 4:35 PM Alan Brady <alan.brady@intel.com>
> > wrote:
> > >
> > > Initializing device registers is offloaded into function pointers given
> > > to iecm from the dependent device driver for a given device, as offsets
> > > can vary wildly. This also adds everything needed to setup and use a
> > > controlq which uses some of those registers.
> > >
> > > At the end of probe we kicked off a hard reset and this implements
> what's
> > > needed to handle that reset and continue init.
> > >
> > > Signed-off-by: Phani Burra <phani.r.burra@intel.com>
> > > Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
> > > Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
> > > Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
> > > Signed-off-by: Alan Brady <alan.brady@intel.com>
> > > ---

<snip/>

> > > +/**
> > > + * iecm_ctlq_add - add one control queue
> > > + * @hw: pointer to hardware struct
> > > + * @qinfo: info for queue to be created
> > > + * @cq_out: (output) double pointer to control queue to be created
> > > + *
> > > + * Allocate and initialize a control queue and add it to the control
> queue
> > list.
> > > + * The cq parameter will be allocated/initialized and passed back to the
> > caller
> > > + * if no errors occur.
> > > + *
> > > + * Note: iecm_ctlq_init must be called prior to any calls to
> > iecm_ctlq_add
> > > + */
> > > +int iecm_ctlq_add(struct iecm_hw *hw,
> > > +                 struct iecm_ctlq_create_info *qinfo,
> > > +                 struct iecm_ctlq_info **cq_out)
> > > +{
> > > +       bool is_rxq = false;
> > > +       int status = 0;
> > > +
> > > +       if (!qinfo->len || !qinfo->buf_size ||
> > > +           qinfo->len > IECM_CTLQ_MAX_RING_SIZE ||
> > > +           qinfo->buf_size > IECM_CTLQ_MAX_BUF_LEN)
> > > +               return -EINVAL;
> > > +
> > > +       *cq_out = kcalloc(1, sizeof(struct iecm_ctlq_info), GFP_KERNEL);
> > > +       if (!(*cq_out))
> > > +               return -ENOMEM;
> >
> > You might keep this as a local variable until you get to a successful
> > end, then set *cq_out when done.
> > Else, you need to be sure to clear it back to NULL on error return to
> > be sure no one uses a bogus value.
> >
> 
> This one I'm not sure I follow.  If it's NULL we fall into the if(!*cq_out) and
> don't need to set it to NULL (it already is?). If it's not NULL then we go on to
> use it like a valid memory address so I hope it's valid.
> 
> > > +
> > > +       (*cq_out)->cq_type = qinfo->type;
> > > +       (*cq_out)->q_id = qinfo->id;
> > > +       (*cq_out)->buf_size = qinfo->buf_size;
> > > +       (*cq_out)->ring_size = qinfo->len;
> > > +
> > > +       (*cq_out)->next_to_use = 0;
> > > +       (*cq_out)->next_to_clean = 0;
> > > +       (*cq_out)->next_to_post = (*cq_out)->ring_size - 1;
> > > +
> > > +       switch (qinfo->type) {
> > > +       case IECM_CTLQ_TYPE_MAILBOX_RX:
> > > +               is_rxq = true;
> > > +               fallthrough;
> > > +       case IECM_CTLQ_TYPE_MAILBOX_TX:
> > > +               status = iecm_ctlq_alloc_ring_res(hw, *cq_out);
> > > +               break;
> > > +       default:
> > > +               status = -EBADR;
> > > +               break;

Ah wait I think I understand, if we end up in error state later in the function like here we probably shouldn't be giving the memory back to the caller.  Will fix.

> > > +       }
> > > +
> > > +       if (status)
> > > +               goto init_free_q;
> > > +
> > > +       if (is_rxq) {
> > > +               iecm_ctlq_init_rxq_bufs(*cq_out);
> > > +       } else {
> > > +               /* Allocate the array of msg pointers for TX queues */
> > > +               (*cq_out)->bi.tx_msg = kcalloc(qinfo->len,
> > > +                                              sizeof(struct iecm_ctlq_msg *),
> > > +                                              GFP_KERNEL);
> > > +               if (!(*cq_out)->bi.tx_msg) {
> > > +                       status = -ENOMEM;
> > > +                       goto init_dealloc_q_mem;
> > > +               }
> > > +       }
> > > +
> > > +       iecm_ctlq_setup_regs(*cq_out, qinfo);
> > > +
> > > +       iecm_ctlq_init_regs(hw, *cq_out, is_rxq);
> > > +
> > > +       mutex_init(&(*cq_out)->cq_lock);
> > > +
> > > +       list_add(&(*cq_out)->cq_list, &hw->cq_list_head);
> > > +
> > > +       return status;
> > > +
> > > +init_dealloc_q_mem:
> > > +       /* free ring buffers and the ring itself */
> > > +       iecm_ctlq_dealloc_ring_res(hw, *cq_out);
> > > +init_free_q:
> > > +       kfree(*cq_out);
.
> >
> 
> Will fix.
> 
> > > +
> > > +       return status;
> > > +}
> > > +

<snip/>

^ permalink raw reply	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 04/19] iecm: add api_init and controlq init
  2022-02-03  3:40       ` Brady, Alan
@ 2022-02-03  5:26         ` Shannon Nelson
  0 siblings, 0 replies; 89+ messages in thread
From: Shannon Nelson @ 2022-02-03  5:26 UTC (permalink / raw)
  To: intel-wired-lan

On Wed, Feb 2, 2022 at 7:40 PM Brady, Alan <alan.brady@intel.com> wrote:
>
> > -----Original Message-----
> > From: Brady, Alan
> > Sent: Wednesday, February 2, 2022 7:25 PM
> > To: Shannon Nelson <shannon.lee.nelson@gmail.com>
> > Cc: Intel Wired LAN <intel-wired-lan@lists.osuosl.org>; Burra, Phani R
> > <phani.r.burra@intel.com>; Chittim, Madhu <madhu.chittim@intel.com>;
> > Linga, Pavan Kumar <Pavan.Kumar.Linga@intel.com>
> > Subject: RE: [Intel-wired-lan] [PATCH net-next 04/19] iecm: add api_init and
> > controlq init
> >
> > > -----Original Message-----
> > > From: Shannon Nelson <shannon.lee.nelson@gmail.com>
> > > Sent: Tuesday, February 1, 2022 1:27 PM
> > > To: Brady, Alan <alan.brady@intel.com>
> > > Cc: Intel Wired LAN <intel-wired-lan@lists.osuosl.org>; Burra, Phani R
> > > <phani.r.burra@intel.com>; Chittim, Madhu
> > <madhu.chittim@intel.com>;
> > > Linga, Pavan Kumar <pavan.kumar.linga@intel.com>
> > > Subject: Re: [Intel-wired-lan] [PATCH net-next 04/19] iecm: add api_init
> > and
> > > controlq init
> > >
> > > On Thu, Jan 27, 2022 at 4:35 PM Alan Brady <alan.brady@intel.com>
> > > wrote:
> > > >
> > > > Initializing device registers is offloaded into function pointers given
> > > > to iecm from the dependent device driver for a given device, as offsets
> > > > can vary wildly. This also adds everything needed to setup and use a
> > > > controlq which uses some of those registers.
> > > >
> > > > At the end of probe we kicked off a hard reset and this implements
> > what's
> > > > needed to handle that reset and continue init.
> > > >
> > > > Signed-off-by: Phani Burra <phani.r.burra@intel.com>
> > > > Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
> > > > Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
> > > > Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
> > > > Signed-off-by: Alan Brady <alan.brady@intel.com>
> > > > ---
>
> <snip/>
>
> > > > +/**
> > > > + * iecm_ctlq_add - add one control queue
> > > > + * @hw: pointer to hardware struct
> > > > + * @qinfo: info for queue to be created
> > > > + * @cq_out: (output) double pointer to control queue to be created
> > > > + *
> > > > + * Allocate and initialize a control queue and add it to the control
> > queue
> > > list.
> > > > + * The cq parameter will be allocated/initialized and passed back to the
> > > caller
> > > > + * if no errors occur.
> > > > + *
> > > > + * Note: iecm_ctlq_init must be called prior to any calls to
> > > iecm_ctlq_add
> > > > + */
> > > > +int iecm_ctlq_add(struct iecm_hw *hw,
> > > > +                 struct iecm_ctlq_create_info *qinfo,
> > > > +                 struct iecm_ctlq_info **cq_out)
> > > > +{
> > > > +       bool is_rxq = false;
> > > > +       int status = 0;
> > > > +
> > > > +       if (!qinfo->len || !qinfo->buf_size ||
> > > > +           qinfo->len > IECM_CTLQ_MAX_RING_SIZE ||
> > > > +           qinfo->buf_size > IECM_CTLQ_MAX_BUF_LEN)
> > > > +               return -EINVAL;
> > > > +
> > > > +       *cq_out = kcalloc(1, sizeof(struct iecm_ctlq_info), GFP_KERNEL);
> > > > +       if (!(*cq_out))
> > > > +               return -ENOMEM;
> > >
> > > You might keep this as a local variable until you get to a successful
> > > end, then set *cq_out when done.
> > > Else, you need to be sure to clear it back to NULL on error return to
> > > be sure no one uses a bogus value.
> > >
> >
> > This one I'm not sure I follow.  If it's NULL we fall into the if(!*cq_out) and
> > don't need to set it to NULL (it already is?). If it's not NULL then we go on to
> > use it like a valid memory address so I hope it's valid.
> >
> > > > +
> > > > +       (*cq_out)->cq_type = qinfo->type;
> > > > +       (*cq_out)->q_id = qinfo->id;
> > > > +       (*cq_out)->buf_size = qinfo->buf_size;
> > > > +       (*cq_out)->ring_size = qinfo->len;
> > > > +
> > > > +       (*cq_out)->next_to_use = 0;
> > > > +       (*cq_out)->next_to_clean = 0;
> > > > +       (*cq_out)->next_to_post = (*cq_out)->ring_size - 1;
> > > > +
> > > > +       switch (qinfo->type) {
> > > > +       case IECM_CTLQ_TYPE_MAILBOX_RX:
> > > > +               is_rxq = true;
> > > > +               fallthrough;
> > > > +       case IECM_CTLQ_TYPE_MAILBOX_TX:
> > > > +               status = iecm_ctlq_alloc_ring_res(hw, *cq_out);
> > > > +               break;
> > > > +       default:
> > > > +               status = -EBADR;
> > > > +               break;
>
> Ah wait I think I understand, if we end up in error state later in the function like here we probably shouldn't be giving the memory back to the caller.  Will fix.

Right.  If you store the pointer in a local variable first, then you
can do the final assignment to *cq_out at the end once you know that
all is done and safe.

Some folks might thinik that at the end they could just set *cq_out
back to NULL if there was an error, but I think it is safer and less
of a surprise to leave the caller's data alone until we have a happy
answer.


>
> > > > +       }
> > > > +
> > > > +       if (status)
> > > > +               goto init_free_q;
> > > > +
> > > > +       if (is_rxq) {
> > > > +               iecm_ctlq_init_rxq_bufs(*cq_out);
> > > > +       } else {
> > > > +               /* Allocate the array of msg pointers for TX queues */
> > > > +               (*cq_out)->bi.tx_msg = kcalloc(qinfo->len,
> > > > +                                              sizeof(struct iecm_ctlq_msg *),
> > > > +                                              GFP_KERNEL);
> > > > +               if (!(*cq_out)->bi.tx_msg) {
> > > > +                       status = -ENOMEM;
> > > > +                       goto init_dealloc_q_mem;
> > > > +               }
> > > > +       }
> > > > +
> > > > +       iecm_ctlq_setup_regs(*cq_out, qinfo);
> > > > +
> > > > +       iecm_ctlq_init_regs(hw, *cq_out, is_rxq);
> > > > +
> > > > +       mutex_init(&(*cq_out)->cq_lock);
> > > > +
> > > > +       list_add(&(*cq_out)->cq_list, &hw->cq_list_head);
> > > > +

Here you can set *cq_out = local_ptr;

> > > > +       return status;
> > > > +
> > > > +init_dealloc_q_mem:
> > > > +       /* free ring buffers and the ring itself */
> > > > +       iecm_ctlq_dealloc_ring_res(hw, *cq_out);
> > > > +init_free_q:
> > > > +       kfree(*cq_out);

Here you would kfree() your local pointer

sln

> .
> > >
> >
> > Will fix.
> >
> > > > +
> > > > +       return status;
> > > > +}
> > > > +
>
> <snip/>

^ permalink raw reply	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 06/19] iecm: add virtchnl messages for queues
  2022-02-02 22:48     ` Brady, Alan
@ 2022-02-03 10:08       ` Maciej Fijalkowski
  2022-02-03 14:09       ` Alexander Lobakin
  1 sibling, 0 replies; 89+ messages in thread
From: Maciej Fijalkowski @ 2022-02-03 10:08 UTC (permalink / raw)
  To: intel-wired-lan

On Wed, Feb 02, 2022 at 10:48:48PM +0000, Brady, Alan wrote:
> > -----Original Message-----
> > From: Lobakin, Alexandr <alexandr.lobakin@intel.com>
> > Sent: Friday, January 28, 2022 5:03 AM
> > To: Brady, Alan <alan.brady@intel.com>
> > Cc: Lobakin, Alexandr <alexandr.lobakin@intel.com>; intel-wired-
> > lan at lists.osuosl.org; Linga, Pavan Kumar <pavan.kumar.linga@intel.com>;
> > Chittim, Madhu <madhu.chittim@intel.com>; Burra, Phani R
> > <phani.r.burra@intel.com>
> > Subject: Re: [Intel-wired-lan] [PATCH net-next 06/19] iecm: add virtchnl
> > messages for queues
> > 
> > From: Alan Brady <alan.brady@intel.com>
> > Date: Thu, 27 Jan 2022 16:09:56 -0800
> > 
> > > This continues adding virtchnl messages. This largely relates to adding
> > > messages needed to negotiate and setup traffic queues.
> > >
> > > Signed-off-by: Phani Burra <phani.r.burra@intel.com>
> > > Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
> > > Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
> > > Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
> > > Signed-off-by: Alice Michael <alice.michael@intel.com>
> > > Signed-off-by: Alan Brady <alan.brady@intel.com>
> > > ---
> > >  drivers/net/ethernet/intel/iecm/iecm_lib.c    |   14 +
> > >  drivers/net/ethernet/intel/iecm/iecm_txrx.c   |  161 +++
> > >  .../net/ethernet/intel/iecm/iecm_virtchnl.c   | 1127 ++++++++++++++++-
> > >  drivers/net/ethernet/intel/include/iecm.h     |   22 +
> > >  .../net/ethernet/intel/include/iecm_txrx.h    |  196 +++
> > >  5 files changed, 1505 insertions(+), 15 deletions(-)
> > >
> > > diff --git a/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > > index e2e523f0700e..4e9cc7f2d138 100644
> > > --- a/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > > +++ b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > 
> > --- 8< ---

I think we could trim a bit more in these responses, so they would be
easier to follow for people :)

> > 
> > > +void iecm_vport_calc_num_q_desc(struct iecm_vport *vport)
> > > +{
> > > +	int num_req_txq_desc = vport->adapter-
> > >config_data.num_req_txq_desc;
> > > +	int num_req_rxq_desc = vport->adapter-
> > >config_data.num_req_rxq_desc;
> > > +	int num_bufqs = vport->num_bufqs_per_qgrp;
> > > +	int i = 0;
> > > +
> > > +	vport->complq_desc_count = 0;
> > > +	if (num_req_txq_desc) {
> > > +		vport->txq_desc_count = num_req_txq_desc;
> > > +		if (iecm_is_queue_model_split(vport->txq_model)) {
> > > +			vport->complq_desc_count = num_req_txq_desc;
> > > +			if (vport->complq_desc_count <
> > IECM_MIN_TXQ_COMPLQ_DESC)
> > > +				vport->complq_desc_count =
> > > +					IECM_MIN_TXQ_COMPLQ_DESC;
> > > +		}
> > > +	} else {
> > > +		vport->txq_desc_count =
> > > +			IECM_DFLT_TX_Q_DESC_COUNT;
> > > +		if (iecm_is_queue_model_split(vport->txq_model)) {
> > > +			vport->complq_desc_count =
> > > +				IECM_DFLT_TX_COMPLQ_DESC_COUNT;
> > > +		}
> > 
> > Braces are redundant here since the path is a one-liner.
> > 
> 
> Correct me if I'm wrong but believe the guidance here is if it goes
> beyond one line with line wrapping, it is optional whether or not to use
> braces, even if the statement is 'one line'. We have generally preferred
> to keep braces in multiline statements. However you do have a point that
> it is not consistent in this function. Will fix.

That's the first time I hear about something like that. Interesting that
checkpatch won't scream at you for this, but I'm with Alex in here.

> 
> > > +	}
> > > +
> > > +	if (num_req_rxq_desc)
> > > +		vport->rxq_desc_count = num_req_rxq_desc;
> > > +	else
> > > +		vport->rxq_desc_count = IECM_DFLT_RX_Q_DESC_COUNT;
> > > +
> > > +	for (i = 0; i < num_bufqs; i++) {
> > > +		if (!vport->bufq_desc_count[i])
> > > +			vport->bufq_desc_count[i] =
> > > +				IECM_RX_BUFQ_DESC_COUNT(vport-
> > >rxq_desc_count,
> > > +							num_bufqs);
> > 
> > 		if (vport->bufq_desc_count[i])
> > 			continue;
> > 
> > 		vport-> ...
> > 
> > -1 indent level with that.
> > 
> > > +	}
> > > +}
> > > +

(...)

> > > +/**
> > > + * iecm_send_config_rx_queues_msg - Send virtchnl config rx queues
> > message
> > > + * @vport: virtual port data structure
> > > + *
> > > + * Send config rx queues virtchnl message.  Returns 0 on success, negative on
> > > + * failure.
> > > + */
> > > +int iecm_send_config_rx_queues_msg(struct iecm_vport *vport)
> > > +{
> > > +	struct virtchnl2_config_rx_queues *crq = NULL;
> > > +	int config_data_size, chunk_size, buf_size = 0;
> > > +	int totqs, num_msgs, num_chunks;
> > > +	struct virtchnl2_rxq_info *qi;
> > > +	int err = 0, i, k = 0;
> > > +	bool alloc = false;
> > > +
> > > +	totqs = vport->num_rxq + vport->num_bufq;
> > > +	qi = kcalloc(totqs, sizeof(struct virtchnl2_rxq_info), GFP_KERNEL);
> > > +	if (!qi)
> > > +		return -ENOMEM;
> > > +
> > > +	/* Populate the queue info buffer with all queue context info */
> > > +	for (i = 0; i < vport->num_rxq_grp; i++) {
> > > +		struct iecm_rxq_group *rx_qgrp = &vport->rxq_grps[i];
> > > +		int num_rxq;
> > > +		int j;
> > > +
> > > +		if (iecm_is_queue_model_split(vport->rxq_model)) {
> > > +			for (j = 0; j < vport->num_bufqs_per_qgrp; j++, k++) {
> > > +				struct iecm_queue *bufq =
> > > +					&rx_qgrp->splitq.bufq_sets[j].bufq;
> > > +
> > > +				qi[k].queue_id =
> > > +					cpu_to_le32(bufq->q_id);
> > > +				qi[k].model =
> > > +					cpu_to_le16(vport->rxq_model);
> > > +				qi[k].type =
> > > +					cpu_to_le32(bufq->q_type);
> > > +				qi[k].desc_ids =
> > > +
> > 	cpu_to_le64(VIRTCHNL2_RXDID_1_FLEX_SPLITQ_M);
> > > +				qi[k].ring_len =
> > > +					cpu_to_le16(bufq->desc_count);
> > > +				qi[k].dma_ring_addr =
> > > +					cpu_to_le64(bufq->dma);
> > > +				qi[k].data_buffer_size =
> > > +					cpu_to_le32(bufq->rx_buf_size);
> > > +				qi[k].buffer_notif_stride =
> > > +					bufq->rx_buf_stride;
> > > +				qi[k].rx_buffer_low_watermark =
> > > +					cpu_to_le16(bufq-
> > >rx_buffer_low_watermark);
> > > +			}
> > > +		}
> > 
> > 		if (iecm_is_queue_model_split(vport->rxq_model))
> > 			goto here;
> > 
> > -1 indent level for the for-loop.
> 
> I'm afraid I'm not following, please elaborate. Where are we goto'ing?
> The for loop below needs to be executed for both and if we just tack the
> above for loop at the bottom of the function and goto in and out of it
> to save an indent does not sound great and makes the code harder to
> follow IMO.

Below or above? I think Alex meant to skip the for loop execution for
nonsplitq model. And by reducing indent you probably could have all of the
assignments stored on the single line.

> 
> > Braces for 'if' are not needed since the for-loop has their own.
> > 
> 
> They're not required but we have generally preferred to keep braces on
> statements extending across more than one line.

Here I'm actually okay with keeping the braces as in future maybe you
might want to introduce something for the splitq case outside of the
current loop so it might get tricky when you forgot to supply the outer
'if' with braces. Just my 0.02$.

> 
> > > +
> > > +		if (iecm_is_queue_model_split(vport->rxq_model))
> > > +			num_rxq = rx_qgrp->splitq.num_rxq_sets;
> > > +		else
> > > +			num_rxq = rx_qgrp->singleq.num_rxq;
> > > +
> > > +		for (j = 0; j < num_rxq; j++, k++) {
> > > +			struct iecm_queue *rxq;
> > > +
> > > +			if (iecm_is_queue_model_split(vport->rxq_model)) {
> > > +				rxq = &rx_qgrp->splitq.rxq_sets[j]->rxq;
> > > +				qi[k].rx_bufq1_id =
> > > +				  cpu_to_le16(rxq->rxq_grp-
> > >splitq.bufq_sets[0].bufq.q_id);
> > > +				qi[k].rx_bufq2_id =
> > > +				  cpu_to_le16(rxq->rxq_grp-
> > >splitq.bufq_sets[1].bufq.q_id);
> > > +				qi[k].hdr_buffer_size =
> > > +					cpu_to_le16(rxq->rx_hbuf_size);
> > > +				qi[k].rx_buffer_low_watermark =
> > > +					cpu_to_le16(rxq-
> > >rx_buffer_low_watermark);
> > > +
> > > +				if (rxq->rx_hsplit_en) {
> > > +					qi[k].qflags =
> > > +
> > 	cpu_to_le16(VIRTCHNL2_RXQ_HDR_SPLIT);
> > > +					qi[k].hdr_buffer_size =
> > > +						cpu_to_le16(rxq-
> > >rx_hbuf_size);
> > > +				}
> > > +			} else {
> > > +				rxq = rx_qgrp->singleq.rxqs[j];
> > > +			}
> > 
> > Same here, but with rxq = ... + goto.
> > 
> 
> Please elaborate.

		if (!iecm_is_queue_model_split(vport->rxq_model)) {
			rxq = rx_qgrp->singleq.rxqs[j];
			goto skip_splitq_init;
		}
		rxq = &rx_qgrp->splitq.rxq_sets[j]->rxq;
		qi[k].rx_bufq1_id = cpu_to_le16(rxq->rxq_grp->splitq.bufq_sets[0].bufq.q_id);
		qi[k].rx_bufq2_id = cpu_to_le16(rxq->rxq_grp->splitq.bufq_sets[1].bufq.q_id);
		qi[k].hdr_buffer_size = cpu_to_le16(rxq->rx_hbuf_size);
		qi[k].rx_buffer_low_watermark = cpu_to_le16(rxq->rx_buffer_low_watermark);

		if (rxq->rx_hsplit_en) {
			qi[k].qflags = cpu_to_le16(VIRTCHNL2_RXQ_HDR_SPLIT);
			qi[k].hdr_buffer_size = cpu_to_le16(rxq->rx_hbuf_size);
		}
skip_splitq_init:
		(...)

More readable to me.
What's 'k' though? Maybe let's think of better variable naming in here?

> 
> > > +
> > > +			qi[k].queue_id =
> > > +				cpu_to_le32(rxq->q_id);
> > > +			qi[k].model =
> > > +				cpu_to_le16(vport->rxq_model);
> > > +			qi[k].type =
> > > +				cpu_to_le32(rxq->q_type);
> > > +			qi[k].ring_len =
> > > +				cpu_to_le16(rxq->desc_count);
> > > +			qi[k].dma_ring_addr =
> > > +				cpu_to_le64(rxq->dma);
> > > +			qi[k].max_pkt_size =
> > > +				cpu_to_le32(rxq->rx_max_pkt_size);
> > > +			qi[k].data_buffer_size =
> > > +				cpu_to_le32(rxq->rx_buf_size);
> > > +			qi[k].qflags |=
> > > +
> > 	cpu_to_le16(VIRTCHNL2_RX_DESC_SIZE_32BYTE);
> > > +			qi[k].desc_ids =
> > > +				cpu_to_le64(rxq->rxdids);
> > > +		}
> > > +	}
> > > +
> > > +	/* Make sure accounting agrees */
> > > +	if (k != totqs) {
> > > +		err = -EINVAL;
> > > +		goto error;
> > > +	}
> > > +
> > > +	/* Chunk up the queue contexts into multiple messages to avoid
> > > +	 * sending a control queue message buffer that is too large
> > > +	 */
> > > +	config_data_size = sizeof(struct virtchnl2_config_rx_queues);
> > > +	chunk_size = sizeof(struct virtchnl2_rxq_info);
> > > +
> > > +	num_chunks = IECM_NUM_CHUNKS_PER_MSG(config_data_size,
> > chunk_size) + 1;
> > > +	if (totqs < num_chunks)
> > > +		num_chunks = totqs;
> > > +
> > > +	num_msgs = totqs / num_chunks;
> > > +	if (totqs % num_chunks)
> > > +		num_msgs++;
> > > +
> > > +	for (i = 0, k = 0; i < num_msgs; i++) {
> > > +		if (!crq || alloc) {
> > > +			buf_size = (chunk_size * (num_chunks - 1)) +
> > > +					config_data_size;
> > > +			kfree(crq);
> > > +			crq = kzalloc(buf_size, GFP_KERNEL);
> > > +			if (!crq) {
> > > +				err = -ENOMEM;
> > > +				goto error;
> > > +			}
> > > +		} else {
> > > +			memset(crq, 0, buf_size);
> > > +		}
> > > +
> > > +		crq->vport_id = cpu_to_le32(vport->vport_id);
> > > +		crq->num_qinfo = cpu_to_le16(num_chunks);
> > > +		memcpy(crq->qinfo, &qi[k], chunk_size * num_chunks);
> > > +
> > > +		err = iecm_send_mb_msg(vport->adapter,
> > > +				       VIRTCHNL2_OP_CONFIG_RX_QUEUES,
> > > +				       buf_size, (u8 *)crq);
> > > +		if (err)
> > > +			goto mbx_error;
> > > +
> > > +		err = iecm_wait_for_event(vport->adapter,
> > IECM_VC_CONFIG_RXQ,
> > > +					  IECM_VC_CONFIG_RXQ_ERR);
> > > +		if (err)
> > > +			goto mbx_error;
> > > +
> > > +		k += num_chunks;
> > > +		totqs -= num_chunks;
> > > +		if (totqs < num_chunks) {
> > > +			num_chunks = totqs;
> > > +			alloc = true;
> > > +		}
> > > +	}
> > > +
> > > +mbx_error:
> > > +	kfree(crq);
> > > +error:
> > > +	kfree(qi);
> > > +	return err;
> > > +}
> > > +

(...)

> > > +/* queue associated with a vport */
> > > +struct iecm_queue {
> > > +	struct device *dev;		/* Used for DMA mapping */
> > > +	struct iecm_vport *vport;	/* Backreference to associated vport
> > */
> > > +	union {
> > > +		struct iecm_txq_group *txq_grp;
> > > +		struct iecm_rxq_group *rxq_grp;
> > > +	};
> > > +	/* bufq: Used as group id, either 0 or 1, on clean Buf Q uses this
> > > +	 *       index to determine which group of refill queues to clean.
> > > +	 *       Bufqs are use in splitq only.
> > > +	 * txq: Index to map between Tx Q group and hot path Tx ptrs stored in
> > > +	 *      vport.  Used in both single Q/split Q
> > > +	 * rxq: Index to total rxq across groups, used for skb reporting
> > > +	 */
> > > +	u16 idx;
> > > +	/* Used for both Q models single and split. In split Q model relevant
> > > +	 * only to Tx Q and Rx Q
> > > +	 */
> > > +	u8 __iomem *tail;
> > > +	/* Used in both single and split Q.  In single Q, Tx Q uses tx_buf and
> > > +	 * Rx Q uses rx_buf.  In split Q, Tx Q uses tx_buf, Rx Q uses skb, and
> > > +	 * Buf Q uses rx_buf.
> > > +	 */
> > > +	union {
> > > +		struct iecm_tx_buf *tx_buf;
> > > +		struct {
> > > +			struct iecm_rx_buf *buf;
> > > +			struct iecm_dma_mem **hdr_buf;
> > > +		} rx_buf;
> > > +		struct sk_buff *skb;
> > > +	};
> > > +	u16 q_type;
> > > +	/* Queue id(Tx/Tx compl/Rx/Bufq) */
> > > +	u32 q_id;
> > > +	u16 desc_count;		/* Number of descriptors */
> > > +
> > > +	/* Relevant in both split & single Tx Q & Buf Q*/
> > > +	u16 next_to_use;
> > > +	/* In split q model only relevant for Tx Compl Q and Rx Q */
> > > +	u16 next_to_clean;	/* used in interrupt processing */
> > > +	/* Used only for Rx. In split Q model only relevant to Rx Q */
> > > +	u16 next_to_alloc;
> > > +	/* Generation bit check stored, as HW flips the bit at Queue end */
> > > +	DECLARE_BITMAP(flags, __IECM_Q_FLAGS_NBITS);
> > > +
> > > +	union iecm_queue_stats q_stats;
> > > +	struct u64_stats_sync stats_sync;
> > > +
> > > +	bool rx_hsplit_en;
> > > +
> > > +	u16 rx_hbuf_size;	/* Header buffer size */
> > > +	u16 rx_buf_size;
> > > +	u16 rx_max_pkt_size;
> > > +	u16 rx_buf_stride;
> > > +	u8 rx_buffer_low_watermark;
> > > +	u64 rxdids;
> > > +	/* Used for both Q models single and split. In split Q model relavant
> > > +	 * only to Tx compl Q and Rx compl Q
> > > +	 */
> > > +	struct iecm_q_vector *q_vector;	/* Backreference to associated vector
> > */
> > > +	unsigned int size;		/* length of descriptor ring in bytes */
> > > +	dma_addr_t dma;			/* physical address of ring */
> > > +	void *desc_ring;		/* Descriptor ring memory */
> > > +
> > > +	u16 tx_buf_key;			/* 16 bit unique "identifier" (index)
> > > +					 * to be used as the completion tag
> > when
> > > +					 * queue is using flow based scheduling
> > > +					 */
> > > +	u16 tx_max_bufs;		/* Max buffers that can be transmitted
> > > +					 * with scatter-gather
> > > +					 */
> > > +	DECLARE_HASHTABLE(sched_buf_hash, 12);
> > > +} ____cacheline_internodealigned_in_smp;
> > > +
> > > +/* Software queues are used in splitq mode to manage buffers between rxq
> > > + * producer and the bufq consumer.  These are required in order to maintain a
> > > + * lockless buffer management system and are strictly software only
> > constructs.
> > > + */
> > > +struct iecm_sw_queue {
> > > +	u16 next_to_clean ____cacheline_aligned_in_smp;
> > > +	u16 next_to_alloc ____cacheline_aligned_in_smp;
> > > +	u16 next_to_use ____cacheline_aligned_in_smp;
> > > +	DECLARE_BITMAP(flags, __IECM_Q_FLAGS_NBITS)
> > > +		____cacheline_aligned_in_smp;
> > > +	u16 *ring ____cacheline_aligned_in_smp;
> > 
> > This will result in this part being FIVE cachelines long for
> > 3 * 2 + 8 + 8 = 22 bytes, i.e. 320 bytes for 22!
> > Just making the entire structure cacheline-aligned after its
> > declaration is enough, these ones are not even an overkill,
> > it's an overslaughter.

Good catch!

> > 
> > > +	u16 desc_count;
> > > +	u16 buf_size;
> > > +	struct device *dev;
> > > +} ____cacheline_internodealigned_in_smp;
> > > +
> > > +/* Splitq only.  iecm_rxq_set associates an rxq with at an array of refillqs.
> > > + * Each rxq needs a refillq to return used buffers back to the respective bufq.
> > > + * Bufqs then clean these refillqs for buffers to give to hardware.
> > > + */
> > > +struct iecm_rxq_set {
> > > +	struct iecm_queue rxq;
> > > +	/* refillqs assoc with bufqX mapped to this rxq */
> > > +	struct iecm_sw_queue *refillq0;
> > > +	struct iecm_sw_queue *refillq1;
> > > +};
> > > +
> > > +/* Splitq only.  iecm_bufq_set associates a bufq to an array of refillqs.
> > > + * In this bufq_set, there will be one refillq for each rxq in this rxq_group.
> > > + * Used buffers received by rxqs will be put on refillqs which bufqs will
> > > + * clean to return new buffers back to hardware.
> > > + *
> > > + * Buffers needed by some number of rxqs associated in this rxq_group are
> > > + * managed by at most two bufqs (depending on performance configuration).
> > > + */
> > > +struct iecm_bufq_set {
> > > +	struct iecm_queue bufq;
> > > +	/* This is always equal to num_rxq_sets in iecm_rxq_group */
> > > +	int num_refillqs;
> > > +	struct iecm_sw_queue *refillqs;
> > > +};
> > > +
> > > +/* In singleq mode, an rxq_group is simply an array of rxqs.  In splitq, a
> > > + * rxq_group contains all the rxqs, bufqs and refillqs needed to
> > > + * manage buffers in splitq mode.
> > > + */
> > > +struct iecm_rxq_group {
> > > +	struct iecm_vport *vport; /* back pointer */
> > > +
> > > +	union {
> > > +		struct {
> > > +			int num_rxq;
> > > +			/* store queue pointers */
> > > +			struct iecm_queue *rxqs[IECM_LARGE_MAX_Q];
> > > +		} singleq;
> > > +		struct {
> > > +			int num_rxq_sets;
> > > +			/* store queue pointers */
> > > +			struct iecm_rxq_set *rxq_sets[IECM_LARGE_MAX_Q];
> > > +			struct iecm_bufq_set *bufq_sets;
> > > +		} splitq;
> > > +	};
> > > +};
> > > +
> > > +/* Between singleq and splitq, a txq_group is largely the same except for the
> > > + * complq.  In splitq a single complq is responsible for handling completions
> > > + * for some number of txqs associated in this txq_group.
> > > + */
> > > +struct iecm_txq_group {
> > > +	struct iecm_vport *vport; /* back pointer */
> > > +
> > > +	int num_txq;
> > > +	/* store queue pointers */
> > > +	struct iecm_queue *txqs[IECM_LARGE_MAX_Q];
> > > +
> > > +	/* splitq only */
> > > +	struct iecm_queue *complq;
> > > +};
> > > +
> > > +struct iecm_adapter;
> > > +
> > > +void iecm_vport_init_num_qs(struct iecm_vport *vport,
> > > +			    struct virtchnl2_create_vport *vport_msg);
> > > +void iecm_vport_calc_num_q_desc(struct iecm_vport *vport);
> > > +void iecm_vport_calc_total_qs(struct iecm_adapter *adapter,
> > > +			      struct virtchnl2_create_vport *vport_msg);
> > > +void iecm_vport_calc_num_q_groups(struct iecm_vport *vport);
> > > +void iecm_vport_calc_num_q_vec(struct iecm_vport *vport);
> > >  irqreturn_t
> > >  iecm_vport_intr_clean_queues(int __always_unused irq, void *data);
> > >  #endif /* !_IECM_TXRX_H_ */
> > > --
> > > 2.33.0
> > 
> > Thanks,
> > Al
> _______________________________________________
> Intel-wired-lan mailing list
> Intel-wired-lan at osuosl.org
> https://lists.osuosl.org/mailman/listinfo/intel-wired-lan

^ permalink raw reply	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 18/19] iecm: add advanced rss
  2022-02-03  2:55     ` Brady, Alan
@ 2022-02-03 10:46       ` Maciej Fijalkowski
  2022-02-04 10:22       ` Alexander Lobakin
  1 sibling, 0 replies; 89+ messages in thread
From: Maciej Fijalkowski @ 2022-02-03 10:46 UTC (permalink / raw)
  To: intel-wired-lan

On Thu, Feb 03, 2022 at 02:55:57AM +0000, Brady, Alan wrote:
> > -----Original Message-----
> > From: Lobakin, Alexandr <alexandr.lobakin@intel.com>
> > Sent: Friday, January 28, 2022 11:54 AM
> > To: Brady, Alan <alan.brady@intel.com>
> > Cc: Lobakin, Alexandr <alexandr.lobakin@intel.com>; Wang, Haiyue
> > <haiyue.wang@intel.com>; intel-wired-lan at lists.osuosl.org; Burra, Phani R
> > <phani.r.burra@intel.com>; Chittim, Madhu <madhu.chittim@intel.com>;
> > Linga, Pavan Kumar <pavan.kumar.linga@intel.com>
> > Subject: Re: [Intel-wired-lan] [PATCH net-next 18/19] iecm: add advanced
> > rss
> > 
> > From: Alan Brady <alan.brady@intel.com>
> > Date: Thu, 27 Jan 2022 16:10:08 -0800
> > 
> > > From: Haiyue Wang <haiyue.wang@intel.com>
> > >
> > > Continuing with advanced features this implements what's needed to do
> > > advanced rss.
> > 
> > I'm sorry for not mentioned it before, but most of the series'
> > commit messages are poor and would probably get rejected upstream.
> > If they were explaining at least some very basics, it would be better. Even
> > better if there were explanations of some tricky code that happens time to
> > time.

I agree this should be improved. I don't know if this would be rejected
upstream, but in the future when looking at the commit history it would
just be more convenient for us and future readers if the description would
have more content in it.

> > 
> > >
> > > Signed-off-by: Haiyue Wang <haiyue.wang@intel.com>
> > > ---
> > >  drivers/net/ethernet/intel/iecm/iecm_lib.c    | 547
> > ++++++++++++++++++
> > >  .../net/ethernet/intel/iecm/iecm_virtchnl.c   |  71 +++
> > >  drivers/net/ethernet/intel/include/iecm.h     |  73 +++
> > >  3 files changed, 691 insertions(+)
> > >
> > > diff --git a/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > > b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > > index d11413cb438c..baa1e312652a 100644
> > > --- a/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > > +++ b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > > @@ -1013,6 +1013,52 @@ static void iecm_remove_vlan_filters(struct
> > iecm_vport *vport)
> > >  	}
> > >  }
> > >
> > > +/**
> > > + * iecm_remove_adv_rss_cfgs - Remove all RSS configuration
> > > + * @vport: vport structure
> > > + */
> > > +static void iecm_remove_adv_rss_cfgs(struct iecm_vport *vport) {
> > > +	struct iecm_adapter *adapter = vport->adapter;
> > > +
> > > +	if (!iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
> > VIRTCHNL2_CAP_ADV_RSS))
> > > +		return;
> > > +
> > > +	if (!list_empty(&adapter->config_data.adv_rss_list)) {
> > > +		struct iecm_adv_rss *rss;
> > > +
> > > +		spin_lock_bh(&adapter->adv_rss_list_lock);
> > > +		list_for_each_entry(rss, &adapter-
> > >config_data.adv_rss_list,
> > > +				    list) {
> > > +			rss->remove = true;
> > > +		}
> > 
> > Redundant braces arond an one-liner.
> > 
> 
> Maybe will fix.

? :D this one-liner is not even spread onto multiple lines, right? So why
'maybe' ?

> 
> > > +		spin_unlock_bh(&adapter->adv_rss_list_lock);
> > > +		iecm_send_add_del_adv_rss_cfg_msg(vport, false);
> > > +	}
> > 
> > Invert the condition for -1 indent level.
> > 
> 
> Will fix.
> 

^ permalink raw reply	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 04/19] iecm: add api_init and controlq init
  2022-02-03  3:24     ` Brady, Alan
  2022-02-03  3:40       ` Brady, Alan
@ 2022-02-03 13:13       ` Alexander Lobakin
  1 sibling, 0 replies; 89+ messages in thread
From: Alexander Lobakin @ 2022-02-03 13:13 UTC (permalink / raw)
  To: intel-wired-lan

From: Alan Brady <alan.brady@intel.com>
Date: Thu, 3 Feb 2022 03:24:57 +0000

> In-Reply-To: <CO1PR11MB518657515DD2AEBF539B9B768F289@CO1PR11MB5186.namprd11.prod.outlook.com>

Hmm, I wouldn't use Outlook for replying to patches discussion.
Apart from breaking the original code formatting, messages with
Outlook in X-mailer often get rejected from upstream vger.kernel.org
mailing lists.

> > -----Original Message-----
> > From: Shannon Nelson <shannon.lee.nelson@gmail.com>
> > Sent: Tuesday, February 1, 2022 1:27 PM
> > To: Brady, Alan <alan.brady@intel.com>
> > Cc: Intel Wired LAN <intel-wired-lan@lists.osuosl.org>; Burra, Phani R
> > <phani.r.burra@intel.com>; Chittim, Madhu <madhu.chittim@intel.com>;
> > Linga, Pavan Kumar <pavan.kumar.linga@intel.com>
> > Subject: Re: [Intel-wired-lan] [PATCH net-next 04/19] iecm: add api_init and
> > controlq init
> > 
> > On Thu, Jan 27, 2022 at 4:35 PM Alan Brady <alan.brady@intel.com>
> > wrote:
> > >
> > > Initializing device registers is offloaded into function pointers given
> > > to iecm from the dependent device driver for a given device, as offsets
> > > can vary wildly. This also adds everything needed to setup and use a
> > > controlq which uses some of those registers.
> > >
> > > At the end of probe we kicked off a hard reset and this implements what's
> > > needed to handle that reset and continue init.
> > >
> > > Signed-off-by: Phani Burra <phani.r.burra@intel.com>
> > > Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
> > > Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
> > > Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
> > > Signed-off-by: Alan Brady <alan.brady@intel.com>
> > > ---
> > >  drivers/net/ethernet/intel/iecm/Makefile      |   3 +
> > >  .../net/ethernet/intel/iecm/iecm_controlq.c   | 649
> > ++++++++++++++++++
> > >  .../ethernet/intel/iecm/iecm_controlq_setup.c | 175 +++++
> > >  drivers/net/ethernet/intel/iecm/iecm_lib.c    | 191 +++++-
> > >  .../net/ethernet/intel/iecm/iecm_virtchnl.c   | 172 +++++
> > >  drivers/net/ethernet/intel/include/iecm.h     |  52 ++
> > >  .../ethernet/intel/include/iecm_controlq.h    | 117 ++++
> > >  .../intel/include/iecm_controlq_api.h         | 185 +++++
> > >  drivers/net/ethernet/intel/include/iecm_mem.h |  20 +
> > >  9 files changed, 1563 insertions(+), 1 deletion(-)
> > >  create mode 100644 drivers/net/ethernet/intel/iecm/iecm_controlq.c
> > >  create mode 100644
> > drivers/net/ethernet/intel/iecm/iecm_controlq_setup.c
> > >  create mode 100644 drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> > >  create mode 100644 drivers/net/ethernet/intel/include/iecm_controlq.h
> > >  create mode 100644
> > drivers/net/ethernet/intel/include/iecm_controlq_api.h
> > >  create mode 100644 drivers/net/ethernet/intel/include/iecm_mem.h
> > >
> > > diff --git a/drivers/net/ethernet/intel/iecm/Makefile
> > b/drivers/net/ethernet/intel/iecm/Makefile
> > > index 4f497723419d..db8fecb075a6 100644
> > > --- a/drivers/net/ethernet/intel/iecm/Makefile
> > > +++ b/drivers/net/ethernet/intel/iecm/Makefile
> > > @@ -11,4 +11,7 @@ ccflags-y += -
> > I$(srctree)/drivers/net/ethernet/intel/include
> > >
> > >  iecm-y := \
> > >         iecm_lib.o \
> > > +       iecm_virtchnl.o \
> > > +       iecm_controlq.o \
> > > +       iecm_controlq_setup.o \
> > >         iecm_main.o
> > > diff --git a/drivers/net/ethernet/intel/iecm/iecm_controlq.c
> > b/drivers/net/ethernet/intel/iecm/iecm_controlq.c
> > > new file mode 100644
> > > index 000000000000..f9682a7b3e44
> > > --- /dev/null
> > > +++ b/drivers/net/ethernet/intel/iecm/iecm_controlq.c
> > > @@ -0,0 +1,649 @@
> > > +// SPDX-License-Identifier: GPL-2.0-only
> > > +/* Copyright (c) 2020, Intel Corporation. */
> > > +
> > > +#include "iecm_controlq.h"
> > > +
> > > +/**
> > > + * iecm_ctlq_setup_regs - initialize control queue registers
> > > + * @cq: pointer to the specific control queue
> > > + * @q_create_info: structs containing info for each queue to be
> > initialized
> > > + */
> > > +static void
> > > +iecm_ctlq_setup_regs(struct iecm_ctlq_info *cq,
> > > +                    struct iecm_ctlq_create_info *q_create_info)
> > > +{
> > > +       /* set head and tail registers in our local struct */
> > > +       cq->reg.head = q_create_info->reg.head;
> > > +       cq->reg.tail = q_create_info->reg.tail;
> > > +       cq->reg.len = q_create_info->reg.len;
> > > +       cq->reg.bah = q_create_info->reg.bah;
> > > +       cq->reg.bal = q_create_info->reg.bal;
> > > +       cq->reg.len_mask = q_create_info->reg.len_mask;
> > > +       cq->reg.len_ena_mask = q_create_info->reg.len_ena_mask;
> > > +       cq->reg.head_mask = q_create_info->reg.head_mask;
> > > +}
> > > +
> > > +/**
> > > + * iecm_ctlq_init_regs - Initialize control queue registers
> > > + * @hw: pointer to hw struct
> > > + * @cq: pointer to the specific Control queue
> > > + * @is_rxq: true if receive control queue, false otherwise
> > > + *
> > > + * Initialize registers. The caller is expected to have already initialized the
> > > + * descriptor ring memory and buffer memory
> > > + */
> > > +static void iecm_ctlq_init_regs(struct iecm_hw *hw, struct
> > iecm_ctlq_info *cq,
> > > +                               bool is_rxq)
> > > +{
> > > +       /* Update tail to post pre-allocated buffers for rx queues */
> > > +       if (is_rxq)
> > > +               wr32(hw, cq->reg.tail, (u32)(cq->ring_size - 1));
> > > +
> > > +       /* For non-Mailbox control queues only TAIL need to be set */
> > > +       if (cq->q_id != -1)
> > > +               return;
> > > +
> > > +       /* Clear Head for both send or receive */
> > > +       wr32(hw, cq->reg.head, 0);
> > > +
> > > +       /* set starting point */
> > > +       wr32(hw, cq->reg.bal, lower_32_bits(cq->desc_ring.pa));
> > > +       wr32(hw, cq->reg.bah, upper_32_bits(cq->desc_ring.pa));
> > > +       wr32(hw, cq->reg.len, (cq->ring_size | cq->reg.len_ena_mask));
> > > +}
> > > +
> > > +/**
> > > + * iecm_ctlq_init_rxq_bufs - populate receive queue descriptors with buf
> > > + * @cq: pointer to the specific Control queue
> > > + *
> > > + * Record the address of the receive queue DMA buffers in the
> > descriptors.
> > > + * The buffers must have been previously allocated.
> > > + */
> > > +static void iecm_ctlq_init_rxq_bufs(struct iecm_ctlq_info *cq)
> > > +{
> > > +       int i = 0;
> > > +
> > > +       for (i = 0; i < cq->ring_size; i++) {
> > > +               struct iecm_ctlq_desc *desc = IECM_CTLQ_DESC(cq, i);
> > > +               struct iecm_dma_mem *bi = cq->bi.rx_buff[i];
> > > +
> > > +               /* No buffer to post to descriptor, continue */
> > > +               if (!bi)
> > > +                       continue;
> > > +
> > > +               desc->flags =
> > > +                       cpu_to_le16(IECM_CTLQ_FLAG_BUF |
> > IECM_CTLQ_FLAG_RD);
> > > +               desc->opcode = 0;
> > > +               desc->datalen = (__le16)cpu_to_le16(bi->size);
> > 
> > Why the typecast to __le16, aren't we there already with the
> > cpu_to_le16()?
> > 
> 
> Yeah this is weird. Will fix.
> 
> > > +               desc->ret_val = 0;
> > > +               desc->cookie_high = 0;
> > > +               desc->cookie_low = 0;
> > > +               desc->params.indirect.addr_high =
> > > +                       cpu_to_le32(upper_32_bits(bi->pa));
> > > +               desc->params.indirect.addr_low =
> > > +                       cpu_to_le32(lower_32_bits(bi->pa));
> > > +               desc->params.indirect.param0 = 0;
> > > +               desc->params.indirect.param1 = 0;
> > > +       }
> > > +}
> > > +
> > > +/**
> > > + * iecm_ctlq_shutdown - shutdown the CQ
> > > + * @hw: pointer to hw struct
> > > + * @cq: pointer to the specific Control queue
> > > + *
> > > + * The main shutdown routine for any controq queue
> > > + */
> > > +static void iecm_ctlq_shutdown(struct iecm_hw *hw, struct
> > iecm_ctlq_info *cq)
> > > +{
> > > +       mutex_lock(&cq->cq_lock);
> > > +
> > > +       if (!cq->ring_size)
> > > +               goto shutdown_sq_out;
> > > +
> > > +       /* free ring buffers and the ring itself */
> > > +       iecm_ctlq_dealloc_ring_res(hw, cq);
> > > +
> > > +       /* Set ring_size to 0 to indicate uninitialized queue */
> > > +       cq->ring_size = 0;
> > > +
> > > +shutdown_sq_out:
> > > +       mutex_unlock(&cq->cq_lock);
> > > +       mutex_destroy(&cq->cq_lock);
> > > +}
> > > +
> > > +/**
> > > + * iecm_ctlq_add - add one control queue
> > > + * @hw: pointer to hardware struct
> > > + * @qinfo: info for queue to be created
> > > + * @cq_out: (output) double pointer to control queue to be created
> > > + *
> > > + * Allocate and initialize a control queue and add it to the control queue
> > list.
> > > + * The cq parameter will be allocated/initialized and passed back to the
> > caller
> > > + * if no errors occur.
> > > + *
> > > + * Note: iecm_ctlq_init must be called prior to any calls to
> > iecm_ctlq_add
> > > + */
> > > +int iecm_ctlq_add(struct iecm_hw *hw,
> > > +                 struct iecm_ctlq_create_info *qinfo,
> > > +                 struct iecm_ctlq_info **cq_out)
> > > +{
> > > +       bool is_rxq = false;
> > > +       int status = 0;
> > > +
> > > +       if (!qinfo->len || !qinfo->buf_size ||
> > > +           qinfo->len > IECM_CTLQ_MAX_RING_SIZE ||
> > > +           qinfo->buf_size > IECM_CTLQ_MAX_BUF_LEN)
> > > +               return -EINVAL;
> > > +
> > > +       *cq_out = kcalloc(1, sizeof(struct iecm_ctlq_info), GFP_KERNEL);
> > > +       if (!(*cq_out))
> > > +               return -ENOMEM;
> > 
> > You might keep this as a local variable until you get to a successful
> > end, then set *cq_out when done.
> > Else, you need to be sure to clear it back to NULL on error return to
> > be sure no one uses a bogus value.
> > 
> 
> This one I'm not sure I follow.  If it's NULL we fall into the if(!*cq_out) and don't need to set it to NULL (it already is?). If it's not NULL then we go on to use it like a valid memory address so I hope it's valid.

Please also fix

kcalloc(1, sizeof(struct iecm_ctlq_info), GFP_KERNEL)

to

kzalloc(sizeof(**cq_out), GFP_KERNEL)

while at it.
kcalloc(1) is excessive and it's preferred to take sizeof(*variable)
you assign to exclude type mismatches. checkpatch should've
complained on both of these.

> 
> > > +
> > > +       (*cq_out)->cq_type = qinfo->type;
> > > +       (*cq_out)->q_id = qinfo->id;
> > > +       (*cq_out)->buf_size = qinfo->buf_size;
> > > +       (*cq_out)->ring_size = qinfo->len;
> > > +
> > > +       (*cq_out)->next_to_use = 0;
> > > +       (*cq_out)->next_to_clean = 0;
> > > +       (*cq_out)->next_to_post = (*cq_out)->ring_size - 1;

I would follow Shannon's advice to create a local variable. All
those look like an unneeded complication.

> > > +
> > > +       switch (qinfo->type) {
> > > +       case IECM_CTLQ_TYPE_MAILBOX_RX:
> > > +               is_rxq = true;
> > > +               fallthrough;
> > > +       case IECM_CTLQ_TYPE_MAILBOX_TX:
> > > +               status = iecm_ctlq_alloc_ring_res(hw, *cq_out);
> > > +               break;
> > > +       default:
> > > +               status = -EBADR;
> > > +               break;
> > > +       }
> > > +
> > > +       if (status)
> > > +               goto init_free_q;
> > > +
> > > +       if (is_rxq) {
> > > +               iecm_ctlq_init_rxq_bufs(*cq_out);
> > > +       } else {
> > > +               /* Allocate the array of msg pointers for TX queues */
> > > +               (*cq_out)->bi.tx_msg = kcalloc(qinfo->len,

This kcalloc() is okay since we're allocating an array indeed.

> > > +                                              sizeof(struct iecm_ctlq_msg *),
> > > +                                              GFP_KERNEL);
> > > +               if (!(*cq_out)->bi.tx_msg) {
> > > +                       status = -ENOMEM;
> > > +                       goto init_dealloc_q_mem;
> > > +               }
> > > +       }
> > > +
> > > +       iecm_ctlq_setup_regs(*cq_out, qinfo);
> > > +
> > > +       iecm_ctlq_init_regs(hw, *cq_out, is_rxq);
> > > +
> > > +       mutex_init(&(*cq_out)->cq_lock);
> > > +
> > > +       list_add(&(*cq_out)->cq_list, &hw->cq_list_head);
> > > +
> > > +       return status;
> > > +
> > > +init_dealloc_q_mem:
> > > +       /* free ring buffers and the ring itself */
> > > +       iecm_ctlq_dealloc_ring_res(hw, *cq_out);
> > > +init_free_q:
> > > +       kfree(*cq_out);
> > 
> > Probably should clear this back to NULL.
> > 
> 
> Will fix.
> 
> > > +
> > > +       return status;
> > > +}
> > > +
> > > +/**
> > > + * iecm_ctlq_remove - deallocate and remove specified control queue
> > > + * @hw: pointer to hardware struct
> > > + * @cq: pointer to control queue to be removed
> > > + */
> > > +void iecm_ctlq_remove(struct iecm_hw *hw,
> > > +                     struct iecm_ctlq_info *cq)
> > > +{
> > > +       list_del(&cq->cq_list);
> > > +       iecm_ctlq_shutdown(hw, cq);
> > > +       kfree(cq);
> > > +}
> > > +
> > > +/**
> > > + * iecm_ctlq_init - main initialization routine for all control queues
> > > + * @hw: pointer to hardware struct
> > > + * @num_q: number of queues to initialize
> > > + * @q_info: array of structs containing info for each queue to be
> > initialized
> > > + *
> > > + * This initializes any number and any type of control queues. This is an
> > all
> > > + * or nothing routine; if one fails, all previously allocated queues will be
> > > + * destroyed. This must be called prior to using the individual
> > add/remove
> > > + * APIs.
> > > + */
> > > +int iecm_ctlq_init(struct iecm_hw *hw, u8 num_q,
> > > +                  struct iecm_ctlq_create_info *q_info)
> > > +{
> > > +       struct iecm_ctlq_info *cq = NULL, *tmp = NULL;
> > > +       int ret_code = 0;
> > > +       int i = 0;
> > > +
> > > +       INIT_LIST_HEAD(&hw->cq_list_head);
> > > +
> > > +       for (i = 0; i < num_q; i++) {
> > > +               struct iecm_ctlq_create_info *qinfo = q_info + i;
> > > +
> > > +               ret_code = iecm_ctlq_add(hw, qinfo, &cq);
> > > +               if (ret_code)
> > > +                       goto init_destroy_qs;
> > > +       }
> > > +
> > > +       return ret_code;
> > > +
> > > +init_destroy_qs:
> > > +       list_for_each_entry_safe(cq, tmp, &hw->cq_list_head, cq_list)
> > > +               iecm_ctlq_remove(hw, cq);
> > 
> > This could call iecm_ctlq_deinit() rather than repeat the same code.
> > 
> 
> Seems fair, will fix.
> 
> > > +
> > > +       return ret_code;
> > > +}
> > > +
> > > +/**
> > > + * iecm_ctlq_deinit - destroy all control queues
> > > + * @hw: pointer to hw struct
> > > + */
> > > +int iecm_ctlq_deinit(struct iecm_hw *hw)
> > > +{
> > > +       struct iecm_ctlq_info *cq = NULL, *tmp = NULL;
> > > +       int ret_code = 0;
> > > +
> > > +       list_for_each_entry_safe(cq, tmp, &hw->cq_list_head, cq_list)
> > > +               iecm_ctlq_remove(hw, cq);
> > > +
> > > +       return ret_code;
> > 
> > Why is there a return code here when there is never an error?
> > 
> 
> Yeah this is weird, will fix.
> 
> > > +}
> > > +
> > > +/**
> > > + * iecm_ctlq_send - send command to Control Queue (CTQ)
> > > + * @hw: pointer to hw struct
> > > + * @cq: handle to control queue struct to send on
> > > + * @num_q_msg: number of messages to send on control queue
> > > + * @q_msg: pointer to array of queue messages to be sent
> > > + *
> > > + * The caller is expected to allocate DMAable buffers and pass them to
> > the
> > > + * send routine via the q_msg struct / control queue specific data struct.
> > > + * The control queue will hold a reference to each send message until
> > > + * the completion for that message has been cleaned.
> > > + */
> > > +int iecm_ctlq_send(struct iecm_hw *hw, struct iecm_ctlq_info *cq,
> > > +                  u16 num_q_msg, struct iecm_ctlq_msg q_msg[])
> > > +{
> > > +       struct iecm_ctlq_desc *desc;
> > > +       int num_desc_avail = 0;
> > > +       int status = 0;
> > > +       int i = 0;
> > > +
> > > +       if (!cq || !cq->ring_size)
> > > +               return -ENOBUFS;
> > > +
> > > +       mutex_lock(&cq->cq_lock);
> > > +
> > > +       /* Ensure there are enough descriptors to send all messages */
> > > +       num_desc_avail = IECM_CTLQ_DESC_UNUSED(cq);
> > > +       if (num_desc_avail == 0 || num_desc_avail < num_q_msg) {
> > > +               status = -ENOSPC;
> > > +               goto sq_send_command_out;
> > > +       }
> > > +
> > > +       for (i = 0; i < num_q_msg; i++) {
> > > +               struct iecm_ctlq_msg *msg = &q_msg[i];
> > > +               u64 msg_cookie;
> > > +
> > > +               desc = IECM_CTLQ_DESC(cq, cq->next_to_use);
> > > +
> > > +               desc->opcode = cpu_to_le16(msg->opcode);
> > > +               desc->pfid_vfid = cpu_to_le16(msg->func_id);
> > > +
> > > +               msg_cookie = *(u64 *)&msg->cookie;
> > > +               desc->cookie_high =
> > > +                       cpu_to_le32(upper_32_bits(msg_cookie));
> > > +               desc->cookie_low =
> > > +                       cpu_to_le32(lower_32_bits(msg_cookie));
> > > +
> > > +               if (msg->data_len) {
> > > +                       struct iecm_dma_mem *buff = msg->ctx.indirect.payload;
> > > +
> > > +                       desc->datalen = cpu_to_le16(msg->data_len);
> > > +                       desc->flags |= cpu_to_le16(IECM_CTLQ_FLAG_BUF);
> > > +                       desc->flags |= cpu_to_le16(IECM_CTLQ_FLAG_RD);
> > > +
> > > +                       /* Update the address values in the desc with the pa
> > > +                        * value for respective buffer
> > > +                        */
> > > +                       desc->params.indirect.addr_high =
> > > +                               cpu_to_le32(upper_32_bits(buff->pa));
> > > +                       desc->params.indirect.addr_low =
> > > +                               cpu_to_le32(lower_32_bits(buff->pa));
> > > +
> > > +                       memcpy(&desc->params, msg->ctx.indirect.context,
> > > +                              IECM_INDIRECT_CTX_SIZE);
> > > +               } else {
> > > +                       memcpy(&desc->params, msg->ctx.direct,
> > > +                              IECM_DIRECT_CTX_SIZE);
> > > +               }
> > > +
> > > +               /* Store buffer info */
> > > +               cq->bi.tx_msg[cq->next_to_use] = msg;
> > > +
> > > +               (cq->next_to_use)++;
> > > +               if (cq->next_to_use == cq->ring_size)
> > > +                       cq->next_to_use = 0;
> > > +       }
> > > +
> > > +       /* Force memory write to complete before letting hardware
> > > +        * know that there are new descriptors to fetch.
> > > +        */
> > > +       dma_wmb();
> > > +
> > > +       wr32(hw, cq->reg.tail, cq->next_to_use);
> > > +
> > > +sq_send_command_out:
> > > +       mutex_unlock(&cq->cq_lock);
> > > +
> > > +       return status;
> > > +}
> > > +
> > > +/**
> > > + * iecm_ctlq_clean_sq - reclaim send descriptors on HW write back for
> > the
> > > + * requested queue
> > > + * @cq: pointer to the specific Control queue
> > > + * @clean_count: (input|output) number of descriptors to clean as
> > input, and
> > > + * number of descriptors actually cleaned as output
> > 
> > Rather than two meanings for one parameter, can the number cleaned be
> > a positive return value?
> > 
> 
> That seems fair. Will check.
> 
> > > + * @msg_status: (output) pointer to msg pointer array to be populated;
> > needs
> > > + * to be allocated by caller
> > > + *
> > > + * Returns an array of message pointers associated with the cleaned
> > > + * descriptors. The pointers are to the original ctlq_msgs sent on the
> > cleaned
> > > + * descriptors.  The status will be returned for each; any messages that
> > failed
> > > + * to send will have a non-zero status. The caller is expected to free
> > original
> > > + * ctlq_msgs and free or reuse the DMA buffers.
> > > + */
> > > +int iecm_ctlq_clean_sq(struct iecm_ctlq_info *cq, u16 *clean_count,
> > > +                      struct iecm_ctlq_msg *msg_status[])
> > > +{
> > > +       struct iecm_ctlq_desc *desc;
> > > +       u16 i = 0, num_to_clean;
> > > +       u16 ntc, desc_err;
> > > +       int ret = 0;
> > > +
> > > +       if (!cq || !cq->ring_size)
> > > +               return -ENOBUFS;
> > > +
> > > +       if (*clean_count == 0)
> > > +               return 0;
> > > +       if (*clean_count > cq->ring_size)
> > > +               return -EBADR;
> > > +
> > > +       mutex_lock(&cq->cq_lock);
> > > +
> > > +       ntc = cq->next_to_clean;
> > > +
> > > +       num_to_clean = *clean_count;
> > > +
> > > +       for (i = 0; i < num_to_clean; i++) {
> > > +               /* Fetch next descriptor and check if marked as done */
> > > +               desc = IECM_CTLQ_DESC(cq, ntc);
> > > +               if (!(le16_to_cpu(desc->flags) & IECM_CTLQ_FLAG_DD))
> > > +                       break;
> > > +
> > > +               desc_err = le16_to_cpu(desc->ret_val);
> > > +               if (desc_err) {
> > > +                       /* strip off FW internal code */
> > > +                       desc_err &= 0xff;
> > > +               }
> > 
> > Why not simply
> >     desc_err = le16_to_cpu(desc->ret_val) & 0xff;
> > 
> 
> Will fix.
> 
> > > +
> > > +               msg_status[i] = cq->bi.tx_msg[ntc];
> > > +               msg_status[i]->status = desc_err;
> > > +
> > > +               cq->bi.tx_msg[ntc] = NULL;
> > > +
> > > +               /* Zero out any stale data */
> > > +               memset(desc, 0, sizeof(*desc));
> > > +
> > > +               ntc++;
> > > +               if (ntc == cq->ring_size)
> > > +                       ntc = 0;
> > > +       }
> > > +
> > > +       cq->next_to_clean = ntc;
> > > +
> > > +       mutex_unlock(&cq->cq_lock);
> > > +
> > > +       /* Return number of descriptors actually cleaned */
> > > +       *clean_count = i;
> > > +
> > > +       return ret;
> > > +}
> > > +
> > > +/**
> > > + * iecm_ctlq_post_rx_buffs - post buffers to descriptor ring
> > > + * @hw: pointer to hw struct
> > > + * @cq: pointer to control queue handle
> > > + * @buff_count: (input|output) input is number of buffers caller is
> > trying to
> > > + * return; output is number of buffers that were not posted
> > 
> > Same comment.
> > And the return would make more sense if consistent with the previous
> > function where the return is how many were successful.
> > 
> 
> Yeah this seems more sane, need a little time to see what the refactor looks like.
> 
> > > + * @buffs: array of pointers to dma mem structs to be given to
> > hardware
> > > + *
> > > + * Caller uses this function to return DMA buffers to the descriptor ring
> > after
> > > + * consuming them; buff_count will be the number of buffers.
> > > + *
> > > + * Note: this function needs to be called after a receive call even
> > > + * if there are no DMA buffers to be returned, i.e. buff_count = 0,
> > > + * buffs = NULL to support direct commands
> > > + */
> > > +int iecm_ctlq_post_rx_buffs(struct iecm_hw *hw, struct iecm_ctlq_info
> > *cq,
> > > +                           u16 *buff_count, struct iecm_dma_mem **buffs)
> > > +{
> > > +       struct iecm_ctlq_desc *desc;
> > > +       u16 ntp = cq->next_to_post;
> > > +       bool buffs_avail = false;
> > > +       u16 tbp = ntp + 1;
> > > +       int status = 0;
> > > +       int i = 0;
> > > +
> > > +       if (*buff_count > cq->ring_size)
> > > +               return -EBADR;
> > > +
> > > +       if (*buff_count > 0)
> > > +               buffs_avail = true;
> > > +
> > > +       mutex_lock(&cq->cq_lock);
> > > +
> > > +       if (tbp >= cq->ring_size)
> > > +               tbp = 0;
> > > +
> > > +       if (tbp == cq->next_to_clean)
> > > +               /* Nothing to do */
> > > +               goto post_buffs_out;
> > > +
> > > +       /* Post buffers for as many as provided or up until the last one used
> > */
> > > +       while (ntp != cq->next_to_clean) {
> > > +               desc = IECM_CTLQ_DESC(cq, ntp);
> > > +
> > > +               if (cq->bi.rx_buff[ntp])
> > > +                       goto fill_desc;
> > > +               if (!buffs_avail) {
> > > +                       /* If the caller hasn't given us any buffers or
> > > +                        * there are none left, search the ring itself
> > > +                        * for an available buffer to move to this
> > > +                        * entry starting at the next entry in the ring
> > > +                        */
> > > +                       tbp = ntp + 1;
> > > +
> > > +                       /* Wrap ring if necessary */
> > > +                       if (tbp >= cq->ring_size)
> > > +                               tbp = 0;
> > > +
> > > +                       while (tbp != cq->next_to_clean) {
> > > +                               if (cq->bi.rx_buff[tbp]) {
> > > +                                       cq->bi.rx_buff[ntp] =
> > > +                                               cq->bi.rx_buff[tbp];
> > > +                                       cq->bi.rx_buff[tbp] = NULL;
> > > +
> > > +                                       /* Found a buffer, no need to
> > > +                                        * search anymore
> > > +                                        */
> > > +                                       break;
> > > +                               }
> > > +
> > > +                               /* Wrap ring if necessary */
> > > +                               tbp++;
> > > +                               if (tbp >= cq->ring_size)
> > > +                                       tbp = 0;
> > > +                       }
> > > +
> > > +                       if (tbp == cq->next_to_clean)
> > > +                               goto post_buffs_out;
> > > +               } else {
> > > +                       /* Give back pointer to DMA buffer */
> > > +                       cq->bi.rx_buff[ntp] = buffs[i];
> > > +                       i++;
> > > +
> > > +                       if (i >= *buff_count)
> > > +                               buffs_avail = false;
> > > +               }
> > > +
> > > +fill_desc:
> > > +               desc->flags =
> > > +                       cpu_to_le16(IECM_CTLQ_FLAG_BUF |
> > IECM_CTLQ_FLAG_RD);
> > > +
> > > +               /* Post buffers to descriptor */
> > > +               desc->datalen = cpu_to_le16(cq->bi.rx_buff[ntp]->size);
> > > +               desc->params.indirect.addr_high =
> > > +                       cpu_to_le32(upper_32_bits(cq->bi.rx_buff[ntp]->pa));
> > > +               desc->params.indirect.addr_low =
> > > +                       cpu_to_le32(lower_32_bits(cq->bi.rx_buff[ntp]->pa));
> > > +
> > > +               ntp++;
> > > +               if (ntp == cq->ring_size)
> > > +                       ntp = 0;
> > > +       }
> > > +
> > > +post_buffs_out:
> > > +       /* Only update tail if buffers were actually posted */
> > > +       if (cq->next_to_post != ntp) {
> > > +               if (ntp)
> > > +                       /* Update next_to_post to ntp - 1 since current ntp
> > > +                        * will not have a buffer
> > > +                        */
> > > +                       cq->next_to_post = ntp - 1;
> > > +               else
> > > +                       /* Wrap to end of end ring since current ntp is 0 */
> > > +                       cq->next_to_post = cq->ring_size - 1;
> > > +
> > > +               wr32(hw, cq->reg.tail, cq->next_to_post);
> > > +       }
> > > +
> > > +       mutex_unlock(&cq->cq_lock);
> > > +
> > > +       /* return the number of buffers that were not posted */
> > > +       *buff_count = *buff_count - i;
> > > +
> > > +       return status;
> > > +}
> > > +
> > > +/**
> > > + * iecm_ctlq_recv - receive control queue message call back
> > > + * @cq: pointer to control queue handle to receive on
> > > + * @num_q_msg: (input|output) input number of messages that should
> > be received;
> > > + * output number of messages actually received
> > > + * @q_msg: (output) array of received control queue messages on this
> > q;
> > > + * needs to be pre-allocated by caller for as many messages as requested
> > > + *
> > > + * Called by interrupt handler or polling mechanism. Caller is expected
> > > + * to free buffers
> > > + */
> > > +int iecm_ctlq_recv(struct iecm_ctlq_info *cq, u16 *num_q_msg,
> > > +                  struct iecm_ctlq_msg *q_msg)
> > > +{
> > > +       u16 num_to_clean, ntc, ret_val, flags;
> > > +       struct iecm_ctlq_desc *desc;
> > > +       int ret_code = 0;
> > > +       u16 i = 0;
> > > +
> > > +       if (!cq || !cq->ring_size)
> > > +               return -ENOBUFS;
> > 
> > Should *num_q_msgs get set to 0 here since none were received?
> > 
> 
> Based on the function description it does indeed sound like we should be.
> 
> > > +
> > > +       if (*num_q_msg == 0)
> > > +               return 0;
> > > +       else if (*num_q_msg > cq->ring_size)
> > > +               return -EBADR;
> > 
> > Again, set *num_q_msgs to 0?
> > 
> > > +
> > > +       /* take the lock before we start messing with the ring */
> > > +       mutex_lock(&cq->cq_lock);
> > > +
> > > +       ntc = cq->next_to_clean;
> > > +
> > > +       num_to_clean = *num_q_msg;
> > > +
> > > +       for (i = 0; i < num_to_clean; i++) {
> > > +               u64 msg_cookie;
> > > +
> > > +               /* Fetch next descriptor and check if marked as done */
> > > +               desc = IECM_CTLQ_DESC(cq, ntc);
> > > +               flags = le16_to_cpu(desc->flags);
> > > +
> > > +               if (!(flags & IECM_CTLQ_FLAG_DD))
> > > +                       break;
> > > +
> > > +               ret_val = le16_to_cpu(desc->ret_val);
> > > +
> > > +               q_msg[i].vmvf_type = (flags &
> > > +                                     (IECM_CTLQ_FLAG_FTYPE_VM |
> > > +                                      IECM_CTLQ_FLAG_FTYPE_PF)) >>
> > > +                                     IECM_CTLQ_FLAG_FTYPE_S;
> > > +
> > > +               if (flags & IECM_CTLQ_FLAG_ERR)
> > > +                       ret_code = -EBADMSG;
> > 
> > So you might return -EBADMSG, even if you've cleaned many messages?
> > Will this be reflected in ret_val and does the caller then need to
> > hunt down the bad q_msg[].status?
> > How these work together should probably be clearly explained in the
> > function header.
> > 
> 
> I'll need some time to look at this, but it does seem suspect.
> 
> > > +
> > > +               msg_cookie = (u64)le32_to_cpu(desc->cookie_high) << 32;
> > > +               msg_cookie |= (u64)le32_to_cpu(desc->cookie_low);
> > > +               memcpy(&q_msg[i].cookie, &msg_cookie, sizeof(u64));
> > > +
> > > +               q_msg[i].opcode = le16_to_cpu(desc->opcode);
> > > +               q_msg[i].data_len = le16_to_cpu(desc->datalen);
> > > +               q_msg[i].status = ret_val;
> > > +
> > > +               if (desc->datalen) {
> > > +                       memcpy(q_msg[i].ctx.indirect.context,
> > > +                              &desc->params.indirect, IECM_INDIRECT_CTX_SIZE);
> > > +
> > > +                       /* Assign pointer to dma buffer to ctlq_msg array
> > > +                        * to be given to upper layer
> > > +                        */
> > > +                       q_msg[i].ctx.indirect.payload = cq->bi.rx_buff[ntc];
> > > +
> > > +                       /* Zero out pointer to DMA buffer info;
> > > +                        * will be repopulated by post buffers API
> > > +                        */
> > > +                       cq->bi.rx_buff[ntc] = NULL;
> > > +               } else {
> > > +                       memcpy(q_msg[i].ctx.direct, desc->params.raw,
> > > +                              IECM_DIRECT_CTX_SIZE);
> > > +               }
> > > +
> > > +               /* Zero out stale data in descriptor */
> > > +               memset(desc, 0, sizeof(struct iecm_ctlq_desc));
> > > +
> > > +               ntc++;
> > > +               if (ntc == cq->ring_size)
> > > +                       ntc = 0;
> > > +       };
> > > +
> > > +       cq->next_to_clean = ntc;
> > > +
> > > +       mutex_unlock(&cq->cq_lock);
> > > +
> > > +       *num_q_msg = i;
> > > +       if (*num_q_msg == 0)
> > > +               ret_code = -ENOMSG;
> > > +
> > > +       return ret_code;
> > > +}
> > > diff --git a/drivers/net/ethernet/intel/iecm/iecm_controlq_setup.c
> > b/drivers/net/ethernet/intel/iecm/iecm_controlq_setup.c
> > > new file mode 100644
> > > index 000000000000..a36fc88d6bb5
> > > --- /dev/null
> > > +++ b/drivers/net/ethernet/intel/iecm/iecm_controlq_setup.c
> > > @@ -0,0 +1,175 @@
> > > +// SPDX-License-Identifier: GPL-2.0-only
> > > +/* Copyright (c) 2020, Intel Corporation. */
> > > +
> > > +#include "iecm_controlq.h"
> > > +
> > > +/**
> > > + * iecm_ctlq_alloc_desc_ring - Allocate Control Queue (CQ) rings
> > > + * @hw: pointer to hw struct
> > > + * @cq: pointer to the specific Control queue
> > > + */
> > > +static int
> > > +iecm_ctlq_alloc_desc_ring(struct iecm_hw *hw,
> > > +                         struct iecm_ctlq_info *cq)
> > > +{
> > > +       size_t size = cq->ring_size * sizeof(struct iecm_ctlq_desc);
> > > +
> > > +       cq->desc_ring.va = iecm_alloc_dma_mem(hw, &cq->desc_ring,
> > size);
> > > +       if (!cq->desc_ring.va)
> > > +               return -ENOMEM;
> > > +
> > > +       return 0;
> > > +}
> > > +
> > > +/**
> > > + * iecm_ctlq_alloc_bufs - Allocate Control Queue (CQ) buffers
> > > + * @hw: pointer to hw struct
> > > + * @cq: pointer to the specific Control queue
> > > + *
> > > + * Allocate the buffer head for all control queues, and if it's a receive
> > > + * queue, allocate DMA buffers
> > > + */
> > > +static int iecm_ctlq_alloc_bufs(struct iecm_hw *hw,
> > > +                               struct iecm_ctlq_info *cq)
> > > +{
> > > +       int i = 0;
> > > +
> > > +       /* Do not allocate DMA buffers for transmit queues */
> > > +       if (cq->cq_type == IECM_CTLQ_TYPE_MAILBOX_TX)
> > > +               return 0;
> > > +
> > > +       /* We'll be allocating the buffer info memory first, then we can
> > > +        * allocate the mapped buffers for the event processing
> > > +        */
> > > +       cq->bi.rx_buff = kcalloc(cq->ring_size, sizeof(struct iecm_dma_mem
> > *),
> > > +                                GFP_KERNEL);
> > > +       if (!cq->bi.rx_buff)
> > > +               return -ENOMEM;
> > > +
> > > +       /* allocate the mapped buffers (except for the last one) */
> > > +       for (i = 0; i < cq->ring_size - 1; i++) {
> > > +               struct iecm_dma_mem *bi;
> > > +               int num = 1; /* number of iecm_dma_mem to be allocated */
> > > +
> > > +               cq->bi.rx_buff[i] = kcalloc(num, sizeof(struct iecm_dma_mem),
> > > +                                           GFP_KERNEL);
> > > +               if (!cq->bi.rx_buff[i])
> > > +                       goto unwind_alloc_cq_bufs;
> > > +
> > > +               bi = cq->bi.rx_buff[i];
> > > +
> > > +               bi->va = iecm_alloc_dma_mem(hw, bi, cq->buf_size);
> > > +               if (!bi->va) {
> > > +                       /* unwind will not free the failed entry */
> > > +                       kfree(cq->bi.rx_buff[i]);
> > > +                       goto unwind_alloc_cq_bufs;
> > > +               }
> > > +       }
> > > +
> > > +       return 0;
> > > +
> > > +unwind_alloc_cq_bufs:
> > > +       /* don't try to free the one that failed... */
> > > +       i--;
> > > +       for (; i >= 0; i--) {
> > > +               iecm_free_dma_mem(hw, cq->bi.rx_buff[i]);
> > > +               kfree(cq->bi.rx_buff[i]);
> > > +       }
> > > +       kfree(cq->bi.rx_buff);
> > > +
> > > +       return -ENOMEM;
> > > +}
> > > +
> > > +/**
> > > + * iecm_ctlq_free_desc_ring - Free Control Queue (CQ) rings
> > > + * @hw: pointer to hw struct
> > > + * @cq: pointer to the specific Control queue
> > > + *
> > > + * This assumes the posted send buffers have already been cleaned
> > > + * and de-allocated
> > > + */
> > > +static void iecm_ctlq_free_desc_ring(struct iecm_hw *hw,
> > > +                                    struct iecm_ctlq_info *cq)
> > > +{
> > > +       iecm_free_dma_mem(hw, &cq->desc_ring);
> > > +}
> > > +
> > > +/**
> > > + * iecm_ctlq_free_bufs - Free CQ buffer info elements
> > > + * @hw: pointer to hw struct
> > > + * @cq: pointer to the specific Control queue
> > > + *
> > > + * Free the DMA buffers for RX queues, and DMA buffer header for both
> > RX and TX
> > > + * queues.  The upper layers are expected to manage freeing of TX DMA
> > buffers
> > > + */
> > > +static void iecm_ctlq_free_bufs(struct iecm_hw *hw, struct
> > iecm_ctlq_info *cq)
> > > +{
> > > +       void *bi;
> > > +
> > > +       if (cq->cq_type == IECM_CTLQ_TYPE_MAILBOX_RX) {
> > > +               int i;
> > > +
> > > +               /* free DMA buffers for rx queues*/
> > > +               for (i = 0; i < cq->ring_size; i++) {
> > > +                       if (cq->bi.rx_buff[i]) {
> > > +                               iecm_free_dma_mem(hw, cq->bi.rx_buff[i]);
> > > +                               kfree(cq->bi.rx_buff[i]);
> > > +                       }
> > > +               }
> > > +
> > > +               bi = (void *)cq->bi.rx_buff;
> > > +       } else {
> > > +               bi = (void *)cq->bi.tx_msg;
> > > +       }
> > > +
> > > +       /* free the buffer header */
> > > +       kfree(bi);
> > > +}
> > > +
> > > +/**
> > > + * iecm_ctlq_dealloc_ring_res - Free memory allocated for control queue
> > > + * @hw: pointer to hw struct
> > > + * @cq: pointer to the specific Control queue
> > > + *
> > > + * Free the memory used by the ring, buffers and other related
> > structures
> > > + */
> > > +void iecm_ctlq_dealloc_ring_res(struct iecm_hw *hw, struct
> > iecm_ctlq_info *cq)
> > > +{
> > > +       /* free ring buffers and the ring itself */
> > > +       iecm_ctlq_free_bufs(hw, cq);
> > > +       iecm_ctlq_free_desc_ring(hw, cq);
> > > +}
> > > +
> > > +/**
> > > + * iecm_ctlq_alloc_ring_res - allocate memory for descriptor ring and
> > bufs
> > > + * @hw: pointer to hw struct
> > > + * @cq: pointer to control queue struct
> > > + *
> > > + * Do *NOT* hold the lock when calling this as the memory allocation
> > routines
> > > + * called are not going to be atomic context safe
> > > + */
> > > +int iecm_ctlq_alloc_ring_res(struct iecm_hw *hw, struct iecm_ctlq_info
> > *cq)
> > > +{
> > > +       int ret_code;
> > > +
> > > +       /* verify input for valid configuration */
> > > +       if (!cq->ring_size || !cq->buf_size)
> > > +               return -EINVAL;
> > > +
> > > +       /* allocate the ring memory */
> > > +       ret_code = iecm_ctlq_alloc_desc_ring(hw, cq);
> > > +       if (ret_code)
> > > +               return ret_code;
> > > +
> > > +       /* allocate buffers in the rings */
> > > +       ret_code = iecm_ctlq_alloc_bufs(hw, cq);
> > > +       if (ret_code)
> > > +               goto iecm_init_cq_free_ring;
> > > +
> > > +       /* success! */
> > > +       return 0;
> > > +
> > > +iecm_init_cq_free_ring:
> > > +       iecm_free_dma_mem(hw, &cq->desc_ring);
> > > +       return ret_code;
> > > +}
> > > diff --git a/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > > index e6d0b418a27f..64cdbce2c842 100644
> > > --- a/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > > +++ b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > > @@ -5,6 +5,25 @@
> > >
> > >  #include "iecm.h"
> > >
> > > +/**
> > > + * iecm_cfg_hw - Initialize HW struct
> > > + * @adapter: adapter to setup hw struct for
> > > + *
> > > + * Returns 0 on success, negative on failure
> > > + */
> > > +static int iecm_cfg_hw(struct iecm_adapter *adapter)
> > > +{
> > > +       struct pci_dev *pdev = adapter->pdev;
> > > +       struct iecm_hw *hw = &adapter->hw;
> > > +
> > > +       hw->hw_addr = pcim_iomap_table(pdev)[IECM_BAR0];
> > > +       if (!hw->hw_addr)
> > > +               return -EIO;
> > > +       hw->back = adapter;
> > > +
> > > +       return 0;
> > > +}
> > > +
> > >  /**
> > >   * iecm_statistics_task - Delayed task to get statistics over mailbox
> > >   * @work: work_struct handle to our data
> > > @@ -39,6 +58,32 @@ static void iecm_init_task(struct work_struct
> > *work)
> > >         /* stub */
> > >  }
> > >
> > > +/**
> > > + * iecm_api_init - Initialize and verify device API
> > > + * @adapter: driver specific private structure
> > > + *
> > > + * Returns 0 on success, negative on failure
> > > + */
> > > +static int iecm_api_init(struct iecm_adapter *adapter)
> > > +{
> > > +       struct iecm_reg_ops *reg_ops = &adapter->dev_ops.reg_ops;
> > > +       struct pci_dev *pdev = adapter->pdev;
> > > +
> > > +       if (!adapter->dev_ops.reg_ops_init) {
> > > +               dev_err(&pdev->dev, "Invalid device, register API init not
> > defined\n");
> > > +               return -EINVAL;
> > > +       }
> > > +       adapter->dev_ops.reg_ops_init(adapter);
> > > +       if (!(reg_ops->ctlq_reg_init && reg_ops->intr_reg_init &&
> > > +             reg_ops->mb_intr_reg_init && reg_ops->reset_reg_init &&
> > > +             reg_ops->trigger_reset)) {
> > > +               dev_err(&pdev->dev, "Invalid device, missing one or more
> > register functions\n");
> > > +               return -EINVAL;
> > > +       }
> > > +
> > > +       return 0;
> > > +}
> > > +
> > >  /**
> > >   * iecm_deinit_task - Device deinit routine
> > >   * @adapter: Driver specific private structue
> > > @@ -51,13 +96,108 @@ static void iecm_deinit_task(struct
> > iecm_adapter *adapter)
> > >         /* stub */
> > >  }
> > >
> > > +/**
> > > + * iecm_check_reset_complete - check that reset is complete
> > > + * @hw: pointer to hw struct
> > > + * @reset_reg: struct with reset registers
> > > + *
> > > + * Returns 0 if device is ready to use, or -EBUSY if it's in reset.
> > > + **/
> > > +static int iecm_check_reset_complete(struct iecm_hw *hw,
> > > +                                    struct iecm_reset_reg *reset_reg)
> > > +{
> > > +       struct iecm_adapter *adapter = (struct iecm_adapter *)hw->back;
> > > +       int i;
> > > +
> > > +       for (i = 0; i < 2000; i++) {
> > > +               u32 reg_val = rd32(hw, reset_reg->rstat);
> > > +
> > > +               /* 0xFFFFFFFF might be read if other side hasn't cleared the
> > > +                * register for us yet and 0xFFFFFFFF is not a valid value for
> > > +                * the register, so treat that as invalid.
> > > +                */
> > > +               if (reg_val != 0xFFFFFFFF && (reg_val & reset_reg->rstat_m))
> > > +                       return 0;
> > > +               usleep_range(5000, 10000);
> > > +       }
> > > +
> > > +       dev_warn(&adapter->pdev->dev, "Device reset timeout!\n");
> > > +       return -EBUSY;
> > > +}
> > > +
> > > +/**
> > > + * iecm_init_hard_reset - Initiate a hardware reset
> > > + * @adapter: Driver specific private structure
> > > + *
> > > + * Deallocate the vports and all the resources associated with them and
> > > + * reallocate. Also reinitialize the mailbox. Return 0 on success,
> > > + * negative on failure.
> > > + */
> > > +static int iecm_init_hard_reset(struct iecm_adapter *adapter)
> > > +{
> > > +       int err = 0;
> > > +
> > > +       mutex_lock(&adapter->reset_lock);
> > > +
> > > +       /* Prepare for reset */
> > > +       if (test_and_clear_bit(__IECM_HR_DRV_LOAD, adapter->flags)) {
> > > +               adapter->dev_ops.reg_ops.trigger_reset(adapter,
> > > +                                                      __IECM_HR_DRV_LOAD);
> > > +       } else if (test_and_clear_bit(__IECM_HR_FUNC_RESET, adapter-
> > >flags)) {
> > > +               bool is_reset = iecm_is_reset_detected(adapter);
> > > +
> > > +               if (adapter->state == __IECM_UP)
> > > +                       set_bit(__IECM_UP_REQUESTED, adapter->flags);
> > > +               iecm_deinit_task(adapter);
> > > +               if (!is_reset)
> > > +                       adapter->dev_ops.reg_ops.trigger_reset(adapter,
> > > +                                                              __IECM_HR_FUNC_RESET);
> > > +               iecm_deinit_dflt_mbx(adapter);
> > > +       } else if (test_and_clear_bit(__IECM_HR_CORE_RESET, adapter-
> > >flags)) {
> > > +               if (adapter->state == __IECM_UP)
> > > +                       set_bit(__IECM_UP_REQUESTED, adapter->flags);
> > > +               iecm_deinit_task(adapter);
> > > +       } else {
> > > +               dev_err(&adapter->pdev->dev, "Unhandled hard reset
> > cause\n");
> > > +               err = -EBADRQC;
> > > +               goto handle_err;
> > > +       }
> > > +
> > > +       /* Wait for reset to complete */
> > > +       err = iecm_check_reset_complete(&adapter->hw, &adapter-
> > >reset_reg);
> > > +       if (err) {
> > > +               dev_err(&adapter->pdev->dev, "The driver was unable to
> > contact the device's firmware.  Check that the FW is running. Driver
> > state=%u\n",
> > > +                       adapter->state);
> > > +               goto handle_err;
> > > +       }
> > > +
> > > +       /* Reset is complete and so start building the driver resources again
> > */
> > > +       err = iecm_init_dflt_mbx(adapter);
> > > +       if (err) {
> > > +               dev_err(&adapter->pdev->dev, "Failed to initialize default
> > mailbox: %d\n",
> > > +                       err);
> > > +       }
> > > +handle_err:
> > > +       mutex_unlock(&adapter->reset_lock);
> > > +       return err;
> > > +}
> > > +
> > >  /**
> > >   * iecm_vc_event_task - Handle virtchannel event logic
> > >   * @work: work queue struct
> > >   */
> > >  static void iecm_vc_event_task(struct work_struct *work)
> > >  {
> > > -       /* stub */
> > > +       struct iecm_adapter *adapter = container_of(work,
> > > +                                                   struct iecm_adapter,
> > > +                                                   vc_event_task.work);
> > > +
> > > +       if (test_bit(__IECM_HR_CORE_RESET, adapter->flags) ||
> > > +           test_bit(__IECM_HR_FUNC_RESET, adapter->flags) ||
> > > +           test_bit(__IECM_HR_DRV_LOAD, adapter->flags)) {
> > > +               set_bit(__IECM_HR_RESET_IN_PROG, adapter->flags);
> > > +               iecm_init_hard_reset(adapter);
> > > +       }
> > >  }
> > >
> > >  /**
> > > @@ -75,6 +215,11 @@ int iecm_probe(struct pci_dev *pdev,
> > >         int err;
> > >
> > >         adapter->pdev = pdev;
> > > +       err = iecm_api_init(adapter);
> > > +       if (err) {
> > > +               dev_err(&pdev->dev, "Device API is incorrectly configured\n");
> > > +               return err;
> > > +       }
> > >
> > >         err = pcim_enable_device(pdev);
> > >         if (err)
> > > @@ -147,6 +292,20 @@ int iecm_probe(struct pci_dev *pdev,
> > >                 goto err_netdev_alloc;
> > >         }
> > >
> > > +       err = iecm_vport_params_buf_alloc(adapter);
> > > +       if (err) {
> > > +               dev_err(&pdev->dev, "Failed to alloc vport params buffer:
> > %d\n",
> > > +                       err);
> > > +               goto err_mb_res;
> > > +       }
> > > +
> > > +       err = iecm_cfg_hw(adapter);
> > > +       if (err) {
> > > +               dev_err(&pdev->dev, "Failed to configure HW structure for
> > adapter: %d\n",
> > > +                       err);
> > > +               goto err_cfg_hw;
> > > +       }
> > > +
> > >         mutex_init(&adapter->sw_mutex);
> > >         mutex_init(&adapter->reset_lock);
> > >         init_waitqueue_head(&adapter->vchnl_wq);
> > > @@ -166,11 +325,16 @@ int iecm_probe(struct pci_dev *pdev,
> > >         INIT_DELAYED_WORK(&adapter->init_task, iecm_init_task);
> > >         INIT_DELAYED_WORK(&adapter->vc_event_task,
> > iecm_vc_event_task);
> > >
> > > +       adapter->dev_ops.reg_ops.reset_reg_init(&adapter->reset_reg);
> > >         set_bit(__IECM_HR_DRV_LOAD, adapter->flags);
> > >         queue_delayed_work(adapter->vc_event_wq, &adapter-
> > >vc_event_task,
> > >                            msecs_to_jiffies(10 * (pdev->devfn & 0x07)));
> > >
> > >         return 0;
> > > +err_cfg_hw:
> > > +       iecm_vport_params_buf_rel(adapter);
> > > +err_mb_res:
> > > +       kfree(adapter->netdevs);
> > >  err_netdev_alloc:
> > >         kfree(adapter->vports);
> > >  err_vport_alloc:
> > > @@ -214,6 +378,7 @@ void iecm_remove(struct pci_dev *pdev)
> > >         cancel_delayed_work_sync(&adapter->vc_event_task);
> > >         iecm_deinit_task(adapter);
> > >         iecm_del_user_cfg_data(adapter);
> > > +       iecm_deinit_dflt_mbx(adapter);
> > >         msleep(20);
> > >         destroy_workqueue(adapter->serv_wq);
> > >         destroy_workqueue(adapter->vc_event_wq);
> > > @@ -222,6 +387,7 @@ void iecm_remove(struct pci_dev *pdev)
> > >         kfree(adapter->vports);
> > >         kfree(adapter->netdevs);
> > >         kfree(adapter->vlan_caps);
> > > +       iecm_vport_params_buf_rel(adapter);
> > >         mutex_destroy(&adapter->sw_mutex);
> > >         mutex_destroy(&adapter->reset_lock);
> > >         pci_disable_pcie_error_reporting(pdev);
> > > @@ -229,3 +395,26 @@ void iecm_remove(struct pci_dev *pdev)
> > >         pci_disable_device(pdev);
> > >  }
> > >  EXPORT_SYMBOL(iecm_remove);
> > > +
> > > +void *iecm_alloc_dma_mem(struct iecm_hw *hw, struct
> > iecm_dma_mem *mem, u64 size)
> > > +{
> > > +       struct iecm_adapter *adapter = (struct iecm_adapter *)hw->back;
> > > +       size_t sz = ALIGN(size, 4096);
> > > +
> > > +       mem->va = dma_alloc_coherent(&adapter->pdev->dev, sz,
> > > +                                    &mem->pa, GFP_KERNEL | __GFP_ZERO);
> > > +       mem->size = size;
> > > +
> > > +       return mem->va;
> > > +}
> > > +
> > > +void iecm_free_dma_mem(struct iecm_hw *hw, struct iecm_dma_mem
> > *mem)
> > > +{
> > > +       struct iecm_adapter *adapter = (struct iecm_adapter *)hw->back;
> > > +
> > > +       dma_free_coherent(&adapter->pdev->dev, mem->size,
> > > +                         mem->va, mem->pa);
> > > +       mem->size = 0;
> > > +       mem->va = NULL;
> > > +       mem->pa = 0;
> > > +}
> > > diff --git a/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> > b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> > > new file mode 100644
> > > index 000000000000..b8f54b8c700a
> > > --- /dev/null
> > > +++ b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> > > @@ -0,0 +1,172 @@
> > > +// SPDX-License-Identifier: GPL-2.0-only
> > > +/* Copyright (C) 2019 Intel Corporation */
> > > +
> > > +#include "iecm.h"
> > > +
> > > +/**
> > > + * iecm_mb_clean - Reclaim the send mailbox queue entries
> > > + * @adapter: Driver specific private structure
> > > + *
> > > + * Reclaim the send mailbox queue entries to be used to send further
> > messages
> > > + *
> > > + * Returns 0 on success, negative on failure
> > > + */
> > > +static int iecm_mb_clean(struct iecm_adapter *adapter)
> > > +{
> > > +       u16 i, num_q_msg = IECM_DFLT_MBX_Q_LEN;
> > > +       struct iecm_ctlq_msg **q_msg;
> > > +       struct iecm_dma_mem *dma_mem;
> > > +       int err = 0;
> > > +
> > > +       q_msg = kcalloc(num_q_msg, sizeof(struct iecm_ctlq_msg *),
> > GFP_KERNEL);
> > > +       if (!q_msg)
> > > +               return -ENOMEM;
> > > +
> > > +       err = iecm_ctlq_clean_sq(adapter->hw.asq, &num_q_msg, q_msg);
> > > +       if (err)
> > > +               goto error;
> > > +
> > > +       for (i = 0; i < num_q_msg; i++) {
> > > +               dma_mem = q_msg[i]->ctx.indirect.payload;
> > > +               if (dma_mem)
> > > +                       dmam_free_coherent(&adapter->pdev->dev, dma_mem-
> > >size,
> > > +                                          dma_mem->va, dma_mem->pa);
> > > +               kfree(q_msg[i]);
> > > +               kfree(dma_mem);
> > > +       }
> > > +error:
> > > +       kfree(q_msg);
> > > +       return err;
> > > +}
> > > +
> > > +/**
> > > + * iecm_find_ctlq - Given a type and id, find ctlq info
> > > + * @hw: hardware struct
> > > + * @type: type of ctrlq to find
> > > + * @id: ctlq id to find
> > > + *
> > > + * Returns pointer to found ctlq info struct, NULL otherwise.
> > > + */
> > > +static struct iecm_ctlq_info *iecm_find_ctlq(struct iecm_hw *hw,
> > > +                                            enum iecm_ctlq_type type, int id)
> > > +{
> > > +       struct iecm_ctlq_info *cq, *tmp;
> > > +
> > > +       list_for_each_entry_safe(cq, tmp, &hw->cq_list_head, cq_list) {
> > > +               if (cq->q_id == id && cq->cq_type == type)
> > > +                       return cq;
> > > +       }
> > > +
> > > +       return NULL;
> > > +}
> > > +
> > > +/**
> > > + * iecm_init_dflt_mbx - Setup default mailbox parameters and make
> > request
> > > + * @adapter: adapter info struct
> > > + *
> > > + * Returns 0 on success, negative otherwise
> > > + */
> > > +int iecm_init_dflt_mbx(struct iecm_adapter *adapter)
> > > +{
> > > +       struct iecm_ctlq_create_info ctlq_info[] = {
> > > +               {
> > > +                       .type = IECM_CTLQ_TYPE_MAILBOX_TX,
> > > +                       .id = IECM_DFLT_MBX_ID,
> > > +                       .len = IECM_DFLT_MBX_Q_LEN,
> > > +                       .buf_size = IECM_DFLT_MBX_BUF_SIZE
> > > +               },
> > > +               {
> > > +                       .type = IECM_CTLQ_TYPE_MAILBOX_RX,
> > > +                       .id = IECM_DFLT_MBX_ID,
> > > +                       .len = IECM_DFLT_MBX_Q_LEN,
> > > +                       .buf_size = IECM_DFLT_MBX_BUF_SIZE
> > > +               }
> > > +       };
> > > +       struct iecm_hw *hw = &adapter->hw;
> > > +       int err;
> > > +
> > > +       adapter->dev_ops.reg_ops.ctlq_reg_init(ctlq_info);
> > > +
> > > +#define NUM_Q 2
> > > +       err = iecm_ctlq_init(hw, NUM_Q, ctlq_info);
> > > +       if (err)
> > > +               return err;
> > > +
> > > +       hw->asq = iecm_find_ctlq(hw, IECM_CTLQ_TYPE_MAILBOX_TX,
> > > +                                IECM_DFLT_MBX_ID);
> > > +       hw->arq = iecm_find_ctlq(hw, IECM_CTLQ_TYPE_MAILBOX_RX,
> > > +                                IECM_DFLT_MBX_ID);
> > > +
> > > +       if (!hw->asq || !hw->arq) {
> > > +               iecm_ctlq_deinit(hw);
> > > +               return -ENOENT;
> > > +       }
> > > +       adapter->state = __IECM_STARTUP;
> > > +       /* Skew the delay for init tasks for each function based on fn
> > number
> > > +        * to prevent every function from making the same call
> > simulatenously.
> > > +        */
> > > +       queue_delayed_work(adapter->init_wq, &adapter->init_task,
> > > +                          msecs_to_jiffies(5 * (adapter->pdev->devfn & 0x07)));
> > > +       return 0;
> > > +}
> > > +
> > > +/**
> > > + * iecm_deinit_dflt_mbx - Free up ctlqs setup
> > > + * @adapter: Driver specific private data structure
> > > + */
> > > +void iecm_deinit_dflt_mbx(struct iecm_adapter *adapter)
> > > +{
> > > +       if (adapter->hw.arq && adapter->hw.asq) {
> > > +               iecm_mb_clean(adapter);
> > > +               iecm_ctlq_deinit(&adapter->hw);
> > > +       }
> > > +       adapter->hw.arq = NULL;
> > > +       adapter->hw.asq = NULL;
> > > +}
> > > +
> > > +/**
> > > + * iecm_vport_params_buf_alloc - Allocate memory for MailBox
> > resources
> > > + * @adapter: Driver specific private data structure
> > > + *
> > > + * Will alloc memory to hold the vport parameters received on MailBox
> > > + */
> > > +int iecm_vport_params_buf_alloc(struct iecm_adapter *adapter)
> > > +{
> > > +       adapter->vport_params_reqd = kcalloc(IECM_MAX_NUM_VPORTS,
> > > +                                            sizeof(*adapter->vport_params_reqd),
> > > +                                            GFP_KERNEL);
> > > +       if (!adapter->vport_params_reqd)
> > > +               return -ENOMEM;
> > > +
> > > +       adapter->vport_params_recvd = kcalloc(IECM_MAX_NUM_VPORTS,
> > > +                                             sizeof(*adapter->vport_params_recvd),
> > > +                                             GFP_KERNEL);
> > > +       if (!adapter->vport_params_recvd) {
> > > +               kfree(adapter->vport_params_reqd);
> > > +               return -ENOMEM;
> > > +       }
> > > +
> > > +       return 0;
> > > +}
> > > +
> > > +/**
> > > + * iecm_vport_params_buf_rel - Release memory for MailBox resources
> > > + * @adapter: Driver specific private data structure
> > > + *
> > > + * Will release memory to hold the vport parameters received on
> > MailBox
> > > + */
> > > +void iecm_vport_params_buf_rel(struct iecm_adapter *adapter)
> > > +{
> > > +       int i = 0;
> > > +
> > > +       for (i = 0; i < IECM_MAX_NUM_VPORTS; i++) {
> > > +               kfree(adapter->vport_params_recvd[i]);
> > > +               kfree(adapter->vport_params_reqd[i]);
> > > +       }
> > > +
> > > +       kfree(adapter->vport_params_recvd);
> > > +       kfree(adapter->vport_params_reqd);
> > > +
> > > +       kfree(adapter->caps);
> > > +       kfree(adapter->config_data.req_qs_chunks);
> > > +}
> > > diff --git a/drivers/net/ethernet/intel/include/iecm.h
> > b/drivers/net/ethernet/intel/include/iecm.h
> > > index e19e014e9817..ca9029224e06 100644
> > > --- a/drivers/net/ethernet/intel/include/iecm.h
> > > +++ b/drivers/net/ethernet/intel/include/iecm.h
> > > @@ -12,15 +12,33 @@
> > >  #include <linux/dim.h>
> > >
> > >  #include "iecm_txrx.h"
> > > +#include "iecm_controlq.h"
> > >
> > >  #define IECM_BAR0                      0
> > >  #define IECM_NO_FREE_SLOT              0xffff
> > >
> > > +/* Default Mailbox settings */
> > > +#define IECM_DFLT_MBX_BUF_SIZE         (4 * 1024)
> > > +#define IECM_NUM_QCTX_PER_MSG          3
> > > +#define IECM_NUM_FILTERS_PER_MSG       20
> > > +#define IECM_VLANS_PER_MSG \
> > > +       ((IECM_DFLT_MBX_BUF_SIZE - sizeof(struct
> > virtchnl_vlan_filter_list)) \
> > > +        / sizeof(u16))
> > > +#define IECM_DFLT_MBX_Q_LEN            64
> > > +#define IECM_DFLT_MBX_ID               -1
> > > +/* maximum number of times to try before resetting mailbox */
> > > +#define IECM_MB_MAX_ERR                        20
> > > +#define IECM_NUM_CHUNKS_PER_MSG(a, b)
> > ((IECM_DFLT_MBX_BUF_SIZE - (a)) / (b))
> > > +
> > >  #define IECM_MAX_NUM_VPORTS            1
> > >
> > >  /* available message levels */
> > >  #define IECM_AVAIL_NETIF_M (NETIF_MSG_DRV | NETIF_MSG_PROBE |
> > NETIF_MSG_LINK)
> > >
> > > +/* Forward declaration */
> > > +struct iecm_adapter;
> > > +struct iecm_vport;
> > > +
> > >  enum iecm_state {
> > >         __IECM_STARTUP,
> > >         __IECM_VER_CHECK,
> > > @@ -77,6 +95,22 @@ struct iecm_reset_reg {
> > >         u32 rstat_m;
> > >  };
> > >
> > > +/* product specific register API */
> > > +struct iecm_reg_ops {
> > > +       void (*ctlq_reg_init)(struct iecm_ctlq_create_info *cq);
> > > +       int (*intr_reg_init)(struct iecm_vport *vport);
> > > +       void (*mb_intr_reg_init)(struct iecm_adapter *adapter);
> > > +       void (*reset_reg_init)(struct iecm_reset_reg *reset_reg);
> > > +       void (*trigger_reset)(struct iecm_adapter *adapter,
> > > +                             enum iecm_flags trig_cause);
> > > +};
> > > +
> > > +struct iecm_dev_ops {
> > > +       void (*reg_ops_init)(struct iecm_adapter *adapter);
> > > +       void (*crc_enable)(u64 *td_cmd);
> > > +       struct iecm_reg_ops reg_ops;
> > > +};
> > > +
> > >  /* stub */
> > >  struct iecm_vport {
> > >  };
> > > @@ -124,6 +158,7 @@ struct iecm_adapter {
> > >         DECLARE_BITMAP(flags, __IECM_FLAGS_NBITS);
> > >         struct mutex reset_lock; /* lock to protect reset flows */
> > >         struct iecm_reset_reg reset_reg;
> > > +       struct iecm_hw hw;
> > >
> > >         u16 num_req_msix;
> > >         u16 num_msix_entries;
> > > @@ -156,6 +191,7 @@ struct iecm_adapter {
> > >         wait_queue_head_t vchnl_wq;
> > >         wait_queue_head_t sw_marker_wq;
> > >         struct iecm_rss_data rss_data;
> > > +       struct iecm_dev_ops dev_ops;
> > >         s32 link_speed;
> > >         /* This is only populated if the VIRTCHNL_VF_CAP_ADV_LINK_SPEED
> > is set
> > >          * in vf_res->vf_cap_flags. This field should be used going forward
> > and
> > > @@ -179,8 +215,24 @@ struct iecm_adapter {
> > >         spinlock_t fdir_fltr_list_lock;
> > >  };
> > >
> > > +/**
> > > + * iecm_is_reset_detected - check if we were reset at some point
> > > + * @adapter: driver specific private structure
> > > + *
> > > + * Returns true if we are either in reset currently or were previously
> > reset.
> > > + */
> > > +static inline bool iecm_is_reset_detected(struct iecm_adapter *adapter)
> > > +{
> > > +       return !(rd32(&adapter->hw, adapter->hw.arq->reg.len) &
> > > +                adapter->hw.arq->reg.len_ena_mask);
> > > +}
> > > +
> > >  int iecm_probe(struct pci_dev *pdev,
> > >                const struct pci_device_id __always_unused *ent,
> > >                struct iecm_adapter *adapter);
> > >  void iecm_remove(struct pci_dev *pdev);
> > > +int iecm_init_dflt_mbx(struct iecm_adapter *adapter);
> > > +void iecm_deinit_dflt_mbx(struct iecm_adapter *adapter);
> > > +int iecm_vport_params_buf_alloc(struct iecm_adapter *adapter);
> > > +void iecm_vport_params_buf_rel(struct iecm_adapter *adapter);
> > >  #endif /* !_IECM_H_ */
> > > diff --git a/drivers/net/ethernet/intel/include/iecm_controlq.h
> > b/drivers/net/ethernet/intel/include/iecm_controlq.h
> > > new file mode 100644
> > > index 000000000000..f2539baa2ce1
> > > --- /dev/null
> > > +++ b/drivers/net/ethernet/intel/include/iecm_controlq.h
> > > @@ -0,0 +1,117 @@
> > > +/* SPDX-License-Identifier: GPL-2.0-only */
> > > +/* Copyright (c) 2020, Intel Corporation. */
> > > +
> > > +#ifndef _IECM_CONTROLQ_H_
> > > +#define _IECM_CONTROLQ_H_
> > > +
> > > +#include <linux/slab.h>
> > > +
> > > +#include "iecm_controlq_api.h"
> > > +
> > > +/* Maximum buffer lengths for all control queue types */
> > > +#define IECM_CTLQ_MAX_RING_SIZE 1024
> > > +#define IECM_CTLQ_MAX_BUF_LEN  4096
> > > +
> > > +#define IECM_CTLQ_DESC(R, i) \
> > > +       (&(((struct iecm_ctlq_desc *)((R)->desc_ring.va))[i]))
> > > +
> > > +#define IECM_CTLQ_DESC_UNUSED(R) \
> > > +       ((u16)((((R)->next_to_clean > (R)->next_to_use) ? 0 : (R)->ring_size)
> > + \
> > > +             (R)->next_to_clean - (R)->next_to_use - 1))
> > > +
> > > +/* Control Queue default settings */
> > > +#define IECM_CTRL_SQ_CMD_TIMEOUT       250  /* msecs */
> > > +
> > > +struct iecm_ctlq_desc {
> > > +       __le16  flags;
> > > +       __le16  opcode;
> > > +       __le16  datalen;        /* 0 for direct commands */
> > > +       union {
> > > +               __le16 ret_val;
> > > +               __le16 pfid_vfid;
> > > +#define IECM_CTLQ_DESC_VF_ID_S 0
> > > +#define IECM_CTLQ_DESC_VF_ID_M (0x7FF <<
> > IECM_CTLQ_DESC_VF_ID_S)
> > > +#define IECM_CTLQ_DESC_PF_ID_S 11
> > > +#define IECM_CTLQ_DESC_PF_ID_M (0x1F <<
> > IECM_CTLQ_DESC_PF_ID_S)
> > > +       };
> > > +       __le32 cookie_high;
> > > +       __le32 cookie_low;
> > > +       union {
> > > +               struct {
> > > +                       __le32 param0;
> > > +                       __le32 param1;
> > > +                       __le32 param2;
> > > +                       __le32 param3;
> > > +               } direct;
> > > +               struct {
> > > +                       __le32 param0;
> > > +                       __le32 param1;
> > > +                       __le32 addr_high;
> > > +                       __le32 addr_low;
> > > +               } indirect;
> > > +               u8 raw[16];
> > > +       } params;
> > > +};
> > > +
> > > +/* Flags sub-structure
> > > + * |0  |1  |2  |3  |4  |5  |6  |7  |8  |9  |10 |11 |12 |13 |14 |15 |
> > > + * |DD |CMP|ERR|  * RSV *  |FTYPE  | *RSV* |RD |VFC|BUF|  * RSV *  |
> > > + */
> > > +/* command flags and offsets */
> > > +#define IECM_CTLQ_FLAG_DD_S    0
> > > +#define IECM_CTLQ_FLAG_CMP_S   1
> > > +#define IECM_CTLQ_FLAG_ERR_S   2
> > > +#define IECM_CTLQ_FLAG_FTYPE_S 6
> > > +#define IECM_CTLQ_FLAG_RD_S    10
> > > +#define IECM_CTLQ_FLAG_VFC_S   11
> > > +#define IECM_CTLQ_FLAG_BUF_S   12
> > > +
> > > +#define IECM_CTLQ_FLAG_DD      BIT(IECM_CTLQ_FLAG_DD_S)        /*
> > 0x1    */
> > > +#define IECM_CTLQ_FLAG_CMP     BIT(IECM_CTLQ_FLAG_CMP_S)       /*
> > 0x2    */
> > > +#define IECM_CTLQ_FLAG_ERR     BIT(IECM_CTLQ_FLAG_ERR_S)       /*
> > 0x4    */
> > > +#define IECM_CTLQ_FLAG_FTYPE_VM
> > BIT(IECM_CTLQ_FLAG_FTYPE_S)     /* 0x40   */
> > > +#define IECM_CTLQ_FLAG_FTYPE_PF        BIT(IECM_CTLQ_FLAG_FTYPE_S
> > + 1) /* 0x80   */
> > > +#define IECM_CTLQ_FLAG_RD      BIT(IECM_CTLQ_FLAG_RD_S)        /*
> > 0x400  */
> > > +#define IECM_CTLQ_FLAG_VFC     BIT(IECM_CTLQ_FLAG_VFC_S)       /*
> > 0x800  */
> > > +#define IECM_CTLQ_FLAG_BUF     BIT(IECM_CTLQ_FLAG_BUF_S)       /*
> > 0x1000 */
> > > +
> > > +struct iecm_mbxq_desc {
> > > +       u8 pad[8];              /* CTLQ flags/opcode/len/retval fields */
> > > +       u32 chnl_opcode;        /* avoid confusion with desc->opcode */
> > > +       u32 chnl_retval;        /* ditto for desc->retval */
> > > +       u32 pf_vf_id;           /* used by CP when sending to PF */
> > > +};
> > > +
> > > +/* Define the APF hardware struct to replace other control structs as
> > needed
> > > + * Align to ctlq_hw_info
> > > + */
> > > +struct iecm_hw {
> > > +       u8 __iomem *hw_addr;
> > > +       u64 hw_addr_len;
> > > +       void *back;
> > > +
> > > +       /* control queue - send and receive */
> > > +       struct iecm_ctlq_info *asq;
> > > +       struct iecm_ctlq_info *arq;
> > > +
> > > +       /* pci info */
> > > +       u16 device_id;
> > > +       u16 vendor_id;
> > > +       u16 subsystem_device_id;
> > > +       u16 subsystem_vendor_id;
> > > +       u8 revision_id;
> > > +       bool adapter_stopped;
> > > +
> > > +       struct list_head cq_list_head;
> > > +};
> > > +
> > > +int iecm_ctlq_alloc_ring_res(struct iecm_hw *hw,
> > > +                            struct iecm_ctlq_info *cq);
> > > +
> > > +void iecm_ctlq_dealloc_ring_res(struct iecm_hw *hw, struct
> > iecm_ctlq_info *cq);
> > > +
> > > +/* prototype for functions used for dynamic memory allocation */
> > > +void *iecm_alloc_dma_mem(struct iecm_hw *hw, struct
> > iecm_dma_mem *mem,
> > > +                        u64 size);
> > > +void iecm_free_dma_mem(struct iecm_hw *hw, struct iecm_dma_mem
> > *mem);
> > > +#endif /* _IECM_CONTROLQ_H_ */
> > > diff --git a/drivers/net/ethernet/intel/include/iecm_controlq_api.h
> > b/drivers/net/ethernet/intel/include/iecm_controlq_api.h
> > > new file mode 100644
> > > index 000000000000..5f624f005d33
> > > --- /dev/null
> > > +++ b/drivers/net/ethernet/intel/include/iecm_controlq_api.h
> > > @@ -0,0 +1,185 @@
> > > +/* SPDX-License-Identifier: GPL-2.0-only */
> > > +/* Copyright (c) 2020, Intel Corporation. */
> > > +
> > > +#ifndef _IECM_CONTROLQ_API_H_
> > > +#define _IECM_CONTROLQ_API_H_
> > > +
> > > +#include "iecm_mem.h"
> > > +
> > > +struct iecm_hw;
> > > +
> > > +/* Used for queue init, response and events */
> > > +enum iecm_ctlq_type {
> > > +       IECM_CTLQ_TYPE_MAILBOX_TX       = 0,
> > > +       IECM_CTLQ_TYPE_MAILBOX_RX       = 1,
> > > +       IECM_CTLQ_TYPE_CONFIG_TX        = 2,
> > > +       IECM_CTLQ_TYPE_CONFIG_RX        = 3,
> > > +       IECM_CTLQ_TYPE_EVENT_RX         = 4,
> > > +       IECM_CTLQ_TYPE_RDMA_TX          = 5,
> > > +       IECM_CTLQ_TYPE_RDMA_RX          = 6,
> > > +       IECM_CTLQ_TYPE_RDMA_COMPL       = 7
> > > +};
> > > +
> > > +/* Generic Control Queue Structures */
> > > +struct iecm_ctlq_reg {
> > > +       /* used for queue tracking */
> > > +       u32 head;
> > > +       u32 tail;
> > > +       /* Below applies only to default mb (if present) */
> > > +       u32 len;
> > > +       u32 bah;
> > > +       u32 bal;
> > > +       u32 len_mask;
> > > +       u32 len_ena_mask;
> > > +       u32 head_mask;
> > > +};
> > > +
> > > +/* Generic queue msg structure */
> > > +struct iecm_ctlq_msg {
> > > +       u16 vmvf_type; /* represents the source of the message on recv */
> > > +#define IECM_VMVF_TYPE_VF 0
> > > +#define IECM_VMVF_TYPE_VM 1
> > > +#define IECM_VMVF_TYPE_PF 2
> > > +       u16 opcode;
> > > +       u16 data_len;   /* data_len = 0 when no payload is attached */
> > > +       union {
> > > +               u16 func_id;    /* when sending a message */
> > > +               u16 status;     /* when receiving a message */
> > > +       };
> > > +       union {
> > > +               struct {
> > > +                       u32 chnl_retval;
> > > +                       u32 chnl_opcode;
> > > +               } mbx;
> > > +       } cookie;
> > > +       union {
> > > +#define IECM_DIRECT_CTX_SIZE   16
> > > +#define IECM_INDIRECT_CTX_SIZE 8
> > > +               /* 16 bytes of context can be provided or 8 bytes of context
> > > +                * plus the address of a DMA buffer
> > > +                */
> > > +               u8 direct[IECM_DIRECT_CTX_SIZE];
> > > +               struct {
> > > +                       u8 context[IECM_INDIRECT_CTX_SIZE];
> > > +                       struct iecm_dma_mem *payload;
> > > +               } indirect;
> > > +       } ctx;
> > > +};
> > > +
> > > +/* Generic queue info structures */
> > > +/* MB, CONFIG and EVENT q do not have extended info */
> > > +struct iecm_ctlq_create_info {
> > > +       enum iecm_ctlq_type type;
> > > +       int id; /* absolute queue offset passed as input
> > > +                * -1 for default mailbox if present
> > > +                */
> > > +       u16 len; /* Queue length passed as input */
> > > +       u16 buf_size; /* buffer size passed as input */
> > > +       u64 base_address; /* output, HPA of the Queue start  */
> > > +       struct iecm_ctlq_reg reg; /* registers accessed by ctlqs */
> > > +
> > > +       int ext_info_size;
> > > +       void *ext_info; /* Specific to q type */
> > > +};
> > > +
> > > +/* Control Queue information */
> > > +struct iecm_ctlq_info {
> > > +       struct list_head cq_list;
> > > +
> > > +       enum iecm_ctlq_type cq_type;
> > > +       int q_id;
> > > +       /* control queue lock */
> > > +       struct mutex cq_lock;
> > > +
> > > +       /* used for interrupt processing */
> > > +       u16 next_to_use;
> > > +       u16 next_to_clean;
> > > +       u16 next_to_post;               /* starting descriptor to post buffers
> > > +                                        * to after recev
> > > +                                        */
> > > +
> > > +       struct iecm_dma_mem desc_ring;  /* descriptor ring memory
> > > +                                        * iecm_dma_mem is defined in OSdep.h
> > > +                                        */
> > > +       union {
> > > +               struct iecm_dma_mem **rx_buff;
> > > +               struct iecm_ctlq_msg **tx_msg;
> > > +       } bi;
> > > +
> > > +       u16 buf_size;                   /* queue buffer size */
> > > +       u16 ring_size;                  /* Number of descriptors */
> > > +       struct iecm_ctlq_reg reg;       /* registers accessed by ctlqs */
> > > +};
> > > +
> > > +/* PF/VF mailbox commands */
> > > +enum iecm_mbx_opc {
> > > +       /* iecm_mbq_opc_send_msg_to_pf:
> > > +        *      usage: used by PF or VF to send a message to its CPF
> > > +        *      target: RX queue and function ID of parent PF taken from HW
> > > +        */
> > > +       iecm_mbq_opc_send_msg_to_pf             = 0x0801,
> > > +
> > > +       /* iecm_mbq_opc_send_msg_to_vf:
> > > +        *      usage: used by PF to send message to a VF
> > > +        *      target: VF control queue ID must be specified in descriptor
> > > +        */
> > > +       iecm_mbq_opc_send_msg_to_vf             = 0x0802,
> > > +
> > > +       /* iecm_mbq_opc_send_msg_to_peer_pf:
> > > +        *      usage: used by any function to send message to any peer PF
> > > +        *      target: RX queue and host of parent PF taken from HW
> > > +        */
> > > +       iecm_mbq_opc_send_msg_to_peer_pf        = 0x0803,
> > > +
> > > +       /* iecm_mbq_opc_send_msg_to_peer_drv:
> > > +        *      usage: used by any function to send message to any peer driver
> > > +        *      target: RX queue and target host must be specific in descriptor
> > > +        */
> > > +       iecm_mbq_opc_send_msg_to_peer_drv       = 0x0804,
> > > +};
> > > +
> > > +/* API support for control queue management */
> > > +
> > > +/* Will init all required q including default mb.  "q_info" is an array of
> > > + * create_info structs equal to the number of control queues to be
> > created.
> > > + */
> > > +int iecm_ctlq_init(struct iecm_hw *hw, u8 num_q,
> > > +                  struct iecm_ctlq_create_info *q_info);
> > > +
> > > +/* Allocate and initialize a single control queue, which will be added to
> > the
> > > + * control queue list; returns a handle to the created control queue
> > > + */
> > > +int iecm_ctlq_add(struct iecm_hw *hw,
> > > +                 struct iecm_ctlq_create_info *qinfo,
> > > +                 struct iecm_ctlq_info **cq);
> > > +
> > > +/* Deinitialize and deallocate a single control queue */
> > > +void iecm_ctlq_remove(struct iecm_hw *hw,
> > > +                     struct iecm_ctlq_info *cq);
> > > +
> > > +/* Sends messages to HW and will also free the buffer*/
> > > +int iecm_ctlq_send(struct iecm_hw *hw,
> > > +                  struct iecm_ctlq_info *cq,
> > > +                  u16 num_q_msg,
> > > +                  struct iecm_ctlq_msg q_msg[]);
> > > +
> > > +/* Receives messages and called by interrupt handler/polling
> > > + * initiated by app/process. Also caller is supposed to free the buffers
> > > + */
> > > +int iecm_ctlq_recv(struct iecm_ctlq_info *cq, u16 *num_q_msg,
> > > +                  struct iecm_ctlq_msg *q_msg);
> > > +
> > > +/* Reclaims send descriptors on HW write back */
> > > +int iecm_ctlq_clean_sq(struct iecm_ctlq_info *cq, u16 *clean_count,
> > > +                      struct iecm_ctlq_msg *msg_status[]);
> > > +
> > > +/* Indicate RX buffers are done being processed */
> > > +int iecm_ctlq_post_rx_buffs(struct iecm_hw *hw,
> > > +                           struct iecm_ctlq_info *cq,
> > > +                           u16 *buff_count,
> > > +                           struct iecm_dma_mem **buffs);
> > > +
> > > +/* Will destroy all q including the default mb */
> > > +int iecm_ctlq_deinit(struct iecm_hw *hw);
> > > +
> > > +#endif /* _IECM_CONTROLQ_API_H_ */
> > > diff --git a/drivers/net/ethernet/intel/include/iecm_mem.h
> > b/drivers/net/ethernet/intel/include/iecm_mem.h
> > > new file mode 100644
> > > index 000000000000..064dd6e10c24
> > > --- /dev/null
> > > +++ b/drivers/net/ethernet/intel/include/iecm_mem.h
> > > @@ -0,0 +1,20 @@
> > > +/* SPDX-License-Identifier: GPL-2.0-only */
> > > +/* Copyright (C) 2019 Intel Corporation */
> > > +
> > > +#ifndef _IECM_MEM_H_
> > > +#define _IECM_MEM_H_
> > > +
> > > +#include <linux/io.h>
> > > +
> > > +struct iecm_dma_mem {
> > > +       void *va;
> > > +       dma_addr_t pa;
> > > +       size_t size;
> > > +};
> > > +
> > > +#define wr32(a, reg, value)    writel((value), ((a)->hw_addr + (reg)))
> > > +#define rd32(a, reg)           readl((a)->hw_addr + (reg))
> > > +#define wr64(a, reg, value)    writeq((value), ((a)->hw_addr + (reg)))
> > > +#define rd64(a, reg)           readq((a)->hw_addr + (reg))
> > > +
> > > +#endif /* _IECM_MEM_H_ */
> > > --
> > > 2.33.0
> > >
> > > _______________________________________________
> > > Intel-wired-lan mailing list
> > > Intel-wired-lan at osuosl.org
> > > https://lists.osuosl.org/mailman/listinfo/intel-wired-lan

Thanks,
Al

^ permalink raw reply	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 05/19] iecm: add vport alloc and virtchnl messages
  2022-02-02 22:21     ` Brady, Alan
@ 2022-02-03 13:23       ` Alexander Lobakin
  0 siblings, 0 replies; 89+ messages in thread
From: Alexander Lobakin @ 2022-02-03 13:23 UTC (permalink / raw)
  To: intel-wired-lan

From: Alan Brady <alan.brady@intel.com>
Date: Wed, 2 Feb 2022 23:21:54 +0100

> > -----Original Message-----
> > From: Lobakin, Alexandr <alexandr.lobakin@intel.com>
> > Sent: Friday, January 28, 2022 4:33 AM
> > To: Brady, Alan <alan.brady@intel.com>
> > Cc: Lobakin, Alexandr <alexandr.lobakin@intel.com>; intel-wired-
> > lan at lists.osuosl.org; Burra, Phani R <phani.r.burra@intel.com>; Chittim, Madhu
> > <madhu.chittim@intel.com>; Linga, Pavan Kumar
> > <pavan.kumar.linga@intel.com>
> > Subject: Re: [Intel-wired-lan] [PATCH net-next 05/19] iecm: add vport alloc and
> > virtchnl messages
> > 
> > From: Alan Brady <alan.brady@intel.com>
> > Date: Thu, 27 Jan 2022 16:09:55 -0800
> > 
> > > After handling hard reset, we end up in init task. This starts by
> > > allocating and setting up a vport. To do that we need implement virtchnl
> > > messages.
> > >
> > > The virtchnl messages are also offloaded into function pointers so that a
> > > device driver may override them. Here a default implementation is provided
> > > for devices using virtchnl 2.0 but there exists the flexibility add
> > > virtchnl 1.1 support through function pointers.
> > >
> > > Signed-off-by: Phani Burra <phani.r.burra@intel.com>
> > > Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
> > > Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
> > > Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
> > > Signed-off-by: Alan Brady <alan.brady@intel.com>
> > > ---
> > >  drivers/net/ethernet/intel/iecm/Makefile      |    4 +-
> > >  drivers/net/ethernet/intel/iecm/iecm_lib.c    |  167 ++-
> > >  drivers/net/ethernet/intel/iecm/iecm_txrx.c   |   22 +
> > >  .../net/ethernet/intel/iecm/iecm_virtchnl.c   | 1299 +++++++++++++++++
> > >  drivers/net/ethernet/intel/include/iecm.h     |  316 +++-
> > >  .../net/ethernet/intel/include/iecm_txrx.h    |   94 ++
> > >  6 files changed, 1898 insertions(+), 4 deletions(-)
> > >  create mode 100644 drivers/net/ethernet/intel/iecm/iecm_txrx.c
> > >
> > > diff --git a/drivers/net/ethernet/intel/iecm/Makefile
> > b/drivers/net/ethernet/intel/iecm/Makefile
> > > index db8fecb075a6..fcb49402334f 100644
> > > --- a/drivers/net/ethernet/intel/iecm/Makefile
> > > +++ b/drivers/net/ethernet/intel/iecm/Makefile
> > > @@ -7,11 +7,13 @@
> > >
> > >  obj-$(CONFIG_IECM) += iecm.o
> > >
> > > -ccflags-y += -I$(srctree)/drivers/net/ethernet/intel/include
> > > +ccflags-y += -I$(srctree)/drivers/net/ethernet/intel/include \
> > > +	     -I$(srctree)/include/linux/avf
> > >
> > >  iecm-y := \
> > >  	iecm_lib.o \
> > >  	iecm_virtchnl.o \
> > > +	iecm_txrx.o \
> > >  	iecm_controlq.o \
> > >  	iecm_controlq_setup.o \
> > >  	iecm_main.o
> > > diff --git a/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > > index 64cdbce2c842..e2e523f0700e 100644
> > > --- a/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > > +++ b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > > @@ -5,6 +5,11 @@
> > >
> > >  #include "iecm.h"
> > >
> > > +const char * const iecm_vport_vc_state_str[] = {
> > > +	IECM_FOREACH_VPORT_VC_STATE(IECM_GEN_STRING)
> > > +};
> > > +EXPORT_SYMBOL(iecm_vport_vc_state_str);
> > > +
> > >  /**
> > >   * iecm_cfg_hw - Initialize HW struct
> > >   * @adapter: adapter to setup hw struct for
> > > @@ -24,6 +29,113 @@ static int iecm_cfg_hw(struct iecm_adapter *adapter)
> > >  	return 0;
> > >  }
> > >
> > > +/**
> > > + * iecm_get_free_slot - get the next non-NULL location index in array
> > > + * @array: array to search
> > > + * @size: size of the array
> > > + * @curr: last known occupied index to be used as a search hint
> > > + *
> > > + * void * is being used to keep the functionality generic. This lets us use this
> > > + * function on any array of pointers.
> > > + */
> > > +static int iecm_get_free_slot(void *array, int size, int curr)
> > > +{
> > > +	int **tmp_array = (int **)array;
> > > +	int next;
> > > +
> > > +	if (curr < (size - 1) && !tmp_array[curr + 1]) {
> > 
> > Redundant braces around `size - 1`.
> 
> Will fix
> 
> > 
> > > +		next = curr + 1;
> > > +	} else {
> > > +		int i = 0;
> > > +
> > > +		while ((i < size) && (tmp_array[i]))
> > > +			i++;
> > > +		if (i == size)
> > > +			next = IECM_NO_FREE_SLOT;
> > > +		else
> > > +			next = i;
> > > +	}
> > 
> > One indent level is redundant here. First condition is an oneliner:
> > 
> > 	if (curr < (size - 1) && !tmp_array[curr + 1]) {
> > 		return curr + 1;
> > 
> > 	while ((i < size) && (tmp_array[i])) {
> > 		...
> > 
> > > +	return next;
> > > +}
> > > +
> > > +/**
> > > + * iecm_vport_rel - Delete a vport and free its resources
> > > + * @vport: the vport being removed
> > > + */
> > > +static void iecm_vport_rel(struct iecm_vport *vport)
> > > +{
> > > +	mutex_destroy(&vport->stop_mutex);
> > > +	kfree(vport);
> > > +}
> > > +
> > > +/**
> > > + * iecm_vport_rel_all - Delete all vports
> > > + * @adapter: adapter from which all vports are being removed
> > > + */
> > > +static void iecm_vport_rel_all(struct iecm_adapter *adapter)
> > > +{
> > > +	int i;
> > > +
> > > +	if (!adapter->vports)
> > > +		return;
> > > +
> > > +	for (i = 0; i < adapter->num_alloc_vport; i++) {
> > > +		if (!adapter->vports[i])
> > > +			continue;
> > > +
> > > +		iecm_vport_rel(adapter->vports[i]);
> > > +		adapter->vports[i] = NULL;
> > > +		adapter->next_vport = 0;
> > > +	}
> > > +	adapter->num_alloc_vport = 0;
> > > +}
> > > +
> > > +/**
> > > + * iecm_vport_alloc - Allocates the next available struct vport in the adapter
> > > + * @adapter: board private structure
> > > + * @vport_id: vport identifier
> > > + *
> > > + * returns a pointer to a vport on success, NULL on failure.
> > > + */
> > > +static struct iecm_vport *
> > > +iecm_vport_alloc(struct iecm_adapter *adapter, int vport_id)
> > > +{
> > > +	struct iecm_vport *vport = NULL;
> > > +
> > > +	if (adapter->next_vport == IECM_NO_FREE_SLOT)
> > > +		return vport;
> > > +
> > > +	/* Need to protect the allocation of the vports at the adapter level */
> > > +	mutex_lock(&adapter->sw_mutex);
> > > +
> > > +	vport = kzalloc(sizeof(*vport), GFP_KERNEL);
> > > +	if (!vport)
> > > +		goto unlock_adapter;
> > > +
> > > +	vport->adapter = adapter;
> > > +	vport->idx = adapter->next_vport;
> > > +	vport->compln_clean_budget = IECM_TX_COMPLQ_CLEAN_BUDGET;
> > > +	adapter->num_alloc_vport++;
> > > +
> > > +	/* Setup default MSIX irq handler for the vport */
> > > +	vport->irq_q_handler = iecm_vport_intr_clean_queues;
> > > +	vport->q_vector_base = IECM_NONQ_VEC;
> > > +
> > > +	mutex_init(&vport->stop_mutex);
> > > +
> > > +	/* fill vport slot in the adapter struct */
> > > +	adapter->vports[adapter->next_vport] = vport;
> > > +
> > > +	/* prepare adapter->next_vport for next use */
> > > +	adapter->next_vport = iecm_get_free_slot(adapter->vports,
> > > +						 adapter->num_alloc_vport,
> > > +						 adapter->next_vport);
> > > +
> > > +unlock_adapter:
> > > +	mutex_unlock(&adapter->sw_mutex);
> > > +	return vport;
> > > +}
> > > +
> > >  /**
> > >   * iecm_statistics_task - Delayed task to get statistics over mailbox
> > >   * @work: work_struct handle to our data
> > > @@ -55,7 +167,25 @@ static void iecm_service_task(struct work_struct
> > *work)
> > >   */
> > >  static void iecm_init_task(struct work_struct *work)
> > >  {
> > > -	/* stub */
> > > +	struct iecm_adapter *adapter = container_of(work,
> > > +						    struct iecm_adapter,
> > > +						    init_task.work);
> > > +	struct iecm_vport *vport;
> > > +	struct pci_dev *pdev;
> > > +	int vport_id, err;
> > > +
> > > +	err = adapter->dev_ops.vc_ops.core_init(adapter, &vport_id);
> > > +	if (err)
> > > +		return;
> > > +
> > > +	pdev = adapter->pdev;
> > > +	vport = iecm_vport_alloc(adapter, vport_id);
> > > +	if (!vport) {
> > > +		err = -EFAULT;
> > > +		dev_err(&pdev->dev, "failed to allocate vport: %d\n",
> > > +			err);
> > > +		return;
> > > +	}
> > >  }
> > >
> > >  /**
> > > @@ -81,6 +211,31 @@ static int iecm_api_init(struct iecm_adapter *adapter)
> > >  		return -EINVAL;
> > >  	}
> > >
> > > +	if (adapter->dev_ops.vc_ops_init) {
> > > +		struct iecm_virtchnl_ops *vc_ops;
> > > +
> > > +		adapter->dev_ops.vc_ops_init(adapter);
> > > +		vc_ops = &adapter->dev_ops.vc_ops;
> > > +		if (!(vc_ops->core_init &&
> > > +		      vc_ops->vport_init &&
> > > +		      vc_ops->vport_queue_ids_init &&
> > > +		      vc_ops->get_caps &&
> > > +		      vc_ops->config_queues &&
> > > +		      vc_ops->enable_queues &&
> > > +		      vc_ops->disable_queues &&
> > > +		      vc_ops->irq_map_unmap &&
> > > +		      vc_ops->get_set_rss_lut &&
> > > +		      vc_ops->get_set_rss_hash &&
> > > +		      vc_ops->adjust_qs &&
> > > +		      vc_ops->get_ptype &&
> > > +		      vc_ops->init_max_queues)) {
> > 
> > if (!op1 ||
> >     !op2 ||
> >     !opn) would look more natural and more readable here.
> > 
> 
> I'm not sure I understand this comment.  Adding an extra symbol for '!' seems worse.

But now you have two extra spaces, one for leading '!' and another
one for leading '('.
I just wrote how it's usually being done in the kernel code,
e.g. [0].

> 
> > > +			dev_err(&pdev->dev, "Invalid device, missing one or
> > more virtchnl functions\n");
> > > +			return -EINVAL;
> > > +		}
> > > +	} else {
> > > +		iecm_vc_ops_init(adapter);
> > > +	}

--- 8< ---

> > > --
> > > 2.33.0
> > 
> > Thanks,
> > Al

[0] https://elixir.bootlin.com/linux/v5.17-rc2/source/net/mac80211/main.c#L552

Thanks,
Al

^ permalink raw reply	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 06/19] iecm: add virtchnl messages for queues
  2022-02-02 22:48     ` Brady, Alan
  2022-02-03 10:08       ` Maciej Fijalkowski
@ 2022-02-03 14:09       ` Alexander Lobakin
  1 sibling, 0 replies; 89+ messages in thread
From: Alexander Lobakin @ 2022-02-03 14:09 UTC (permalink / raw)
  To: intel-wired-lan

From: Alan Brady <alan.brady@intel.com>
Date: Wed, 2 Feb 2022 23:48:48 +0100

> > -----Original Message-----
> > From: Lobakin, Alexandr <alexandr.lobakin@intel.com>
> > Sent: Friday, January 28, 2022 5:03 AM
> > To: Brady, Alan <alan.brady@intel.com>
> > Cc: Lobakin, Alexandr <alexandr.lobakin@intel.com>; intel-wired-
> > lan at lists.osuosl.org; Linga, Pavan Kumar <pavan.kumar.linga@intel.com>;
> > Chittim, Madhu <madhu.chittim@intel.com>; Burra, Phani R
> > <phani.r.burra@intel.com>
> > Subject: Re: [Intel-wired-lan] [PATCH net-next 06/19] iecm: add virtchnl
> > messages for queues
> > 
> > From: Alan Brady <alan.brady@intel.com>
> > Date: Thu, 27 Jan 2022 16:09:56 -0800
> > 
> > > This continues adding virtchnl messages. This largely relates to adding
> > > messages needed to negotiate and setup traffic queues.
> > >
> > > Signed-off-by: Phani Burra <phani.r.burra@intel.com>
> > > Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
> > > Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
> > > Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
> > > Signed-off-by: Alice Michael <alice.michael@intel.com>
> > > Signed-off-by: Alan Brady <alan.brady@intel.com>
> > > ---
> > >  drivers/net/ethernet/intel/iecm/iecm_lib.c    |   14 +
> > >  drivers/net/ethernet/intel/iecm/iecm_txrx.c   |  161 +++
> > >  .../net/ethernet/intel/iecm/iecm_virtchnl.c   | 1127 ++++++++++++++++-
> > >  drivers/net/ethernet/intel/include/iecm.h     |   22 +
> > >  .../net/ethernet/intel/include/iecm_txrx.h    |  196 +++
> > >  5 files changed, 1505 insertions(+), 15 deletions(-)
> > >
> > > diff --git a/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > > index e2e523f0700e..4e9cc7f2d138 100644
> > > --- a/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > > +++ b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > 
> > --- 8< ---
> > 
> > > +void iecm_vport_calc_num_q_desc(struct iecm_vport *vport)
> > > +{
> > > +	int num_req_txq_desc = vport->adapter-
> > >config_data.num_req_txq_desc;
> > > +	int num_req_rxq_desc = vport->adapter-
> > >config_data.num_req_rxq_desc;
> > > +	int num_bufqs = vport->num_bufqs_per_qgrp;
> > > +	int i = 0;
> > > +
> > > +	vport->complq_desc_count = 0;
> > > +	if (num_req_txq_desc) {
> > > +		vport->txq_desc_count = num_req_txq_desc;
> > > +		if (iecm_is_queue_model_split(vport->txq_model)) {
> > > +			vport->complq_desc_count = num_req_txq_desc;
> > > +			if (vport->complq_desc_count <
> > IECM_MIN_TXQ_COMPLQ_DESC)
> > > +				vport->complq_desc_count =
> > > +					IECM_MIN_TXQ_COMPLQ_DESC;
> > > +		}
> > > +	} else {
> > > +		vport->txq_desc_count =
> > > +			IECM_DFLT_TX_Q_DESC_COUNT;
> > > +		if (iecm_is_queue_model_split(vport->txq_model)) {
> > > +			vport->complq_desc_count =
> > > +				IECM_DFLT_TX_COMPLQ_DESC_COUNT;
> > > +		}
> > 
> > Braces are redundant here since the path is a one-liner.
> > 
> 
> Correct me if I'm wrong but believe the guidance here is if it goes beyond one line with line wrapping, it is optional whether or not to use braces, even if the statement is 'one line'. We have generally preferred to keep braces in multiline statements. However you do have a point that it is not consistent in this function. Will fix.

It's not really optional whether to use or not braces. They should
be used in a minimum possible amount, i.e.:

* when we need to perform several statements separated by ';' under
  the same condition;

	if (c1) {
		f2(v1);
		f3(v2);
	}

* when we do only one statement with one ';' at the end, but we have
  another branch that must be braced due to bullet #1.

	if (c1) {
		f2(v1);
	} else {
		f3(v2);
		f4(v5);
	}

Line breaks are not included. Even comments are not included, thus

	if (c1)
		/* Do f2, because r3 */
		f2(v1);
	else
		extremely_long_v2 =
			f3(v9);

is fully correct and should be preferred.

> 
> > > +	}
> > > +
> > > +	if (num_req_rxq_desc)
> > > +		vport->rxq_desc_count = num_req_rxq_desc;
> > > +	else
> > > +		vport->rxq_desc_count = IECM_DFLT_RX_Q_DESC_COUNT;
> > > +
> > > +	for (i = 0; i < num_bufqs; i++) {
> > > +		if (!vport->bufq_desc_count[i])
> > > +			vport->bufq_desc_count[i] =
> > > +				IECM_RX_BUFQ_DESC_COUNT(vport-
> > >rxq_desc_count,
> > > +							num_bufqs);
> > 
> > 		if (vport->bufq_desc_count[i])
> > 			continue;
> > 
> > 		vport-> ...
> > 
> > -1 indent level with that.
> > 
> > > +	}
> > > +}
> > > +EXPORT_SYMBOL(iecm_vport_calc_num_q_desc);
> > > +
> > > +/**
> > > + * iecm_vport_calc_total_qs - Calculate total number of queues
> > > + * @adapter: private data struct
> > > + * @vport_msg: message to fill with data
> > > + */
> > > +void iecm_vport_calc_total_qs(struct iecm_adapter *adapter,
> > > +			      struct virtchnl2_create_vport *vport_msg)
> > > +{
> > > +	unsigned int num_req_tx_qs = adapter->config_data.num_req_tx_qs;
> > > +	unsigned int num_req_rx_qs = adapter->config_data.num_req_rx_qs;
> > > +	int dflt_splitq_txq_grps, dflt_singleq_txqs;
> > > +	int dflt_splitq_rxq_grps, dflt_singleq_rxqs;
> > > +	int num_txq_grps, num_rxq_grps;
> > > +	int num_cpus;
> > > +	u16 max_q;
> > > +
> > > +	/* Restrict num of queues to cpus online as a default configuration to
> > > +	 * give best performance. User can always override to a max number
> > > +	 * of queues via ethtool.
> > > +	 */
> > > +	num_cpus = num_online_cpus();
> > > +	max_q = adapter->max_queue_limit;
> > > +
> > > +	dflt_splitq_txq_grps = min_t(int, max_q, num_cpus);
> > > +	dflt_singleq_txqs = min_t(int, max_q, num_cpus);
> > > +	dflt_splitq_rxq_grps = min_t(int, max_q, num_cpus);
> > > +	dflt_singleq_rxqs = min_t(int, max_q, num_cpus);
> > > +
> > > +	if (iecm_is_queue_model_split(le16_to_cpu(vport_msg->txq_model))) {
> > > +		num_txq_grps = num_req_tx_qs ? num_req_tx_qs :
> > dflt_splitq_txq_grps;
> > > +		vport_msg->num_tx_complq = cpu_to_le16(num_txq_grps *
> > > +
> > IECM_COMPLQ_PER_GROUP);
> > > +		vport_msg->num_tx_q = cpu_to_le16(num_txq_grps *
> > > +
> > IECM_DFLT_SPLITQ_TXQ_PER_GROUP);
> > > +	} else {
> > > +		num_txq_grps = IECM_DFLT_SINGLEQ_TX_Q_GROUPS;
> > > +		vport_msg->num_tx_q =
> > > +				cpu_to_le16(num_txq_grps *
> > > +					    (num_req_tx_qs ? num_req_tx_qs :
> > > +					    dflt_singleq_txqs));
> > > +		vport_msg->num_tx_complq = 0;
> > > +	}
> > > +	if (iecm_is_queue_model_split(le16_to_cpu(vport_msg->rxq_model))) {
> > > +		num_rxq_grps = num_req_rx_qs ? num_req_rx_qs :
> > dflt_splitq_rxq_grps;
> > > +		vport_msg->num_rx_bufq =
> > > +					cpu_to_le16(num_rxq_grps *
> > > +
> > IECM_MAX_BUFQS_PER_RXQ_GRP);
> > > +
> > > +		vport_msg->num_rx_q = cpu_to_le16(num_rxq_grps *
> > > +
> > IECM_DFLT_SPLITQ_RXQ_PER_GROUP);
> > > +	} else {
> > > +		num_rxq_grps = IECM_DFLT_SINGLEQ_RX_Q_GROUPS;
> > > +		vport_msg->num_rx_bufq = 0;
> > > +		vport_msg->num_rx_q =
> > > +				cpu_to_le16(num_rxq_grps *
> > > +					    (num_req_rx_qs ? num_req_rx_qs :
> > > +					    dflt_singleq_rxqs));
> > > +	}
> > > +}
> > > +
> > > +/**
> > > + * iecm_vport_calc_num_q_groups - Calculate number of queue groups
> > > + * @vport: vport to calculate q groups for
> > > + */
> > > +void iecm_vport_calc_num_q_groups(struct iecm_vport *vport)
> > > +{
> > > +	if (iecm_is_queue_model_split(vport->txq_model))
> > > +		vport->num_txq_grp = vport->num_txq;
> > > +	else
> > > +		vport->num_txq_grp = IECM_DFLT_SINGLEQ_TX_Q_GROUPS;
> > > +
> > > +	if (iecm_is_queue_model_split(vport->rxq_model))
> > > +		vport->num_rxq_grp = vport->num_rxq;
> > > +	else
> > > +		vport->num_rxq_grp = IECM_DFLT_SINGLEQ_RX_Q_GROUPS;
> > > +}
> > > +EXPORT_SYMBOL(iecm_vport_calc_num_q_groups);
> > > +
> > > +/**
> > > + * iecm_vport_calc_num_q_vec - Calculate total number of vectors required
> > for
> > > + * this vport
> > > + * @vport: virtual port
> > > + *
> > > + */
> > > +void iecm_vport_calc_num_q_vec(struct iecm_vport *vport)
> > > +{
> > > +	if (iecm_is_queue_model_split(vport->txq_model))
> > > +		vport->num_q_vectors = vport->num_txq_grp;
> > > +	else
> > > +		vport->num_q_vectors = vport->num_txq;
> > > +}
> > > +EXPORT_SYMBOL(iecm_vport_calc_num_q_vec);
> > > diff --git a/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> > b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> > > index aae06064d706..d8152e657e24 100644
> > > --- a/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> > > +++ b/drivers/net/ethernet/intel/iecm/iecm_virtchnl.c
> > > @@ -859,6 +859,48 @@ static int iecm_recv_get_caps_msg(struct
> > iecm_adapter *adapter)
> > >  				sizeof(struct virtchnl2_get_capabilities));
> > >  }
> > >
> > > +/**
> > > + * iecm_get_reg_intr_vecs - Get vector queue register offset
> > > + * @vport: virtual port structure
> > > + * @reg_vals: Register offsets to store in
> > > + * @num_vecs: Number of vector registers
> > > + *
> > > + * Returns number of regsiters that got populated
> > > + */
> > > +int iecm_get_reg_intr_vecs(struct iecm_vport *vport,
> > > +			   struct iecm_vec_regs *reg_vals, int num_vecs)
> > > +{
> > > +	struct virtchnl2_vector_chunks *chunks;
> > > +	struct iecm_vec_regs reg_val;
> > > +	u16 num_vchunks, num_vec;
> > > +	int num_regs = 0, i, j;
> > > +
> > > +	chunks = &vport->adapter->req_vec_chunks->vchunks;
> > > +	num_vchunks = le16_to_cpu(chunks->num_vchunks);
> > > +
> > > +	for (j = 0; j < num_vchunks; j++) {
> > > +		struct virtchnl2_vector_chunk *chunk = &chunks->vchunks[j];
> > > +
> > > +		num_vec = le16_to_cpu(chunk->num_vectors);
> > > +		reg_val.dyn_ctl_reg = le32_to_cpu(chunk->dynctl_reg_start);
> > > +		reg_val.itrn_reg = le32_to_cpu(chunk->itrn_reg_start);
> > > +		for (i = 0; i < num_vec; i++) {
> > > +			if (num_regs == num_vecs)
> > > +				break;
> > > +			reg_vals[i].dyn_ctl_reg = reg_val.dyn_ctl_reg;
> > > +			reg_vals[i].itrn_reg = reg_val.itrn_reg;
> > > +			reg_val.dyn_ctl_reg +=
> > > +				le32_to_cpu(chunk->dynctl_reg_spacing);
> > > +			reg_val.itrn_reg +=
> > > +				le32_to_cpu(chunk->itrn_reg_spacing);
> > > +			num_regs++;
> > > +		}
> > > +	}
> > > +
> > > +	return num_regs;
> > > +}
> > > +EXPORT_SYMBOL(iecm_get_reg_intr_vecs);
> > > +
> > >  /**
> > >   * iecm_send_create_vport_msg - Send virtchnl create vport message
> > >   * @adapter: Driver specific private structure
> > > @@ -869,8 +911,36 @@ static int iecm_recv_get_caps_msg(struct
> > iecm_adapter *adapter)
> > >   */
> > >  static int iecm_send_create_vport_msg(struct iecm_adapter *adapter)
> > >  {
> > > -	/* stub */
> > > -	return 0;
> > > +	struct virtchnl2_create_vport *vport_msg;
> > > +	int buf_size;
> > > +
> > > +	buf_size = sizeof(struct virtchnl2_create_vport);
> > > +	if (!adapter->vport_params_reqd[0]) {
> > > +		adapter->vport_params_reqd[0] = kzalloc(buf_size,
> > GFP_KERNEL);
> > > +		if (!adapter->vport_params_reqd[0])
> > > +			return -ENOMEM;
> > > +	}
> > > +
> > > +	vport_msg = (struct virtchnl2_create_vport *)
> > > +			adapter->vport_params_reqd[0];
> > > +	vport_msg->vport_type =
> > cpu_to_le16(VIRTCHNL2_VPORT_TYPE_DEFAULT);
> > > +
> > > +	if (test_bit(__IECM_REQ_TX_SPLITQ, adapter->flags))
> > > +		vport_msg->txq_model =
> > cpu_to_le16(VIRTCHNL2_QUEUE_MODEL_SPLIT);
> > > +	else
> > > +		vport_msg->txq_model =
> > cpu_to_le16(VIRTCHNL2_QUEUE_MODEL_SINGLE);
> > > +
> > > +	if (test_bit(__IECM_REQ_RX_SPLITQ, adapter->flags))
> > > +		vport_msg->rxq_model =
> > cpu_to_le16(VIRTCHNL2_QUEUE_MODEL_SPLIT);
> > > +	else
> > > +		vport_msg->rxq_model =
> > cpu_to_le16(VIRTCHNL2_QUEUE_MODEL_SINGLE);
> > > +
> > > +	adapter->dev_ops.vc_ops.init_max_queues(adapter);
> > > +
> > > +	iecm_vport_calc_total_qs(adapter, vport_msg);
> > > +
> > > +	return iecm_send_mb_msg(adapter, VIRTCHNL2_OP_CREATE_VPORT,
> > buf_size,
> > > +				(u8 *)vport_msg);
> > >  }
> > >
> > >  /**
> > > @@ -884,7 +954,25 @@ static int iecm_send_create_vport_msg(struct
> > iecm_adapter *adapter)
> > >  static int iecm_recv_create_vport_msg(struct iecm_adapter *adapter,
> > >  				      int *vport_id)
> > >  {
> > > -	/* stub */
> > > +	struct virtchnl2_create_vport *vport_msg;
> > > +	int err;
> > > +
> > > +	if (!adapter->vport_params_recvd[0]) {
> > > +		adapter->vport_params_recvd[0] =
> > kzalloc(IECM_DFLT_MBX_BUF_SIZE,
> > > +							 GFP_KERNEL);
> > > +		if (!adapter->vport_params_recvd[0])
> > > +			return -ENOMEM;
> > > +	}
> > > +
> > > +	vport_msg = (struct virtchnl2_create_vport *)
> > > +			adapter->vport_params_recvd[0];
> > > +
> > > +	err = iecm_recv_mb_msg(adapter, VIRTCHNL2_OP_CREATE_VPORT,
> > vport_msg,
> > > +			       IECM_DFLT_MBX_BUF_SIZE);
> > > +	if (err)
> > > +		return err;
> > > +
> > > +	*vport_id = le32_to_cpu(vport_msg->vport_id);
> > >  	return 0;
> > >  }
> > >
> > > @@ -966,6 +1054,920 @@ int iecm_wait_for_event(struct iecm_adapter
> > *adapter,
> > >  }
> > >  EXPORT_SYMBOL(iecm_wait_for_event);
> > >
> > > +/**
> > > + * iecm_wait_for_marker_event - wait for software marker response
> > > + * @vport: virtual port data structure
> > > + *
> > > + * Returns 0 success, negative on failure.
> > > + **/
> > > +static int iecm_wait_for_marker_event(struct iecm_vport *vport)
> > > +{
> > > +	int event = 0;
> > > +	int i;
> > > +
> > > +	for (i = 0; i < vport->num_txq; i++)
> > > +		set_bit(__IECM_Q_SW_MARKER, vport->txqs[i]->flags);
> > > +
> > > +	event = wait_event_timeout(vport->adapter->sw_marker_wq,
> > > +				   test_and_clear_bit(__IECM_SW_MARKER,
> > > +						      vport->adapter->flags),
> > > +				   msecs_to_jiffies(500));
> > > +	if (event)
> > > +		return 0;
> > > +	return -ETIMEDOUT;
> > > +}
> > > +
> > > +/**
> > > + * iecm_send_destroy_vport_msg - Send virtchnl destroy vport message
> > > + * @vport: virtual port data structure
> > > + *
> > > + * Send virtchnl destroy vport message.  Returns 0 on success, negative on
> > > + * failure.
> > > + */
> > > +int iecm_send_destroy_vport_msg(struct iecm_vport *vport)
> > > +{
> > > +	struct iecm_adapter *adapter = vport->adapter;
> > > +	struct virtchnl2_vport v_id;
> > > +	int err;
> > > +
> > > +	v_id.vport_id = cpu_to_le32(vport->vport_id);
> > > +
> > > +	err = iecm_send_mb_msg(adapter, VIRTCHNL2_OP_DESTROY_VPORT,
> > > +			       sizeof(v_id), (u8 *)&v_id);
> > > +	if (err)
> > > +		return err;
> > > +
> > > +	return iecm_min_wait_for_event(adapter, IECM_VC_DESTROY_VPORT,
> > > +				       IECM_VC_DESTROY_VPORT_ERR);
> > > +}
> > > +
> > > +/**
> > > + * iecm_send_enable_vport_msg - Send virtchnl enable vport message
> > > + * @vport: virtual port data structure
> > > + *
> > > + * Send enable vport virtchnl message.  Returns 0 on success, negative on
> > > + * failure.
> > > + */
> > > +int iecm_send_enable_vport_msg(struct iecm_vport *vport)
> > > +{
> > > +	struct iecm_adapter *adapter = vport->adapter;
> > > +	struct virtchnl2_vport v_id;
> > > +	int err;
> > > +
> > > +	v_id.vport_id = cpu_to_le32(vport->vport_id);
> > > +
> > > +	err = iecm_send_mb_msg(adapter, VIRTCHNL2_OP_ENABLE_VPORT,
> > > +			       sizeof(v_id), (u8 *)&v_id);
> > > +	if (err)
> > > +		return err;
> > > +
> > > +	return iecm_wait_for_event(adapter, IECM_VC_ENA_VPORT,
> > > +				   IECM_VC_ENA_VPORT_ERR);
> > > +}
> > > +
> > > +/**
> > > + * iecm_send_disable_vport_msg - Send virtchnl disable vport message
> > > + * @vport: virtual port data structure
> > > + *
> > > + * Send disable vport virtchnl message.  Returns 0 on success, negative on
> > > + * failure.
> > > + */
> > > +int iecm_send_disable_vport_msg(struct iecm_vport *vport)
> > > +{
> > > +	struct iecm_adapter *adapter = vport->adapter;
> > > +	struct virtchnl2_vport v_id;
> > > +	int err;
> > > +
> > > +	v_id.vport_id = cpu_to_le32(vport->vport_id);
> > > +
> > > +	err = iecm_send_mb_msg(adapter, VIRTCHNL2_OP_DISABLE_VPORT,
> > > +			       sizeof(v_id), (u8 *)&v_id);
> > > +	if (err)
> > > +		return err;
> > > +
> > > +	return iecm_min_wait_for_event(adapter, IECM_VC_DIS_VPORT,
> > > +				       IECM_VC_DIS_VPORT_ERR);
> > > +}
> > > +
> > > +/**
> > > + * iecm_send_config_tx_queues_msg - Send virtchnl config tx queues
> > message
> > > + * @vport: virtual port data structure
> > > + *
> > > + * Send config tx queues virtchnl message. Returns 0 on success, negative on
> > > + * failure.
> > > + */
> > > +int iecm_send_config_tx_queues_msg(struct iecm_vport *vport)
> > > +{
> > > +	struct virtchnl2_config_tx_queues *ctq = NULL;
> > > +	int config_data_size, chunk_size, buf_size = 0;
> > > +	int totqs, num_msgs, num_chunks;
> > > +	struct virtchnl2_txq_info *qi;
> > > +	int err = 0, i, k = 0;
> > > +	bool alloc = false;
> > > +
> > > +	totqs = vport->num_txq + vport->num_complq;
> > > +	qi = kcalloc(totqs, sizeof(struct virtchnl2_txq_info), GFP_KERNEL);
> > > +	if (!qi)
> > > +		return -ENOMEM;
> > > +
> > > +	/* Populate the queue info buffer with all queue context info */
> > > +	for (i = 0; i < vport->num_txq_grp; i++) {
> > > +		struct iecm_txq_group *tx_qgrp = &vport->txq_grps[i];
> > > +		int j;
> > > +
> > > +		for (j = 0; j < tx_qgrp->num_txq; j++, k++) {
> > > +			qi[k].queue_id =
> > > +				cpu_to_le32(tx_qgrp->txqs[j]->q_id);
> > > +			qi[k].model =
> > > +				cpu_to_le16(vport->txq_model);
> > > +			qi[k].type =
> > > +				cpu_to_le32(tx_qgrp->txqs[j]->q_type);
> > > +			qi[k].ring_len =
> > > +				cpu_to_le16(tx_qgrp->txqs[j]->desc_count);
> > > +			qi[k].dma_ring_addr =
> > > +				cpu_to_le64(tx_qgrp->txqs[j]->dma);
> > > +			if (iecm_is_queue_model_split(vport->txq_model)) {
> > > +				struct iecm_queue *q = tx_qgrp->txqs[j];
> > > +
> > > +				qi[k].tx_compl_queue_id =
> > > +					cpu_to_le16(tx_qgrp->complq->q_id);
> > > +
> > > +				if (test_bit(__IECM_Q_FLOW_SCH_EN, q-
> > >flags))
> > > +					qi[k].sched_mode =
> > > +
> > 	cpu_to_le16(VIRTCHNL2_TXQ_SCHED_MODE_FLOW);
> > > +				else
> > > +					qi[k].sched_mode =
> > > +
> > 	cpu_to_le16(VIRTCHNL2_TXQ_SCHED_MODE_QUEUE);
> > > +			} else {
> > > +				qi[k].sched_mode =
> > > +
> > 	cpu_to_le16(VIRTCHNL2_TXQ_SCHED_MODE_QUEUE);
> > > +			}
> > > +		}
> > > +
> > > +		if (iecm_is_queue_model_split(vport->txq_model)) {
> > > +			qi[k].queue_id =
> > > +				cpu_to_le32(tx_qgrp->complq->q_id);
> > > +			qi[k].model =
> > > +				cpu_to_le16(vport->txq_model);
> > > +			qi[k].type =
> > > +				cpu_to_le32(tx_qgrp->complq->q_type);
> > > +			qi[k].ring_len =
> > > +				cpu_to_le16(tx_qgrp->complq->desc_count);
> > > +			qi[k].dma_ring_addr =
> > > +				cpu_to_le64(tx_qgrp->complq->dma);
> > > +			k++;
> > > +		}
> > > +	}
> > > +
> > > +	/* Make sure accounting agrees */
> > > +	if (k != totqs) {
> > > +		err = -EINVAL;
> > > +		goto error;
> > > +	}
> > > +
> > > +	/* Chunk up the queue contexts into multiple messages to avoid
> > > +	 * sending a control queue message buffer that is too large
> > > +	 */
> > > +	config_data_size = sizeof(struct virtchnl2_config_tx_queues);
> > > +	chunk_size = sizeof(struct virtchnl2_txq_info);
> > > +
> > > +	num_chunks = IECM_NUM_CHUNKS_PER_MSG(config_data_size,
> > chunk_size) + 1;
> > > +	if (totqs < num_chunks)
> > > +		num_chunks = totqs;
> > > +
> > > +	num_msgs = totqs / num_chunks;
> > > +	if (totqs % num_chunks)
> > > +		num_msgs++;
> > > +
> > > +	for (i = 0, k = 0; i < num_msgs; i++) {
> > > +		if (!ctq || alloc) {
> > > +			buf_size = (chunk_size * (num_chunks - 1)) +
> > > +					config_data_size;
> > > +			kfree(ctq);
> > > +			ctq = kzalloc(buf_size, GFP_KERNEL);
> > > +			if (!ctq) {
> > > +				err = -ENOMEM;
> > > +				goto error;
> > > +			}
> > > +		} else {
> > > +			memset(ctq, 0, buf_size);
> > > +		}
> > > +
> > > +		ctq->vport_id = cpu_to_le32(vport->vport_id);
> > > +		ctq->num_qinfo = cpu_to_le16(num_chunks);
> > > +		memcpy(ctq->qinfo, &qi[k], chunk_size * num_chunks);
> > > +
> > > +		err = iecm_send_mb_msg(vport->adapter,
> > > +				       VIRTCHNL2_OP_CONFIG_TX_QUEUES,
> > > +				       buf_size, (u8 *)ctq);
> > > +		if (err)
> > > +			goto mbx_error;
> > > +
> > > +		err = iecm_wait_for_event(vport->adapter,
> > IECM_VC_CONFIG_TXQ,
> > > +					  IECM_VC_CONFIG_TXQ_ERR);
> > > +		if (err)
> > > +			goto mbx_error;
> > > +
> > > +		k += num_chunks;
> > > +		totqs -= num_chunks;
> > > +		if (totqs < num_chunks) {
> > > +			num_chunks = totqs;
> > > +			alloc = true;
> > > +		}
> > > +	}
> > > +
> > > +mbx_error:
> > > +	kfree(ctq);
> > > +error:
> > > +	kfree(qi);
> > > +	return err;
> > > +}
> > > +
> > > +/**
> > > + * iecm_send_config_rx_queues_msg - Send virtchnl config rx queues
> > message
> > > + * @vport: virtual port data structure
> > > + *
> > > + * Send config rx queues virtchnl message.  Returns 0 on success, negative on
> > > + * failure.
> > > + */
> > > +int iecm_send_config_rx_queues_msg(struct iecm_vport *vport)
> > > +{
> > > +	struct virtchnl2_config_rx_queues *crq = NULL;
> > > +	int config_data_size, chunk_size, buf_size = 0;
> > > +	int totqs, num_msgs, num_chunks;
> > > +	struct virtchnl2_rxq_info *qi;
> > > +	int err = 0, i, k = 0;
> > > +	bool alloc = false;
> > > +
> > > +	totqs = vport->num_rxq + vport->num_bufq;
> > > +	qi = kcalloc(totqs, sizeof(struct virtchnl2_rxq_info), GFP_KERNEL);
> > > +	if (!qi)
> > > +		return -ENOMEM;
> > > +
> > > +	/* Populate the queue info buffer with all queue context info */
> > > +	for (i = 0; i < vport->num_rxq_grp; i++) {
> > > +		struct iecm_rxq_group *rx_qgrp = &vport->rxq_grps[i];
> > > +		int num_rxq;
> > > +		int j;
> > > +
> > > +		if (iecm_is_queue_model_split(vport->rxq_model)) {
> > > +			for (j = 0; j < vport->num_bufqs_per_qgrp; j++, k++) {
> > > +				struct iecm_queue *bufq =
> > > +					&rx_qgrp->splitq.bufq_sets[j].bufq;
> > > +
> > > +				qi[k].queue_id =
> > > +					cpu_to_le32(bufq->q_id);
> > > +				qi[k].model =
> > > +					cpu_to_le16(vport->rxq_model);
> > > +				qi[k].type =
> > > +					cpu_to_le32(bufq->q_type);
> > > +				qi[k].desc_ids =
> > > +
> > 	cpu_to_le64(VIRTCHNL2_RXDID_1_FLEX_SPLITQ_M);
> > > +				qi[k].ring_len =
> > > +					cpu_to_le16(bufq->desc_count);
> > > +				qi[k].dma_ring_addr =
> > > +					cpu_to_le64(bufq->dma);
> > > +				qi[k].data_buffer_size =
> > > +					cpu_to_le32(bufq->rx_buf_size);
> > > +				qi[k].buffer_notif_stride =
> > > +					bufq->rx_buf_stride;
> > > +				qi[k].rx_buffer_low_watermark =
> > > +					cpu_to_le16(bufq-
> > >rx_buffer_low_watermark);
> > > +			}
> > > +		}
> > 
> > 		if (iecm_is_queue_model_split(vport->rxq_model))
> > 			goto here;
> > 
> > -1 indent level for the for-loop.
> 
> I'm afraid I'm not following, please elaborate. Where are we goto'ing? The for loop below needs to be executed for both and if we just tack the above for loop at the bottom of the function and goto in and out of it to save an indent does not sound great and makes the code harder to follow IMO.

Ah sorry, I forgot to invert the condition.
Here's the full loop:

	for (i = 0; i < vport->num_rxq_grp; i++) {
		struct iecm_rxq_group *rx_qgrp = &vport->rxq_grps[i];
		int num_rxq;
		int j;

		if (!iecm_is_queue_model_split(vport->rxq_model))
			goto label_name_1;

		for (j = 0; j < vport->num_bufqs_per_qgrp; j++, k++) {
			struct iecm_queue *bufq =
				&rx_qgrp->splitq.bufq_sets[j].bufq;
			/* bufq ^ should be const here BTW, you only read it */

			qi[k].queue_id =
				cpu_to_le32(bufq->q_id);

			...

			qi[k].rx_buffer_low_watermark =
				cpu_to_le16(bufq->rx_buffer_low_watermark);
		}

label_name_1:
		if (iecm_is_queue_model_split(vport->rxq_model))
			num_rxq = rx_qgrp->splitq.num_rxq_sets;
		else
			num_rxq = rx_qgrp->singleq.num_rxq;

		for (j = 0; j < num_rxq; j++, k++) {
			struct iecm_queue *rxq;
			/* const here ^ as well */

			if (!iecm_is_queue_model_split(vport->rxq_model)) {
				rxq = rx_qgrp->singleq.rxqs[j];
				goto label_name_2;
			}

			rxq = &rx_qgrp->splitq.rxq_sets[j]->rxq;
			qi[k].rx_bufq1_id =

			...

			qi[k].hdr_buffer_size =
				cpu_to_le16(rxq->rx_hbuf_size);

label_name_2:
			qi[k].queue_id =
				cpu_to_le32(rxq->q_id);
			qi[k].model =
				cpu_to_le16(vport->rxq_model);

			...

			qi[k].qflags |=
				cpu_to_le16(VIRTCHNL2_RX_DESC_SIZE_32BYTE);
			qi[k].desc_ids =
				cpu_to_le64(rxq->rxdids);
		}
	}

	/* Make sure accounting agrees */
	if (k != totqs) {
		err = -EINVAL;
		goto error;
	}

	/* Chunk up the queue contexts into multiple messages to avoid
	...

Hope I didn't m{i,e}ss anything this time.

> 
> > Braces for 'if' are not needed since the for-loop has their own.
> > 
> 
> They're not required but we have generally preferred to keep braces on statements extending across more than one line.

See my answer to the prev. patch.

> 
> > > +
> > > +		if (iecm_is_queue_model_split(vport->rxq_model))
> > > +			num_rxq = rx_qgrp->splitq.num_rxq_sets;
> > > +		else
> > > +			num_rxq = rx_qgrp->singleq.num_rxq;
> > > +
> > > +		for (j = 0; j < num_rxq; j++, k++) {
> > > +			struct iecm_queue *rxq;
> > > +
> > > +			if (iecm_is_queue_model_split(vport->rxq_model)) {
> > > +				rxq = &rx_qgrp->splitq.rxq_sets[j]->rxq;
> > > +				qi[k].rx_bufq1_id =
> > > +				  cpu_to_le16(rxq->rxq_grp-
> > >splitq.bufq_sets[0].bufq.q_id);
> > > +				qi[k].rx_bufq2_id =
> > > +				  cpu_to_le16(rxq->rxq_grp-
> > >splitq.bufq_sets[1].bufq.q_id);
> > > +				qi[k].hdr_buffer_size =
> > > +					cpu_to_le16(rxq->rx_hbuf_size);
> > > +				qi[k].rx_buffer_low_watermark =
> > > +					cpu_to_le16(rxq-
> > >rx_buffer_low_watermark);
> > > +
> > > +				if (rxq->rx_hsplit_en) {
> > > +					qi[k].qflags =
> > > +
> > 	cpu_to_le16(VIRTCHNL2_RXQ_HDR_SPLIT);
> > > +					qi[k].hdr_buffer_size =
> > > +						cpu_to_le16(rxq-
> > >rx_hbuf_size);
> > > +				}
> > > +			} else {
> > > +				rxq = rx_qgrp->singleq.rxqs[j];
> > > +			}
> > 
> > Same here, but with rxq = ... + goto.
> > 
> 
> Please elaborate.

2 replies above.

> 
> > > +
> > > +			qi[k].queue_id =
> > > +				cpu_to_le32(rxq->q_id);
> > > +			qi[k].model =
> > > +				cpu_to_le16(vport->rxq_model);
> > > +			qi[k].type =
> > > +				cpu_to_le32(rxq->q_type);
> > > +			qi[k].ring_len =
> > > +				cpu_to_le16(rxq->desc_count);
> > > +			qi[k].dma_ring_addr =
> > > +				cpu_to_le64(rxq->dma);
> > > +			qi[k].max_pkt_size =
> > > +				cpu_to_le32(rxq->rx_max_pkt_size);
> > > +			qi[k].data_buffer_size =
> > > +				cpu_to_le32(rxq->rx_buf_size);
> > > +			qi[k].qflags |=
> > > +
> > 	cpu_to_le16(VIRTCHNL2_RX_DESC_SIZE_32BYTE);
> > > +			qi[k].desc_ids =
> > > +				cpu_to_le64(rxq->rxdids);
> > > +		}
> > > +	}
> > > +
> > > +	/* Make sure accounting agrees */
> > > +	if (k != totqs) {
> > > +		err = -EINVAL;
> > > +		goto error;
> > > +	}
> > > +
> > > +	/* Chunk up the queue contexts into multiple messages to avoid
> > > +	 * sending a control queue message buffer that is too large
> > > +	 */
> > > +	config_data_size = sizeof(struct virtchnl2_config_rx_queues);
> > > +	chunk_size = sizeof(struct virtchnl2_rxq_info);
> > > +
> > > +	num_chunks = IECM_NUM_CHUNKS_PER_MSG(config_data_size,
> > chunk_size) + 1;
> > > +	if (totqs < num_chunks)
> > > +		num_chunks = totqs;
> > > +
> > > +	num_msgs = totqs / num_chunks;
> > > +	if (totqs % num_chunks)
> > > +		num_msgs++;
> > > +
> > > +	for (i = 0, k = 0; i < num_msgs; i++) {
> > > +		if (!crq || alloc) {
> > > +			buf_size = (chunk_size * (num_chunks - 1)) +
> > > +					config_data_size;
> > > +			kfree(crq);
> > > +			crq = kzalloc(buf_size, GFP_KERNEL);
> > > +			if (!crq) {
> > > +				err = -ENOMEM;
> > > +				goto error;
> > > +			}
> > > +		} else {
> > > +			memset(crq, 0, buf_size);
> > > +		}
> > > +
> > > +		crq->vport_id = cpu_to_le32(vport->vport_id);
> > > +		crq->num_qinfo = cpu_to_le16(num_chunks);
> > > +		memcpy(crq->qinfo, &qi[k], chunk_size * num_chunks);
> > > +
> > > +		err = iecm_send_mb_msg(vport->adapter,
> > > +				       VIRTCHNL2_OP_CONFIG_RX_QUEUES,
> > > +				       buf_size, (u8 *)crq);
> > > +		if (err)
> > > +			goto mbx_error;
> > > +
> > > +		err = iecm_wait_for_event(vport->adapter,
> > IECM_VC_CONFIG_RXQ,
> > > +					  IECM_VC_CONFIG_RXQ_ERR);
> > > +		if (err)
> > > +			goto mbx_error;
> > > +
> > > +		k += num_chunks;
> > > +		totqs -= num_chunks;
> > > +		if (totqs < num_chunks) {
> > > +			num_chunks = totqs;
> > > +			alloc = true;
> > > +		}
> > > +	}
> > > +
> > > +mbx_error:
> > > +	kfree(crq);
> > > +error:
> > > +	kfree(qi);
> > > +	return err;
> > > +}
> > > +
> > > +/**
> > > + * iecm_send_ena_dis_queues_msg - Send virtchnl enable or disable
> > > + * queues message
> > > + * @vport: virtual port data structure
> > > + * @vc_op: virtchnl op code to send
> > > + *
> > > + * Send enable or disable queues virtchnl message. Returns 0 on success,
> > > + * negative on failure.
> > > + */
> > > +static int iecm_send_ena_dis_queues_msg(struct iecm_vport *vport,
> > > +					enum virtchnl_ops vc_op)
> > > +{
> > > +	int num_msgs, num_chunks, config_data_size, chunk_size;
> > > +	int num_txq, num_rxq, num_q, buf_size, err = 0;
> > > +	struct virtchnl2_del_ena_dis_queues *eq = NULL;
> > > +	struct virtchnl2_queue_chunk *qc;
> > > +	bool alloc = false;
> > > +	int i, j, k = 0;
> > > +
> > > +	/* validate virtchnl op */
> > > +	switch (vc_op) {
> > > +	case VIRTCHNL2_OP_ENABLE_QUEUES:
> > > +	case VIRTCHNL2_OP_DISABLE_QUEUES:
> > > +		break;
> > > +	default:
> > > +		return -EINVAL;
> > > +	}
> > > +
> > > +	num_txq = vport->num_txq + vport->num_complq;
> > > +	num_rxq = vport->num_rxq + vport->num_bufq;
> > > +	num_q = num_txq + num_rxq;
> > > +	buf_size = sizeof(struct virtchnl2_queue_chunk) * (num_q);
> > > +	qc = kzalloc(buf_size, GFP_KERNEL);
> > > +	if (!qc)
> > > +		return -ENOMEM;
> > > +
> > > +	for (i = 0; i < vport->num_txq_grp; i++) {
> > > +		struct iecm_txq_group *tx_qgrp = &vport->txq_grps[i];
> > > +
> > > +		for (j = 0; j < tx_qgrp->num_txq; j++, k++) {
> > > +			qc[k].type = cpu_to_le32(tx_qgrp->txqs[j]->q_type);
> > > +			qc[k].start_queue_id =
> > > +					cpu_to_le32(tx_qgrp->txqs[j]->q_id);
> > > +			qc[k].num_queues = cpu_to_le32(1);
> > > +		}
> > > +	}
> > > +	if (vport->num_txq != k) {
> > > +		err = -EINVAL;
> > > +		goto error;
> > > +	}
> > > +
> > > +	if (iecm_is_queue_model_split(vport->txq_model)) {
> > > +		for (i = 0; i < vport->num_txq_grp; i++, k++) {
> > > +			struct iecm_txq_group *tx_qgrp = &vport->txq_grps[i];
> > > +
> > > +			qc[k].type = cpu_to_le32(tx_qgrp->complq->q_type);
> > > +			qc[k].start_queue_id =
> > > +					cpu_to_le32(tx_qgrp->complq->q_id);
> > > +			qc[k].num_queues = cpu_to_le32(1);
> > > +		}
> > > +		if (vport->num_complq != (k - vport->num_txq)) {
> > > +			err = -EINVAL;
> > > +			goto error;
> > > +		}
> > > +	}
> > 
> > ...and here.
> > 
> > > +
> > > +	for (i = 0; i < vport->num_rxq_grp; i++) {
> > > +		struct iecm_rxq_group *rx_qgrp = &vport->rxq_grps[i];
> > > +
> > > +		if (iecm_is_queue_model_split(vport->rxq_model))
> > > +			num_rxq = rx_qgrp->splitq.num_rxq_sets;
> > > +		else
> > > +			num_rxq = rx_qgrp->singleq.num_rxq;
> > > +
> > > +		for (j = 0; j < num_rxq; j++, k++) {
> > > +			if (iecm_is_queue_model_split(vport->rxq_model)) {
> > > +				qc[k].start_queue_id =
> > > +				cpu_to_le32(rx_qgrp->splitq.rxq_sets[j]-
> > >rxq.q_id);
> > > +				qc[k].type =
> > > +				cpu_to_le32(rx_qgrp->splitq.rxq_sets[j]-
> > >rxq.q_type);
> > > +			} else {
> > > +				qc[k].start_queue_id =
> > > +				cpu_to_le32(rx_qgrp->singleq.rxqs[j]->q_id);
> > > +				qc[k].type =
> > > +				cpu_to_le32(rx_qgrp->singleq.rxqs[j]->q_type);
> > > +			}
> > > +			qc[k].num_queues = cpu_to_le32(1);
> > > +		}
> > > +	}
> > > +	if (vport->num_rxq != k - (vport->num_txq + vport->num_complq)) {
> > > +		err = -EINVAL;
> > > +		goto error;
> > > +	}
> > > +
> > > +	if (iecm_is_queue_model_split(vport->rxq_model)) {
> > > +		for (i = 0; i < vport->num_rxq_grp; i++) {
> > > +			struct iecm_rxq_group *rx_qgrp = &vport->rxq_grps[i];
> > > +
> > > +			for (j = 0; j < vport->num_bufqs_per_qgrp; j++, k++) {
> > > +				struct iecm_queue *q = &rx_qgrp-
> > >splitq.bufq_sets[j].bufq;
> > > +
> > > +				qc[k].type = cpu_to_le32(q->q_type);
> > > +				qc[k].start_queue_id = cpu_to_le32(q->q_id);
> > > +				qc[k].num_queues = cpu_to_le32(1);
> > > +			}
> > > +		}
> > > +		if (vport->num_bufq != k - (vport->num_txq +
> > > +					       vport->num_complq +
> > > +					       vport->num_rxq)) {
> > > +			err = -EINVAL;
> > > +			goto error;
> > > +		}
> > > +	}
> > 
> > ...and here.
> > 
> > > +
> > > +	/* Chunk up the queue info into multiple messages */
> > > +	config_data_size = sizeof(struct virtchnl2_del_ena_dis_queues);
> > > +	chunk_size = sizeof(struct virtchnl2_queue_chunk);
> > > +
> > > +	num_chunks = IECM_NUM_CHUNKS_PER_MSG(config_data_size,
> > chunk_size) + 1;
> > > +	if (num_q < num_chunks)
> > > +		num_chunks = num_q;
> > > +
> > > +	num_msgs = num_q / num_chunks;
> > > +	if (num_q % num_chunks)
> > > +		num_msgs++;
> > > +
> > > +	for (i = 0, k = 0; i < num_msgs; i++) {
> > > +		if (!eq || alloc) {
> > > +			buf_size = (chunk_size * (num_chunks - 1)) +
> > > +					config_data_size;
> > > +			kfree(eq);
> > > +			eq = kzalloc(buf_size, GFP_KERNEL);
> > > +			if (!eq) {
> > > +				err = -ENOMEM;
> > > +				goto error;
> > > +			}
> > > +		} else {
> > > +			memset(eq, 0, buf_size);
> > > +		}
> > > +		eq->vport_id = cpu_to_le32(vport->vport_id);
> > > +		eq->chunks.num_chunks = cpu_to_le16(num_chunks);
> > > +		memcpy(eq->chunks.chunks, &qc[k], chunk_size *
> > num_chunks);
> > > +
> > > +		err = iecm_send_mb_msg(vport->adapter, vc_op, buf_size,
> > > +				       (u8 *)eq);
> > > +		if (err)
> > > +			goto mbx_error;
> > > +		k += num_chunks;
> > > +		num_q -= num_chunks;
> > > +		if (num_q < num_chunks) {
> > > +			num_chunks = num_q;
> > > +			alloc = true;
> > > +		}
> > > +	}
> > > +mbx_error:
> > > +	kfree(eq);
> > > +error:
> > > +	kfree(qc);
> > > +	return err;
> > > +}
> > > +
> > > +/**
> > > + * iecm_send_map_unmap_queue_vector_msg - Send virtchnl map or unmap
> > queue
> > > + * vector message
> > > + * @vport: virtual port data structure
> > > + * @map: true for map and false for unmap
> > > + *
> > > + * Send map or unmap queue vector virtchnl message.  Returns 0 on success,
> > > + * negative on failure.
> > > + */
> > > +int iecm_send_map_unmap_queue_vector_msg(struct iecm_vport *vport,
> > bool map)
> > > +{
> > > +	int num_msgs, num_chunks, config_data_size, chunk_size;
> > > +	struct virtchnl2_queue_vector_maps *vqvm = NULL;
> > > +	struct iecm_adapter *adapter = vport->adapter;
> > > +	struct virtchnl2_queue_vector *vqv;
> > > +	int buf_size, num_q, err = 0;
> > > +	bool alloc = false;
> > > +	int i, j, k = 0;
> > > +
> > > +	num_q = vport->num_txq + vport->num_rxq;
> > > +
> > > +	buf_size = sizeof(struct virtchnl2_queue_vector) * num_q;
> > > +	vqv = kzalloc(buf_size, GFP_KERNEL);
> > > +	if (!vqv)
> > > +		return -ENOMEM;
> > > +
> > > +	for (i = 0; i < vport->num_txq_grp; i++) {
> > > +		struct iecm_txq_group *tx_qgrp = &vport->txq_grps[i];
> > > +
> > > +		for (j = 0; j < tx_qgrp->num_txq; j++, k++) {
> > > +			vqv[k].queue_type = cpu_to_le32(tx_qgrp->txqs[j]-
> > >q_type);
> > > +			vqv[k].queue_id = cpu_to_le32(tx_qgrp->txqs[j]->q_id);
> > > +
> > > +			if (iecm_is_queue_model_split(vport->txq_model)) {
> > > +				vqv[k].vector_id =
> > > +				cpu_to_le16(tx_qgrp->complq->q_vector-
> > >v_idx);
> > > +				vqv[k].itr_idx =
> > > +				cpu_to_le32(tx_qgrp->complq->q_vector-
> > >tx_itr_idx);
> > > +			} else {
> > > +				vqv[k].vector_id =
> > > +				cpu_to_le16(tx_qgrp->txqs[j]->q_vector-
> > >v_idx);
> > > +				vqv[k].itr_idx =
> > > +				cpu_to_le32(tx_qgrp->txqs[j]->q_vector-
> > >tx_itr_idx);
> > > +			}
> > > +		}
> > > +	}
> > > +
> > > +	if (vport->num_txq != k) {
> > > +		err = -EINVAL;
> > > +		goto error;
> > > +	}
> > > +
> > > +	for (i = 0; i < vport->num_rxq_grp; i++) {
> > > +		struct iecm_rxq_group *rx_qgrp = &vport->rxq_grps[i];
> > > +		int num_rxq;
> > > +
> > > +		if (iecm_is_queue_model_split(vport->rxq_model))
> > > +			num_rxq = rx_qgrp->splitq.num_rxq_sets;
> > > +		else
> > > +			num_rxq = rx_qgrp->singleq.num_rxq;
> > > +
> > > +		for (j = 0; j < num_rxq; j++, k++) {
> > > +			struct iecm_queue *rxq;
> > > +
> > > +			if (iecm_is_queue_model_split(vport->rxq_model))
> > > +				rxq = &rx_qgrp->splitq.rxq_sets[j]->rxq;
> > > +			else
> > > +				rxq = rx_qgrp->singleq.rxqs[j];
> > > +
> > > +			vqv[k].queue_type = cpu_to_le32(rxq->q_type);
> > > +			vqv[k].queue_id = cpu_to_le32(rxq->q_id);
> > > +			vqv[k].vector_id = cpu_to_le16(rxq->q_vector->v_idx);
> > > +			vqv[k].itr_idx = cpu_to_le32(rxq->q_vector->rx_itr_idx);
> > > +		}
> > > +	}
> > > +
> > > +	if (iecm_is_queue_model_split(vport->txq_model)) {
> > > +		if (vport->num_rxq != k - vport->num_complq) {
> > 
> > 	if (iecm_is_queue_model_split() && vport->num_rxq != ...) {
> > 
> > > +			err = -EINVAL;
> > > +			goto error;
> > > +		}
> > > +	} else {
> > 
> > Don't forget to convert this then into `!split + ...`, either with
> > 'else' or not.
> > 
> > > +		if (vport->num_rxq != k - vport->num_txq) {
> > > +			err = -EINVAL;
> > > +			goto error;
> > > +		}
> > > +	}
> > > +
> > > +	/* Chunk up the vector info into multiple messages */
> > > +	config_data_size = sizeof(struct virtchnl2_queue_vector_maps);
> > > +	chunk_size = sizeof(struct virtchnl2_queue_vector);
> > > +
> > > +	num_chunks = IECM_NUM_CHUNKS_PER_MSG(config_data_size,
> > chunk_size) + 1;
> > > +	if (num_q < num_chunks)
> > > +		num_chunks = num_q;
> > > +
> > > +	num_msgs = num_q / num_chunks;
> > > +	if (num_q % num_chunks)
> > > +		num_msgs++;
> > > +
> > > +	for (i = 0, k = 0; i < num_msgs; i++) {
> > > +		if (!vqvm || alloc) {
> > > +			buf_size = (chunk_size * (num_chunks - 1)) +
> > > +					config_data_size;
> > > +			kfree(vqvm);
> > > +			vqvm = kzalloc(buf_size, GFP_KERNEL);
> > > +			if (!vqvm) {
> > > +				err = -ENOMEM;
> > > +				goto error;
> > > +			}
> > > +		} else {
> > > +			memset(vqvm, 0, buf_size);
> > > +		}
> > > +		vqvm->vport_id = cpu_to_le32(vport->vport_id);
> > > +		vqvm->num_qv_maps = cpu_to_le16(num_chunks);
> > > +		memcpy(vqvm->qv_maps, &vqv[k], chunk_size * num_chunks);
> > > +
> > > +		if (map) {
> > > +			err = iecm_send_mb_msg(adapter,
> > > +
> > VIRTCHNL2_OP_MAP_QUEUE_VECTOR,
> > > +					       buf_size, (u8 *)vqvm);
> > > +			if (!err)
> > > +				err = iecm_wait_for_event(adapter,
> > > +							  IECM_VC_MAP_IRQ,
> > > +
> > IECM_VC_MAP_IRQ_ERR);
> > > +		} else {
> > > +			err = iecm_send_mb_msg(adapter,
> > > +
> > VIRTCHNL2_OP_UNMAP_QUEUE_VECTOR,
> > > +					       buf_size, (u8 *)vqvm);
> > > +			if (!err)
> > > +				err =
> > > +				iecm_min_wait_for_event(adapter,
> > > +
> > 	IECM_VC_UNMAP_IRQ,
> > > +
> > 	IECM_VC_UNMAP_IRQ_ERR);
> > > +		}
> > > +		if (err)
> > > +			goto mbx_error;
> > > +
> > > +		k += num_chunks;
> > > +		num_q -= num_chunks;
> > > +		if (num_q < num_chunks) {
> > > +			num_chunks = num_q;
> > > +			alloc = true;
> > > +		}
> > > +	}
> > > +mbx_error:
> > > +	kfree(vqvm);
> > > +error:
> > > +	kfree(vqv);
> > > +	return err;
> > > +}
> > > +EXPORT_SYMBOL(iecm_send_map_unmap_queue_vector_msg);
> > > +
> > > +/**
> > > + * iecm_send_enable_queues_msg - send enable queues virtchnl message
> > > + * @vport: Virtual port private data structure
> > > + *
> > > + * Will send enable queues virtchnl message.  Returns 0 on success, negative
> > on
> > > + * failure.
> > > + */
> > > +static int iecm_send_enable_queues_msg(struct iecm_vport *vport)
> > > +{
> > > +	struct iecm_adapter *adapter = vport->adapter;
> > > +	int err;
> > > +
> > > +	err = iecm_send_ena_dis_queues_msg(vport,
> > > +					   VIRTCHNL2_OP_ENABLE_QUEUES);
> > > +	if (err)
> > > +		return err;
> > > +
> > > +	return iecm_wait_for_event(adapter, IECM_VC_ENA_QUEUES,
> > > +				   IECM_VC_ENA_QUEUES_ERR);
> > > +}
> > > +
> > > +/**
> > > + * iecm_send_disable_queues_msg - send disable queues virtchnl message
> > > + * @vport: Virtual port private data structure
> > > + *
> > > + * Will send disable queues virtchnl message.  Returns 0 on success, negative
> > > + * on failure.
> > > + */
> > > +static int iecm_send_disable_queues_msg(struct iecm_vport *vport)
> > > +{
> > > +	struct iecm_adapter *adapter = vport->adapter;
> > > +	int err;
> > > +
> > > +	err = iecm_send_ena_dis_queues_msg(vport,
> > > +					   VIRTCHNL2_OP_DISABLE_QUEUES);
> > > +	if (err)
> > > +		return err;
> > > +
> > > +	err = iecm_min_wait_for_event(adapter, IECM_VC_DIS_QUEUES,
> > > +				      IECM_VC_DIS_QUEUES_ERR);
> > > +	if (err)
> > > +		return err;
> > > +
> > > +	return iecm_wait_for_marker_event(vport);
> > > +}
> > > +
> > > +/**
> > > + * iecm_convert_reg_to_queue_chunks - Copy queue chunk information to
> > the right
> > > + * structure
> > > + * @dchunks: Destination chunks to store data to
> > > + * @schunks: Source chunks to copy data from
> > > + * @num_chunks: number of chunks to copy
> > > + */
> > > +static void
> > > +iecm_convert_reg_to_queue_chunks(struct virtchnl2_queue_chunk
> > *dchunks,
> > > +				 struct virtchnl2_queue_reg_chunk *schunks,
> > > +				 u16 num_chunks)
> > > +{
> > > +	u16 i;
> > > +
> > > +	for (i = 0; i < num_chunks; i++) {
> > > +		dchunks[i].type = schunks[i].type;
> > > +		dchunks[i].start_queue_id = schunks[i].start_queue_id;
> > > +		dchunks[i].num_queues = schunks[i].num_queues;
> > > +	}
> > > +}
> > > +
> > > +/**
> > > + * iecm_send_delete_queues_msg - send delete queues virtchnl message
> > > + * @vport: Virtual port private data structure
> > > + *
> > > + * Will send delete queues virtchnl message. Return 0 on success, negative on
> > > + * failure.
> > > + */
> > > +int iecm_send_delete_queues_msg(struct iecm_vport *vport)
> > > +{
> > > +	struct iecm_adapter *adapter = vport->adapter;
> > > +	struct virtchnl2_create_vport *vport_params;
> > > +	struct virtchnl2_queue_reg_chunks *chunks;
> > > +	struct virtchnl2_del_ena_dis_queues *eq;
> > > +	int buf_size, err;
> > > +	u16 num_chunks;
> > > +
> > > +	if (vport->adapter->config_data.req_qs_chunks) {
> > > +		struct virtchnl2_add_queues *vc_aq =
> > > +			(struct virtchnl2_add_queues *)
> > > +			vport->adapter->config_data.req_qs_chunks;
> > > +		chunks = &vc_aq->chunks;
> > > +	} else {
> > > +		vport_params = (struct virtchnl2_create_vport *)
> > > +				vport->adapter->vport_params_recvd[0];
> > > +		 chunks = &vport_params->chunks;
> > > +	}
> > > +
> > > +	num_chunks = le16_to_cpu(chunks->num_chunks);
> > > +	buf_size = sizeof(struct virtchnl2_del_ena_dis_queues) +
> > > +			  (sizeof(struct virtchnl2_queue_chunk) *
> > > +			  (num_chunks - 1));
> > > +
> > > +	eq = kzalloc(buf_size, GFP_KERNEL);
> > > +	if (!eq)
> > > +		return -ENOMEM;
> > > +
> > > +	eq->vport_id = cpu_to_le32(vport->vport_id);
> > > +	eq->chunks.num_chunks = cpu_to_le16(num_chunks);
> > > +
> > > +	iecm_convert_reg_to_queue_chunks(eq->chunks.chunks, chunks-
> > >chunks,
> > > +					 num_chunks);
> > > +
> > > +	err = iecm_send_mb_msg(vport->adapter,
> > VIRTCHNL2_OP_DEL_QUEUES,
> > > +			       buf_size, (u8 *)eq);
> > > +	if (err)
> > > +		goto error;
> > > +
> > > +	err = iecm_min_wait_for_event(adapter, IECM_VC_DEL_QUEUES,
> > > +				      IECM_VC_DEL_QUEUES_ERR);
> > > +error:
> > > +	kfree(eq);
> > > +	return err;
> > > +}
> > > +
> > > +/**
> > > + * iecm_send_config_queues_msg - Send config queues virtchnl message
> > > + * @vport: Virtual port private data structure
> > > + *
> > > + * Will send config queues virtchnl message. Returns 0 on success, negative
> > on
> > > + * failure.
> > > + */
> > > +static int iecm_send_config_queues_msg(struct iecm_vport *vport)
> > > +{
> > > +	int err;
> > > +
> > > +	err = iecm_send_config_tx_queues_msg(vport);
> > > +	if (err)
> > > +		return err;
> > > +
> > > +	return iecm_send_config_rx_queues_msg(vport);
> > > +}
> > > +
> > > +/**
> > > + * iecm_send_add_queues_msg - Send virtchnl add queues message
> > > + * @vport: Virtual port private data structure
> > > + * @num_tx_q: number of transmit queues
> > > + * @num_complq: number of transmit completion queues
> > > + * @num_rx_q: number of receive queues
> > > + * @num_rx_bufq: number of receive buffer queues
> > > + *
> > > + * Returns 0 on success, negative on failure.
> > > + */
> > > +int iecm_send_add_queues_msg(struct iecm_vport *vport, u16 num_tx_q,
> > > +			     u16 num_complq, u16 num_rx_q, u16 num_rx_bufq)
> > > +{
> > > +	struct iecm_adapter *adapter = vport->adapter;
> > > +	struct virtchnl2_add_queues aq = {0};
> > > +	struct virtchnl2_add_queues *vc_msg;
> > > +	int size, err;
> > > +
> > > +	vc_msg = (struct virtchnl2_add_queues *)adapter->vc_msg;
> > > +
> > > +	aq.vport_id = cpu_to_le32(vport->vport_id);
> > > +	aq.num_tx_q = cpu_to_le16(num_tx_q);
> > > +	aq.num_tx_complq = cpu_to_le16(num_complq);
> > > +	aq.num_rx_q = cpu_to_le16(num_rx_q);
> > > +	aq.num_rx_bufq = cpu_to_le16(num_rx_bufq);
> > > +
> > > +	err = iecm_send_mb_msg(adapter,
> > > +			       VIRTCHNL2_OP_ADD_QUEUES,
> > > +			       sizeof(struct virtchnl2_add_queues), (u8 *)&aq);
> > > +	if (err)
> > > +		return err;
> > > +
> > > +	err = iecm_wait_for_event(adapter, IECM_VC_ADD_QUEUES,
> > > +				  IECM_VC_ADD_QUEUES_ERR);
> > > +	if (err)
> > > +		return err;
> > > +
> > > +	kfree(adapter->config_data.req_qs_chunks);
> > > +	adapter->config_data.req_qs_chunks = NULL;
> > > +
> > > +	/* compare vc_msg num queues with vport num queues */
> > > +	if (le16_to_cpu(vc_msg->num_tx_q) != num_tx_q ||
> > > +	    le16_to_cpu(vc_msg->num_rx_q) != num_rx_q ||
> > > +	    le16_to_cpu(vc_msg->num_tx_complq) != num_complq ||
> > > +	    le16_to_cpu(vc_msg->num_rx_bufq) != num_rx_bufq) {
> > > +		err = -EINVAL;
> > > +		goto error;
> > > +	}
> > > +
> > > +	size = sizeof(struct virtchnl2_add_queues) +
> > > +			((le16_to_cpu(vc_msg->chunks.num_chunks) - 1) *
> > > +			sizeof(struct virtchnl2_queue_reg_chunk));
> > > +	adapter->config_data.req_qs_chunks =
> > > +		kzalloc(size, GFP_KERNEL);
> > > +	if (!adapter->config_data.req_qs_chunks) {
> > > +		err = -ENOMEM;
> > > +		goto error;
> > > +	}
> > > +	memcpy(adapter->config_data.req_qs_chunks,
> > > +	       adapter->vc_msg, size);
> > > +error:
> > > +	clear_bit(__IECM_VC_MSG_PENDING, adapter->flags);
> > > +	return err;
> > > +}
> > > +
> > >  /**
> > >   * iecm_find_ctlq - Given a type and id, find ctlq info
> > >   * @hw: hardware struct
> > > @@ -1217,6 +2219,13 @@ static void iecm_vport_init(struct iecm_vport
> > *vport,
> > >  	/*Initialize Tx and Rx profiles for Dynamic Interrupt Moderation */
> > >  	memcpy(vport->rx_itr_profile, rx_itr, IECM_DIM_PROFILE_SLOTS);
> > >  	memcpy(vport->tx_itr_profile, tx_itr, IECM_DIM_PROFILE_SLOTS);
> > > +
> > > +	iecm_vport_set_hsplit(vport, true);
> > > +
> > > +	iecm_vport_init_num_qs(vport, vport_msg);
> > > +	iecm_vport_calc_num_q_desc(vport);
> > > +	iecm_vport_calc_num_q_groups(vport);
> > > +	iecm_vport_calc_num_q_vec(vport);
> > >  }
> > >
> > >  /**
> > > @@ -1316,8 +2325,82 @@ static int
> > >  __iecm_vport_queue_ids_init(struct iecm_vport *vport, u32 *qids,
> > >  			    int num_qids, u32 q_type)
> > >  {
> > > -	/* stub */
> > > -	return 0;
> > > +	struct iecm_queue *q;
> > > +	int i, j, k = 0;
> > > +
> > > +	switch (q_type) {
> > > +	case VIRTCHNL2_QUEUE_TYPE_TX:
> > > +		for (i = 0; i < vport->num_txq_grp; i++) {
> > > +			struct iecm_txq_group *tx_qgrp = &vport->txq_grps[i];
> > > +
> > > +			for (j = 0; j < tx_qgrp->num_txq; j++) {
> > > +				if (k < num_qids) {
> > > +					tx_qgrp->txqs[j]->q_id = qids[k];
> > > +					tx_qgrp->txqs[j]->q_type =
> > > +						VIRTCHNL2_QUEUE_TYPE_TX;
> > > +					k++;
> > > +				} else {
> > > +					break;
> > > +				}
> > > +			}
> > > +		}
> > > +		break;
> > > +	case VIRTCHNL2_QUEUE_TYPE_RX:
> > > +		for (i = 0; i < vport->num_rxq_grp; i++) {
> > > +			struct iecm_rxq_group *rx_qgrp = &vport->rxq_grps[i];
> > > +			int num_rxq;
> > > +
> > > +			if (iecm_is_queue_model_split(vport->rxq_model))
> > > +				num_rxq = rx_qgrp->splitq.num_rxq_sets;
> > > +			else
> > > +				num_rxq = rx_qgrp->singleq.num_rxq;
> > > +
> > > +			for (j = 0; j < num_rxq && k < num_qids; j++, k++) {
> > > +				if (iecm_is_queue_model_split(vport-
> > >rxq_model))
> > > +					q = &rx_qgrp->splitq.rxq_sets[j]->rxq;
> > > +				else
> > > +					q = rx_qgrp->singleq.rxqs[j];
> > > +				q->q_id = qids[k];
> > > +				q->q_type = VIRTCHNL2_QUEUE_TYPE_RX;
> > > +			}
> > > +		}
> > > +		break;
> > > +	case VIRTCHNL2_QUEUE_TYPE_TX_COMPLETION:
> > > +		for (i = 0; i < vport->num_txq_grp; i++) {
> > > +			struct iecm_txq_group *tx_qgrp = &vport->txq_grps[i];
> > > +
> > > +			if (k < num_qids) {
> > > +				tx_qgrp->complq->q_id = qids[k];
> > > +				tx_qgrp->complq->q_type =
> > > +
> > 	VIRTCHNL2_QUEUE_TYPE_TX_COMPLETION;
> > > +				k++;
> > > +			} else {
> > > +				break;
> > > +			}
> > > +		}
> > > +		break;
> > > +	case VIRTCHNL2_QUEUE_TYPE_RX_BUFFER:
> > > +		for (i = 0; i < vport->num_rxq_grp; i++) {
> > > +			struct iecm_rxq_group *rx_qgrp = &vport->rxq_grps[i];
> > > +
> > > +			for (j = 0; j < vport->num_bufqs_per_qgrp; j++) {
> > > +				if (k < num_qids) {
> > > +					q = &rx_qgrp->splitq.bufq_sets[j].bufq;
> > > +					q->q_id = qids[k];
> > > +					q->q_type =
> > > +
> > 	VIRTCHNL2_QUEUE_TYPE_RX_BUFFER;
> > > +					k++;
> > > +				} else {
> > > +					break;
> > > +				}
> > > +			}
> > > +		}
> > > +		break;
> > > +	default:
> > > +		break;
> > > +	}
> > > +
> > > +	return k;
> > >  }
> > >
> > >  /**
> > > @@ -1425,6 +2508,20 @@ static bool iecm_is_capability_ena(struct
> > iecm_adapter *adapter, bool all,
> > >  		return !!(*cap_field & flag);
> > >  }
> > >
> > > +/**
> > > + * iecm_get_reserved_vectors - Default implementation to get reserved
> > vectors
> > > + * @adapter: Private data struct
> > > + *
> > > + * Return number of vectors reserved
> > > + */
> > > +static u16 iecm_get_reserved_vectors(struct iecm_adapter *adapter)
> > > +{
> > > +	struct virtchnl2_get_capabilities *caps;
> > > +
> > > +	caps = (struct virtchnl2_get_capabilities *)adapter->caps;
> > > +	return le16_to_cpu(caps->num_allocated_vectors);
> > > +}
> > > +
> > >  /**
> > >   * iecm_vc_ops_init - Initialize virtchnl common api
> > >   * @adapter: Driver specific private structure
> > > @@ -1441,16 +2538,16 @@ void iecm_vc_ops_init(struct iecm_adapter
> > *adapter)
> > >  	vc_ops->vport_queue_ids_init = iecm_vport_queue_ids_init;
> > >  	vc_ops->get_caps = iecm_send_get_caps_msg;
> > >  	vc_ops->is_cap_ena = iecm_is_capability_ena;
> > > -	vc_ops->get_reserved_vecs = NULL;
> > > -	vc_ops->config_queues = NULL;
> > > -	vc_ops->enable_queues = NULL;
> > > -	vc_ops->disable_queues = NULL;
> > > -	vc_ops->add_queues = NULL;
> > > -	vc_ops->delete_queues = NULL;
> > > -	vc_ops->irq_map_unmap = NULL;
> > > -	vc_ops->enable_vport = NULL;
> > > -	vc_ops->disable_vport = NULL;
> > > -	vc_ops->destroy_vport = NULL;
> > > +	vc_ops->get_reserved_vecs = iecm_get_reserved_vectors;
> > > +	vc_ops->config_queues = iecm_send_config_queues_msg;
> > > +	vc_ops->enable_queues = iecm_send_enable_queues_msg;
> > > +	vc_ops->disable_queues = iecm_send_disable_queues_msg;
> > > +	vc_ops->add_queues = iecm_send_add_queues_msg;
> > > +	vc_ops->delete_queues = iecm_send_delete_queues_msg;
> > > +	vc_ops->irq_map_unmap =
> > iecm_send_map_unmap_queue_vector_msg;
> > > +	vc_ops->enable_vport = iecm_send_enable_vport_msg;
> > > +	vc_ops->disable_vport = iecm_send_disable_vport_msg;
> > > +	vc_ops->destroy_vport = iecm_send_destroy_vport_msg;
> > >  	vc_ops->get_ptype = NULL;
> > >  	vc_ops->get_set_rss_key = NULL;
> > >  	vc_ops->get_set_rss_lut = NULL;
> > 
> > Forgot to mention earlier, any reason to not declare this ops as
> > static const and just assign a pointer then? I don't see any
> > alternations here to e.g. fill callbacks with different functions
> > or so.
> > 
> > > diff --git a/drivers/net/ethernet/intel/include/iecm.h
> > b/drivers/net/ethernet/intel/include/iecm.h
> > > index 994664dfe419..8dd6272db7d3 100644
> > > --- a/drivers/net/ethernet/intel/include/iecm.h
> > > +++ b/drivers/net/ethernet/intel/include/iecm.h
> > > @@ -432,6 +432,8 @@ struct iecm_adapter {
> > >  	u16 num_alloc_vport;
> > >  	u16 next_vport;		/* Next free slot in pf->vport[] - 0-based! */
> > >
> > > +	u16 max_queue_limit;	/* Max number of queues user can request */
> > > +
> > >  	struct delayed_work init_task; /* delayed init task */
> > >  	struct workqueue_struct *init_wq;
> > >  	u32 mb_wait_count;
> > > @@ -510,6 +512,12 @@ static inline bool __iecm_is_cap_ena(struct
> > iecm_adapter *adapter, bool all,
> > >  	return adapter->dev_ops.vc_ops.is_cap_ena(adapter, all, field, flag);
> > >  }
> > >
> > > +#define IECM_CAP_HSPLIT (\
> > > +	VIRTCHNL2_CAP_RX_HSPLIT_AT_L2   |\
> > > +	VIRTCHNL2_CAP_RX_HSPLIT_AT_L3   |\
> > > +	VIRTCHNL2_CAP_RX_HSPLIT_AT_L4V4 |\
> > > +	VIRTCHNL2_CAP_RX_HSPLIT_AT_L4V6)
> > > +
> > >  /**
> > >   * iecm_is_reset_detected - check if we were reset at some point
> > >   * @adapter: driver specific private structure
> > > @@ -530,6 +538,8 @@ int iecm_init_dflt_mbx(struct iecm_adapter *adapter);
> > >  void iecm_deinit_dflt_mbx(struct iecm_adapter *adapter);
> > >  void iecm_vc_ops_init(struct iecm_adapter *adapter);
> > >  int iecm_vc_core_init(struct iecm_adapter *adapter, int *vport_id);
> > > +int iecm_get_reg_intr_vecs(struct iecm_vport *vport,
> > > +			   struct iecm_vec_regs *reg_vals, int num_vecs);
> > >  int iecm_wait_for_event(struct iecm_adapter *adapter,
> > >  			enum iecm_vport_vc_state state,
> > >  			enum iecm_vport_vc_state err_check);
> > > @@ -537,6 +547,14 @@ int iecm_min_wait_for_event(struct iecm_adapter
> > *adapter,
> > >  			    enum iecm_vport_vc_state state,
> > >  			    enum iecm_vport_vc_state err_check);
> > >  int iecm_send_get_caps_msg(struct iecm_adapter *adapter);
> > > +int iecm_send_delete_queues_msg(struct iecm_vport *vport);
> > > +int iecm_send_add_queues_msg(struct iecm_vport *vport, u16 num_tx_q,
> > > +			     u16 num_complq, u16 num_rx_q, u16 num_rx_bufq);
> > > +int iecm_send_config_tx_queues_msg(struct iecm_vport *vport);
> > > +int iecm_send_config_rx_queues_msg(struct iecm_vport *vport);
> > > +int iecm_send_enable_vport_msg(struct iecm_vport *vport);
> > > +int iecm_send_disable_vport_msg(struct iecm_vport *vport);
> > > +int iecm_send_destroy_vport_msg(struct iecm_vport *vport);
> > >  int iecm_vport_params_buf_alloc(struct iecm_adapter *adapter);
> > >  void iecm_vport_params_buf_rel(struct iecm_adapter *adapter);
> > >  int iecm_get_vec_ids(struct iecm_adapter *adapter,
> > > @@ -546,7 +564,11 @@ int iecm_recv_mb_msg(struct iecm_adapter
> > *adapter, enum virtchnl_ops op,
> > >  		     void *msg, int msg_size);
> > >  int iecm_send_mb_msg(struct iecm_adapter *adapter, enum virtchnl_ops op,
> > >  		     u16 msg_size, u8 *msg);
> > > +void iecm_vport_set_hsplit(struct iecm_vport *vport, bool ena);
> > > +int iecm_send_enable_channels_msg(struct iecm_vport *vport);
> > > +int iecm_send_disable_channels_msg(struct iecm_vport *vport);
> > >  int iecm_set_msg_pending(struct iecm_adapter *adapter,
> > >  			 struct iecm_ctlq_msg *ctlq_msg,
> > >  			 enum iecm_vport_vc_state err_enum);
> > > +int iecm_send_map_unmap_queue_vector_msg(struct iecm_vport *vport,
> > bool map);
> > >  #endif /* !_IECM_H_ */
> > > diff --git a/drivers/net/ethernet/intel/include/iecm_txrx.h
> > b/drivers/net/ethernet/intel/include/iecm_txrx.h
> > > index e1348011c991..448cae0bf6e7 100644
> > > --- a/drivers/net/ethernet/intel/include/iecm_txrx.h
> > > +++ b/drivers/net/ethernet/intel/include/iecm_txrx.h
> > > @@ -81,6 +81,22 @@
> > >
> > >  #define IECM_TX_COMPLQ_CLEAN_BUDGET	256
> > >
> > > +enum iecm_queue_flags_t {
> > > +	__IECM_Q_GEN_CHK,
> > > +	__IECM_RFLQ_GEN_CHK,
> > > +	__IECM_Q_FLOW_SCH_EN,
> > > +	__IECM_Q_ETF_EN,
> > > +	__IECM_Q_SW_MARKER,
> > > +	__IECM_Q_VLAN_TAG_LOC_L2TAG1,
> > > +	__IECM_Q_VLAN_TAG_LOC_L2TAG2,
> > > +	__IECM_Q_FLAGS_NBITS,
> > > +};
> > > +
> > > +struct iecm_vec_regs {
> > > +	u32 dyn_ctl_reg;
> > > +	u32 itrn_reg;
> > > +};
> > > +
> > >  struct iecm_intr_reg {
> > >  	u32 dyn_ctl;
> > >  	u32 dyn_ctl_intena_m;
> > > @@ -122,6 +138,186 @@ struct iecm_q_vector {
> > >  	char name[IECM_INT_NAME_STR_LEN];
> > >  };
> > >
> > > +struct iecm_rx_queue_stats {
> > > +	u64 packets;
> > > +	u64 bytes;
> > > +	u64 rsc_pkts;
> > > +};
> > > +
> > > +struct iecm_tx_queue_stats {
> > > +	u64 packets;
> > > +	u64 bytes;
> > > +	u64 lso_pkts;
> > > +};
> > > +
> > > +union iecm_queue_stats {
> > > +	struct iecm_rx_queue_stats rx;
> > > +	struct iecm_tx_queue_stats tx;
> > > +};
> > > +
> > > +/* queue associated with a vport */
> > > +struct iecm_queue {
> > > +	struct device *dev;		/* Used for DMA mapping */
> > > +	struct iecm_vport *vport;	/* Backreference to associated vport
> > */
> > > +	union {
> > > +		struct iecm_txq_group *txq_grp;
> > > +		struct iecm_rxq_group *rxq_grp;
> > > +	};
> > > +	/* bufq: Used as group id, either 0 or 1, on clean Buf Q uses this
> > > +	 *       index to determine which group of refill queues to clean.
> > > +	 *       Bufqs are use in splitq only.
> > > +	 * txq: Index to map between Tx Q group and hot path Tx ptrs stored in
> > > +	 *      vport.  Used in both single Q/split Q
> > > +	 * rxq: Index to total rxq across groups, used for skb reporting
> > > +	 */
> > > +	u16 idx;
> > > +	/* Used for both Q models single and split. In split Q model relevant
> > > +	 * only to Tx Q and Rx Q
> > > +	 */
> > > +	u8 __iomem *tail;
> > > +	/* Used in both single and split Q.  In single Q, Tx Q uses tx_buf and
> > > +	 * Rx Q uses rx_buf.  In split Q, Tx Q uses tx_buf, Rx Q uses skb, and
> > > +	 * Buf Q uses rx_buf.
> > > +	 */
> > > +	union {
> > > +		struct iecm_tx_buf *tx_buf;
> > > +		struct {
> > > +			struct iecm_rx_buf *buf;
> > > +			struct iecm_dma_mem **hdr_buf;
> > > +		} rx_buf;
> > > +		struct sk_buff *skb;
> > > +	};
> > > +	u16 q_type;
> > > +	/* Queue id(Tx/Tx compl/Rx/Bufq) */
> > > +	u32 q_id;
> > > +	u16 desc_count;		/* Number of descriptors */
> > > +
> > > +	/* Relevant in both split & single Tx Q & Buf Q*/
> > > +	u16 next_to_use;
> > > +	/* In split q model only relevant for Tx Compl Q and Rx Q */
> > > +	u16 next_to_clean;	/* used in interrupt processing */
> > > +	/* Used only for Rx. In split Q model only relevant to Rx Q */
> > > +	u16 next_to_alloc;
> > > +	/* Generation bit check stored, as HW flips the bit at Queue end */
> > > +	DECLARE_BITMAP(flags, __IECM_Q_FLAGS_NBITS);
> > > +
> > > +	union iecm_queue_stats q_stats;
> > > +	struct u64_stats_sync stats_sync;
> > > +
> > > +	bool rx_hsplit_en;
> > > +
> > > +	u16 rx_hbuf_size;	/* Header buffer size */
> > > +	u16 rx_buf_size;
> > > +	u16 rx_max_pkt_size;
> > > +	u16 rx_buf_stride;
> > > +	u8 rx_buffer_low_watermark;
> > > +	u64 rxdids;
> > > +	/* Used for both Q models single and split. In split Q model relavant
> > > +	 * only to Tx compl Q and Rx compl Q
> > > +	 */
> > > +	struct iecm_q_vector *q_vector;	/* Backreference to associated vector
> > */
> > > +	unsigned int size;		/* length of descriptor ring in bytes */
> > > +	dma_addr_t dma;			/* physical address of ring */
> > > +	void *desc_ring;		/* Descriptor ring memory */
> > > +
> > > +	u16 tx_buf_key;			/* 16 bit unique "identifier" (index)
> > > +					 * to be used as the completion tag
> > when
> > > +					 * queue is using flow based scheduling
> > > +					 */
> > > +	u16 tx_max_bufs;		/* Max buffers that can be transmitted
> > > +					 * with scatter-gather
> > > +					 */
> > > +	DECLARE_HASHTABLE(sched_buf_hash, 12);
> > > +} ____cacheline_internodealigned_in_smp;
> > > +
> > > +/* Software queues are used in splitq mode to manage buffers between rxq
> > > + * producer and the bufq consumer.  These are required in order to maintain a
> > > + * lockless buffer management system and are strictly software only
> > constructs.
> > > + */
> > > +struct iecm_sw_queue {
> > > +	u16 next_to_clean ____cacheline_aligned_in_smp;
> > > +	u16 next_to_alloc ____cacheline_aligned_in_smp;
> > > +	u16 next_to_use ____cacheline_aligned_in_smp;
> > > +	DECLARE_BITMAP(flags, __IECM_Q_FLAGS_NBITS)
> > > +		____cacheline_aligned_in_smp;
> > > +	u16 *ring ____cacheline_aligned_in_smp;
> > 
> > This will result in this part being FIVE cachelines long for
> > 3 * 2 + 8 + 8 = 22 bytes, i.e. 320 bytes for 22!
> > Just making the entire structure cacheline-aligned after its
> > declaration is enough, these ones are not even an overkill,
> > it's an overslaughter.
> > 
> > > +	u16 desc_count;
> > > +	u16 buf_size;
> > > +	struct device *dev;
> > > +} ____cacheline_internodealigned_in_smp;
> > > +
> > > +/* Splitq only.  iecm_rxq_set associates an rxq with at an array of refillqs.
> > > + * Each rxq needs a refillq to return used buffers back to the respective bufq.
> > > + * Bufqs then clean these refillqs for buffers to give to hardware.
> > > + */
> > > +struct iecm_rxq_set {
> > > +	struct iecm_queue rxq;
> > > +	/* refillqs assoc with bufqX mapped to this rxq */
> > > +	struct iecm_sw_queue *refillq0;
> > > +	struct iecm_sw_queue *refillq1;
> > > +};
> > > +
> > > +/* Splitq only.  iecm_bufq_set associates a bufq to an array of refillqs.
> > > + * In this bufq_set, there will be one refillq for each rxq in this rxq_group.
> > > + * Used buffers received by rxqs will be put on refillqs which bufqs will
> > > + * clean to return new buffers back to hardware.
> > > + *
> > > + * Buffers needed by some number of rxqs associated in this rxq_group are
> > > + * managed by at most two bufqs (depending on performance configuration).
> > > + */
> > > +struct iecm_bufq_set {
> > > +	struct iecm_queue bufq;
> > > +	/* This is always equal to num_rxq_sets in iecm_rxq_group */
> > > +	int num_refillqs;
> > > +	struct iecm_sw_queue *refillqs;
> > > +};
> > > +
> > > +/* In singleq mode, an rxq_group is simply an array of rxqs.  In splitq, a
> > > + * rxq_group contains all the rxqs, bufqs and refillqs needed to
> > > + * manage buffers in splitq mode.
> > > + */
> > > +struct iecm_rxq_group {
> > > +	struct iecm_vport *vport; /* back pointer */
> > > +
> > > +	union {
> > > +		struct {
> > > +			int num_rxq;
> > > +			/* store queue pointers */
> > > +			struct iecm_queue *rxqs[IECM_LARGE_MAX_Q];
> > > +		} singleq;
> > > +		struct {
> > > +			int num_rxq_sets;
> > > +			/* store queue pointers */
> > > +			struct iecm_rxq_set *rxq_sets[IECM_LARGE_MAX_Q];
> > > +			struct iecm_bufq_set *bufq_sets;
> > > +		} splitq;
> > > +	};
> > > +};
> > > +
> > > +/* Between singleq and splitq, a txq_group is largely the same except for the
> > > + * complq.  In splitq a single complq is responsible for handling completions
> > > + * for some number of txqs associated in this txq_group.
> > > + */
> > > +struct iecm_txq_group {
> > > +	struct iecm_vport *vport; /* back pointer */
> > > +
> > > +	int num_txq;
> > > +	/* store queue pointers */
> > > +	struct iecm_queue *txqs[IECM_LARGE_MAX_Q];
> > > +
> > > +	/* splitq only */
> > > +	struct iecm_queue *complq;
> > > +};
> > > +
> > > +struct iecm_adapter;
> > > +
> > > +void iecm_vport_init_num_qs(struct iecm_vport *vport,
> > > +			    struct virtchnl2_create_vport *vport_msg);
> > > +void iecm_vport_calc_num_q_desc(struct iecm_vport *vport);
> > > +void iecm_vport_calc_total_qs(struct iecm_adapter *adapter,
> > > +			      struct virtchnl2_create_vport *vport_msg);
> > > +void iecm_vport_calc_num_q_groups(struct iecm_vport *vport);
> > > +void iecm_vport_calc_num_q_vec(struct iecm_vport *vport);
> > >  irqreturn_t
> > >  iecm_vport_intr_clean_queues(int __always_unused irq, void *data);
> > >  #endif /* !_IECM_TXRX_H_ */
> > > --
> > > 2.33.0
> > 
> > Thanks,
> > Al

Please don't forget to fix parts you didn't comment such as
cacheline and const ops issues.

Thanks,
Al

^ permalink raw reply	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 07/19] iecm: finish virtchnl messages
  2022-02-02 23:06     ` Brady, Alan
@ 2022-02-03 15:05       ` Alexander Lobakin
  2022-02-03 15:16         ` Maciej Fijalkowski
  0 siblings, 1 reply; 89+ messages in thread
From: Alexander Lobakin @ 2022-02-03 15:05 UTC (permalink / raw)
  To: intel-wired-lan

From: Alan Brady <alan.brady@intel.com>
Date: Thu, 3 Feb 2022 00:06:00 +0100

> > -----Original Message-----
> > From: Lobakin, Alexandr <alexandr.lobakin@intel.com>
> > Sent: Friday, January 28, 2022 5:20 AM
> > To: Brady, Alan <alan.brady@intel.com>
> > Cc: Lobakin, Alexandr <alexandr.lobakin@intel.com>; intel-wired-
> > lan at lists.osuosl.org; Linga, Pavan Kumar <pavan.kumar.linga@intel.com>;
> > Chittim, Madhu <madhu.chittim@intel.com>; Burra, Phani R
> > <phani.r.burra@intel.com>
> > Subject: Re: [Intel-wired-lan] [PATCH net-next 07/19] iecm: finish virtchnl
> > messages
> > 
> > From: Alan Brady <alan.brady@intel.com>
> > Date: Thu, 27 Jan 2022 16:09:57 -0800
> > 
> > > This adds the rest of the needed virtchnl messages mostly related to
> > > negotiating ptypes and initializing queue registers.
> > >
> > > Signed-off-by: Phani Burra <phani.r.burra@intel.com>
> > > Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
> > > Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
> > > Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
> > > Signed-off-by: Alice Michael <alice.michael@intel.com>
> > > Signed-off-by: Alan Brady <alan.brady@intel.com>
> > > ---
> > >  drivers/net/ethernet/intel/iecm/iecm_lib.c    |   21 +-
> > >  drivers/net/ethernet/intel/iecm/iecm_txrx.c   |  226 +++-
> > >  .../net/ethernet/intel/iecm/iecm_virtchnl.c   | 1187 ++++++++++++++++-
> > >  drivers/net/ethernet/intel/include/iecm.h     |   36 +
> > >  .../net/ethernet/intel/include/iecm_txrx.h    |  198 ++-
> > >  5 files changed, 1635 insertions(+), 33 deletions(-)
> > >
> > > diff --git a/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > > index 4e9cc7f2d138..aab8ee40424e 100644
> > > --- a/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > > +++ b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > > @@ -10,6 +10,25 @@ const char * const iecm_vport_vc_state_str[] = {
> > >  };
> > >  EXPORT_SYMBOL(iecm_vport_vc_state_str);
> > >
> > > +/**
> > > + * iecm_is_feature_ena - Determine if a particular feature is enabled
> > > + * @vport: vport to check
> > > + * @feature: netdev flag to check
> > > + *
> > > + * Returns true or false if a particular feature is enabled.
> > > + */
> > > +bool iecm_is_feature_ena(struct iecm_vport *vport, netdev_features_t
> > feature)
> > > +{
> > > +	bool ena;
> > > +
> > > +	switch (feature) {
> > > +	default:
> > > +		ena = vport->netdev->features & feature;
> > > +		break;
> > > +	}
> > > +	return ena;
> > > +}
> > 
> > This makes absolutely no sense, please rewrite to
> > 
> > 	return vport->netdev->features & feature;
> > 
> > If it will be expanded later, convert it to a switch-case only then.
> > 
> 
> A case is added later in this series of patches but I can thrash this in the middle of the series if you feel strongly about it.

Doesn't matter. The code of each patch should be reasonable.
Even those function stubs don't look nice, but I don't know for sure
if this is acceptable, so I didn't speak about them.
Sometimes we do such stuff if hardly needed, but at least we leave
some good comment then. But I'd go for converting this into a
switch-case ad hoc later.

> 
> > > +
> > >  /**
> > >   * iecm_cfg_hw - Initialize HW struct
> > >   * @adapter: adapter to setup hw struct for

--- 8< ---

> > > +	u16 num_chunks = le16_to_cpu(chunks->num_chunks);
> > > +	int reg_filled = 0, i;
> > > +	u32 reg_val;
> > > +	u16 num_q;
> > > +
> > > +	while (num_chunks) {
> > > +		struct virtchnl2_queue_reg_chunk *chunk = &chunks-
> > >chunks[num_chunks - 1];
> > > +
> > > +		if (le32_to_cpu(chunk->type) == q_type) {
> > > +			num_q = le32_to_cpu(chunk->num_queues);
> > > +			reg_val = le64_to_cpu(chunk->qtail_reg_start);
> > > +			for (i = 0; i < num_q; i++) {
> > > +				if (reg_filled == num_regs)
> > > +					break;
> > > +				reg_vals[reg_filled++] = reg_val;
> > > +				reg_val +=
> > > +					le32_to_cpu(chunk-
> > >qtail_reg_spacing);
> > > +			}
> > > +		}
> > > +		num_chunks--;
> > > +	}
> > 
> > 	while (num_chunks--) {
> > 		struct ... = ... [num_chunks];
> > 
> > 		if (le32_to_cpu(chunk->type) != q_type)
> > 			continue;
> > 
> > 		...
> > 	}
> > 
> > -1 indent level, -complexity.
> > 
> > > +
> > > +	return reg_filled;
> > > +}
> > > +
> > > +/**
> > > + * __iecm_queue_reg_init - initialize queue registers

--- 8< ---

> > > +struct iecm_page_info {
> > > +	dma_addr_t dma;
> > > +	struct page *page;
> > > +	unsigned int page_offset;
> > > +	u16 pagecnt_bias;
> > > +};
> > > +
> > > +struct iecm_rx_buf {
> > > +#define IECM_RX_BUF_MAX_PAGES 2
> > > +	struct iecm_page_info page_info[IECM_RX_BUF_MAX_PAGES];
> > 
> > As I said previously, it will most likely be rejected upstream. They
> > will either suggest using compounds or page_pool (it uses compounds
> > for non-zero-order pages) or maybe introduce folio support to the
> > networking stack or so, but not such stuff.
> > 
> 
> Perhaps I missed it but I didn't see previous comment about this. We have done our own buffer management in the past and it hasn't been issue. I believe there was an attempt to implement this with compound pages but it didn't work in the ways we needed it to for one reason or another (I don't recall exactly why it was problematic but I can check if you're interested).
> 
> A page pool might be a different solution here that may be worth trying, but for many caveats of the data path we've relied on the methods otherwise done in other Intel drivers that have otherwise seemed to do well.

All Intel upstream drivers starting from at least ixgbe and up to
(including) ice use compound pages with no issues. There are no
2-page arrays neither in any Intel driver nor in any driver in
drivers/net/ at all. Compounds, Page Pool, local page ring,
order-0 + multi-frags, kmalloc(HW_JUMBO_LIMIT) and that's it.
"We use this [wrong] approach because any other would require more
work to be done" is not an argument at all in upstream.

> 
> > > +	u8 page_indx;
> > > +	u16 buf_id;
> > > +	u16 buf_size;
> > > +	struct sk_buff *skb;
> > > +};

--- 8< ---

> > > --
> > > 2.33.0
> > 
> > Thanks,
> > Al

Thanks,
Al

^ permalink raw reply	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 07/19] iecm: finish virtchnl messages
  2022-02-03 15:05       ` Alexander Lobakin
@ 2022-02-03 15:16         ` Maciej Fijalkowski
  0 siblings, 0 replies; 89+ messages in thread
From: Maciej Fijalkowski @ 2022-02-03 15:16 UTC (permalink / raw)
  To: intel-wired-lan

On Thu, Feb 03, 2022 at 04:05:03PM +0100, Alexander Lobakin wrote:
> From: Alan Brady <alan.brady@intel.com>
> Date: Thu, 3 Feb 2022 00:06:00 +0100
> 
> > > -----Original Message-----
> > > From: Lobakin, Alexandr <alexandr.lobakin@intel.com>
> > > Sent: Friday, January 28, 2022 5:20 AM
> > > To: Brady, Alan <alan.brady@intel.com>
> > > Cc: Lobakin, Alexandr <alexandr.lobakin@intel.com>; intel-wired-
> > > lan at lists.osuosl.org; Linga, Pavan Kumar <pavan.kumar.linga@intel.com>;
> > > Chittim, Madhu <madhu.chittim@intel.com>; Burra, Phani R
> > > <phani.r.burra@intel.com>
> > > Subject: Re: [Intel-wired-lan] [PATCH net-next 07/19] iecm: finish virtchnl
> > > messages
> > > 
> > > From: Alan Brady <alan.brady@intel.com>
> > > Date: Thu, 27 Jan 2022 16:09:57 -0800
> > > 
> > > > This adds the rest of the needed virtchnl messages mostly related to
> > > > negotiating ptypes and initializing queue registers.
> > > >
> > > > Signed-off-by: Phani Burra <phani.r.burra@intel.com>
> > > > Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
> > > > Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
> > > > Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
> > > > Signed-off-by: Alice Michael <alice.michael@intel.com>
> > > > Signed-off-by: Alan Brady <alan.brady@intel.com>
> > > > ---
> > > >  drivers/net/ethernet/intel/iecm/iecm_lib.c    |   21 +-
> > > >  drivers/net/ethernet/intel/iecm/iecm_txrx.c   |  226 +++-
> > > >  .../net/ethernet/intel/iecm/iecm_virtchnl.c   | 1187 ++++++++++++++++-
> > > >  drivers/net/ethernet/intel/include/iecm.h     |   36 +
> > > >  .../net/ethernet/intel/include/iecm_txrx.h    |  198 ++-
> > > >  5 files changed, 1635 insertions(+), 33 deletions(-)
> > > >
> > > > diff --git a/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > > b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > > > index 4e9cc7f2d138..aab8ee40424e 100644
> > > > --- a/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > > > +++ b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > > > @@ -10,6 +10,25 @@ const char * const iecm_vport_vc_state_str[] = {
> > > >  };
> > > >  EXPORT_SYMBOL(iecm_vport_vc_state_str);
> > > >
> > > > +/**
> > > > + * iecm_is_feature_ena - Determine if a particular feature is enabled
> > > > + * @vport: vport to check
> > > > + * @feature: netdev flag to check
> > > > + *
> > > > + * Returns true or false if a particular feature is enabled.
> > > > + */
> > > > +bool iecm_is_feature_ena(struct iecm_vport *vport, netdev_features_t
> > > feature)
> > > > +{
> > > > +	bool ena;
> > > > +
> > > > +	switch (feature) {
> > > > +	default:
> > > > +		ena = vport->netdev->features & feature;
> > > > +		break;
> > > > +	}
> > > > +	return ena;
> > > > +}
> > > 
> > > This makes absolutely no sense, please rewrite to
> > > 
> > > 	return vport->netdev->features & feature;
> > > 
> > > If it will be expanded later, convert it to a switch-case only then.
> > > 
> > 
> > A case is added later in this series of patches but I can thrash this in the middle of the series if you feel strongly about it.
> 
> Doesn't matter. The code of each patch should be reasonable.
> Even those function stubs don't look nice, but I don't know for sure
> if this is acceptable, so I didn't speak about them.
> Sometimes we do such stuff if hardly needed, but at least we leave
> some good comment then. But I'd go for converting this into a
> switch-case ad hoc later.
> 
> > 
> > > > +
> > > >  /**
> > > >   * iecm_cfg_hw - Initialize HW struct
> > > >   * @adapter: adapter to setup hw struct for
> 
> --- 8< ---
> 
> > > > +	u16 num_chunks = le16_to_cpu(chunks->num_chunks);
> > > > +	int reg_filled = 0, i;
> > > > +	u32 reg_val;
> > > > +	u16 num_q;
> > > > +
> > > > +	while (num_chunks) {
> > > > +		struct virtchnl2_queue_reg_chunk *chunk = &chunks-
> > > >chunks[num_chunks - 1];
> > > > +
> > > > +		if (le32_to_cpu(chunk->type) == q_type) {
> > > > +			num_q = le32_to_cpu(chunk->num_queues);
> > > > +			reg_val = le64_to_cpu(chunk->qtail_reg_start);
> > > > +			for (i = 0; i < num_q; i++) {
> > > > +				if (reg_filled == num_regs)
> > > > +					break;
> > > > +				reg_vals[reg_filled++] = reg_val;
> > > > +				reg_val +=
> > > > +					le32_to_cpu(chunk-
> > > >qtail_reg_spacing);
> > > > +			}
> > > > +		}
> > > > +		num_chunks--;
> > > > +	}
> > > 
> > > 	while (num_chunks--) {
> > > 		struct ... = ... [num_chunks];
> > > 
> > > 		if (le32_to_cpu(chunk->type) != q_type)
> > > 			continue;
> > > 
> > > 		...
> > > 	}
> > > 
> > > -1 indent level, -complexity.
> > > 
> > > > +
> > > > +	return reg_filled;
> > > > +}
> > > > +
> > > > +/**
> > > > + * __iecm_queue_reg_init - initialize queue registers
> 
> --- 8< ---
> 
> > > > +struct iecm_page_info {
> > > > +	dma_addr_t dma;
> > > > +	struct page *page;
> > > > +	unsigned int page_offset;
> > > > +	u16 pagecnt_bias;
> > > > +};
> > > > +
> > > > +struct iecm_rx_buf {
> > > > +#define IECM_RX_BUF_MAX_PAGES 2
> > > > +	struct iecm_page_info page_info[IECM_RX_BUF_MAX_PAGES];
> > > 
> > > As I said previously, it will most likely be rejected upstream. They
> > > will either suggest using compounds or page_pool (it uses compounds
> > > for non-zero-order pages) or maybe introduce folio support to the
> > > networking stack or so, but not such stuff.
> > > 
> > 
> > Perhaps I missed it but I didn't see previous comment about this. We have done our own buffer management in the past and it hasn't been issue. I believe there was an attempt to implement this with compound pages but it didn't work in the ways we needed it to for one reason or another (I don't recall exactly why it was problematic but I can check if you're interested).
> > 
> > A page pool might be a different solution here that may be worth trying, but for many caveats of the data path we've relied on the methods otherwise done in other Intel drivers that have otherwise seemed to do well.
> 
> All Intel upstream drivers starting from at least ixgbe and up to
> (including) ice use compound pages with no issues. There are no
> 2-page arrays neither in any Intel driver nor in any driver in
> drivers/net/ at all. Compounds, Page Pool, local page ring,
> order-0 + multi-frags, kmalloc(HW_JUMBO_LIMIT) and that's it.
> "We use this [wrong] approach because any other would require more
> work to be done" is not an argument at all in upstream.

Maybe not purely rejected but might be controversial to throw in driver
quirk onto a patch that by title suppose to add virtchnl messages?

Could you at least describe this change in the commit message? And provide
some justification why this approach was picked?

> 
> > 
> > > > +	u8 page_indx;
> > > > +	u16 buf_id;
> > > > +	u16 buf_size;
> > > > +	struct sk_buff *skb;
> > > > +};
> 
> --- 8< ---
> 
> > > > --
> > > > 2.33.0
> > > 
> > > Thanks,
> > > Al
> 
> Thanks,
> Al
> _______________________________________________
> Intel-wired-lan mailing list
> Intel-wired-lan at osuosl.org
> https://lists.osuosl.org/mailman/listinfo/intel-wired-lan

^ permalink raw reply	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 08/19] iecm: add interrupts and configure netdev
  2022-02-02 23:17     ` Brady, Alan
@ 2022-02-03 15:55       ` Alexander Lobakin
  0 siblings, 0 replies; 89+ messages in thread
From: Alexander Lobakin @ 2022-02-03 15:55 UTC (permalink / raw)
  To: intel-wired-lan

From: Alan Brady <alan.brady@intel.com>
Date: Thu, 3 Feb 2022 00:17:37 +0100

> > -----Original Message-----
> > From: Lobakin, Alexandr <alexandr.lobakin@intel.com>
> > Sent: Friday, January 28, 2022 5:35 AM
> > To: Brady, Alan <alan.brady@intel.com>
> > Cc: Lobakin, Alexandr <alexandr.lobakin@intel.com>; intel-wired-
> > lan at lists.osuosl.org; Linga, Pavan Kumar <pavan.kumar.linga@intel.com>;
> > Chittim, Madhu <madhu.chittim@intel.com>; Burra, Phani R
> > <phani.r.burra@intel.com>
> > Subject: Re: [Intel-wired-lan] [PATCH net-next 08/19] iecm: add interrupts and
> > configure netdev
> > 
> > From: Alan Brady <alan.brady@intel.com>
> > Date: Thu, 27 Jan 2022 16:09:58 -0800
> > 
> > > This finishes implementing init_task by adding everything we need to
> > > configure the netdevice for the vport and setup its interrupts.
> > >
> > > Signed-off-by: Phani Burra <phani.r.burra@intel.com>
> > > Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
> > > Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
> > > Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
> > > Signed-off-by: Alice Michael <alice.michael@intel.com>
> > > Signed-off-by: Alan Brady <alan.brady@intel.com>
> > > ---
> > >  drivers/net/ethernet/intel/iecm/iecm_lib.c    | 813 +++++++++++++++++-
> > >  drivers/net/ethernet/intel/iecm/iecm_txrx.c   |  17 +
> > >  .../net/ethernet/intel/iecm/iecm_virtchnl.c   | 165 ++++
> > >  drivers/net/ethernet/intel/include/iecm.h     | 112 ++-
> > >  .../net/ethernet/intel/include/iecm_txrx.h    |   2 +
> > >  5 files changed, 1104 insertions(+), 5 deletions(-)
> > >

--- 8< ---

> > > +/**
> > > + * iecm_find_mac_filter - Search filter list for specific mac filter
> > > + * @vport: main vport structure
> > > + * @macaddr: the MAC address
> > > + *
> > > + * Returns ptr to the filter object or NULL. Must be called while
> > > +holding the
> > > + * mac_filter_list_lock.
> > > + **/
> > > +static struct
> > > +iecm_mac_filter *iecm_find_mac_filter(struct iecm_vport *vport,
> > > +				      const u8 *macaddr)
> > > +{
> > > +	struct iecm_adapter *adapter = vport->adapter;
> > > +	struct iecm_mac_filter *f;
> > > +
> > > +	if (!macaddr)
> > > +		return NULL;
> > > +
> > > +	list_for_each_entry(f, &adapter->config_data.mac_filter_list, list) {
> > > +		if (ether_addr_equal(macaddr, f->macaddr))
> > > +			return f;
> > > +	}
> > 
> > Excessive braces again.
> > 
> 
> Will not fix.

I'd like to remind here that it's a technical discussion in a public
list, and every participant should keep it sane, with sane arguments
and reasoning.

My arguments are:
 * it adds chars and locs for no reason;
 * it adds code complexity -> lowers readability a tiny bit;
 * checkpatch and/or cocchinelle may complain on this.

I'd double-check the last bullet at first. If it's the case, there's
no other choice except for deleting them. Otherwise I'd like to hear
reasoning for keeping those braces and some other folks' opinions as
well.

> 
> > > +	return NULL;
> > > +}
> > > +
> > > +/**
> > > + * __iecm_add_mac_filter - Add mac filter helper function

--- 8< ---

> > > +		dflt_features |= NETIF_F_TSO6;
> > > +	if (iecm_is_cap_ena_all(adapter, IECM_SEG_CAPS,
> > > +				VIRTCHNL2_CAP_SEG_IPV4_UDP |
> > > +				VIRTCHNL2_CAP_SEG_IPV6_UDP))
> > > +		dflt_features |= NETIF_F_GSO_UDP_L4;
> > > +	if (iecm_is_cap_ena_all(adapter, IECM_RSC_CAPS, IECM_CAP_RSC))
> > > +		offloads |= NETIF_F_GRO_HW;
> > 
> > I see `offloads` being used only here, |= -> =.
> > 
> 
> It makes it easier to add others in future or if some happen to get stripped in/out in the future it makes the code more resilient.  We prefer to keep it this way.

Ah, again.
It's not that usual in the kernel to make some thing more
complicated due to some kind of a hypothetical case in the future.
That case may never happen, while code burden will still be there.

Also, a problem might occur *only* if someone places `offloads`
modification before this sentence. If it gets deleted, if `offloads`
gets modified later, assignment is still correct here.
And it's a developer's duty then to check for this.

The last thing. Assignment is a pure single store. OR is load +
store, 2 operations. Compilers *probably* optimize this on -O2,
but that's up to them.
The function itself is cold, yet still this is avoidable.

> 
> > > +	netdev->features |= dflt_features;
> > > +	netdev->hw_features |= dflt_features | offloads;
> > > +	netdev->hw_enc_features |= dflt_features | offloads;

--- 8< ---

> > > +	.ndo_set_mac_address = NULL,
> > > +	.ndo_change_mtu = NULL,
> > > +	.ndo_get_stats64 = NULL,
> > > +	.ndo_fix_features = NULL,
> > > +	.ndo_set_features = NULL,
> > > +	.ndo_vlan_rx_add_vid = NULL,
> > > +	.ndo_vlan_rx_kill_vid = NULL,
> > > +	.ndo_setup_tc = NULL,
> > 
> > Non-initialized members get zeroed by default, NULLing them is excessive.
> > 
> 
> The next patches will add these, they're placeholders. I left it like that because as you read the patches you can know what functions are there yet (as well as a checklist for myself as made the patches).

As I wrote in one of my prevous replies, each patch is
a self-contained/self-complete entity.
With those NULLs being present newly implemented callbacks
will result in

 .ndo_old_function_1 = iecm_old_function_1,
-.ndo_new_function_2 = NULL,
-.ndo_new_function_3 = NULL,
+.ndo_new_function_2 = iecm_new_function_2,
+.ndo_new_function_3 = iecm_new_function_3,
 .ndo_old_function_4 = iecm_old_function_4,

instead of

 .ndo_old_function_1 = iecm_old_function_1,
+.ndo_new_function_2 = iecm_new_function_2,
+.ndo_new_function_3 = iecm_new_function_3,
 .ndo_old_function_4 = iecm_old_function_4,

This makes a lot of sense, at least to me.

> 
> > > +};
> > > +
> > > +static const struct net_device_ops iecm_netdev_ops_singleq = {
> > > +	.ndo_open = NULL,
> > > +	.ndo_stop = NULL,
> > > +	.ndo_start_xmit = NULL,
> > > +	.ndo_set_rx_mode = NULL,
> > > +	.ndo_validate_addr = eth_validate_addr,
> > > +	.ndo_set_mac_address = NULL,
> > > +	.ndo_change_mtu = NULL,
> > > +	.ndo_get_stats64 = NULL,
> > > +	.ndo_fix_features = NULL,
> > > +	.ndo_set_features = NULL,
> > > +	.ndo_vlan_rx_add_vid = NULL,
> > > +	.ndo_vlan_rx_kill_vid = NULL,
> > > +	.ndo_setup_tc           = NULL,
> > 
> > Same 2 previous points.
> > 
> > .ndo_setup_tc assignment indentation is weird. Either align all the initializers
> > with tabs or don't align at all (both are valid cases).
> > 
> > > +};

--- 8< ---

> > > --
> > > 2.33.0
> > 
> > Thanks,
> > Al

Thanks,
Al

^ permalink raw reply	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 09/19] iecm: alloc vport TX resources
  2022-02-02 23:45   ` Brady, Alan
@ 2022-02-03 17:56     ` Alexander Lobakin
  0 siblings, 0 replies; 89+ messages in thread
From: Alexander Lobakin @ 2022-02-03 17:56 UTC (permalink / raw)
  To: intel-wired-lan

From: Alan Brady <alan.brady@intel.com>
Date: Thu, 3 Feb 2022 00:45:19 +0100

> > -----Original Message-----
> > From: Lobakin, Alexandr <alexandr.lobakin@intel.com>
> > Sent: Friday, January 28, 2022 5:57 AM
> > To: Brady, Alan <alan.brady@intel.com>
> > Cc: Lobakin, Alexandr <alexandr.lobakin@intel.com>; intel-wired-
> > lan at lists.osuosl.org; Burra, Phani R <phani.r.burra@intel.com>; Chittim, Madhu
> > <madhu.chittim@intel.com>; Linga, Pavan Kumar
> > <pavan.kumar.linga@intel.com>
> > Subject: Re: [Intel-wired-lan] [PATCH net-next 09/19] iecm: alloc vport TX
> > resources
> > 
> > From: Alan Brady <alan.brady@intel.com>
> > Date: Thu, 27 Jan 2022 16:09:59 -0800
> > 
> > > With init_task out of the way, we can start implementing open and data
> > > path. During open we'll allocate queue resources for vport. This only
> > > includes what's needed to get the TX resources. The next patch will get RX
> > > resources.
> > >
> > > The splitq model is unique in that it introduces the concept of "queue
> > > groups" where, for TX, we have some number of descriptor queues being
> > > serviced by one completion queue in a given group association. By
> > > 'splitting' a normal queue into two queues, one context is just handling
> > > descriptors and one context handling buffers, we can more effeciently deal
> > > with both and configure asymmetric setups (multiple descriptor queues to
> > > one completion queue).
> > >
> > > Signed-off-by: Phani Burra <phani.r.burra@intel.com>
> > > Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
> > > Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
> > > Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
> > > Signed-off-by: Alan Brady <alan.brady@intel.com>
> > > ---
> > >  drivers/net/ethernet/intel/iecm/Makefile      |    1 +
> > >  drivers/net/ethernet/intel/iecm/iecm_lib.c    |  369 ++++-
> > >  .../ethernet/intel/iecm/iecm_singleq_txrx.c   |   29 +
> > >  drivers/net/ethernet/intel/iecm/iecm_txrx.c   | 1282 ++++++++++++++++-
> > >  .../net/ethernet/intel/iecm/iecm_virtchnl.c   |   29 +
> > >  drivers/net/ethernet/intel/include/iecm.h     |   28 +
> > >  .../ethernet/intel/include/iecm_lan_txrx.h    |  394 +++++
> > >  .../net/ethernet/intel/include/iecm_txrx.h    |   96 ++
> > >  8 files changed, 2214 insertions(+), 14 deletions(-)
> > >  create mode 100644 drivers/net/ethernet/intel/iecm/iecm_singleq_txrx.c
> > >  create mode 100644 drivers/net/ethernet/intel/include/iecm_lan_txrx.h
> > >

--- 8< ---

> > > +	spin_lock_bh(&adapter->mac_filter_list_lock);
> > > +	list_for_each_entry(f, &adapter->config_data.mac_filter_list, list) {
> > > +		if (!f->remove)
> > > +			f->add = true;
> > > +	}
> > 
> > Redundant braces around a single statement.
> > 
> 
> Will not fix.

As I said in my previous message, this answer doesn't make this
choice clearer neither to me, nor to other reviewers.

> 
> > > +	spin_unlock_bh(&adapter->mac_filter_list_lock);
> > > +
> > > +	iecm_add_del_ether_addrs(vport, true, false);
> > > +}
> > > +
> > > +/**
> > > + * iecm_set_all_vlans - Re-add all VLANs in list

--- 8< ---

> > > @@ -1395,8 +1754,8 @@ void iecm_free_dma_mem(struct iecm_hw *hw,
> > struct iecm_dma_mem *mem)
> > >  }
> > >
> > >  static const struct net_device_ops iecm_netdev_ops_splitq = {
> > > -	.ndo_open = NULL,
> > > -	.ndo_stop = NULL,
> > > +	.ndo_open = iecm_open,
> > > +	.ndo_stop = iecm_stop,
> > >  	.ndo_start_xmit = NULL,
> > 
> > Hmm, forgot to mention this earlier as well. Consider marking
> > CONFIG_IECM as `depends on BROKEN` in Kconfig and remove this line
> > in the last commit. Otherwise, it will be possible to panic the
> > kernel as at least .ndo_start_xmit should always be set, kernel
> > doesn't check for it being non-NULL, it just calls it. Same with
> > open, stop and probably more, so it's a good practice to disable
> > drivers with depending on BROKEN until it receives the workable
> > state.
> > 
> 
> I don't have the full history on 'BROKEN' being a dependency but it seems silly to me to add some thrash just for that. Will consider.

Here: [0] I add 'depends on BROKEN' as the patch itself only adds
some basic functionality and will be ready only after couple patches
more.
It's a common practice, we mark some Kconfig symbols such way when
they're either will become usable after some more changes or when
a particular feature stops working properly or building on some
configurations.

This driver is not ready to work right after you add a Kconfig
option for it. Notably already mentioned NULLed .ndo_start_xmit()
will cause nullptr accesses if register_netdev() is called. It's
not a rare case at all when automation build systems and people
use Git bisecting and test kernels in the middle of a series. If
there is a chance to crash the kernel that way, it will crash
sooner or later.

Ok, maybe `depends on BROKEN` doesn't incremental-building friendly
as allmodconfigs will start to build iecm only after removing this
line, but at least make sure there's no chance for this driver to
probe earlier than it's fully functional.

> 
> > >  	.ndo_set_rx_mode = NULL,
> > >  	.ndo_validate_addr = eth_validate_addr,

--- 8< ---

> > > +static void iecm_set_vlan_tag_loc(struct iecm_adapter *adapter,
> > > +				  struct iecm_queue *q)
> > > +{
> > > +	if (iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
> > VIRTCHNL2_CAP_VLAN)) {
> > > +		struct virtchnl_vlan_supported_caps *insertion_support;
> > > +
> > > +		insertion_support =
> > > +				&adapter->vlan_caps-
> > >offloads.insertion_support;
> > > +		if (insertion_support->outer) {
> > > +			if (insertion_support->outer &
> > > +			    VIRTCHNL_VLAN_TAG_LOCATION_L2TAG1)
> > > +				set_bit(__IECM_Q_VLAN_TAG_LOC_L2TAG1,
> > > +					q->flags);
> > > +			else if (insertion_support->outer &
> > > +				 VIRTCHNL_VLAN_TAG_LOCATION_L2TAG2)
> > > +				set_bit(__IECM_Q_VLAN_TAG_LOC_L2TAG2,
> > > +					q->flags);
> > > +		} else if (insertion_support->inner) {
> > > +			if (insertion_support->inner &
> > > +			    VIRTCHNL_VLAN_TAG_LOCATION_L2TAG1)
> > > +				set_bit(__IECM_Q_VLAN_TAG_LOC_L2TAG1,
> > > +					q->flags);
> > > +			else if (insertion_support->inner &
> > > +				 VIRTCHNL_VLAN_TAG_LOCATION_L2TAG2)
> > > +				set_bit(__IECM_Q_VLAN_TAG_LOC_L2TAG2,
> > > +					q->flags);
> > > +		}
> > > +	} else if (iecm_is_cap_ena(adapter, IECM_BASE_CAPS,
> > > +				   VIRTCHNL2_CAP_VLAN)) {
> > > +		set_bit(__IECM_Q_VLAN_TAG_LOC_L2TAG1, q->flags);
> > > +	}
> > 
> > If !ena -> set_bit() + return, -1 indent.
> > 
> 
> I'm afraid I'm not following here.

	/* Do we have OTHER? */
	if (!iecm_is_cap_ena(adapter, IECM_OTHER_CAPS, VIRTCHNL2_CAP_VLAN)) {
		/* OK, we don't. If we have BASE, set L2TAG1 bit and exit,
		 * otherwise just exit.
		 */
		if (iecm_is_cap_ena(adapter, IECM_BASE_CAPS,
				    VIRTCHNL2_CAP_VLAN))
			set_bit(__IECM_Q_VLAN_TAG_LOC_L2TAG1, q->flags);

		return;
	}

	/* We have OTHER, let's proceed with full-blown features */
	if (insertion_support->outer) {
		...
	}

BTW, just have noticed that these checks for `outer` and `inner`
being non-0 are pointless, bitops will do the same job.

> 
> > > +}
> > > +
> > > +/**
> > > + * iecm_txq_group_alloc - Allocate all txq group resources

--- 8< ---

> > > +	/* Initialize flow scheduling for queues that were requested
> > > +	 * before the interface was brought up
> > > +	 */
> > > +	for (i = 0; i < vport->num_txq; i++) {
> > > +		if (test_bit(i, vport->adapter->config_data.etf_qenable)) {
> > > +			set_bit(__IECM_Q_FLOW_SCH_EN, vport->txqs[i]-
> > >flags);
> > > +			set_bit(__IECM_Q_ETF_EN, vport->txqs[i]->flags);
> > > +		}
> > > +	}
> > 
> > Redundant braces for the for-loop.
> > 
> 
> Will not fix.
> 
> > > +
> > > +	return 0;
> > > +err_out:
> > > +	iecm_vport_queues_rel(vport);
> > > +	return err;
> > > +}
> > > +
> > > +/**
> > > + * iecm_vport_intr_clean_queues - MSIX mode Interrupt Handler

--- 8< ---

> > > +	if (iecm_is_queue_model_split(vport->txq_model)) {
> > > +		for (i = 0; i < vport->num_txq_grp; i++)
> > > +			vport->txq_grps[i].complq->q_vector = NULL;
> > > +	} else {
> > > +		for (i = 0; i < vport->num_txq_grp; i++) {
> > > +			for (j = 0; j < vport->txq_grps[i].num_txq; j++)
> > > +				vport->txq_grps[i].txqs[j]->q_vector = NULL;
> > > +		}
> > 
> > Redundant braces.
> > 
> 
> Will not fix.
> 
> > > +	}
> > > +
> > > +	kfree(vport->q_vectors);
> > > +	vport->q_vectors = NULL;
> > > +}
> > > +
> > > +/**
> > > + * iecm_vport_intr_rel_irq - Free the IRQ association with the OS

--- 8< ---

> > > +static void iecm_net_dim(struct iecm_q_vector *q_vector)
> > > +{
> > > +	if (IECM_ITR_IS_DYNAMIC(q_vector->tx_intr_mode)) {
> > > +		struct dim_sample dim_sample = {};
> > > +		u64 packets = 0, bytes = 0;
> > > +		int i;
> > > +
> > > +		for (i = 0; i < q_vector->num_txq; i++) {
> > > +			packets += q_vector->tx[i]->q_stats.tx.packets;
> > > +			bytes += q_vector->tx[i]->q_stats.tx.bytes;
> > > +		}
> > > +
> > > +		dim_update_sample(q_vector->total_events, packets, bytes,
> > > +				  &dim_sample);
> > > +		net_dim(&q_vector->tx_dim, dim_sample);
> > > +	}
> > 
> > 	if (!dynamic_tx)
> > 		goto check_rx;
> > 
> > -1 level.
> > 
> > > +
> > > +	if (IECM_ITR_IS_DYNAMIC(q_vector->rx_intr_mode)) {
> > > +		struct dim_sample dim_sample = {};
> > > +		u64 packets = 0, bytes = 0;
> > > +		int i;
> > > +
> > > +		for (i = 0; i < q_vector->num_rxq; i++) {
> > > +			packets += q_vector->rx[i]->q_stats.rx.packets;
> > > +			bytes += q_vector->rx[i]->q_stats.rx.bytes;
> > > +		}
> > > +
> > > +		dim_update_sample(q_vector->total_events, packets, bytes,
> > > +				  &dim_sample);
> > > +		net_dim(&q_vector->rx_dim, dim_sample);
> > > +	}
> > 
> > 	if (!dynamic_rx)
> > 		return;
> > 
> > -1 as well.
> > 
> 
> I'm not entirely convinced this is better or more readable but I guess will fix.

	/* Do we have DIM enabled for Tx? If no, proceed with Rx */
	if (!IECM_ITR_IS_DYNAMIC(q_vector->tx_intr_mode))
		goto rx;

	for (i = 0; i < q_vector->num_txq; i++) {
		...

rx:
	/* Do we have DIM enabled for Rx? If no, just exit */
	if (!IECM_ITR_IS_DYNAMIC(q_vector->rx_intr_mode))
		return;

	for (i = 0; i < q_vector->num_rxq; i++) {
		...

I'm not entirely convinced this can look less readable then the code
above.

You could even split it into two small functions or even compress it
as they only differ with the direction suffixes:

/* Can be a function as well, would just require more arguments */
#define iecm_net_dim_dir(q_vector, dir) ({			\
	struct iecm_q_vector *__qv = (q_vector);		\
								\
	if (IECM_ITR_IS_DYNAMIC(__qv->dir##_intr_mode)) {	\
		struct dim_sample dim_sample = {};		\
		...						\
		net_dim(&__qv->dir##_dim, dim_sample);		\
	}							\
})

#define iecm_net_dim_rx(q_vector)	iecm_net_dim_dir(q_vector, rx)
#define iecm_net_dim_tx(q_vector)	iecm_net_dim_dir(q_vector, tx)

static void iecm_net_dim(struct iecm_q_vector *q_vector)
{
	iecm_net_dim_rx(q_vector);
	iecm_net_dim_tx(q_vector);
}

> 
> > > +}
> > > +
> > > +/**
> > > + * iecm_vport_intr_update_itr_ena_irq - Update itr and re-enable MSIX
> > interrupt

--- 8< ---

> > > +void iecm_vport_intr_write_itr(struct iecm_q_vector *q_vector, u16 itr, bool
> > tx)
> > > +{
> > > +	struct iecm_hw *hw = &q_vector->vport->adapter->hw;
> > > +	struct iecm_intr_reg *intr_reg;
> > > +
> > > +	if (tx && !q_vector->tx)
> > > +		return;
> > > +	else if (!tx && !q_vector->rx)
> > > +		return;
> > 
> > 	if ((tx && !q_vector->tx) || (!tx && !q_vector->rx))
> > 		return;
> > 
> > Fits into 79 cols and looks more elegant-ish.
> > 
> 
> Yours is a bit harder for humans to grok, would be prefer to keep this as-is.

For me it's harder to follow the ladder with two identical branches
(return-return).
I'd also rename `tx` to at least `is_tx` as it makes things even
more confusing.

> 
> > > +
> > > +	intr_reg = &q_vector->intr_reg;
> > > +	wr32(hw, tx ? intr_reg->tx_itr : intr_reg->rx_itr,
> > > +	     ITR_REG_ALIGN(itr) >> IECM_ITR_GRAN_S);
> > > +}
> > > +
> > > +/**
> > > + * iecm_vport_intr_ena_irq_all - Enable IRQ for the given vport

--- 8< ---

> > > +void iecm_fill_dflt_rss_lut(struct iecm_vport *vport)
> > > +{
> > > +	u16 num_active_rxq = vport->num_rxq;
> > > +	int i;
> > > +
> > > +	for (i = 0; i < vport->adapter->rss_data.rss_lut_size; i++)
> > > +		vport->adapter->rss_data.rss_lut[i] = i % num_active_rxq;
> > 
> > I think I saw a built-in kernel function for that, I'd grep for sth
> > like fill_default_rss.
> > 
> 
> Hmm I grep'd around and didn't see anything like that. Didn't see anything like that in other drivers I briefly looked at either.

I was sure it's there. It's ethtool_rxfh_indir_default(): [1].

You can deref vport->adapter->rss_data in a separate var to shorten
all this.

> 
> > > +}
> > > +
> > > +/**
> > > + * iecm_init_rss - Prepare for RSS

--- 8< ---

> > > +
> > >  #define MAKEMASK(m, s)	((m) << (s))
> > 
> > Consider using stock BIT(s) insteead of introducing this MAKEMASK().
> > 
> > >
> > > +struct iecm_tx_buf {
> > > +	struct hlist_node hlist;
> > > +	void *next_to_watch;
> > > +	union {
> > > +		struct sk_buff *skb;
> > > +		struct xdp_frame *xdpf;
> > > +	};
> > > +	unsigned int bytecount;
> > > +	unsigned short gso_segs;
> > > +#define IECM_TX_FLAGS_TSO			BIT(0)
> > > +#define IECM_TX_FLAGS_VLAN_TAG			BIT(1)
> > > +#define IECM_TX_FLAGS_HW_VLAN			BIT(2)
> > > +#define IECM_TX_FLAGS_HW_OUTER_SINGLE_VLAN	BIT(3)
> > > +#define IECM_TX_FLAGS_VLAN_SHIFT		16
> > > +#define IECM_TX_FLAGS_VLAN_MASK			0xFFFF0000
> > > +	u32 tx_flags;
> > > +	DEFINE_DMA_UNMAP_ADDR(dma);
> > > +	DEFINE_DMA_UNMAP_LEN(len);
> > > +	u16 compl_tag;		/* Unique identifier for buffer; used to
> > > +				 * compare with completion tag returned
> > > +				 * in buffer completion event
> > > +				 */
> > > +};
> > > +
> > > +struct iecm_buf_lifo {
> > > +	u16 top;
> > > +	u16 size;
> > > +	struct iecm_tx_buf **bufs;
> > 
> > There'll probably be a 4-byte gap before @bufs, move @top and @size
> > to the bottop to avoid this.
> > 
> > > +};

--- 8< ---

> > > --
> > > 2.33.0
> > 
> > Thanks,
> > Al

[0] https://lore.kernel.org/all/20211223002209.1092165-13-alexandr.lobakin at intel.com
[1] https://elixir.bootlin.com/linux/v5.17-rc2/source/include/linux/ethtool.h#L125

Thanks,
Al

^ permalink raw reply	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 10/19] iecm: alloc vport RX resources
  2022-02-03  0:13     ` Brady, Alan
@ 2022-02-03 18:29       ` Alexander Lobakin
  0 siblings, 0 replies; 89+ messages in thread
From: Alexander Lobakin @ 2022-02-03 18:29 UTC (permalink / raw)
  To: intel-wired-lan

From: Alan Brady <alan.brady@intel.com>
Date: Thu, 3 Feb 2022 01:13:23 +0100

> > -----Original Message-----
> > From: Lobakin, Alexandr <alexandr.lobakin@intel.com>
> > Sent: Friday, January 28, 2022 6:16 AM
> > To: Brady, Alan <alan.brady@intel.com>
> > Cc: Lobakin, Alexandr <alexandr.lobakin@intel.com>; intel-wired-
> > lan at lists.osuosl.org; Burra, Phani R <phani.r.burra@intel.com>; Chittim, Madhu
> > <madhu.chittim@intel.com>; Linga, Pavan Kumar
> > <pavan.kumar.linga@intel.com>
> > Subject: Re: [Intel-wired-lan] [PATCH net-next 10/19] iecm: alloc vport RX
> > resources
> > 
> > From: Alan Brady <alan.brady@intel.com>
> > Date: Thu, 27 Jan 2022 16:10:00 -0800
> > 
> > > This finishes what we need to do for open by adding RX resource
> > > allocations.
> > >
> > > As noted in the TX alloc patch, the splitq model is unique in
> > > introducing the concept of queue groups, which also applies to RX,
> > > albeit in a slightly different way. For RX we also split the queue
> > > between descriptor handling and buffer handling. We have some number
> > > of RX completion queues associated with up to two buffer queues in a
> > > given queue group. Once buffers are cleaned and recycled, they're
> > > given the buffer queues which then posts the buffers back to hardware.
> > > To enable this in a lockless way, there's also the concept of 'refill
> > > queues' introduced. Recycled buffers are put onto refill queues which is what
> > the buffer queues clean to get buffers back.
> > >
> > > Signed-off-by: Phani Burra <phani.r.burra@intel.com>
> > > Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
> > > Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
> > > Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
> > > Signed-off-by: Alan Brady <alan.brady@intel.com>
> > > ---
> > >  drivers/net/ethernet/intel/iecm/iecm_txrx.c   | 769 ++++++++++++++++++
> > >  .../net/ethernet/intel/include/iecm_txrx.h    |   7 +
> > >  2 files changed, 776 insertions(+)
> > >

--- 8< ---

> > > +	rxq->hbuf_pages.nr_pages = nr_pages;
> > > +	for (i = 0; i < nr_pages; i++) {
> > > +		if (iecm_alloc_page(rxq, &rxq->hbuf_pages.pages[i]))
> > 
> > And here you allocate pages with GFP_ATOMIC in process context.
> > Atomic allocations must not be used if the function may sleep.
> > Please add gfp_t gfp argument to iecm_alloc_page() and use GFP_KERNEL here
> > (and GFP_ATOMIC on buffer refill hotpath).
> > 
> 
> Perhaps I am confused here but it's my understanding we need GFP_ATOMIC when potentially used in a case where we can't sleep as it signals to the memory allocator to not sleep.  Not the other way around; we can't sleep if we have memory taken with GFP_ATOMIC.  We use it in hotpath as you said, where we can't sleep. What it really means to us is that it has a higher chance of failure to not get alloc'd if the kernel isn't allowed to sleep to free up some memory.

GFP_ATOMIC must be used only in atomic contexts i.e. hardirq and
softirq processing (using them inside spinlocks is debatable). With
this flag set, we have less resources available to allocate from,
and you can consider those resources as the reserves for critical
situations (and e.g. hardirqs are of those kind). This mostly comes
from GFP_NOWAIT embedded into GFP_ATOMIC.
This function allocates all the resources on ifup, the context is
non-critical and it's okay to sleep there, so using GFP_ATOMIC is
an excessive waste of critical memory in case of e.g. memory
pressure.
So we use GFP_ATOMIC for buffer refilling only inside NAPI context
itself.

> > > +			goto unroll_buf_alloc;
> > > +	}
> > > +
> > > +	page_info = &rxq->hbuf_pages.pages[0];

--- 8< ---

> > > +
> > > +		buf++;
> > > +		nta++;
> > > +		if (unlikely(nta == rxbufq->desc_count)) {
> > 
> > 		if (unlikely(++nta == ...)) { /* Just in one line */
> > 
> 
> Yes but pre-increments are gross and hard for humans to grok.

Could you please elaborate on "pre-increments are gross"? I don't
get it at all.

Also, "hard for humans to grok" to me doesn't seem really correct
here (and in all other places you used it) since you're reflecting
your personal opinion, not a mathematical fact I believe?

> 
> > > +			buf = rxbufq->rx_buf.buf;
> > > +			nta = 0;
> > > +		}
> > > +
> > > +		alloc_count--;
> > > +	} while (alloc_count);
> > 
> > 	} while (alloc_count--); /* Just in one line */
> > 
> 
> I believe
> 
> } while (--alloc_count);
> 
> would be accurate but pre increment/decrement are hard for humans to grok (as evidenced here).

Right, sorry, there should be a pre-increment.
Evidenced what and where?
If you're about my off-by-one, it doesn't mean anything, I use
pre-increments much more often than post-check-loops (usually it's
either for-loop or `while {}`).

> 
> > > +
> > > +	return !!alloc_count;
> > > +}
> > > +
> > > +/**
> > > + * iecm_rx_post_buf_desc - Post buffer to bufq descriptor ring

--- 8< ---

> > > +	iecm_rx_buf_hw_update(bufq, bufq->next_to_alloc &
> > > +~(bufq->rx_buf_stride - 1));
> > 
> > 87-cols line.
> > Please test all your patches with `checkpatch --strict --codespell`.
> > 
> 
> Just an FYI, all of these patches do mostly pass checkpatch since otherwise (except for net apparently) in the kernel 100 cols are now acceptable. You must also add `--max-line-length=80` to get a warning about 80 cols now.

Netdev maintainers still prefer 79/80 (and so do I), thus pointing
out all those long lines here.
Wasn't sure about checkpatch detecting whether it's a networking
patch and applying the corresponding settings since I can't recall
my last time going past 79.

> 
> > > +}
> > > +
> > > +/**
> > > + * iecm_rx_buf_alloc_all - Allocate memory for all buffer resources

--- 8< ---

> > > +	if (bufq) {
> > > +		rxq->size = rxq->desc_count *
> > > +			sizeof(struct virtchnl2_splitq_rx_buf_desc);
> > > +	} else {
> > > +		rxq->size = rxq->desc_count *
> > > +			sizeof(union virtchnl2_rx_desc);
> > > +	}
> > 
> > Oneliners, braces are unneeded.
> > 
> 
> Keeping because multi-line with line wrap.

Please see my previous messages.

> 
> > For counting the array sizes it's required to use array_size():

--- 8< ---

> > > +
> > > +	rxq->next_to_alloc = 0;
> > > +	rxq->next_to_clean = 0;
> > > +	rxq->next_to_use = 0;
> > 
> > You allocate rxq with kzalloc() (or derivative) IIRC, 'z'-versions zero the memory
> > before returning. These initializers are redundant.
> > 
> 
> This is allocating descriptors which can change.  If we change the descriptor ring it's probably a good idea to reset the queue indexes.

Again, this function takes a freshly zero-allocated ring structure.
Those are zeroes in 100% cases. Please correct if I'm wrong.

Also, while searching for iecm_rx_desc_alloc() usages, I spotted
that both it and iecm_rx_desc_alloc_all() are used only inside the
same file which they're declared in. So they should be static.
Please correct if not.

> 
> > > +	set_bit(__IECM_Q_GEN_CHK, rxq->flags);
> > > +
> > > +	/* Allocate buffers for a rx queue if the q_model is single OR if it

--- 8< ---

> > > --
> > > 2.33.0
> > 
> > Thanks,
> > Al

Thanks,
Al

^ permalink raw reply	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 14/19] iecm: implement singleq napi_poll
  2022-02-03  1:45     ` Brady, Alan
@ 2022-02-03 19:05       ` Alexander Lobakin
  0 siblings, 0 replies; 89+ messages in thread
From: Alexander Lobakin @ 2022-02-03 19:05 UTC (permalink / raw)
  To: intel-wired-lan

From: Alan Brady <alan.brady@intel.com>
Date: Thu, 3 Feb 2022 02:45:14 +0100

> > -----Original Message-----
> > From: Lobakin, Alexandr <alexandr.lobakin@intel.com>
> > Sent: Friday, January 28, 2022 9:58 AM
> > To: Brady, Alan <alan.brady@intel.com>
> > Cc: Lobakin, Alexandr <alexandr.lobakin@intel.com>; intel-wired-
> > lan at lists.osuosl.org; Burra, Phani R <phani.r.burra@intel.com>; Chittim,
> > Madhu <madhu.chittim@intel.com>; Linga, Pavan Kumar
> > <pavan.kumar.linga@intel.com>
> > Subject: [Intel-wired-lan] [PATCH net-next 14/19] iecm: implement singleq
> > napi_poll
> > 
> > > From: Alan Brady <alan.brady@intel.com>
> > > Date: Thu, 27 Jan 2022 16:10:04 -0800
> > >
> > > This adds everything we do the more traditional singleq model data path.
> > >
> > > Signed-off-by: Phani Burra <phani.r.burra@intel.com>
> > > Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
> > > Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
> > > Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
> > > Signed-off-by: Alan Brady <alan.brady@intel.com>
> > > ---
> > >  drivers/net/ethernet/intel/iecm/iecm_lib.c    |    2 +-
> > >  .../ethernet/intel/iecm/iecm_singleq_txrx.c   | 1208
> > ++++++++++++++++-
> > >  drivers/net/ethernet/intel/include/iecm.h     |    1 +
> > >  .../net/ethernet/intel/include/iecm_txrx.h    |   31 +
> > >  4 files changed, 1237 insertions(+), 5 deletions(-)
> > >

--- 8< ---

> > > +		/* align size to end of page */
> > > +		max_data += -dma & (IECM_TX_MAX_READ_REQ_SIZE - 1);
> > 
> > Here applies the same I said for splitq before, this code is counter-intuitive.
> > 
> 
> I'm failing to find a matching comment for iecm_tx_splitq_map but I'm sure we're open to suggestion how to make it better.

As I said in my reply to 11/19, I don't get the logics here at all,
so I kindly asked to explain it to me.
Since I don't get it, I'm not able to provide any suggestions.

> 
> > > +		tx_desc->buf_addr = cpu_to_le64(dma);

--- 8< ---

> > > +	do {
> > > +		struct iecm_base_tx_desc *eop_desc =
> > > +			(struct iecm_base_tx_desc *)tx_buf-
> > >next_to_watch;
> > 
> > 		struct iecm_base_tx_desc *eop_desc;
> > 
> > 		eop_desc = (typeof(*eop_desc))tx_buf->next_to_watch;
> > 
> 
> Again not sure I see the benefit to prefer assigning it's own line over letting it wrap.  Suggestion as written is wrong also.

Declaration, then assignment is a normal practice.
Assignment with a line-wrap is an exception only for the lines which
can't be composed any other way. Here you have at least two:

		struct iecm_base_tx_desc *eop_desc;

		/* This is fixed variant from my initial reply, I apologize
		 * for the excessive '*'.
		 */
		eop_desc = (typeof(eop_desc))tx_buf->next_to_watch;

		/* More classic variant, fits into 79 as well */
		eop_desc = (struct iecm_base_tx_desc *)tx_buf->next_to_watch;

Breaking declaration and assignment into two separate lines is the
first choice when a line exceeds the char limit.

> 
> > > +
> > > +		/* if next_to_watch is not set then no work pending */

--- 8< ---

> > > +	status0 = rx_desc->flex_nic_wb.status_error0;
> > > +	if (status0 &
> > > +
> > 	cpu_to_le16(BIT(VIRTCHNL2_RX_FLEX_DESC_STATUS0_RSS_VALID_
> > S))) {
> > > +		u32 hash = le32_to_cpu(rx_desc->flex_nic_wb.rss_hash);
> > > +
> > > +		skb_set_hash(skb, hash, iecm_ptype_to_htype(decoded));
> > > +	}
> > 
> > 	if (!status)
> > 		return;
> > 
> > -1 indent level.
> > 
> 
> if (!(status0 & cpu_to_le16(BIT(VIRTCHNL2_RX_FLEX_DESC_STATUS0_RSS_VALID_S))))
>     return;
> 
> skb_set_hash(...);

hash = 
skb_set_hash()

There are two lines there.

> 
> vs.
> 
> if (status0 & cpu_to_le16(BIT(VIRTCHNL2_RX_FLEX_DESC_STATUS0_RSS_VALID_S)))
>     skb_set_hash(....);
> 
> Doesn't seem any better to me, in fact it seems worse.

Perhaps due to that train starting from cpu_to_le16 should be hidden
into a compact definition, like

#define IECM_RX_DESC_HASH_BIT						  \
	BIT(VIRTCHNL2_RX_FLEX_DESC_STATUS0_RSS_VALID_S)));

#define __iecm_rx_desc_hash_present(status0)				  \
	!!((status0) & cpu_to_le16(IECM_RX_DESC_HASH_BIT))

#define iecm_rx_desc_hash_present(rx_desc)				  \
	__iecm_rx_desc_hash_present((rx_desc)->flex_nic_wb.status_error0)

...

then either way (`if (hash)` or `if (!hash)`) starts working.

	if (!(rx_q->vport->netdev->features & NETIF_F_RXHASH))
		return;

	if (iecm_rx_desc_hash_present(rx_desc)) {
		u32 hash = le32_to_cpu(rx_desc->flex_nic_wb.rss_hash);

		skb_set_hash(skb, hash, iecm_ptype_to_htype(decoded));
	}

||

	if (!(rx_q->vport->netdev->features & NETIF_F_RXHASH) ||
	    !iecm_rx_desc_hash_present(rx_desc))
		return;

	hash = le32_to_cpu(rx_desc->flex_nic_wb.rss_hash);
	skb_set_hash(skb, hash, iecm_ptype_to_htype(decoded));

> 
> > > +}
> > > +
> > > +/**
> > > + * iecm_rx_singleq_process_skb_fields - Populate skb header fields

--- 8< ---

> > > +void
> > > +iecm_rx_singleq_process_skb_fields(struct iecm_queue *rx_q, struct
> > sk_buff *skb,
> > > +				   union virtchnl2_rx_desc *rx_desc,
> > > +				   u16 ptype)
> > > +{
> > > +	struct iecm_rx_ptype_decoded decoded =
> > > +					rx_q->vport->rx_ptype_lkup[ptype];
> > 
> > Declare, then assign to avoid wraps.
> > 
> 
> I'm just not seeing the benefit in doing that.  If 'net' tree is concerned with wrapping I would assume they would move to 100 cols with the rest of the kernel.

Everyone is concerned with wrapping.
But one thing is to have a function call wrapped by arguments:

	ret = some_long_function_name(argument1,
				      ARG2);

and completely different -- line breaks, when no alignment is
possible:

	some_really_long_variable =
		some_long_function_name();

At least I can tell for sure that declaration + assignment is way
more preferred over such line breaks for not altering readability
in any way.

> 
> > > +
> > > +	/* modifies the skb - consumes the enet header */

--- 8< ---

> > > +static bool iecm_rx_singleq_recycle_buf(struct iecm_queue *rxq,
> > > +					struct iecm_rx_buf *rx_buf)
> > > +{
> > > +	struct iecm_page_info *page_info =
> > > +			&rx_buf->page_info[rx_buf->page_indx];
> > 
> > Declare, then assign to avoid wraps.
> >
> 
> Will not fix.

The very same questions may come from the upstream maintainers.
The less such trivial places there will be, the better for everyone
I believe? Especially for the maintainers since they review tons of
code daily and aren't happy to see simple style issues again and
again.

>  
> > > +

--- 8< ---

> > > --
> > > 2.33.0
> > 
> > Thanks,
> > Al

Thanks,
Al

^ permalink raw reply	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 15/19] iecm: implement ethtool callbacks
  2022-02-03  2:13     ` Brady, Alan
@ 2022-02-03 19:54       ` Alexander Lobakin
  0 siblings, 0 replies; 89+ messages in thread
From: Alexander Lobakin @ 2022-02-03 19:54 UTC (permalink / raw)
  To: intel-wired-lan

From: Alan Brady <alan.brady@intel.com>
Date: Thu, 3 Feb 2022 03:13:41 +0100

> > -----Original Message-----
> > From: Lobakin, Alexandr <alexandr.lobakin@intel.com>
> > Sent: Friday, January 28, 2022 10:14 AM
> > To: Brady, Alan <alan.brady@intel.com>
> > Cc: Lobakin, Alexandr <alexandr.lobakin@intel.com>; intel-wired-
> > lan at lists.osuosl.org; Burra, Phani R <phani.r.burra@intel.com>; Chittim,
> > Madhu <madhu.chittim@intel.com>; Linga, Pavan Kumar
> > <pavan.kumar.linga@intel.com>
> > Subject: Re: [Intel-wired-lan] [PATCH net-next 15/19] iecm: implement
> > ethtool callbacks
> > 
> > From: Alan Brady <alan.brady@intel.com>
> > Date: Thu, 27 Jan 2022 16:10:05 -0800
> > 
> > > This does everything needed to handle ethtool ops minus a few stubs
> > > for cloud filters and other advanced features which will be added in
> > > later in this series.
> > >
> > > Signed-off-by: Phani Burra <phani.r.burra@intel.com>
> > > Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
> > > Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
> > > Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
> > > Signed-off-by: Alan Brady <alan.brady@intel.com>
> > > ---
> > >  drivers/net/ethernet/intel/iecm/Makefile      |    1 +
> > >  .../net/ethernet/intel/iecm/iecm_ethtool.c    | 1325
> > +++++++++++++++++
> > >  drivers/net/ethernet/intel/iecm/iecm_lib.c    |   11 +-
> > >  drivers/net/ethernet/intel/include/iecm.h     |    1 +
> > >  4 files changed, 1337 insertions(+), 1 deletion(-)  create mode
> > > 100644 drivers/net/ethernet/intel/iecm/iecm_ethtool.c
> > >
> > > diff --git a/drivers/net/ethernet/intel/iecm/Makefile
> > > b/drivers/net/ethernet/intel/iecm/Makefile
> > > index 205d6f2b436a..fe2ed403d35c 100644
> > > --- a/drivers/net/ethernet/intel/iecm/Makefile
> > > +++ b/drivers/net/ethernet/intel/iecm/Makefile
> > > @@ -15,6 +15,7 @@ iecm-y := \
> > >  	iecm_virtchnl.o \
> > >  	iecm_txrx.o \
> > >  	iecm_singleq_txrx.o \
> > > +	iecm_ethtool.o \
> > >  	iecm_controlq.o \
> > >  	iecm_controlq_setup.o \
> > >  	iecm_main.o
> > > diff --git a/drivers/net/ethernet/intel/iecm/iecm_ethtool.c
> > > b/drivers/net/ethernet/intel/iecm/iecm_ethtool.c
> > > new file mode 100644
> > > index 000000000000..32d905fb1bb6
> > > --- /dev/null
> > > +++ b/drivers/net/ethernet/intel/iecm/iecm_ethtool.c
> > > @@ -0,0 +1,1325 @@
> > > +// SPDX-License-Identifier: GPL-2.0-only
> > > +/* Copyright (C) 2019 Intel Corporation */
> > > +
> > > +#include "iecm.h"
> > > +
> > > +/**
> > > + * iecm_get_rxnfc - command to get RX flow classification rules
> > > + * @netdev: network interface device structure
> > > + * @cmd: ethtool rxnfc command
> > > + * @rule_locs: pointer to store rule locations
> > > + *
> > > + * Returns Success if the command is supported.
> > > + */
> > > +static int iecm_get_rxnfc(struct net_device *netdev, struct ethtool_rxnfc
> > *cmd,
> > > +			  u32 __always_unused *rule_locs)
> > 
> > Kernel Kbuild system tell compilers to not complain on unused function
> > arguments.
> > It's pointless to add __always_unused here.
> > 
> 
> Sparse does complain about it (e.g. make ... C=2) and it's present in other (non-Intel) network drivers. I think I need a better argument to remove it.

No, it doesn't.
No, it's not.

Mellanox and ChelsIO almost never mark unused arguments, see e.g.
to [0] and [1] for reference.
And their drivers build cleanly with the latest sparse 0.6.4.

I've been using it for more than two years already and can't recall
a single episode when it would've complained about unused arguments.
If you talk about OOT, please clearly state this, to not confuse me
and other reviewers.

Marking unused arguments as __always_unused is at least a
deprecation and is not recommended for any new code. Kbuild (kernel
build system) explicitly sets -Wno-unused-parameter ([2]) to disable
this warning when building with -Wextra (W=1+).
This is due to that almost any driver has at least one (usually much
more) callback, and if we would've marked every unused argument with
this attribute, there would've been 1.5 times more lines of code.

Is this enough?

> 
> > > +{
> > > +	struct iecm_vport *vport = iecm_netdev_to_vport(netdev);

--- 8< ---

> > > +	if (adapter->virt_ver_maj < VIRTCHNL_VERSION_MAJOR_2) {
> > > +		err = adapter->dev_ops.vc_ops.add_queues(vport,
> > > +							 num_req_tx_q, 0,
> > > +							 num_req_rx_q, 0);
> > > +	} else {
> > > +		err = iecm_initiate_soft_reset(vport,
> > __IECM_SR_Q_CHANGE);
> > > +	}
> > 
> > One-liners, no need for braces.
> > 
> 
> The `if` exists across multiple lines so we would prefer to keep braces. As such the 'else' also gets them.

Please refer to my previous messages. There shouldn't be any braces,
unless they're required for code to work properly (except for
symmetrical if-else).

> 
> > > +
> > > +	if (err) {

--- 8< ---

> > > +
> > > +	is_reset_needed =
> > > +		!!(test_bit(__IECM_PRIV_FLAGS_HDR_SPLIT,
> > change_flags));
> > 
> > Shorter name would allow avoiding line break.
> > 
> 
> For IECM_PRIV_FLAGS_HDR_SPLIT? Sure we're open to suggestion if you have a name that communicates the same level of information effectively.

For `is_reset_needed`. HDR_SPLIT is perfect to me.

> 
> > > +
> > > +	/* Issue reset to cause things to take effect, as additional bits

--- 8< ---

> > > +	case ETH_SS_PRIV_FLAGS:
> > > +		iecm_get_priv_flag_strings(netdev, data);
> > > +		break;
> > > +	default:
> > > +		break;
> > 
> > Equivalent to not having a 'default' case.
> > 
> 
> Yes static tools will complain about uncaught switch without it.

Such as? Can I get some more details please?

Is this again for OOT or for the upstream kernel as well? I think I
can recall some initiatives in the past to enable -Wswitch-default,
but all of them were cancelled due to that it's pointless to have
`default` just to satisfy some checkers and, unlike e.g.
`fallthrough`, this has a very little benefit.
Now you can get it only on W=3 level which nobody takes seriosly.

> 
> > > +	}
> > > +}
> > > +
> > > +/**
> > > + * iecm_get_sset_count - Get length of string set

--- 8< ---

> > > +
> > > +		return IECM_PORT_STATS_LEN +
> > > +		       (IECM_TX_QUEUE_STATS_LEN * max_q) +
> > > +			(IECM_RX_QUEUE_STATS_LEN * max_q);
> > > +	} else if (sset == ETH_SS_PRIV_FLAGS) {
> > > +		return IECM_PRIV_FLAGS_STR_LEN;
> > > +	} else {
> > > +		return -EINVAL;
> > 
> > Check for this at first and save 1 level of indentation for ETH_SS_STATS.
> > 
> 
> So you would have it something like:
> 
> if (sset != ETH_SS_STATS && sset != ETH_SS_PRIV_FLAGS)
>    return -EINVAL
> 
> if (sset == ETH_SS_PRIV_FLAGS)
>    return IECM_PRIV_FLAGS_STR_LEN
> 
> /* if sset code */
> 
> This to me seems less readable because now the reader has to identify the bottom half of the function is actually for the SSET case whereas the way it is written currently, it's very explicit about what we're doing for what. I commend the intent to shave off indents where possible but many of these indent suggestions are teetering on excessive and sacrificing readability to save a tab.

You guessed a correct comment block here, if you feel that some code
is not super-intuitive, you can always add a short comment to give
a hint.

What more important for readability is to keep the code as flat as
possible, otherwise the main eye focus should be moved to the right,
plus 79-col limits starts wrapping more and more lines which doesn't
improve readability either.
It means that the longest code blocks should usually have a single
Tab indent.

I'm not speaking for this particular function right now -- I'm fine
with having all of it at 2 Tabs, it's just a big comment block which
makes it look like a big chunk of code. But at least make it a
switch-case then I guess?

> 
> > > +	}
> > > +}
> > > +
> > > +/**
> > > + * iecm_add_one_ethtool_stat - copy the stat into the supplied buffer

--- 8< ---

> > > +	do {
> > > +		start = u64_stats_fetch_begin_irq(&vport-
> > >port_stats.stats_sync);
> > > +		for (i = 0; i < size; i++) {
> > > +			iecm_add_one_ethtool_stat(&(*data)[i], vport,
> > > +
> > &iecm_gstrings_port_stats[i]);
> > > +		}
> > 
> > Redundant braces.
> > 
> 
> I believe this is multi line with wrapping.  Will not fix.

Explained previously.

> 
> > > +	} while (u64_stats_fetch_retry_irq(&vport->port_stats.stats_sync,
> > > +start));
> > > +
> > > +	*data += size;
> > > +}
> > > +
> > > +/**
> > > + * iecm_get_ethtool_stats - report device statistics

--- 8< ---

> > > +void iecm_set_ethtool_ops(struct net_device *netdev) {
> > > +	netdev->ethtool_ops = &iecm_ethtool_ops; }
> > 
> > Declaring @iecm_ethtool_ops as external and directly assigning it in
> > iecm_cfg_netdev() would result in smaller code size than this.
> > 
> 
> It seems trivial either way, but I'm having a hard time justifying this function, so will fix.

Some people believe ops structs should be not only const, but also
static. I don't really get why, but it's somewhat common across
different subsystems.

There's nothing bad in having ethtool_ops global, at the same time
global functions really occupy much more space than global
variables, so just a suggestion here to avoid that.

> 
> > > diff --git a/drivers/net/ethernet/intel/iecm/iecm_lib.c

--- 8< ---

> > > --
> > > 2.33.0

I'm sorry, I'll get back to your replies on patch #16 and further in about
14 hours.

> > 
> > Thanks,
> > Al

[0] https://elixir.bootlin.com/linux/v5.17-rc2/source/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c#L1736
[1] https://elixir.bootlin.com/linux/v5.17-rc2/source/drivers/net/ethernet/mellanox/mlx5/core/en/xsk/tx.c#L10
[2] https://elixir.bootlin.com/linux/v5.17-rc2/source/scripts/Makefile.extrawarn#L25

Thanks,
Al

^ permalink raw reply	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 16/19] iecm: implement flow director
  2022-02-03  2:41     ` Brady, Alan
@ 2022-02-04 10:08       ` Alexander Lobakin
  0 siblings, 0 replies; 89+ messages in thread
From: Alexander Lobakin @ 2022-02-04 10:08 UTC (permalink / raw)
  To: intel-wired-lan

From: Alan Brady <alan.brady@intel.com>
Date: Thu, 3 Feb 2022 03:41:26 +0100

> > -----Original Message-----
> > From: Lobakin, Alexandr <alexandr.lobakin@intel.com>
> > Sent: Friday, January 28, 2022 11:04 AM
> > To: Brady, Alan <alan.brady@intel.com>
> > Cc: Lobakin, Alexandr <alexandr.lobakin@intel.com>; Wang, Haiyue
> > <haiyue.wang@intel.com>; Burra, Phani R <phani.r.burra@intel.com>;
> > Chittim, Madhu <madhu.chittim@intel.com>; Linga, Pavan Kumar
> > <pavan.kumar.linga@intel.com>; intel-wired-lan at lists.osuosl.org
> > Subject: Re: [Intel-wired-lan] [PATCH net-next 16/19] iecm: implement flow
> > director
> > 
> > From: Alan Brady <alan.brady@intel.com>
> > Date: Thu, 27 Jan 2022 16:10:06 -0800
> > 
> > > From: Haiyue Wang <haiyue.wang@intel.com>
> > >
> > > This adds everthing needed to do flow director commands.
> > >
> > > Signed-off-by: Haiyue Wang <haiyue.wang@intel.com>
> > > ---
> > >  .../net/ethernet/intel/iecm/iecm_ethtool.c    |   17 +-
> > >  drivers/net/ethernet/intel/iecm/iecm_lib.c    | 1528
> > ++++++++++++++++-
> > >  .../net/ethernet/intel/iecm/iecm_virtchnl.c   |  119 ++
> > >  drivers/net/ethernet/intel/include/iecm.h     |  112 ++
> > >  4 files changed, 1770 insertions(+), 6 deletions(-)
> > >

--- 8< ---

> > > +	switch (fltr->flow_type) {
> > > +	case IECM_FDIR_FLOW_IPV4_TCP:
> > > +	case IECM_FDIR_FLOW_IPV4_UDP:
> > > +	case IECM_FDIR_FLOW_IPV4_SCTP:
> > > +		dev_info(dev, "Rule ID: %u dst_ip: %pI4 src_ip %pI4 %s:
> > dst_port %hu src_port %hu\n",
> > > +			 fltr->loc,
> > > +			 &fltr->ip_data.v4_addrs.dst_ip,
> > > +			 &fltr->ip_data.v4_addrs.src_ip,
> > > +			 proto,
> > > +			 ntohs(fltr->ip_data.dst_port),
> > > +			 ntohs(fltr->ip_data.src_port));
> > 
> > Some of those can fit into previous line, there's no need to put
> > each argument onto separate one.
> > 
> 
> It technically can fit on one line, but it's much easier for humans to parse what's going where the way it's written now.

Not sure there's a choice here, usually it's preferred to compress
lines if there's a possibility.

> 
> > > +		break;
> > > +	case IECM_FDIR_FLOW_IPV4_AH:

--- 8< ---

> > > +	fdir_config = &adapter->config_data.fdir_config;
> > > +	list_for_each_entry(rule, &fdir_config->fdir_fltr_list, list)
> > > +		if (rule->loc == loc)
> > > +			return rule;
> > 
> > Here's a good example that a single `if` statement shouldn't be
> > placed into a pair of braces.
> > 
> 
> You're right it does need some braces on the list_for_each, will fix.

I said the opposite, please don't present your opinion as mine. I'm
not sure it's a correct thing to do during the review.
As mentioned previously plenty of times, braces are expected only
when they're purely necessary. Your approach doesn't even improve
the readability as you claim.

> 
> > > +
> > > +	return NULL;
> > > +}
> > > +
> > > +/**
> > > + * iecm_fdir_list_add_fltr - add a new node to the flow director filter list

--- 8< ---

> > > + * If the mask is fully set return true. If it is not valid for field return
> > > + * false.
> > > + */
> > > +static bool iecm_is_mask_valid(u64 mask, u64 field)
> > > +{
> > > +	return (mask & field) == field;
> > > +}
> > 
> > That is something really basic and should at least be placed
> > somewhere in the headers and used module-wide.
> > 
> 
> I'm not convinced it makes sense to do that if it's not actually being used module wide. I'm pretty sure this is only validating user input for flow director filters.

`(mask & field) == field` pattern is used widely across iecm code.
This function simply won't pass the review. If it reflects FD logics
somehow, this should be explained in the kdoc block, so there'll be
less questions why it's here at all. For now it looks like a generic
function, not FD-specific one.
Anyway, I've just highlighted all references to this function and
it's used only *once* throughout the code. So it's a pure overkill
and should be just open-coded there.

> 
> > > +
> > > +/**
> > > + * iecm_parse_rx_flow_user_data - deconstruct user-defined data

--- 8< ---

> > > +enum iecm_fdir_flow_type {
> > > +	/* NONE - used for undef/error */
> > > +	IECM_FDIR_FLOW_NONE = 0,
> > 
> > Enums always start with 0 unless other value specified, this is a
> > bit excessive.
> > 
> 
> AFAIK this is the normal thing to do in Linux kernel.  In cases where the value actually matters, as is here, it's better to be explicit even though yes you're right it should be getting the default value of zero.

Does it? It mostly only matters when it's being passed to HW anyhow,
but I'm not sure it's the case here.
Anyways, I admit it's rather a personal preference, so I don't mind.

> 
> E.g. in kernel/sched/sched.h you can find:
> 
> /* The regions in numa_faults array from task_struct */
> enum numa_faults_stats {
>         NUMA_MEM = 0,
>         NUMA_CPU,
>         NUMA_MEMBUF,
>         NUMA_CPUBUF
> };
> 
> and probably many more.
> 
> > > +	IECM_FDIR_FLOW_IPV4_TCP,

--- 8< ---

> > > --
> > > 2.33.0
> > 
> > Thanks,
> > Al

Thanks,
Al

^ permalink raw reply	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 18/19] iecm: add advanced rss
  2022-02-03  2:55     ` Brady, Alan
  2022-02-03 10:46       ` Maciej Fijalkowski
@ 2022-02-04 10:22       ` Alexander Lobakin
  1 sibling, 0 replies; 89+ messages in thread
From: Alexander Lobakin @ 2022-02-04 10:22 UTC (permalink / raw)
  To: intel-wired-lan

From: Alan Brady <alan.brady@intel.com>

From: Alan Brady <alan.brady@intel.com>
Date: Thu, 3 Feb 2022 03:55:57 +0100

> > -----Original Message-----
> > From: Lobakin, Alexandr <alexandr.lobakin@intel.com>
> > Sent: Friday, January 28, 2022 11:54 AM
> > To: Brady, Alan <alan.brady@intel.com>
> > Cc: Lobakin, Alexandr <alexandr.lobakin@intel.com>; Wang, Haiyue
> > <haiyue.wang@intel.com>; intel-wired-lan at lists.osuosl.org; Burra, Phani R
> > <phani.r.burra@intel.com>; Chittim, Madhu <madhu.chittim@intel.com>;
> > Linga, Pavan Kumar <pavan.kumar.linga@intel.com>
> > Subject: Re: [Intel-wired-lan] [PATCH net-next 18/19] iecm: add advanced
> > rss
> > 
> > From: Alan Brady <alan.brady@intel.com>
> > Date: Thu, 27 Jan 2022 16:10:08 -0800
> > 
> > > From: Haiyue Wang <haiyue.wang@intel.com>
> > >
> > > Continuing with advanced features this implements what's needed to do
> > > advanced rss.
> > 
> > I'm sorry for not mentioned it before, but most of the series'
> > commit messages are poor and would probably get rejected upstream.
> > If they were explaining at least some very basics, it would be better. Even
> > better if there were explanations of some tricky code that happens time to
> > time.
> > 
> > >
> > > Signed-off-by: Haiyue Wang <haiyue.wang@intel.com>
> > > ---
> > >  drivers/net/ethernet/intel/iecm/iecm_lib.c    | 547
> > ++++++++++++++++++
> > >  .../net/ethernet/intel/iecm/iecm_virtchnl.c   |  71 +++
> > >  drivers/net/ethernet/intel/include/iecm.h     |  73 +++
> > >  3 files changed, 691 insertions(+)
> > >
> > > diff --git a/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > > b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > > index d11413cb438c..baa1e312652a 100644
> > > --- a/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > > +++ b/drivers/net/ethernet/intel/iecm/iecm_lib.c
> > > @@ -1013,6 +1013,52 @@ static void iecm_remove_vlan_filters(struct
> > iecm_vport *vport)
> > >  	}
> > >  }
> > >
> > > +/**
> > > + * iecm_remove_adv_rss_cfgs - Remove all RSS configuration
> > > + * @vport: vport structure
> > > + */
> > > +static void iecm_remove_adv_rss_cfgs(struct iecm_vport *vport) {
> > > +	struct iecm_adapter *adapter = vport->adapter;
> > > +
> > > +	if (!iecm_is_cap_ena(adapter, IECM_OTHER_CAPS,
> > VIRTCHNL2_CAP_ADV_RSS))
> > > +		return;
> > > +
> > > +	if (!list_empty(&adapter->config_data.adv_rss_list)) {
> > > +		struct iecm_adv_rss *rss;
> > > +
> > > +		spin_lock_bh(&adapter->adv_rss_list_lock);
> > > +		list_for_each_entry(rss, &adapter-
> > >config_data.adv_rss_list,
> > > +				    list) {
> > > +			rss->remove = true;
> > > +		}
> > 
> > Redundant braces arond an one-liner.
> > 
> 
> Maybe will fix.

It's a pure error here.

> 
> > > +		spin_unlock_bh(&adapter->adv_rss_list_lock);
> > > +		iecm_send_add_del_adv_rss_cfg_msg(vport, false);
> > > +	}
> > 
> > Invert the condition for -1 indent level.
> > 
> 
> Will fix.
> 
> > > +}
> > > +
> > > +/**
> > > + * iecm_del_all_adv_rss_cfgs - delete all RSS configuration

--- 8< ---

> > > --
> > > 2.33.0
> > 
> > Thanks,
> > Al

Al

^ permalink raw reply	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 19/19] idpf: introduce idpf driver
  2022-02-03  3:07     ` Brady, Alan
@ 2022-02-04 10:35       ` Alexander Lobakin
  0 siblings, 0 replies; 89+ messages in thread
From: Alexander Lobakin @ 2022-02-04 10:35 UTC (permalink / raw)
  To: intel-wired-lan

From: Alan Brady <alan.brady@intel.com>
Date: Thu, 3 Feb 2022 04:07:10 +0100

> > -----Original Message-----
> > From: Lobakin, Alexandr <alexandr.lobakin@intel.com>
> > Sent: Friday, January 28, 2022 12:08 PM
> > To: Brady, Alan <alan.brady@intel.com>
> > Cc: Lobakin, Alexandr <alexandr.lobakin@intel.com>; intel-wired-
> > lan at lists.osuosl.org; Burra, Phani R <phani.r.burra@intel.com>; Chittim,
> > Madhu <madhu.chittim@intel.com>; Linga, Pavan Kumar
> > <pavan.kumar.linga@intel.com>
> > Subject: Re: [Intel-wired-lan] [PATCH net-next 19/19] idpf: introduce idpf
> > driver
> > 
> > From: Alan Brady <alan.brady@intel.com>
> > Date: Thu, 27 Jan 2022 16:10:09 -0800
> > 
> > > This adds the idpf driver which uses the iecm module to provide common
> > > functionality. Device specific behavior and registers are defined here
> > > and handed off to iecm which takes over the rest of the flow.
> > 
> > Ok I missed that before, so I say it now.
> > Multi-function networking devices (Ethernet, SFs, VF representors, RDMA,
> > storage offload etc.) nowadays kinda *must* be based on top of auxiliary
> > bus. Otherwise, maintaining of hundreds a direct call with recursive
> > dependencies between modules and stuff will become a burden.
> > All of the mentioned functionality will be added to the driver(s), that's a
> > fact, and as these are new drivers, it's way better to start off the right way
> > now than to bug your mind on how to refactor this later.
> > 
> 
> I suspect a refactor now will actually be more painful than later for other reasons and I believe we have other motivations for not using aux bus in this. It is however worth considering but we need some time to discuss. Will reply with something firm after some internal discussion.

The earlier the easier, it's always more difficult to remodel stuff
later than during the initial preparation. It then will start absorb
changes from both the community and a pool of Intel employees,
iecm/idpf maintainers will have to support the accepted in-tree
driver along with the internal dev tree, and nothing from those
eases any core changes.
I heard the phrase "we have a motivation" several times, but nobody
explained it to me yet (thus I'm kindly asking for this once again),
so I speak only from the development and feature-richness
perspective.

> 
> > >
> > > Signed-off-by: Phani Burra <phani.r.burra@intel.com>
> > > Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
> > > Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
> > > Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
> > > Signed-off-by: Alan Brady <alan.brady@intel.com>
> > > ---
> > >  .../device_drivers/ethernet/intel/idpf.rst    |  47 ++++++
> > >  drivers/net/ethernet/intel/Kconfig            |  16 ++
> > >  drivers/net/ethernet/intel/Makefile           |   1 +
> > >  drivers/net/ethernet/intel/idpf/Makefile      |  15 ++
> > >  drivers/net/ethernet/intel/idpf/idpf_dev.h    |  17 +++
> > >  drivers/net/ethernet/intel/idpf/idpf_devids.h |  10 ++
> > >  drivers/net/ethernet/intel/idpf/idpf_main.c   | 140
> > ++++++++++++++++++
> > >  drivers/net/ethernet/intel/idpf/idpf_reg.c    | 130 ++++++++++++++++
> > >  .../ethernet/intel/include/iecm_lan_pf_regs.h | 131 ++++++++++++++++
> > >  9 files changed, 507 insertions(+)
> > >  create mode 100644
> > > Documentation/networking/device_drivers/ethernet/intel/idpf.rst
> > >  create mode 100644 drivers/net/ethernet/intel/idpf/Makefile
> > >  create mode 100644 drivers/net/ethernet/intel/idpf/idpf_dev.h
> > >  create mode 100644 drivers/net/ethernet/intel/idpf/idpf_devids.h
> > >  create mode 100644 drivers/net/ethernet/intel/idpf/idpf_main.c
> > >  create mode 100644 drivers/net/ethernet/intel/idpf/idpf_reg.c
> > >  create mode 100644
> > > drivers/net/ethernet/intel/include/iecm_lan_pf_regs.h
> > >

--- 8< ---

> > > --
> > > 2.33.0
> > 
> > Thanks,
> > Al

Al

^ permalink raw reply	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 13/19] iecm: implement splitq napi_poll
  2022-02-03  1:07     ` Brady, Alan
@ 2022-02-04 11:50       ` Alexander Lobakin
  0 siblings, 0 replies; 89+ messages in thread
From: Alexander Lobakin @ 2022-02-04 11:50 UTC (permalink / raw)
  To: intel-wired-lan

From: Alan Brady <alan.brady@intel.com>
Date: Thu, 3 Feb 2022 02:07:08 +0100

> > -----Original Message-----
> > From: Lobakin, Alexandr <alexandr.lobakin@intel.com>
> > Sent: Friday, January 28, 2022 9:39 AM
> > To: Brady, Alan <alan.brady@intel.com>
> > Cc: Lobakin, Alexandr <alexandr.lobakin@intel.com>; intel-wired-
> > lan at lists.osuosl.org; Burra, Phani R <phani.r.burra@intel.com>; Chittim, Madhu
> > <madhu.chittim@intel.com>; Linga, Pavan Kumar
> > <pavan.kumar.linga@intel.com>
> > Subject: Re: [Intel-wired-lan] [PATCH net-next 13/19] iecm: implement splitq
> > napi_poll
> > 
> > From: Alan Brady <alan.brady@intel.com>
> > Date: Thu, 27 Jan 2022 16:10:03 -0800
> > 
> > > This adds everything we need to actually receive packets and process spent
> > > buffers using the splitq model. This contrasts to more traditional queueing
> > > models by essentially splitting a normal queue of descriptors and mapped
> > > buffers into separate queues. This allows us to deal with both more
> > > efficiently and also allows us to implement asymmetric queuing setups where
> > > you have multiple completion queues associated with a single buffer queue.
> > >
> > > Signed-off-by: Phani Burra <phani.r.burra@intel.com>
> > > Signed-off-by: Joshua Hay <joshua.a.hay@intel.com>
> > > Signed-off-by: Madhu Chittim <madhu.chittim@intel.com>
> > > Signed-off-by: Pavan Kumar Linga <pavan.kumar.linga@intel.com>
> > > Signed-off-by: Alan Brady <alan.brady@intel.com>
> > > ---
> > >  drivers/net/ethernet/intel/iecm/iecm_txrx.c   | 1468 ++++++++++++++++-
> > >  drivers/net/ethernet/intel/include/iecm.h     |    4 +
> > >  .../net/ethernet/intel/include/iecm_txrx.h    |   20 +
> > >  3 files changed, 1490 insertions(+), 2 deletions(-)
> > >

--- 8< ---

> > > +	bi = IECM_SPLITQ_RX_BI_DESC(refillq, nta);
> > > +	/* store the buffer ID and the SW maintained GEN bit to the refillq */
> > > +	*bi = ((buf_id << IECM_RX_BI_BUFID_S) & IECM_RX_BI_BUFID_M) |
> > > +	      (!!(test_bit(__IECM_Q_GEN_CHK, refillq->flags)) <<
> > > +	       IECM_RX_BI_GEN_S);
> > 
> > Please use FIELD_GET() and FIELD_PREP() for masks. This won't pass
> > the maintainers.
> > 
> 
> We've never had a problem before, I'm assuming these are new? Will check.

Ok, maybe "won't pass" is not quite correct, but at least "can
provoke questions". It depends on a reviewer, e.g. I generally
discourage those when it coule be just

	*bi = FIELD_PREP(IECM_RX_BI_BUFID_M, buf_id) |
	      FIELD_PREP(IECM_RX_BI_GEN_M,
			 test_bit(__IECM_Q_GEN_CHK, refillq->flags));

or even

(somewhere near the _M/_S definitions)

#define iecm_rx_bi_prep(gen, id)	\
	(FIELD_PREP(IECM_RX_BI_BUFID_M, id) | FIELD_PREP(IECM_RX_BI_GEN_M, gen))

(inside the function itself)

	*bi = iecm_rx_bi_prep(test_bit(__IECM_Q_GEN_CHK, refillq->flags),
			      buf_id);

Note that there's no `!!` before test_bit() since it's boolean
already.
There's nothing bad in introducing macros and/or static inlines to
make the functions themselves more compact and readable. Those
`(value << SOME_KIND_OF_A_FIELD_S) & SOME_KIND_OF_A_FIELD_M` each
time we need to load or store from/to registers or descriptors don't
make any sense to me.

> 
> > > +
> > > +	nta++;
> > > +	if (unlikely(nta == refillq->desc_count)) {
> > 
> > Could be compressed into one line.
> > 
> 
> Could be, but we'd prefer not to.

Just a personal preference to consider, no pressure at all.

> 
> > > +		nta = 0;
> > > +		change_bit(__IECM_Q_GEN_CHK, refillq->flags);

--- 8< ---

> > 	for (i = ...)
> > 		if (vport->txqs[i].q_id == q_id)
> > 			return tx_q;
> > 
> > No need to create a variable.
> >
> 
> It would actually look like
> 
>  	for (i = ...)
>  		if (vport->txqs[i]->q_id == q_id)
>  			return vport->txqs[i];
> 
> 
> You had another comment about adding a vc_ops variable where it was being used twice.  I'm not seeing a huge difference here and seems like splitting hairs. I think we would prefer to keep this.

I'm usually ok with using something not stored locally up to three
times, so please quote me if I really did that somewhere.
Creating a variable is really pointless here, it involves, apart
from growing the stack, braces and a newline which can be easily
avoided for no cost.

> 
> > > +
> > > +	return NULL;
> > > +}
> > > +
> > > +/**
> > > + * iecm_tx_handle_sw_marker - Handle queue marker packet

--- 8< ---

> > > +
> > > +	/* modifies the skb - consumes the enet header */
> > > +	skb->protocol = eth_type_trans(skb, rxq->vport->netdev);
> > 
> > eth_type_trans() should generally be called *right* before
> > napi_gro_receive() to still have caches warm.
> > 
> 
> I'm pretty sure this happening here because we need to consume header before messing with checksum stuff but I'll have to dig deeper.  Will check.

You can always do skb_pull_inline() or skb_pull() or __skb_pull() to
shift skb->data to the right by ETH_HLEN, and then skb_push() (or
its inline variant) right before eth_type_trans(). They're all +/-
one operation (ptr increment/decrement) and are much cheaper than
the cache misses (esp. on DMA buffers).
Or even just use `skb->data + ETH_HLEN, skb->len - ETH_HLEN` if
there are not much places using them.

> 
> > > +	iecm_rx_splitq_extract_csum_bits(rx_desc, &csum_bits);
> > > +	iecm_rx_csum(rxq, skb, &csum_bits, &decoded);
> > > +	/* process RSS/hash */

--- 8< ---

> > > +static bool iecm_rx_page_is_reserved(struct page *page)
> > > +{
> > > +	return (page_to_nid(page) != numa_mem_id()) ||
> > page_is_pfmemalloc(page);
> > > +}
> > 
> > Please check generic dev_page_is_reusable(), it's almost the same
> > (a bit more optimized).
> > 
> 
> Will check and see if it does what we need.

I introduced it upstream exactly for that purpose and switched all
Intel drivers (and in general all the drivers which use such a
construct) to it, so it *does*.

> 
> > > +
> > > +/**
> > > + * iecm_rx_buf_adjust_pg - Prepare rx buffer for reuse

--- 8< ---

> > > +/**
> > > + * iecm_rx_can_reuse_page - Determine if page can be reused for another rx
> > > + * @rx_buf: buffer containing the page
> > > + *
> > > + * If page is reusable, we have a green light for calling iecm_reuse_rx_page,
> > > + * which will assign the current buffer to the buffer that next_to_alloc is
> > > + * pointing to; otherwise, the dma mapping needs to be destroyed and
> > > + * page freed
> > > + */
> > > +bool iecm_rx_can_reuse_page(struct iecm_rx_buf *rx_buf)

BTW, this is a relatively small, but very hotpath function, please
consider making it static inline. Compilers can uninline it then
if they find it big enough.

> > > +{
> > > +	struct iecm_page_info *page_info = &rx_buf->page_info[rx_buf-
> > >page_indx];
> > > +
> > > +#if (PAGE_SIZE >= 8192)
> > > +	unsigned int last_offset = PAGE_SIZE - rx_buf->buf_size;
> > > +#endif /* PAGE_SIZE < 8192) */
> > > +	unsigned int pagecnt_bias = page_info->pagecnt_bias;
> > > +	struct page *page = page_info->page;
> > > +
> > > +	/* avoid re-using remote pages */
> > > +	if (unlikely(iecm_rx_page_is_reserved(page)))
> > > +		return false;
> > > +
> > > +#if (PAGE_SIZE < 8192)
> > > +	/* if we are only owner of page we can reuse it */
> > > +	if (unlikely((page_count(page) - pagecnt_bias) > 1))
> > > +		return false;
> > > +#else
> > > +	if (rx_buf->page_offset > last_offset)
> > > +		return false;
> > > +#endif /* PAGE_SIZE < 8192) */
> > 
> > Same here 2 times.
> > 
> > > +
> > > +	/* If we have drained the page fragment pool we need to update
> > > +	 * the pagecnt_bias and page count so that we fully restock the
> > > +	 * number of references the driver holds.
> > > +	 */
> > > +	if (unlikely(pagecnt_bias == 1)) {
> > 
> > With 1532 byte frames, this condition will be hit 50% of times. It's
> > definitely not a good place for unlkely().
> > 
> 
> I'm afraid I'm not following here, mind elaborating? Keep in mind the buffer is 4k not 2k on MEV.

4k doesn't make any difference, but I confused reaching the bottom
of the @pagecnt_bias with reaching the end of the page, nevermind.

> 
> > > +		page_ref_add(page, USHRT_MAX - 1);
> > > +		page_info->pagecnt_bias = USHRT_MAX;
> > > +	}
> > > +
> > > +	return true;
> > > +}
> > > +
> > > +/**
> > > + * iecm_rx_add_frag - Add contents of Rx buffer to sk_buff as a frag

--- 8< ---

> > > +
> > > +		/* protocol */
> > > +		if (unlikely(iecm_rx_process_skb_fields(rxq, skb,
> > > +							splitq_flex_rx_desc))) {
> > 
> > You can define variables with shorter names to avoid this.
> > 
> 
> The only variable I see here with a questionably long name is splitq_flex_rx_desc but I don't see a way to shorten it without losing information. Note 'rx_desc' is also a variable that exists in this context and `splitq_base_rx_desc` could conceivably also exist so splitq_rx_desc doesn't work.

You don't actually need all that information. SplitQ model has its
own separate file, and it's reflected in the name of this function.
Rx processing is reflected as well. "flexd" ("flex_desc" after all)
is enough to get it all.
@rxq is named "rxq", not "splitq_rx_queue", right?

> 
> > > +			dev_kfree_skb_any(skb);
> > > +			skb = NULL;
> > > +			continue;
> > > +		}

--- 8< ---

> > >  static int iecm_vport_splitq_napi_poll(struct napi_struct *napi, int budget)
> > >  {
> > > -	/* stub */
> > > -	return 0;
> > > +	struct iecm_q_vector *q_vector =
> > > +				container_of(napi, struct iecm_q_vector, napi);
> > 
> > You can assign it later (before clean_complete assignment) to avoid
> > this.
> > 
> 
> To avoid the word wrap? I'm not sure I see the difference between letting it wrap and assigning it on it's own line.

Yes. As I mentioned in one of my previous replies, declaration and
assignment which have their own lines is a regular occasion, and
a line wrap of an assignment is an exception *only* for the cases
when there's no other way.

I can't get why people are fighting so hardly for not splitting the
declarations + assignments. Like, you do

	clean_complete = iecm_tx_splitq_clean_all(q_vector, budget);

right after the declaration block, why don't you try to merge it
with the declaration itself? Because it's a non-read-only operation?
What is the actual problem with

	struct iecm_q_vector *q_vector;
	bool clean_complete;
	int work_done = 0;

	q_vector = container_of(napi, typeof(*q_vector), napi);
	clean_complete = iecm_tx_splitq_clean_all(q_vector, budget);

then? Is it less readable? To me it is actually more. Is it less
logical? Probably a newline between them will help if needed.

And also, if you have a really good reason for keeping it that way,
you should've wrapped it by arguments actually, not by operation.

	struct iecm_q_vector *q_vector = container_of(napi, typeof(*q_vector,
						      napi);

Wrapping by operation takes place *only* when there's no other way,
and here are at least two now.

> 
> > > +	bool clean_complete;
> > > +	int work_done = 0;
> > > +
> > > +	clean_complete = iecm_tx_splitq_clean_all(q_vector, budget);
> > > +
> > > +	/* Handle case where we are called by netpoll with a budget of 0 */

--- 8< ---

> > > --
> > > 2.33.0
> > 
> > Thanks,
> > Al

Thanks,
Al

^ permalink raw reply	[flat|nested] 89+ messages in thread

* [Intel-wired-lan] [PATCH net-next 00/19] Add iecm and idpf
  2022-01-28  0:09 [Intel-wired-lan] [PATCH net-next 00/19] Add iecm and idpf Alan Brady
                   ` (18 preceding siblings ...)
  2022-01-28  0:10 ` [Intel-wired-lan] [PATCH net-next 19/19] idpf: introduce idpf driver Alan Brady
@ 2022-02-04 12:05 ` Alexander Lobakin
  19 siblings, 0 replies; 89+ messages in thread
From: Alexander Lobakin @ 2022-02-04 12:05 UTC (permalink / raw)
  To: intel-wired-lan

From: Alan Brady <alan.brady@intel.com>
Date: Thu, 27 Jan 2022 16:09:50 -0800

> This series introduces both the Intel Ethernet Common Module and the
> Intel Data Plane Function.  This also adds extended features and
> functionality to virtchnl with virtchnl_2.h.
>  
> The format of the series generally follows the flow of driver init to
> interface open. We go from probe into a hard reset followed by an init
> task. From there the rest of the netdev_ops and data path are added.
> Then lastly advanced features and idpf are introduced.
> 
> Currently this common layer (iecm) is initially only being used by only
> the idpf driver (PF driver for SmartNIC).  However, the plan is to
> eventually switch our iavf driver along with future drivers to use this
> common module.  The hope is to better enable code sharing going forward
> as well as support other developers writing drivers for our hardware

                                                                      ^

There's a dot '.' missing.

> 
> Alan Brady (17):
>   virtchnl: Add new virtchnl2 ops
>   iecm: add basic module init and documentation
>   iecm: add probe and remove
>   iecm: add api_init and controlq init
>   iecm: add vport alloc and virtchnl messages
>   iecm: add virtchnl messages for queues
>   iecm: finish virtchnl messages
>   iecm: add interrupts and configure netdev
>   iecm: alloc vport TX resources
>   iecm: alloc vport RX resources
>   iecm: add start_xmit and set_rx_mode
>   iecm: finish netdev_ops
>   iecm: implement splitq napi_poll
>   iecm: implement singleq napi_poll
>   iecm: implement ethtool callbacks
>   iecm: implement cloud filters
>   idpf: introduce idpf driver
> 
> Haiyue Wang (2):
>   iecm: implement flow director
>   iecm: add advanced rss

I'd like to remind here that you can *not* proceed with publishing
a v2 to netdev ML before at least me *and* Shannon (we're the only
two for now who did reviews here) say "I'm okay with your vN
revision".
I'm doing this because this is what you did previously, transferring
from our internal ML to IWL. In fact, you changed a bunch of code
and the authorship of two patches, but didn't publish a v2 there
and went directly here instead, without even providing a changelog,
so I only realized there were other changes apart from the authors
only when started comparing those two submissions.
Incorporating only the changes marked by you as "will fix" and not
getting the others resolved (places you didn't explain or answer to,
questionable "won't fix" which the reviewers disagree with etc.)
doesn't make this series ready for netdev ML.
And please supply changelogs at the end of the cover letter for each
subsequent revision, so people could easily see what was addressed
and what was not.

> 
>  .../device_drivers/ethernet/intel/idpf.rst    |   47 +
>  .../device_drivers/ethernet/intel/iecm.rst    |   93 +
>  MAINTAINERS                                   |    1 +
>  drivers/net/ethernet/intel/Kconfig            |   31 +
>  drivers/net/ethernet/intel/Makefile           |    2 +
>  drivers/net/ethernet/intel/idpf/Makefile      |   15 +
>  drivers/net/ethernet/intel/idpf/idpf_dev.h    |   17 +
>  drivers/net/ethernet/intel/idpf/idpf_devids.h |   10 +
>  drivers/net/ethernet/intel/idpf/idpf_main.c   |  140 +
>  drivers/net/ethernet/intel/idpf/idpf_reg.c    |  130 +
>  drivers/net/ethernet/intel/iecm/Makefile      |   21 +
>  .../net/ethernet/intel/iecm/iecm_controlq.c   |  649 ++
>  .../ethernet/intel/iecm/iecm_controlq_setup.c |  175 +
>  .../net/ethernet/intel/iecm/iecm_ethtool.c    | 1332 ++++
>  drivers/net/ethernet/intel/iecm/iecm_lib.c    | 5717 +++++++++++++++++
>  drivers/net/ethernet/intel/iecm/iecm_main.c   |   40 +
>  .../ethernet/intel/iecm/iecm_singleq_txrx.c   | 1229 ++++
>  drivers/net/ethernet/intel/iecm/iecm_txrx.c   | 4577 +++++++++++++
>  .../net/ethernet/intel/iecm/iecm_virtchnl.c   | 4240 ++++++++++++
>  drivers/net/ethernet/intel/include/iecm.h     |  973 +++
>  .../ethernet/intel/include/iecm_controlq.h    |  117 +
>  .../intel/include/iecm_controlq_api.h         |  185 +
>  .../ethernet/intel/include/iecm_lan_pf_regs.h |  131 +
>  .../ethernet/intel/include/iecm_lan_txrx.h    |  394 ++
>  drivers/net/ethernet/intel/include/iecm_mem.h |   20 +
>  .../net/ethernet/intel/include/iecm_txrx.h    |  733 +++
>  include/linux/avf/virtchnl.h                  | 1507 ++++-
>  include/linux/avf/virtchnl_2.h                | 1243 ++++
>  include/linux/avf/virtchnl_lan_desc.h         |  603 ++

--- 8< ---

> -- 
> 2.33.0

Thanks,
Al

^ permalink raw reply	[flat|nested] 89+ messages in thread

end of thread, other threads:[~2022-02-04 12:05 UTC | newest]

Thread overview: 89+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-01-28  0:09 [Intel-wired-lan] [PATCH net-next 00/19] Add iecm and idpf Alan Brady
2022-01-28  0:09 ` [Intel-wired-lan] [PATCH net-next 01/19] virtchnl: Add new virtchnl2 ops Alan Brady
2022-02-02 22:13   ` Brady, Alan
2022-01-28  0:09 ` [Intel-wired-lan] [PATCH net-next 02/19] iecm: add basic module init and documentation Alan Brady
2022-01-28 11:56   ` Alexander Lobakin
2022-02-02 22:15     ` Brady, Alan
2022-02-01 19:44   ` Shannon Nelson
2022-02-03  3:08     ` Brady, Alan
2022-01-28  0:09 ` [Intel-wired-lan] [PATCH net-next 03/19] iecm: add probe and remove Alan Brady
2022-02-01 20:02   ` Shannon Nelson
2022-02-03  3:13     ` Brady, Alan
2022-01-28  0:09 ` [Intel-wired-lan] [PATCH net-next 04/19] iecm: add api_init and controlq init Alan Brady
2022-01-28 12:09   ` Alexander Lobakin
2022-02-02 22:16     ` Brady, Alan
2022-02-01 21:26   ` Shannon Nelson
2022-02-03  3:24     ` Brady, Alan
2022-02-03  3:40       ` Brady, Alan
2022-02-03  5:26         ` Shannon Nelson
2022-02-03 13:13       ` Alexander Lobakin
2022-01-28  0:09 ` [Intel-wired-lan] [PATCH net-next 05/19] iecm: add vport alloc and virtchnl messages Alan Brady
2022-01-28  4:19   ` kernel test robot
2022-01-28  4:19     ` kernel test robot
2022-01-28 12:39     ` Alexander Lobakin
2022-01-28 12:39       ` Alexander Lobakin
2022-02-02 22:23       ` Brady, Alan
2022-02-02 22:23         ` Brady, Alan
2022-01-28 12:32   ` Alexander Lobakin
2022-02-02 22:21     ` Brady, Alan
2022-02-03 13:23       ` Alexander Lobakin
2022-01-28  0:09 ` [Intel-wired-lan] [PATCH net-next 06/19] iecm: add virtchnl messages for queues Alan Brady
2022-01-28 13:03   ` Alexander Lobakin
2022-02-02 22:48     ` Brady, Alan
2022-02-03 10:08       ` Maciej Fijalkowski
2022-02-03 14:09       ` Alexander Lobakin
2022-01-28  0:09 ` [Intel-wired-lan] [PATCH net-next 07/19] iecm: finish virtchnl messages Alan Brady
2022-01-28 13:19   ` Alexander Lobakin
2022-02-02 23:06     ` Brady, Alan
2022-02-03 15:05       ` Alexander Lobakin
2022-02-03 15:16         ` Maciej Fijalkowski
2022-01-28  0:09 ` [Intel-wired-lan] [PATCH net-next 08/19] iecm: add interrupts and configure netdev Alan Brady
2022-01-28 13:34   ` Alexander Lobakin
2022-02-02 23:17     ` Brady, Alan
2022-02-03 15:55       ` Alexander Lobakin
2022-01-28  0:09 ` [Intel-wired-lan] [PATCH net-next 09/19] iecm: alloc vport TX resources Alan Brady
2022-02-02 23:45   ` Brady, Alan
2022-02-03 17:56     ` Alexander Lobakin
2022-01-28  0:10 ` [Intel-wired-lan] [PATCH net-next 10/19] iecm: alloc vport RX resources Alan Brady
2022-01-28 14:16   ` Alexander Lobakin
2022-02-03  0:13     ` Brady, Alan
2022-02-03 18:29       ` Alexander Lobakin
2022-01-28  0:10 ` [Intel-wired-lan] [PATCH net-next 11/19] iecm: add start_xmit and set_rx_mode Alan Brady
2022-01-28 16:35   ` Alexander Lobakin
2022-01-28  0:10 ` [Intel-wired-lan] [PATCH net-next 12/19] iecm: finish netdev_ops Alan Brady
2022-01-28 17:06   ` Alexander Lobakin
2022-01-28  0:10 ` [Intel-wired-lan] [PATCH net-next 13/19] iecm: implement splitq napi_poll Alan Brady
2022-01-28  5:21   ` kernel test robot
2022-01-28  5:21     ` kernel test robot
2022-01-28 17:44     ` Alexander Lobakin
2022-01-28 17:44       ` Alexander Lobakin
2022-02-03  1:15       ` Brady, Alan
2022-02-03  1:15         ` Brady, Alan
2022-01-28 17:38   ` Alexander Lobakin
2022-02-03  1:07     ` Brady, Alan
2022-02-04 11:50       ` Alexander Lobakin
2022-01-28  0:10 ` [Intel-wired-lan] [PATCH net-next 14/19] iecm: implement singleq napi_poll Alan Brady
2022-01-28 17:57   ` Alexander Lobakin
2022-02-03  1:45     ` Brady, Alan
2022-02-03 19:05       ` Alexander Lobakin
2022-01-28  0:10 ` [Intel-wired-lan] [PATCH net-next 15/19] iecm: implement ethtool callbacks Alan Brady
2022-01-28 18:13   ` Alexander Lobakin
2022-02-03  2:13     ` Brady, Alan
2022-02-03 19:54       ` Alexander Lobakin
2022-01-28  0:10 ` [Intel-wired-lan] [PATCH net-next 16/19] iecm: implement flow director Alan Brady
2022-01-28 19:04   ` Alexander Lobakin
2022-02-03  2:41     ` Brady, Alan
2022-02-04 10:08       ` Alexander Lobakin
2022-01-28  0:10 ` [Intel-wired-lan] [PATCH net-next 17/19] iecm: implement cloud filters Alan Brady
2022-01-28 19:38   ` Alexander Lobakin
2022-02-03  2:53     ` Brady, Alan
2022-01-28  0:10 ` [Intel-wired-lan] [PATCH net-next 18/19] iecm: add advanced rss Alan Brady
2022-01-28 19:53   ` Alexander Lobakin
2022-02-03  2:55     ` Brady, Alan
2022-02-03 10:46       ` Maciej Fijalkowski
2022-02-04 10:22       ` Alexander Lobakin
2022-01-28  0:10 ` [Intel-wired-lan] [PATCH net-next 19/19] idpf: introduce idpf driver Alan Brady
2022-01-28 20:08   ` Alexander Lobakin
2022-02-03  3:07     ` Brady, Alan
2022-02-04 10:35       ` Alexander Lobakin
2022-02-04 12:05 ` [Intel-wired-lan] [PATCH net-next 00/19] Add iecm and idpf Alexander Lobakin

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.