|
@@ -105,6 +105,8 @@ static void qed_qm_info_free(struct qed_hwfn *p_hwfn)
|
|
|
qm_info->qm_vport_params = NULL;
|
|
|
kfree(qm_info->qm_port_params);
|
|
|
qm_info->qm_port_params = NULL;
|
|
|
+ kfree(qm_info->wfq_data);
|
|
|
+ qm_info->wfq_data = NULL;
|
|
|
}
|
|
|
|
|
|
void qed_resc_free(struct qed_dev *cdev)
|
|
@@ -175,6 +177,11 @@ static int qed_init_qm_info(struct qed_hwfn *p_hwfn)
|
|
|
if (!qm_info->qm_port_params)
|
|
|
goto alloc_err;
|
|
|
|
|
|
+ qm_info->wfq_data = kcalloc(num_vports, sizeof(*qm_info->wfq_data),
|
|
|
+ GFP_KERNEL);
|
|
|
+ if (!qm_info->wfq_data)
|
|
|
+ goto alloc_err;
|
|
|
+
|
|
|
vport_id = (u8)RESC_START(p_hwfn, QED_VPORT);
|
|
|
|
|
|
/* First init per-TC PQs */
|
|
@@ -213,18 +220,19 @@ static int qed_init_qm_info(struct qed_hwfn *p_hwfn)
|
|
|
|
|
|
qm_info->start_vport = (u8)RESC_START(p_hwfn, QED_VPORT);
|
|
|
|
|
|
+ for (i = 0; i < qm_info->num_vports; i++)
|
|
|
+ qm_info->qm_vport_params[i].vport_wfq = 1;
|
|
|
+
|
|
|
qm_info->pf_wfq = 0;
|
|
|
qm_info->pf_rl = 0;
|
|
|
qm_info->vport_rl_en = 1;
|
|
|
+ qm_info->vport_wfq_en = 1;
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
alloc_err:
|
|
|
DP_NOTICE(p_hwfn, "Failed to allocate memory for QM params\n");
|
|
|
- kfree(qm_info->qm_pq_params);
|
|
|
- kfree(qm_info->qm_vport_params);
|
|
|
- kfree(qm_info->qm_port_params);
|
|
|
-
|
|
|
+ qed_qm_info_free(p_hwfn);
|
|
|
return -ENOMEM;
|
|
|
}
|
|
|
|
|
@@ -575,7 +583,7 @@ static int qed_hw_init_pf(struct qed_hwfn *p_hwfn,
|
|
|
p_hwfn->qm_info.pf_wfq = p_info->bandwidth_min;
|
|
|
|
|
|
/* Update rate limit once we'll actually have a link */
|
|
|
- p_hwfn->qm_info.pf_rl = 100;
|
|
|
+ p_hwfn->qm_info.pf_rl = 100000;
|
|
|
}
|
|
|
|
|
|
qed_cxt_hw_init_pf(p_hwfn);
|
|
@@ -1595,3 +1603,312 @@ int qed_fw_rss_eng(struct qed_hwfn *p_hwfn,
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
|
+
|
|
|
+/* Calculate final WFQ values for all vports and configure them.
|
|
|
+ * After this configuration each vport will have
|
|
|
+ * approx min rate = min_pf_rate * (vport_wfq / QED_WFQ_UNIT)
|
|
|
+ */
|
|
|
+static void qed_configure_wfq_for_all_vports(struct qed_hwfn *p_hwfn,
|
|
|
+ struct qed_ptt *p_ptt,
|
|
|
+ u32 min_pf_rate)
|
|
|
+{
|
|
|
+ struct init_qm_vport_params *vport_params;
|
|
|
+ int i;
|
|
|
+
|
|
|
+ vport_params = p_hwfn->qm_info.qm_vport_params;
|
|
|
+
|
|
|
+ for (i = 0; i < p_hwfn->qm_info.num_vports; i++) {
|
|
|
+ u32 wfq_speed = p_hwfn->qm_info.wfq_data[i].min_speed;
|
|
|
+
|
|
|
+ vport_params[i].vport_wfq = (wfq_speed * QED_WFQ_UNIT) /
|
|
|
+ min_pf_rate;
|
|
|
+ qed_init_vport_wfq(p_hwfn, p_ptt,
|
|
|
+ vport_params[i].first_tx_pq_id,
|
|
|
+ vport_params[i].vport_wfq);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static void qed_init_wfq_default_param(struct qed_hwfn *p_hwfn,
|
|
|
+ u32 min_pf_rate)
|
|
|
+
|
|
|
+{
|
|
|
+ int i;
|
|
|
+
|
|
|
+ for (i = 0; i < p_hwfn->qm_info.num_vports; i++)
|
|
|
+ p_hwfn->qm_info.qm_vport_params[i].vport_wfq = 1;
|
|
|
+}
|
|
|
+
|
|
|
+static void qed_disable_wfq_for_all_vports(struct qed_hwfn *p_hwfn,
|
|
|
+ struct qed_ptt *p_ptt,
|
|
|
+ u32 min_pf_rate)
|
|
|
+{
|
|
|
+ struct init_qm_vport_params *vport_params;
|
|
|
+ int i;
|
|
|
+
|
|
|
+ vport_params = p_hwfn->qm_info.qm_vport_params;
|
|
|
+
|
|
|
+ for (i = 0; i < p_hwfn->qm_info.num_vports; i++) {
|
|
|
+ qed_init_wfq_default_param(p_hwfn, min_pf_rate);
|
|
|
+ qed_init_vport_wfq(p_hwfn, p_ptt,
|
|
|
+ vport_params[i].first_tx_pq_id,
|
|
|
+ vport_params[i].vport_wfq);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/* This function performs several validations for WFQ
|
|
|
+ * configuration and required min rate for a given vport
|
|
|
+ * 1. req_rate must be greater than one percent of min_pf_rate.
|
|
|
+ * 2. req_rate should not cause other vports [not configured for WFQ explicitly]
|
|
|
+ * rates to get less than one percent of min_pf_rate.
|
|
|
+ * 3. total_req_min_rate [all vports min rate sum] shouldn't exceed min_pf_rate.
|
|
|
+ */
|
|
|
+static int qed_init_wfq_param(struct qed_hwfn *p_hwfn,
|
|
|
+ u16 vport_id, u32 req_rate,
|
|
|
+ u32 min_pf_rate)
|
|
|
+{
|
|
|
+ u32 total_req_min_rate = 0, total_left_rate = 0, left_rate_per_vp = 0;
|
|
|
+ int non_requested_count = 0, req_count = 0, i, num_vports;
|
|
|
+
|
|
|
+ num_vports = p_hwfn->qm_info.num_vports;
|
|
|
+
|
|
|
+ /* Accounting for the vports which are configured for WFQ explicitly */
|
|
|
+ for (i = 0; i < num_vports; i++) {
|
|
|
+ u32 tmp_speed;
|
|
|
+
|
|
|
+ if ((i != vport_id) &&
|
|
|
+ p_hwfn->qm_info.wfq_data[i].configured) {
|
|
|
+ req_count++;
|
|
|
+ tmp_speed = p_hwfn->qm_info.wfq_data[i].min_speed;
|
|
|
+ total_req_min_rate += tmp_speed;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Include current vport data as well */
|
|
|
+ req_count++;
|
|
|
+ total_req_min_rate += req_rate;
|
|
|
+ non_requested_count = num_vports - req_count;
|
|
|
+
|
|
|
+ if (req_rate < min_pf_rate / QED_WFQ_UNIT) {
|
|
|
+ DP_VERBOSE(p_hwfn, NETIF_MSG_LINK,
|
|
|
+ "Vport [%d] - Requested rate[%d Mbps] is less than one percent of configured PF min rate[%d Mbps]\n",
|
|
|
+ vport_id, req_rate, min_pf_rate);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (num_vports > QED_WFQ_UNIT) {
|
|
|
+ DP_VERBOSE(p_hwfn, NETIF_MSG_LINK,
|
|
|
+ "Number of vports is greater than %d\n",
|
|
|
+ QED_WFQ_UNIT);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (total_req_min_rate > min_pf_rate) {
|
|
|
+ DP_VERBOSE(p_hwfn, NETIF_MSG_LINK,
|
|
|
+ "Total requested min rate for all vports[%d Mbps] is greater than configured PF min rate[%d Mbps]\n",
|
|
|
+ total_req_min_rate, min_pf_rate);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ total_left_rate = min_pf_rate - total_req_min_rate;
|
|
|
+
|
|
|
+ left_rate_per_vp = total_left_rate / non_requested_count;
|
|
|
+ if (left_rate_per_vp < min_pf_rate / QED_WFQ_UNIT) {
|
|
|
+ DP_VERBOSE(p_hwfn, NETIF_MSG_LINK,
|
|
|
+ "Non WFQ configured vports rate [%d Mbps] is less than one percent of configured PF min rate[%d Mbps]\n",
|
|
|
+ left_rate_per_vp, min_pf_rate);
|
|
|
+ return -EINVAL;
|
|
|
+ }
|
|
|
+
|
|
|
+ p_hwfn->qm_info.wfq_data[vport_id].min_speed = req_rate;
|
|
|
+ p_hwfn->qm_info.wfq_data[vport_id].configured = true;
|
|
|
+
|
|
|
+ for (i = 0; i < num_vports; i++) {
|
|
|
+ if (p_hwfn->qm_info.wfq_data[i].configured)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ p_hwfn->qm_info.wfq_data[i].min_speed = left_rate_per_vp;
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int __qed_configure_vp_wfq_on_link_change(struct qed_hwfn *p_hwfn,
|
|
|
+ struct qed_ptt *p_ptt,
|
|
|
+ u32 min_pf_rate)
|
|
|
+{
|
|
|
+ bool use_wfq = false;
|
|
|
+ int rc = 0;
|
|
|
+ u16 i;
|
|
|
+
|
|
|
+ /* Validate all pre configured vports for wfq */
|
|
|
+ for (i = 0; i < p_hwfn->qm_info.num_vports; i++) {
|
|
|
+ u32 rate;
|
|
|
+
|
|
|
+ if (!p_hwfn->qm_info.wfq_data[i].configured)
|
|
|
+ continue;
|
|
|
+
|
|
|
+ rate = p_hwfn->qm_info.wfq_data[i].min_speed;
|
|
|
+ use_wfq = true;
|
|
|
+
|
|
|
+ rc = qed_init_wfq_param(p_hwfn, i, rate, min_pf_rate);
|
|
|
+ if (rc) {
|
|
|
+ DP_NOTICE(p_hwfn,
|
|
|
+ "WFQ validation failed while configuring min rate\n");
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!rc && use_wfq)
|
|
|
+ qed_configure_wfq_for_all_vports(p_hwfn, p_ptt, min_pf_rate);
|
|
|
+ else
|
|
|
+ qed_disable_wfq_for_all_vports(p_hwfn, p_ptt, min_pf_rate);
|
|
|
+
|
|
|
+ return rc;
|
|
|
+}
|
|
|
+
|
|
|
+/* API to configure WFQ from mcp link change */
|
|
|
+void qed_configure_vp_wfq_on_link_change(struct qed_dev *cdev, u32 min_pf_rate)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+
|
|
|
+ for_each_hwfn(cdev, i) {
|
|
|
+ struct qed_hwfn *p_hwfn = &cdev->hwfns[i];
|
|
|
+
|
|
|
+ __qed_configure_vp_wfq_on_link_change(p_hwfn,
|
|
|
+ p_hwfn->p_dpc_ptt,
|
|
|
+ min_pf_rate);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+int __qed_configure_pf_max_bandwidth(struct qed_hwfn *p_hwfn,
|
|
|
+ struct qed_ptt *p_ptt,
|
|
|
+ struct qed_mcp_link_state *p_link,
|
|
|
+ u8 max_bw)
|
|
|
+{
|
|
|
+ int rc = 0;
|
|
|
+
|
|
|
+ p_hwfn->mcp_info->func_info.bandwidth_max = max_bw;
|
|
|
+
|
|
|
+ if (!p_link->line_speed && (max_bw != 100))
|
|
|
+ return rc;
|
|
|
+
|
|
|
+ p_link->speed = (p_link->line_speed * max_bw) / 100;
|
|
|
+ p_hwfn->qm_info.pf_rl = p_link->speed;
|
|
|
+
|
|
|
+ /* Since the limiter also affects Tx-switched traffic, we don't want it
|
|
|
+ * to limit such traffic in case there's no actual limit.
|
|
|
+ * In that case, set limit to imaginary high boundary.
|
|
|
+ */
|
|
|
+ if (max_bw == 100)
|
|
|
+ p_hwfn->qm_info.pf_rl = 100000;
|
|
|
+
|
|
|
+ rc = qed_init_pf_rl(p_hwfn, p_ptt, p_hwfn->rel_pf_id,
|
|
|
+ p_hwfn->qm_info.pf_rl);
|
|
|
+
|
|
|
+ DP_VERBOSE(p_hwfn, NETIF_MSG_LINK,
|
|
|
+ "Configured MAX bandwidth to be %08x Mb/sec\n",
|
|
|
+ p_link->speed);
|
|
|
+
|
|
|
+ return rc;
|
|
|
+}
|
|
|
+
|
|
|
+/* Main API to configure PF max bandwidth where bw range is [1 - 100] */
|
|
|
+int qed_configure_pf_max_bandwidth(struct qed_dev *cdev, u8 max_bw)
|
|
|
+{
|
|
|
+ int i, rc = -EINVAL;
|
|
|
+
|
|
|
+ if (max_bw < 1 || max_bw > 100) {
|
|
|
+ DP_NOTICE(cdev, "PF max bw valid range is [1-100]\n");
|
|
|
+ return rc;
|
|
|
+ }
|
|
|
+
|
|
|
+ for_each_hwfn(cdev, i) {
|
|
|
+ struct qed_hwfn *p_hwfn = &cdev->hwfns[i];
|
|
|
+ struct qed_hwfn *p_lead = QED_LEADING_HWFN(cdev);
|
|
|
+ struct qed_mcp_link_state *p_link;
|
|
|
+ struct qed_ptt *p_ptt;
|
|
|
+
|
|
|
+ p_link = &p_lead->mcp_info->link_output;
|
|
|
+
|
|
|
+ p_ptt = qed_ptt_acquire(p_hwfn);
|
|
|
+ if (!p_ptt)
|
|
|
+ return -EBUSY;
|
|
|
+
|
|
|
+ rc = __qed_configure_pf_max_bandwidth(p_hwfn, p_ptt,
|
|
|
+ p_link, max_bw);
|
|
|
+
|
|
|
+ qed_ptt_release(p_hwfn, p_ptt);
|
|
|
+
|
|
|
+ if (rc)
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ return rc;
|
|
|
+}
|
|
|
+
|
|
|
+int __qed_configure_pf_min_bandwidth(struct qed_hwfn *p_hwfn,
|
|
|
+ struct qed_ptt *p_ptt,
|
|
|
+ struct qed_mcp_link_state *p_link,
|
|
|
+ u8 min_bw)
|
|
|
+{
|
|
|
+ int rc = 0;
|
|
|
+
|
|
|
+ p_hwfn->mcp_info->func_info.bandwidth_min = min_bw;
|
|
|
+ p_hwfn->qm_info.pf_wfq = min_bw;
|
|
|
+
|
|
|
+ if (!p_link->line_speed)
|
|
|
+ return rc;
|
|
|
+
|
|
|
+ p_link->min_pf_rate = (p_link->line_speed * min_bw) / 100;
|
|
|
+
|
|
|
+ rc = qed_init_pf_wfq(p_hwfn, p_ptt, p_hwfn->rel_pf_id, min_bw);
|
|
|
+
|
|
|
+ DP_VERBOSE(p_hwfn, NETIF_MSG_LINK,
|
|
|
+ "Configured MIN bandwidth to be %d Mb/sec\n",
|
|
|
+ p_link->min_pf_rate);
|
|
|
+
|
|
|
+ return rc;
|
|
|
+}
|
|
|
+
|
|
|
+/* Main API to configure PF min bandwidth where bw range is [1-100] */
|
|
|
+int qed_configure_pf_min_bandwidth(struct qed_dev *cdev, u8 min_bw)
|
|
|
+{
|
|
|
+ int i, rc = -EINVAL;
|
|
|
+
|
|
|
+ if (min_bw < 1 || min_bw > 100) {
|
|
|
+ DP_NOTICE(cdev, "PF min bw valid range is [1-100]\n");
|
|
|
+ return rc;
|
|
|
+ }
|
|
|
+
|
|
|
+ for_each_hwfn(cdev, i) {
|
|
|
+ struct qed_hwfn *p_hwfn = &cdev->hwfns[i];
|
|
|
+ struct qed_hwfn *p_lead = QED_LEADING_HWFN(cdev);
|
|
|
+ struct qed_mcp_link_state *p_link;
|
|
|
+ struct qed_ptt *p_ptt;
|
|
|
+
|
|
|
+ p_link = &p_lead->mcp_info->link_output;
|
|
|
+
|
|
|
+ p_ptt = qed_ptt_acquire(p_hwfn);
|
|
|
+ if (!p_ptt)
|
|
|
+ return -EBUSY;
|
|
|
+
|
|
|
+ rc = __qed_configure_pf_min_bandwidth(p_hwfn, p_ptt,
|
|
|
+ p_link, min_bw);
|
|
|
+ if (rc) {
|
|
|
+ qed_ptt_release(p_hwfn, p_ptt);
|
|
|
+ return rc;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (p_link->min_pf_rate) {
|
|
|
+ u32 min_rate = p_link->min_pf_rate;
|
|
|
+
|
|
|
+ rc = __qed_configure_vp_wfq_on_link_change(p_hwfn,
|
|
|
+ p_ptt,
|
|
|
+ min_rate);
|
|
|
+ }
|
|
|
+
|
|
|
+ qed_ptt_release(p_hwfn, p_ptt);
|
|
|
+ }
|
|
|
+
|
|
|
+ return rc;
|
|
|
+}
|