diff options
Diffstat (limited to 'sys/dev/ice/ice_lib.c')
| -rw-r--r-- | sys/dev/ice/ice_lib.c | 1648 |
1 files changed, 1516 insertions, 132 deletions
diff --git a/sys/dev/ice/ice_lib.c b/sys/dev/ice/ice_lib.c index ecea51a95ae4..501c39eb3986 100644 --- a/sys/dev/ice/ice_lib.c +++ b/sys/dev/ice/ice_lib.c @@ -106,6 +106,15 @@ static uint64_t ice_phy_types_to_max_rate(struct ice_port_info *pi); static void ice_add_sysctls_sw_stats(struct ice_vsi *vsi, struct sysctl_ctx_list *ctx, struct sysctl_oid *parent); +static void +ice_add_sysctls_mac_pfc_one_stat(struct sysctl_ctx_list *ctx, + struct sysctl_oid_list *parent_list, + u64* pfc_stat_location, + const char *node_name, + const char *descr); +static void ice_add_sysctls_mac_pfc_stats(struct sysctl_ctx_list *ctx, + struct sysctl_oid *parent, + struct ice_hw_port_stats *stats); static void ice_setup_vsi_common(struct ice_softc *sc, struct ice_vsi *vsi, enum ice_vsi_type type, int idx, bool dynamic); @@ -141,13 +150,31 @@ ice_print_ldo_tlv(struct ice_softc *sc, static void ice_sysctl_speeds_to_aq_phy_types(u16 sysctl_speeds, u64 *phy_type_low, u64 *phy_type_high); -static u16 ice_apply_supported_speed_filter(u16 report_speeds); +static u16 ice_apply_supported_speed_filter(u16 report_speeds, u8 mod_type); static void ice_handle_health_status_event(struct ice_softc *sc, struct ice_rq_event_info *event); static void ice_print_health_status_string(device_t dev, struct ice_aqc_health_status_elem *elem); +static void +ice_debug_print_mib_change_event(struct ice_softc *sc, + struct ice_rq_event_info *event); +static bool ice_check_ets_bw(u8 *table); +static bool +ice_dcb_needs_reconfig(struct ice_softc *sc, struct ice_dcbx_cfg *old_cfg, + struct ice_dcbx_cfg *new_cfg); +static void ice_dcb_recfg(struct ice_softc *sc); +static u8 ice_dcb_num_tc(u8 tc_map); +static int ice_ets_str_to_tbl(const char *str, u8 *table, u8 limit); +static int ice_pf_vsi_cfg_tc(struct ice_softc *sc, u8 tc_map); +static void ice_sbuf_print_ets_cfg(struct sbuf *sbuf, const char *name, + struct ice_dcb_ets_cfg *ets); +static void ice_stop_pf_vsi(struct ice_softc *sc); +static void ice_vsi_setup_q_map(struct ice_vsi *vsi, struct ice_vsi_ctx *ctxt); +static void ice_do_dcb_reconfig(struct ice_softc *sc); +static int ice_config_pfc(struct ice_softc *sc, u8 new_mode); +static u8 ice_dcb_get_tc_map(const struct ice_dcbx_cfg *dcbcfg); static int ice_module_init(void); static int ice_module_exit(void); @@ -195,6 +222,12 @@ static int ice_sysctl_tx_cso_stat(SYSCTL_HANDLER_ARGS); static int ice_sysctl_rx_cso_stat(SYSCTL_HANDLER_ARGS); static int ice_sysctl_pba_number(SYSCTL_HANDLER_ARGS); static int ice_sysctl_rx_errors_stat(SYSCTL_HANDLER_ARGS); +static int ice_sysctl_dump_dcbx_cfg(SYSCTL_HANDLER_ARGS); +static int ice_sysctl_dump_vsi_cfg(SYSCTL_HANDLER_ARGS); +static int ice_sysctl_ets_min_rate(SYSCTL_HANDLER_ARGS); +static int ice_sysctl_up2tc_map(SYSCTL_HANDLER_ARGS); +static int ice_sysctl_pfc_config(SYSCTL_HANDLER_ARGS); +static int ice_sysctl_query_port_ets(SYSCTL_HANDLER_ARGS); /** * ice_map_bar - Map PCIe BAR memory @@ -607,7 +640,7 @@ ice_initialize_vsi(struct ice_vsi *vsi) } vsi->info = ctx.info; - /* TODO: DCB traffic class support? */ + /* Initialize VSI with just 1 TC to start */ max_txqs[0] = vsi->num_tx_queues; status = ice_cfg_vsi_lan(hw->port_info, vsi->idx, @@ -691,7 +724,12 @@ ice_release_vsi(struct ice_vsi *vsi) ice_del_vsi_sysctl_ctx(vsi); - ice_deinit_vsi(vsi); + /* + * If we unload the driver after a reset fails, we do not need to do + * this step. + */ + if (!ice_test_state(&sc->state, ICE_STATE_RESET_FAILED)) + ice_deinit_vsi(vsi); ice_free_vsi_qmaps(vsi); @@ -1420,12 +1458,14 @@ ice_cfg_vsi_for_tx(struct ice_vsi *vsi) ice_set_ctx(hw, (u8 *)&tlan_ctx, qg->txqs[0].txq_ctx, ice_tlan_ctx_info); - status = ice_ena_vsi_txq(hw->port_info, vsi->idx, 0, - i, 1, qg, qg_size, NULL); + status = ice_ena_vsi_txq(hw->port_info, vsi->idx, txq->tc, + txq->q_handle, 1, qg, qg_size, NULL); if (status) { device_printf(dev, - "Failed to set LAN Tx queue context, err %s aq_err %s\n", - ice_status_str(status), ice_aq_str(hw->adminq.sq_last_status)); + "Failed to set LAN Tx queue %d (TC %d, handle %d) context, err %s aq_err %s\n", + i, txq->tc, txq->q_handle, + ice_status_str(status), + ice_aq_str(hw->adminq.sq_last_status)); err = ENODEV; goto free_txqg; } @@ -1986,6 +2026,9 @@ ice_process_ctrlq_event(struct ice_softc *sc, const char *qname, case ice_mbx_opc_send_msg_to_pf: /* TODO: handle IOV event */ break; + case ice_aqc_opc_fw_logs_event: + ice_handle_fw_log_event(sc, &event->desc, event->msg_buf); + break; case ice_aqc_opc_lldp_set_mib_change: ice_handle_mib_change_event(sc, event); break; @@ -2347,6 +2390,11 @@ ice_update_pf_stats(struct ice_softc *sc) cur_ps = &sc->stats.cur; lport = hw->port_info->lport; +#define ICE_PF_STAT_PFC(name, location, index) \ + ice_stat_update40(hw, name(lport, index), \ + sc->stats.offsets_loaded, \ + &prev_ps->location[index], &cur_ps->location[index]) + #define ICE_PF_STAT40(name, location) \ ice_stat_update40(hw, name ## L(lport), \ sc->stats.offsets_loaded, \ @@ -2386,6 +2434,15 @@ ice_update_pf_stats(struct ice_softc *sc) ICE_PF_STAT40(GLPRT_PTC1522, tx_size_1522); ICE_PF_STAT40(GLPRT_PTC9522, tx_size_big); + /* Update Priority Flow Control Stats */ + for (int i = 0; i <= GLPRT_PXOFFRXC_MAX_INDEX; i++) { + ICE_PF_STAT_PFC(GLPRT_PXONRXC, priority_xon_rx, i); + ICE_PF_STAT_PFC(GLPRT_PXOFFRXC, priority_xoff_rx, i); + ICE_PF_STAT_PFC(GLPRT_PXONTXC, priority_xon_tx, i); + ICE_PF_STAT_PFC(GLPRT_PXOFFTXC, priority_xoff_tx, i); + ICE_PF_STAT_PFC(GLPRT_RXON2OFFCNT, priority_xon_2_xoff, i); + } + ICE_PF_STAT32(GLPRT_LXONRXC, link_xon_rx); ICE_PF_STAT32(GLPRT_LXOFFRXC, link_xoff_rx); ICE_PF_STAT32(GLPRT_LXONTXC, link_xon_tx); @@ -2402,6 +2459,7 @@ ice_update_pf_stats(struct ice_softc *sc) #undef ICE_PF_STAT40 #undef ICE_PF_STAT32 +#undef ICE_PF_STAT_PFC sc->stats.offsets_loaded = true; } @@ -2912,7 +2970,8 @@ ice_intersect_phy_types_and_speeds(struct ice_softc *sc, report_speeds = ice_aq_phy_types_to_link_speeds(phy_data->phy_low_orig, phy_data->phy_high_orig); if (apply_speed_filter) { - temp_speeds = ice_apply_supported_speed_filter(report_speeds); + temp_speeds = ice_apply_supported_speed_filter(report_speeds, + pcaps.module_type[0]); if ((phy_data->user_speeds_orig & temp_speeds) == 0) { device_printf(sc->dev, "User-specified speeds (\"0x%04X\") not supported\n", @@ -3202,9 +3261,18 @@ ice_sysctl_fc_config(SYSCTL_HANDLER_ARGS) } } - /* Set the flow control mode in FW */ + /* Save flow control mode from user */ pi->phy.curr_user_fc_req = new_mode; + /* Turn off Priority Flow Control when Link Flow Control is enabled */ + if ((hw->port_info->qos_cfg.is_sw_lldp) && + (hw->port_info->qos_cfg.local_dcbx_cfg.pfc.pfcena != 0) && + (new_mode != ICE_FC_NONE)) { + ret = ice_config_pfc(sc, 0x0); + if (ret) + return (ret); + } + /* Apply settings requested by user */ return ice_apply_saved_phy_cfg(sc, ICE_APPLY_FC); } @@ -3682,6 +3750,321 @@ retry_start_lldp: return (ret); } +#define ICE_SYSCTL_HELP_ETS_MIN_RATE \ +"\nIn FW DCB mode (fw_lldp_agent=1), displays the current ETS bandwidth table." \ +"\nIn SW DCB mode, displays and allows setting the table." \ +"\nInput must be in the format e.g. 30,10,10,10,10,10,10,10" \ +"\nWhere the bandwidth total must add up to 100" + +/** + * ice_sysctl_ets_min_rate - Report/configure ETS bandwidth + * @oidp: sysctl oid structure + * @arg1: pointer to private data structure + * @arg2: unused + * @req: sysctl request pointer + * + * Returns the current ETS TC bandwidth table + * cached by the driver. + * + * In SW DCB mode this sysctl also accepts a value that will + * be sent to the firmware for configuration. + */ +static int +ice_sysctl_ets_min_rate(SYSCTL_HANDLER_ARGS) +{ + struct ice_softc *sc = (struct ice_softc *)arg1; + struct ice_dcbx_cfg *local_dcbx_cfg; + struct ice_port_info *pi; + struct ice_hw *hw = &sc->hw; + device_t dev = sc->dev; + enum ice_status status; + struct sbuf *sbuf; + int ret; + + /* Store input rates from user */ + char ets_user_buf[128] = ""; + u8 new_ets_table[ICE_MAX_TRAFFIC_CLASS] = {}; + + UNREFERENCED_PARAMETER(arg2); + + if (ice_driver_is_detaching(sc)) + return (ESHUTDOWN); + + if (req->oldptr == NULL && req->newptr == NULL) { + ret = SYSCTL_OUT(req, 0, 128); + return (ret); + } + + pi = hw->port_info; + local_dcbx_cfg = &pi->qos_cfg.local_dcbx_cfg; + + sbuf = sbuf_new(NULL, ets_user_buf, 128, SBUF_FIXEDLEN | SBUF_INCLUDENUL); + + /* Format ETS BW data for output */ + for (int i = 0; i < ICE_MAX_TRAFFIC_CLASS; i++) { + sbuf_printf(sbuf, "%d", local_dcbx_cfg->etscfg.tcbwtable[i]); + if (i != ICE_MAX_TRAFFIC_CLASS - 1) + sbuf_printf(sbuf, ","); + } + + sbuf_finish(sbuf); + sbuf_delete(sbuf); + + /* Read in the new ETS values */ + ret = sysctl_handle_string(oidp, ets_user_buf, sizeof(ets_user_buf), req); + if ((ret) || (req->newptr == NULL)) + return (ret); + + /* Don't allow setting changes in FW DCB mode */ + if (!hw->port_info->qos_cfg.is_sw_lldp) + return (EPERM); + + ret = ice_ets_str_to_tbl(ets_user_buf, new_ets_table, 100); + if (ret) { + device_printf(dev, "%s: Could not parse input BW table: %s\n", + __func__, ets_user_buf); + return (ret); + } + + if (!ice_check_ets_bw(new_ets_table)) { + device_printf(dev, "%s: Bandwidth sum does not equal 100: %s\n", + __func__, ets_user_buf); + return (EINVAL); + } + + memcpy(local_dcbx_cfg->etscfg.tcbwtable, new_ets_table, + sizeof(new_ets_table)); + + /* If BW > 0, then set TSA entry to 2 */ + for (int i = 0; i < ICE_MAX_TRAFFIC_CLASS; i++) { + if (new_ets_table[i] > 0) + local_dcbx_cfg->etscfg.tsatable[i] = 2; + else + local_dcbx_cfg->etscfg.tsatable[i] = 0; + } + local_dcbx_cfg->etscfg.willing = 0; + local_dcbx_cfg->etsrec = local_dcbx_cfg->etscfg; + local_dcbx_cfg->app_mode = ICE_DCBX_APPS_NON_WILLING; + + status = ice_set_dcb_cfg(pi); + if (status) { + device_printf(dev, + "%s: Failed to set DCB config; status %s, aq_err %s\n", + __func__, ice_status_str(status), + ice_aq_str(hw->adminq.sq_last_status)); + return (EIO); + } + + ice_do_dcb_reconfig(sc); + + return (0); +} + +#define ICE_SYSCTL_HELP_UP2TC_MAP \ +"\nIn FW DCB mode (fw_lldp_agent=1), displays the current ETS priority assignment table." \ +"\nIn SW DCB mode, displays and allows setting the table." \ +"\nInput must be in this format: 0,1,2,3,4,5,6,7" \ +"\nWhere the 1st number is the TC for UP0, 2nd number is the TC for UP1, etc" + +/** + * ice_sysctl_up2tc_map - Report or configure UP2TC mapping + * @oidp: sysctl oid structure + * @arg1: pointer to private data structure + * @arg2: unused + * @req: sysctl request pointer + * + * In FW DCB mode, returns the current ETS prio table / + * UP2TC mapping from the local MIB. + * + * In SW DCB mode this sysctl also accepts a value that will + * be sent to the firmware for configuration. + */ +static int +ice_sysctl_up2tc_map(SYSCTL_HANDLER_ARGS) +{ + struct ice_softc *sc = (struct ice_softc *)arg1; + struct ice_dcbx_cfg *local_dcbx_cfg; + struct ice_port_info *pi; + struct ice_hw *hw = &sc->hw; + device_t dev = sc->dev; + enum ice_status status; + struct sbuf *sbuf; + int ret; + + /* Store input rates from user */ + char up2tc_user_buf[128] = ""; + /* This array is indexed by UP, not TC */ + u8 new_up2tc[ICE_MAX_TRAFFIC_CLASS] = {}; + + UNREFERENCED_PARAMETER(arg2); + + if (ice_driver_is_detaching(sc)) + return (ESHUTDOWN); + + if (req->oldptr == NULL && req->newptr == NULL) { + ret = SYSCTL_OUT(req, 0, 128); + return (ret); + } + + pi = hw->port_info; + local_dcbx_cfg = &pi->qos_cfg.local_dcbx_cfg; + + sbuf = sbuf_new(NULL, up2tc_user_buf, 128, SBUF_FIXEDLEN | SBUF_INCLUDENUL); + + /* Format ETS Priority Mapping Table for output */ + for (int i = 0; i < ICE_MAX_TRAFFIC_CLASS; i++) { + sbuf_printf(sbuf, "%d", local_dcbx_cfg->etscfg.prio_table[i]); + if (i != ICE_MAX_TRAFFIC_CLASS - 1) + sbuf_printf(sbuf, ","); + } + + sbuf_finish(sbuf); + sbuf_delete(sbuf); + + /* Read in the new ETS priority mapping */ + ret = sysctl_handle_string(oidp, up2tc_user_buf, sizeof(up2tc_user_buf), req); + if ((ret) || (req->newptr == NULL)) + return (ret); + + /* Don't allow setting changes in FW DCB mode */ + if (!hw->port_info->qos_cfg.is_sw_lldp) + return (EPERM); + + ret = ice_ets_str_to_tbl(up2tc_user_buf, new_up2tc, 7); + if (ret) { + device_printf(dev, "%s: Could not parse input priority assignment table: %s\n", + __func__, up2tc_user_buf); + return (ret); + } + + /* Prepare updated ETS TLV */ + memcpy(local_dcbx_cfg->etscfg.prio_table, new_up2tc, + sizeof(new_up2tc)); + + status = ice_set_dcb_cfg(pi); + if (status) { + device_printf(dev, + "%s: Failed to set DCB config; status %s, aq_err %s\n", + __func__, ice_status_str(status), + ice_aq_str(hw->adminq.sq_last_status)); + return (EIO); + } + + ice_do_dcb_reconfig(sc); + + return (0); +} + +/** + * ice_config_pfc - helper function to set PFC config in FW + * @sc: device private structure + * @new_mode: bit flags indicating PFC status for TCs + * + * @pre must be in SW DCB mode + * + * Configures the driver's local PFC TLV and sends it to the + * FW for configuration, then reconfigures the driver/VSI + * for DCB if needed. + */ +static int +ice_config_pfc(struct ice_softc *sc, u8 new_mode) +{ + struct ice_dcbx_cfg *local_dcbx_cfg; + struct ice_hw *hw = &sc->hw; + struct ice_port_info *pi; + device_t dev = sc->dev; + enum ice_status status; + + pi = hw->port_info; + local_dcbx_cfg = &pi->qos_cfg.local_dcbx_cfg; + + /* Prepare updated PFC TLV */ + local_dcbx_cfg->pfc.pfcena = new_mode; + local_dcbx_cfg->pfc.pfccap = ICE_MAX_TRAFFIC_CLASS; + local_dcbx_cfg->pfc.willing = 0; + local_dcbx_cfg->pfc.mbc = 0; + + status = ice_set_dcb_cfg(pi); + if (status) { + device_printf(dev, + "%s: Failed to set DCB config; status %s, aq_err %s\n", + __func__, ice_status_str(status), + ice_aq_str(hw->adminq.sq_last_status)); + return (EIO); + } + + ice_do_dcb_reconfig(sc); + + return (0); +} + +#define ICE_SYSCTL_HELP_PFC_CONFIG \ +"\nIn FW DCB mode (fw_lldp_agent=1), displays the current Priority Flow Control configuration" \ +"\nIn SW DCB mode, displays and allows setting the configuration" \ +"\nInput/Output is in this format: 0xff" \ +"\nWhere bit position # enables/disables PFC for that Traffic Class #" + +/** + * ice_sysctl_pfc_config - Report or configure enabled PFC TCs + * @oidp: sysctl oid structure + * @arg1: pointer to private data structure + * @arg2: unused + * @req: sysctl request pointer + * + * In FW DCB mode, returns a bitmap containing the current TCs + * that have PFC enabled on them. + * + * In SW DCB mode this sysctl also accepts a value that will + * be sent to the firmware for configuration. + */ +static int +ice_sysctl_pfc_config(SYSCTL_HANDLER_ARGS) +{ + struct ice_softc *sc = (struct ice_softc *)arg1; + struct ice_dcbx_cfg *local_dcbx_cfg; + struct ice_port_info *pi; + struct ice_hw *hw = &sc->hw; + int ret; + + /* Store input flags from user */ + u8 user_pfc; + + UNREFERENCED_PARAMETER(arg2); + + if (ice_driver_is_detaching(sc)) + return (ESHUTDOWN); + + if (req->oldptr == NULL && req->newptr == NULL) { + ret = SYSCTL_OUT(req, 0, sizeof(u8)); + return (ret); + } + + pi = hw->port_info; + local_dcbx_cfg = &pi->qos_cfg.local_dcbx_cfg; + + /* Format current PFC enable setting for output */ + user_pfc = local_dcbx_cfg->pfc.pfcena; + + /* Read in the new PFC config */ + ret = sysctl_handle_8(oidp, &user_pfc, 0, req); + if ((ret) || (req->newptr == NULL)) + return (ret); + + /* Don't allow setting changes in FW DCB mode */ + if (!hw->port_info->qos_cfg.is_sw_lldp) + return (EPERM); + + /* If LFC is active and PFC is going to be turned on, turn LFC off */ + if (user_pfc != 0 && pi->phy.curr_user_fc_req != ICE_FC_NONE) { + pi->phy.curr_user_fc_req = ICE_FC_NONE; + ret = ice_apply_saved_phy_cfg(sc, ICE_APPLY_FC); + if (ret) + return (ret); + } + + return ice_config_pfc(sc, user_pfc); +} + /** * ice_add_device_sysctls - add device specific dynamic sysctls * @sc: device private structure @@ -3707,9 +4090,11 @@ ice_add_device_sysctls(struct ice_softc *sc) OID_AUTO, "fw_version", CTLTYPE_STRING | CTLFLAG_RD, sc, 0, ice_sysctl_show_fw, "A", "Firmware version"); - SYSCTL_ADD_PROC(ctx, ctx_list, - OID_AUTO, "pba_number", CTLTYPE_STRING | CTLFLAG_RD, - sc, 0, ice_sysctl_pba_number, "A", "Product Board Assembly Number"); + if (ice_is_bit_set(sc->feat_en, ICE_FEATURE_HAS_PBA)) { + SYSCTL_ADD_PROC(ctx, ctx_list, + OID_AUTO, "pba_number", CTLTYPE_STRING | CTLFLAG_RD, sc, 0, + ice_sysctl_pba_number, "A", "Product Board Assembly Number"); + } SYSCTL_ADD_PROC(ctx, ctx_list, OID_AUTO, "ddp_version", CTLTYPE_STRING | CTLFLAG_RD, @@ -3739,6 +4124,18 @@ ice_add_device_sysctls(struct ice_softc *sc) OID_AUTO, "fw_lldp_agent", CTLTYPE_U8 | CTLFLAG_RWTUN, sc, 0, ice_sysctl_fw_lldp_agent, "CU", ICE_SYSCTL_HELP_FW_LLDP_AGENT); + SYSCTL_ADD_PROC(ctx, ctx_list, + OID_AUTO, "ets_min_rate", CTLTYPE_STRING | CTLFLAG_RW, + sc, 0, ice_sysctl_ets_min_rate, "A", ICE_SYSCTL_HELP_ETS_MIN_RATE); + + SYSCTL_ADD_PROC(ctx, ctx_list, + OID_AUTO, "up2tc_map", CTLTYPE_STRING | CTLFLAG_RW, + sc, 0, ice_sysctl_up2tc_map, "A", ICE_SYSCTL_HELP_UP2TC_MAP); + + SYSCTL_ADD_PROC(ctx, ctx_list, + OID_AUTO, "pfc", CTLTYPE_U8 | CTLFLAG_RW, + sc, 0, ice_sysctl_pfc_config, "CU", ICE_SYSCTL_HELP_PFC_CONFIG); + /* Differentiate software and hardware statistics, by keeping hw stats * in their own node. This isn't in ice_add_device_tunables, because * we won't have any CTLFLAG_TUN sysctls under this node. @@ -4120,7 +4517,7 @@ ice_add_vsi_sysctls(struct ice_vsi *vsi) { struct sysctl_ctx_list *ctx = &vsi->ctx; struct sysctl_oid *hw_node, *sw_node; - struct sysctl_oid_list *vsi_list, *hw_list, *sw_list; + struct sysctl_oid_list *vsi_list, *hw_list; vsi_list = SYSCTL_CHILDREN(vsi->vsi_node); @@ -4152,12 +4549,89 @@ ice_add_vsi_sysctls(struct ice_vsi *vsi) /* Add a node for statistics tracked by software. */ sw_node = SYSCTL_ADD_NODE(ctx, vsi_list, OID_AUTO, "sw", CTLFLAG_RD, NULL, "VSI Software Statistics"); - sw_list = SYSCTL_CHILDREN(sw_node); ice_add_sysctls_sw_stats(vsi, ctx, sw_node); } /** + * ice_add_sysctls_mac_pfc_one_stat - Add sysctl node for a PFC statistic + * @ctx: sysctl ctx to use + * @parent_list: parent sysctl list to add sysctls under + * @pfc_stat_location: address of statistic for sysctl to display + * @node_name: Name for statistic node + * @descr: Description used for nodes added in this function + * + * A helper function for ice_add_sysctls_mac_pfc_stats that adds a node + * for a stat and leaves for each traffic class for that stat. + */ +static void +ice_add_sysctls_mac_pfc_one_stat(struct sysctl_ctx_list *ctx, + struct sysctl_oid_list *parent_list, + u64* pfc_stat_location, + const char *node_name, + const char *descr) +{ + struct sysctl_oid_list *node_list; + struct sysctl_oid *node; + struct sbuf *namebuf, *descbuf; + + node = SYSCTL_ADD_NODE(ctx, parent_list, OID_AUTO, node_name, CTLFLAG_RD, + NULL, descr); + node_list = SYSCTL_CHILDREN(node); + + namebuf = sbuf_new_auto(); + descbuf = sbuf_new_auto(); + for (int i = 0; i < ICE_MAX_DCB_TCS; i++) { + sbuf_clear(namebuf); + sbuf_clear(descbuf); + + sbuf_printf(namebuf, "%d", i); + sbuf_printf(descbuf, "%s for TC %d", descr, i); + + sbuf_finish(namebuf); + sbuf_finish(descbuf); + + SYSCTL_ADD_U64(ctx, node_list, OID_AUTO, sbuf_data(namebuf), + CTLFLAG_RD | CTLFLAG_STATS, &pfc_stat_location[i], 0, + sbuf_data(descbuf)); + } + + sbuf_delete(namebuf); + sbuf_delete(descbuf); +} + +/** + * ice_add_sysctls_mac_pfc_stats - Add sysctls for MAC PFC statistics + * @ctx: the sysctl ctx to use + * @parent: parent node to add the sysctls under + * @stats: the hw ports stat structure to pull values from + * + * Add global Priority Flow Control MAC statistics sysctls. These are + * structured as a node with the PFC statistic, where there are eight + * nodes for each traffic class. + */ +static void +ice_add_sysctls_mac_pfc_stats(struct sysctl_ctx_list *ctx, + struct sysctl_oid *parent, + struct ice_hw_port_stats *stats) +{ + struct sysctl_oid_list *parent_list; + + parent_list = SYSCTL_CHILDREN(parent); + + ice_add_sysctls_mac_pfc_one_stat(ctx, parent_list, stats->priority_xon_rx, + "p_xon_recvd", "PFC XON received"); + ice_add_sysctls_mac_pfc_one_stat(ctx, parent_list, stats->priority_xoff_rx, + "p_xoff_recvd", "PFC XOFF received"); + ice_add_sysctls_mac_pfc_one_stat(ctx, parent_list, stats->priority_xon_tx, + "p_xon_txd", "PFC XON transmitted"); + ice_add_sysctls_mac_pfc_one_stat(ctx, parent_list, stats->priority_xoff_tx, + "p_xoff_txd", "PFC XOFF transmitted"); + ice_add_sysctls_mac_pfc_one_stat(ctx, parent_list, stats->priority_xon_2_xoff, + "p_xon2xoff", "PFC XON to XOFF transitions"); +} + +/** * ice_add_sysctls_mac_stats - Add sysctls for global MAC statistics * @ctx: the sysctl ctx to use * @parent: parent node to add the sysctls under @@ -4179,9 +4653,12 @@ ice_add_sysctls_mac_stats(struct sysctl_ctx_list *ctx, NULL, "Mac Hardware Statistics"); mac_list = SYSCTL_CHILDREN(mac_node); - /* add the common ethernet statistics */ + /* Add the ethernet statistics common to VSI and MAC */ ice_add_sysctls_eth_stats(ctx, mac_node, &stats->eth); + /* Add PFC stats that add per-TC counters */ + ice_add_sysctls_mac_pfc_stats(ctx, mac_node, stats); + const struct ice_sysctl_info ctls[] = { /* Packet Reception Stats */ {&stats->rx_size_64, "rx_frames_64", "64 byte frames received"}, @@ -5129,23 +5606,27 @@ ice_add_debug_tunables(struct ice_softc *sc) debug_list = SYSCTL_CHILDREN(sc->debug_sysctls); SYSCTL_ADD_U64(ctx, debug_list, OID_AUTO, "debug_mask", - CTLFLAG_RW | CTLFLAG_TUN, &sc->hw.debug_mask, 0, + ICE_CTLFLAG_DEBUG | CTLFLAG_RW | CTLFLAG_TUN, + &sc->hw.debug_mask, 0, "Debug message enable/disable mask"); /* Load the default value from the global sysctl first */ sc->enable_tx_fc_filter = ice_enable_tx_fc_filter; SYSCTL_ADD_BOOL(ctx, debug_list, OID_AUTO, "enable_tx_fc_filter", - CTLFLAG_RDTUN, &sc->enable_tx_fc_filter, 0, + ICE_CTLFLAG_DEBUG | CTLFLAG_RDTUN, + &sc->enable_tx_fc_filter, 0, "Drop Ethertype 0x8808 control frames originating from software on this PF"); /* Load the default value from the global sysctl first */ sc->enable_tx_lldp_filter = ice_enable_tx_lldp_filter; SYSCTL_ADD_BOOL(ctx, debug_list, OID_AUTO, "enable_tx_lldp_filter", - CTLFLAG_RDTUN, &sc->enable_tx_lldp_filter, 0, + ICE_CTLFLAG_DEBUG | CTLFLAG_RDTUN, + &sc->enable_tx_lldp_filter, 0, "Drop Ethertype 0x88cc LLDP frames originating from software on this PF"); + ice_add_fw_logging_tunables(sc, sc->debug_sysctls); } #define ICE_SYSCTL_HELP_REQUEST_RESET \ @@ -5302,105 +5783,146 @@ ice_add_debug_sysctls(struct ice_softc *sc) debug_list = SYSCTL_CHILDREN(sc->debug_sysctls); SYSCTL_ADD_PROC(ctx, debug_list, OID_AUTO, "request_reset", - CTLTYPE_STRING | CTLFLAG_WR, sc, 0, + ICE_CTLFLAG_DEBUG | CTLTYPE_STRING | CTLFLAG_WR, sc, 0, ice_sysctl_request_reset, "A", ICE_SYSCTL_HELP_REQUEST_RESET); - SYSCTL_ADD_U32(ctx, debug_list, OID_AUTO, "pfr_count", CTLFLAG_RD, - &sc->soft_stats.pfr_count, 0, "# of PF resets handled"); + SYSCTL_ADD_U32(ctx, debug_list, OID_AUTO, "pfr_count", + ICE_CTLFLAG_DEBUG | CTLFLAG_RD, + &sc->soft_stats.pfr_count, 0, + "# of PF resets handled"); - SYSCTL_ADD_U32(ctx, debug_list, OID_AUTO, "corer_count", CTLFLAG_RD, - &sc->soft_stats.corer_count, 0, "# of CORE resets handled"); + SYSCTL_ADD_U32(ctx, debug_list, OID_AUTO, "corer_count", + ICE_CTLFLAG_DEBUG | CTLFLAG_RD, + &sc->soft_stats.corer_count, 0, + "# of CORE resets handled"); - SYSCTL_ADD_U32(ctx, debug_list, OID_AUTO, "globr_count", CTLFLAG_RD, - &sc->soft_stats.globr_count, 0, "# of Global resets handled"); + SYSCTL_ADD_U32(ctx, debug_list, OID_AUTO, "globr_count", + ICE_CTLFLAG_DEBUG | CTLFLAG_RD, + &sc->soft_stats.globr_count, 0, + "# of Global resets handled"); - SYSCTL_ADD_U32(ctx, debug_list, OID_AUTO, "empr_count", CTLFLAG_RD, - &sc->soft_stats.empr_count, 0, "# of EMP resets handled"); + SYSCTL_ADD_U32(ctx, debug_list, OID_AUTO, "empr_count", + ICE_CTLFLAG_DEBUG | CTLFLAG_RD, + &sc->soft_stats.empr_count, 0, + "# of EMP resets handled"); - SYSCTL_ADD_U32(ctx, debug_list, OID_AUTO, "tx_mdd_count", CTLFLAG_RD, - &sc->soft_stats.tx_mdd_count, 0, "# of Tx MDD events detected"); + SYSCTL_ADD_U32(ctx, debug_list, OID_AUTO, "tx_mdd_count", + ICE_CTLFLAG_DEBUG | CTLFLAG_RD, + &sc->soft_stats.tx_mdd_count, 0, + "# of Tx MDD events detected"); - SYSCTL_ADD_U32(ctx, debug_list, OID_AUTO, "rx_mdd_count", CTLFLAG_RD, - &sc->soft_stats.rx_mdd_count, 0, "# of Rx MDD events detected"); + SYSCTL_ADD_U32(ctx, debug_list, OID_AUTO, "rx_mdd_count", + ICE_CTLFLAG_DEBUG | CTLFLAG_RD, + &sc->soft_stats.rx_mdd_count, 0, + "# of Rx MDD events detected"); - SYSCTL_ADD_PROC(ctx, debug_list, - OID_AUTO, "state", CTLTYPE_STRING | CTLFLAG_RD, - sc, 0, ice_sysctl_dump_state_flags, "A", "Driver State Flags"); + SYSCTL_ADD_PROC(ctx, debug_list, OID_AUTO, "state", + ICE_CTLFLAG_DEBUG | CTLTYPE_STRING | CTLFLAG_RD, sc, 0, + ice_sysctl_dump_state_flags, "A", + "Driver State Flags"); - SYSCTL_ADD_PROC(ctx, debug_list, - OID_AUTO, "phy_type_low", CTLTYPE_U64 | CTLFLAG_RW, - sc, 0, ice_sysctl_phy_type_low, "QU", + SYSCTL_ADD_PROC(ctx, debug_list, OID_AUTO, "phy_type_low", + ICE_CTLFLAG_DEBUG | CTLTYPE_U64 | CTLFLAG_RW, sc, 0, + ice_sysctl_phy_type_low, "QU", "PHY type Low from Get PHY Caps/Set PHY Cfg"); - SYSCTL_ADD_PROC(ctx, debug_list, - OID_AUTO, "phy_type_high", CTLTYPE_U64 | CTLFLAG_RW, - sc, 0, ice_sysctl_phy_type_high, "QU", + SYSCTL_ADD_PROC(ctx, debug_list, OID_AUTO, "phy_type_high", + ICE_CTLFLAG_DEBUG | CTLTYPE_U64 | CTLFLAG_RW, sc, 0, + ice_sysctl_phy_type_high, "QU", "PHY type High from Get PHY Caps/Set PHY Cfg"); - SYSCTL_ADD_PROC(ctx, debug_list, - OID_AUTO, "phy_sw_caps", CTLTYPE_STRUCT | CTLFLAG_RD, - sc, 0, ice_sysctl_phy_sw_caps, "", + SYSCTL_ADD_PROC(ctx, debug_list, OID_AUTO, "phy_sw_caps", + ICE_CTLFLAG_DEBUG | CTLTYPE_STRUCT | CTLFLAG_RD, sc, 0, + ice_sysctl_phy_sw_caps, "", "Get PHY Capabilities (Software configuration)"); - SYSCTL_ADD_PROC(ctx, debug_list, - OID_AUTO, "phy_nvm_caps", CTLTYPE_STRUCT | CTLFLAG_RD, - sc, 0, ice_sysctl_phy_nvm_caps, "", + SYSCTL_ADD_PROC(ctx, debug_list, OID_AUTO, "phy_nvm_caps", + ICE_CTLFLAG_DEBUG | CTLTYPE_STRUCT | CTLFLAG_RD, sc, 0, + ice_sysctl_phy_nvm_caps, "", "Get PHY Capabilities (NVM configuration)"); - SYSCTL_ADD_PROC(ctx, debug_list, - OID_AUTO, "phy_topo_caps", CTLTYPE_STRUCT | CTLFLAG_RD, - sc, 0, ice_sysctl_phy_topo_caps, "", + SYSCTL_ADD_PROC(ctx, debug_list, OID_AUTO, "phy_topo_caps", + ICE_CTLFLAG_DEBUG | CTLTYPE_STRUCT | CTLFLAG_RD, sc, 0, + ice_sysctl_phy_topo_caps, "", "Get PHY Capabilities (Topology configuration)"); - SYSCTL_ADD_PROC(ctx, debug_list, - OID_AUTO, "phy_link_status", CTLTYPE_STRUCT | CTLFLAG_RD, - sc, 0, ice_sysctl_phy_link_status, "", + SYSCTL_ADD_PROC(ctx, debug_list, OID_AUTO, "phy_link_status", + ICE_CTLFLAG_DEBUG | CTLTYPE_STRUCT | CTLFLAG_RD, sc, 0, + ice_sysctl_phy_link_status, "", "Get PHY Link Status"); - SYSCTL_ADD_PROC(ctx, debug_list, - OID_AUTO, "read_i2c_diag_data", CTLTYPE_STRING | CTLFLAG_RD, - sc, 0, ice_sysctl_read_i2c_diag_data, "A", + SYSCTL_ADD_PROC(ctx, debug_list, OID_AUTO, "read_i2c_diag_data", + ICE_CTLFLAG_DEBUG | CTLTYPE_STRING | CTLFLAG_RD, sc, 0, + ice_sysctl_read_i2c_diag_data, "A", "Dump selected diagnostic data from FW"); - SYSCTL_ADD_U32(ctx, debug_list, OID_AUTO, "fw_build", CTLFLAG_RD, - &sc->hw.fw_build, 0, "FW Build ID"); + SYSCTL_ADD_U32(ctx, debug_list, OID_AUTO, "fw_build", + ICE_CTLFLAG_DEBUG | CTLFLAG_RD, &sc->hw.fw_build, 0, + "FW Build ID"); - SYSCTL_ADD_PROC(ctx, debug_list, OID_AUTO, "os_ddp_version", CTLTYPE_STRING | CTLFLAG_RD, - sc, 0, ice_sysctl_os_pkg_version, "A", + SYSCTL_ADD_PROC(ctx, debug_list, OID_AUTO, "os_ddp_version", + ICE_CTLFLAG_DEBUG | CTLTYPE_STRING | CTLFLAG_RD, sc, 0, + ice_sysctl_os_pkg_version, "A", "DDP package name and version found in ice_ddp"); - SYSCTL_ADD_PROC(ctx, debug_list, - OID_AUTO, "cur_lldp_persist_status", CTLTYPE_STRING | CTLFLAG_RD, - sc, 0, ice_sysctl_fw_cur_lldp_persist_status, "A", "Current LLDP persistent status"); + SYSCTL_ADD_PROC(ctx, debug_list, OID_AUTO, "cur_lldp_persist_status", + ICE_CTLFLAG_DEBUG | CTLTYPE_STRING | CTLFLAG_RD, sc, 0, + ice_sysctl_fw_cur_lldp_persist_status, "A", + "Current LLDP persistent status"); - SYSCTL_ADD_PROC(ctx, debug_list, - OID_AUTO, "dflt_lldp_persist_status", CTLTYPE_STRING | CTLFLAG_RD, - sc, 0, ice_sysctl_fw_dflt_lldp_persist_status, "A", "Default LLDP persistent status"); + SYSCTL_ADD_PROC(ctx, debug_list, OID_AUTO, "dflt_lldp_persist_status", + ICE_CTLFLAG_DEBUG | CTLTYPE_STRING | CTLFLAG_RD, sc, 0, + ice_sysctl_fw_dflt_lldp_persist_status, "A", + "Default LLDP persistent status"); - SYSCTL_ADD_PROC(ctx, debug_list, - OID_AUTO, "negotiated_fc", CTLTYPE_STRING | CTLFLAG_RD, - sc, 0, ice_sysctl_negotiated_fc, "A", "Current Negotiated Flow Control mode"); + SYSCTL_ADD_PROC(ctx, debug_list, OID_AUTO, "negotiated_fc", + ICE_CTLFLAG_DEBUG | CTLTYPE_STRING | CTLFLAG_RD, sc, 0, + ice_sysctl_negotiated_fc, "A", + "Current Negotiated Flow Control mode"); + + SYSCTL_ADD_PROC(ctx, debug_list, OID_AUTO, "local_dcbx_cfg", + CTLTYPE_STRING | CTLFLAG_RD, sc, ICE_AQ_LLDP_MIB_LOCAL, + ice_sysctl_dump_dcbx_cfg, "A", + "Dumps Local MIB information from firmware"); + + SYSCTL_ADD_PROC(ctx, debug_list, OID_AUTO, "remote_dcbx_cfg", + CTLTYPE_STRING | CTLFLAG_RD, sc, ICE_AQ_LLDP_MIB_REMOTE, + ice_sysctl_dump_dcbx_cfg, "A", + "Dumps Remote MIB information from firmware"); + + SYSCTL_ADD_PROC(ctx, debug_list, OID_AUTO, "pf_vsi_cfg", CTLTYPE_STRING | CTLFLAG_RD, + sc, 0, ice_sysctl_dump_vsi_cfg, "A", + "Dumps Selected PF VSI parameters from firmware"); + + SYSCTL_ADD_PROC(ctx, debug_list, OID_AUTO, "query_port_ets", CTLTYPE_STRING | CTLFLAG_RD, + sc, 0, ice_sysctl_query_port_ets, "A", + "Prints selected output from Query Port ETS AQ command"); sw_node = SYSCTL_ADD_NODE(ctx, debug_list, OID_AUTO, "switch", - CTLFLAG_RD, NULL, "Switch Configuration"); + ICE_CTLFLAG_DEBUG | CTLFLAG_RD, NULL, + "Switch Configuration"); sw_list = SYSCTL_CHILDREN(sw_node); - SYSCTL_ADD_PROC(ctx, sw_list, - OID_AUTO, "mac_filters", CTLTYPE_STRING | CTLFLAG_RD, - sc, 0, ice_sysctl_dump_mac_filters, "A", "MAC Filters"); + SYSCTL_ADD_PROC(ctx, sw_list, OID_AUTO, "mac_filters", + ICE_CTLFLAG_DEBUG | CTLTYPE_STRING | CTLFLAG_RD, sc, 0, + ice_sysctl_dump_mac_filters, "A", + "MAC Filters"); - SYSCTL_ADD_PROC(ctx, sw_list, - OID_AUTO, "vlan_filters", CTLTYPE_STRING | CTLFLAG_RD, - sc, 0, ice_sysctl_dump_vlan_filters, "A", "VLAN Filters"); + SYSCTL_ADD_PROC(ctx, sw_list, OID_AUTO, "vlan_filters", + ICE_CTLFLAG_DEBUG | CTLTYPE_STRING | CTLFLAG_RD, sc, 0, + ice_sysctl_dump_vlan_filters, "A", + "VLAN Filters"); - SYSCTL_ADD_PROC(ctx, sw_list, - OID_AUTO, "ethertype_filters", CTLTYPE_STRING | CTLFLAG_RD, - sc, 0, ice_sysctl_dump_ethertype_filters, "A", "Ethertype Filters"); + SYSCTL_ADD_PROC(ctx, sw_list, OID_AUTO, "ethertype_filters", + ICE_CTLFLAG_DEBUG | CTLTYPE_STRING | CTLFLAG_RD, sc, 0, + ice_sysctl_dump_ethertype_filters, "A", + "Ethertype Filters"); - SYSCTL_ADD_PROC(ctx, sw_list, - OID_AUTO, "ethertype_mac_filters", CTLTYPE_STRING | CTLFLAG_RD, - sc, 0, ice_sysctl_dump_ethertype_mac_filters, "A", "Ethertype/MAC Filters"); + SYSCTL_ADD_PROC(ctx, sw_list, OID_AUTO, "ethertype_mac_filters", + ICE_CTLFLAG_DEBUG | CTLTYPE_STRING | CTLFLAG_RD, sc, 0, + ice_sysctl_dump_ethertype_mac_filters, "A", + "Ethertype/MAC Filters"); } @@ -5419,51 +5941,68 @@ ice_vsi_disable_tx(struct ice_vsi *vsi) enum ice_status status; u32 *q_teids; u16 *q_ids, *q_handles; - int i, err = 0; + size_t q_teids_size, q_ids_size, q_handles_size; + int tc, j, buf_idx, err = 0; if (vsi->num_tx_queues > 255) return (ENOSYS); - q_teids = (u32 *)malloc(sizeof(*q_teids) * vsi->num_tx_queues, - M_ICE, M_NOWAIT|M_ZERO); + q_teids_size = sizeof(*q_teids) * vsi->num_tx_queues; + q_teids = (u32 *)malloc(q_teids_size, M_ICE, M_NOWAIT|M_ZERO); if (!q_teids) return (ENOMEM); - q_ids = (u16 *)malloc(sizeof(*q_ids) * vsi->num_tx_queues, - M_ICE, M_NOWAIT|M_ZERO); + q_ids_size = sizeof(*q_ids) * vsi->num_tx_queues; + q_ids = (u16 *)malloc(q_ids_size, M_ICE, M_NOWAIT|M_ZERO); if (!q_ids) { err = (ENOMEM); goto free_q_teids; } - q_handles = (u16 *)malloc(sizeof(*q_handles) * vsi->num_tx_queues, - M_ICE, M_NOWAIT|M_ZERO); + q_handles_size = sizeof(*q_handles) * vsi->num_tx_queues; + q_handles = (u16 *)malloc(q_handles_size, M_ICE, M_NOWAIT|M_ZERO); if (!q_handles) { err = (ENOMEM); goto free_q_ids; } + ice_for_each_traffic_class(tc) { + buf_idx = 0; + for (j = 0; j < vsi->num_tx_queues; j++) { + struct ice_tx_queue *txq = &vsi->tx_queues[j]; - for (i = 0; i < vsi->num_tx_queues; i++) { - struct ice_tx_queue *txq = &vsi->tx_queues[i]; + if (txq->tc != tc) + continue; - q_ids[i] = vsi->tx_qmap[i]; - q_handles[i] = i; - q_teids[i] = txq->q_teid; - } + q_ids[buf_idx] = vsi->tx_qmap[j]; + q_handles[buf_idx] = txq->q_handle; + q_teids[buf_idx] = txq->q_teid; + buf_idx++; + } + /* Skip TC if no queues belong to it */ + if (buf_idx == 0) + continue; - status = ice_dis_vsi_txq(hw->port_info, vsi->idx, 0, vsi->num_tx_queues, - q_handles, q_ids, q_teids, ICE_NO_RESET, 0, NULL); - if (status == ICE_ERR_DOES_NOT_EXIST) { - ; /* Queues have already been disabled, no need to report this as an error */ - } else if (status == ICE_ERR_RESET_ONGOING) { - device_printf(sc->dev, - "Reset in progress. LAN Tx queues already disabled\n"); - } else if (status) { - device_printf(sc->dev, - "Failed to disable LAN Tx queues: err %s aq_err %s\n", - ice_status_str(status), ice_aq_str(hw->adminq.sq_last_status)); - err = (ENODEV); + status = ice_dis_vsi_txq(hw->port_info, vsi->idx, tc, buf_idx, + q_handles, q_ids, q_teids, ICE_NO_RESET, 0, NULL); + if (status == ICE_ERR_DOES_NOT_EXIST) { + ; /* Queues have already been disabled, no need to report this as an error */ + } else if (status == ICE_ERR_RESET_ONGOING) { + device_printf(sc->dev, + "Reset in progress. LAN Tx queues already disabled\n"); + break; + } else if (status) { + device_printf(sc->dev, + "Failed to disable LAN Tx queues: err %s aq_err %s\n", + ice_status_str(status), ice_aq_str(hw->adminq.sq_last_status)); + err = (ENODEV); + break; + } + + /* Clear buffers */ + memset(q_teids, 0, q_teids_size); + memset(q_ids, 0, q_ids_size); + memset(q_handles, 0, q_handles_size); } /* free_q_handles: */ @@ -5639,6 +6178,10 @@ ice_add_txq_sysctls(struct ice_tx_queue *txq) entry->description); entry++; } + + SYSCTL_ADD_U8(ctx, this_txq_list, OID_AUTO, "tc", + CTLFLAG_RD, &txq->tc, 0, + "Traffic Class that Queue belongs to"); } /** @@ -5681,6 +6224,10 @@ ice_add_rxq_sysctls(struct ice_rx_queue *rxq) entry->description); entry++; } + + SYSCTL_ADD_U8(ctx, this_rxq_list, OID_AUTO, "tc", + CTLFLAG_RD, &rxq->tc, 0, + "Traffic Class that Queue belongs to"); } /** @@ -6813,7 +7360,12 @@ ice_init_dcb_setup(struct ice_softc *sc) * checked here because a more detailed dcbx agent status is * retrieved and checked in ice_init_dcb() and below. */ - ice_aq_start_stop_dcbx(hw, true, &dcbx_agent_status, NULL); + status = ice_aq_start_stop_dcbx(hw, true, &dcbx_agent_status, NULL); + if (status && hw->adminq.sq_last_status != ICE_AQ_RC_EPERM) + device_printf(dev, + "start_stop_dcbx failed, err %s aq_err %s\n", + ice_status_str(status), + ice_aq_str(hw->adminq.sq_last_status)); } /* This sets hw->port_info->qos_cfg.is_sw_lldp */ @@ -6856,25 +7408,79 @@ ice_init_dcb_setup(struct ice_softc *sc) } /** - * ice_handle_mib_change_event - helper function to log LLDP MIB change events - * @sc: device softc - * @event: event received on a control queue + * ice_dcb_get_tc_map - Scans config to get bitmap of enabled TCs + * @dcbcfg: DCB configuration to examine + * + * Scans a TC mapping table inside dcbcfg to find traffic classes + * enabled and @returns a bitmask of enabled TCs + */ +static u8 +ice_dcb_get_tc_map(const struct ice_dcbx_cfg *dcbcfg) +{ + u8 tc_map = 0; + int i = 0; + + switch (dcbcfg->pfc_mode) { + case ICE_QOS_MODE_VLAN: + /* XXX: "i" is actually "User Priority" here, not + * Traffic Class, but the max for both is 8, so it works + * out here. + */ + for (i = 0; i < ICE_MAX_TRAFFIC_CLASS; i++) + tc_map |= BIT(dcbcfg->etscfg.prio_table[i]); + break; + default: + /* Invalid Mode */ + tc_map = ICE_DFLT_TRAFFIC_CLASS; + break; + } + + return (tc_map); +} + +/** + * ice_dcb_num_tc - Count the number of TCs in a bitmap + * @tc_map: bitmap of enabled traffic classes * - * Prints out the type of an LLDP MIB change event in a DCB debug message. + * @return the number of traffic classes in + * an 8-bit TC bitmap, or 0 if they are noncontiguous + */ +static u8 +ice_dcb_num_tc(u8 tc_map) +{ + bool tc_unused = false; + u8 ret = 0; + int i = 0; + + ice_for_each_traffic_class(i) { + if (tc_map & BIT(i)) { + if (!tc_unused) { + ret++; + } else { + /* Non-contiguous TCs detected */ + return (0); + } + } else + tc_unused = true; + } + + return (ret); +} + +/** + * ice_debug_print_mib_change_event - helper function to log LLDP MIB change events + * @sc: the device private softc + * @event: event received on a control queue * - * XXX: Should be extended to do more if the driver decides to notify other SW - * of LLDP MIB changes, or needs to extract info from the MIB. + * Prints out the type and contents of an LLDP MIB change event in a DCB debug message. */ static void -ice_handle_mib_change_event(struct ice_softc *sc, struct ice_rq_event_info *event) +ice_debug_print_mib_change_event(struct ice_softc *sc, struct ice_rq_event_info *event) { struct ice_aqc_lldp_get_mib *params = (struct ice_aqc_lldp_get_mib *)&event->desc.params.lldp_get_mib; u8 mib_type, bridge_type, tx_status; - /* XXX: To get the contents of the MIB that caused the event, set the - * ICE_DBG_AQ debug mask and read that output - */ static const char* mib_type_strings[] = { "Local MIB", "Remote MIB", @@ -6891,7 +7497,7 @@ ice_handle_mib_change_event(struct ice_softc *sc, struct ice_rq_event_info *even "Port's TX active", "Port's TX suspended and drained", "Reserved", - "Port's TX suspended and srained; blocked TC pipe flushed" + "Port's TX suspended and drained; blocked TC pipe flushed" }; mib_type = (params->type & ICE_AQ_LLDP_MIB_TYPE_M) >> @@ -6904,6 +7510,409 @@ ice_handle_mib_change_event(struct ice_softc *sc, struct ice_rq_event_info *even ice_debug(&sc->hw, ICE_DBG_DCB, "LLDP MIB Change Event (%s, %s, %s)\n", mib_type_strings[mib_type], bridge_type_strings[bridge_type], tx_status_strings[tx_status]); + + /* Nothing else to report */ + if (!event->msg_buf) + return; + + ice_debug(&sc->hw, ICE_DBG_DCB, "- %s contents:\n", mib_type_strings[mib_type]); + ice_debug_array(&sc->hw, ICE_DBG_DCB, 16, 1, event->msg_buf, + event->msg_len); +} + +/** + * ice_dcb_needs_reconfig - Returns true if driver needs to reconfigure + * @sc: the device private softc + * @old_cfg: Old DCBX configuration to compare against + * @new_cfg: New DCBX configuration to check + * + * @return true if something changed in new_cfg that requires the driver + * to do some reconfiguration. + */ +static bool +ice_dcb_needs_reconfig(struct ice_softc *sc, struct ice_dcbx_cfg *old_cfg, + struct ice_dcbx_cfg *new_cfg) +{ + struct ice_hw *hw = &sc->hw; + bool needs_reconfig = false; + + /* Check if ETS config has changed */ + if (memcmp(&new_cfg->etscfg, &old_cfg->etscfg, + sizeof(new_cfg->etscfg))) { + /* If Priority Table has changed, then driver reconfig is needed */ + if (memcmp(&new_cfg->etscfg.prio_table, + &old_cfg->etscfg.prio_table, + sizeof(new_cfg->etscfg.prio_table))) { + ice_debug(hw, ICE_DBG_DCB, "ETS UP2TC changed\n"); + needs_reconfig = true; + } + + /* These are just informational */ + if (memcmp(&new_cfg->etscfg.tcbwtable, + &old_cfg->etscfg.tcbwtable, + sizeof(new_cfg->etscfg.tcbwtable))) + ice_debug(hw, ICE_DBG_DCB, "ETS TCBW table changed\n"); + + if (memcmp(&new_cfg->etscfg.tsatable, + &old_cfg->etscfg.tsatable, + sizeof(new_cfg->etscfg.tsatable))) + ice_debug(hw, ICE_DBG_DCB, "ETS TSA table changed\n"); + } + + /* Check if PFC config has changed */ + if (memcmp(&new_cfg->pfc, &old_cfg->pfc, sizeof(new_cfg->pfc))) { + needs_reconfig = true; + ice_debug(hw, ICE_DBG_DCB, "PFC config changed\n"); + } + + ice_debug(hw, ICE_DBG_DCB, "%s result: %d\n", __func__, needs_reconfig); + + return (needs_reconfig); +} + +/** + * ice_stop_pf_vsi - Stop queues for PF LAN VSI + * @sc: the device private softc + * + * Flushes interrupts and stops the queues associated with the PF LAN VSI. + */ +static void +ice_stop_pf_vsi(struct ice_softc *sc) +{ + /* Dissociate the Tx and Rx queues from the interrupts */ + ice_flush_txq_interrupts(&sc->pf_vsi); + ice_flush_rxq_interrupts(&sc->pf_vsi); + + if (!ice_testandclear_state(&sc->state, ICE_STATE_DRIVER_INITIALIZED)) + return; + + /* Disable the Tx and Rx queues */ + ice_vsi_disable_tx(&sc->pf_vsi); + ice_control_rx_queues(&sc->pf_vsi, false); +} + +/** + * ice_vsi_setup_q_map - Setup a VSI queue map + * @vsi: the VSI being configured + * @ctxt: VSI context structure + */ +static void +ice_vsi_setup_q_map(struct ice_vsi *vsi, struct ice_vsi_ctx *ctxt) +{ + u16 offset = 0, qmap = 0, pow = 0; + u16 num_txq_per_tc, num_rxq_per_tc, qcount_rx; + int i, j, k; + + if (vsi->num_tcs == 0) { + /* at least TC0 should be enabled by default */ + vsi->num_tcs = 1; + vsi->tc_map = 0x1; + } + + qcount_rx = vsi->num_rx_queues; + num_rxq_per_tc = min(qcount_rx / vsi->num_tcs, ICE_MAX_RXQS_PER_TC); + if (!num_rxq_per_tc) + num_rxq_per_tc = 1; + + /* Have TX queue count match RX queue count */ + num_txq_per_tc = num_rxq_per_tc; + + /* find the (rounded up) power-of-2 of qcount */ + pow = flsl(num_rxq_per_tc - 1); + + /* TC mapping is a function of the number of Rx queues assigned to the + * VSI for each traffic class and the offset of these queues. + * The first 10 bits are for queue offset for TC0, next 4 bits for no:of + * queues allocated to TC0. No:of queues is a power-of-2. + * + * If TC is not enabled, the queue offset is set to 0, and allocate one + * queue, this way, traffic for the given TC will be sent to the default + * queue. + * + * Setup number and offset of Rx queues for all TCs for the VSI + */ + ice_for_each_traffic_class(i) { + if (!(vsi->tc_map & BIT(i))) { + /* TC is not enabled */ + vsi->tc_info[i].qoffset = 0; + vsi->tc_info[i].qcount_rx = 1; + vsi->tc_info[i].qcount_tx = 1; + + ctxt->info.tc_mapping[i] = 0; + continue; + } + + /* TC is enabled */ + vsi->tc_info[i].qoffset = offset; + vsi->tc_info[i].qcount_rx = num_rxq_per_tc; + vsi->tc_info[i].qcount_tx = num_txq_per_tc; + + qmap = ((offset << ICE_AQ_VSI_TC_Q_OFFSET_S) & + ICE_AQ_VSI_TC_Q_OFFSET_M) | + ((pow << ICE_AQ_VSI_TC_Q_NUM_S) & + ICE_AQ_VSI_TC_Q_NUM_M); + ctxt->info.tc_mapping[i] = CPU_TO_LE16(qmap); + + /* Store traffic class and handle data in queue structures */ + for (j = offset, k = 0; j < offset + num_txq_per_tc; j++, k++) { + vsi->tx_queues[j].q_handle = k; + vsi->tx_queues[j].tc = i; + } + for (j = offset; j < offset + num_rxq_per_tc; j++) + vsi->rx_queues[j].tc = i; + + offset += num_rxq_per_tc; + } + + /* Rx queue mapping */ + ctxt->info.mapping_flags |= CPU_TO_LE16(ICE_AQ_VSI_Q_MAP_CONTIG); + ctxt->info.q_mapping[0] = CPU_TO_LE16(vsi->rx_qmap[0]); + ctxt->info.q_mapping[1] = CPU_TO_LE16(vsi->num_rx_queues); +} + +/** + * ice_pf_vsi_cfg_tc - Configure PF VSI for a given TC map + * @sc: the device private softc + * @tc_map: traffic class bitmap + * + * @pre VSI queues are stopped + * + * @return 0 if configuration is successful + * @return EIO if Update VSI AQ cmd fails + * @return ENODEV if updating Tx Scheduler fails + */ +static int +ice_pf_vsi_cfg_tc(struct ice_softc *sc, u8 tc_map) +{ + u16 max_txqs[ICE_MAX_TRAFFIC_CLASS] = { 0 }; + struct ice_vsi *vsi = &sc->pf_vsi; + struct ice_hw *hw = &sc->hw; + struct ice_vsi_ctx ctx = { 0 }; + device_t dev = sc->dev; + enum ice_status status; + u8 num_tcs = 0; + int i = 0; + + /* Count the number of enabled Traffic Classes */ + ice_for_each_traffic_class(i) + if (tc_map & BIT(i)) + num_tcs++; + + vsi->tc_map = tc_map; + vsi->num_tcs = num_tcs; + + /* Set default parameters for context */ + ctx.vf_num = 0; + ctx.info = vsi->info; + + /* Setup queue map */ + ice_vsi_setup_q_map(vsi, &ctx); + + /* Update VSI configuration in firmware (RX queues) */ + ctx.info.valid_sections = CPU_TO_LE16(ICE_AQ_VSI_PROP_RXQ_MAP_VALID); + status = ice_update_vsi(hw, vsi->idx, &ctx, NULL); + if (status) { + device_printf(dev, + "%s: Update VSI AQ call failed, err %s aq_err %s\n", + __func__, ice_status_str(status), + ice_aq_str(hw->adminq.sq_last_status)); + return (EIO); + } + vsi->info = ctx.info; + + /* Use values derived in ice_vsi_setup_q_map() */ + for (i = 0; i < num_tcs; i++) + max_txqs[i] = vsi->tc_info[i].qcount_tx; + + /* Update LAN Tx queue info in firmware */ + status = ice_cfg_vsi_lan(hw->port_info, vsi->idx, vsi->tc_map, + max_txqs); + if (status) { + device_printf(dev, + "%s: Failed VSI lan queue config, err %s aq_err %s\n", + __func__, ice_status_str(status), + ice_aq_str(hw->adminq.sq_last_status)); + return (ENODEV); + } + + vsi->info.valid_sections = 0; + + return (0); +} + +/** + * ice_dcb_recfg - Reconfigure VSI with new DCB settings + * @sc: the device private softc + * + * @pre All VSIs have been disabled/stopped + * + * Reconfigures VSI settings based on local_dcbx_cfg. + */ +static void +ice_dcb_recfg(struct ice_softc *sc) +{ + struct ice_dcbx_cfg *dcbcfg = + &sc->hw.port_info->qos_cfg.local_dcbx_cfg; + device_t dev = sc->dev; + u8 tc_map = 0; + int ret; + + tc_map = ice_dcb_get_tc_map(dcbcfg); + + /* If non-contiguous TCs are used, then configure + * the default TC instead. There's no support for + * non-contiguous TCs being used. + */ + if (ice_dcb_num_tc(tc_map) == 0) { + tc_map = ICE_DFLT_TRAFFIC_CLASS; + ice_set_default_local_lldp_mib(sc); + } + + /* Reconfigure VSI queues to add/remove traffic classes */ + ret = ice_pf_vsi_cfg_tc(sc, tc_map); + if (ret) + device_printf(dev, + "Failed to configure TCs for PF VSI, err %s\n", + ice_err_str(ret)); + +} + +/** + * ice_do_dcb_reconfig - notify RDMA and reconfigure PF LAN VSI + * @sc: the device private softc + * + * @pre Determined that the DCB configuration requires a change + * + * Reconfigures the PF LAN VSI based on updated DCB configuration + * found in the hw struct's/port_info's/ local dcbx configuration. + */ +static void +ice_do_dcb_reconfig(struct ice_softc *sc) +{ + struct ice_aqc_port_ets_elem port_ets = { 0 }; + struct ice_dcbx_cfg *local_dcbx_cfg; + struct ice_hw *hw = &sc->hw; + struct ice_port_info *pi; + device_t dev = sc->dev; + enum ice_status status; + u8 tc_map; + + pi = sc->hw.port_info; + local_dcbx_cfg = &pi->qos_cfg.local_dcbx_cfg; + + /* Set state when there's more than one TC */ + tc_map = ice_dcb_get_tc_map(local_dcbx_cfg); + if (ice_dcb_num_tc(tc_map) > 1) { + device_printf(dev, "Multiple traffic classes enabled\n"); + ice_set_state(&sc->state, ICE_STATE_MULTIPLE_TCS); + } else { + device_printf(dev, "Multiple traffic classes disabled\n"); + ice_clear_state(&sc->state, ICE_STATE_MULTIPLE_TCS); + } + + /* Disable PF VSI since it's going to be reconfigured */ + ice_stop_pf_vsi(sc); + + /* Query ETS configuration and update SW Tx scheduler info */ + status = ice_query_port_ets(pi, &port_ets, sizeof(port_ets), NULL); + if (status != ICE_SUCCESS) { + device_printf(dev, + "Query Port ETS AQ call failed, err %s aq_err %s\n", + ice_status_str(status), + ice_aq_str(hw->adminq.sq_last_status)); + /* This won't break traffic, but QoS will not work as expected */ + } + + /* Change PF VSI configuration */ + ice_dcb_recfg(sc); + + ice_request_stack_reinit(sc); +} + +/** + * ice_handle_mib_change_event - helper function to handle LLDP MIB change events + * @sc: the device private softc + * @event: event received on a control queue + * + * Checks the updated MIB it receives and possibly reconfigures the PF LAN + * VSI depending on what has changed. This will also print out some debug + * information about the MIB event if ICE_DBG_DCB is enabled in the debug_mask. + */ +static void +ice_handle_mib_change_event(struct ice_softc *sc, struct ice_rq_event_info *event) +{ + struct ice_aqc_lldp_get_mib *params = + (struct ice_aqc_lldp_get_mib *)&event->desc.params.lldp_get_mib; + struct ice_dcbx_cfg tmp_dcbx_cfg, *local_dcbx_cfg; + struct ice_port_info *pi; + device_t dev = sc->dev; + struct ice_hw *hw = &sc->hw; + bool needs_reconfig; + enum ice_status status; + u8 mib_type, bridge_type; + + ASSERT_CFG_LOCKED(sc); + + ice_debug_print_mib_change_event(sc, event); + + pi = sc->hw.port_info; + + mib_type = (params->type & ICE_AQ_LLDP_MIB_TYPE_M) >> + ICE_AQ_LLDP_MIB_TYPE_S; + bridge_type = (params->type & ICE_AQ_LLDP_BRID_TYPE_M) >> + ICE_AQ_LLDP_BRID_TYPE_S; + + /* Ignore if event is not for Nearest Bridge */ + if (bridge_type != ICE_AQ_LLDP_BRID_TYPE_NEAREST_BRID) + return; + + /* Check MIB Type and return if event for Remote MIB update */ + if (mib_type == ICE_AQ_LLDP_MIB_REMOTE) { + /* Update the cached remote MIB and return */ + status = ice_aq_get_dcb_cfg(pi->hw, ICE_AQ_LLDP_MIB_REMOTE, + ICE_AQ_LLDP_BRID_TYPE_NEAREST_BRID, + &pi->qos_cfg.remote_dcbx_cfg); + if (status) + device_printf(dev, + "%s: Failed to get Remote DCB config; status %s, aq_err %s\n", + __func__, ice_status_str(status), + ice_aq_str(hw->adminq.sq_last_status)); + /* Not fatal if this fails */ + return; + } + + /* Save line length by aliasing the local dcbx cfg */ + local_dcbx_cfg = &pi->qos_cfg.local_dcbx_cfg; + /* Save off the old configuration and clear current config */ + tmp_dcbx_cfg = *local_dcbx_cfg; + memset(local_dcbx_cfg, 0, sizeof(*local_dcbx_cfg)); + + /* Get updated DCBX data from firmware */ + status = ice_get_dcb_cfg(pi); + if (status) { + device_printf(dev, + "%s: Failed to get Local DCB config; status %s, aq_err %s\n", + __func__, ice_status_str(status), + ice_aq_str(hw->adminq.sq_last_status)); + return; + } + + /* No change detected in DCBX config */ + if (!memcmp(&tmp_dcbx_cfg, local_dcbx_cfg, + sizeof(tmp_dcbx_cfg))) { + ice_debug(hw, ICE_DBG_DCB, "No change detected in local DCBX configuration\n"); + return; + } + + /* Check to see if DCB needs reconfiguring */ + needs_reconfig = ice_dcb_needs_reconfig(sc, &tmp_dcbx_cfg, + local_dcbx_cfg); + + if (!needs_reconfig) + return; + + /* Reconfigure */ + ice_do_dcb_reconfig(sc); } /** @@ -7441,7 +8450,6 @@ ice_apply_saved_phy_cfg(struct ice_softc *sc, u8 settings) u64 phy_low, phy_high; enum ice_status status; enum ice_fec_mode dflt_fec_mode; - enum ice_fc_mode dflt_fc_mode; u16 dflt_user_speed; if (!settings || settings > ICE_APPLY_LS_FEC_FC) { @@ -7465,7 +8473,6 @@ ice_apply_saved_phy_cfg(struct ice_softc *sc, u8 settings) /* Save off initial config parameters */ dflt_user_speed = ice_aq_phy_types_to_link_speeds(phy_low, phy_high); dflt_fec_mode = ice_caps_to_fec_mode(pcaps.caps, pcaps.link_fec_options); - dflt_fc_mode = ice_caps_to_fc_mode(pcaps.caps); /* Setup new PHY config */ ice_copy_phy_caps_to_cfg(pi, &pcaps, &cfg); @@ -8058,23 +9065,50 @@ ice_free_intr_tracking(struct ice_softc *sc) /** * ice_apply_supported_speed_filter - Mask off unsupported speeds * @report_speeds: bit-field for the desired link speeds + * @mod_type: type of module/sgmii connection we have * * Given a bitmap of the desired lenient mode link speeds, * this function will mask off the speeds that are not currently * supported by the device. */ static u16 -ice_apply_supported_speed_filter(u16 report_speeds) +ice_apply_supported_speed_filter(u16 report_speeds, u8 mod_type) { u16 speed_mask; + enum { IS_SGMII, IS_SFP, IS_QSFP } module; - /* We won't offer anything lower than 1G for any part, - * but we also won't offer anything under 25G for 100G - * parts or under 10G for 50G parts. + /* + * The SFF specification says 0 is unknown, so we'll + * treat it like we're connected through SGMII for now. + * This may need revisiting if a new type is supported + * in the future. */ - speed_mask = ~((u16)ICE_AQ_LINK_SPEED_1000MB - 1); - if (report_speeds & ICE_AQ_LINK_SPEED_50GB) - speed_mask = ~((u16)ICE_AQ_LINK_SPEED_10GB - 1); + switch (mod_type) { + case 0: + module = IS_SGMII; + break; + case 3: + module = IS_SFP; + break; + default: + module = IS_QSFP; + break; + } + + /* We won't offer anything lower than 100M for any part, + * but we'll need to mask off other speeds based on the + * device and module type. + */ + speed_mask = ~((u16)ICE_AQ_LINK_SPEED_100MB - 1); + if ((report_speeds & ICE_AQ_LINK_SPEED_10GB) && (module == IS_SFP)) + speed_mask = ~((u16)ICE_AQ_LINK_SPEED_1000MB - 1); + if (report_speeds & ICE_AQ_LINK_SPEED_25GB) + speed_mask = ~((u16)ICE_AQ_LINK_SPEED_1000MB - 1); + if (report_speeds & ICE_AQ_LINK_SPEED_50GB) { + speed_mask = ~((u16)ICE_AQ_LINK_SPEED_1000MB - 1); + if (module == IS_QSFP) + speed_mask = ~((u16)ICE_AQ_LINK_SPEED_10GB - 1); + } if (report_speeds & ICE_AQ_LINK_SPEED_100GB) speed_mask = ~((u16)ICE_AQ_LINK_SPEED_25GB - 1); return (report_speeds & speed_mask); @@ -8303,12 +9337,15 @@ ice_set_default_local_lldp_mib(struct ice_softc *sc) struct ice_port_info *pi; device_t dev = sc->dev; enum ice_status status; + u8 maxtcs, maxtcs_ets; pi = hw->port_info; + dcbcfg = &pi->qos_cfg.local_dcbx_cfg; + maxtcs = hw->func_caps.common_cap.maxtc; /* This value is only 3 bits; 8 TCs maps to 0 */ - u8 maxtcs = hw->func_caps.common_cap.maxtc & ICE_IEEE_ETS_MAXTC_M; + maxtcs_ets = maxtcs & ICE_IEEE_ETS_MAXTC_M; /** * Setup the default settings used by the driver for the Set Local @@ -8318,10 +9355,8 @@ ice_set_default_local_lldp_mib(struct ice_softc *sc) memset(dcbcfg, 0, sizeof(*dcbcfg)); dcbcfg->etscfg.willing = 1; dcbcfg->etscfg.tcbwtable[0] = 100; - dcbcfg->etscfg.maxtcs = maxtcs; - dcbcfg->etsrec.willing = 1; - dcbcfg->etsrec.tcbwtable[0] = 100; - dcbcfg->etsrec.maxtcs = maxtcs; + dcbcfg->etscfg.maxtcs = maxtcs_ets; + dcbcfg->etsrec = dcbcfg->etscfg; dcbcfg->pfc.willing = 1; dcbcfg->pfc.pfccap = maxtcs; @@ -8333,3 +9368,352 @@ ice_set_default_local_lldp_mib(struct ice_softc *sc) ice_status_str(status), ice_aq_str(hw->adminq.sq_last_status)); } + +/** + * ice_sbuf_print_ets_cfg - Helper function to print ETS cfg + * @sbuf: string buffer to print to + * @name: prefix string to use + * @ets: structure to pull values from + * + * A helper function for ice_sysctl_dump_dcbx_cfg(), this + * formats the ETS rec and cfg TLVs into text. + */ +static void +ice_sbuf_print_ets_cfg(struct sbuf *sbuf, const char *name, struct ice_dcb_ets_cfg *ets) +{ + sbuf_printf(sbuf, "%s.willing: %u\n", name, ets->willing); + sbuf_printf(sbuf, "%s.cbs: %u\n", name, ets->cbs); + sbuf_printf(sbuf, "%s.maxtcs: %u\n", name, ets->maxtcs); + + sbuf_printf(sbuf, "%s.prio_table:", name); + for (int i = 0; i < ICE_MAX_TRAFFIC_CLASS; i++) + sbuf_printf(sbuf, " %d", ets->prio_table[i]); + sbuf_printf(sbuf, "\n"); + + sbuf_printf(sbuf, "%s.tcbwtable:", name); + for (int i = 0; i < ICE_MAX_TRAFFIC_CLASS; i++) + sbuf_printf(sbuf, " %d", ets->tcbwtable[i]); + sbuf_printf(sbuf, "\n"); + + sbuf_printf(sbuf, "%s.tsatable:", name); + for (int i = 0; i < ICE_MAX_TRAFFIC_CLASS; i++) + sbuf_printf(sbuf, " %d", ets->tsatable[i]); + sbuf_printf(sbuf, "\n"); +} + +/** + * ice_sysctl_dump_dcbx_cfg - Print out DCBX/DCB config info + * @oidp: sysctl oid structure + * @arg1: pointer to private data structure + * @arg2: AQ define for either Local or Remote MIB + * @req: sysctl request pointer + * + * Prints out DCB/DCBX configuration, including the contents + * of either the local or remote MIB, depending on the value + * used in arg2. + */ +static int +ice_sysctl_dump_dcbx_cfg(SYSCTL_HANDLER_ARGS) +{ + struct ice_softc *sc = (struct ice_softc *)arg1; + struct ice_aqc_get_cee_dcb_cfg_resp cee_cfg = {}; + struct ice_dcbx_cfg dcb_buf = {}; + struct ice_dcbx_cfg *dcbcfg; + struct ice_hw *hw = &sc->hw; + device_t dev = sc->dev; + struct sbuf *sbuf; + enum ice_status status; + u8 maxtcs, dcbx_status, is_sw_lldp; + + UNREFERENCED_PARAMETER(oidp); + + if (ice_driver_is_detaching(sc)) + return (ESHUTDOWN); + + is_sw_lldp = hw->port_info->qos_cfg.is_sw_lldp; + + /* The driver doesn't receive a Remote MIB via SW */ + if (is_sw_lldp && arg2 == ICE_AQ_LLDP_MIB_REMOTE) + return (ENOENT); + + dcbcfg = &hw->port_info->qos_cfg.local_dcbx_cfg; + if (!is_sw_lldp) { + /* Collect information from the FW in FW LLDP mode */ + dcbcfg = &dcb_buf; + status = ice_aq_get_dcb_cfg(hw, (u8)arg2, + ICE_AQ_LLDP_BRID_TYPE_NEAREST_BRID, dcbcfg); + if (status && arg2 == ICE_AQ_LLDP_MIB_REMOTE && + hw->adminq.sq_last_status == ICE_AQ_RC_ENOENT) { + device_printf(dev, + "Unable to query Remote MIB; port has not received one yet\n"); + return (ENOENT); + } + if (status) { + device_printf(dev, "Unable to query LLDP MIB, err %s aq_err %s\n", + ice_status_str(status), + ice_aq_str(hw->adminq.sq_last_status)); + return (EIO); + } + } + + status = ice_aq_get_cee_dcb_cfg(hw, &cee_cfg, NULL); + if (status == ICE_SUCCESS) + dcbcfg->dcbx_mode = ICE_DCBX_MODE_CEE; + else if (hw->adminq.sq_last_status == ICE_AQ_RC_ENOENT) + dcbcfg->dcbx_mode = ICE_DCBX_MODE_IEEE; + + maxtcs = hw->func_caps.common_cap.maxtc; + dcbx_status = ice_get_dcbx_status(hw); + + sbuf = sbuf_new_for_sysctl(NULL, NULL, 128, req); + + /* Do the actual printing */ + sbuf_printf(sbuf, "\n"); + sbuf_printf(sbuf, "SW LLDP mode: %d\n", is_sw_lldp); + sbuf_printf(sbuf, "Function caps maxtcs: %d\n", maxtcs); + sbuf_printf(sbuf, "dcbx_status: %d\n", dcbx_status); + + sbuf_printf(sbuf, "numapps: %u\n", dcbcfg->numapps); + sbuf_printf(sbuf, "CEE TLV status: %u\n", dcbcfg->tlv_status); + sbuf_printf(sbuf, "pfc_mode: %s\n", (dcbcfg->pfc_mode == ICE_QOS_MODE_DSCP) ? + "DSCP" : "VLAN"); + sbuf_printf(sbuf, "dcbx_mode: %s\n", + (dcbcfg->dcbx_mode == ICE_DCBX_MODE_IEEE) ? "IEEE" : + (dcbcfg->dcbx_mode == ICE_DCBX_MODE_CEE) ? "CEE" : + "Unknown"); + + ice_sbuf_print_ets_cfg(sbuf, "etscfg", &dcbcfg->etscfg); + ice_sbuf_print_ets_cfg(sbuf, "etsrec", &dcbcfg->etsrec); + + sbuf_printf(sbuf, "pfc.willing: %u\n", dcbcfg->pfc.willing); + sbuf_printf(sbuf, "pfc.mbc: %u\n", dcbcfg->pfc.mbc); + sbuf_printf(sbuf, "pfc.pfccap: 0x%0x\n", dcbcfg->pfc.pfccap); + sbuf_printf(sbuf, "pfc.pfcena: 0x%0x\n", dcbcfg->pfc.pfcena); + + if (arg2 == ICE_AQ_LLDP_MIB_LOCAL) { + sbuf_printf(sbuf, "\nLocal registers:\n"); + sbuf_printf(sbuf, "PRTDCB_GENC.NUMTC: %d\n", + (rd32(hw, PRTDCB_GENC) & PRTDCB_GENC_NUMTC_M) + >> PRTDCB_GENC_NUMTC_S); + sbuf_printf(sbuf, "PRTDCB_TUP2TC: 0x%0x\n", + (rd32(hw, PRTDCB_TUP2TC))); + sbuf_printf(sbuf, "PRTDCB_RUP2TC: 0x%0x\n", + (rd32(hw, PRTDCB_RUP2TC))); + sbuf_printf(sbuf, "GLDCB_TC2PFC: 0x%0x\n", + (rd32(hw, GLDCB_TC2PFC))); + } + + /* Finish */ + sbuf_finish(sbuf); + sbuf_delete(sbuf); + + return (0); +} + +/** + * ice_sysctl_dump_vsi_cfg - print PF LAN VSI configuration + * @oidp: sysctl oid structure + * @arg1: pointer to private data structure + * @arg2: unused + * @req: sysctl request pointer + * + * XXX: This could be extended to apply to arbitrary PF-owned VSIs, + * but for simplicity, this only works on the PF's LAN VSI. + */ +static int +ice_sysctl_dump_vsi_cfg(SYSCTL_HANDLER_ARGS) +{ + struct ice_softc *sc = (struct ice_softc *)arg1; + struct ice_vsi_ctx ctx = { 0 }; + struct ice_hw *hw = &sc->hw; + device_t dev = sc->dev; + struct sbuf *sbuf; + enum ice_status status; + + UNREFERENCED_PARAMETER(oidp); + UNREFERENCED_PARAMETER(arg2); + + if (ice_driver_is_detaching(sc)) + return (ESHUTDOWN); + + /* Get HW absolute index of a VSI */ + ctx.vsi_num = ice_get_hw_vsi_num(hw, sc->pf_vsi.idx); + + status = ice_aq_get_vsi_params(hw, &ctx, NULL); + if (status != ICE_SUCCESS) { + device_printf(dev, + "Get VSI AQ call failed, err %s aq_err %s\n", + ice_status_str(status), + ice_aq_str(hw->adminq.sq_last_status)); + return (EIO); + } + + sbuf = sbuf_new_for_sysctl(NULL, NULL, 128, req); + + /* Do the actual printing */ + sbuf_printf(sbuf, "\n"); + + sbuf_printf(sbuf, "VSI NUM: %d\n", ctx.vsi_num); + sbuf_printf(sbuf, "VF NUM: %d\n", ctx.vf_num); + sbuf_printf(sbuf, "VSIs allocated: %d\n", ctx.vsis_allocd); + sbuf_printf(sbuf, "VSIs unallocated: %d\n", ctx.vsis_unallocated); + + sbuf_printf(sbuf, "Rx Queue Map method: %d\n", + LE16_TO_CPU(ctx.info.mapping_flags)); + /* The PF VSI is always contiguous, so there's no if-statement here */ + sbuf_printf(sbuf, "Rx Queue base: %d\n", + LE16_TO_CPU(ctx.info.q_mapping[0])); + sbuf_printf(sbuf, "Rx Queue count: %d\n", + LE16_TO_CPU(ctx.info.q_mapping[1])); + + sbuf_printf(sbuf, "TC qbases :"); + for (int i = 0; i < ICE_MAX_TRAFFIC_CLASS; i++) { + sbuf_printf(sbuf, " %4d", + ctx.info.tc_mapping[i] & ICE_AQ_VSI_TC_Q_OFFSET_M); + } + sbuf_printf(sbuf, "\n"); + + sbuf_printf(sbuf, "TC qcounts :"); + for (int i = 0; i < ICE_MAX_TRAFFIC_CLASS; i++) { + sbuf_printf(sbuf, " %4d", + 1 << (ctx.info.tc_mapping[i] >> ICE_AQ_VSI_TC_Q_NUM_S)); + } + + /* Finish */ + sbuf_finish(sbuf); + sbuf_delete(sbuf); + + return (0); +} + +/** + * ice_ets_str_to_tbl - Parse string into ETS table + * @str: input string to parse + * @table: output eight values used for ETS values + * @limit: max valid value to accept for ETS values + * + * Parses a string and converts the eight values within + * into a table that can be used in setting ETS settings + * in a MIB. + * + * @return 0 on success, EINVAL if a parsed value is + * not between 0 and limit. + */ +static int +ice_ets_str_to_tbl(const char *str, u8 *table, u8 limit) +{ + const char *str_start = str; + char *str_end; + long token; + + for (int i = 0; i < ICE_MAX_TRAFFIC_CLASS; i++) { + token = strtol(str_start, &str_end, 0); + if (token < 0 || token > limit) + return (EINVAL); + + table[i] = (u8)token; + str_start = (str_end + 1); + } + + return (0); +} + +/** + * ice_check_ets_bw - Check if ETS bw vals are valid + * @table: eight values used for ETS bandwidth + * + * @return true if the sum of all 8 values in table + * equals 100. + */ +static bool +ice_check_ets_bw(u8 *table) +{ + int sum = 0; + for (int i = 0; i < ICE_MAX_TRAFFIC_CLASS; i++) + sum += (int)table[i]; + + return (sum == 100); +} + +/** + * ice_cfg_pba_num - Determine if PBA Number is retrievable + * @sc: the device private softc structure + * + * Sets the feature flag for the existence of a PBA number + * based on the success of the read command. This does not + * cache the result. + */ +void +ice_cfg_pba_num(struct ice_softc *sc) +{ + u8 pba_string[32] = ""; + + if ((ice_is_bit_set(sc->feat_cap, ICE_FEATURE_HAS_PBA)) && + (ice_read_pba_string(&sc->hw, pba_string, sizeof(pba_string)) == 0)) + ice_set_bit(ICE_FEATURE_HAS_PBA, sc->feat_en); +} + +/** + * ice_sysctl_query_port_ets - print Port ETS Config from AQ + * @oidp: sysctl oid structure + * @arg1: pointer to private data structure + * @arg2: unused + * @req: sysctl request pointer + */ +static int +ice_sysctl_query_port_ets(SYSCTL_HANDLER_ARGS) +{ + struct ice_softc *sc = (struct ice_softc *)arg1; + struct ice_aqc_port_ets_elem port_ets = { 0 }; + struct ice_hw *hw = &sc->hw; + struct ice_port_info *pi; + device_t dev = sc->dev; + struct sbuf *sbuf; + enum ice_status status; + int i = 0; + + UNREFERENCED_PARAMETER(oidp); + UNREFERENCED_PARAMETER(arg2); + + if (ice_driver_is_detaching(sc)) + return (ESHUTDOWN); + + pi = hw->port_info; + + status = ice_aq_query_port_ets(pi, &port_ets, sizeof(port_ets), NULL); + if (status != ICE_SUCCESS) { + device_printf(dev, + "Query Port ETS AQ call failed, err %s aq_err %s\n", + ice_status_str(status), + ice_aq_str(hw->adminq.sq_last_status)); + return (EIO); + } + + sbuf = sbuf_new_for_sysctl(NULL, NULL, 128, req); + + /* Do the actual printing */ + sbuf_printf(sbuf, "\n"); + + sbuf_printf(sbuf, "Valid TC map: 0x%x\n", port_ets.tc_valid_bits); + + sbuf_printf(sbuf, "TC BW %%:"); + ice_for_each_traffic_class(i) { + sbuf_printf(sbuf, " %3d", port_ets.tc_bw_share[i]); + } + sbuf_printf(sbuf, "\n"); + + sbuf_printf(sbuf, "EIR profile ID: %d\n", port_ets.port_eir_prof_id); + sbuf_printf(sbuf, "CIR profile ID: %d\n", port_ets.port_cir_prof_id); + sbuf_printf(sbuf, "TC Node prio: 0x%x\n", port_ets.tc_node_prio); + + sbuf_printf(sbuf, "TC Node TEIDs:\n"); + ice_for_each_traffic_class(i) { + sbuf_printf(sbuf, "%d: %d\n", i, port_ets.tc_node_teid[i]); + } + + /* Finish */ + sbuf_finish(sbuf); + sbuf_delete(sbuf); + + return (0); +} |
