On 3/9/2018 9:21 AM, Anirudh Venkataramanan wrote:
This patch adds to the initialization flow by getting switch
configuration, scheduler configuration and device capabilities.

Switch configuration:
On boot, an L2 switch element is created in the firmware per physical
function. Each physical function is also mapped to a port, to which its
switch element is connected. In other words, this switch can be visualized
as an embedded vSwitch that can connect a physical functions's virtual
station interfaces (VSIs) to the egress/ingress port. Egress/ingress
filters will be eventually created and applied on this switch element.
As part of the initialization flow, the driver gets configuration data
from this switch element and stores it.

Scheduler configuration:
The Tx scheduler is a subsystem responsible for setting and enforcing QoS.
As part of the initialization flow, the driver queries and stores the
default scheduler configuration for the given physical function.

Device capabilities:
As part of initialization, the driver has to determine what the device is
capable of (ex. max queues, VSIs, etc). This information is obtained from
the firmware and stored by the driver.

Signed-off-by: Anirudh Venkataramanan <anirudh.venkatarama...@intel.com>
---
  drivers/net/ethernet/intel/ice/Makefile         |   4 +-
  drivers/net/ethernet/intel/ice/ice.h            |   2 +
  drivers/net/ethernet/intel/ice/ice_adminq_cmd.h | 209 ++++++++++++++
  drivers/net/ethernet/intel/ice/ice_common.c     | 223 +++++++++++++++
  drivers/net/ethernet/intel/ice/ice_common.h     |   2 +
  drivers/net/ethernet/intel/ice/ice_sched.c      | 354 ++++++++++++++++++++++++
  drivers/net/ethernet/intel/ice/ice_sched.h      |  42 +++
  drivers/net/ethernet/intel/ice/ice_switch.c     | 158 +++++++++++
  drivers/net/ethernet/intel/ice/ice_switch.h     |  28 ++
  drivers/net/ethernet/intel/ice/ice_type.h       | 109 ++++++++
  10 files changed, 1130 insertions(+), 1 deletion(-)
  create mode 100644 drivers/net/ethernet/intel/ice/ice_sched.c
  create mode 100644 drivers/net/ethernet/intel/ice/ice_sched.h
  create mode 100644 drivers/net/ethernet/intel/ice/ice_switch.c
  create mode 100644 drivers/net/ethernet/intel/ice/ice_switch.h

diff --git a/drivers/net/ethernet/intel/ice/Makefile 
b/drivers/net/ethernet/intel/ice/Makefile
index 373d481dbb25..809d85c04398 100644
--- a/drivers/net/ethernet/intel/ice/Makefile
+++ b/drivers/net/ethernet/intel/ice/Makefile
@@ -27,4 +27,6 @@ obj-$(CONFIG_ICE) += ice.o
  ice-y := ice_main.o   \
         ice_controlq.o \
         ice_common.o   \
-        ice_nvm.o
+        ice_nvm.o      \
+        ice_switch.o   \
+        ice_sched.o
diff --git a/drivers/net/ethernet/intel/ice/ice.h 
b/drivers/net/ethernet/intel/ice/ice.h
index ab2800c31906..f6e3339591bb 100644
--- a/drivers/net/ethernet/intel/ice/ice.h
+++ b/drivers/net/ethernet/intel/ice/ice.h
@@ -30,7 +30,9 @@
  #include <linux/bitmap.h>
  #include "ice_devids.h"
  #include "ice_type.h"
+#include "ice_switch.h"
  #include "ice_common.h"
+#include "ice_sched.h"
#define ICE_BAR0 0
  #define ICE_AQ_LEN            64
diff --git a/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h 
b/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
index 05b22a1ffd70..66a3f41df673 100644
--- a/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
+++ b/drivers/net/ethernet/intel/ice/ice_adminq_cmd.h
@@ -22,6 +22,8 @@
   * descriptor format.  It is shared between Firmware and Software.
   */
