RE: [PATCH v3 02/20] ice: Initialize and register a virtual bus to provide RDMA

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



> -----Original Message-----
> From: netdev-owner@xxxxxxxxxxxxxxx <netdev-owner@xxxxxxxxxxxxxxx> On
> Behalf Of Jeff Kirsher
> Sent: Monday, December 09, 2019 2:49 PM
> To: davem@xxxxxxxxxxxxx; gregkh@xxxxxxxxxxxxxxxxxxx
> Cc: Ertman, David M <david.m.ertman@xxxxxxxxx>; netdev@xxxxxxxxxxxxxxx;
> linux-rdma@xxxxxxxxxxxxxxx; nhorman@xxxxxxxxxx; sassmann@xxxxxxxxxx;
> jgg@xxxxxxxx; parav@xxxxxxxxxxxx; Nguyen, Anthony L
> <anthony.l.nguyen@xxxxxxxxx>; Kirsher, Jeffrey T
> <jeffrey.t.kirsher@xxxxxxxxx>
> Subject: [PATCH v3 02/20] ice: Initialize and register a virtual bus to provide
> RDMA
> 
> From: Dave Ertman <david.m.ertman@xxxxxxxxx>
> 
> The RDMA block does not have its own PCI function, instead it must utilize
> the ice driver to gain access to the PCI device. Create a virtual bus
> for the RDMA driver to bind to the device data. The device data contains
> all of the relevant information that the IRDMA peer will need to access
> this PF's IIDC API callbacks.
> 
> Note the header file iidc.h is located under include/linux/net/intel
> as this is a unified header file to be used by all consumers of the
> IIDC interface.
> 
> Signed-off-by: Dave Ertman <david.m.ertman@xxxxxxxxx>
> Signed-off-by: Tony Nguyen <anthony.l.nguyen@xxxxxxxxx>
> Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@xxxxxxxxx>
> ---
>  MAINTAINERS                                   |   1 +
>  drivers/net/ethernet/intel/Kconfig            |   1 +
>  drivers/net/ethernet/intel/ice/Makefile       |   1 +
>  drivers/net/ethernet/intel/ice/ice.h          |  14 +
>  .../net/ethernet/intel/ice/ice_adminq_cmd.h   |   1 +
>  drivers/net/ethernet/intel/ice/ice_common.c   |  15 +
>  drivers/net/ethernet/intel/ice/ice_dcb_lib.c  |  31 ++
>  drivers/net/ethernet/intel/ice/ice_dcb_lib.h  |   3 +
>  .../net/ethernet/intel/ice/ice_hw_autogen.h   |   1 +
>  drivers/net/ethernet/intel/ice/ice_idc.c      | 410 ++++++++++++++++++
>  drivers/net/ethernet/intel/ice/ice_idc_int.h  |  79 ++++
>  drivers/net/ethernet/intel/ice/ice_lib.c      |  11 +
>  drivers/net/ethernet/intel/ice/ice_lib.h      |   1 +
>  drivers/net/ethernet/intel/ice/ice_main.c     |  65 ++-
>  drivers/net/ethernet/intel/ice/ice_type.h     |   1 +
>  include/linux/net/intel/iidc.h                | 336 ++++++++++++++
>  16 files changed, 969 insertions(+), 2 deletions(-)
>  create mode 100644 drivers/net/ethernet/intel/ice/ice_idc.c
>  create mode 100644 drivers/net/ethernet/intel/ice/ice_idc_int.h
>  create mode 100644 include/linux/net/intel/iidc.h
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index ce7fd2e7aa1a..1de817eef0ff 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -8329,6 +8329,7 @@ F:
> 	Documentation/networking/device_drivers/intel/ice.rst
>  F:	drivers/net/ethernet/intel/
>  F:	drivers/net/ethernet/intel/*/
>  F:	include/linux/avf/virtchnl.h
> +F:	include/linux/net/intel/iidc.h
> 
>  INTEL FRAMEBUFFER DRIVER (excluding 810 and 815)
>  M:	Maik Broemme <mbroemme@xxxxxxxxxx>
> diff --git a/drivers/net/ethernet/intel/Kconfig
> b/drivers/net/ethernet/intel/Kconfig
> index 154e2e818ec6..b88328fea1d0 100644
> --- a/drivers/net/ethernet/intel/Kconfig
> +++ b/drivers/net/ethernet/intel/Kconfig
> @@ -294,6 +294,7 @@ config ICE
>  	tristate "Intel(R) Ethernet Connection E800 Series Support"
>  	default n
>  	depends on PCI_MSI
> +	select VIRTUAL_BUS
>  	---help---
>  	  This driver supports Intel(R) Ethernet Connection E800 Series of
>  	  devices.  For more information on how to identify your adapter, go
> diff --git a/drivers/net/ethernet/intel/ice/Makefile
> b/drivers/net/ethernet/intel/ice/Makefile
> index 7cb829132d28..b5ffe4ea6071 100644
> --- a/drivers/net/ethernet/intel/ice/Makefile
> +++ b/drivers/net/ethernet/intel/ice/Makefile
> @@ -18,6 +18,7 @@ ice-y := ice_main.o	\
>  	 ice_txrx_lib.o	\
>  	 ice_txrx.o	\
>  	 ice_flex_pipe.o	\
> +	 ice_idc.o	\
>  	 ice_ethtool.o
>  ice-$(CONFIG_PCI_IOV) += ice_virtchnl_pf.o ice_sriov.o
>  ice-$(CONFIG_DCB) += ice_dcb.o ice_dcb_nl.o ice_dcb_lib.o
> diff --git a/drivers/net/ethernet/intel/ice/ice.h
> b/drivers/net/ethernet/intel/ice/ice.h
> index f972dce8aebb..1f09e9b53106 100644
> --- a/drivers/net/ethernet/intel/ice/ice.h
> +++ b/drivers/net/ethernet/intel/ice/ice.h
> @@ -33,6 +33,7 @@
>  #include <linux/if_bridge.h>
>  #include <linux/ctype.h>
>  #include <linux/bpf.h>
> +#include <linux/virtual_bus.h>
>  #include <linux/avf/virtchnl.h>
>  #include <net/ipv6.h>
>  #include <net/xdp_sock.h>
> @@ -43,6 +44,7 @@
>  #include "ice_switch.h"
>  #include "ice_common.h"
>  #include "ice_sched.h"
> +#include "ice_idc_int.h"
>  #include "ice_virtchnl_pf.h"
>  #include "ice_sriov.h"
>  #include "ice_xsk.h"
> @@ -73,6 +75,7 @@ extern const char ice_drv_ver[];
>  #define ICE_MAX_SMALL_RSS_QS	8
>  #define ICE_RES_VALID_BIT	0x8000
>  #define ICE_RES_MISC_VEC_ID	(ICE_RES_VALID_BIT - 1)
> +#define ICE_RES_RDMA_VEC_ID	(ICE_RES_MISC_VEC_ID - 1)
>  #define ICE_INVAL_Q_INDEX	0xffff
>  #define ICE_INVAL_VFID		256
> 
> @@ -326,11 +329,13 @@ struct ice_q_vector {
> 
>  enum ice_pf_flags {
>  	ICE_FLAG_FLTR_SYNC,
> +	ICE_FLAG_IWARP_ENA,
>  	ICE_FLAG_RSS_ENA,
>  	ICE_FLAG_SRIOV_ENA,
>  	ICE_FLAG_SRIOV_CAPABLE,
>  	ICE_FLAG_DCB_CAPABLE,
>  	ICE_FLAG_DCB_ENA,
> +	ICE_FLAG_PEER_ENA,
>  	ICE_FLAG_ADV_FEATURES,
>  	ICE_FLAG_LINK_DOWN_ON_CLOSE_ENA,
>  	ICE_FLAG_NO_MEDIA,
> @@ -372,6 +377,9 @@ struct ice_pf {
>  	struct mutex sw_mutex;		/* lock for protecting VSI alloc flow */
>  	struct mutex tc_mutex;		/* lock to protect TC changes */
>  	u32 msg_enable;
> +	/* Total number of MSIX vectors reserved for base driver */
> +	u32 num_rdma_msix;
> +	u32 rdma_base_vector;
>  	u32 hw_csum_rx_error;
>  	u32 oicr_idx;		/* Other interrupt cause MSIX vector index */
>  	u32 num_avail_sw_msix;	/* remaining MSIX SW vectors left
> unclaimed */
> @@ -398,6 +406,8 @@ struct ice_pf {
>  	unsigned long tx_timeout_last_recovery;
>  	u32 tx_timeout_recovery_level;
>  	char int_name[ICE_INT_NAME_STR_LEN];
> +	struct ice_peer_dev_int **peers;
> +	int peer_idx;
>  	u32 sw_int_count;
>  };
> 
> @@ -510,6 +520,10 @@ int ice_get_rss(struct ice_vsi *vsi, u8 *seed, u8 *lut,
> u16 lut_size);
>  void ice_fill_rss_lut(u8 *lut, u16 rss_table_size, u16 rss_size);
>  int ice_schedule_reset(struct ice_pf *pf, enum ice_reset_req reset);
>  void ice_print_link_msg(struct ice_vsi *vsi, bool isup);
> +int ice_init_peer_devices(struct ice_pf *pf);
> +int
> +ice_for_each_peer(struct ice_pf *pf, void *data,
> +		  int (*fn)(struct ice_peer_dev_int *, void *));
>  int ice_open(struct net_device *netdev);
>  int ice_stop(struct net_device *netdev);
> 
> diff --git a/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
> b/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
> index 5421fc413f94..d413980f0370 100644
> --- a/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
> +++ b/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
> @@ -108,6 +108,7 @@ struct ice_aqc_list_caps_elem {
>  #define ICE_AQC_CAPS_TXQS				0x0042
>  #define ICE_AQC_CAPS_MSIX				0x0043
>  #define ICE_AQC_CAPS_MAX_MTU				0x0047
> +#define ICE_AQC_CAPS_IWARP				0x0051
> 
>  	u8 major_ver;
>  	u8 minor_ver;
> diff --git a/drivers/net/ethernet/intel/ice/ice_common.c
> b/drivers/net/ethernet/intel/ice/ice_common.c
> index fb1d930470c7..42ecb0d7063c 100644
> --- a/drivers/net/ethernet/intel/ice/ice_common.c
> +++ b/drivers/net/ethernet/intel/ice/ice_common.c
> @@ -1771,6 +1771,11 @@ ice_parse_caps(struct ice_hw *hw, void *buf, u32
> cap_count,
>  				  "%s: msix_vector_first_id = %d\n", prefix,
>  				  caps->msix_vector_first_id);
>  			break;
> +		case ICE_AQC_CAPS_IWARP:
> +			caps->iwarp = (number == 1);
> +			ice_debug(hw, ICE_DBG_INIT,
> +				  "%s: iwarp = %d\n", prefix, caps->iwarp);
> +			break;
>  		case ICE_AQC_CAPS_MAX_MTU:
>  			caps->max_mtu = number;
>  			ice_debug(hw, ICE_DBG_INIT, "%s: max_mtu = %d\n",
> @@ -1794,6 +1799,16 @@ ice_parse_caps(struct ice_hw *hw, void *buf, u32
> cap_count,
>  		ice_debug(hw, ICE_DBG_INIT,
>  			  "%s: maxtc = %d (based on #ports)\n", prefix,
>  			  caps->maxtc);
> +		if (caps->iwarp) {
> +			ice_debug(hw, ICE_DBG_INIT, "%s: forcing RDMA
> off\n",
> +				  prefix);
> +			caps->iwarp = 0;
> +		}
> +
> +		/* print message only when processing device capabilities */
> +		if (dev_p)
> +			dev_info(ice_hw_to_dev(hw),
> +				 "RDMA functionality is not available with the
> current device configuration.\n");
>  	}
>  }
> 
> diff --git a/drivers/net/ethernet/intel/ice/ice_dcb_lib.c
> b/drivers/net/ethernet/intel/ice/ice_dcb_lib.c
> index d3d3ec29def9..ec03038ea2ba 100644
> --- a/drivers/net/ethernet/intel/ice/ice_dcb_lib.c
> +++ b/drivers/net/ethernet/intel/ice/ice_dcb_lib.c
> @@ -720,6 +720,37 @@ ice_tx_prepare_vlan_flags_dcb(struct ice_ring
> *tx_ring,
>  	return 0;
>  }
> 
> +/**
> + * ice_setup_dcb_qos_info - Setup DCB QoS information
> + * @pf: ptr to ice_pf
> + * @qos_info: QoS param instance
> + */
> +void ice_setup_dcb_qos_info(struct ice_pf *pf, struct iidc_qos_params
> *qos_info)
> +{
> +	struct ice_dcbx_cfg *dcbx_cfg;
> +	u32 up2tc;
> +	int i;
> +
> +	dcbx_cfg = &pf->hw.port_info->local_dcbx_cfg;
> +	up2tc = rd32(&pf->hw, PRTDCB_TUP2TC);
> +	qos_info->num_apps = dcbx_cfg->numapps;
> +
> +	qos_info->num_tc = ice_dcb_get_num_tc(dcbx_cfg);
> +
> +	for (i = 0; i < IIDC_MAX_USER_PRIORITY; i++)
> +		qos_info->up2tc[i] = (up2tc >> (i * 3)) & 0x7;
> +
> +	for (i = 0; i < IEEE_8021QAZ_MAX_TCS; i++)
> +		qos_info->tc_info[i].rel_bw =
> +			dcbx_cfg->etscfg.tcbwtable[i];
> +
> +	for (i = 0; i < qos_info->num_apps; i++) {
> +		qos_info->apps[i].priority = dcbx_cfg->app[i].priority;
> +		qos_info->apps[i].prot_id = dcbx_cfg->app[i].prot_id;
> +		qos_info->apps[i].selector = dcbx_cfg->app[i].selector;
> +	}
> +}
> +
>  /**
>   * ice_dcb_process_lldp_set_mib_change - Process MIB change
>   * @pf: ptr to ice_pf
> diff --git a/drivers/net/ethernet/intel/ice/ice_dcb_lib.h
> b/drivers/net/ethernet/intel/ice/ice_dcb_lib.h
> index f15e5776f287..bb53edf462ba 100644
> --- a/drivers/net/ethernet/intel/ice/ice_dcb_lib.h
> +++ b/drivers/net/ethernet/intel/ice/ice_dcb_lib.h
> @@ -28,6 +28,8 @@ int
>  ice_tx_prepare_vlan_flags_dcb(struct ice_ring *tx_ring,
>  			      struct ice_tx_buf *first);
>  void
> +ice_setup_dcb_qos_info(struct ice_pf *pf, struct iidc_qos_params *qos_info);
> +void
>  ice_dcb_process_lldp_set_mib_change(struct ice_pf *pf,
>  				    struct ice_rq_event_info *event);
>  void ice_vsi_cfg_netdev_tc(struct ice_vsi *vsi, u8 ena_tc);
> @@ -81,6 +83,7 @@ ice_tx_prepare_vlan_flags_dcb(struct ice_ring
> __always_unused *tx_ring,
>  #define ice_update_dcb_stats(pf) do {} while (0)
>  #define ice_pf_dcb_recfg(pf) do {} while (0)
>  #define ice_vsi_cfg_dcb_rings(vsi) do {} while (0)
> +#define ice_setup_dcb_qos_info(pf, qos_info) do {} while (0)
>  #define ice_dcb_process_lldp_set_mib_change(pf, event) do {} while (0)
>  #define ice_set_cgd_num(tlan_ctx, ring) do {} while (0)
>  #define ice_vsi_cfg_netdev_tc(vsi, ena_tc) do {} while (0)
> diff --git a/drivers/net/ethernet/intel/ice/ice_hw_autogen.h
> b/drivers/net/ethernet/intel/ice/ice_hw_autogen.h
> index e8f32350fed2..bf9743c970fe 100644
> --- a/drivers/net/ethernet/intel/ice/ice_hw_autogen.h
> +++ b/drivers/net/ethernet/intel/ice/ice_hw_autogen.h
> @@ -58,6 +58,7 @@
>  #define PRTDCB_GENS				0x00083020
>  #define PRTDCB_GENS_DCBX_STATUS_S		0
>  #define PRTDCB_GENS_DCBX_STATUS_M		ICE_M(0x7, 0)
> +#define PRTDCB_TUP2TC				0x001D26C0
>  #define GL_PREEXT_L2_PMASK0(_i)			(0x0020F0FC + ((_i) *
> 4))
>  #define GL_PREEXT_L2_PMASK1(_i)			(0x0020F108 + ((_i) *
> 4))
>  #define GLFLXP_RXDID_FLAGS(_i, _j)		(0x0045D000 + ((_i) * 4 + (_j) *
> 256))
> diff --git a/drivers/net/ethernet/intel/ice/ice_idc.c
> b/drivers/net/ethernet/intel/ice/ice_idc.c
> new file mode 100644
> index 000000000000..54783ae6511c
> --- /dev/null
> +++ b/drivers/net/ethernet/intel/ice/ice_idc.c
> @@ -0,0 +1,410 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/* Copyright (c) 2019, Intel Corporation. */
> +
> +/* Inter-Driver Communication */
> +#include "ice.h"
> +#include "ice_lib.h"
> +#include "ice_dcb_lib.h"
> +
> +static struct peer_dev_id ice_peers[] = ASSIGN_PEER_INFO;
> +
> +/**
> + * ice_peer_state_change - manage state machine for peer
> + * @peer_dev: pointer to peer's configuration
> + * @new_state: the state requested to transition into
> + * @locked: boolean to determine if call made with mutex held
> + *
> + * This function handles all state transitions for peer devices.
> + * The state machine is as follows:
> + *
> + *     +<-----------------------+<-----------------------------+
> + *				|<-------+<----------+	       +
> + *				\/	 +	     +	       +
> + *    INIT  --------------> PROBED --> OPENING	  CLOSED --> REMOVED
> + *					 +           +
> + *				       OPENED --> CLOSING
> + *					 +	     +
> + *				       PREP_RST	     +
> + *					 +	     +
> + *				      PREPPED	     +
> + *					 +---------->+
> + */
> +static void
> +ice_peer_state_change(struct ice_peer_dev_int *peer_dev, long new_state,
> +		      bool locked)
> +{
> +	struct device *dev = NULL;
> +
> +	if (peer_dev)
> +		dev = &peer_dev->vobj.vdev.dev;

Why is this check for non-NULL peer_dev here?  If peer_dev is ever NULL there will be NULL pointer de-references in the mutex_lock/test_and_clear_bit statements below.

> +
> +	if (!locked)
> +		mutex_lock(&peer_dev->peer_dev_state_mutex);
> +
> +	switch (new_state) {
> +	case ICE_PEER_DEV_STATE_INIT:
> +		if (test_and_clear_bit(ICE_PEER_DEV_STATE_REMOVED,
> +				       peer_dev->state)) {
> +			set_bit(ICE_PEER_DEV_STATE_INIT, peer_dev->state);
> +			dev_dbg(dev, "state transition from _REMOVED to
> _INIT\n");
> +		} else {
> +			set_bit(ICE_PEER_DEV_STATE_INIT, peer_dev->state);
> +			if (dev)
> +				dev_dbg(dev, "state set to _INIT\n");
> +		}
> +		break;
> +	case ICE_PEER_DEV_STATE_PROBED:
> +		if (test_and_clear_bit(ICE_PEER_DEV_STATE_INIT,
> +				       peer_dev->state)) {
> +			set_bit(ICE_PEER_DEV_STATE_PROBED, peer_dev-
> >state);
> +			dev_dbg(dev, "state transition from _INIT to
> _PROBED\n");
> +		} else if (test_and_clear_bit(ICE_PEER_DEV_STATE_REMOVED,
> +					      peer_dev->state)) {
> +			set_bit(ICE_PEER_DEV_STATE_PROBED, peer_dev-
> >state);
> +			dev_dbg(dev, "state transition from _REMOVED to
> _PROBED\n");
> +		} else if (test_and_clear_bit(ICE_PEER_DEV_STATE_OPENING,
> +					      peer_dev->state)) {
> +			set_bit(ICE_PEER_DEV_STATE_PROBED, peer_dev-
> >state);
> +			dev_dbg(dev, "state transition from _OPENING to
> _PROBED\n");
> +		}
> +		break;
> +	case ICE_PEER_DEV_STATE_OPENING:
> +		if (test_and_clear_bit(ICE_PEER_DEV_STATE_PROBED,
> +				       peer_dev->state)) {
> +			set_bit(ICE_PEER_DEV_STATE_OPENING, peer_dev-
> >state);
> +			dev_dbg(dev, "state transition from _PROBED to
> _OPENING\n");
> +		} else if (test_and_clear_bit(ICE_PEER_DEV_STATE_CLOSED,
> +					      peer_dev->state)) {
> +			set_bit(ICE_PEER_DEV_STATE_OPENING, peer_dev-
> >state);
> +			dev_dbg(dev, "state transition from _CLOSED to
> _OPENING\n");
> +		}
> +		break;
> +	case ICE_PEER_DEV_STATE_OPENED:
> +		if (test_and_clear_bit(ICE_PEER_DEV_STATE_OPENING,
> +				       peer_dev->state)) {
> +			set_bit(ICE_PEER_DEV_STATE_OPENED, peer_dev-
> >state);
> +			dev_dbg(dev, "state transition from _OPENING to
> _OPENED\n");
> +		}
> +		break;
> +	case ICE_PEER_DEV_STATE_PREP_RST:
> +		if (test_and_clear_bit(ICE_PEER_DEV_STATE_OPENED,
> +				       peer_dev->state)) {
> +			set_bit(ICE_PEER_DEV_STATE_PREP_RST, peer_dev-
> >state);
> +			dev_dbg(dev, "state transition from _OPENED to
> _PREP_RST\n");
> +		}
> +		break;
> +	case ICE_PEER_DEV_STATE_PREPPED:
> +		if (test_and_clear_bit(ICE_PEER_DEV_STATE_PREP_RST,
> +				       peer_dev->state)) {
> +			set_bit(ICE_PEER_DEV_STATE_PREPPED, peer_dev-
> >state);
> +			dev_dbg(dev, "state transition _PREP_RST to
> _PREPPED\n");
> +		}
> +		break;
> +	case ICE_PEER_DEV_STATE_CLOSING:
> +		if (test_and_clear_bit(ICE_PEER_DEV_STATE_OPENED,
> +				       peer_dev->state)) {
> +			set_bit(ICE_PEER_DEV_STATE_CLOSING, peer_dev-
> >state);
> +			dev_dbg(dev, "state transition from _OPENED to
> _CLOSING\n");
> +		}
> +		if (test_and_clear_bit(ICE_PEER_DEV_STATE_PREPPED,
> +				       peer_dev->state)) {
> +			set_bit(ICE_PEER_DEV_STATE_CLOSING, peer_dev-
> >state);
> +			dev_dbg(dev, "state transition _PREPPED to
> _CLOSING\n");
> +		}
> +		/* NOTE - up to peer to handle this situation correctly */
> +		if (test_and_clear_bit(ICE_PEER_DEV_STATE_PREP_RST,
> +				       peer_dev->state)) {
> +			set_bit(ICE_PEER_DEV_STATE_CLOSING, peer_dev-
> >state);
> +			dev_warn(dev, "WARN: Peer state PREP_RST to
> _CLOSING\n");
> +		}
> +		break;
> +	case ICE_PEER_DEV_STATE_CLOSED:
> +		if (test_and_clear_bit(ICE_PEER_DEV_STATE_CLOSING,
> +				       peer_dev->state)) {
> +			set_bit(ICE_PEER_DEV_STATE_CLOSED, peer_dev-
> >state);
> +			dev_dbg(dev, "state transition from _CLOSING to
> _CLOSED\n");
> +		}
> +		break;
> +	case ICE_PEER_DEV_STATE_REMOVED:
> +		if (test_and_clear_bit(ICE_PEER_DEV_STATE_OPENED,
> +				       peer_dev->state) ||
> +		    test_and_clear_bit(ICE_PEER_DEV_STATE_CLOSED,
> +				       peer_dev->state)) {
> +			set_bit(ICE_PEER_DEV_STATE_REMOVED, peer_dev-
> >state);
> +			dev_dbg(dev, "state from _OPENED/_CLOSED to
> _REMOVED\n");
> +			/* Clear registration for events when peer removed */
> +			bitmap_zero(peer_dev->events,
> ICE_PEER_DEV_STATE_NBITS);
> +		}
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	if (!locked)
> +		mutex_unlock(&peer_dev->peer_dev_state_mutex);
> +}
> +
> +/**
> + * ice_peer_update_vsi - update the pf_vsi info in peer_dev struct
> + * @peer_dev_int: pointer to peer dev internal struct
> + * @data: opaque pointer - VSI to be updated
> + */
> +int ice_peer_update_vsi(struct ice_peer_dev_int *peer_dev_int, void *data)
> +{
> +	struct ice_vsi *vsi = (struct ice_vsi *)data;
> +	struct iidc_peer_dev *peer_dev;
> +
> +	peer_dev = ice_get_peer_dev(peer_dev_int);
> +	if (!peer_dev)
> +		return 0;
> +
> +	peer_dev->pf_vsi_num = vsi->vsi_num;
> +	return 0;
> +}
> +
> +/**
> + * ice_for_each_peer - iterate across and call function for each peer dev
> + * @pf: pointer to private board struct
> + * @data: data to pass to function on each call
> + * @fn: pointer to function to call for each peer
> + */
> +int
> +ice_for_each_peer(struct ice_pf *pf, void *data,
> +		  int (*fn)(struct ice_peer_dev_int *, void *))
> +{
> +	int i;
> +
> +	if (!pf->peers)
> +		return 0;
> +
> +	for (i = 0; i < ARRAY_SIZE(ice_peers); i++) {
> +		struct ice_peer_dev_int *peer_dev_int;
> +
> +		peer_dev_int = pf->peers[i];
> +		if (peer_dev_int) {
> +			int ret = fn(peer_dev_int, data);
> +
> +			if (ret)
> +				return ret;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * ice_unreg_peer_device - unregister specified device
> + * @peer_dev_int: ptr to peer device internal
> + * @data: ptr to opaque data
> + *
> + * This function invokes device unregistration, removes ID associated with
> + * the specified device.
> + */
> +int
> +ice_unreg_peer_device(struct ice_peer_dev_int *peer_dev_int,
> +		      void __always_unused *data)
> +{
> +	struct ice_peer_drv_int *peer_drv_int;
> +	struct iidc_peer_dev *peer_dev;
> +	struct pci_dev *pdev;
> +	struct device *dev;
> +	struct ice_pf *pf;
> +
> +	if (!peer_dev_int)
> +		return 0;
> +
> +	peer_dev = ice_get_peer_dev(peer_dev_int);
> +	pdev = peer_dev->pdev;
> +	if (!pdev)
> +		return 0;
> +
> +	pf = pci_get_drvdata(pdev);
> +	if (!pf)
> +		return 0;
> +	dev = ice_pf_to_dev(pf);
> +
> +	virtbus_dev_unregister(&peer_dev_int->vobj.vdev);
> +
> +	peer_drv_int = peer_dev_int->peer_drv_int;
> +
> +	if (peer_dev_int->ice_peer_wq) {
> +		if (peer_dev_int->peer_prep_task.func)
> +			cancel_work_sync(&peer_dev_int->peer_prep_task);
> +
> +		if (peer_dev_int->peer_close_task.func)
> +			cancel_work_sync(&peer_dev_int->peer_close_task);
> +		destroy_workqueue(peer_dev_int->ice_peer_wq);
> +	}
> +
> +	devm_kfree(dev, peer_drv_int);
> +
> +	devm_kfree(dev, peer_dev_int);
> +
> +	return 0;
> +}
> +
> +/**
> + * ice_unroll_peer - destroy peers and peer_wq in case of error
> + * @peer_dev_int: ptr to peer device internal struct
> + * @data: ptr to opaque data
> + *
> + * This function releases resources in the event of a failure in creating
> + * peer devices or their individual work_queues. Meant to be called from
> + * a ice_for_each_peer invocation
> + */
> +int
> +ice_unroll_peer(struct ice_peer_dev_int *peer_dev_int,
> +		void __always_unused *data)
> +{
> +	struct iidc_peer_dev *peer_dev;
> +	struct ice_pf *pf;
> +
> +	peer_dev = ice_get_peer_dev(peer_dev_int);
> +	if (!peer_dev)
> +		return 0;
> +
> +	pf = pci_get_drvdata(peer_dev->pdev);
> +	if (!pf)
> +		return 0;
> +
> +	if (peer_dev_int->ice_peer_wq)
> +		destroy_workqueue(peer_dev_int->ice_peer_wq);
> +	devm_kfree(ice_pf_to_dev(pf), peer_dev_int);
> +
> +	return 0;
> +}
> +
> +/**
> + * ice_reserve_peer_qvector - Reserve vector resources for peer drivers
> + * @pf: board private structure to initialize
> + */
> +static int ice_reserve_peer_qvector(struct ice_pf *pf)
> +{
> +	if (test_bit(ICE_FLAG_IWARP_ENA, pf->flags)) {
> +		int index;
> +
> +		index = ice_get_res(pf, pf->irq_tracker, pf->num_rdma_msix,
> +				    ICE_RES_RDMA_VEC_ID);
> +		if (index < 0)
> +			return index;
> +		pf->num_avail_sw_msix -= pf->num_rdma_msix;
> +		pf->rdma_base_vector = index;
> +	}
> +	return 0;
> +}
> +
> +/**
> + * ice_init_peer_devices - initializes peer devices
> + * @pf: ptr to ice_pf
> + *
> + * This function initializes peer devices on the virtual bus.
> + */
> +int ice_init_peer_devices(struct ice_pf *pf)
> +{
> +	struct ice_vsi *vsi = pf->vsi[0];
> +	struct pci_dev *pdev = pf->pdev;
> +	struct device *dev = &pdev->dev;
> +	int status = 0;
> +	int i;
> +
> +	/* Reserve vector resources */
> +	status = ice_reserve_peer_qvector(pf);
> +	if (status < 0) {
> +		dev_err(dev, "failed to reserve vectors for peer drivers\n");
> +		return status;
> +	}
> +	for (i = 0; i < ARRAY_SIZE(ice_peers); i++) {
> +		struct ice_peer_dev_int *peer_dev_int;
> +		struct ice_peer_drv_int *peer_drv_int;
> +		struct iidc_qos_params *qos_info;
> +		struct msix_entry *entry = NULL;
> +		struct iidc_peer_dev *peer_dev;
> +		struct virtbus_device *vdev;
> +		int j;
> +
> +		peer_dev_int = devm_kzalloc(dev, sizeof(*peer_dev_int),
> +					    GFP_KERNEL);
> +		if (!peer_dev_int)
> +			return -ENOMEM;
> +		pf->peers[i] = peer_dev_int;
> +
> +		peer_drv_int = devm_kzalloc(dev, sizeof(*peer_drv_int),
> +					    GFP_KERNEL);
> +		if (!peer_drv_int) {
> +			devm_kfree(dev, peer_dev_int);
> +			return -ENOMEM;
> +		}
> +
> +		peer_dev_int->peer_drv_int = peer_drv_int;
> +
> +		/* Initialize driver values */
> +		for (j = 0; j < IIDC_EVENT_NBITS; j++)
> +			bitmap_zero(peer_drv_int->current_events[j].type,
> +				    IIDC_EVENT_NBITS);
> +
> +		mutex_init(&peer_dev_int->peer_dev_state_mutex);
> +
> +		peer_dev = ice_get_peer_dev(peer_dev_int);
> +		peer_dev->peer_ops = NULL;
> +		peer_dev->hw_addr = (u8 __iomem *)pf->hw.hw_addr;
> +		peer_dev->peer_dev_id = ice_peers[i].id;
> +		peer_dev->pf_vsi_num = vsi->vsi_num;
> +		peer_dev->netdev = vsi->netdev;
> +
> +		peer_dev_int->ice_peer_wq =
> +			alloc_ordered_workqueue("ice_peer_wq_%d",
> WQ_UNBOUND,
> +						i);
> +		if (!peer_dev_int->ice_peer_wq)
> +			return -ENOMEM;
> +
> +		peer_dev->pdev = pdev;
> +		qos_info = &peer_dev->initial_qos_info;
> +
> +		/* setup qos_info fields with defaults */
> +		qos_info->num_apps = 0;
> +		qos_info->num_tc = 1;
> +
> +		for (j = 0; j < IIDC_MAX_USER_PRIORITY; j++)
> +			qos_info->up2tc[j] = 0;
> +
> +		qos_info->tc_info[0].rel_bw = 100;
> +		for (j = 1; j < IEEE_8021QAZ_MAX_TCS; j++)
> +			qos_info->tc_info[j].rel_bw = 0;
> +
> +		/* for DCB, override the qos_info defaults. */
> +		ice_setup_dcb_qos_info(pf, qos_info);
> +
> +		/* make sure peer specific resources such as msix_count and
> +		 * msix_entries are initialized
> +		 */
> +		switch (ice_peers[i].id) {
> +		case IIDC_PEER_RDMA_ID:
> +			if (test_bit(ICE_FLAG_IWARP_ENA, pf->flags)) {
> +				peer_dev->msix_count = pf->num_rdma_msix;
> +				entry = &pf->msix_entries[pf-
> >rdma_base_vector];
> +			}
> +			break;
> +		default:
> +			break;
> +		}
> +
> +		peer_dev->msix_entries = entry;
> +		ice_peer_state_change(peer_dev_int,
> ICE_PEER_DEV_STATE_INIT,
> +				      false);
> +
> +		vdev = &peer_dev_int->vobj.vdev;
> +		vdev->name = ice_peers[i].name;
> +		vdev->dev.parent = &pdev->dev;
> +
> +		status = virtbus_dev_register(vdev);
> +		if (status) {
> +			dev_err(dev, "Failure adding peer dev %s %d\n",
> +				ice_peers[i].name, status);
> +			virtbus_dev_unregister(vdev);
> +			vdev = NULL;
> +			return status;
> +		}
> +	}
> +
> +	return status;
> +}
> diff --git a/drivers/net/ethernet/intel/ice/ice_idc_int.h
> b/drivers/net/ethernet/intel/ice/ice_idc_int.h
> new file mode 100644
> index 000000000000..bba5a925aefb
> --- /dev/null
> +++ b/drivers/net/ethernet/intel/ice/ice_idc_int.h
> @@ -0,0 +1,79 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/* Copyright (c) 2019, Intel Corporation. */
> +
> +#ifndef _ICE_IDC_INT_H_
> +#define _ICE_IDC_INT_H_
> +
> +#include <linux/net/intel/iidc.h>
> +#include "ice.h"
> +
> +enum ice_peer_dev_state {
> +	ICE_PEER_DEV_STATE_INIT,
> +	ICE_PEER_DEV_STATE_PROBED,
> +	ICE_PEER_DEV_STATE_OPENING,
> +	ICE_PEER_DEV_STATE_OPENED,
> +	ICE_PEER_DEV_STATE_PREP_RST,
> +	ICE_PEER_DEV_STATE_PREPPED,
> +	ICE_PEER_DEV_STATE_CLOSING,
> +	ICE_PEER_DEV_STATE_CLOSED,
> +	ICE_PEER_DEV_STATE_REMOVED,
> +	ICE_PEER_DEV_STATE_API_RDY,
> +	ICE_PEER_DEV_STATE_NBITS,               /* must be last */
> +};
> +
> +enum ice_peer_drv_state {
> +	ICE_PEER_DRV_STATE_MBX_RDY,
> +	ICE_PEER_DRV_STATE_NBITS,               /* must be last */
> +};
> +
> +struct ice_peer_drv_int {
> +	struct iidc_peer_drv *peer_drv;
> +
> +	/* States associated with peer driver */
> +	DECLARE_BITMAP(state, ICE_PEER_DRV_STATE_NBITS);
> +
> +	/* if this peer_dev is the originator of an event, these are the
> +	 * most recent events of each type
> +	 */
> +	struct iidc_event current_events[IIDC_EVENT_NBITS];
> +};
> +
> +#define ICE_MAX_PEER_NAME 64
> +
> +struct ice_peer_dev_int {
> +	struct ice_peer_drv_int *peer_drv_int; /* driver private structure */
> +	struct iidc_virtbus_object vobj;
> +
> +	/* if this peer_dev is the originator of an event, these are the
> +	 * most recent events of each type
> +	 */
> +	struct iidc_event current_events[IIDC_EVENT_NBITS];
> +	/* Events a peer has registered to be notified about */
> +	DECLARE_BITMAP(events, IIDC_EVENT_NBITS);
> +
> +	/* States associated with peer device */
> +	DECLARE_BITMAP(state, ICE_PEER_DEV_STATE_NBITS);
> +	struct mutex peer_dev_state_mutex; /* peer_dev state mutex */
> +
> +	/* per peer workqueue */
> +	struct workqueue_struct *ice_peer_wq;
> +
> +	struct work_struct peer_prep_task;
> +	struct work_struct peer_close_task;
> +
> +	enum iidc_close_reason rst_type;
> +};
> +
> +int ice_peer_update_vsi(struct ice_peer_dev_int *peer_dev_int, void *data);
> +int ice_unroll_peer(struct ice_peer_dev_int *peer_dev_int, void *data);
> +int ice_unreg_peer_device(struct ice_peer_dev_int *peer_dev_int, void
> *data);
> +
> +static inline struct
> +iidc_peer_dev *ice_get_peer_dev(struct ice_peer_dev_int *peer_dev_int)
> +{
> +	if (peer_dev_int)
> +		return &peer_dev_int->vobj.peer_dev;
> +	else
> +		return NULL;
> +}
> +#endif /* !_ICE_IDC_INT_H_ */
> diff --git a/drivers/net/ethernet/intel/ice/ice_lib.c
> b/drivers/net/ethernet/intel/ice/ice_lib.c
> index e7449248fab4..1456eed1b3b9 100644
> --- a/drivers/net/ethernet/intel/ice/ice_lib.c
> +++ b/drivers/net/ethernet/intel/ice/ice_lib.c
> @@ -492,6 +492,17 @@ bool ice_is_safe_mode(struct ice_pf *pf)
>  	return !test_bit(ICE_FLAG_ADV_FEATURES, pf->flags);
>  }
> 
> +/**
> + * ice_is_peer_ena
> + * @pf: pointer to the PF struct
> + *
> + * returns true if peer devices/drivers are supported, false otherwise
> + */
> +bool ice_is_peer_ena(struct ice_pf *pf)
> +{
> +	return test_bit(ICE_FLAG_PEER_ENA, pf->flags);
> +}
> +
>  /**
>   * ice_rss_clean - Delete RSS related VSI structures that hold user inputs
>   * @vsi: the VSI being removed
> diff --git a/drivers/net/ethernet/intel/ice/ice_lib.h
> b/drivers/net/ethernet/intel/ice/ice_lib.h
> index 6e31e30aba39..502c05c1e84b 100644
> --- a/drivers/net/ethernet/intel/ice/ice_lib.h
> +++ b/drivers/net/ethernet/intel/ice/ice_lib.h
> @@ -103,4 +103,5 @@ enum ice_status
>  ice_vsi_cfg_mac_fltr(struct ice_vsi *vsi, const u8 *macaddr, bool set);
> 
>  bool ice_is_safe_mode(struct ice_pf *pf);
> +bool ice_is_peer_ena(struct ice_pf *pf);
>  #endif /* !_ICE_LIB_H_ */
> diff --git a/drivers/net/ethernet/intel/ice/ice_main.c
> b/drivers/net/ethernet/intel/ice/ice_main.c
> index 69bff085acf7..950c213a8802 100644
> --- a/drivers/net/ethernet/intel/ice/ice_main.c
> +++ b/drivers/net/ethernet/intel/ice/ice_main.c
> @@ -2657,6 +2657,12 @@ static void ice_set_pf_caps(struct ice_pf *pf)
>  {
>  	struct ice_hw_func_caps *func_caps = &pf->hw.func_caps;
> 
> +	clear_bit(ICE_FLAG_IWARP_ENA, pf->flags);
> +	clear_bit(ICE_FLAG_PEER_ENA, pf->flags);
> +	if (func_caps->common_cap.iwarp) {
> +		set_bit(ICE_FLAG_IWARP_ENA, pf->flags);
> +		set_bit(ICE_FLAG_PEER_ENA, pf->flags);
> +	}
>  	clear_bit(ICE_FLAG_DCB_CAPABLE, pf->flags);
>  	if (func_caps->common_cap.dcb)
>  		set_bit(ICE_FLAG_DCB_CAPABLE, pf->flags);
> @@ -2738,6 +2744,17 @@ static int ice_ena_msix_range(struct ice_pf *pf)
>  	v_budget += needed;
>  	v_left -= needed;
> 
> +	/* reserve vectors for RDMA peer driver */
> +	if (test_bit(ICE_FLAG_IWARP_ENA, pf->flags)) {
> +		/* RDMA peer driver needs one extra to handle misc causes */
> +		needed = min_t(int, num_online_cpus(), v_left) + 1;
> +		if (v_left < needed)
> +			goto no_hw_vecs_left_err;
> +		pf->num_rdma_msix = needed;
> +		v_budget += needed;
> +		v_left -= needed;
> +	}
> +
>  	pf->msix_entries = devm_kcalloc(dev, v_budget,
>  					sizeof(*pf->msix_entries),
> GFP_KERNEL);
> 
> @@ -2763,16 +2780,19 @@ static int ice_ena_msix_range(struct ice_pf *pf)
>  		dev_warn(dev,
>  			 "not enough OS MSI-X vectors. requested = %d,
> obtained = %d\n",
>  			 v_budget, v_actual);
> -/* 2 vectors for LAN (traffic + OICR) */
> +/* 2 vectors for LAN and RDMA (traffic + OICR) */
>  #define ICE_MIN_LAN_VECS 2
> +#define ICE_MIN_RDMA_VECS 2
> +#define ICE_MIN_VECS (ICE_MIN_LAN_VECS + ICE_MIN_RDMA_VECS)
> 
> -		if (v_actual < ICE_MIN_LAN_VECS) {
> +		if (v_actual < ICE_MIN_VECS) {
>  			/* error if we can't get minimum vectors */
>  			pci_disable_msix(pf->pdev);
>  			err = -ERANGE;
>  			goto msix_err;
>  		} else {
>  			pf->num_lan_msix = ICE_MIN_LAN_VECS;
> +			pf->num_rdma_msix = ICE_MIN_RDMA_VECS;
>  		}
>  	}
> 
> @@ -2789,6 +2809,7 @@ static int ice_ena_msix_range(struct ice_pf *pf)
>  	err = -ERANGE;
>  exit_err:
>  	pf->num_lan_msix = 0;
> +	pf->num_rdma_msix = 0;
>  	return err;
>  }
> 
> @@ -3354,6 +3375,27 @@ ice_probe(struct pci_dev *pdev, const struct
> pci_device_id __always_unused *ent)
> 
>  	/* initialize DDP driven features */
> 
> +	/* init peers only if supported */
> +	if (ice_is_peer_ena(pf)) {
> +		pf->peers = devm_kcalloc(dev, IIDC_MAX_NUM_PEERS,
> +					 sizeof(*pf->peers), GFP_KERNEL);
> +		if (!pf->peers) {
> +			err = -ENOMEM;
> +			goto err_init_peer_unroll;
> +		}
> +
> +		err = ice_init_peer_devices(pf);
> +		if (err) {
> +			dev_err(dev,
> +				"Failed to initialize peer devices: 0x%x\n",
> +				err);
> +			err = -EIO;
> +			goto err_init_peer_unroll;
> +		}
> +	} else {
> +		dev_warn(dev, "RDMA is not supported on this device\n");
> +	}
> +
>  	/* Note: DCB init failure is non-fatal to load */
>  	if (ice_init_pf_dcb(pf, false)) {
>  		clear_bit(ICE_FLAG_DCB_CAPABLE, pf->flags);
> @@ -3367,6 +3409,14 @@ ice_probe(struct pci_dev *pdev, const struct
> pci_device_id __always_unused *ent)
> 
>  	return 0;
> 
> +err_init_peer_unroll:
> +	if (ice_is_peer_ena(pf)) {
> +		ice_for_each_peer(pf, NULL, ice_unroll_peer);
> +		if (pf->peers) {
> +			devm_kfree(dev, pf->peers);
> +			pf->peers = NULL;
> +		}
> +	}
>  err_alloc_sw_unroll:
>  	set_bit(__ICE_SERVICE_DIS, pf->state);
>  	set_bit(__ICE_DOWN, pf->state);
> @@ -3408,6 +3458,7 @@ static void ice_remove(struct pci_dev *pdev)
>  	if (test_bit(ICE_FLAG_SRIOV_ENA, pf->flags))
>  		ice_free_vfs(pf);
>  	ice_vsi_release_all(pf);
> +	ice_for_each_peer(pf, NULL, ice_unreg_peer_device);
>  	ice_free_irq_msix_misc(pf);
>  	ice_for_each_vsi(pf, i) {
>  		if (!pf->vsi[i])
> @@ -4703,6 +4754,16 @@ static void ice_rebuild(struct ice_pf *pf, enum
> ice_reset_req reset_type)
>  		goto err_vsi_rebuild;
>  	}
> 
> +	if (ice_is_peer_ena(pf)) {
> +		struct ice_vsi *vsi = ice_get_main_vsi(pf);
> +
> +		if (!vsi) {
> +			dev_err(dev, "No PF_VSI to update peer\n");
> +			goto err_vsi_rebuild;
> +		}
> +		ice_for_each_peer(pf, vsi, ice_peer_update_vsi);
> +	}
> +
>  	if (test_bit(ICE_FLAG_SRIOV_ENA, pf->flags)) {
>  		err = ice_vsi_rebuild_by_type(pf, ICE_VSI_VF);
>  		if (err) {
> diff --git a/drivers/net/ethernet/intel/ice/ice_type.h
> b/drivers/net/ethernet/intel/ice/ice_type.h
> index c4854a987130..e73784e093c5 100644
> --- a/drivers/net/ethernet/intel/ice/ice_type.h
> +++ b/drivers/net/ethernet/intel/ice/ice_type.h
> @@ -187,6 +187,7 @@ struct ice_hw_common_caps {
>  	u8 rss_table_entry_width;	/* RSS Entry width in bits */
> 
>  	u8 dcb;
> +	u8 iwarp;
>  };
> 
>  /* Function specific capabilities */
> diff --git a/include/linux/net/intel/iidc.h b/include/linux/net/intel/iidc.h
> new file mode 100644
> index 000000000000..a7d694276aa0
> --- /dev/null
> +++ b/include/linux/net/intel/iidc.h
> @@ -0,0 +1,336 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/* Copyright (c) 2019, Intel Corporation. */
> +
> +#ifndef _IIDC_H_
> +#define _IIDC_H_
> +
> +#include <linux/dcbnl.h>
> +#include <linux/device.h>
> +#include <linux/if_ether.h>
> +#include <linux/kernel.h>
> +#include <linux/netdevice.h>
> +#include <linux/virtual_bus.h>
> +
> +enum iidc_event_type {
> +	IIDC_EVENT_LINK_CHANGE,
> +	IIDC_EVENT_MTU_CHANGE,
> +	IIDC_EVENT_TC_CHANGE,
> +	IIDC_EVENT_API_CHANGE,
> +	IIDC_EVENT_MBX_CHANGE,
> +	IIDC_EVENT_NBITS		/* must be last */
> +};
> +
> +enum iidc_res_type {
> +	IIDC_INVAL_RES,
> +	IIDC_VSI,
> +	IIDC_VEB,
> +	IIDC_EVENT_Q,
> +	IIDC_EGRESS_CMPL_Q,
> +	IIDC_CMPL_EVENT_Q,
> +	IIDC_ASYNC_EVENT_Q,
> +	IIDC_DOORBELL_Q,
> +	IIDC_RDMA_QSETS_TXSCHED,
> +};
> +
> +enum iidc_peer_reset_type {
> +	IIDC_PEER_PFR,
> +	IIDC_PEER_CORER,
> +	IIDC_PEER_CORER_SW_CORE,
> +	IIDC_PEER_CORER_SW_FULL,
> +	IIDC_PEER_GLOBR,
> +};
> +
> +/* reason notified to peer driver as part of event handling */
> +enum iidc_close_reason {
> +	IIDC_REASON_INVAL,
> +	IIDC_REASON_HW_UNRESPONSIVE,
> +	IIDC_REASON_INTERFACE_DOWN, /* Administrative down */
> +	IIDC_REASON_PEER_DRV_UNREG, /* peer driver getting unregistered
> */
> +	IIDC_REASON_PEER_DEV_UNINIT,
> +	IIDC_REASON_GLOBR_REQ,
> +	IIDC_REASON_CORER_REQ,
> +	IIDC_REASON_EMPR_REQ,
> +	IIDC_REASON_PFR_REQ,
> +	IIDC_REASON_HW_RESET_PENDING,
> +	IIDC_REASON_RECOVERY_MODE,
> +	IIDC_REASON_PARAM_CHANGE,
> +};
> +
> +enum iidc_rdma_filter {
> +	IIDC_RDMA_FILTER_INVAL,
> +	IIDC_RDMA_FILTER_IWARP,
> +	IIDC_RDMA_FILTER_ROCEV2,
> +	IIDC_RDMA_FILTER_BOTH,
> +};
> +
> +/* Struct to hold per DCB APP info */
> +struct iidc_dcb_app_info {
> +	u8  priority;
> +	u8  selector;
> +	u16 prot_id;
> +};
> +
> +struct iidc_peer_dev;
> +
> +#define IIDC_MAX_USER_PRIORITY		8
> +#define IIDC_MAX_APPS			8
> +
> +/* Struct to hold per RDMA Qset info */
> +struct iidc_rdma_qset_params {
> +	u32 teid;	/* qset TEID */
> +	u16 qs_handle; /* RDMA driver provides this */
> +	u16 vsi_id; /* VSI index */
> +	u8 tc; /* TC branch the QSet should belong to */
> +	u8 reserved[3];
> +};
> +
> +struct iidc_res_base {
> +	/* Union for future provision e.g. other res_type */
> +	union {
> +		struct iidc_rdma_qset_params qsets;
> +	} res;
> +};
> +
> +struct iidc_res {
> +	/* Type of resource. Filled by peer driver */
> +	enum iidc_res_type res_type;
> +	/* Count requested by peer driver */
> +	u16 cnt_req;
> +
> +	/* Number of resources allocated. Filled in by callee.
> +	 * Based on this value, caller to fill up "resources"
> +	 */
> +	u16 res_allocated;
> +
> +	/* Unique handle to resources allocated. Zero if call fails.
> +	 * Allocated by callee and for now used by caller for internal
> +	 * tracking purpose.
> +	 */
> +	u32 res_handle;
> +
> +	/* Peer driver has to allocate sufficient memory, to accommodate
> +	 * cnt_requested before calling this function.
> +	 * Memory has to be zero initialized. It is input/output param.
> +	 * As a result of alloc_res API, this structures will be populated.
> +	 */
> +	struct iidc_res_base res[1];
> +};
> +
> +struct iidc_qos_info {
> +	u64 tc_ctx;
> +	u8 rel_bw;
> +	u8 prio_type;
> +	u8 egress_virt_up;
> +	u8 ingress_virt_up;
> +};
> +
> +/* Struct to hold QoS info */
> +struct iidc_qos_params {
> +	struct iidc_qos_info tc_info[IEEE_8021QAZ_MAX_TCS];
> +	u8 up2tc[IIDC_MAX_USER_PRIORITY];
> +	u8 vsi_relative_bw;
> +	u8 vsi_priority_type;
> +	u32 num_apps;
> +	struct iidc_dcb_app_info apps[IIDC_MAX_APPS];
> +	u8 num_tc;
> +};
> +
> +union iidc_event_info {
> +	/* IIDC_EVENT_LINK_CHANGE */
> +	struct {
> +		struct net_device *lwr_nd;
> +		u16 vsi_num; /* HW index of VSI corresponding to lwr ndev */
> +		u8 new_link_state;
> +		u8 lport;
> +	} link_info;
> +	/* IIDC_EVENT_MTU_CHANGE */
> +	u16 mtu;
> +	/* IIDC_EVENT_TC_CHANGE */
> +	struct iidc_qos_params port_qos;
> +	/* IIDC_EVENT_API_CHANGE */
> +	u8 api_rdy;
> +	/* IIDC_EVENT_MBX_CHANGE */
> +	u8 mbx_rdy;
> +};
> +
> +/* iidc_event elements are to be passed back and forth between the device
> + * owner and the peer drivers. They are to be used to both register/unregister
> + * for event reporting and to report an event (events can be either device
> + * owner generated or peer generated).
> + *
> + * For (un)registering for events, the structure needs to be populated with:
> + *   reporter - pointer to the iidc_peer_dev struct of the peer (un)registering
> + *   type - bitmap with bits set for event types to (un)register for
> + *
> + * For reporting events, the structure needs to be populated with:
> + *   reporter - pointer to peer that generated the event (NULL for ice)
> + *   type - bitmap with single bit set for this event type
> + *   info - union containing data relevant to this event type
> + */
> +struct iidc_event {
> +	struct iidc_peer_dev *reporter;
> +	DECLARE_BITMAP(type, IIDC_EVENT_NBITS);
> +	union iidc_event_info info;
> +};
> +
> +/* Following APIs are implemented by device owner and invoked by peer
> + * drivers
> + */
> +struct iidc_ops {
> +	/* APIs to allocate resources such as VEB, VSI, Doorbell queues,
> +	 * completion queues, Tx/Rx queues, etc...
> +	 */
> +	int (*alloc_res)(struct iidc_peer_dev *peer_dev,
> +			 struct iidc_res *res,
> +			 int partial_acceptable);
> +	int (*free_res)(struct iidc_peer_dev *peer_dev,
> +			struct iidc_res *res);
> +
> +	int (*is_vsi_ready)(struct iidc_peer_dev *peer_dev);
> +	int (*peer_register)(struct iidc_peer_dev *peer_dev);
> +	int (*peer_unregister)(struct iidc_peer_dev *peer_dev);
> +	int (*request_reset)(struct iidc_peer_dev *dev,
> +			     enum iidc_peer_reset_type reset_type);
> +
> +	void (*notify_state_change)(struct iidc_peer_dev *dev,
> +				    struct iidc_event *event);
> +
> +	/* Notification APIs */
> +	void (*reg_for_notification)(struct iidc_peer_dev *dev,
> +				     struct iidc_event *event);
> +	void (*unreg_for_notification)(struct iidc_peer_dev *dev,
> +				       struct iidc_event *event);
> +	int (*update_vsi_filter)(struct iidc_peer_dev *peer_dev,
> +				 enum iidc_rdma_filter filter, bool enable);
> +	int (*vc_send)(struct iidc_peer_dev *peer_dev, u32 vf_id, u8 *msg,
> +		       u16 len);
> +};
> +
> +/* Following APIs are implemented by peer drivers and invoked by device
> + * owner
> + */
> +struct iidc_peer_ops {
> +	void (*event_handler)(struct iidc_peer_dev *peer_dev,
> +			      struct iidc_event *event);
> +
> +	/* Why we have 'open' and when it is expected to be called:
> +	 * 1. symmetric set of API w.r.t close
> +	 * 2. To be invoked form driver initialization path
> +	 *     - call peer_driver:open once device owner is fully
> +	 *     initialized
> +	 * 3. To be invoked upon RESET complete
> +	 */
> +	int (*open)(struct iidc_peer_dev *peer_dev);
> +
> +	/* Peer's close function is to be called when the peer needs to be
> +	 * quiesced. This can be for a variety of reasons (enumerated in the
> +	 * iidc_close_reason enum struct). A call to close will only be
> +	 * followed by a call to either remove or open. No IDC calls from the
> +	 * peer should be accepted until it is re-opened.
> +	 *
> +	 * The *reason* parameter is the reason for the call to close. This
> +	 * can be for any reason enumerated in the iidc_close_reason struct.
> +	 * It's primary reason is for the peer's bookkeeping and in case the
> +	 * peer want to perform any different tasks dictated by the reason.
> +	 */
> +	void (*close)(struct iidc_peer_dev *peer_dev,
> +		      enum iidc_close_reason reason);
> +
> +	int (*vc_receive)(struct iidc_peer_dev *peer_dev, u32 vf_id, u8 *msg,
> +			  u16 len);
> +	/* tell RDMA peer to prepare for TC change in a blocking call
> +	 * that will directly precede the change event
> +	 */
> +	void (*prep_tc_change)(struct iidc_peer_dev *peer_dev);
> +};
> +
> +#define IIDC_PEER_RDMA_NAME	"ice_rdma"
> +#define IIDC_PEER_RDMA_ID	0x00000010
> +#define IIDC_MAX_NUM_PEERS	4
> +
> +/* The const struct that instantiates peer_dev_id needs to be initialized
> + * in the .c with the macro ASSIGN_PEER_INFO.
> + * For example:
> + * static const struct peer_dev_id peer_dev_ids[] = ASSIGN_PEER_INFO;
> + */
> +struct peer_dev_id {
> +	char *name;
> +	int id;
> +};
> +
> +#define ASSIGN_PEER_INFO						\
> +{									\
> +	{ .name = IIDC_PEER_RDMA_NAME, .id = IIDC_PEER_RDMA_ID },	\
> +}
> +
> +#define iidc_peer_priv(x) ((x)->peer_priv)
> +
> +/* Structure representing peer specific information, each peer using the IIDC
> + * interface will have an instance of this struct dedicated to it.
> + */
> +struct iidc_peer_dev {
> +	struct pci_dev *pdev; /* PCI device of corresponding to main function
> */
> +	/* KVA / Linear address corresponding to BAR0 of underlying
> +	 * pci_device.
> +	 */
> +	u8 __iomem *hw_addr;
> +	int peer_dev_id;
> +
> +	/* Opaque pointer for peer specific data tracking.  This memory will
> +	 * be alloc'd and freed by the peer driver and used for private data
> +	 * accessible only to the specific peer.  It is stored here so that
> +	 * when this struct is passed to the peer via an IDC call, the data
> +	 * can be accessed by the peer at that time.
> +	 * The peers should only retrieve the pointer by the macro:
> +	 *    iidc_peer_priv(struct iidc_peer_dev *)
> +	 */
> +	void *peer_priv;
> +
> +	u8 ftype;	/* PF(false) or VF (true) */
> +
> +	/* Data VSI created by driver */
> +	u16 pf_vsi_num;
> +
> +	struct iidc_qos_params initial_qos_info;
> +	struct net_device *netdev;
> +
> +	/* Based on peer driver type, this shall point to corresponding MSIx
> +	 * entries in pf->msix_entries (which were allocated as part of driver
> +	 * initialization) e.g. for RDMA driver, msix_entries reserved will be
> +	 * num_online_cpus + 1.
> +	 */
> +	u16 msix_count; /* How many vectors are reserved for this device */
> +	struct msix_entry *msix_entries;
> +
> +	/* Following struct contains function pointers to be initialized
> +	 * by device owner and called by peer driver
> +	 */
> +	const struct iidc_ops *ops;
> +
> +	/* Following struct contains function pointers to be initialized
> +	 * by peer driver and called by device owner
> +	 */
> +	const struct iidc_peer_ops *peer_ops;
> +
> +	/* Pointer to peer_drv struct to be populated by peer driver */
> +	struct iidc_peer_drv *peer_drv;
> +};
> +
> +struct iidc_virtbus_object {
> +	struct virtbus_device vdev;
> +	struct iidc_peer_dev peer_dev;
> +};
> +
> +/* structure representing peer driver
> + * Peer driver to initialize those function ptrs and it will be invoked
> + * by device owner as part of driver_registration via bus infrastructure
> + */
> +struct iidc_peer_drv {
> +	u16 driver_id;
> +#define IIDC_PEER_DEVICE_OWNER		0
> +#define IIDC_PEER_RDMA_DRIVER		4
> +
> +	const char *name;
> +
> +};
> +#endif /* _IIDC_H_*/
> --
> 2.23.0




[Index of Archives]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Photo]     [Yosemite News]     [Yosemite Photos]     [Linux Kernel]     [Linux SCSI]     [XFree86]

  Powered by Linux