|
@@ -536,6 +536,43 @@ static int qede_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+int qede_setup_tc(struct net_device *ndev, u8 num_tc)
|
|
|
+{
|
|
|
+ struct qede_dev *edev = netdev_priv(ndev);
|
|
|
+ int cos, count, offset;
|
|
|
+
|
|
|
+ if (num_tc > edev->dev_info.num_tc)
|
|
|
+ return -EINVAL;
|
|
|
+
|
|
|
+ netdev_reset_tc(ndev);
|
|
|
+ netdev_set_num_tc(ndev, num_tc);
|
|
|
+
|
|
|
+ for_each_cos_in_txq(edev, cos) {
|
|
|
+ count = QEDE_TSS_COUNT(edev);
|
|
|
+ offset = cos * QEDE_TSS_COUNT(edev);
|
|
|
+ netdev_set_tc_queue(ndev, cos, count, offset);
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int
|
|
|
+qede_setup_tc_offload(struct net_device *dev, enum tc_setup_type type,
|
|
|
+ void *type_data)
|
|
|
+{
|
|
|
+ struct tc_mqprio_qopt *mqprio;
|
|
|
+
|
|
|
+ switch (type) {
|
|
|
+ case TC_SETUP_QDISC_MQPRIO:
|
|
|
+ mqprio = type_data;
|
|
|
+
|
|
|
+ mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS;
|
|
|
+ return qede_setup_tc(dev, mqprio->num_tc);
|
|
|
+ default:
|
|
|
+ return -EOPNOTSUPP;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
static const struct net_device_ops qede_netdev_ops = {
|
|
|
.ndo_open = qede_open,
|
|
|
.ndo_stop = qede_close,
|
|
@@ -568,6 +605,7 @@ static const struct net_device_ops qede_netdev_ops = {
|
|
|
#ifdef CONFIG_RFS_ACCEL
|
|
|
.ndo_rx_flow_steer = qede_rx_flow_steer,
|
|
|
#endif
|
|
|
+ .ndo_setup_tc = qede_setup_tc_offload,
|
|
|
};
|
|
|
|
|
|
static const struct net_device_ops qede_netdev_vf_ops = {
|
|
@@ -621,7 +659,8 @@ static struct qede_dev *qede_alloc_etherdev(struct qed_dev *cdev,
|
|
|
struct qede_dev *edev;
|
|
|
|
|
|
ndev = alloc_etherdev_mqs(sizeof(*edev),
|
|
|
- info->num_queues, info->num_queues);
|
|
|
+ info->num_queues * info->num_tc,
|
|
|
+ info->num_queues);
|
|
|
if (!ndev) {
|
|
|
pr_err("etherdev allocation failed\n");
|
|
|
return NULL;
|
|
@@ -830,7 +869,8 @@ static int qede_alloc_fp_array(struct qede_dev *edev)
|
|
|
}
|
|
|
|
|
|
if (fp->type & QEDE_FASTPATH_TX) {
|
|
|
- fp->txq = kzalloc(sizeof(*fp->txq), GFP_KERNEL);
|
|
|
+ fp->txq = kcalloc(edev->dev_info.num_tc,
|
|
|
+ sizeof(*fp->txq), GFP_KERNEL);
|
|
|
if (!fp->txq)
|
|
|
goto err;
|
|
|
}
|
|
@@ -879,10 +919,15 @@ static void qede_sp_task(struct work_struct *work)
|
|
|
static void qede_update_pf_params(struct qed_dev *cdev)
|
|
|
{
|
|
|
struct qed_pf_params pf_params;
|
|
|
+ u16 num_cons;
|
|
|
|
|
|
/* 64 rx + 64 tx + 64 XDP */
|
|
|
memset(&pf_params, 0, sizeof(struct qed_pf_params));
|
|
|
- pf_params.eth_pf_params.num_cons = (MAX_SB_PER_PF_MIMD - 1) * 3;
|
|
|
+
|
|
|
+ /* 1 rx + 1 xdp + max tx cos */
|
|
|
+ num_cons = QED_MIN_L2_CONS;
|
|
|
+
|
|
|
+ pf_params.eth_pf_params.num_cons = (MAX_SB_PER_PF_MIMD - 1) * num_cons;
|
|
|
|
|
|
/* Same for VFs - make sure they'll have sufficient connections
|
|
|
* to support XDP Tx queues.
|
|
@@ -1363,8 +1408,12 @@ static void qede_free_mem_fp(struct qede_dev *edev, struct qede_fastpath *fp)
|
|
|
if (fp->type & QEDE_FASTPATH_XDP)
|
|
|
qede_free_mem_txq(edev, fp->xdp_tx);
|
|
|
|
|
|
- if (fp->type & QEDE_FASTPATH_TX)
|
|
|
- qede_free_mem_txq(edev, fp->txq);
|
|
|
+ if (fp->type & QEDE_FASTPATH_TX) {
|
|
|
+ int cos;
|
|
|
+
|
|
|
+ for_each_cos_in_txq(edev, cos)
|
|
|
+ qede_free_mem_txq(edev, &fp->txq[cos]);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/* This function allocates all memory needed for a single fp (i.e. an entity
|
|
@@ -1391,9 +1440,13 @@ static int qede_alloc_mem_fp(struct qede_dev *edev, struct qede_fastpath *fp)
|
|
|
}
|
|
|
|
|
|
if (fp->type & QEDE_FASTPATH_TX) {
|
|
|
- rc = qede_alloc_mem_txq(edev, fp->txq);
|
|
|
- if (rc)
|
|
|
- goto out;
|
|
|
+ int cos;
|
|
|
+
|
|
|
+ for_each_cos_in_txq(edev, cos) {
|
|
|
+ rc = qede_alloc_mem_txq(edev, &fp->txq[cos]);
|
|
|
+ if (rc)
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
out:
|
|
@@ -1466,10 +1519,23 @@ static void qede_init_fp(struct qede_dev *edev)
|
|
|
}
|
|
|
|
|
|
if (fp->type & QEDE_FASTPATH_TX) {
|
|
|
- fp->txq->index = txq_index++;
|
|
|
- if (edev->dev_info.is_legacy)
|
|
|
- fp->txq->is_legacy = 1;
|
|
|
- fp->txq->dev = &edev->pdev->dev;
|
|
|
+ int cos;
|
|
|
+
|
|
|
+ for_each_cos_in_txq(edev, cos) {
|
|
|
+ struct qede_tx_queue *txq = &fp->txq[cos];
|
|
|
+ u16 ndev_tx_id;
|
|
|
+
|
|
|
+ txq->cos = cos;
|
|
|
+ txq->index = txq_index;
|
|
|
+ ndev_tx_id = QEDE_TXQ_TO_NDEV_TXQ_ID(edev, txq);
|
|
|
+ txq->ndev_txq_id = ndev_tx_id;
|
|
|
+
|
|
|
+ if (edev->dev_info.is_legacy)
|
|
|
+ txq->is_legacy = 1;
|
|
|
+ txq->dev = &edev->pdev->dev;
|
|
|
+ }
|
|
|
+
|
|
|
+ txq_index++;
|
|
|
}
|
|
|
|
|
|
snprintf(fp->name, sizeof(fp->name), "%s-fp-%d",
|
|
@@ -1483,7 +1549,9 @@ static int qede_set_real_num_queues(struct qede_dev *edev)
|
|
|
{
|
|
|
int rc = 0;
|
|
|
|
|
|
- rc = netif_set_real_num_tx_queues(edev->ndev, QEDE_TSS_COUNT(edev));
|
|
|
+ rc = netif_set_real_num_tx_queues(edev->ndev,
|
|
|
+ QEDE_TSS_COUNT(edev) *
|
|
|
+ edev->dev_info.num_tc);
|
|
|
if (rc) {
|
|
|
DP_NOTICE(edev, "Failed to set real number of Tx queues\n");
|
|
|
return rc;
|
|
@@ -1685,9 +1753,13 @@ static int qede_stop_queues(struct qede_dev *edev)
|
|
|
fp = &edev->fp_array[i];
|
|
|
|
|
|
if (fp->type & QEDE_FASTPATH_TX) {
|
|
|
- rc = qede_drain_txq(edev, fp->txq, true);
|
|
|
- if (rc)
|
|
|
- return rc;
|
|
|
+ int cos;
|
|
|
+
|
|
|
+ for_each_cos_in_txq(edev, cos) {
|
|
|
+ rc = qede_drain_txq(edev, &fp->txq[cos], true);
|
|
|
+ if (rc)
|
|
|
+ return rc;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
if (fp->type & QEDE_FASTPATH_XDP) {
|
|
@@ -1703,9 +1775,13 @@ static int qede_stop_queues(struct qede_dev *edev)
|
|
|
|
|
|
/* Stop the Tx Queue(s) */
|
|
|
if (fp->type & QEDE_FASTPATH_TX) {
|
|
|
- rc = qede_stop_txq(edev, fp->txq, i);
|
|
|
- if (rc)
|
|
|
- return rc;
|
|
|
+ int cos;
|
|
|
+
|
|
|
+ for_each_cos_in_txq(edev, cos) {
|
|
|
+ rc = qede_stop_txq(edev, &fp->txq[cos], i);
|
|
|
+ if (rc)
|
|
|
+ return rc;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/* Stop the Rx Queue */
|
|
@@ -1758,6 +1834,7 @@ static int qede_start_txq(struct qede_dev *edev,
|
|
|
|
|
|
params.p_sb = fp->sb_info;
|
|
|
params.sb_idx = sb_idx;
|
|
|
+ params.tc = txq->cos;
|
|
|
|
|
|
rc = edev->ops->q_tx_start(edev->cdev, rss_id, ¶ms, phys_table,
|
|
|
page_cnt, &ret_params);
|
|
@@ -1877,9 +1954,14 @@ static int qede_start_queues(struct qede_dev *edev, bool clear_stats)
|
|
|
}
|
|
|
|
|
|
if (fp->type & QEDE_FASTPATH_TX) {
|
|
|
- rc = qede_start_txq(edev, fp, fp->txq, i, TX_PI(0));
|
|
|
- if (rc)
|
|
|
- goto out;
|
|
|
+ int cos;
|
|
|
+
|
|
|
+ for_each_cos_in_txq(edev, cos) {
|
|
|
+ rc = qede_start_txq(edev, fp, &fp->txq[cos], i,
|
|
|
+ TX_PI(cos));
|
|
|
+ if (rc)
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -1973,6 +2055,7 @@ static int qede_load(struct qede_dev *edev, enum qede_load_mode mode,
|
|
|
bool is_locked)
|
|
|
{
|
|
|
struct qed_link_params link_params;
|
|
|
+ u8 num_tc;
|
|
|
int rc;
|
|
|
|
|
|
DP_INFO(edev, "Starting qede load\n");
|
|
@@ -2019,6 +2102,10 @@ static int qede_load(struct qede_dev *edev, enum qede_load_mode mode,
|
|
|
goto err4;
|
|
|
DP_INFO(edev, "Start VPORT, RXQ and TXQ succeeded\n");
|
|
|
|
|
|
+ num_tc = netdev_get_num_tc(edev->ndev);
|
|
|
+ num_tc = num_tc ? num_tc : edev->dev_info.num_tc;
|
|
|
+ qede_setup_tc(edev->ndev, num_tc);
|
|
|
+
|
|
|
/* Program un-configured VLANs */
|
|
|
qede_configure_vlan_filters(edev);
|
|
|
|
|
@@ -2143,7 +2230,7 @@ static bool qede_is_txq_full(struct qede_dev *edev, struct qede_tx_queue *txq)
|
|
|
{
|
|
|
struct netdev_queue *netdev_txq;
|
|
|
|
|
|
- netdev_txq = netdev_get_tx_queue(edev->ndev, txq->index);
|
|
|
+ netdev_txq = netdev_get_tx_queue(edev->ndev, txq->ndev_txq_id);
|
|
|
if (netif_xmit_stopped(netdev_txq))
|
|
|
return true;
|
|
|
|
|
@@ -2208,9 +2295,11 @@ static void qede_get_eth_tlv_data(void *dev, void *data)
|
|
|
for_each_queue(i) {
|
|
|
fp = &edev->fp_array[i];
|
|
|
if (fp->type & QEDE_FASTPATH_TX) {
|
|
|
- if (fp->txq->sw_tx_cons != fp->txq->sw_tx_prod)
|
|
|
+ struct qede_tx_queue *txq = QEDE_FP_TC0_TXQ(fp);
|
|
|
+
|
|
|
+ if (txq->sw_tx_cons != txq->sw_tx_prod)
|
|
|
etlv->txqs_empty = false;
|
|
|
- if (qede_is_txq_full(edev, fp->txq))
|
|
|
+ if (qede_is_txq_full(edev, txq))
|
|
|
etlv->num_txqs_full++;
|
|
|
}
|
|
|
if (fp->type & QEDE_FASTPATH_RX) {
|