+#define ICE_AQC_TOPO_MAX_LEVEL_NUM 0x9
+
  struct ice_aqc_generic {
        __le32 param0;
        __le32 param1;
@@ -82,6 +84,40 @@ struct ice_aqc_req_res {
        u8 reserved[2];
  };
+/* Get function capabilities (indirect 0x000A)
+ * Get device capabilities (indirect 0x000B)
+ */
+struct ice_aqc_list_caps {
+       u8 cmd_flags;
+       u8 pf_index;
+       u8 reserved[2];
+       __le32 count;
+       __le32 addr_high;
+       __le32 addr_low;
+};
+
+/* Device/Function buffer entry, repeated per reported capability */
+struct ice_aqc_list_caps_elem {
+       __le16 cap;
+#define ICE_AQC_CAPS_VSI                               0x0017
+#define ICE_AQC_CAPS_RSS                               0x0040
+#define ICE_AQC_CAPS_RXQS                              0x0041
+#define ICE_AQC_CAPS_TXQS                              0x0042
+#define ICE_AQC_CAPS_MSIX                              0x0043
+#define ICE_AQC_CAPS_MAX_MTU                           0x0047
+
+       u8 major_ver;
+       u8 minor_ver;
+       /* Number of resources described by this capability */
+       __le32 number;
+       /* Only meaningful for some types of resources */
+       __le32 logical_id;
+       /* Only meaningful for some types of resources */
+       __le32 phys_id;
+       __le64 rsvd1;
+       __le64 rsvd2;
+};
+
  /* Clear PXE Command and response (direct 0x0110) */
  struct ice_aqc_clear_pxe {
        u8 rx_cnt;
@@ -89,6 +125,161 @@ struct ice_aqc_clear_pxe {
        u8 reserved[15];
  };
+/* Get switch configuration (0x0200) */
+struct ice_aqc_get_sw_cfg {
+       /* Reserved for command and copy of request flags for response */
+       __le16 flags;
+       /* First desc in case of command and next_elem in case of response
+        * In case of response, if it is not zero, means all the configuration
+        * was not returned and new command shall be sent with this value in
+        * the 'first desc' field
+        */
+       __le16 element;
+       /* Reserved for command, only used for response */
+       __le16 num_elems;
+       __le16 rsvd;
+       __le32 addr_high;
+       __le32 addr_low;
+};
+
+/* Each entry in the response buffer is of the following type: */
+struct ice_aqc_get_sw_cfg_resp_elem {
+       /* VSI/Port Number */
+       __le16 vsi_port_num;
+#define ICE_AQC_GET_SW_CONF_RESP_VSI_PORT_NUM_S        0
+#define ICE_AQC_GET_SW_CONF_RESP_VSI_PORT_NUM_M        \
+                       (0x3FF << ICE_AQC_GET_SW_CONF_RESP_VSI_PORT_NUM_S)
+#define ICE_AQC_GET_SW_CONF_RESP_TYPE_S        14
+#define ICE_AQC_GET_SW_CONF_RESP_TYPE_M        (0x3 << 
ICE_AQC_GET_SW_CONF_RESP_TYPE_S)
+#define ICE_AQC_GET_SW_CONF_RESP_PHYS_PORT     0
+#define ICE_AQC_GET_SW_CONF_RESP_VIRT_PORT     1
+#define ICE_AQC_GET_SW_CONF_RESP_VSI           2
+
+       /* SWID VSI/Port belongs to */
+       __le16 swid;
+
+       /* Bit 14..0 : PF/VF number VSI belongs to
+        * Bit 15 : VF indication bit
+        */
+       __le16 pf_vf_num;
+#define ICE_AQC_GET_SW_CONF_RESP_FUNC_NUM_S    0
+#define ICE_AQC_GET_SW_CONF_RESP_FUNC_NUM_M    \
+                               (0x7FFF << ICE_AQC_GET_SW_CONF_RESP_FUNC_NUM_S)
+#define ICE_AQC_GET_SW_CONF_RESP_IS_VF         BIT(15)
+};
+
+/* The response buffer is as follows. Note that the length of the
+ * elements array varies with the length of the command response.
+ */
+struct ice_aqc_get_sw_cfg_resp {
+       struct ice_aqc_get_sw_cfg_resp_elem elements[1];
+};
+
+/* Add TSE (indirect 0x0401)
+ * Delete TSE (indirect 0x040F)
+ * Move TSE (indirect 0x0408)
+ */
+struct ice_aqc_add_move_delete_elem {
+       __le16 num_grps_req;
+       __le16 num_grps_updated;
+       __le32 reserved;
+       __le32 addr_high;
+       __le32 addr_low;
+};
+
+struct ice_aqc_elem_info_bw {
+       __le16 bw_profile_idx;
+       __le16 bw_alloc;
+};
+
+struct ice_aqc_txsched_elem {
+       u8 elem_type; /* Special field, reserved for some aq calls */
+#define ICE_AQC_ELEM_TYPE_UNDEFINED            0x0
+#define ICE_AQC_ELEM_TYPE_ROOT_PORT            0x1
+#define ICE_AQC_ELEM_TYPE_TC                   0x2
+#define ICE_AQC_ELEM_TYPE_SE_GENERIC           0x3
+#define ICE_AQC_ELEM_TYPE_ENTRY_POINT          0x4
+#define ICE_AQC_ELEM_TYPE_LEAF                 0x5
+#define ICE_AQC_ELEM_TYPE_SE_PADDED            0x6
+       u8 valid_sections;
+#define ICE_AQC_ELEM_VALID_GENERIC             BIT(0)
+#define ICE_AQC_ELEM_VALID_CIR                 BIT(1)
+#define ICE_AQC_ELEM_VALID_EIR                 BIT(2)
+#define ICE_AQC_ELEM_VALID_SHARED              BIT(3)
+       u8 generic;
+#define ICE_AQC_ELEM_GENERIC_MODE_M            0x1
+#define ICE_AQC_ELEM_GENERIC_PRIO_S            0x1
+#define ICE_AQC_ELEM_GENERIC_PRIO_M    (0x7 << ICE_AQC_ELEM_GENERIC_PRIO_S)
+#define ICE_AQC_ELEM_GENERIC_SP_S              0x4
+#define ICE_AQC_ELEM_GENERIC_SP_M      (0x1 << ICE_AQC_ELEM_GENERIC_SP_S)
+#define ICE_AQC_ELEM_GENERIC_ADJUST_VAL_S      0x5
+#define ICE_AQC_ELEM_GENERIC_ADJUST_VAL_M      \
+       (0x3 << ICE_AQC_ELEM_GENERIC_ADJUST_VAL_S)
+       u8 flags; /* Special field, reserved for some aq calls */
+#define ICE_AQC_ELEM_FLAG_SUSPEND_M            0x1
+       struct ice_aqc_elem_info_bw cir_bw;
+       struct ice_aqc_elem_info_bw eir_bw;
+       __le16 srl_id;
+       __le16 reserved2;
+};
+
+struct ice_aqc_txsched_elem_data {
+       __le32 parent_teid;
+       __le32 node_teid;
+       struct ice_aqc_txsched_elem data;
+};
+
+struct ice_aqc_txsched_topo_grp_info_hdr {
+       __le32 parent_teid;
+       __le16 num_elems;
+       __le16 reserved2;
+};
+
+struct ice_aqc_delete_elem {
+       struct ice_aqc_txsched_topo_grp_info_hdr hdr;
+       __le32 teid[1];
+};
+
+/* Query Scheduler Resource Allocation (indirect 0x0412)
+ * This indirect command retrieves the scheduler resources allocated by
+ * EMP Firmware to the given PF.
+ */
+struct ice_aqc_query_txsched_res {
+       u8 reserved[8];
+       __le32 addr_high;
+       __le32 addr_low;
+};
+
+struct ice_aqc_generic_sched_props {
+       __le16 phys_levels;
+       __le16 logical_levels;
+       u8 flattening_bitmap;
+       u8 max_device_cgds;
+       u8 max_pf_cgds;
+       u8 rsvd0;
+       __le16 rdma_qsets;
+       u8 rsvd1[22];
+};
+
+struct ice_aqc_layer_props {
+       u8 logical_layer;
+       u8 chunk_size;
+       __le16 max_device_nodes;
+       __le16 max_pf_nodes;
+       u8 rsvd0[2];
+       __le16 max_shared_rate_lmtr;
+       __le16 max_children;
+       __le16 max_cir_rl_profiles;
+       __le16 max_eir_rl_profiles;
+       __le16 max_srl_profiles;
+       u8 rsvd1[14];
+};
+
+struct ice_aqc_query_txsched_res_resp {
+       struct ice_aqc_generic_sched_props sched_props;
+       struct ice_aqc_layer_props layer_props[ICE_AQC_TOPO_MAX_LEVEL_NUM];
+};
+
  /* NVM Read command (indirect 0x0701)
   * NVM Erase commands (direct 0x0702)
   * NVM Update commands (indirect 0x0703)
@@ -142,6 +333,10 @@ struct ice_aq_desc {
                struct ice_aqc_q_shutdown q_shutdown;
                struct ice_aqc_req_res res_owner;
                struct ice_aqc_clear_pxe clear_pxe;
+               struct ice_aqc_list_caps get_cap;
+               struct ice_aqc_get_sw_cfg get_sw_conf;
+               struct ice_aqc_query_txsched_res query_sched_res;
+               struct ice_aqc_add_move_delete_elem add_move_delete_elem;
                struct ice_aqc_nvm nvm;
        } params;
  };
@@ -150,16 +345,19 @@ struct ice_aq_desc {
  #define ICE_AQ_LG_BUF 512
#define ICE_AQ_FLAG_LB_S 9
+#define ICE_AQ_FLAG_RD_S       10
  #define ICE_AQ_FLAG_BUF_S     12
  #define ICE_AQ_FLAG_SI_S      13
#define ICE_AQ_FLAG_LB BIT(ICE_AQ_FLAG_LB_S) /* 0x200 */
+#define ICE_AQ_FLAG_RD         BIT(ICE_AQ_FLAG_RD_S)  /* 0x400  */
  #define ICE_AQ_FLAG_BUF               BIT(ICE_AQ_FLAG_BUF_S) /* 0x1000 */
  #define ICE_AQ_FLAG_SI                BIT(ICE_AQ_FLAG_SI_S)  /* 0x2000 */
/* error codes */
  enum ice_aq_err {
        ICE_AQ_RC_OK            = 0,  /* success */
+       ICE_AQ_RC_ENOMEM        = 9,  /* Out of memory */
        ICE_AQ_RC_EBUSY         = 12, /* Device or resource busy */
        ICE_AQ_RC_EEXIST        = 13, /* object already exists */
  };
@@ -174,11 +372,22 @@ enum ice_adminq_opc {
        ice_aqc_opc_req_res                             = 0x0008,
        ice_aqc_opc_release_res                         = 0x0009,
+ /* device/function capabilities */
+       ice_aqc_opc_list_func_caps                      = 0x000A,
+       ice_aqc_opc_list_dev_caps                       = 0x000B,
+
        /* PXE */
        ice_aqc_opc_clear_pxe_mode                      = 0x0110,
+ /* internal switch commands */
+       ice_aqc_opc_get_sw_cfg                          = 0x0200,
+
        ice_aqc_opc_clear_pf_cfg                        = 0x02A4,
+ /* transmit scheduler commands */
+       ice_aqc_opc_delete_sched_elems                  = 0x040F,
+       ice_aqc_opc_query_sched_res                     = 0x0412,
+
        /* NVM commands */
        ice_aqc_opc_nvm_read                            = 0x0701,
diff --git a/drivers/net/ethernet/intel/ice/ice_common.c b/drivers/net/ethernet/intel/ice/ice_common.c
index eb3e06488705..a64e406c129a 100644
--- a/drivers/net/ethernet/intel/ice/ice_common.c
+++ b/drivers/net/ethernet/intel/ice/ice_common.c
@@ -16,9 +16,12 @@
   */
#include "ice_common.h"
+#include "ice_sched.h"
  #include "ice_adminq_cmd.h"
#define ICE_PF_RESET_WAIT_COUNT 200
+#define ICE_GET_CAP_BUF_COUNT  40
+#define ICE_GET_CAP_RETRY_COUNT        20
/**
   * ice_set_mac_type - Sets MAC type
@@ -84,8 +87,37 @@ enum ice_status ice_init_hw(struct ice_hw *hw)
        if (status)
                goto err_unroll_cqinit;
+ status = ice_get_caps(hw);
+       if (status)
+               goto err_unroll_cqinit;
+
+       hw->port_info = devm_kzalloc(ice_hw_to_dev(hw),
+                                    sizeof(*hw->port_info), GFP_KERNEL);
+       if (!hw->port_info) {
+               status = ICE_ERR_NO_MEMORY;
+               goto err_unroll_cqinit;
+       }
+
+       /* set the back pointer to hw */
+       hw->port_info->hw = hw;
+
+       /* Initialize port_info struct with switch configuration data */
+       status = ice_get_initial_sw_cfg(hw);
+       if (status)
+               goto err_unroll_alloc;
+
+       /* Query the allocated resources for tx scheduler */
+       status = ice_sched_query_res_alloc(hw);
+       if (status) {
+               ice_debug(hw, ICE_DBG_SCHED,
+                         "Failed to get scheduler allocated resources\n");
+               goto err_unroll_alloc;
+       }
+
        return 0;
+err_unroll_alloc:
+       devm_kfree(ice_hw_to_dev(hw), hw->port_info);
  err_unroll_cqinit:
        ice_shutdown_all_ctrlq(hw);
        return status;
@@ -97,7 +129,12 @@ enum ice_status ice_init_hw(struct ice_hw *hw)
   */
  void ice_deinit_hw(struct ice_hw *hw)
  {
+       ice_sched_cleanup_all(hw);
        ice_shutdown_all_ctrlq(hw);
+       if (hw->port_info) {
+               devm_kfree(ice_hw_to_dev(hw), hw->port_info);
+               hw->port_info = NULL;
+       }
  }
/**
@@ -519,6 +556,192 @@ void ice_release_res(struct ice_hw *hw, enum 
ice_aq_res_ids res)
        }
  }
+/**
+ * ice_parse_caps - parse function/device capabilities
+ * @hw: pointer to the hw struct
+ * @buf: pointer to a buffer containing function/device capability records
+ * @cap_count: number of capability records in the list
+ * @opc: type of capabilities list to parse
+ *
+ * Helper function to parse function(0x000a)/device(0x000b) capabilities list.
+ */
+static void
+ice_parse_caps(struct ice_hw *hw, void *buf, u32 cap_count,
+              enum ice_adminq_opc opc)
+{
+       struct ice_aqc_list_caps_elem *cap_resp;
+       struct ice_hw_func_caps *func_p = NULL;
+       struct ice_hw_dev_caps *dev_p = NULL;
+       struct ice_hw_common_caps *caps;
+       u32 i;
+
+       if (!buf)
+               return;
+
+       cap_resp = (struct ice_aqc_list_caps_elem *)buf;
+
+       if (opc == ice_aqc_opc_list_dev_caps) {
+               dev_p = &hw->dev_caps;
+               caps = &dev_p->common_cap;
+       } else if (opc == ice_aqc_opc_list_func_caps) {
+               func_p = &hw->func_caps;
+               caps = &func_p->common_cap;
+       } else {
+               ice_debug(hw, ICE_DBG_INIT, "wrong opcode\n");
+               return;
+       }
+
+       for (i = 0; caps && i < cap_count; i++, cap_resp++) {
+               u32 logical_id = le32_to_cpu(cap_resp->logical_id);
+               u32 phys_id = le32_to_cpu(cap_resp->phys_id);
+               u32 number = le32_to_cpu(cap_resp->number);
+               u16 cap = le16_to_cpu(cap_resp->cap);
+
+               switch (cap) {
+               case ICE_AQC_CAPS_VSI:
+                       if (dev_p) {
+                               dev_p->num_vsi_allocd_to_host = number;
+                               ice_debug(hw, ICE_DBG_INIT,
+                                         "HW caps: Dev.VSI cnt = %d\n",
+                                         dev_p->num_vsi_allocd_to_host);
+                       } else if (func_p) {
+                               func_p->guaranteed_num_vsi = number;
+                               ice_debug(hw, ICE_DBG_INIT,
+                                         "HW caps: Func.VSI cnt = %d\n",
+                                         func_p->guaranteed_num_vsi);
+                       }
+                       break;
+               case ICE_AQC_CAPS_RSS:
+                       caps->rss_table_size = number;
+                       caps->rss_table_entry_width = logical_id;
+                       ice_debug(hw, ICE_DBG_INIT,
+                                 "HW caps: RSS table size = %d\n",
+                                 caps->rss_table_size);
+                       ice_debug(hw, ICE_DBG_INIT,
+                                 "HW caps: RSS table width = %d\n",
+                                 caps->rss_table_entry_width);
+                       break;
+               case ICE_AQC_CAPS_RXQS:
+                       caps->num_rxq = number;
+                       caps->rxq_first_id = phys_id;
+                       ice_debug(hw, ICE_DBG_INIT,
+                                 "HW caps: Num Rx Qs = %d\n", caps->num_rxq);
+                       ice_debug(hw, ICE_DBG_INIT,
+                                 "HW caps: Rx first queue ID = %d\n",
+                                 caps->rxq_first_id);
+                       break;
+               case ICE_AQC_CAPS_TXQS:
+                       caps->num_txq = number;
+                       caps->txq_first_id = phys_id;
+                       ice_debug(hw, ICE_DBG_INIT,
+                                 "HW caps: Num Tx Qs = %d\n", caps->num_txq);
+                       ice_debug(hw, ICE_DBG_INIT,
+                                 "HW caps: Tx first queue ID = %d\n",
+                                 caps->txq_first_id);
+                       break;
+               case ICE_AQC_CAPS_MSIX:
+                       caps->num_msix_vectors = number;
+                       caps->msix_vector_first_id = phys_id;
+                       ice_debug(hw, ICE_DBG_INIT,
+                                 "HW caps: MSIX vector count = %d\n",
+                                 caps->num_msix_vectors);
+                       ice_debug(hw, ICE_DBG_INIT,
+                                 "HW caps: MSIX first vector index = %d\n",
+                                 caps->msix_vector_first_id);
+                       break;
+               case ICE_AQC_CAPS_MAX_MTU:
+                       caps->max_mtu = number;
+                       if (dev_p)
+                               ice_debug(hw, ICE_DBG_INIT,
+                                         "HW caps: Dev.MaxMTU = %d\n",
+                                         caps->max_mtu);
+                       else if (func_p)
+                               ice_debug(hw, ICE_DBG_INIT,
+                                         "HW caps: func.MaxMTU = %d\n",
+                                         caps->max_mtu);
+                       break;
+               default:
+                       ice_debug(hw, ICE_DBG_INIT,
+                                 "HW caps: Unknown capability[%d]: 0x%x\n", i,
+                                 cap);
+                       break;
+               }
+       }
+}
+
+/**
+ * ice_aq_discover_caps - query function/device capabilities
+ * @hw: pointer to the hw struct
+ * @buf: a virtual buffer to hold the capabilities
+ * @buf_size: Size of the virtual buffer
+ * @data_size: Size of the returned data, or buf size needed if AQ err==ENOMEM
+ * @opc: capabilities type to discover - pass in the command opcode
+ * @cd: pointer to command details structure or NULL
+ *
+ * Get the function(0x000a)/device(0x000b) capabilities description from
+ * the firmware.
+ */
+static enum ice_status
+ice_aq_discover_caps(struct ice_hw *hw, void *buf, u16 buf_size, u16 
*data_size,
+                    enum ice_adminq_opc opc, struct ice_sq_cd *cd)
+{
+       struct ice_aqc_list_caps *cmd;
+       struct ice_aq_desc desc;
+       enum ice_status status;
+
+       cmd = &desc.params.get_cap;
+
+       if (opc != ice_aqc_opc_list_func_caps &&
+           opc != ice_aqc_opc_list_dev_caps)
+               return ICE_ERR_PARAM;
+
+       ice_fill_dflt_direct_cmd_desc(&desc, opc);
+
+       status = ice_aq_send_cmd(hw, &desc, buf, buf_size, cd);
+       if (!status)
+               ice_parse_caps(hw, buf, le32_to_cpu(cmd->count), opc);
+       *data_size = le16_to_cpu(desc.datalen);
+
+       return status;
+}
+
+/**
+ * ice_get_caps - get info about the HW
+ * @hw: pointer to the hardware structure
+ */
+enum ice_status ice_get_caps(struct ice_hw *hw)
+{
+       enum ice_status status;
+       u16 data_size = 0;
+       u16 cbuf_len;
+       u8 retries;
+
+       cbuf_len = ICE_GET_CAP_BUF_COUNT *
+               sizeof(struct ice_aqc_list_caps_elem);
+
+       retries = ICE_GET_CAP_RETRY_COUNT;
+
+       do {
+               void *cbuf;
+
+               cbuf = devm_kzalloc(ice_hw_to_dev(hw), cbuf_len, GFP_KERNEL);
+               if (!cbuf)
+                       return ICE_ERR_NO_MEMORY;
+
+               status = ice_aq_discover_caps(hw, cbuf, cbuf_len, &data_size,
+                                             ice_aqc_opc_list_func_caps, NULL);
+               devm_kfree(ice_hw_to_dev(hw), cbuf);
+
+               if (!status || hw->adminq.sq_last_status != ICE_AQ_RC_ENOMEM)
+                       break;
+
+               /* If ENOMEM is returned, try again with bigger buffer */
+               cbuf_len = data_size;
+       } while (--retries);

If data size is the only reason for a retry, why bother with a retries variable of 20? Is there any reason you won't be given the right data_size hint the first time?

sln

Reply via email to